summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2010-12-23 03:25:57 -0800
committerXavier Ducrohet <xav@android.com>2010-12-24 00:21:50 -0800
commit20805343296eef04081fee82fd04547f51225fe3 (patch)
tree2a5ec42a96c385a526b59e69447993b0d988c9c8 /tools
parent1039872f597995cab2f88b9c841f8c2b8e71c451 (diff)
downloadframeworks_base-20805343296eef04081fee82fd04547f51225fe3.zip
frameworks_base-20805343296eef04081fee82fd04547f51225fe3.tar.gz
frameworks_base-20805343296eef04081fee82fd04547f51225fe3.tar.bz2
LayoutLib: Fix Canvas layer support with 2+ layers
- When drawing is not clipped to the top layers, drawing should not happen automatically on all existing layers. Instead each layer's flags dictate whether drawing should happen on layers beneath, starting with the top layer. - upon restore, the same mechanism is taken. Only blit a layer into the layer beneath if it was drawn into using the logic above. Also fixed: - saveLayer() does not, in fact, always save matrix and clip info. The flag dictate this, the same way it does in save() - drawing code didn't properly detect the case of drawing into layers if a save() was called after saveLayer(). Now the code only looks at the layer list which provide all the needed info (flags mostly), and doesn't rely on mLocalLayer (which is used during restore only now). - Properly handle HAS_ALPHA_LAYER_SAVE_FLAG (or lack thereof) when creating the BufferedImage for the layer. Change-Id: I2fbbcc0f5d3a3dd208763705bc23e6658fd4e573
Diffstat (limited to 'tools')
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java190
1 files changed, 121 insertions, 69 deletions
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 27268fc..5c2c7cd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -52,13 +52,17 @@ import java.util.ArrayList;
* This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
* {@link #draw(Drawable, Paint_Delegate)}
*
+ * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
+ * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
+ * for each layer. Doing a save() will duplicate this list so that each graphics2D object
+ * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
*/
public class GcSnapshot {
private final GcSnapshot mPrevious;
private final int mFlags;
- /** list of layers. */
+ /** list of layers. The first item in the list is always the */
private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
/** temp transform in case transformation are set before a Graphics2D exists */
@@ -67,25 +71,39 @@ public class GcSnapshot {
private Area mClip = null;
// local layer data
+ /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
+ * If this is null, this does not mean there's no layer, just that the snapshot is not the
+ * one that created the layer.
+ * @see #getLayerSnapshot()
+ */
private final Layer mLocalLayer;
private final Paint_Delegate mLocalLayerPaint;
- private Rect mLocalLayerRegion;
+ private final Rect mLayerBounds;
public interface Drawable {
void draw(Graphics2D graphics, Paint_Delegate paint);
}
/**
- * class containing information about a layer.
+ * Class containing information about a layer.
+ *
+ * This contains graphics, bitmap and layer information.
*/
private static class Layer {
private final Graphics2D mGraphics;
private final Bitmap_Delegate mBitmap;
private final BufferedImage mImage;
+ /** the flags that were used to configure the layer. This is never changed, and passed
+ * as is when {@link #makeCopy()} is called */
+ private final int mFlags;
+ /** the original content of the layer when the next object was created. This is not
+ * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
+ * (depending on its flags) */
private BufferedImage mOriginalCopy;
/**
- * Creates a layer with a graphics and a bitmap.
+ * Creates a layer with a graphics and a bitmap. This is only used to create
+ * the base layer.
*
* @param graphics the graphics
* @param bitmap the bitmap
@@ -94,20 +112,23 @@ public class GcSnapshot {
mGraphics = graphics;
mBitmap = bitmap;
mImage = mBitmap.getImage();
+ mFlags = 0;
}
/**
* Creates a layer with a graphics and an image. If the image belongs to a
- * {@link Bitmap_Delegate}, then {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should
- * be used.
+ * {@link Bitmap_Delegate} (case of the base layer), then
+ * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
*
- * @param graphics the graphics
- * @param image the image
+ * @param graphics the graphics the new graphics for this layer
+ * @param image the image the image from which the graphics came
+ * @param flags the flags that were used to save this layer
*/
- Layer(Graphics2D graphics, BufferedImage image) {
+ Layer(Graphics2D graphics, BufferedImage image, int flags) {
mGraphics = graphics;
mBitmap = null;
mImage = image;
+ mFlags = flags;
}
/** The Graphics2D, guaranteed to be non null */
@@ -120,12 +141,21 @@ public class GcSnapshot {
return mImage;
}
+ /** Returns the layer save flags. This is only valid for additional layers.
+ * For the base layer this will always return 0;
+ * For a given layer, all further copies of this {@link Layer} object in new snapshots
+ * will always return the same value.
+ */
+ int getFlags() {
+ return mFlags;
+ }
+
Layer makeCopy() {
if (mBitmap != null) {
return new Layer((Graphics2D) mGraphics.create(), mBitmap);
}
- return new Layer((Graphics2D) mGraphics.create(), mImage);
+ return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
}
/** sets an optional copy of the original content to be used during restore */
@@ -203,6 +233,7 @@ public class GcSnapshot {
mFlags = 0;
mLocalLayer = null;
mLocalLayerPaint = null;
+ mLayerBounds = null;
}
/**
@@ -233,11 +264,11 @@ public class GcSnapshot {
// get the current transform
AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
- // transform the layerBounds and puts it into a int rect
+ // transform the layerBounds with the current transform and stores it into a int rect
RectF rect2 = new RectF();
mapRect(matrix, rect2, layerBounds);
- mLocalLayerRegion = new Rect();
- rect2.round(mLocalLayerRegion);
+ mLayerBounds = new Rect();
+ rect2.round(mLayerBounds);
// get the base layer (always at index 0)
Layer baseLayer = mLayers.get(0);
@@ -246,7 +277,9 @@ public class GcSnapshot {
BufferedImage layerImage = new BufferedImage(
baseLayer.getImage().getWidth(),
baseLayer.getImage().getHeight(),
- BufferedImage.TYPE_INT_ARGB);
+ (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
+ BufferedImage.TYPE_INT_ARGB :
+ BufferedImage.TYPE_INT_RGB);
// create a graphics for it so that drawing can be done.
Graphics2D layerGraphics = layerImage.createGraphics();
@@ -260,7 +293,7 @@ public class GcSnapshot {
layerGraphics.setClip(currentClip);
// create a new layer for this new layer and add it to the list at the end.
- mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage));
+ mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
// if the drawing is not clipped to the local layer only, we save the current content
// of all other layers. We are only interested in the part that will actually
@@ -268,16 +301,16 @@ public class GcSnapshot {
// This is so that we can erase the drawing that goes in the layers below that will
// be coming from the layer itself.
if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
- int w = mLocalLayerRegion.width();
- int h = mLocalLayerRegion.height();
+ int w = mLayerBounds.width();
+ int h = mLayerBounds.height();
for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
Layer layer = mLayers.get(i);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.drawImage(layer.getImage(),
0, 0, w, h,
- mLocalLayerRegion.left, mLocalLayerRegion.top,
- mLocalLayerRegion.right, mLocalLayerRegion.bottom,
+ mLayerBounds.left, mLayerBounds.top,
+ mLayerBounds.right, mLayerBounds.bottom,
null);
graphics.dispose();
layer.setOriginalCopy(image);
@@ -285,6 +318,7 @@ public class GcSnapshot {
}
} else {
mLocalLayer = null;
+ mLayerBounds = null;
}
mLocalLayerPaint = paint;
@@ -529,14 +563,28 @@ public class GcSnapshot {
*/
public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
boolean forceSrcMode) {
- // if we clip to the layer, then we only draw in the layer
- if (mLocalLayer != null && (mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) != 0) {
- drawInLayer(mLocalLayer, drawable, paint, compositeOnly, forceSrcMode);
+ // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
+ // of saveLayer(), but that doesn't mean there's no layer.
+ // mLayers however saves all the information we need (flags).
+ if (mLayers.size() == 1) {
+ // no layer, only base layer. easy case.
+ drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
} else {
- // draw in all the layers
- for (Layer layer : mLayers) {
+ // draw in all the layers until the layer save flags tells us to stop (ie drawing
+ // in that layer is limited to the layer itself.
+ int flags;
+ int i = mLayers.size() - 1;
+
+ do {
+ Layer layer = mLayers.get(i);
+
drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
- }
+
+ // then go to previous layer, only if there are any left, and its flags
+ // doesn't restrict drawing to the layer itself.
+ i--;
+ flags = layer.getFlags();
+ } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
}
}
@@ -560,56 +608,26 @@ public class GcSnapshot {
private GcSnapshot doRestore() {
if (mPrevious != null) {
- boolean forceAllSave = false;
if (mLocalLayer != null) {
- forceAllSave = true; // layers always save both clip and transform
-
- // prepare to draw the current layer in the previous layers, ie all layers but
- // the last one, since the last one is the local layer
- for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
- Layer layer = mLayers.get(i);
-
- Graphics2D baseGfx = layer.getImage().createGraphics();
-
- // if the layer contains an original copy this means the flags
- // didn't restrict drawing to the local layer and we need to make sure the
- // layer bounds in the layer beneath didn't receive any drawing.
- // so we use the originalCopy to erase the new drawings in there.
- BufferedImage originalCopy = layer.getOriginalCopy();
- if (originalCopy != null) {
- Graphics2D g = (Graphics2D) baseGfx.create();
- g.setComposite(AlphaComposite.Src);
-
- g.drawImage(originalCopy,
- mLocalLayerRegion.left, mLocalLayerRegion.top,
- mLocalLayerRegion.right, mLocalLayerRegion.bottom,
- 0, 0, mLocalLayerRegion.width(), mLocalLayerRegion.height(),
- null);
- g.dispose();
- }
-
- // now draw put the content of the local layer onto the layer, using the paint
- // information
- Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
- true /*alphaOnly*/, false /*forceSrcMode*/);
-
- g.drawImage(mLocalLayer.getImage(),
- mLocalLayerRegion.left, mLocalLayerRegion.top,
- mLocalLayerRegion.right, mLocalLayerRegion.bottom,
- mLocalLayerRegion.left, mLocalLayerRegion.top,
- mLocalLayerRegion.right, mLocalLayerRegion.bottom,
- null);
- g.dispose();
-
- baseGfx.dispose();
- }
+ // prepare to blit the layers in which we have draw, in the layer beneath
+ // them, starting with the top one (which is the current local layer).
+ int i = mLayers.size() - 1;
+ int flags;
+ do {
+ Layer dstLayer = mLayers.get(i - 1);
+
+ restoreLayer(dstLayer);
+
+ flags = dstLayer.getFlags();
+ i--;
+ } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
}
// if this snapshot does not save everything, then set the previous snapshot
// to this snapshot content
// didn't save the matrix? set the current matrix on the previous snapshot
- if (forceAllSave == false && (mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
+ if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
AffineTransform mtx = getTransform();
for (Layer layer : mPrevious.mLayers) {
layer.getGraphics().setTransform(mtx);
@@ -617,7 +635,7 @@ public class GcSnapshot {
}
// didn't save the clip? set the current clip on the previous snapshot
- if (forceAllSave == false && (mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
+ if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
Shape clip = getClip();
for (Layer layer : mPrevious.mLayers) {
layer.getGraphics().setClip(clip);
@@ -632,6 +650,40 @@ public class GcSnapshot {
return mPrevious;
}
+ private void restoreLayer(Layer dstLayer) {
+
+ Graphics2D baseGfx = dstLayer.getImage().createGraphics();
+
+ // if the layer contains an original copy this means the flags
+ // didn't restrict drawing to the local layer and we need to make sure the
+ // layer bounds in the layer beneath didn't receive any drawing.
+ // so we use the originalCopy to erase the new drawings in there.
+ BufferedImage originalCopy = dstLayer.getOriginalCopy();
+ if (originalCopy != null) {
+ Graphics2D g = (Graphics2D) baseGfx.create();
+ g.setComposite(AlphaComposite.Src);
+
+ g.drawImage(originalCopy,
+ mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+ 0, 0, mLayerBounds.width(), mLayerBounds.height(),
+ null);
+ g.dispose();
+ }
+
+ // now draw put the content of the local layer onto the layer,
+ // using the paint information
+ Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
+ true /*alphaOnly*/, false /*forceSrcMode*/);
+
+ g.drawImage(mLocalLayer.getImage(),
+ mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+ mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
+ null);
+ g.dispose();
+
+ baseGfx.dispose();
+ }
+
/**
* Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.