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