Loading core/tests/coretests/res/drawable-nodpi/test_too_big.png 0 → 100644 +151 KiB Loading image diff... core/tests/coretests/src/android/graphics/drawable/IconTest.java +85 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.RecordingCanvas; import android.graphics.Region; import android.os.Handler; import android.os.HandlerThread; Loading Loading @@ -371,6 +372,90 @@ public class IconTest extends AndroidTestCase { } } private int getMaxWidth(int origWidth, int origHeight, int maxNumPixels) { float aspRatio = (float) origWidth / (float) origHeight; int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio); return (int) (newHeight * aspRatio); } private int getMaxHeight(int origWidth, int origHeight, int maxNumPixels) { float aspRatio = (float) origWidth / (float) origHeight; return (int) Math.sqrt(maxNumPixels / aspRatio); } @SmallTest public void testScaleDownMaxSizeWithBitmap() throws Exception { final int bmpWidth = 13_000; final int bmpHeight = 10_000; final int bmpBpp = 4; final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp; final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels); final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels); final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); final Icon ic = Icon.createWithBitmap(bm); final Drawable drawable = ic.loadDrawable(mContext); assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth); assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight); } @SmallTest public void testScaleDownMaxSizeWithAdaptiveBitmap() throws Exception { final int bmpWidth = 20_000; final int bmpHeight = 10_000; final int bmpBpp = 4; final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp; final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels); final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels); final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); final Icon ic = Icon.createWithAdaptiveBitmap(bm); final AdaptiveIconDrawable adaptiveDrawable = (AdaptiveIconDrawable) ic.loadDrawable( mContext); final Drawable drawable = adaptiveDrawable.getForeground(); assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth); assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight); } @SmallTest public void testScaleDownMaxSizeWithResource() throws Exception { final Icon ic = Icon.createWithResource(getContext(), R.drawable.test_too_big); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } @SmallTest public void testScaleDownMaxSizeWithFile() throws Exception { final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.test_too_big)) .getBitmap(); final File dir = getContext().getExternalFilesDir(null); final File file1 = new File(dir, "file1-too-big.png"); bit1.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file1)); final Icon ic = Icon.createWithFilePath(file1.toString()); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } @SmallTest public void testScaleDownMaxSizeWithData() throws Exception { final int bmpBpp = 4; final Bitmap originalBits = ((BitmapDrawable) getContext().getDrawable( R.drawable.test_too_big)).getBitmap(); final ByteArrayOutputStream ostream = new ByteArrayOutputStream( originalBits.getWidth() * originalBits.getHeight() * bmpBpp); originalBits.compress(Bitmap.CompressFormat.PNG, 100, ostream); final byte[] pngdata = ostream.toByteArray(); final Icon ic = Icon.createWithData(pngdata, 0, pngdata.length); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } // ======== utils ======== Loading graphics/java/android/graphics/drawable/Icon.java +48 −8 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BlendMode; import android.graphics.PorterDuff; import android.graphics.RecordingCanvas; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; Loading Loading @@ -70,6 +71,7 @@ import java.util.Objects; public final class Icon implements Parcelable { private static final String TAG = "Icon"; private static final boolean DEBUG = false; /** * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}. Loading Loading @@ -360,16 +362,53 @@ public final class Icon implements Parcelable { return result; } /** * Resizes image if size too large for Canvas to draw * @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE} * @return resized bitmap */ private Bitmap fixMaxBitmapSize(Bitmap bitmap) { if (bitmap != null && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) { int bytesPerPixel = bitmap.getRowBytes() / bitmap.getWidth(); int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bytesPerPixel; float aspRatio = (float) bitmap.getWidth() / (float) bitmap.getHeight(); int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio); int newWidth = (int) (newHeight * aspRatio); if (DEBUG) { Log.d(TAG, "Image size too large: " + bitmap.getByteCount() + ". Resizing bitmap to: " + newWidth + " " + newHeight); } return scaleDownIfNecessary(bitmap, newWidth, newHeight); } return bitmap; } /** * Resizes BitmapDrawable if size too large for Canvas to draw * @param drawable Drawable to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE} * @return resized Drawable */ private Drawable fixMaxBitmapSize(Resources res, Drawable drawable) { if (drawable instanceof BitmapDrawable) { Bitmap scaledBmp = fixMaxBitmapSize(((BitmapDrawable) drawable).getBitmap()); return new BitmapDrawable(res, scaledBmp); } return drawable; } /** * Do the heavy lifting of loading the drawable, but stop short of applying any tint. */ private Drawable loadDrawableInner(Context context) { switch (mType) { case TYPE_BITMAP: return new BitmapDrawable(context.getResources(), getBitmap()); return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap())); case TYPE_ADAPTIVE_BITMAP: return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(), getBitmap())); new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap()))); case TYPE_RESOURCE: if (getResources() == null) { // figure out where to load resources from Loading Loading @@ -400,7 +439,8 @@ public final class Icon implements Parcelable { } } try { return getResources().getDrawable(getResId(), context.getTheme()); return fixMaxBitmapSize(getResources(), getResources().getDrawable(getResId(), context.getTheme())); } catch (RuntimeException e) { Log.e(TAG, String.format("Unable to load resource 0x%08x from pkg=%s", getResId(), Loading @@ -409,21 +449,21 @@ public final class Icon implements Parcelable { } break; case TYPE_DATA: return new BitmapDrawable(context.getResources(), BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength()) ); return new BitmapDrawable(context.getResources(), fixMaxBitmapSize( BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength()))); case TYPE_URI: InputStream is = getUriInputStream(context); if (is != null) { return new BitmapDrawable(context.getResources(), BitmapFactory.decodeStream(is)); fixMaxBitmapSize(BitmapFactory.decodeStream(is))); } break; case TYPE_URI_ADAPTIVE_BITMAP: is = getUriInputStream(context); if (is != null) { return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(), BitmapFactory.decodeStream(is))); fixMaxBitmapSize(BitmapFactory.decodeStream(is)))); } break; } Loading Loading
core/tests/coretests/src/android/graphics/drawable/IconTest.java +85 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.RecordingCanvas; import android.graphics.Region; import android.os.Handler; import android.os.HandlerThread; Loading Loading @@ -371,6 +372,90 @@ public class IconTest extends AndroidTestCase { } } private int getMaxWidth(int origWidth, int origHeight, int maxNumPixels) { float aspRatio = (float) origWidth / (float) origHeight; int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio); return (int) (newHeight * aspRatio); } private int getMaxHeight(int origWidth, int origHeight, int maxNumPixels) { float aspRatio = (float) origWidth / (float) origHeight; return (int) Math.sqrt(maxNumPixels / aspRatio); } @SmallTest public void testScaleDownMaxSizeWithBitmap() throws Exception { final int bmpWidth = 13_000; final int bmpHeight = 10_000; final int bmpBpp = 4; final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp; final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels); final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels); final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); final Icon ic = Icon.createWithBitmap(bm); final Drawable drawable = ic.loadDrawable(mContext); assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth); assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight); } @SmallTest public void testScaleDownMaxSizeWithAdaptiveBitmap() throws Exception { final int bmpWidth = 20_000; final int bmpHeight = 10_000; final int bmpBpp = 4; final int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bmpBpp; final int maxWidth = getMaxWidth(bmpWidth, bmpHeight, maxNumPixels); final int maxHeight = getMaxHeight(bmpWidth, bmpHeight, maxNumPixels); final Bitmap bm = Bitmap.createBitmap(bmpWidth, bmpHeight, Bitmap.Config.ARGB_8888); final Icon ic = Icon.createWithAdaptiveBitmap(bm); final AdaptiveIconDrawable adaptiveDrawable = (AdaptiveIconDrawable) ic.loadDrawable( mContext); final Drawable drawable = adaptiveDrawable.getForeground(); assertThat(drawable.getIntrinsicWidth()).isEqualTo(maxWidth); assertThat(drawable.getIntrinsicHeight()).isEqualTo(maxHeight); } @SmallTest public void testScaleDownMaxSizeWithResource() throws Exception { final Icon ic = Icon.createWithResource(getContext(), R.drawable.test_too_big); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } @SmallTest public void testScaleDownMaxSizeWithFile() throws Exception { final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.test_too_big)) .getBitmap(); final File dir = getContext().getExternalFilesDir(null); final File file1 = new File(dir, "file1-too-big.png"); bit1.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(file1)); final Icon ic = Icon.createWithFilePath(file1.toString()); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } @SmallTest public void testScaleDownMaxSizeWithData() throws Exception { final int bmpBpp = 4; final Bitmap originalBits = ((BitmapDrawable) getContext().getDrawable( R.drawable.test_too_big)).getBitmap(); final ByteArrayOutputStream ostream = new ByteArrayOutputStream( originalBits.getWidth() * originalBits.getHeight() * bmpBpp); originalBits.compress(Bitmap.CompressFormat.PNG, 100, ostream); final byte[] pngdata = ostream.toByteArray(); final Icon ic = Icon.createWithData(pngdata, 0, pngdata.length); final BitmapDrawable drawable = (BitmapDrawable) ic.loadDrawable(mContext); assertThat(drawable.getBitmap().getByteCount()).isAtMost(RecordingCanvas.MAX_BITMAP_SIZE); } // ======== utils ======== Loading
graphics/java/android/graphics/drawable/Icon.java +48 −8 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BlendMode; import android.graphics.PorterDuff; import android.graphics.RecordingCanvas; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; Loading Loading @@ -70,6 +71,7 @@ import java.util.Objects; public final class Icon implements Parcelable { private static final String TAG = "Icon"; private static final boolean DEBUG = false; /** * An icon that was created using {@link Icon#createWithBitmap(Bitmap)}. Loading Loading @@ -360,16 +362,53 @@ public final class Icon implements Parcelable { return result; } /** * Resizes image if size too large for Canvas to draw * @param bitmap Bitmap to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE} * @return resized bitmap */ private Bitmap fixMaxBitmapSize(Bitmap bitmap) { if (bitmap != null && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) { int bytesPerPixel = bitmap.getRowBytes() / bitmap.getWidth(); int maxNumPixels = RecordingCanvas.MAX_BITMAP_SIZE / bytesPerPixel; float aspRatio = (float) bitmap.getWidth() / (float) bitmap.getHeight(); int newHeight = (int) Math.sqrt(maxNumPixels / aspRatio); int newWidth = (int) (newHeight * aspRatio); if (DEBUG) { Log.d(TAG, "Image size too large: " + bitmap.getByteCount() + ". Resizing bitmap to: " + newWidth + " " + newHeight); } return scaleDownIfNecessary(bitmap, newWidth, newHeight); } return bitmap; } /** * Resizes BitmapDrawable if size too large for Canvas to draw * @param drawable Drawable to be resized if size > {@link RecordingCanvas.MAX_BITMAP_SIZE} * @return resized Drawable */ private Drawable fixMaxBitmapSize(Resources res, Drawable drawable) { if (drawable instanceof BitmapDrawable) { Bitmap scaledBmp = fixMaxBitmapSize(((BitmapDrawable) drawable).getBitmap()); return new BitmapDrawable(res, scaledBmp); } return drawable; } /** * Do the heavy lifting of loading the drawable, but stop short of applying any tint. */ private Drawable loadDrawableInner(Context context) { switch (mType) { case TYPE_BITMAP: return new BitmapDrawable(context.getResources(), getBitmap()); return new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap())); case TYPE_ADAPTIVE_BITMAP: return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(), getBitmap())); new BitmapDrawable(context.getResources(), fixMaxBitmapSize(getBitmap()))); case TYPE_RESOURCE: if (getResources() == null) { // figure out where to load resources from Loading Loading @@ -400,7 +439,8 @@ public final class Icon implements Parcelable { } } try { return getResources().getDrawable(getResId(), context.getTheme()); return fixMaxBitmapSize(getResources(), getResources().getDrawable(getResId(), context.getTheme())); } catch (RuntimeException e) { Log.e(TAG, String.format("Unable to load resource 0x%08x from pkg=%s", getResId(), Loading @@ -409,21 +449,21 @@ public final class Icon implements Parcelable { } break; case TYPE_DATA: return new BitmapDrawable(context.getResources(), BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength()) ); return new BitmapDrawable(context.getResources(), fixMaxBitmapSize( BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength()))); case TYPE_URI: InputStream is = getUriInputStream(context); if (is != null) { return new BitmapDrawable(context.getResources(), BitmapFactory.decodeStream(is)); fixMaxBitmapSize(BitmapFactory.decodeStream(is))); } break; case TYPE_URI_ADAPTIVE_BITMAP: is = getUriInputStream(context); if (is != null) { return new AdaptiveIconDrawable(null, new BitmapDrawable(context.getResources(), BitmapFactory.decodeStream(is))); fixMaxBitmapSize(BitmapFactory.decodeStream(is)))); } break; } Loading