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