• 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 "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 
42 static jclass gFileDescriptor_class;
43 static jfieldID gFileDescriptor_descriptor;
44 
45 #if 0
46     #define TRACE_BITMAP(code)  code
47 #else
48     #define TRACE_BITMAP(code)
49 #endif
50 
51 using namespace android;
52 
buildSkMemoryStream(SkStream * stream)53 static 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 
doBuildTileIndex(JNIEnv * env,SkStream * stream)74 static 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 
nativeNewInstanceFromByteArray(JNIEnv * env,jobject,jbyteArray byteArray,int offset,int length,jboolean isShareable)102 static 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 
nativeNewInstanceFromFileDescriptor(JNIEnv * env,jobject clazz,jobject fileDescriptor,jboolean isShareable)113 static 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 
nativeNewInstanceFromStream(JNIEnv * env,jobject clazz,jobject is,jbyteArray storage,jboolean isShareable)155 static 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 
nativeNewInstanceFromAsset(JNIEnv * env,jobject clazz,jint native_asset,jboolean isShareable)171 static 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  */
nativeDecodeRegion(JNIEnv * env,jobject,SkBitmapRegionDecoder * brd,int start_x,int start_y,int width,int height,jobject options)188 static 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 
nativeGetHeight(JNIEnv * env,jobject,SkBitmapRegionDecoder * brd)255 static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
256     return brd->getHeight();
257 }
258 
nativeGetWidth(JNIEnv * env,jobject,SkBitmapRegionDecoder * brd)259 static int nativeGetWidth(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
260     return brd->getWidth();
261 }
262 
nativeClean(JNIEnv * env,jobject,SkBitmapRegionDecoder * brd)263 static void nativeClean(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
264     delete brd;
265 }
266 
267 ///////////////////////////////////////////////////////////////////////////////
268 
269 #include <android_runtime/AndroidRuntime.h>
270 
271 static 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 
make_globalref(JNIEnv * env,const char classname[])305 static 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 
getFieldIDCheck(JNIEnv * env,jclass clazz,const char fieldname[],const char type[])311 static 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 
318 int register_android_graphics_BitmapRegionDecoder(JNIEnv* env);
register_android_graphics_BitmapRegionDecoder(JNIEnv * env)319 int 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