Canvas_Delegate.java revision 66225224938402d96919bf3ddfb0a296eed91598
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( 532 PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF); 533 if (composite != null) { 534 graphics.setComposite(composite); 535 } 536 537 graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(), 538 canvasDelegate.mBufferedImage.getHeight()); 539 } finally { 540 // dispose Graphics2D object 541 graphics.dispose(); 542 } 543 } 544 545 /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { 546 // FIXME 547 throw new UnsupportedOperationException(); 548 } 549 550 /*package*/ static void native_drawLine(int nativeCanvas, float startX, 551 float startY, float stopX, 552 float stopY, int paint) { 553 // get the delegate from the native int. 554 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 555 if (canvasDelegate == null) { 556 assert false; 557 return; 558 } 559 560 // get the delegate from the native int. 561 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 562 if (paintDelegate == null) { 563 assert false; 564 return; 565 } 566 567 // get a Graphics2D object configured with the drawing parameters. 568 Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); 569 570 try { 571 g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); 572 } finally { 573 // dispose Graphics2D object 574 g.dispose(); 575 } 576 } 577 578 /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, 579 int paint) { 580 native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint); 581 } 582 583 /*package*/ static void native_drawRect(int nativeCanvas, float left, 584 float top, float right, 585 float bottom, int paint) { 586 // get the delegate from the native int. 587 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 588 if (canvasDelegate == null) { 589 assert false; 590 return; 591 } 592 593 // get the delegate from the native int. 594 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 595 if (paintDelegate == null) { 596 assert false; 597 return; 598 } 599 600 if (right > left && bottom > top) { 601 // get a Graphics2D object configured with the drawing parameters. 602 Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); 603 604 try { 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 } finally { 618 // dispose Graphics2D object 619 g.dispose(); 620 } 621 } 622 } 623 624 /*package*/ static void native_drawOval(int nativeCanvas, RectF oval, 625 int paint) { 626 // get the delegate from the native int. 627 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 628 if (canvasDelegate == null) { 629 assert false; 630 return; 631 } 632 633 // get the delegate from the native int. 634 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 635 if (paintDelegate == null) { 636 assert false; 637 return; 638 } 639 640 if (oval.right > oval.left && oval.bottom > oval.top) { 641 // get a Graphics2D object configured with the drawing parameters. 642 Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); 643 644 int style = paintDelegate.getStyle(); 645 646 // draw 647 if (style == Paint.Style.FILL.nativeInt || 648 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 649 g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); 650 } 651 652 if (style == Paint.Style.STROKE.nativeInt || 653 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 654 g.drawOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); 655 } 656 657 // dispose Graphics2D object 658 g.dispose(); 659 } 660 } 661 662 /*package*/ static void native_drawCircle(int nativeCanvas, float cx, 663 float cy, float radius, 664 int paint) { 665 native_drawOval(nativeCanvas, 666 new RectF(cx - radius, cy - radius, radius*2, radius*2), 667 paint); 668 } 669 670 /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, 671 float startAngle, float sweep, 672 boolean useCenter, int paint) { 673 // FIXME 674 throw new UnsupportedOperationException(); 675 } 676 677 /*package*/ static void native_drawRoundRect(int nativeCanvas, 678 RectF rect, float rx, 679 float ry, int paint) { 680 // get the delegate from the native int. 681 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 682 if (canvasDelegate == null) { 683 assert false; 684 return; 685 } 686 687 // get the delegate from the native int. 688 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 689 if (paintDelegate == null) { 690 assert false; 691 return; 692 } 693 694 if (rect.right > rect.left && rect.bottom > rect.top) { 695 // get a Graphics2D object configured with the drawing parameters. 696 Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); 697 698 int style = paintDelegate.getStyle(); 699 700 // draw 701 if (style == Paint.Style.FILL.nativeInt || 702 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 703 g.fillRoundRect( 704 (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), 705 (int)rx, (int)ry); 706 } 707 708 if (style == Paint.Style.STROKE.nativeInt || 709 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 710 g.drawRoundRect( 711 (int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), 712 (int)rx, (int)ry); 713 } 714 715 // dispose Graphics2D object 716 g.dispose(); 717 } 718 } 719 720 /*package*/ static void native_drawPath(int nativeCanvas, int path, 721 int paint) { 722 final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path); 723 if (pathDelegate == null) { 724 assert false; 725 return; 726 } 727 728 draw(nativeCanvas, paint, new Drawable() { 729 public void draw(Graphics2D graphics, Paint_Delegate paint) { 730 Shape shape = pathDelegate.getJavaShape(); 731 int style = paint.getStyle(); 732 733 if (style == Paint.Style.FILL.nativeInt || 734 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 735 graphics.fill(shape); 736 } 737 738 if (style == Paint.Style.STROKE.nativeInt || 739 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 740 graphics.draw(shape); 741 } 742 } 743 }); 744 } 745 746 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 747 float left, float top, 748 int nativePaintOrZero, 749 int canvasDensity, 750 int screenDensity, 751 int bitmapDensity) { 752 // get the delegate from the native int. 753 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 754 if (bitmapDelegate == null) { 755 assert false; 756 return; 757 } 758 759 BufferedImage image = bitmapDelegate.getImage(); 760 float right = left + image.getWidth(); 761 float bottom = top + image.getHeight(); 762 763 drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, 764 0, 0, image.getWidth(), image.getHeight(), 765 (int)left, (int)top, (int)right, (int)bottom); 766 } 767 768 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 769 Rect src, RectF dst, 770 int nativePaintOrZero, 771 int screenDensity, 772 int bitmapDensity) { 773 // get the delegate from the native int. 774 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 775 if (bitmapDelegate == null) { 776 assert false; 777 return; 778 } 779 780 BufferedImage image = bitmapDelegate.getImage(); 781 782 if (src == null) { 783 drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, 784 0, 0, image.getWidth(), image.getHeight(), 785 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 786 } else { 787 drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, 788 src.left, src.top, src.width(), src.height(), 789 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 790 } 791 } 792 793 /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, 794 Rect src, Rect dst, 795 int nativePaintOrZero, 796 int screenDensity, 797 int bitmapDensity) { 798 // get the delegate from the native int. 799 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 800 if (bitmapDelegate == null) { 801 assert false; 802 return; 803 } 804 805 BufferedImage image = bitmapDelegate.getImage(); 806 807 if (src == null) { 808 drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, 809 0, 0, image.getWidth(), image.getHeight(), 810 dst.left, dst.top, dst.right, dst.bottom); 811 } else { 812 drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero, 813 src.left, src.top, src.width(), src.height(), 814 dst.left, dst.top, dst.right, dst.bottom); 815 } 816 } 817 818 /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, 819 int offset, int stride, float x, 820 float y, int width, int height, 821 boolean hasAlpha, 822 int nativePaintOrZero) { 823 // FIXME 824 throw new UnsupportedOperationException(); 825 } 826 827 /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, 828 int nMatrix, int nPaint) { 829 // FIXME 830 throw new UnsupportedOperationException(); 831 } 832 833 /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, 834 int meshWidth, int meshHeight, 835 float[] verts, int vertOffset, 836 int[] colors, int colorOffset, int nPaint) { 837 // FIXME 838 throw new UnsupportedOperationException(); 839 } 840 841 /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, 842 float[] verts, int vertOffset, float[] texs, int texOffset, 843 int[] colors, int colorOffset, short[] indices, 844 int indexOffset, int indexCount, int nPaint) { 845 // FIXME 846 throw new UnsupportedOperationException(); 847 } 848 849 /*package*/ static void native_drawText(int nativeCanvas, char[] text, 850 int index, int count, float x, 851 float y, int flags, int paint) { 852 // WARNING: the logic in this method is similar to Paint.measureText. 853 // Any change to this method should be reflected in Paint.measureText 854 855 // get the delegate from the native int. 856 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 857 if (canvasDelegate == null) { 858 assert false; 859 return; 860 } 861 862 // get the delegate from the native int. 863 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 864 if (paintDelegate == null) { 865 assert false; 866 return; 867 } 868 869 Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate); 870 try { 871 // Paint.TextAlign indicates how the text is positioned relative to X. 872 // LEFT is the default and there's nothing to do. 873 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { 874 float m = paintDelegate.measureText(text, index, count); 875 if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { 876 x -= m / 2; 877 } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { 878 x -= m; 879 } 880 } 881 882 List<FontInfo> fonts = paintDelegate.getFonts(); 883 884 if (fonts.size() > 0) { 885 FontInfo mainFont = fonts.get(0); 886 int i = index; 887 int lastIndex = index + count; 888 while (i < lastIndex) { 889 // always start with the main font. 890 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); 891 if (upTo == -1) { 892 // draw all the rest and exit. 893 g.setFont(mainFont.mFont); 894 g.drawChars(text, i, lastIndex - i, (int)x, (int)y); 895 return; 896 } else if (upTo > 0) { 897 // draw what's possible 898 g.setFont(mainFont.mFont); 899 g.drawChars(text, i, upTo - i, (int)x, (int)y); 900 901 // compute the width that was drawn to increase x 902 x += mainFont.mMetrics.charsWidth(text, i, upTo - i); 903 904 // move index to the first non displayed char. 905 i = upTo; 906 907 // don't call continue at this point. Since it is certain the main font 908 // cannot display the font a index upTo (now ==i), we move on to the 909 // fallback fonts directly. 910 } 911 912 // no char supported, attempt to read the next char(s) with the 913 // fallback font. In this case we only test the first character 914 // and then go back to test with the main font. 915 // Special test for 2-char characters. 916 boolean foundFont = false; 917 for (int f = 1 ; f < fonts.size() ; f++) { 918 FontInfo fontInfo = fonts.get(f); 919 920 // need to check that the font can display the character. We test 921 // differently if the char is a high surrogate. 922 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 923 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); 924 if (upTo == -1) { 925 // draw that char 926 g.setFont(fontInfo.mFont); 927 g.drawChars(text, i, charCount, (int)x, (int)y); 928 929 // update x 930 x += fontInfo.mMetrics.charsWidth(text, i, charCount); 931 932 // update the index in the text, and move on 933 i += charCount; 934 foundFont = true; 935 break; 936 937 } 938 } 939 940 // in case no font can display the char, display it with the main font. 941 // (it'll put a square probably) 942 if (foundFont == false) { 943 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 944 945 g.setFont(mainFont.mFont); 946 g.drawChars(text, i, charCount, (int)x, (int)y); 947 948 // measure it to advance x 949 x += mainFont.mMetrics.charsWidth(text, i, charCount); 950 951 // and move to the next chars. 952 i += charCount; 953 } 954 } 955 } 956 } finally { 957 g.dispose(); 958 } 959 } 960 961 /*package*/ static void native_drawText(int nativeCanvas, String text, 962 int start, int end, float x, 963 float y, int flags, int paint) { 964 int count = end - start; 965 char[] buffer = TemporaryBuffer.obtain(count); 966 TextUtils.getChars(text, start, end, buffer, 0); 967 968 native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); 969 } 970 971 /*package*/ static void native_drawTextRun(int nativeCanvas, String text, 972 int start, int end, int contextStart, int contextEnd, 973 float x, float y, int flags, int paint) { 974 int count = end - start; 975 char[] buffer = TemporaryBuffer.obtain(count); 976 TextUtils.getChars(text, start, end, buffer, 0); 977 978 native_drawText(nativeCanvas, buffer, start, end, x, y, flags, paint); 979 } 980 981 /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, 982 int start, int count, int contextStart, int contextCount, 983 float x, float y, int flags, int paint) { 984 native_drawText(nativeCanvas, text, 0, count, x, y, flags, paint); 985 } 986 987 /*package*/ static void native_drawPosText(int nativeCanvas, 988 char[] text, int index, 989 int count, float[] pos, 990 int paint) { 991 // FIXME 992 throw new UnsupportedOperationException(); 993 } 994 995 /*package*/ static void native_drawPosText(int nativeCanvas, 996 String text, float[] pos, 997 int paint) { 998 // FIXME 999 throw new UnsupportedOperationException(); 1000 } 1001 1002 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 1003 char[] text, int index, 1004 int count, int path, 1005 float hOffset, 1006 float vOffset, int bidiFlags, 1007 int paint) { 1008 // FIXME 1009 throw new UnsupportedOperationException(); 1010 } 1011 1012 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 1013 String text, int path, 1014 float hOffset, 1015 float vOffset, 1016 int flags, int paint) { 1017 // FIXME 1018 throw new UnsupportedOperationException(); 1019 } 1020 1021 /*package*/ static void native_drawPicture(int nativeCanvas, 1022 int nativePicture) { 1023 // FIXME 1024 throw new UnsupportedOperationException(); 1025 } 1026 1027 /*package*/ static void finalizer(int nativeCanvas) { 1028 // get the delegate from the native int so that it can be disposed. 1029 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 1030 if (canvasDelegate == null) { 1031 assert false; 1032 return; 1033 } 1034 1035 canvasDelegate.dispose(); 1036 1037 // remove it from the manager. 1038 sManager.removeDelegate(nativeCanvas); 1039 } 1040 1041 // ---- Private delegate/helper methods ---- 1042 1043 /** 1044 * Executes a {@link Drawable} with a given canvas and paint. 1045 */ 1046 private static void draw(int nCanvas, int nPaint, Drawable drawable) { 1047 // get the delegate from the native int. 1048 Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas); 1049 if (canvasDelegate == null) { 1050 assert false; 1051 return; 1052 } 1053 1054 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint); 1055 if (paintDelegate == null) { 1056 assert false; 1057 return; 1058 } 1059 1060 // get a Graphics2D object configured with the drawing parameters. 1061 Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); 1062 1063 try { 1064 drawable.draw(g, paintDelegate); 1065 } finally { 1066 // dispose Graphics2D object 1067 g.dispose(); 1068 } 1069 } 1070 1071 private Canvas_Delegate(BufferedImage image) { 1072 setBitmap(image); 1073 } 1074 1075 private Canvas_Delegate() { 1076 } 1077 1078 /** 1079 * Disposes of the {@link Graphics2D} stack. 1080 */ 1081 private void dispose() { 1082 mSnapshot.dispose(); 1083 } 1084 1085 private int save(int saveFlags) { 1086 // get the current save count 1087 int count = mSnapshot.size(); 1088 1089 // create a new snapshot and add it to the stack 1090 mSnapshot = new GcSnapshot(mSnapshot, saveFlags); 1091 1092 // return the old save count 1093 return count; 1094 } 1095 1096 /** 1097 * Restores the {@link GcSnapshot} to <var>saveCount</var> 1098 * @param saveCount the saveCount 1099 */ 1100 private void restoreTo(int saveCount) { 1101 mSnapshot = mSnapshot.restoreTo(saveCount); 1102 } 1103 1104 /** 1105 * Restores the {@link GcSnapshot} to <var>saveCount</var> 1106 * @param saveCount the saveCount 1107 */ 1108 private void restore() { 1109 mSnapshot = mSnapshot.restore(); 1110 } 1111 1112 private boolean clipRect(float left, float top, float right, float bottom, int regionOp) { 1113 return mSnapshot.clipRect(left, top, right, bottom, regionOp); 1114 } 1115 1116 private void setBitmap(BufferedImage image) { 1117 mBufferedImage = image; 1118 assert mSnapshot.size() == 1; 1119 mSnapshot.setGraphics2D(mBufferedImage.createGraphics()); 1120 } 1121 1122 /** 1123 * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. 1124 * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. 1125 */ 1126 /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) { 1127 // make new one 1128 Graphics2D g = getGcSnapshot().create(); 1129 1130 // configure it 1131 1132 if (paint.isAntiAliased()) { 1133 g.setRenderingHint( 1134 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 1135 g.setRenderingHint( 1136 RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 1137 } 1138 1139 // get the shader first, as it'll replace the color if it can be used it. 1140 boolean customShader = false; 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 customShader = true; 1152 } 1153 } else { 1154 Bridge.getLog().fidelityWarning(null, 1155 shaderDelegate.getSupportMessage(), 1156 null); 1157 } 1158 } 1159 } 1160 1161 // if no shader, use the paint color 1162 if (customShader == false) { 1163 g.setColor(new Color(paint.getColor(), true /*hasAlpha*/)); 1164 } 1165 1166 boolean customStroke = false; 1167 int pathEffect = paint.getPathEffect(); 1168 if (pathEffect > 0) { 1169 PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect); 1170 assert effectDelegate != null; 1171 if (effectDelegate != null) { 1172 if (effectDelegate.isSupported()) { 1173 Stroke stroke = effectDelegate.getStroke(paint); 1174 assert stroke != null; 1175 if (stroke != null) { 1176 g.setStroke(stroke); 1177 customStroke = true; 1178 } 1179 } else { 1180 Bridge.getLog().fidelityWarning(null, 1181 effectDelegate.getSupportMessage(), 1182 null); 1183 } 1184 } 1185 } 1186 1187 // if no custom stroke as been set, set the default one. 1188 if (customStroke == false) { 1189 g.setStroke(new BasicStroke( 1190 paint.getStrokeWidth(), 1191 paint.getJavaCap(), 1192 paint.getJavaJoin(), 1193 paint.getJavaStrokeMiter())); 1194 } 1195 1196 // the alpha for the composite. Always opaque if the normal paint color is used since 1197 // it contains the alpha 1198 int alpha = customShader ? paint.getAlpha() : 0xFF; 1199 1200 boolean customXfermode = false; 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(alpha); 1208 assert composite != null; 1209 if (composite != null) { 1210 g.setComposite(composite); 1211 customXfermode = true; 1212 } 1213 } else { 1214 Bridge.getLog().fidelityWarning(null, 1215 xfermodeDelegate.getSupportMessage(), 1216 null); 1217 } 1218 } 1219 } 1220 1221 // if there was no custom xfermode, but we have alpha (due to a shader and a non 1222 // opaque alpha channel in the paint color), then we create an AlphaComposite anyway 1223 // that will handle the alpha. 1224 if (customXfermode == false && alpha != 0xFF) { 1225 g.setComposite(PorterDuffXfermode_Delegate.getComposite( 1226 PorterDuff.Mode.SRC_OVER, alpha)); 1227 } 1228 1229 return g; 1230 } 1231 1232 private static void drawBitmap( 1233 int nativeCanvas, 1234 BufferedImage image, 1235 Bitmap.Config mBitmapConfig, 1236 int nativePaintOrZero, 1237 int sleft, int stop, int sright, int sbottom, 1238 int dleft, int dtop, int dright, int dbottom) { 1239 // get the delegate from the native int. 1240 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 1241 if (canvasDelegate == null) { 1242 assert false; 1243 return; 1244 } 1245 1246 // get the delegate from the native int. 1247 Paint_Delegate paintDelegate = null; 1248 if (nativePaintOrZero > 0) { 1249 paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero); 1250 if (paintDelegate == null) { 1251 assert false; 1252 return; 1253 } 1254 } 1255 1256 drawBitmap(canvasDelegate, image, mBitmapConfig, paintDelegate, 1257 sleft, stop, sright, sbottom, 1258 dleft, dtop, dright, dbottom); 1259 } 1260 1261 private static void drawBitmap( 1262 Canvas_Delegate canvasDelegate, 1263 BufferedImage image, 1264 Bitmap.Config mBitmapConfig, 1265 Paint_Delegate paintDelegate, 1266 int sleft, int stop, int sright, int sbottom, 1267 int dleft, int dtop, int dright, int dbottom) { 1268 //FIXME add support for canvas, screen and bitmap densities. 1269 1270 Graphics2D g = canvasDelegate.getGcSnapshot().create(); 1271 try { 1272 if (paintDelegate != null && paintDelegate.isFilterBitmap()) { 1273 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1274 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1275 } 1276 1277 // if the bitmap config is alpha_8, then we erase all color value from it 1278 // before drawing it. 1279 if (mBitmapConfig == Bitmap.Config.ALPHA_8) { 1280 int w = image.getWidth(); 1281 int h = image.getHeight(); 1282 int[] argb = new int[w*h]; 1283 image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth()); 1284 1285 final int length = argb.length; 1286 for (int i = 0 ; i < length; i++) { 1287 argb[i] &= 0xFF000000; 1288 } 1289 image.setRGB(0, 0, w, h, argb, 0, w); 1290 } 1291 1292 g.drawImage(image, dleft, dtop, dright, dbottom, 1293 sleft, stop, sright, sbottom, null); 1294 } finally { 1295 g.dispose(); 1296 } 1297 } 1298} 1299 1300