Merge "LayoutLib: support for Path and BitmapShader using delegates."

This commit is contained in:
Xavier Ducrohet
2010-12-21 10:47:48 -08:00
committed by Android (Google) Code Review
31 changed files with 1926 additions and 816 deletions

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.android.test.hwui.PathsActivity.PathsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</com.android.test.hwui.PathsActivity.PathsView>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<com.android.test.hwui.ShadersActivity.ShadersView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</com.android.test.hwui.ShadersActivity.ShadersView>

View File

@@ -37,7 +37,7 @@ public class PathsActivity extends Activity {
setContentView(view);
}
static class PathsView extends View {
public static class PathsView extends View {
private final Bitmap mBitmap1;
private final Paint mSmallPaint;
private final Paint mMediumPaint;
@@ -50,7 +50,7 @@ public class PathsActivity extends Activity {
private final float mOffset;
private final Paint mLinePaint;
PathsView(Context c) {
public PathsView(Context c) {
super(c);
mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
@@ -60,7 +60,7 @@ public class PathsActivity extends Activity {
mSmallPaint.setColor(0xffff0000);
mSmallPaint.setStrokeWidth(1.0f);
mSmallPaint.setStyle(Paint.Style.STROKE);
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(0xffff00ff);
@@ -112,7 +112,7 @@ public class PathsActivity extends Activity {
canvas.save();
canvas.translate(200.0f, 60.0f);
canvas.drawPath(mPath, mSmallPaint);
canvas.translate(350.0f, 0.0f);
canvas.drawPath(mPath, mMediumPaint);
@@ -121,12 +121,12 @@ public class PathsActivity extends Activity {
canvas.drawPath(mPath, mLargePaint);
mLargePaint.setShader(null);
canvas.restore();
canvas.save();
canvas.translate(200.0f, 360.0f);
canvas.drawPath(mPath, mSmallPaint);
canvas.drawRect(mPathBounds, mBoundsPaint);
canvas.translate(350.0f, 0.0f);
canvas.drawBitmap(mBitmap, mPathBounds.left - mOffset * 1.5f,
mPathBounds.top - mOffset * 1.5f, null);

View File

@@ -39,7 +39,7 @@ public class ShadersActivity extends Activity {
setContentView(new ShadersView(this));
}
static class ShadersView extends View {
public static class ShadersView extends View {
private BitmapShader mRepeatShader;
private BitmapShader mTranslatedShader;
private BitmapShader mScaledShader;
@@ -53,7 +53,7 @@ public class ShadersActivity extends Activity {
private LinearGradient mVertGradient;
private Bitmap mTexture;
ShadersView(Context c) {
public ShadersView(Context c) {
super(c);
mTexture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
@@ -71,7 +71,7 @@ public class ShadersActivity extends Activity {
m1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
m1.postRotate(45, 0, 0);
mTranslatedShader.setLocalMatrix(m1);
mScaledShader = new BitmapShader(mTexture, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
Matrix m2 = new Matrix();
@@ -85,10 +85,10 @@ public class ShadersActivity extends Activity {
m3.postRotate(-90.0f);
m3.postTranslate(0.0f, mDrawHeight);
mHorGradient.setLocalMatrix(m3);
mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 1.5f, mDrawHeight,
Color.BLUE, Color.MAGENTA, Shader.TileMode.CLAMP);
mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
@@ -107,7 +107,7 @@ public class ShadersActivity extends Activity {
mPaint.setShader(mRepeatShader);
canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
canvas.translate(0.0f, 40.0f + mDrawHeight);
mPaint.setShader(mTranslatedShader);
canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
@@ -124,7 +124,7 @@ public class ShadersActivity extends Activity {
mPaint.setShader(mHorGradient);
canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
canvas.translate(0.0f, 40.0f + mDrawHeight);
mPaint.setShader(mDiagGradient);
canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
@@ -132,7 +132,7 @@ public class ShadersActivity extends Activity {
canvas.translate(0.0f, 40.0f + mDrawHeight);
mPaint.setShader(mVertGradient);
canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
canvas.restore();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Composite;
/**
* Delegate implementing the native methods of android.graphics.AvoidXfermode
*
* Through the layoutlib_create tool, the original native methods of AvoidXfermode have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original AvoidXfermode class.
*
* Because this extends {@link Xfermode_Delegate}, there's no need to use a
* {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
* {@link Xfermode_Delegate}.
*
*/
public class AvoidXfermode_Delegate extends Xfermode_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Composite getComposite() {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Avoid Xfermodes are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(int opColor, int tolerance, int nativeMode) {
AvoidXfermode_Delegate newDelegate = new AvoidXfermode_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -0,0 +1,242 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Shader.TileMode;
/**
* Delegate implementing the native methods of android.graphics.BitmapShader
*
* Through the layoutlib_create tool, the original native methods of BitmapShader have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original BitmapShader class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class BitmapShader_Delegate extends Shader_Delegate {
// ---- delegate data ----
private java.awt.Paint mJavaPaint;
// ---- Public Helper methods ----
@Override
public java.awt.Paint getJavaPaint() {
return mJavaPaint;
}
@Override
public boolean isSupported() {
return true;
}
@Override
public String getSupportMessage() {
// no message since isSupported returns true;
return null;
}
// ---- native methods ----
/*package*/ static int nativeCreate(int native_bitmap, int shaderTileModeX,
int shaderTileModeY) {
Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(native_bitmap);
assert bitmap != null;
if (bitmap == null) {
return 0;
}
BitmapShader_Delegate newDelegate = new BitmapShader_Delegate(
bitmap.getImage(),
Shader_Delegate.getTileMode(shaderTileModeX),
Shader_Delegate.getTileMode(shaderTileModeY));
return sManager.addDelegate(newDelegate);
}
/*package*/ static int nativePostCreate(int native_shader, int native_bitmap,
int shaderTileModeX, int shaderTileModeY) {
// pass, not needed.
return 0;
}
// ---- Private delegate/helper methods ----
private BitmapShader_Delegate(java.awt.image.BufferedImage image,
TileMode tileModeX, TileMode tileModeY) {
mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY);
}
private class BitmapShaderPaint implements java.awt.Paint {
private final java.awt.image.BufferedImage mImage;
private final TileMode mTileModeX;
private final TileMode mTileModeY;
BitmapShaderPaint(java.awt.image.BufferedImage image,
TileMode tileModeX, TileMode tileModeY) {
mImage = image;
mTileModeX = tileModeX;
mTileModeY = tileModeY;
}
public java.awt.PaintContext createContext(
java.awt.image.ColorModel colorModel,
java.awt.Rectangle deviceBounds,
java.awt.geom.Rectangle2D userBounds,
java.awt.geom.AffineTransform xform,
java.awt.RenderingHints hints) {
java.awt.geom.AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in BitmapShader", e);
canvasMatrix = new java.awt.geom.AffineTransform();
}
java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in BitmapShader", e);
localMatrix = new java.awt.geom.AffineTransform();
}
return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel);
}
private class BitmapShaderContext implements java.awt.PaintContext {
private final java.awt.geom.AffineTransform mCanvasMatrix;
private final java.awt.geom.AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel;
public BitmapShaderContext(
java.awt.geom.AffineTransform canvasMatrix,
java.awt.geom.AffineTransform localMatrix,
java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel;
}
public void dispose() {
}
public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
java.awt.image.BufferedImage.TYPE_INT_ARGB);
int[] data = new int[w*h];
int index = 0;
float[] pt1 = new float[2];
float[] pt2 = new float[2];
for (int iy = 0 ; iy < h ; iy++) {
for (int ix = 0 ; ix < w ; ix++) {
// handle the canvas transform
pt1[0] = x + ix;
pt1[1] = y + iy;
mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
// handle the local matrix.
pt1[0] = pt2[0];
pt1[1] = pt2[1];
mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
data[index++] = getColor(pt2[0], pt2[1]);
}
}
image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
return image.getRaster();
}
}
/**
* Returns a color for an arbitrary point.
*/
private int getColor(float fx, float fy) {
int x = getCoordinate(Math.round(fx), mImage.getWidth(), mTileModeX);
int y = getCoordinate(Math.round(fy), mImage.getHeight(), mTileModeY);
return mImage.getRGB(x, y);
}
private int getCoordinate(int i, int size, TileMode mode) {
if (i < 0) {
switch (mode) {
case CLAMP:
i = 0;
break;
case REPEAT:
i = size - 1 - (-i % size);
break;
case MIRROR:
// this is the same as the positive side, just make the value positive
// first.
i = -i;
int count = i / size;
i = i % size;
if ((count % 2) == 1) {
i = size - 1 - i;
}
break;
}
} else if (i >= size) {
switch (mode) {
case CLAMP:
i = size - 1;
break;
case REPEAT:
i = i % size;
break;
case MIRROR:
int count = i / size;
i = i % size;
if ((count % 2) == 1) {
i = size - 1 - i;
}
break;
}
}
return i;
}
public int getTransparency() {
return java.awt.Paint.TRANSLUCENT;
}
}
}

View File

@@ -45,7 +45,7 @@ import javax.imageio.ImageIO;
* @see DelegateManager
*
*/
public class Bitmap_Delegate {
public final class Bitmap_Delegate {
// ---- delegate manager ----
private static final DelegateManager<Bitmap_Delegate> sManager =

View File

@@ -23,12 +23,14 @@ import com.android.layoutlib.bridge.impl.GcSnapshot;
import android.graphics.Paint_Delegate.FontInfo;
import android.text.TextUtils;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.List;
@@ -47,7 +49,7 @@ import java.util.List;
* @see DelegateManager
*
*/
public class Canvas_Delegate {
public final class Canvas_Delegate {
// ---- delegate manager ----
private static final DelegateManager<Canvas_Delegate> sManager =
@@ -55,6 +57,10 @@ public class Canvas_Delegate {
// ---- delegate helper data ----
private interface Drawable {
void draw(Graphics2D graphics, Paint_Delegate paint);
}
// ---- delegate data ----
private BufferedImage mBufferedImage;
private GcSnapshot mSnapshot = new GcSnapshot();
@@ -403,9 +409,9 @@ public class Canvas_Delegate {
if (matrixDelegate.hasPerspective()) {
assert false;
Bridge.getLog().warning(null,
Bridge.getLog().fidelityWarning(null,
"android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
"supports affine transformations in the Layout Preview.");
"supports affine transformations in the Layout Preview.", null);
}
}
@@ -522,7 +528,10 @@ public class Canvas_Delegate {
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
setModeInGraphics(graphics, mode);
Composite composite = PorterDuffXfermode_Delegate.getComposite(mode);
if (composite != null) {
graphics.setComposite(composite);
}
graphics.fillRect(0, 0, canvasDelegate.mBufferedImage.getWidth(),
canvasDelegate.mBufferedImage.getHeight());
@@ -709,8 +718,28 @@ public class Canvas_Delegate {
/*package*/ static void native_drawPath(int nativeCanvas, int path,
int paint) {
// FIXME
throw new UnsupportedOperationException();
final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
if (pathDelegate == null) {
assert false;
return;
}
draw(nativeCanvas, paint, new Drawable() {
public void draw(Graphics2D graphics, Paint_Delegate paint) {
Shape shape = pathDelegate.getJavaShape();
int style = paint.getStyle();
if (style == Paint.Style.FILL.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
graphics.fill(shape);
}
if (style == Paint.Style.STROKE.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
graphics.draw(shape);
}
}
});
}
/*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
@@ -719,8 +748,20 @@ public class Canvas_Delegate {
int canvasDensity,
int screenDensity,
int bitmapDensity) {
// FIXME
throw new UnsupportedOperationException();
// get the delegate from the native int.
Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
if (bitmapDelegate == null) {
assert false;
return;
}
BufferedImage image = bitmapDelegate.getImage();
float right = left + image.getWidth();
float bottom = top + image.getHeight();
drawBitmap(nativeCanvas, image, nativePaintOrZero,
0, 0, image.getWidth(), image.getHeight(),
(int)left, (int)top, (int)right, (int)bottom);
}
/*package*/ static void native_drawBitmap(Canvas thisCanvas, int nativeCanvas, int bitmap,
@@ -998,6 +1039,34 @@ public class Canvas_Delegate {
// ---- Private delegate/helper methods ----
/**
* Executes a {@link Drawable} with a given canvas and paint.
*/
private static void draw(int nCanvas, int nPaint, Drawable runnable) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
if (canvasDelegate == null) {
assert false;
return;
}
Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
if (paintDelegate == null) {
assert false;
return;
}
// get a Graphics2D object configured with the drawing parameters.
Graphics2D g = canvasDelegate.createCustomGraphics(paintDelegate);
try {
runnable.draw(g, paintDelegate);
} finally {
// dispose Graphics2D object
g.dispose();
}
}
private Canvas_Delegate(BufferedImage image) {
setBitmap(image);
}
@@ -1069,18 +1138,23 @@ public class Canvas_Delegate {
boolean useColorPaint = true;
// get the shader first, as it'll replace the color if it can be used it.
Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(paint.getShader());
if (shaderDelegate != null) {
java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
assert shaderPaint != null;
if (shaderPaint != null) {
g.setPaint(shaderPaint);
useColorPaint = false;
} else {
Bridge.getLog().warning(null,
String.format(
"Shader '%1$s' is not supported in the Layout Preview.",
shaderDelegate.getClass().getCanonicalName()));
int nativeShader = paint.getShader();
if (nativeShader > 0) {
Shader_Delegate shaderDelegate = Shader_Delegate.getDelegate(nativeShader);
assert shaderDelegate != null;
if (shaderDelegate != null) {
if (shaderDelegate.isSupported()) {
java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
assert shaderPaint != null;
if (shaderPaint != null) {
g.setPaint(shaderPaint);
useColorPaint = false;
}
} else {
Bridge.getLog().fidelityWarning(null,
shaderDelegate.getSupportMessage(),
null);
}
}
}
@@ -1092,19 +1166,30 @@ public class Canvas_Delegate {
if (style == Paint.Style.STROKE.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
PathEffect_Delegate effectDelegate = PathEffect_Delegate.getDelegate(
paint.getPathEffect());
boolean customStroke = false;
if (effectDelegate instanceof DashPathEffect_Delegate) {
DashPathEffect_Delegate dpe = (DashPathEffect_Delegate)effectDelegate;
g.setStroke(new BasicStroke(
paint.getStrokeWidth(),
paint.getJavaCap(),
paint.getJavaJoin(),
paint.getStrokeMiter(),
dpe.getIntervals(),
dpe.getPhase()));
} else {
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(),
@@ -1113,87 +1198,28 @@ public class Canvas_Delegate {
}
}
Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
if (xfermodeDelegate instanceof PorterDuffXfermode_Delegate) {
int mode = ((PorterDuffXfermode_Delegate)xfermodeDelegate).getMode();
setModeInGraphics(g, mode);
} else {
// default mode is src_over
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// if xfermode wasn't null, then it's something we don't support. log it.
int xfermode = paint.getXfermode();
if (xfermode > 0) {
Xfermode_Delegate xfermodeDelegate = Xfermode_Delegate.getDelegate(paint.getXfermode());
assert xfermodeDelegate != null;
if (xfermodeDelegate != null) {
assert false;
Bridge.getLog().warning(null,
String.format(
"Xfermode '%1$s' is not supported in the Layout Preview.",
xfermodeDelegate.getClass().getCanonicalName()));
if (xfermodeDelegate.isSupported()) {
Composite composite = xfermodeDelegate.getComposite();
assert composite != null;
if (composite != null) {
g.setComposite(composite);
}
} else {
Bridge.getLog().fidelityWarning(null,
xfermodeDelegate.getSupportMessage(),
null);
}
}
}
return g;
}
private static void setModeInGraphics(Graphics2D g, int mode) {
for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
if (m.nativeInt == mode) {
setModeInGraphics(g, m);
return;
}
}
}
private static void setModeInGraphics(Graphics2D g, PorterDuff.Mode mode) {
switch (mode) {
case CLEAR:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/));
break;
case DARKEN:
break;
case DST:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/));
break;
case DST_ATOP:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/));
break;
case DST_IN:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/));
break;
case DST_OUT:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/));
break;
case DST_OVER:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/));
break;
case LIGHTEN:
break;
case MULTIPLY:
break;
case SCREEN:
break;
case SRC:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/));
break;
case SRC_ATOP:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/));
break;
case SRC_IN:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/));
break;
case SRC_OUT:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/));
break;
case SRC_OVER:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/));
break;
case XOR:
g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/));
break;
}
}
private static void drawBitmap(
int nativeCanvas,
BufferedImage image,
@@ -1228,6 +1254,7 @@ public class Canvas_Delegate {
Paint_Delegate paintDelegate,
int sleft, int stop, int sright, int sbottom,
int dleft, int dtop, int dright, int dbottom) {
//FIXME add support for canvas, screen and bitmap densities.
Graphics2D g = canvasDelegate.getGcSnapshot().create();
try {

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.ComposePathEffect
*
* Through the layoutlib_create tool, the original native methods of ComposePathEffect have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original ComposePathEffect class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class ComposePathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Stroke getStroke(Paint_Delegate paint) {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Composte Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(int outerpe, int innerpe) {
ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Paint;
/**
* Delegate implementing the native methods of android.graphics.ComposeShader
*
* Through the layoutlib_create tool, the original native methods of ComposeShader have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original ComposeShader class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class ComposeShader_Delegate extends Shader_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Paint getJavaPaint() {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Compose Shader are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate1(int native_shaderA, int native_shaderB,
int native_mode) {
// FIXME not supported yet.
ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
return sManager.addDelegate(newDelegate);
}
/*package*/ static int nativeCreate2(int native_shaderA, int native_shaderB,
int porterDuffMode) {
// FIXME not supported yet.
ComposeShader_Delegate newDelegate = new ComposeShader_Delegate();
return sManager.addDelegate(newDelegate);
}
/*package*/ static int nativePostCreate1(int native_shader, int native_skiaShaderA,
int native_skiaShaderB, int native_mode) {
// pass, not needed.
return 0;
}
/*package*/ static int nativePostCreate2(int native_shader, int native_skiaShaderA,
int native_skiaShaderB, int porterDuffMode) {
// pass, not needed.
return 0;
}
// ---- Private delegate/helper methods ----
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.CornerPathEffect
*
* Through the layoutlib_create tool, the original native methods of CornerPathEffect have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original CornerPathEffect class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class CornerPathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Stroke getStroke(Paint_Delegate paint) {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Corner Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(float radius) {
CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -18,6 +18,9 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.BasicStroke;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.DashPathEffect
*
@@ -32,8 +35,10 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
* {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
* {@link PathEffect_Delegate}.
*
* @see PathEffect_Delegate
*
*/
public class DashPathEffect_Delegate extends PathEffect_Delegate {
public final class DashPathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
@@ -42,12 +47,26 @@ public class DashPathEffect_Delegate extends PathEffect_Delegate {
// ---- Public Helper methods ----
public float[] getIntervals() {
return mIntervals;
@Override
public Stroke getStroke(Paint_Delegate paint) {
return new BasicStroke(
paint.getStrokeWidth(),
paint.getJavaCap(),
paint.getJavaJoin(),
paint.getStrokeMiter(),
mIntervals,
mPhase);
}
public float getPhase() {
return mPhase;
@Override
public boolean isSupported() {
return true;
}
@Override
public String getSupportMessage() {
// no message since isSupported returns true;
return null;
}
// ---- native methods ----

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.DiscretePathEffect
*
* Through the layoutlib_create tool, the original native methods of DiscretePathEffect have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original DiscretePathEffect class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class DiscretePathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Stroke getStroke(Paint_Delegate paint) {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Discrete Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(float length, float deviation) {
DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -26,6 +26,18 @@ public abstract class Gradient_Delegate extends Shader_Delegate {
protected final int[] mColors;
protected final float[] mPositions;
@Override
public boolean isSupported() {
// all gradient shaders are supported.
return true;
}
@Override
public String getSupportMessage() {
// all gradient shaders are supported, no need for a gradient support
return null;
}
/**
* Creates the base shader and do some basic test on the parameters.
*

View File

@@ -21,10 +21,6 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Shader.TileMode;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/**
* Delegate implementing the native methods of android.graphics.LinearGradient
*
@@ -41,7 +37,7 @@ import java.awt.geom.NoninvertibleTransformException;
* @see Shader_Delegate
*
*/
public class LinearGradient_Delegate extends Gradient_Delegate {
public final class LinearGradient_Delegate extends Gradient_Delegate {
// ---- delegate data ----
private java.awt.Paint mJavaPaint;
@@ -49,7 +45,7 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
// ---- Public Helper methods ----
@Override
public Paint getJavaPaint() {
public java.awt.Paint getJavaPaint() {
return mJavaPaint;
}
@@ -58,17 +54,8 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
/*package*/ static int nativeCreate1(LinearGradient thisGradient,
float x0, float y0, float x1, float y1,
int colors[], float positions[], int tileMode) {
// figure out the tile
TileMode tile = null;
for (TileMode tm : TileMode.values()) {
if (tm.nativeInt == tileMode) {
tile = tm;
break;
}
}
LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(x0, y0, x1, y1,
colors, positions, tile);
colors, positions, Shader_Delegate.getTileMode(tileMode));
return sManager.addDelegate(newDelegate);
}
/*package*/ static int nativeCreate2(LinearGradient thisGradient,
@@ -144,20 +131,20 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
java.awt.RenderingHints hints) {
precomputeGradientColors();
AffineTransform canvasMatrix;
java.awt.geom.AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
canvasMatrix = new AffineTransform();
canvasMatrix = new java.awt.geom.AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in LinearGradient", e);
localMatrix = new AffineTransform();
localMatrix = new java.awt.geom.AffineTransform();
}
return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
@@ -165,12 +152,14 @@ public class LinearGradient_Delegate extends Gradient_Delegate {
private class LinearGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.geom.AffineTransform mCanvasMatrix;
private final java.awt.geom.AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel;
public LinearGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
private LinearGradientPaintContext(
java.awt.geom.AffineTransform canvasMatrix,
java.awt.geom.AffineTransform localMatrix,
java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel;

View File

@@ -1052,6 +1052,4 @@ public final class Matrix_Delegate {
return tmp;
}
}

View File

@@ -43,7 +43,7 @@ import java.util.Map;
* around to map int to instance of the delegate.
*
*/
public class NinePatch_Delegate {
public final class NinePatch_Delegate {
/**
* Cache map for {@link NinePatchChunk}.

View File

@@ -1,611 +0,0 @@
/*
* Copyright (C) 2006 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;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
* It can be drawn with canvas.drawPath(path, paint), either filled or stroked
* (based on the paint's Style), or it can be used for clipping or to draw
* text on a path.
*/
public class Path {
private FillType mFillType = FillType.WINDING;
private GeneralPath mPath = new GeneralPath();
private float mLastX = 0;
private float mLastY = 0;
//---------- Custom methods ----------
public Shape getAwtShape() {
return mPath;
}
//----------
/**
* Create an empty path
*/
public Path() {
}
/**
* Create a new path, copying the contents from the src path.
*
* @param src The path to copy from when initializing the new path
*/
public Path(Path src) {
mPath.append(src.mPath, false /* connect */);
}
/**
* Clear any lines and curves from the path, making it empty.
* This does NOT change the fill-type setting.
*/
public void reset() {
mPath = new GeneralPath();
}
/**
* Rewinds the path: clears any lines and curves from the path but
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
// FIXME
throw new UnsupportedOperationException();
}
/** Replace the contents of this with the contents of src.
*/
public void set(Path src) {
mPath.append(src.mPath, false /* connect */);
}
/** Enum for the ways a path may be filled
*/
public enum FillType {
// these must match the values in SkPath.h
WINDING (GeneralPath.WIND_NON_ZERO, false),
EVEN_ODD (GeneralPath.WIND_EVEN_ODD, false),
INVERSE_WINDING (GeneralPath.WIND_NON_ZERO, true),
INVERSE_EVEN_ODD(GeneralPath.WIND_EVEN_ODD, true);
FillType(int rule, boolean inverse) {
this.rule = rule;
this.inverse = inverse;
}
final int rule;
final boolean inverse;
}
/**
* Return the path's fill type. This defines how "inside" is
* computed. The default value is WINDING.
*
* @return the path's fill type
*/
public FillType getFillType() {
return mFillType;
}
/**
* Set the path's fill type. This defines how "inside" is computed.
*
* @param ft The new fill type for this path
*/
public void setFillType(FillType ft) {
mFillType = ft;
mPath.setWindingRule(ft.rule);
}
/**
* Returns true if the filltype is one of the INVERSE variants
*
* @return true if the filltype is one of the INVERSE variants
*/
public boolean isInverseFillType() {
return mFillType.inverse;
}
/**
* Toggles the INVERSE state of the filltype
*/
public void toggleInverseFillType() {
switch (mFillType) {
case WINDING:
mFillType = FillType.INVERSE_WINDING;
break;
case EVEN_ODD:
mFillType = FillType.INVERSE_EVEN_ODD;
break;
case INVERSE_WINDING:
mFillType = FillType.WINDING;
break;
case INVERSE_EVEN_ODD:
mFillType = FillType.EVEN_ODD;
break;
}
}
/**
* Returns true if the path is empty (contains no lines or curves)
*
* @return true if the path is empty (contains no lines or curves)
*/
public boolean isEmpty() {
return mPath.getCurrentPoint() == null;
}
/**
* Returns true if the path specifies a rectangle. If so, and if rect is
* not null, set rect to the bounds of the path. If the path does not
* specify a rectangle, return false and ignore rect.
*
* @param rect If not null, returns the bounds of the path if it specifies
* a rectangle
* @return true if the path specifies a rectangle
*/
public boolean isRect(RectF rect) {
// FIXME
throw new UnsupportedOperationException();
}
/**
* Compute the bounds of the path, and write the answer into bounds. If the
* path contains 0 or 1 points, the bounds is set to (0,0,0,0)
*
* @param bounds Returns the computed bounds of the path
* @param exact If true, return the exact (but slower) bounds, else return
* just the bounds of all control points
*/
public void computeBounds(RectF bounds, boolean exact) {
Rectangle2D rect = mPath.getBounds2D();
bounds.left = (float)rect.getMinX();
bounds.right = (float)rect.getMaxX();
bounds.top = (float)rect.getMinY();
bounds.bottom = (float)rect.getMaxY();
}
/**
* Hint to the path to prepare for adding more points. This can allow the
* path to more efficiently allocate its storage.
*
* @param extraPtCount The number of extra points that may be added to this
* path
*/
public void incReserve(int extraPtCount) {
// pass
}
/**
* Set the beginning of the next contour to the point (x,y).
*
* @param x The x-coordinate of the start of a new contour
* @param y The y-coordinate of the start of a new contour
*/
public void moveTo(float x, float y) {
mPath.moveTo(mLastX = x, mLastY = y);
}
/**
* Set the beginning of the next contour relative to the last point on the
* previous contour. If there is no previous contour, this is treated the
* same as moveTo().
*
* @param dx The amount to add to the x-coordinate of the end of the
* previous contour, to specify the start of a new contour
* @param dy The amount to add to the y-coordinate of the end of the
* previous contour, to specify the start of a new contour
*/
public void rMoveTo(float dx, float dy) {
dx += mLastX;
dy += mLastY;
mPath.moveTo(mLastX = dx, mLastY = dy);
}
/**
* Add a line from the last point to the specified point (x,y).
* If no moveTo() call has been made for this contour, the first point is
* automatically set to (0,0).
*
* @param x The x-coordinate of the end of a line
* @param y The y-coordinate of the end of a line
*/
public void lineTo(float x, float y) {
mPath.lineTo(mLastX = x, mLastY = y);
}
/**
* Same as lineTo, but the coordinates are considered relative to the last
* point on this contour. If there is no previous point, then a moveTo(0,0)
* is inserted automatically.
*
* @param dx The amount to add to the x-coordinate of the previous point on
* this contour, to specify a line
* @param dy The amount to add to the y-coordinate of the previous point on
* this contour, to specify a line
*/
public void rLineTo(float dx, float dy) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx += mLastX;
dy += mLastY;
mPath.lineTo(mLastX = dx, mLastY = dy);
}
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
}
/**
* Same as quadTo, but the coordinates are considered relative to the last
* point on this contour. If there is no previous point, then a moveTo(0,0)
* is inserted automatically.
*
* @param dx1 The amount to add to the x-coordinate of the last point on
* this contour, for the control point of a quadratic curve
* @param dy1 The amount to add to the y-coordinate of the last point on
* this contour, for the control point of a quadratic curve
* @param dx2 The amount to add to the x-coordinate of the last point on
* this contour, for the end point of a quadratic curve
* @param dy2 The amount to add to the y-coordinate of the last point on
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx1 += mLastX;
dy1 += mLastY;
dx2 += mLastX;
dy2 += mLastY;
mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
}
/**
* Add a cubic bezier from the last point, approaching control points
* (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
* made for this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the 1st control point on a cubic curve
* @param y1 The y-coordinate of the 1st control point on a cubic curve
* @param x2 The x-coordinate of the 2nd control point on a cubic curve
* @param y2 The y-coordinate of the 2nd control point on a cubic curve
* @param x3 The x-coordinate of the end point on a cubic curve
* @param y3 The y-coordinate of the end point on a cubic curve
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
}
/**
* Same as cubicTo, but the coordinates are considered relative to the
* current point on this contour. If there is no previous point, then a
* moveTo(0,0) is inserted automatically.
*/
public void rCubicTo(float dx1, float dy1, float dx2, float dy2,
float dx3, float dy3) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx1 += mLastX;
dy1 += mLastY;
dx2 += mLastX;
dy2 += mLastY;
dx3 += mLastX;
dy3 += mLastY;
mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
}
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
* with the first point of the arc. The sweep angle is tread mod 360.
*
* @param oval The bounds of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
* mod 360.
* @param forceMoveTo If true, always begin a new contour with the arc
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
throw new UnsupportedOperationException();
}
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
* with the first point of the arc.
*
* @param oval The bounds of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
throw new UnsupportedOperationException();
}
/**
* Close the current contour. If the current point is not equal to the
* first point of the contour, a line segment is automatically added.
*/
public void close() {
mPath.closePath();
}
/**
* Specifies how closed shapes (e.g. rects, ovals) are oriented when they
* are added to a path.
*/
public enum Direction {
/** clockwise */
CW (0), // must match enum in SkPath.h
/** counter-clockwise */
CCW (1); // must match enum in SkPath.h
Direction(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
/**
* Add a closed rectangle contour to the path
*
* @param rect The rectangle to add as a closed contour to the path
* @param dir The direction to wind the rectangle's contour
*/
public void addRect(RectF rect, Direction dir) {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}
/**
* Add a closed rectangle contour to the path
*
* @param left The left side of a rectangle to add to the path
* @param top The top of a rectangle to add to the path
* @param right The right side of a rectangle to add to the path
* @param bottom The bottom of a rectangle to add to the path
* @param dir The direction to wind the rectangle's contour
*/
public void addRect(float left, float top, float right, float bottom,
Direction dir) {
moveTo(left, top);
switch (dir) {
case CW:
lineTo(right, top);
lineTo(right, bottom);
lineTo(left, bottom);
break;
case CCW:
lineTo(left, bottom);
lineTo(right, bottom);
lineTo(right, top);
break;
}
close();
}
/**
* Add a closed oval contour to the path
*
* @param oval The bounds of the oval to add as a closed contour to the path
* @param dir The direction to wind the oval's contour
*/
public void addOval(RectF oval, Direction dir) {
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
// FIXME Need to support direction
Ellipse2D ovalShape = new Ellipse2D.Float(oval.left, oval.top, oval.width(), oval.height());
mPath.append(ovalShape, false /* connect */);
}
/**
* Add a closed circle contour to the path
*
* @param x The x-coordinate of the center of a circle to add to the path
* @param y The y-coordinate of the center of a circle to add to the path
* @param radius The radius of a circle to add to the path
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, Direction dir) {
// FIXME
throw new UnsupportedOperationException();
}
/**
* Add the specified arc to the path as a new contour.
*
* @param oval The bounds of oval defining the shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void addArc(RectF oval, float startAngle, float sweepAngle) {
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
// FIXME
throw new UnsupportedOperationException();
}
/**
* Add a closed round-rectangle contour to the path
*
* @param rect The bounds of a round-rectangle to add to the path
* @param rx The x-radius of the rounded corners on the round-rectangle
* @param ry The y-radius of the rounded corners on the round-rectangle
* @param dir The direction to wind the round-rectangle's contour
*/
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
// FIXME
throw new UnsupportedOperationException();
}
/**
* Add a closed round-rectangle contour to the path. Each corner receives
* two radius values [X, Y]. The corners are ordered top-left, top-right,
* bottom-right, bottom-left
*
* @param rect The bounds of a round-rectangle to add to the path
* @param radii Array of 8 values, 4 pairs of [X,Y] radii
* @param dir The direction to wind the round-rectangle's contour
*/
public void addRoundRect(RectF rect, float[] radii, Direction dir) {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
// FIXME
throw new UnsupportedOperationException();
}
/**
* Add a copy of src to the path, offset by (dx,dy)
*
* @param src The path to add as a new contour
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(Path src, float dx, float dy) {
PathIterator iterator = src.mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
mPath.append(iterator, false /* connect */);
}
/**
* Add a copy of src to the path
*
* @param src The path that is appended to the current path
*/
public void addPath(Path src) {
addPath(src, 0, 0);
}
/**
* Add a copy of src to the path, transformed by matrix
*
* @param src The path to add as a new contour
*/
public void addPath(Path src, Matrix matrix) {
// FIXME
throw new UnsupportedOperationException();
}
/**
* Offset the path by (dx,dy), returning true on success
*
* @param dx The amount in the X direction to offset the entire path
* @param dy The amount in the Y direction to offset the entire path
* @param dst The translated path is written here. If this is null, then
* the original path is modified.
*/
public void offset(float dx, float dy, Path dst) {
GeneralPath newPath = new GeneralPath();
PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
newPath.append(iterator, false /* connect */);
if (dst != null) {
dst.mPath = newPath;
} else {
mPath = newPath;
}
}
/**
* Offset the path by (dx,dy), returning true on success
*
* @param dx The amount in the X direction to offset the entire path
* @param dy The amount in the Y direction to offset the entire path
*/
public void offset(float dx, float dy) {
offset(dx, dy, null /* dst */);
}
/**
* Sets the last point of the path.
*
* @param dx The new X coordinate for the last point
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
mLastX = dx;
mLastY = dy;
}
/**
* Transform the points in this path by matrix, and write the answer
* into dst. If dst is null, then the the original path is modified.
*
* @param matrix The matrix to apply to the path
* @param dst The transformed path is written here. If dst is null,
* then the the original path is modified
*/
public void transform(Matrix matrix, Path dst) {
// FIXME
throw new UnsupportedOperationException();
}
/**
* Transform the points in this path by matrix.
*
* @param matrix The matrix to apply to the path
*/
public void transform(Matrix matrix) {
transform(matrix, null /* dst */);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.PathDashPathEffect
*
* Through the layoutlib_create tool, the original native methods of PathDashPathEffect have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original PathDashPathEffect class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class PathDashPathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Stroke getStroke(Paint_Delegate paint) {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Path Dash Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(int native_path, float advance, float phase,
int native_style) {
PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -18,6 +18,8 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.PathEffect
*
@@ -33,7 +35,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
* @see DelegateManager
*
*/
public class PathEffect_Delegate {
public abstract class PathEffect_Delegate {
// ---- delegate manager ----
protected static final DelegateManager<PathEffect_Delegate> sManager =
@@ -49,6 +51,11 @@ public class PathEffect_Delegate {
return sManager.getDelegate(nativeShader);
}
public abstract Stroke getStroke(Paint_Delegate paint);
public abstract boolean isSupported();
public abstract String getSupportMessage();
// ---- native methods ----
/*package*/ static void nativeDestructor(int native_patheffect) {

View File

@@ -0,0 +1,748 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Path.Direction;
import android.graphics.Path.FillType;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
/**
* Delegate implementing the native methods of android.graphics.Path
*
* Through the layoutlib_create tool, the original native methods of Path have been replaced
* by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original Path class.
*
* @see DelegateManager
*
*/
public final class Path_Delegate {
// ---- delegate manager ----
private static final DelegateManager<Path_Delegate> sManager =
new DelegateManager<Path_Delegate>();
// ---- delegate data ----
private FillType mFillType = FillType.WINDING;
private GeneralPath mPath = new GeneralPath();
private float mLastX = 0;
private float mLastY = 0;
// ---- Public Helper methods ----
public static Path_Delegate getDelegate(int nPath) {
return sManager.getDelegate(nPath);
}
public Shape getJavaShape() {
return mPath;
}
// ---- native methods ----
/*package*/ static int init1() {
// create the delegate
Path_Delegate newDelegate = new Path_Delegate();
return sManager.addDelegate(newDelegate);
}
/*package*/ static int init2(int nPath) {
// create the delegate
Path_Delegate newDelegate = new Path_Delegate();
// get the delegate to copy
if (nPath > 0) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return 0;
}
newDelegate.set(pathDelegate);
}
return sManager.addDelegate(newDelegate);
}
/*package*/ static void native_reset(int nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.mPath.reset();
}
/*package*/ static void native_rewind(int nPath) {
// call out to reset since there's nothing to optimize in
// terms of data structs.
native_reset(nPath);
}
/*package*/ static void native_set(int native_dst, int native_src) {
Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
if (pathDstDelegate == null) {
assert false;
return;
}
Path_Delegate pathSrcDelegate = sManager.getDelegate(native_src);
if (pathSrcDelegate == null) {
assert false;
return;
}
pathDstDelegate.set(pathSrcDelegate);
}
/*package*/ static int native_getFillType(int nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return 0;
}
return pathDelegate.mFillType.nativeInt;
}
/*package*/ static void native_setFillType(int nPath, int ft) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.mFillType = Path.sFillTypeArray[ft];
}
/*package*/ static boolean native_isEmpty(int nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return true;
}
return pathDelegate.isEmpty();
}
/*package*/ static boolean native_isRect(int nPath, RectF rect) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return false;
}
// create an Area that can test if the path is a rect
Area area = new Area(pathDelegate.mPath);
if (area.isRectangular()) {
if (rect != null) {
pathDelegate.fillBounds(rect);
}
return true;
}
return false;
}
/*package*/ static void native_computeBounds(int nPath, RectF bounds) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.fillBounds(bounds);
}
/*package*/ static void native_incReserve(int nPath, int extraPtCount) {
// since we use a java2D path, there's no way to pre-allocate new points,
// so we do nothing.
}
/*package*/ static void native_moveTo(int nPath, float x, float y) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.moveTo(x, y);
}
/*package*/ static void native_rMoveTo(int nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.rMoveTo(dx, dy);
}
/*package*/ static void native_lineTo(int nPath, float x, float y) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.lineTo(x, y);
}
/*package*/ static void native_rLineTo(int nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.rLineTo(dx, dy);
}
/*package*/ static void native_quadTo(int nPath, float x1, float y1, float x2, float y2) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.quadTo(x1, y1, x2, y2);
}
/*package*/ static void native_rQuadTo(int nPath, float dx1, float dy1,
float dx2, float dy2) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.rQuadTo(dx1, dy1, dx2, dy2);
}
/*package*/ static void native_cubicTo(int nPath, float x1, float y1,
float x2, float y2, float x3, float y3) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3);
}
/*package*/ static void native_rCubicTo(int nPath, float x1, float y1,
float x2, float y2, float x3, float y3) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3);
}
/*package*/ static void native_arcTo(int nPath, RectF oval,
float startAngle, float sweepAngle, boolean forceMoveTo) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.arcTo(oval, startAngle, sweepAngle, forceMoveTo);
}
/*package*/ static void native_close(int nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.close();
}
/*package*/ static void native_addRect(int nPath, RectF rect, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}
/*package*/ static void native_addRect(int nPath, float left, float top,
float right, float bottom, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.addRect(left, top, right, bottom, dir);
}
/*package*/ static void native_addOval(int nPath, RectF oval, int dir) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addCircle(int nPath, float x, float y,
float radius, int dir) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addArc(int nPath, RectF oval,
float startAngle, float sweepAngle) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addRoundRect(int nPath, RectF rect,
float rx, float ry, int dir) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addRoundRect(int nPath, RectF r,
float[] radii, int dir) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addPath(int nPath, int src, float dx,
float dy) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addPath(int nPath, int src) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_addPath(int nPath, int src, int matrix) {
// FIXME
throw new UnsupportedOperationException();
}
/*package*/ static void native_offset(int nPath, float dx, float dy,
int dst_path) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
Path_Delegate dstDelegate = null;
if (dst_path > 0) {
dstDelegate = sManager.getDelegate(dst_path);
if (dstDelegate == null) {
assert false;
return;
}
}
pathDelegate.offset(dx, dy, dstDelegate);
}
/*package*/ static void native_offset(int nPath, float dx, float dy) {
native_offset(nPath, dx, dy, 0);
}
/*package*/ static void native_setLastPoint(int nPath, float dx, float dy) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
pathDelegate.mLastX = dx;
pathDelegate.mLastY = dy;
}
/*package*/ static void native_transform(int nPath, int matrix,
int dst_path) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
assert false;
return;
}
Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
if (matrixDelegate == null) {
assert false;
return;
}
Path_Delegate dstDelegate = null;
if (dst_path > 0) {
dstDelegate = sManager.getDelegate(dst_path);
if (dstDelegate == null) {
assert false;
return;
}
}
pathDelegate.transform(matrixDelegate, dstDelegate);
}
/*package*/ static void native_transform(int nPath, int matrix) {
native_transform(nPath, matrix, 0);
}
/*package*/ static void finalizer(int nPath) {
sManager.removeDelegate(nPath);
}
// ---- Private helper methods ----
private void set(Path_Delegate delegate) {
mPath.reset();
setFillType(delegate.mFillType);
mPath.append(delegate.mPath, false /*connect*/);
}
private void setFillType(FillType fillType) {
mFillType = fillType;
mPath.setWindingRule(getWindingRule(fillType));
}
/**
* Returns the Java2D winding rules matching a given Android {@link FillType}.
* @param type the android fill type
* @return the matching java2d winding rule.
*/
private static int getWindingRule(FillType type) {
switch (type) {
case WINDING:
case INVERSE_WINDING:
return GeneralPath.WIND_NON_ZERO;
case EVEN_ODD:
case INVERSE_EVEN_ODD:
return GeneralPath.WIND_EVEN_ODD;
}
assert false;
throw new IllegalArgumentException();
}
private static Direction getDirection(int direction) {
for (Direction d : Direction.values()) {
if (direction == d.nativeInt) {
return d;
}
}
assert false;
return null;
}
/**
* Returns whether the path is empty.
* @return true if the path is empty.
*/
private boolean isEmpty() {
return mPath.getCurrentPoint() == null;
}
/**
* Fills the given {@link RectF} with the path bounds.
* @param bounds the RectF to be filled.
*/
private void fillBounds(RectF bounds) {
Rectangle2D rect = mPath.getBounds2D();
bounds.left = (float)rect.getMinX();
bounds.right = (float)rect.getMaxX();
bounds.top = (float)rect.getMinY();
bounds.bottom = (float)rect.getMaxY();
}
/**
* Set the beginning of the next contour to the point (x,y).
*
* @param x The x-coordinate of the start of a new contour
* @param y The y-coordinate of the start of a new contour
*/
private void moveTo(float x, float y) {
mPath.moveTo(mLastX = x, mLastY = y);
}
/**
* Set the beginning of the next contour relative to the last point on the
* previous contour. If there is no previous contour, this is treated the
* same as moveTo().
*
* @param dx The amount to add to the x-coordinate of the end of the
* previous contour, to specify the start of a new contour
* @param dy The amount to add to the y-coordinate of the end of the
* previous contour, to specify the start of a new contour
*/
private void rMoveTo(float dx, float dy) {
dx += mLastX;
dy += mLastY;
mPath.moveTo(mLastX = dx, mLastY = dy);
}
/**
* Add a line from the last point to the specified point (x,y).
* If no moveTo() call has been made for this contour, the first point is
* automatically set to (0,0).
*
* @param x The x-coordinate of the end of a line
* @param y The y-coordinate of the end of a line
*/
private void lineTo(float x, float y) {
mPath.lineTo(mLastX = x, mLastY = y);
}
/**
* Same as lineTo, but the coordinates are considered relative to the last
* point on this contour. If there is no previous point, then a moveTo(0,0)
* is inserted automatically.
*
* @param dx The amount to add to the x-coordinate of the previous point on
* this contour, to specify a line
* @param dy The amount to add to the y-coordinate of the previous point on
* this contour, to specify a line
*/
private void rLineTo(float dx, float dy) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx += mLastX;
dy += mLastY;
mPath.lineTo(mLastX = dx, mLastY = dy);
}
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
private void quadTo(float x1, float y1, float x2, float y2) {
mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
}
/**
* Same as quadTo, but the coordinates are considered relative to the last
* point on this contour. If there is no previous point, then a moveTo(0,0)
* is inserted automatically.
*
* @param dx1 The amount to add to the x-coordinate of the last point on
* this contour, for the control point of a quadratic curve
* @param dy1 The amount to add to the y-coordinate of the last point on
* this contour, for the control point of a quadratic curve
* @param dx2 The amount to add to the x-coordinate of the last point on
* this contour, for the end point of a quadratic curve
* @param dy2 The amount to add to the y-coordinate of the last point on
* this contour, for the end point of a quadratic curve
*/
private void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx1 += mLastX;
dy1 += mLastY;
dx2 += mLastX;
dy2 += mLastY;
mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
}
/**
* Add a cubic bezier from the last point, approaching control points
* (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
* made for this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the 1st control point on a cubic curve
* @param y1 The y-coordinate of the 1st control point on a cubic curve
* @param x2 The x-coordinate of the 2nd control point on a cubic curve
* @param y2 The y-coordinate of the 2nd control point on a cubic curve
* @param x3 The x-coordinate of the end point on a cubic curve
* @param y3 The y-coordinate of the end point on a cubic curve
*/
private void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
}
/**
* Same as cubicTo, but the coordinates are considered relative to the
* current point on this contour. If there is no previous point, then a
* moveTo(0,0) is inserted automatically.
*/
private void rCubicTo(float dx1, float dy1, float dx2, float dy2,
float dx3, float dy3) {
if (isEmpty()) {
mPath.moveTo(mLastX = 0, mLastY = 0);
}
dx1 += mLastX;
dy1 += mLastY;
dx2 += mLastX;
dy2 += mLastY;
dx3 += mLastX;
dy3 += mLastY;
mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
}
/**
* Append the specified arc to the path as a new contour. If the start of
* the path is different from the path's current last point, then an
* automatic lineTo() is added to connect the current contour to the
* start of the arc. However, if the path is empty, then we call moveTo()
* with the first point of the arc. The sweep angle is tread mod 360.
*
* @param oval The bounds of oval defining shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
* mod 360.
* @param forceMoveTo If true, always begin a new contour with the arc
*/
private void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), startAngle,
sweepAngle, Arc2D.OPEN);
mPath.append(arc, true /*connect*/);
resetLastPointFromPath();
}
/**
* Close the current contour. If the current point is not equal to the
* first point of the contour, a line segment is automatically added.
*/
private void close() {
mPath.closePath();
}
private void resetLastPointFromPath() {
Point2D last = mPath.getCurrentPoint();
mLastX = (float) last.getX();
mLastY = (float) last.getY();
}
/**
* Add a closed rectangle contour to the path
*
* @param left The left side of a rectangle to add to the path
* @param top The top of a rectangle to add to the path
* @param right The right side of a rectangle to add to the path
* @param bottom The bottom of a rectangle to add to the path
* @param dir The direction to wind the rectangle's contour
*/
private void addRect(float left, float top, float right, float bottom,
int dir) {
moveTo(left, top);
Direction direction = getDirection(dir);
switch (direction) {
case CW:
lineTo(right, top);
lineTo(right, bottom);
lineTo(left, bottom);
break;
case CCW:
lineTo(left, bottom);
lineTo(right, bottom);
lineTo(right, top);
break;
}
close();
resetLastPointFromPath();
}
/**
* Offset the path by (dx,dy), returning true on success
*
* @param dx The amount in the X direction to offset the entire path
* @param dy The amount in the Y direction to offset the entire path
* @param dst The translated path is written here. If this is null, then
* the original path is modified.
*/
public void offset(float dx, float dy, Path_Delegate dst) {
GeneralPath newPath = new GeneralPath();
PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
newPath.append(iterator, false /*connect*/);
if (dst != null) {
dst.mPath = newPath;
} else {
mPath = newPath;
}
}
/**
* Transform the points in this path by matrix, and write the answer
* into dst. If dst is null, then the the original path is modified.
*
* @param matrix The matrix to apply to the path
* @param dst The transformed path is written here. If dst is null,
* then the the original path is modified
*/
public void transform(Matrix_Delegate matrix, Path_Delegate dst) {
if (matrix.hasPerspective()) {
assert false;
Bridge.getLog().fidelityWarning(null,
"android.graphics.Path#transform() only " +
"supports affine transformations in the Layout Preview.", null);
}
GeneralPath newPath = new GeneralPath();
PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform());
newPath.append(iterator, false /*connect*/);
if (dst != null) {
dst.mPath = newPath;
} else {
mPath = newPath;
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Composite;
/**
* Delegate implementing the native methods of android.graphics.PixelXorXfermode
*
* Through the layoutlib_create tool, the original native methods of PixelXorXfermode have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original PixelXorXfermode class.
*
* Because this extends {@link Xfermode_Delegate}, there's no need to use a
* {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
* {@link Xfermode_Delegate}.
*
* @see Xfermode_Delegate
*/
public class PixelXorXfermode_Delegate extends Xfermode_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Composite getComposite() {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Pixel XOR Xfermodes are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(int opColor) {
PixelXorXfermode_Delegate newDelegate = new PixelXorXfermode_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -16,8 +16,12 @@
package android.graphics;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.AlphaComposite;
import java.awt.Composite;
/**
* Delegate implementing the native methods of android.graphics.PorterDuffXfermode
*
@@ -41,8 +45,64 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
// ---- Public Helper methods ----
public int getMode() {
return mMode;
@Override
public Composite getComposite() {
return getComposite(mMode);
}
@Override
public boolean isSupported() {
return true;
}
@Override
public String getSupportMessage() {
// no message since isSupported returns true;
return null;
}
public static Composite getComposite(int mode) {
PorterDuff.Mode m = getMode(mode);
switch (m) {
case CLEAR:
return AlphaComposite.getInstance(AlphaComposite.CLEAR, 1.0f /*alpha*/);
case DARKEN:
break;
case DST:
return AlphaComposite.getInstance(AlphaComposite.DST, 1.0f /*alpha*/);
case DST_ATOP:
return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, 1.0f /*alpha*/);
case DST_IN:
return AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0f /*alpha*/);
case DST_OUT:
return AlphaComposite.getInstance(AlphaComposite.DST_OUT, 1.0f /*alpha*/);
case DST_OVER:
return AlphaComposite.getInstance(AlphaComposite.DST_OVER, 1.0f /*alpha*/);
case LIGHTEN:
break;
case MULTIPLY:
break;
case SCREEN:
break;
case SRC:
return AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f /*alpha*/);
case SRC_ATOP:
return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1.0f /*alpha*/);
case SRC_IN:
return AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f /*alpha*/);
case SRC_OUT:
return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, 1.0f /*alpha*/);
case SRC_OVER:
return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f /*alpha*/);
case XOR:
return AlphaComposite.getInstance(AlphaComposite.XOR, 1.0f /*alpha*/);
}
Bridge.getLog().fidelityWarning(null,
String.format("Unsupported PorterDuff Mode: %s", m.name()),
null);
return AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
}
// ---- native methods ----
@@ -58,4 +118,15 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
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;
}
}

