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