From cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Tue, 14 Dec 2010 18:05:22 -0800 Subject: [PATCH] LayoutLib: replace the stack of Graphics2D with custom snapshots. The first step is to allow the canvas to save only the matrix or the clip. Previously, since the graphics2D were created completely new on each save, everything was saved. Also allows to set transform/clip on the Canvas when no bitmap has been set (and therefore no Graphics2D exists). This is required by BitmapFactory. Change-Id: I37eff6dde1ca708d58ba317b40e9b3236f3ef3d0 --- .../src/android/graphics/Canvas_Delegate.java | 208 ++++++------- .../src/android/graphics/Matrix_Delegate.java | 15 +- .../android/graphics/NinePatch_Delegate.java | 9 +- .../layoutlib/bridge/impl/GcSnapshot.java | 288 ++++++++++++++++++ 4 files changed, 405 insertions(+), 115 deletions(-) create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index f6d7b3a0243ae..c75e1b6a23d7b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -18,7 +18,7 @@ package android.graphics; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.layoutlib.bridge.impl.Stack; +import com.android.layoutlib.bridge.impl.GcSnapshot; import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; @@ -26,7 +26,6 @@ import android.text.TextUtils; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; -import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; @@ -58,7 +57,7 @@ public class Canvas_Delegate { // ---- delegate data ---- private BufferedImage mBufferedImage; - private final Stack mGraphicsStack = new Stack(); + private GcSnapshot mSnapshot = new GcSnapshot(); // ---- Public Helper methods ---- @@ -79,8 +78,8 @@ public class Canvas_Delegate { /** * Returns the current {@link Graphics2D} used to draw. */ - public Graphics2D getGraphics2d() { - return mGraphicsStack.peek(); + public GcSnapshot getGcSnapshot() { + return mSnapshot; } // ---- native methods ---- @@ -120,7 +119,7 @@ public class Canvas_Delegate { return; } - canvasDelegate.getGraphics2d().translate(dx, dy); + canvasDelegate.getGcSnapshot().translate(dx, dy); } /*package*/ static void rotate(Canvas thisCanvas, float degrees) { @@ -131,7 +130,7 @@ public class Canvas_Delegate { return; } - canvasDelegate.getGraphics2d().rotate(Math.toRadians(degrees)); + canvasDelegate.getGcSnapshot().rotate(Math.toRadians(degrees)); } /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { @@ -142,7 +141,7 @@ public class Canvas_Delegate { return; } - canvasDelegate.getGraphics2d().scale(sx, sy); + canvasDelegate.getGcSnapshot().scale(sx, sy); } /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { @@ -154,7 +153,7 @@ public class Canvas_Delegate { } // get the current top graphics2D object. - Graphics2D g = canvasDelegate.getGraphics2d(); + GcSnapshot g = canvasDelegate.getGcSnapshot(); // get its current matrix AffineTransform currentTx = g.getTransform(); @@ -170,21 +169,16 @@ public class Canvas_Delegate { } /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) { - return clipRect(thisCanvas, - (int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); + return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); } /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) { - return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom); + return clipRect(thisCanvas, (float) rect.left, (float) rect.top, + (float) rect.right, (float) rect.bottom); } /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, float bottom) { - return clipRect(thisCanvas, (int) left, (int) top, (int) right, (int) bottom); - } - - /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, - int bottom) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); if (canvasDelegate == null) { @@ -195,7 +189,17 @@ public class Canvas_Delegate { return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } + /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right, + int bottom) { + + return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom); + } + /*package*/ static int save(Canvas thisCanvas) { + return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); + } + + /*package*/ static int save(Canvas thisCanvas, int saveFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); if (canvasDelegate == null) { @@ -203,21 +207,7 @@ public class Canvas_Delegate { return 0; } - // get the current save count - int count = canvasDelegate.mGraphicsStack.size(); - - // create a new graphics and add it to the stack - Graphics2D g = (Graphics2D)canvasDelegate.getGraphics2d().create(); - canvasDelegate.mGraphicsStack.push(g); - - // return the old save count - return count; - - } - - /*package*/ static int save(Canvas thisCanvas, int saveFlags) { - // FIXME implement save(flags) - return save(thisCanvas); + return canvasDelegate.save(saveFlags); } /*package*/ static void restore(Canvas thisCanvas) { @@ -228,7 +218,7 @@ public class Canvas_Delegate { return; } - canvasDelegate.mGraphicsStack.pop(); + canvasDelegate.restore(); } /*package*/ static int getSaveCount(Canvas thisCanvas) { @@ -239,7 +229,7 @@ public class Canvas_Delegate { return 0; } - return canvasDelegate.mGraphicsStack.size(); + return canvasDelegate.getGcSnapshot().size(); } /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { @@ -250,9 +240,7 @@ public class Canvas_Delegate { return; } - while (canvasDelegate.mGraphicsStack.size() > saveCount) { - canvasDelegate.mGraphicsStack.pop(); - } + canvasDelegate.restoreTo(saveCount); } /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count, @@ -282,7 +270,7 @@ public class Canvas_Delegate { } // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); try { for (int i = 0 ; i < count ; i += 4) { @@ -378,10 +366,10 @@ public class Canvas_Delegate { } // get the current top graphics2D object. - Graphics2D g = canvasDelegate.getGraphics2d(); + GcSnapshot snapshot = canvasDelegate.getGcSnapshot(); // get its current matrix - AffineTransform currentTx = g.getTransform(); + AffineTransform currentTx = snapshot.getTransform(); // get the AffineTransform of the given matrix AffineTransform matrixTx = matrixDelegate.getAffineTransform(); @@ -389,7 +377,7 @@ public class Canvas_Delegate { currentTx.preConcatenate(matrixTx); // give it to the graphics2D as a new matrix replacing all previous transform - g.setTransform(currentTx); + snapshot.setTransform(currentTx); } /*package*/ static void native_setMatrix(int nCanvas, int nMatrix) { @@ -405,15 +393,16 @@ public class Canvas_Delegate { } // get the current top graphics2D object. - Graphics2D g = canvasDelegate.getGraphics2d(); + GcSnapshot snapshot = canvasDelegate.getGcSnapshot(); // get the AffineTransform of the given matrix AffineTransform matrixTx = matrixDelegate.getAffineTransform(); // give it to the graphics2D as a new matrix replacing all previous transform - g.setTransform(matrixTx); + snapshot.setTransform(matrixTx); if (matrixDelegate.hasPerspective()) { + assert false; Bridge.getLog().warning(null, "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " + "supports affine transformations in the Layout Preview."); @@ -431,9 +420,7 @@ public class Canvas_Delegate { assert false; } - return canvasDelegate.clipRect( - (int) left, (int) top, (int) right, (int) bottom, - regionOp); + return canvasDelegate.clipRect(left, top, right, bottom, regionOp); } /*package*/ static boolean native_clipPath(int nativeCanvas, @@ -465,7 +452,7 @@ public class Canvas_Delegate { return false; } - Rectangle rect = canvasDelegate.getGraphics2d().getClipBounds(); + Rectangle rect = canvasDelegate.getGcSnapshot().getClip().getBounds(); if (rect != null) { bounds.left = rect.x; bounds.top = rect.y; @@ -527,7 +514,7 @@ public class Canvas_Delegate { } // get a new graphics context. - Graphics2D graphics = (Graphics2D)canvasDelegate.getGraphics2d().create(); + Graphics2D graphics = (Graphics2D)canvasDelegate.getGcSnapshot().create(); try { // reset its transform just in case graphics.setTransform(new AffineTransform()); @@ -568,12 +555,14 @@ public class Canvas_Delegate { } // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); - g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); - - // dispose Graphics2D object - g.dispose(); + try { + g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); + } finally { + // dispose Graphics2D object + g.dispose(); + } } /*package*/ static void native_drawRect(int nativeCanvas, RectF rect, @@ -600,23 +589,25 @@ public class Canvas_Delegate { if (right > left && bottom > top) { // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); - int style = paintDelegate.getStyle(); + try { + int style = paintDelegate.getStyle(); - // draw - if (style == Paint.Style.FILL.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); + // draw + if (style == Paint.Style.FILL.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + g.fillRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); + } + + if (style == Paint.Style.STROKE.nativeInt || + style == Paint.Style.FILL_AND_STROKE.nativeInt) { + g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); + } + } finally { + // dispose Graphics2D object + g.dispose(); } - - if (style == Paint.Style.STROKE.nativeInt || - style == Paint.Style.FILL_AND_STROKE.nativeInt) { - g.drawRect((int)left, (int)top, (int)(right-left), (int)(bottom-top)); - } - - // dispose Graphics2D object - g.dispose(); } } @@ -638,7 +629,7 @@ public class Canvas_Delegate { if (oval.right > oval.left && oval.bottom > oval.top) { // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); int style = paintDelegate.getStyle(); @@ -692,7 +683,7 @@ public class Canvas_Delegate { if (rect.right > rect.left && rect.bottom > rect.top) { // get a Graphics2D object configured with the drawing parameters. - Graphics2D g = canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate); int style = paintDelegate.getStyle(); @@ -833,7 +824,7 @@ public class Canvas_Delegate { return; } - Graphics2D g = (Graphics2D) canvasDelegate.getCustomGraphics(paintDelegate); + Graphics2D g = (Graphics2D) canvasDelegate.createCustomGraphics(paintDelegate); try { // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. @@ -1018,34 +1009,53 @@ public class Canvas_Delegate { * Disposes of the {@link Graphics2D} stack. */ private void dispose() { - while (mGraphicsStack.size() > 0) { - mGraphicsStack.pop().dispose(); - } + mSnapshot.dispose(); } - private boolean clipRect(int left, int top, int right, int bottom, int regionOp) { - if (regionOp == Region.Op.INTERSECT.nativeInt) { - Graphics2D gc = getGraphics2d(); - gc.clipRect(left, top, right - left, bottom - top); - return gc.getClip().getBounds().isEmpty() == false; - } else { - throw new UnsupportedOperationException(); - } + private int save(int saveFlags) { + // get the current save count + int count = mSnapshot.size(); + + // create a new snapshot and add it to the stack + mSnapshot = new GcSnapshot(mSnapshot, saveFlags); + + // return the old save count + return count; + } + + /** + * Restores the {@link GcSnapshot} to saveCount + * @param saveCount the saveCount + */ + private void restoreTo(int saveCount) { + mSnapshot = mSnapshot.restoreTo(saveCount); + } + + /** + * Restores the {@link GcSnapshot} to saveCount + * @param saveCount the saveCount + */ + private void restore() { + mSnapshot = mSnapshot.restore(); + } + + private boolean clipRect(float left, float top, float right, float bottom, int regionOp) { + return mSnapshot.clipRect(left, top, right, bottom, regionOp); } private void setBitmap(BufferedImage image) { mBufferedImage = image; - mGraphicsStack.push(mBufferedImage.createGraphics()); + assert mSnapshot.size() == 1; + mSnapshot.setGraphics2D(mBufferedImage.createGraphics()); } /** * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. *

The object must be disposed ({@link Graphics2D#dispose()}) after being used. */ - /*package*/ Graphics2D getCustomGraphics(Paint_Delegate paint) { + /*package*/ Graphics2D createCustomGraphics(Paint_Delegate paint) { // make new one - Graphics2D g = getGraphics2d(); - g = (Graphics2D)g.create(); + Graphics2D g = getGcSnapshot().create(); // configure it @@ -1062,6 +1072,7 @@ public class Canvas_Delegate { Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader()); if (shaderDelegate != null) { java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint(); + assert shaderPaint != null; if (shaderPaint != null) { g.setPaint(shaderPaint); useColorPaint = false; @@ -1113,6 +1124,7 @@ public class Canvas_Delegate { // if xfermode wasn't null, then it's something we don't support. log it. if (xfermodeDelegate != null) { + assert false; Bridge.getLog().warning(null, String.format( "Xfermode '%1$s' is not supported in the Layout Preview.", @@ -1217,30 +1229,18 @@ public class Canvas_Delegate { int sleft, int stop, int sright, int sbottom, int dleft, int dtop, int dright, int dbottom) { - Graphics2D g = canvasDelegate.getGraphics2d(); - - Composite c = null; - - if (paintDelegate != null) { - if (paintDelegate.isFilterBitmap()) { - g = (Graphics2D)g.create(); + Graphics2D g = canvasDelegate.getGcSnapshot().create(); + try { + if (paintDelegate != null && paintDelegate.isFilterBitmap()) { g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); } - } - g.drawImage(image, dleft, dtop, dright, dbottom, - sleft, stop, sright, sbottom, null); - - if (paintDelegate != null) { - if (paintDelegate.isFilterBitmap()) { - g.dispose(); - } - if (c != null) { - g.setComposite(c); - } + g.drawImage(image, dleft, dtop, dright, dbottom, + sleft, stop, sright, sbottom, null); + } finally { + g.dispose(); } } - } diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 6b43544f19df9..b464f66b4311c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -195,7 +195,8 @@ public final class Matrix_Delegate { setTranslate(d.mValues, dx, dy); } - /*package*/ static void native_setScale(int native_object, float sx, float sy, float px, float py) { + /*package*/ static void native_setScale(int native_object, float sx, float sy, + float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { assert false; @@ -272,7 +273,8 @@ public final class Matrix_Delegate { setRotate(d.mValues, sinValue, cosValue); } - /*package*/ static void native_setSkew(int native_object, float kx, float ky, float px, float py) { + /*package*/ static void native_setSkew(int native_object, float kx, float ky, + float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { assert false; @@ -364,7 +366,8 @@ public final class Matrix_Delegate { return true; } - /*package*/ static boolean native_preRotate(int native_object, float degrees, float px, float py) { + /*package*/ static boolean native_preRotate(int native_object, float degrees, + float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { assert false; @@ -464,7 +467,8 @@ public final class Matrix_Delegate { return true; } - /*package*/ static boolean native_postRotate(int native_object, float degrees, float px, float py) { + /*package*/ static boolean native_postRotate(int native_object, float degrees, + float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { assert false; @@ -526,7 +530,8 @@ public final class Matrix_Delegate { return true; } - /*package*/ static boolean native_setRectToRect(int native_object, RectF src, RectF dst, int stf) { + /*package*/ static boolean native_setRectToRect(int native_object, RectF src, + RectF dst, int stf) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { assert false; diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java index 95663ec6c28c9..fe9bef934bd1d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java @@ -160,20 +160,17 @@ public class NinePatch_Delegate { Graphics2D graphics; if (paint_delegate != null) { - graphics = canvas_delegate.getCustomGraphics(paint_delegate); + graphics = canvas_delegate.createCustomGraphics(paint_delegate); } else { - graphics = canvas_delegate.getGraphics2d(); + graphics = canvas_delegate.getGcSnapshot().create(); } try { chunkObject.draw(bitmap_delegate.getImage(), graphics, left, top, right - left, bottom - top, destDensity, srcDensity); } finally { - if (paint_delegate != null) { - graphics.dispose(); - } + graphics.dispose(); } - } /*package*/ static int nativeGetTransparentRegion(int bitmap, byte[] chunk, Rect location) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java new file mode 100644 index 0000000000000..8c6b1be82551b --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -0,0 +1,288 @@ +/* + * 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 com.android.layoutlib.bridge.impl; + +import android.graphics.Canvas; +import android.graphics.Region; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Area; +import java.awt.geom.Rectangle2D; + +/** + * Class representing a graphics context snapshot, as well as a context stack as a linked list. + *

+ * This is based on top of {@link Graphics2D} but can operate independently if none are available + * yet when setting transforms and clip information. + * + */ +public class GcSnapshot { + + private final GcSnapshot mPrevious; + private final int mFlags; + + private Graphics2D mGraphics2D = null; + /** temp transform in case transformation are set before a Graphics2D exists */ + private AffineTransform mTransform = null; + /** temp clip in case clipping is set before a Graphics2D exists */ + private Area mClip = null; + + /** + * Creates a new {@link GcSnapshot} on top of another one. + *

+ * This is basically the equivalent of {@link Canvas#save(int)} + * @param previous the previous snapshot head. + * @param flags the flags regarding what should be saved. + */ + public GcSnapshot(GcSnapshot previous, int flags) { + assert previous != null; + mPrevious = previous; + mFlags = flags; + mGraphics2D = (Graphics2D) previous.mGraphics2D.create(); + } + + /** + * Creates the root snapshot. + * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible. + */ + public GcSnapshot() { + mPrevious = null; + mFlags = 0; + } + + public void dispose() { + if (mGraphics2D != null) { + mGraphics2D.dispose(); + } + + if (mPrevious != null) { + mPrevious.dispose(); + } + } + + /** + * Restores the top {@link GcSnapshot}, and returns the next one. + */ + public GcSnapshot restore() { + return doRestore(); + } + + /** + * Restores the {@link GcSnapshot} to saveCount. + * @param saveCount the saveCount or -1 to only restore 1. + * + * @return the new head of the Gc snapshot stack. + */ + public GcSnapshot restoreTo(int saveCount) { + return doRestoreTo(size(), saveCount); + } + + public int size() { + if (mPrevious != null) { + return mPrevious.size() + 1; + } + + return 1; + } + + /** + * Sets the Graphics2D object for this snapshot if it was created through {@link #GcSnapshot()}. + * If any transform or clip information was set before, they are put into the Graphics object. + * @param graphics2D the graphics object to set. + */ + public void setGraphics2D(Graphics2D graphics2D) { + mGraphics2D = graphics2D; + if (mTransform != null) { + mGraphics2D.setTransform(mTransform); + mTransform = null; + } + + if (mClip != null) { + mGraphics2D.setClip(mClip); + mClip = null; + } + } + + /** + * Creates and return a copy of the current {@link Graphics2D}. + * @return a new {@link Graphics2D}. + */ + public Graphics2D create() { + assert mGraphics2D != null; + return (Graphics2D) mGraphics2D.create(); + } + + public void translate(float dx, float dy) { + if (mGraphics2D != null) { + mGraphics2D.translate(dx, dy); + } else { + if (mTransform == null) { + mTransform = new AffineTransform(); + } + mTransform.translate(dx, dy); + } + } + + public void rotate(double radians) { + if (mGraphics2D != null) { + mGraphics2D.rotate(radians); + } else { + if (mTransform == null) { + mTransform = new AffineTransform(); + } + mTransform.rotate(radians); + } + } + + public void scale(float sx, float sy) { + if (mGraphics2D != null) { + mGraphics2D.scale(sx, sy); + } else { + if (mTransform == null) { + mTransform = new AffineTransform(); + } + mTransform.scale(sx, sy); + } + } + + public AffineTransform getTransform() { + if (mGraphics2D != null) { + return mGraphics2D.getTransform(); + } else { + if (mTransform == null) { + mTransform = new AffineTransform(); + } + return mTransform; + } + } + + public void setTransform(AffineTransform transform) { + if (mGraphics2D != null) { + mGraphics2D.setTransform(transform); + } else { + if (mTransform == null) { + mTransform = new AffineTransform(); + } + mTransform.setTransform(transform); + } + } + + public boolean clipRect(float left, float top, float right, float bottom, int regionOp) { + if (mGraphics2D != null) { + if (regionOp == Region.Op.DIFFERENCE.nativeInt) { + Area newClip = new Area(mGraphics2D.getClip()); + newClip.subtract(new Area( + new Rectangle2D.Float(left, top, right - left, bottom - top))); + mGraphics2D.setClip(newClip); + + } else if (regionOp == Region.Op.INTERSECT.nativeInt) { + mGraphics2D.clipRect((int) left, (int) top, + (int) (right - left), (int) (bottom - top)); + + } else if (regionOp == Region.Op.UNION.nativeInt) { + Area newClip = new Area(mGraphics2D.getClip()); + newClip.add(new Area( + new Rectangle2D.Float(left, top, right - left, bottom - top))); + mGraphics2D.setClip(newClip); + + } else if (regionOp == Region.Op.XOR.nativeInt) { + Area newClip = new Area(mGraphics2D.getClip()); + newClip.exclusiveOr(new Area( + new Rectangle2D.Float(left, top, right - left, bottom - top))); + mGraphics2D.setClip(newClip); + + } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) { + Area newClip = new Area( + new Rectangle2D.Float(left, top, right - left, bottom - top)); + newClip.subtract(new Area(mGraphics2D.getClip())); + mGraphics2D.setClip(newClip); + } else if (regionOp == Region.Op.REPLACE.nativeInt) { + mGraphics2D.setClip((int) left, (int) top, + (int) (right - left), (int) (bottom - top)); + } + + return mGraphics2D.getClip().getBounds().isEmpty() == false; + } else { + if (mClip == null) { + mClip = new Area(); + } + + if (regionOp == Region.Op.DIFFERENCE.nativeInt) { + //FIXME + } else if (regionOp == Region.Op.DIFFERENCE.nativeInt) { + } else if (regionOp == Region.Op.INTERSECT.nativeInt) { + } else if (regionOp == Region.Op.UNION.nativeInt) { + } else if (regionOp == Region.Op.XOR.nativeInt) { + } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) { + } else if (regionOp == Region.Op.REPLACE.nativeInt) { + } + + return mClip.getBounds().isEmpty() == false; + } + } + + public Shape getClip() { + if (mGraphics2D != null) { + return mGraphics2D.getClip(); + } else { + if (mClip == null) { + mClip = new Area(); + } + return mClip; + } + } + + private GcSnapshot doRestoreTo(int size, int saveCount) { + if (size <= saveCount) { + return this; + } + + // restore the current one first. + GcSnapshot previous = doRestore(); + + if (size == saveCount + 1) { // this was the only one that needed restore. + return previous; + } else { + return previous.doRestoreTo(size - 1, saveCount); + } + } + + private GcSnapshot doRestore() { + // if this snapshot does not save everything, then set the previous snapshot + // to this snapshot content + if (mPrevious != null) { + // didn't save the matrix? set the current matrix on the previous snapshot + if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) { + mPrevious.mGraphics2D.setTransform(getTransform()); + } + + // didn't save the clip? set the current clip on the previous snapshot + if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) { + mPrevious.mGraphics2D.setClip(mGraphics2D.getClip()); + } + } + + if (mGraphics2D != null) { + mGraphics2D.dispose(); + } + + return mPrevious; + } + +}