From c062c7003f34999632fe4ad721dcd47cd71b2159 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Tue, 14 Jan 2014 17:16:29 -0800 Subject: [PATCH] Cache display lists for Drawables Change-Id: I86e2dd4cd4ac8871f4131450cb9082c2b8de2eed --- api/current.txt | 15 +-- .../graphics/drawable/BitmapDrawable.java | 2 +- .../android/graphics/drawable/Drawable.java | 94 ++++++++++++++++++- .../graphics/drawable/GradientDrawable.java | 2 +- .../graphics/drawable/NinePatchDrawable.java | 2 +- .../graphics/drawable/PictureDrawable.java | 2 +- .../graphics/drawable/ShapeDrawable.java | 2 +- 7 files changed, 97 insertions(+), 22 deletions(-) diff --git a/api/current.txt b/api/current.txt index 241c540c3fdbc..2c2247202f131 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10338,7 +10338,6 @@ package android.graphics.drawable { ctor public BitmapDrawable(android.content.res.Resources, java.lang.String); ctor public deprecated BitmapDrawable(java.io.InputStream); ctor public BitmapDrawable(android.content.res.Resources, java.io.InputStream); - method public void draw(android.graphics.Canvas); method public final android.graphics.Bitmap getBitmap(); method public final android.graphics.drawable.Drawable.ConstantState getConstantState(); method public int getGravity(); @@ -10364,7 +10363,6 @@ package android.graphics.drawable { public class ClipDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public ClipDrawable(android.graphics.drawable.Drawable, int, int); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public void invalidateDrawable(android.graphics.drawable.Drawable); method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long); @@ -10378,7 +10376,6 @@ package android.graphics.drawable { public class ColorDrawable extends android.graphics.drawable.Drawable { ctor public ColorDrawable(); ctor public ColorDrawable(int); - method public void draw(android.graphics.Canvas); method public int getColor(); method public int getOpacity(); method public void setAlpha(int); @@ -10397,7 +10394,7 @@ package android.graphics.drawable { method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String); method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public abstract void draw(android.graphics.Canvas); + method public void draw(android.graphics.Canvas); method public int getAlpha(); method public final android.graphics.Rect getBounds(); method public android.graphics.drawable.Drawable.Callback getCallback(); @@ -10421,6 +10418,7 @@ package android.graphics.drawable { method public void jumpToCurrentState(); method public android.graphics.drawable.Drawable mutate(); method protected void onBoundsChange(android.graphics.Rect); + method protected void onDraw(android.graphics.Canvas); method protected boolean onLevelChange(int); method protected boolean onStateChange(int[]); method public static int resolveOpacity(int, int); @@ -10456,7 +10454,6 @@ package android.graphics.drawable { public class DrawableContainer extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public DrawableContainer(); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public void invalidateDrawable(android.graphics.drawable.Drawable); method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long); @@ -10497,7 +10494,6 @@ package android.graphics.drawable { public class GradientDrawable extends android.graphics.drawable.Drawable { ctor public GradientDrawable(); ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public android.graphics.drawable.GradientDrawable.Orientation getOrientation(); method public boolean onStateChange(int[]); @@ -10544,7 +10540,6 @@ package android.graphics.drawable { public class InsetDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public InsetDrawable(android.graphics.drawable.Drawable, int); ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int); - method public void draw(android.graphics.Canvas); method public android.graphics.drawable.Drawable getDrawable(); method public int getOpacity(); method public void invalidateDrawable(android.graphics.drawable.Drawable); @@ -10556,7 +10551,6 @@ package android.graphics.drawable { public class LayerDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public LayerDrawable(android.graphics.drawable.Drawable[]); - method public void draw(android.graphics.Canvas); method public android.graphics.drawable.Drawable findDrawableByLayerId(int); method public android.graphics.drawable.Drawable getDrawable(int); method public int getId(int); @@ -10587,7 +10581,6 @@ package android.graphics.drawable { ctor public NinePatchDrawable(android.content.res.Resources, android.graphics.Bitmap, byte[], android.graphics.Rect, java.lang.String); ctor public deprecated NinePatchDrawable(android.graphics.NinePatch); ctor public NinePatchDrawable(android.content.res.Resources, android.graphics.NinePatch); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public android.graphics.Paint getPaint(); method public void setAlpha(int); @@ -10606,7 +10599,6 @@ package android.graphics.drawable { public class PictureDrawable extends android.graphics.drawable.Drawable { ctor public PictureDrawable(android.graphics.Picture); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public android.graphics.Picture getPicture(); method public void setAlpha(int); @@ -10620,7 +10612,6 @@ package android.graphics.drawable { public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public RotateDrawable(); - method public void draw(android.graphics.Canvas); method public android.graphics.drawable.Drawable getDrawable(); method public int getOpacity(); method public void invalidateDrawable(android.graphics.drawable.Drawable); @@ -10632,7 +10623,6 @@ package android.graphics.drawable { public class ScaleDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { ctor public ScaleDrawable(android.graphics.drawable.Drawable, int, float, float); - method public void draw(android.graphics.Canvas); method public android.graphics.drawable.Drawable getDrawable(); method public int getOpacity(); method public void invalidateDrawable(android.graphics.drawable.Drawable); @@ -10645,7 +10635,6 @@ package android.graphics.drawable { public class ShapeDrawable extends android.graphics.drawable.Drawable { ctor public ShapeDrawable(); ctor public ShapeDrawable(android.graphics.drawable.shapes.Shape); - method public void draw(android.graphics.Canvas); method public int getOpacity(); method public android.graphics.Paint getPaint(); method public android.graphics.drawable.ShapeDrawable.ShaderFactory getShaderFactory(); diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 936055889d45e..52e5b5be5c15f 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -456,7 +456,7 @@ public class BitmapDrawable extends Drawable { } @Override - public void draw(Canvas canvas) { + protected void onDraw(Canvas canvas) { Bitmap bitmap = mBitmap; if (bitmap != null) { final BitmapState state = mBitmapState; diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index b8365aab655c7..cfb1983b30250 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -39,6 +39,9 @@ import android.util.DisplayMetrics; import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; +import android.view.DisplayList; +import android.view.HardwareCanvas; +import android.view.HardwareRenderer; import android.view.View; import java.io.IOException; @@ -139,16 +142,96 @@ public abstract class Drawable { private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect() private WeakReference mCallback = null; private boolean mVisible = true; + private DisplayList mDisplayList; private int mLayoutDirection; /** * Draw in its bounds (set via setBounds) respecting optional effects such * as alpha (set via setAlpha) and color filter (set via setColorFilter). + *

+ * Overriding this method will prevent caching optimizations. To enable + * optimizations, override {@link #onDraw} instead. * * @param canvas The canvas to draw into */ - public abstract void draw(Canvas canvas); + public void draw(Canvas canvas) { + if (canvas != null && canvas.isHardwareAccelerated()) { + final HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas; + final DisplayList displayList = getDisplayList(hardwareCanvas); + if (displayList != null) { + final int restoreCount = hardwareCanvas.save(Canvas.MATRIX_SAVE_FLAG); + hardwareCanvas.translate(mBounds.left, mBounds.top); + hardwareCanvas.drawDisplayList(displayList); + hardwareCanvas.restoreToCount(restoreCount); + return; + } + } + + onDraw(canvas); + } + + /** + * Draw in its bounds (set via setBounds) respecting optional effects such + * as alpha (set via setAlpha) and color filter (set via setColorFilter). + *

+ * Overriding this method, rather than {@link #draw}, enables caching + * optimizations that avoid re-drawing when unnecessary. + * + * @param canvas The canvas to draw into + */ + protected void onDraw(Canvas canvas) { + throw new UnsupportedOperationException( + "Drawable subclasses must implement either draw or onDraw"); + } + + /** + * Gets a display list that can be used to draw this drawable again without + * invoking its draw method. + * + * @param hardwareCanvas The hardware canvas. + * @return A DisplayList ready to replay, or null if caching is not enabled. + * @hide + */ + private DisplayList getDisplayList(HardwareCanvas hardwareCanvas) { + DisplayList displayList = mDisplayList; + if (displayList != null && displayList.isValid()) { + // Note: This code assumes that the display list previously generated + // is compatible with the given hardware canvas. That might not be true + // if we start using multiple different types of hardware canvas + // in the system someday. + return displayList; + } + + displayList = DisplayList.create(getClass().getName()); + mDisplayList = displayList; + + final Rect bounds = mBounds; + final int width = bounds.width(); + final int height = bounds.height(); + final HardwareCanvas canvas = displayList.start(width, height); + canvas.onPreDraw(null); + + // Draw the display list with origin (0,0) so that if the position + // changes then we can translate without recreating the display list. + final int restoreCount = canvas.save(); + try { + canvas.translate(-bounds.left, -bounds.top); + onDraw(canvas); + } finally { + canvas.restoreToCount(restoreCount); + canvas.onPostDraw(); + displayList.end(); + } + + displayList.setLeftTopRightBottom(0, 0, width, height); + displayList.setClipToBounds(false); + return displayList; + } + + private void invalidateDisplayList() { + mDisplayList = null; + } /** * Specify a bounding rectangle for the Drawable. This is where the drawable @@ -163,10 +246,11 @@ public abstract class Drawable { if (oldBounds.left != left || oldBounds.top != top || oldBounds.right != right || oldBounds.bottom != bottom) { - if (!oldBounds.isEmpty()) { - // first invalidate the previous bounds - invalidateSelf(); + if (oldBounds.right - oldBounds.left != right - left + || oldBounds.bottom - oldBounds.top != bottom - top) { + invalidateDisplayList(); } + mBounds.set(left, top, right, bottom); onBoundsChange(mBounds); } @@ -354,6 +438,8 @@ public abstract class Drawable { * @see #setCallback(android.graphics.drawable.Drawable.Callback) */ public void invalidateSelf() { + invalidateDisplayList(); + final Callback callback = getCallback(); if (callback != null) { callback.invalidateDrawable(this); diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 75184e05e44f8..6a9454c1d2cb8 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -472,7 +472,7 @@ public class GradientDrawable extends Drawable { } @Override - public void draw(Canvas canvas) { + protected void onDraw(Canvas canvas) { if (!ensureValidRect()) { // nothing to draw return; diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 515d3c1e10d01..ee64d7aa6e01d 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -223,7 +223,7 @@ public class NinePatchDrawable extends Drawable { } @Override - public void draw(Canvas canvas) { + protected void onDraw(Canvas canvas) { final Rect bounds = getBounds(); final boolean needsMirroring = needsMirroring(); if (needsMirroring) { diff --git a/graphics/java/android/graphics/drawable/PictureDrawable.java b/graphics/java/android/graphics/drawable/PictureDrawable.java index cb2d8f67a4baa..2118b2390870c 100644 --- a/graphics/java/android/graphics/drawable/PictureDrawable.java +++ b/graphics/java/android/graphics/drawable/PictureDrawable.java @@ -60,7 +60,7 @@ public class PictureDrawable extends Drawable { } @Override - public void draw(Canvas canvas) { + protected void onDraw(Canvas canvas) { if (mPicture != null) { Rect bounds = getBounds(); canvas.save(); diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index 93f2dc603ee98..9ca3bbfd3f5e7 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -210,7 +210,7 @@ public class ShapeDrawable extends Drawable { } @Override - public void draw(Canvas canvas) { + protected void onDraw(Canvas canvas) { Rect r = getBounds(); Paint paint = mShapeState.mPaint;