View File

@@ -21,10 +21,6 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
import android.graphics.Shader.TileMode;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/**
* Delegate implementing the native methods of android.graphics.RadialGradient
*
@@ -49,7 +45,7 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
// ---- Public Helper methods ----
@Override
public Paint getJavaPaint() {
public java.awt.Paint getJavaPaint() {
return mJavaPaint;
}
@@ -57,17 +53,8 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
/*package*/ static int nativeCreate1(float x, float y, float radius,
int colors[], float positions[], int tileMode) {
// figure out the tile
TileMode tile = null;
for (TileMode tm : TileMode.values()) {
if (tm.nativeInt == tileMode) {
tile = tm;
break;
}
}
RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(x, y, radius,
colors, positions, tile);
colors, positions, Shader_Delegate.getTileMode(tileMode));
return sManager.addDelegate(newDelegate);
}
@@ -133,20 +120,20 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
java.awt.RenderingHints hints) {
precomputeGradientColors();
AffineTransform canvasMatrix;
java.awt.geom.AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
canvasMatrix = new AffineTransform();
canvasMatrix = new java.awt.geom.AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in RadialGradient", e);
localMatrix = new AffineTransform();
localMatrix = new java.awt.geom.AffineTransform();
}
return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
@@ -154,12 +141,14 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
private class RadialGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.geom.AffineTransform mCanvasMatrix;
private final java.awt.geom.AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel;
public RadialGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
public RadialGradientPaintContext(
java.awt.geom.AffineTransform canvasMatrix,
java.awt.geom.AffineTransform localMatrix,
java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel;

View File

@@ -18,7 +18,7 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.geom.AffineTransform;
import android.graphics.Shader.TileMode;
/**
* Delegate implementing the native methods of android.graphics.Shader
@@ -52,7 +52,25 @@ public abstract class Shader_Delegate {
return sManager.getDelegate(nativeShader);
}
/**
* Returns the {@link TileMode} matching the given int.
* @param tileMode the tile mode int value
* @return the TileMode enum.
*/
public static TileMode getTileMode(int tileMode) {
for (TileMode tm : TileMode.values()) {
if (tm.nativeInt == tileMode) {
return tm;
}
}
assert false;
return TileMode.CLAMP;
}
public abstract java.awt.Paint getJavaPaint();
public abstract boolean isSupported();
public abstract String getSupportMessage();
// ---- native methods ----
@@ -111,19 +129,19 @@ public abstract class Shader_Delegate {
// ---- Private delegate/helper methods ----
protected AffineTransform getLocalMatrix() {
protected java.awt.geom.AffineTransform getLocalMatrix() {
Matrix_Delegate localMatrixDelegate = null;
if (mLocalMatrix > 0) {
localMatrixDelegate = Matrix_Delegate.getDelegate(mLocalMatrix);
if (localMatrixDelegate == null) {
assert false;
return new AffineTransform();
return new java.awt.geom.AffineTransform();
}
return localMatrixDelegate.getAffineTransform();
}
return new AffineTransform();
return new java.awt.geom.AffineTransform();
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2010 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;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Stroke;
/**
* Delegate implementing the native methods of android.graphics.SumPathEffect
*
* Through the layoutlib_create tool, the original native methods of SumPathEffect have been
* replaced by calls to methods of the same name in this delegate class.
*
* This class behaves like the original native implementation, but in Java, keeping previously
* native data into its own objects and mapping them to int that are sent back and forth between
* it and the original SumPathEffect class.
*
* Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
* as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
*
* @see Shader_Delegate
*
*/
public class SumPathEffect_Delegate extends PathEffect_Delegate {
// ---- delegate data ----
// ---- Public Helper methods ----
@Override
public Stroke getStroke(Paint_Delegate paint) {
// FIXME
return null;
}
@Override
public boolean isSupported() {
return false;
}
@Override
public String getSupportMessage() {
return "Sum Path Effects are not supported in Layout Preview mode.";
}
// ---- native methods ----
/*package*/ static int nativeCreate(int first, int second) {
SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate();
return sManager.addDelegate(newDelegate);
}
// ---- Private delegate/helper methods ----
}

View File

@@ -19,10 +19,6 @@ package android.graphics;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Paint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
/**
* Delegate implementing the native methods of android.graphics.SweepGradient
*
@@ -47,7 +43,7 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
// ---- Public Helper methods ----
@Override
public Paint getJavaPaint() {
public java.awt.Paint getJavaPaint() {
return mJavaPaint;
}
@@ -116,20 +112,20 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
java.awt.RenderingHints hints) {
precomputeGradientColors();
AffineTransform canvasMatrix;
java.awt.geom.AffineTransform canvasMatrix;
try {
canvasMatrix = xform.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
canvasMatrix = new AffineTransform();
canvasMatrix = new java.awt.geom.AffineTransform();
}
AffineTransform localMatrix = getLocalMatrix();
java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
try {
localMatrix = localMatrix.createInverse();
} catch (NoninvertibleTransformException e) {
} catch (java.awt.geom.NoninvertibleTransformException e) {
Bridge.getLog().error(null, "Unable to inverse matrix in SweepGradient", e);
localMatrix = new AffineTransform();
localMatrix = new java.awt.geom.AffineTransform();
}
return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
@@ -137,12 +133,14 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
private class SweepGradientPaintContext implements java.awt.PaintContext {
private final AffineTransform mCanvasMatrix;
private final AffineTransform mLocalMatrix;
private final java.awt.geom.AffineTransform mCanvasMatrix;
private final java.awt.geom.AffineTransform mLocalMatrix;
private final java.awt.image.ColorModel mColorModel;
public SweepGradientPaintContext(AffineTransform canvasMatrix,
AffineTransform localMatrix, java.awt.image.ColorModel colorModel) {
public SweepGradientPaintContext(
java.awt.geom.AffineTransform canvasMatrix,
java.awt.geom.AffineTransform localMatrix,
java.awt.image.ColorModel colorModel) {
mCanvasMatrix = canvasMatrix;
mLocalMatrix = localMatrix;
mColorModel = colorModel;

View File

@@ -18,6 +18,8 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import java.awt.Composite;
/**
* Delegate implementing the native methods of android.graphics.Xfermode
*
@@ -33,7 +35,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
* @see DelegateManager
*
*/
public class Xfermode_Delegate {
public abstract class Xfermode_Delegate {
// ---- delegate manager ----
protected static final DelegateManager<Xfermode_Delegate> sManager =
@@ -49,6 +51,11 @@ public class Xfermode_Delegate {
return sManager.getDelegate(native_instance);
}
public abstract Composite getComposite();
public abstract boolean isSupported();
public abstract String getSupportMessage();
// ---- native methods ----
/*package*/ static void finalizer(int native_instance) {

View File

@@ -106,17 +106,27 @@ public final class CreateInfo implements ICreateInfo {
*/
private final static String[] DELEGATE_CLASS_NATIVES = new String[] {
"android.animation.PropertyValuesHolder",
"android.graphics.AvoidXfermode",
"android.graphics.Bitmap",
"android.graphics.BitmapShader",
"android.graphics.Canvas",
"android.graphics.ComposePathEffect",
"android.graphics.ComposeShader",
"android.graphics.CornerPathEffect",
"android.graphics.DashPathEffect",
"android.graphics.DiscretePathEffect",
"android.graphics.LinearGradient",
"android.graphics.Matrix",
"android.graphics.NinePatch",
"android.graphics.Paint",
"android.graphics.Path",
"android.graphics.PathDashPathEffect",
"android.graphics.PathEffect",
"android.graphics.PixelXorXfermode",
"android.graphics.PorterDuffXfermode",
"android.graphics.RadialGradient",
"android.graphics.Shader",
"android.graphics.SumPathEffect",
"android.graphics.SweepGradient",
"android.graphics.Typeface",
"android.graphics.Xfermode",
@@ -140,7 +150,6 @@ public final class CreateInfo implements ICreateInfo {
private final static String[] RENAMED_CLASSES =
new String[] {
"android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory",
"android.graphics.Path", "android.graphics._Original_Path",
"android.os.ServiceManager", "android.os._Original_ServiceManager",
"android.view.SurfaceView", "android.view._Original_SurfaceView",
"android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",