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