1cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet/*
2cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * Copyright (C) 2010 The Android Open Source Project
3cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet *
4cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * Licensed under the Apache License, Version 2.0 (the "License");
5cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * you may not use this file except in compliance with the License.
6cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * You may obtain a copy of the License at
7cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet *
8cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet *      http://www.apache.org/licenses/LICENSE-2.0
9cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet *
10cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * Unless required by applicable law or agreed to in writing, software
11cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * distributed under the License is distributed on an "AS IS" BASIS,
12cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * See the License for the specific language governing permissions and
14cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * limitations under the License.
15cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet */
16cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
17cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetpackage com.android.layoutlib.bridge.impl;
18cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
19918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohetimport com.android.ide.common.rendering.api.LayoutLog;
20d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport com.android.layoutlib.bridge.Bridge;
21d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
22b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohetimport android.graphics.Bitmap_Delegate;
23cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport android.graphics.Canvas;
24d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.Paint;
25d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.Paint_Delegate;
26d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.Rect;
27d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.RectF;
28cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport android.graphics.Region;
29b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohetimport android.graphics.Region_Delegate;
30d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.Shader_Delegate;
31d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport android.graphics.Xfermode_Delegate;
32cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
33d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.awt.AlphaComposite;
34d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.awt.Color;
35d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.awt.Composite;
36cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport java.awt.Graphics2D;
37d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.awt.RenderingHints;
38cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport java.awt.Shape;
39cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport java.awt.geom.AffineTransform;
40cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport java.awt.geom.Area;
41cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetimport java.awt.geom.Rectangle2D;
42d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.awt.image.BufferedImage;
43d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohetimport java.util.ArrayList;
44cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
45cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet/**
46cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * Class representing a graphics context snapshot, as well as a context stack as a linked list.
47cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * <p>
48cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * This is based on top of {@link Graphics2D} but can operate independently if none are available
49cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet * yet when setting transforms and clip information.
50d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet * <p>
51d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
52d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet * {@link #draw(Drawable, Paint_Delegate)}
53cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet *
5420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
5520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
5620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet * for each layer. Doing a save() will duplicate this list so that each graphics2D object
5720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
58cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet */
59cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohetpublic class GcSnapshot {
60cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
61cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private final GcSnapshot mPrevious;
62cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private final int mFlags;
63cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
6420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet    /** list of layers. The first item in the list is always the  */
65d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
66d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
67cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /** temp transform in case transformation are set before a Graphics2D exists */
68cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private AffineTransform mTransform = null;
69cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /** temp clip in case clipping is set before a Graphics2D exists */
70cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private Area mClip = null;
71cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
72d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    // local layer data
7320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet    /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
7420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     * If this is null, this does not mean there's no layer, just that the snapshot is not the
7520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     * one that created the layer.
7620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     * @see #getLayerSnapshot()
7720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     */
78d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private final Layer mLocalLayer;
79d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private final Paint_Delegate mLocalLayerPaint;
8020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet    private final Rect mLayerBounds;
81d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
82d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    public interface Drawable {
83d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        void draw(Graphics2D graphics, Paint_Delegate paint);
84d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
85d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
86d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
8720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     * Class containing information about a layer.
8820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     *
8920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet     * This contains graphics, bitmap and layer information.
90d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
91d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private static class Layer {
92d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        private final Graphics2D mGraphics;
93b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        private final Bitmap_Delegate mBitmap;
94d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        private final BufferedImage mImage;
9520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        /** the flags that were used to configure the layer. This is never changed, and passed
9620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * as is when {@link #makeCopy()} is called */
9720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        private final int mFlags;
9820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        /** the original content of the layer when the next object was created. This is not
9920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
10020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * (depending on its flags) */
101d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        private BufferedImage mOriginalCopy;
102d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
103b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        /**
10420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * Creates a layer with a graphics and a bitmap. This is only used to create
10520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * the base layer.
106b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         *
107b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         * @param graphics the graphics
108b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         * @param bitmap the bitmap
109b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         */
110b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
111b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            mGraphics = graphics;
112b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            mBitmap = bitmap;
113b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            mImage = mBitmap.getImage();
11420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            mFlags = 0;
115b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        }
116b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet
117b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        /**
118b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         * Creates a layer with a graphics and an image. If the image belongs to a
11920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * {@link Bitmap_Delegate} (case of the base layer), then
12020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
121b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         *
12220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * @param graphics the graphics the new graphics for this layer
12320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * @param image the image the image from which the graphics came
12420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * @param flags the flags that were used to save this layer
125b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet         */
12620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        Layer(Graphics2D graphics, BufferedImage image, int flags) {
127d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mGraphics = graphics;
128b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            mBitmap = null;
129d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mImage = image;
13020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            mFlags = flags;
131d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
132d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
133d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        /** The Graphics2D, guaranteed to be non null */
134d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        Graphics2D getGraphics() {
135d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            return mGraphics;
136d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
137d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
138d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        /** The BufferedImage, guaranteed to be non null */
139d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        BufferedImage getImage() {
140d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            return mImage;
141d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
142d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
14320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        /** Returns the layer save flags. This is only valid for additional layers.
14420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * For the base layer this will always return 0;
14520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * For a given layer, all further copies of this {@link Layer} object in new snapshots
14620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         * will always return the same value.
14720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet         */
14820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        int getFlags() {
14920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            return mFlags;
15020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        }
15120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
152d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        Layer makeCopy() {
153b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            if (mBitmap != null) {
154b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                return new Layer((Graphics2D) mGraphics.create(), mBitmap);
155b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            }
156b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet
15720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
158d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
159d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
160d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        /** sets an optional copy of the original content to be used during restore */
161d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        void setOriginalCopy(BufferedImage image) {
162d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mOriginalCopy = image;
163d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
164d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
165d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        BufferedImage getOriginalCopy() {
166d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            return mOriginalCopy;
167d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
168b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet
169b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        void change() {
170b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            if (mBitmap != null) {
171b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                mBitmap.change();
172b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            }
173b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        }
174a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
175a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        /**
176a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * Sets the clip for the graphics2D object associated with the layer.
177a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * This should be used over the normal Graphics2D setClip method.
178a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         *
179a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * @param clipShape the shape to use a the clip shape.
180a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         */
181a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        void setClip(Shape clipShape) {
182a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            // because setClip is only guaranteed to work with rectangle shape,
183a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            // first reset the clip to max and then intersect the current (empty)
184a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            // clip with the shap.
185a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            mGraphics.setClip(null);
186a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            mGraphics.clip(clipShape);
187a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        }
188a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
189a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        /**
190a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * Clips the layer with the given shape. This performs an intersect between the current
191a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * clip shape and the given shape.
192a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         * @param shape the new clip shape.
193a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet         */
194a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        public void clip(Shape shape) {
195a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            mGraphics.clip(shape);
196a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        }
197d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
198d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
199cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /**
200b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * Creates the root snapshot associating it with a given bitmap.
201d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p>
202b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
203d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * called before the snapshot can be used to draw. Transform and clip operations are permitted
204d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * before.
205d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
206d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param image the image to associate to the snapshot or null.
207d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @return the root snapshot
208d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
209b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet    public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
210d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        GcSnapshot snapshot = new GcSnapshot();
211b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        if (bitmap != null) {
212b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            snapshot.setBitmap(bitmap);
213d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
214d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
215d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        return snapshot;
216d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
217d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
218d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
219d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Saves the current state according to the given flags and returns the new current snapshot.
220cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * <p/>
221d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * This is the equivalent of {@link Canvas#save(int)}
222d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
223d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param flags the save flags.
224d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @return the new snapshot
225d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
226d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @see Canvas#save(int)
227cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     */
228d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    public GcSnapshot save(int flags) {
229d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
230d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
231d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
232d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
233d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Saves the current state and creates a new layer, and returns the new current snapshot.
234d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>
235d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
236d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
237d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param layerBounds the layer bounds
238d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param paint the Paint information used to blit the layer back into the layers underneath
239d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *          upon restore
240d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param flags the save flags.
241d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @return the new snapshot
242d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
243d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @see Canvas#saveLayer(RectF, Paint, int)
244d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
245d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
246d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        return new GcSnapshot(this, layerBounds, paint, flags);
247cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
248cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
249cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /**
250cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * Creates the root snapshot.
251cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
252cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     */
253d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private GcSnapshot() {
254cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        mPrevious = null;
255cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        mFlags = 0;
256d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        mLocalLayer = null;
257d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        mLocalLayerPaint = null;
25820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        mLayerBounds = null;
259d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
260d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
261d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
262d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
263d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * into the main graphics when {@link #restore()} is called.
264d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *
265d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param previous the previous snapshot head.
266d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
267d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param paint the Paint information used to blit the layer back into the layers underneath
268d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *          upon restore
269d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param flags the flags regarding what should be saved.
270d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
271d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
272d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        assert previous != null;
273d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        mPrevious = previous;
274d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        mFlags = flags;
275d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
276d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // make a copy of the current layers before adding the new one.
277d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // This keeps the same BufferedImage reference but creates new Graphics2D for this
278d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // snapshot.
279d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // It does not copy whatever original copy the layers have, as they will be done
280d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // only if the new layer doesn't clip drawing to itself.
281d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        for (Layer layer : mPrevious.mLayers) {
282d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mLayers.add(layer.makeCopy());
283d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
284d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
285d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (layerBounds != null) {
286d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // get the current transform
287d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
288d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
28920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            // transform the layerBounds with the current transform and stores it into a int rect
290d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            RectF rect2 = new RectF();
291d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mapRect(matrix, rect2, layerBounds);
29220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            mLayerBounds = new Rect();
29320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            rect2.round(mLayerBounds);
294d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
295d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // get the base layer (always at index 0)
296d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            Layer baseLayer = mLayers.get(0);
297d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
298d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // create the image for the layer
299d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            BufferedImage layerImage = new BufferedImage(
300d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    baseLayer.getImage().getWidth(),
301d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    baseLayer.getImage().getHeight(),
30220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
30320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                            BufferedImage.TYPE_INT_ARGB :
30420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                                BufferedImage.TYPE_INT_RGB);
305d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
306d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // create a graphics for it so that drawing can be done.
307d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            Graphics2D layerGraphics = layerImage.createGraphics();
308d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
309d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // because this layer inherits the current context for transform and clip,
310d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // set them to one from the base layer.
311d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
312d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            layerGraphics.setTransform(currentMtx);
313d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
314d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // create a new layer for this new layer and add it to the list at the end.
31520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
316d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
317a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            // set the clip on it.
318a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            Shape currentClip = baseLayer.getGraphics().getClip();
319a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            mLocalLayer.setClip(currentClip);
320a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
321d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // if the drawing is not clipped to the local layer only, we save the current content
322d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // of all other layers. We are only interested in the part that will actually
323d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // be drawn, so we create as small bitmaps as we can.
324d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // This is so that we can erase the drawing that goes in the layers below that will
325d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // be coming from the layer itself.
326d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
32720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                int w = mLayerBounds.width();
32820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                int h = mLayerBounds.height();
329d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
330d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    Layer layer = mLayers.get(i);
331d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
332d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    Graphics2D graphics = image.createGraphics();
333d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    graphics.drawImage(layer.getImage(),
334d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                            0, 0, w, h,
33520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                            mLayerBounds.left, mLayerBounds.top,
33620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                                    mLayerBounds.right, mLayerBounds.bottom,
337d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                            null);
338d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    graphics.dispose();
339d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    layer.setOriginalCopy(image);
340d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
341d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
342d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        } else {
343d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            mLocalLayer = null;
34420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            mLayerBounds = null;
345d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
346d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
347d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        mLocalLayerPaint  = paint;
348cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
349cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
350cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public void dispose() {
351d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        for (Layer layer : mLayers) {
352d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            layer.getGraphics().dispose();
353cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
354cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
355cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        if (mPrevious != null) {
356cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            mPrevious.dispose();
357cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
358cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
359cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
360cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /**
361cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * Restores the top {@link GcSnapshot}, and returns the next one.
362cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     */
363cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public GcSnapshot restore() {
364cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        return doRestore();
365cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
366cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
367cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /**
368cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
369cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * @param saveCount the saveCount or -1 to only restore 1.
370cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     *
371cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * @return the new head of the Gc snapshot stack.
372cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     */
373cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public GcSnapshot restoreTo(int saveCount) {
374cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        return doRestoreTo(size(), saveCount);
375cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
376cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
377cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public int size() {
378cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        if (mPrevious != null) {
379cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return mPrevious.size() + 1;
380cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
381cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
382cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        return 1;
383cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
384cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
385cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    /**
386b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * Link the snapshot to a Bitmap_Delegate.
387d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>
388d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * This is only for the case where the snapshot was created with a null image when calling
389b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
390d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * a previous snapshot.
391d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>
392cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     * If any transform or clip information was set before, they are put into the Graphics object.
393b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * @param bitmap the bitmap to link to.
394cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet     */
395b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet    public void setBitmap(Bitmap_Delegate bitmap) {
396a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        // create a new Layer for the bitmap. This will be the base layer.
397b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        Graphics2D graphics2D = bitmap.getImage().createGraphics();
398a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        Layer baseLayer = new Layer(graphics2D, bitmap);
399a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
4000831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // Set the current transform and clip which can either come from mTransform/mClip if they
4010831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // were set when there was no bitmap/layers or from the current base layers if there is
4020831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // one already.
403a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
4040831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        graphics2D.setTransform(getTransform());
4050831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // reset mTransform in case there was one.
4060831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        mTransform = null;
407a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
4080831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        baseLayer.setClip(getClip());
4090831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // reset mClip in case there was one.
4100831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        mClip = null;
4110831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet
4120831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        // replace whatever current layers we have with this.
4130831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        mLayers.clear();
4140831b3fae504e8fa94e6b1cc0d4e6c3fccaef231Xavier Ducrohet        mLayers.add(baseLayer);
415cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
416cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
417cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
418cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public void translate(float dx, float dy) {
419d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
420d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            for (Layer layer : mLayers) {
421d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                layer.getGraphics().translate(dx, dy);
422d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
423cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
424cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            if (mTransform == null) {
425cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mTransform = new AffineTransform();
426cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
427cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            mTransform.translate(dx, dy);
428cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
429cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
430cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
431cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public void rotate(double radians) {
432d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
433d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            for (Layer layer : mLayers) {
434d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                layer.getGraphics().rotate(radians);
435d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
436cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
437cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            if (mTransform == null) {
438cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mTransform = new AffineTransform();
439cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
440cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            mTransform.rotate(radians);
441cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
442cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
443cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
444cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public void scale(float sx, float sy) {
445d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
446d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            for (Layer layer : mLayers) {
447d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                layer.getGraphics().scale(sx, sy);
448d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
449cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
450cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            if (mTransform == null) {
451cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mTransform = new AffineTransform();
452cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
453cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            mTransform.scale(sx, sy);
454cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
455cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
456cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
457cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public AffineTransform getTransform() {
458d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
459d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // all graphics2D in the list have the same transform
460d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            return mLayers.get(0).getGraphics().getTransform();
461cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
462cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            if (mTransform == null) {
463cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mTransform = new AffineTransform();
464cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
465cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return mTransform;
466cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
467cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
468cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
469cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public void setTransform(AffineTransform transform) {
470d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
471d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            for (Layer layer : mLayers) {
472d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                layer.getGraphics().setTransform(transform);
473d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
474cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
475cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            if (mTransform == null) {
476cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mTransform = new AffineTransform();
477cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
478cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            mTransform.setTransform(transform);
479cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
480cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
481cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
482b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet    public boolean clip(Shape shape, int regionOp) {
483a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        // Simple case of intersect with existing layers.
484a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        // Because Graphics2D#setClip works a bit peculiarly, we optimize
485a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        // the case of clipping by intersection, as it's supported natively.
486a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
487a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            for (Layer layer : mLayers) {
488a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet                layer.clip(shape);
489a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            }
490a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
491a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            Shape currentClip = getClip();
492a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            return currentClip != null && currentClip.getBounds().isEmpty() == false;
493a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet        }
494a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
495b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        Area area = null;
496a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet
497b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        if (regionOp == Region.Op.REPLACE.nativeInt) {
498b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            area = new Area(shape);
499b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        } else {
500b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
501b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        }
502cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
503b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        assert area != null;
504cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
505b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        if (mLayers.size() > 0) {
506b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            if (area != null) {
507d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                for (Layer layer : mLayers) {
508a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet                    layer.setClip(area);
509d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
510d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
511d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
512a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            Shape currentClip = getClip();
513a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet            return currentClip != null && currentClip.getBounds().isEmpty() == false;
514cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
515b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            if (area != null) {
516b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet                mClip = area;
517b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            } else {
518cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet                mClip = new Area();
519cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
520cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
521cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return mClip.getBounds().isEmpty() == false;
522cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
523cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
524cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
525b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
526b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet        return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
527b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet    }
528b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet
529a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet    /**
530a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet     * Returns the current clip, or null if none have been setup.
531a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet     */
532cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    public Shape getClip() {
533d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (mLayers.size() > 0) {
534d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // they all have the same clip
535d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            return mLayers.get(0).getGraphics().getClip();
536cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
537cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return mClip;
538cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
539cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
540cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
541cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private GcSnapshot doRestoreTo(int size, int saveCount) {
542cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        if (size <= saveCount) {
543cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return this;
544cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
545cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
546cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        // restore the current one first.
547cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        GcSnapshot previous = doRestore();
548cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
549cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        if (size == saveCount + 1) { // this was the only one that needed restore.
550cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return previous;
551cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        } else {
552cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            return previous.doRestoreTo(size - 1, saveCount);
553cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
554cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
555cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
556d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
557d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Executes the Drawable's draw method, with a null paint delegate.
558d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>
559d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Note that the method can be called several times if there are more than one active layer.
560d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param drawable
561d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
562d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    public void draw(Drawable drawable) {
563b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
564d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
565d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
566d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
567d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Executes the Drawable's draw method.
568d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>
569d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Note that the method can be called several times if there are more than one active layer.
570d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param drawable
571d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param paint
572d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * @param compositeOnly whether the paint is used for composite only. This is typically
573d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     *          the case for bitmaps.
574b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet     * @param forceSrcMode if true, this overrides the composite to be SRC
575d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
576b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet    public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
577b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            boolean forceSrcMode) {
57820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
57920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // of saveLayer(), but that doesn't mean there's no layer.
58020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // mLayers however saves all the information we need (flags).
58120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        if (mLayers.size() == 1) {
58220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            // no layer, only base layer. easy case.
58320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
584d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        } else {
58520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            // draw in all the layers until the layer save flags tells us to stop (ie drawing
58620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            // in that layer is limited to the layer itself.
58720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            int flags;
58820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            int i = mLayers.size() - 1;
58920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
59020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            do {
59120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                Layer layer = mLayers.get(i);
59220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
593b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
59420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
59520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                // then go to previous layer, only if there are any left, and its flags
59620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                // doesn't restrict drawing to the layer itself.
59720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                i--;
59820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                flags = layer.getFlags();
59920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
600d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
601d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
602d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
603d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
604b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            boolean compositeOnly, boolean forceSrcMode) {
605d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        Graphics2D originalGraphics = layer.getGraphics();
606d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // get a Graphics2D object configured with the drawing parameters.
607d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        Graphics2D configuredGraphics2D =
608d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            paint != null ?
609b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                    createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
610d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                        (Graphics2D) originalGraphics.create();
611d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
612d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        try {
613d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            drawable.draw(configuredGraphics2D, paint);
614b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            layer.change();
615d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        } finally {
616d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // dispose Graphics2D object
617d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            configuredGraphics2D.dispose();
618d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
619d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
620d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
621cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    private GcSnapshot doRestore() {
622cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        if (mPrevious != null) {
623d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            if (mLocalLayer != null) {
62420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                // prepare to blit the layers in which we have draw, in the layer beneath
62520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                // them, starting with the top one (which is the current local layer).
62620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                int i = mLayers.size() - 1;
62720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                int flags;
62820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                do {
62920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    Layer dstLayer = mLayers.get(i - 1);
63020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
63120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    restoreLayer(dstLayer);
63220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
63320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    flags = dstLayer.getFlags();
63420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    i--;
63520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
636d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
637d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
638d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // if this snapshot does not save everything, then set the previous snapshot
639d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // to this snapshot content
640d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
641cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            // didn't save the matrix? set the current matrix on the previous snapshot
64220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
643d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                AffineTransform mtx = getTransform();
644d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                for (Layer layer : mPrevious.mLayers) {
645d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    layer.getGraphics().setTransform(mtx);
646d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
647cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
648cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
649cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            // didn't save the clip? set the current clip on the previous snapshot
65020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
651d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                Shape clip = getClip();
652d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                for (Layer layer : mPrevious.mLayers) {
653a7cac5e0542779cadf0f5ccf71584e4b4425f7a6Xavier Ducrohet                    layer.setClip(clip);
654d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
655cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet            }
656cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
657cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
658d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        for (Layer layer : mLayers) {
659d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            layer.getGraphics().dispose();
660cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        }
661cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
662cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet        return mPrevious;
663cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet    }
664cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet
66520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet    private void restoreLayer(Layer dstLayer) {
66620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
66720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        Graphics2D baseGfx = dstLayer.getImage().createGraphics();
66820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
66920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // if the layer contains an original copy this means the flags
67020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // didn't restrict drawing to the local layer and we need to make sure the
67120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // layer bounds in the layer beneath didn't receive any drawing.
67220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // so we use the originalCopy to erase the new drawings in there.
67320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        BufferedImage originalCopy = dstLayer.getOriginalCopy();
67420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        if (originalCopy != null) {
67520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            Graphics2D g = (Graphics2D) baseGfx.create();
67620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            g.setComposite(AlphaComposite.Src);
67720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
67820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            g.drawImage(originalCopy,
67920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
68020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    0, 0, mLayerBounds.width(), mLayerBounds.height(),
68120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                    null);
68220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet            g.dispose();
68320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        }
68420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
68520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // now draw put the content of the local layer onto the layer,
68620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        // using the paint information
68720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
68820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                true /*alphaOnly*/, false /*forceSrcMode*/);
68920805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
69020805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        g.drawImage(mLocalLayer.getImage(),
69120805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
69220805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
69320805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet                null);
69420805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        g.dispose();
69520805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
69620805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet        baseGfx.dispose();
69720805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet    }
69820805343296eef04081fee82fd04547f51225fe3Xavier Ducrohet
699d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    /**
700d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
701d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
702d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet     */
703d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
704b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            boolean compositeOnly, boolean forceSrcMode) {
705d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // make new one graphics
706d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        Graphics2D g = (Graphics2D) original.create();
707d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
708d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // configure it
709d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
710d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (paint.isAntiAliased()) {
711d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            g.setRenderingHint(
712d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
713d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            g.setRenderingHint(
714d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
715d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
716d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
717d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        boolean customShader = false;
718d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
719d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // get the shader first, as it'll replace the color if it can be used it.
720d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        if (compositeOnly == false) {
721d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            Shader_Delegate shaderDelegate = paint.getShader();
722d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            if (shaderDelegate != null) {
723d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                if (shaderDelegate.isSupported()) {
724d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
725d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    assert shaderPaint != null;
726d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    if (shaderPaint != null) {
727d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                        g.setPaint(shaderPaint);
728d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                        customShader = true;
729d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    }
730d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                } else {
731918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohet                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
732d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                            shaderDelegate.getSupportMessage(),
73351a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                            null /*throwable*/, null /*data*/);
734d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
735d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
736d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
737d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            // if no shader, use the paint color
738d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            if (customShader == false) {
739d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
740d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
741d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
742b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            // set the stroke
743b44b43b1579486ff7ecd0f7528f17711acdeae98Xavier Ducrohet            g.setStroke(paint.getJavaStroke());
744d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
745d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
746d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // the alpha for the composite. Always opaque if the normal paint color is used since
747d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // it contains the alpha
748d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
749d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
750b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        if (forceSrcMode) {
751b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            g.setComposite(AlphaComposite.getInstance(
752b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                    AlphaComposite.SRC, (float) alpha / 255.f));
753b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet        } else {
754b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            boolean customXfermode = false;
755d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
756d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet            if (xfermodeDelegate != null) {
757d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                if (xfermodeDelegate.isSupported()) {
758d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    Composite composite = xfermodeDelegate.getComposite(alpha);
759d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    assert composite != null;
760d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                    if (composite != null) {
761d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                        g.setComposite(composite);
762d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                        customXfermode = true;
763d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                    }
764d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                } else {
765918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohet                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
766d43909c7503e11eb335a452d296a10804bb01fd6Xavier Ducrohet                            xfermodeDelegate.getSupportMessage(),
76751a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                            null /*throwable*/, null /*data*/);
768d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                }
769d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet            }
770d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
771b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            // if there was no custom xfermode, but we have alpha (due to a shader and a non
772b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
773b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            // that will handle the alpha.
774b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            if (customXfermode == false && alpha != 0xFF) {
775b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                g.setComposite(AlphaComposite.getInstance(
776b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet                        AlphaComposite.SRC_OVER, (float) alpha / 255.f));
777b1da1afa7418960b650780250bbd34c81af61aa3Xavier Ducrohet            }
778d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        }
779d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
780d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        return g;
781d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
782d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
783d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
784d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // array with 4 corners
785d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        float[] corners = new float[] {
786d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                src.left, src.top,
787d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                src.right, src.top,
788d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                src.right, src.bottom,
789d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet                src.left, src.bottom,
790d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        };
791d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
792d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // apply the transform to them.
793d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        matrix.transform(corners, 0, corners, 0, 4);
794d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
795d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        // now put the result in the rect. We take the min/max of Xs and min/max of Ys
796d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
797d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
798d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
799d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
800d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet        dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
801d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet    }
802d38e776a3cc8cb53945cbebafbe6f6c2e3501fa5Xavier Ducrohet
803cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3Xavier Ducrohet}
804