From 35289f12d6cb0f0db67489876c805ad4a3cbd5f6 Mon Sep 17 00:00:00 2001 From: ztenghui Date: Tue, 13 Jan 2015 16:21:11 -0800 Subject: Scaling (Animated)VectorDrawable inside ImageView Before, the VectorDrawable is behaving like BitmapDrawable inside a ImageView, and it can be blurry due to scaling. Now apply the scaling information to the cached bitmap, then the size of bitmap will match the ImageView's screen size. Therefore, no blurry any more. b/18185626 Change-Id: I979cef3b5178a9bd37ee6cc776df3361ca47c803 --- .../android/graphics/drawable/VectorDrawable.java | 83 ++++++++++++++-------- 1 file changed, 54 insertions(+), 29 deletions(-) (limited to 'graphics') diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 24cb055..1cfccc4 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -200,6 +200,11 @@ public class VectorDrawable extends Drawable { private static final int LINEJOIN_ROUND = 1; private static final int LINEJOIN_BEVEL = 2; + // Cap the bitmap size, such that it won't hurt the performance too much + // and it won't crash due to a very large scale. + // The drawable will look blurry above this size. + private static final int MAX_CACHED_BITMAP_SIZE = 2048; + private static final boolean DBG_VECTOR_DRAWABLE = false; private VectorDrawableState mVectorState; @@ -219,6 +224,11 @@ public class VectorDrawable extends Drawable { private int mDpiScaledHeight = 0; private Insets mDpiScaleInsets = Insets.NONE; + // Temp variable, only for saving "new" operation at the draw() time. + private final float[] mTmpFloats = new float[9]; + private final Matrix mTmpMatrix = new Matrix(); + private final Rect mTmpBounds = new Rect(); + public VectorDrawable() { this(null, null); } @@ -262,44 +272,59 @@ public class VectorDrawable extends Drawable { @Override public void draw(Canvas canvas) { - final Rect bounds = getBounds(); - if (bounds.width() <= 0 || bounds.height() <= 0) { + // We will offset the bounds for drawBitmap, so copyBounds() here instead + // of getBounds(). + copyBounds(mTmpBounds); + if (mTmpBounds.width() <= 0 || mTmpBounds.height() <= 0) { // Nothing to draw return; } + // Color filters always override tint filters. + final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter); + + // The imageView can scale the canvas in different ways, in order to + // avoid blurry scaling, we have to draw into a bitmap with exact pixel + // size first. This bitmap size is determined by the bounds and the + // canvas scale. + canvas.getMatrix(mTmpMatrix); + mTmpMatrix.getValues(mTmpFloats); + float canvasScaleX = Math.abs(mTmpFloats[Matrix.MSCALE_X]); + float canvasScaleY = Math.abs(mTmpFloats[Matrix.MSCALE_Y]); + int scaledWidth = (int) (mTmpBounds.width() * canvasScaleX); + int scaledHeight = (int) (mTmpBounds.height() * canvasScaleY); + scaledWidth = Math.min(MAX_CACHED_BITMAP_SIZE, scaledWidth); + scaledHeight = Math.min(MAX_CACHED_BITMAP_SIZE, scaledHeight); + + if (scaledWidth <= 0 || scaledHeight <= 0) { + return; + } + final int saveCount = canvas.save(); - final boolean needMirroring = needMirroring(); + canvas.translate(mTmpBounds.left, mTmpBounds.top); - canvas.translate(bounds.left, bounds.top); + // Handle RTL mirroring. + final boolean needMirroring = needMirroring(); if (needMirroring) { - canvas.translate(bounds.width(), 0); + canvas.translate(mTmpBounds.width(), 0); canvas.scale(-1.0f, 1.0f); } - // Color filters always override tint filters. - final ColorFilter colorFilter = mColorFilter == null ? mTintFilter : mColorFilter; + // At this point, canvas has been translated to the right position. + // And we use this bound for the destination rect for the drawBitmap, so + // we offset to (0, 0); + mTmpBounds.offsetTo(0, 0); + mVectorState.createCachedBitmapIfNeeded(scaledWidth, scaledHeight); if (!mAllowCaching) { - // AnimatedVectorDrawable - if (!mVectorState.hasTranslucentRoot()) { - mVectorState.mVPathRenderer.draw( - canvas, bounds.width(), bounds.height(), colorFilter); - } else { - mVectorState.createCachedBitmapIfNeeded(bounds); - mVectorState.updateCachedBitmap(bounds); - mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter); - } + mVectorState.updateCachedBitmap(scaledWidth, scaledHeight); } else { - // Static Vector Drawable case. - mVectorState.createCachedBitmapIfNeeded(bounds); if (!mVectorState.canReuseCache()) { - mVectorState.updateCachedBitmap(bounds); + mVectorState.updateCachedBitmap(scaledWidth, scaledHeight); mVectorState.updateCacheStates(); } - mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter); } - + mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter, mTmpBounds); canvas.restoreToCount(saveCount); } @@ -770,10 +795,11 @@ public class VectorDrawable extends Drawable { } } - public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter) { + public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter, + Rect originalBounds) { // The bitmap's size is the same as the bounds. final Paint p = getPaint(filter); - canvas.drawBitmap(mCachedBitmap, 0, 0, p); + canvas.drawBitmap(mCachedBitmap, null, originalBounds, p); } public boolean hasTranslucentRoot() { @@ -797,16 +823,15 @@ public class VectorDrawable extends Drawable { return mTempPaint; } - public void updateCachedBitmap(Rect bounds) { + public void updateCachedBitmap(int width, int height) { mCachedBitmap.eraseColor(Color.TRANSPARENT); Canvas tmpCanvas = new Canvas(mCachedBitmap); - mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height(), null); + mVPathRenderer.draw(tmpCanvas, width, height, null); } - public void createCachedBitmapIfNeeded(Rect bounds) { - if (mCachedBitmap == null || !canReuseBitmap(bounds.width(), - bounds.height())) { - mCachedBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), + public void createCachedBitmapIfNeeded(int width, int height) { + if (mCachedBitmap == null || !canReuseBitmap(width, height)) { + mCachedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); mCacheDirty = true; } -- cgit v1.1