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

Commit 28834ba4 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Separate ImageDecoder JNI from actual work"

parents f00ce020 753a56fa
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@
#include <SkPicture.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
#include <SkPictureRecorder.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/AnimatedImageDrawable.h>
#include <hwui/ImageDecoder.h>
#include <hwui/Canvas.h>
#include <hwui/Canvas.h>
#include <utils/Looper.h>
#include <utils/Looper.h>


+95 −130
Original line number Original line Diff line number Diff line
@@ -20,10 +20,12 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include "GraphicsJNI.h"
#include "GraphicsJNI.h"
#include "ImageDecoder.h"
#include "ImageDecoder.h"
#include "NinePatchPeeker.h"
#include "Utils.h"
#include "Utils.h"
#include "core_jni_helpers.h"
#include "core_jni_helpers.h"


#include <hwui/Bitmap.h>
#include <hwui/Bitmap.h>
#include <hwui/ImageDecoder.h>
#include <HardwareBitmapUploader.h>
#include <HardwareBitmapUploader.h>


#include <SkAndroidCodec.h>
#include <SkAndroidCodec.h>
@@ -49,6 +51,28 @@ static jmethodID gCallback_onPartialImageMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_constructorMethodID;
static jmethodID gCanvas_releaseMethodID;
static jmethodID gCanvas_releaseMethodID;


// These need to stay in sync with ImageDecoder.java's Allocator constants.
enum Allocator {
    kDefault_Allocator      = 0,
    kSoftware_Allocator     = 1,
    kSharedMemory_Allocator = 2,
    kHardware_Allocator     = 3,
};

// These need to stay in sync with ImageDecoder.java's Error constants.
enum Error {
    kSourceException     = 1,
    kSourceIncomplete    = 2,
    kSourceMalformedData = 3,
};

// These need to stay in sync with PixelFormat.java's Format constants.
enum PixelFormat {
    kUnknown     =  0,
    kTranslucent = -3,
    kOpaque      = -1,
};

// Clear and return any pending exception for handling other than throwing directly.
// Clear and return any pending exception for handling other than throwing directly.
static jthrowable get_and_clear_exception(JNIEnv* env) {
static jthrowable get_and_clear_exception(JNIEnv* env) {
    jthrowable jexception = env->ExceptionOccurred();
    jthrowable jexception = env->ExceptionOccurred();
@@ -59,7 +83,7 @@ static jthrowable get_and_clear_exception(JNIEnv* env) {
}
}


// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
// Throw a new ImageDecoder.DecodeException. Returns null for convenience.
static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const char* msg,
static jobject throw_exception(JNIEnv* env, Error error, const char* msg,
                               jthrowable cause, jobject source) {
                               jthrowable cause, jobject source) {
    jstring jstr = nullptr;
    jstring jstr = nullptr;
    if (msg) {
    if (msg) {
@@ -81,27 +105,27 @@ static jobject throw_exception(JNIEnv* env, ImageDecoder::Error error, const cha
static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
        jobject source, jboolean preferAnimation) {
        jobject source, jboolean preferAnimation) {
    if (!stream.get()) {
    if (!stream.get()) {
        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
        return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
                               nullptr, source);
                               nullptr, source);
    }
    }
    std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
    sk_sp<NinePatchPeeker> peeker(new NinePatchPeeker);
    SkCodec::Result result;
    SkCodec::Result result;
    auto codec = SkCodec::MakeFromStream(
    auto codec = SkCodec::MakeFromStream(
            std::move(stream), &result, decoder->mPeeker.get(),
            std::move(stream), &result, peeker.get(),
            preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
            preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
                            : SkCodec::SelectionPolicy::kPreferStillImage);
                            : SkCodec::SelectionPolicy::kPreferStillImage);
    if (jthrowable jexception = get_and_clear_exception(env)) {
    if (jthrowable jexception = get_and_clear_exception(env)) {
        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
        return throw_exception(env, kSourceException, "", jexception, source);
    }
    }
    if (!codec) {
    if (!codec) {
        switch (result) {
        switch (result) {
            case SkCodec::kIncompleteInput:
            case SkCodec::kIncompleteInput:
                return throw_exception(env, ImageDecoder::kSourceIncomplete, "", nullptr, source);
                return throw_exception(env, kSourceIncomplete, "", nullptr, source);
            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));
                return throw_exception(env, ImageDecoder::kSourceMalformedData,  msg.c_str(),
                return throw_exception(env, kSourceMalformedData,  msg.c_str(),
                                       nullptr, source);
                                       nullptr, source);


        }
        }
@@ -109,21 +133,22 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,


    const bool animated = codec->getFrameCount() > 1;
    const bool animated = codec->getFrameCount() > 1;
    if (jthrowable jexception = get_and_clear_exception(env)) {
    if (jthrowable jexception = get_and_clear_exception(env)) {
        return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
        return throw_exception(env, kSourceException, "", jexception, source);
    }
    }


    decoder->mCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
            SkAndroidCodec::ExifOrientationBehavior::kRespect);
            SkAndroidCodec::ExifOrientationBehavior::kRespect);
    if (!decoder->mCodec.get()) {
    if (!androidCodec.get()) {
        return throw_exception(env, ImageDecoder::kSourceMalformedData, "", nullptr, source);
        return throw_exception(env, kSourceMalformedData, "", nullptr, source);
    }
    }


    const auto& info = decoder->mCodec->getInfo();
    const auto& info = androidCodec->getInfo();
    const int width = info.width();
    const int width = info.width();
    const int height = info.height();
    const int height = info.height();
    const bool isNinePatch = decoder->mPeeker->mPatch != nullptr;
    const bool isNinePatch = peeker->mPatch != nullptr;
    ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
    return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
    return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
                          reinterpret_cast<jlong>(decoder.release()), width, height,
                          reinterpret_cast<jlong>(decoder), width, height,
                          animated, isNinePatch);
                          animated, isNinePatch);
}
}


