blob: b38d107cf3cdf835b7277ff95a82497376ed7fb7 [file] [log] [blame]
Wei-Ta Chen6b849e22010-09-07 17:32:18 +08001/* Copyright (C) 2010 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package android.graphics;
17
18import android.content.res.AssetManager;
19
20import java.io.BufferedInputStream;
21import java.io.FileDescriptor;
22import java.io.FileInputStream;
23import java.io.IOException;
24import java.io.InputStream;
25
26/**
27 * BitmapRegionDecoder can be used to decode a rectangle region from an image.
28 * BitmapRegionDecoder is particularly useful when an original image is large and
29 * you only need parts of the image.
30 *
31 * <p>To create a BitmapRegionDecoder, call newInstance(...).
32 * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
33 * to get a decoded Bitmap of the specified region.
34 *
35 */
36public final class BitmapRegionDecoder {
37 private int mNativeBitmapRegionDecoder;
38 private boolean mRecycled;
Derek Sollenbergercd775832012-09-21 08:19:05 -040039 // ensures that the native decoder object exists and that only one decode can
40 // occur at a time.
41 private final Object mNativeLock = new Object();
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080042
43 /**
44 * Create a BitmapRegionDecoder from the specified byte array.
Wei-Ta Chen96d3b5e2011-01-11 22:42:17 -080045 * Currently only the JPEG and PNG formats are supported.
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080046 *
47 * @param data byte array of compressed image data.
48 * @param offset offset into data for where the decoder should begin
49 * parsing.
50 * @param length the number of bytes, beginning at offset, to parse
51 * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
52 * shallow reference to the input. If this is false,
53 * then the BitmapRegionDecoder will explicitly make a copy of the
54 * input data, and keep that. Even if sharing is allowed,
55 * the implementation may still decide to make a deep
56 * copy of the input data. If an image is progressively encoded,
57 * allowing sharing may degrade the decoding speed.
58 * @return BitmapRegionDecoder, or null if the image data could not be decoded.
59 * @throws IOException if the image format is not supported or can not be decoded.
60 */
61 public static BitmapRegionDecoder newInstance(byte[] data,
62 int offset, int length, boolean isShareable) throws IOException {
63 if ((offset | length) < 0 || data.length < offset + length) {
64 throw new ArrayIndexOutOfBoundsException();
65 }
66 return nativeNewInstance(data, offset, length, isShareable);
67 }
68
69 /**
70 * Create a BitmapRegionDecoder from the file descriptor.
71 * The position within the descriptor will not be changed when
72 * this returns, so the descriptor can be used again as is.
Wei-Ta Chen96d3b5e2011-01-11 22:42:17 -080073 * Currently only the JPEG and PNG formats are supported.
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080074 *
75 * @param fd The file descriptor containing the data to decode
76 * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
77 * shallow reference to the input. If this is false,
78 * then the BitmapRegionDecoder will explicitly make a copy of the
79 * input data, and keep that. Even if sharing is allowed,
80 * the implementation may still decide to make a deep
81 * copy of the input data. If an image is progressively encoded,
82 * allowing sharing may degrade the decoding speed.
83 * @return BitmapRegionDecoder, or null if the image data could not be decoded.
84 * @throws IOException if the image format is not supported or can not be decoded.
85 */
86 public static BitmapRegionDecoder newInstance(
87 FileDescriptor fd, boolean isShareable) throws IOException {
88 return nativeNewInstance(fd, isShareable);
89 }
90
91 /**
92 * Create a BitmapRegionDecoder from an input stream.
93 * The stream's position will be where ever it was after the encoded data
94 * was read.
Wei-Ta Chen96d3b5e2011-01-11 22:42:17 -080095 * Currently only the JPEG and PNG formats are supported.
Wei-Ta Chen6b849e22010-09-07 17:32:18 +080096 *
97 * @param is The input stream that holds the raw data to be decoded into a
98 * BitmapRegionDecoder.
99 * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
100 * shallow reference to the input. If this is false,
101 * then the BitmapRegionDecoder will explicitly make a copy of the
102 * input data, and keep that. Even if sharing is allowed,
103 * the implementation may still decide to make a deep
104 * copy of the input data. If an image is progressively encoded,
105 * allowing sharing may degrade the decoding speed.
106 * @return BitmapRegionDecoder, or null if the image data could not be decoded.
107 * @throws IOException if the image format is not supported or can not be decoded.
108 */
109 public static BitmapRegionDecoder newInstance(InputStream is,
110 boolean isShareable) throws IOException {
111 // we need mark/reset to work properly in JNI
112
113 if (!is.markSupported()) {
114 is = new BufferedInputStream(is, 16 * 1024);
115 }
116
117 if (is instanceof AssetManager.AssetInputStream) {
118 return nativeNewInstance(
119 ((AssetManager.AssetInputStream) is).getAssetInt(),
120 isShareable);
121 } else {
122 // pass some temp storage down to the native code. 1024 is made up,
123 // but should be large enough to avoid too many small calls back
124 // into is.read(...).
125 byte [] tempStorage = new byte[16 * 1024];
126 return nativeNewInstance(is, tempStorage, isShareable);
127 }
128 }
129
130 /**
131 * Create a BitmapRegionDecoder from a file path.
Wei-Ta Chen96d3b5e2011-01-11 22:42:17 -0800132 * Currently only the JPEG and PNG formats are supported.
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800133 *
134 * @param pathName complete path name for the file to be decoded.
135 * @param isShareable If this is true, then the BitmapRegionDecoder may keep a
136 * shallow reference to the input. If this is false,
137 * then the BitmapRegionDecoder will explicitly make a copy of the
138 * input data, and keep that. Even if sharing is allowed,
139 * the implementation may still decide to make a deep
140 * copy of the input data. If an image is progressively encoded,
141 * allowing sharing may degrade the decoding speed.
142 * @return BitmapRegionDecoder, or null if the image data could not be decoded.
143 * @throws IOException if the image format is not supported or can not be decoded.
144 */
145 public static BitmapRegionDecoder newInstance(String pathName,
146 boolean isShareable) throws IOException {
147 BitmapRegionDecoder decoder = null;
148 InputStream stream = null;
149
150 try {
151 stream = new FileInputStream(pathName);
152 decoder = newInstance(stream, isShareable);
153 } finally {
154 if (stream != null) {
155 try {
156 stream.close();
157 } catch (IOException e) {
158 // do nothing here
159 }
160 }
161 }
162 return decoder;
163 }
164
165 /* Private constructor that must receive an already allocated native
166 region decoder int (pointer).
167
168 This can be called from JNI code.
169 */
170 private BitmapRegionDecoder(int decoder) {
171 mNativeBitmapRegionDecoder = decoder;
172 mRecycled = false;
173 }
174
175 /**
176 * Decodes a rectangle region in the image specified by rect.
177 *
178 * @param rect The rectangle that specified the region to be decode.
179 * @param options null-ok; Options that control downsampling.
180 * inPurgeable is not supported.
181 * @return The decoded bitmap, or null if the image data could not be
182 * decoded.
183 */
184 public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
Derek Sollenbergercd775832012-09-21 08:19:05 -0400185 synchronized (mNativeLock) {
186 checkRecycled("decodeRegion called on recycled region decoder");
187 if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
188 || rect.top >= getHeight())
189 throw new IllegalArgumentException("rectangle is outside the image");
190 return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
191 rect.right - rect.left, rect.bottom - rect.top, options);
192 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800193 }
194
195 /** Returns the original image's width */
196 public int getWidth() {
Derek Sollenbergercd775832012-09-21 08:19:05 -0400197 synchronized (mNativeLock) {
198 checkRecycled("getWidth called on recycled region decoder");
199 return nativeGetWidth(mNativeBitmapRegionDecoder);
200 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800201 }
202
203 /** Returns the original image's height */
204 public int getHeight() {
Derek Sollenbergercd775832012-09-21 08:19:05 -0400205 synchronized (mNativeLock) {
206 checkRecycled("getHeight called on recycled region decoder");
207 return nativeGetHeight(mNativeBitmapRegionDecoder);
208 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800209 }
210
211 /**
212 * Frees up the memory associated with this region decoder, and mark the
213 * region decoder as "dead", meaning it will throw an exception if decodeRegion(),
214 * getWidth() or getHeight() is called.
215 *
216 * <p>This operation cannot be reversed, so it should only be called if you are
217 * sure there are no further uses for the region decoder. This is an advanced call,
218 * and normally need not be called, since the normal GC process will free up this
219 * memory when there are no more references to this region decoder.
220 */
221 public void recycle() {
Derek Sollenbergercd775832012-09-21 08:19:05 -0400222 synchronized (mNativeLock) {
223 if (!mRecycled) {
224 nativeClean(mNativeBitmapRegionDecoder);
225 mRecycled = true;
226 }
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800227 }
228 }
229
230 /**
231 * Returns true if this region decoder has been recycled.
232 * If so, then it is an error to try use its method.
233 *
234 * @return true if the region decoder has been recycled
235 */
236 public final boolean isRecycled() {
237 return mRecycled;
238 }
239
240 /**
241 * Called by methods that want to throw an exception if the region decoder
242 * has already been recycled.
243 */
244 private void checkRecycled(String errorMessage) {
245 if (mRecycled) {
246 throw new IllegalStateException(errorMessage);
247 }
248 }
249
250 @Override
251 protected void finalize() throws Throwable {
252 try {
253 recycle();
254 } finally {
255 super.finalize();
256 }
257 }
258
259 private static native Bitmap nativeDecodeRegion(int lbm,
260 int start_x, int start_y, int width, int height,
261 BitmapFactory.Options options);
262 private static native int nativeGetWidth(int lbm);
263 private static native int nativeGetHeight(int lbm);
264 private static native void nativeClean(int lbm);
265
266 private static native BitmapRegionDecoder nativeNewInstance(
267 byte[] data, int offset, int length, boolean isShareable);
268 private static native BitmapRegionDecoder nativeNewInstance(
269 FileDescriptor fd, boolean isShareable);
270 private static native BitmapRegionDecoder nativeNewInstance(
271 InputStream is, byte[] storage, boolean isShareable);
272 private static native BitmapRegionDecoder nativeNewInstance(
273 int asset, boolean isShareable);
274}