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