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