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