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

Commit 1a823488 authored by Leon Scroggins III's avatar Leon Scroggins III Committed by android-build-merger
Browse files

Re-write onPartialImage API

am: 1d2bf2b8

Change-Id: I7436bbe89b9efd9a406958b14f79294e982c5b3a
parents 153592fa 1d2bf2b8
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
@@ -13638,7 +13638,9 @@ package android.graphics {
    field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
  }
  public static abstract class ImageDecoder.Error implements java.lang.annotation.Annotation {
  public static final class ImageDecoder.DecodeException extends java.io.IOException {
    method public int getError();
    method public android.graphics.ImageDecoder.Source getSource();
  }
  public static class ImageDecoder.ImageInfo {
@@ -13647,16 +13649,12 @@ package android.graphics {
    method public boolean isAnimated();
  }
  public static class ImageDecoder.IncompleteException extends java.io.IOException {
    ctor public ImageDecoder.IncompleteException();
  }
  public static abstract interface ImageDecoder.OnHeaderDecodedListener {
    method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source);
  }
  public static abstract interface ImageDecoder.OnPartialImageListener {
    method public abstract boolean onPartialImage(int, android.graphics.ImageDecoder.Source);
    method public abstract boolean onPartialImage(android.graphics.ImageDecoder.DecodeException);
  }
  public static abstract class ImageDecoder.Source {
+4 −0
Original line number Diff line number Diff line
@@ -157,6 +157,10 @@ package android.graphics {
    method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
  }

  public static deprecated class ImageDecoder.IncompleteException extends java.io.IOException {
    ctor public ImageDecoder.IncompleteException();
  }

  public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
    ctor public LayerRasterizer();
    method public void addLayer(android.graphics.Paint, float, float);
+8 −0
Original line number Diff line number Diff line
@@ -295,6 +295,14 @@ package android.database.sqlite {

}

package android.graphics {

  public final class ImageDecoder implements java.lang.AutoCloseable {
    method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
  }

}

package android.graphics.drawable {

  public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
+0 −1
Original line number Diff line number Diff line
@@ -669,7 +669,6 @@ Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
Landroid/graphics/GraphicBuffer;->mNativeObject:J
Landroid/graphics/ImageDecoder;-><init>(JIIZ)V
Landroid/graphics/ImageDecoder;->onPartialImage(I)Z
Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
Landroid/graphics/LinearGradient;->mColors:[I
Landroid/graphics/Matrix;->native_instance:J
+85 −72
Original line number Diff line number Diff line
@@ -38,48 +38,81 @@ using namespace android;

static jclass    gImageDecoder_class;
static jclass    gSize_class;
static jclass    gIncomplete_class;
static jclass    gDecodeException_class;
static jclass    gCanvas_class;
static jmethodID gImageDecoder_constructorMethodID;
static jmethodID gImageDecoder_postProcessMethodID;
static jmethodID gSize_constructorMethodID;
static jmethodID gIncomplete_constructorMethodID;
static jmethodID gDecodeException_constructorMethodID;
static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;

static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
    if (!stream.get()) {
        doThrowIOE(env, "Failed to create a stream");
// Clear and return any pending exception for handling other than throwing directly.
static jthrowable get_and_clear_exception(JNIEnv* env) {
    jthrowable jexception = env->ExceptionOccurred();
    if (jexception) {
        env->ExceptionClear();
    }
    return jexception;
}

// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const char* msg,
                               jthrowable cause, jobject source) {
    jstring jstr = nullptr;
    if (msg) {
        jstr = env->NewStringUTF(msg);
        if (!jstr) {
            // Out of memory.
            return nullptr;
        }
    }
    jthrowable exception = (jthrowable) env->NewObject(gDecodeException_class,
            gDecodeException_constructorMethodID, error, jstr, cause, source);
    // Only throw if not out of memory.
    if (exception) {
        env->Throw(exception);
    }
    return nullptr;
}

static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
    if (!stream.get()) {
        return throw_exception(env, ImageDecoder::kSourceError, "Failed to create a stream",
                               nullptr, source);
    }
    std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
    SkCodec::Result result;
    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
    if (jthrowable jexception = get_and_clear_exception(env)) {
        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
    }
    if (!codec) {
        switch (result) {
            case SkCodec::kIncompleteInput:
                env->ThrowNew(gIncomplete_class, "Incomplete input");
                break;
                return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source);
            default:
                SkString msg;
                msg.printf("Failed to create image decoder with message '%s'",
                           SkCodec::ResultToString(result));
                doThrowIOE(env, msg.c_str());
                break;
                return throw_exception(env, ImageDecoder::kSourceError,  msg.c_str(), nullptr,
                                       source);

        }
        return nullptr;
    }

    // FIXME: Avoid parsing the whole image?
    const bool animated = codec->getFrameCount() > 1;
    if (jthrowable jexception = get_and_clear_exception(env)) {
        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
    }

    decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
            SkAndroidCodec::ExifOrientationBehavior::kRespect);
    if (!decoder->mCodec.get()) {
        doThrowIOE(env, "Could not create AndroidCodec");
        return nullptr;
        return throw_exception(env, ImageDecoder::kSourceError, "", nullptr, source);
    }

    const auto& info = decoder->mCodec->getInfo();
    const int width = info.width();
    const int height = info.height();
@@ -89,26 +122,26 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) {
}

static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
        jobject fileDescriptor) {
        jobject fileDescriptor, jobject source) {
    int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);

    struct stat fdStat;
    if (fstat(descriptor, &fdStat) == -1) {
        doThrowIOE(env, "broken file descriptor; fstat returned -1");
        return nullptr;
        return throw_exception(env, ImageDecoder::kSourceError,
                               "broken file descriptor; fstat returned -1", nullptr, source);
    }

    int dupDescriptor = dup(descriptor);
    FILE* file = fdopen(dupDescriptor, "r");
    if (file == NULL) {
        close(dupDescriptor);
        doThrowIOE(env, "Could not open file");
        return nullptr;
        return throw_exception(env, ImageDecoder::kSourceError, "Could not open file", nullptr,
                               source);
    }
    std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));

    if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
        return native_create(env, std::move(fileStream));
        return native_create(env, std::move(fileStream), source);
    }

    // FIXME: This allows us to pretend the current location is the beginning,
