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