Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "BitmapRegionDecoder"
     18 
     19 #include "BitmapFactory.h"
     20 #include "CreateJavaOutputStreamAdaptor.h"
     21 #include "GraphicsJNI.h"
     22 #include "Utils.h"
     23 
     24 #include "SkBitmap.h"
     25 #include "SkBitmapRegionDecoder.h"
     26 #include "SkCodec.h"
     27 #include "SkData.h"
     28 #include "SkUtils.h"
     29 #include "SkPixelRef.h"
     30 #include "SkStream.h"
     31 
     32 #include "android_nio_utils.h"
     33 #include "android_util_Binder.h"
     34 #include "core_jni_helpers.h"
     35 
     36 #include <nativehelper/JNIHelp.h>
     37 #include <androidfw/Asset.h>
     38 #include <binder/Parcel.h>
     39 #include <jni.h>
     40 #include <sys/stat.h>
     41 
     42 #include <memory>
     43 
     44 using namespace android;
     45 
     46 static jobject createBitmapRegionDecoder(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream) {
     47   std::unique_ptr<SkBitmapRegionDecoder> brd(
     48             SkBitmapRegionDecoder::Create(stream.release(),
     49                                           SkBitmapRegionDecoder::kAndroidCodec_Strategy));
     50     if (!brd) {
     51         doThrowIOE(env, "Image format not supported");
     52         return nullObjectReturn("CreateBitmapRegionDecoder returned null");
     53     }
     54 
     55     return GraphicsJNI::createBitmapRegionDecoder(env, brd.release());
     56 }
     57 
     58 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
     59                                      jint offset, jint length, jboolean isShareable) {
     60     /*  If isShareable we could decide to just wrap the java array and
     61         share it, but that means adding a globalref to the java array object
     62         For now we just always copy the array's data if isShareable.
     63      */
     64     AutoJavaByteArray ar(env, byteArray);
     65     std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(ar.ptr() + offset, length, true));
     66 
     67     // the decoder owns the stream.
     68     jobject brd = createBitmapRegionDecoder(env, std::move(stream));
     69     return brd;
     70 }
     71 
     72 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
     73                                           jobject fileDescriptor, jboolean isShareable) {
     74     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
     75 
     76     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
     77 
     78     struct stat fdStat;
     79     if (fstat(descriptor, &fdStat) == -1) {
     80         doThrowIOE(env, "broken file descriptor");
     81         return nullObjectReturn("fstat return -1");
     82     }
     83 
     84     sk_sp<SkData> data(SkData::MakeFromFD(descriptor));
     85     std::unique_ptr<SkMemoryStream> stream(new SkMemoryStream(std::move(data)));
     86 
     87     // the decoder owns the stream.
     88     jobject brd = createBitmapRegionDecoder(env, std::move(stream));
     89     return brd;
     90 }
     91 
     92 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
     93                                   jobject is,       // InputStream
     94                                   jbyteArray storage, // byte[]
     95                                   jboolean isShareable) {
     96     jobject brd = NULL;
     97     // for now we don't allow shareable with java inputstreams
     98     std::unique_ptr<SkStreamRewindable> stream(CopyJavaInputStream(env, is, storage));
     99 
    100     if (stream) {
    101         // the decoder owns the stream.
    102         brd = createBitmapRegionDecoder(env, std::move(stream));
    103     }
    104     return brd;
    105 }
    106 
    107 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
    108                                  jlong native_asset, // Asset
    109                                  jboolean isShareable) {
    110     Asset* asset = reinterpret_cast<Asset*>(native_asset);
    111     std::unique_ptr<SkMemoryStream> stream(CopyAssetToStream(asset));
    112     if (NULL == stream) {
    113         return NULL;
    114     }
    115 
    116     // the decoder owns the stream.
    117     jobject brd = createBitmapRegionDecoder(env, std::move(stream));
    118     return brd;
    119 }
    120 
    121 /*
    122  * nine patch not supported
    123  * purgeable not supported
    124  * reportSizeToVM not supported
    125  */
    126 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
    127         jint inputY, jint inputWidth, jint inputHeight, jobject options) {
    128 
    129     // Set default options.
    130     int sampleSize = 1;
    131     SkColorType colorType = kN32_SkColorType;
    132     bool requireUnpremul = false;
    133     jobject javaBitmap = NULL;
    134     bool isHardware = false;
    135     sk_sp<SkColorSpace> colorSpace = nullptr;
    136     // Update the default options with any options supplied by the client.
    137     if (NULL != options) {
    138         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
    139         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
    140         colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
    141         jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
    142         colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
    143         isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
    144         requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
    145         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
    146         // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
    147         // ignore the values of these fields.
    148 
    149         // Initialize these fields to indicate a failure.  If the decode succeeds, we
    150         // will update them later on.
    151         env->SetIntField(options, gOptions_widthFieldID, -1);
    152         env->SetIntField(options, gOptions_heightFieldID, -1);
    153         env->SetObjectField(options, gOptions_mimeFieldID, 0);
    154         env->SetObjectField(options, gOptions_outConfigFieldID, 0);
    155         env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
    156     }
    157 
    158     // Recycle a bitmap if possible.
    159     android::Bitmap* recycledBitmap = nullptr;
    160     size_t recycledBytes = 0;
    161     if (javaBitmap) {
    162         recycledBitmap = &bitmap::toBitmap(env, javaBitmap);
    163         if (recycledBitmap->isImmutable()) {
    164             ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
    165         }
    166         recycledBytes = bitmap::getBitmapAllocationByteCount(env, javaBitmap);
    167     }
    168 
    169     SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    170     SkColorType decodeColorType = brd->computeOutputColorType(colorType);
    171 
    172     // Set up the pixel allocator
    173     SkBRDAllocator* allocator = nullptr;
    174     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
    175     HeapAllocator heapAlloc;
    176     if (javaBitmap) {
    177         allocator = &recycleAlloc;
    178         // We are required to match the color type of the recycled bitmap.
    179         decodeColorType = recycledBitmap->info().colorType();
    180     } else {
    181         allocator = &heapAlloc;
    182     }
    183 
    184     sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
    185             decodeColorType, colorSpace);
    186 
    187     // Decode the region.
    188     SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
    189     SkBitmap bitmap;
    190     if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
    191             decodeColorType, requireUnpremul, decodeColorSpace)) {
    192         return nullObjectReturn("Failed to decode region.");
    193     }
    194 
    195     // If the client provided options, indicate that the decode was successful.
    196     if (NULL != options) {
    197         env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
    198         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
    199 
    200         env->SetObjectField(options, gOptions_mimeFieldID,
    201                 encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat()));
    202         if (env->ExceptionCheck()) {
    203             return nullObjectReturn("OOM in encodedFormatToString()");
    204         }
    205 
    206         jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
    207         if (isHardware) {
    208             configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
    209         }
    210         jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
    211                 gBitmapConfig_nativeToConfigMethodID, configID);
    212         env->SetObjectField(options, gOptions_outConfigFieldID, config);
    213 
    214         env->SetObjectField(options, gOptions_outColorSpaceFieldID,
    215                 GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
    216     }
    217 
    218     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
    219     if (javaBitmap) {
    220         recycleAlloc.copyIfNecessary();
    221         bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
    222         return javaBitmap;
    223     }
    224 
    225     int bitmapCreateFlags = 0;
    226     if (!requireUnpremul) {
    227         bitmapCreateFlags |= android::bitmap::kBitmapCreateFlag_Premultiplied;
    228     }
    229     if (isHardware) {
    230         sk_sp<Bitmap> hardwareBitmap = Bitmap::allocateHardwareBitmap(bitmap);
    231         return bitmap::createBitmap(env, hardwareBitmap.release(), bitmapCreateFlags);
    232     }
    233     return android::bitmap::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
    234 }
    235 
    236 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
    237     SkBitmapRegionDecoder* brd =
    238             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    239     return static_cast<jint>(brd->height());
    240 }
    241 
    242 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
    243     SkBitmapRegionDecoder* brd =
    244             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    245     return static_cast<jint>(brd->width());
    246 }
    247 
    248 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
    249     SkBitmapRegionDecoder* brd =
    250             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    251     delete brd;
    252 }
    253 
    254 ///////////////////////////////////////////////////////////////////////////////
    255 
    256 static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
    257     {   "nativeDecodeRegion",
    258         "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
    259         (void*)nativeDecodeRegion},
    260 
    261     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
    262 
    263     {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
    264 
    265     {   "nativeClean", "(J)V", (void*)nativeClean},
    266 
    267     {   "nativeNewInstance",
    268         "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
    269         (void*)nativeNewInstanceFromByteArray
    270     },
    271 
    272     {   "nativeNewInstance",
    273         "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
    274         (void*)nativeNewInstanceFromStream
    275     },
    276 
    277     {   "nativeNewInstance",
    278         "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
    279         (void*)nativeNewInstanceFromFileDescriptor
    280     },
    281 
    282     {   "nativeNewInstance",
    283         "(JZ)Landroid/graphics/BitmapRegionDecoder;",
    284         (void*)nativeNewInstanceFromAsset
    285     },
    286 };
    287 
    288 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
    289 {
    290     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
    291             gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
    292 }
    293