AndroidGraphics2D.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
1/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.awt;
18
19import com.android.internal.awt.AndroidGraphicsConfiguration;
20import com.android.internal.graphics.NativeUtils;
21
22import java.awt.AlphaComposite;
23import java.awt.BasicStroke;
24import java.awt.Color;
25import java.awt.Composite;
26import java.awt.Font;
27import java.awt.FontMetrics;
28import java.awt.Graphics;
29import java.awt.Graphics2D;
30import java.awt.GraphicsConfiguration;
31import java.awt.Image;
32import java.awt.Polygon;
33import java.awt.Rectangle;
34import java.awt.RenderingHints;
35import java.awt.Shape;
36import java.awt.Stroke;
37import java.awt.font.FontRenderContext;
38import java.awt.font.GlyphVector;
39import java.awt.geom.AffineTransform;
40import java.awt.geom.Area;
41import java.awt.geom.GeneralPath;
42import java.awt.geom.NoninvertibleTransformException;
43import java.awt.geom.PathIterator;
44import java.awt.image.AffineTransformOp;
45import java.awt.image.BufferedImage;
46import java.awt.image.BufferedImageOp;
47import java.awt.image.DataBuffer;
48import java.awt.image.DirectColorModel;
49import java.awt.image.ImageObserver;
50import java.awt.image.Raster;
51import java.awt.image.RenderedImage;
52import java.awt.image.SinglePixelPackedSampleModel;
53import java.awt.image.WritableRaster;
54import java.awt.image.renderable.RenderableImage;
55import java.text.AttributedCharacterIterator;
56import java.util.Map;
57
58import org.apache.harmony.awt.gl.ImageSurface;
59import org.apache.harmony.awt.gl.MultiRectArea;
60import org.apache.harmony.awt.gl.Surface;
61import org.apache.harmony.awt.gl.font.AndroidGlyphVector;
62import org.apache.harmony.awt.gl.font.FontMetricsImpl;
63import org.apache.harmony.awt.gl.image.OffscreenImage;
64
65import android.graphics.Bitmap;
66import android.graphics.Canvas;
67import android.graphics.Matrix;
68import android.graphics.Paint;
69import android.graphics.Path;
70
71import android.graphics.Rect;
72import android.graphics.RectF;
73import android.graphics.Region;
74import android.graphics.Typeface;
75import android.graphics.PixelXorXfermode;
76import android.view.Display;
77import android.view.WindowManager;
78import android.content.Context;
79
80public class AndroidGraphics2D extends Graphics2D {
81
82    private int displayWidth, displayHeight;
83
84    protected Surface dstSurf = null;
85    protected MultiRectArea clip = null;
86
87    protected Composite composite = AlphaComposite.SrcOver;
88    protected AffineTransform transform = new AffineTransform();
89
90    private static AndroidGraphics2D mAg;
91    private static Canvas mC;
92
93    // Android Paint
94    public static Paint mP;
95
96    private static java.awt.Font mFnt;
97
98    // Cached Matrix
99    public static Matrix mM;
100    private static FontMetrics mFm;
101    private static RenderingHints mRh;
102    private static Color mBc;
103
104    private Area mCurrClip;
105
106    public final static double RAD_360 = Math.PI / 180 * 360;
107
108    // Image drawing
109    private AndroidJavaBlitter blitter;
110    private DirectColorModel cm;
111    private SinglePixelPackedSampleModel sm;
112    private WritableRaster wr;
113
114
115    public static AndroidGraphics2D getInstance() {
116        if (mAg == null) {
117            throw new RuntimeException("AndroidGraphics2D not instantiated!");
118        }
119        return mAg;
120    }
121
122    public static AndroidGraphics2D getInstance(Context ctx, Canvas c, Paint p) {
123        if (c == null || ctx == null) {
124            throw new RuntimeException(
125                    "Illegal argument, Canvas cannot be null!");
126        }
127        mAg = new AndroidGraphics2D(ctx, c, p);
128        return mAg;
129    }
130
131    private AndroidGraphics2D(Context ctx, Canvas c, Paint p) {
132        super();
133        mC = c;
134        mP = p;
135        mM = new Matrix();
136        mM.reset();
137        mM = mC.getMatrix();
138        Rect r = mC.getClipBounds();
139        int cl[] = {-1, r.top, r.left, -2, r.top, r.right, -2, r.bottom, r.right, -2, r.bottom, r.left};
140        mCurrClip = new Area(createShape(cl));
141        if(ctx != null) {
142            WindowManager wm = (WindowManager)ctx.getSystemService(Context.WINDOW_SERVICE);
143            Display d = wm.getDefaultDisplay();
144            displayWidth = d.getWidth();
145            displayHeight = d.getHeight();
146        }
147        blitter = new AndroidJavaBlitter(c);
148        cm = new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000);
149        sm = new SinglePixelPackedSampleModel(
150                DataBuffer.TYPE_INT, displayWidth, displayHeight, cm.getMasks());
151        wr = Raster.createWritableRaster(sm, null);
152        dstSurf = new ImageSurface(cm, wr);
153    }
154
155    @Override
156    public void addRenderingHints(Map<?, ?> hints) {
157        if (mRh == null) {
158            mRh = (RenderingHints) hints;
159        }
160        mRh.add((RenderingHints) hints);
161    }
162
163    public float[] getMatrix() {
164        float[] f = new float[9];
165        mC.getMatrix().getValues(f);
166        return f;
167    }
168
169    /**
170     *
171     * @return a Matrix in Android format
172     */
173    public float[] getInverseMatrix() {
174        AffineTransform af = new AffineTransform(createAWTMatrix(getMatrix()));
175        try {
176            af = af.createInverse();
177        } catch (NoninvertibleTransformException e) {
178        }
179        return createMatrix(af);
180    }
181
182    private Path getPath(Shape s) {
183        Path path = new Path();
184        PathIterator pi = s.getPathIterator(null);
185        while (pi.isDone() == false) {
186            getCurrentSegment(pi, path);
187            pi.next();
188        }
189        return path;
190    }
191
192    private void getCurrentSegment(PathIterator pi, Path path) {
193        float[] coordinates = new float[6];
194        int type = pi.currentSegment(coordinates);
195        switch (type) {
196        case PathIterator.SEG_MOVETO:
197            path.moveTo(coordinates[0], coordinates[1]);
198            break;
199        case PathIterator.SEG_LINETO:
200            path.lineTo(coordinates[0], coordinates[1]);
201            break;
202        case PathIterator.SEG_QUADTO:
203            path.quadTo(coordinates[0], coordinates[1], coordinates[2],
204                    coordinates[3]);
205            break;
206        case PathIterator.SEG_CUBICTO:
207            path.cubicTo(coordinates[0], coordinates[1], coordinates[2],
208                    coordinates[3], coordinates[4], coordinates[5]);
209            break;
210        case PathIterator.SEG_CLOSE:
211            path.close();
212            break;
213        default:
214            break;
215        }
216    }
217
218    private Shape createShape(int[] arr) {
219        Shape s = new GeneralPath();
220        for(int i = 0; i < arr.length; i++) {
221            int type = arr[i];
222            switch (type) {
223            case -1:
224                //MOVETO
225                ((GeneralPath)s).moveTo(arr[++i], arr[++i]);
226                break;
227            case -2:
228                //LINETO
229                ((GeneralPath)s).lineTo(arr[++i], arr[++i]);
230                break;
231            case -3:
232                //QUADTO
233                ((GeneralPath)s).quadTo(arr[++i], arr[++i], arr[++i],
234                        arr[++i]);
235                break;
236            case -4:
237                //CUBICTO
238                ((GeneralPath)s).curveTo(arr[++i], arr[++i], arr[++i],
239                        arr[++i], arr[++i], arr[++i]);
240                break;
241            case -5:
242                //CLOSE
243                return s;
244            default:
245                break;
246            }
247        }
248        return s;
249    }
250    /*
251    public int[] getPixels() {
252        return mC.getPixels();
253    }*/
254
255    public static float getRadian(float degree) {
256        return (float) ((Math.PI / 180) * degree);
257    }
258
259    private Shape getShape() {
260        return null;
261    }
262
263    public static float getDegree(float radian) {
264        return (float) ((180 / Math.PI) * radian);
265    }
266
267    /*
268     * Degree in radian
269     */
270    public static float getEllipsisX(float degree, float princAxis) {
271        return (float) Math.cos(degree) * princAxis;
272    }
273
274    public static float getEllipsisY(float degree, float conAxis) {
275        return (float) Math.sin(degree) * conAxis;
276    }
277
278    @Override
279    public void clip(Shape s) {
280        mC.clipPath(getPath(s));
281    }
282
283    public void setCanvas(Canvas c) {
284        mC = c;
285    }
286
287    @Override
288    public void draw(Shape s) {
289        if (mP == null) {
290            mP = new Paint();
291        }
292        Paint.Style tmp = mP.getStyle();
293        mP.setStyle(Paint.Style.STROKE);
294        mC.drawPath(getPath(s), mP);
295        mP.setStyle(tmp);
296    }
297/*
298    private ArrayList getSegments(Shape s) {
299        ArrayList arr = new ArrayList();
300        PathIterator pi = s.getPathIterator(null);
301        while (pi.isDone() == false) {
302            getCurrentSegment(pi, arr);
303            pi.next();
304        }
305        return arr;
306    }
307
308    private void getCurrentSegment(PathIterator pi, ArrayList arr) {
309        float[] coordinates = new float[6];
310        int type = pi.currentSegment(coordinates);
311        switch (type) {
312        case PathIterator.SEG_MOVETO:
313            arr.add(new Integer(-1));
314            break;
315        case PathIterator.SEG_LINETO:
316            arr.add(new Integer(-2));
317            break;
318        case PathIterator.SEG_QUADTO:
319            arr.add(new Integer(-3));
320            break;
321        case PathIterator.SEG_CUBICTO:
322            arr.add(new Integer(-4));
323            break;
324        case PathIterator.SEG_CLOSE:
325            arr.add(new Integer(-5));
326            break;
327        default:
328            break;
329        }
330    }
331*/
332    /*
333     * Convenience method, not standard AWT
334     */
335    public void draw(Path s) {
336        if (mP == null) {
337            mP = new Paint();
338        }
339        Paint.Style tmp = mP.getStyle();
340        mP.setStyle(Paint.Style.STROKE);
341        s.transform(mM);
342        mC.drawPath(s, mP);
343        mP.setStyle(tmp);
344    }
345
346    @Override
347    public void drawGlyphVector(GlyphVector g, float x, float y) {
348        // TODO draw at x, y
349        // draw(g.getOutline());
350        /*
351        Matrix matrix = new Matrix();
352        matrix.setTranslate(x, y);
353        Path pth = getPath(g.getOutline());
354        pth.transform(matrix);
355        draw(pth);
356        */
357        Path path = new Path();
358        char[] c = ((AndroidGlyphVector)g).getGlyphs();
359        mP.getTextPath(c, 0, c.length, x, y, path);
360        mC.drawPath(path, mP);
361    }
362
363    @Override
364    public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
365        throw new RuntimeException("Not implemented!");
366    }
367
368    @Override
369    public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
370        throw new RuntimeException("Not implemented!");
371    }
372
373    @Override
374    public void drawString(AttributedCharacterIterator iterator, float x,
375            float y) {
376        throw new RuntimeException("AttributedCharacterIterator not supported!");
377
378    }
379
380    @Override
381    public void drawString(AttributedCharacterIterator iterator, int x, int y) {
382        throw new RuntimeException("AttributedCharacterIterator not supported!");
383
384    }
385
386    @Override
387    public void drawString(String s, float x, float y) {
388            if (mP == null) {
389                mP = new Paint();
390            }
391            Paint.Style tmp = mP.getStyle();
392
393            mP.setStyle(Paint.Style.FILL);
394            Path pth = new Path();
395            mP.getTextPath(s, 0, s.length(), x, y, pth);
396            mC.drawPath(pth, mP);
397            mP.setStyle(tmp);
398    }
399
400    @Override
401    public void drawString(String str, int x, int y) {
402            if (mP == null) {
403                mP = new Paint();
404            }
405            Paint.Style tmp = mP.getStyle();
406            mP.setStrokeWidth(0);
407
408            mC.drawText(str.toCharArray(), 0, str.toCharArray().length, x, y,
409                    mP);
410            mP.setStyle(tmp);
411    }
412
413    @Override
414    public void fill(Shape s) {
415            if (mP == null) {
416                mP = new Paint();
417            }
418            Paint.Style tmp = mP.getStyle();
419            mP.setStyle(Paint.Style.FILL);
420            mC.drawPath(getPath(s), mP);
421            mP.setStyle(tmp);
422    }
423
424    @Override
425    public Color getBackground() {
426        return mBc;
427    }
428
429    @Override
430    public Composite getComposite() {
431        throw new RuntimeException("Composite not implemented!");
432    }
433
434    @Override
435    public GraphicsConfiguration getDeviceConfiguration() {
436        return new AndroidGraphicsConfiguration();
437    }
438
439    @Override
440    public FontRenderContext getFontRenderContext() {
441        return new FontRenderContext(getTransform(), mP.isAntiAlias(), true);
442    }
443
444    @Override
445    public java.awt.Paint getPaint() {
446        throw new RuntimeException("AWT Paint not implemented in Android!");
447    }
448
449    public static Canvas getAndroidCanvas() {
450        return mC;
451    }
452
453    public static Paint getAndroidPaint() {
454        return mP;
455    }
456
457    @Override
458    public RenderingHints getRenderingHints() {
459        return mRh;
460    }
461
462    @Override
463    public Stroke getStroke() {
464        if (mP != null) {
465            return new BasicStroke(mP.getStrokeWidth(), mP.getStrokeCap()
466                    .ordinal(), mP.getStrokeJoin().ordinal());
467        }
468        return null;
469    }
470
471    @Override
472    public AffineTransform getTransform() {
473        return new AffineTransform(createAWTMatrix(getMatrix()));
474    }
475
476    @Override
477    public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
478        // ???AWT TODO check if on stroke
479        return s.intersects(rect.getX(), rect.getY(), rect.getWidth(), rect
480                .getHeight());
481    }
482
483    @Override
484    public void rotate(double theta) {
485        mM.preRotate((float) AndroidGraphics2D
486                .getDegree((float) (RAD_360 - theta)));
487        mC.concat(mM);
488    }
489
490    @Override
491    public void rotate(double theta, double x, double y) {
492        mM.preRotate((float) AndroidGraphics2D.getDegree((float) theta),
493                (float) x, (float) y);
494        mC.concat(mM);
495    }
496
497    @Override
498    public void scale(double sx, double sy) {
499        mM.setScale((float) sx, (float) sy);
500        mC.concat(mM);
501    }
502
503    @Override
504    public void setBackground(Color color) {
505        mBc = color;
506        mC.clipRect(new Rect(0, 0, mC.getWidth(), mC.getHeight()));
507        // TODO don't limit to current clip
508        mC.drawARGB(color.getAlpha(), color.getRed(), color.getGreen(), color
509                .getBlue());
510    }
511
512    @Override
513    public void setComposite(Composite comp) {
514        throw new RuntimeException("Composite not implemented!");
515    }
516
517    public void setSpaint(Paint paint) {
518        mP = paint;
519    }
520
521    @Override
522    public void setPaint(java.awt.Paint paint) {
523        setColor((Color)paint);
524    }
525
526    @Override
527    public Object getRenderingHint(RenderingHints.Key key) {
528        if (mRh == null) {
529            return null;
530        }
531        return mRh.get(key);
532    }
533
534    @Override
535    public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) {
536        if (mRh == null) {
537            mRh = new RenderingHints(hintKey, hintValue);
538        } else {
539            mRh.put(hintKey, hintValue);
540        }
541        applyHints();
542    }
543
544    @Override
545    public void setRenderingHints(Map<?, ?> hints) {
546        mRh = (RenderingHints) hints;
547        applyHints();
548    }
549
550    private void applyHints() {
551        Object o;
552
553        // TODO do something like this:
554        /*
555         * Set s = mRh.keySet(); Iterator it = s.iterator(); while(it.hasNext()) {
556         * o = it.next(); }
557         */
558
559        // /////////////////////////////////////////////////////////////////////
560        // not supported in skia
561        /*
562         * o = mRh.get(RenderingHints.KEY_ALPHA_INTERPOLATION); if
563         * (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT)) { } else
564         * if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)) { }
565         * else if (o.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED)) { }
566         *
567         * o = mRh.get(RenderingHints.KEY_COLOR_RENDERING); if
568         * (o.equals(RenderingHints.VALUE_COLOR_RENDER_DEFAULT)) { } else if
569         * (o.equals(RenderingHints.VALUE_COLOR_RENDER_QUALITY)) { } else if
570         * (o.equals(RenderingHints.VALUE_COLOR_RENDER_SPEED)) { }
571         *
572         * o = mRh.get(RenderingHints.KEY_DITHERING); if
573         * (o.equals(RenderingHints.VALUE_DITHER_DEFAULT)) { } else if
574         * (o.equals(RenderingHints.VALUE_DITHER_DISABLE)) { } else if
575         * (o.equals(RenderingHints.VALUE_DITHER_ENABLE)) { }
576         *
577         * o = mRh.get(RenderingHints.KEY_FRACTIONALMETRICS); if
578         * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT)) { } else
579         * if (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_OFF)) { } else if
580         * (o.equals(RenderingHints.VALUE_FRACTIONALMETRICS_ON)) { }
581         *
582         * o = mRh.get(RenderingHints.KEY_INTERPOLATION); if
583         * (o.equals(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) { } else if
584         * (o.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) { } else if
585         * (o .equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) { }
586         *
587         * o = mRh.get(RenderingHints.KEY_RENDERING); if
588         * (o.equals(RenderingHints.VALUE_RENDER_DEFAULT)) { } else if
589         * (o.equals(RenderingHints.VALUE_RENDER_QUALITY)) { } else if
590         * (o.equals(RenderingHints.VALUE_RENDER_SPEED)) { }
591         *
592         * o = mRh.get(RenderingHints.KEY_STROKE_CONTROL); if
593         * (o.equals(RenderingHints.VALUE_STROKE_DEFAULT)) { } else if
594         * (o.equals(RenderingHints.VALUE_STROKE_NORMALIZE)) { } else if
595         * (o.equals(RenderingHints.VALUE_STROKE_PURE)) { }
596         */
597
598        o = mRh.get(RenderingHints.KEY_ANTIALIASING);
599        if (o != null) {
600            if (o.equals(RenderingHints.VALUE_ANTIALIAS_DEFAULT)) {
601                mP.setAntiAlias(false);
602            } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_OFF)) {
603                mP.setAntiAlias(false);
604            } else if (o.equals(RenderingHints.VALUE_ANTIALIAS_ON)) {
605                mP.setAntiAlias(true);
606            }
607        }
608
609        o = mRh.get(RenderingHints.KEY_TEXT_ANTIALIASING);
610        if (o != null) {
611            if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT)) {
612                mP.setAntiAlias(false);
613            } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)) {
614                mP.setAntiAlias(false);
615            } else if (o.equals(RenderingHints.VALUE_TEXT_ANTIALIAS_ON)) {
616                mP.setAntiAlias(true);
617            }
618        }
619    }
620
621    @Override
622    public void setStroke(Stroke s) {
623        if (mP == null) {
624            mP = new Paint();
625        }
626        BasicStroke bs = (BasicStroke) s;
627        mP.setStyle(Paint.Style.STROKE);
628        mP.setStrokeWidth(bs.getLineWidth());
629
630        int cap = bs.getEndCap();
631        if (cap == 0) {
632            mP.setStrokeCap(Paint.Cap.BUTT);
633        } else if (cap == 1) {
634            mP.setStrokeCap(Paint.Cap.ROUND);
635        } else if (cap == 2) {
636            mP.setStrokeCap(Paint.Cap.SQUARE);
637        }
638
639        int join = bs.getLineJoin();
640        if (join == 0) {
641            mP.setStrokeJoin(Paint.Join.MITER);
642        } else if (join == 1) {
643            mP.setStrokeJoin(Paint.Join.ROUND);
644        } else if (join == 2) {
645            mP.setStrokeJoin(Paint.Join.BEVEL);
646        }
647    }
648
649    public static float[] createMatrix(AffineTransform Tx) {
650        double[] at = new double[9];
651        Tx.getMatrix(at);
652        float[] f = new float[at.length];
653        f[0] = (float) at[0];
654        f[1] = (float) at[2];
655        f[2] = (float) at[4];
656        f[3] = (float) at[1];
657        f[4] = (float) at[3];
658        f[5] = (float) at[5];
659        f[6] = 0;
660        f[7] = 0;
661        f[8] = 1;
662        return f;
663    }
664
665    private float[] createAWTMatrix(float[] matrix) {
666        float[] at = new float[9];
667        at[0] = matrix[0];
668        at[1] = matrix[3];
669        at[2] = matrix[1];
670        at[3] = matrix[4];
671        at[4] = matrix[2];
672        at[5] = matrix[5];
673        at[6] = 0;
674        at[7] = 0;
675        at[8] = 1;
676        return at;
677    }
678
679    public static Matrix createMatrixObj(AffineTransform Tx) {
680        Matrix m = new Matrix();
681        m.reset();
682        m.setValues(createMatrix(Tx));
683        return m;
684    }
685
686    @Override
687    public void setTransform(AffineTransform Tx) {
688        mM.reset();
689        /*
690         * if(Tx.isIdentity()) { mM = new Matrix(); }
691         */
692        mM.setValues(createMatrix(Tx));
693        Matrix m = new Matrix();
694        m.setValues(getInverseMatrix());
695        mC.concat(m);
696        mC.concat(mM);
697    }
698
699    @Override
700    public void shear(double shx, double shy) {
701        mM.setSkew((float) shx, (float) shy);
702        mC.concat(mM);
703    }
704
705    @Override
706    public void transform(AffineTransform Tx) {
707        Matrix m = new Matrix();
708        m.setValues(createMatrix(Tx));
709        mC.concat(m);
710    }
711
712    @Override
713    public void translate(double tx, double ty) {
714        mM.setTranslate((float) tx, (float) ty);
715        mC.concat(mM);
716    }
717
718    @Override
719    public void translate(int x, int y) {
720        mM.setTranslate((float) x, (float) y);
721        mC.concat(mM);
722    }
723
724    @Override
725    public void clearRect(int x, int y, int width, int height) {
726        mC.clipRect(x, y, x + width, y + height);
727        if (mBc != null) {
728            mC.drawARGB(mBc.getAlpha(), mBc.getBlue(), mBc.getGreen(), mBc
729                    .getRed());
730        } else {
731            mC.drawARGB(0xff, 0xff, 0xff, 0xff);
732        }
733    }
734
735    @Override
736    public void clipRect(int x, int y, int width, int height) {
737        int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y};
738        Shape shp = createShape(cl);
739        mCurrClip.intersect(new Area(shp));
740        mC.clipRect(new Rect(x, y, x + width, y + height), Region.Op.INTERSECT);
741    }
742
743    @Override
744    public void copyArea(int sx, int sy, int width, int height, int dx, int dy) {
745        copyArea(mC, sx, sy, width + dx, height + dy, dx, dy);
746    }
747
748    @Override
749    public Graphics create() {
750        return this;
751    }
752
753    @Override
754    public void dispose() {
755            mC = null;
756            mP = null;
757    }
758
759    @Override
760    public void drawArc(int x, int y, int width, int height, int sa, int ea) {
761            if (mP == null) {
762                mP = new Paint();
763            }
764            mP.setStrokeWidth(0);
765            mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (ea + sa),
766                       ea, true, mP);
767    }
768
769
770    // ???AWT: only used for debuging, delete in final version
771    public void drawBitmap(Bitmap bm, float x, float y, Paint p) {
772        mC.drawBitmap(bm, x, y, null);
773    }
774
775    @Override
776    public boolean drawImage(Image image, int x, int y, Color bgcolor,
777            ImageObserver imageObserver) {
778
779        if(image == null) {
780            return true;
781        }
782
783        boolean done = false;
784        boolean somebits = false;
785        Surface srcSurf = null;
786        if(image instanceof OffscreenImage){
787            OffscreenImage oi = (OffscreenImage) image;
788            if((oi.getState() & ImageObserver.ERROR) != 0) {
789                return false;
790            }
791            done = oi.prepareImage(imageObserver);
792            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
793            srcSurf = oi.getImageSurface();
794        }else{
795            done = true;
796            srcSurf = Surface.getImageSurface(image);
797        }
798
799        if(done || somebits) {
800            int w = srcSurf.getWidth();
801            int h = srcSurf.getHeight();
802
803            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(),
804                    composite, bgcolor, clip);
805        }
806        return done;
807    }
808
809    @Override
810    public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) {
811        return drawImage(image, x, y, null, imageObserver);
812    }
813
814    @Override
815    public boolean drawImage(Image image, int x, int y, int width, int height,
816            Color bgcolor, ImageObserver imageObserver) {
817
818        if(image == null) {
819            return true;
820        }
821        if(width == 0 || height == 0) {
822            return true;
823        }
824
825        boolean done = false;
826        boolean somebits = false;
827        Surface srcSurf = null;
828
829        if(image instanceof OffscreenImage){
830            OffscreenImage oi = (OffscreenImage) image;
831            if((oi.getState() & ImageObserver.ERROR) != 0) {
832                return false;
833            }
834            done = oi.prepareImage(imageObserver);
835            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
836            srcSurf = oi.getImageSurface();
837        }else{
838            done = true;
839            srcSurf = Surface.getImageSurface(image);
840        }
841
842        if(done || somebits) {
843            int w = srcSurf.getWidth();
844            int h = srcSurf.getHeight();
845            if(w == width && h == height){
846                blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
847                        (AffineTransform) transform.clone(),
848                        composite, bgcolor, clip);
849            }else{
850                AffineTransform xform = new AffineTransform();
851                xform.setToScale((float)width / w, (float)height / h);
852                blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
853                        (AffineTransform) transform.clone(),
854                        xform, composite, bgcolor, clip);
855            }
856        }
857        return done;
858    }
859
860    @Override
861    public boolean drawImage(Image image, int x, int y, int width, int height,
862            ImageObserver imageObserver) {
863        return drawImage(image, x, y, width, height, null, imageObserver);
864    }
865
866    @Override
867    public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
868            int sx1, int sy1, int sx2, int sy2, Color bgcolor,
869            ImageObserver imageObserver) {
870
871        if(image == null) {
872            return true;
873        }
874        if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) {
875            return true;
876        }
877
878        boolean done = false;
879        boolean somebits = false;
880        Surface srcSurf = null;
881        if(image instanceof OffscreenImage){
882            OffscreenImage oi = (OffscreenImage) image;
883            if((oi.getState() & ImageObserver.ERROR) != 0) {
884                return false;
885            }
886            done = oi.prepareImage(imageObserver);
887            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
888            srcSurf = oi.getImageSurface();
889        }else{
890            done = true;
891            srcSurf = Surface.getImageSurface(image);
892        }
893
894        if(done || somebits) {
895
896            int dstX = dx1;
897            int dstY = dy1;
898            int srcX = sx1;
899            int srcY = sy1;
900
901            int dstW = dx2 - dx1;
902            int dstH = dy2 - dy1;
903            int srcW = sx2 - sx1;
904            int srcH = sy2 - sy1;
905
906            if(srcW == dstW && srcH == dstH){
907                blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
908                        (AffineTransform) transform.clone(),
909                        composite, bgcolor, clip);
910            }else{
911                AffineTransform xform = new AffineTransform();
912                xform.setToScale((float)dstW / srcW, (float)dstH / srcH);
913                blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
914                        (AffineTransform) transform.clone(),
915                        xform, composite, bgcolor, clip);
916            }
917        }
918        return done;
919    }
920
921    @Override
922    public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
923            int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) {
924
925        return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
926                imageObserver);
927     }
928
929    @Override
930    public void drawImage(BufferedImage bufImage, BufferedImageOp op,
931            int x, int y) {
932
933        if(bufImage == null) {
934            return;
935        }
936
937        if(op == null) {
938            drawImage(bufImage, x, y, null);
939        } else if(op instanceof AffineTransformOp){
940            AffineTransformOp atop = (AffineTransformOp) op;
941            AffineTransform xform = atop.getTransform();
942            Surface srcSurf = Surface.getImageSurface(bufImage);
943            int w = srcSurf.getWidth();
944            int h = srcSurf.getHeight();
945            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
946                    (AffineTransform) transform.clone(), xform,
947                    composite, null, clip);
948        } else {
949            bufImage = op.filter(bufImage, null);
950            Surface srcSurf = Surface.getImageSurface(bufImage);
951            int w = srcSurf.getWidth();
952            int h = srcSurf.getHeight();
953            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
954                    (AffineTransform) transform.clone(),
955                    composite, null, clip);
956        }
957    }
958
959    @Override
960    public boolean drawImage(Image image, AffineTransform trans,
961            ImageObserver imageObserver) {
962
963        if(image == null) {
964            return true;
965        }
966        if(trans == null || trans.isIdentity()) {
967            return drawImage(image, 0, 0, imageObserver);
968        }
969
970        boolean done = false;
971        boolean somebits = false;
972        Surface srcSurf = null;
973        if(image instanceof OffscreenImage){
974            OffscreenImage oi = (OffscreenImage) image;
975            if((oi.getState() & ImageObserver.ERROR) != 0) {
976                return false;
977            }
978            done = oi.prepareImage(imageObserver);
979            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
980            srcSurf = oi.getImageSurface();
981        }else{
982            done = true;
983            srcSurf = Surface.getImageSurface(image);
984        }
985
986        if(done || somebits) {
987            int w = srcSurf.getWidth();
988            int h = srcSurf.getHeight();
989            AffineTransform xform = (AffineTransform) transform.clone();
990            xform.concatenate(trans);
991            blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite,
992                    null, clip);
993        }
994        return done;
995    }
996
997    @Override
998    public void drawLine(int x1, int y1, int x2, int y2) {
999        if (mP == null) {
1000            mP = new Paint();
1001        }
1002            mC.drawLine(x1, y1, x2, y2, mP);
1003    }
1004
1005    @Override
1006    public void drawOval(int x, int y, int width, int height) {
1007            if (mP == null) {
1008                mP = new Paint();
1009            }
1010            mP.setStyle(Paint.Style.STROKE);
1011            mC.drawOval(new RectF(x, y, x + width, y + height), mP);
1012    }
1013
1014    @Override
1015    public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) {
1016            if (mP == null) {
1017                mP = new Paint();
1018            }
1019            mC.drawLine(xpoints[npoints - 1], ypoints[npoints - 1], xpoints[0],
1020                    ypoints[0], mP);
1021            for (int i = 0; i < npoints - 1; i++) {
1022                mC.drawLine(xpoints[i], ypoints[i], xpoints[i + 1],
1023                        ypoints[i + 1], mP);
1024            }
1025    }
1026
1027    @Override
1028    public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) {
1029        for (int i = 0; i < npoints - 1; i++) {
1030            drawLine(xpoints[i], ypoints[i], xpoints[i + 1], ypoints[i + 1]);
1031        }
1032
1033    }
1034
1035    @Override
1036    public void drawRoundRect(int x, int y, int width, int height,
1037            int arcWidth, int arcHeight) {
1038            if (mP == null) {
1039                mP = new Paint();
1040            }
1041            mC.drawRoundRect(new RectF(x, y, width, height), arcWidth,
1042                    arcHeight, mP);
1043    }
1044
1045    @Override
1046    public void fillArc(int x, int y, int width, int height, int sa, int ea) {
1047            if (mP == null) {
1048                mP = new Paint();
1049            }
1050
1051            Paint.Style tmp = mP.getStyle();
1052            mP.setStyle(Paint.Style.FILL_AND_STROKE);
1053            mC.drawArc(new RectF(x, y, x + width, y + height), 360 - (sa + ea),
1054                    ea, true, mP);
1055
1056            mP.setStyle(tmp);
1057    }
1058
1059    @Override
1060    public void fillOval(int x, int y, int width, int height) {
1061            if (mP == null) {
1062                mP = new Paint();
1063            }
1064            Paint.Style tmp = mP.getStyle();
1065            mP.setStyle(Paint.Style.FILL);
1066            mC.drawOval(new RectF(x, y, x + width, y + height), mP);
1067            mP.setStyle(tmp);
1068    }
1069
1070    @Override
1071    public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) {
1072            if (mP == null) {
1073                mP = new Paint();
1074            }
1075            Paint.Style tmp = mP.getStyle();
1076            mC.save(Canvas.CLIP_SAVE_FLAG);
1077
1078            mP.setStyle(Paint.Style.FILL);
1079
1080            GeneralPath filledPolygon = new GeneralPath(
1081                    GeneralPath.WIND_EVEN_ODD, npoints);
1082            filledPolygon.moveTo(xpoints[0], ypoints[0]);
1083            for (int index = 1; index < xpoints.length; index++) {
1084                filledPolygon.lineTo(xpoints[index], ypoints[index]);
1085            }
1086            filledPolygon.closePath();
1087            Path path = getPath(filledPolygon);
1088            mC.clipPath(path);
1089            mC.drawPath(path, mP);
1090
1091            mP.setStyle(tmp);
1092            mC.restore();
1093    }
1094
1095    @Override
1096    public void fillRect(int x, int y, int width, int height) {
1097            if (mP == null) {
1098                mP = new Paint();
1099            }
1100            Paint.Style tmp = mP.getStyle();
1101            mP.setStyle(Paint.Style.FILL);
1102            mC.drawRect(new Rect(x, y, x + width, y + height), mP);
1103            mP.setStyle(tmp);
1104    }
1105
1106    @Override
1107    public void drawRect(int x, int y, int width, int height) {
1108        int[] xpoints = { x, x, x + width, x + width };
1109        int[] ypoints = { y, y + height, y + height, y };
1110        drawPolygon(xpoints, ypoints, 4);
1111    }
1112
1113    @Override
1114    public void fillRoundRect(int x, int y, int width, int height,
1115            int arcWidth, int arcHeight) {
1116            if (mP == null) {
1117                mP = new Paint();
1118            }
1119            mP.setStyle(Paint.Style.FILL);
1120            mC.drawRoundRect(new RectF(x, y, x + width, y + height), arcWidth,
1121                    arcHeight, mP);
1122    }
1123
1124    @Override
1125    public Shape getClip() {
1126        return mCurrClip;
1127    }
1128
1129    @Override
1130    public Rectangle getClipBounds() {
1131            Rect r = mC.getClipBounds();
1132            return new Rectangle(r.left, r.top, r.width(), r.height());
1133    }
1134
1135    @Override
1136    public Color getColor() {
1137        if (mP != null) {
1138            return new Color(mP.getColor());
1139        }
1140        return null;
1141    }
1142
1143    @Override
1144    public Font getFont() {
1145        return mFnt;
1146    }
1147
1148    @Override
1149    public FontMetrics getFontMetrics(Font font) {
1150        mFm = new FontMetricsImpl(font);
1151        return mFm;
1152    }
1153
1154    @Override
1155    public void setClip(int x, int y, int width, int height) {
1156        int cl[] = {-1, x, y, -2, x, y + width, -2, x + height, y + width, -2, x + height, y};
1157        mCurrClip = new Area(createShape(cl));
1158        mC.clipRect(x, y, x + width, y + height, Region.Op.REPLACE);
1159
1160    }
1161
1162    @Override
1163    public void setClip(Shape clip) {
1164        mCurrClip = new Area(clip);
1165        mC.clipPath(getPath(clip), Region.Op.REPLACE);
1166    }
1167
1168    @Override
1169    public void setColor(Color c) {
1170        if (mP == null) {
1171            mP = new Paint();
1172        }
1173        mP.setColor(c.getRGB());
1174    }
1175
1176    /**
1177     * Font mapping:
1178     *
1179     * Family:
1180     *
1181     * Android         AWT
1182     * -------------------------------------
1183     * serif           Serif / TimesRoman
1184     * sans-serif      SansSerif / Helvetica
1185     * monospace       Monospaced / Courier
1186     *
1187     * Style:
1188     *
1189     * Android            AWT
1190     * -------------------------------------
1191     * normal          Plain
1192     * bold            bold
1193     * italic          italic
1194     *
1195     */
1196    @Override
1197    public void setFont(Font font) {
1198        if (font == null) {
1199            return;
1200        }
1201        if (mP == null) {
1202            mP = new Paint();
1203        }
1204
1205        mFnt = font;
1206        Typeface tf = null;
1207        int sty = font.getStyle();
1208        String nam = font.getName();
1209        String aF = "";
1210        if (nam != null) {
1211            if (nam.equalsIgnoreCase("Serif")
1212                    || nam.equalsIgnoreCase("TimesRoman")) {
1213                aF = "serif";
1214            } else if (nam.equalsIgnoreCase("SansSerif")
1215                    || nam.equalsIgnoreCase("Helvetica")) {
1216                aF = "sans-serif";
1217            } else if (nam.equalsIgnoreCase("Monospaced")
1218                    || nam.equalsIgnoreCase("Courier")) {
1219                aF = "monospace";
1220            }
1221        }
1222
1223        switch (sty) {
1224        case Font.PLAIN:
1225            tf = Typeface.create(aF, Typeface.NORMAL);
1226            break;
1227        case Font.BOLD:
1228            tf = Typeface.create(aF, Typeface.BOLD);
1229            break;
1230        case Font.ITALIC:
1231            tf = Typeface.create(aF, Typeface.ITALIC);
1232            break;
1233        case Font.BOLD | Font.ITALIC:
1234            tf = Typeface.create(aF, Typeface.BOLD_ITALIC);
1235            break;
1236        default:
1237            tf = Typeface.DEFAULT;
1238        }
1239
1240        mP.setTextSize(font.getSize());
1241        mP.setTypeface(tf);
1242    }
1243
1244    @Override
1245    public void drawBytes(byte[] data, int offset, int length, int x, int y) {
1246        drawString(new String(data, offset, length), x, y);
1247    }
1248
1249    @Override
1250    public void drawPolygon(Polygon p) {
1251        drawPolygon(p.xpoints, p.ypoints, p.npoints);
1252    }
1253
1254    @Override
1255    public void fillPolygon(Polygon p) {
1256        fillPolygon(p.xpoints, p.ypoints, p.npoints);
1257    }
1258
1259    @Override
1260    public Rectangle getClipBounds(Rectangle r) {
1261        Shape clip = getClip();
1262        if (clip != null) {
1263            Rectangle b = clip.getBounds();
1264            r.x = b.x;
1265            r.y = b.y;
1266            r.width = b.width;
1267            r.height = b.height;
1268        }
1269        return r;
1270    }
1271
1272    @Override
1273    public boolean hitClip(int x, int y, int width, int height) {
1274        return getClipBounds().intersects(new Rectangle(x, y, width, height));
1275    }
1276
1277    @Override
1278    public void drawChars(char[] data, int offset, int length, int x, int y) {
1279        mC.drawText(data, offset, length, x, y, mP);
1280    }
1281
1282    @Override
1283    public void setPaintMode() {
1284        if (mP == null) {
1285            mP = new Paint();
1286        }
1287        mP.setXfermode(null);
1288    }
1289
1290    @Override
1291    public void setXORMode(Color color) {
1292        if (mP == null) {
1293            mP = new Paint();
1294        }
1295        mP.setXfermode(new PixelXorXfermode(color.getRGB()));
1296    }
1297
1298    @Override
1299    public void fill3DRect(int x, int y, int width, int height, boolean raised) {
1300        Color color = getColor();
1301        Color colorUp, colorDown;
1302        if (raised) {
1303            colorUp = color.brighter();
1304            colorDown = color.darker();
1305            setColor(color);
1306        } else {
1307            colorUp = color.darker();
1308            colorDown = color.brighter();
1309            setColor(colorUp);
1310        }
1311
1312        width--;
1313        height--;
1314        fillRect(x+1, y+1, width-1, height-1);
1315
1316        setColor(colorUp);
1317        fillRect(x, y, width, 1);
1318        fillRect(x, y+1, 1, height);
1319
1320        setColor(colorDown);
1321        fillRect(x+width, y, 1, height);
1322        fillRect(x+1, y+height, width, 1);
1323    }
1324
1325    @Override
1326    public void draw3DRect(int x, int y, int width, int height, boolean raised) {
1327        Color color = getColor();
1328        Color colorUp, colorDown;
1329        if (raised) {
1330            colorUp = color.brighter();
1331            colorDown = color.darker();
1332        } else {
1333            colorUp = color.darker();
1334            colorDown = color.brighter();
1335        }
1336
1337        setColor(colorUp);
1338        fillRect(x, y, width, 1);
1339        fillRect(x, y+1, 1, height);
1340
1341        setColor(colorDown);
1342        fillRect(x+width, y, 1, height);
1343        fillRect(x+1, y+height, width, 1);
1344    }
1345
1346    public void copyArea(Canvas canvas, int sx, int sy, int width, int height, int dx, int dy) {
1347        sx += getTransform().getTranslateX();
1348        sy += getTransform().getTranslateY();
1349
1350        NativeUtils.nativeScrollRect(canvas,
1351                new Rect(sx, sy, sx + width, sy + height),
1352                dx, dy);
1353    }
1354}
1355