• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkData.h"
21 #include "SkImageEncoder.h"
22 #include "GraphicsJNI.h"
23 #include "SkUtils.h"
24 #include "SkTemplates.h"
25 #include "SkPixelRef.h"
26 #include "SkStream.h"
27 #include "BitmapFactory.h"
28 #include "AutoDecodeCancel.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 
49 using namespace android;
50 
51 class SkBitmapRegionDecoder {
52 public:
SkBitmapRegionDecoder(SkImageDecoder * decoder,int width,int height)53     SkBitmapRegionDecoder(SkImageDecoder* decoder, int width, int height) {
54         fDecoder = decoder;
55         fWidth = width;
56         fHeight = height;
57     }
~SkBitmapRegionDecoder()58     ~SkBitmapRegionDecoder() {
59         SkDELETE(fDecoder);
60     }
61 
decodeRegion(SkBitmap * bitmap,const SkIRect & rect,SkColorType pref,int sampleSize)62     bool decodeRegion(SkBitmap* bitmap, const SkIRect& rect,
63                       SkColorType pref, int sampleSize) {
64         fDecoder->setSampleSize(sampleSize);
65         return fDecoder->decodeSubset(bitmap, rect, pref);
66     }
67 
getDecoder() const68     SkImageDecoder* getDecoder() const { return fDecoder; }
getWidth() const69     int getWidth() const { return fWidth; }
getHeight() const70     int getHeight() const { return fHeight; }
71 
72 private:
73     SkImageDecoder* fDecoder;
74     int fWidth;
75     int fHeight;
76 };
77 
createBitmapRegionDecoder(JNIEnv * env,SkStreamRewindable * stream)78 static jobject createBitmapRegionDecoder(JNIEnv* env, SkStreamRewindable* stream) {
79     SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
80     int width, height;
81     if (NULL == decoder) {
82         doThrowIOE(env, "Image format not supported");
83         return nullObjectReturn("SkImageDecoder::Factory returned null");
84     }
85 
86     JavaPixelAllocator *javaAllocator = new JavaPixelAllocator(env);
87     decoder->setAllocator(javaAllocator);
88     javaAllocator->unref();
89 
90     if (!decoder->buildTileIndex(stream, &width, &height)) {
91         char msg[100];
92         snprintf(msg, sizeof(msg), "Image failed to decode using %s decoder",
93                 decoder->getFormatName());
94         doThrowIOE(env, msg);
95         SkDELETE(decoder);
96         return nullObjectReturn("decoder->buildTileIndex returned false");
97     }
98 
99     SkBitmapRegionDecoder *bm = new SkBitmapRegionDecoder(decoder, width, height);
100     return GraphicsJNI::createBitmapRegionDecoder(env, bm);
101 }
102 
nativeNewInstanceFromByteArray(JNIEnv * env,jobject,jbyteArray byteArray,jint offset,jint length,jboolean isShareable)103 static jobject nativeNewInstanceFromByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
104                                      jint offset, jint length, jboolean isShareable) {
105     /*  If isShareable we could decide to just wrap the java array and
106         share it, but that means adding a globalref to the java array object
107         For now we just always copy the array's data if isShareable.
108      */
109     AutoJavaByteArray ar(env, byteArray);
110     SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, true);
111 
112     jobject brd = createBitmapRegionDecoder(env, stream);
113     SkSafeUnref(stream); // the decoder now holds a reference
114     return brd;
115 }
116 
nativeNewInstanceFromFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean isShareable)117 static jobject nativeNewInstanceFromFileDescriptor(JNIEnv* env, jobject clazz,
118                                           jobject fileDescriptor, jboolean isShareable) {
119     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
120 
121     jint descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
122 
123     struct stat fdStat;
124     if (fstat(descriptor, &fdStat) == -1) {
125         doThrowIOE(env, "broken file descriptor");
126         return nullObjectReturn("fstat return -1");
127     }
128 
129     SkAutoTUnref<SkData> data(SkData::NewFromFD(descriptor));
130     SkMemoryStream* stream = new SkMemoryStream(data);
131 
132     jobject brd = createBitmapRegionDecoder(env, stream);
133     SkSafeUnref(stream); // the decoder now holds a reference
134     return brd;
135 }
136 
nativeNewInstanceFromStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jboolean isShareable)137 static jobject nativeNewInstanceFromStream(JNIEnv* env, jobject clazz,
138                                   jobject is,       // InputStream
139                                   jbyteArray storage, // byte[]
140                                   jboolean isShareable) {
141     jobject brd = NULL;
142     // for now we don't allow shareable with java inputstreams
143     SkStreamRewindable* stream = CopyJavaInputStream(env, is, storage);
144 
145     if (stream) {
146         brd = createBitmapRegionDecoder(env, stream);
147         stream->unref(); // the decoder now holds a reference
148     }
149     return brd;
150 }
151 
nativeNewInstanceFromAsset(JNIEnv * env,jobject clazz,jlong native_asset,jboolean isShareable)152 static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
153                                  jlong native_asset, // Asset
154                                  jboolean isShareable) {
155     Asset* asset = reinterpret_cast<Asset*>(native_asset);
156     SkAutoTUnref<SkMemoryStream> stream(CopyAssetToStream(asset));
157     if (NULL == stream.get()) {
158         return NULL;
159     }
160 
161     jobject brd = createBitmapRegionDecoder(env, stream.get());
162     // The decoder now holds a reference to stream.
163     return brd;
164 }
165 
166 /*
167  * nine patch not supported
168  *
169  * purgeable not supported
170  * reportSizeToVM not supported
171  */
nativeDecodeRegion(JNIEnv * env,jobject,jlong brdHandle,jint start_x,jint start_y,jint width,jint height,jobject options)172 static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle,
173                                 jint start_x, jint start_y, jint width, jint height, jobject options) {
174     SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
175     jobject tileBitmap = NULL;
176     SkImageDecoder *decoder = brd->getDecoder();
177     int sampleSize = 1;
178     SkColorType prefColorType = kUnknown_SkColorType;
179     bool doDither = true;
180     bool preferQualityOverSpeed = false;
181     bool requireUnpremultiplied = false;
182 
183     if (NULL != options) {
184         sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
185         // initialize these, in case we fail later on
186         env->SetIntField(options, gOptions_widthFieldID, -1);
187         env->SetIntField(options, gOptions_heightFieldID, -1);
188         env->SetObjectField(options, gOptions_mimeFieldID, 0);
189 
190         jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
191         prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
192         doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
193         preferQualityOverSpeed = env->GetBooleanField(options,
194                 gOptions_preferQualityOverSpeedFieldID);
195         // Get the bitmap for re-use if it exists.
196         tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
197         requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
198     }
199 
200     decoder->setDitherImage(doDither);
201     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
202     decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
203     AutoDecoderCancel adc(options, decoder);
204 
205     // To fix the race condition in case "requestCancelDecode"
206     // happens earlier than AutoDecoderCancel object is added
207     // to the gAutoDecoderCancelMutex linked list.
208     if (NULL != options && env->GetBooleanField(options, gOptions_mCancelID)) {
209         return nullObjectReturn("gOptions_mCancelID");;
210     }
211 
212     SkIRect region;
213     region.fLeft = start_x;
214     region.fTop = start_y;
215     region.fRight = start_x + width;
216     region.fBottom = start_y + height;
217     SkBitmap* bitmap = NULL;
218     SkAutoTDelete<SkBitmap> adb;
219 
220     if (tileBitmap != NULL) {
221         // Re-use bitmap.
222         bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
223     }
224     if (bitmap == NULL) {
225         bitmap = new SkBitmap;
226         adb.reset(bitmap);
227     }
228 
229     if (!brd->decodeRegion(bitmap, region, prefColorType, 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     if (tileBitmap != NULL) {
245         return tileBitmap;
246     }
247 
248     // detach bitmap from its autodeleter, since we want to own it now
249     adb.detach();
250 
251     JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
252     jbyteArray buff = allocator->getStorageObjAndReset();
253 
254     int bitmapCreateFlags = 0;
255     if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
256     return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
257 }
258 
nativeGetHeight(JNIEnv * env,jobject,jlong brdHandle)259 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
260     SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
261     return static_cast<jint>(brd->getHeight());
262 }
263 
nativeGetWidth(JNIEnv * env,jobject,jlong brdHandle)264 static jint nativeGetWidth(JNIEnv* env, jobject, jlong brdHandle) {
265     SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
266     return static_cast<jint>(brd->getWidth());
267 }
268 
nativeClean(JNIEnv * env,jobject,jlong brdHandle)269 static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
270     SkBitmapRegionDecoder *brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
271     delete brd;
272 }
273 
274 ///////////////////////////////////////////////////////////////////////////////
275 
276 #include <android_runtime/AndroidRuntime.h>
277 
278 static JNINativeMethod gBitmapRegionDecoderMethods[] = {
279     {   "nativeDecodeRegion",
280         "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
281         (void*)nativeDecodeRegion},
282 
283     {   "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
284 
285     {   "nativeGetWidth", "(J)I", (void*)nativeGetWidth},
286 
287     {   "nativeClean", "(J)V", (void*)nativeClean},
288 
289     {   "nativeNewInstance",
290         "([BIIZ)Landroid/graphics/BitmapRegionDecoder;",
291         (void*)nativeNewInstanceFromByteArray
292     },
293 
294     {   "nativeNewInstance",
295         "(Ljava/io/InputStream;[BZ)Landroid/graphics/BitmapRegionDecoder;",
296         (void*)nativeNewInstanceFromStream
297     },
298 
299     {   "nativeNewInstance",
300         "(Ljava/io/FileDescriptor;Z)Landroid/graphics/BitmapRegionDecoder;",
301         (void*)nativeNewInstanceFromFileDescriptor
302     },
303 
304     {   "nativeNewInstance",
305         "(JZ)Landroid/graphics/BitmapRegionDecoder;",
306         (void*)nativeNewInstanceFromAsset
307     },
308 };
309 
310 #define kClassPathName  "android/graphics/BitmapRegionDecoder"
311 
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)312 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env)
313 {
314     return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
315             gBitmapRegionDecoderMethods, SK_ARRAY_COUNT(gBitmapRegionDecoderMethods));
316 }
317