blob: c36b37bcb9c1375354b683521ff2454f973b5f61 [file] [log] [blame]
Xavier Ducrohet238857e2010-01-13 16:43:07 -08001/*
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
17package android.graphics;
18
Xavier Ducrohet221f6782010-10-29 16:01:40 -070019import com.android.layoutlib.api.IDensityBasedResourceValue.Density;
Xavier Ducrohet3054fe62010-12-06 10:11:44 -080020import com.android.layoutlib.bridge.Bridge;
Xavier Ducrohet221f6782010-10-29 16:01:40 -070021
Xavier Ducrohet238857e2010-01-13 16:43:07 -080022import android.content.res.AssetManager;
23import android.content.res.Resources;
24import android.util.DisplayMetrics;
25import android.util.TypedValue;
26
27import java.io.BufferedInputStream;
28import java.io.FileDescriptor;
29import java.io.FileInputStream;
30import java.io.IOException;
31import java.io.InputStream;
32
33/**
34 * Creates Bitmap objects from various sources, including files, streams,
35 * and byte-arrays.
36 */
37public class BitmapFactory {
38 public static class Options {
39 /**
40 * Create a default Options object, which if left unchanged will give
41 * the same result from the decoder as if null were passed.
42 */
43 public Options() {
44 inDither = true;
45 inScaled = true;
46 }
47
48 /**
49 * If set to true, the decoder will return null (no bitmap), but
50 * the out... fields will still be set, allowing the caller to query
51 * the bitmap without having to allocate the memory for its pixels.
52 */
53 public boolean inJustDecodeBounds;
54
55 /**
56 * If set to a value > 1, requests the decoder to subsample the original
57 * image, returning a smaller image to save memory. The sample size is
58 * the number of pixels in either dimension that correspond to a single
59 * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
60 * an image that is 1/4 the width/height of the original, and 1/16 the
61 * number of pixels. Any value <= 1 is treated the same as 1. Note: the
62 * decoder will try to fulfill this request, but the resulting bitmap
63 * may have different dimensions that precisely what has been requested.
64 * Also, powers of 2 are often faster/easier for the decoder to honor.
65 */
66 public int inSampleSize;
67
68 /**
69 * If this is non-null, the decoder will try to decode into this
70 * internal configuration. If it is null, or the request cannot be met,
71 * the decoder will try to pick the best matching config based on the
72 * system's screen depth, and characteristics of the original image such
73 * as if it has per-pixel alpha (requiring a config that also does).
74 */
75 public Bitmap.Config inPreferredConfig;
76
77 /**
78 * If dither is true, the decoder will attempt to dither the decoded
79 * image.
80 */
81 public boolean inDither;
82
83 /**
84 * The pixel density to use for the bitmap. This will always result
85 * in the returned bitmap having a density set for it (see
86 * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)). In addition,
87 * if {@link #inScaled} is set (which it is by default} and this
88 * density does not match {@link #inTargetDensity}, then the bitmap
89 * will be scaled to the target density before being returned.
90 *
91 * <p>If this is 0,
92 * {@link BitmapFactory#decodeResource(Resources, int)},
93 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
94 * and {@link BitmapFactory#decodeResourceStream}
95 * will fill in the density associated with the resource. The other
96 * functions will leave it as-is and no density will be applied.
97 *
98 * @see #inTargetDensity
99 * @see #inScreenDensity
100 * @see #inScaled
101 * @see Bitmap#setDensity(int)
102 * @see android.util.DisplayMetrics#densityDpi
103 */
104 public int inDensity;
105
106 /**
107 * The pixel density of the destination this bitmap will be drawn to.
108 * This is used in conjunction with {@link #inDensity} and
109 * {@link #inScaled} to determine if and how to scale the bitmap before
110 * returning it.
111 *
112 * <p>If this is 0,
113 * {@link BitmapFactory#decodeResource(Resources, int)},
114 * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
115 * and {@link BitmapFactory#decodeResourceStream}
116 * will fill in the density associated the Resources object's
117 * DisplayMetrics. The other
118 * functions will leave it as-is and no scaling for density will be
119 * performed.
120 *
121 * @see #inDensity
122 * @see #inScreenDensity
123 * @see #inScaled
124 * @see android.util.DisplayMetrics#densityDpi
125 */
126 public int inTargetDensity;
127
128 /**
129 * The pixel density of the actual screen that is being used. This is
130 * purely for applications running in density compatibility code, where
131 * {@link #inTargetDensity} is actually the density the application
132 * sees rather than the real screen density.
133 *
134 * <p>By setting this, you
135 * allow the loading code to avoid scaling a bitmap that is currently
136 * in the screen density up/down to the compatibility density. Instead,
137 * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
138 * bitmap will be left as-is. Anything using the resulting bitmap
139 * must also used {@link Bitmap#getScaledWidth(int)
140 * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
141 * Bitmap.getScaledHeight} to account for any different between the
142 * bitmap's density and the target's density.
143 *
144 * <p>This is never set automatically for the caller by
145 * {@link BitmapFactory} itself. It must be explicitly set, since the
146 * caller must deal with the resulting bitmap in a density-aware way.
147 *
148 * @see #inDensity
149 * @see #inTargetDensity
150 * @see #inScaled
151 * @see android.util.DisplayMetrics#densityDpi
152 */
153 public int inScreenDensity;
154
155 /**
156 * When this flag is set, if {@link #inDensity} and
157 * {@link #inTargetDensity} are not 0, the
158 * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
159 * rather than relying on the graphics system scaling it each time it
160 * is drawn to a Canvas.
161 *
162 * <p>This flag is turned on by default and should be turned off if you need
163 * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this
164 * flag and are always scaled.
165 */
166 public boolean inScaled;
167
168 /**
169 * If this is set to true, then the resulting bitmap will allocate its
170 * pixels such that they can be purged if the system needs to reclaim
171 * memory. In that instance, when the pixels need to be accessed again
172 * (e.g. the bitmap is drawn, getPixels() is called), they will be
173 * automatically re-decoded.
174 *
175 * For the re-decode to happen, the bitmap must have access to the
176 * encoded data, either by sharing a reference to the input
177 * or by making a copy of it. This distinction is controlled by
178 * inInputShareable. If this is true, then the bitmap may keep a shallow
179 * reference to the input. If this is false, then the bitmap will
180 * explicitly make a copy of the input data, and keep that. Even if
181 * sharing is allowed, the implementation may still decide to make a
182 * deep copy of the input data.
183 */
184 public boolean inPurgeable;
185
186 /**
187 * This field works in conjuction with inPurgeable. If inPurgeable is
188 * false, then this field is ignored. If inPurgeable is true, then this
189 * field determines whether the bitmap can share a reference to the
190 * input data (inputstream, array, etc.) or if it must make a deep copy.
191 */
192 public boolean inInputShareable;
193
194 /**
195 * Normally bitmap allocations count against the dalvik heap, which
196 * means they help trigger GCs when a lot have been allocated. However,
197 * in rare cases, the caller may want to allocate the bitmap outside of
198 * that heap. To request that, set inNativeAlloc to true. In these
199 * rare instances, it is solely up to the caller to ensure that OOM is
200 * managed explicitly by calling bitmap.recycle() as soon as such a
201 * bitmap is no longer needed.
202 *
203 * @hide pending API council approval
204 */
205 public boolean inNativeAlloc;
206
207 /**
208 * The resulting width of the bitmap, set independent of the state of
209 * inJustDecodeBounds. However, if there is an error trying to decode,
210 * outWidth will be set to -1.
211 */
212 public int outWidth;
213
214 /**
215 * The resulting height of the bitmap, set independent of the state of
216 * inJustDecodeBounds. However, if there is an error trying to decode,
217 * outHeight will be set to -1.
218 */
219 public int outHeight;
220
221 /**
222 * If known, this string is set to the mimetype of the decoded image.
223 * If not know, or there is an error, it is set to null.
224 */
225 public String outMimeType;
226
227 /**
228 * Temp storage to use for decoding. Suggest 16K or so.
229 */
230 public byte[] inTempStorage;
231
232 private native void requestCancel();
233
234 /**
235 * Flag to indicate that cancel has been called on this object. This
236 * is useful if there's an intermediary that wants to first decode the
237 * bounds and then decode the image. In that case the intermediary
238 * can check, inbetween the bounds decode and the image decode, to see
239 * if the operation is canceled.
240 */
241 public boolean mCancel;
242
243 /**
244 * This can be called from another thread while this options object is
245 * inside a decode... call. Calling this will notify the decoder that
246 * it should cancel its operation. This is not guaranteed to cancel
247 * the decode, but if it does, the decoder... operation will return
248 * null, or if inJustDecodeBounds is true, will set outWidth/outHeight
249 * to -1
250 */
251 public void requestCancelDecode() {
252 mCancel = true;
253 requestCancel();
254 }
255 }
256
257 /**
258 * Decode a file path into a bitmap. If the specified file name is null,
259 * or cannot be decoded into a bitmap, the function returns null.
260 *
261 * @param pathName complete path name for the file to be decoded.
262 * @param opts null-ok; Options that control downsampling and whether the
263 * image should be completely decoded, or just is size returned.
264 * @return The decoded bitmap, or null if the image data could not be
265 * decoded, or, if opts is non-null, if opts requested only the
266 * size be returned (in opts.outWidth and opts.outHeight)
267 */
268 public static Bitmap decodeFile(String pathName, Options opts) {
269 Bitmap bm = null;
270 InputStream stream = null;
271 try {
272 stream = new FileInputStream(pathName);
273 bm = decodeStream(stream, null, opts);
274 } catch (Exception e) {
275 /* do nothing.
276 If the exception happened on open, bm will be null.
277 */
278 } finally {
279 if (stream != null) {
280 try {
281 stream.close();
282 } catch (IOException e) {
283 // do nothing here
284 }
285 }
286 }
287 return bm;
288 }
289
290 /**
291 * Decode a file path into a bitmap. If the specified file name is null,
292 * or cannot be decoded into a bitmap, the function returns null.
293 *
294 * @param pathName complete path name for the file to be decoded.
295 * @return the resulting decoded bitmap, or null if it could not be decoded.
296 */
297 public static Bitmap decodeFile(String pathName) {
298 return decodeFile(pathName, null);
299 }
300
301 /**
302 * Decode a new Bitmap from an InputStream. This InputStream was obtained from
303 * resources, which we pass to be able to scale the bitmap accordingly.
304 */
305 public static Bitmap decodeResourceStream(Resources res, TypedValue value,
306 InputStream is, Rect pad, Options opts) {
307
308 if (opts == null) {
309 opts = new Options();
310 }
311
312 if (opts.inDensity == 0 && value != null) {
313 final int density = value.density;
314 if (density == TypedValue.DENSITY_DEFAULT) {
315 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
316 } else if (density != TypedValue.DENSITY_NONE) {
317 opts.inDensity = density;
318 }
319 }
320
321 if (opts.inTargetDensity == 0 && res != null) {
322 opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
323 }
324
325 return decodeStream(is, pad, opts);
326 }
327
328 /**
329 * Synonym for opening the given resource and calling
330 * {@link #decodeResourceStream}.
331 *
332 * @param res The resources object containing the image data
333 * @param id The resource id of the image data
334 * @param opts null-ok; Options that control downsampling and whether the
335 * image should be completely decoded, or just is size returned.
336 * @return The decoded bitmap, or null if the image data could not be
337 * decoded, or, if opts is non-null, if opts requested only the
338 * size be returned (in opts.outWidth and opts.outHeight)
339 */
340 public static Bitmap decodeResource(Resources res, int id, Options opts) {
341 Bitmap bm = null;
342 InputStream is = null;
343
344 try {
345 final TypedValue value = new TypedValue();
346 is = res.openRawResource(id, value);
347
348 bm = decodeResourceStream(res, value, is, null, opts);
349 } catch (Exception e) {
350 /* do nothing.
351 If the exception happened on open, bm will be null.
352 If it happened on close, bm is still valid.
353 */
354 } finally {
355 try {
356 if (is != null) is.close();
357 } catch (IOException e) {
358 // Ignore
359 }
360 }
361
362 return bm;
363 }
364
365 /**
366 * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
367 * will null Options.
368 *
369 * @param res The resources object containing the image data
370 * @param id The resource id of the image data
371 * @return The decoded bitmap, or null if the image could not be decode.
372 */
373 public static Bitmap decodeResource(Resources res, int id) {
374 return decodeResource(res, id, null);
375 }
376
377 /**
378 * Decode an immutable bitmap from the specified byte array.
379 *
380 * @param data byte array of compressed image data
381 * @param offset offset into imageData for where the decoder should begin
382 * parsing.
383 * @param length the number of bytes, beginning at offset, to parse
384 * @param opts null-ok; Options that control downsampling and whether the
385 * image should be completely decoded, or just is size returned.
386 * @return The decoded bitmap, or null if the image data could not be
387 * decoded, or, if opts is non-null, if opts requested only the
388 * size be returned (in opts.outWidth and opts.outHeight)
389 */
390 public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
391 if ((offset | length) < 0 || data.length < offset + length) {
392 throw new ArrayIndexOutOfBoundsException();
393 }
394
395 // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
396 return null;
397 //return nativeDecodeByteArray(data, offset, length, opts);
398 }
399
400 /**
401 * Decode an immutable bitmap from the specified byte array.
402 *
403 * @param data byte array of compressed image data
404 * @param offset offset into imageData for where the decoder should begin
405 * parsing.
406 * @param length the number of bytes, beginning at offset, to parse
407 * @return The decoded bitmap, or null if the image could not be decode.
408 */
409 public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
410 return decodeByteArray(data, offset, length, null);
411 }
412
413 /**
414 * Decode an input stream into a bitmap. If the input stream is null, or
415 * cannot be used to decode a bitmap, the function returns null.
416 * The stream's position will be where ever it was after the encoded data
417 * was read.
418 *
419 * @param is The input stream that holds the raw data to be decoded into a
420 * bitmap.
421 * @param outPadding If not null, return the padding rect for the bitmap if
422 * it exists, otherwise set padding to [-1,-1,-1,-1]. If
423 * no bitmap is returned (null) then padding is
424 * unchanged.
425 * @param opts null-ok; Options that control downsampling and whether the
426 * image should be completely decoded, or just is size returned.
427 * @return The decoded bitmap, or null if the image data could not be
428 * decoded, or, if opts is non-null, if opts requested only the
429 * size be returned (in opts.outWidth and opts.outHeight)
430 */
431 public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
432 // we don't throw in this case, thus allowing the caller to only check
433 // the cache, and not force the image to be decoded.
434 if (is == null) {
435 return null;
436 }
437
438 // we need mark/reset to work properly
439
440 if (!is.markSupported()) {
441 is = new BufferedInputStream(is, 16 * 1024);
442 }
443
444 // so we can call reset() if a given codec gives up after reading up to
445 // this many bytes. FIXME: need to find out from the codecs what this
446 // value should be.
447 is.mark(1024);
448
449 Bitmap bm;
450
451 if (is instanceof AssetManager.AssetInputStream) {
Xavier Ducrohet3054fe62010-12-06 10:11:44 -0800452 Bridge.getLog().error(null,
453 "Bitmap.decodeStream: " +
454 "InputStream is unsupported (AssetManager.AssetInputStream)");
Xavier Ducrohet238857e2010-01-13 16:43:07 -0800455 return null;
456 } else {
457 // pass some temp storage down to the native code. 1024 is made up,
458 // but should be large enough to avoid too many small calls back
459 // into is.read(...) This number is not related to the value passed
460 // to mark(...) above.
461 try {
Xavier Ducrohete4d97872010-11-22 20:09:55 -0800462 Density density = Density.MEDIUM;
463 if (opts != null) {
464 density = Density.getEnum(opts.inDensity);
465 }
466 bm = Bitmap_Delegate.createBitmap(is, true, density);
Xavier Ducrohet238857e2010-01-13 16:43:07 -0800467 } catch (IOException e) {
468 return null;
469 }
470 }
471
472 return finishDecode(bm, outPadding, opts);
473 }
474
475 private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
476 if (bm == null || opts == null) {
477 return bm;
478 }
479
480 final int density = opts.inDensity;
481 if (density == 0) {
482 return bm;
483 }
484
485 bm.setDensity(density);
486 final int targetDensity = opts.inTargetDensity;
487 if (targetDensity == 0 || density == targetDensity
488 || density == opts.inScreenDensity) {
489 return bm;
490 }
491
492 byte[] np = bm.getNinePatchChunk();
493 final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
494 if (opts.inScaled || isNinePatch) {
495 float scale = targetDensity / (float)density;
496 // TODO: This is very inefficient and should be done in native by Skia
497 final Bitmap oldBitmap = bm;
498 bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
499 (int) (bm.getHeight() * scale + 0.5f), true);
500 oldBitmap.recycle();
501
502 if (isNinePatch) {
503 //np = nativeScaleNinePatch(np, scale, outPadding);
504 bm.setNinePatchChunk(np);
505 }
506 bm.setDensity(targetDensity);
507 }
508
509 return bm;
510 }
511
512 /**
513 * Decode an input stream into a bitmap. If the input stream is null, or
514 * cannot be used to decode a bitmap, the function returns null.
515 * The stream's position will be where ever it was after the encoded data
516 * was read.
517 *
518 * @param is The input stream that holds the raw data to be decoded into a
519 * bitmap.
520 * @return The decoded bitmap, or null if the image data could not be
521 * decoded, or, if opts is non-null, if opts requested only the
522 * size be returned (in opts.outWidth and opts.outHeight)
523 */
524 public static Bitmap decodeStream(InputStream is) {
525 return decodeStream(is, null, null);
526 }
527
528 /**
529 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
530 * return null. The position within the descriptor will not be changed when
531 * this returns, so the descriptor can be used again as-is.
532 *
533 * @param fd The file descriptor containing the bitmap data to decode
534 * @param outPadding If not null, return the padding rect for the bitmap if
535 * it exists, otherwise set padding to [-1,-1,-1,-1]. If
536 * no bitmap is returned (null) then padding is
537 * unchanged.
538 * @param opts null-ok; Options that control downsampling and whether the
539 * image should be completely decoded, or just is size returned.
540 * @return the decoded bitmap, or null
541 */
542 public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
543 return null;
544
545 /* FIXME: implement as needed
546 try {
547 if (MemoryFile.isMemoryFile(fd)) {
548 int mappedlength = MemoryFile.getMappedSize(fd);
549 MemoryFile file = new MemoryFile(fd, mappedlength, "r");
550 InputStream is = file.getInputStream();
551 Bitmap bm = decodeStream(is, outPadding, opts);
552 return finishDecode(bm, outPadding, opts);
553 }
554 } catch (IOException ex) {
555 // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
556 return null;
557 }
558 //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
559 //return finishDecode(bm, outPadding, opts);
560 */
561 }
562
563 /**
564 * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
565 * return null. The position within the descriptor will not be changed when
566 * this returns, so the descriptor can be used again as is.
567 *
568 * @param fd The file descriptor containing the bitmap data to decode
569 * @return the decoded bitmap, or null
570 */
571 public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
572 return decodeFileDescriptor(fd, null, null);
573 }
574}
575