diff options
author | Xavier Ducrohet <xav@android.com> | 2010-01-19 00:01:32 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-01-19 00:01:32 -0800 |
commit | 05bc8d739768c8e2e025c291706f6c10ac362636 (patch) | |
tree | 22fb2ea37745740365c1e29687917a746efb9270 /tools/layoutlib | |
parent | 2b7ff1c47147e31521a3ef9e7d02252111192cb3 (diff) | |
parent | cff6c8459ca05f3fee2d2999989d07a7176f955c (diff) | |
download | frameworks_base-05bc8d739768c8e2e025c291706f6c10ac362636.zip frameworks_base-05bc8d739768c8e2e025c291706f6c10ac362636.tar.gz frameworks_base-05bc8d739768c8e2e025c291706f6c10ac362636.tar.bz2 |
am cff6c845: am ae4bd059: ADT/Layoutlib: Reimplement parts of BitmapFactory
Merge commit 'cff6c8459ca05f3fee2d2999989d07a7176f955c'
* commit 'cff6c8459ca05f3fee2d2999989d07a7176f955c':
ADT/Layoutlib: Reimplement parts of BitmapFactory
Diffstat (limited to 'tools/layoutlib')
3 files changed, 605 insertions, 0 deletions
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java index ff1b295..35f022e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java @@ -20,6 +20,7 @@ package android.graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.io.InputStream; import javax.imageio.ImageIO; @@ -33,6 +34,12 @@ public final class Bitmap extends _Original_Bitmap { mImage = ImageIO.read(input); } + public Bitmap(InputStream is) throws IOException { + super(1, true, null, -1); + + mImage = ImageIO.read(is); + } + Bitmap(BufferedImage image) { super(1, true, null, -1); mImage = image; @@ -237,4 +244,35 @@ public final class Bitmap extends _Original_Bitmap { return createBitmap(colors, 0, width, width, height, config); } + public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, + int dstHeight, boolean filter) { + Matrix m; + synchronized (Bitmap.class) { + // small pool of just 1 matrix + m = sScaleMatrix; + sScaleMatrix = null; + } + + if (m == null) { + m = new Matrix(); + } + + final int width = src.getWidth(); + final int height = src.getHeight(); + final float sx = dstWidth / (float)width; + final float sy = dstHeight / (float)height; + m.setScale(sx, sy); + Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter); + + synchronized (Bitmap.class) { + // do we need to check for null? why not just assign everytime? + if (sScaleMatrix == null) { + sScaleMatrix = m; + } + } + + return b; + } + + } diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java new file mode 100644 index 0000000..e978fe8 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.util.DisplayMetrics; +import android.util.TypedValue; + +import java.io.BufferedInputStream; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Creates Bitmap objects from various sources, including files, streams, + * and byte-arrays. + */ +public class BitmapFactory { + public static class Options { + /** + * Create a default Options object, which if left unchanged will give + * the same result from the decoder as if null were passed. + */ + public Options() { + inDither = true; + inScaled = true; + } + + /** + * If set to true, the decoder will return null (no bitmap), but + * the out... fields will still be set, allowing the caller to query + * the bitmap without having to allocate the memory for its pixels. + */ + public boolean inJustDecodeBounds; + + /** + * If set to a value > 1, requests the decoder to subsample the original + * image, returning a smaller image to save memory. The sample size is + * the number of pixels in either dimension that correspond to a single + * pixel in the decoded bitmap. For example, inSampleSize == 4 returns + * an image that is 1/4 the width/height of the original, and 1/16 the + * number of pixels. Any value <= 1 is treated the same as 1. Note: the + * decoder will try to fulfill this request, but the resulting bitmap + * may have different dimensions that precisely what has been requested. + * Also, powers of 2 are often faster/easier for the decoder to honor. + */ + public int inSampleSize; + + /** + * If this is non-null, the decoder will try to decode into this + * internal configuration. If it is null, or the request cannot be met, + * the decoder will try to pick the best matching config based on the + * system's screen depth, and characteristics of the original image such + * as if it has per-pixel alpha (requiring a config that also does). + */ + public Bitmap.Config inPreferredConfig; + + /** + * If dither is true, the decoder will attempt to dither the decoded + * image. + */ + public boolean inDither; + + /** + * The pixel density to use for the bitmap. This will always result + * in the returned bitmap having a density set for it (see + * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)). In addition, + * if {@link #inScaled} is set (which it is by default} and this + * density does not match {@link #inTargetDensity}, then the bitmap + * will be scaled to the target density before being returned. + * + * <p>If this is 0, + * {@link BitmapFactory#decodeResource(Resources, int)}, + * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, + * and {@link BitmapFactory#decodeResourceStream} + * will fill in the density associated with the resource. The other + * functions will leave it as-is and no density will be applied. + * + * @see #inTargetDensity + * @see #inScreenDensity + * @see #inScaled + * @see Bitmap#setDensity(int) + * @see android.util.DisplayMetrics#densityDpi + */ + public int inDensity; + + /** + * The pixel density of the destination this bitmap will be drawn to. + * This is used in conjunction with {@link #inDensity} and + * {@link #inScaled} to determine if and how to scale the bitmap before + * returning it. + * + * <p>If this is 0, + * {@link BitmapFactory#decodeResource(Resources, int)}, + * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}, + * and {@link BitmapFactory#decodeResourceStream} + * will fill in the density associated the Resources object's + * DisplayMetrics. The other + * functions will leave it as-is and no scaling for density will be + * performed. + * + * @see #inDensity + * @see #inScreenDensity + * @see #inScaled + * @see android.util.DisplayMetrics#densityDpi + */ + public int inTargetDensity; + + /** + * The pixel density of the actual screen that is being used. This is + * purely for applications running in density compatibility code, where + * {@link #inTargetDensity} is actually the density the application + * sees rather than the real screen density. + * + * <p>By setting this, you + * allow the loading code to avoid scaling a bitmap that is currently + * in the screen density up/down to the compatibility density. Instead, + * if {@link #inDensity} is the same as {@link #inScreenDensity}, the + * bitmap will be left as-is. Anything using the resulting bitmap + * must also used {@link Bitmap#getScaledWidth(int) + * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight + * Bitmap.getScaledHeight} to account for any different between the + * bitmap's density and the target's density. + * + * <p>This is never set automatically for the caller by + * {@link BitmapFactory} itself. It must be explicitly set, since the + * caller must deal with the resulting bitmap in a density-aware way. + * + * @see #inDensity + * @see #inTargetDensity + * @see #inScaled + * @see android.util.DisplayMetrics#densityDpi + */ + public int inScreenDensity; + + /** + * When this flag is set, if {@link #inDensity} and + * {@link #inTargetDensity} are not 0, the + * bitmap will be scaled to match {@link #inTargetDensity} when loaded, + * rather than relying on the graphics system scaling it each time it + * is drawn to a Canvas. + * + * <p>This flag is turned on by default and should be turned off if you need + * a non-scaled version of the bitmap. Nine-patch bitmaps ignore this + * flag and are always scaled. + */ + public boolean inScaled; + + /** + * If this is set to true, then the resulting bitmap will allocate its + * pixels such that they can be purged if the system needs to reclaim + * memory. In that instance, when the pixels need to be accessed again + * (e.g. the bitmap is drawn, getPixels() is called), they will be + * automatically re-decoded. + * + * For the re-decode to happen, the bitmap must have access to the + * encoded data, either by sharing a reference to the input + * or by making a copy of it. This distinction is controlled by + * inInputShareable. If this is true, then the bitmap may keep a shallow + * reference to the input. If this is false, then the bitmap will + * explicitly make a copy of the input data, and keep that. Even if + * sharing is allowed, the implementation may still decide to make a + * deep copy of the input data. + */ + public boolean inPurgeable; + + /** + * This field works in conjuction with inPurgeable. If inPurgeable is + * false, then this field is ignored. If inPurgeable is true, then this + * field determines whether the bitmap can share a reference to the + * input data (inputstream, array, etc.) or if it must make a deep copy. + */ + public boolean inInputShareable; + + /** + * Normally bitmap allocations count against the dalvik heap, which + * means they help trigger GCs when a lot have been allocated. However, + * in rare cases, the caller may want to allocate the bitmap outside of + * that heap. To request that, set inNativeAlloc to true. In these + * rare instances, it is solely up to the caller to ensure that OOM is + * managed explicitly by calling bitmap.recycle() as soon as such a + * bitmap is no longer needed. + * + * @hide pending API council approval + */ + public boolean inNativeAlloc; + + /** + * The resulting width of the bitmap, set independent of the state of + * inJustDecodeBounds. However, if there is an error trying to decode, + * outWidth will be set to -1. + */ + public int outWidth; + + /** + * The resulting height of the bitmap, set independent of the state of + * inJustDecodeBounds. However, if there is an error trying to decode, + * outHeight will be set to -1. + */ + public int outHeight; + + /** + * If known, this string is set to the mimetype of the decoded image. + * If not know, or there is an error, it is set to null. + */ + public String outMimeType; + + /** + * Temp storage to use for decoding. Suggest 16K or so. + */ + public byte[] inTempStorage; + + private native void requestCancel(); + + /** + * Flag to indicate that cancel has been called on this object. This + * is useful if there's an intermediary that wants to first decode the + * bounds and then decode the image. In that case the intermediary + * can check, inbetween the bounds decode and the image decode, to see + * if the operation is canceled. + */ + public boolean mCancel; + + /** + * This can be called from another thread while this options object is + * inside a decode... call. Calling this will notify the decoder that + * it should cancel its operation. This is not guaranteed to cancel + * the decode, but if it does, the decoder... operation will return + * null, or if inJustDecodeBounds is true, will set outWidth/outHeight + * to -1 + */ + public void requestCancelDecode() { + mCancel = true; + requestCancel(); + } + } + + /** + * Decode a file path into a bitmap. If the specified file name is null, + * or cannot be decoded into a bitmap, the function returns null. + * + * @param pathName complete path name for the file to be decoded. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeFile(String pathName, Options opts) { + Bitmap bm = null; + InputStream stream = null; + try { + stream = new FileInputStream(pathName); + bm = decodeStream(stream, null, opts); + } catch (Exception e) { + /* do nothing. + If the exception happened on open, bm will be null. + */ + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + // do nothing here + } + } + } + return bm; + } + + /** + * Decode a file path into a bitmap. If the specified file name is null, + * or cannot be decoded into a bitmap, the function returns null. + * + * @param pathName complete path name for the file to be decoded. + * @return the resulting decoded bitmap, or null if it could not be decoded. + */ + public static Bitmap decodeFile(String pathName) { + return decodeFile(pathName, null); + } + + /** + * Decode a new Bitmap from an InputStream. This InputStream was obtained from + * resources, which we pass to be able to scale the bitmap accordingly. + */ + public static Bitmap decodeResourceStream(Resources res, TypedValue value, + InputStream is, Rect pad, Options opts) { + + if (opts == null) { + opts = new Options(); + } + + if (opts.inDensity == 0 && value != null) { + final int density = value.density; + if (density == TypedValue.DENSITY_DEFAULT) { + opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; + } else if (density != TypedValue.DENSITY_NONE) { + opts.inDensity = density; + } + } + + if (opts.inTargetDensity == 0 && res != null) { + opts.inTargetDensity = res.getDisplayMetrics().densityDpi; + } + + return decodeStream(is, pad, opts); + } + + /** + * Synonym for opening the given resource and calling + * {@link #decodeResourceStream}. + * + * @param res The resources object containing the image data + * @param id The resource id of the image data + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeResource(Resources res, int id, Options opts) { + Bitmap bm = null; + InputStream is = null; + + try { + final TypedValue value = new TypedValue(); + is = res.openRawResource(id, value); + + bm = decodeResourceStream(res, value, is, null, opts); + } catch (Exception e) { + /* do nothing. + If the exception happened on open, bm will be null. + If it happened on close, bm is still valid. + */ + } finally { + try { + if (is != null) is.close(); + } catch (IOException e) { + // Ignore + } + } + + return bm; + } + + /** + * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)} + * will null Options. + * + * @param res The resources object containing the image data + * @param id The resource id of the image data + * @return The decoded bitmap, or null if the image could not be decode. + */ + public static Bitmap decodeResource(Resources res, int id) { + return decodeResource(res, id, null); + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param data byte array of compressed image data + * @param offset offset into imageData for where the decoder should begin + * parsing. + * @param length the number of bytes, beginning at offset, to parse + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { + if ((offset | length) < 0 || data.length < offset + length) { + throw new ArrayIndexOutOfBoundsException(); + } + + // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge. + return null; + //return nativeDecodeByteArray(data, offset, length, opts); + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param data byte array of compressed image data + * @param offset offset into imageData for where the decoder should begin + * parsing. + * @param length the number of bytes, beginning at offset, to parse + * @return The decoded bitmap, or null if the image could not be decode. + */ + public static Bitmap decodeByteArray(byte[] data, int offset, int length) { + return decodeByteArray(data, offset, length, null); + } + + /** + * Decode an input stream into a bitmap. If the input stream is null, or + * cannot be used to decode a bitmap, the function returns null. + * The stream's position will be where ever it was after the encoded data + * was read. + * + * @param is The input stream that holds the raw data to be decoded into a + * bitmap. + * @param outPadding If not null, return the padding rect for the bitmap if + * it exists, otherwise set padding to [-1,-1,-1,-1]. If + * no bitmap is returned (null) then padding is + * unchanged. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { + // we don't throw in this case, thus allowing the caller to only check + // the cache, and not force the image to be decoded. + if (is == null) { + return null; + } + + // we need mark/reset to work properly + + if (!is.markSupported()) { + is = new BufferedInputStream(is, 16 * 1024); + } + + // so we can call reset() if a given codec gives up after reading up to + // this many bytes. FIXME: need to find out from the codecs what this + // value should be. + is.mark(1024); + + Bitmap bm; + + if (is instanceof AssetManager.AssetInputStream) { + // FIXME: log this. + return null; + } else { + // pass some temp storage down to the native code. 1024 is made up, + // but should be large enough to avoid too many small calls back + // into is.read(...) This number is not related to the value passed + // to mark(...) above. + try { + bm = new Bitmap(is); + } catch (IOException e) { + return null; + } + } + + return finishDecode(bm, outPadding, opts); + } + + private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) { + if (bm == null || opts == null) { + return bm; + } + + final int density = opts.inDensity; + if (density == 0) { + return bm; + } + + bm.setDensity(density); + final int targetDensity = opts.inTargetDensity; + if (targetDensity == 0 || density == targetDensity + || density == opts.inScreenDensity) { + return bm; + } + + byte[] np = bm.getNinePatchChunk(); + final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np); + if (opts.inScaled || isNinePatch) { + float scale = targetDensity / (float)density; + // TODO: This is very inefficient and should be done in native by Skia + final Bitmap oldBitmap = bm; + bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f), + (int) (bm.getHeight() * scale + 0.5f), true); + oldBitmap.recycle(); + + if (isNinePatch) { + //np = nativeScaleNinePatch(np, scale, outPadding); + bm.setNinePatchChunk(np); + } + bm.setDensity(targetDensity); + } + + return bm; + } + + /** + * Decode an input stream into a bitmap. If the input stream is null, or + * cannot be used to decode a bitmap, the function returns null. + * The stream's position will be where ever it was after the encoded data + * was read. + * + * @param is The input stream that holds the raw data to be decoded into a + * bitmap. + * @return The decoded bitmap, or null if the image data could not be + * decoded, or, if opts is non-null, if opts requested only the + * size be returned (in opts.outWidth and opts.outHeight) + */ + public static Bitmap decodeStream(InputStream is) { + return decodeStream(is, null, null); + } + + /** + * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded + * return null. The position within the descriptor will not be changed when + * this returns, so the descriptor can be used again as-is. + * + * @param fd The file descriptor containing the bitmap data to decode + * @param outPadding If not null, return the padding rect for the bitmap if + * it exists, otherwise set padding to [-1,-1,-1,-1]. If + * no bitmap is returned (null) then padding is + * unchanged. + * @param opts null-ok; Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return the decoded bitmap, or null + */ + public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { + return null; + + /* FIXME: implement as needed + try { + if (MemoryFile.isMemoryFile(fd)) { + int mappedlength = MemoryFile.getMappedSize(fd); + MemoryFile file = new MemoryFile(fd, mappedlength, "r"); + InputStream is = file.getInputStream(); + Bitmap bm = decodeStream(is, outPadding, opts); + return finishDecode(bm, outPadding, opts); + } + } catch (IOException ex) { + // invalid filedescriptor, no need to call nativeDecodeFileDescriptor() + return null; + } + //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts); + //return finishDecode(bm, outPadding, opts); + */ + } + + /** + * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded + * return null. The position within the descriptor will not be changed when + * this returns, so the descriptor can be used again as is. + * + * @param fd The file descriptor containing the bitmap data to decode + * @return the decoded bitmap, or null + */ + public static Bitmap decodeFileDescriptor(FileDescriptor fd) { + return decodeFileDescriptor(fd, null, null); + } +} + diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 5a13b0b..2623570 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -43,6 +43,7 @@ public class CreateInfo { public final static String[] RENAMED_CLASSES = new String[] { "android.graphics.Bitmap", "android.graphics._Original_Bitmap", + "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", "android.graphics.Canvas", "android.graphics._Original_Canvas", "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", |