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 */ 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 */ 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 */ 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 */ 83 public DrawFilter_Delegate getDrawFilter() { 84 return mDrawFilter; 85 } 86 87 // ---- native methods ---- 88 89 @LayoutlibDelegate 90 /*package*/ static void nFreeCaches() { 91 // nothing to be done here. 92 } 93 94 @LayoutlibDelegate 95 /*package*/ static void nFreeTextLayoutCaches() { 96 // nothing to be done here yet. 97 } 98 99 @LayoutlibDelegate 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 442 public static boolean nQuickReject(long nativeCanvas, long path) { 443 // FIXME properly implement quickReject 444 return false; 445 } 446 447 @LayoutlibDelegate 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 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 472 /*package*/ static void nSetCompatibilityVersion(int apiLevel) { 473 // Unsupported by layoutlib, do nothing 474 } 475 476 private Canvas_Delegate(Bitmap_Delegate bitmap) { 477 super(bitmap); 478 } 479 480 private Canvas_Delegate() { 481 super(); 482 } 483 } 484 485