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