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