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