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