From 0d2a46b7336b6d255f202b878003be59ecbae52b Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Fri, 7 Oct 2016 16:23:32 -0400 Subject: [PATCH] Scale bitmap shaders for target density Also fixes progress bar sample tile to reflect density and ensures that ProgressBar.tileify() clones inner drawables into the correct density. Bug: 31841123 Test: BitmapDrawableTest#testPreloadDensity() Test: ThemeHostTest Test: Visual inspection of ApiDemos Change-Id: I9dcb9817d8d91d61ff0215987247e9e7fb089c46 --- core/java/android/widget/ProgressBar.java | 16 ++-- core/java/android/widget/RatingBar.java | 6 +- .../graphics/drawable/BitmapDrawable.java | 82 +++++++++++-------- 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 3c967acdfc558..1f379c94cc03a 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -230,7 +231,7 @@ public class ProgressBar extends View { private Drawable mCurrentDrawable; private ProgressTintInfo mProgressTintInfo; - Bitmap mSampleTile; + int mSampleWidth = 0; private boolean mNoInvalidate; private Interpolator mInterpolator; private RefreshProgressRunnable mRefreshProgressRunnable; @@ -505,15 +506,14 @@ public class ProgressBar extends View { } if (drawable instanceof BitmapDrawable) { - final BitmapDrawable bitmap = (BitmapDrawable) drawable; - final Bitmap tileBitmap = bitmap.getBitmap(); - if (mSampleTile == null) { - mSampleTile = tileBitmap; - } - - final BitmapDrawable clone = (BitmapDrawable) bitmap.getConstantState().newDrawable(); + final Drawable.ConstantState cs = drawable.getConstantState(); + final BitmapDrawable clone = (BitmapDrawable) cs.newDrawable(getResources()); clone.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); + if (mSampleWidth <= 0) { + mSampleWidth = clone.getIntrinsicWidth(); + } + if (clip) { return new ClipDrawable(clone, Gravity.LEFT, ClipDrawable.HORIZONTAL); } else { diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java index 3ad05b5e7c01f..62dd90fddcd0c 100644 --- a/core/java/android/widget/RatingBar.java +++ b/core/java/android/widget/RatingBar.java @@ -281,10 +281,8 @@ public class RatingBar extends AbsSeekBar { protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (mSampleTile != null) { - // TODO: Once ProgressBar's TODOs are gone, this can be done more - // cleanly than mSampleTile - final int width = mSampleTile.getWidth() * mNumStars; + if (mSampleWidth > 0) { + final int width = mSampleWidth * mNumStars; setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0), getMeasuredHeight()); } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 9d8ede0489249..df107f5ce3ef5 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -463,31 +463,14 @@ public class BitmapDrawable extends Drawable { return isAutoMirrored() && getLayoutDirection() == LayoutDirection.RTL; } - private void updateMirrorMatrix(float dx) { - if (mMirrorMatrix == null) { - mMirrorMatrix = new Matrix(); - } - mMirrorMatrix.setTranslate(dx, 0); - mMirrorMatrix.preScale(-1.0f, 1.0f); - } - @Override protected void onBoundsChange(Rect bounds) { mDstRectAndInsetsDirty = true; + final Bitmap bitmap = mBitmapState.mBitmap; final Shader shader = mBitmapState.mPaint.getShader(); - if (shader != null) { - if (needMirroring()) { - updateMirrorMatrix(bounds.right - bounds.left); - shader.setLocalMatrix(mMirrorMatrix); - mBitmapState.mPaint.setShader(shader); - } else { - if (mMirrorMatrix != null) { - mMirrorMatrix = null; - shader.setLocalMatrix(Matrix.IDENTITY_MATRIX); - mBitmapState.mPaint.setShader(shader); - } - } + if (bitmap != null && shader != null) { + updateShaderMatrix(bitmap, mBitmapState.mPaint, shader, needMirroring()); } } @@ -548,19 +531,7 @@ public class BitmapDrawable extends Drawable { canvas.restore(); } } else { - if (needMirroring) { - // Mirror the bitmap - updateMirrorMatrix(mDstRect.right - mDstRect.left); - shader.setLocalMatrix(mMirrorMatrix); - paint.setShader(shader); - } else { - if (mMirrorMatrix != null) { - mMirrorMatrix = null; - shader.setLocalMatrix(Matrix.IDENTITY_MATRIX); - paint.setShader(shader); - } - } - + updateShaderMatrix(bitmap, paint, shader, needMirroring); canvas.drawRect(mDstRect, paint); } @@ -573,6 +544,51 @@ public class BitmapDrawable extends Drawable { } } + /** + * Updates the {@code paint}'s shader matrix to be consistent with the + * destination size and layout direction. + * + * @param bitmap the bitmap to be drawn + * @param paint the paint used to draw the bitmap + * @param shader the shader to set on the paint + * @param needMirroring whether the bitmap should be mirrored + */ + private void updateShaderMatrix(@NonNull Bitmap bitmap, @NonNull Paint paint, + @NonNull Shader shader, boolean needMirroring) { + final int sourceDensity = bitmap.getDensity(); + final int targetDensity = mTargetDensity; + final boolean needScaling = sourceDensity != 0 && sourceDensity != targetDensity; + if (needScaling || needMirroring) { + final Matrix matrix = getOrCreateMirrorMatrix(); + matrix.reset(); + + if (needMirroring) { + final int dx = mDstRect.right - mDstRect.left; + matrix.setTranslate(dx, 0); + matrix.setScale(-1, 1); + } + + if (needScaling) { + final float densityScale = targetDensity / (float) sourceDensity; + matrix.postScale(densityScale, densityScale); + } + + shader.setLocalMatrix(matrix); + } else { + mMirrorMatrix = null; + shader.setLocalMatrix(Matrix.IDENTITY_MATRIX); + } + + paint.setShader(shader); + } + + private Matrix getOrCreateMirrorMatrix() { + if (mMirrorMatrix == null) { + mMirrorMatrix = new Matrix(); + } + return mMirrorMatrix; + } + private void updateDstRectAndInsetsIfDirty() { if (mDstRectAndInsetsDirty) { if (mBitmapState.mTileModeX == null && mBitmapState.mTileModeY == null) {