001    /* ===========================================================
002     * JFreeChart : a free chart library for the Java(tm) platform
003     * ===========================================================
004     *
005     * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
006     *
007     * Project Info:  http://www.jfree.org/jfreechart/index.html
008     *
009     * This library is free software; you can redistribute it and/or modify it 
010     * under the terms of the GNU Lesser General Public License as published by 
011     * the Free Software Foundation; either version 2.1 of the License, or 
012     * (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful, but 
015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017     * License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022     * USA.  
023     *
024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025     * in the United States and other countries.]
026     *
027     * ------------------
028     * SWTGraphics2D.java
029     * ------------------
030     * (C) Copyright 2006, 2007, by Henry Proudhon and Contributors.
031     *
032     * Original Author:  Henry Proudhon (henry.proudhon AT ensmp.fr);
033     * Contributor(s):   Cedric Chabanois (cchabanois AT no-log.org);
034     *                   David Gilbert (for Object Refinery Limited);
035     *
036     * Changes
037     * -------
038     * 14-Jun-2006 : New class (HP);
039     * 29-Jan-2007 : Fixed the fillRect method (HP);
040     * 31-Jan-2007 : Moved the dummy JPanel to SWTUtils.java,
041     *               implemented the drawLine method (HP);
042     * 07-Apr-2007 : Dispose some of the swt ressources, 
043     *               thanks to silent for pointing this out (HP);
044     * 23-May-2007 : Removed resource leaks by adding a resource pool (CC);
045     * 15-Jun-2007 : Fixed compile error for JDK 1.4 (DG);
046     * 22-Oct-2007 : Implemented clipping (HP);
047     * 22-Oct-2007 : Implemented some AlphaComposite support (HP);
048     * 23-Oct-2007 : Added mechanism for storing RenderingHints (which are 
049     *               still ignored at this point) (DG);
050     * 23-Oct-2007 : Implemented drawPolygon(), drawPolyline(), drawOval(),
051     *               fillOval(), drawArc() and fillArc() (DG);
052     *
053     */
054    
055    package org.jfree.experimental.swt;
056    
057    import java.awt.AlphaComposite;
058    import java.awt.BasicStroke;
059    import java.awt.Color;
060    import java.awt.Composite;
061    import java.awt.Font;
062    import java.awt.FontMetrics;
063    import java.awt.Graphics;
064    import java.awt.Graphics2D;
065    import java.awt.GraphicsConfiguration;
066    import java.awt.Image;
067    import java.awt.Paint;
068    import java.awt.Rectangle;
069    import java.awt.RenderingHints;
070    import java.awt.Shape;
071    import java.awt.Stroke;
072    import java.awt.RenderingHints.Key;
073    import java.awt.font.FontRenderContext;
074    import java.awt.font.GlyphVector;
075    import java.awt.geom.AffineTransform;
076    import java.awt.geom.PathIterator;
077    import java.awt.image.BufferedImage;
078    import java.awt.image.BufferedImageOp;
079    import java.awt.image.DirectColorModel;
080    import java.awt.image.ImageObserver;
081    import java.awt.image.IndexColorModel;
082    import java.awt.image.RenderedImage;
083    import java.awt.image.WritableRaster;
084    import java.awt.image.renderable.RenderableImage;
085    import java.text.AttributedCharacterIterator;
086    import java.util.ArrayList;
087    import java.util.HashMap;
088    import java.util.Iterator;
089    import java.util.List;
090    import java.util.Map;
091    
092    import org.eclipse.swt.SWT;
093    import org.eclipse.swt.graphics.FontData;
094    import org.eclipse.swt.graphics.GC;
095    import org.eclipse.swt.graphics.ImageData;
096    import org.eclipse.swt.graphics.PaletteData;
097    import org.eclipse.swt.graphics.Path;
098    import org.eclipse.swt.graphics.RGB;
099    import org.eclipse.swt.graphics.Resource;
100    import org.eclipse.swt.graphics.Transform;
101    
102    /**
103     * This is a class utility to draw Graphics2D stuff on a swt composite.
104     * It is presently developed to use JFreeChart with the Standard 
105     * Widget Toolkit but may be of a wider use later.
106     */
107    public class SWTGraphics2D extends Graphics2D {
108    
109        /** The swt graphic composite */
110        private GC gc;
111    
112        /** 
113         * The rendering hints.  For now, these are not used, but at least the
114         * basic mechanism is present.
115         */
116        private RenderingHints hints;
117        
118        /** A reference to the compositing rule to apply. This is necessary 
119         * due to the poor compositing interface of the SWT toolkit. */
120        private java.awt.Composite composite;
121        
122        /** A HashMap to store the Swt color resources. */
123        private Map colorsPool = new HashMap();
124    
125        /** A HashMap to store the Swt font resources. */
126        private Map fontsPool = new HashMap();
127    
128        /** A HashMap to store the Swt color resources. */
129        private List resourcePool = new ArrayList();
130    
131        /**
132         * Creates a new instance.
133         * 
134         * @param gc  the graphics context.
135         */
136        public SWTGraphics2D(GC gc) {
137            super();
138            this.gc = gc;
139            this.hints = new RenderingHints(null);
140            this.composite = AlphaComposite.getInstance(AlphaComposite.SRC, 1.0f);
141        }
142    
143        /* (non-Javadoc)
144         * @see java.awt.Graphics#create()
145         */
146        public Graphics create() {
147            // TODO Auto-generated method stub
148            return null;
149        }
150    
151        /* (non-Javadoc)
152         * @see java.awt.Graphics2D#getDeviceConfiguration()
153         */
154        public GraphicsConfiguration getDeviceConfiguration() {
155            // TODO Auto-generated method stub
156            return null;
157        }
158    
159        /**
160         * Returns the current value for the specified hint key, or 
161         * <code>null</code> if no value is set.
162         * 
163         * @param hintKey  the hint key (<code>null</code> permitted).
164         * 
165         * @return The hint value, or <code>null</code>.
166         * 
167         * @see #setRenderingHint(Key, Object)
168         */
169        public Object getRenderingHint(Key hintKey) {
170            return this.hints.get(hintKey);
171        }
172    
173        /**
174         * Sets the value for a rendering hint.  For now, this graphics context
175         * ignores all hints.
176         * 
177         * @param hintKey  the key (<code>null</code> not permitted).
178         * @param hintValue  the value (must be compatible with the specified key).
179         * 
180         * @throws IllegalArgumentException if <code>hintValue</code> is not
181         *         compatible with the <code>hintKey</code>.
182         *         
183         * @see #getRenderingHint(Key)
184         */
185        public void setRenderingHint(Key hintKey, Object hintValue) {
186            this.hints.put(hintKey, hintValue);
187        }
188    
189        /**
190         * Returns a copy of the hints collection for this graphics context.
191         * 
192         * @return A copy of the hints collection.
193         */
194        public RenderingHints getRenderingHints() {
195            return (RenderingHints) this.hints.clone();
196        }
197    
198        /**
199         * Adds the hints in the specified map to the graphics context, replacing
200         * any existing hints.  For now, this graphics context ignores all hints.
201         * 
202         * @param hints  the hints (<code>null</code> not permitted).
203         * 
204         * @see #setRenderingHints(Map)
205         */
206        public void addRenderingHints(Map hints) {
207            this.hints.putAll(hints);
208        }
209    
210        /**
211         * Replaces the existing hints with those contained in the specified
212         * map.  Note that, for now, this graphics context ignores all hints.
213         * 
214         * @param hints  the hints (<code>null</code> not permitted).
215         * 
216         * @see #addRenderingHints(Map)
217         */
218        public void setRenderingHints(Map hints) {
219            if (hints == null) {
220                throw new NullPointerException("Null 'hints' argument.");
221            }
222            this.hints = new RenderingHints(hints);
223        }
224    
225        /**
226         * Returns the current paint for this graphics context.
227         * 
228         * @return The current paint.
229         * 
230         * @see #setPaint(Paint)
231         */
232        public Paint getPaint() {
233            // TODO: it might be a good idea to keep a reference to the color
234            // specified in setPaint() or setColor(), rather than creating a 
235            // new object every time getPaint() is called.
236            return SWTUtils.toAwtColor(this.gc.getForeground());
237        }
238    
239        /**
240         * Sets the paint for this graphics context.  For now, this graphics
241         * context only supports instances of {@link Color}.
242         * 
243         * @param paint  the paint (<code>null</code> not permitted).
244         * 
245         * @see #getPaint()
246         * @see #setColor(Color)
247         */
248        public void setPaint(Paint paint) {
249            if (paint instanceof Color) {
250                setColor((Color) paint);
251            }
252            else {
253                throw new RuntimeException("Can only handle 'Color' at present.");
254            }
255        }
256    
257        /**
258         * Returns the current color for this graphics context.
259         * 
260         * @return The current color.
261         * 
262         * @see #setColor(Color)
263         */
264        public Color getColor() {
265            // TODO: it might be a good idea to keep a reference to the color
266            // specified in setPaint() or setColor(), rather than creating a 
267            // new object every time getPaint() is called.
268            return SWTUtils.toAwtColor(this.gc.getForeground());
269        }
270    
271        /**
272         * Sets the current color for this graphics context.
273         * 
274         * @param color  the color.
275         * 
276         * @see #getColor()
277         */
278        public void setColor(Color color) {
279            org.eclipse.swt.graphics.Color swtColor = getSwtColorFromPool(color);
280            this.gc.setForeground(swtColor);
281            // handle transparency and compositing.
282            if (this.composite instanceof AlphaComposite) {
283                AlphaComposite acomp = (AlphaComposite) this.composite;
284                switch (acomp.getRule()) {
285                case AlphaComposite.SRC_OVER:
286                    this.gc.setAlpha((int) (color.getAlpha()*acomp.getAlpha()));
287                    break;
288                default:
289                    this.gc.setAlpha(color.getAlpha());
290                    break;
291                }
292            }
293        }
294    
295        /* (non-Javadoc)
296         * @see java.awt.Graphics2D#setBackground(java.awt.Color)
297         */
298        public void setBackground(Color color) {
299            this.gc.getBackground().dispose();
300            org.eclipse.swt.graphics.Color swtColor = SWTUtils.toSwtColor(
301                    this.gc.getDevice(), color);
302            this.gc.setBackground(swtColor);
303            swtColor.dispose(); 
304        }
305    
306        /* (non-Javadoc)
307         * @see java.awt.Graphics2D#getBackground()
308         */
309        public Color getBackground() {
310            return SWTUtils.toAwtColor(this.gc.getBackground());
311        }
312    
313        /* (non-Javadoc)
314         * @see java.awt.Graphics#setPaintMode()
315         */
316        public void setPaintMode() {
317            // TODO Auto-generated method stub
318        }
319    
320        /* (non-Javadoc)
321         * @see java.awt.Graphics#setXORMode(java.awt.Color)
322         */
323        public void setXORMode(Color color) {
324            // TODO Auto-generated method stub
325        }
326    
327        /**
328         * Returns the current composite.
329         * 
330         * @return The current composite.
331         * 
332         * @see #setComposite(Composite)
333         */
334        public Composite getComposite() {
335            return this.composite;
336        }
337    
338        /**
339         * Sets the current composite.  This implementation currently supports
340         * only the {@link AlphaComposite} class.
341         * 
342         * @param comp  the composite.
343         */
344        public void setComposite(Composite comp) {
345            this.composite = comp;
346            if (comp instanceof AlphaComposite) {
347                AlphaComposite acomp = (AlphaComposite) comp; 
348                int alpha = (int) (acomp.getAlpha()*0xFF);
349                this.gc.setAlpha(alpha);
350            } 
351            else {
352                System.out.println("warning, can only handle alpha composite at the moment.");
353            }
354        }
355    
356        /**
357         * Returns the current stroke for this graphics context.
358         * 
359         * @return The current stroke.
360         * 
361         * @see #setStroke(Stroke)
362         */
363        public Stroke getStroke() {
364            return new BasicStroke(this.gc.getLineWidth(), this.gc.getLineCap(), 
365                    this.gc.getLineJoin());
366        }
367    
368        /**
369         * Sets the stroke for this graphics context.  For now, this implementation
370         * only recognises the {@link BasicStroke} class.
371         * 
372         * @param stroke  the stroke (<code>null</code> not permitted).
373         * 
374         * @see #getStroke()
375         */
376        public void setStroke(Stroke stroke) {
377            if (stroke instanceof BasicStroke) {
378                BasicStroke bs = (BasicStroke) stroke;
379                // linewidth
380                this.gc.setLineWidth((int) bs.getLineWidth());
381    
382                // line join
383                switch (bs.getLineJoin()) {
384                    case BasicStroke.JOIN_BEVEL :
385                        this.gc.setLineJoin(SWT.JOIN_BEVEL);
386                        break;
387                    case BasicStroke.JOIN_MITER :
388                        this.gc.setLineJoin(SWT.JOIN_MITER);
389                        break;
390                    case BasicStroke.JOIN_ROUND :
391                        this.gc.setLineJoin(SWT.JOIN_ROUND);
392                        break;
393                }
394    
395                // line cap
396                switch (bs.getEndCap()) {
397                    case BasicStroke.CAP_BUTT :
398                        this.gc.setLineCap(SWT.CAP_FLAT);
399                        break;
400                    case BasicStroke.CAP_ROUND :
401                        this.gc.setLineCap(SWT.CAP_ROUND);
402                        break;
403                    case BasicStroke.CAP_SQUARE :
404                        this.gc.setLineCap(SWT.CAP_SQUARE);
405                        break;
406                }
407    
408                // set the line style to solid by default
409                this.gc.setLineStyle(SWT.LINE_SOLID);
410    
411                // apply dash style if any
412                float[] dashes = bs.getDashArray();
413                if (dashes != null) {
414                    int[] swtDashes = new int[dashes.length];
415                    for (int i = 0; i < swtDashes.length; i++) {
416                        swtDashes[i] = (int) dashes[i];
417                    }
418                    this.gc.setLineDash(swtDashes);
419                }
420            }
421            else {
422                throw new RuntimeException(
423                        "Can only handle 'Basic Stroke' at present.");
424            }
425        }
426    
427        /* (non-Javadoc)
428         * @see java.awt.Graphics2D#clip(java.awt.Shape)
429         */
430        public void clip(Shape s) {
431            Path path = toSwtPath(s);
432            this.gc.setClipping(path);
433            path.dispose();
434        }
435    
436        /* (non-Javadoc)
437         * @see java.awt.Graphics#getClipBounds()
438         */
439        public Rectangle getClipBounds() {
440            org.eclipse.swt.graphics.Rectangle clip = this.gc.getClipping();
441            return new Rectangle(clip.x, clip.y, clip.width, clip.height);
442        }
443    
444        /* (non-Javadoc)
445         * @see java.awt.Graphics#clipRect(int, int, int, int)
446         */
447        public void clipRect(int x, int y, int width, int height) {
448            org.eclipse.swt.graphics.Rectangle clip = this.gc.getClipping();
449            clip.intersects(x, y, width, height);
450            this.gc.setClipping(clip);
451        }
452    
453        /* (non-Javadoc)
454         * @see java.awt.Graphics#getClip()
455         */
456        public Shape getClip() {
457            return SWTUtils.toAwtRectangle(this.gc.getClipping());
458        }
459    
460        /* (non-Javadoc)
461         * @see java.awt.Graphics#setClip(java.awt.Shape)
462         */
463        public void setClip(Shape clip) {
464            if (clip == null) 
465                return;
466            Path clipPath = toSwtPath(clip);
467            this.gc.setClipping(clipPath);
468            clipPath.dispose();
469        }
470    
471        /* (non-Javadoc)
472         * @see java.awt.Graphics#setClip(int, int, int, int)
473         */
474        public void setClip(int x, int y, int width, int height) {
475            this.gc.setClipping(x, y, width, height);
476        }
477    
478        /* (non-Javadoc)
479         * @see java.awt.Graphics2D#getTransform()
480         */
481        public AffineTransform getTransform() {
482            Transform swtTransform = new Transform(this.gc.getDevice()); 
483            this.gc.getTransform(swtTransform);
484            AffineTransform awtTransform = toAwtTransform(swtTransform);
485            swtTransform.dispose();
486            return awtTransform; 
487        }
488    
489        /* (non-Javadoc)
490         * @see java.awt.Graphics2D#setTransform(java.awt.geom.AffineTransform)
491         */
492        public void setTransform(AffineTransform Tx) {
493            this.gc.setTransform(toSwtTransform(Tx));
494        }
495    
496        /* (non-Javadoc)
497         * @see java.awt.Graphics2D#transform(java.awt.geom.AffineTransform)
498         */
499        public void transform(AffineTransform Tx) {
500            Transform swtTransform = new Transform(this.gc.getDevice()); 
501            this.gc.getTransform(swtTransform);
502            Transform swtMatrix = toSwtTransform(Tx);
503            swtTransform.multiply(swtMatrix);
504            this.gc.setTransform(swtTransform);
505            swtMatrix.dispose();
506            swtTransform.dispose();
507        }
508    
509        /* (non-Javadoc)
510         * @see java.awt.Graphics2D#translate(int, int)
511         */
512        public void translate(int x, int y) {
513            Transform swtTransform = new Transform(this.gc.getDevice()); 
514            this.gc.getTransform(swtTransform);
515            swtTransform.translate(x, y);
516            this.gc.setTransform(swtTransform);
517            swtTransform.dispose();
518        }
519    
520        /* (non-Javadoc)
521         * @see java.awt.Graphics2D#translate(double, double)
522         */
523        public void translate(double tx, double ty) {
524            translate((int) tx, (int) ty);
525        }
526    
527        /* (non-Javadoc)
528         * @see java.awt.Graphics2D#rotate(double)
529         */
530        public void rotate(double theta) {
531            Transform swtTransform = new Transform(this.gc.getDevice()); 
532            this.gc.getTransform(swtTransform);
533            swtTransform.rotate( (float) (theta * 180 / Math.PI));
534            this.gc.setTransform(swtTransform);
535            swtTransform.dispose();
536        }
537    
538        /* (non-Javadoc)
539         * @see java.awt.Graphics2D#rotate(double, double, double)
540         */
541        public void rotate(double theta, double x, double y) {
542            // TODO Auto-generated method stub
543        }
544    
545        /* (non-Javadoc)
546         * @see java.awt.Graphics2D#scale(double, double)
547         */
548        public void scale(double scaleX, double scaleY) {
549            Transform swtTransform = new Transform(this.gc.getDevice()); 
550            this.gc.getTransform(swtTransform);
551            swtTransform.scale((float) scaleX, (float) scaleY);
552            this.gc.setTransform(swtTransform);
553            swtTransform.dispose();
554        }
555    
556        /* (non-Javadoc)
557         * @see java.awt.Graphics2D#shear(double, double)
558         */
559        public void shear(double shearX, double shearY) {
560            Transform swtTransform = new Transform(this.gc.getDevice()); 
561            this.gc.getTransform(swtTransform);
562            Transform shear = new Transform(this.gc.getDevice(), 1f, (float) shearX, 
563                    (float) shearY, 1f, 0, 0);
564            swtTransform.multiply(shear);
565            this.gc.setTransform(swtTransform);
566            swtTransform.dispose();
567        }
568    
569        /**
570         * Draws the outline of the specified shape using the current stroke and
571         * paint settings.
572         * 
573         * @param shape  the shape (<code>null</code> not permitted).
574         * 
575         * @see #getPaint()
576         * @see #getStroke()
577         * @see #fill(Shape)
578         */
579        public void draw(Shape shape) {
580            Path path = toSwtPath(shape);
581            this.gc.drawPath(path);
582            path.dispose();
583        }
584    
585        /**
586         * Draws a line from (x1, y1) to (x2, y2) using the current stroke
587         * and paint settings.
588         * 
589         * @param x1  the x-coordinate for the starting point.
590         * @param y1  the y-coordinate for the starting point.
591         * @param x2  the x-coordinate for the ending point.
592         * @param y2  the y-coordinate for the ending point.
593         * 
594         * @see #draw(Shape)
595         */
596        public void drawLine(int x1, int y1, int x2, int y2) {
597            this.gc.drawLine(x1, y1, x2, y2);
598        }
599    
600        /**
601         * Draws the outline of the polygon specified by the given points, using
602         * the current paint and stroke settings.
603         * 
604         * @param xPoints  the x-coordinates.
605         * @param yPoints  the y-coordinates.
606         * @param npoints  the number of points in the polygon.
607         * 
608         * @see #draw(Shape)
609         */
610        public void drawPolygon(int [] xPoints, int [] yPoints, int npoints) {
611            drawPolyline(xPoints, yPoints, npoints);
612            if (npoints > 1) {
613                this.gc.drawLine(xPoints[npoints-1], yPoints[npoints-1], 
614                        xPoints[0], yPoints[0]);            
615            }
616        }
617    
618        /**
619         * Draws a sequence of connected lines specified by the given points, using
620         * the current paint and stroke settings.
621         * 
622         * @param xPoints  the x-coordinates.
623         * @param yPoints  the y-coordinates.
624         * @param npoints  the number of points in the polygon.
625         * 
626         * @see #draw(Shape)
627         */
628        public void drawPolyline(int [] xPoints, int [] yPoints, int npoints) {
629            if (npoints > 1) {
630                int x0 = xPoints[0];
631                int y0 = yPoints[0];
632                int x1 = 0, y1 = 0;
633                for (int i = 1; i < npoints; i++) {
634                    x1 = xPoints[i];
635                    y1 = yPoints[i];
636                    this.gc.drawLine(x0, y0, x1, y1);
637                    x0 = x1;
638                    y0 = y1;
639                }
640            }    
641        }
642    
643        /**
644         * Draws an oval that fits within the specified rectangular region.
645         * 
646         * @param x  the x-coordinate.
647         * @param y  the y-coordinate.
648         * @param width  the frame width.
649         * @param height  the frame height.
650         * 
651         * @see #fillOval(int, int, int, int)
652         * @see #draw(Shape)
653         */
654        public void drawOval(int x, int y, int width, int height) {
655            this.gc.drawOval(x, y, width - 1, height - 1);
656        }
657    
658        /**
659         * Draws an arc that is part of an ellipse that fits within the specified
660         * framing rectangle.
661         * 
662         * @param x  the x-coordinate.
663         * @param y  the y-coordinate.
664         * @param width  the frame width.
665         * @param height  the frame height.
666         * @param arcStart  the arc starting point, in degrees.
667         * @param arcAngle  the extent of the arc.
668         * 
669         * @see #fillArc(int, int, int, int, int, int)
670         */
671        public void drawArc(int x, int y, int width, int height, int arcStart,
672                int arcAngle) {
673            this.gc.drawArc(x, y, width - 1, height - 1, arcStart, arcAngle);
674        }
675    
676        /**
677         * Draws a rectangle with rounded corners that fits within the specified
678         * framing rectangle.
679         * 
680         * @param x  the x-coordinate.
681         * @param y  the y-coordinate.
682         * @param width  the frame width.
683         * @param height  the frame height.
684         * @param arcWidth  the width of the arc defining the roundedness of the
685         *         rectangle's corners.
686         * @param arcHeight the height of the arc defining the roundedness of the
687         *         rectangle's corners.
688         *         
689         * @see #fillRoundRect(int, int, int, int, int, int)
690         */
691        public void drawRoundRect(int x, int y, int width, int height,
692                int arcWidth, int arcHeight) {
693            this.gc.drawRoundRectangle(x, y, width - 1, height - 1, arcWidth, 
694                    arcHeight);
695        }
696    
697        /** 
698         * Fills the specified shape using the current paint.
699         * 
700         * @param shape  the shape (<code>null</code> not permitted).
701         * 
702         * @see #getPaint()
703         * @see #draw(Shape)
704         */
705        public void fill(Shape shape) {
706            Path path = toSwtPath(shape);
707            // Note that for consistency with the AWT implementation, it is 
708            // necessary to switch temporarily the foreground and background 
709            // colours
710            switchColors();
711            this.gc.fillPath(path);
712            switchColors();
713            path.dispose();
714        }
715    
716        /**
717         * Fill a rectangle area on the swt graphic composite.
718         * The <code>fillRectangle</code> method of the <code>GC</code> 
719         * class uses the background color so we must switch colors.
720         * @see java.awt.Graphics#fillRect(int, int, int, int)
721         */
722        public void fillRect(int x, int y, int width, int height) {
723            this.switchColors();
724            this.gc.fillRectangle(x, y, width, height);
725            this.switchColors();
726        }
727    
728        /**
729         * Fills the specified rectangle with the current background colour.
730         * 
731         * @param x  the x-coordinate for the rectangle.
732         * @param y  the y-coordinate for the rectangle.
733         * @param width  the width.
734         * @param height  the height.
735         * 
736         * @see #fillRect(int, int, int, int)
737         */
738        public void clearRect(int x, int y, int width, int height) {
739            Paint saved = getPaint();
740            setPaint(getBackground());
741            fillRect(x, y, width, height);
742            setPaint(saved);
743        }
744    
745        /* (non-Javadoc)
746         * @see java.awt.Graphics#fillPolygon(int[], int[], int)
747         */
748        public void fillPolygon(int [] xPoints, int [] yPoints, int npoints) {
749            // TODO Auto-generated method stub
750        }
751    
752        /**
753         * Draws a rectangle with rounded corners that fits within the specified
754         * framing rectangle.
755         * 
756         * @param x  the x-coordinate.
757         * @param y  the y-coordinate.
758         * @param width  the frame width.
759         * @param height  the frame height.
760         * @param arcWidth  the width of the arc defining the roundedness of the
761         *         rectangle's corners.
762         * @param arcHeight the height of the arc defining the roundedness of the
763         *         rectangle's corners.
764         *         
765         * @see #drawRoundRect(int, int, int, int, int, int)
766         */
767        public void fillRoundRect(int x, int y, int width, int height,
768                int arcWidth, int arcHeight) {
769            switchColors();
770            this.gc.fillRoundRectangle(x, y, width - 1, height - 1, arcWidth, 
771                    arcHeight);
772            switchColors();
773        }
774    
775        /**
776         * Fills an oval that fits within the specified rectangular region.
777         * 
778         * @param x  the x-coordinate.
779         * @param y  the y-coordinate.
780         * @param width  the frame width.
781         * @param height  the frame height.
782         * 
783         * @see #drawOval(int, int, int, int)
784         * @see #fill(Shape)
785         */
786        public void fillOval(int x, int y, int width, int height) {
787            switchColors();
788            this.gc.fillOval(x, y, width - 1, height - 1);
789            switchColors();
790        }
791    
792        /**
793         * Fills an arc that is part of an ellipse that fits within the specified
794         * framing rectangle.
795         * 
796         * @param x  the x-coordinate.
797         * @param y  the y-coordinate.
798         * @param width  the frame width.
799         * @param height  the frame height.
800         * @param arcStart  the arc starting point, in degrees.
801         * @param arcAngle  the extent of the arc.
802         * 
803         * @see #drawArc(int, int, int, int, int, int)
804         */
805        public void fillArc(int x, int y, int width, int height, int arcStart,
806                int arcAngle) {
807            switchColors();
808            this.gc.fillArc(x, y, width - 1, height - 1, arcStart, arcAngle);
809            switchColors();
810        }
811    
812        /**
813         * Returns the font in form of an awt font created 
814         * with the parameters of the font of the swt graphic 
815         * composite.
816         * @see java.awt.Graphics#getFont()
817         */
818        public Font getFont() {
819            // retrieve the swt font description in an os indept way
820            FontData[] fontData = this.gc.getFont().getFontData();
821            // create a new awt font with the appropiate data
822            return SWTUtils.toAwtFont(this.gc.getDevice(), fontData[0], true);
823        }
824    
825        /**
826         * Set the font swt graphic composite from the specified 
827         * awt font. Be careful that the newly created swt font 
828         * must be disposed separately.
829         * @see java.awt.Graphics#setFont(java.awt.Font)
830         */
831        public void setFont(Font font) {
832            org.eclipse.swt.graphics.Font swtFont = getSwtFontFromPool(font);
833            this.gc.setFont(swtFont);
834        }
835    
836        /* (non-Javadoc)
837         * @see java.awt.Graphics#getFontMetrics(java.awt.Font)
838         */
839        public FontMetrics getFontMetrics(Font font) {
840            return SWTUtils.DUMMY_PANEL.getFontMetrics(font);
841        }
842    
843        /* (non-Javadoc)
844         * @see java.awt.Graphics2D#getFontRenderContext()
845         */
846        public FontRenderContext getFontRenderContext() {
847            FontRenderContext fontRenderContext = new FontRenderContext(
848                    new AffineTransform(), true, true);
849            return fontRenderContext;
850        }
851    
852        /* (non-Javadoc)
853         * @see java.awt.Graphics2D#drawGlyphVector(java.awt.font.GlyphVector, 
854         * float, float)
855         */
856        public void drawGlyphVector(GlyphVector g, float x, float y) {
857            // TODO Auto-generated method stub
858        
859        }
860    
861        /**
862         * Draws a string on the receiver. note that 
863         * to be consistent with the awt method, 
864         * the y has to be modified with the ascent of the font. 
865         * 
866         * @see java.awt.Graphics#drawString(java.lang.String, int, int)
867         */
868        public void drawString(String text, int x, int y) {
869            float fm = this.gc.getFontMetrics().getAscent();
870            this.gc.drawString(text, x, (int) (y - fm), true);
871        }
872    
873        /* (non-Javadoc)
874         * @see java.awt.Graphics2D#drawString(java.lang.String, float, float)
875         */
876        public void drawString(String text, float x, float y) {
877            float fm = this.gc.getFontMetrics().getAscent();
878            this.gc.drawString(text, (int) x, (int) ( y - fm ), true);
879        }
880    
881        /* (non-Javadoc)
882         * @see java.awt.Graphics2D#drawString(
883         * java.text.AttributedCharacterIterator, int, int)
884         */
885        public void drawString(AttributedCharacterIterator iterator, int x, int y) {
886            // TODO Auto-generated method stub
887        }
888    
889        /* (non-Javadoc)
890         * @see java.awt.Graphics2D#drawString(
891         * java.text.AttributedCharacterIterator, float, float)
892         */
893        public void drawString(AttributedCharacterIterator iterator, float x, 
894                float y) {
895            // TODO Auto-generated method stub
896        }
897    
898        /* (non-Javadoc)
899         * @see java.awt.Graphics2D#hit(java.awt.Rectangle, java.awt.Shape, boolean)
900         */
901        public boolean hit(Rectangle rect, Shape text, boolean onStroke) {
902            // TODO Auto-generated method stub
903            return false;
904        }
905    
906        /* (non-Javadoc)
907         * @see java.awt.Graphics#copyArea(int, int, int, int, int, int)
908         */
909        public void copyArea(int x, int y, int width, int height, int dx, int dy) {
910            // TODO Auto-generated method stub
911    
912        }
913    
914        /* (non-Javadoc)
915         * @see java.awt.Graphics2D#drawImage(java.awt.Image, 
916         * java.awt.geom.AffineTransform, java.awt.image.ImageObserver)
917         */
918        public boolean drawImage(Image image, AffineTransform xform,
919                ImageObserver obs) {
920            // TODO Auto-generated method stub
921            return false;
922        }
923    
924        /* (non-Javadoc)
925         * @see java.awt.Graphics2D#drawImage(java.awt.image.BufferedImage, 
926         * java.awt.image.BufferedImageOp, int, int)
927         */
928        public void drawImage(BufferedImage image, BufferedImageOp op, int x, 
929                int y) {
930            org.eclipse.swt.graphics.Image im = new org.eclipse.swt.graphics.Image(
931                    this.gc.getDevice(), convertToSWT(image));
932            this.gc.drawImage(im, x, y);
933            im.dispose();
934        }
935    
936        /**
937         * Draws an image at (x, y).
938         * 
939         * @param image  the image.
940         * @param x  the x-coordinate.
941         * @param y  the y-coordinate.
942         */
943        public void drawImage(org.eclipse.swt.graphics.Image image, int x, int y) {
944            this.gc.drawImage(image, x, y);
945        }
946    
947        /* (non-Javadoc)
948         * @see java.awt.Graphics2D#drawRenderedImage(java.awt.image.RenderedImage,
949         * java.awt.geom.AffineTransform)
950         */
951        public void drawRenderedImage(RenderedImage image, AffineTransform xform) {
952            // TODO Auto-generated method stub
953        }
954    
955        /* (non-Javadoc)
956         * @see java.awt.Graphics2D#drawRenderableImage(
957         * java.awt.image.renderable.RenderableImage, java.awt.geom.AffineTransform)
958         */
959        public void drawRenderableImage(RenderableImage image, 
960                AffineTransform xform) {
961            // TODO Auto-generated method stub
962    
963        }
964    
965        /* (non-Javadoc)
966         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, 
967         * java.awt.image.ImageObserver)
968         */
969        public boolean drawImage(Image image, int x, int y, 
970                ImageObserver observer) {
971            // TODO Auto-generated method stub
972            return false;
973        }
974    
975        /* (non-Javadoc)
976         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, 
977         * java.awt.image.ImageObserver)
978         */
979        public boolean drawImage(Image image, int x, int y, int width, int height,
980                ImageObserver observer) {
981            // TODO Auto-generated method stub
982            return false;
983        }
984    
985        /* (non-Javadoc)
986         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, 
987         * java.awt.Color, java.awt.image.ImageObserver)
988         */
989        public boolean drawImage(Image image, int x, int y, Color bgcolor,
990                ImageObserver observer) {
991            // TODO Auto-generated method stub
992            return false;
993        }
994    
995        /* (non-Javadoc)
996         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, 
997         * java.awt.Color, java.awt.image.ImageObserver)
998         */
999        public boolean drawImage(Image image, int x, int y, int width, int height,
1000                Color bgcolor, ImageObserver observer) {
1001            // TODO Auto-generated method stub
1002            return false;
1003        }
1004    
1005        /* (non-Javadoc)
1006         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, 
1007         * int, int, int, int, java.awt.image.ImageObserver)
1008         */
1009        public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1010                int sx1, int sy1, int sx2, int sy2, ImageObserver observer) {
1011            // TODO Auto-generated method stub
1012            return false;
1013        }
1014    
1015        /* (non-Javadoc)
1016         * @see java.awt.Graphics#drawImage(java.awt.Image, int, int, int, int, 
1017         * int, int, int, int, java.awt.Color, java.awt.image.ImageObserver)
1018         */
1019        public boolean drawImage(Image image, int dx1, int dy1, int dx2, int dy2,
1020                int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1021                ImageObserver observer) {
1022            // TODO Auto-generated method stub
1023            return false;
1024        }
1025    
1026        /* (non-Javadoc)
1027         * @see java.awt.Graphics#dispose()
1028         */
1029        public void dispose() {
1030            // we dispose resources we own but user must dispose gc
1031            disposeResourcePool();
1032        }
1033    
1034        /**
1035         * Add given swt resource to the resource pool. All resources added
1036         * to the resource pool will be disposed when {@link #dispose()} is called.
1037         *  
1038         * @param resource the resource to add to the pool.
1039         * @return the swt <code>Resource</code> just added.
1040         */
1041        private Resource addToResourcePool(Resource resource) {
1042            this.resourcePool.add(resource);
1043            return resource;
1044        }
1045    
1046        /**
1047         * Dispose the resource pool.
1048         */
1049        private void disposeResourcePool() {
1050            for (Iterator it = this.resourcePool.iterator(); it.hasNext();) {
1051                Resource resource = (Resource) it.next();
1052                resource.dispose();
1053            }
1054            this.resourcePool.clear();
1055            this.colorsPool.clear();
1056            this.resourcePool.clear();
1057        }
1058    
1059        /**
1060         * Internal method to convert a AWT font object into 
1061         * a SWT font resource. If a corresponding SWT font
1062         * instance is already in the pool, it will be used 
1063         * instead of creating a new one. This is used in 
1064         * {@link #setFont()} for instance. 
1065         * 
1066         * @param font The AWT font to convert.
1067         * @return The SWT font instance.
1068         */
1069        private org.eclipse.swt.graphics.Font getSwtFontFromPool(Font font) {
1070            org.eclipse.swt.graphics.Font swtFont = (org.eclipse.swt.graphics.Font)
1071            this.fontsPool.get(font);
1072            if (swtFont == null) {
1073                swtFont = new org.eclipse.swt.graphics.Font(this.gc.getDevice(), 
1074                        SWTUtils.toSwtFontData(this.gc.getDevice(), font, true));
1075                addToResourcePool(swtFont);
1076                this.fontsPool.put(font, swtFont);
1077            }
1078            return swtFont;
1079        }
1080    
1081        /**
1082         * Internal method to convert a AWT color object into 
1083         * a SWT color resource. If a corresponding SWT color
1084         * instance is already in the pool, it will be used 
1085         * instead of creating a new one. This is used in 
1086         * {@link #setColor()} for instance. 
1087         * 
1088         * @param awtColor The AWT color to convert.
1089         * @return A SWT color instance.
1090         */
1091        private org.eclipse.swt.graphics.Color getSwtColorFromPool(Color awtColor) {
1092            org.eclipse.swt.graphics.Color swtColor = 
1093                    (org.eclipse.swt.graphics.Color)
1094                    // we can't use the following valueOf() method, because it 
1095                    // won't compile with JDK1.4
1096                    // this.colorsPool.get(Integer.valueOf(awtColor.getRGB()));
1097                    this.colorsPool.get(new Integer(awtColor.getRGB()));
1098            if (swtColor == null) {
1099                swtColor = SWTUtils.toSwtColor(this.gc.getDevice(), awtColor);
1100                addToResourcePool(swtColor);
1101                // see comment above
1102                //this.colorsPool.put(Integer.valueOf(awtColor.getRGB()), swtColor);
1103                this.colorsPool.put(new Integer(awtColor.getRGB()), swtColor);
1104            }
1105            return swtColor;
1106        }
1107    
1108        /**
1109         * Perform a switch between foreground and background 
1110         * color of gc. This is needed for consistency with 
1111         * the awt behaviour, and is required notably for the 
1112         * filling methods.
1113         */
1114        private void switchColors() {
1115            org.eclipse.swt.graphics.Color bg = this.gc.getBackground();
1116            org.eclipse.swt.graphics.Color fg = this.gc.getForeground();
1117            this.gc.setBackground(fg);
1118            this.gc.setForeground(bg);
1119        }
1120    
1121        /**
1122         * Converts an AWT <code>Shape</code> into a SWT <code>Path</code>.
1123         * 
1124         * @param shape  the shape (<code>null</code> not permitted).
1125         * 
1126         * @return The path.
1127         */
1128        private Path toSwtPath(Shape shape) {
1129            int type;
1130            float[] coords = new float[6];
1131            Path path = new Path(this.gc.getDevice());
1132            PathIterator pit = shape.getPathIterator(null);
1133            while (!pit.isDone()) {
1134                type = pit.currentSegment(coords);
1135                switch (type) {
1136                    case (PathIterator.SEG_MOVETO):
1137                        path.moveTo(coords[0], coords[1]);
1138                        break;
1139                    case (PathIterator.SEG_LINETO):
1140                        path.lineTo(coords[0], coords[1]);
1141                        break;
1142                    case (PathIterator.SEG_QUADTO):
1143                        path.quadTo(coords[0], coords[1], coords[2], coords[3]);
1144                        break;
1145                    case (PathIterator.SEG_CUBICTO):
1146                        path.cubicTo(coords[0], coords[1], coords[2], 
1147                                coords[3], coords[4], coords[5]);
1148                        break;
1149                    case (PathIterator.SEG_CLOSE):
1150                        path.close();
1151                        break;
1152                    default:
1153                        break;
1154                }
1155                pit.next();
1156            }
1157            return path;
1158        }
1159    
1160        /**
1161         * Converts an AWT transform into the equivalent SWT transform.
1162         * 
1163         * @param awtTransform  the AWT transform.
1164         * 
1165         * @return The SWT transform.
1166         */
1167        private Transform toSwtTransform(AffineTransform awtTransform) {
1168            Transform t = new Transform(this.gc.getDevice());
1169            double[] matrix = new double[6];
1170            awtTransform.getMatrix(matrix);
1171            t.setElements((float) matrix[0], (float) matrix[1],
1172                    (float) matrix[2], (float) matrix[3],
1173                    (float) matrix[4], (float) matrix[5]); 
1174            return t;
1175        }
1176    
1177        /**
1178         * Converts an SWT transform into the equivalent AWT transform.
1179         * 
1180         * @param swtTransform  the SWT transform.
1181         * 
1182         * @return The AWT transform.
1183         */
1184        private AffineTransform toAwtTransform(Transform swtTransform) {
1185            float[] elements = new float[6];
1186            swtTransform.getElements(elements);
1187            AffineTransform awtTransform = new AffineTransform(elements);
1188            return awtTransform;
1189        }
1190    
1191        static ImageData convertToSWT(BufferedImage bufferedImage) {
1192            if (bufferedImage.getColorModel() instanceof DirectColorModel) {
1193                DirectColorModel colorModel 
1194                        = (DirectColorModel) bufferedImage.getColorModel();
1195                PaletteData palette = new PaletteData(colorModel.getRedMask(),
1196                        colorModel.getGreenMask(), colorModel.getBlueMask());
1197                ImageData data = new ImageData(bufferedImage.getWidth(), 
1198                        bufferedImage.getHeight(), colorModel.getPixelSize(),
1199                        palette);
1200                WritableRaster raster = bufferedImage.getRaster();
1201                int[] pixelArray = new int[3];
1202                for (int y = 0; y < data.height; y++) {
1203                    for (int x = 0; x < data.width; x++) {
1204                        raster.getPixel(x, y, pixelArray);
1205                        int pixel = palette.getPixel(new RGB(pixelArray[0], 
1206                                pixelArray[1], pixelArray[2]));
1207                        data.setPixel(x, y, pixel);
1208                    }
1209                }
1210                return data;
1211            } 
1212            else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
1213                IndexColorModel colorModel = (IndexColorModel) 
1214                        bufferedImage.getColorModel();
1215                int size = colorModel.getMapSize();
1216                byte[] reds = new byte[size];
1217                byte[] greens = new byte[size];
1218                byte[] blues = new byte[size];
1219                colorModel.getReds(reds);
1220                colorModel.getGreens(greens);
1221                colorModel.getBlues(blues);
1222                RGB[] rgbs = new RGB[size];
1223                for (int i = 0; i < rgbs.length; i++) {
1224                    rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF, 
1225                            blues[i] & 0xFF);
1226                }
1227                PaletteData palette = new PaletteData(rgbs);
1228                ImageData data = new ImageData(bufferedImage.getWidth(),
1229                        bufferedImage.getHeight(), colorModel.getPixelSize(),
1230                        palette);
1231                data.transparentPixel = colorModel.getTransparentPixel();
1232                WritableRaster raster = bufferedImage.getRaster();
1233                int[] pixelArray = new int[1];
1234                for (int y = 0; y < data.height; y++) {
1235                    for (int x = 0; x < data.width; x++) {
1236                        raster.getPixel(x, y, pixelArray);
1237                        data.setPixel(x, y, pixelArray[0]);
1238                    }
1239                }
1240                return data;
1241            }
1242            return null;
1243        }
1244    }