Loading api/current.txt +4 −6 Original line number Original line Diff line number Diff line Loading @@ -13638,7 +13638,9 @@ package android.graphics { field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2 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 { public static class ImageDecoder.ImageInfo { Loading @@ -13647,16 +13649,12 @@ package android.graphics { method public boolean isAnimated(); method public boolean isAnimated(); } } public static class ImageDecoder.IncompleteException extends java.io.IOException { ctor public ImageDecoder.IncompleteException(); } public static abstract interface ImageDecoder.OnHeaderDecodedListener { public static abstract interface ImageDecoder.OnHeaderDecodedListener { method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source); method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source); } } public static abstract interface ImageDecoder.OnPartialImageListener { 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 { public static abstract class ImageDecoder.Source { api/removed.txt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -157,6 +157,10 @@ package android.graphics { method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean); 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 { public deprecated class LayerRasterizer extends android.graphics.Rasterizer { ctor public LayerRasterizer(); ctor public LayerRasterizer(); method public void addLayer(android.graphics.Paint, float, float); method public void addLayer(android.graphics.Paint, float, float); Loading api/test-current.txt +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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 { package android.graphics.drawable { public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { Loading config/hiddenapi-light-greylist.txt +0 −1 Original line number Original line Diff line number Diff line Loading @@ -669,7 +669,6 @@ Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V Landroid/graphics/GraphicBuffer;->mNativeObject:J Landroid/graphics/GraphicBuffer;->mNativeObject:J Landroid/graphics/ImageDecoder;-><init>(JIIZ)V Landroid/graphics/ImageDecoder;-><init>(JIIZ)V Landroid/graphics/ImageDecoder;->onPartialImage(I)Z Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I Landroid/graphics/LinearGradient;->mColors:[I Landroid/graphics/LinearGradient;->mColors:[I Landroid/graphics/Matrix;->native_instance:J Landroid/graphics/Matrix;->native_instance:J Loading core/jni/android/graphics/ImageDecoder.cpp +85 −72 Original line number Original line Diff line number Diff line Loading @@ -38,48 +38,81 @@ using namespace android; static jclass gImageDecoder_class; static jclass gImageDecoder_class; static jclass gSize_class; static jclass gSize_class; static jclass gIncomplete_class; static jclass gDecodeException_class; static jclass gCanvas_class; static jclass gCanvas_class; static jmethodID gImageDecoder_constructorMethodID; static jmethodID gImageDecoder_constructorMethodID; static jmethodID gImageDecoder_postProcessMethodID; static jmethodID gImageDecoder_postProcessMethodID; static jmethodID gSize_constructorMethodID; static jmethodID gSize_constructorMethodID; static jmethodID gIncomplete_constructorMethodID; static jmethodID gDecodeException_constructorMethodID; static jmethodID gCallback_onPartialImageMethodID; static jmethodID gCallback_onPartialImageMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_releaseMethodID; static jmethodID gCanvas_releaseMethodID; static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { // Clear and return any pending exception for handling other than throwing directly. if (!stream.get()) { static jthrowable get_and_clear_exception(JNIEnv* env) { doThrowIOE(env, "Failed to create a stream"); 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; 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); std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); SkCodec::Result result; SkCodec::Result result; auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get()); 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) { if (!codec) { switch (result) { switch (result) { case SkCodec::kIncompleteInput: case SkCodec::kIncompleteInput: env->ThrowNew(gIncomplete_class, "Incomplete input"); return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source); break; default: default: SkString msg; SkString msg; msg.printf("Failed to create image decoder with message '%s'", msg.printf("Failed to create image decoder with message '%s'", SkCodec::ResultToString(result)); SkCodec::ResultToString(result)); doThrowIOE(env, msg.c_str()); return throw_exception(env, ImageDecoder::kSourceError, msg.c_str(), nullptr, break; source); } } return nullptr; } } // FIXME: Avoid parsing the whole image? const bool animated = codec->getFrameCount() > 1; 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), decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec), SkAndroidCodec::ExifOrientationBehavior::kRespect); SkAndroidCodec::ExifOrientationBehavior::kRespect); if (!decoder->mCodec.get()) { if (!decoder->mCodec.get()) { doThrowIOE(env, "Could not create AndroidCodec"); return throw_exception(env, ImageDecoder::kSourceError, "", nullptr, source); return nullptr; } } const auto& info = decoder->mCodec->getInfo(); const auto& info = decoder->mCodec->getInfo(); const int width = info.width(); const int width = info.width(); const int height = info.height(); const int height = info.height(); Loading @@ -89,26 +122,26 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { } } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor) { jobject fileDescriptor, jobject source) { int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; struct stat fdStat; if (fstat(descriptor, &fdStat) == -1) { if (fstat(descriptor, &fdStat) == -1) { doThrowIOE(env, "broken file descriptor; fstat returned -1"); return throw_exception(env, ImageDecoder::kSourceError, return nullptr; "broken file descriptor; fstat returned -1", nullptr, source); } } int dupDescriptor = dup(descriptor); int dupDescriptor = dup(descriptor); FILE* file = fdopen(dupDescriptor, "r"); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { if (file == NULL) { close(dupDescriptor); close(dupDescriptor); doThrowIOE(env, "Could not open file"); return throw_exception(env, ImageDecoder::kSourceError, "Could not open file", nullptr, return nullptr; source); } } std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); if (::lseek(descriptor, 0, SEEK_CUR) == 0) { 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, // FIXME: This allows us to pretend the current location is the beginning, Loading @@ -116,44 +149,46 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, // point as the beginning. // point as the beginning. std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); 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*/, 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)); std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); if (!stream.get()) { if (!stream.get()) { doThrowIOE(env, "Failed to create stream!"); return throw_exception(env, ImageDecoder::kSourceError, "Failed to create a stream", return nullptr; nullptr, source); } } std::unique_ptr<SkStream> bufferedStream( std::unique_ptr<SkStream> bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); 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); Asset* asset = reinterpret_cast<Asset*>(assetPtr); std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); 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, 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, std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, initialPosition, limit); initialPosition, limit); if (!stream) { if (!stream) { doThrowIOE(env, "Failed to read ByteBuffer"); return throw_exception(env, ImageDecoder::kSourceError, "Failed to read ByteBuffer", return nullptr; 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, 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)); 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) { jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) { Loading @@ -170,10 +205,8 @@ jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<C return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas); 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, static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jobject jdecoder, jboolean jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, jint desiredWidth, jint desiredHeight, jobject jsubset, jboolean requireMutable, jint allocator, jboolean requireMutable, jint allocator, jboolean requireUnpremul, jboolean preferRamOverQuality, jboolean requireUnpremul, jboolean preferRamOverQuality, Loading Loading @@ -264,11 +297,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkAndroidCodec::AndroidOptions options; SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; options.fSampleSize = sampleSize; auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); jthrowable jexception = env->ExceptionOccurred(); jthrowable jexception = get_and_clear_exception(env); if (jexception) { int onPartialImageError = jexception ? ImageDecoder::kSourceException env->ExceptionClear(); } int onPartialImageError = jexception ? 1 // ImageDecoder.java's ERROR_SOURCE_EXCEPTION : 0; // No error. : 0; // No error. switch (result) { switch (result) { case SkCodec::kSuccess: case SkCodec::kSuccess: Loading @@ -278,12 +308,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong break; break; case SkCodec::kIncompleteInput: case SkCodec::kIncompleteInput: if (!jexception) { if (!jexception) { onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION onPartialImageError = ImageDecoder::kSourceIncomplete; } } break; break; case SkCodec::kErrorInInput: case SkCodec::kErrorInInput: if (!jexception) { if (!jexception) { onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR onPartialImageError = ImageDecoder::kSourceError; } } break; break; default: default: Loading @@ -293,24 +323,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong return nullptr; return nullptr; } } if (jexception || onPartialImageError) { if (onPartialImageError) { bool throwException = !jcallback || env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError, !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID, jexception); onPartialImageError); if (env->ExceptionCheck()) { if (env->ExceptionCheck()) { return nullptr; 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; float scaleX = 1.0f; Loading Loading @@ -357,11 +375,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkIRect subset; SkIRect subset; GraphicsJNI::jrect_to_irect(env, jsubset, &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; translateX = -subset.fLeft; translateY = -subset.fTop; translateY = -subset.fTop; desiredWidth = subset.width(); desiredWidth = subset.width(); Loading Loading @@ -404,7 +417,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (jpostProcess) { if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); 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()) { if (env->ExceptionCheck()) { return nullptr; return nullptr; } } Loading Loading @@ -495,12 +508,12 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n } } static const JNINativeMethod gImageDecoderMethods[] = { static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, Loading @@ -516,10 +529,10 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException")); gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException")); gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V"); 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_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); Loading Loading
api/current.txt +4 −6 Original line number Original line Diff line number Diff line Loading @@ -13638,7 +13638,9 @@ package android.graphics { field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2 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 { public static class ImageDecoder.ImageInfo { Loading @@ -13647,16 +13649,12 @@ package android.graphics { method public boolean isAnimated(); method public boolean isAnimated(); } } public static class ImageDecoder.IncompleteException extends java.io.IOException { ctor public ImageDecoder.IncompleteException(); } public static abstract interface ImageDecoder.OnHeaderDecodedListener { public static abstract interface ImageDecoder.OnHeaderDecodedListener { method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source); method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source); } } public static abstract interface ImageDecoder.OnPartialImageListener { 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 { public static abstract class ImageDecoder.Source {
api/removed.txt +4 −0 Original line number Original line Diff line number Diff line Loading @@ -157,6 +157,10 @@ package android.graphics { method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean); 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 { public deprecated class LayerRasterizer extends android.graphics.Rasterizer { ctor public LayerRasterizer(); ctor public LayerRasterizer(); method public void addLayer(android.graphics.Paint, float, float); method public void addLayer(android.graphics.Paint, float, float); Loading
api/test-current.txt +8 −0 Original line number Original line Diff line number Diff line Loading @@ -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 { package android.graphics.drawable { public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { Loading
config/hiddenapi-light-greylist.txt +0 −1 Original line number Original line Diff line number Diff line Loading @@ -669,7 +669,6 @@ Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator; Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V Landroid/graphics/GraphicBuffer;->mNativeObject:J Landroid/graphics/GraphicBuffer;->mNativeObject:J Landroid/graphics/ImageDecoder;-><init>(JIIZ)V Landroid/graphics/ImageDecoder;-><init>(JIIZ)V Landroid/graphics/ImageDecoder;->onPartialImage(I)Z Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I Landroid/graphics/LinearGradient;->mColors:[I Landroid/graphics/LinearGradient;->mColors:[I Landroid/graphics/Matrix;->native_instance:J Landroid/graphics/Matrix;->native_instance:J Loading
core/jni/android/graphics/ImageDecoder.cpp +85 −72 Original line number Original line Diff line number Diff line Loading @@ -38,48 +38,81 @@ using namespace android; static jclass gImageDecoder_class; static jclass gImageDecoder_class; static jclass gSize_class; static jclass gSize_class; static jclass gIncomplete_class; static jclass gDecodeException_class; static jclass gCanvas_class; static jclass gCanvas_class; static jmethodID gImageDecoder_constructorMethodID; static jmethodID gImageDecoder_constructorMethodID; static jmethodID gImageDecoder_postProcessMethodID; static jmethodID gImageDecoder_postProcessMethodID; static jmethodID gSize_constructorMethodID; static jmethodID gSize_constructorMethodID; static jmethodID gIncomplete_constructorMethodID; static jmethodID gDecodeException_constructorMethodID; static jmethodID gCallback_onPartialImageMethodID; static jmethodID gCallback_onPartialImageMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_constructorMethodID; static jmethodID gCanvas_releaseMethodID; static jmethodID gCanvas_releaseMethodID; static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { // Clear and return any pending exception for handling other than throwing directly. if (!stream.get()) { static jthrowable get_and_clear_exception(JNIEnv* env) { doThrowIOE(env, "Failed to create a stream"); 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; 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); std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); SkCodec::Result result; SkCodec::Result result; auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get()); 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) { if (!codec) { switch (result) { switch (result) { case SkCodec::kIncompleteInput: case SkCodec::kIncompleteInput: env->ThrowNew(gIncomplete_class, "Incomplete input"); return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source); break; default: default: SkString msg; SkString msg; msg.printf("Failed to create image decoder with message '%s'", msg.printf("Failed to create image decoder with message '%s'", SkCodec::ResultToString(result)); SkCodec::ResultToString(result)); doThrowIOE(env, msg.c_str()); return throw_exception(env, ImageDecoder::kSourceError, msg.c_str(), nullptr, break; source); } } return nullptr; } } // FIXME: Avoid parsing the whole image? const bool animated = codec->getFrameCount() > 1; 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), decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec), SkAndroidCodec::ExifOrientationBehavior::kRespect); SkAndroidCodec::ExifOrientationBehavior::kRespect); if (!decoder->mCodec.get()) { if (!decoder->mCodec.get()) { doThrowIOE(env, "Could not create AndroidCodec"); return throw_exception(env, ImageDecoder::kSourceError, "", nullptr, source); return nullptr; } } const auto& info = decoder->mCodec->getInfo(); const auto& info = decoder->mCodec->getInfo(); const int width = info.width(); const int width = info.width(); const int height = info.height(); const int height = info.height(); Loading @@ -89,26 +122,26 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { } } static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor) { jobject fileDescriptor, jobject source) { int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; struct stat fdStat; if (fstat(descriptor, &fdStat) == -1) { if (fstat(descriptor, &fdStat) == -1) { doThrowIOE(env, "broken file descriptor; fstat returned -1"); return throw_exception(env, ImageDecoder::kSourceError, return nullptr; "broken file descriptor; fstat returned -1", nullptr, source); } } int dupDescriptor = dup(descriptor); int dupDescriptor = dup(descriptor); FILE* file = fdopen(dupDescriptor, "r"); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { if (file == NULL) { close(dupDescriptor); close(dupDescriptor); doThrowIOE(env, "Could not open file"); return throw_exception(env, ImageDecoder::kSourceError, "Could not open file", nullptr, return nullptr; source); } } std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); if (::lseek(descriptor, 0, SEEK_CUR) == 0) { 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, // FIXME: This allows us to pretend the current location is the beginning, Loading @@ -116,44 +149,46 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, // point as the beginning. // point as the beginning. std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); 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*/, 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)); std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); if (!stream.get()) { if (!stream.get()) { doThrowIOE(env, "Failed to create stream!"); return throw_exception(env, ImageDecoder::kSourceError, "Failed to create a stream", return nullptr; nullptr, source); } } std::unique_ptr<SkStream> bufferedStream( std::unique_ptr<SkStream> bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); 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); Asset* asset = reinterpret_cast<Asset*>(assetPtr); std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); 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, 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, std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, initialPosition, limit); initialPosition, limit); if (!stream) { if (!stream) { doThrowIOE(env, "Failed to read ByteBuffer"); return throw_exception(env, ImageDecoder::kSourceError, "Failed to read ByteBuffer", return nullptr; 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, 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)); 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) { jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) { Loading @@ -170,10 +205,8 @@ jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<C return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas); 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, static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jobject jdecoder, jboolean jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, jint desiredWidth, jint desiredHeight, jobject jsubset, jboolean requireMutable, jint allocator, jboolean requireMutable, jint allocator, jboolean requireUnpremul, jboolean preferRamOverQuality, jboolean requireUnpremul, jboolean preferRamOverQuality, Loading Loading @@ -264,11 +297,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkAndroidCodec::AndroidOptions options; SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; options.fSampleSize = sampleSize; auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); jthrowable jexception = env->ExceptionOccurred(); jthrowable jexception = get_and_clear_exception(env); if (jexception) { int onPartialImageError = jexception ? ImageDecoder::kSourceException env->ExceptionClear(); } int onPartialImageError = jexception ? 1 // ImageDecoder.java's ERROR_SOURCE_EXCEPTION : 0; // No error. : 0; // No error. switch (result) { switch (result) { case SkCodec::kSuccess: case SkCodec::kSuccess: Loading @@ -278,12 +308,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong break; break; case SkCodec::kIncompleteInput: case SkCodec::kIncompleteInput: if (!jexception) { if (!jexception) { onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION onPartialImageError = ImageDecoder::kSourceIncomplete; } } break; break; case SkCodec::kErrorInInput: case SkCodec::kErrorInInput: if (!jexception) { if (!jexception) { onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR onPartialImageError = ImageDecoder::kSourceError; } } break; break; default: default: Loading @@ -293,24 +323,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong return nullptr; return nullptr; } } if (jexception || onPartialImageError) { if (onPartialImageError) { bool throwException = !jcallback || env->CallVoidMethod(jdecoder, gCallback_onPartialImageMethodID, onPartialImageError, !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID, jexception); onPartialImageError); if (env->ExceptionCheck()) { if (env->ExceptionCheck()) { return nullptr; 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; float scaleX = 1.0f; Loading Loading @@ -357,11 +375,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkIRect subset; SkIRect subset; GraphicsJNI::jrect_to_irect(env, jsubset, &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; translateX = -subset.fLeft; translateY = -subset.fTop; translateY = -subset.fTop; desiredWidth = subset.width(); desiredWidth = subset.width(); Loading Loading @@ -404,7 +417,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (jpostProcess) { if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); 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()) { if (env->ExceptionCheck()) { return nullptr; return nullptr; } } Loading Loading @@ -495,12 +508,12 @@ static jstring ImageDecoder_nGetMimeType(JNIEnv* env, jobject /*clazz*/, jlong n } } static const JNINativeMethod gImageDecoderMethods[] = { static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset }, { "nCreate", "(Ljava/nio/ByteBuffer;II)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer }, { "nCreate", "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray }, { "nCreate", "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream }, { "nCreate", "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd }, { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, Loading @@ -516,10 +529,10 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException")); gDecodeException_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$DecodeException")); gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V"); 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_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas")); gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V"); Loading