LayoutLib: Misc rendering fixes.
- always set up the stroke. Paint may not have the proper
style when drawing lines. stroke should still be setup.
- Fixed vertical linear gradient. Old code generated
a gradient ratio of NaN
- Fixed alpha rendering when using shaders. In that
case the alpha channel from the paint color should be
used in conjunction with the shader.
- Fixed miter limit. Java expects the value to be multiplied
by the stroke width
- Fixed support for drawing ALPHA_8 bitmaps. Java2D doesn't
have bitmaps with only alpha channels, so we keep using
ARGB bitmaps but when drawing them into a bitmap we erase
the color information.
Change-Id: I4f04341fc843e3f7dadd1fdbf709b11a4f1e24b9
diff --git a/bridge/src/android/graphics/AvoidXfermode_Delegate.java b/bridge/src/android/graphics/AvoidXfermode_Delegate.java
index 9c49bb8..190eb37 100644
--- a/bridge/src/android/graphics/AvoidXfermode_Delegate.java
+++ b/bridge/src/android/graphics/AvoidXfermode_Delegate.java
@@ -42,7 +42,7 @@
// ---- Public Helper methods ----
@Override
- public Composite getComposite() {
+ public Composite getComposite(int alpha) {
// FIXME
return null;
}
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
index 9d60a43..b5f5005 100644
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -54,9 +54,11 @@
// ---- delegate helper data ----
// ---- delegate data ----
+ private final Config mConfig;
private BufferedImage mImage;
private boolean mHasAlpha = true;
+
// ---- Public Helper methods ----
/**
@@ -86,7 +88,7 @@
public static Bitmap createBitmap(File input, boolean isMutable, ResourceDensity density)
throws IOException {
// create a delegate with the content of the file.
- Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
+ Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
return createBitmap(delegate, isMutable, density.getDpi());
}
@@ -104,7 +106,7 @@
public static Bitmap createBitmap(InputStream input, boolean isMutable, ResourceDensity density)
throws IOException {
// create a delegate with the content of the stream.
- Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input));
+ Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
return createBitmap(delegate, isMutable, density.getDpi());
}
@@ -122,7 +124,7 @@
public static Bitmap createBitmap(BufferedImage image, boolean isMutable,
ResourceDensity density) throws IOException {
// create a delegate with the given image.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image);
+ Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
return createBitmap(delegate, isMutable, density.getDpi());
}
@@ -163,6 +165,15 @@
return mImage;
}
+ /**
+ * Returns the Android bitmap config. Note that this not the config of the underlying
+ * Java2D bitmap.
+ */
+ public Config getConfig() {
+ return mConfig;
+ }
+
+
// ---- native methods ----
/*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
@@ -175,7 +186,7 @@
// FIXME fill the bitmap!
// create a delegate with the content of the stream.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image);
+ Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.sConfigs[nativeConfig]);
return createBitmap(delegate, mutable, Bitmap.getDefaultDensity());
}
@@ -211,10 +222,7 @@
Graphics2D g = image.createGraphics();
try {
- if (delegate.mHasAlpha == false) {
- color |= color & 0xFF000000;
- }
- g.setColor(new java.awt.Color(color));
+ g.setColor(new java.awt.Color(color, delegate.mHasAlpha));
g.fillRect(0, 0, image.getWidth(), image.getHeight());
} finally {
@@ -256,7 +264,14 @@
}
/*package*/ static int nativeConfig(int nativeBitmap) {
- return Config.ARGB_8888.nativeInt;
+ // get the delegate from the native int.
+ Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
+ if (delegate == null) {
+ assert false;
+ return 0;
+ }
+
+ return delegate.mConfig.nativeInt;
}
/*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
@@ -355,8 +370,9 @@
// ---- Private delegate/helper methods ----
- private Bitmap_Delegate(BufferedImage image) {
+ private Bitmap_Delegate(BufferedImage image, Config config) {
mImage = image;
+ mConfig = config;
}
private static Bitmap createBitmap(Bitmap_Delegate delegate, boolean isMutable, int density) {
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index ba4bed6..2720b61 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -528,7 +528,8 @@
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
- Composite composite = PorterDuffXfermode_Delegate.getComposite(mode);
+ Composite composite = PorterDuffXfermode_Delegate.getComposite(
+ PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
if (composite != null) {
graphics.setComposite(composite);
}
@@ -759,7 +760,7 @@
float right = left + image.getWidth();
float bottom = top + image.getHeight();
- drawBitmap(nativeCanvas, image, nativePaintOrZero,
+ drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
(int)left, (int)top, (int)right, (int)bottom);
}
@@ -779,11 +780,11 @@
BufferedImage image = bitmapDelegate.getImage();
if (src == null) {
- drawBitmap(nativeCanvas, image, nativePaintOrZero,
+ drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
(int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
} else {
- drawBitmap(nativeCanvas, image, nativePaintOrZero,
+ drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
src.left, src.top, src.width(), src.height(),
(int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
}
@@ -804,11 +805,11 @@
BufferedImage image = bitmapDelegate.getImage();
if (src == null) {
- drawBitmap(nativeCanvas, image, nativePaintOrZero,
+ drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
dst.left, dst.top, dst.right, dst.bottom);
} else {
- drawBitmap(nativeCanvas, image, nativePaintOrZero,
+ drawBitmap(nativeCanvas, image, bitmapDelegate.getConfig(), nativePaintOrZero,
src.left, src.top, src.width(), src.height(),
dst.left, dst.top, dst.right, dst.bottom);
}
@@ -1042,7 +1043,7 @@
/**
* Executes a {@link Drawable} with a given canvas and paint.
*/
- private static void draw(int nCanvas, int nPaint, Drawable runnable) {
+ private static void draw(int nCanvas, int nPaint, Drawable drawable) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
if (canvasDelegate == null) {
@@ -1060,7 +1061,7 @@
Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
try {
- runnable.draw(g, paintDelegate);
+ drawable.draw(g, paintDelegate);
} finally {
// dispose Graphics2D object
g.dispose();
@@ -1135,9 +1136,8 @@
RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
- boolean useColorPaint = true;
-
// get the shader first, as it'll replace the color if it can be used it.
+ boolean customShader = false;
int nativeShader = paint.getShader();
if (nativeShader > 0) {
Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader);
@@ -1148,7 +1148,7 @@
assert shaderPaint != null;
if (shaderPaint != null) {
g.setPaint(shaderPaint);
- useColorPaint = false;
+ customShader = true;
}
} else {
Bridge.getLog().fidelityWarning(null,
@@ -1158,56 +1158,57 @@
}
}
- if (useColorPaint) {
+ // if no shader, use the paint color
+ if (customShader == false) {
g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
}
- int style = paint.getStyle();
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
-
- boolean customStroke = false;
-
- int pathEffect = paint.getPathEffect();
- if (pathEffect > 0) {
- PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect);
- assert effectDelegate != null;
- if (effectDelegate != null) {
- if (effectDelegate.isSupported()) {
- Stroke stroke = effectDelegate.getStroke(paint);
- assert stroke != null;
- if (stroke != null) {
- g.setStroke(stroke);
- customStroke = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(null,
- effectDelegate.getSupportMessage(),
- null);
+ boolean customStroke = false;
+ int pathEffect = paint.getPathEffect();
+ if (pathEffect > 0) {
+ PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(pathEffect);
+ assert effectDelegate != null;
+ if (effectDelegate != null) {
+ if (effectDelegate.isSupported()) {
+ Stroke stroke = effectDelegate.getStroke(paint);
+ assert stroke != null;
+ if (stroke != null) {
+ g.setStroke(stroke);
+ customStroke = true;
}
+ } else {
+ Bridge.getLog().fidelityWarning(null,
+ effectDelegate.getSupportMessage(),
+ null);
}
}
-
- // if no custom stroke as been set, set the default one.
- if (customStroke == false) {
- g.setStroke(new BasicStroke(
- paint.getStrokeWidth(),
- paint.getJavaCap(),
- paint.getJavaJoin(),
- paint.getStrokeMiter()));
- }
}
+ // if no custom stroke as been set, set the default one.
+ if (customStroke == false) {
+ g.setStroke(new BasicStroke(
+ paint.getStrokeWidth(),
+ paint.getJavaCap(),
+ paint.getJavaJoin(),
+ paint.getJavaStrokeMiter()));
+ }
+
+ // the alpha for the composite. Always opaque if the normal paint color is used since
+ // it contains the alpha
+ int alpha = customShader ? paint.getAlpha() : 0xFF;
+
+ boolean customXfermode = false;
int xfermode = paint.getXfermode();
if (xfermode > 0) {
Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
assert xfermodeDelegate != null;
if (xfermodeDelegate != null) {
if (xfermodeDelegate.isSupported()) {
- Composite composite = xfermodeDelegate.getComposite();
+ Composite composite = xfermodeDelegate.getComposite(alpha);
assert composite != null;
if (composite != null) {
g.setComposite(composite);
+ customXfermode = true;
}
} else {
Bridge.getLog().fidelityWarning(null,
@@ -1217,12 +1218,21 @@
}
}
+ // if there was no custom xfermode, but we have alpha (due to a shader and a non
+ // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+ // that will handle the alpha.
+ if (customXfermode == false && alpha != 0xFF) {
+ g.setComposite(PorterDuffXfermode_Delegate.getComposite(
+ PorterDuff.Mode.SRC_OVER, alpha));
+ }
+
return g;
}
private static void drawBitmap(
int nativeCanvas,
BufferedImage image,
+ Bitmap.Config mBitmapConfig,
int nativePaintOrZero,
int sleft, int stop, int sright, int sbottom,
int dleft, int dtop, int dright, int dbottom) {
@@ -1243,7 +1253,7 @@
}
}
- drawBitmap(canvasDelegate, image, paintDelegate,
+ drawBitmap(canvasDelegate, image, mBitmapConfig, paintDelegate,
sleft, stop, sright, sbottom,
dleft, dtop, dright, dbottom);
}
@@ -1251,6 +1261,7 @@
private static void drawBitmap(
Canvas_Delegate canvasDelegate,
BufferedImage image,
+ Bitmap.Config mBitmapConfig,
Paint_Delegate paintDelegate,
int sleft, int stop, int sright, int sbottom,
int dleft, int dtop, int dright, int dbottom) {
@@ -1263,6 +1274,21 @@
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
}
+ // if the bitmap config is alpha_8, then we erase all color value from it
+ // before drawing it.
+ if (mBitmapConfig == Bitmap.Config.ALPHA_8) {
+ int w = image.getWidth();
+ int h = image.getHeight();
+ int[] argb = new int[w*h];
+ image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
+
+ final int length = argb.length;
+ for (int i = 0 ; i < length; i++) {
+ argb[i] &= 0xFF000000;
+ }
+ image.setRGB(0, 0, w, h, argb, 0, w);
+ }
+
g.drawImage(image, dleft, dtop, dright, dbottom,
sleft, stop, sright, sbottom, null);
} finally {
diff --git a/bridge/src/android/graphics/DashPathEffect_Delegate.java b/bridge/src/android/graphics/DashPathEffect_Delegate.java
index 1b539ce..5a704a7 100644
--- a/bridge/src/android/graphics/DashPathEffect_Delegate.java
+++ b/bridge/src/android/graphics/DashPathEffect_Delegate.java
@@ -53,7 +53,7 @@
paint.getStrokeWidth(),
paint.getJavaCap(),
paint.getJavaJoin(),
- paint.getStrokeMiter(),
+ paint.getJavaStrokeMiter(),
mIntervals,
mPhase);
}
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
index baa0a6a..9fb48c8 100644
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -207,10 +207,17 @@
* Returns a color for an arbitrary point.
*/
private int getColor(float x, float y) {
- // find the x position on the gradient vector.
- float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
- // from it get the position relative to the vector
- float pos = (float) ((_x - mX0) / mDx);
+ float pos;
+ if (mDx == 0) {
+ pos = (y - mY0) / mDy;
+ } else if (mDy == 0) {
+ pos = (x - mX0) / mDx;
+ } else {
+ // find the x position on the gradient vector.
+ float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
+ // from it get the position relative to the vector
+ pos = (_x - mX0) / mDx;
+ }
return getGradientColor(pos);
}
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
index fa26bcf..0a597ca 100644
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ b/bridge/src/android/graphics/Paint_Delegate.java
@@ -114,6 +114,10 @@
return mColor;
}
+ public int getAlpha() {
+ return mColor >>> 24;
+ }
+
public int getTextAlign() {
return mTextAlign;
}
@@ -122,8 +126,11 @@
return mStrokeWidth;
}
- public float getStrokeMiter() {
- return mStrokeMiter;
+ /**
+ * returns the value of stroke miter needed by the java api.
+ */
+ public float getJavaStrokeMiter() {
+ return mStrokeMiter * mStrokeWidth;
}
public int getJavaCap() {
@@ -256,7 +263,7 @@
return 0;
}
- return delegate.mColor >>> 24;
+ return delegate.getAlpha();
}
/*package*/ static void setAlpha(Paint thisPaint, int a) {
@@ -860,9 +867,9 @@
private void reset() {
mFlags = Paint.DEFAULT_PAINT_FLAGS;
mColor = 0;
- mStyle = 0;
- mCap = 0;
- mJoin = 0;
+ mStyle = Paint.Style.FILL.nativeInt;
+ mCap = Paint.Cap.BUTT.nativeInt;
+ mJoin = Paint.Join.MITER.nativeInt;
mTextAlign = 0;
mTypeface = Typeface.sDefaults[0].native_instance;
mStrokeWidth = 1.f;
diff --git a/bridge/src/android/graphics/PixelXorXfermode_Delegate.java b/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
index e8f29ad..516a2b9 100644
--- a/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
+++ b/bridge/src/android/graphics/PixelXorXfermode_Delegate.java
@@ -42,7 +42,7 @@
// ---- Public Helper methods ----
@Override
- public Composite getComposite() {
+ public Composite getComposite(int alpha) {
// FIXME
return null;
}
diff --git a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index 3e9a346..097dfce 100644
--- a/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -46,8 +46,8 @@
// ---- Public Helper methods ----
@Override
- public Composite getComposite() {
- return getComposite(mMode);
+ public Composite getComposite(int alpha) {
+ return getComposite(getPorterDuffMode(mMode), alpha);
}
@Override
@@ -61,23 +61,35 @@
return null;
}
- public static Composite getComposite(int mode) {
- PorterDuff.Mode m = getMode(mode);
- switch (m) {
+ public static PorterDuff.Mode getPorterDuffMode(int mode) {
+ for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
+ if (m.nativeInt == mode) {
+ return m;
+ }
+ }
+
+ Bridge.getLog().error(null, String.format("Unknown PorterDuff.Mode: %d", mode));
+ assert false;
+ return PorterDuff.Mode.SRC_OVER;
+ }
+
+ public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
+ float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
+ switch (mode) {
case CLEAR:
- return AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
case DARKEN:
break;
case DST:
- return AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
case DST_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
case DST_IN:
- return AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
case DST_OUT:
- return AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
case DST_OVER:
- return AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
case LIGHTEN:
break;
case MULTIPLY:
@@ -85,24 +97,24 @@
case SCREEN:
break;
case SRC:
- return AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
case SRC_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
case SRC_IN:
- return AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
case SRC_OUT:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
case SRC_OVER:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
case XOR:
- return AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/);
+ return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
}
Bridge.getLog().fidelityWarning(null,
- String.format("Unsupported PorterDuff Mode: %s", m.name()),
+ String.format("Unsupported PorterDuff Mode: %s", mode.name()),
null);
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
}
// ---- native methods ----
@@ -117,16 +129,4 @@
private PorterDuffXfermode_Delegate(int mode) {
mMode = mode;
}
-
- private static PorterDuff.Mode getMode(int mode) {
- for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
- if (m.nativeInt == mode) {
- return m;
- }
- }
-
- Bridge.getLog().error(null, String.format("Unknown PorterDuff.Mode: %d", mode));
- assert false;
- return PorterDuff.Mode.SRC_OVER;
- }
}
diff --git a/bridge/src/android/graphics/Xfermode_Delegate.java b/bridge/src/android/graphics/Xfermode_Delegate.java
index e783fd1..312318a 100644
--- a/bridge/src/android/graphics/Xfermode_Delegate.java
+++ b/bridge/src/android/graphics/Xfermode_Delegate.java
@@ -51,7 +51,7 @@
return sManager.getDelegate(native_instance);
}
- public abstract Composite getComposite();
+ public abstract Composite getComposite(int alpha);
public abstract boolean isSupported();
public abstract String getSupportMessage();