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