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