/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import com.android.ide.common.rendering.api.ILayoutLog; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.GcSnapshot; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.graphics.Bitmap.Config; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import libcore.util.NativeAllocationRegistry_Delegate; /** * Delegate implementing the native methods of android.graphics.Canvas * * Through the layoutlib_create tool, the original native methods of Canvas have been replaced * by calls to methods of the same name in this delegate class. * * This class behaves like the original native implementation, but in Java, keeping previously * native data into its own objects and mapping them to int that are sent back and forth between * it and the original Canvas class. * * @see DelegateManager * */ public final class Canvas_Delegate extends BaseCanvas_Delegate { // ---- delegate manager ---- private static long sFinalizer = -1; private DrawFilter_Delegate mDrawFilter = null; // ---- Public Helper methods ---- /** * Returns the native delegate associated to a given {@link Canvas} object. */ public static Canvas_Delegate getDelegate(Canvas canvas) { return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper()); } /** * Returns the native delegate associated to a given an int referencing a {@link Canvas} object. */ public static Canvas_Delegate getDelegate(long native_canvas) { return (Canvas_Delegate) sManager.getDelegate(native_canvas); } /** * Returns the current {@link Graphics2D} used to draw. */ public GcSnapshot getSnapshot() { return mSnapshot; } /** * Returns the {@link DrawFilter} delegate or null if none have been set. * * @return the delegate or null. */ public DrawFilter_Delegate getDrawFilter() { return mDrawFilter; } // ---- native methods ---- @LayoutlibDelegate /*package*/ static void nFreeCaches() { // nothing to be done here. } @LayoutlibDelegate /*package*/ static void nFreeTextLayoutCaches() { // nothing to be done here yet. } @LayoutlibDelegate /*package*/ static long nInitRaster(long bitmapHandle) { if (bitmapHandle > 0) { // get the Bitmap from the int Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle); // create a new Canvas_Delegate with the given bitmap and return its new native int. Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate); return sManager.addNewDelegate(newDelegate); } // create a new Canvas_Delegate and return its new native int. Canvas_Delegate newDelegate = new Canvas_Delegate(); return sManager.addNewDelegate(newDelegate); } @LayoutlibDelegate public static void nSetBitmap(long canvas, long bitmapHandle) { Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle); if (canvasDelegate == null || bitmapDelegate == null) { return; } canvasDelegate.mBitmap = bitmapDelegate; canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate); } @LayoutlibDelegate public static boolean nIsOpaque(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return false; } return canvasDelegate.mBitmap.getConfig() == Config.RGB_565; } @LayoutlibDelegate public static int nGetWidth(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } return canvasDelegate.mBitmap.getImage().getWidth(); } @LayoutlibDelegate public static int nGetHeight(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } return canvasDelegate.mBitmap.getImage().getHeight(); } @LayoutlibDelegate public static int nSave(long nativeCanvas, int saveFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } return canvasDelegate.save(saveFlags); } @LayoutlibDelegate public static int nSaveLayer(long nativeCanvas, float l, float t, float r, float b, long paint, int layerFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint); return canvasDelegate.saveLayer(new RectF(l, t, r, b), paintDelegate, layerFlags); } @LayoutlibDelegate public static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) { return nSaveLayer(nativeCanvas, l, t, r, b, 0, 0); } @LayoutlibDelegate public static int nSaveLayerAlpha(long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags); } @LayoutlibDelegate public static void nRestoreUnclippedLayer(long nativeCanvas, int saveCount, long nativePaint) { nRestoreToCount(nativeCanvas, saveCount); } @LayoutlibDelegate public static boolean nRestore(long nativeCanvas) { // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return false; } canvasDelegate.restore(); return true; } @LayoutlibDelegate public static void nRestoreToCount(long nativeCanvas, int saveCount) { // FIXME: implement throwOnUnderflow. // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } canvasDelegate.restoreTo(saveCount); } @LayoutlibDelegate public static int nGetSaveCount(long nativeCanvas) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return 0; } return canvasDelegate.getSnapshot().size(); } @LayoutlibDelegate public static void nTranslate(long nativeCanvas, float dx, float dy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } canvasDelegate.getSnapshot().translate(dx, dy); } @LayoutlibDelegate public static void nScale(long nativeCanvas, float sx, float sy) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } canvasDelegate.getSnapshot().scale(sx, sy); } @LayoutlibDelegate public static void nRotate(long nativeCanvas, float degrees) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees)); } @LayoutlibDelegate public static void nSkew(long nativeCanvas, float kx, float ky) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } // get the current top graphics2D object. GcSnapshot g = canvasDelegate.getSnapshot(); // get its current matrix AffineTransform currentTx = g.getTransform(); // get the AffineTransform for the given skew. float[] mtx = Matrix_Delegate.getSkew(kx, ky); AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx); // combine them so that the given matrix is applied after. currentTx.preConcatenate(matrixTx); // give it to the graphics2D as a new matrix replacing all previous transform g.setTransform(currentTx); } @LayoutlibDelegate public static void nConcat(long nCanvas, long nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); if (canvasDelegate == null) { return; } Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); if (matrixDelegate == null) { return; } // get the current top graphics2D object. GcSnapshot snapshot = canvasDelegate.getSnapshot(); // get its current matrix AffineTransform currentTx = snapshot.getTransform(); // get the AffineTransform of the given matrix AffineTransform matrixTx = matrixDelegate.getAffineTransform(); // combine them so that the given matrix is applied after. currentTx.concatenate(matrixTx); // give it to the graphics2D as a new matrix replacing all previous transform snapshot.setTransform(currentTx); } @LayoutlibDelegate public static void nSetMatrix(long nCanvas, long nMatrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); if (canvasDelegate == null) { return; } Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix); if (matrixDelegate == null) { return; } // get the current top graphics2D object. GcSnapshot snapshot = canvasDelegate.getSnapshot(); // get the AffineTransform of the given matrix AffineTransform matrixTx = matrixDelegate.getAffineTransform(); // give it to the graphics2D as a new matrix replacing all previous transform snapshot.setTransform(matrixTx); if (matrixDelegate.hasPerspective()) { assert false; Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_AFFINE, "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + "supports affine transformations.", null, null, null /*data*/); } } @LayoutlibDelegate public static boolean nClipRect(long nCanvas, float left, float top, float right, float bottom, int regionOp) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas); if (canvasDelegate == null) { return false; } return canvasDelegate.clipRect(left, top, right, bottom, regionOp); } @LayoutlibDelegate public static boolean nClipPath(long nativeCanvas, long nativePath, int regionOp) { Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return true; } Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath); if (pathDelegate == null) { return true; } return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp); } @LayoutlibDelegate public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) { Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return; } canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter); if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) { Bridge.getLog().fidelityWarning(ILayoutLog.TAG_DRAWFILTER, canvasDelegate.mDrawFilter.getSupportMessage(), null, null, null /*data*/); } } @LayoutlibDelegate public static boolean nGetClipBounds(long nativeCanvas, Rect bounds) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas); if (canvasDelegate == null) { return false; } Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds(); if (rect != null && !rect.isEmpty()) { bounds.left = rect.x; bounds.top = rect.y; bounds.right = rect.x + rect.width; bounds.bottom = rect.y + rect.height; return true; } return false; } @LayoutlibDelegate public static void nGetMatrix(long canvas, long matrix) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); if (canvasDelegate == null) { return; } Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); if (matrixDelegate == null) { return; } AffineTransform transform = canvasDelegate.getSnapshot().getTransform(); matrixDelegate.set(Matrix_Delegate.makeValues(transform)); } @LayoutlibDelegate public static boolean nQuickReject(long nativeCanvas, long path) { // FIXME properly implement quickReject return false; } @LayoutlibDelegate public static boolean nQuickReject(long nativeCanvas, float left, float top, float right, float bottom) { // FIXME properly implement quickReject return false; } @LayoutlibDelegate /*package*/ static long nGetNativeFinalizer() { synchronized (Canvas_Delegate.class) { if (sFinalizer == -1) { sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> { Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr); if (delegate != null) { delegate.dispose(); } sManager.removeJavaReferenceFor(nativePtr); }); } } return sFinalizer; } @LayoutlibDelegate /*package*/ static void nSetCompatibilityVersion(int apiLevel) { // Unsupported by layoutlib, do nothing } private Canvas_Delegate(Bitmap_Delegate bitmap) { super(bitmap); } private Canvas_Delegate() { super(); } }