LayoutLib: Fix gradient rendering.

- fully support canvas transform
- fully support shader local transform
- fix repeat/mirror issue in the negative values.

Change-Id: Ib2aa7ade1c2702da4364cbda9a5a3ae72c1d3174
This commit is contained in:
Xavier Ducrohet
2010-12-14 14:40:41 -08:00
parent f1416e4dff
commit d9c64369cf
6 changed files with 166 additions and 64 deletions

View File

@ -119,20 +119,23 @@ public abstract class Gradient_Delegate extends Shader_Delegate {
pos = 0.f; pos = 0.f;
break; break;
case REPEAT: case REPEAT:
// remove the integer part to stay in the [0,1] range // remove the integer part to stay in the [0,1] range.
// careful: this is a negative value, so use ceil instead of floor // we also need to invert the value from [-1,0] to [0, 1]
pos = pos - (float)Math.ceil(pos); pos = pos - (float)Math.floor(pos);
break; break;
case MIRROR: case MIRROR:
// this is the same as the positive side, just make the value positive
// first.
pos = Math.abs(pos);
// get the integer and the decimal part // get the integer and the decimal part
// careful: this is a negative value, so use ceil instead of floor int intPart = (int)Math.floor(pos);
int intPart = (int)Math.ceil(pos);
pos = pos - intPart; pos = pos - intPart;
// 0 -> -1 : mirrored order // 0 -> 1 : normal order
// -1 -> -2: normal order // 1 -> 2: mirrored
// etc.. // etc..
// this means if the intpart is even we invert // this means if the intpart is odd we invert
if ((intPart % 2) == 0) { if ((intPart % 2) == 1) {
pos = 1.f - pos; pos = 1.f - pos;
} }
break; break;

View File

@ -16,11 +16,14 @@
package android.graphics; package android.graphics;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Shader.TileMode; import android.graphics.Shader.TileMode;
import java.awt.Paint; import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/** /**
* Delegate implementing the native methods of android.graphics.LinearGradient * Delegate implementing the native methods of android.graphics.LinearGradient
@ -115,7 +118,7 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
* {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
* modes. * modes.
*/ */
private static class LinearGradientPaint extends GradientPaint { private class LinearGradientPaint extends GradientPaint {
private final float mX0; private final float mX0;
private final float mY0; private final float mY0;
@ -126,11 +129,11 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[], public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
float positions[], TileMode tile) { float positions[], TileMode tile) {
super(colors, positions, tile); super(colors, positions, tile);
mX0 = x0; mX0 = x0;
mY0 = y0; mY0 = y0;
mDx = x1 - x0; mDx = x1 - x0;
mDy = y1 - y0; mDy = y1 - y0;
mDSize2 = mDx * mDx + mDy * mDy; mDSize2 = mDx * mDx + mDy * mDy;
} }
public java.awt.PaintContext createContext( public java.awt.PaintContext createContext(
@ -140,16 +143,37 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
java.awt.geom.AffineTransform xform, java.awt.geom.AffineTransform xform,
java.awt.RenderingHints hints) { java.awt.RenderingHints hints) {
precomputeGradientColors(); precomputeGradientColors();
return new LinearGradientPaintContext(colorModel);
AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
canvasMatrix = new AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
localMatrix = new AffineTransform();
}
return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
} }
private class LinearGradientPaintContext implements java.awt.PaintContext { private class LinearGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel; private final java.awt.image.ColorModel mColorModel;
public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) { public LinearGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel; mColorModel = colorModel;
// FIXME: so far all this is always the same rect gotten in getRaster with an identity matrix?
} }
public void dispose() { public void dispose() {
@ -165,31 +189,22 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
int[] data = new int[w*h]; int[] data = new int[w*h];
if (mDx == 0) { // vertical gradient int index = 0;
// compute first column and copy to all other columns float[] pt1 = new float[2];
int index = 0; float[] pt2 = new float[2];
for (int iy = 0 ; iy < h ; iy++) { for (int iy = 0 ; iy < h ; iy++) {
int color = getColor(iy + y, mY0, mDy);
for (int ix = 0 ; ix < w ; ix++) {
data[index++] = color;
}
}
} else if (mDy == 0) { // horizontal
// compute first line in a tmp array and copy to all lines
int[] line = new int[w];
for (int ix = 0 ; ix < w ; ix++) { for (int ix = 0 ; ix < w ; ix++) {
line[ix] = getColor(ix + x, mX0, mDx); // handle the canvas transform
} pt1[0] = x + ix;
pt1[1] = y + iy;
mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
for (int iy = 0 ; iy < h ; iy++) { // handle the local matrix.
System.arraycopy(line, 0, data, iy*w, line.length); pt1[0] = pt2[0];
} pt1[1] = pt2[1];
} else { mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
int index = 0;
for (int iy = 0 ; iy < h ; iy++) { data[index++] = getColor(pt2[0], pt2[1]);
for (int ix = 0 ; ix < w ; ix++) {
data[index++] = getColor(ix + x, iy + y);
}
} }
} }
@ -199,13 +214,6 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
} }
} }
/** Returns a color for the easy vertical/horizontal mode */
private int getColor(float absPos, float refPos, float refSize) {
float pos = (absPos - refPos) / refSize;
return getGradientColor(pos);
}
/** /**
* Returns a color for an arbitrary point. * Returns a color for an arbitrary point.
*/ */

