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