BitmapRegionDecoder.cpp revision 58c1579ce2634de31d24429c1b870d4256ee4f21
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 "SkImageEncoder.h" 21#include "GraphicsJNI.h" 22#include "SkUtils.h" 23#include "SkTemplates.h" 24#include "SkPixelRef.h" 25#include "SkStream.h" 26#include "BitmapFactory.h" 27#include "AutoDecodeCancel.h" 28#include "SkBitmapRegionDecoder.h" 29#include "CreateJavaOutputStreamAdaptor.h" 30#include "Utils.h" 31 32#include <android_runtime/AndroidRuntime.h> 33#include "android_util_Binder.h" 34#include "android_nio_utils.h" 35#include "CreateJavaOutputStreamAdaptor.h" 36 37#include <binder/Parcel.h> 38#include <jni.h> 39#include <utils/Asset.h> 40#include <sys/stat.h> 41 42static jclass gFileDescriptor_class; 43static jfieldID gFileDescriptor_descriptor; 44 45#if 0 46 #define TRACE_BITMAP(code) code 47#else 48 #define TRACE_BITMAP(code) 49#endif 50 51using namespace android; 52 53static SkMemoryStream* buildSkMemoryStream(SkStream *stream) { 54 size_t bufferSize = 4096; 55 size_t streamLen = 0; 56 size_t len; 57 char* data = (char*)sk_malloc_throw(bufferSize); 58 59 while ((len = stream->read(data + streamLen, 60 bufferSize - streamLen)) != 0) { 61 streamLen += len; 62 if (streamLen == bufferSize) { 63 bufferSize *= 2; 64 data = (char*)sk_realloc_throw(data, bufferSize); 65 } 66 } 67 data = (char*)sk_realloc_throw(data, streamLen); 68 69 SkMemoryStream* streamMem = new SkMemoryStream(); 70 streamMem->setMemoryOwned(data, streamLen); 71 return streamMem; 72} 73 74static jobject doBuildTileIndex(JNIEnv* env, SkStream* stream) { 75 SkImageDecoder* decoder = SkImageDecoder::Factory(stream); 76 int width, height; 77 if (NULL == decoder) { 78 doThrowIOE(env, "Image format not supported"); 79 return nullObjectReturn("SkImageDecoder::Factory returned null"); 80 } 81 82 JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env, true); 83 decoder->setAllocator(javaAllocator); 84 JavaMemoryUsageReporter *javaMemoryReporter = new JavaMemoryUsageReporter(env); 85 decoder->setReporter(javaMemoryReporter); 86 javaAllocator->unref(); 87 javaMemoryReporter->unref(); 88 89 if (!decoder->buildTileIndex(stream, &width, &height)) { 90 char msg[100]; 91 snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder", 92 decoder->getFormatName()); 93 doThrowIOE(env, msg); 94 return nullObjectReturn("decoder->buildTileIndex returned false"); 95 } 96 97 SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height); 98 99 return GraphicsJNI::createBitmapRegionDecoder(env, bm); 100} 101 102static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 103 int offset, int length, jboolean isShareable) { 104 /* If isShareable we could decide to just wrap the java array and 105 share it, but that means adding a globalref to the java array object 106 For now we just always copy the array's data if isShareable. 107 */ 108 AutoJavaByteArray ar(env, byteArray); 109 SkStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true); 110 return doBuildTileIndex(env, stream); 111} 112 113static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, 114 jobject fileDescriptor, jboolean isShareable) { 115 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 116 117 jint descriptor = env->GetIntField(fileDescriptor, 118 gFileDescriptor_descriptor); 119 SkStream *stream = NULL; 120 struct stat fdStat; 121 int newFD; 122 if (fstat(descriptor, &fdStat) == -1) { 123 doThrowIOE(env, "broken file descriptor"); 124 return nullObjectReturn("fstat return -1"); 125 } 126 127 if (isShareable && 128 S_ISREG(fdStat.st_mode) && 129 (newFD = ::dup(descriptor)) != -1) { 130 SkFDStream* fdStream = new SkFDStream(newFD, true); 131 if (!fdStream->isValid()) { 132 fdStream->unref(); 133 return NULL; 134 } 135 stream = fdStream; 136 } else { 137 /* Restore our offset when we leave, so we can be called more than once 138 with the same descriptor. This is only required if we didn't dup the 139 file descriptor, but it is OK to do it all the time. 140 */ 141 AutoFDSeek as(descriptor); 142 143 SkFDStream* fdStream = new SkFDStream(descriptor, false); 144 if (!fdStream->isValid()) { 145 fdStream->unref(); 146 return NULL; 147 } 148 stream = buildSkMemoryStream(fdStream); 149 fdStream->unref(); 150 } 151 152 return doBuildTileIndex(env, stream); 153} 154 155static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, 156 jobject is, // InputStream 157 jbyteArray storage, // byte[] 158 jboolean isShareable) { 159 jobject largeBitmap = NULL; 160 SkStream* stream = CreateJavaInputStreamAdaptor(env, is, storage, 1024); 161 162 if (stream) { 163 // for now we don't allow shareable with java inputstreams 164 SkMemoryStream *mStream = buildSkMemoryStream(stream); 165 largeBitmap = doBuildTileIndex(env, mStream); 166 stream->unref(); 167 } 168 return largeBitmap; 169} 170 171static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, 172 jint native_asset, // Asset 173 jboolean isShareable) { 174 SkStream* stream, *assStream; 175 Asset* asset = reinterpret_cast<Asset*>(native_asset); 176 assStream = new AssetStreamAdaptor(asset); 177 stream = buildSkMemoryStream(assStream); 178 assStream->unref(); 179 return doBuildTileIndex(env, stream); 180} 181 182/* 183 * nine patch not supported 184 * 185 * purgeable not supported 186 * reportSizeToVM not supported 187 */ 188static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd, 189 int start_x, int start_y, int width, int height, jobject options) { 190 SkImageDecoder *decoder = brd->getDecoder(); 191 int sampleSize = 1; 192 SkBitmap::Config prefConfig = SkBitmap::kNo_Config; 193 bool doDither = true; 194 195 if (NULL != options) { 196 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 197 // initialize these, in case we fail later on 198 env->SetIntField(options, gOptions_widthFieldID, -1); 199 env->SetIntField(options, gOptions_heightFieldID, -1); 200 env->SetObjectField(options, gOptions_mimeFieldID, 0); 201 202 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 203 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 204 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 205 } 206 207 decoder->setDitherImage(doDither); 208 SkBitmap* bitmap = new SkBitmap; 209 SkAutoTDelete<SkBitmap> adb(bitmap); 210 AutoDecoderCancel adc(options, decoder); 211 212 // To fix the race condition in case "requestCancelDecode" 213 // happens earlier than AutoDecoderCancel object is added 214 // to the gAutoDecoderCancelMutex linked list. 215 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 216 return nullObjectReturn("gOptions_mCancelID");; 217 } 218 219 SkIRect region; 220 region.fLeft = start_x; 221 region.fTop = start_y; 222 region.fRight = start_x + width; 223 region.fBottom = start_y + height; 224 225 if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) { 226 return nullObjectReturn("decoder->decodeRegion returned false"); 227 } 228 229 // update options (if any) 230 if (NULL != options) { 231 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 232 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 233 // TODO: set the mimeType field with the data from the codec. 234 // but how to reuse a set of strings, rather than allocating new one 235 // each time? 236 env->SetObjectField(options, gOptions_mimeFieldID, 237 getMimeTypeString(env, decoder->getFormat())); 238 } 239 240 // detach bitmap from its autotdeleter, since we want to own it now 241 adb.detach(); 242 243 SkPixelRef* pr; 244 pr = bitmap->pixelRef(); 245 // promise we will never change our pixels (great for sharing and pictures) 246 pr->setImmutable(); 247 // now create the java bitmap 248 return GraphicsJNI::createBitmap(env, bitmap, false, NULL); 249} 250 251static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 252 return brd->getHeight(); 253} 254 255static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 256 return brd->getWidth(); 257} 258 259static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 260 delete brd; 261} 262 263/////////////////////////////////////////////////////////////////////////////// 264 265#include <android_runtime/AndroidRuntime.h> 266 267static JNINativeMethod gBitmapRegionDecoderMethods[] = { 268 { "nativeDecodeRegion", 269 "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 270 (void*)nativeDecodeRegion}, 271 272 { "nativeGetHeight", "(I)I", (void*)nativeGetHeight}, 273 274 { "nativeGetWidth", "(I)I", (void*)nativeGetWidth}, 275 276 { "nativeClean", "(I)V", (void*)nativeClean}, 277 278 { "nativeNewInstance", 279 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", 280 (void*)nativeNewInstanceFromByteArray 281 }, 282 283 { "nativeNewInstance", 284 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", 285 (void*)nativeNewInstanceFromStream 286 }, 287 288 { "nativeNewInstance", 289 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", 290 (void*)nativeNewInstanceFromFileDescriptor 291 }, 292 293 { "nativeNewInstance", 294 "(IZ)Landroid/graphics/BitmapRegionDecoder;", 295 (void*)nativeNewInstanceFromAsset 296 }, 297}; 298 299#define kClassPathName "android/graphics/BitmapRegionDecoder" 300 301static jclass make_globalref(JNIEnv* env, const char classname[]) { 302 jclass c = env->FindClass(classname); 303 SkASSERT(c); 304 return (jclass)env->NewGlobalRef(c); 305} 306 307static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 308 const char fieldname[], const char type[]) { 309 jfieldID id = env->GetFieldID(clazz, fieldname, type); 310 SkASSERT(id); 311 return id; 312} 313 314int register_android_graphics_BitmapRegionDecoder(JNIEnv* env); 315int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 316{ 317 318 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 319 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 320 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 321 gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods)); 322} 323