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