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