1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17/**
18 * @author Alexey A. Petrenko
19 * @version $Revision$
20 */
21package org.apache.harmony.awt.gl;
22
23
24import java.awt.AlphaComposite;
25import java.awt.BasicStroke;
26import java.awt.Color;
27import java.awt.Composite;
28import java.awt.Font;
29import java.awt.FontMetrics;
30import java.awt.Graphics2D;
31import java.awt.Image;
32import java.awt.Paint;
33import java.awt.PaintContext;
34import java.awt.Point;
35import java.awt.Polygon;
36import java.awt.Rectangle;
37import java.awt.RenderingHints;
38import java.awt.Shape;
39import java.awt.Stroke;
40import java.awt.Toolkit;
41import java.awt.font.FontRenderContext;
42import java.awt.font.GlyphVector;
43import java.awt.image.AffineTransformOp;
44import java.awt.image.ImageObserver;
45import java.awt.image.BufferedImage;
46import java.awt.image.BufferedImageOp;
47import java.awt.image.Raster;
48import java.awt.image.RenderedImage;
49import java.awt.image.WritableRaster;
50import java.awt.image.renderable.RenderableImage;
51import java.awt.geom.AffineTransform;
52import java.awt.geom.Arc2D;
53import java.awt.geom.Ellipse2D;
54import java.awt.geom.Line2D;
55import java.awt.geom.PathIterator;
56import java.awt.geom.RoundRectangle2D;
57import java.text.AttributedCharacterIterator;
58import java.util.Map;
59
60import org.apache.harmony.awt.gl.Surface;
61import org.apache.harmony.awt.gl.image.OffscreenImage;
62import org.apache.harmony.awt.gl.render.Blitter;
63import org.apache.harmony.awt.gl.render.JavaArcRasterizer;
64import org.apache.harmony.awt.gl.render.JavaLineRasterizer;
65import org.apache.harmony.awt.gl.render.JavaShapeRasterizer;
66import org.apache.harmony.awt.gl.render.JavaTextRenderer;
67import org.apache.harmony.awt.gl.render.NullBlitter;
68
69/*
70 * List of abstract methods to implement in subclusses
71 * Graphics.copyArea(int x, int y, int width, int height, int dx, int dy)
72 * Graphics.create()
73 * Graphics2D.getDeviceConfiguration()
74 * CommonGraphics2D.fillMultiRectAreaColor(MultiRectArea mra);
75 * CommonGraphics2D.fillMultiRectAreaPaint(MultiRectArea mra);
76 */
77
78/**
79 * CommonGraphics2D class is a super class for all system-dependent
80 * implementations. It implements major part of Graphics and Graphics2D
81 * abstract methods.
82 * <h2>CommonGraphics2D Class Internals</h2>
83 * <h3>Line and Shape Rasterizers</h3>
84 * <p>
85 * The CommonGraphics2D class splits all shapes into a set of rectangles
86 * to unify the drawing process for different operating systems and architectures.
87 * For this purpose Java 2D* uses the JavaShapeRasterizer and the JavaLineRasterizer
88 * classes from the org.apache.harmony.awt.gl.render package. The JavaShapeRasterizer
89 * class splits an object implementing a Shape interface into a set of rectangles and
90 * produces a MultiRectArea object. The JavaLineRasterizer class makes line drawing
91 * more accurate and processes lines with strokes, which are instances of the BasicStroke
92 * class.
93 * </p>
94 * <p>
95 * To port the shape drawing to another platform you just need to override
96 * rectangle-drawing methods. However, if your operating system has functions to draw
97 * particular shapes, you can optimize your subclass of the CommonGraphics2D class by
98 * using this functionality in overridden methods.
99 * </p>
100
101 * <h3>Blitters</h3>
102 * <p>
103 * Blitter classes draw images on the display or buffered images. All blitters inherit
104 * the org.apache.harmony.awt.gl.render.Blitter interface.
105 * </p>
106 * <p>Blitters are divided into:
107 * <ul>
108 * <li>Native blitters for simple types of images, which the underlying native library
109 * can draw.</li>
110 * <li>Java* blitters for those types of images, which the underlying native library
111 * cannot handle.</li>
112 * </ul></p>
113 * <p>
114 * DRL Java 2D* also uses blitters to fill the shapes and the user-defined subclasses
115 * of the java.awt.Paint class with paints, which the system does not support.
116 * </p>
117 *
118 *<h3>Text Renderers</h3>
119 *<p>
120 *Text renderers draw strings and glyph vectors. All text renderers are subclasses
121 *of the org.apache.harmony.awt.gl.TextRenderer class.
122 *</p>
123 *
124 */
125public abstract class CommonGraphics2D extends Graphics2D {
126    protected Surface dstSurf = null;
127    protected Blitter blitter = NullBlitter.getInstance();
128    protected RenderingHints hints = new RenderingHints(null);
129
130    // Clipping things
131    protected MultiRectArea clip = null;
132
133    protected Paint paint = Color.WHITE;
134    protected Color fgColor = Color.WHITE;
135    protected Color bgColor = Color.BLACK;
136
137    protected Composite composite = AlphaComposite.SrcOver;
138
139    protected Stroke stroke = new BasicStroke();
140
141    //TODO: Think more about FontRenderContext
142    protected FontRenderContext frc = new FontRenderContext(null, false, false);
143
144    protected JavaShapeRasterizer jsr = new JavaShapeRasterizer();
145
146    protected Font font = new Font("Dialog", Font.PLAIN, 12);; //$NON-NLS-1$
147
148    protected TextRenderer jtr = JavaTextRenderer.inst;
149
150    // Current graphics transform
151    protected AffineTransform transform = new AffineTransform();
152    protected double[] matrix = new double[6];
153
154    // Original user->device translation as transform and point
155    //public AffineTransform origTransform = new AffineTransform();
156    public Point origPoint = new Point(0, 0);
157
158
159    // Print debug output or not
160    protected static final boolean debugOutput = "1".equals(System.getProperty("g2d.debug")); //$NON-NLS-1$ //$NON-NLS-2$
161
162    // Constructors
163    protected CommonGraphics2D() {
164    }
165
166    protected CommonGraphics2D(int tx, int ty) {
167        this(tx, ty, null);
168    }
169
170    protected CommonGraphics2D(int tx, int ty, MultiRectArea clip) {
171        setTransform(AffineTransform.getTranslateInstance(tx, ty));
172        //origTransform = AffineTransform.getTranslateInstance(tx, ty);
173        origPoint = new Point(tx, ty);
174        setClip(clip);
175    }
176
177    // Public methods
178    @Override
179    public void addRenderingHints(Map<?,?> hints) {
180        this.hints.putAll(hints);
181    }
182
183    @Override
184    public void clearRect(int x, int y, int width, int height) {
185        Color c = getColor();
186        Paint p = getPaint();
187        setColor(getBackground());
188        fillRect(x, y, width, height);
189        setColor(c);
190        setPaint(p);
191        if (debugOutput) {
192            System.err.println("CommonGraphics2D.clearRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
193        }
194    }
195
196    @Override
197    public void clipRect(int x, int y, int width, int height) {
198        clip(new Rectangle(x, y, width, height));
199    }
200
201
202    @Override
203    public void clip(Shape s) {
204        if (s == null) {
205            clip = null;
206            return;
207        }
208
209        MultiRectArea mra = null;
210        if (s instanceof MultiRectArea) {
211            mra = new MultiRectArea((MultiRectArea)s);
212            mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
213        } else {
214            int type = transform.getType();
215            if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY |
216                AffineTransform.TYPE_TRANSLATION)) != 0){
217                    mra = new MultiRectArea((Rectangle)s);
218                    if(type == AffineTransform.TYPE_TRANSLATION){
219                        mra.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
220                    }
221            } else {
222                s = transform.createTransformedShape(s);
223                mra = jsr.rasterize(s, 0.5);
224            }
225        }
226
227        if (clip == null) {
228            setTransformedClip(mra);
229        } else {
230            clip.intersect(mra);
231            setTransformedClip(clip);
232        }
233    }
234
235    @Override
236    public void dispose() {
237        // Do nothing for Java only classes
238    }
239
240
241
242
243    /***************************************************************************
244     *
245     *  Draw methods
246     *
247     ***************************************************************************/
248
249    @Override
250    public void draw(Shape s) {
251        if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) {
252            //TODO: Think about drawing the shape in one fillMultiRectArea call
253            BasicStroke bstroke = (BasicStroke)stroke;
254            JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase());
255            PathIterator pi = s.getPathIterator(transform, 0.5);
256            float []points = new float[6];
257            int x1 = Integer.MIN_VALUE;
258            int y1 = Integer.MIN_VALUE;
259            int cx1 = Integer.MIN_VALUE;
260            int cy1 = Integer.MIN_VALUE;
261            while (!pi.isDone()) {
262                switch (pi.currentSegment(points)) {
263                    case PathIterator.SEG_MOVETO:
264                        x1 = (int)Math.floor(points[0]);
265                        y1 = (int)Math.floor(points[1]);
266                        cx1 = x1;
267                        cy1 = y1;
268                        break;
269                    case PathIterator.SEG_LINETO:
270                        int x2 = (int)Math.floor(points[0]);
271                        int y2 = (int)Math.floor(points[1]);
272                        fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false));
273                        x1 = x2;
274                        y1 = y2;
275                        break;
276                    case PathIterator.SEG_CLOSE:
277                        x2 = cx1;
278                        y2 = cy1;
279                        fillMultiRectArea(JavaLineRasterizer.rasterize(x1, y1, x2, y2, null, ld, false));
280                        x1 = x2;
281                        y1 = y2;
282                        break;
283                }
284                pi.next();
285            }
286        } else {
287            s = stroke.createStrokedShape(s);
288            s = transform.createTransformedShape(s);
289            fillMultiRectArea(jsr.rasterize(s, 0.5));
290        }
291    }
292
293    @Override
294    public void drawArc(int x, int y, int width, int height, int sa, int ea) {
295        if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 &&
296                ((BasicStroke)stroke).getDashArray() == null &&
297                (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) {
298            Point p = new Point(x, y);
299            transform.transform(p, p);
300            MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, sa, ea, clip);
301            fillMultiRectArea(mra);
302            return;
303        }
304        draw(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.OPEN));
305    }
306
307
308    @Override
309    public boolean drawImage(Image image, int x, int y, Color bgcolor,
310            ImageObserver imageObserver) {
311
312        if(image == null) {
313            return true;
314        }
315
316        boolean done = false;
317        boolean somebits = false;
318        Surface srcSurf = null;
319        if(image instanceof OffscreenImage){
320            OffscreenImage oi = (OffscreenImage) image;
321            if((oi.getState() & ImageObserver.ERROR) != 0) {
322                return false;
323            }
324            done = oi.prepareImage(imageObserver);
325            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
326            srcSurf = oi.getImageSurface();
327        }else{
328            done = true;
329            srcSurf = Surface.getImageSurface(image);
330        }
331
332        if(done || somebits) {
333            int w = srcSurf.getWidth();
334            int h = srcSurf.getHeight();
335            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h, (AffineTransform) transform.clone(),
336                    composite, bgcolor, clip);
337        }
338        return done;
339    }
340
341    @Override
342    public boolean drawImage(Image image, int x, int y, ImageObserver imageObserver) {
343        return drawImage(image, x, y, null, imageObserver);
344    }
345
346    @Override
347    public boolean drawImage(Image image, int x, int y, int width, int height,
348            Color bgcolor, ImageObserver imageObserver) {
349
350        if(image == null) {
351            return true;
352        }
353        if(width == 0 || height == 0) {
354            return true;
355        }
356
357        boolean done = false;
358        boolean somebits = false;
359        Surface srcSurf = null;
360
361        if(image instanceof OffscreenImage){
362            OffscreenImage oi = (OffscreenImage) image;
363            if((oi.getState() & ImageObserver.ERROR) != 0) {
364                return false;
365            }
366            done = oi.prepareImage(imageObserver);
367            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
368            srcSurf = oi.getImageSurface();
369        }else{
370            done = true;
371            srcSurf = Surface.getImageSurface(image);
372        }
373
374        if(done || somebits) {
375            int w = srcSurf.getWidth();
376            int h = srcSurf.getHeight();
377            if(w == width && h == height){
378                blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
379                        (AffineTransform) transform.clone(),
380                        composite, bgcolor, clip);
381            }else{
382                AffineTransform xform = new AffineTransform();
383                xform.setToScale((float)width / w, (float)height / h);
384                blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
385                        (AffineTransform) transform.clone(),
386                        xform, composite, bgcolor, clip);
387            }
388        }
389        return done;
390    }
391
392    @Override
393    public boolean drawImage(Image image, int x, int y, int width, int height,
394            ImageObserver imageObserver) {
395        return drawImage(image, x, y, width, height, null, imageObserver);
396    }
397
398    @Override
399    public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
400            int sx1, int sy1, int sx2, int sy2, Color bgcolor,
401            ImageObserver imageObserver) {
402
403        if(image == null) {
404            return true;
405        }
406        if(dx1 == dx2 || dy1 == dy2 || sx1 == sx2 || sy1 == sy2) {
407            return true;
408        }
409
410        boolean done = false;
411        boolean somebits = false;
412        Surface srcSurf = null;
413        if(image instanceof OffscreenImage){
414            OffscreenImage oi = (OffscreenImage) image;
415            if((oi.getState() & ImageObserver.ERROR) != 0) {
416                return false;
417            }
418            done = oi.prepareImage(imageObserver);
419            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
420            srcSurf = oi.getImageSurface();
421        }else{
422            done = true;
423            srcSurf = Surface.getImageSurface(image);
424        }
425
426        if(done || somebits) {
427
428            int dstX = dx1;
429            int dstY = dy1;
430            int srcX = sx1;
431            int srcY = sy1;
432
433            int dstW = dx2 - dx1;
434            int dstH = dy2 - dy1;
435            int srcW = sx2 - sx1;
436            int srcH = sy2 - sy1;
437
438            if(srcW == dstW && srcH == dstH){
439                blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
440                        (AffineTransform) transform.clone(),
441                        composite, bgcolor, clip);
442            }else{
443                AffineTransform xform = new AffineTransform();
444                xform.setToScale((float)dstW / srcW, (float)dstH / srcH);
445                blitter.blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, srcW, srcH,
446                        (AffineTransform) transform.clone(),
447                        xform, composite, bgcolor, clip);
448            }
449        }
450        return done;
451    }
452
453    @Override
454    public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
455            int sx1, int sy1, int sx2, int sy2, ImageObserver imageObserver) {
456
457        return drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
458                imageObserver);
459     }
460
461    @Override
462    public void drawImage(BufferedImage bufImage, BufferedImageOp op,
463            int x, int y) {
464
465        if(bufImage == null) {
466            return;
467        }
468
469        if(op == null) {
470            drawImage(bufImage, x, y, null);
471        } else if(op instanceof AffineTransformOp){
472            AffineTransformOp atop = (AffineTransformOp) op;
473            AffineTransform xform = atop.getTransform();
474            Surface srcSurf = Surface.getImageSurface(bufImage);
475            int w = srcSurf.getWidth();
476            int h = srcSurf.getHeight();
477            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
478                    (AffineTransform) transform.clone(), xform,
479                    composite, null, clip);
480        } else {
481            bufImage = op.filter(bufImage, null);
482            Surface srcSurf = Surface.getImageSurface(bufImage);
483            int w = srcSurf.getWidth();
484            int h = srcSurf.getHeight();
485            blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
486                    (AffineTransform) transform.clone(),
487                    composite, null, clip);
488        }
489    }
490
491    @Override
492    public boolean drawImage(Image image, AffineTransform trans,
493            ImageObserver imageObserver) {
494
495        if(image == null) {
496            return true;
497        }
498        if(trans == null || trans.isIdentity()) {
499            return drawImage(image, 0, 0, imageObserver);
500        }
501
502        boolean done = false;
503        boolean somebits = false;
504        Surface srcSurf = null;
505        if(image instanceof OffscreenImage){
506            OffscreenImage oi = (OffscreenImage) image;
507            if((oi.getState() & ImageObserver.ERROR) != 0) {
508                return false;
509            }
510            done = oi.prepareImage(imageObserver);
511            somebits = (oi.getState() & ImageObserver.SOMEBITS) != 0;
512            srcSurf = oi.getImageSurface();
513        }else{
514            done = true;
515            srcSurf = Surface.getImageSurface(image);
516        }
517
518        if(done || somebits) {
519            int w = srcSurf.getWidth();
520            int h = srcSurf.getHeight();
521            AffineTransform xform = (AffineTransform) transform.clone();
522            xform.concatenate(trans);
523            blitter.blit(0, 0, srcSurf, 0, 0, dstSurf, w, h, xform, composite,
524                    null, clip);
525        }
526        return done;
527    }
528
529    @Override
530    public void drawLine(int x1, int y1, int x2, int y2) {
531        if (debugOutput) {
532            System.err.println("CommonGraphics2D.drawLine("+x1+", "+y1+", "+x2+", "+y2+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
533        }
534
535        if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1) {
536            BasicStroke bstroke = (BasicStroke)stroke;
537            Point p1 = new Point(x1, y1);
538            Point p2 = new Point(x2, y2);
539            transform.transform(p1, p1);
540            transform.transform(p2, p2);
541            JavaLineRasterizer.LineDasher ld = (bstroke.getDashArray() == null)?null:new JavaLineRasterizer.LineDasher(bstroke.getDashArray(), bstroke.getDashPhase());
542            MultiRectArea mra = JavaLineRasterizer.rasterize(p1.x, p1.y, p2.x, p2.y, null, ld, false);
543            fillMultiRectArea(mra);
544            return;
545        }
546        draw(new Line2D.Float(x1, y1, x2, y2));
547    }
548
549    @Override
550    public void drawOval(int x, int y, int width, int height) {
551        if (stroke instanceof BasicStroke && ((BasicStroke)stroke).getLineWidth() <= 1 &&
552                ((BasicStroke)stroke).getDashArray() == null &&
553                (transform.isIdentity() || transform.getType() == AffineTransform.TYPE_TRANSLATION)) {
554            Point p = new Point(x, y);
555            transform.transform(p, p);
556            MultiRectArea mra = JavaArcRasterizer.rasterize(x, y, width, height, 0, 360, clip);
557            fillMultiRectArea(mra);
558            return;
559        }
560        draw(new Ellipse2D.Float(x, y, width, height));
561    }
562
563    @Override
564    public void drawPolygon(int[] xpoints, int[] ypoints, int npoints) {
565        draw(new Polygon(xpoints, ypoints, npoints));
566    }
567
568    @Override
569    public void drawPolygon(Polygon polygon) {
570        draw(polygon);
571    }
572
573    @Override
574    public void drawPolyline(int[] xpoints, int[] ypoints, int npoints) {
575        for (int i = 0; i < npoints-1; i++) {
576            drawLine(xpoints[i], ypoints[i], xpoints[i+1], ypoints[i+1]);
577        }
578    }
579
580    @Override
581    public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
582        if (img == null) {
583            return;
584        }
585
586        double scaleX = xform.getScaleX();
587        double scaleY = xform.getScaleY();
588        if (scaleX == 1 && scaleY == 1) {
589            drawRenderedImage(img.createDefaultRendering(), xform);
590        } else {
591            int width = (int)Math.round(img.getWidth()*scaleX);
592            int height = (int)Math.round(img.getHeight()*scaleY);
593            xform = (AffineTransform)xform.clone();
594            xform.scale(1, 1);
595            drawRenderedImage(img.createScaledRendering(width, height, null), xform);
596        }
597    }
598
599    @Override
600    public void drawRenderedImage(RenderedImage rimg, AffineTransform xform) {
601        if (rimg == null) {
602            return;
603        }
604
605        Image img = null;
606
607        if (rimg instanceof Image) {
608            img = (Image)rimg;
609        } else {
610            //TODO: Create new class to provide Image interface for RenderedImage or rewrite this method
611            img = new BufferedImage(rimg.getColorModel(), rimg.copyData(null), false, null);
612        }
613
614        drawImage(img, xform, null);
615    }
616
617    @Override
618    public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
619        if (debugOutput) {
620            System.err.println("CommonGraphics2D.drawRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
621        }
622
623        draw(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
624    }
625
626
627
628
629
630    /***************************************************************************
631     *
632     *  String methods
633     *
634     ***************************************************************************/
635
636    @Override
637    public void drawString(AttributedCharacterIterator iterator, float x, float y) {
638        GlyphVector gv = font.createGlyphVector(frc, iterator);
639        drawGlyphVector(gv, x, y);
640    }
641
642    @Override
643    public void drawString(AttributedCharacterIterator iterator, int x, int y) {
644        drawString(iterator, (float)x, (float)y);
645    }
646
647    @Override
648    public void drawString(String str, int x, int y) {
649        drawString(str, (float)x, (float)y);
650    }
651
652    @Override
653    public void drawString(String str, float x, float y) {
654        if (debugOutput) {
655            System.err.println("CommonGraphics2D.drawString("+str+", "+x+", "+y+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
656        }
657
658        AffineTransform at = (AffineTransform)this.getTransform().clone();
659        AffineTransform fontTransform = font.getTransform();
660        at.concatenate(fontTransform);
661
662        double[] matrix = new double[6];
663        if (!at.isIdentity()){
664
665            int atType = at.getType();
666            at.getMatrix(matrix);
667
668            // TYPE_TRANSLATION
669            if (atType == AffineTransform.TYPE_TRANSLATION){
670                jtr.drawString(this, str,
671                        (float)(x+fontTransform.getTranslateX()),
672                        (float)(y+fontTransform.getTranslateY()));
673                return;
674            }
675            // TODO: we use slow type of drawing strings when Font object
676            // in Graphics has transforms, we just fill outlines. New textrenderer
677            // is to be implemented.
678            Shape sh = font.createGlyphVector(this.getFontRenderContext(), str).getOutline(x, y);
679            this.fill(sh);
680
681        } else {
682            jtr.drawString(this, str, x, y);
683        }
684
685    }
686
687    @Override
688    public void drawGlyphVector(GlyphVector gv, float x, float y) {
689
690        AffineTransform at = gv.getFont().getTransform();
691
692        double[] matrix = new double[6];
693        if ((at != null) && (!at.isIdentity())){
694
695            int atType = at.getType();
696            at.getMatrix(matrix);
697
698            // TYPE_TRANSLATION
699            if ((atType == AffineTransform.TYPE_TRANSLATION) &&
700                ((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){
701                jtr.drawGlyphVector(this, gv, (int)(x+matrix[4]), (int)(y+matrix[5]));
702                return;
703            }
704        } else {
705            if (((gv.getLayoutFlags() & GlyphVector.FLAG_HAS_TRANSFORMS) == 0)){
706                jtr.drawGlyphVector(this, gv, x, y);
707                return;
708            }
709        }
710
711        // TODO: we use slow type of drawing strings when Font object
712        // in Graphics has transforms, we just fill outlines. New textrenderer
713        // is to be implemented.
714
715        Shape sh = gv.getOutline(x, y);
716        this.fill(sh);
717
718        }
719
720
721
722
723    /***************************************************************************
724     *
725     *  Fill methods
726     *
727     ***************************************************************************/
728
729    @Override
730    public void fill(Shape s) {
731        s = transform.createTransformedShape(s);
732        MultiRectArea mra = jsr.rasterize(s, 0.5);
733        fillMultiRectArea(mra);
734    }
735
736    @Override
737    public void fillArc(int x, int y, int width, int height, int sa, int ea) {
738        fill(new Arc2D.Float(x, y, width, height, sa, ea, Arc2D.PIE));
739    }
740
741    @Override
742    public void fillOval(int x, int y, int width, int height) {
743        fill(new Ellipse2D.Float(x, y, width, height));
744    }
745
746    @Override
747    public void fillPolygon(int[] xpoints, int[] ypoints, int npoints) {
748        fill(new Polygon(xpoints, ypoints, npoints));
749    }
750
751    @Override
752    public void fillPolygon(Polygon polygon) {
753        fill(polygon);
754    }
755
756    @Override
757    public void fillRect(int x, int y, int width, int height) {
758        if (debugOutput) {
759            System.err.println("CommonGraphics2D.fillRect("+x+", "+y+", "+width+", "+height+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
760        }
761
762        fill(new Rectangle(x, y, width, height));
763    }
764
765    @Override
766    public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
767        if (debugOutput) {
768            System.err.println("CommonGraphics2D.fillRoundRect("+x+", "+y+", "+width+", "+height+","+arcWidth+", "+arcHeight+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
769        }
770
771        fill(new RoundRectangle2D.Float(x, y, width, height, arcWidth, arcHeight));
772    }
773
774
775
776
777    /***************************************************************************
778     *
779     *  Get methods
780     *
781     ***************************************************************************/
782
783    @Override
784    public Color getBackground() {
785        return bgColor;
786    }
787
788    @Override
789    public Shape getClip() {
790        if (clip == null) {
791            return null;
792        }
793
794        MultiRectArea res = new MultiRectArea(clip);
795        res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY()));
796        return res;
797    }
798
799    @Override
800    public Rectangle getClipBounds() {
801        if (clip == null) {
802            return null;
803        }
804
805        Rectangle res = (Rectangle) clip.getBounds().clone();
806        res.translate(-Math.round((float)transform.getTranslateX()), -Math.round((float)transform.getTranslateY()));
807        return res;
808    }
809
810    @Override
811    public Color getColor() {
812        return fgColor;
813    }
814
815    @Override
816    public Composite getComposite() {
817        return composite;
818    }
819
820    @Override
821    public Font getFont() {
822        return font;
823    }
824
825    @SuppressWarnings("deprecation")
826    @Override
827    public FontMetrics getFontMetrics(Font font) {
828        return Toolkit.getDefaultToolkit().getFontMetrics(font);
829    }
830
831    @Override
832    public FontRenderContext getFontRenderContext() {
833        return frc;
834    }
835
836    @Override
837    public Paint getPaint() {
838        return paint;
839    }
840
841    @Override
842    public Object getRenderingHint(RenderingHints.Key key) {
843        return hints.get(key);
844    }
845
846    @Override
847    public RenderingHints getRenderingHints() {
848        return hints;
849    }
850
851    @Override
852    public Stroke getStroke() {
853        return stroke;
854    }
855
856    @Override
857    public AffineTransform getTransform() {
858        return (AffineTransform)transform.clone();
859    }
860
861    @Override
862    public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
863        //TODO: Implement method....
864        return false;
865    }
866
867
868
869
870    /***************************************************************************
871     *
872     *  Transformation methods
873     *
874     ***************************************************************************/
875
876    @Override
877    public void rotate(double theta) {
878        transform.rotate(theta);
879        transform.getMatrix(matrix);
880    }
881
882    @Override
883    public void rotate(double theta, double x, double y) {
884        transform.rotate(theta, x, y);
885        transform.getMatrix(matrix);
886    }
887
888    @Override
889    public void scale(double sx, double sy) {
890        transform.scale(sx, sy);
891        transform.getMatrix(matrix);
892    }
893
894    @Override
895    public void shear(double shx, double shy) {
896        transform.shear(shx, shy);
897        transform.getMatrix(matrix);
898    }
899
900    @Override
901    public void transform(AffineTransform at) {
902        transform.concatenate(at);
903        transform.getMatrix(matrix);
904    }
905
906    @Override
907    public void translate(double tx, double ty) {
908        if (debugOutput) {
909            System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
910        }
911
912        transform.translate(tx, ty);
913        transform.getMatrix(matrix);
914    }
915
916    @Override
917    public void translate(int tx, int ty) {
918        if (debugOutput) {
919            System.err.println("CommonGraphics2D.translate("+tx+", "+ty+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
920        }
921
922        transform.translate(tx, ty);
923        transform.getMatrix(matrix);
924    }
925
926
927
928
929    /***************************************************************************
930     *
931     *  Set methods
932     *
933     ***************************************************************************/
934
935    @Override
936    public void setBackground(Color color) {
937        bgColor = color;
938    }
939
940    @Override
941    public void setClip(int x, int y, int width, int height) {
942        setClip(new Rectangle(x, y, width, height));
943    }
944
945    @Override
946    public void setClip(Shape s) {
947        if (s == null) {
948            setTransformedClip(null);
949            if (debugOutput) {
950                System.err.println("CommonGraphics2D.setClip(null)"); //$NON-NLS-1$
951            }
952            return;
953        }
954
955        if (debugOutput) {
956            System.err.println("CommonGraphics2D.setClip("+s.getBounds()+")"); //$NON-NLS-1$ //$NON-NLS-2$
957        }
958
959        if (s instanceof MultiRectArea) {
960            MultiRectArea nclip = new MultiRectArea((MultiRectArea)s);
961            nclip.translate(Math.round((float)transform.getTranslateX()), Math.round((float)transform.getTranslateY()));
962            setTransformedClip(nclip);
963        } else {
964            int type = transform.getType();
965            if(s instanceof Rectangle && (type & (AffineTransform.TYPE_IDENTITY |
966                AffineTransform.TYPE_TRANSLATION)) != 0){
967                    MultiRectArea nclip = new MultiRectArea((Rectangle)s);
968                    if(type == AffineTransform.TYPE_TRANSLATION){
969                        nclip.translate((int)transform.getTranslateX(), (int)transform.getTranslateY());
970                    }
971                    setTransformedClip(nclip);
972            } else {
973                s = transform.createTransformedShape(s);
974                setTransformedClip(jsr.rasterize(s, 0.5));
975            }
976        }
977    }
978
979    @Override
980    public void setColor(Color color) {
981        if (color != null) {
982            fgColor = color;
983            paint = color;
984        }
985    }
986
987    @Override
988    public void setComposite(Composite composite) {
989        this.composite = composite;
990    }
991
992    @Override
993    public void setFont(Font font) {
994        this.font = font;
995    }
996
997    @Override
998    public void setPaint(Paint paint) {
999        if (paint == null)
1000            return;
1001
1002        this.paint = paint;
1003        if (paint instanceof Color) {
1004            fgColor = (Color)paint;
1005        }
1006    }
1007
1008    @Override
1009    public void setPaintMode() {
1010        composite = AlphaComposite.SrcOver;
1011    }
1012
1013    @Override
1014    public void setRenderingHint(RenderingHints.Key key, Object value) {
1015        hints.put(key, value);
1016    }
1017
1018    @Override
1019    public void setRenderingHints(Map<?,?> hints) {
1020        this.hints.clear();
1021        this.hints.putAll(hints);
1022    }
1023
1024    @Override
1025    public void setStroke(Stroke stroke) {
1026        this.stroke = stroke;
1027    }
1028
1029    @Override
1030    public void setTransform(AffineTransform transform) {
1031        this.transform = transform;
1032
1033        transform.getMatrix(matrix);
1034    }
1035
1036    @Override
1037    public void setXORMode(Color color) {
1038        composite = new XORComposite(color);
1039    }
1040
1041
1042    // Protected methods
1043    protected void setTransformedClip(MultiRectArea clip) {
1044        this.clip = clip;
1045    }
1046
1047    /**
1048     * This method fills the given MultiRectArea with current paint.
1049     * It calls fillMultiRectAreaColor and fillMultiRectAreaPaint
1050     * methods depending on the type of current paint.
1051     * @param mra MultiRectArea to fill
1052     */
1053    protected void fillMultiRectArea(MultiRectArea mra) {
1054        if (clip != null) {
1055            mra.intersect(clip);
1056        }
1057
1058        // Return if all stuff is clipped
1059        if (mra.rect[0] < 5) {
1060            return;
1061        }
1062
1063        if (debugOutput) {
1064            System.err.println("CommonGraphics2D.fillMultiRectArea("+mra+")"); //$NON-NLS-1$ //$NON-NLS-2$
1065        }
1066
1067        if (paint instanceof Color){
1068            fillMultiRectAreaColor(mra);
1069        }else{
1070            fillMultiRectAreaPaint(mra);
1071        }
1072    }
1073
1074    /**
1075     * This method fills the given MultiRectArea with solid color.
1076     * @param mra MultiRectArea to fill
1077     */
1078    protected void fillMultiRectAreaColor(MultiRectArea mra) {
1079        fillMultiRectAreaPaint(mra);
1080    }
1081
1082    /**
1083     * This method fills the given MultiRectArea with any paint.
1084     * @param mra MultiRectArea to fill
1085     */
1086    protected void fillMultiRectAreaPaint(MultiRectArea mra) {
1087        Rectangle rec = mra.getBounds();
1088        int x = rec.x;
1089        int y = rec.y;
1090        int w = rec.width;
1091        int h = rec.height;
1092        if(w <= 0 || h <= 0) {
1093            return;
1094        }
1095        PaintContext pc = paint.createContext(null, rec, rec, transform, hints);
1096        Raster r = pc.getRaster(x, y, w, h);
1097        WritableRaster wr;
1098        if(r instanceof WritableRaster){
1099            wr = (WritableRaster) r;
1100        }else{
1101            wr = r.createCompatibleWritableRaster();
1102            wr.setRect(r);
1103        }
1104        Surface srcSurf = new ImageSurface(pc.getColorModel(), wr);
1105        blitter.blit(0, 0, srcSurf, x, y, dstSurf, w, h,
1106                composite, null, mra);
1107        srcSurf.dispose();
1108    }
1109
1110    /**
1111     * Copies graphics class fields.
1112     * Used in create method
1113     *
1114     * @param copy Graphics class to copy
1115     */
1116    protected void copyInternalFields(CommonGraphics2D copy) {
1117        if (clip == null) {
1118            copy.setTransformedClip(null);
1119        } else {
1120            copy.setTransformedClip(new MultiRectArea(clip));
1121        }
1122        copy.setBackground(bgColor);
1123        copy.setColor(fgColor);
1124        copy.setPaint(paint);
1125        copy.setComposite(composite);
1126        copy.setStroke(stroke);
1127        copy.setFont(font);
1128        copy.setTransform(new AffineTransform(transform));
1129        //copy.origTransform = new AffineTransform(origTransform);
1130        copy.origPoint = new Point(origPoint);
1131    }
1132}