Canvas_Delegate.java revision a313b65bd4d0cc38f2237003e770b28aad39d136
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 435 return false; 436 } 437 438 /*package*/ static boolean native_quickReject(int nativeCanvas, 439 int path, 440 int native_edgeType) { 441 // FIXME 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 450 return false; 451 } 452 453 /*package*/ static void native_drawRGB(int nativeCanvas, int r, int g, 454 int b) { 455 // FIXME 456 throw new UnsupportedOperationException(); 457 } 458 459 /*package*/ static void native_drawARGB(int nativeCanvas, int a, int r, 460 int g, int b) { 461 // get the delegate from the native int. 462 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 463 if (canvasDelegate == null) { 464 assert false; 465 return; 466 } 467 468 // get a new graphics context. 469 Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create(); 470 try { 471 // reset its transform just in case 472 graphics.setTransform(new AffineTransform()); 473 474 // set the color 475 graphics.setColor(new Color(r, g, b, a)); 476 477 graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(), 478 canvasDelegate.mBufferedImage.getHeight()); 479 } finally { 480 // dispose Graphics2D object 481 graphics.dispose(); 482 } 483 } 484 485 /*package*/ static void native_drawColor(int nativeCanvas, int color) { 486 // FIXME 487 throw new UnsupportedOperationException(); 488 } 489 490 /*package*/ static void native_drawColor(int nativeCanvas, int color, 491 int mode) { 492 // FIXME 493 throw new UnsupportedOperationException(); 494 } 495 496 /*package*/ static void native_drawPaint(int nativeCanvas, int paint) { 497 // FIXME 498 throw new UnsupportedOperationException(); 499 } 500 501 /*package*/ static void native_drawLine(int nativeCanvas, float startX, 502 float startY, float stopX, 503 float stopY, int paint) { 504 // FIXME 505 throw new UnsupportedOperationException(); 506 } 507 508 /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, 509 int paint) { 510 // FIXME 511 throw new UnsupportedOperationException(); 512 } 513 514 /*package*/ static void native_drawRect(int nativeCanvas, float left, 515 float top, float right, 516 float bottom, int paint) { 517 // get the delegate from the native int. 518 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 519 if (canvasDelegate == null) { 520 assert false; 521 return; 522 } 523 524 // get the delegate from the native int. 525 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 526 if (paintDelegate == null) { 527 assert false; 528 return; 529 } 530 531 if (right > left && bottom > top) { 532 // get a Graphics2D object configured with the drawing parameters. 533 Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); 534 535 int style = paintDelegate.getStyle(); 536 537 // draw 538 if (style == Paint.Style.FILL.nativeInt || 539 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 540 g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); 541 } 542 543 if (style == Paint.Style.STROKE.nativeInt || 544 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 545 g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); 546 } 547 548 // dispose Graphics2D object 549 g.dispose(); 550 } 551 552 } 553 554 /*package*/ static void native_drawOval(int nativeCanvas, RectF oval, 555 int paint) { 556 // FIXME 557 throw new UnsupportedOperationException(); 558 } 559 560 /*package*/ static void native_drawCircle(int nativeCanvas, float cx, 561 float cy, float radius, 562 int paint) { 563 // FIXME 564 throw new UnsupportedOperationException(); 565 } 566 567 /*package*/ static void native_drawArc(int nativeCanvas, RectF oval, 568 float startAngle, float sweep, 569 boolean useCenter, int paint) { 570 // FIXME 571 throw new UnsupportedOperationException(); 572 } 573 574 /*package*/ static void native_drawRoundRect(int nativeCanvas, 575 RectF rect, float rx, 576 float ry, int paint) { 577 // FIXME 578 throw new UnsupportedOperationException(); 579 } 580 581 /*package*/ static void native_drawPath(int nativeCanvas, int path, 582 int paint) { 583 // FIXME 584 throw new UnsupportedOperationException(); 585 } 586 587 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 588 float left, float top, 589 int nativePaintOrZero, 590 int canvasDensity, 591 int screenDensity, 592 int bitmapDensity) { 593 // FIXME 594 throw new UnsupportedOperationException(); 595 } 596 597 /*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap, 598 Rect src, RectF dst, 599 int nativePaintOrZero, 600 int screenDensity, 601 int bitmapDensity) { 602 // get the delegate from the native int. 603 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 604 if (bitmapDelegate == null) { 605 assert false; 606 return; 607 } 608 609 BufferedImage image = bitmapDelegate.getImage(); 610 611 if (src == null) { 612 drawBitmap(nativeCanvas, image, nativePaintOrZero, 613 0, 0, image.getWidth(), image.getHeight(), 614 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 615 } else { 616 drawBitmap(nativeCanvas, image, nativePaintOrZero, 617 src.left, src.top, src.width(), src.height(), 618 (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom); 619 } 620 } 621 622 /*package*/ static void native_drawBitmap(int nativeCanvas, int bitmap, 623 Rect src, Rect dst, 624 int nativePaintOrZero, 625 int screenDensity, 626 int bitmapDensity) { 627 // get the delegate from the native int. 628 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 629 if (bitmapDelegate == null) { 630 assert false; 631 return; 632 } 633 634 BufferedImage image = bitmapDelegate.getImage(); 635 636 if (src == null) { 637 drawBitmap(nativeCanvas, image, nativePaintOrZero, 638 0, 0, image.getWidth(), image.getHeight(), 639 dst.left, dst.top, dst.right, dst.bottom); 640 } else { 641 drawBitmap(nativeCanvas, image, nativePaintOrZero, 642 src.left, src.top, src.width(), src.height(), 643 dst.left, dst.top, dst.right, dst.bottom); 644 } 645 } 646 647 /*package*/ static void native_drawBitmap(int nativeCanvas, int[] colors, 648 int offset, int stride, float x, 649 float y, int width, int height, 650 boolean hasAlpha, 651 int nativePaintOrZero) { 652 // FIXME 653 throw new UnsupportedOperationException(); 654 } 655 656 /*package*/ static void nativeDrawBitmapMatrix(int nCanvas, int nBitmap, 657 int nMatrix, int nPaint) { 658 // FIXME 659 throw new UnsupportedOperationException(); 660 } 661 662 /*package*/ static void nativeDrawBitmapMesh(int nCanvas, int nBitmap, 663 int meshWidth, int meshHeight, 664 float[] verts, int vertOffset, 665 int[] colors, int colorOffset, int nPaint) { 666 // FIXME 667 throw new UnsupportedOperationException(); 668 } 669 670 /*package*/ static void nativeDrawVertices(int nCanvas, int mode, int n, 671 float[] verts, int vertOffset, float[] texs, int texOffset, 672 int[] colors, int colorOffset, short[] indices, 673 int indexOffset, int indexCount, int nPaint) { 674 // FIXME 675 throw new UnsupportedOperationException(); 676 } 677 678 679 /*package*/ static void native_drawText(int nativeCanvas, char[] text, 680 int index, int count, float x, 681 float y, int flags, int paint) { 682 // WARNING: the logic in this method is similar to Paint.measureText. 683 // Any change to this method should be reflected in Paint.measureText 684 685 // get the delegate from the native int. 686 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 687 if (canvasDelegate == null) { 688 assert false; 689 return; 690 } 691 692 // get the delegate from the native int. 693 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 694 if (paintDelegate == null) { 695 assert false; 696 return; 697 } 698 699 Graphics2D g = (Graphics2D) canvasDelegate.getGraphics2d().create(); 700 try { 701 if (paintDelegate.isAntiAliased()) { 702 g.setRenderingHint( 703 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 704 } 705 706 // set the color. because this only handles RGB, the alpha channel is handled 707 // as a composite. 708 g.setColor(new Color(paintDelegate.getColor())); 709 int alpha = paintDelegate.getAlpha(); 710 float falpha = alpha / 255.f; 711 712 setModeInGraphics(g, PorterDuff.Mode.SRC_OVER, falpha); 713 714 // Paint.TextAlign indicates how the text is positioned relative to X. 715 // LEFT is the default and there's nothing to do. 716 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) { 717 float m = paintDelegate.measureText(text, index, count); 718 if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) { 719 x -= m / 2; 720 } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) { 721 x -= m; 722 } 723 } 724 725 List<FontInfo> fonts = paintDelegate.getFonts(); 726 727 if (fonts.size() > 0) { 728 FontInfo mainFont = fonts.get(0); 729 int i = index; 730 int lastIndex = index + count; 731 while (i < lastIndex) { 732 // always start with the main font. 733 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); 734 if (upTo == -1) { 735 // draw all the rest and exit. 736 g.setFont(mainFont.mFont); 737 g.drawChars(text, i, lastIndex - i, (int)x, (int)y); 738 return; 739 } else if (upTo > 0) { 740 // draw what's possible 741 g.setFont(mainFont.mFont); 742 g.drawChars(text, i, upTo - i, (int)x, (int)y); 743 744 // compute the width that was drawn to increase x 745 x += mainFont.mMetrics.charsWidth(text, i, upTo - i); 746 747 // move index to the first non displayed char. 748 i = upTo; 749 750 // don't call continue at this point. Since it is certain the main font 751 // cannot display the font a index upTo (now ==i), we move on to the 752 // fallback fonts directly. 753 } 754 755 // no char supported, attempt to read the next char(s) with the 756 // fallback font. In this case we only test the first character 757 // and then go back to test with the main font. 758 // Special test for 2-char characters. 759 boolean foundFont = false; 760 for (int f = 1 ; f < fonts.size() ; f++) { 761 FontInfo fontInfo = fonts.get(f); 762 763 // need to check that the font can display the character. We test 764 // differently if the char is a high surrogate. 765 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 766 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); 767 if (upTo == -1) { 768 // draw that char 769 g.setFont(fontInfo.mFont); 770 g.drawChars(text, i, charCount, (int)x, (int)y); 771 772 // update x 773 x += fontInfo.mMetrics.charsWidth(text, i, charCount); 774 775 // update the index in the text, and move on 776 i += charCount; 777 foundFont = true; 778 break; 779 780 } 781 } 782 783 // in case no font can display the char, display it with the main font. 784 // (it'll put a square probably) 785 if (foundFont == false) { 786 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 787 788 g.setFont(mainFont.mFont); 789 g.drawChars(text, i, charCount, (int)x, (int)y); 790 791 // measure it to advance x 792 x += mainFont.mMetrics.charsWidth(text, i, charCount); 793 794 // and move to the next chars. 795 i += charCount; 796 } 797 } 798 } 799 } finally { 800 g.dispose(); 801 } 802 } 803 804 /*package*/ static void native_drawText(int nativeCanvas, String text, 805 int start, int end, float x, 806 float y, int flags, int paint) { 807 int count = end - start; 808 char[] buffer = TemporaryBuffer.obtain(count); 809 TextUtils.getChars(text, start, end, buffer, 0); 810 811 native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); 812 } 813 814 815 /*package*/ static void native_drawTextRun(int nativeCanvas, String text, 816 int start, int end, int contextStart, int contextEnd, 817 float x, float y, int flags, int paint) { 818 // FIXME 819 throw new UnsupportedOperationException(); 820 } 821 822 823 /*package*/ static void native_drawTextRun(int nativeCanvas, char[] text, 824 int start, int count, int contextStart, int contextCount, 825 float x, float y, int flags, int paint) { 826 // FIXME 827 throw new UnsupportedOperationException(); 828 } 829 830 831 /*package*/ static void native_drawPosText(int nativeCanvas, 832 char[] text, int index, 833 int count, float[] pos, 834 int paint) { 835 // FIXME 836 throw new UnsupportedOperationException(); 837 } 838 839 /*package*/ static void native_drawPosText(int nativeCanvas, 840 String text, float[] pos, 841 int paint) { 842 // FIXME 843 throw new UnsupportedOperationException(); 844 } 845 846 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 847 char[] text, int index, 848 int count, int path, 849 float hOffset, 850 float vOffset, int bidiFlags, 851 int paint) { 852 // FIXME 853 throw new UnsupportedOperationException(); 854 } 855 856 /*package*/ static void native_drawTextOnPath(int nativeCanvas, 857 String text, int path, 858 float hOffset, 859 float vOffset, 860 int flags, int paint) { 861 // FIXME 862 throw new UnsupportedOperationException(); 863 } 864 865 /*package*/ static void native_drawPicture(int nativeCanvas, 866 int nativePicture) { 867 // FIXME 868 throw new UnsupportedOperationException(); 869 } 870 871 /*package*/ static void finalizer(int nativeCanvas) { 872 sManager.removeDelegate(nativeCanvas); 873 } 874 875 // ---- Private delegate/helper methods ---- 876 877 private Canvas_Delegate(BufferedImage image) { 878 setBitmap(image); 879 } 880 881 private Canvas_Delegate() { 882 } 883 884 private void setBitmap(BufferedImage image) { 885 mBufferedImage = image; 886 mGraphicsStack.push(mBufferedImage.createGraphics()); 887 } 888 889 /** 890 * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. 891 * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. 892 */ 893 private Graphics2D getCustomGraphics(Paint_Delegate paint) { 894 // make new one 895 Graphics2D g = getGraphics2d(); 896 g = (Graphics2D)g.create(); 897 898 if (paint.isAntiAliased()) { 899 g.setRenderingHint( 900 RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 901 } 902 903 // configure it 904 g.setColor(new Color(paint.getColor())); 905 int alpha = paint.getAlpha(); 906 float falpha = alpha / 255.f; 907 908 int style = paint.getStyle(); 909 if (style == Paint.Style.STROKE.nativeInt || 910 style == Paint.Style.FILL_AND_STROKE.nativeInt) { 911 /* FIXME 912 PathEffect e = paint.getPathEffect(); 913 if (e instanceof DashPathEffect) { 914 DashPathEffect dpe = (DashPathEffect)e; 915 g.setStroke(new BasicStroke( 916 paint.getStrokeWidth(), 917 paint.getStrokeCap().getJavaCap(), 918 paint.getStrokeJoin().getJavaJoin(), 919 paint.getStrokeMiter(), 920 dpe.getIntervals(), 921 dpe.getPhase())); 922 } else {*/ 923 g.setStroke(new BasicStroke( 924 paint.getStrokeWidth(), 925 paint.getJavaCap(), 926 paint.getJavaJoin(), 927 paint.getStrokeMiter())); 928 /* }*/ 929 } 930/* 931 Xfermode xfermode = paint.getXfermode(); 932 if (xfermode instanceof PorterDuffXfermode) { 933 PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode(); 934 935 setModeInGraphics(mode, g, falpha); 936 } else { 937 if (mLogger != null && xfermode != null) { 938 mLogger.warning(String.format( 939 "Xfermode '%1$s' is not supported in the Layout Editor.", 940 xfermode.getClass().getCanonicalName())); 941 } 942 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); 943 } 944 945 Shader shader = paint.getShader(); 946 if (shader != null) { 947 java.awt.Paint shaderPaint = shader.getJavaPaint(); 948 if (shaderPaint != null) { 949 g.setPaint(shaderPaint); 950 } else { 951 if (mLogger != null) { 952 mLogger.warning(String.format( 953 "Shader '%1$s' is not supported in the Layout Editor.", 954 shader.getClass().getCanonicalName())); 955 } 956 } 957 } 958*/ 959 return g; 960 } 961 962 private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode, float falpha) { 963 switch (mode) { 964 case CLEAR: 965 g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha)); 966 break; 967 case DARKEN: 968 break; 969 case DST: 970 g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, falpha)); 971 break; 972 case DST_ATOP: 973 g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha)); 974 break; 975 case DST_IN: 976 g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha)); 977 break; 978 case DST_OUT: 979 g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha)); 980 break; 981 case DST_OVER: 982 g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha)); 983 break; 984 case LIGHTEN: 985 break; 986 case MULTIPLY: 987 break; 988 case SCREEN: 989 break; 990 case SRC: 991 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, falpha)); 992 break; 993 case SRC_ATOP: 994 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha)); 995 break; 996 case SRC_IN: 997 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha)); 998 break; 999 case SRC_OUT: 1000 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha)); 1001 break; 1002 case SRC_OVER: 1003 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); 1004 break; 1005 case XOR: 1006 g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, falpha)); 1007 break; 1008 } 1009 } 1010 1011 1012 private static void drawBitmap( 1013 int nativeCanvas, 1014 BufferedImage image, 1015 int nativePaintOrZero, 1016 int sleft, int stop, int sright, int sbottom, 1017 int dleft, int dtop, int dright, int dbottom) { 1018 // get the delegate from the native int. 1019 Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); 1020 if (canvasDelegate == null) { 1021 assert false; 1022 return; 1023 } 1024 1025 // get the delegate from the native int. 1026 Paint_Delegate paintDelegate = null; 1027 if (nativePaintOrZero > 0) { 1028 paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero); 1029 if (paintDelegate == null) { 1030 assert false; 1031 return; 1032 } 1033 } 1034 1035 drawBitmap(canvasDelegate, image, paintDelegate, 1036 sleft, stop, sright, sbottom, 1037 dleft, dtop, dright, dbottom); 1038 } 1039 1040 private static void drawBitmap( 1041 Canvas_Delegate canvasDelegate, 1042 BufferedImage image, 1043 Paint_Delegate paintDelegate, 1044 int sleft, int stop, int sright, int sbottom, 1045 int dleft, int dtop, int dright, int dbottom) { 1046 1047 Graphics2D g = canvasDelegate.getGraphics2d(); 1048 1049 Composite c = null; 1050 1051 if (paintDelegate != null) { 1052 if (paintDelegate.isFilterBitmap()) { 1053 g = (Graphics2D)g.create(); 1054 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 1055 RenderingHints.VALUE_INTERPOLATION_BILINEAR); 1056 } 1057 1058 if (paintDelegate.getAlpha() != 0xFF) { 1059 c = g.getComposite(); 1060 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1061 paintDelegate.getAlpha()/255.f)); 1062 } 1063 } 1064 1065 g.drawImage(image, dleft, dtop, dright, dbottom, 1066 sleft, stop, sright, sbottom, null); 1067 1068 if (paintDelegate != null) { 1069 if (paintDelegate.isFilterBitmap()) { 1070 g.dispose(); 1071 } 1072 if (c != null) { 1073 g.setComposite(c); 1074 } 1075 } 1076 } 1077 1078} 1079 1080