@@ -133,7 +158,7 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,


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


@@ -141,7 +166,7 @@ static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
    FILE* file = fdopen(dupDescriptor, "r");
    FILE* file = fdopen(dupDescriptor, "r");
    if (file == NULL) {
    if (file == NULL) {
        close(dupDescriptor);
        close(dupDescriptor);
        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file",
        return throw_exception(env, kSourceMalformedData, "Could not open file",
                               nullptr, source);
                               nullptr, source);
    }
    }


@@ -154,7 +179,7 @@ static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
    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()) {
        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
        return throw_exception(env, kSourceMalformedData, "Failed to create a stream",
                               nullptr, source);
                               nullptr, source);
    }
    }


@@ -177,7 +202,7 @@ static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
    std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
    std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
                                                                     initialPosition, limit);
                                                                     initialPosition, limit);
    if (!stream) {
    if (!stream) {
        return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
        return throw_exception(env, kSourceMalformedData, "Failed to read ByteBuffer",
                               nullptr, source);
                               nullptr, source);
    }
    }
    return native_create(env, std::move(stream), source, preferAnimation);
    return native_create(env, std::move(stream), source, preferAnimation);
@@ -195,7 +220,7 @@ jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<C
                                     reinterpret_cast<jlong>(canvas.get()));
                                     reinterpret_cast<jlong>(canvas.get()));
    if (!jcanvas) {
    if (!jcanvas) {
        doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
        doThrowOOME(env, "Failed to create Java Canvas for PostProcess!");
        return ImageDecoder::kUnknown;
        return kUnknown;
    }
    }


    // jcanvas now owns canvas.
    // jcanvas now owns canvas.
@@ -206,43 +231,23 @@ jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<C


