Canvas_Delegate.java revision 3f9b0376c69a5f679661d773ea7b59b1e6787090
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.graphics; 18 19import com.android.ide.common.rendering.api.LayoutLog; 20import com.android.layoutlib.bridge.Bridge; 21import com.android.layoutlib.bridge.impl.DelegateManager; 22import com.android.layoutlib.bridge.impl.GcSnapshot; 23 24import android.graphics.Bitmap.Config; 25import android.graphics.Paint_Delegate.FontInfo; 26import android.text.TextUtils; 27 28import java.awt.Color; 29import java.awt.Composite; 30import java.awt.Graphics2D; 31import java.awt.Rectangle; 32import java.awt.RenderingHints; 33import java.awt.Shape; 34import java.awt.geom.AffineTransform; 35import java.awt.image.BufferedImage; 36import java.util.List; 37 38 39/** 40 * Delegate implementing the native methods of android.graphics.Canvas 41 * 42 * Through the layoutlib_create tool, the original native methods of Canvas have been replaced 43 * by calls to methods of the same name in this delegate class. 44 * 45 * This class behaves like the original native implementation, but in Java, keeping previously 46 * native data into its own objects and mapping them to int that are sent back and forth between 47 * it and the original Canvas class. 48 * 49 * @see DelegateManager 50 * 51 */ 52public final class Canvas_Delegate { 53 54 // ---- delegate manager ---- 55 private static final DelegateManager<Canvas_Delegate> sManager = 56 new DelegateManager<Canvas_Delegate>(); 57 58 // ---- delegate helper data ---- 59 60 private final static boolean[] sBoolOut = new boolean[1]; 61 62 // ---- delegate data ---- 63 private Bitmap_Delegate mBitmap; 64 private GcSnapshot mSnapshot; 65 66 private int mDrawFilter = 0; 67 68 // ---- Public Helper methods ---- 69 70 /** 71 * Returns the native delegate associated to a given {@link Canvas} object. 72 */ 73 public static Canvas_Delegate getDelegate(Canvas canvas) { 74 return sManager.getDelegate(canvas.mNativeCanvas); 75 } 76 77 /** 78 * Returns the native delegate associated to a given an int referencing a {@link Canvas} object. 79 */ 80 public static Canvas_Delegate getDelegate(int native_canvas) { 81 return sManager.getDelegate(native_canvas); 82 } 83 84 /** 85 * Returns the current {@link Graphics2D} used to draw. 86 */ 87 public GcSnapshot getSnapshot() { 88 return mSnapshot; 89 } 90 91 /** 92 * Returns the {@link DrawFilter} delegate or null if none have been set. 93 * 94 * @return the delegate or null. 95 */ 96 public DrawFilter_Delegate getDrawFilter() { 97 return DrawFilter_Delegate.getDelegate(mDrawFilter); 98 } 99 100 // ---- native methods ---- 101 102 /*package*/ static boolean isOpaque(Canvas thisCanvas) { 103 // get the delegate from the native int. 104 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 105 if (canvasDelegate == null) { 106 return false; 107 } 108 109 return canvasDelegate.mBitmap.getConfig() == Config.RGB_565; 110 } 111 112 /*package*/ static int getWidth(Canvas thisCanvas) { 113 // get the delegate from the native int. 114 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 115 if (canvasDelegate == null) { 116 return 0; 117 } 118 119 return canvasDelegate.mBitmap.getImage().getWidth(); 120 } 121 122 /*package*/ static int getHeight(Canvas thisCanvas) { 123 // get the delegate from the native int. 124 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 125 if (canvasDelegate == null) { 126 return 0; 127 } 128 129 return canvasDelegate.mBitmap.getImage().getHeight(); 130 } 131 132 /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { 133 // get the delegate from the native int. 134 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 135 if (canvasDelegate == null) { 136 return; 137 } 138 139 canvasDelegate.getSnapshot().translate(dx, dy); 140 } 141 142 /*package*/ static void rotate(Canvas thisCanvas, float degrees) { 143 // get the delegate from the native int. 144 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 145 if (canvasDelegate == null) { 146 return; 147 } 148 149 canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees)); 150 } 151 152 /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { 153 // get the delegate from the native int. 154 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 155 if (canvasDelegate == null) { 156 return; 157 } 158 159 canvasDelegate.getSnapshot().scale(sx, sy); 160 } 161 162 /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { 163 // get the delegate from the native int. 164 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 165 if (canvasDelegate == null) { 166 return; 167 } 168 169 // get the current top graphics2D object. 170 GcSnapshot g = canvasDelegate.getSnapshot(); 171 172 // get its current matrix 173 AffineTransform currentTx = g.getTransform(); 174 // get the AffineTransform for the given skew. 175 float[] mtx = Matrix_Delegate.getSkew(kx, ky); 176 AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx); 177 178 // combine them so that the given matrix is applied after. 179 currentTx.preConcatenate(matrixTx); 180 181 // give it to the graphics2D as a new matrix replacing all previous transform 182 g.setTransform(currentTx); 183 } 184 185 /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) { 186 return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); 187 } 188 189 /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) { 190 return clipRect(thisCanvas, (float) rect.left, (float) rect.top, 191 (float) rect.right, (float) rect.bottom); 192 } 193 194 /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, 195 float bottom) { 196 // get the delegate from the native int. 197 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 198 if (canvasDelegate == null) { 199 return false; 200 } 201 202 return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt); 203 } 204 205 /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, 206 int bottom) { 207 208 return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom); 209 } 210 211 /*package*/ static int save(Canvas thisCanvas) { 212 return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); 213 } 214 215 /*package*/ static int save(Canvas thisCanvas, int saveFlags) { 216 // get the delegate from the native int. 217 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 218 if (canvasDelegate == null) { 219 return 0; 220 } 221 222 return canvasDelegate.save(saveFlags); 223 } 224 225 /*package*/ static void restore(Canvas thisCanvas) { 226 // get the delegate from the native int. 227 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 228 if (canvasDelegate == null) { 229 return; 230 } 231 232 canvasDelegate.restore(); 233 } 234 235 /*package*/ static int getSaveCount(Canvas thisCanvas) { 236 // get the delegate from the native int. 237 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 238 if (canvasDelegate == null) { 239 return 0; 240 } 241 242 return canvasDelegate.getSnapshot().size(); 243 } 244 245 /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { 246 // get the delegate from the native int. 247 Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); 248 if (canvasDelegate == null) { 249 return; 250 } 251 252 canvasDelegate.restoreTo(saveCount); 253 } 254 255 /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, 256 Paint paint) { 257 // FIXME 258 throw new UnsupportedOperationException(); 259 } 260 261 /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) { 262 // FIXME 263 throw new UnsupportedOperationException(); 264 } 265 266 /*package*/ static void drawLines(Canvas thisCanvas, 267 final float[] pts, final int offset, final int count, 268 Paint paint) { 269 draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/, 270 false /*forceSrcMode*/, new GcSnapshot.Drawable() { 271 public void draw(Graphics2D graphics, Paint_Delegate paint) { 272 for (int i = 0 ; i < count ; i += 4) { 273 graphics.drawLine((int)pts[i + offset], (int)pts[i + offset + 1], 274 (int)pts[i + offset + 2], (int)pts[i + offset + 3]); 275 } 276 } 277 }); 278 } 279 280 /*package*/ static void freeCaches() { 281 // nothing to be done here. 282 } 283 284 /*package*/ static int initRaster(int nativeBitmapOrZero) { 285 if (nativeBitmapOrZero > 0) { 286 // get the Bitmap from the int 287 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); 288 289 // create a new Canvas_Delegate with the given bitmap and return its new native int. 290 Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); 291 292 return sManager.addDelegate(newDelegate); 293 } else { 294 // create a new Canvas_Delegate and return its new native int. 295 Canvas_Delegate newDelegate = new Canvas_Delegate(); 296 297 return sManager.addDelegate(newDelegate); 298 } 299 } 300 301 /*package*/ static void native_setBitmap(int nativeCanvas, int bitmap) { 302 // get the delegate from the native int. 303 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 304 if (canvasDelegate == null) { 305 return; 306 } 307 308 // get the delegate from the native int. 309 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 310 if (bitmapDelegate == null) { 311 return; 312 } 313 314 canvasDelegate.setBitmap(bitmapDelegate); 315 } 316 317 /*package*/ static int native_saveLayer(int nativeCanvas, RectF bounds, 318 int paint, int layerFlags) { 319 // get the delegate from the native int. 320 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 321 if (canvasDelegate == null) { 322 return 0; 323 } 324 325 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 326 if (paintDelegate == null) { 327 return 0; 328 } 329 330 return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags); 331 } 332 333 /*package*/ static int native_saveLayer(int nativeCanvas, float l, 334 float t, float r, float b, 335 int paint, int layerFlags) { 336 // get the delegate from the native int. 337 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 338 if (canvasDelegate == null) { 339 return 0; 340 } 341 342 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 343 if (paintDelegate == null) { 344 return 0; 345 } 346 347 return canvasDelegate.saveLayer(new RectF(l, t, r, b), 348 paintDelegate, layerFlags); 349 } 350 351 /*package*/ static int native_saveLayerAlpha(int nativeCanvas, 352 RectF bounds, int alpha, 353 int layerFlags) { 354 // get the delegate from the native int. 355 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 356 if (canvasDelegate == null) { 357 return 0; 358 } 359 360 return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags); 361 } 362 363 /*package*/ static int native_saveLayerAlpha(int nativeCanvas, float l, 364 float t, float r, float b, 365 int alpha, int layerFlags) { 366 // get the delegate from the native int. 367 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 368 if (canvasDelegate == null) { 369 return 0; 370 } 371 372 return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags); 373 } 374 375 376 /*package*/ static void native_concat(int nCanvas, int nMatrix) { 377 // get the delegate from the native int. 378 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 379 if (canvasDelegate == null) { 380 return; 381 } 382 383 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); 384 if (matrixDelegate == null) { 385 return; 386 } 387 388 // get the current top graphics2D object. 389 GcSnapshot snapshot = canvasDelegate.getSnapshot(); 390 391 // get its current matrix 392 AffineTransform currentTx = snapshot.getTransform(); 393 // get the AffineTransform of the given matrix 394 AffineTransform matrixTx = matrixDelegate.getAffineTransform(); 395 396 // combine them so that the given matrix is applied after. 397 currentTx.preConcatenate(matrixTx); 398 399 // give it to the graphics2D as a new matrix replacing all previous transform 400 snapshot.setTransform(currentTx); 401 } 402 403 /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { 404 // get the delegate from the native int. 405 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 406 if (canvasDelegate == null) { 407 return; 408 } 409 410 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); 411 if (matrixDelegate == null) { 412 return; 413 } 414 415 // get the current top graphics2D object. 416 GcSnapshot snapshot = canvasDelegate.getSnapshot(); 417 418 // get the AffineTransform of the given matrix 419 AffineTransform matrixTx = matrixDelegate.getAffineTransform(); 420 421 // give it to the graphics2D as a new matrix replacing all previous transform 422 snapshot.setTransform(matrixTx); 423 424 if (matrixDelegate.hasPerspective()) { 425 assert false; 426 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE, 427 "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + 428 "supports affine transformations.", null); 429 } 430 } 431 432 /*package*/ static boolean native_clipRect(int nCanvas, 433 float left, float top, 434 float right, float bottom, 435 int regionOp) { 436 437 // get the delegate from the native int. 438 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 439 if (canvasDelegate == null) { 440 return false; 441 } 442 443 return canvasDelegate.clipRect(left, top, right, bottom, regionOp); 444 } 445 446 /*package*/ static boolean native_clipPath(int nativeCanvas, 447 int nativePath, 448 int regionOp) { 449 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 450 if (canvasDelegate == null) { 451 return true; 452 } 453 454 Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath); 455 if (pathDelegate == null) { 456 return true; 457 } 458 459 return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp); 460 } 461 462 /*package*/ static boolean native_clipRegion(int nativeCanvas, 463 int nativeRegion, 464 int regionOp) { 465 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 466 if (canvasDelegate == null) { 467 return true; 468 } 469 470 Region_Delegate region = Region_Delegate.getDelegate(nativeRegion); 471 if (region == null) { 472 return true; 473 } 474 475 return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp); 476 } 477 478 /*package*/ static void nativeSetDrawFilter(int nativeCanvas, 479 int nativeFilter) { 480 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 481 if (canvasDelegate == null) { 482 return; 483 } 484 485 canvasDelegate.mDrawFilter = nativeFilter; 486 487 // get the delegate only because we don't support them at all for the moment, so 488 // we can display the message now. 489 490 DrawFilter_Delegate filterDelegate = DrawFilter_Delegate.getDelegate(nativeFilter); 491 if (canvasDelegate == null) { 492 return; 493 } 494 495 if (filterDelegate.isSupported() == false) { 496 Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, 497 filterDelegate.getSupportMessage(), null); 498 } 499 } 500 501 /*package*/ static boolean native_getClipBounds(int nativeCanvas, 502 Rect bounds) { 503 // get the delegate from the native int. 504 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 505 if (canvasDelegate == null) { 506 return false; 507 } 508 509 Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); 510 if (rect != null && rect.isEmpty() == false) { 511 bounds.left = rect.x; 512 bounds.top = rect.y; 513 bounds.right = rect.x + rect.width; 514 bounds.bottom = rect.y + rect.height; 515 return true; 516 } 517 518 return false; 519 } 520 521 /*package*/ static void native_getCTM(int canvas, int matrix) { 522 // get the delegate from the native int. 523 Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas); 524 if (canvasDelegate == null) { 525 return; 526 } 527 528 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); 529 if (matrixDelegate == null) { 530 return; 531 } 532 533 AffineTransform transform = canvasDelegate.getSnapshot().getTransform(); 534 matrixDelegate.set(Matrix_Delegate.makeValues(transform)); 535 } 536 537 /*package*/ static boolean native_quickReject(int nativeCanvas, 538 RectF rect, 539 int native_edgeType) { 540 // FIXME properly implement quickReject 541 return false; 542 } 543 544 /*package*/ static boolean native_quickReject(int nativeCanvas, 545 int path, 546 int native_edgeType) { 547 // FIXME properly implement quickReject 548 return false; 549 } 550 551 /*package*/ static boolean native_quickReject(int nativeCanvas, 552 float left, float top, 553 float right, float bottom, 554 int native_edgeType) { 555 // FIXME properly implement quickReject 556 return false; 557 } 558 559 /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, int b) { 560 native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF), 561 PorterDuff.Mode.SRC_OVER.nativeInt); 562 563 } 564 565 /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, int g, int b) { 566 native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF), 567 PorterDuff.Mode.SRC_OVER.nativeInt); 568 } 569 570 /*package*/ static void native_drawColor(int nativeCanvas, int color) { 571 native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt); 572 } 573 574 /*package*/ static void native_drawColor(int nativeCanvas, final int color, final int mode) { 575 // get the delegate from the native int. 576 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 577 if (canvasDelegate == null) { 578 return; 579 } 580 581 final int w = canvasDelegate.mBitmap.getImage().getWidth(); 582 final int h = canvasDelegate.mBitmap.getImage().getHeight(); 583 draw(nativeCanvas, new GcSnapshot.Drawable() { 584 585 public void draw(Graphics2D graphics, Paint_Delegate paint) { 586 // reset its transform just in case 587 graphics.setTransform(new AffineTransform()); 588 589 // set the color 590 graphics.setColor(new Color(color, true /*alpha*/)); 591 592 Composite composite = PorterDuffXfermode_Delegate.getComposite( 593 PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF); 594 if (composite != null) { 595 graphics.setComposite(composite); 596 } 597 598 graphics.fillRect(0, 0, w, h); 599 } 600 }); 601 } 602 603 /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { 604 // FIXME 605 throw new UnsupportedOperationException(); 606 } 607 608 /*package*/ static void native_drawLine(int nativeCanvas, 609 final float startX, final float startY, final float stopX, final float stopY, 610 int paint) { 611 612 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 613 new GcSnapshot.Drawable() { 614 public void draw(Graphics2D graphics, Paint_Delegate paint) { 615 graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); 616 } 617 }); 618 } 619 620 /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, 621 int paint) { 622 native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint); 623 } 624 625 /*package*/ static void native_drawRect(int nativeCanvas, 626 final float left, final float top, final float right, final float bottom, int paint) { 627 628 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 629 new GcSnapshot.Drawable() { 630 public void draw(Graphics2D graphics, Paint_Delegate paint) { 631 int style = paint.getStyle(); 632 633 // draw 634 if (style == Paint.Style.FILL.nativeInt || 635 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 636 graphics.fillRect((int)left, (int)top, 637 (int)(right-left), (int)(bottom-top)); 638 } 639 640 if (style == Paint.Style.STROKE.nativeInt || 641 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 642 graphics.drawRect((int)left, (int)top, 643 (int)(right-left), (int)(bottom-top)); 644 } 645 } 646 }); 647 } 648 649 /*package*/ static void native_drawOval(int nativeCanvas, final RectF oval, int paint) { 650 if (oval.right > oval.left && oval.bottom > oval.top) { 651 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 652 new GcSnapshot.Drawable() { 653 public void draw(Graphics2D graphics, Paint_Delegate paint) { 654 int style = paint.getStyle(); 655 656 // draw 657 if (style == Paint.Style.FILL.nativeInt || 658 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 659 graphics.fillOval((int)oval.left, (int)oval.top, 660 (int)oval.width(), (int)oval.height()); 661 } 662 663 if (style == Paint.Style.STROKE.nativeInt || 664 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 665 graphics.drawOval((int)oval.left, (int)oval.top, 666 (int)oval.width(), (int)oval.height()); 667 } 668 } 669 }); 670 } 671 } 672 673 /*package*/ static void native_drawCircle(int nativeCanvas, float cx, 674 float cy, float radius, 675 int paint) { 676 native_drawOval(nativeCanvas, 677 new RectF(cx - radius, cy - radius, radius*2, radius*2), 678 paint); 679 } 680 681 /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, 682 float startAngle, float sweep, 683 boolean useCenter, int paint) { 684 // FIXME 685 throw new UnsupportedOperationException(); 686 } 687 688 /*package*/ static void native_drawRoundRect(int nativeCanvas, 689 final RectF rect, final float rx, final float ry, int paint) { 690 691 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 692 new GcSnapshot.Drawable() { 693 public void draw(Graphics2D graphics, Paint_Delegate paint) { 694 int style = paint.getStyle(); 695 696 // draw 697 if (style == Paint.Style.FILL.nativeInt || 698 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 699 graphics.fillRoundRect( 700 (int)rect.left, (int)rect.top, 701 (int)rect.width(), (int)rect.height(), 702 (int)rx, (int)ry); 703 } 704 705 if (style == Paint.Style.STROKE.nativeInt || 706 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 707 graphics.drawRoundRect( 708 (int)rect.left, (int)rect.top, 709 (int)rect.width(), (int)rect.height(), 710 (int)rx, (int)ry); 711 } 712 } 713 }); 714 } 715 716 /*package*/ static void native_drawPath(int nativeCanvas, int path, 717 int paint) { 718 final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path); 719 if (pathDelegate == null) { 720 return; 721 } 722 723 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 724 new GcSnapshot.Drawable() { 725 public void draw(Graphics2D graphics, Paint_Delegate paint) { 726 Shape shape = pathDelegate.getJavaShape(); 727 int style = paint.getStyle(); 728 729 if (style == Paint.Style.FILL.nativeInt || 730 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 731 graphics.fill(shape); 732 } 733 734 if (style == Paint.Style.STROKE.nativeInt || 735 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 736 graphics.draw(shape); 737 } 738 } 739 }); 740 } 741 742 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 743 float left, float top, 744 int nativePaintOrZero, 745 int canvasDensity, 746 int screenDensity, 747 int bitmapDensity) { 748 // get the delegate from the native int. 749 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 750 if (bitmapDelegate == null) { 751 return; 752 } 753 754 BufferedImage image = bitmapDelegate.getImage(); 755 float right = left + image.getWidth(); 756 float bottom = top + image.getHeight(); 757 758 drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 759 0, 0, image.getWidth(), image.getHeight(), 760 (int)left, (int)top, (int)right, (int)bottom); 761 } 762 763 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 764 Rect src, RectF dst, 765 int nativePaintOrZero, 766 int screenDensity, 767 int bitmapDensity) { 768 // get the delegate from the native int. 769 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 770 if (bitmapDelegate == null) { 771 return; 772 } 773 774 BufferedImage image = bitmapDelegate.getImage(); 775 776 if (src == null) { 777 drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 778 0, 0, image.getWidth(), image.getHeight(), 779 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 780 } else { 781 drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 782 src.left, src.top, src.width(), src.height(), 783 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 784 } 785 } 786 787 /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, 788 Rect src, Rect dst, 789 int nativePaintOrZero, 790 int screenDensity, 791 int bitmapDensity) { 792 // get the delegate from the native int. 793 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 794 if (bitmapDelegate == null) { 795 return; 796 } 797 798 BufferedImage image = bitmapDelegate.getImage(); 799 800 if (src == null) { 801 drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 802 0, 0, image.getWidth(), image.getHeight(), 803 dst.left, dst.top, dst.right, dst.bottom); 804 } else { 805 drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 806 src.left, src.top, src.width(), src.height(), 807 dst.left, dst.top, dst.right, dst.bottom); 808 } 809 } 810 811 /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, 812 int offset, int stride, final float x, 813 final float y, int width, int height, 814 boolean hasAlpha, 815 int nativePaintOrZero) { 816 817 // create a temp BufferedImage containing the content. 818 final BufferedImage image = new BufferedImage(width, height, 819 hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); 820 image.setRGB(0, 0, width, height, colors, offset, stride); 821 822 draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/, 823 new GcSnapshot.Drawable() { 824 public void draw(Graphics2D graphics, Paint_Delegate paint) { 825 if (paint != null && paint.isFilterBitmap()) { 826 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 827 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 828 } 829 830 graphics.drawImage(image, (int) x, (int) y, null); 831 } 832 }); 833 } 834 835 /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, 836 int nMatrix, int nPaint) { 837 // get the delegate from the native int. 838 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 839 if (canvasDelegate == null) { 840 return; 841 } 842 843 // get the delegate from the native int, which can be null 844 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); 845 846 // get the delegate from the native int. 847 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nBitmap); 848 if (bitmapDelegate == null) { 849 return; 850 } 851 852 final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut); 853 854 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); 855 if (matrixDelegate == null) { 856 return; 857 } 858 859 final AffineTransform mtx = matrixDelegate.getAffineTransform(); 860 861 canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() { 862 public void draw(Graphics2D graphics, Paint_Delegate paint) { 863 if (paint != null && paint.isFilterBitmap()) { 864 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 865 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 866 } 867 868 //FIXME add support for canvas, screen and bitmap densities. 869 graphics.drawImage(image, mtx, null); 870 } 871 }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/); 872 } 873 874 /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, 875 int meshWidth, int meshHeight, 876 float[] verts, int vertOffset, 877 int[] colors, int colorOffset, int nPaint) { 878 // FIXME 879 throw new UnsupportedOperationException(); 880 } 881 882 /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, 883 float[] verts, int vertOffset, float[] texs, int texOffset, 884 int[] colors, int colorOffset, short[] indices, 885 int indexOffset, int indexCount, int nPaint) { 886 // FIXME 887 throw new UnsupportedOperationException(); 888 } 889 890 /*package*/ static void native_drawText(int nativeCanvas, 891 final char[] text, final int index, final int count, 892 final float startX, final float startY, int flags, int paint) { 893 draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, 894 new GcSnapshot.Drawable() { 895 public void draw(Graphics2D graphics, Paint_Delegate paint) { 896 // WARNING: the logic in this method is similar to Paint.measureText. 897 // Any change to this method should be reflected in Paint.measureText 898 // Paint.TextAlign indicates how the text is positioned relative to X. 899 // LEFT is the default and there's nothing to do. 900 float x = startX; 901 float y = startY; 902 if (paint.getTextAlign() != Paint.Align.LEFT.nativeInt) { 903 float m = paint.measureText(text, index, count); 904 if (paint.getTextAlign() == Paint.Align.CENTER.nativeInt) { 905 x -= m / 2; 906 } else if (paint.getTextAlign() == Paint.Align.RIGHT.nativeInt) { 907 x -= m; 908 } 909 } 910 911 List<FontInfo> fonts = paint.getFonts(); 912 913 if (fonts.size() > 0) { 914 FontInfo mainFont = fonts.get(0); 915 int i = index; 916 int lastIndex = index + count; 917 while (i < lastIndex) { 918 // always start with the main font. 919 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); 920 if (upTo == -1) { 921 // draw all the rest and exit. 922 graphics.setFont(mainFont.mFont); 923 graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y); 924 return; 925 } else if (upTo > 0) { 926 // draw what's possible 927 graphics.setFont(mainFont.mFont); 928 graphics.drawChars(text, i, upTo - i, (int)x, (int)y); 929 930 // compute the width that was drawn to increase x 931 x += mainFont.mMetrics.charsWidth(text, i, upTo - i); 932 933 // move index to the first non displayed char. 934 i = upTo; 935 936 // don't call continue at this point. Since it is certain the main font 937 // cannot display the font a index upTo (now ==i), we move on to the 938 // fallback fonts directly. 939 } 940 941 // no char supported, attempt to read the next char(s) with the 942 // fallback font. In this case we only test the first character 943 // and then go back to test with the main font. 944 // Special test for 2-char characters. 945 boolean foundFont = false; 946 for (int f = 1 ; f < fonts.size() ; f++) { 947 FontInfo fontInfo = fonts.get(f); 948 949 // need to check that the font can display the character. We test 950 // differently if the char is a high surrogate. 951 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 952 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); 953 if (upTo == -1) { 954 // draw that char 955 graphics.setFont(fontInfo.mFont); 956 graphics.drawChars(text, i, charCount, (int)x, (int)y); 957 958 // update x 959 x += fontInfo.mMetrics.charsWidth(text, i, charCount); 960 961 // update the index in the text, and move on 962 i += charCount; 963 foundFont = true; 964 break; 965 966 } 967 } 968 969 // in case no font can display the char, display it with the main font. 970 // (it'll put a square probably) 971 if (foundFont == false) { 972 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 973 974 graphics.setFont(mainFont.mFont); 975 graphics.drawChars(text, i, charCount, (int)x, (int)y); 976 977 // measure it to advance x 978 x += mainFont.mMetrics.charsWidth(text, i, charCount); 979 980 // and move to the next chars. 981 i += charCount; 982 } 983 } 984 } 985 } 986 }); 987 } 988 989 /*package*/ static void native_drawText(int nativeCanvas, String text, 990 int start, int end, float x, 991 float y, int flags, int paint) { 992 int count = end - start; 993 char[] buffer = TemporaryBuffer.obtain(count); 994 TextUtils.getChars(text, start, end, buffer, 0); 995 996 native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); 997 } 998 999 /*package*/ static void native_drawTextRun(int nativeCanvas, String text, 1000 int start, int end, int contextStart, int contextEnd, 1001 float x, float y, int flags, int paint) { 1002 int count = end - start; 1003 char[] buffer = TemporaryBuffer.obtain(count); 1004 TextUtils.getChars(text, start, end, buffer, 0); 1005 1006 native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint); 1007 } 1008 1009 /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, 1010 int start, int count, int contextStart, int contextCount, 1011 float x, float y, int flags, int paint) { 1012 native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint); 1013 } 1014 1015 /*package*/ static void native_drawPosText(int nativeCanvas, 1016 char[] text, int index, 1017 int count, float[] pos, 1018 int paint) { 1019 // FIXME 1020 throw new UnsupportedOperationException(); 1021 } 1022 1023 /*package*/ static void native_drawPosText(int nativeCanvas, 1024 String text, float[] pos, 1025 int paint) { 1026 // FIXME 1027 throw new UnsupportedOperationException(); 1028 } 1029 1030 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 1031 char[] text, int index, 1032 int count, int path, 1033 float hOffset, 1034 float vOffset, int bidiFlags, 1035 int paint) { 1036 // FIXME 1037 throw new UnsupportedOperationException(); 1038 } 1039 1040 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 1041 String text, int path, 1042 float hOffset, 1043 float vOffset, 1044 int flags, int paint) { 1045 // FIXME 1046 throw new UnsupportedOperationException(); 1047 } 1048 1049 /*package*/ static void native_drawPicture(int nativeCanvas, 1050 int nativePicture) { 1051 // FIXME 1052 throw new UnsupportedOperationException(); 1053 } 1054 1055 /*package*/ static void finalizer(int nativeCanvas) { 1056 // get the delegate from the native int so that it can be disposed. 1057 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 1058 if (canvasDelegate == null) { 1059 return; 1060 } 1061 1062 canvasDelegate.dispose(); 1063 1064 // remove it from the manager. 1065 sManager.removeDelegate(nativeCanvas); 1066 } 1067 1068 // ---- Private delegate/helper methods ---- 1069 1070 /** 1071 * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint. 1072 * <p>Note that the drawable may actually be executed several times if there are 1073 * layers involved (see {@link #saveLayer(RectF, int, int)}. 1074 */ 1075 private static void draw(int nCanvas, int nPaint, boolean compositeOnly, boolean forceSrcMode, 1076 GcSnapshot.Drawable drawable) { 1077 // get the delegate from the native int. 1078 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 1079 if (canvasDelegate == null) { 1080 return; 1081 } 1082 1083 // get the paint which can be null if nPaint is 0; 1084 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); 1085 1086 canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode); 1087 } 1088 1089 /** 1090 * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided 1091 * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}. 1092 * <p>Note that the drawable may actually be executed several times if there are 1093 * layers involved (see {@link #saveLayer(RectF, int, int)}. 1094 */ 1095 private static void draw(int nCanvas, GcSnapshot.Drawable drawable) { 1096 // get the delegate from the native int. 1097 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 1098 if (canvasDelegate == null) { 1099 return; 1100 } 1101 1102 canvasDelegate.mSnapshot.draw(drawable); 1103 } 1104 1105 private Canvas_Delegate(Bitmap_Delegate bitmap) { 1106 mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap); 1107 } 1108 1109 private Canvas_Delegate() { 1110 mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/); 1111 } 1112 1113 /** 1114 * Disposes of the {@link Graphics2D} stack. 1115 */ 1116 private void dispose() { 1117 mSnapshot.dispose(); 1118 } 1119 1120 private int save(int saveFlags) { 1121 // get the current save count 1122 int count = mSnapshot.size(); 1123 1124 mSnapshot = mSnapshot.save(saveFlags); 1125 1126 // return the old save count 1127 return count; 1128 } 1129 1130 private int saveLayerAlpha(RectF rect, int alpha, int saveFlags) { 1131 Paint_Delegate paint = new Paint_Delegate(); 1132 paint.setAlpha(alpha); 1133 return saveLayer(rect, paint, saveFlags); 1134 } 1135 1136 private int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) { 1137 // get the current save count 1138 int count = mSnapshot.size(); 1139 1140 mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags); 1141 1142 // return the old save count 1143 return count; 1144 } 1145 1146 /** 1147 * Restores the {@link GcSnapshot} to <var>saveCount</var> 1148 * @param saveCount the saveCount 1149 */ 1150 private void restoreTo(int saveCount) { 1151 mSnapshot = mSnapshot.restoreTo(saveCount); 1152 } 1153 1154 /** 1155 * Restores the {@link GcSnapshot} to <var>saveCount</var> 1156 * @param saveCount the saveCount 1157 */ 1158 private void restore() { 1159 mSnapshot = mSnapshot.restore(); 1160 } 1161 1162 private boolean clipRect(float left, float top, float right, float bottom, int regionOp) { 1163 return mSnapshot.clipRect(left, top, right, bottom, regionOp); 1164 } 1165 1166 private void setBitmap(Bitmap_Delegate bitmap) { 1167 mBitmap = bitmap; 1168 assert mSnapshot.size() == 1; 1169 mSnapshot.setBitmap(mBitmap); 1170 } 1171 1172 private static void drawBitmap( 1173 int nativeCanvas, 1174 Bitmap_Delegate bitmap, 1175 int nativePaintOrZero, 1176 final int sleft, final int stop, final int sright, final int sbottom, 1177 final int dleft, final int dtop, final int dright, final int dbottom) { 1178 // get the delegate from the native int. 1179 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 1180 if (canvasDelegate == null) { 1181 return; 1182 } 1183 1184 // get the paint, which could be null if the int is 0 1185 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero); 1186 1187 final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut); 1188 1189 draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0], 1190 new GcSnapshot.Drawable() { 1191 public void draw(Graphics2D graphics, Paint_Delegate paint) { 1192 if (paint != null && paint.isFilterBitmap()) { 1193 graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1194 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1195 } 1196 1197 //FIXME add support for canvas, screen and bitmap densities. 1198 graphics.drawImage(image, dleft, dtop, dright, dbottom, 1199 sleft, stop, sright, sbottom, null); 1200 } 1201 }); 1202 } 1203 1204 1205 /** 1206 * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate. 1207 * The image returns, through a 1-size boolean array, whether the drawing code should 1208 * use a SRC composite no matter what the paint says. 1209 * 1210 * @param bitmap the bitmap 1211 * @param paint the paint that will be used to draw 1212 * @param forceSrcMode whether the composite will have to be SRC 1213 * @return the image to draw 1214 */ 1215 private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint, 1216 boolean[] forceSrcMode) { 1217 BufferedImage image = bitmap.getImage(); 1218 forceSrcMode[0] = false; 1219 1220 // if the bitmap config is alpha_8, then we erase all color value from it 1221 // before drawing it. 1222 if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) { 1223 fixAlpha8Bitmap(image); 1224 } else if (bitmap.hasAlpha() == false) { 1225 // hasAlpha is merely a rendering hint. There can in fact be alpha values 1226 // in the bitmap but it should be ignored at drawing time. 1227 // There is two ways to do this: 1228 // - override the composite to be SRC. This can only be used if the composite 1229 // was going to be SRC or SRC_OVER in the first place 1230 // - Create a different bitmap to draw in which all the alpha channel values is set 1231 // to 0xFF. 1232 if (paint != null) { 1233 Xfermode_Delegate xfermodeDelegate = paint.getXfermode(); 1234 if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) { 1235 PorterDuff.Mode mode = 1236 ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode(); 1237 1238 forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || 1239 mode == PorterDuff.Mode.SRC; 1240 } 1241 } 1242 1243 // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB 1244 if (forceSrcMode[0] == false) { 1245 image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF); 1246 } 1247 } 1248 1249 return image; 1250 } 1251 1252 private static void fixAlpha8Bitmap(final BufferedImage image) { 1253 int w = image.getWidth(); 1254 int h = image.getHeight(); 1255 int[] argb = new int[w * h]; 1256 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); 1257 1258 final int length = argb.length; 1259 for (int i = 0 ; i < length; i++) { 1260 argb[i] &= 0xFF000000; 1261 } 1262 image.setRGB(0, 0, w, h, argb, 0, w); 1263 } 1264} 1265 1266