Canvas: trying to draw too large(1765130656bytes) bitmap
quekangkang opened this issue · 6 comments
java.lang.RuntimeException: Canvas: trying to draw too large(1765130656bytes) bitmap.
at android.graphics.RecordingCanvas.throwIfCannotDraw(RecordingCanvas.java:280)
at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:88)
at pl.droidsonroids.gif.GifDrawable.draw(Unknown Source:786)
at android.widget.ImageView.onDraw(ImageView.java:1430)
Hello, I met a crash. Here is a 1683.359771728516M bitmap, it is impossiable. When local gif file is damaged, may cause width and height obtain not correctly!
following is the source code I find.
android-gif-drawable/src/main/c/metadata.c
__unused JNIEXPORT jint JNICALL
Java_pl_droidsonroids_gif_GifInfoHandle_getWidth(__unused JNIEnv *env, jclass __unused class, jlong gifInfo) {
GifInfo *info = (GifInfo *) (intptr_t) gifInfo;
if (info == NULL) {
return 0;
}
return (jint) info->gifFilePtr->SWidth;
}
__unused JNIEXPORT jint JNICALL
Java_pl_droidsonroids_gif_GifInfoHandle_getHeight(__unused JNIEnv *env, jclass __unused class, jlong gifInfo) {
GifInfo *info = (GifInfo *) (intptr_t) gifInfo;
if (info == NULL) {
return 0;
}
return (jint) info->gifFilePtr->SHeight;
}
it is not occured on my devices but reported by user online.
I will be appreciated if you could check the issue !
Updated:
I find a method to reproduce the crash
- find one normal gif file
- edit it with notepad++ or other editor can modify it in bytecode.
- modify the the sixth and seventh (correspond with width) to "40","40". and eighth and ninth (correspond with height) to "40',"40".
- run with a demo like following to load the gif file
try {
GifDrawable gifDrawable = new GifDrawable(getResources(), R.mipmap.horrizontal_end_arrow_error);
int intrinsicWidth = gifDrawable.getIntrinsicWidth();
int height = gifDrawable.getIntrinsicHeight();
Log.i(TAG, "width: " + intrinsicWidth + " height: " + height);
imageView.setImageDrawable(gifDrawable);
} catch (IOException e) {
e.printStackTrace();
}
- then the crash reproduced!
I am sorry for cannot upload the damaged gif local file.
refer: https://segmentfault.com/a/1190000020886959
What is your expectation in case of malformed files?
@koral-- Thanks for reply. I have tried create a New Drawable class extends android.graphics.drawable.Drawable and override draw() method,check size before super.draw(), but the draw() method was invoked many times also.
/**
* bitmap {@link RecordingCanvas#MAX_BITMAP_SIZE}
*/
public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024;
@Override
public void draw(@NonNull Canvas canvas) {
int frameByteCount = getFrameByteCount();
if (frameByteCount > MAX_BITMAP_SIZE) {
Log.i("GifDrawableSafe", "frameByteCount > MAX_BITMAP_SIZE frameByeCount: " + frameByteCount + " MAX_BITMAP_SIZE: " + MAX_BITMAP_SIZE);
return;
}
super.draw(canvas);
}
2021-03-15 10:28:24.956 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.041 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.054 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.120 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.137 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.187 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.220 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.270 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.303 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.337 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.387 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
2021-03-15 10:28:25.420 16893-16893/com.example.test1 I/GifDrawableSafe: frameByteCount > MAX_BITMAP_SIZE frameByeCount: 1082146816 MAX_BITMAP_SIZE: 104857600
Maybe add a check code on constructor method is better. Checking bitmap size before create bitmap or after.
pl.droidsonroids.gif.GifDrawable#GifDrawable(pl.droidsonroids.gif.GifInfoHandle, pl.droidsonroids.gif.GifDrawable, java.util.concurrent.ScheduledThreadPoolExecutor, boolean)
if (this.mNativeInfoHandle.getWidth() * this.mNativeInfoHandle.getHeight() * 4 > MAX_BITMAP_SIZE) { //argb for 4 bytes
Log.i("GifDrawable", "trying to add too large gif size : width : " + this.mNativeInfoHandle.getWidth() + "height: " + this.mNativeInfoHandle.getHeight() );
// ... do draw nothing (I am not clearly about the GifDrawable, what I mean is do nothing but not drawing a too big bitmap)
return;
}
if (oldBitmap == null) {
this.mBuffer = Bitmap.createBitmap(this.mNativeInfoHandle.getWidth(), this.mNativeInfoHandle.getHeight(), Config.ARGB_8888);
} else {
this.mBuffer = oldBitmap;
}
Why 100 MB as MAX bitmap size?
Why 100 MB as MAX bitmap size?
Android API hide this constant field.
/** @hide */
public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
...
/** @hide */
@Override
protected void throwIfCannotDraw(Bitmap bitmap) {
super.throwIfCannotDraw(bitmap);
int bitmapSize = bitmap.getByteCount();
if (bitmapSize > MAX_BITMAP_SIZE) {
throw new RuntimeException(
"Canvas: trying to draw too large(" + bitmapSize + "bytes) bitmap.");
}
}
It can be designed to supported setting max value,100 MB is the default value. : )
I don't think that it should be a library responsibility to do checks like that.
One may want to draw on something else than RecordingCanvas
or draw in a customized way or maybe don't draw at all but only get some metadata.
OK, I see. I'll do some checks before setImageResource, thanks !