1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2010 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.layoutlib.bridge.impl;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.LayoutLog;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.Bridge;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Bitmap_Delegate;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Canvas;
24f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Guptaimport android.graphics.ColorFilter_Delegate;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Paint;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Paint_Delegate;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Rect;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.RectF;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Region;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Region_Delegate;
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Shader_Delegate;
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Xfermode_Delegate;
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.AlphaComposite;
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Color;
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Composite;
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Graphics2D;
38f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perezimport java.awt.Rectangle;
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.RenderingHints;
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Shape;
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.AffineTransform;
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.Area;
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.Rectangle2D;
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.image.BufferedImage;
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.ArrayList;
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Class representing a graphics context snapshot, as well as a context stack as a linked list.
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p>
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is based on top of {@link Graphics2D} but can operate independently if none are available
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * yet when setting transforms and clip information.
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p>
53f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
54f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta * {@link #draw(Drawable)}
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * for each layer. Doing a save() will duplicate this list so that each graphics2D object
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic class GcSnapshot {
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final GcSnapshot mPrevious;
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final int mFlags;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** list of layers. The first item in the list is always the  */
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** temp transform in case transformation are set before a Graphics2D exists */
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private AffineTransform mTransform = null;
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** temp clip in case clipping is set before a Graphics2D exists */
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private Area mClip = null;
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // local layer data
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If this is null, this does not mean there's no layer, just that the snapshot is not the
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * one that created the layer.
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @see #getLayerSnapshot()
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Layer mLocalLayer;
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Paint_Delegate mLocalLayerPaint;
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final Rect mLayerBounds;
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public interface Drawable {
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        void draw(Graphics2D graphics, Paint_Delegate paint);
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Class containing information about a layer.
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This contains graphics, bitmap and layer information.
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static class Layer {
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private final Graphics2D mGraphics;
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private final Bitmap_Delegate mBitmap;
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private final BufferedImage mImage;
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** the flags that were used to configure the layer. This is never changed, and passed
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * as is when {@link #makeCopy()} is called */
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private final int mFlags;
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** the original content of the layer when the next object was created. This is not
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * (depending on its flags) */
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        private BufferedImage mOriginalCopy;
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /**
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * Creates a layer with a graphics and a bitmap. This is only used to create
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * the base layer.
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         *
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param graphics the graphics
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param bitmap the bitmap
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         */
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mGraphics = graphics;
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mBitmap = bitmap;
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mImage = mBitmap.getImage();
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mFlags = 0;
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /**
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * Creates a layer with a graphics and an image. If the image belongs to a
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * {@link Bitmap_Delegate} (case of the base layer), then
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         *
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param graphics the graphics the new graphics for this layer
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param image the image the image from which the graphics came
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param flags the flags that were used to save this layer
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         */
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Layer(Graphics2D graphics, BufferedImage image, int flags) {
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mGraphics = graphics;
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mBitmap = null;
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mImage = image;
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mFlags = flags;
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** The Graphics2D, guaranteed to be non null */
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D getGraphics() {
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mGraphics;
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** The BufferedImage, guaranteed to be non null */
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        BufferedImage getImage() {
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mImage;
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** Returns the layer save flags. This is only valid for additional layers.
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * For the base layer this will always return 0;
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * For a given layer, all further copies of this {@link Layer} object in new snapshots
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * will always return the same value.
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         */
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int getFlags() {
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mFlags;
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Layer makeCopy() {
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mBitmap != null) {
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return new Layer((Graphics2D) mGraphics.create(), mBitmap);
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /** sets an optional copy of the original content to be used during restore */
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        void setOriginalCopy(BufferedImage image) {
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mOriginalCopy = image;
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        BufferedImage getOriginalCopy() {
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mOriginalCopy;
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        void change() {
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mBitmap != null) {
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mBitmap.change();
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /**
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * Sets the clip for the graphics2D object associated with the layer.
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * This should be used over the normal Graphics2D setClip method.
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         *
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param clipShape the shape to use a the clip shape.
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         */
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        void setClip(Shape clipShape) {
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // because setClip is only guaranteed to work with rectangle shape,
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // first reset the clip to max and then intersect the current (empty)
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // clip with the shap.
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mGraphics.setClip(null);
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mGraphics.clip(clipShape);
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        /**
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * Clips the layer with the given shape. This performs an intersect between the current
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * clip shape and the given shape.
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         * @param shape the new clip shape.
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski         */
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        public void clip(Shape shape) {
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mGraphics.clip(shape);
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Creates the root snapshot associating it with a given bitmap.
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p>
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * called before the snapshot can be used to draw. Transform and clip operations are permitted
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * before.
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
208f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta     * @param bitmap the image to associate to the snapshot or null.
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the root snapshot
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        GcSnapshot snapshot = new GcSnapshot();
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (bitmap != null) {
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            snapshot.setBitmap(bitmap);
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return snapshot;
218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Saves the current state according to the given flags and returns the new current snapshot.
222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This is the equivalent of {@link Canvas#save(int)}
224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param flags the save flags.
226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the new snapshot
227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @see Canvas#save(int)
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public GcSnapshot save(int flags) {
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Saves the current state and creates a new layer, and returns the new current snapshot.
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param layerBounds the layer bounds
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param paint the Paint information used to blit the layer back into the layers underneath
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *          upon restore
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param flags the save flags.
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the new snapshot
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @see Canvas#saveLayer(RectF, Paint, int)
246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return new GcSnapshot(this, layerBounds, paint, flags);
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Creates the root snapshot.
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private GcSnapshot() {
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mPrevious = null;
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mFlags = 0;
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLocalLayer = null;
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLocalLayerPaint = null;
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLayerBounds = null;
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * into the main graphics when {@link #restore()} is called.
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param previous the previous snapshot head.
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param paint the Paint information used to blit the layer back into the layers underneath
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *          upon restore
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param flags the flags regarding what should be saved.
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert previous != null;
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mPrevious = previous;
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mFlags = flags;
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // make a copy of the current layers before adding the new one.
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // This keeps the same BufferedImage reference but creates new Graphics2D for this
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // snapshot.
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // It does not copy whatever original copy the layers have, as they will be done
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // only if the new layer doesn't clip drawing to itself.
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (Layer layer : mPrevious.mLayers) {
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLayers.add(layer.makeCopy());
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (layerBounds != null) {
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // get the current transform
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // transform the layerBounds with the current transform and stores it into a int rect
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            RectF rect2 = new RectF();
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mapRect(matrix, rect2, layerBounds);
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLayerBounds = new Rect();
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            rect2.round(mLayerBounds);
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // get the base layer (always at index 0)
298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Layer baseLayer = mLayers.get(0);
299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create the image for the layer
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            BufferedImage layerImage = new BufferedImage(
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    baseLayer.getImage().getWidth(),
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    baseLayer.getImage().getHeight(),
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            BufferedImage.TYPE_INT_ARGB :
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                BufferedImage.TYPE_INT_RGB);
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create a graphics for it so that drawing can be done.
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Graphics2D layerGraphics = layerImage.createGraphics();
310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // because this layer inherits the current context for transform and clip,
312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // set them to one from the base layer.
313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            layerGraphics.setTransform(currentMtx);
315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create a new layer for this new layer and add it to the list at the end.
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // set the clip on it.
320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Shape currentClip = baseLayer.getGraphics().getClip();
321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLocalLayer.setClip(currentClip);
322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // if the drawing is not clipped to the local layer only, we save the current content
324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // of all other layers. We are only interested in the part that will actually
325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // be drawn, so we create as small bitmaps as we can.
326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // This is so that we can erase the drawing that goes in the layers below that will
327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // be coming from the layer itself.
328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                int w = mLayerBounds.width();
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                int h = mLayerBounds.height();
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Layer layer = mLayers.get(i);
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Graphics2D graphics = image.createGraphics();
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    graphics.drawImage(layer.getImage(),
336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            0, 0, w, h,
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            mLayerBounds.left, mLayerBounds.top,
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                    mLayerBounds.right, mLayerBounds.bottom,
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            null);
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    graphics.dispose();
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    layer.setOriginalCopy(image);
342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLocalLayer = null;
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mLayerBounds = null;
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLocalLayerPaint  = paint;
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void dispose() {
353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (Layer layer : mLayers) {
354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            layer.getGraphics().dispose();
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mPrevious != null) {
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mPrevious.dispose();
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Restores the top {@link GcSnapshot}, and returns the next one.
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public GcSnapshot restore() {
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return doRestore();
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param saveCount the saveCount or -1 to only restore 1.
372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *
373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the new head of the Gc snapshot stack.
374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public GcSnapshot restoreTo(int saveCount) {
376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return doRestoreTo(size(), saveCount);
377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public int size() {
380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mPrevious != null) {
381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mPrevious.size() + 1;
382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return 1;
385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Link the snapshot to a Bitmap_Delegate.
389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This is only for the case where the snapshot was created with a null image when calling
391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * a previous snapshot.
393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * If any transform or clip information was set before, they are put into the Graphics object.
395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param bitmap the bitmap to link to.
396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void setBitmap(Bitmap_Delegate bitmap) {
398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // create a new Layer for the bitmap. This will be the base layer.
399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D graphics2D = bitmap.getImage().createGraphics();
400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Layer baseLayer = new Layer(graphics2D, bitmap);
401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Set the current transform and clip which can either come from mTransform/mClip if they
403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // were set when there was no bitmap/layers or from the current base layers if there is
404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // one already.
405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        graphics2D.setTransform(getTransform());
407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // reset mTransform in case there was one.
408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mTransform = null;
409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        baseLayer.setClip(getClip());
411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // reset mClip in case there was one.
412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mClip = null;
413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // replace whatever current layers we have with this.
415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLayers.clear();
416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        mLayers.add(baseLayer);
417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void translate(float dx, float dy) {
421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (Layer layer : mLayers) {
423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                layer.getGraphics().translate(dx, dy);
424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mTransform == null) {
427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mTransform = new AffineTransform();
428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mTransform.translate(dx, dy);
430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void rotate(double radians) {
434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (Layer layer : mLayers) {
436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                layer.getGraphics().rotate(radians);
437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mTransform == null) {
440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mTransform = new AffineTransform();
441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mTransform.rotate(radians);
443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void scale(float sx, float sy) {
447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (Layer layer : mLayers) {
449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                layer.getGraphics().scale(sx, sy);
450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mTransform == null) {
453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mTransform = new AffineTransform();
454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mTransform.scale(sx, sy);
456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public AffineTransform getTransform() {
460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // all graphics2D in the list have the same transform
462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mLayers.get(0).getGraphics().getTransform();
463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mTransform == null) {
465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mTransform = new AffineTransform();
466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mTransform;
468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void setTransform(AffineTransform transform) {
472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (Layer layer : mLayers) {
474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                layer.getGraphics().setTransform(transform);
475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mTransform == null) {
478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mTransform = new AffineTransform();
479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mTransform.setTransform(transform);
481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public boolean clip(Shape shape, int regionOp) {
485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Simple case of intersect with existing layers.
486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // Because Graphics2D#setClip works a bit peculiarly, we optimize
487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // the case of clipping by intersection, as it's supported natively.
488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            for (Layer layer : mLayers) {
490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                layer.clip(shape);
491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Shape currentClip = getClip();
494282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return currentClip != null && currentClip.getBounds().isEmpty() == false;
495282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Area area = null;
498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (regionOp == Region.Op.REPLACE.nativeInt) {
500282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            area = new Area(shape);
501282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
502282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
503282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
504282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
505282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert area != null;
506282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
507282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
508282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (area != null) {
509282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                for (Layer layer : mLayers) {
510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    layer.setClip(area);
511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Shape currentClip = getClip();
515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return currentClip != null && currentClip.getBounds().isEmpty() == false;
516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (area != null) {
518282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mClip = area;
519282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mClip = new Area();
521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mClip.getBounds().isEmpty() == false;
524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the current clip, or null if none have been setup.
533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public Shape getClip() {
535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() > 0) {
536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // they all have the same clip
537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mLayers.get(0).getGraphics().getClip();
538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mClip;
540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private GcSnapshot doRestoreTo(int size, int saveCount) {
544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (size <= saveCount) {
545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return this;
546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // restore the current one first.
549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        GcSnapshot previous = doRestore();
550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
551282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (size == saveCount + 1) { // this was the only one that needed restore.
552282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return previous;
553282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
554282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return previous.doRestoreTo(size - 1, saveCount);
555282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
559282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Executes the Drawable's draw method, with a null paint delegate.
560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
561282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Note that the method can be called several times if there are more than one active layer.
562282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void draw(Drawable drawable) {
564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Executes the Drawable's draw method.
569282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>
570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Note that the method can be called several times if there are more than one active layer.
571282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param compositeOnly whether the paint is used for composite only. This is typically
572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     *          the case for bitmaps.
573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param forceSrcMode if true, this overrides the composite to be SRC
574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            boolean forceSrcMode) {
577f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // of saveLayer(), but that doesn't mean there's no layer.
580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // mLayers however saves all the information we need (flags).
581282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mLayers.size() == 1) {
582282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // no layer, only base layer. easy case.
583f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
584282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
585282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // draw in all the layers until the layer save flags tells us to stop (ie drawing
586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // in that layer is limited to the layer itself.
587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int flags;
588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int i = mLayers.size() - 1;
589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
590282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            do {
591282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                Layer layer = mLayers.get(i);
592282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
593f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // then go to previous layer, only if there are any left, and its flags
596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // doesn't restrict drawing to the layer itself.
597282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                i--;
598282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                flags = layer.getFlags();
599282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
600282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
601282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
602282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
603282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
604f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            boolean compositeOnly, int forceMode) {
605282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D originalGraphics = layer.getGraphics();
606f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (paint == null) {
607f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
608f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    null /*paint*/, layer);
609f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        } else {
610f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            ColorFilter_Delegate filter = paint.getColorFilter();
611f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            if (filter == null || !filter.isSupported()) {
612f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                // get a Graphics2D object configured with the drawing parameters.
613f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
614f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                        compositeOnly, forceMode);
615f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                drawOnGraphics(configuredGraphics, drawable, paint, layer);
616f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                return;
617f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            }
618f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta
6198ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez            int x = 0;
6208ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez            int y = 0;
621f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            int width;
622f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            int height;
623f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            Rectangle clipBounds = originalGraphics.getClipBounds();
624f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            if (clipBounds != null) {
625f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                if (clipBounds.width == 0 || clipBounds.height == 0) {
626f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                    // Clip is 0 so no need to paint anything.
627f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                    return;
628f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                }
629f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                // If we have clipBounds available, use them as they will always be
630f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                // smaller than the full layer size.
6318ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                x = clipBounds.x;
6328ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                y = clipBounds.y;
633f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                width = clipBounds.width;
634f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                height = clipBounds.height;
635f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            } else {
636f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                width = layer.getImage().getWidth();
637f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez                height = layer.getImage().getHeight();
638f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez            }
639f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta
640f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            // Create a temporary image to which the color filter will be applied.
641f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            BufferedImage image = new BufferedImage(width, height,
642f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    BufferedImage.TYPE_INT_ARGB);
643f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
644f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            // Configure the Graphics2D object with drawing parameters and shader.
645f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            Graphics2D imageGraphics = createCustomGraphics(
646f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    imageBaseGraphics, paint, compositeOnly,
647f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    AlphaComposite.SRC_OVER);
648f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            // get a Graphics2D object configured with the drawing parameters, but no shader.
649f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
650f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    true /*compositeOnly*/, forceMode);
651f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            try {
652f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                // The main draw operation.
6538ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // We translate the operation to take into account that the rendering does not
6548ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // know about the clipping area.
6558ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                imageGraphics.translate(-x, -y);
656f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                drawable.draw(imageGraphics, paint);
657f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta
658f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                // Apply the color filter.
6598ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // Restore the original coordinates system and apply the filter only to the
6608ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // clipped area.
6618ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                imageGraphics.translate(x, y);
662f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                filter.applyFilter(imageGraphics, width, height);
663f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta
6648ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // Draw the tinted image on the main layer using as start point the clipping
6658ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                // upper left coordinates.
6668ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez                configuredGraphics.drawImage(image, x, y, null);
667f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                layer.change();
668f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            } finally {
669f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                // dispose Graphics2D objects
670f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                imageGraphics.dispose();
671f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                imageBaseGraphics.dispose();
672f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                configuredGraphics.dispose();
673f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            }
674f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        }
675f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    }
676282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
677f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
678f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            Layer layer) {
679282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        try {
680f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            drawable.draw(g, paint);
681282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            layer.change();
682282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } finally {
683f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            g.dispose();
684282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
685282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
686282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
687282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private GcSnapshot doRestore() {
688282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (mPrevious != null) {
689282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (mLocalLayer != null) {
690282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // prepare to blit the layers in which we have draw, in the layer beneath
691282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // them, starting with the top one (which is the current local layer).
692282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                int i = mLayers.size() - 1;
693282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                int flags;
694282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                do {
695282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Layer dstLayer = mLayers.get(i - 1);
696282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
697282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    restoreLayer(dstLayer);
698282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
699282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    flags = dstLayer.getFlags();
700282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    i--;
701282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
702282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
703282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
704282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // if this snapshot does not save everything, then set the previous snapshot
705282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // to this snapshot content
706282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
707282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // didn't save the matrix? set the current matrix on the previous snapshot
708282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
709282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                AffineTransform mtx = getTransform();
710282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                for (Layer layer : mPrevious.mLayers) {
711282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    layer.getGraphics().setTransform(mtx);
712282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
713282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
714282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
715282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // didn't save the clip? set the current clip on the previous snapshot
716282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
717282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                Shape clip = getClip();
718282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                for (Layer layer : mPrevious.mLayers) {
719282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    layer.setClip(clip);
720282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
721282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
722282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
723282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
724282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (Layer layer : mLayers) {
725282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            layer.getGraphics().dispose();
726282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
727282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
728282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return mPrevious;
729282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
730282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
731282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private void restoreLayer(Layer dstLayer) {
732282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
733282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D baseGfx = dstLayer.getImage().createGraphics();
734282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
735282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // if the layer contains an original copy this means the flags
736282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // didn't restrict drawing to the local layer and we need to make sure the
737282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // layer bounds in the layer beneath didn't receive any drawing.
738282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // so we use the originalCopy to erase the new drawings in there.
739282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        BufferedImage originalCopy = dstLayer.getOriginalCopy();
740282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (originalCopy != null) {
741282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Graphics2D g = (Graphics2D) baseGfx.create();
742282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            g.setComposite(AlphaComposite.Src);
743282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
744282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            g.drawImage(originalCopy,
745282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
746282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    0, 0, mLayerBounds.width(), mLayerBounds.height(),
747282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    null);
748282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            g.dispose();
749282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
750282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
751282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // now draw put the content of the local layer onto the layer,
752282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // using the paint information
753282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
754f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                true /*alphaOnly*/, 0 /*forceMode*/);
755282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
756282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        g.drawImage(mLocalLayer.getImage(),
757282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
758282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
759282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                null);
760282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        g.dispose();
761282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
762282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        baseGfx.dispose();
763282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
764282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
765282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
766282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
767282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
768282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
769282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
770f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            boolean compositeOnly, int forceMode) {
771282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // make new one graphics
772282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Graphics2D g = (Graphics2D) original.create();
773282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
774282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // configure it
775282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
776282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (paint.isAntiAliased()) {
777282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            g.setRenderingHint(
778282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
779282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            g.setRenderingHint(
780282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
781282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
782282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
783f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // set the shader first, as it'll replace the color if it can be used it.
784282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        boolean customShader = false;
785f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (!compositeOnly) {
786f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            customShader = setShader(g, paint);
787f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            // set the stroke
788f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            g.setStroke(paint.getJavaStroke());
789f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        }
790f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // set the composite.
791f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        setComposite(g, paint, compositeOnly || customShader, forceMode);
792282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
793f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        return g;
794f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    }
795f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta
796f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    private boolean setShader(Graphics2D g, Paint_Delegate paint) {
797f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        Shader_Delegate shaderDelegate = paint.getShader();
798f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (shaderDelegate != null) {
799f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            if (shaderDelegate.isSupported()) {
800f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
801f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                assert shaderPaint != null;
802f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                if (shaderPaint != null) {
803f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    g.setPaint(shaderPaint);
804f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    return true;
805282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
806f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            } else {
807f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
808f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                        shaderDelegate.getSupportMessage(),
809f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                        null /*throwable*/, null /*data*/);
810282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
811f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        }
812282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
813f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // if no shader, use the paint color
814f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
815282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
816f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        return false;
817f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    }
818282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
819f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta    private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
820f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            int forceMode) {
821282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // the alpha for the composite. Always opaque if the normal paint color is used since
822282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // it contains the alpha
823f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
824f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (forceMode != 0) {
825f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
826f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            return;
827f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        }
828f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
829f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (xfermodeDelegate != null) {
830f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            if (xfermodeDelegate.isSupported()) {
831f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                Composite composite = xfermodeDelegate.getComposite(alpha);
832f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                assert composite != null;
833f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                if (composite != null) {
834f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    g.setComposite(composite);
835f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    return;
836282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
837f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            } else {
838f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
839f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                        xfermodeDelegate.getSupportMessage(),
840f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                        null /*throwable*/, null /*data*/);
841282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
842282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
843f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // if there was no custom xfermode, but we have alpha (due to a shader and a non
844f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
845f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        // that will handle the alpha.
846f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        if (alpha != 0xFF) {
847f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta            g.setComposite(AlphaComposite.getInstance(
848f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta                    AlphaComposite.SRC_OVER, (float) alpha / 255.f));
849f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta        }
850282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
851282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
852282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
853282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // array with 4 corners
854282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float[] corners = new float[] {
855282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                src.left, src.top,
856282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                src.right, src.top,
857282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                src.right, src.bottom,
858282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                src.left, src.bottom,
859282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        };
860282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
861282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // apply the transform to them.
862282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        matrix.transform(corners, 0, corners, 0, 4);
863282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
864282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
865282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
866282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
867282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
868282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
869282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
870282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
871282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
872282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
873