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