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