/* * Copyright 2007, 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 com.android.internal.awt; import com.android.internal.awt.AndroidGraphicsConfiguration; import com.android.internal.graphics.NativeUtils; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.PathIterator; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.ImageObserver; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.text.AttributedCharacterIterator; import java.util.Map; import org.apache.harmony.awt.gl.ImageSurface; import org.apache.harmony.awt.gl.MultiRectArea; import org.apache.harmony.awt.gl.Surface; import org.apache.harmony.awt.gl.font.AndroidGlyphVector; import org.apache.harmony.awt.gl.font.FontMetricsImpl; import org.apache.harmony.awt.gl.image.OffscreenImage; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Typeface; import android.graphics.PixelXorXfermode; import android.view.Display; import android.view.WindowManager; import android.content.Context; public class AndroidGraphics2D extends Graphics2D { private int displayWidth, displayHeight; protected Surface dstSurf = null; protected MultiRectArea clip = null; protected Composite composite = AlphaComposite.SrcOver; protected AffineTransform transform = new AffineTransform(); private static AndroidGraphics2D mAg; private static Canvas mC; // Android Paint public static Paint mP; private static java.awt.Font mFnt; // Cached Matrix public static Matrix mM; private static FontMetrics mFm; private static RenderingHints mRh; private static Color mBc; private Area mCurrClip; public final static double RAD_360 = Math.PI / 180 * 360; // Image drawing private AndroidJavaBlitter blitter; private DirectColorModel cm; private SinglePixelPackedSampleModel sm; private WritableRaster wr; public static AndroidGraphics2D getInstance() { if (mAg == null) { throw new RuntimeException("AndroidGraphics2D not instantiated!"); } return mAg; } public static AndroidGraphics2D getInstance(Context ctx, Canvas c, Paint p) { if (c == null || ctx == null) { throw new RuntimeException( "Illegal argument, Canvas cannot be null!"); } mAg = new AndroidGraphics2D(ctx, c, p); return mAg; } private AndroidGraphics2D(Context ctx, Canvas c, Paint p) { super(); mC = c; mP = p; mM = new Matrix(); mM.reset(); mM = mC.getMatrix(); Rect r = mC.getClipBounds(); int cl[] = {-1, r.top, r.left, -2, r.top, r.right, -2, r.bottom, r.right, -2, r.bottom, r.left}; mCurrClip = new Area(createShape(cl)); if(ctx != null) { WindowManager wm = (WindowManager)ctx.getSystemService(Context.WINDOW_SERVICE); Display d = wm.getDefaultDisplay(); displayWidth = d.getWidth(); displayHeight = d.getHeight(); } blitter = new AndroidJavaBlitter(c); cm = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000); sm = new SinglePixelPackedSampleModel( DataBuffer.TYPE_INT, displayWidth, displayHeight, cm.getMasks()); wr = Raster.createWritableRaster(sm, null); dstSurf = new ImageSurface(cm, wr); } @Override public void addRenderingHints(Map<?, ?> hints) { if (mRh == null) { mRh = (RenderingHints) hints; } mRh.add((RenderingHints) hints); } public float[] getMatrix() { float[] f = new float[9]; mC.getMatrix().getValues(f); return f; } /** * * @return a Matrix in Android format */ public float[] getInverseMatrix() { AffineTransform af = new AffineTransform(createAWTMatrix(getMatrix())); try { af = af.createInverse(); } catch (NoninvertibleTransformException e) { } return createMatrix(af); } private Path getPath(Shape s) { Path path = new Path(); PathIterator pi = s.getPathIterator(null); while (pi.isDone() == false) { getCurrentSegment(pi, path); pi.next(); } return path; } private void getCurrentSegment(PathIterator pi, Path path) { float[] coordinates = new float[6]; int type = pi.currentSegment(coordinates); switch (type) { case PathIterator.SEG_MOVETO: path.moveTo(coordinates[0], coordinates[1]); break; case PathIterator.SEG_LINETO: path.lineTo(coordinates[0], coordinates[1]); break; case PathIterator.SEG_QUADTO: path.quadTo(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); break; case PathIterator.SEG_CUBICTO: path.cubicTo(coordinates[0], coordinates[1], coordinates[2], coordinates[3], coordinates[4], coordinates[5]); break; case PathIterator.SEG_CLOSE: path.close(); break; default: break; } } private Shape createShape(int[] arr) { Shape s = new GeneralPath(); for(int i = 0; i < arr.length; i++) { int type = arr[i]; switch (type) { case -1: //MOVETO ((GeneralPath)s).moveTo(arr[++i], arr[++i]); break; case -2: //LINETO ((GeneralPath)s).lineTo(arr[++i], arr[++i]); break; case -3: //QUADTO ((GeneralPath)s).quadTo(arr[++i], arr[++i], arr[++i], arr[++i]); break; case -4: //CUBICTO ((GeneralPath)s).curveTo(arr[++i], arr[++i], arr[++i], arr[++i], arr[++i], arr[++i]); break; case -5: //CLOSE return s; default: break; } } return s; } /* public int[] getPixels() { return mC.getPixels(); }*/ public static float getRadian(float degree) { return (float) ((Math.PI / 180) * degree); } private Shape getShape() { return null; } public static float getDegree(float radian) { return (float) ((180 / Math.PI) * radian); } /* * Degree in radian */ public static float getEllipsisX(float degree, float princAxis) { return (float) Math.cos(degree) * princAxis; } public static float getEllipsisY(float degree, float conAxis) { return (float) Math.sin(degree) * conAxis; } @Override public void clip(Shape s) { mC.clipPath(getPath(s)); } public void setCanvas(Canvas c) { mC = c; } @Override public void draw(Shape s) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.STROKE); mC.drawPath(getPath(s), mP); mP.setStyle(tmp); } /* private ArrayList getSegments(Shape s) { ArrayList arr = new ArrayList(); PathIterator pi = s.getPathIterator(null); while (pi.isDone() == false) { getCurrentSegment(pi, arr); pi.next(); } return arr; } private void getCurrentSegment(PathIterator pi, ArrayList arr) { float[] coordinates = new float[6]; int type = pi.currentSegment(coordinates); switch (type) { case PathIterator.SEG_MOVETO: arr.add(new Integer(-1)); break; case PathIterator.SEG_LINETO: arr.add(new Integer(-2)); break; case PathIterator.SEG_QUADTO: arr.add(new Integer(-3)); break; case PathIterator.SEG_CUBICTO: arr.add(new Integer(-4)); break; case PathIterator.SEG_CLOSE: arr.add(new Integer(-5)); break; default: break; } } */ /* * Convenience method, not standard AWT */ public void draw(Path s) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.STROKE); s.transform(mM); mC.drawPath(s, mP); mP.setStyle(tmp); } @Override public void drawGlyphVector(GlyphVector g, float x, float y) { // TODO draw at x, y // draw(g.getOutline()); /* Matrix matrix = new Matrix(); matrix.setTranslate(x, y); Path pth = getPath(g.getOutline()); pth.transform(matrix); draw(pth); */ Path path = new Path(); char[] c = ((AndroidGlyphVector)g).getGlyphs(); mP.getTextPath(c, 0, c.length, x, y, path); mC.drawPath(path, mP); } @Override public void drawRenderableImage(RenderableImage img, AffineTransform xform) { throw new RuntimeException("Not implemented!"); } @Override public void drawRenderedImage(RenderedImage img, AffineTransform xform) { throw new RuntimeException("Not implemented!"); } @Override public void drawString(AttributedCharacterIterator iterator, float x, float y) { throw new RuntimeException("AttributedCharacterIterator not supported!"); } @Override public void drawString(AttributedCharacterIterator iterator, int x, int y) { throw new RuntimeException("AttributedCharacterIterator not supported!"); } @Override public void drawString(String s, float x, float y) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.FILL); Path pth = new Path(); mP.getTextPath(s, 0, s.length(), x, y, pth); mC.drawPath(pth, mP); mP.setStyle(tmp); } @Override public void drawString(String str, int x, int y) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStrokeWidth(0); mC.drawText(str.toCharArray(), 0, str.toCharArray().length, x, y, mP); mP.setStyle(tmp); } @Override public void fill(Shape s) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.FILL); mC.drawPath(getPath(s), mP); mP.setStyle(tmp); } @Override public Color getBackground() { return mBc; } @Override public Composite getComposite() { throw new RuntimeException("Composite not implemented!"); } @Override public GraphicsConfiguration getDeviceConfiguration() { return new AndroidGraphicsConfiguration(); } @Override public FontRenderContext getFontRenderContext() { return new FontRenderContext(getTransform(), mP.isAntiAlias(), true); } @Override public java.awt.Paint getPaint() { throw new RuntimeException("AWT Paint not implemented in Android!"); } public static Canvas getAndroidCanvas() { return mC; } public static Paint getAndroidPaint() { return mP; } @Override public RenderingHints getRenderingHints() { return mRh; } @Override public Stroke getStroke() { if (mP != null) { return new BasicStroke(mP.getStrokeWidth(), mP.getStrokeCap() .ordinal(), mP.getStrokeJoin().ordinal()); } return null; } @Override public AffineTransform getTransform() { return new AffineTransform(createAWTMatrix(getMatrix())); } @Override public boolean hit(Rectangle rect, Shape s, boolean onStroke) { // ???AWT TODO check if on stroke return s.intersects(rect.getX(), rect.getY(), rect.getWidth(), rect .getHeight()); } @Override public void rotate(double theta) { mM.preRotate((float) AndroidGraphics2D .getDegree((float) (RAD_360 - theta))); mC.concat(mM); } @Override public void rotate(double theta, double x, double y) { mM.preRotate((float) AndroidGraphics2D.getDegree((float) theta), (float) x, (float) y); mC.concat(mM); } @Override public void scale(double sx, double sy) { mM.setScale((float) sx, (float) sy); mC.concat(mM); } @Override public void setBackground(Color color) { mBc = color; mC.clipRect(new Rect(0, 0, mC.getWidth(), mC.getHeight())); // TODO don't limit to current clip mC.drawARGB(color.getAlpha(), color.getRed(), color.getGreen(), color .getBlue()); } @Override public void setComposite(Composite comp) { throw new RuntimeException("Composite not implemented!"); } public void setSpaint(Paint paint) { mP = paint; } @Override public void setPaint(java.awt.Paint paint) { setColor((Color)paint); } @Override public Object getRenderingHint(RenderingHints.Key key) { if (mRh == null) { return null; } return mRh.get(key); } @Override public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { if (mRh == null) { mRh = new RenderingHints(hintKey, hintValue); } else { mRh.put(hintKey, hintValue); } applyHints(); } @Override public void setRenderingHints(Map<?, ?> hints) { mRh = (RenderingHints) hints; applyHints(); } private void applyHints() { Object o; // TODO do something like this: /* * Set s = mRh.keySet(); Iterator it = s.iterator(); while(it.hasNext()) { * o = it.next(); } */ // ///////////////////////////////////////////////////////////////////// // not supported in skia /* * o = mRh.get(RenderingHints.KEY_ALPHA_INTERPOLATION); if * (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) { } else * if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) { } * else if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) { } * * o = mRh.get(RenderingHints.KEY_COLOR_RENDERING); if * (o.equals(RenderingHints.VALUE_COLOR_RENDER_DEFAULT)) { } else if * (o.equals(RenderingHints.VALUE_COLOR_RENDER_QUALITY)) { } else if * (o.equals(RenderingHints.VALUE_COLOR_RENDER_SPEED)) { } * * o = mRh.get(RenderingHints.KEY_DITHERING); if * (o.equals(RenderingHints.VALUE_DITHER_DEFAULT)) { } else if * (o.equals(RenderingHints.VALUE_DITHER_DISABLE)) { } else if * (o.equals(RenderingHints.VALUE_DITHER_ENABLE)) { } * * o = mRh.get(RenderingHints.KEY_FRACTIONALMETRICS); if * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT)) { } else * if (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_OFF)) { } else if * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_ON)) { } * * o = mRh.get(RenderingHints.KEY_INTERPOLATION); if * (o.equals(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) { } else if * (o.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) { } else if * (o .equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) { } * * o = mRh.get(RenderingHints.KEY_RENDERING); if * (o.equals(RenderingHints.VALUE_RENDER_DEFAULT)) { } else if * (o.equals(RenderingHints.VALUE_RENDER_QUALITY)) { } else if * (o.equals(RenderingHints.VALUE_RENDER_SPEED)) { } * * o = mRh.get(RenderingHints.KEY_STROKE_CONTROL); if * (o.equals(RenderingHints.VALUE_STROKE_DEFAULT)) { } else if * (o.equals(RenderingHints.VALUE_STROKE_NORMALIZE)) { } else if * (o.equals(RenderingHints.VALUE_STROKE_PURE)) { } */ o = mRh.get(RenderingHints.KEY_ANTIALIASING); if (o != null) { if (o.equals(RenderingHints.VALUE_ANTIALIAS_DEFAULT)) { mP.setAntiAlias(false); } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_OFF)) { mP.setAntiAlias(false); } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_ON)) { mP.setAntiAlias(true); } } o = mRh.get(RenderingHints.KEY_TEXT_ANTIALIASING); if (o != null) { if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT)) { mP.setAntiAlias(false); } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)) { mP.setAntiAlias(false); } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_ON)) { mP.setAntiAlias(true); } } } @Override public void setStroke(Stroke s) { if (mP == null) { mP = new Paint(); } BasicStroke bs = (BasicStroke) s; mP.setStyle(Paint.Style.STROKE); mP.setStrokeWidth(bs.getLineWidth()); int cap = bs.getEndCap(); if (cap == 0) { mP.setStrokeCap(Paint.Cap.BUTT); } else if (cap == 1) { mP.setStrokeCap(Paint.Cap.ROUND); } else if (cap == 2) { mP.setStrokeCap(Paint.Cap.SQUARE); } int join = bs.getLineJoin(); if (join == 0) { mP.setStrokeJoin(Paint.Join.MITER); } else if (join == 1) { mP.setStrokeJoin(Paint.Join.ROUND); } else if (join == 2) { mP.setStrokeJoin(Paint.Join.BEVEL); } } public static float[] createMatrix(AffineTransform Tx) { double[] at = new double[9]; Tx.getMatrix(at); float[] f = new float[at.length]; f[0] = (float) at[0]; f[1] = (float) at[2]; f[2] = (float) at[4]; f[3] = (float) at[1]; f[4] = (float) at[3]; f[5] = (float) at[5]; f[6] = 0; f[7] = 0; f[8] = 1; return f; } private float[] createAWTMatrix(float[] matrix) { float[] at = new float[9]; at[0] = matrix[0]; at[1] = matrix[3]; at[2] = matrix[1]; at[3] = matrix[4]; at[4] = matrix[2]; at[5] = matrix[5]; at[6] = 0; at[7] = 0; at[8] = 1; return at; } public static Matrix createMatrixObj(AffineTransform Tx) { Matrix m = new Matrix(); m.reset(); m.setValues(createMatrix(Tx)); return m; } @Override public void setTransform(AffineTransform Tx) { mM.reset(); /* * if(Tx.isIdentity()) { mM = new Matrix(); } */ mM.setValues(createMatrix(Tx)); Matrix m = new Matrix(); m.setValues(getInverseMatrix()); mC.concat(m); mC.concat(mM); } @Override public void shear(double shx, double shy) { mM.setSkew((float) shx, (float) shy); mC.concat(mM); } @Override public void transform(AffineTransform Tx) { Matrix m = new Matrix(); m.setValues(createMatrix(Tx)); mC.concat(m); } @Override public void translate(double tx, double ty) { mM.setTranslate((float) tx, (float) ty); mC.concat(mM); } @Override public void translate(int x, int y) { mM.setTranslate((float) x, (float) y); mC.concat(mM); } @Override public void clearRect(int x, int y, int width, int height) { mC.clipRect(x, y, x + width, y + height); if (mBc != null) { mC.drawARGB(mBc.getAlpha(), mBc.getBlue(), mBc.getGreen(), mBc .getRed()); } else { mC.drawARGB(0xff, 0xff, 0xff, 0xff); } } @Override public void clipRect(int x, int y, int width, int height) { int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y}; Shape shp = createShape(cl); mCurrClip.intersect(new Area(shp)); mC.clipRect(new Rect(x, y, x + width, y + height), Region.Op.INTERSECT); } @Override public void copyArea(int sx, int sy, int width, int height, int dx, int dy) { copyArea(mC, sx, sy, width + dx, height + dy, dx, dy); } @Override public Graphics create() { return this; } @Override public void dispose() { mC = null; mP = null; } @Override public void drawArc(int x, int y, int width, int height, int sa, int ea) { if (mP == null) { mP = new Paint(); } mP.setStrokeWidth(0); mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (ea + sa), ea, true, mP); } // ???AWT: only used for debuging, delete in final version public void drawBitmap(Bitmap bm, float x, float y, Paint p) { mC.drawBitmap(bm, x, y, null); } @Override public boolean drawImage(Image image, int x, int y, Color bgcolor, ImageObserver imageObserver) { if(image == null) { return true; } boolean done = false; boolean somebits = false; Surface srcSurf = null; if(image instanceof OffscreenImage){ OffscreenImage oi = (OffscreenImage) image; if((oi.getState() & ImageObserver.ERROR) != 0) { return false; } done = oi.prepareImage(imageObserver); somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; srcSurf = oi.getImageSurface(); }else{ done = true; srcSurf = Surface.getImageSurface(image); } if(done || somebits) { int w = srcSurf.getWidth(); int h = srcSurf.getHeight(); blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), composite, bgcolor, clip); } return done; } @Override public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) { return drawImage(image, x, y, null, imageObserver); } @Override public boolean drawImage(Image image, int x, int y, int width, int height, Color bgcolor, ImageObserver imageObserver) { if(image == null) { return true; } if(width == 0 || height == 0) { return true; } boolean done = false; boolean somebits = false; Surface srcSurf = null; if(image instanceof OffscreenImage){ OffscreenImage oi = (OffscreenImage) image; if((oi.getState() & ImageObserver.ERROR) != 0) { return false; } done = oi.prepareImage(imageObserver); somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; srcSurf = oi.getImageSurface(); }else{ done = true; srcSurf = Surface.getImageSurface(image); } if(done || somebits) { int w = srcSurf.getWidth(); int h = srcSurf.getHeight(); if(w == width && h == height){ blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), composite, bgcolor, clip); }else{ AffineTransform xform = new AffineTransform(); xform.setToScale((float)width / w, (float)height / h); blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), xform, composite, bgcolor, clip); } } return done; } @Override public boolean drawImage(Image image, int x, int y, int width, int height, ImageObserver imageObserver) { return drawImage(image, x, y, width, height, null, imageObserver); } @Override public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver imageObserver) { if(image == null) { return true; } if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) { return true; } boolean done = false; boolean somebits = false; Surface srcSurf = null; if(image instanceof OffscreenImage){ OffscreenImage oi = (OffscreenImage) image; if((oi.getState() & ImageObserver.ERROR) != 0) { return false; } done = oi.prepareImage(imageObserver); somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; srcSurf = oi.getImageSurface(); }else{ done = true; srcSurf = Surface.getImageSurface(image); } if(done || somebits) { int dstX = dx1; int dstY = dy1; int srcX = sx1; int srcY = sy1; int dstW = dx2 - dx1; int dstH = dy2 - dy1; int srcW = sx2 - sx1; int srcH = sy2 - sy1; if(srcW == dstW && srcH == dstH){ blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, (AffineTransform) transform.clone(), composite, bgcolor, clip); }else{ AffineTransform xform = new AffineTransform(); xform.setToScale((float)dstW / srcW, (float)dstH / srcH); blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH, (AffineTransform) transform.clone(), xform, composite, bgcolor, clip); } } return done; } @Override public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) { return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null, imageObserver); } @Override public void drawImage(BufferedImage bufImage, BufferedImageOp op, int x, int y) { if(bufImage == null) { return; } if(op == null) { drawImage(bufImage, x, y, null); } else if(op instanceof AffineTransformOp){ AffineTransformOp atop = (AffineTransformOp) op; AffineTransform xform = atop.getTransform(); Surface srcSurf = Surface.getImageSurface(bufImage); int w = srcSurf.getWidth(); int h = srcSurf.getHeight(); blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), xform, composite, null, clip); } else { bufImage = op.filter(bufImage, null); Surface srcSurf = Surface.getImageSurface(bufImage); int w = srcSurf.getWidth(); int h = srcSurf.getHeight(); blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(), composite, null, clip); } } @Override public boolean drawImage(Image image, AffineTransform trans, ImageObserver imageObserver) { if(image == null) { return true; } if(trans == null || trans.isIdentity()) { return drawImage(image, 0, 0, imageObserver); } boolean done = false; boolean somebits = false; Surface srcSurf = null; if(image instanceof OffscreenImage){ OffscreenImage oi = (OffscreenImage) image; if((oi.getState() & ImageObserver.ERROR) != 0) { return false; } done = oi.prepareImage(imageObserver); somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0; srcSurf = oi.getImageSurface(); }else{ done = true; srcSurf = Surface.getImageSurface(image); } if(done || somebits) { int w = srcSurf.getWidth(); int h = srcSurf.getHeight(); AffineTransform xform = (AffineTransform) transform.clone(); xform.concatenate(trans); blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite, null, clip); } return done; } @Override public void drawLine(int x1, int y1, int x2, int y2) { if (mP == null) { mP = new Paint(); } mC.drawLine(x1, y1, x2, y2, mP); } @Override public void drawOval(int x, int y, int width, int height) { if (mP == null) { mP = new Paint(); } mP.setStyle(Paint.Style.STROKE); mC.drawOval(new RectF(x, y, x + width, y + height), mP); } @Override public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) { if (mP == null) { mP = new Paint(); } mC.drawLine(xpoints[npoints - 1], ypoints[npoints - 1], xpoints[0], ypoints[0], mP); for (int i = 0; i < npoints - 1; i++) { mC.drawLine(xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1], mP); } } @Override public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) { for (int i = 0; i < npoints - 1; i++) { drawLine(xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1]); } } @Override public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if (mP == null) { mP = new Paint(); } mC.drawRoundRect(new RectF(x, y, width, height), arcWidth, arcHeight, mP); } @Override public void fillArc(int x, int y, int width, int height, int sa, int ea) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.FILL_AND_STROKE); mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (sa + ea), ea, true, mP); mP.setStyle(tmp); } @Override public void fillOval(int x, int y, int width, int height) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.FILL); mC.drawOval(new RectF(x, y, x + width, y + height), mP); mP.setStyle(tmp); } @Override public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mC.save(Canvas.CLIP_SAVE_FLAG); mP.setStyle(Paint.Style.FILL); GeneralPath filledPolygon = new GeneralPath( GeneralPath.WIND_EVEN_ODD, npoints); filledPolygon.moveTo(xpoints[0], ypoints[0]); for (int index = 1; index < xpoints.length; index++) { filledPolygon.lineTo(xpoints[index], ypoints[index]); } filledPolygon.closePath(); Path path = getPath(filledPolygon); mC.clipPath(path); mC.drawPath(path, mP); mP.setStyle(tmp); mC.restore(); } @Override public void fillRect(int x, int y, int width, int height) { if (mP == null) { mP = new Paint(); } Paint.Style tmp = mP.getStyle(); mP.setStyle(Paint.Style.FILL); mC.drawRect(new Rect(x, y, x + width, y + height), mP); mP.setStyle(tmp); } @Override public void drawRect(int x, int y, int width, int height) { int[] xpoints = { x, x, x + width, x + width }; int[] ypoints = { y, y + height, y + height, y }; drawPolygon(xpoints, ypoints, 4); } @Override public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { if (mP == null) { mP = new Paint(); } mP.setStyle(Paint.Style.FILL); mC.drawRoundRect(new RectF(x, y, x + width, y + height), arcWidth, arcHeight, mP); } @Override public Shape getClip() { return mCurrClip; } @Override public Rectangle getClipBounds() { Rect r = mC.getClipBounds(); return new Rectangle(r.left, r.top, r.width(), r.height()); } @Override public Color getColor() { if (mP != null) { return new Color(mP.getColor()); } return null; } @Override public Font getFont() { return mFnt; } @Override public FontMetrics getFontMetrics(Font font) { mFm = new FontMetricsImpl(font); return mFm; } @Override public void setClip(int x, int y, int width, int height) { int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y}; mCurrClip = new Area(createShape(cl)); mC.clipRect(x, y, x + width, y + height, Region.Op.REPLACE); } @Override public void setClip(Shape clip) { mCurrClip = new Area(clip); mC.clipPath(getPath(clip), Region.Op.REPLACE); } @Override public void setColor(Color c) { if (mP == null) { mP = new Paint(); } mP.setColor(c.getRGB()); } /** * Font mapping: * * Family: * * Android AWT * ------------------------------------- * serif Serif / TimesRoman * sans-serif SansSerif / Helvetica * monospace Monospaced / Courier * * Style: * * Android AWT * ------------------------------------- * normal Plain * bold bold * italic italic * */ @Override public void setFont(Font font) { if (font == null) { return; } if (mP == null) { mP = new Paint(); } mFnt = font; Typeface tf = null; int sty = font.getStyle(); String nam = font.getName(); String aF = ""; if (nam != null) { if (nam.equalsIgnoreCase("Serif") || nam.equalsIgnoreCase("TimesRoman")) { aF = "serif"; } else if (nam.equalsIgnoreCase("SansSerif") || nam.equalsIgnoreCase("Helvetica")) { aF = "sans-serif"; } else if (nam.equalsIgnoreCase("Monospaced") || nam.equalsIgnoreCase("Courier")) { aF = "monospace"; } } switch (sty) { case Font.PLAIN: tf = Typeface.create(aF, Typeface.NORMAL); break; case Font.BOLD: tf = Typeface.create(aF, Typeface.BOLD); break; case Font.ITALIC: tf = Typeface.create(aF, Typeface.ITALIC); break; case Font.BOLD | Font.ITALIC: tf = Typeface.create(aF, Typeface.BOLD_ITALIC); break; default: tf = Typeface.DEFAULT; } mP.setTextSize(font.getSize()); mP.setTypeface(tf); } @Override public void drawBytes(byte[] data, int offset, int length, int x, int y) { drawString(new String(data, offset, length), x, y); } @Override public void drawPolygon(Polygon p) { drawPolygon(p.xpoints, p.ypoints, p.npoints); } @Override public void fillPolygon(Polygon p) { fillPolygon(p.xpoints, p.ypoints, p.npoints); } @Override public Rectangle getClipBounds(Rectangle r) { Shape clip = getClip(); if (clip != null) { Rectangle b = clip.getBounds(); r.x = b.x; r.y = b.y; r.width = b.width; r.height = b.height; } return r; } @Override public boolean hitClip(int x, int y, int width, int height) { return getClipBounds().intersects(new Rectangle(x, y, width, height)); } @Override public void drawChars(char[] data, int offset, int length, int x, int y) { mC.drawText(data, offset, length, x, y, mP); } @Override public void setPaintMode() { if (mP == null) { mP = new Paint(); } mP.setXfermode(null); } @Override public void setXORMode(Color color) { if (mP == null) { mP = new Paint(); } mP.setXfermode(new PixelXorXfermode(color.getRGB())); } @Override public void fill3DRect(int x, int y, int width, int height, boolean raised) { Color color = getColor(); Color colorUp, colorDown; if (raised) { colorUp = color.brighter(); colorDown = color.darker(); setColor(color); } else { colorUp = color.darker(); colorDown = color.brighter(); setColor(colorUp); } width--; height--; fillRect(x+1, y+1, width-1, height-1); setColor(colorUp); fillRect(x, y, width, 1); fillRect(x, y+1, 1, height); setColor(colorDown); fillRect(x+width, y, 1, height); fillRect(x+1, y+height, width, 1); } @Override public void draw3DRect(int x, int y, int width, int height, boolean raised) { Color color = getColor(); Color colorUp, colorDown; if (raised) { colorUp = color.brighter(); colorDown = color.darker(); } else { colorUp = color.darker(); colorDown = color.brighter(); } setColor(colorUp); fillRect(x, y, width, 1); fillRect(x, y+1, 1, height); setColor(colorDown); fillRect(x+width, y, 1, height); fillRect(x+1, y+height, width, 1); } public void copyArea(Canvas canvas, int sx, int sy, int width, int height, int dx, int dy) { sx += getTransform().getTranslateX(); sy += getTransform().getTranslateY(); NativeUtils.nativeScrollRect(canvas, new Rect(sx, sy, sx + width, sy + height), dx, dy); } }