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