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