BitmapRegionDecoder.cpp revision 3449789b9ca58fee7e5cd02ff89d544f4a6bc9b5
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 "core_jni_helpers.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 43using namespace android; 44 45class SkBitmapRegionDecoder { 46public: 47 SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) { 48 fDecoder = decoder; 49 fWidth = width; 50 fHeight = height; 51 } 52 ~SkBitmapRegionDecoder() { 53 SkDELETE(fDecoder); 54 } 55 56 bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect, 57 SkColorType pref, int sampleSize) { 58 fDecoder->setSampleSize(sampleSize); 59 return fDecoder->decodeSubset(bitmap, rect, pref); 60 } 61 62 SkImageDecoder* getDecoder() const { return fDecoder; } 63 int getWidth() const { return fWidth; } 64 int getHeight() const { return fHeight; } 65 66private: 67 SkImageDecoder* fDecoder; 68 int fWidth; 69 int fHeight; 70}; 71 72// Takes ownership of the SkStreamRewindable. For consistency, deletes stream even 73// when returning null. 74static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) { 75 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 76 int width, height; 77 if (NULL == decoder) { 78 SkDELETE(stream); 79 doThrowIOE(env, "Image format not supported"); 80 return nullObjectReturn("SkImageDecoder::Factory returned null"); 81 } 82 83 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env); 84 decoder->setAllocator(javaAllocator); 85 javaAllocator->unref(); 86 87 // This call passes ownership of stream to the decoder, or deletes on failure. 88 if (!decoder->buildTileIndex(stream, &width, &height)) { 89 char msg[100]; 90 snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder", 91 decoder->getFormatName()); 92 doThrowIOE(env, msg); 93 SkDELETE(decoder); 94 return nullObjectReturn("decoder->buildTileIndex returned false"); 95 } 96 97 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height); 98 return GraphicsJNI::createBitmapRegionDecoder(env, bm); 99} 100 101static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 102 jint offset, jint length, jboolean isShareable) { 103 /* If isShareable we could decide to just wrap the java array and 104 share it, but that means adding a globalref to the java array object 105 For now we just always copy the array's data if isShareable. 106 */ 107 AutoJavaByteArray ar(env, byteArray); 108 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true); 109 110 // the decoder owns the stream. 111 jobject brd = createBitmapRegionDecoder(env, stream); 112 return brd; 113} 114 115static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, 116 jobject fileDescriptor, jboolean isShareable) { 117 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 118 119 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 120 121 struct stat fdStat; 122 if (fstat(descriptor, &fdStat) == -1) { 123 doThrowIOE(env, "broken file descriptor"); 124 return nullObjectReturn("fstat return -1"); 125 } 126 127 SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor)); 128 SkMemoryStream* stream = new SkMemoryStream(data); 129 130 // the decoder owns the stream. 131 jobject brd = createBitmapRegionDecoder(env, stream); 132 return brd; 133} 134 135static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, 136 jobject is, // InputStream 137 jbyteArray storage, // byte[] 138 jboolean isShareable) { 139 jobject brd = NULL; 140 // for now we don't allow shareable with java inputstreams 141 SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage); 142 143 if (stream) { 144 // the decoder owns the stream. 145 brd = createBitmapRegionDecoder(env, stream); 146 } 147 return brd; 148} 149 150static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, 151 jlong native_asset, // Asset 152 jboolean isShareable) { 153 Asset* asset = reinterpret_cast<Asset*>(native_asset); 154 SkMemoryStream* stream = CopyAssetToStream(asset); 155 if (NULL == stream) { 156 return NULL; 157 } 158 159 // the decoder owns the stream. 160 jobject brd = createBitmapRegionDecoder(env, stream); 161 return brd; 162} 163 164/* 165 * nine patch not supported 166 * 167 * purgeable not supported 168 * reportSizeToVM not supported 169 */ 170static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, 171 jint start_x, jint start_y, jint width, jint height, jobject options) { 172 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 173 jobject tileBitmap = NULL; 174 SkImageDecoder *decoder = brd->getDecoder(); 175 int sampleSize = 1; 176 SkColorType prefColorType = kUnknown_SkColorType; 177 bool doDither = true; 178 bool preferQualityOverSpeed = false; 179 bool requireUnpremultiplied = false; 180 181 if (NULL != options) { 182 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 183 // initialize these, in case we fail later on 184 env->SetIntField(options, gOptions_widthFieldID, -1); 185 env->SetIntField(options, gOptions_heightFieldID, -1); 186 env->SetObjectField(options, gOptions_mimeFieldID, 0); 187 188 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 189 prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 190 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 191 preferQualityOverSpeed = env->GetBooleanField(options, 192 gOptions_preferQualityOverSpeedFieldID); 193 // Get the bitmap for re-use if it exists. 194 tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 195 requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 196 } 197 198 decoder->setDitherImage(doDither); 199 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 200 decoder->setRequireUnpremultipliedColors(requireUnpremultiplied); 201 AutoDecoderCancel adc(options, decoder); 202 203 // To fix the race condition in case "requestCancelDecode" 204 // happens earlier than AutoDecoderCancel object is added 205 // to the gAutoDecoderCancelMutex linked list. 206 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 207 return nullObjectReturn("gOptions_mCancelID");; 208 } 209 210 SkIRect region; 211 region.fLeft = start_x; 212 region.fTop = start_y; 213 region.fRight = start_x + width; 214 region.fBottom = start_y + height; 215 SkBitmap* bitmap = NULL; 216 SkAutoTDelete<SkBitmap> adb; 217 218 if (tileBitmap != NULL) { 219 // Re-use bitmap. 220 bitmap = GraphicsJNI::getSkBitmap(env, tileBitmap); 221 } 222 if (bitmap == NULL) { 223 bitmap = new SkBitmap; 224 adb.reset(bitmap); 225 } 226 227 if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) { 228 return nullObjectReturn("decoder->decodeRegion returned false"); 229 } 230 231 // update options (if any) 232 if (NULL != options) { 233 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 234 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 235 // TODO: set the mimeType field with the data from the codec. 236 // but how to reuse a set of strings, rather than allocating new one 237 // each time? 238 env->SetObjectField(options, gOptions_mimeFieldID, 239 getMimeTypeString(env, decoder->getFormat())); 240 } 241 242 if (tileBitmap != NULL) { 243 bitmap->notifyPixelsChanged(); 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 258static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { 259 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 260 return static_cast<jint>(brd->getHeight()); 261} 262 263static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { 264 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 265 return static_cast<jint>(brd->getWidth()); 266} 267 268static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { 269 SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 270 delete brd; 271} 272 273/////////////////////////////////////////////////////////////////////////////// 274 275static JNINativeMethod gBitmapRegionDecoderMethods[] = { 276 { "nativeDecodeRegion", 277 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 278 (void*)nativeDecodeRegion}, 279 280 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, 281 282 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, 283 284 { "nativeClean", "(J)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 "(JZ)Landroid/graphics/BitmapRegionDecoder;", 303 (void*)nativeNewInstanceFromAsset 304 }, 305}; 306 307int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 308{ 309 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", 310 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); 311} 312