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