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 "BitmapFactory.h" 20 #include "CreateJavaOutputStreamAdaptor.h" 21 #include "GraphicsJNI.h" 22 #include "Utils.h" 23 24 #include "SkBitmap.h" 25 #include "SkBitmapRegionDecoder.h" 26 #include "SkCodec.h" 27 #include "SkData.h" 28 #include "SkUtils.h" 29 #include "SkPixelRef.h" 30 #include "SkStream.h" 31 32 #include "android_nio_utils.h" 33 #include "android_util_Binder.h" 34 #include "core_jni_helpers.h" 35 36 #include <JNIHelp.h> 37 #include <androidfw/Asset.h> 38 #include <binder/Parcel.h> 39 #include <jni.h> 40 #include <sys/stat.h> 41 42 using namespace android; 43 44 // Takes ownership of the SkStreamRewindable. For consistency, deletes stream even 45 // when returning null. 46 static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) { 47 SkAutoTDelete<SkBitmapRegionDecoder> brd( 48 SkBitmapRegionDecoder::Create(stream, SkBitmapRegionDecoder::kAndroidCodec_Strategy)); 49 if (NULL == brd) { 50 doThrowIOE(env, "Image format not supported"); 51 return nullObjectReturn("CreateBitmapRegionDecoder returned null"); 52 } 53 54 return GraphicsJNI::createBitmapRegionDecoder(env, brd.detach()); 55 } 56 57 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray, 58 jint offset, jint length, jboolean isShareable) { 59 /* If isShareable we could decide to just wrap the java array and 60 share it, but that means adding a globalref to the java array object 61 For now we just always copy the array's data if isShareable. 62 */ 63 AutoJavaByteArray ar(env, byteArray); 64 SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true); 65 66 // the decoder owns the stream. 67 jobject brd = createBitmapRegionDecoder(env, stream); 68 return brd; 69 } 70 71 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz, 72 jobject fileDescriptor, jboolean isShareable) { 73 NPE_CHECK_RETURN_ZERO(env, fileDescriptor); 74 75 jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor); 76 77 struct stat fdStat; 78 if (fstat(descriptor, &fdStat) == -1) { 79 doThrowIOE(env, "broken file descriptor"); 80 return nullObjectReturn("fstat return -1"); 81 } 82 83 SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor)); 84 SkMemoryStream* stream = new SkMemoryStream(data); 85 86 // the decoder owns the stream. 87 jobject brd = createBitmapRegionDecoder(env, stream); 88 return brd; 89 } 90 91 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz, 92 jobject is, // InputStream 93 jbyteArray storage, // byte[] 94 jboolean isShareable) { 95 jobject brd = NULL; 96 // for now we don't allow shareable with java inputstreams 97 SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage); 98 99 if (stream) { 100 // the decoder owns the stream. 101 brd = createBitmapRegionDecoder(env, stream); 102 } 103 return brd; 104 } 105 106 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz, 107 jlong native_asset, // Asset 108 jboolean isShareable) { 109 Asset* asset = reinterpret_cast<Asset*>(native_asset); 110 SkMemoryStream* stream = CopyAssetToStream(asset); 111 if (NULL == stream) { 112 return NULL; 113 } 114 115 // the decoder owns the stream. 116 jobject brd = createBitmapRegionDecoder(env, stream); 117 return brd; 118 } 119 120 /* 121 * nine patch not supported 122 * purgeable not supported 123 * reportSizeToVM not supported 124 */ 125 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX, 126 jint inputY, jint inputWidth, jint inputHeight, jobject options) { 127 128 // Set default options. 129 int sampleSize = 1; 130 SkColorType colorType = kN32_SkColorType; 131 bool requireUnpremul = false; 132 jobject javaBitmap = NULL; 133 134 // Update the default options with any options supplied by the client. 135 if (NULL != options) { 136 sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID); 137 jobject jconfig = env->GetObjectField(options, gOptions_configFieldID); 138 colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig); 139 requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID); 140 javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID); 141 // The Java options of ditherMode and preferQualityOverSpeed are deprecated. We will 142 // ignore the values of these fields. 143 144 // Initialize these fields to indicate a failure. If the decode succeeds, we 145 // will update them later on. 146 env->SetIntField(options, gOptions_widthFieldID, -1); 147 env->SetIntField(options, gOptions_heightFieldID, -1); 148 env->SetObjectField(options, gOptions_mimeFieldID, 0); 149 } 150 151 // Recycle a bitmap if possible. 152 android::Bitmap* recycledBitmap = nullptr; 153 size_t recycledBytes = 0; 154 if (javaBitmap) { 155 recycledBitmap = GraphicsJNI::getBitmap(env, javaBitmap); 156 if (recycledBitmap->peekAtPixelRef()->isImmutable()) { 157 ALOGW("Warning: Reusing an immutable bitmap as an image decoder target."); 158 } 159 recycledBytes = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap); 160 } 161 162 // Set up the pixel allocator 163 SkBRDAllocator* allocator = nullptr; 164 RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes); 165 JavaPixelAllocator javaAlloc(env); 166 if (javaBitmap) { 167 allocator = &recycleAlloc; 168 // We are required to match the color type of the recycled bitmap. 169 colorType = recycledBitmap->info().colorType(); 170 } else { 171 allocator = &javaAlloc; 172 } 173 174 // Decode the region. 175 SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight); 176 SkBitmapRegionDecoder* brd = 177 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 178 SkBitmap bitmap; 179 if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) { 180 return nullObjectReturn("Failed to decode region."); 181 } 182 183 // If the client provided options, indicate that the decode was successful. 184 if (NULL != options) { 185 env->SetIntField(options, gOptions_widthFieldID, bitmap.width()); 186 env->SetIntField(options, gOptions_heightFieldID, bitmap.height()); 187 env->SetObjectField(options, gOptions_mimeFieldID, 188 encodedFormatToString(env, brd->getEncodedFormat())); 189 if (env->ExceptionCheck()) { 190 return nullObjectReturn("OOM in encodedFormatToString()"); 191 } 192 } 193 194 // If we may have reused a bitmap, we need to indicate that the pixels have changed. 195 if (javaBitmap) { 196 recycleAlloc.copyIfNecessary(); 197 return javaBitmap; 198 } 199 200 int bitmapCreateFlags = 0; 201 if (!requireUnpremul) { 202 bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied; 203 } 204 return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags); 205 } 206 207 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { 208 SkBitmapRegionDecoder* brd = 209 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 210 return static_cast<jint>(brd->height()); 211 } 212 213 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { 214 SkBitmapRegionDecoder* brd = 215 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 216 return static_cast<jint>(brd->width()); 217 } 218 219 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { 220 SkBitmapRegionDecoder* brd = 221 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 222 delete brd; 223 } 224 225 /////////////////////////////////////////////////////////////////////////////// 226 227 static const JNINativeMethod gBitmapRegionDecoderMethods[] = { 228 { "nativeDecodeRegion", 229 "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;", 230 (void*)nativeDecodeRegion}, 231 232 { "nativeGetHeight", "(J)I", (void*)nativeGetHeight}, 233 234 { "nativeGetWidth", "(J)I", (void*)nativeGetWidth}, 235 236 { "nativeClean", "(J)V", (void*)nativeClean}, 237 238 { "nativeNewInstance", 239 "([BIIZ)Landroid/graphics/BitmapRegionDecoder;", 240 (void*)nativeNewInstanceFromByteArray 241 }, 242 243 { "nativeNewInstance", 244 "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;", 245 (void*)nativeNewInstanceFromStream 246 }, 247 248 { "nativeNewInstance", 249 "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;", 250 (void*)nativeNewInstanceFromFileDescriptor 251 }, 252 253 { "nativeNewInstance", 254 "(JZ)Landroid/graphics/BitmapRegionDecoder;", 255 (void*)nativeNewInstanceFromAsset 256 }, 257 }; 258 259 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 260 { 261 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", 262 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); 263 } 264