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