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 "SkBitmap.h"
     20 #include "SkData.h"
     21 #include "SkImageEncoder.h"
     22 #include "GraphicsJNI.h"
     23 #include "SkUtils.h"
     24 #include "SkTemplates.h"
     25 #include "SkPixelRef.h"
     26 #include "SkStream.h"
     27 #include "BitmapFactory.h"
     28 #include "AutoDecodeCancel.h"
     29 #include "CreateJavaOutputStreamAdaptor.h"
     30 #include "Utils.h"
     31 #include "JNIHelp.h"
     32 
     33 #include <android_runtime/AndroidRuntime.h>
     34 #include "android_util_Binder.h"
     35 #include "android_nio_utils.h"
     36 #include "CreateJavaOutputStreamAdaptor.h"
     37 
     38 #include <binder/Parcel.h>
     39 #include <jni.h>
     40 #include <androidfw/Asset.h>
     41 #include <sys/stat.h>
     42 
     43 #if 0
     44     #define TRACE_BITMAP(code)  code
     45 #else
     46     #define TRACE_BITMAP(code)
     47 #endif
     48 
     49 using namespace android;
     50 
     51 class SkBitmapRegionDecoder {
     52 public:
     53     SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
     54         fDecoder = decoder;
     55         fWidth = width;
     56         fHeight = height;
     57     }
     58     ~SkBitmapRegionDecoder() {
     59         SkDELETE(fDecoder);
     60     }
     61 
     62     bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
     63                       SkBitmap::Config pref, int sampleSize) {
     64         fDecoder->setSampleSize(sampleSize);
     65         return fDecoder->decodeRegion(bitmap, rect, pref);
     66     }
     67 
     68     SkImageDecoder* getDecoder() const { return fDecoder; }
     69     int getWidth() const { return fWidth; }
     70     int getHeight() const { return fHeight; }
     71 
     72 private:
     73     SkImageDecoder* fDecoder;
     74     int fWidth;
     75     int fHeight;
     76 };
     77 
     78 static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
     79     SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
     80     int width, height;
     81     if (NULL == decoder) {
     82         doThrowIOE(env, "Image format not supported");
     83         return nullObjectReturn("SkImageDecoder::Factory returned null");
     84     }
     85 
     86     JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
     87     decoder->setAllocator(javaAllocator);
     88     javaAllocator->unref();
     89 
     90     if (!decoder->buildTileIndex(stream, &width, &height)) {
     91         char msg[100];
     92         snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
     93                 decoder->getFormatName());
     94         doThrowIOE(env, msg);
     95         SkDELETE(decoder);
     96         return nullObjectReturn("decoder->buildTileIndex returned false");
     97     }
     98 
     99     SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
    100     return GraphicsJNI::createBitmapRegionDecoder(env, bm);
    101 }
    102 
    103 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
    104                                      int offset, int length, jboolean isShareable) {
    105     /*  If isShareable we could decide to just wrap the java array and
    106         share it, but that means adding a globalref to the java array object
    107         For now we just always copy the array's data if isShareable.
    108      */
    109     AutoJavaByteArray ar(env, byteArray);
    110     SkStreamRewindable* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
    111 
    112     jobject brd = createBitmapRegionDecoder(env, stream);
    113     SkSafeUnref(stream); // the decoder now holds a reference
    114     return brd;
    115 }
    116 
    117 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
    118                                           jobject fileDescriptor, jboolean isShareable) {
    119     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
    120 
    121     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
    122 
    123     struct stat fdStat;
    124     if (fstat(descriptor, &fdStat) == -1) {
    125         doThrowIOE(env, "broken file descriptor");
    126         return nullObjectReturn("fstat return -1");
    127     }
    128 
    129     SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
    130     SkMemoryStream* stream = new SkMemoryStream(data);
    131 
    132     jobject brd = createBitmapRegionDecoder(env, stream);
    133     SkSafeUnref(stream); // the decoder now holds a reference
    134     return brd;
    135 }
    136 
    137 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
    138                                   jobject is,       // InputStream
    139                                   jbyteArray storage, // byte[]
    140                                   jboolean isShareable) {
    141     jobject brd = NULL;
    142     // for now we don't allow shareable with java inputstreams
    143     SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
    144 
    145     if (stream) {
    146         brd = createBitmapRegionDecoder(env, stream);
    147         stream->unref(); // the decoder now holds a reference
    148     }
    149     return brd;
    150 }
    151 
    152 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
    153                                  jint native_asset, // Asset
    154                                  jboolean isShareable) {
    155     Asset* asset = reinterpret_cast<Asset*>(native_asset);
    156     SkAutoTUnref<SkMemoryStream> stream(CopyAssetToStream(asset));
    157     if (NULL == stream.get()) {
    158         return NULL;
    159     }
    160 
    161     jobject brd = createBitmapRegionDecoder(env, stream.get());
    162     // The decoder now holds a reference to stream.
    163     return brd;
    164 }
    165 
    166 /*
    167  * nine patch not supported
    168  *
    169  * purgeable not supported
    170  * reportSizeToVM not supported
    171  */
    172 static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
    173                                 int start_x, int start_y, int width, int height, jobject options) {
    174     jobject tileBitmap = NULL;
    175     SkImageDecoder *decoder = brd->getDecoder();
    176     int sampleSize = 1;
    177     SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
    178     bool doDither = true;
    179     bool preferQualityOverSpeed = false;
    180     bool requireUnpremultiplied = false;
    181 
    182     if (NULL != options) {
    183         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
    184         // initialize these, in case we fail later on
    185         env->SetIntField(options, gOptions_widthFieldID, -1);
    186         env->SetIntField(options, gOptions_heightFieldID, -1);
    187         env->SetObjectField(options, gOptions_mimeFieldID, 0);
    188 
    189         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
    190         prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig);
    191         doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
    192         preferQualityOverSpeed = env->GetBooleanField(options,
    193                 gOptions_preferQualityOverSpeedFieldID);
    194         // Get the bitmap for re-use if it exists.
    195         tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
    196         requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
    197     }
    198 
    199     decoder->setDitherImage(doDither);
    200     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
    201     decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
    202     AutoDecoderCancel adc(options, decoder);
    203 
    204     // To fix the race condition in case "requestCancelDecode"
    205     // happens earlier than AutoDecoderCancel object is added
    206     // to the gAutoDecoderCancelMutex linked list.
    207     if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
    208         return nullObjectReturn("gOptions_mCancelID");;
    209     }
    210 
    211     SkIRect region;
    212     region.fLeft = start_x;
    213     region.fTop = start_y;
    214     region.fRight = start_x + width;
    215     region.fBottom = start_y + height;
    216     SkBitmap* bitmap = NULL;
    217     SkAutoTDelete<SkBitmap> adb;
    218 
    219     if (tileBitmap != NULL) {
    220         // Re-use bitmap.
    221         bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
    222     }
    223     if (bitmap == NULL) {
    224         bitmap = new SkBitmap;
    225         adb.reset(bitmap);
    226     }
    227 
    228     if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
    229         return nullObjectReturn("decoder->decodeRegion returned false");
    230     }
    231 
    232     // update options (if any)
    233     if (NULL != options) {
    234         env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
    235         env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
    236         // TODO: set the mimeType field with the data from the codec.
    237         // but how to reuse a set of strings, rather than allocating new one
    238         // each time?
    239         env->SetObjectField(options, gOptions_mimeFieldID,
    240                             getMimeTypeString(env, decoder->getFormat()));
    241     }
    242 
    243     if (tileBitmap != NULL) {
    244         return tileBitmap;
    245     }
    246 
    247     // detach bitmap from its autodeleter, since we want to own it now
    248     adb.detach();
    249 
    250     JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
    251     jbyteArray buff = allocator->getStorageObjAndReset();
    252 
    253     int bitmapCreateFlags = 0;
    254     if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
    255     return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
    256 }
    257 
    258 static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
    259     return brd->getHeight();
    260 }
    261 
    262 static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
    263     return brd->getWidth();
    264 }
    265 
    266 static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
    267     delete brd;
    268 }
    269 
    270 ///////////////////////////////////////////////////////////////////////////////
    271 
    272 #include <android_runtime/AndroidRuntime.h>
    273 
    274 static JNINativeMethod gBitmapRegionDecoderMethods[] = {
    275     {   "nativeDecodeRegion",
    276         "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
    277         (void*)nativeDecodeRegion},
    278 
    279     {   "nativeGetHeight", "(I)I", (void*)nativeGetHeight},
    280 
    281     {   "nativeGetWidth", "(I)I", (void*)nativeGetWidth},
    282 
    283     {   "nativeClean", "(I)V", (void*)nativeClean},
    284 
    285     {   "nativeNewInstance",
    286         "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
    287         (void*)nativeNewInstanceFromByteArray
    288     },
    289 
    290     {   "nativeNewInstance",
    291         "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
    292         (void*)nativeNewInstanceFromStream
    293     },
    294 
    295     {   "nativeNewInstance",
    296         "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
    297         (void*)nativeNewInstanceFromFileDescriptor
    298     },
    299 
    300     {   "nativeNewInstance",
    301         "(IZ)Landroid/graphics/BitmapRegionDecoder;",
    302         (void*)nativeNewInstanceFromAsset
    303     },
    304 };
    305 
    306 #define kClassPathName  "android/graphics/BitmapRegionDecoder"
    307 
    308 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
    309 {
    310     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
    311             gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
    312 }
    313