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