Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 134a7602 authored by Valentin Iftime's avatar Valentin Iftime Committed by Automerger Merge Worker
Browse files

[DO NOT MERGE] Limit Icon Drawables to MAX_BITMAP_SIZE am: 640966b4

parents cde3d32c 640966b4
Loading
Loading
Loading
Loading
+151 KiB
Loading image diff...
+85 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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 ========

+48 −8
Original line number Diff line number Diff line
@@ -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;
@@ -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)}.
@@ -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
@@ -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(),
@@ -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;
        }