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