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 42using namespace android; 43 44// Takes ownership of the SkStreamRewindable. For consistency, deletes stream even 45// when returning null. 46static 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 57static 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 71static 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 91static 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 106static 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 */ 125static 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 207static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) { 208 SkBitmapRegionDecoder* brd = 209 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 210 return static_cast<jint>(brd->height()); 211} 212 213static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) { 214 SkBitmapRegionDecoder* brd = 215 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 216 return static_cast<jint>(brd->width()); 217} 218 219static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) { 220 SkBitmapRegionDecoder* brd = 221 reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle); 222 delete brd; 223} 224 225/////////////////////////////////////////////////////////////////////////////// 226 227static 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 259int register_android_graphics_BitmapRegionDecoder(JNIEnv* env) 260{ 261 return android::RegisterMethodsOrDie(env, "android/graphics/BitmapRegionDecoder", 262 gBitmapRegionDecoderMethods, NELEM(gBitmapRegionDecoderMethods)); 263} 264