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

Commit 9f58361e authored by Chris Craik's avatar Chris Craik
Browse files

Support larger bitmaps in BitmapFactory.Options.inBitmap

bug:8121994

Adds a new distiction between bitmap size and the allocation
(pixel ref/buffer) used to store its data.

BitmapFactory.inBitmap will allow a bitmap to be reinitialized with
new data if the bitmap being decoded is (after sampleSize) equal or
smaller.

Change-Id: I747750a735c858882df3af74fca6cdc46f2a9f81
parent 0efd4f02
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8633,6 +8633,7 @@ package android.graphics {
    method public void eraseColor(int);
    method public android.graphics.Bitmap extractAlpha();
    method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
    method public final int getAllocationByteCount();
    method public final int getByteCount();
    method public final android.graphics.Bitmap.Config getConfig();
    method public int getDensity();
+23 −5
Original line number Diff line number Diff line
@@ -211,19 +211,17 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,

    SkBitmap* bitmap;
    bool useExistingBitmap = false;
    unsigned int existingBufferSize = 0;
    if (javaBitmap == NULL) {
        bitmap = new SkBitmap;
    } else {
        if (sampleSize != 1) {
            return nullObjectReturn("SkImageDecoder: Cannot reuse bitmap with sampleSize != 1");
        }

        bitmap = (SkBitmap*) env->GetIntField(javaBitmap, gBitmap_nativeBitmapFieldID);
        // only reuse the provided bitmap if it is immutable
        // only reuse the provided bitmap if it is mutable
        if (!bitmap->isImmutable()) {
            useExistingBitmap = true;
            // config of supplied bitmap overrules config set in options
            prefConfig = bitmap->getConfig();
            existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
        } else {
            ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
            bitmap = new SkBitmap;
@@ -252,6 +250,26 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
        decodeMode = SkImageDecoder::kDecodeBounds_Mode;
    }

    if (javaBitmap != NULL) {
        // If we're reusing the pixelref from an existing bitmap, decode the bounds and
        // reinitialize the native object for the new content, keeping the pixelRef
        SkPixelRef* pixelRef = bitmap->pixelRef();
        SkSafeRef(pixelRef);

        SkBitmap boundsBitmap;
        decoder->decode(stream, &boundsBitmap, prefConfig, SkImageDecoder::kDecodeBounds_Mode);
        stream->rewind();

        if (boundsBitmap.getSize() > existingBufferSize) {
            return nullObjectReturn("bitmap marked for reuse too small to contain decoded data");
        }

        bitmap->setConfig(boundsBitmap.config(), boundsBitmap.width(), boundsBitmap.height(), 0);
        bitmap->setPixelRef(pixelRef);
        SkSafeUnref(pixelRef);
        GraphicsJNI::reinitBitmap(env, javaBitmap);
    }

    SkBitmap* decoded;
    if (willScale) {
        decoded = new SkBitmap;
+13 −0
Original line number Diff line number Diff line
@@ -152,6 +152,8 @@ static jfieldID gPointF_yFieldID;
static jclass   gBitmap_class;
static jfieldID gBitmap_nativeInstanceID;
static jmethodID gBitmap_constructorMethodID;
static jmethodID gBitmap_reinitMethodID;
static jmethodID gBitmap_getAllocationByteCountMethodID;

static jclass   gBitmapConfig_class;
static jfieldID gBitmapConfig_nativeInstanceID;
@@ -363,6 +365,15 @@ jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
    return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
}

void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap)
{
    env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID);
}

int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
{
    return env->CallIntMethod(javaBitmap, gBitmap_getAllocationByteCountMethodID);
}

jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
{
@@ -584,6 +595,8 @@ int register_android_graphics_Graphics(JNIEnv* env)
    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
                                            "(I[BZ[B[II)V");
    gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "()V");
    gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");

+4 −0
Original line number Diff line number Diff line
@@ -59,6 +59,10 @@ public:
    static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
                                jbyteArray ninepatch, int density = -1);

    static void reinitBitmap(JNIEnv* env, jobject javaBitmap);

    static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);

    static jobject createRegion(JNIEnv* env, SkRegion* region);

    static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
+38 −14
Original line number Diff line number Diff line
@@ -85,26 +85,20 @@ public final class Bitmap implements Parcelable {
    }

    /**
     * @noinspection UnusedDeclaration
     */
    /*  Private constructor that must received an already allocated native
        bitmap int (pointer).

        This can be called from JNI code.
     * Private constructor that must received an already allocated native
     * bitmap int (pointer).
     */
    @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
    Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
            int density) {
        this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
    }

    /**
     * @noinspection UnusedDeclaration
     */
    /*  Private constructor that must received an already allocated native
        bitmap int (pointer).

        This can be called from JNI code.
     * Private constructor that must received an already allocated native bitmap
     * int (pointer).
     */
    @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
    Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
            int[] layoutBounds, int density) {
        if (nativeBitmap == 0) {
@@ -124,6 +118,14 @@ public final class Bitmap implements Parcelable {
        }
    }

    /**
     * Native bitmap has been reconfigured, so discard cached width/height
     */
    @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
    void reinit() {
        mWidth = mHeight = -1;
    }

    /**
     * <p>Returns the density for this bitmap.</p>
     *
@@ -454,7 +456,7 @@ public final class Bitmap implements Parcelable {
    /**
     * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
     * specified width and height are the same as the current width and height of 
     * the source btimap, the source bitmap is returned and now new bitmap is
     * the source bitmap, the source bitmap is returned and no new bitmap is
     * created.
     *
     * @param src       The source bitmap.
@@ -989,6 +991,10 @@ public final class Bitmap implements Parcelable {
     * getPixels() or setPixels(), then the pixels are uniformly treated as
     * 32bit values, packed according to the Color class.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, this method
     * should not be used to calculate the memory usage of the bitmap. Instead,
     * see {@link #getAllocationByteCount()}.
     *
     * @return number of bytes between rows of the native bitmap pixels.
     */
    public final int getRowBytes() {
@@ -996,13 +1002,31 @@ public final class Bitmap implements Parcelable {
    }

    /**
     * Returns the number of bytes used to store this bitmap's pixels.
     * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, the result of this method can
     * no longer be used to determine memory usage of a bitmap. See {@link
     * #getAllocationByteCount()}.
     */
    public final int getByteCount() {
        // int result permits bitmaps up to 46,340 x 46,340
        return getRowBytes() * getHeight();
    }

    /**
     * Returns the size of the allocated memory used to store this bitmap's pixels.
     *
     * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
     * decode other bitmaps of smaller size. See {@link BitmapFactory.Options#inBitmap inBitmap in
     * BitmapFactory.Options}. If a bitmap is not reused in this way, this value will be the same as
     * that returned by {@link #getByteCount()}.
     *
     * <p>This value will not change over the lifetime of a Bitmap.
     */
    public final int getAllocationByteCount() {
        return mBuffer.length;
    }

    /**
     * If the bitmap's internal config is in one of the public formats, return
     * that config, otherwise return null.
Loading