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