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