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