blob: 79052f8fc64fa8f8e8bd94cfa9be7d0f1087cff2 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.drawable.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.NinePatch;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.cts.R;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NinePatchDrawableTest {
// A small value is actually making sure that the values are matching
// exactly with the golden image.
// We can increase the threshold if the Skia is drawing with some variance
// on different devices. So far, the tests show they are matching correctly.
private static final float PIXEL_ERROR_THRESHOLD = 0.03f;
private static final float PIXEL_ERROR_COUNT_THRESHOLD = 0.005f;
private static final int MIN_CHUNK_SIZE = 32;
// Set true to generate golden images, false for normal tests.
private static final boolean DBG_DUMP_PNG = false;
private NinePatchDrawable mNinePatchDrawable;
private Resources mResources;
@Before
public void setup() {
mResources = InstrumentationRegistry.getTargetContext().getResources();
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_0);
}
@SuppressWarnings("deprecation")
@Test
public void testConstructors() {
byte[] chunk = new byte[MIN_CHUNK_SIZE];
chunk[MIN_CHUNK_SIZE - 1] = 1;
Rect r = new Rect();
Bitmap bmp = BitmapFactory.decodeResource(mResources, R.drawable.ninepatch_0);
String name = mResources.getResourceName(R.drawable.ninepatch_0);
new NinePatchDrawable(bmp, chunk, r, name);
new NinePatchDrawable(new NinePatch(bmp, chunk, name));
chunk = new byte[MIN_CHUNK_SIZE - 1];
chunk[MIN_CHUNK_SIZE - 2] = 1;
try {
new NinePatchDrawable(bmp, chunk, r, name);
fail("The constructor should check whether the chunk is illegal.");
} catch (RuntimeException e) {
// This exception is thrown by native method.
}
}
@Test
public void testDraw() {
Bitmap bmp = Bitmap.createBitmap(9, 9, Config.ARGB_8888);
Canvas c = new Canvas(bmp);
int ocean = Color.rgb(0, 0xFF, 0x80);
mNinePatchDrawable.setBounds(0, 0, 9, 9);
mNinePatchDrawable.draw(c);
verifyColorFillRect(bmp, 0, 0, 4, 4, Color.RED);
verifyColorFillRect(bmp, 5, 0, 4, 4, Color.BLUE);
verifyColorFillRect(bmp, 0, 5, 4, 4, ocean);
verifyColorFillRect(bmp, 5, 5, 4, 4, Color.YELLOW);
verifyColorFillRect(bmp, 4, 0, 1, 9, Color.WHITE);
verifyColorFillRect(bmp, 0, 4, 9, 1, Color.WHITE);
bmp.eraseColor(0xff000000);
mNinePatchDrawable.setBounds(0, 0, 3, 3);
mNinePatchDrawable.draw(c);
verifyColorFillRect(bmp, 0, 0, 1, 1, Color.RED);
verifyColorFillRect(bmp, 2, 0, 1, 1, Color.BLUE);
verifyColorFillRect(bmp, 0, 2, 1, 1, ocean);
verifyColorFillRect(bmp, 2, 2, 1, 1, Color.YELLOW);
verifyColorFillRect(bmp, 1, 0, 1, 3, Color.WHITE);
verifyColorFillRect(bmp, 0, 1, 3, 1, Color.WHITE);
}
@Test(expected=NullPointerException.class)
public void testDrawNullCanvas() {
mNinePatchDrawable.draw(null);
}
@Test
public void testGetChangingConfigurations() {
ConstantState constantState = mNinePatchDrawable.getConstantState();
// default
assertEquals(0, constantState.getChangingConfigurations());
assertEquals(0, mNinePatchDrawable.getChangingConfigurations());
// change the drawable's configuration does not affect the state's configuration
mNinePatchDrawable.setChangingConfigurations(0xff);
assertEquals(0xff, mNinePatchDrawable.getChangingConfigurations());
assertEquals(0, constantState.getChangingConfigurations());
// the state's configuration get refreshed
constantState = mNinePatchDrawable.getConstantState();
assertEquals(0xff, constantState.getChangingConfigurations());
// set a new configuration to drawable
mNinePatchDrawable.setChangingConfigurations(0xff00);
assertEquals(0xff, constantState.getChangingConfigurations());
assertEquals(0xffff, mNinePatchDrawable.getChangingConfigurations());
}
@Test
public void testGetPadding() {
Rect r = new Rect();
NinePatchDrawable npd = (NinePatchDrawable) mResources.getDrawable(R.drawable.ninepatch_0);
assertTrue(npd.getPadding(r));
// exact padding unknown due to possible density scaling
assertEquals(0, r.left);
assertEquals(0, r.top);
assertTrue(r.right > 0);
assertTrue(r.bottom > 0);
npd = (NinePatchDrawable) mResources.getDrawable(R.drawable.ninepatch_1);
assertTrue(npd.getPadding(r));
assertTrue(r.left > 0);
assertTrue(r.top > 0);
assertTrue(r.right > 0);
assertTrue(r.bottom > 0);
}
@Test
public void testSetAlpha() {
assertEquals(0xff, mNinePatchDrawable.getPaint().getAlpha());
mNinePatchDrawable.setAlpha(0);
assertEquals(0, mNinePatchDrawable.getPaint().getAlpha());
mNinePatchDrawable.setAlpha(-1);
assertEquals(0xff, mNinePatchDrawable.getPaint().getAlpha());
mNinePatchDrawable.setAlpha(0xfffe);
assertEquals(0xfe, mNinePatchDrawable.getPaint().getAlpha());
}
@Test
public void testSetColorFilter() {
assertNull(mNinePatchDrawable.getPaint().getColorFilter());
ColorFilter cf = new ColorFilter();
mNinePatchDrawable.setColorFilter(cf);
assertSame(cf, mNinePatchDrawable.getPaint().getColorFilter());
mNinePatchDrawable.setColorFilter(null);
assertNull(mNinePatchDrawable.getPaint().getColorFilter());
}
@Test
public void testSetTint() {
mNinePatchDrawable.setTint(Color.BLACK);
mNinePatchDrawable.setTintMode(Mode.SRC_OVER);
assertEquals("Nine-patch is tinted", Color.BLACK,
DrawableTestUtils.getPixel(mNinePatchDrawable, 0, 0));
mNinePatchDrawable.setTintList(null);
mNinePatchDrawable.setTintMode(null);
}
@Test
public void testSetDither() {
mNinePatchDrawable.setDither(false);
assertFalse(mNinePatchDrawable.getPaint().isDither());
mNinePatchDrawable.setDither(true);
assertTrue(mNinePatchDrawable.getPaint().isDither());
}
@Test
public void testSetFilterBitmap() {
mNinePatchDrawable.setFilterBitmap(false);
assertFalse(mNinePatchDrawable.getPaint().isFilterBitmap());
mNinePatchDrawable.setFilterBitmap(true);
assertTrue(mNinePatchDrawable.getPaint().isFilterBitmap());
}
@Test
public void testIsFilterBitmap() {
mNinePatchDrawable.setFilterBitmap(false);
assertFalse(mNinePatchDrawable.isFilterBitmap());
assertEquals(mNinePatchDrawable.isFilterBitmap(),
mNinePatchDrawable.getPaint().isFilterBitmap());
mNinePatchDrawable.setFilterBitmap(true);
assertTrue(mNinePatchDrawable.isFilterBitmap());
assertEquals(mNinePatchDrawable.isFilterBitmap(),
mNinePatchDrawable.getPaint().isFilterBitmap());
}
@Test
public void testGetPaint() {
Paint paint = mNinePatchDrawable.getPaint();
assertNotNull(paint);
assertSame(paint, mNinePatchDrawable.getPaint());
}
@Test
public void testGetIntrinsicWidth() {
Bitmap bmp = getBitmapUnscaled(R.drawable.ninepatch_0);
assertEquals(bmp.getWidth(), mNinePatchDrawable.getIntrinsicWidth());
assertEquals(5, mNinePatchDrawable.getIntrinsicWidth());
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
bmp = getBitmapUnscaled(R.drawable.ninepatch_1);
assertEquals(bmp.getWidth(), mNinePatchDrawable.getIntrinsicWidth());
assertEquals(9, mNinePatchDrawable.getIntrinsicWidth());
}
@Test
public void testGetMinimumWidth() {
Bitmap bmp = getBitmapUnscaled(R.drawable.ninepatch_0);
assertEquals(bmp.getWidth(), mNinePatchDrawable.getMinimumWidth());
assertEquals(5, mNinePatchDrawable.getMinimumWidth());
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
bmp = getBitmapUnscaled(R.drawable.ninepatch_1);
assertEquals(bmp.getWidth(), mNinePatchDrawable.getMinimumWidth());
assertEquals(9, mNinePatchDrawable.getMinimumWidth());
}
@Test
public void testGetIntrinsicHeight() {
Bitmap bmp = getBitmapUnscaled(R.drawable.ninepatch_0);
assertEquals(bmp.getHeight(), mNinePatchDrawable.getIntrinsicHeight());
assertEquals(5, mNinePatchDrawable.getIntrinsicHeight());
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
bmp = getBitmapUnscaled(R.drawable.ninepatch_1);
assertEquals(bmp.getHeight(), mNinePatchDrawable.getIntrinsicHeight());
assertEquals(9, mNinePatchDrawable.getIntrinsicHeight());
}
@Test
public void testGetMinimumHeight() {
Bitmap bmp = getBitmapUnscaled(R.drawable.ninepatch_0);
assertEquals(bmp.getHeight(), mNinePatchDrawable.getMinimumHeight());
assertEquals(5, mNinePatchDrawable.getMinimumHeight());
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
bmp = getBitmapUnscaled(R.drawable.ninepatch_1);
assertEquals(bmp.getHeight(), mNinePatchDrawable.getMinimumHeight());
assertEquals(9, mNinePatchDrawable.getMinimumHeight());
}
// Known failure: Bug 2834281 - Bitmap#hasAlpha seems to return true for
// images without alpha
@Ignore
@Test
public void testGetOpacity() {
assertEquals(PixelFormat.OPAQUE, mNinePatchDrawable.getOpacity());
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
assertEquals(PixelFormat.TRANSLUCENT, mNinePatchDrawable.getOpacity());
}
@Test
public void testGetTransparentRegion() {
// opaque image
Region r = mNinePatchDrawable.getTransparentRegion();
assertNull(r);
mNinePatchDrawable.setBounds(0, 0, 7, 7);
r = mNinePatchDrawable.getTransparentRegion();
assertNull(r);
// translucent image
mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
r = mNinePatchDrawable.getTransparentRegion();
assertNull(r);
mNinePatchDrawable.setBounds(1, 1, 7, 7);
r = mNinePatchDrawable.getTransparentRegion();
assertNotNull(r);
assertEquals(new Rect(1, 1, 7, 7), r.getBounds());
}
@Test
public void testGetConstantState() {
assertNotNull(mNinePatchDrawable.getConstantState());
ConstantState constantState = mNinePatchDrawable.getConstantState();
// change the drawable's configuration does not affect the state's configuration
mNinePatchDrawable.setChangingConfigurations(0xff);
assertEquals(0, constantState.getChangingConfigurations());
// the state's configuration refreshed when getConstantState is called.
constantState = mNinePatchDrawable.getConstantState();
assertEquals(0xff, constantState.getChangingConfigurations());
}
@Test
public void testInflate() throws XmlPullParserException, IOException {
int sourceWidth = 80;
int sourceHeight = 120;
int[] colors = new int[sourceWidth * sourceHeight];
Bitmap bitmap = Bitmap.createBitmap(
colors, sourceWidth, sourceHeight, Bitmap.Config.RGB_565);
NinePatchDrawable ninePatchDrawable = new NinePatchDrawable(
mResources, bitmap, new byte[1000], null, "TESTNAME");
int sourceDensity = bitmap.getDensity();
int targetDensity = mResources.getDisplayMetrics().densityDpi;
int targetWidth = DrawableTestUtils.scaleBitmapFromDensity(
sourceWidth, sourceDensity, targetDensity);
int targetHeight = DrawableTestUtils.scaleBitmapFromDensity(
sourceHeight, sourceDensity, targetDensity);
assertEquals(targetWidth, ninePatchDrawable.getIntrinsicWidth());
assertEquals(targetHeight, ninePatchDrawable.getIntrinsicHeight());
XmlResourceParser parser = mResources.getXml(R.drawable.ninepatchdrawable);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
AttributeSet attrs = Xml.asAttributeSet(parser);
ninePatchDrawable.inflate(mResources, parser, attrs);
assertTrue(ninePatchDrawable.getPaint().isDither());
assertTrue(sourceHeight != ninePatchDrawable.getIntrinsicHeight());
assertTrue(sourceWidth != ninePatchDrawable.getIntrinsicWidth());
}
@Test
public void testMutate() {
NinePatchDrawable d1 =
(NinePatchDrawable) mResources.getDrawable(R.drawable.ninepatchdrawable);
NinePatchDrawable d2 =
(NinePatchDrawable) mResources.getDrawable(R.drawable.ninepatchdrawable);
NinePatchDrawable d3 =
(NinePatchDrawable) mResources.getDrawable(R.drawable.ninepatchdrawable);
// the state is not shared before mutate.
d1.setDither(false);
assertFalse(d1.getPaint().isDither());
assertTrue(d2.getPaint().isDither());
assertTrue(d3.getPaint().isDither());
// cannot test if mutate worked, since state was not shared before
d1.mutate();
}
private static final int[] DENSITY_VALUES = new int[] {
160, 80, 320
};
private static final int[] DENSITY_IMAGES = new int[] {
R.drawable.nine_patch_density
};
private static final int[][] DENSITY_GOLDEN_IMAGES = new int[][] {
{
R.drawable.nine_patch_density_golden_160,
R.drawable.nine_patch_density_golden_80,
R.drawable.nine_patch_density_golden_320,
}
};
private interface TargetDensitySetter {
void setTargetDensity(NinePatchDrawable dr, int density);
}
private void verifySetTargetDensityOuter(TargetDensitySetter densitySetter) {
final Resources res = mResources;
final int densityDpi = res.getConfiguration().densityDpi;
try {
verifySetTargetDensityInner(res, DENSITY_IMAGES[0], DENSITY_VALUES, densitySetter);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
DrawableTestUtils.setResourcesDensity(res, densityDpi);
}
}
private void verifySetTargetDensityInner(Resources res, int sourceResId, int[] densities,
TargetDensitySetter densitySetter) throws XmlPullParserException, IOException {
final Rect tempPadding = new Rect();
// Capture initial state at preload density.
final int preloadDensityDpi = densities[0];
DrawableTestUtils.setResourcesDensity(res, preloadDensityDpi);
final NinePatchDrawable preloadedDrawable =
(NinePatchDrawable) res.getDrawable(sourceResId).mutate();
final int origWidth = preloadedDrawable.getIntrinsicWidth();
final int origHeight = preloadedDrawable.getIntrinsicHeight();
final Rect origPadding = new Rect();
preloadedDrawable.getPadding(origPadding);
for (int i = 1; i < densities.length; i++) {
final int scaledDensityDpi = densities[i];
final float scale = scaledDensityDpi / (float) preloadDensityDpi;
final NinePatchDrawable scaledDrawable =
(NinePatchDrawable) res.getDrawable(sourceResId).mutate();
densitySetter.setTargetDensity(scaledDrawable, scaledDensityDpi);
// Sizes are rounded.
assertEquals(Math.round(origWidth * scale), scaledDrawable.getIntrinsicWidth());
assertEquals(Math.round(origHeight * scale), scaledDrawable.getIntrinsicHeight());
// Padding is truncated.
assertTrue(scaledDrawable.getPadding(tempPadding));
assertEquals((int) (origPadding.left * scale), tempPadding.left);
assertEquals((int) (origPadding.top * scale), tempPadding.top);
assertEquals((int) (origPadding.right * scale), tempPadding.right);
assertEquals((int) (origPadding.bottom * scale), tempPadding.bottom);
// Ensure theme density is applied correctly. Unlike most
// drawables, we don't have any loss of accuracy because density
// changes are re-computed from the source every time.
DrawableTestUtils.setResourcesDensity(res, preloadDensityDpi);
final Theme t = res.newTheme();
scaledDrawable.applyTheme(t);
assertEquals(origWidth, scaledDrawable.getIntrinsicWidth());
assertEquals(origHeight, scaledDrawable.getIntrinsicHeight());
assertTrue(scaledDrawable.getPadding(tempPadding));
assertEquals(origPadding, tempPadding);
}
}
@Test
public void testSetTargetDensity() {
verifySetTargetDensityOuter((dr, density) -> dr.setTargetDensity(density));
}
@Test
public void testSetTargetDensity_Canvas() {
// This should be identical to calling setTargetDensity(int) with the
// value returned by Canvas.getDensity().
verifySetTargetDensityOuter((dr, density) -> {
Canvas c = new Canvas();
c.setDensity(density);
dr.setTargetDensity(c);
});
}
@Test
public void testSetTargetDensity_DisplayMetrics() {
// This should be identical to calling setTargetDensity(int) with the
// value of DisplayMetrics.densityDpi.
verifySetTargetDensityOuter((dr, density) -> {
DisplayMetrics dm = new DisplayMetrics();
dm.densityDpi = density;
dr.setTargetDensity(dm);
});
}
@Test
public void testPreloadDensity() throws XmlPullParserException, IOException {
final Resources res = mResources;
final int densityDpi = res.getConfiguration().densityDpi;
try {
verifyPreloadDensityInner(res, DENSITY_IMAGES[0], DENSITY_VALUES,
DENSITY_GOLDEN_IMAGES[0]);
} finally {
DrawableTestUtils.setResourcesDensity(res, densityDpi);
}
}
private void verifyPreloadDensityInner(Resources res, int sourceResId, int[] densities,
int[] goldenResIds) throws XmlPullParserException, IOException {
// Capture initial state at preload density.
final int preloadDensityDpi = densities[0];
final NinePatchDrawable preloadedDrawable = preloadedDrawable(res,
densities[0], sourceResId);
final ConstantState preloadedConstantState = preloadedDrawable.getConstantState();
final int origWidth = preloadedDrawable.getIntrinsicWidth();
final int origHeight = preloadedDrawable.getIntrinsicHeight();
final Rect origPadding = new Rect();
preloadedDrawable.getPadding(origPadding);
compareOrSave(preloadedDrawable, preloadDensityDpi, sourceResId, goldenResIds[0]);
for (int i = 1; i < densities.length; i++) {
final int scaledDensityDpi = densities[i];
final float scale = scaledDensityDpi / (float) preloadDensityDpi;
DrawableTestUtils.setResourcesDensity(res, scaledDensityDpi);
final NinePatchDrawable scaledDrawable =
(NinePatchDrawable) preloadedConstantState.newDrawable(res);
assertEquals(Math.round(origWidth * scale), scaledDrawable.getIntrinsicWidth());
assertEquals(Math.round(origHeight * scale), scaledDrawable.getIntrinsicHeight());
// Padding is truncated.
final Rect tempPadding = new Rect();
assertTrue(scaledDrawable.getPadding(tempPadding));
assertEquals((int) (origPadding.left * scale), tempPadding.left);
assertEquals((int) (origPadding.top * scale), tempPadding.top);
assertEquals((int) (origPadding.right * scale), tempPadding.right);
assertEquals((int) (origPadding.bottom * scale), tempPadding.bottom);
compareOrSave(scaledDrawable, scaledDensityDpi, sourceResId, goldenResIds[i]);
// Ensure theme density is applied correctly. Unlike most
// drawables, we don't have any loss of accuracy because density
// changes are re-computed from the source every time.
DrawableTestUtils.setResourcesDensity(res, preloadDensityDpi);
final Theme t = res.newTheme();
scaledDrawable.applyTheme(t);
assertEquals(origWidth, scaledDrawable.getIntrinsicWidth());
assertEquals(origHeight, scaledDrawable.getIntrinsicHeight());
assertTrue(scaledDrawable.getPadding(tempPadding));
assertEquals(origPadding, tempPadding);
}
}
private static NinePatchDrawable preloadedDrawable(Resources res, int densityDpi, int sourceResId)
throws XmlPullParserException, IOException {
DrawableTestUtils.setResourcesDensity(res, densityDpi);
final XmlResourceParser parser = DrawableTestUtils.getResourceParser(res, sourceResId);
final NinePatchDrawable preloadedDrawable = new NinePatchDrawable(null);
preloadedDrawable.inflate(res, parser, Xml.asAttributeSet(parser));
return preloadedDrawable;
}
@Test
public void testOutlinePreloadDensity() throws XmlPullParserException, IOException {
final Resources res = mResources;
final int densityDpi = res.getConfiguration().densityDpi;
try {
verifyOutlinePreloadDensityInner(res);
} finally {
DrawableTestUtils.setResourcesDensity(res, densityDpi);
}
}
private static void verifyOutlinePreloadDensityInner(Resources res)
throws XmlPullParserException, IOException {
// Capture initial state at preload density.
final int preloadDensityDpi = DENSITY_VALUES[0];
final NinePatchDrawable preloadedDrawable = preloadedDrawable(res, preloadDensityDpi,
R.drawable.nine_patch_odd_insets);
final ConstantState preloadedConstantState = preloadedDrawable.getConstantState();
final int bound = 40;
final int expectedInset = 5;
preloadedDrawable.setBounds(0, 0, bound, bound);
final Outline origOutline = new Outline();
preloadedDrawable.getOutline(origOutline);
final Rect origOutlineRect = new Rect();
origOutline.getRect(origOutlineRect);
assertEquals(new Rect(expectedInset, expectedInset, bound - expectedInset,
bound - expectedInset), origOutlineRect);
final float origOutlineRadius = origOutline.getRadius();
float expectedRadius = 6.8f;
assertEquals(expectedRadius, origOutlineRadius, 0.1f);
for (int i = 1; i < DENSITY_VALUES.length; i++) {
final int scaledDensityDpi = DENSITY_VALUES[i];
final float scale = scaledDensityDpi / (float) preloadDensityDpi;
DrawableTestUtils.setResourcesDensity(res, scaledDensityDpi);
final NinePatchDrawable scaledDrawable =
(NinePatchDrawable) preloadedConstantState.newDrawable(res);
int scaledBound = (int) (bound * scale);
scaledDrawable.setBounds(0, 0, scaledBound, scaledBound);
final Outline tempOutline = new Outline();
scaledDrawable.getOutline(tempOutline);
final Rect tempOutlineRect = new Rect();
assertTrue(tempOutline.getRect(tempOutlineRect));
assertEquals((int) Math.ceil(origOutlineRect.left * scale), tempOutlineRect.left);
assertEquals((int) Math.ceil(origOutlineRect.top * scale), tempOutlineRect.top);
assertEquals((int) Math.floor(origOutlineRect.right * scale), tempOutlineRect.right);
assertEquals((int) Math.floor(origOutlineRect.bottom * scale), tempOutlineRect.bottom);
assertEquals(origOutlineRadius * scale, tempOutline.getRadius(), 0.1f);
}
}
private void verifyColorFillRect(Bitmap bmp, int x, int y, int w, int h, int color) {
for (int i = x; i < x + w; i++) {
for (int j = y; j < y + h; j++) {
assertEquals(color, bmp.getPixel(i, j));
}
}
}
private NinePatchDrawable getNinePatchDrawable(int resId) {
// jump through hoops to avoid scaling the tiny ninepatch, which would skew the results
// depending on device density
Bitmap bitmap = getBitmapUnscaled(resId);
NinePatch np = new NinePatch(bitmap, bitmap.getNinePatchChunk(), null);
return new NinePatchDrawable(mResources, np);
}
private Bitmap getBitmapUnscaled(int resId) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inDensity = opts.inTargetDensity = mResources.getDisplayMetrics().densityDpi;
Bitmap bitmap = BitmapFactory.decodeResource(mResources, resId, opts);
return bitmap;
}
private void compareOrSave(Drawable dr, int densityDpi, int sourceResId, int goldenResId) {
final int width = dr.getIntrinsicWidth();
final int height = dr.getIntrinsicHeight();
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setDensity(0);
final Canvas canvas = new Canvas(bitmap);
dr.setBounds(0, 0, width, height);
dr.draw(canvas);
if (DBG_DUMP_PNG) {
saveGoldenImage(bitmap, sourceResId, densityDpi);
} else {
final Bitmap golden = BitmapFactory.decodeResource(mResources, goldenResId);
DrawableTestUtils.compareImages(densityDpi + " dpi", golden, bitmap,
PIXEL_ERROR_THRESHOLD, PIXEL_ERROR_COUNT_THRESHOLD, 0 /* tolerance */);
}
}
private void saveGoldenImage(Bitmap bitmap, int sourceResId, int densityDpi) {
// Save the image to the disk.
FileOutputStream out = null;
try {
final String outputFolder = "/sdcard/temp/";
final File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdir();
}
final String sourceFilename = new File(mResources.getString(sourceResId)).getName();
final String sourceTitle = sourceFilename.substring(0, sourceFilename.lastIndexOf("."));
final String outputTitle = sourceTitle + "_golden_" + densityDpi;
final String outputFilename = outputFolder + outputTitle + ".png";
final File outputFile = new File(outputFilename);
if (!outputFile.exists()) {
outputFile.createNewFile();
}
out = new FileOutputStream(outputFile, false);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}