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