diff --git a/app/src/main/java/com/android/internal/awt/BufferedImageGraphics2D.java b/app/src/main/java/com/android/internal/awt/BufferedImageGraphics2D.java new file mode 100644 index 0000000000..a243664bd5 --- /dev/null +++ b/app/src/main/java/com/android/internal/awt/BufferedImageGraphics2D.java @@ -0,0 +1,1350 @@ +/* + * 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 BufferedImageGraphics2D 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 Bitmap mB; + private Canvas mC; + + // Android Paint + public Paint mP; + + private java.awt.Font mFnt; + + // Cached Matrix + public Matrix mM; + private FontMetrics mFm; + private RenderingHints mRh; + private Color mBc; + private BufferedImage mImg; + + 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 BufferedImageGraphics2D getInstance(BufferedImage img, Paint p) { + if (img == null) { + throw new RuntimeException( + "Illegal argument, BufferedImage cannot be null!"); + } + return new BufferedImageGraphics2D(img, p); + } + + private BufferedImageGraphics2D(BufferedImage img, Paint p) { + super(); + displayWidth = img.getWidth(); + displayHeight = img.getHeight(); + mB = Bitmap.createBitmap(displayWidth, displayHeight, Bitmap.Config.ARGB_8888); + mC = new Canvas(mB); + mP = p; + mM = new Matrix(); + mM.reset(); + mM = mC.getMatrix(); + mImg = img; + 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)); + blitter = new AndroidJavaBlitter(mC); + 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 Canvas getAndroidCanvas() { + return mC; + } + + public 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) BufferedImageGraphics2D + .getDegree((float) (RAD_360 - theta))); + mC.concat(mM); + } + + @Override + public void rotate(double theta, double x, double y) { + mM.preRotate((float) BufferedImageGraphics2D.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() { + int[] pixels = new int[displayWidth * displayHeight]; + mB.getPixels(pixels, 0, displayWidth, 0, 0, displayWidth, displayHeight); + mImg.setRGB(0, 0, displayWidth, displayHeight, pixels, 0, displayWidth); + + 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(); // (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(); + } + if (c == null) { + c = Color.decode("#00000000"); + } + 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); + } +} diff --git a/app/src/main/java/java/awt/image/BufferedImage.java b/app/src/main/java/java/awt/image/BufferedImage.java index da5711b050..c3edb67f44 100644 --- a/app/src/main/java/java/awt/image/BufferedImage.java +++ b/app/src/main/java/java/awt/image/BufferedImage.java @@ -21,7 +21,7 @@ package java.awt.image; -import com.android.internal.awt.AndroidGraphics2D; +import com.android.internal.awt.BufferedImageGraphics2D; import java.awt.Graphics; import java.awt.Graphics2D; @@ -710,10 +710,12 @@ public Point[] getWritableTileIndices() { * @return the graphics2D object. */ public Graphics2D createGraphics() { - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + return BufferedImageGraphics2D.getInstance(this, null); + + //GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); // return ge.createGraphics(this); // ???AWT hack, FIXME - return AndroidGraphics2D.getInstance(); + //return AndroidGraphics2D.getInstance(); // throw new RuntimeException("Not implemented!"); // return null; }