GcSnapshot.java revision cfdc784b6cdcbbb2bf2ba4d53d9a9eb2c37278a3
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.layoutlib.bridge.impl;
18
19import android.graphics.Canvas;
20import android.graphics.Region;
21
22import java.awt.Graphics2D;
23import java.awt.Shape;
24import java.awt.geom.AffineTransform;
25import java.awt.geom.Area;
26import java.awt.geom.Rectangle2D;
27
28/**
29 * Class representing a graphics context snapshot, as well as a context stack as a linked list.
30 * <p>
31 * This is based on top of {@link Graphics2D} but can operate independently if none are available
32 * yet when setting transforms and clip information.
33 *
34 */
35public class GcSnapshot {
36
37    private final GcSnapshot mPrevious;
38    private final int mFlags;
39
40    private Graphics2D mGraphics2D = null;
41    /** temp transform in case transformation are set before a Graphics2D exists */
42    private AffineTransform mTransform = null;
43    /** temp clip in case clipping is set before a Graphics2D exists */
44    private Area mClip = null;
45
46    /**
47     * Creates a new {@link GcSnapshot} on top of another one.
48     * <p/>
49     * This is basically the equivalent of {@link Canvas#save(int)}
50     * @param previous the previous snapshot head.
51     * @param flags the flags regarding what should be saved.
52     */
53    public GcSnapshot(GcSnapshot previous, int flags) {
54        assert previous != null;
55        mPrevious = previous;
56        mFlags = flags;
57        mGraphics2D = (Graphics2D) previous.mGraphics2D.create();
58    }
59
60    /**
61     * Creates the root snapshot.
62     * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
63     */
64    public GcSnapshot() {
65        mPrevious = null;
66        mFlags = 0;
67    }
68
69    public void dispose() {
70        if (mGraphics2D != null) {
71            mGraphics2D.dispose();
72        }
73
74        if (mPrevious != null) {
75            mPrevious.dispose();
76        }
77    }
78
79    /**
80     * Restores the top {@link GcSnapshot}, and returns the next one.
81     */
82    public GcSnapshot restore() {
83        return doRestore();
84    }
85
86    /**
87     * Restores the {@link GcSnapshot} to <var>saveCount</var>.
88     * @param saveCount the saveCount or -1 to only restore 1.
89     *
90     * @return the new head of the Gc snapshot stack.
91     */
92    public GcSnapshot restoreTo(int saveCount) {
93        return doRestoreTo(size(), saveCount);
94    }
95
96    public int size() {
97        if (mPrevious != null) {
98            return mPrevious.size() + 1;
99        }
100
101        return 1;
102    }
103
104    /**
105     * Sets the Graphics2D object for this snapshot if it was created through {@link #GcSnapshot()}.
106     * If any transform or clip information was set before, they are put into the Graphics object.
107     * @param graphics2D the graphics object to set.
108     */
109    public void setGraphics2D(Graphics2D graphics2D) {
110        mGraphics2D = graphics2D;
111        if (mTransform != null) {
112            mGraphics2D.setTransform(mTransform);
113            mTransform = null;
114        }
115
116        if (mClip != null) {
117            mGraphics2D.setClip(mClip);
118            mClip = null;
119        }
120    }
121
122    /**
123     * Creates and return a copy of the current {@link Graphics2D}.
124     * @return a new {@link Graphics2D}.
125     */
126    public Graphics2D create() {
127        assert mGraphics2D != null;
128        return (Graphics2D) mGraphics2D.create();
129    }
130
131    public void translate(float dx, float dy) {
132        if (mGraphics2D != null) {
133            mGraphics2D.translate(dx, dy);
134        } else {
135            if (mTransform == null) {
136                mTransform = new AffineTransform();
137            }
138            mTransform.translate(dx, dy);
139        }
140    }
141
142    public void rotate(double radians) {
143        if (mGraphics2D != null) {
144            mGraphics2D.rotate(radians);
145        } else {
146            if (mTransform == null) {
147                mTransform = new AffineTransform();
148            }
149            mTransform.rotate(radians);
150        }
151    }
152
153    public void scale(float sx, float sy) {
154        if (mGraphics2D != null) {
155            mGraphics2D.scale(sx, sy);
156        } else {
157            if (mTransform == null) {
158                mTransform = new AffineTransform();
159            }
160            mTransform.scale(sx, sy);
161        }
162    }
163
164    public AffineTransform getTransform() {
165        if (mGraphics2D != null) {
166            return mGraphics2D.getTransform();
167        } else {
168            if (mTransform == null) {
169                mTransform = new AffineTransform();
170            }
171            return mTransform;
172        }
173    }
174
175    public void setTransform(AffineTransform transform) {
176        if (mGraphics2D != null) {
177            mGraphics2D.setTransform(transform);
178        } else {
179            if (mTransform == null) {
180                mTransform = new AffineTransform();
181            }
182            mTransform.setTransform(transform);
183        }
184    }
185
186    public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
187        if (mGraphics2D != null) {
188            if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
189                Area newClip = new Area(mGraphics2D.getClip());
190                newClip.subtract(new Area(
191                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
192                mGraphics2D.setClip(newClip);
193
194            } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
195                mGraphics2D.clipRect((int) left, (int) top,
196                        (int) (right - left), (int) (bottom - top));
197
198            } else if (regionOp == Region.Op.UNION.nativeInt) {
199                Area newClip = new Area(mGraphics2D.getClip());
200                newClip.add(new Area(
201                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
202                mGraphics2D.setClip(newClip);
203
204            } else if (regionOp == Region.Op.XOR.nativeInt) {
205                Area newClip = new Area(mGraphics2D.getClip());
206                newClip.exclusiveOr(new Area(
207                        new Rectangle2D.Float(left, top, right - left, bottom - top)));
208                mGraphics2D.setClip(newClip);
209
210            } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
211                Area newClip = new Area(
212                        new Rectangle2D.Float(left, top, right - left, bottom - top));
213                newClip.subtract(new Area(mGraphics2D.getClip()));
214                mGraphics2D.setClip(newClip);
215            } else if (regionOp == Region.Op.REPLACE.nativeInt) {
216                mGraphics2D.setClip((int) left, (int) top,
217                        (int) (right - left), (int) (bottom - top));
218            }
219
220            return mGraphics2D.getClip().getBounds().isEmpty() == false;
221        } else {
222            if (mClip == null) {
223                mClip = new Area();
224            }
225
226            if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
227                //FIXME
228            } else if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
229            } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
230            } else if (regionOp == Region.Op.UNION.nativeInt) {
231            } else if (regionOp == Region.Op.XOR.nativeInt) {
232            } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
233            } else if (regionOp == Region.Op.REPLACE.nativeInt) {
234            }
235
236            return mClip.getBounds().isEmpty() == false;
237        }
238    }
239
240    public Shape getClip() {
241        if (mGraphics2D != null) {
242            return mGraphics2D.getClip();
243        } else {
244            if (mClip == null) {
245                mClip = new Area();
246            }
247            return mClip;
248        }
249    }
250
251    private GcSnapshot doRestoreTo(int size, int saveCount) {
252        if (size <= saveCount) {
253            return this;
254        }
255
256        // restore the current one first.
257        GcSnapshot previous = doRestore();
258
259        if (size == saveCount + 1) { // this was the only one that needed restore.
260            return previous;
261        } else {
262            return previous.doRestoreTo(size - 1, saveCount);
263        }
264    }
265
266    private GcSnapshot doRestore() {
267        // if this snapshot does not save everything, then set the previous snapshot
268        // to this snapshot content
269        if (mPrevious != null) {
270            // didn't save the matrix? set the current matrix on the previous snapshot
271            if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
272                mPrevious.mGraphics2D.setTransform(getTransform());
273            }
274
275            // didn't save the clip? set the current clip on the previous snapshot
276            if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
277                mPrevious.mGraphics2D.setClip(mGraphics2D.getClip());
278            }
279        }
280
281        if (mGraphics2D != null) {
282            mGraphics2D.dispose();
283        }
284
285        return mPrevious;
286    }
287
288}
289