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