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