@@ -116,44 +149,46 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
    // point as the beginning.
    std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream),
                SkCodec::MinBufferedBytesNeeded()));
    return native_create(env, std::move(stream));
    return native_create(env, std::move(stream), source);
}

static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
        jobject is, jbyteArray storage) {
        jobject is, jbyteArray storage, jobject source) {
    std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));

    if (!stream.get()) {
        doThrowIOE(env, "Failed to create stream!");
        return nullptr;
        return throw_exception(env, ImageDecoder::kSourceError, "Failed to create a stream",
                               nullptr, source);
    }

    std::unique_ptr<SkStream> bufferedStream(
        SkFrontBufferedStream::Make(std::move(stream),
        SkCodec::MinBufferedBytesNeeded()));
    return native_create(env, std::move(bufferedStream));
    return native_create(env, std::move(bufferedStream), source);
}

static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) {
static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
                                         jobject source) {
    Asset* asset = reinterpret_cast<Asset*>(assetPtr);
    std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
    return native_create(env, std::move(stream));
    return native_create(env, std::move(stream), source);
}

static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
                                              jint initialPosition, jint limit) {
                                              jint initialPosition, jint limit, jobject source) {
    std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
                                                                     initialPosition, limit);
    if (!stream) {
        doThrowIOE(env, "Failed to read ByteBuffer");
        return nullptr;
        return throw_exception(env, ImageDecoder::kSourceError, "Failed to read ByteBuffer",
                               nullptr, source);
    }
    return native_create(env, std::move(stream));
    return native_create(env, std::move(stream), source);
}

