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