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; 27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Rect; 28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.RectF; 29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Region; 30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Region_Delegate; 31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Shader_Delegate; 32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Xfermode_Delegate; 33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.AlphaComposite; 35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Color; 36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Composite; 37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Graphics2D; 38f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perezimport java.awt.Rectangle; 39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.RenderingHints; 40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.Shape; 41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.AffineTransform; 42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.Area; 43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.geom.Rectangle2D; 44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.awt.image.BufferedImage; 45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.ArrayList; 46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/** 48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Class representing a graphics context snapshot, as well as a context stack as a linked list. 49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p> 50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is based on top of {@link Graphics2D} but can operate independently if none are available 51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * yet when setting transforms and clip information. 52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p> 53f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and 54f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta * {@link #draw(Drawable)} 55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through 57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer} 58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * for each layer. Doing a save() will duplicate this list so that each graphics2D object 59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * ({@link Layer#getGraphics()}) is configured only for the new snapshot. 60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic class GcSnapshot { 62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final GcSnapshot mPrevious; 64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final int mFlags; 65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** list of layers. The first item in the list is always the */ 67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final ArrayList<Layer> mLayers = new ArrayList<Layer>(); 68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** temp transform in case transformation are set before a Graphics2D exists */ 70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private AffineTransform mTransform = null; 71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** temp clip in case clipping is set before a Graphics2D exists */ 72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private Area mClip = null; 73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // local layer data 75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}. 76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * If this is null, this does not mean there's no layer, just that the snapshot is not the 77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * one that created the layer. 78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @see #getLayerSnapshot() 79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final Layer mLocalLayer; 81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final Paint_Delegate mLocalLayerPaint; 82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final Rect mLayerBounds; 83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public interface Drawable { 85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski void draw(Graphics2D graphics, Paint_Delegate paint); 86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Class containing information about a layer. 90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This contains graphics, bitmap and layer information. 92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private static class Layer { 94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final Graphics2D mGraphics; 95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final Bitmap_Delegate mBitmap; 96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final BufferedImage mImage; 97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** the flags that were used to configure the layer. This is never changed, and passed 98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * as is when {@link #makeCopy()} is called */ 99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private final int mFlags; 100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** the original content of the layer when the next object was created. This is not 101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * passed in {@link #makeCopy()} and instead is recreated when a new layer is added 102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * (depending on its flags) */ 103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private BufferedImage mOriginalCopy; 104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates a layer with a graphics and a bitmap. This is only used to create 107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * the base layer. 108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param graphics the graphics 110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param bitmap the bitmap 111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer(Graphics2D graphics, Bitmap_Delegate bitmap) { 113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mGraphics = graphics; 114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mBitmap = bitmap; 115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mImage = mBitmap.getImage(); 116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mFlags = 0; 117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates a layer with a graphics and an image. If the image belongs to a 121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * {@link Bitmap_Delegate} (case of the base layer), then 122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used. 123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param graphics the graphics the new graphics for this layer 125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param image the image the image from which the graphics came 126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param flags the flags that were used to save this layer 127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer(Graphics2D graphics, BufferedImage image, int flags) { 129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mGraphics = graphics; 130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mBitmap = null; 131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mImage = image; 132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mFlags = flags; 133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** The Graphics2D, guaranteed to be non null */ 136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D getGraphics() { 137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mGraphics; 138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** The BufferedImage, guaranteed to be non null */ 141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage getImage() { 142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mImage; 143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** Returns the layer save flags. This is only valid for additional layers. 146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * For the base layer this will always return 0; 147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * For a given layer, all further copies of this {@link Layer} object in new snapshots 148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * will always return the same value. 149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int getFlags() { 151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mFlags; 152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer makeCopy() { 155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mBitmap != null) { 156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return new Layer((Graphics2D) mGraphics.create(), mBitmap); 157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags); 160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** sets an optional copy of the original content to be used during restore */ 163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski void setOriginalCopy(BufferedImage image) { 164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mOriginalCopy = image; 165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage getOriginalCopy() { 168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mOriginalCopy; 169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski void change() { 172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mBitmap != null) { 173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mBitmap.change(); 174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Sets the clip for the graphics2D object associated with the layer. 179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This should be used over the normal Graphics2D setClip method. 180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param clipShape the shape to use a the clip shape. 182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski void setClip(Shape clipShape) { 184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // because setClip is only guaranteed to work with rectangle shape, 185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // first reset the clip to max and then intersect the current (empty) 186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // clip with the shap. 187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mGraphics.setClip(null); 188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mGraphics.clip(clipShape); 189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Clips the layer with the given shape. This performs an intersect between the current 193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * clip shape and the given shape. 194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param shape the new clip shape. 195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void clip(Shape shape) { 197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mGraphics.clip(shape); 198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates the root snapshot associating it with a given bitmap. 203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p> 204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be 205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * called before the snapshot can be used to draw. Transform and clip operations are permitted 206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * before. 207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 208f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta * @param bitmap the image to associate to the snapshot or null. 209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @return the root snapshot 210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) { 212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski GcSnapshot snapshot = new GcSnapshot(); 213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (bitmap != null) { 214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski snapshot.setBitmap(bitmap); 215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return snapshot; 218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Saves the current state according to the given flags and returns the new current snapshot. 222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is the equivalent of {@link Canvas#save(int)} 224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param flags the save flags. 226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @return the new snapshot 227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @see Canvas#save(int) 229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public GcSnapshot save(int flags) { 231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags); 232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Saves the current state and creates a new layer, and returns the new current snapshot. 236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)} 238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param layerBounds the layer bounds 240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param paint the Paint information used to blit the layer back into the layers underneath 241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * upon restore 242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param flags the save flags. 243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @return the new snapshot 244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @see Canvas#saveLayer(RectF, Paint, int) 246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) { 248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return new GcSnapshot(this, layerBounds, paint, flags); 249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates the root snapshot. 253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible. 254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private GcSnapshot() { 256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mPrevious = null; 257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mFlags = 0; 258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLocalLayer = null; 259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLocalLayerPaint = null; 260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds = null; 261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored 265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * into the main graphics when {@link #restore()} is called. 266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param previous the previous snapshot head. 268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param layerBounds the region of the layer. Optional, if null, this is a normal save() 269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param paint the Paint information used to blit the layer back into the layers underneath 270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * upon restore 271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param flags the flags regarding what should be saved. 272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) { 274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assert previous != null; 275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mPrevious = previous; 276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mFlags = flags; 277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // make a copy of the current layers before adding the new one. 279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // This keeps the same BufferedImage reference but creates new Graphics2D for this 280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // snapshot. 281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // It does not copy whatever original copy the layers have, as they will be done 282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // only if the new layer doesn't clip drawing to itself. 283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mPrevious.mLayers) { 284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayers.add(layer.makeCopy()); 285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (layerBounds != null) { 288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // get the current transform 289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski AffineTransform matrix = mLayers.get(0).getGraphics().getTransform(); 290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // transform the layerBounds with the current transform and stores it into a int rect 292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski RectF rect2 = new RectF(); 293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mapRect(matrix, rect2, layerBounds); 294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds = new Rect(); 295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski rect2.round(mLayerBounds); 296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // get the base layer (always at index 0) 298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer baseLayer = mLayers.get(0); 299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // create the image for the layer 301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage layerImage = new BufferedImage( 302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski baseLayer.getImage().getWidth(), 303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski baseLayer.getImage().getHeight(), 304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ? 305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage.TYPE_INT_ARGB : 306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage.TYPE_INT_RGB); 307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // create a graphics for it so that drawing can be done. 309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D layerGraphics = layerImage.createGraphics(); 310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // because this layer inherits the current context for transform and clip, 312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // set them to one from the base layer. 313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski AffineTransform currentMtx = baseLayer.getGraphics().getTransform(); 314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layerGraphics.setTransform(currentMtx); 315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // create a new layer for this new layer and add it to the list at the end. 317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags)); 318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // set the clip on it. 320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Shape currentClip = baseLayer.getGraphics().getClip(); 321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLocalLayer.setClip(currentClip); 322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // if the drawing is not clipped to the local layer only, we save the current content 324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // of all other layers. We are only interested in the part that will actually 325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // be drawn, so we create as small bitmaps as we can. 326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // This is so that we can erase the drawing that goes in the layers below that will 327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // be coming from the layer itself. 328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) { 329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int w = mLayerBounds.width(); 330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int h = mLayerBounds.height(); 331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (int i = 0 ; i < mLayers.size() - 1 ; i++) { 332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer layer = mLayers.get(i); 333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D graphics = image.createGraphics(); 335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski graphics.drawImage(layer.getImage(), 336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 0, 0, w, h, 337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds.left, mLayerBounds.top, 338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds.right, mLayerBounds.bottom, 339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski null); 340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski graphics.dispose(); 341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.setOriginalCopy(image); 342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLocalLayer = null; 346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds = null; 347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLocalLayerPaint = paint; 350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void dispose() { 353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().dispose(); 355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mPrevious != null) { 358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mPrevious.dispose(); 359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Restores the top {@link GcSnapshot}, and returns the next one. 364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public GcSnapshot restore() { 366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return doRestore(); 367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Restores the {@link GcSnapshot} to <var>saveCount</var>. 371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param saveCount the saveCount or -1 to only restore 1. 372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * 373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @return the new head of the Gc snapshot stack. 374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public GcSnapshot restoreTo(int saveCount) { 376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return doRestoreTo(size(), saveCount); 377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public int size() { 380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mPrevious != null) { 381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mPrevious.size() + 1; 382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return 1; 385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Link the snapshot to a Bitmap_Delegate. 389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * This is only for the case where the snapshot was created with a null image when calling 391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to 392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * a previous snapshot. 393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * If any transform or clip information was set before, they are put into the Graphics object. 395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param bitmap the bitmap to link to. 396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void setBitmap(Bitmap_Delegate bitmap) { 398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // create a new Layer for the bitmap. This will be the base layer. 399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D graphics2D = bitmap.getImage().createGraphics(); 400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer baseLayer = new Layer(graphics2D, bitmap); 401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Set the current transform and clip which can either come from mTransform/mClip if they 403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // were set when there was no bitmap/layers or from the current base layers if there is 404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // one already. 405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski graphics2D.setTransform(getTransform()); 407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // reset mTransform in case there was one. 408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = null; 409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski baseLayer.setClip(getClip()); 411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // reset mClip in case there was one. 412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mClip = null; 413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // replace whatever current layers we have with this. 415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayers.clear(); 416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayers.add(baseLayer); 417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void translate(float dx, float dy) { 421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().translate(dx, dy); 424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mTransform == null) { 427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = new AffineTransform(); 428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform.translate(dx, dy); 430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void rotate(double radians) { 434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().rotate(radians); 437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mTransform == null) { 440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = new AffineTransform(); 441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform.rotate(radians); 443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void scale(float sx, float sy) { 447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().scale(sx, sy); 450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mTransform == null) { 453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = new AffineTransform(); 454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform.scale(sx, sy); 456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public AffineTransform getTransform() { 460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // all graphics2D in the list have the same transform 462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mLayers.get(0).getGraphics().getTransform(); 463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mTransform == null) { 465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = new AffineTransform(); 466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mTransform; 468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void setTransform(AffineTransform transform) { 472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().setTransform(transform); 475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mTransform == null) { 478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform = new AffineTransform(); 479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mTransform.setTransform(transform); 481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public boolean clip(Shape shape, int regionOp) { 485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Simple case of intersect with existing layers. 486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // Because Graphics2D#setClip works a bit peculiarly, we optimize 487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // the case of clipping by intersection, as it's supported natively. 488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) { 489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.clip(shape); 491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Shape currentClip = getClip(); 494282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return currentClip != null && currentClip.getBounds().isEmpty() == false; 495282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Area area = null; 498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (regionOp == Region.Op.REPLACE.nativeInt) { 500282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski area = new Area(shape); 501282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 502282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski area = Region_Delegate.combineShapes(getClip(), shape, regionOp); 503282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 504282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 505282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski assert area != null; 506282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 507282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 508282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (area != null) { 509282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 510282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.setClip(area); 511282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 512282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 513282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 514282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Shape currentClip = getClip(); 515282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return currentClip != null && currentClip.getBounds().isEmpty() == false; 516282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 517282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (area != null) { 518282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mClip = area; 519282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mClip = new Area(); 521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mClip.getBounds().isEmpty() == false; 524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public boolean clipRect(float left, float top, float right, float bottom, int regionOp) { 528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp); 529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Returns the current clip, or null if none have been setup. 533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public Shape getClip() { 535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() > 0) { 536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // they all have the same clip 537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mLayers.get(0).getGraphics().getClip(); 538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mClip; 540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private GcSnapshot doRestoreTo(int size, int saveCount) { 544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (size <= saveCount) { 545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return this; 546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // restore the current one first. 549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski GcSnapshot previous = doRestore(); 550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 551282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (size == saveCount + 1) { // this was the only one that needed restore. 552282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return previous; 553282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 554282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return previous.doRestoreTo(size - 1, saveCount); 555282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 559282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Executes the Drawable's draw method, with a null paint delegate. 560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 561282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Note that the method can be called several times if there are more than one active layer. 562282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void draw(Drawable drawable) { 564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/); 565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Executes the Drawable's draw method. 569282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/> 570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Note that the method can be called several times if there are more than one active layer. 571282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param compositeOnly whether the paint is used for composite only. This is typically 572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * the case for bitmaps. 573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * @param forceSrcMode if true, this overrides the composite to be SRC 574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly, 576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski boolean forceSrcMode) { 577f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta int forceMode = forceSrcMode ? AlphaComposite.SRC : 0; 578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // the current snapshot may not have a mLocalLayer (ie it was created on save() instead 579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // of saveLayer(), but that doesn't mean there's no layer. 580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // mLayers however saves all the information we need (flags). 581282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLayers.size() == 1) { 582282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // no layer, only base layer. easy case. 583f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode); 584282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } else { 585282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // draw in all the layers until the layer save flags tells us to stop (ie drawing 586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // in that layer is limited to the layer itself. 587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int flags; 588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int i = mLayers.size() - 1; 589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 590282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski do { 591282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer layer = mLayers.get(i); 592282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 593f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawInLayer(layer, drawable, paint, compositeOnly, forceMode); 594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // then go to previous layer, only if there are any left, and its flags 596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // doesn't restrict drawing to the layer itself. 597282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski i--; 598282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski flags = layer.getFlags(); 599282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0); 600282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 601282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 602282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 603282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint, 604f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta boolean compositeOnly, int forceMode) { 605282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D originalGraphics = layer.getGraphics(); 606f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (paint == null) { 607f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawOnGraphics((Graphics2D) originalGraphics.create(), drawable, 608f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta null /*paint*/, layer); 609f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } else { 610f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta ColorFilter_Delegate filter = paint.getColorFilter(); 611f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (filter == null || !filter.isSupported()) { 612f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // get a Graphics2D object configured with the drawing parameters. 613f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint, 614f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta compositeOnly, forceMode); 615f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawOnGraphics(configuredGraphics, drawable, paint, layer); 616f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return; 617f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 618f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta 6198ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez int x = 0; 6208ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez int y = 0; 621f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez int width; 622f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez int height; 623f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez Rectangle clipBounds = originalGraphics.getClipBounds(); 624f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez if (clipBounds != null) { 625f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez if (clipBounds.width == 0 || clipBounds.height == 0) { 626f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez // Clip is 0 so no need to paint anything. 627f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez return; 628f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez } 629f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez // If we have clipBounds available, use them as they will always be 630f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez // smaller than the full layer size. 6318ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez x = clipBounds.x; 6328ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez y = clipBounds.y; 633f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez width = clipBounds.width; 634f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez height = clipBounds.height; 635f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez } else { 636f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez width = layer.getImage().getWidth(); 637f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez height = layer.getImage().getHeight(); 638f3cb4ba213a0fa4d1c184c430a2eaac7e27ccf6fDiego Perez } 639f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta 640f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // Create a temporary image to which the color filter will be applied. 641f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta BufferedImage image = new BufferedImage(width, height, 642f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta BufferedImage.TYPE_INT_ARGB); 643f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics(); 644f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // Configure the Graphics2D object with drawing parameters and shader. 645f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Graphics2D imageGraphics = createCustomGraphics( 646f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta imageBaseGraphics, paint, compositeOnly, 647f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta AlphaComposite.SRC_OVER); 648f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // get a Graphics2D object configured with the drawing parameters, but no shader. 649f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint, 650f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta true /*compositeOnly*/, forceMode); 651f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta try { 652f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // The main draw operation. 6538ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // We translate the operation to take into account that the rendering does not 6548ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // know about the clipping area. 6558ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez imageGraphics.translate(-x, -y); 656f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawable.draw(imageGraphics, paint); 657f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta 658f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // Apply the color filter. 6598ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // Restore the original coordinates system and apply the filter only to the 6608ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // clipped area. 6618ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez imageGraphics.translate(x, y); 662f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta filter.applyFilter(imageGraphics, width, height); 663f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta 6648ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // Draw the tinted image on the main layer using as start point the clipping 6658ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez // upper left coordinates. 6668ab069f3aed15c7e5de32ff52a3b4710218f9895Diego Perez configuredGraphics.drawImage(image, x, y, null); 667f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta layer.change(); 668f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } finally { 669f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // dispose Graphics2D objects 670f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta imageGraphics.dispose(); 671f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta imageBaseGraphics.dispose(); 672f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta configuredGraphics.dispose(); 673f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 674f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 675f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 676282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 677f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint, 678f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Layer layer) { 679282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski try { 680f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta drawable.draw(g, paint); 681282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.change(); 682282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } finally { 683f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.dispose(); 684282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 685282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 686282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 687282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private GcSnapshot doRestore() { 688282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mPrevious != null) { 689282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (mLocalLayer != null) { 690282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // prepare to blit the layers in which we have draw, in the layer beneath 691282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // them, starting with the top one (which is the current local layer). 692282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int i = mLayers.size() - 1; 693282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski int flags; 694282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski do { 695282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Layer dstLayer = mLayers.get(i - 1); 696282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 697282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski restoreLayer(dstLayer); 698282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 699282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski flags = dstLayer.getFlags(); 700282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski i--; 701282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0); 702282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 703282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 704282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // if this snapshot does not save everything, then set the previous snapshot 705282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // to this snapshot content 706282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 707282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // didn't save the matrix? set the current matrix on the previous snapshot 708282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) { 709282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski AffineTransform mtx = getTransform(); 710282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mPrevious.mLayers) { 711282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().setTransform(mtx); 712282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 713282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 714282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 715282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // didn't save the clip? set the current clip on the previous snapshot 716282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) { 717282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Shape clip = getClip(); 718282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mPrevious.mLayers) { 719282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.setClip(clip); 720282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 721282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 722282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 723282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 724282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski for (Layer layer : mLayers) { 725282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski layer.getGraphics().dispose(); 726282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 727282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 728282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski return mPrevious; 729282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 730282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 731282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private void restoreLayer(Layer dstLayer) { 732282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 733282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D baseGfx = dstLayer.getImage().createGraphics(); 734282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 735282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // if the layer contains an original copy this means the flags 736282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // didn't restrict drawing to the local layer and we need to make sure the 737282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // layer bounds in the layer beneath didn't receive any drawing. 738282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // so we use the originalCopy to erase the new drawings in there. 739282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski BufferedImage originalCopy = dstLayer.getOriginalCopy(); 740282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (originalCopy != null) { 741282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D g = (Graphics2D) baseGfx.create(); 742282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.setComposite(AlphaComposite.Src); 743282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 744282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.drawImage(originalCopy, 745282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom, 746282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 0, 0, mLayerBounds.width(), mLayerBounds.height(), 747282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski null); 748282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.dispose(); 749282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 750282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 751282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // now draw put the content of the local layer onto the layer, 752282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // using the paint information 753282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint, 754f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta true /*alphaOnly*/, 0 /*forceMode*/); 755282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 756282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.drawImage(mLocalLayer.getImage(), 757282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom, 758282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom, 759282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski null); 760282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.dispose(); 761282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 762282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski baseGfx.dispose(); 763282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 764282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 765282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski /** 766282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. 767282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. 768282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */ 769282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint, 770f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta boolean compositeOnly, int forceMode) { 771282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // make new one graphics 772282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski Graphics2D g = (Graphics2D) original.create(); 773282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 774282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // configure it 775282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 776282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski if (paint.isAntiAliased()) { 777282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.setRenderingHint( 778282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 779282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski g.setRenderingHint( 780282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); 781282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 782282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 783f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // set the shader first, as it'll replace the color if it can be used it. 784282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski boolean customShader = false; 785f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (!compositeOnly) { 786f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta customShader = setShader(g, paint); 787f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // set the stroke 788f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setStroke(paint.getJavaStroke()); 789f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 790f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // set the composite. 791f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta setComposite(g, paint, compositeOnly || customShader, forceMode); 792282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 793f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return g; 794f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 795f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta 796f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta private boolean setShader(Graphics2D g, Paint_Delegate paint) { 797f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Shader_Delegate shaderDelegate = paint.getShader(); 798f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (shaderDelegate != null) { 799f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (shaderDelegate.isSupported()) { 800f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint(); 801f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta assert shaderPaint != null; 802f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (shaderPaint != null) { 803f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setPaint(shaderPaint); 804f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return true; 805282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 806f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } else { 807f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER, 808f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta shaderDelegate.getSupportMessage(), 809f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta null /*throwable*/, null /*data*/); 810282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 811f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 812282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 813f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // if no shader, use the paint color 814f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setColor(new Color(paint.getColor(), true /*hasAlpha*/)); 815282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 816f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return false; 817f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 818282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 819f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha, 820f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta int forceMode) { 821282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // the alpha for the composite. Always opaque if the normal paint color is used since 822282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // it contains the alpha 823f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF; 824f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (forceMode != 0) { 825f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f)); 826f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return; 827f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 828f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Xfermode_Delegate xfermodeDelegate = paint.getXfermode(); 829f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (xfermodeDelegate != null) { 830f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (xfermodeDelegate.isSupported()) { 831f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Composite composite = xfermodeDelegate.getComposite(alpha); 832f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta assert composite != null; 833f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (composite != null) { 834f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setComposite(composite); 835f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta return; 836282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 837f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } else { 838f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE, 839f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta xfermodeDelegate.getSupportMessage(), 840f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta null /*throwable*/, null /*data*/); 841282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 842282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 843f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // if there was no custom xfermode, but we have alpha (due to a shader and a non 844f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // opaque alpha channel in the paint color), then we create an AlphaComposite anyway 845f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta // that will handle the alpha. 846f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta if (alpha != 0xFF) { 847f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta g.setComposite(AlphaComposite.getInstance( 848f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta AlphaComposite.SRC_OVER, (float) alpha / 255.f)); 849f2af1f5d8aaff684f8cc6d8e31454c945e190976Deepanshu Gupta } 850282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 851282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 852282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski private void mapRect(AffineTransform matrix, RectF dst, RectF src) { 853282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // array with 4 corners 854282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski float[] corners = new float[] { 855282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski src.left, src.top, 856282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski src.right, src.top, 857282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski src.right, src.bottom, 858282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski src.left, src.bottom, 859282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski }; 860282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 861282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // apply the transform to them. 862282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski matrix.transform(corners, 0, corners, 0, 4); 863282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 864282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski // now put the result in the rect. We take the min/max of Xs and min/max of Ys 865282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); 866282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); 867282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 868282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); 869282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); 870282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski } 871282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski 872282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski} 873