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