View File

@ -471,7 +471,7 @@ public final class Matrix_Delegate {
return false; return false;
} }
d.preTransform(getRotate(degrees, px, py)); d.postTransform(getRotate(degrees, px, py));
return true; return true;
} }

View File

@ -16,11 +16,14 @@
package android.graphics; package android.graphics;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Shader.TileMode; import android.graphics.Shader.TileMode;
import java.awt.Paint; import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/** /**
* Delegate implementing the native methods of android.graphics.RadialGradient * Delegate implementing the native methods of android.graphics.RadialGradient
@ -105,18 +108,17 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
private RadialGradient_Delegate(float x, float y, float radius, int colors[], float positions[], private RadialGradient_Delegate(float x, float y, float radius, int colors[], float positions[],
TileMode tile) { TileMode tile) {
super(colors, positions); super(colors, positions);
mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile); mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
} }
private static class RadialGradientPaint extends GradientPaint { private class RadialGradientPaint extends GradientPaint {
private final float mX; private final float mX;
private final float mY; private final float mY;
private final float mRadius; private final float mRadius;
public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, public RadialGradientPaint(float x, float y, float radius,
TileMode mode) { int[] colors, float[] positions, TileMode mode) {
super(colors, positions, mode); super(colors, positions, mode);
mX = x; mX = x;
mY = y; mY = y;
@ -130,14 +132,36 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
java.awt.geom.AffineTransform xform, java.awt.geom.AffineTransform xform,
java.awt.RenderingHints hints) { java.awt.RenderingHints hints) {
precomputeGradientColors(); precomputeGradientColors();
return new RadialGradientPaintContext(colorModel);
AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
canvasMatrix = new AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
localMatrix = new AffineTransform();
}
return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
} }
private class RadialGradientPaintContext implements java.awt.PaintContext { private class RadialGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel; private final java.awt.image.ColorModel mColorModel;
public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) { public RadialGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel; mColorModel = colorModel;
} }
@ -157,10 +181,22 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
// compute distance from each point to the center, and figure out the distance from // compute distance from each point to the center, and figure out the distance from
// it. // it.
int index = 0; int index = 0;
float[] pt1 = new float[2];
float[] pt2 = new float[2];
for (int iy = 0 ; iy < h ; iy++) { for (int iy = 0 ; iy < h ; iy++) {
for (int ix = 0 ; ix < w ; ix++) { for (int ix = 0 ; ix < w ; ix++) {
float _x = x + ix - mX; // handle the canvas transform
float _y = y + iy - mY; pt1[0] = x + ix;
pt1[1] = y + iy;
mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
// handle the local matrix
pt1[0] = pt2[0] - mX;
pt1[1] = pt2[1] - mY;
mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
float _x = pt2[0];
float _y = pt2[1];
float distance = (float) Math.sqrt(_x * _x + _y * _y); float distance = (float) Math.sqrt(_x * _x + _y * _y);
data[index++] = getGradientColor(distance / mRadius); data[index++] = getGradientColor(distance / mRadius);

View File

@ -18,6 +18,8 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.geom.AffineTransform;
/** /**
* Delegate implementing the native methods of android.graphics.Shader * Delegate implementing the native methods of android.graphics.Shader
* *
@ -109,4 +111,19 @@ public abstract class Shader_Delegate {
// ---- Private delegate/helper methods ---- // ---- Private delegate/helper methods ----
protected AffineTransform getLocalMatrix() {
Matrix_Delegate localMatrixDelegate = null;
if (mLocalMatrix > 0) {
localMatrixDelegate = Matrix_Delegate.getDelegate(mLocalMatrix);
if (localMatrixDelegate == null) {
assert false;
return new AffineTransform();
}
return localMatrixDelegate.getAffineTransform();
}
return new AffineTransform();
}
} }

View File

@ -16,9 +16,12 @@
package android.graphics; package android.graphics;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Paint; import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/** /**
* Delegate implementing the native methods of android.graphics.SweepGradient * Delegate implementing the native methods of android.graphics.SweepGradient
@ -90,16 +93,16 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
private SweepGradient_Delegate(float cx, float cy, private SweepGradient_Delegate(float cx, float cy,
int colors[], float positions[]) { int colors[], float positions[]) {
super(colors, positions); super(colors, positions);
mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions); mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
} }
private static class SweepGradientPaint extends GradientPaint { private class SweepGradientPaint extends GradientPaint {
private final float mCx; private final float mCx;
private final float mCy; private final float mCy;
public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) { public SweepGradientPaint(float cx, float cy, int[] colors,
float[] positions) {
super(colors, positions, null /*tileMode*/); super(colors, positions, null /*tileMode*/);
mCx = cx; mCx = cx;
mCy = cy; mCy = cy;
@ -112,14 +115,36 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
java.awt.geom.AffineTransform xform, java.awt.geom.AffineTransform xform,
java.awt.RenderingHints hints) { java.awt.RenderingHints hints) {
precomputeGradientColors(); precomputeGradientColors();
return new SweepGradientPaintContext(colorModel);
AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
canvasMatrix = new AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
localMatrix = new AffineTransform();
}
return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
} }
private class SweepGradientPaintContext implements java.awt.PaintContext { private class SweepGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel; private final java.awt.image.ColorModel mColorModel;
public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) { public SweepGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel; mColorModel = colorModel;
} }
@ -139,10 +164,23 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
// compute angle from each point to the center, and figure out the distance from // compute angle from each point to the center, and figure out the distance from
// it. // it.
int index = 0; int index = 0;
float[] pt1 = new float[2];
float[] pt2 = new float[2];
for (int iy = 0 ; iy < h ; iy++) { for (int iy = 0 ; iy < h ; iy++) {
for (int ix = 0 ; ix < w ; ix++) { for (int ix = 0 ; ix < w ; ix++) {
float dx = x + ix - mCx; // handle the canvas transform
float dy = y + iy - mCy; pt1[0] = x + ix;
pt1[1] = y + iy;
mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
// handle the local matrix
pt1[0] = pt2[0] - mCx;
pt1[1] = pt2[1] - mCy;
mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
float dx = pt2[0];
float dy = pt2[1];
float angle; float angle;
if (dx == 0) { if (dx == 0) {
angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2); angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);