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
This commit is contained in:
Alan Viverette
2016-10-07 16:23:32 -04:00
parent b8e046a8a2
commit 0d2a46b733
3 changed files with 59 additions and 45 deletions

View File

@ -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 {

View File

@ -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());
}

View File

@ -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) {