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 }