Allow for screen density drawables in compatibility mode.

This change allows us to use drawables that match the current screen
density even when being loaded in compatibility mode.  In this case,
the bitmap is loaded in the screen density, and the bitmap and
nine-patch drawables take care of accounting for the density difference.

This should be safe for existing applications, for the most part, since
they shouldn't really be pulling the bitmap out of the drawable.  For
the small rare chance of them breaking, it worth getting the correct
graphics.  Also this will only happen when there is actually a resource
of the matching density, and no existing apps should have resources for
anything besides the default density (though of course all of the
framework resources will be available in the native density).

As part of this, the bitmap density API has been changed to a single
integer provider the DPI unit density.
This commit is contained in:
Dianne Hackborn
2009-07-22 21:48:55 -07:00
parent 3b99e64e58
commit 11ea33471e
29 changed files with 807 additions and 365 deletions

View File

@@ -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;
}