Canvas_Delegate.java revision 989f4cb6da0c1d20f74a278878bc173c690d0848
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.ide.common.rendering.api.LayoutLog; 20import com.android.layoutlib.bridge.Bridge; 21import com.android.layoutlib.bridge.impl.DelegateManager; 22import com.android.layoutlib.bridge.impl.GcSnapshot; 23import com.android.layoutlib.bridge.impl.PorterDuffUtility; 24import com.android.ninepatch.NinePatchChunk; 25import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 26 27import android.annotation.Nullable; 28import android.graphics.Bitmap.Config; 29import android.text.TextUtils; 30 31import java.awt.Color; 32import java.awt.Composite; 33import java.awt.Graphics2D; 34import java.awt.Rectangle; 35import java.awt.RenderingHints; 36import java.awt.Shape; 37import java.awt.geom.AffineTransform; 38import java.awt.geom.Arc2D; 39import java.awt.geom.Rectangle2D; 40import java.awt.image.BufferedImage; 41 42import libcore.util.NativeAllocationRegistry_Delegate; 43 44 45/** 46 * Delegate implementing the native methods of android.graphics.Canvas 47 * 48 * Through the layoutlib_create tool, the original native methods of Canvas have been replaced 49 * by calls to methods of the same name in this delegate class. 50 * 51 * This class behaves like the original native implementation, but in Java, keeping previously 52 * native data into its own objects and mapping them to int that are sent back and forth between 53 * it and the original Canvas class. 54 * 55 * @see DelegateManager 56 * 57 */ 58public final class Canvas_Delegate extends BaseCanvas_Delegate { 59 60 // ---- delegate manager ---- 61 private static long sFinalizer = -1; 62 63 private DrawFilter_Delegate mDrawFilter = null; 64 65 // ---- Public Helper methods ---- 66 67 /** 68 * Returns the native delegate associated to a given {@link Canvas} object. 69 */ 70 public static Canvas_Delegate getDelegate(Canvas canvas) { 71 return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper()); 72 } 73 74 /** 75 * Returns the native delegate associated to a given an int referencing a {@link Canvas} object. 76 */ 77 public static Canvas_Delegate getDelegate(long native_canvas) { 78 return (Canvas_Delegate) sManager.getDelegate(native_canvas); 79 } 80 81 /** 82 * Returns the current {@link Graphics2D} used to draw. 83 */ 84 public GcSnapshot getSnapshot() { 85 return mSnapshot; 86 } 87 88 /** 89 * Returns the {@link DrawFilter} delegate or null if none have been set. 90 * 91 * @return the delegate or null. 92 */ 93 public DrawFilter_Delegate getDrawFilter() { 94 return mDrawFilter; 95 } 96 97 // ---- native methods ---- 98 99 @LayoutlibDelegate 100 /*package*/ static void nFreeCaches() { 101 // nothing to be done here. 102 } 103 104 @LayoutlibDelegate 105 /*package*/ static void nFreeTextLayoutCaches() { 106 // nothing to be done here yet. 107 } 108 109 @LayoutlibDelegate 110 /*package*/ static long nInitRaster(@Nullable Bitmap bitmap) { 111 long nativeBitmapOrZero = 0; 112 if (bitmap != null) { 113 nativeBitmapOrZero = bitmap.getNativeInstance(); 114 } 115 if (nativeBitmapOrZero > 0) { 116 // get the Bitmap from the int 117 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); 118 119 // create a new Canvas_Delegate with the given bitmap and return its new native int. 120 Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); 121 122 return sManager.addNewDelegate(newDelegate); 123 } 124 125 // create a new Canvas_Delegate and return its new native int. 126 Canvas_Delegate newDelegate = new Canvas_Delegate(); 127 128 return sManager.addNewDelegate(newDelegate); 129 } 130 131 @LayoutlibDelegate 132 public static void nSetBitmap(long canvas, Bitmap bitmap) { 133 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); 134 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 135 if (canvasDelegate == null || bitmapDelegate==null) { 136 return; 137 } 138 canvasDelegate.mBitmap = bitmapDelegate; 139 canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate); 140 } 141 142 @LayoutlibDelegate 143 public static boolean nIsOpaque(long nativeCanvas) { 144 // get the delegate from the native int. 145 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 146 if (canvasDelegate == null) { 147 return false; 148 } 149 150 return canvasDelegate.mBitmap.getConfig() == Config.RGB_565; 151 } 152 153 @LayoutlibDelegate 154 public static void nSetHighContrastText(long nativeCanvas, boolean highContrastText){} 155 156 @LayoutlibDelegate 157 public static int nGetWidth(long nativeCanvas) { 158 // get the delegate from the native int. 159 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 160 if (canvasDelegate == null) { 161 return 0; 162 } 163 164 return canvasDelegate.mBitmap.getImage().getWidth(); 165 } 166 167 @LayoutlibDelegate 168 public static int nGetHeight(long nativeCanvas) { 169 // get the delegate from the native int. 170 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 171 if (canvasDelegate == null) { 172 return 0; 173 } 174 175 return canvasDelegate.mBitmap.getImage().getHeight(); 176 } 177 178 @LayoutlibDelegate 179 public static int nSave(long nativeCanvas, int saveFlags) { 180 // get the delegate from the native int. 181 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 182 if (canvasDelegate == null) { 183 return 0; 184 } 185 186 return canvasDelegate.save(saveFlags); 187 } 188 189 @LayoutlibDelegate 190 public static int nSaveLayer(long nativeCanvas, float l, 191 float t, float r, float b, 192 long paint, int layerFlags) { 193 // get the delegate from the native int. 194 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 195 if (canvasDelegate == null) { 196 return 0; 197 } 198 199 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 200 if (paintDelegate == null) { 201 return 0; 202 } 203 204 return canvasDelegate.saveLayer(new RectF(l, t, r, b), 205 paintDelegate, layerFlags); 206 } 207 208 @LayoutlibDelegate 209 public static int nSaveLayerAlpha(long nativeCanvas, float l, 210 float t, float r, float b, 211 int alpha, int layerFlags) { 212 // get the delegate from the native int. 213 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 214 if (canvasDelegate == null) { 215 return 0; 216 } 217 218 return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags); 219 } 220 221 @LayoutlibDelegate 222 public static void nRestore(long nativeCanvas, boolean throwOnUnderflow) { 223 // FIXME: implement throwOnUnderflow. 224 // get the delegate from the native int. 225 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 226 if (canvasDelegate == null) { 227 return; 228 } 229 230 canvasDelegate.restore(); 231 } 232 233 @LayoutlibDelegate 234 public static void nRestoreToCount(long nativeCanvas, int saveCount, 235 boolean throwOnUnderflow) { 236 // FIXME: implement throwOnUnderflow. 237 // get the delegate from the native int. 238 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 239 if (canvasDelegate == null) { 240 return; 241 } 242 243 canvasDelegate.restoreTo(saveCount); 244 } 245 246 @LayoutlibDelegate 247 public static int nGetSaveCount(long nativeCanvas) { 248 // get the delegate from the native int. 249 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 250 if (canvasDelegate == null) { 251 return 0; 252 } 253 254 return canvasDelegate.getSnapshot().size(); 255 } 256 257 @LayoutlibDelegate 258 public static void nTranslate(long nativeCanvas, float dx, float dy) { 259 // get the delegate from the native int. 260 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 261 if (canvasDelegate == null) { 262 return; 263 } 264 265 canvasDelegate.getSnapshot().translate(dx, dy); 266 } 267 268 @LayoutlibDelegate 269 public static void nScale(long nativeCanvas, float sx, float sy) { 270 // get the delegate from the native int. 271 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 272 if (canvasDelegate == null) { 273 return; 274 } 275 276 canvasDelegate.getSnapshot().scale(sx, sy); 277 } 278 279 @LayoutlibDelegate 280 public static void nRotate(long nativeCanvas, float degrees) { 281 // get the delegate from the native int. 282 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 283 if (canvasDelegate == null) { 284 return; 285 } 286 287 canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees)); 288 } 289 290 @LayoutlibDelegate 291 public static void nSkew(long nativeCanvas, float kx, float ky) { 292 // get the delegate from the native int. 293 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 294 if (canvasDelegate == null) { 295 return; 296 } 297 298 // get the current top graphics2D object. 299 GcSnapshot g = canvasDelegate.getSnapshot(); 300 301 // get its current matrix 302 AffineTransform currentTx = g.getTransform(); 303 // get the AffineTransform for the given skew. 304 float[] mtx = Matrix_Delegate.getSkew(kx, ky); 305 AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx); 306 307 // combine them so that the given matrix is applied after. 308 currentTx.preConcatenate(matrixTx); 309 310 // give it to the graphics2D as a new matrix replacing all previous transform 311 g.setTransform(currentTx); 312 } 313 314 @LayoutlibDelegate 315 public static void nConcat(long nCanvas, long nMatrix) { 316 // get the delegate from the native int. 317 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); 318 if (canvasDelegate == null) { 319 return; 320 } 321 322 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); 323 if (matrixDelegate == null) { 324 return; 325 } 326 327 // get the current top graphics2D object. 328 GcSnapshot snapshot = canvasDelegate.getSnapshot(); 329 330 // get its current matrix 331 AffineTransform currentTx = snapshot.getTransform(); 332 // get the AffineTransform of the given matrix 333 AffineTransform matrixTx = matrixDelegate.getAffineTransform(); 334 335 // combine them so that the given matrix is applied after. 336 currentTx.concatenate(matrixTx); 337 338 // give it to the graphics2D as a new matrix replacing all previous transform 339 snapshot.setTransform(currentTx); 340 } 341 342 @LayoutlibDelegate 343 public static void nSetMatrix(long nCanvas, long nMatrix) { 344 // get the delegate from the native int. 345 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); 346 if (canvasDelegate == null) { 347 return; 348 } 349 350 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); 351 if (matrixDelegate == null) { 352 return; 353 } 354 355 // get the current top graphics2D object. 356 GcSnapshot snapshot = canvasDelegate.getSnapshot(); 357 358 // get the AffineTransform of the given matrix 359 AffineTransform matrixTx = matrixDelegate.getAffineTransform(); 360 361 // give it to the graphics2D as a new matrix replacing all previous transform 362 snapshot.setTransform(matrixTx); 363 364 if (matrixDelegate.hasPerspective()) { 365 assert false; 366 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MATRIX_AFFINE, 367 "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + 368 "supports affine transformations.", null, null /*data*/); 369 } 370 } 371 372 @LayoutlibDelegate 373 public static boolean nClipRect(long nCanvas, 374 float left, float top, 375 float right, float bottom, 376 int regionOp) { 377 // get the delegate from the native int. 378 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); 379 if (canvasDelegate == null) { 380 return false; 381 } 382 383 return canvasDelegate.clipRect(left, top, right, bottom, regionOp); 384 } 385 386 @LayoutlibDelegate 387 public static boolean nClipPath(long nativeCanvas, 388 long nativePath, 389 int regionOp) { 390 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 391 if (canvasDelegate == null) { 392 return true; 393 } 394 395 Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath); 396 if (pathDelegate == null) { 397 return true; 398 } 399 400 return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp); 401 } 402 403 @LayoutlibDelegate 404 public static boolean nClipRegion(long nativeCanvas, 405 long nativeRegion, 406 int regionOp) { 407 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 408 if (canvasDelegate == null) { 409 return true; 410 } 411 412 Region_Delegate region = Region_Delegate.getDelegate(nativeRegion); 413 if (region == null) { 414 return true; 415 } 416 417 return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp); 418 } 419 420 @LayoutlibDelegate 421 public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) { 422 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 423 if (canvasDelegate == null) { 424 return; 425 } 426 427 canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter); 428 429 if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) { 430 Bridge.getLog().fidelityWarning(LayoutLog.TAG_DRAWFILTER, 431 canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/); 432 } 433 } 434 435 @LayoutlibDelegate 436 public static boolean nGetClipBounds(long nativeCanvas, 437 Rect bounds) { 438 // get the delegate from the native int. 439 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 440 if (canvasDelegate == null) { 441 return false; 442 } 443 444 Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); 445 if (rect != null && !rect.isEmpty()) { 446 bounds.left = rect.x; 447 bounds.top = rect.y; 448 bounds.right = rect.x + rect.width; 449 bounds.bottom = rect.y + rect.height; 450 return true; 451 } 452 453 return false; 454 } 455 456 @LayoutlibDelegate 457 public static void nGetCTM(long canvas, long matrix) { 458 // get the delegate from the native int. 459 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); 460 if (canvasDelegate == null) { 461 return; 462 } 463 464 Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); 465 if (matrixDelegate == null) { 466 return; 467 } 468 469 AffineTransform transform = canvasDelegate.getSnapshot().getTransform(); 470 matrixDelegate.set(Matrix_Delegate.makeValues(transform)); 471 } 472 473 @LayoutlibDelegate 474 public static boolean nQuickReject(long nativeCanvas, long path) { 475 // FIXME properly implement quickReject 476 return false; 477 } 478 479 @LayoutlibDelegate 480 public static boolean nQuickReject(long nativeCanvas, 481 float left, float top, 482 float right, float bottom) { 483 // FIXME properly implement quickReject 484 return false; 485 } 486 487 @LayoutlibDelegate 488 /*package*/ static long nGetNativeFinalizer() { 489 synchronized (Canvas_Delegate.class) { 490 if (sFinalizer == -1) { 491 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> { 492 Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr); 493 if (delegate != null) { 494 delegate.dispose(); 495 } 496 sManager.removeJavaReferenceFor(nativePtr); 497 }); 498 } 499 } 500 return sFinalizer; 501 } 502 503 private Canvas_Delegate(Bitmap_Delegate bitmap) { 504 super(bitmap); 505 } 506 507 private Canvas_Delegate() { 508 super(); 509 } 510} 511 512