static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                          jobject jdecoder, jboolean jpostProcess,
                                          jobject jdecoder, jboolean jpostProcess,
                                          jint desiredWidth, jint desiredHeight, jobject jsubset,
                                          jint targetWidth, jint targetHeight, jobject jsubset,
                                          jboolean requireMutable, jint allocator,
                                          jboolean requireMutable, jint allocator,
                                          jboolean requireUnpremul, jboolean preferRamOverQuality,
                                          jboolean requireUnpremul, jboolean preferRamOverQuality,
                                          jboolean asAlphaMask, jlong colorSpaceHandle,
                                          jboolean asAlphaMask, jlong colorSpaceHandle,
                                          jboolean extended) {
                                          jboolean extended) {
    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
    SkAndroidCodec* codec = decoder->mCodec.get();
    if (!decoder->setTargetSize(targetWidth, targetHeight)) {
    const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
        doThrowISE(env, "Could not scale to target size!");
    SkISize decodeSize = desiredSize;
    const int sampleSize = codec->computeSampleSize(&decodeSize);
    const bool scale = desiredSize != decodeSize;
    SkImageInfo decodeInfo = codec->getInfo().makeWH(decodeSize.width(), decodeSize.height());
    if (scale && requireUnpremul && kOpaque_SkAlphaType != decodeInfo.alphaType()) {
        doThrowISE(env, "Cannot scale unpremultiplied pixels!");
        return nullptr;
        return nullptr;
    }
    }

    if (requireUnpremul && !decoder->setOutAlphaType(kUnpremul_SkAlphaType)) {
    switch (decodeInfo.alphaType()) {
        doThrowISE(env, "Cannot scale unpremultiplied pixels!");
        case kUnpremul_SkAlphaType:
            if (!requireUnpremul) {
                decodeInfo = decodeInfo.makeAlphaType(kPremul_SkAlphaType);
            }
            break;
        case kPremul_SkAlphaType:
            if (requireUnpremul) {
                decodeInfo = decodeInfo.makeAlphaType(kUnpremul_SkAlphaType);
            }
            break;
        case kOpaque_SkAlphaType:
            break;
        case kUnknown_SkAlphaType:
            doThrowIOE(env, "Unknown alpha type");
        return nullptr;
        return nullptr;
    }
    }


    SkColorType colorType = kN32_SkColorType;
    SkColorType colorType = kN32_SkColorType;
    if (asAlphaMask && decodeInfo.colorType() == kGray_8_SkColorType) {
    if (asAlphaMask && decoder->gray()) {
        // We have to trick Skia to decode this to a single channel.
        // We have to trick Skia to decode this to a single channel.
        colorType = kGray_8_SkColorType;
        colorType = kGray_8_SkColorType;
    } else if (preferRamOverQuality) {
    } else if (preferRamOverQuality) {
@@ -250,12 +255,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
        // result incorrect. If we call the postProcess before now and record
        // result incorrect. If we call the postProcess before now and record
        // to a picture, we can know whether alpha was added, and if not, we
        // to a picture, we can know whether alpha was added, and if not, we
        // can still use 565.
        // can still use 565.
        if (decodeInfo.alphaType() == kOpaque_SkAlphaType && !jpostProcess) {
        if (decoder->opaque() && !jpostProcess) {
            // If the final result will be hardware, decoding to 565 and then
            // If the final result will be hardware, decoding to 565 and then
            // uploading to the gpu as 8888 will not save memory. This still
            // uploading to the gpu as 8888 will not save memory. This still
            // may save us from using F16, but do not go down to 565.
            // may save us from using F16, but do not go down to 565.
            if (allocator != ImageDecoder::kHardware_Allocator &&
            if (allocator != kHardware_Allocator &&
               (allocator != ImageDecoder::kDefault_Allocator || requireMutable)) {
               (allocator != kDefault_Allocator || requireMutable)) {
                colorType = kRGB_565_SkColorType;
                colorType = kRGB_565_SkColorType;
            }
            }
        }
        }
@@ -263,12 +268,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    } else if (extended) {
    } else if (extended) {
        colorType = kRGBA_F16_SkColorType;
        colorType = kRGBA_F16_SkColorType;
    } else {
    } else {
        colorType = codec->computeOutputColorType(colorType);
        colorType = decoder->mCodec->computeOutputColorType(colorType);
    }
    }


    const bool isHardware = !requireMutable
    const bool isHardware = !requireMutable
        && (allocator == ImageDecoder::kDefault_Allocator ||
        && (allocator == kDefault_Allocator ||
            allocator == ImageDecoder::kHardware_Allocator)
            allocator == kHardware_Allocator)
        && colorType != kGray_8_SkColorType;
        && colorType != kGray_8_SkColorType;


    if (colorType == kRGBA_F16_SkColorType && isHardware &&
    if (colorType == kRGBA_F16_SkColorType && isHardware &&
@@ -276,12 +281,28 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
        colorType = kN32_SkColorType;
        colorType = kN32_SkColorType;
    }
    }


    if (!decoder->setOutColorType(colorType)) {
        doThrowISE(env, "Failed to set out color type!");
        return nullptr;
    }

    {
        sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
        sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
    colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
        colorSpace = decoder->mCodec->computeOutputColorSpace(colorType, colorSpace);
    decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
        decoder->setOutColorSpace(std::move(colorSpace));
    }

    if (jsubset) {
        SkIRect subset;
        GraphicsJNI::jrect_to_irect(env, jsubset, &subset);
        if (!decoder->setCropRect(&subset)) {
            doThrowISE(env, "Invalid crop rect!");
            return nullptr;
        }
    }


    SkBitmap bm;
    SkBitmap bm;
    auto bitmapInfo = decodeInfo;
    SkImageInfo bitmapInfo = decoder->getOutputInfo();
    if (asAlphaMask && colorType == kGray_8_SkColorType) {
    if (asAlphaMask && colorType == kGray_8_SkColorType) {
        bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
        bitmapInfo = bitmapInfo.makeColorType(kAlpha_8_SkColorType);
    }
    }
@@ -291,10 +312,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    }
    }


    sk_sp<Bitmap> nativeBitmap;
    sk_sp<Bitmap> nativeBitmap;
    // If we are going to scale or subset, we will create a new bitmap later on,
    if (allocator == kSharedMemory_Allocator) {
    // so use the heap for the temporary.
    // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
    if (allocator == ImageDecoder::kSharedMemory_Allocator && !scale && !jsubset) {
        nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
        nativeBitmap = Bitmap::allocateAshmemBitmap(&bm);
    } else {
    } else {
        nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
        nativeBitmap = Bitmap::allocateHeapBitmap(&bm);
@@ -302,16 +320,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    if (!nativeBitmap) {
    if (!nativeBitmap) {
        SkString msg;
        SkString msg;
        msg.printf("OOM allocating Bitmap with dimensions %i x %i",
        msg.printf("OOM allocating Bitmap with dimensions %i x %i",
                decodeInfo.width(), decodeInfo.height());
                bitmapInfo.width(), bitmapInfo.height());
        doThrowOOME(env, msg.c_str());
        doThrowOOME(env, msg.c_str());
        return nullptr;
        return nullptr;
    }
    }


    SkAndroidCodec::AndroidOptions options;
    SkCodec::Result result = decoder->decode(bm.getPixels(), bm.rowBytes());
    options.fSampleSize = sampleSize;
    auto result = codec->getAndroidPixels(decodeInfo, bm.getPixels(), bm.rowBytes(), &options);
    jthrowable jexception = get_and_clear_exception(env);
    jthrowable jexception = get_and_clear_exception(env);
    int onPartialImageError = jexception ? ImageDecoder::kSourceException
    int onPartialImageError = jexception ? kSourceException
                                         : 0; // No error.
                                         : 0; // No error.
    switch (result) {
    switch (result) {
        case SkCodec::kSuccess:
        case SkCodec::kSuccess:
@@ -321,12 +337,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
            break;
            break;
        case SkCodec::kIncompleteInput:
        case SkCodec::kIncompleteInput:
            if (!jexception) {
            if (!jexception) {
                onPartialImageError = ImageDecoder::kSourceIncomplete;
                onPartialImageError = kSourceIncomplete;
            }
            }
            break;
            break;
        case SkCodec::kErrorInInput:
        case SkCodec::kErrorInInput:
            if (!jexception) {
            if (!jexception) {
                onPartialImageError = ImageDecoder::kSourceMalformedData;
                onPartialImageError = kSourceMalformedData;
            }
            }
            break;
            break;
        default:
        default:
@@ -350,20 +366,21 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
    // Ignore ninepatch when post-processing.
    // Ignore ninepatch when post-processing.
    if (!jpostProcess) {
    if (!jpostProcess) {
        // FIXME: Share more code with BitmapFactory.cpp.
        // FIXME: Share more code with BitmapFactory.cpp.
        if (decoder->mPeeker->mPatch != nullptr) {
        auto* peeker = reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get());
            size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize();
        if (peeker->mPatch != nullptr) {
            size_t ninePatchArraySize = peeker->mPatch->serializedSize();
            ninePatchChunk = env->NewByteArray(ninePatchArraySize);
            ninePatchChunk = env->NewByteArray(ninePatchArraySize);
            if (ninePatchChunk == nullptr) {
            if (ninePatchChunk == nullptr) {
                doThrowOOME(env, "Failed to allocate nine patch chunk.");
                doThrowOOME(env, "Failed to allocate nine patch chunk.");
                return nullptr;
                return nullptr;
            }
            }


            env->SetByteArrayRegion(ninePatchChunk, 0, decoder->mPeeker->mPatchSize,
            env->SetByteArrayRegion(ninePatchChunk, 0, peeker->mPatchSize,
                                    reinterpret_cast<jbyte*>(decoder->mPeeker->mPatch));
                                    reinterpret_cast<jbyte*>(peeker->mPatch));
        }
        }


        if (decoder->mPeeker->mHasInsets) {
        if (peeker->mHasInsets) {
            ninePatchInsets = decoder->mPeeker->createNinePatchInsets(env, 1.0f);
            ninePatchInsets = peeker->createNinePatchInsets(env, 1.0f);
            if (ninePatchInsets == nullptr) {
            if (ninePatchInsets == nullptr) {
                doThrowOOME(env, "Failed to allocate nine patch insets.");
                doThrowOOME(env, "Failed to allocate nine patch insets.");
                return nullptr;
                return nullptr;
@@ -371,58 +388,6 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
        }
        }
    }
    }


    if (scale || jsubset) {
        int translateX = 0;
        int translateY = 0;
        SkImageInfo scaledInfo;
        if (jsubset) {
            SkIRect subset;
            GraphicsJNI::jrect_to_irect(env, jsubset, &subset);

            translateX = -subset.fLeft;
            translateY = -subset.fTop;
            scaledInfo = bitmapInfo.makeWH(subset.width(), subset.height());
        } else {
            scaledInfo = bitmapInfo.makeWH(desiredWidth, desiredHeight);
        }
        SkBitmap scaledBm;
        if (!scaledBm.setInfo(scaledInfo)) {
            doThrowIOE(env, "Failed scaled setInfo");
            return nullptr;
        }

        sk_sp<Bitmap> scaledPixelRef;
        if (allocator == ImageDecoder::kSharedMemory_Allocator) {
            scaledPixelRef = Bitmap::allocateAshmemBitmap(&scaledBm);
        } else {
            scaledPixelRef = Bitmap::allocateHeapBitmap(&scaledBm);
        }
        if (!scaledPixelRef) {
            SkString msg;
            msg.printf("OOM allocating scaled Bitmap with dimensions %i x %i",
                    desiredWidth, desiredHeight);
            doThrowOOME(env, msg.c_str());
            return nullptr;
        }

        SkPaint paint;
        paint.setBlendMode(SkBlendMode::kSrc);
        paint.setFilterQuality(kLow_SkFilterQuality);  // bilinear filtering

        SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
        canvas.translate(translateX, translateY);
        if (scale) {
            float scaleX = (float) desiredWidth  / decodeInfo.width();
            float scaleY = (float) desiredHeight / decodeInfo.height();
            canvas.scale(scaleX, scaleY);
        }

        canvas.drawBitmap(bm, 0.0f, 0.0f, &paint);

        bm.swap(scaledBm);
        nativeBitmap = std::move(scaledPixelRef);
    }

    if (jpostProcess) {
    if (jpostProcess) {
        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
        std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));


@@ -433,12 +398,12 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong


        SkAlphaType newAlphaType = bm.alphaType();
        SkAlphaType newAlphaType = bm.alphaType();
        switch (pixelFormat) {
        switch (pixelFormat) {
            case ImageDecoder::kUnknown:
            case kUnknown:
                break;
                break;
            case ImageDecoder::kTranslucent:
            case kTranslucent:
                newAlphaType = kPremul_SkAlphaType;
                newAlphaType = kPremul_SkAlphaType;
                break;
                break;
            case ImageDecoder::kOpaque:
            case kOpaque:
                newAlphaType = kOpaque_SkAlphaType;
                newAlphaType = kOpaque_SkAlphaType;
                break;
                break;
            default:
            default:
@@ -477,7 +442,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
                return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
                return bitmap::createBitmap(env, hwBitmap.release(), bitmapCreateFlags,
                                            ninePatchChunk, ninePatchInsets);
                                            ninePatchChunk, ninePatchInsets);
            }
            }
            if (allocator == ImageDecoder::kHardware_Allocator) {
            if (allocator == kHardware_Allocator) {
                doThrowOOME(env, "failed to allocate hardware Bitmap!");
                doThrowOOME(env, "failed to allocate hardware Bitmap!");
                return nullptr;
                return nullptr;
            }
            }
@@ -501,7 +466,7 @@ static jobject ImageDecoder_nGetSampledSize(JNIEnv* env, jobject /*clazz*/, jlon
static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
                                     jobject outPadding) {
                                     jobject outPadding) {
    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
    auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
    decoder->mPeeker->getPadding(env, outPadding);
    reinterpret_cast<NinePatchPeeker*>(decoder->mPeeker.get())->getPadding(env, outPadding);
}
}


static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
static void ImageDecoder_nClose(JNIEnv* /*env*/, jobject /*clazz*/, jlong nativePtr) {
+2 −38
Original line number Original line Diff line number Diff line
@@ -14,48 +14,12 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


#include "NinePatchPeeker.h"

#include <hwui/Canvas.h>
#include <hwui/Canvas.h>


#include <jni.h>
#include <jni.h>


class SkAndroidCodec;

using namespace android;

struct ImageDecoder {
    // These need to stay in sync with ImageDecoder.java's Allocator constants.
    enum Allocator {
        kDefault_Allocator      = 0,
        kSoftware_Allocator     = 1,
        kSharedMemory_Allocator = 2,
        kHardware_Allocator     = 3,
    };

    // These need to stay in sync with ImageDecoder.java's Error constants.
    enum Error {
        kSourceException     = 1,
        kSourceIncomplete    = 2,
        kSourceMalformedData = 3,
    };

    // These need to stay in sync with PixelFormat.java's Format constants.
    enum PixelFormat {
        kUnknown     =  0,
        kTranslucent = -3,
        kOpaque      = -1,
    };

    std::unique_ptr<SkAndroidCodec> mCodec;
    sk_sp<NinePatchPeeker> mPeeker;

    ImageDecoder()
        :mPeeker(new NinePatchPeeker)
    {}
};

// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
// Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
// releases the Canvas.
// releases the Canvas.
// Caller needs to check for exceptions.
// Caller needs to check for exceptions.
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas);
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder,
                           std::unique_ptr<android::Canvas> canvas);
+1 −0
Original line number Original line Diff line number Diff line
@@ -176,6 +176,7 @@ cc_defaults {
        "hwui/AnimatedImageThread.cpp",
        "hwui/AnimatedImageThread.cpp",
        "hwui/Bitmap.cpp",
        "hwui/Bitmap.cpp",
        "hwui/Canvas.cpp",
        "hwui/Canvas.cpp",
        "hwui/ImageDecoder.cpp",
        "hwui/MinikinSkia.cpp",
        "hwui/MinikinSkia.cpp",
        "hwui/MinikinUtils.cpp",
        "hwui/MinikinUtils.cpp",
        "hwui/PaintImpl.cpp",
        "hwui/PaintImpl.cpp",
+2 −4
Original line number Original line Diff line number Diff line
@@ -44,9 +44,7 @@


namespace android {
namespace android {


// returns true if rowBytes * height can be represented by a positive int32_t value
bool Bitmap::computeAllocationSize(size_t rowBytes, int height, size_t* size) {
// and places that value in size.
static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
    return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
    return 0 <= height && height <= std::numeric_limits<size_t>::max() &&
           !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
           !__builtin_mul_overflow(rowBytes, (size_t)height, size) &&
           *size <= std::numeric_limits<int32_t>::max();
           *size <= std::numeric_limits<int32_t>::max();
@@ -66,7 +64,7 @@ static sk_sp<Bitmap> allocateBitmap(SkBitmap* bitmap, AllocPixelRef alloc) {
    // we must respect the rowBytes value already set on the bitmap instead of
    // we must respect the rowBytes value already set on the bitmap instead of
    // attempting to compute our own.
    // attempting to compute our own.
    const size_t rowBytes = bitmap->rowBytes();
    const size_t rowBytes = bitmap->rowBytes();
    if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
    if (!Bitmap::computeAllocationSize(rowBytes, bitmap->height(), &size)) {
        return nullptr;
        return nullptr;
    }
    }


Loading