diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2009-07-31 17:38:20 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2009-07-31 17:38:20 -0700 |
commit | 5c1207be90fdf296c1b83034b7c68915e1749284 (patch) | |
tree | e5679f6183458d8179821d5615dabafcb959704d /graphics/java | |
parent | a8675f67e33bc7337d148358783b0fd138b501ff (diff) | |
download | frameworks_base-5c1207be90fdf296c1b83034b7c68915e1749284.zip frameworks_base-5c1207be90fdf296c1b83034b7c68915e1749284.tar.gz frameworks_base-5c1207be90fdf296c1b83034b7c68915e1749284.tar.bz2 |
donut snapshot
Diffstat (limited to 'graphics/java')
-rw-r--r-- | graphics/java/android/graphics/Bitmap.java | 238 | ||||
-rw-r--r-- | graphics/java/android/graphics/BitmapFactory.java | 180 | ||||
-rw-r--r-- | graphics/java/android/graphics/Canvas.java | 132 | ||||
-rw-r--r-- | graphics/java/android/graphics/NinePatch.java | 31 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/BitmapDrawable.java | 128 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/Drawable.java | 32 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/DrawableContainer.java | 11 | ||||
-rw-r--r-- | graphics/java/android/graphics/drawable/NinePatchDrawable.java | 115 |
8 files changed, 563 insertions, 304 deletions
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index df659ef..eef1096 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -30,11 +30,11 @@ public final class Bitmap implements Parcelable { /** * Indicates that the bitmap was created for an unknown pixel density. * - * @see Bitmap#getDensityScale() - * @see Bitmap#setDensityScale(float) + * @see Bitmap#getDensity() + * @see Bitmap#setDensity(int) */ - public static final float DENSITY_SCALE_UNKNOWN = -1.0f; - + public static final int DENSITY_NONE = 0; + // Note: mNativeBitmap is used by FaceDetector_jni.cpp // Don't change/rename without updating FaceDetector_jni.cpp private final int mNativeBitmap; @@ -45,11 +45,30 @@ public final class Bitmap implements Parcelable { private int mHeight = -1; private boolean mRecycled; - private static volatile Matrix sScaleMatrix; + // Package-scoped for fast access. + /*package*/ int mDensity = sDefaultDensity = getDefaultDensity(); - private float mDensityScale = DENSITY_SCALE_UNKNOWN; - private boolean mAutoScaling; + private static volatile Matrix sScaleMatrix; + private static volatile int sDefaultDensity = -1; + + /** + * For backwards compatibility, allows the app layer to change the default + * density when running old apps. + * @hide + */ + public static void setDefaultDensity(int density) { + sDefaultDensity = density; + } + + /*package*/ static int getDefaultDensity() { + if (sDefaultDensity >= 0) { + return sDefaultDensity; + } + sDefaultDensity = DisplayMetrics.DENSITY_DEVICE; + return sDefaultDensity; + } + /** * @noinspection UnusedDeclaration */ @@ -70,84 +89,46 @@ public final class Bitmap implements Parcelable { } /** - * <p>Returns the density scale for this bitmap, expressed as a factor of - * the default density (160.) For instance, a bitmap designed for - * displays with a density of 240 will have a density scale of 1.5 whereas a bitmap - * designed for a density of 160 will have a density scale of 1.0.</p> + * <p>Returns the density for this bitmap.</p> * - * <p>The default density scale is {@link #DENSITY_SCALE_UNKNOWN}.</p> + * <p>The default density is the same density as the current display, + * unless the current application does not support different screen + * densities in which case it is + * {@link android.util.DisplayMetrics#DENSITY_DEFAULT}. Note that + * compatibility mode is determined by the application that was initially + * loaded into a process -- applications that share the same process should + * all have the same compatibility, or ensure they explicitly set the + * density of their bitmaps appropriately.</p> * - * @return A scaling factor of the default density (160) or {@link #DENSITY_SCALE_UNKNOWN} + * @return A scaling factor of the default density or {@link #DENSITY_NONE} * if the scaling factor is unknown. * - * @see #setDensityScale(float) - * @see #isAutoScalingEnabled() - * @see #setAutoScalingEnabled(boolean) + * @see #setDensity(int) * @see android.util.DisplayMetrics#DENSITY_DEFAULT - * @see android.util.DisplayMetrics#density - * @see #DENSITY_SCALE_UNKNOWN + * @see android.util.DisplayMetrics#densityDpi + * @see #DENSITY_NONE */ - public float getDensityScale() { - return mDensityScale; + public int getDensity() { + return mDensity; } /** - * <p>Specifies the density scale for this bitmap, expressed as a factor of - * the default density (160.) For instance, a bitmap designed for - * displays with a density of 240 will have a density scale of 1.5 whereas a bitmap - * designed for a density of 160 will have a density scale of 1.0.</p> + * <p>Specifies the density for this bitmap. When the bitmap is + * drawn to a Canvas that also has a density, it will be scaled + * appropriately.</p> * - * @param densityScale The density scaling factor to use with this bitmap or - * {@link #DENSITY_SCALE_UNKNOWN} if the factor is unknown. + * @param density The density scaling factor to use with this bitmap or + * {@link #DENSITY_NONE} if the density is unknown. * - * @see #getDensityScale() - * @see #isAutoScalingEnabled() - * @see #setAutoScalingEnabled(boolean) + * @see #getDensity() * @see android.util.DisplayMetrics#DENSITY_DEFAULT - * @see android.util.DisplayMetrics#density - * @see #DENSITY_SCALE_UNKNOWN + * @see android.util.DisplayMetrics#densityDpi + * @see #DENSITY_NONE */ - public void setDensityScale(float densityScale) { - mDensityScale = densityScale; + public void setDensity(int density) { + mDensity = density; } - - /** - * </p>Indicates whether this bitmap will be automatically be scaled at the - * target's density at drawing time. If auto scaling is enabled, this bitmap - * will be drawn with the following scale factor:</p> - * - * <pre>scale = (bitmap density scale factor) / (target density scale factor)</pre> - * - * <p>Auto scaling is turned off by default. If auto scaling is enabled but the - * bitmap has an unknown density scale, then the bitmap will never be automatically - * scaled at drawing time.</p> - * - * @return True if the bitmap must be scaled at drawing time, false otherwise. - * - * @see #setAutoScalingEnabled(boolean) - * @see #getDensityScale() - * @see #setDensityScale(float) - */ - public boolean isAutoScalingEnabled() { - return mAutoScaling; - } - - /** - * <p>Enables or disables auto scaling for this bitmap. When auto scaling is enabled, - * the bitmap will be scaled at drawing time to accomodate the drawing target's pixel - * density. The final scale factor for this bitmap is thus defined:</p> - * - * <pre>scale = (bitmap density scale factor) / (target density scale factor)</pre> - * - * <p>If auto scaling is enabled but the bitmap has an unknown density scale, then - * the bitmap will never be automatically scaled at drawing time.</p> - * - * @param autoScalingEnabled True to scale the bitmap at drawing time, false otherwise. - */ - public void setAutoScalingEnabled(boolean autoScalingEnabled) { - mAutoScaling = autoScalingEnabled; - } - + /** * Sets the nine patch chunk. * @@ -317,7 +298,8 @@ public final class Bitmap implements Parcelable { * Tries to make a new bitmap based on the dimensions of this bitmap, * setting the new bitmap's config to the one specified, and then copying * this bitmap's pixels into the new bitmap. If the conversion is not - * supported, or the allocator fails, then this returns NULL. + * supported, or the allocator fails, then this returns NULL. The returned + * bitmap initially has the same density as the original. * * @param config The desired config for the resulting bitmap * @param isMutable True if the resulting bitmap should be mutable (i.e. @@ -326,7 +308,11 @@ public final class Bitmap implements Parcelable { */ public Bitmap copy(Config config, boolean isMutable) { checkRecycled("Can't copy a recycled bitmap"); - return nativeCopy(mNativeBitmap, config.nativeInt, isMutable); + Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable); + if (b != null) { + b.mDensity = mDensity; + } + return b; } public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, @@ -361,7 +347,8 @@ public final class Bitmap implements Parcelable { /** * Returns an immutable bitmap from the source bitmap. The new bitmap may - * be the same object as source, or a copy may have been made. + * be the same object as source, or a copy may have been made. It is + * initialized with the same density as the original bitmap. */ public static Bitmap createBitmap(Bitmap src) { return createBitmap(src, 0, 0, src.getWidth(), src.getHeight()); @@ -370,7 +357,8 @@ public final class Bitmap implements Parcelable { /** * Returns an immutable bitmap from the specified subset of the source * bitmap. The new bitmap may be the same object as source, or a copy may - * have been made. + * have been made. It is + * initialized with the same density as the original bitmap. * * @param source The bitmap we are subsetting * @param x The x coordinate of the first pixel in source @@ -384,7 +372,8 @@ public final class Bitmap implements Parcelable { /** * Returns an immutable bitmap from subset of the source bitmap, - * transformed by the optional matrix. + * transformed by the optional matrix. It is + * initialized with the same density as the original bitmap. * * @param source The bitmap we are subsetting * @param x The x coordinate of the first pixel in source @@ -451,19 +440,20 @@ public final class Bitmap implements Parcelable { paint.setAntiAlias(true); } } + + // The new bitmap was created from a known bitmap source so assume that + // they use the same density + bitmap.mDensity = source.mDensity; + canvas.setBitmap(bitmap); canvas.drawBitmap(source, srcR, dstR, paint); - // The new bitmap was created from a known bitmap source so assume that - // they use the same density scale - bitmap.mDensityScale = source.mDensityScale; - bitmap.mAutoScaling = source.mAutoScaling; - return bitmap; } /** - * Returns a mutable bitmap with the specified width and height. + * Returns a mutable bitmap with the specified width and height. Its + * initial density is as per {@link #getDensity}. * * @param width The width of the bitmap * @param height The height of the bitmap @@ -478,7 +468,8 @@ public final class Bitmap implements Parcelable { /** * Returns a immutable bitmap with the specified width and height, with each - * pixel value set to the corresponding value in the colors array. + * pixel value set to the corresponding value in the colors array. Its + * initial density is as per {@link #getDensity}. * * @param colors Array of {@link Color} used to initialize the pixels. * @param offset Number of values to skip before the first color in the @@ -512,7 +503,8 @@ public final class Bitmap implements Parcelable { /** * Returns a immutable bitmap with the specified width and height, with each - * pixel value set to the corresponding value in the colors array. + * pixel value set to the corresponding value in the colors array. Its + * initial density is as per {@link #getDensity}. * * @param colors Array of {@link Color} used to initialize the pixels. * This array must be at least as large as width * height. @@ -603,65 +595,71 @@ public final class Bitmap implements Parcelable { } /** - * Convenience method that returns the width of this bitmap divided - * by the density scale factor. - * - * @param canvas The Canvas the bitmap will be drawn to. - * @return The scaled width of this bitmap, according to the density scale factor. + * Convenience for calling {@link #getScaledWidth(int)} with the target + * density of the given {@link Canvas}. */ public int getScaledWidth(Canvas canvas) { - final float scale = mDensityScale; - if (!mAutoScaling || scale < 0) { - return getWidth(); - } - return (int)(getWidth() * canvas.getDensityScale() / scale); + return scaleFromDensity(getWidth(), mDensity, canvas.mDensity); } /** - * Convenience method that returns the height of this bitmap divided - * by the density scale factor. - * - * @param canvas The Canvas the bitmap will be drawn to. - * @return The scaled height of this bitmap, according to the density scale factor. + * Convenience for calling {@link #getScaledHeight(int)} with the target + * density of the given {@link Canvas}. */ public int getScaledHeight(Canvas canvas) { - final float scale = mDensityScale; - if (!mAutoScaling || scale < 0) { - return getHeight(); - } - return (int)(getHeight() * canvas.getDensityScale() / scale); + return scaleFromDensity(getHeight(), mDensity, canvas.mDensity); + } + + /** + * Convenience for calling {@link #getScaledWidth(int)} with the target + * density of the given {@link DisplayMetrics}. + */ + public int getScaledWidth(DisplayMetrics metrics) { + return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi); + } + + /** + * Convenience for calling {@link #getScaledHeight(int)} with the target + * density of the given {@link DisplayMetrics}. + */ + public int getScaledHeight(DisplayMetrics metrics) { + return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi); } /** * Convenience method that returns the width of this bitmap divided * by the density scale factor. * - * @param metrics The target display metrics. + * @param targetDensity The density of the target canvas of the bitmap. * @return The scaled width of this bitmap, according to the density scale factor. */ - public int getScaledWidth(DisplayMetrics metrics) { - final float scale = mDensityScale; - if (!mAutoScaling || scale < 0) { - return getWidth(); - } - return (int)(getWidth() * metrics.density / scale); + public int getScaledWidth(int targetDensity) { + return scaleFromDensity(getWidth(), mDensity, targetDensity); } /** * Convenience method that returns the height of this bitmap divided * by the density scale factor. * - * @param metrics The target display metrics. + * @param targetDensity The density of the target canvas of the bitmap. * @return The scaled height of this bitmap, according to the density scale factor. */ - public int getScaledHeight(DisplayMetrics metrics) { - final float scale = mDensityScale; - if (!mAutoScaling || scale < 0) { - return getHeight(); + public int getScaledHeight(int targetDensity) { + return scaleFromDensity(getHeight(), mDensity, targetDensity); + } + + /** + * @hide + */ + static public int scaleFromDensity(int size, int sdensity, int tdensity) { + if (sdensity == DENSITY_NONE || sdensity == tdensity) { + return size; } - return (int)(getHeight() * metrics.density / scale); + + // Scale by tdensity / sdensity, rounding up. + return ( (size * tdensity) + (sdensity >> 1) ) / sdensity; } - + /** * Return the number of bytes between rows in the bitmap's pixels. Note that * this refers to the pixels as stored natively by the bitmap. If you call @@ -922,6 +920,9 @@ public final class Bitmap implements Parcelable { * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then * drawing the original would result in the blur visually aligning with * the original. + * + * <p>The initial density of the returned bitmap is the same as the original's. + * * @param paint Optional paint used to modify the alpha values in the * resulting bitmap. Pass null for default behavior. * @param offsetXY Optional array that returns the X (index 0) and Y @@ -939,6 +940,7 @@ public final class Bitmap implements Parcelable { if (bm == null) { throw new RuntimeException("Failed to extractAlpha on Bitmap"); } + bm.mDensity = mDensity; return bm; } diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index 76abaa2..c8bed24 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -39,7 +39,6 @@ public class BitmapFactory { */ public Options() { inDither = true; - inDensity = 0; inScaled = true; } @@ -79,22 +78,87 @@ public class BitmapFactory { public boolean inDither; /** - * The desired pixel density of the bitmap. + * 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 android.util.DisplayMetrics#DENSITY_DEFAULT - * @see android.util.DisplayMetrics#density + * @see #inTargetDensity + * @see #inScreenDensity + * @see #inScaled + * @see Bitmap#setDensity(int) + * @see android.util.DisplayMetrics#densityDpi */ public int inDensity; /** - * </p>If the bitmap is loaded from {@link android.content.res.Resources} and - * this flag is turned on, the bitmap will be scaled to match the default - * display's pixel density.</p> + * 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. In this case, - * {@link android.graphics.Bitmap#setAutoScalingEnabled(boolean)} can be used - * to properly scale the bitmap at drawing time.</p> + * <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; @@ -235,58 +299,32 @@ public class BitmapFactory { * 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 decodeStream(Resources res, TypedValue value, InputStream is, - Rect pad, Options opts) { + public static Bitmap decodeResourceStream(Resources res, TypedValue value, + InputStream is, Rect pad, Options opts) { if (opts == null) { opts = new Options(); } - Bitmap bm = decodeStream(is, pad, opts); - - if (bm != null && res != null && value != null) { + if (opts.inDensity == 0 && value != null) { final int density = value.density; - if (density == TypedValue.DENSITY_NONE) { - return bm; - } - - byte[] np = bm.getNinePatchChunk(); - final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); - - if (opts.inDensity == 0) { - opts.inDensity = density == TypedValue.DENSITY_DEFAULT ? - DisplayMetrics.DENSITY_DEFAULT : density; - } - float scale = opts.inDensity / (float) DisplayMetrics.DENSITY_DEFAULT; - - if (opts.inScaled || isNinePatch) { - bm.setDensityScale(1.0f); - bm.setAutoScalingEnabled(false); - // Assume we are going to prescale for the screen - scale = res.getDisplayMetrics().density / scale; - if (scale != 1.0f) { - // 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, pad); - bm.setNinePatchChunk(np); - } - } - } else { - bm.setDensityScale(scale); - bm.setAutoScalingEnabled(true); + if (density == TypedValue.DENSITY_DEFAULT) { + opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; + } else if (density != TypedValue.DENSITY_NONE) { + opts.inDensity = density; } } - - return bm; + + if (opts.inTargetDensity == 0 && res != null) { + opts.inTargetDensity = res.getDisplayMetrics().densityDpi; + } + + return decodeStream(is, pad, opts); } /** - * Decode an image referenced by a resource ID. + * 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 @@ -303,7 +341,7 @@ public class BitmapFactory { final TypedValue value = new TypedValue(); final InputStream is = res.openRawResource(id, value); - bm = decodeStream(res, value, is, null, opts); + bm = decodeResourceStream(res, value, is, null, opts); is.close(); } catch (java.io.IOException e) { /* do nothing. @@ -315,7 +353,8 @@ public class BitmapFactory { } /** - * Decode an image referenced by a resource ID. + * 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 @@ -412,6 +451,39 @@ public class BitmapFactory { bm = nativeDecodeStream(is, tempStorage, outPadding, 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 = 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; } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index da73597..345f810 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import android.text.SpannedString; import android.text.SpannableString; import android.text.GraphicsOperations; +import android.util.DisplayMetrics; import javax.microedition.khronos.opengles.GL; @@ -47,15 +48,21 @@ public class Canvas { // optional field set by the caller private DrawFilter mDrawFilter; + // Package-scoped for quick access. + /*package*/ int mDensity = Bitmap.DENSITY_NONE; + + // Used to determine when compatibility scaling is in effect. + private int mScreenDensity = Bitmap.DENSITY_NONE; + // Used by native code @SuppressWarnings({"UnusedDeclaration"}) private int mSurfaceFormat; - @SuppressWarnings({"UnusedDeclaration"}) - private float mDensityScale = 1.0f; /** * Construct an empty raster canvas. Use setBitmap() to specify a bitmap to - * draw into. + * draw into. The initial target density is {@link Bitmap#DENSITY_NONE}; + * this will typically be replaced when a target bitmap is set for the + * canvas. */ public Canvas() { // 0 means no native bitmap @@ -65,6 +72,9 @@ public class Canvas { /** * Construct a canvas with the specified bitmap to draw into. The bitmap * must be mutable. + * + * <p>The initial target density of the canvas is the same as the given + * bitmap's density. * * @param bitmap Specifies a mutable bitmap for the canvas to draw into. */ @@ -76,8 +86,7 @@ public class Canvas { throwIfRecycled(bitmap); mNativeCanvas = initRaster(bitmap.ni()); mBitmap = bitmap; - mDensityScale = bitmap.getDensityScale(); - if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; + mDensity = bitmap.mDensity; } /*package*/ Canvas(int nativeCanvas) { @@ -85,6 +94,7 @@ public class Canvas { throw new IllegalStateException(); } mNativeCanvas = nativeCanvas; + mDensity = Bitmap.getDefaultDensity(); } /** @@ -93,10 +103,14 @@ public class Canvas { * be supported in this mode (e.g. some GL implementations may not support * antialiasing or certain effects like ColorMatrix or certain Xfermodes). * However, no exception will be thrown in those cases. + * + * <p>The initial target density of the canvas is the same as the initial + * density of bitmaps as per {@link Bitmap#getDensity() Bitmap.getDensity()}. */ public Canvas(GL gl) { mNativeCanvas = initGL(); mGL = gl; + mDensity = Bitmap.getDefaultDensity(); } /** @@ -117,9 +131,13 @@ public class Canvas { } /** - * Specify a bitmap for the canvas to draw into. + * Specify a bitmap for the canvas to draw into. As a side-effect, also + * updates the canvas's target density to match that of the bitmap. * * @param bitmap Specifies a mutable bitmap for the canvas to draw into. + * + * @see #setDensity(int) + * @see #getDensity() */ public void setBitmap(Bitmap bitmap) { if (!bitmap.isMutable()) { @@ -132,8 +150,7 @@ public class Canvas { native_setBitmap(mNativeCanvas, bitmap.ni()); mBitmap = bitmap; - mDensityScale = bitmap.getDensityScale(); - if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; + mDensity = bitmap.mDensity; } /** @@ -172,46 +189,44 @@ public class Canvas { public native int getHeight(); /** - * <p>Returns the density scale for this Canvas' backing bitmap, expressed as a - * factor of the default density (160dpi.) For instance, a bitmap designed for - * 240dpi displays will have a density scale of 1.5 whereas a bitmap - * designed for 160dpi will have a density scale of 1.0.</p> - * - * <p>The default density scale is {@link Bitmap#DENSITY_SCALE_UNKNOWN}.</p> + * <p>Returns the target density of the canvas. The default density is + * derived from the density of its backing bitmap, or + * {@link Bitmap#DENSITY_NONE} if there is not one.</p> * - * @return A scaling factor of the default density (160dpi) or - * {@link Bitmap#DENSITY_SCALE_UNKNOWN} if the scaling factor is unknown. + * @return Returns the current target density of the canvas, which is used + * to determine the scaling factor when drawing a bitmap into it. * - * @see #setDensityScale(float) - * @see Bitmap#getDensityScale() + * @see #setDensity(int) + * @see Bitmap#getDensity() */ - public float getDensityScale() { - if (mBitmap != null) { - return mBitmap.getDensityScale(); - } - return mDensityScale; + public int getDensity() { + return mDensity; } /** - * <p>Specifies the density scale for this Canvas' backing bitmap, expressed as a - * factor of the default density (160dpi.) For instance, a bitmap designed for - * 240dpi displays will have a density scale of 1.5 whereas a bitmap - * designed for 160dpi will have a density scale of 1.0.</p> + * <p>Specifies the density for this Canvas' backing bitmap. This modifies + * the target density of the canvas itself, as well as the density of its + * backing bitmap via {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}. * - * @param densityScale The density scaling factor to use with this bitmap or - * {@link Bitmap#DENSITY_SCALE_UNKNOWN} if the factor is unknown. + * @param density The new target density of the canvas, which is used + * to determine the scaling factor when drawing a bitmap into it. Use + * {@link Bitmap#DENSITY_NONE} to disable bitmap scaling. * - * @see #getDensityScale() - * @see Bitmap#setDensityScale(float) + * @see #getDensity() + * @see Bitmap#setDensity(int) */ - public void setDensityScale(float densityScale) { + public void setDensity(int density) { if (mBitmap != null) { - mBitmap.setDensityScale(densityScale); + mBitmap.setDensity(density); } - mDensityScale = densityScale; - if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f; + mDensity = density; } + /** @hide */ + public void setScreenDensity(int density) { + mScreenDensity = density; + } + // the SAVE_FLAG constants must match their native equivalents /** restore the current matrix when restore() is called */ @@ -945,12 +960,17 @@ public class Canvas { /** * Draw the specified bitmap, with its top/left corner at (x,y), using * the specified paint, transformed by the current matrix. - * Note: if the paint contains a maskfilter that generates a mask which + * + * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. * Thus the color outside of the original width/height will be the edge * color replicated. * + * <p>If the bitmap and canvas have different densities, this function + * will take care of automatically scaling the bitmap to draw at the + * same density as the canvas. + * * @param bitmap The bitmap to be drawn * @param left The position of the left side of the bitmap being drawn * @param top The position of the top side of the bitmap being drawn @@ -959,20 +979,26 @@ public class Canvas { public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) { throwIfRecycled(bitmap); native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top, - paint != null ? paint.mNativePaint : 0, bitmap.isAutoScalingEnabled(), - bitmap.getDensityScale()); + paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, + bitmap.mDensity); } /** * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * Note: if the paint contains a maskfilter that generates a mask which + * + * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. * Thus the color outside of the original width/height will be the edge * color replicated. * + * <p>This function <em>ignores the density associated with the bitmap</em>. + * This is because the source and destination rectangle coordinate + * spaces are in their respective densities, so must already have the + * appropriate scaling factor applied. + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -985,19 +1011,26 @@ public class Canvas { } throwIfRecycled(bitmap); native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, - paint != null ? paint.mNativePaint : 0); + paint != null ? paint.mNativePaint : 0, + mScreenDensity, bitmap.mDensity); } /** * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * Note: if the paint contains a maskfilter that generates a mask which + * + * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. * Thus the color outside of the original width/height will be the edge * color replicated. * + * <p>This function <em>ignores the density associated with the bitmap</em>. + * This is because the source and destination rectangle coordinate + * spaces are in their respective densities, so must already have the + * appropriate scaling factor applied. + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -1010,7 +1043,8 @@ public class Canvas { } throwIfRecycled(bitmap); native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst, - paint != null ? paint.mNativePaint : 0); + paint != null ? paint.mNativePaint : 0, + mScreenDensity, bitmap.mDensity); } /** @@ -1489,14 +1523,20 @@ public class Canvas { int paint); private native void native_drawBitmap(int nativeCanvas, int bitmap, float left, float top, - int nativePaintOrZero, boolean autoScale, - float densityScale); + int nativePaintOrZero, + int canvasDensity, + int screenDensity, + int bitmapDensity); private native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, RectF dst, - int nativePaintOrZero); + int nativePaintOrZero, + int screenDensity, + int bitmapDensity); private static native void native_drawBitmap(int nativeCanvas, int bitmap, Rect src, Rect dst, - int nativePaintOrZero); + int nativePaintOrZero, + int screenDensity, + int bitmapDensity); private static native void native_drawBitmap(int nativeCanvas, int[] colors, int offset, int stride, float x, float y, int width, int height, diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index 778c903..88dfd67 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -68,7 +68,7 @@ public class NinePatch { } /** - * Draw a bitmap to nine patches. + * Draw a bitmap of nine patches. * * @param canvas A container for the current matrix and clip used to draw the bitmap. * @param location Where to draw the bitmap. @@ -76,23 +76,25 @@ public class NinePatch { public void draw(Canvas canvas, RectF location) { nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mChunk, - mPaint != null ? mPaint.mNativePaint : 0); + mPaint != null ? mPaint.mNativePaint : 0, + canvas.mDensity, mBitmap.mDensity); } /** - * Draw a bitmap to nine patches. + * Draw a bitmap of nine patches. * * @param canvas A container for the current matrix and clip used to draw the bitmap. * @param location Where to draw the bitmap. */ public void draw(Canvas canvas, Rect location) { nativeDraw(canvas.mNativeCanvas, location, - mBitmap.ni(), mChunk, - mPaint != null ? mPaint.mNativePaint : 0); + mBitmap.ni(), mChunk, + mPaint != null ? mPaint.mNativePaint : 0, + canvas.mDensity, mBitmap.mDensity); } /** - * Draw a bitmap to nine patches. + * Draw a bitmap of nine patches. * * @param canvas A container for the current matrix and clip used to draw the bitmap. * @param location Where to draw the bitmap. @@ -100,9 +102,18 @@ public class NinePatch { */ public void draw(Canvas canvas, Rect location, Paint paint) { nativeDraw(canvas.mNativeCanvas, location, - mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0); + mBitmap.ni(), mChunk, paint != null ? paint.mNativePaint : 0, + canvas.mDensity, mBitmap.mDensity); } + /** + * Return the underlying bitmap's density, as per + * {@link Bitmap#getDensity() Bitmap.getDensity()}. + */ + public int getDensity() { + return mBitmap.mDensity; + } + public int getWidth() { return mBitmap.getWidth(); } @@ -129,9 +140,11 @@ public class NinePatch { private static native void validateNinePatchChunk(int bitmap, byte[] chunk); private static native void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance, - byte[] c, int paint_instance_or_null); + byte[] c, int paint_instance_or_null, + int destDensity, int srcDensity); private static native void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance, - byte[] c, int paint_instance_or_null); + byte[] c, int paint_instance_or_null, + int destDensity, int srcDensity); private static native int nativeGetTransparentRegion( int bitmap, byte[] chunk, Rect location); } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 5b32246..eade73a 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -63,18 +63,56 @@ public class BitmapDrawable extends Drawable { private boolean mApplyGravity; private boolean mRebuildShader; - private int mBitmapWidth; - private int mBitmapHeight; private boolean mMutated; + + private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; + // These are scaled to match the target density. + private int mBitmapWidth; + private int mBitmapHeight; + + /** + * Create an empty drawable, not dealing with density. + * @deprecated Use {@link #BitmapDrawable(Resources)} to ensure + * that the drawable has correctly set its target density. + */ public BitmapDrawable() { mBitmapState = new BitmapState((Bitmap) null); } + /** + * Create an empty drawable, setting initial target density based on + * the display metrics of the resources. + */ + public BitmapDrawable(Resources res) { + mBitmapState = new BitmapState((Bitmap) null); + if (res != null) { + setTargetDensity(res.getDisplayMetrics()); + mBitmapState.mTargetDensity = mTargetDensity; + } + } + + /** + * Create drawable from a bitmap, not dealing with density. + * @deprecated Use {@link #BitmapDrawable(Resources, Bitmap)} to ensure + * that the drawable has correctly set its target density. + */ public BitmapDrawable(Bitmap bitmap) { this(new BitmapState(bitmap)); } + /** + * Create drawable from a bitmap, setting initial target density based on + * the display metrics of the resources. + */ + public BitmapDrawable(Resources res, Bitmap bitmap) { + this(new BitmapState(bitmap)); + if (res != null) { + setTargetDensity(res.getDisplayMetrics()); + mBitmapState.mTargetDensity = mTargetDensity; + } + } + public BitmapDrawable(String filepath) { this(new BitmapState(BitmapFactory.decodeFile(filepath))); if (mBitmap == null) { @@ -97,11 +135,15 @@ public class BitmapDrawable extends Drawable { return mBitmap; } + private void computeBitmapSize() { + mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity); + mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity); + } + private void setBitmap(Bitmap bitmap) { mBitmap = bitmap; if (bitmap != null) { - mBitmapWidth = bitmap.getWidth(); - mBitmapHeight = bitmap.getHeight(); + computeBitmapSize(); } else { mBitmapWidth = mBitmapHeight = -1; } @@ -114,13 +156,11 @@ public class BitmapDrawable extends Drawable { * * @param canvas The Canvas from which the density scale must be obtained. * - * @see android.graphics.Bitmap#setDensityScale(float) - * @see android.graphics.Bitmap#getDensityScale() - * - * @hide pending API council approval + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() */ - public void setDensityScale(Canvas canvas) { - setDensityScale(canvas.getDensityScale()); + public void setTargetDensity(Canvas canvas) { + setTargetDensity(canvas.getDensity()); } /** @@ -128,32 +168,33 @@ public class BitmapDrawable extends Drawable { * * @param metrics The DisplayMetrics indicating the density scale for this drawable. * - * @see android.graphics.Bitmap#setDensityScale(float) - * @see android.graphics.Bitmap#getDensityScale() - * - * @hide pending API council approval + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() */ - public void setDensityScale(DisplayMetrics metrics) { - setDensityScale(metrics.density); + public void setTargetDensity(DisplayMetrics metrics) { + mTargetDensity = metrics.densityDpi; + if (mBitmap != null) { + computeBitmapSize(); + } } /** - * Set the density scale at which this drawable will be rendered. + * Set the density at which this drawable will be rendered. * * @param density The density scale for this drawable. * - * @see android.graphics.Bitmap#setDensityScale(float) - * @see android.graphics.Bitmap#getDensityScale() - * - * @hide pending API council approval + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() */ - public void setDensityScale(float density) { - density = (density == Bitmap.DENSITY_SCALE_UNKNOWN ? 1.0f : density); - mBitmapState.mTargetDensityScale = density; + public void setTargetDensity(int density) { + mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density; + if (mBitmap != null) { + computeBitmapSize(); + } } /** Get the gravity used to position/stretch the bitmap within its bounds. - See android.view.Gravity + * See android.view.Gravity * @return the gravity applied to the bitmap */ public int getGravity() { @@ -302,7 +343,7 @@ public class BitmapDrawable extends Drawable { } mBitmapState.mBitmap = bitmap; setBitmap(bitmap); - setDensityScale(r.getDisplayMetrics()); + setTargetDensity(r.getDisplayMetrics()); final Paint paint = mBitmapState.mPaint; paint.setAntiAlias(a.getBoolean(com.android.internal.R.styleable.BitmapDrawable_antialias, @@ -332,29 +373,12 @@ public class BitmapDrawable extends Drawable { @Override public int getIntrinsicWidth() { - final Bitmap bitmap = mBitmap; - final BitmapState state = mBitmapState; - - if (!state.mAutoScale || state.mBitmapScale == Bitmap.DENSITY_SCALE_UNKNOWN) { - return mBitmapWidth; - } else { - return bitmap != null ? (int) (mBitmapWidth / - (state.mBitmapScale / state.mTargetDensityScale) + 0.5f) : -1; - - } + return mBitmapWidth; } @Override public int getIntrinsicHeight() { - final Bitmap bitmap = mBitmap; - final BitmapState state = mBitmapState; - - if (!state.mAutoScale || state.mBitmapScale == Bitmap.DENSITY_SCALE_UNKNOWN) { - return mBitmapHeight; - } else { - return bitmap != null ? (int) (mBitmapHeight / - (state.mBitmapScale / state.mTargetDensityScale) + 0.5f) : -1; - } + return mBitmapHeight; } @Override @@ -380,19 +404,10 @@ public class BitmapDrawable extends Drawable { Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); Shader.TileMode mTileModeX; Shader.TileMode mTileModeY; - boolean mAutoScale; - float mBitmapScale; - float mTargetDensityScale = 1.0f; + int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; BitmapState(Bitmap bitmap) { mBitmap = bitmap; - if (bitmap != null) { - mBitmapScale = bitmap.getDensityScale(); - mAutoScale = bitmap.isAutoScalingEnabled(); - } else { - mBitmapScale = 1.0f; - mAutoScale = false; - } } BitmapState(BitmapState bitmapState) { @@ -401,7 +416,7 @@ public class BitmapDrawable extends Drawable { mGravity = bitmapState.mGravity; mTileModeX = bitmapState.mTileModeX; mTileModeY = bitmapState.mTileModeY; - mTargetDensityScale = bitmapState.mTargetDensityScale; + mTargetDensity = bitmapState.mTargetDensity; mPaint = new Paint(bitmapState.mPaint); } @@ -418,6 +433,7 @@ public class BitmapDrawable extends Drawable { private BitmapDrawable(BitmapState state) { mBitmapState = state; + mTargetDensity = state.mTargetDensity; setBitmap(state.mBitmap); } } diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 910e111..0a0e4eb 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -27,6 +27,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.*; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.StateSet; import android.util.Xml; import android.util.TypedValue; @@ -657,9 +658,8 @@ public abstract class Drawable { } /** - * Create a drawable from an inputstream - * - * @hide pending API council approval + * Create a drawable from an inputstream, using the given resources and + * value to determine density information. */ public static Drawable createFromResourceStream(Resources res, TypedValue value, InputStream is, String srcName) { @@ -675,7 +675,17 @@ public abstract class Drawable { Rects only to drop them on the floor. */ Rect pad = new Rect(); - Bitmap bm = BitmapFactory.decodeStream(res, value, is, pad, null); + + // Special stuff for compatibility mode: if the target density is not + // the same as the display density, but the resource -is- the same as + // the display density, then don't scale it down to the target density. + // This allows us to load the system's density-correct resources into + // an application in compatibility mode, without scaling those down + // to the compatibility density only to have them scaled back up when + // drawn to the screen. + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; + Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts); if (bm != null) { byte[] np = bm.getNinePatchChunk(); if (np == null || !NinePatch.isNinePatchChunk(np)) { @@ -754,10 +764,13 @@ public abstract class Drawable { } else if (name.equals("bitmap")) { drawable = new BitmapDrawable(); if (r != null) { - ((BitmapDrawable) drawable).setDensityScale(r.getDisplayMetrics()); + ((BitmapDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); } } else if (name.equals("nine-patch")) { drawable = new NinePatchDrawable(); + if (r != null) { + ((NinePatchDrawable) drawable).setTargetDensity(r.getDisplayMetrics()); + } } else { throw new XmlPullParserException(parser.getPositionDescription() + ": invalid drawable tag " + name); @@ -812,15 +825,10 @@ public abstract class Drawable { Rect pad, String srcName) { if (np != null) { - return new NinePatchDrawable(bm, np, pad, srcName); + return new NinePatchDrawable(res, bm, np, pad, srcName); } - final BitmapDrawable drawable = new BitmapDrawable(bm); - if (res != null) { - drawable.setDensityScale(res.getDisplayMetrics()); - } - - return drawable; + return new BitmapDrawable(res, bm); } } diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 376b1df..dc80cf5 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -272,6 +272,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { boolean mCheckedConstantState; boolean mCanConstantState; + boolean mPaddingChecked = false; + DrawableContainerState(DrawableContainerState orig, DrawableContainer owner) { mOwner = owner; @@ -334,6 +336,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mHaveStateful = false; mConstantPadding = null; + mPaddingChecked = false; mComputedConstantSize = false; return pos; @@ -359,23 +362,25 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (mVariablePadding) { return null; } - if (mConstantPadding != null) { + if (mConstantPadding != null || mPaddingChecked) { return mConstantPadding; } - final Rect r = new Rect(0, 0, 0, 0); + Rect r = null; final Rect t = new Rect(); final int N = getChildCount(); final Drawable[] drawables = mDrawables; for (int i = 0; i < N; i++) { if (drawables[i].getPadding(t)) { + if (r == null) r = new Rect(0, 0, 0, 0); if (t.left > r.left) r.left = t.left; if (t.top > r.top) r.top = t.top; if (t.right > r.right) r.right = t.right; if (t.bottom > r.bottom) r.bottom = t.bottom; } } - return (mConstantPadding=r); + mPaddingChecked = true; + return (mConstantPadding = r); } public final void setConstantSize(boolean constant) { diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index dace96c..d5c8a08 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -20,6 +20,7 @@ import android.graphics.*; import android.content.res.Resources; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.TypedValue; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -41,24 +42,122 @@ public class NinePatchDrawable extends Drawable { private Paint mPaint; private boolean mMutated; + private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; + + // These are scaled to match the target density. + private int mBitmapWidth; + private int mBitmapHeight; + NinePatchDrawable() { } + /** + * Create drawable from raw nine-patch data, not dealing with density. + * @deprecated Use {@link #NinePatchDrawable(Resources, Bitmap, byte[], Rect, String)} + * to ensure that the drawable has correctly set its target density. + */ public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) { this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); } + /** + * Create drawable from raw nine-patch data, setting initial target density + * based on the display metrics of the resources. + */ + public NinePatchDrawable(Resources res, Bitmap bitmap, byte[] chunk, + Rect padding, String srcName) { + this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding)); + if (res != null) { + setTargetDensity(res.getDisplayMetrics()); + mNinePatchState.mTargetDensity = mTargetDensity; + } + } + + /** + * Create drawable from existing nine-patch, not dealing with density. + * @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)} + * to ensure that the drawable has correctly set its target density. + */ public NinePatchDrawable(NinePatch patch) { this(new NinePatchState(patch, null)); } + /** + * Create drawable from existing nine-patch, setting initial target density + * based on the display metrics of the resources. + */ + public NinePatchDrawable(Resources res, NinePatch patch) { + this(new NinePatchState(patch, null)); + if (res != null) { + setTargetDensity(res.getDisplayMetrics()); + mNinePatchState.mTargetDensity = mTargetDensity; + } + } + private void setNinePatchState(NinePatchState state) { mNinePatchState = state; mNinePatch = state.mNinePatch; mPadding = state.mPadding; + mTargetDensity = state.mTargetDensity; if (state.mDither) setDither(state.mDither); + if (mNinePatch != null) { + computeBitmapSize(); + } } + /** + * Set the density scale at which this drawable will be rendered. This + * method assumes the drawable will be rendered at the same density as the + * specified canvas. + * + * @param canvas The Canvas from which the density scale must be obtained. + * + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() + */ + public void setTargetDensity(Canvas canvas) { + setTargetDensity(canvas.getDensity()); + } + + /** + * Set the density scale at which this drawable will be rendered. + * + * @param metrics The DisplayMetrics indicating the density scale for this drawable. + * + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() + */ + public void setTargetDensity(DisplayMetrics metrics) { + mTargetDensity = metrics.densityDpi; + if (mNinePatch != null) { + computeBitmapSize(); + } + } + + /** + * Set the density at which this drawable will be rendered. + * + * @param density The density scale for this drawable. + * + * @see android.graphics.Bitmap#setDensity(int) + * @see android.graphics.Bitmap#getDensity() + */ + public void setTargetDensity(int density) { + mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density; + if (mNinePatch != null) { + computeBitmapSize(); + } + } + + private void computeBitmapSize() { + final int sdensity = mNinePatch.getDensity(); + final int tdensity = mTargetDensity; + mBitmapWidth = Bitmap.scaleFromDensity(mNinePatch.getWidth(), + sdensity, tdensity); + mBitmapHeight = Bitmap.scaleFromDensity(mNinePatch.getHeight(), + sdensity, tdensity); + } + // overrides @Override @@ -111,6 +210,7 @@ public class NinePatchDrawable extends Drawable { if (dither) { options.inDither = false; } + options.inScreenDensity = DisplayMetrics.DENSITY_DEVICE; final Rect padding = new Rect(); Bitmap bitmap = null; @@ -119,7 +219,7 @@ public class NinePatchDrawable extends Drawable { final TypedValue value = new TypedValue(); final InputStream is = r.openRawResource(id, value); - bitmap = BitmapFactory.decodeStream(r, value, is, padding, options); + bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); is.close(); } catch (IOException e) { @@ -136,6 +236,7 @@ public class NinePatchDrawable extends Drawable { setNinePatchState(new NinePatchState( new NinePatch(bitmap, bitmap.getNinePatchChunk(), "XML 9-patch"), padding, dither)); + mNinePatchState.mTargetDensity = mTargetDensity; a.recycle(); } @@ -153,7 +254,7 @@ public class NinePatchDrawable extends Drawable { */ @Override public int getIntrinsicWidth() { - return mNinePatch.getWidth(); + return mBitmapWidth; } /** @@ -161,17 +262,17 @@ public class NinePatchDrawable extends Drawable { */ @Override public int getIntrinsicHeight() { - return mNinePatch.getHeight(); + return mBitmapHeight; } @Override public int getMinimumWidth() { - return mNinePatch.getWidth(); + return mBitmapWidth; } @Override public int getMinimumHeight() { - return mNinePatch.getHeight(); + return mBitmapHeight; } /** @@ -211,6 +312,7 @@ public class NinePatchDrawable extends Drawable { final Rect mPadding; final boolean mDither; int mChangingConfigurations; + int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; NinePatchState(NinePatch ninePatch, Rect padding) { this(ninePatch, padding, false); @@ -225,8 +327,9 @@ public class NinePatchDrawable extends Drawable { NinePatchState(NinePatchState state) { mNinePatch = new NinePatch(state.mNinePatch); mPadding = new Rect(state.mPadding); - mChangingConfigurations = state.mChangingConfigurations; mDither = state.mDither; + mChangingConfigurations = state.mChangingConfigurations; + mTargetDensity = state.mTargetDensity; } @Override |