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.tools.layoutlib.annotations.LayoutlibDelegate; 24 25import android.annotation.Nullable; 26import android.graphics.Bitmap.Config; 27 28import java.awt.Graphics2D; 29import java.awt.Rectangle; 30import java.awt.geom.AffineTransform; 31 32import 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 */ 48public 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(@Nullable Bitmap bitmap) { 101 long nativeBitmapOrZero = 0; 102 if (bitmap != null) { 103 nativeBitmapOrZero = bitmap.getNativeInstance(); 104 } 105 if (nativeBitmapOrZero > 0) { 106 // get the Bitmap from the int 107 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero); 108 109 // create a new Canvas_Delegate with the given bitmap and return its new native int. 110 Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); 111 112 return sManager.addNewDelegate(newDelegate); 113 } 114 115 // create a new Canvas_Delegate and return its new native int. 116 Canvas_Delegate newDelegate = new Canvas_Delegate(); 117 118 return sManager.addNewDelegate(newDelegate); 119 } 120 121 @LayoutlibDelegate 122 public static void nSetBitmap(long canvas, Bitmap bitmap) { 123 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); 124 Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap); 125 if (canvasDelegate == null || bitmapDelegate==null) { 126 return; 127 } 128 canvasDelegate.mBitmap = bitmapDelegate; 129 canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate); 130 } 131 132 @LayoutlibDelegate 133 public static boolean nIsOpaque(long nativeCanvas) { 134 // get the delegate from the native int. 135 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 136 if (canvasDelegate == null) { 137 return false; 138 } 139 140 return canvasDelegate.mBitmap.getConfig() == Config.RGB_565; 141 } 142 143 @LayoutlibDelegate 144 public static void nSetHighContrastText(long nativeCanvas, boolean highContrastText){} 145 146 @LayoutlibDelegate 147 public static int nGetWidth(long nativeCanvas) { 148 // get the delegate from the native int. 149 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 150 if (canvasDelegate == null) { 151 return 0; 152 } 153 154 return canvasDelegate.mBitmap.getImage().getWidth(); 155 } 156 157 @LayoutlibDelegate 158 public static int nGetHeight(long nativeCanvas) { 159 // get the delegate from the native int. 160 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 161 if (canvasDelegate == null) { 162 return 0; 163 } 164 165 return canvasDelegate.mBitmap.getImage().getHeight(); 166 } 167 168 @LayoutlibDelegate 169 public static int nSave(long nativeCanvas, int saveFlags) { 170 // get the delegate from the native int. 171 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 172 if (canvasDelegate == null) { 173 return 0; 174 } 175 176 return canvasDelegate.save(saveFlags); 177 } 178 179 @LayoutlibDelegate 180 public static int nSaveLayer(long nativeCanvas, float l, 181 float t, float r, float b, 182 long paint, int layerFlags) { 183 // get the delegate from the native int. 184 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 185 if (canvasDelegate == null) { 186 return 0; 187 } 188 189 Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); 190 if (paintDelegate == null) { 191 return 0; 192 } 193 194 return canvasDelegate.saveLayer(new RectF(l, t, r, b), 195 paintDelegate, layerFlags); 196 } 197 198 @LayoutlibDelegate 199 public static int nSaveLayerAlpha(long nativeCanvas, float l, 200 float t, float r, float b, 201 int alpha, int layerFlags) { 202 // get the delegate from the native int. 203 Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); 204 if (canvasDelegate == null) { 205 return 0; 206 } 207 208 return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags); 209 } 210 211 @LayoutlibDelegate 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 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 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 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 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 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 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 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 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(LayoutLog.TAG_MATRIX_AFFINE, 357 "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + 358 "supports affine transformations.", null, null /*data*/); 359 } 360 } 361 362 @LayoutlibDelegate 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 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 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(LayoutLog.TAG_DRAWFILTER, 404 canvasDelegate.mDrawFilter.getSupportMessage(), null, null /*data*/); 405 } 406 } 407 408 @LayoutlibDelegate 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 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 447 public static boolean nQuickReject(long nativeCanvas, long path) { 448 // FIXME properly implement quickReject 449 return false; 450 } 451 452 @LayoutlibDelegate 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 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 private Canvas_Delegate(Bitmap_Delegate bitmap) { 477 super(bitmap); 478 } 479 480 private Canvas_Delegate() { 481 super(); 482 } 483} 484 485