From 11ea33471e1a14a8594f0b2cd012d86340dd3bd8 Mon Sep 17 00:00:00 2001
From: Dianne Hackborn
Date: Wed, 22 Jul 2009 21:48:55 -0700
Subject: [PATCH] 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.
---
api/current.xml | 268 +++++++++++++++---
core/java/android/app/ActivityThread.java | 16 +-
core/java/android/app/ApplicationContext.java | 17 +-
core/java/android/app/LauncherActivity.java | 4 +-
core/java/android/text/style/ImageSpan.java | 4 +-
core/java/android/util/DisplayMetrics.java | 9 +
core/java/android/view/Display.java | 1 +
core/java/android/view/View.java | 2 +
core/java/android/view/ViewRoot.java | 6 +-
core/java/android/widget/ImageView.java | 2 +-
.../internal/widget/EditStyledText.java | 3 +-
core/jni/android/graphics/Canvas.cpp | 15 +-
core/jni/android/graphics/Graphics.cpp | 9 -
core/jni/android/graphics/GraphicsJNI.h | 1 -
core/jni/android/graphics/NinePatch.cpp | 40 ++-
graphics/java/android/graphics/Bitmap.java | 178 +++++-------
.../java/android/graphics/BitmapFactory.java | 180 ++++++++----
graphics/java/android/graphics/Canvas.java | 86 +++---
graphics/java/android/graphics/NinePatch.java | 31 +-
.../graphics/drawable/BitmapDrawable.java | 128 +++++----
.../android/graphics/drawable/Drawable.java | 32 ++-
.../graphics/drawable/NinePatchDrawable.java | 115 +++++++-
.../res/drawable-hdpi/npatch240dpi.9.png | Bin 0 -> 17331 bytes
.../res/drawable-hdpi/smlnpatch240dpi.9.png | Bin 0 -> 946 bytes
.../res/drawable-ldpi/npatch120dpi.9.png | Bin 0 -> 6299 bytes
.../res/drawable-ldpi/smlnpatch120dpi.9.png | Bin 0 -> 804 bytes
tests/DpiTest/res/drawable/npatch160dpi.9.png | Bin 0 -> 9978 bytes
.../res/drawable/smlnpatch160dpi.9.png | Bin 0 -> 855 bytes
.../android/test/dpi/DpiTestActivity.java | 25 +-
29 files changed, 807 insertions(+), 365 deletions(-)
create mode 100644 tests/DpiTest/res/drawable-hdpi/npatch240dpi.9.png
create mode 100644 tests/DpiTest/res/drawable-hdpi/smlnpatch240dpi.9.png
create mode 100644 tests/DpiTest/res/drawable-ldpi/npatch120dpi.9.png
create mode 100644 tests/DpiTest/res/drawable-ldpi/smlnpatch120dpi.9.png
create mode 100644 tests/DpiTest/res/drawable/npatch160dpi.9.png
create mode 100644 tests/DpiTest/res/drawable/smlnpatch160dpi.9.png
diff --git a/api/current.xml b/api/current.xml
index 46969e0ade2c1..d56263fd4e5ff 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -49107,8 +49107,8 @@
visibility="public"
>
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
-
-
+
+
+
+
-
-
-
+
+
+
@@ -59139,6 +59172,28 @@
deprecated="not deprecated"
visibility="public"
>
+
+
+
+
+
+
+
+
+
+
@@ -59304,6 +59359,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -61356,6 +61469,36 @@
deprecated="not deprecated"
visibility="public"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -61420,6 +61563,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
wr = mActiveResources.get(key);
Resources r = wr != null ? wr.get() : null;
if (r != null && r.getAssets().isUpToDate()) {
- //Log.w(TAG, "Returning cached resources " + r + " " + resDir);
+ if (false) {
+ Log.w(TAG, "Returning cached resources " + r + " " + resDir
+ + ": appScale=" + r.getCompatibilityInfo().applicationScale);
+ }
return r;
}
@@ -198,7 +204,11 @@ public final class ActivityThread {
//Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics metrics = getDisplayMetricsLocked(false);
r = new Resources(assets, metrics, getConfiguration(), compInfo);
- //Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
+ if (false) {
+ Log.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ + r.getConfiguration() + " appScale="
+ + r.getCompatibilityInfo().applicationScale);
+ }
// XXX need to remove entries when weak references go away
mActiveResources.put(key, new WeakReference(r));
return r;
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index afb2fe99c2e06..92929ea56748e 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -541,7 +541,10 @@ class ApplicationContext extends Context {
if (fd != null) {
Bitmap bm = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
if (bm != null) {
- return new BitmapDrawable(bm);
+ // For now clear the density until we figure out how
+ // to deal with it for wallpapers.
+ bm.setDensity(0);
+ return new BitmapDrawable(getResources(), bm);
}
}
} catch (RemoteException e) {
@@ -1949,6 +1952,15 @@ class ApplicationContext extends Context {
try {
Resources r = getResourcesForApplication(appInfo);
dr = r.getDrawable(resid);
+ if (false) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resid)
+ + " from package " + packageName
+ + ": app scale=" + r.getCompatibilityInfo().applicationScale
+ + ", caller scale=" + mContext.getResources().getCompatibilityInfo().applicationScale,
+ e);
+ }
if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
+ Integer.toHexString(resid) + " from " + r
+ ": " + dr);
@@ -2036,10 +2048,9 @@ class ApplicationContext extends Context {
if (app.packageName.equals("system")) {
return mContext.mMainThread.getSystemContext().getResources();
}
- ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app);
Resources r = mContext.mMainThread.getTopLevelResources(
app.uid == Process.myUid() ? app.sourceDir
- : app.publicSourceDir, pi);
+ : app.publicSourceDir, mContext.mPackageInfo);
if (r != null) {
return r;
}
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index accdda9ba599b..d788c43d7d632 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -297,7 +297,7 @@ public abstract class LauncherActivity extends ListActivity {
icon.setBounds(x, y, x + width, y + height);
icon.draw(canvas);
icon.setBounds(mOldBounds);
- icon = new BitmapDrawable(thumb);
+ icon = new BitmapDrawable(getResources(), thumb);
} else if (iconWidth < width && iconHeight < height) {
final Bitmap.Config c = Bitmap.Config.ARGB_8888;
final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c);
@@ -309,7 +309,7 @@ public abstract class LauncherActivity extends ListActivity {
icon.setBounds(x, y, x + iconWidth, y + iconHeight);
icon.draw(canvas);
icon.setBounds(mOldBounds);
- icon = new BitmapDrawable(thumb);
+ icon = new BitmapDrawable(getResources(), thumb);
}
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 29c0c76ad1b11..911a23c9631c7 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -43,7 +43,7 @@ public class ImageSpan extends DynamicDrawableSpan {
*/
public ImageSpan(Bitmap b, int verticalAlignment) {
super(verticalAlignment);
- mDrawable = new BitmapDrawable(b);
+ mDrawable = new BitmapDrawable(mContext.getResources(), b);
int width = mDrawable.getIntrinsicWidth();
int height = mDrawable.getIntrinsicHeight();
mDrawable.setBounds(0, 0, width > 0 ? width : 0, height > 0 ? height : 0);
@@ -117,7 +117,7 @@ public class ImageSpan extends DynamicDrawableSpan {
InputStream is = mContext.getContentResolver().openInputStream(
mContentUri);
bitmap = BitmapFactory.decodeStream(is);
- drawable = new BitmapDrawable(bitmap);
+ drawable = new BitmapDrawable(mContext.getResources(), bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
is.close();
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 061f98a5a53ed..dd5a4401c4d51 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -79,6 +79,11 @@ public class DisplayMetrics {
* @see #DENSITY_DEFAULT
*/
public float density;
+ /**
+ * The screen density expressed as dots-per-inch. May be either
+ * {@link #DENSITY_LOW}, {@link #DENSITY_MEDIUM}, or {@link #DENSITY_HIGH}.
+ */
+ public int densityDpi;
/**
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
@@ -101,6 +106,7 @@ public class DisplayMetrics {
widthPixels = o.widthPixels;
heightPixels = o.heightPixels;
density = o.density;
+ densityDpi = o.densityDpi;
scaledDensity = o.scaledDensity;
xdpi = o.xdpi;
ydpi = o.ydpi;
@@ -110,6 +116,7 @@ public class DisplayMetrics {
widthPixels = 0;
heightPixels = 0;
density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
+ densityDpi = DENSITY_DEVICE;
scaledDensity = density;
xdpi = DENSITY_DEVICE;
ydpi = DENSITY_DEVICE;
@@ -186,9 +193,11 @@ public class DisplayMetrics {
heightPixels = defaultHeight;
}
}
+
if (compatibilityInfo.isScalingRequired()) {
float invertedRatio = compatibilityInfo.applicationInvertedScale;
density *= invertedRatio;
+ densityDpi = (int)((density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
scaledDensity *= invertedRatio;
xdpi *= invertedRatio;
ydpi *= invertedRatio;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 5551f64bcde34..b055d51c1b40a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -94,6 +94,7 @@ public class Display
outMetrics.widthPixels = getWidth();
outMetrics.heightPixels = getHeight();
outMetrics.density = mDensity;
+ outMetrics.densityDpi = (int)((mDensity*DisplayMetrics.DENSITY_DEFAULT)+.5f);
outMetrics.scaledDensity= outMetrics.density;
outMetrics.xdpi = mDpiX;
outMetrics.ydpi = mDpiY;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ff8868bb0f2fb..7ed2712dc7503 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -46,6 +46,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Config;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Pool;
@@ -5976,6 +5977,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
try {
bitmap = Bitmap.createBitmap(width, height, quality);
+ bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
if (autoScale) {
mDrawingCache = new SoftReference(bitmap);
} else {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index bb61ad3c93c55..b6119aa160611 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -187,7 +187,7 @@ public final class ViewRoot extends Handler implements ViewParent,
*/
AudioManager mAudioManager;
- private final float mDensity;
+ private final int mDensity;
public ViewRoot(Context context) {
super();
@@ -229,7 +229,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mAdded = false;
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
mViewConfiguration = ViewConfiguration.get(context);
- mDensity = context.getResources().getDisplayMetrics().density;
+ mDensity = context.getResources().getDisplayMetrics().densityDpi;
}
@Override
@@ -1270,7 +1270,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
// TODO: Do this in native
- canvas.setDensityScale(mDensity);
+ canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
// TODO: we should ask the window manager to do something!
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 27967742a135d..6a9bcfbb2fc4e 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -317,7 +317,7 @@ public class ImageView extends View {
public void setImageBitmap(Bitmap bm) {
// if this is used frequently, may handle bitmaps explicitly
// to reduce the intermediate drawable object
- setImageDrawable(new BitmapDrawable(bm));
+ setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}
public void setImageState(int[] state, boolean merge) {
diff --git a/core/java/com/android/internal/widget/EditStyledText.java b/core/java/com/android/internal/widget/EditStyledText.java
index f0ad7b3bb3b51..82197c0151f21 100644
--- a/core/java/com/android/internal/widget/EditStyledText.java
+++ b/core/java/com/android/internal/widget/EditStyledText.java
@@ -1242,7 +1242,8 @@ public class EditStyledText extends EditText {
try {
InputStream is = mEST.getContext().getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(is);
- Drawable drawable = new BitmapDrawable(bitmap);
+ Drawable drawable = new BitmapDrawable(
+ getContext().getResources(), bitmap);
drawable.setBounds(0, 0,
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 93d68cbfe0aea..52acf04c21e84 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -462,17 +462,18 @@ public:
static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
SkCanvas* canvas, SkBitmap* bitmap,
jfloat left, jfloat top,
- SkPaint* paint,
- jboolean autoScale, jfloat densityScale) {
+ SkPaint* paint, jint canvasDensity,
+ jint bitmapDensity) {
SkScalar left_ = SkFloatToScalar(left);
SkScalar top_ = SkFloatToScalar(top);
- if (!autoScale || densityScale <= 0.0f) {
+ if (canvasDensity == bitmapDensity || canvasDensity == 0
+ || bitmapDensity == 0) {
canvas->drawBitmap(*bitmap, left_, top_, paint);
} else {
canvas->save();
- SkScalar canvasScale = GraphicsJNI::getCanvasDensityScale(env, jcanvas);
- SkScalar scale = canvasScale / SkFloatToScalar(densityScale);
+ SkScalar scale = SkFloatToScalar(canvasDensity / (float)bitmapDensity);
+ canvas->translate(left_, top_);
canvas->scale(scale, scale);
SkPaint filteredPaint;
@@ -481,7 +482,7 @@ public:
}
filteredPaint.setFilterBitmap(true);
- canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);
+ canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
canvas->restore();
}
@@ -905,7 +906,7 @@ static JNINativeMethod gCanvasMethods[] = {
{"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V",
(void*) SkCanvasGlue::drawRoundRect},
{"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath},
- {"native_drawBitmap","(IIFFIZF)V",
+ {"native_drawBitmap","(IIFFIII)V",
(void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
{"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;I)V",
(void*) SkCanvasGlue::drawBitmapRF},
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6e159a8853d59..ca1cb7d652019 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -163,7 +163,6 @@ static jfieldID gBitmapConfig_nativeInstanceID;
static jclass gCanvas_class;
static jfieldID gCanvas_nativeInstanceID;
-static jfieldID gCanvas_densityScaleID;
static jclass gPaint_class;
static jfieldID gPaint_nativeInstanceID;
@@ -320,13 +319,6 @@ SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
return c;
}
-SkScalar GraphicsJNI::getCanvasDensityScale(JNIEnv* env, jobject canvas) {
- SkASSERT(env);
- SkASSERT(canvas);
- SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
- return SkFloatToScalar(env->GetFloatField(canvas, gCanvas_densityScaleID));
-}
-
SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
SkASSERT(env);
SkASSERT(paint);
@@ -557,7 +549,6 @@ int register_android_graphics_Graphics(JNIEnv* env)
gCanvas_class = make_globalref(env, "android/graphics/Canvas");
gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
- gCanvas_densityScaleID = getFieldIDCheck(env, gCanvas_class, "mDensityScale", "F");
gPaint_class = make_globalref(env, "android/graphics/Paint");
gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 16925e41ab4f3..f8b60a805e1ba 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -38,7 +38,6 @@ public:
static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
static SkPicture* getNativePicture(JNIEnv*, jobject picture);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
- static SkScalar getCanvasDensityScale(JNIEnv*, jobject canvas);
/** Return the corresponding native config from the java Config enum,
or kNo_Config if the java object is null.
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index b11edfc07436e..fd5271e4c18b0 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -1,5 +1,6 @@
#include
+#include "SkCanvas.h"
#include "SkRegion.h"
#include "GraphicsJNI.h"
@@ -45,7 +46,8 @@ public:
}
static void draw(JNIEnv* env, SkCanvas* canvas, SkRect& bounds,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint)
+ const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
+ jint destDensity, jint srcDensity)
{
size_t chunkSize = env->GetArrayLength(chunkObj);
void* storage = alloca(chunkSize);
@@ -56,13 +58,32 @@ public:
Res_png_9patch* chunk = static_cast(storage);
assert(chunkSize == chunk->serializedSize());
// this relies on deserialization being done in place
- Res_png_9patch::deserialize(chunk);
- NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
+ Res_png_9patch::deserialize(chunk);
+
+ if (destDensity == srcDensity || destDensity == 0
+ || srcDensity == 0) {
+ NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
+ } else {
+ canvas->save();
+
+ SkScalar scale = SkFloatToScalar(destDensity / (float)srcDensity);
+ canvas->translate(bounds.fLeft, bounds.fTop);
+ canvas->scale(scale, scale);
+
+ bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale);
+ bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale);
+ bounds.fLeft = bounds.fTop = 0;
+
+ NinePatch_Draw(canvas, bounds, *bitmap, *chunk, paint, NULL);
+
+ canvas->restore();
+ }
}
}
static void drawF(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRectF,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint)
+ const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
+ jint destDensity, jint srcDensity)
{
SkASSERT(canvas);
SkASSERT(boundsRectF);
@@ -73,11 +94,12 @@ public:
SkRect bounds;
GraphicsJNI::jrectf_to_rect(env, boundsRectF, &bounds);
- draw(env, canvas, bounds, bitmap, chunkObj, paint);
+ draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
}
static void drawI(JNIEnv* env, jobject, SkCanvas* canvas, jobject boundsRect,
- const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint)
+ const SkBitmap* bitmap, jbyteArray chunkObj, const SkPaint* paint,
+ jint destDensity, jint srcDensity)
{
SkASSERT(canvas);
SkASSERT(boundsRect);
@@ -87,7 +109,7 @@ public:
SkRect bounds;
GraphicsJNI::jrect_to_rect(env, boundsRect, &bounds);
- draw(env, canvas, bounds, bitmap, chunkObj, paint);
+ draw(env, canvas, bounds, bitmap, chunkObj, paint, destDensity, srcDensity);
}
static jint getTransparentRegion(JNIEnv* env, jobject,
@@ -126,8 +148,8 @@ public:
static JNINativeMethod gNinePatchMethods[] = {
{ "isNinePatchChunk", "([B)Z", (void*)SkNinePatchGlue::isNinePatchChunk },
{ "validateNinePatchChunk", "(I[B)V", (void*)SkNinePatchGlue::validateNinePatchChunk },
- { "nativeDraw", "(ILandroid/graphics/RectF;I[BI)V", (void*)SkNinePatchGlue::drawF },
- { "nativeDraw", "(ILandroid/graphics/Rect;I[BI)V", (void*)SkNinePatchGlue::drawI },
+ { "nativeDraw", "(ILandroid/graphics/RectF;I[BIII)V", (void*)SkNinePatchGlue::drawF },
+ { "nativeDraw", "(ILandroid/graphics/Rect;I[BIII)V", (void*)SkNinePatchGlue::drawI },
{ "nativeGetTransparentRegion", "(I[BLandroid/graphics/Rect;)I",
(void*)SkNinePatchGlue::getTransparentRegion }
};
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index df659ef224dee..bb1922906c27a 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,10 +45,10 @@ 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 = DENSITY_NONE;
- private float mDensityScale = DENSITY_SCALE_UNKNOWN;
- private boolean mAutoScaling;
+ private static volatile Matrix sScaleMatrix;
/**
* @noinspection UnusedDeclaration
@@ -70,84 +70,39 @@ public final class Bitmap implements Parcelable {
}
/**
- * 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.
+ * Returns the density for this bitmap.
*
- * The default density scale is {@link #DENSITY_SCALE_UNKNOWN}.
+ * The default density scale is {@link #DENSITY_NONE}.
*
- * @return A scaling factor of the default density (160) or {@link #DENSITY_SCALE_UNKNOWN}
+ * @return A scaling factor of the default density (160) 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;
}
/**
- * 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.
+ * Specifies the density for this bitmap. When the bitmap is
+ * drawn to a Canvas that also has a density, it will be scaled
+ * appropriately.
*
- * @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;
}
-
- /**
- *
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:
- *
- * scale = (bitmap density scale factor) / (target density scale factor)
- *
- * 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.
- *
- * @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;
- }
-
- /**
- * 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:
- *
- * scale = (bitmap density scale factor) / (target density scale factor)
- *
- * If auto scaling is enabled but the bitmap has an unknown density scale, then
- * the bitmap will never be automatically scaled at drawing time.
- *
- * @param autoScalingEnabled True to scale the bitmap at drawing time, false otherwise.
- */
- public void setAutoScalingEnabled(boolean autoScalingEnabled) {
- mAutoScaling = autoScalingEnabled;
- }
-
+
/**
* Sets the nine patch chunk.
*
@@ -455,9 +410,8 @@ public final class Bitmap implements Parcelable {
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;
+ // they use the same density
+ bitmap.mDensity = source.mDensity;
return bitmap;
}
@@ -603,65 +557,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();
- }
- return (int)(getHeight() * metrics.density / scale);
+ 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;
+ }
+
+ // 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
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 76abaa23b9a09..c8bed24967669 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.
+ *
+ * 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;
/**
- *
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.
+ * 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.
+ *
+ * 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.
+ *
+ *
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.
+ *
+ *
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.
*
- *
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.
+ * 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 da7359772ee38..8ecbfbd95a364 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,11 +48,12 @@ public class Canvas {
// optional field set by the caller
private DrawFilter mDrawFilter;
+ // Package-scoped for quick access.
+ /*package*/ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+
// 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
@@ -76,8 +78,9 @@ public class Canvas {
throwIfRecycled(bitmap);
mNativeCanvas = initRaster(bitmap.ni());
mBitmap = bitmap;
- mDensityScale = bitmap.getDensityScale();
- if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f;
+ final int density = bitmap.mDensity;
+ mDensity = density == Bitmap.DENSITY_NONE
+ ? DisplayMetrics.DENSITY_DEFAULT : density;
}
/*package*/ Canvas(int nativeCanvas) {
@@ -132,8 +135,9 @@ public class Canvas {
native_setBitmap(mNativeCanvas, bitmap.ni());
mBitmap = bitmap;
- mDensityScale = bitmap.getDensityScale();
- if (mDensityScale == Bitmap.DENSITY_SCALE_UNKNOWN) mDensityScale = 1.0f;
+ final int density = bitmap.mDensity;
+ mDensity = density == Bitmap.DENSITY_NONE
+ ? DisplayMetrics.DENSITY_DEFAULT : density;
}
/**
@@ -172,44 +176,34 @@ public class Canvas {
public native int getHeight();
/**
- *
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.
+ * Returns the density for this Canvas' backing bitmap.
*
- * The default density scale is {@link Bitmap#DENSITY_SCALE_UNKNOWN}.
+ * The default density scale is {@link Bitmap#DENSITY_NONE}.
*
* @return A scaling factor of the default density (160dpi) or
- * {@link Bitmap#DENSITY_SCALE_UNKNOWN} if the scaling factor is unknown.
+ * {@link Bitmap#DENSITY_NONE} if the scaling factor is unknown.
*
- * @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;
}
/**
- * 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.
+ * Specifies the density for this Canvas' backing bitmap.
*
- * @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 density scaling factor to use with this bitmap or
+ * {@link Bitmap#DENSITY_NONE} if the factor is unknown.
*
- * @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;
}
// the SAVE_FLAG constants must match their native equivalents
@@ -945,12 +939,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
+ *
+ *
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.
*
+ *
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 +958,25 @@ 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, 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
+ *
+ *
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.
*
+ *
This function ignores the density associated with the bitmap.
+ * 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
@@ -992,12 +996,18 @@ public class Canvas {
* 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
+ *
+ *
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.
*
+ *
This function ignores the density associated with the bitmap.
+ * 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
@@ -1489,8 +1499,8 @@ 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 bitmapDensity);
private native void native_drawBitmap(int nativeCanvas, int bitmap,
Rect src, RectF dst,
int nativePaintOrZero);
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 778c90304dcb5..88dfd6790f3a2 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 5b32246c9bb93..eade73ae92766 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 boolean mMutated;
+
+ private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT;
+
+ // These are scaled to match the target density.
private int mBitmapWidth;
private int mBitmapHeight;
- private boolean mMutated;
-
+
+ /**
+ * 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 910e111e0947c..0a0e4eb51fb89 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/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index dace96c9c8919..d5c8a089b5e0a 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
diff --git a/tests/DpiTest/res/drawable-hdpi/npatch240dpi.9.png b/tests/DpiTest/res/drawable-hdpi/npatch240dpi.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..a362b0ffe281996d2eb324e48df56ccc0031d7cb
GIT binary patch
literal 17331
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
U$N{&z)Bpeg07*qoM6N<$f`>4}{r~^~
literal 0
HcmV?d00001
diff --git a/tests/DpiTest/res/drawable-ldpi/npatch120dpi.9.png b/tests/DpiTest/res/drawable-ldpi/npatch120dpi.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d8115bcfbfceaa1f46c777bba30a49f1e461996
GIT binary patch
literal 6299
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
RfP?@5002ovPDHLkV1kkjBE0|r
literal 0
HcmV?d00001
diff --git a/tests/DpiTest/res/drawable-ldpi/smlnpatch120dpi.9.png b/tests/DpiTest/res/drawable-ldpi/smlnpatch120dpi.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..de8d60764936eac9421c4320ae453f4e5ace2d82
GIT binary patch
literal 804
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
Eg4ZI2KmY&$
literal 0
HcmV?d00001
diff --git a/tests/DpiTest/res/drawable/smlnpatch160dpi.9.png b/tests/DpiTest/res/drawable/smlnpatch160dpi.9.png
new file mode 100644
index 0000000000000000000000000000000000000000..76c4ae8f05c9c56c833e8d77669f3ee89ed0391f
GIT binary patch
literal 855
zcmV-d1E~CoP)4Tx0C)kdlh13@U>L{0S(#IqWmb_P2s!B3p=xM1u(xh)Bi%v`ZO5h;ZIiUA
zwrNb#I;RJZ{sl5nQP6`2Z@Van?kIx47>HhX61?ef2O05s-bNbv%1qyF~fBg^k9ApPR&>krG8U3AuzHpZW
zJX{B=HvxslJ2Xyg0|%}HDoCd`rPJXR^pIp=f4-n7Vroj7meobB{Ks3T-J`SOk)Zpi
zRWF^Tah$F%wx?220lWQ5A>>9&E{3ev^7A3Dn7LBO4M#4Ae5p3aHpp`t)-W#uX+p?
hm{a1F;Rx%<`~|moR;(0BVJiRt002ovPDHLkV1gXtm5~4d
literal 0
HcmV?d00001
diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
index dd4fae3c99bf3..68220a103924e 100644
--- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
@@ -34,6 +34,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.util.DisplayMetrics;
+import android.util.Log;
public class DpiTestActivity extends Activity {
public DpiTestActivity() {
@@ -116,6 +117,13 @@ public class DpiTestActivity extends Activity {
addLabelToRoot(root, "No-dpi resource drawable");
addChildToRoot(root, layout);
+ layout = new LinearLayout(this);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch120dpi);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch160dpi);
+ addNinePatchResourceDrawable(layout, R.drawable.smlnpatch240dpi);
+ addLabelToRoot(root, "Prescaled 9-patch resource drawable");
+ addChildToRoot(root, layout);
+
setContentView(scrollWrap(root));
}
@@ -144,8 +152,8 @@ public class DpiTestActivity extends Activity {
View view = new View(this);
- final BitmapDrawable d = new BitmapDrawable(bitmap);
- if (!scale) d.setDensityScale(getResources().getDisplayMetrics());
+ final BitmapDrawable d = new BitmapDrawable(getResources(), bitmap);
+ if (!scale) d.setTargetDensity(getResources().getDisplayMetrics());
view.setBackgroundDrawable(d);
view.setLayoutParams(new LinearLayout.LayoutParams(d.getIntrinsicWidth(),
@@ -175,6 +183,19 @@ public class DpiTestActivity extends Activity {
layout.addView(view);
}
+ private void addNinePatchResourceDrawable(LinearLayout layout, int resource) {
+ View view = new View(this);
+
+ final Drawable d = getResources().getDrawable(resource);
+ view.setBackgroundDrawable(d);
+
+ Log.i("foo", "9-patch #" + Integer.toHexString(resource)
+ + " w=" + d.getIntrinsicWidth() + " h=" + d.getIntrinsicHeight());
+ view.setLayoutParams(new LinearLayout.LayoutParams(
+ d.getIntrinsicWidth()*2, d.getIntrinsicHeight()*2));
+ layout.addView(view);
+ }
+
private Bitmap loadAndPrintDpi(int id, boolean scale) {
Bitmap bitmap;
if (scale) {
|