BitmapRegionDecoder.cpp revision 25bce3a673afef6a7858270afae4395b4ab53de3
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 bool preferQualityOverSpeed = false; 195 196 if (NULL != options) { 197 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 198 // initialize these, in case we fail later on 199 env->SetIntField(options, gOptions_widthFieldID, -1); 200 env->SetIntField(options, gOptions_heightFieldID, -1); 201 env->SetObjectField(options, gOptions_mimeFieldID, 0); 202 203 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 204 prefConfig = GraphicsJNI::getNativeBitmapConfig(env, jconfig); 205 doDither = env->GetBooleanField(options, gOptions_ditherFieldID); 206 preferQualityOverSpeed = env->GetBooleanField(options, 207 gOptions_preferQualityOverSpeedFieldID); 208 } 209 210 decoder->setDitherImage(doDither); 211 decoder->setPreferQualityOverSpeed(preferQualityOverSpeed); 212 SkBitmap* bitmap = new SkBitmap; 213 SkAutoTDelete<SkBitmap> adb(bitmap); 214 AutoDecoderCancel adc(options, decoder); 215 216 // To fix the race condition in case "requestCancelDecode" 217 // happens earlier than AutoDecoderCancel object is added 218 // to the gAutoDecoderCancelMutex linked list. 219 if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) { 220 return nullObjectReturn("gOptions_mCancelID");; 221 } 222 223 SkIRect region; 224 region.fLeft = start_x; 225 region.fTop = start_y; 226 region.fRight = start_x + width; 227 region.fBottom = start_y + height; 228 229 if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) { 230 return nullObjectReturn("decoder->decodeRegion returned false"); 231 } 232 233 // update options (if any) 234 if (NULL != options) { 235 env->SetIntField(options, gOptions_widthFieldID, bitmap->width()); 236 env->SetIntField(options, gOptions_heightFieldID, bitmap->height()); 237 // TODO: set the mimeType field with the data from the codec. 238 // but how to reuse a set of strings, rather than allocating new one 239 // each time? 240 env->SetObjectField(options, gOptions_mimeFieldID, 241 getMimeTypeString(env, decoder->getFormat())); 242 } 243 244 // detach bitmap from its autotdeleter, since we want to own it now 245 adb.detach(); 246 247 SkPixelRef* pr; 248 pr = bitmap->pixelRef(); 249 // promise we will never change our pixels (great for sharing and pictures) 250 pr->setImmutable(); 251 // now create the java bitmap 252 return GraphicsJNI::createBitmap(env, bitmap, false, NULL); 253} 254 255static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 256 return brd->getHeight(); 257} 258 259static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 260 return brd->getWidth(); 261} 262 263static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) { 264 delete brd; 265} 266 267/////////////////////////////////////////////////////////////////////////////// 268 269#include <android_runtime/AndroidRuntime.h> 270 271static JNINativeMethod gBitmapRegionDecoderMethods[] = { 272 { "nativeDecodeRegion", 273 "(IIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 274 (void*)nativeDecodeRegion}, 275 276 { "nativeGetHeight", "(I)I", (void*)nativeGetHeight}, 277 278 { "nativeGetWidth", "(I)I", (void*)nativeGetWidth}, 279 280 { "nativeClean", "(I)V", (void*)nativeClean}, 281 282 { "nativeNewInstance", 283 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", 284 (void*)nativeNewInstanceFromByteArray 285 }, 286 287 { "nativeNewInstance", 288 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", 289 (void*)nativeNewInstanceFromStream 290 }, 291 292 { "nativeNewInstance", 293 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", 294 (void*)nativeNewInstanceFromFileDescriptor 295 }, 296 297 { "nativeNewInstance", 298 "(IZ)Landroid/graphics/BitmapRegionDecoder;", 299 (void*)nativeNewInstanceFromAsset 300 }, 301}; 302 303#define kClassPathName "android/graphics/BitmapRegionDecoder" 304 305static jclass make_globalref(JNIEnv* env, const char classname[]) { 306 jclass c = env->FindClass(classname); 307 SkASSERT(c); 308 return (jclass)env->NewGlobalRef(c); 309} 310 311static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 312 const char fieldname[], const char type[]) { 313 jfieldID id = env->GetFieldID(clazz, fieldname, type); 314 SkASSERT(id); 315 return id; 316} 317 318int register_android_graphics_BitmapRegionDecoder(JNIEnv* env); 319int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 320{ 321 322 gFileDescriptor_class = make_globalref(env, "java/io/FileDescriptor"); 323 gFileDescriptor_descriptor = getFieldIDCheck(env, gFileDescriptor_class, "descriptor", "I"); 324 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 325 gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods)); 326} 327