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