static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
                                             jint offset, jint length) {
                                             jint offset, jint length, jobject source) {
    std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
    return native_create(env, std::move(stream));
    return native_create(env, std::move(stream), source);
}

jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -170,10 +205,8 @@ jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<C
    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
}

// Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
// otherwise.
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                          jobject jcallback, jobject jpostProcess,
                                          jobject jdecoder, jboolean jpostProcess,
                                          jint desiredWidth, jint desiredHeight, jobject jsubset,
                                          jboolean requireMutable, jint allocator,
                                          jboolean requireUnpremul, jboolean preferRamOverQuality,
@@ -264,11 +297,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    SkAndroidCodec::AndroidOptions options;
    options.fSampleSize = sampleSize;
    auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
    jthrowable jexception = env->ExceptionOccurred();
    if (jexception) {
        env->ExceptionClear();
    }
    int onPartialImageError = jexception ? 1  // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
    jthrowable jexception = get_and_clear_exception(env);
    int onPartialImageError = jexception ? ImageDecoder::kSourceException
                                         : 0; // No error.
    switch (result) {
        case SkCodec::kSuccess:
@@ -278,12 +308,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
            break;
        case SkCodec::kIncompleteInput:
            if (!jexception) {
                onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
                onPartialImageError = ImageDecoder::kSourceIncomplete;
            }
            break;
        case SkCodec::kErrorInInput:
            if (!jexception) {
                onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR
                onPartialImageError = ImageDecoder::kSourceError;
            }
            break;
        default:
@@ -293,24 +323,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
            return nullptr;
    }

    if (jexception || onPartialImageError) {
        bool throwException = !jcallback ||
            !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
                                    onPartialImageError);
    if (onPartialImageError) {
        env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError,
                jexception);
        if (env->ExceptionCheck()) {
            return nullptr;
        }

        if (throwException) {
            if (jexception) {
                env->Throw(jexception);
            } else if (onPartialImageError == 2) {
                env->ThrowNew(gIncomplete_class, "Incomplete input");
            } else {
                doThrowIOE(env, "image has an error!");
            }
            return nullptr;
        }
    }

    float scaleX = 1.0f;
@@ -357,11 +375,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
            SkIRect subset;
            GraphicsJNI::jrect_to_irect(env, jsubset, &subset);

            // FIXME: If there is no scale, should this instead call
            // SkBitmap::extractSubset? If we could upload a subset
            // (b/70626068), this would save memory and time. Even for a
            // software Bitmap, the extra speed might be worth the memory
            // tradeoff if the subset is large?
            translateX    = -subset.fLeft;
            translateY    = -subset.fTop;
            desiredWidth  =  subset.width();
@@ -404,7 +417,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    if (jpostProcess) {
        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));

        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas));
        jint pixelFormat = postProcessAndRelease(env, jdecoder, std::move(canvas));
        if (env->ExceptionCheck()) {
            return nullptr;
        }
@@ -495,12 +508,12 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n
}

static const JNINativeMethod gImageDecoderMethods[] = {
    { "nCreate",        "(J)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
    { "nCreate",        "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
    { "nCreate",        "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
    { "nCreate",        "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
    { "nCreate",        "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
    { "nCreate",        "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
    { "nCreate",        "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
    { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
    { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
    { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
                                                                 (void*) ImageDecoder_nDecodeBitmap },
    { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
    { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
@@ -516,10 +529,10 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) {
    gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
    gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");

    gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException"));
    gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V");
    gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException"));
    gDecodeException_constructorMethodID = GetMethodIDOrDie(env, gDecodeException_class, "<init>", "(ILjava/lang/String;Ljava/lang/Throwable;Landroid/graphics/ImageDecoder$Source;)V");

    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(I)Z");
    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(ILjava/lang/Throwable;)V");

    gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
    gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
Loading