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