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 <JNIHelp.h>
     37 #include <androidfw/Asset.h>
     38 #include <binder/Parcel.h>
     39 #include <jni.h>
     40 #include <sys/stat.h>
     41 
     42 using namespace android;
     43 
     44 // Takes ownership of the SkStreamRewindable. For consistency, deletes stream even
     45 // when returning null.
     46 static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
     47     SkAutoTDelete<SkBitmapRegionDecoder> brd(
     48             SkBitmapRegionDecoder::Create(stream, SkBitmapRegionDecoder::kAndroidCodec_Strategy));
     49     if (NULL == brd) {
     50         doThrowIOE(env, "Image format not supported");
     51         return nullObjectReturn("CreateBitmapRegionDecoder returned null");
     52     }
     53 
     54     return GraphicsJNI::createBitmapRegionDecoder(env, brd.detach());
     55 }
     56 
     57 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
     58                                      jint offset, jint length, jboolean isShareable) {
     59     /*  If isShareable we could decide to just wrap the java array and
     60         share it, but that means adding a globalref to the java array object
     61         For now we just always copy the array's data if isShareable.
     62      */
     63     AutoJavaByteArray ar(env, byteArray);
     64     SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
     65 
     66     // the decoder owns the stream.
     67     jobject brd = createBitmapRegionDecoder(env, stream);
     68     return brd;
     69 }
     70 
     71 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
     72                                           jobject fileDescriptor, jboolean isShareable) {
     73     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
     74 
     75     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
     76 
     77     struct stat fdStat;
     78     if (fstat(descriptor, &fdStat) == -1) {
     79         doThrowIOE(env, "broken file descriptor");
     80         return nullObjectReturn("fstat return -1");
     81     }
     82 
     83     SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
     84     SkMemoryStream* stream = new SkMemoryStream(data);
     85 
     86     // the decoder owns the stream.
     87     jobject brd = createBitmapRegionDecoder(env, stream);
     88     return brd;
     89 }
     90 
     91 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
     92                                   jobject is,       // InputStream
     93                                   jbyteArray storage, // byte[]
     94                                   jboolean isShareable) {
     95     jobject brd = NULL;
     96     // for now we don't allow shareable with java inputstreams
     97     SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
     98 
     99     if (stream) {
    100         // the decoder owns the stream.
    101         brd = createBitmapRegionDecoder(env, stream);
    102     }
    103     return brd;
    104 }
    105 
    106 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
    107                                  jlong native_asset, // Asset
    108                                  jboolean isShareable) {
    109     Asset* asset = reinterpret_cast<Asset*>(native_asset);
    110     SkMemoryStream* stream = CopyAssetToStream(asset);
    111     if (NULL == stream) {
    112         return NULL;
    113     }
    114 
    115     // the decoder owns the stream.
    116     jobject brd = createBitmapRegionDecoder(env, stream);
    117     return brd;
    118 }
    119 
    120 /*
    121  * nine patch not supported
    122  * purgeable not supported
    123  * reportSizeToVM not supported
    124  */
    125 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
    126         jint inputY, jint inputWidth, jint inputHeight, jobject options) {
    127 
    128     // Set default options.
    129     int sampleSize = 1;
    130     SkColorType colorType = kN32_SkColorType;
    131     bool requireUnpremul = false;
    132     jobject javaBitmap = NULL;
    133 
    134     // Update the default options with any options supplied by the client.
    135     if (NULL != options) {
    136         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
    137         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
    138         colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
    139         requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
    140         javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
    141         // The Java options of ditherMode and preferQualityOverSpeed are deprecated.  We will
    142         // ignore the values of these fields.
    143 
    144         // Initialize these fields to indicate a failure.  If the decode succeeds, we
    145         // will update them later on.
    146         env->SetIntField(options, gOptions_widthFieldID, -1);
    147         env->SetIntField(options, gOptions_heightFieldID, -1);
    148         env->SetObjectField(options, gOptions_mimeFieldID, 0);
    149     }
    150 
    151     // Recycle a bitmap if possible.
    152     android::Bitmap* recycledBitmap = nullptr;
    153     size_t recycledBytes = 0;
    154     if (javaBitmap) {
    155         recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
    156         if (recycledBitmap->peekAtPixelRef()->isImmutable()) {
    157             ALOGW("Warning: Reusing an immutable bitmap as an image decoder target.");
    158         }
    159         recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
    160     }
    161 
    162     // Set up the pixel allocator
    163     SkBRDAllocator* allocator = nullptr;
    164     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
    165     JavaPixelAllocator javaAlloc(env);
    166     if (javaBitmap) {
    167         allocator = &recycleAlloc;
    168         // We are required to match the color type of the recycled bitmap.
    169         colorType = recycledBitmap->info().colorType();
    170     } else {
    171         allocator = &javaAlloc;
    172     }
    173 
    174     // Decode the region.
    175     SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
    176     SkBitmapRegionDecoder* brd =
    177             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    178     SkBitmap bitmap;
    179     if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) {
    180         return nullObjectReturn("Failed to decode region.");
    181     }
    182 
    183     // If the client provided options, indicate that the decode was successful.
    184     if (NULL != options) {
    185         env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
    186         env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
    187         env->SetObjectField(options, gOptions_mimeFieldID,
    188                 encodedFormatToString(env, brd->getEncodedFormat()));
    189         if (env->ExceptionCheck()) {
    190             return nullObjectReturn("OOM in encodedFormatToString()");
    191         }
    192     }
    193 
    194     // If we may have reused a bitmap, we need to indicate that the pixels have changed.
    195     if (javaBitmap) {
    196         recycleAlloc.copyIfNecessary();
    197         return javaBitmap;
    198     }
    199 
    200     int bitmapCreateFlags = 0;
    201     if (!requireUnpremul) {
    202         bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
    203     }
    204     return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags);
    205 }
    206 
    207 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
    208     SkBitmapRegionDecoder* brd =
    209             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    210     return static_cast<jint>(brd->height());
    211 }
    212 
    213 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
    214     SkBitmapRegionDecoder* brd =
    215             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    216     return static_cast<jint>(brd->width());
    217 }
    218 
    219 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
    220     SkBitmapRegionDecoder* brd =
    221             reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
    222     delete brd;
    223 }
    224 
    225 ///////////////////////////////////////////////////////////////////////////////
    226 
    227 static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
    228     {   "nativeDecodeRegion",
    229         "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
    230         (void*)nativeDecodeRegion},
    231 
    232     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
    233 
    234     {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
    235 
    236     {   "nativeClean", "(J)V", (void*)nativeClean},
    237 
    238     {   "nativeNewInstance",
    239         "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
    240         (void*)nativeNewInstanceFromByteArray
    241     },
    242 
    243     {   "nativeNewInstance",
    244         "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
    245         (void*)nativeNewInstanceFromStream
    246     },
    247 
    248     {   "nativeNewInstance",
    249         "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
    250         (void*)nativeNewInstanceFromFileDescriptor
    251     },
    252 
    253     {   "nativeNewInstance",
    254         "(JZ)Landroid/graphics/BitmapRegionDecoder;",
    255         (void*)nativeNewInstanceFromAsset
    256     },
    257 };
    258 
    259 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
    260 {
    261     return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder",
    262             gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods));
    263 }
    264