Loading core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +85 −43 Original line number Diff line number Diff line Loading @@ -12,21 +12,62 @@ static jmethodID gInputStream_readMethodID; static jmethodID gInputStream_skipMethodID; // FIXME: Share with ByteBufferStreamAdaptor.cpp? static JNIEnv* get_env_or_die(JavaVM* jvm) { JNIEnv* env; if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); } return env; } /** * Wrapper for a Java InputStream. */ class JavaInputStreamAdaptor : public SkStream { JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity, bool swallowExceptions) : fJvm(jvm) , fJavaInputStream(js) , fJavaByteArray(ar) , fCapacity(capacity) , fBytesRead(0) , fIsAtEnd(false) , fSwallowExceptions(swallowExceptions) {} public: JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { SkASSERT(ar); fCapacity = env->GetArrayLength(ar); SkASSERT(fCapacity > 0); fBytesRead = 0; fIsAtEnd = false; static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar, bool swallowExceptions) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); js = env->NewGlobalRef(js); if (!js) { return nullptr; } ar = (jbyteArray) env->NewGlobalRef(ar); if (!ar) { env->DeleteGlobalRef(js); return nullptr; } virtual size_t read(void* buffer, size_t size) { jint capacity = env->GetArrayLength(ar); return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions); } ~JavaInputStreamAdaptor() override { auto* env = get_env_or_die(fJvm); env->DeleteGlobalRef(fJavaInputStream); env->DeleteGlobalRef(fJavaByteArray); } size_t read(void* buffer, size_t size) override { auto* env = get_env_or_die(fJvm); if (!fSwallowExceptions && checkException(env)) { // Just in case the caller did not clear from a previous exception. return 0; } if (NULL == buffer) { if (0 == size) { return 0; Loading @@ -37,10 +78,10 @@ public: */ size_t amountSkipped = 0; do { size_t amount = this->doSkip(size - amountSkipped); size_t amount = this->doSkip(size - amountSkipped, env); if (0 == amount) { char tmp; amount = this->doRead(&tmp, 1); amount = this->doRead(&tmp, 1, env); if (0 == amount) { // if read returned 0, we're at EOF fIsAtEnd = true; Loading @@ -52,16 +93,13 @@ public: return amountSkipped; } } return this->doRead(buffer, size); return this->doRead(buffer, size, env); } virtual bool isAtEnd() const { return fIsAtEnd; } bool isAtEnd() const override { return fIsAtEnd; } private: size_t doRead(void* buffer, size_t size) { JNIEnv* env = fEnv; size_t doRead(void* buffer, size_t size, JNIEnv* env) { size_t bytesRead = 0; // read the bytes do { Loading @@ -76,13 +114,9 @@ private: jint n = env->CallIntMethod(fJavaInputStream, gInputStream_readMethodID, fJavaByteArray, 0, requested); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("---- read threw an exception\n"); // Consider the stream to be at the end, since there was an error. fIsAtEnd = true; return 0; return bytesRead; } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. Loading @@ -92,14 +126,9 @@ private: env->GetByteArrayRegion(fJavaByteArray, 0, n, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); // The error was not with the stream itself, but consider it to be at the // end, since we do not have a way to recover. fIsAtEnd = true; return 0; return bytesRead; } buffer = (void*)((char*)buffer + n); Loading @@ -111,14 +140,10 @@ private: return bytesRead; } size_t doSkip(size_t size) { JNIEnv* env = fEnv; size_t doSkip(size_t size, JNIEnv* env) { jlong skipped = env->CallLongMethod(fJavaInputStream, gInputStream_skipMethodID, (jlong)size); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("------- skip threw an exception\n"); return 0; } Loading @@ -129,20 +154,37 @@ private: return (size_t)skipped; } JNIEnv* fEnv; jobject fJavaInputStream; // the caller owns this object jbyteArray fJavaByteArray; // the caller owns this object jint fCapacity; bool checkException(JNIEnv* env) { if (!env->ExceptionCheck()) { return false; } env->ExceptionDescribe(); if (fSwallowExceptions) { env->ExceptionClear(); } // There is no way to recover from the error, so consider the stream // to be at the end. fIsAtEnd = true; return true; } JavaVM* fJvm; jobject fJavaInputStream; jbyteArray fJavaByteArray; const jint fCapacity; size_t fBytesRead; bool fIsAtEnd; const bool fSwallowExceptions; }; SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage) { return new JavaInputStreamAdaptor(env, stream, storage); SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, bool swallowExceptions) { return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); } static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { SkASSERT(stream != NULL); size_t bufferSize = 4096; Loading core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +7 −6 Original line number Diff line number Diff line Loading @@ -16,13 +16,16 @@ class SkWStream; * @param stream Pointer to Java InputStream. * @param storage Java byte array for retrieving data from the * Java InputStream. * @param swallowExceptions Whether to call ExceptionClear() after * an Exception is thrown. If false, it is up to the client to * clear or propagate the exception. * @return SkStream Simple subclass of SkStream which supports its * basic methods like reading. Only valid until the calling * function returns, since the Java InputStream is not managed * by the SkStream. */ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, bool swallowExceptions = true); /** * Copy a Java InputStream. The result will be rewindable. Loading @@ -33,10 +36,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, * @return SkStreamRewindable The data in stream will be copied * to a new SkStreamRewindable. */ SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); #endif // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_ core/jni/android/graphics/ImageDecoder.cpp +102 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "Bitmap.h" #include "ByteBufferStreamAdaptor.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" #include "NinePatchPeeker.h" #include "Utils.h" Loading @@ -26,10 +27,12 @@ #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> #include <SkFrontBufferedStream.h> #include <SkStream.h> #include <androidfw/Asset.h> #include <jni.h> #include <sys/stat.h> using namespace android; Loading Loading @@ -69,15 +72,15 @@ struct ImageDecoder { static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { if (!stream.get()) { return nullObjectReturn("Failed to create a stream"); doThrowIOE(env, "Failed to create a stream"); return nullptr; } std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker); if (!decoder->mCodec.get()) { // FIXME: Add an error code to SkAndroidCodec::MakeFromStream, like // SkCodec? Then this can print a more informative error message. // (Or we can print one from within SkCodec.) ALOGE("Failed to create an SkCodec"); // FIXME: (b/71578461) Use the error message from // SkCodec::MakeFromStream to report a more informative error message. doThrowIOE(env, "Failed to create an SkCodec"); return nullptr; } Loading @@ -88,7 +91,52 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { reinterpret_cast<jlong>(decoder.release()), width, height); } static jobject ImageDecoder_nCreate(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor) { int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; if (fstat(descriptor, &fdStat) == -1) { doThrowIOE(env, "broken file descriptor; fstat returned -1"); return nullptr; } int dupDescriptor = dup(descriptor); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { close(dupDescriptor); doThrowIOE(env, "Could not open file"); return nullptr; } std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); if (::lseek(descriptor, 0, SEEK_CUR) == 0) { return native_create(env, std::move(fileStream)); } // FIXME: This allows us to pretend the current location is the beginning, // but it would be better if SkFILEStream allowed treating its starting // point as the beginning. std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); return native_create(env, std::move(stream)); } static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, jobject is, jbyteArray storage) { std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); if (!stream.get()) { doThrowIOE(env, "Failed to create stream!"); return nullptr; } std::unique_ptr<SkStream> bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); return native_create(env, std::move(bufferedStream)); } static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { Asset* asset = reinterpret_cast<Asset*>(assetPtr); std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); return native_create(env, std::move(stream)); Loading @@ -99,6 +147,7 @@ static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jo std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, initialPosition, limit); if (!stream) { doThrowIOE(env, "Failed to read ByteBuffer"); return nullptr; } return native_create(env, std::move(stream)); Loading @@ -114,6 +163,7 @@ static bool supports_any_down_scale(const SkAndroidCodec* codec) { return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; } // This method should never return null. Instead, it should throw an exception. static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, Loading Loading @@ -165,7 +215,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong case kOpaque_SkAlphaType: break; case kUnknown_SkAlphaType: return nullObjectReturn("Unknown alpha type"); doThrowIOE(env, "Unknown alpha type"); return nullptr; } SkColorType colorType = kN32_SkColorType; Loading Loading @@ -200,7 +251,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } if (!bm.setInfo(bitmapInfo)) { return nullObjectReturn("Failed to setInfo properly"); doThrowIOE(env, "Failed to setInfo properly"); return nullptr; } sk_sp<Bitmap> nativeBitmap; Loading @@ -213,35 +265,44 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativeBitmap = Bitmap::allocateHeapBitmap(&bm); } if (!nativeBitmap) { ALOGE("OOM allocating Bitmap with dimensions %i x %i", SkString msg; msg.printf("OOM allocating Bitmap with dimensions %i x %i", decodeInfo.width(), decodeInfo.height()); doThrowOOME(env); doThrowOOME(env, msg.c_str()); return nullptr; } jobject jexception = nullptr; SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); jobject jexception = env->ExceptionOccurred(); if (jexception) { env->ExceptionClear(); } switch (result) { case SkCodec::kSuccess: // Ignore the exception, since the decode was successful anyway. jexception = nullptr; break; case SkCodec::kIncompleteInput: if (jcallback) { if (jcallback && !jexception) { jexception = env->NewObject(gIncomplete_class, gIncomplete_constructorMethodID); } break; case SkCodec::kErrorInInput: if (jcallback) { if (jcallback && !jexception) { jexception = env->NewObject(gCorrupt_class, gCorrupt_constructorMethodID); } break; default: ALOGE("getPixels failed with error %i", result); SkString msg; msg.printf("getPixels failed with error %i", result); doThrowIOE(env, msg.c_str()); return nullptr; } if (jexception) { // FIXME: Do not provide a way for the client to force the method to return null. if (!env->CallBooleanMethod(jcallback, gCallback_onExceptionMethodID, jexception) || env->ExceptionCheck()) { return nullptr; Loading @@ -268,7 +329,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == nullptr) { return nullObjectReturn("ninePatchChunk == null"); doThrowOOME(env, "Failed to allocate nine patch chunk."); return nullptr; } env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize, Loading @@ -278,7 +340,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (decoder->mPeeker.mHasInsets) { ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f); if (ninePatchInsets == nullptr) { return nullObjectReturn("nine patch insets == null"); doThrowOOME(env, "Failed to allocate nine patch insets."); return nullptr; } } } Loading @@ -303,7 +366,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkImageInfo scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight); SkBitmap scaledBm; if (!scaledBm.setInfo(scaledInfo)) { nullObjectReturn("Failed scaled setInfo"); doThrowIOE(env, "Failed scaled setInfo"); return nullptr; } sk_sp<Bitmap> scaledPixelRef; Loading @@ -313,9 +377,10 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm); } if (!scaledPixelRef) { ALOGE("OOM allocating scaled Bitmap with dimensions %i x %i", SkString msg; msg.printf("OOM allocating scaled Bitmap with dimensions %i x %i", desiredWidth, desiredHeight); doThrowOOME(env); doThrowOOME(env, msg.c_str()); return nullptr; } Loading @@ -334,13 +399,11 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); if (!canvas) { return nullObjectReturn("Failed to create Canvas for PostProcess!"); } jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, reinterpret_cast<jlong>(canvas.get())); if (!jcanvas) { return nullObjectReturn("Failed to create Java Canvas for PostProcess!"); doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); return nullptr; } // jcanvas will now own canvas. canvas.release(); Loading Loading @@ -368,15 +431,17 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong newAlphaType = kOpaque_SkAlphaType; break; default: ALOGE("invalid return from postProcess: %i", pixelFormat); doThrowIAE(env); SkString msg; msg.printf("invalid return from postProcess: %i", pixelFormat); doThrowIAE(env, msg.c_str()); return nullptr; } if (newAlphaType != bm.alphaType()) { if (!bm.setAlphaType(newAlphaType)) { ALOGE("incompatible return from postProcess: %i", pixelFormat); doThrowIAE(env); SkString msg; msg.printf("incompatible return from postProcess: %i", pixelFormat); doThrowIAE(env, msg.c_str()); return nullptr; } nativeBitmap->setAlphaType(newAlphaType); Loading Loading @@ -405,7 +470,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong ninePatchChunk, ninePatchInsets); } if (allocator == ImageDecoder::kHardware_Allocator) { return nullObjectReturn("failed to allocate hardware Bitmap!"); doThrowOOME(env, "failed to allocate hardware Bitmap!"); return nullptr; } // If we failed to create a hardware bitmap, go ahead and create a // software one. Loading @@ -430,19 +496,21 @@ static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativ decoder->mPeeker.getPadding(env, outPadding); } static void ImageDecoder_nRecycle(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { delete reinterpret_cast<ImageDecoder*>(nativePtr); } static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreate }, { "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$OnExceptionListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, { "nRecycle", "(J)V", (void*) ImageDecoder_nRecycle}, { "nClose", "(J)V", (void*) ImageDecoder_nClose}, }; int register_android_graphics_ImageDecoder(JNIEnv* env) { Loading @@ -459,7 +527,7 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V"); jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnExceptionListener"); gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/lang/Exception;)Z"); gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/io/IOException;)Z"); jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess"); gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I"); Loading graphics/java/android/graphics/ImageDecoder.java +218 −116 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +85 −43 Original line number Diff line number Diff line Loading @@ -12,21 +12,62 @@ static jmethodID gInputStream_readMethodID; static jmethodID gInputStream_skipMethodID; // FIXME: Share with ByteBufferStreamAdaptor.cpp? static JNIEnv* get_env_or_die(JavaVM* jvm) { JNIEnv* env; if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", jvm); } return env; } /** * Wrapper for a Java InputStream. */ class JavaInputStreamAdaptor : public SkStream { JavaInputStreamAdaptor(JavaVM* jvm, jobject js, jbyteArray ar, jint capacity, bool swallowExceptions) : fJvm(jvm) , fJavaInputStream(js) , fJavaByteArray(ar) , fCapacity(capacity) , fBytesRead(0) , fIsAtEnd(false) , fSwallowExceptions(swallowExceptions) {} public: JavaInputStreamAdaptor(JNIEnv* env, jobject js, jbyteArray ar) : fEnv(env), fJavaInputStream(js), fJavaByteArray(ar) { SkASSERT(ar); fCapacity = env->GetArrayLength(ar); SkASSERT(fCapacity > 0); fBytesRead = 0; fIsAtEnd = false; static JavaInputStreamAdaptor* Create(JNIEnv* env, jobject js, jbyteArray ar, bool swallowExceptions) { JavaVM* jvm; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&jvm) != JNI_OK); js = env->NewGlobalRef(js); if (!js) { return nullptr; } ar = (jbyteArray) env->NewGlobalRef(ar); if (!ar) { env->DeleteGlobalRef(js); return nullptr; } virtual size_t read(void* buffer, size_t size) { jint capacity = env->GetArrayLength(ar); return new JavaInputStreamAdaptor(jvm, js, ar, capacity, swallowExceptions); } ~JavaInputStreamAdaptor() override { auto* env = get_env_or_die(fJvm); env->DeleteGlobalRef(fJavaInputStream); env->DeleteGlobalRef(fJavaByteArray); } size_t read(void* buffer, size_t size) override { auto* env = get_env_or_die(fJvm); if (!fSwallowExceptions && checkException(env)) { // Just in case the caller did not clear from a previous exception. return 0; } if (NULL == buffer) { if (0 == size) { return 0; Loading @@ -37,10 +78,10 @@ public: */ size_t amountSkipped = 0; do { size_t amount = this->doSkip(size - amountSkipped); size_t amount = this->doSkip(size - amountSkipped, env); if (0 == amount) { char tmp; amount = this->doRead(&tmp, 1); amount = this->doRead(&tmp, 1, env); if (0 == amount) { // if read returned 0, we're at EOF fIsAtEnd = true; Loading @@ -52,16 +93,13 @@ public: return amountSkipped; } } return this->doRead(buffer, size); return this->doRead(buffer, size, env); } virtual bool isAtEnd() const { return fIsAtEnd; } bool isAtEnd() const override { return fIsAtEnd; } private: size_t doRead(void* buffer, size_t size) { JNIEnv* env = fEnv; size_t doRead(void* buffer, size_t size, JNIEnv* env) { size_t bytesRead = 0; // read the bytes do { Loading @@ -76,13 +114,9 @@ private: jint n = env->CallIntMethod(fJavaInputStream, gInputStream_readMethodID, fJavaByteArray, 0, requested); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("---- read threw an exception\n"); // Consider the stream to be at the end, since there was an error. fIsAtEnd = true; return 0; return bytesRead; } if (n < 0) { // n == 0 should not be possible, see InputStream read() specifications. Loading @@ -92,14 +126,9 @@ private: env->GetByteArrayRegion(fJavaByteArray, 0, n, reinterpret_cast<jbyte*>(buffer)); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("---- read:GetByteArrayRegion threw an exception\n"); // The error was not with the stream itself, but consider it to be at the // end, since we do not have a way to recover. fIsAtEnd = true; return 0; return bytesRead; } buffer = (void*)((char*)buffer + n); Loading @@ -111,14 +140,10 @@ private: return bytesRead; } size_t doSkip(size_t size) { JNIEnv* env = fEnv; size_t doSkip(size_t size, JNIEnv* env) { jlong skipped = env->CallLongMethod(fJavaInputStream, gInputStream_skipMethodID, (jlong)size); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); if (checkException(env)) { SkDebugf("------- skip threw an exception\n"); return 0; } Loading @@ -129,20 +154,37 @@ private: return (size_t)skipped; } JNIEnv* fEnv; jobject fJavaInputStream; // the caller owns this object jbyteArray fJavaByteArray; // the caller owns this object jint fCapacity; bool checkException(JNIEnv* env) { if (!env->ExceptionCheck()) { return false; } env->ExceptionDescribe(); if (fSwallowExceptions) { env->ExceptionClear(); } // There is no way to recover from the error, so consider the stream // to be at the end. fIsAtEnd = true; return true; } JavaVM* fJvm; jobject fJavaInputStream; jbyteArray fJavaByteArray; const jint fCapacity; size_t fBytesRead; bool fIsAtEnd; const bool fSwallowExceptions; }; SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage) { return new JavaInputStreamAdaptor(env, stream, storage); SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, bool swallowExceptions) { return JavaInputStreamAdaptor::Create(env, stream, storage, swallowExceptions); } static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { SkASSERT(stream != NULL); size_t bufferSize = 4096; Loading
core/jni/android/graphics/CreateJavaOutputStreamAdaptor.h +7 −6 Original line number Diff line number Diff line Loading @@ -16,13 +16,16 @@ class SkWStream; * @param stream Pointer to Java InputStream. * @param storage Java byte array for retrieving data from the * Java InputStream. * @param swallowExceptions Whether to call ExceptionClear() after * an Exception is thrown. If false, it is up to the client to * clear or propagate the exception. * @return SkStream Simple subclass of SkStream which supports its * basic methods like reading. Only valid until the calling * function returns, since the Java InputStream is not managed * by the SkStream. */ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage, bool swallowExceptions = true); /** * Copy a Java InputStream. The result will be rewindable. Loading @@ -33,10 +36,8 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, * @return SkStreamRewindable The data in stream will be copied * to a new SkStreamRewindable. */ SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, jbyteArray storage); SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); SkWStream* CreateJavaOutputStreamAdaptor(JNIEnv* env, jobject stream, jbyteArray storage); #endif // _ANDROID_GRAPHICS_CREATE_JAVA_OUTPUT_STREAM_ADAPTOR_H_
core/jni/android/graphics/ImageDecoder.cpp +102 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include "Bitmap.h" #include "ByteBufferStreamAdaptor.h" #include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" #include "NinePatchPeeker.h" #include "Utils.h" Loading @@ -26,10 +27,12 @@ #include <SkAndroidCodec.h> #include <SkEncodedImageFormat.h> #include <SkFrontBufferedStream.h> #include <SkStream.h> #include <androidfw/Asset.h> #include <jni.h> #include <sys/stat.h> using namespace android; Loading Loading @@ -69,15 +72,15 @@ struct ImageDecoder { static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { if (!stream.get()) { return nullObjectReturn("Failed to create a stream"); doThrowIOE(env, "Failed to create a stream"); return nullptr; } std::unique_ptr<ImageDecoder> decoder(new ImageDecoder); decoder->mCodec = SkAndroidCodec::MakeFromStream(std::move(stream), &decoder->mPeeker); if (!decoder->mCodec.get()) { // FIXME: Add an error code to SkAndroidCodec::MakeFromStream, like // SkCodec? Then this can print a more informative error message. // (Or we can print one from within SkCodec.) ALOGE("Failed to create an SkCodec"); // FIXME: (b/71578461) Use the error message from // SkCodec::MakeFromStream to report a more informative error message. doThrowIOE(env, "Failed to create an SkCodec"); return nullptr; } Loading @@ -88,7 +91,52 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream) { reinterpret_cast<jlong>(decoder.release()), width, height); } static jobject ImageDecoder_nCreate(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/, jobject fileDescriptor) { int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); struct stat fdStat; if (fstat(descriptor, &fdStat) == -1) { doThrowIOE(env, "broken file descriptor; fstat returned -1"); return nullptr; } int dupDescriptor = dup(descriptor); FILE* file = fdopen(dupDescriptor, "r"); if (file == NULL) { close(dupDescriptor); doThrowIOE(env, "Could not open file"); return nullptr; } std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file)); if (::lseek(descriptor, 0, SEEK_CUR) == 0) { return native_create(env, std::move(fileStream)); } // FIXME: This allows us to pretend the current location is the beginning, // but it would be better if SkFILEStream allowed treating its starting // point as the beginning. std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream), SkCodec::MinBufferedBytesNeeded())); return native_create(env, std::move(stream)); } static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/, jobject is, jbyteArray storage) { std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false)); if (!stream.get()) { doThrowIOE(env, "Failed to create stream!"); return nullptr; } std::unique_ptr<SkStream> bufferedStream( SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded())); return native_create(env, std::move(bufferedStream)); } static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr) { Asset* asset = reinterpret_cast<Asset*>(assetPtr); std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset)); return native_create(env, std::move(stream)); Loading @@ -99,6 +147,7 @@ static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jo std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer, initialPosition, limit); if (!stream) { doThrowIOE(env, "Failed to read ByteBuffer"); return nullptr; } return native_create(env, std::move(stream)); Loading @@ -114,6 +163,7 @@ static bool supports_any_down_scale(const SkAndroidCodec* codec) { return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; } // This method should never return null. Instead, it should throw an exception. static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jcallback, jobject jpostProcess, jint desiredWidth, jint desiredHeight, jobject jsubset, Loading Loading @@ -165,7 +215,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong case kOpaque_SkAlphaType: break; case kUnknown_SkAlphaType: return nullObjectReturn("Unknown alpha type"); doThrowIOE(env, "Unknown alpha type"); return nullptr; } SkColorType colorType = kN32_SkColorType; Loading Loading @@ -200,7 +251,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType); } if (!bm.setInfo(bitmapInfo)) { return nullObjectReturn("Failed to setInfo properly"); doThrowIOE(env, "Failed to setInfo properly"); return nullptr; } sk_sp<Bitmap> nativeBitmap; Loading @@ -213,35 +265,44 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativeBitmap = Bitmap::allocateHeapBitmap(&bm); } if (!nativeBitmap) { ALOGE("OOM allocating Bitmap with dimensions %i x %i", SkString msg; msg.printf("OOM allocating Bitmap with dimensions %i x %i", decodeInfo.width(), decodeInfo.height()); doThrowOOME(env); doThrowOOME(env, msg.c_str()); return nullptr; } jobject jexception = nullptr; SkAndroidCodec::AndroidOptions options; options.fSampleSize = sampleSize; auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options); jobject jexception = env->ExceptionOccurred(); if (jexception) { env->ExceptionClear(); } switch (result) { case SkCodec::kSuccess: // Ignore the exception, since the decode was successful anyway. jexception = nullptr; break; case SkCodec::kIncompleteInput: if (jcallback) { if (jcallback && !jexception) { jexception = env->NewObject(gIncomplete_class, gIncomplete_constructorMethodID); } break; case SkCodec::kErrorInInput: if (jcallback) { if (jcallback && !jexception) { jexception = env->NewObject(gCorrupt_class, gCorrupt_constructorMethodID); } break; default: ALOGE("getPixels failed with error %i", result); SkString msg; msg.printf("getPixels failed with error %i", result); doThrowIOE(env, msg.c_str()); return nullptr; } if (jexception) { // FIXME: Do not provide a way for the client to force the method to return null. if (!env->CallBooleanMethod(jcallback, gCallback_onExceptionMethodID, jexception) || env->ExceptionCheck()) { return nullptr; Loading @@ -268,7 +329,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong size_t ninePatchArraySize = decoder->mPeeker.mPatch->serializedSize(); ninePatchChunk = env->NewByteArray(ninePatchArraySize); if (ninePatchChunk == nullptr) { return nullObjectReturn("ninePatchChunk == null"); doThrowOOME(env, "Failed to allocate nine patch chunk."); return nullptr; } env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker.mPatchSize, Loading @@ -278,7 +340,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (decoder->mPeeker.mHasInsets) { ninePatchInsets = decoder->mPeeker.createNinePatchInsets(env, 1.0f); if (ninePatchInsets == nullptr) { return nullObjectReturn("nine patch insets == null"); doThrowOOME(env, "Failed to allocate nine patch insets."); return nullptr; } } } Loading @@ -303,7 +366,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong SkImageInfo scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight); SkBitmap scaledBm; if (!scaledBm.setInfo(scaledInfo)) { nullObjectReturn("Failed scaled setInfo"); doThrowIOE(env, "Failed scaled setInfo"); return nullptr; } sk_sp<Bitmap> scaledPixelRef; Loading @@ -313,9 +377,10 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm); } if (!scaledPixelRef) { ALOGE("OOM allocating scaled Bitmap with dimensions %i x %i", SkString msg; msg.printf("OOM allocating scaled Bitmap with dimensions %i x %i", desiredWidth, desiredHeight); doThrowOOME(env); doThrowOOME(env, msg.c_str()); return nullptr; } Loading @@ -334,13 +399,11 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong if (jpostProcess) { std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm)); if (!canvas) { return nullObjectReturn("Failed to create Canvas for PostProcess!"); } jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID, reinterpret_cast<jlong>(canvas.get())); if (!jcanvas) { return nullObjectReturn("Failed to create Java Canvas for PostProcess!"); doThrowOOME(env, "Failed to create Java Canvas for PostProcess!"); return nullptr; } // jcanvas will now own canvas. canvas.release(); Loading Loading @@ -368,15 +431,17 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong newAlphaType = kOpaque_SkAlphaType; break; default: ALOGE("invalid return from postProcess: %i", pixelFormat); doThrowIAE(env); SkString msg; msg.printf("invalid return from postProcess: %i", pixelFormat); doThrowIAE(env, msg.c_str()); return nullptr; } if (newAlphaType != bm.alphaType()) { if (!bm.setAlphaType(newAlphaType)) { ALOGE("incompatible return from postProcess: %i", pixelFormat); doThrowIAE(env); SkString msg; msg.printf("incompatible return from postProcess: %i", pixelFormat); doThrowIAE(env, msg.c_str()); return nullptr; } nativeBitmap->setAlphaType(newAlphaType); Loading Loading @@ -405,7 +470,8 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong ninePatchChunk, ninePatchInsets); } if (allocator == ImageDecoder::kHardware_Allocator) { return nullObjectReturn("failed to allocate hardware Bitmap!"); doThrowOOME(env, "failed to allocate hardware Bitmap!"); return nullptr; } // If we failed to create a hardware bitmap, go ahead and create a // software one. Loading @@ -430,19 +496,21 @@ static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativ decoder->mPeeker.getPadding(env, outPadding); } static void ImageDecoder_nRecycle(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) { delete reinterpret_cast<ImageDecoder*>(nativePtr); } static const JNINativeMethod gImageDecoderMethods[] = { { "nCreate", "(J)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreate }, { "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$OnExceptionListener;Landroid/graphics/PostProcess;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;", (void*) ImageDecoder_nDecodeBitmap }, { "nGetSampledSize","(JI)Landroid/graphics/Point;", (void*) ImageDecoder_nGetSampledSize }, { "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding }, { "nRecycle", "(J)V", (void*) ImageDecoder_nRecycle}, { "nClose", "(J)V", (void*) ImageDecoder_nClose}, }; int register_android_graphics_ImageDecoder(JNIEnv* env) { Loading @@ -459,7 +527,7 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V"); jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnExceptionListener"); gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/lang/Exception;)Z"); gCallback_onExceptionMethodID = GetMethodIDOrDie(env, callback_class, "onException", "(Ljava/io/IOException;)Z"); jclass postProcess_class = FindClassOrDie(env, "android/graphics/PostProcess"); gPostProcess_postProcessMethodID = GetMethodIDOrDie(env, postProcess_class, "postProcess", "(Landroid/graphics/Canvas;II)I"); Loading
graphics/java/android/graphics/ImageDecoder.java +218 −116 File changed.Preview size limit exceeded, changes collapsed. Show changes