Imported GNU Classpath 0.20
[official-gcc.git] / libjava / classpath / gnu / java / awt / peer / gtk / GdkGraphics2D.java
blob195304dcee23d87c703290dc42cac49a2c057ec2
1 /* GdkGraphics2D.java --
2 Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.awt.peer.gtk;
41 import gnu.classpath.Configuration;
42 import gnu.java.awt.ClasspathToolkit;
44 import java.awt.AlphaComposite;
45 import java.awt.BasicStroke;
46 import java.awt.Color;
47 import java.awt.Composite;
48 import java.awt.Dimension;
49 import java.awt.Font;
50 import java.awt.FontMetrics;
51 import java.awt.GradientPaint;
52 import java.awt.Graphics;
53 import java.awt.Graphics2D;
54 import java.awt.GraphicsConfiguration;
55 import java.awt.Image;
56 import java.awt.Paint;
57 import java.awt.Rectangle;
58 import java.awt.RenderingHints;
59 import java.awt.Shape;
60 import java.awt.Stroke;
61 import java.awt.TexturePaint;
62 import java.awt.Toolkit;
63 import java.awt.font.FontRenderContext;
64 import java.awt.font.GlyphVector;
65 import java.awt.geom.AffineTransform;
66 import java.awt.geom.Arc2D;
67 import java.awt.geom.GeneralPath;
68 import java.awt.geom.NoninvertibleTransformException;
69 import java.awt.geom.PathIterator;
70 import java.awt.geom.Point2D;
71 import java.awt.geom.Rectangle2D;
72 import java.awt.image.AffineTransformOp;
73 import java.awt.image.BufferedImage;
74 import java.awt.image.BufferedImageOp;
75 import java.awt.image.ColorModel;
76 import java.awt.image.CropImageFilter;
77 import java.awt.image.DataBuffer;
78 import java.awt.image.DataBufferInt;
79 import java.awt.image.DirectColorModel;
80 import java.awt.image.FilteredImageSource;
81 import java.awt.image.ImageObserver;
82 import java.awt.image.ImagingOpException;
83 import java.awt.image.MultiPixelPackedSampleModel;
84 import java.awt.image.Raster;
85 import java.awt.image.RenderedImage;
86 import java.awt.image.SampleModel;
87 import java.awt.image.WritableRaster;
88 import java.awt.image.renderable.RenderContext;
89 import java.awt.image.renderable.RenderableImage;
90 import java.text.AttributedCharacterIterator;
91 import java.util.HashMap;
92 import java.util.Map;
93 import java.util.Stack;
95 public class GdkGraphics2D extends Graphics2D
97 //////////////////////////////////////
98 ////// State Management Methods //////
99 //////////////////////////////////////
101 static
103 if (! Configuration.GTK_CAIRO_ENABLED)
104 throw new Error("Graphics2D not implemented. "
105 + "Cairo was not found or disabled at configure time");
107 if (Configuration.INIT_LOAD_LIBRARY)
108 System.loadLibrary("gtkpeer");
110 initStaticState();
113 static native void initStaticState();
115 private final int native_state = GtkGenericPeer.getUniqueInteger();
117 // These are package-private to avoid accessor methods.
118 Paint paint;
119 Stroke stroke;
120 Color fg;
121 Color bg;
122 Shape clip;
123 AffineTransform transform;
124 private GtkComponentPeer component;
125 // This is package-private to avoid an accessor method.
126 Font font;
127 private RenderingHints hints;
128 private BufferedImage bimage;
129 private boolean pixelConversionRequired;
130 private int[] pixelBuffer;
131 // This is package-private to avoid an accessor method.
132 Composite comp;
133 private Stack stateStack;
135 private native void initStateUnlocked(GtkComponentPeer component);
136 private native void initState(GtkComponentPeer component);
137 private native void initState(int width, int height);
138 private native void initState(int[] pixes, int width, int height);
139 private native void copyState(GdkGraphics2D g);
140 public native void dispose();
141 private native void cairoSurfaceSetFilter(int filter);
142 private native void cairoSurfaceSetFilterUnlocked(int filter);
143 native void connectSignals(GtkComponentPeer component);
145 public void finalize()
147 dispose();
150 public Graphics create()
152 return new GdkGraphics2D(this);
155 public Graphics create(int x, int y, int width, int height)
157 return new GdkGraphics2D(this, x, y, width, height);
160 private void fail_g2d ()
162 System.err.println ("Attempted to instantiate GdkGraphics2D"
163 + " but Graphics2D not enabled. Try again with"
164 + " -Dgnu.java.awt.peer.gtk.Graphics=Graphics2D");
165 System.exit (1);
168 GdkGraphics2D(GdkGraphics2D g)
170 if (!GtkToolkit.useGraphics2D ())
171 fail_g2d ();
173 paint = g.paint;
174 stroke = g.stroke;
175 setRenderingHints(g.hints);
177 if (g.fg.getAlpha() != -1)
178 fg = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(),
179 g.fg.getAlpha());
180 else
181 fg = new Color(g.fg.getRGB());
183 if (g.bg != null)
185 if (g.bg.getAlpha() != -1)
186 bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(),
187 g.bg.getAlpha());
188 else
189 bg = new Color(g.bg.getRGB());
192 if (g.clip == null)
193 clip = null;
194 else
195 clip = new Rectangle(g.getClipBounds());
197 if (g.transform == null)
198 transform = new AffineTransform();
199 else
200 transform = new AffineTransform(g.transform);
202 font = g.font;
203 component = g.component;
204 copyState(g);
206 setColor(fg);
207 setBackground(bg);
208 setPaint(paint);
209 setStroke(stroke);
210 setTransform(transform);
211 setClip(clip);
212 stateStack = new Stack();
215 GdkGraphics2D(GdkGraphics2D g, int x, int y, int widht, int height)
217 this(g);
218 translate(x, y);
219 clipRect(0, 0, widht, height);
222 GdkGraphics2D(int width, int height)
224 if (!GtkToolkit.useGraphics2D ())
225 fail_g2d ();
227 initState(width, height);
229 setColor(Color.black);
230 setBackground(new Color(0, 0, 0, 0));
231 setPaint(getColor());
232 setFont(new Font("SansSerif", Font.PLAIN, 12));
233 setTransform(new AffineTransform());
234 setStroke(new BasicStroke());
235 setRenderingHints(getDefaultHints());
237 stateStack = new Stack();
240 GdkGraphics2D(GtkComponentPeer component)
242 if (!GtkToolkit.useGraphics2D ())
243 fail_g2d ();
245 this.component = component;
247 if (component.isRealized())
248 initComponentGraphics2D();
249 else
250 connectSignals(component);
253 void initComponentGraphics2D()
255 initState(component);
257 setColor(component.awtComponent.getForeground());
258 setBackground(component.awtComponent.getBackground());
259 setPaint(getColor());
260 setTransform(new AffineTransform());
261 setStroke(new BasicStroke());
262 setRenderingHints(getDefaultHints());
263 setFont(new Font("SansSerif", Font.PLAIN, 12));
265 stateStack = new Stack();
268 void initComponentGraphics2DUnlocked()
270 initStateUnlocked(component);
272 setColorUnlocked(component.awtComponent.getForeground());
273 setBackgroundUnlocked(component.awtComponent.getBackground());
274 setPaintUnlocked(getColorUnlocked());
275 setTransformUnlocked(new AffineTransform());
276 setStrokeUnlocked(new BasicStroke());
277 setRenderingHintsUnlocked(getDefaultHints());
278 setFontUnlocked(new Font("SansSerif", Font.PLAIN, 12));
280 stateStack = new Stack();
283 GdkGraphics2D(BufferedImage bimage)
285 this.bimage = bimage;
286 this.pixelBuffer = findSimpleIntegerArray(bimage.getColorModel(),
287 bimage.getRaster());
288 if (this.pixelBuffer == null)
290 this.pixelBuffer = new int[bimage.getRaster().getWidth() * bimage.getRaster()
291 .getHeight()];
292 this.pixelConversionRequired = true;
294 else
296 this.pixelConversionRequired = false;
299 initState(this.pixelBuffer, bimage.getWidth(), bimage.getHeight());
301 setColor(Color.black);
302 setBackground(new Color(0, 0, 0, 0));
303 setPaint(getColor());
304 setFont(new Font("SansSerif", Font.PLAIN, 12));
305 setTransform(new AffineTransform());
306 setStroke(new BasicStroke());
307 setRenderingHints(getDefaultHints());
309 stateStack = new Stack();
311 // draw current buffered image to the pixmap associated
312 // with it, if the image is not equal to our paint buffer.
313 if (pixelConversionRequired)
314 drawImage(bimage, new AffineTransform(1, 0, 0, 1, 0, 0), bg, null);
317 ////////////////////////////////////
318 ////// Native Drawing Methods //////
319 ////////////////////////////////////
321 // GDK drawing methods
322 private native void gdkDrawDrawable(GdkGraphics2D other, int x, int y);
324 // drawing utility methods
325 private native void drawPixels(int[] pixels, int w, int h, int stride,
326 double[] i2u);
327 private native void setTexturePixelsUnlocked(int[] pixels, int w, int h, int stride);
328 private native void setTexturePixels(int[] pixels, int w, int h, int stride);
329 private native void setGradient(double x1, double y1, double x2, double y2,
330 int r1, int g1, int b1, int a1, int r2,
331 int g2, int b2, int a2, boolean cyclic);
332 private native void setGradientUnlocked(double x1, double y1, double x2, double y2,
333 int r1, int g1, int b1, int a1, int r2,
334 int g2, int b2, int a2, boolean cyclic);
336 // simple passthroughs to cairo
337 private native void cairoSave();
338 private native void cairoRestore();
339 private native void cairoSetMatrix(double[] m);
340 private native void cairoSetMatrixUnlocked(double[] m);
341 private native void cairoSetOperator(int cairoOperator);
342 private native void cairoSetRGBAColor(double red, double green,
343 double blue, double alpha);
344 private native void cairoSetRGBAColorUnlocked(double red, double green,
345 double blue, double alpha);
346 private native void cairoSetFillRule(int cairoFillRule);
347 private native void cairoSetLineWidth(double width);
348 private native void cairoSetLineWidthUnlocked(double width);
349 private native void cairoSetLineCap(int cairoLineCap);
350 private native void cairoSetLineCapUnlocked(int cairoLineCap);
351 private native void cairoSetLineJoin(int cairoLineJoin);
352 private native void cairoSetLineJoinUnlocked(int cairoLineJoin);
353 private native void cairoSetDash(double[] dashes, int ndash, double offset);
354 private native void cairoSetDashUnlocked(double[] dashes, int ndash, double offset);
356 private native void cairoSetMiterLimit(double limit);
357 private native void cairoSetMiterLimitUnlocked(double limit);
358 private native void cairoNewPath();
359 private native void cairoMoveTo(double x, double y);
360 private native void cairoLineTo(double x, double y);
361 private native void cairoCurveTo(double x1, double y1, double x2, double y2,
362 double x3, double y3);
363 private native void cairoRelMoveTo(double dx, double dy);
364 private native void cairoRelLineTo(double dx, double dy);
365 private native void cairoRelCurveTo(double dx1, double dy1, double dx2,
366 double dy2, double dx3, double dy3);
367 private native void cairoRectangle(double x, double y, double width,
368 double height);
369 private native void cairoClosePath();
370 private native void cairoStroke();
371 private native void cairoFill();
372 private native void cairoClip();
374 /////////////////////////////////////////////
375 ////// General Drawing Support Methods //////
376 /////////////////////////////////////////////
378 private class DrawState
380 private Paint paint;
381 private Stroke stroke;
382 private Color fg;
383 private Color bg;
384 private Shape clip;
385 private AffineTransform transform;
386 private Font font;
387 private Composite comp;
389 DrawState(GdkGraphics2D g)
391 this.paint = g.paint;
392 this.stroke = g.stroke;
393 this.fg = g.fg;
394 this.bg = g.bg;
395 this.clip = g.clip;
396 if (g.transform != null)
397 this.transform = (AffineTransform) g.transform.clone();
398 this.font = g.font;
399 this.comp = g.comp;
402 public void restore(GdkGraphics2D g)
404 g.paint = this.paint;
405 g.stroke = this.stroke;
406 g.fg = this.fg;
407 g.bg = this.bg;
408 g.clip = this.clip;
409 g.transform = this.transform;
410 g.font = this.font;
411 g.comp = this.comp;
415 private void stateSave()
417 stateStack.push(new DrawState(this));
418 cairoSave();
421 private void stateRestore()
423 ((DrawState) (stateStack.pop())).restore(this);
424 cairoRestore();
427 // Some operations (drawing rather than filling) require that their
428 // coords be shifted to land on 0.5-pixel boundaries, in order to land on
429 // "middle of pixel" coordinates and light up complete pixels.
430 private boolean shiftDrawCalls = false;
432 private double shifted(double coord, boolean doShift)
434 if (doShift)
435 return Math.floor(coord) + 0.5;
436 else
437 return coord;
440 private void walkPath(PathIterator p, boolean doShift)
442 double x = 0;
443 double y = 0;
444 double[] coords = new double[6];
446 cairoSetFillRule(p.getWindingRule());
447 for (; ! p.isDone(); p.next())
449 int seg = p.currentSegment(coords);
450 switch (seg)
452 case PathIterator.SEG_MOVETO:
453 x = shifted(coords[0], doShift);
454 y = shifted(coords[1], doShift);
455 cairoMoveTo(x, y);
456 break;
457 case PathIterator.SEG_LINETO:
458 x = shifted(coords[0], doShift);
459 y = shifted(coords[1], doShift);
460 cairoLineTo(x, y);
461 break;
462 case PathIterator.SEG_QUADTO:
463 // splitting a quadratic bezier into a cubic:
464 // see: http://pfaedit.sourceforge.net/bezier.html
465 double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x);
466 double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y);
468 double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x);
469 double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y);
471 x = shifted(coords[2], doShift);
472 y = shifted(coords[3], doShift);
473 cairoCurveTo(x1, y1, x2, y2, x, y);
474 break;
475 case PathIterator.SEG_CUBICTO:
476 x = shifted(coords[4], doShift);
477 y = shifted(coords[5], doShift);
478 cairoCurveTo(shifted(coords[0], doShift),
479 shifted(coords[1], doShift),
480 shifted(coords[2], doShift),
481 shifted(coords[3], doShift), x, y);
482 break;
483 case PathIterator.SEG_CLOSE:
484 cairoClosePath();
485 break;
490 private Map getDefaultHints()
492 HashMap defaultHints = new HashMap();
494 defaultHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
495 RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
497 defaultHints.put(RenderingHints.KEY_STROKE_CONTROL,
498 RenderingHints.VALUE_STROKE_DEFAULT);
500 defaultHints.put(RenderingHints.KEY_FRACTIONALMETRICS,
501 RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
503 defaultHints.put(RenderingHints.KEY_ANTIALIASING,
504 RenderingHints.VALUE_ANTIALIAS_OFF);
506 defaultHints.put(RenderingHints.KEY_RENDERING,
507 RenderingHints.VALUE_RENDER_DEFAULT);
509 return defaultHints;
512 public static int[] findSimpleIntegerArray (ColorModel cm, Raster raster)
514 if (cm == null || raster == null)
515 return null;
517 if (! cm.getColorSpace().isCS_sRGB())
518 return null;
520 if (! (cm instanceof DirectColorModel))
521 return null;
523 DirectColorModel dcm = (DirectColorModel) cm;
525 if (dcm.getRedMask() != 0x00FF0000 || dcm.getGreenMask() != 0x0000FF00
526 || dcm.getBlueMask() != 0x000000FF)
527 return null;
529 if (! (raster instanceof WritableRaster))
530 return null;
532 if (raster.getSampleModel().getDataType() != DataBuffer.TYPE_INT)
533 return null;
535 if (! (raster.getDataBuffer() instanceof DataBufferInt))
536 return null;
538 DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
540 if (db.getNumBanks() != 1)
541 return null;
543 // Finally, we have determined that this is a single bank, [A]RGB-int
544 // buffer in sRGB space. It's worth checking all this, because it means
545 // that cairo can paint directly into the data buffer, which is very
546 // fast compared to all the normal copying and converting.
548 return db.getData();
551 private void updateBufferedImage()
553 if (bimage != null && pixelConversionRequired)
555 int height = bimage.getHeight();
556 int width = bimage.getWidth();
557 int index = 0;
558 for (int y = 0; y < height; ++y)
559 for (int x = 0; x < width; ++x)
560 bimage.setRGB(x, y, pixelBuffer[index++]);
564 private boolean drawImage(Image img, AffineTransform xform,
565 Color bgcolor, ImageObserver obs)
567 if (img == null)
568 return false;
570 // FIXME: I'll fix this, /Sven
571 // if (img instanceof GtkOffScreenImage
572 // && img.getGraphics() instanceof GdkGraphics2D
573 // && (xform == null || xform.getType() == AffineTransform.TYPE_IDENTITY
574 // || xform.getType() == AffineTransform.TYPE_TRANSLATION))
575 // {
576 // // we are being asked to flush a double buffer from Gdk
577 // GdkGraphics2D g2 = (GdkGraphics2D) img.getGraphics();
578 // gdkDrawDrawable(g2, (int) xform.getTranslateX(),
579 // (int) xform.getTranslateY());
581 // updateBufferedImage();
583 // return true;
584 // }
585 // else
587 // In this case, xform is an AffineTransform that transforms bounding
588 // box of the specified image from image space to user space. However
589 // when we pass this transform to cairo, cairo will use this transform
590 // to map "user coordinates" to "pixel" coordinates, which is the
591 // other way around. Therefore to get the "user -> pixel" transform
592 // that cairo wants from "image -> user" transform that we currently
593 // have, we will need to invert the transformation matrix.
594 AffineTransform invertedXform = new AffineTransform();
598 invertedXform = xform.createInverse();
599 if (img instanceof BufferedImage)
601 // draw an image which has actually been loaded
602 // into memory fully
603 BufferedImage b = (BufferedImage) img;
604 return drawRaster(b.getColorModel(), b.getTile(0, 0),
605 invertedXform, bgcolor);
607 else
608 return this.drawImage(GdkPixbufDecoder.createBufferedImage(img
609 .getSource()),
610 xform, bgcolor, obs);
612 catch (NoninvertibleTransformException e)
614 throw new ImagingOpException("Unable to invert transform "
615 + xform.toString());
620 //////////////////////////////////////////////////
621 ////// Implementation of Graphics2D Methods //////
622 //////////////////////////////////////////////////
624 public void draw(Shape s)
626 if (stroke != null && ! (stroke instanceof BasicStroke))
628 fill(stroke.createStrokedShape(s));
629 return;
632 cairoNewPath();
634 if (s instanceof Rectangle2D)
636 Rectangle2D r = (Rectangle2D) s;
637 cairoRectangle(shifted(r.getX(), shiftDrawCalls),
638 shifted(r.getY(), shiftDrawCalls), r.getWidth(),
639 r.getHeight());
641 else
642 walkPath(s.getPathIterator(null), shiftDrawCalls);
643 cairoStroke();
645 updateBufferedImage();
648 public void fill(Shape s)
650 cairoNewPath();
651 if (s instanceof Rectangle2D)
653 Rectangle2D r = (Rectangle2D) s;
654 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
656 else
657 walkPath(s.getPathIterator(null), false);
659 cairoFill();
661 updateBufferedImage();
664 public void clip(Shape s)
666 // update it
667 if (clip == null || s == null)
668 clip = s;
669 else if (s instanceof Rectangle2D && clip instanceof Rectangle2D)
671 Rectangle2D r = (Rectangle2D) s;
672 Rectangle2D curr = (Rectangle2D) clip;
673 clip = curr.createIntersection(r);
675 else
676 throw new UnsupportedOperationException();
678 // draw it
679 if (clip != null)
681 cairoNewPath();
682 if (clip instanceof Rectangle2D)
684 Rectangle2D r = (Rectangle2D) clip;
685 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
687 else
688 walkPath(clip.getPathIterator(null), false);
690 // cairoClosePath ();
691 cairoClip();
695 public Paint getPaint()
697 return paint;
700 public AffineTransform getTransform()
702 return (AffineTransform) transform.clone();
705 public void setPaint(Paint p)
707 if (paint == null)
708 return;
710 paint = p;
711 if (paint instanceof Color)
713 setColor((Color) paint);
715 else if (paint instanceof TexturePaint)
717 TexturePaint tp = (TexturePaint) paint;
718 BufferedImage img = tp.getImage();
720 // map the image to the anchor rectangle
721 int width = (int) tp.getAnchorRect().getWidth();
722 int height = (int) tp.getAnchorRect().getHeight();
724 double scaleX = width / (double) img.getWidth();
725 double scaleY = width / (double) img.getHeight();
727 AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0);
728 AffineTransformOp op = new AffineTransformOp(at, getRenderingHints());
729 BufferedImage texture = op.filter(img, null);
730 int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width);
731 setTexturePixels(pixels, width, height, width);
733 else if (paint instanceof GradientPaint)
735 GradientPaint gp = (GradientPaint) paint;
736 Point2D p1 = gp.getPoint1();
737 Point2D p2 = gp.getPoint2();
738 Color c1 = gp.getColor1();
739 Color c2 = gp.getColor2();
740 setGradient(p1.getX(), p1.getY(), p2.getX(), p2.getY(), c1.getRed(),
741 c1.getGreen(), c1.getBlue(), c1.getAlpha(), c2.getRed(),
742 c2.getGreen(), c2.getBlue(), c2.getAlpha(), gp.isCyclic());
744 else
745 throw new java.lang.UnsupportedOperationException();
748 public void setPaintUnlocked(Paint p)
750 if (paint == null)
751 return;
753 paint = p;
754 if (paint instanceof Color)
756 setColorUnlocked((Color) paint);
758 else if (paint instanceof TexturePaint)
760 TexturePaint tp = (TexturePaint) paint;
761 BufferedImage img = tp.getImage();
763 // map the image to the anchor rectangle
764 int width = (int) tp.getAnchorRect().getWidth();
765 int height = (int) tp.getAnchorRect().getHeight();
767 double scaleX = width / (double) img.getWidth();
768 double scaleY = width / (double) img.getHeight();
770 AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0);
771 AffineTransformOp op = new AffineTransformOp(at, getRenderingHints());
772 BufferedImage texture = op.filter(img, null);
773 int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width);
774 setTexturePixelsUnlocked(pixels, width, height, width);
776 else if (paint instanceof GradientPaint)
778 GradientPaint gp = (GradientPaint) paint;
779 Point2D p1 = gp.getPoint1();
780 Point2D p2 = gp.getPoint2();
781 Color c1 = gp.getColor1();
782 Color c2 = gp.getColor2();
783 setGradientUnlocked(p1.getX(), p1.getY(), p2.getX(), p2.getY(), c1.getRed(),
784 c1.getGreen(), c1.getBlue(), c1.getAlpha(), c2.getRed(),
785 c2.getGreen(), c2.getBlue(), c2.getAlpha(), gp.isCyclic());
787 else
788 throw new java.lang.UnsupportedOperationException();
791 public void setTransform(AffineTransform tx)
793 transform = tx;
794 if (transform != null)
796 double[] m = new double[6];
797 transform.getMatrix(m);
798 cairoSetMatrix(m);
802 public void setTransformUnlocked(AffineTransform tx)
804 transform = tx;
805 if (transform != null)
807 double[] m = new double[6];
808 transform.getMatrix(m);
809 cairoSetMatrixUnlocked(m);
813 public void transform(AffineTransform tx)
815 if (transform == null)
816 transform = new AffineTransform(tx);
817 else
818 transform.concatenate(tx);
819 setTransform(transform);
820 if (clip != null)
822 // FIXME: this should actuall try to transform the shape
823 // rather than degrade to bounds.
824 Rectangle2D r = clip.getBounds2D();
825 double[] coords = new double[]
827 r.getX(), r.getY(), r.getX() + r.getWidth(),
828 r.getY() + r.getHeight()
832 tx.createInverse().transform(coords, 0, coords, 0, 2);
833 r.setRect(coords[0], coords[1], coords[2] - coords[0],
834 coords[3] - coords[1]);
835 clip = r;
837 catch (java.awt.geom.NoninvertibleTransformException e)
843 public void rotate(double theta)
845 transform(AffineTransform.getRotateInstance(theta));
848 public void rotate(double theta, double x, double y)
850 transform(AffineTransform.getRotateInstance(theta, x, y));
853 public void scale(double sx, double sy)
855 transform(AffineTransform.getScaleInstance(sx, sy));
858 public void translate(double tx, double ty)
860 transform(AffineTransform.getTranslateInstance(tx, ty));
863 public void translate(int x, int y)
865 translate((double) x, (double) y);
868 public void shear(double shearX, double shearY)
870 transform(AffineTransform.getShearInstance(shearX, shearY));
873 public Stroke getStroke()
875 return stroke;
878 public void setStroke(Stroke st)
880 stroke = st;
881 if (stroke instanceof BasicStroke)
883 BasicStroke bs = (BasicStroke) stroke;
884 cairoSetLineCap(bs.getEndCap());
885 cairoSetLineWidth(bs.getLineWidth());
886 cairoSetLineJoin(bs.getLineJoin());
887 cairoSetMiterLimit(bs.getMiterLimit());
888 float[] dashes = bs.getDashArray();
889 if (dashes != null)
891 double[] double_dashes = new double[dashes.length];
892 for (int i = 0; i < dashes.length; i++)
893 double_dashes[i] = dashes[i];
894 cairoSetDash(double_dashes, double_dashes.length,
895 (double) bs.getDashPhase());
897 else
898 cairoSetDash(new double[0], 0, 0.0);
902 public void setStrokeUnlocked(Stroke st)
904 stroke = st;
905 if (stroke instanceof BasicStroke)
907 BasicStroke bs = (BasicStroke) stroke;
908 cairoSetLineCapUnlocked(bs.getEndCap());
909 cairoSetLineWidthUnlocked(bs.getLineWidth());
910 cairoSetLineJoinUnlocked(bs.getLineJoin());
911 cairoSetMiterLimitUnlocked(bs.getMiterLimit());
912 float[] dashes = bs.getDashArray();
913 if (dashes != null)
915 double[] double_dashes = new double[dashes.length];
916 for (int i = 0; i < dashes.length; i++)
917 double_dashes[i] = dashes[i];
918 cairoSetDashUnlocked(double_dashes, double_dashes.length,
919 (double) bs.getDashPhase());
921 else
922 cairoSetDashUnlocked(new double[0], 0, 0.0);
926 ////////////////////////////////////////////////
927 ////// Implementation of Graphics Methods //////
928 ////////////////////////////////////////////////
930 public void setPaintMode()
932 setComposite(java.awt.AlphaComposite.SrcOver);
935 public void setXORMode(Color c)
937 setComposite(new gnu.java.awt.BitwiseXORComposite(c));
940 public void setColor(Color c)
942 if (c == null)
943 c = Color.BLACK;
945 fg = c;
946 paint = c;
947 cairoSetRGBAColor(fg.getRed() / 255.0, fg.getGreen() / 255.0,
948 fg.getBlue() / 255.0, fg.getAlpha() / 255.0);
951 public void setColorUnlocked(Color c)
953 if (c == null)
954 c = Color.BLACK;
956 fg = c;
957 paint = c;
958 cairoSetRGBAColorUnlocked(fg.getRed() / 255.0, fg.getGreen() / 255.0,
959 fg.getBlue() / 255.0, fg.getAlpha() / 255.0);
962 public Color getColor()
964 return fg;
967 public Color getColorUnlocked()
969 return getColor();
972 public void clipRect(int x, int y, int width, int height)
974 clip(new Rectangle(x, y, width, height));
977 public Shape getClip()
979 if (clip == null)
980 return null;
981 else
982 return clip.getBounds2D(); //getClipInDevSpace();
985 public Rectangle getClipBounds()
987 if (clip == null)
988 return null;
989 else
990 return clip.getBounds();
993 protected Rectangle2D getClipInDevSpace()
995 Rectangle2D uclip = clip.getBounds2D();
996 if (transform == null)
997 return uclip;
998 else
1000 Point2D pos = transform.transform(new Point2D.Double(uclip.getX(),
1001 uclip.getY()),
1002 (Point2D) null);
1003 Point2D extent = transform.deltaTransform(new Point2D.Double(uclip
1004 .getWidth(),
1005 uclip
1006 .getHeight()),
1007 (Point2D) null);
1008 return new Rectangle2D.Double(pos.getX(), pos.getY(), extent.getX(),
1009 extent.getY());
1013 public void setClip(int x, int y, int width, int height)
1015 setClip(new Rectangle2D.Double((double) x, (double) y, (double) width,
1016 (double) height));
1019 public void setClip(Shape s)
1021 clip = s;
1022 if (clip == null)
1024 // Reset clipping.
1025 if (component != null)
1027 Dimension d = component.awtComponent.getSize();
1028 setClip(0, 0, d.width, d.height);
1031 else
1033 cairoNewPath();
1034 if (s instanceof Rectangle2D)
1036 Rectangle2D r = (Rectangle2D) s;
1037 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
1039 else
1040 walkPath(s.getPathIterator(null), false);
1042 // cairoClosePath ();
1043 cairoClip();
1047 private static BasicStroke draw3DRectStroke = new BasicStroke();
1049 public void draw3DRect(int x, int y, int width, int height, boolean raised)
1051 Stroke tmp = stroke;
1052 setStroke(draw3DRectStroke);
1053 super.draw3DRect(x, y, width, height, raised);
1054 setStroke(tmp);
1055 updateBufferedImage();
1058 public void fill3DRect(int x, int y, int width, int height, boolean raised)
1060 Stroke tmp = stroke;
1061 setStroke(draw3DRectStroke);
1062 super.fill3DRect(x, y, width, height, raised);
1063 setStroke(tmp);
1064 updateBufferedImage();
1067 public void drawRect(int x, int y, int width, int height)
1069 draw(new Rectangle(x, y, width, height));
1072 public void fillRect(int x, int y, int width, int height)
1074 cairoNewPath();
1075 cairoRectangle(x, y, width, height);
1076 cairoFill();
1079 public void clearRect(int x, int y, int width, int height)
1081 if (bg != null)
1082 cairoSetRGBAColor(bg.getRed() / 255.0, bg.getGreen() / 255.0,
1083 bg.getBlue() / 255.0, 1.0);
1084 cairoNewPath();
1085 cairoRectangle(x, y, width, height);
1086 cairoFill();
1087 setColor(fg);
1089 updateBufferedImage();
1092 public void setBackground(Color c)
1094 if (c == null)
1095 c = Color.WHITE;
1096 bg = c;
1099 public void setBackgroundUnlocked(Color c)
1101 setBackground(c);
1104 public Color getBackground()
1106 return bg;
1109 private void doPolygon(int[] xPoints, int[] yPoints, int nPoints,
1110 boolean close, boolean fill)
1112 if (nPoints < 1)
1113 return;
1114 GeneralPath gp = new GeneralPath(PathIterator.WIND_EVEN_ODD);
1115 gp.moveTo((float) xPoints[0], (float) yPoints[0]);
1116 for (int i = 1; i < nPoints; i++)
1117 gp.lineTo((float) xPoints[i], (float) yPoints[i]);
1119 if (close)
1120 gp.closePath();
1122 Shape sh = gp;
1123 if (fill == false && stroke != null && ! (stroke instanceof BasicStroke))
1125 sh = stroke.createStrokedShape(gp);
1126 fill = true;
1129 if (fill)
1130 fill(sh);
1131 else
1132 draw(sh);
1135 public void drawLine(int x1, int y1, int x2, int y2)
1137 int[] xp = new int[2];
1138 int[] yp = new int[2];
1140 xp[0] = x1;
1141 xp[1] = x2;
1142 yp[0] = y1;
1143 yp[1] = y2;
1145 doPolygon(xp, yp, 2, false, false);
1148 public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
1150 doPolygon(xPoints, yPoints, nPoints, true, true);
1153 public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
1155 doPolygon(xPoints, yPoints, nPoints, true, false);
1158 public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
1160 doPolygon(xPoints, yPoints, nPoints, false, false);
1163 private boolean drawRaster(ColorModel cm, Raster r,
1164 AffineTransform imageToUser, Color bgcolor)
1166 if (r == null)
1167 return false;
1169 SampleModel sm = r.getSampleModel();
1170 DataBuffer db = r.getDataBuffer();
1172 if (db == null || sm == null)
1173 return false;
1175 if (cm == null)
1176 cm = ColorModel.getRGBdefault();
1178 double[] i2u = new double[6];
1179 if (imageToUser != null)
1180 imageToUser.getMatrix(i2u);
1181 else
1183 i2u[0] = 1;
1184 i2u[1] = 0;
1185 i2u[2] = 0;
1186 i2u[3] = 1;
1187 i2u[4] = 0;
1188 i2u[5] = 0;
1191 int[] pixels = findSimpleIntegerArray(cm, r);
1193 if (pixels == null)
1195 // FIXME: I don't think this code will work correctly with a non-RGB
1196 // MultiPixelPackedSampleModel. Although this entire method should
1197 // probably be rewritten to better utilize Cairo's different supported
1198 // data formats.
1199 if (sm instanceof MultiPixelPackedSampleModel)
1201 pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), pixels);
1202 for (int i = 0; i < pixels.length; i++)
1203 pixels[i] = cm.getRGB(pixels[i]);
1205 else
1207 pixels = new int[r.getWidth() * r.getHeight()];
1208 for (int i = 0; i < pixels.length; i++)
1209 pixels[i] = cm.getRGB(db.getElem(i));
1213 // Change all transparent pixels in the image to the specified bgcolor,
1214 // or (if there's no alpha) fill in an alpha channel so that it paints
1215 // correctly.
1216 if (cm.hasAlpha())
1218 if (bgcolor != null && cm.hasAlpha())
1219 for (int i = 0; i < pixels.length; i++)
1221 if (cm.getAlpha(pixels[i]) == 0)
1222 pixels[i] = bgcolor.getRGB();
1225 else
1226 for (int i = 0; i < pixels.length; i++)
1227 pixels[i] |= 0xFF000000;
1229 drawPixels(pixels, r.getWidth(), r.getHeight(), r.getWidth(), i2u);
1231 updateBufferedImage();
1233 return true;
1236 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
1238 drawRaster(image.getColorModel(), image.getData(), xform, bg);
1241 public void drawRenderableImage(RenderableImage image, AffineTransform xform)
1243 drawRenderedImage(image.createRendering(new RenderContext(xform)), xform);
1246 public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
1248 return drawImage(img, xform, bg, obs);
1251 public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
1253 Image filtered = op.filter(image, null);
1254 drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg, null);
1257 public boolean drawImage(Image img, int x, int y, ImageObserver observer)
1259 return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg,
1260 observer);
1263 ///////////////////////////////////////////////
1264 ////// Unimplemented Stubs and Overloads //////
1265 ///////////////////////////////////////////////
1267 public boolean hit(Rectangle rect, Shape text, boolean onStroke)
1269 throw new java.lang.UnsupportedOperationException();
1272 public GraphicsConfiguration getDeviceConfiguration()
1274 throw new java.lang.UnsupportedOperationException();
1277 public void setComposite(Composite comp)
1279 this.comp = comp;
1281 if (comp instanceof AlphaComposite)
1283 AlphaComposite a = (AlphaComposite) comp;
1284 cairoSetOperator(a.getRule());
1285 Color c = getColor();
1286 setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(),
1287 (int) (a.getAlpha() * ((float) c.getAlpha()))));
1289 else
1290 throw new java.lang.UnsupportedOperationException();
1293 public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
1295 hints.put(hintKey, hintValue);
1297 if (hintKey.equals(RenderingHints.KEY_INTERPOLATION)
1298 || hintKey.equals(RenderingHints.KEY_ALPHA_INTERPOLATION))
1300 if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1301 cairoSurfaceSetFilter(0);
1303 else if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1304 cairoSurfaceSetFilter(1);
1306 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1307 cairoSurfaceSetFilter(2);
1309 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1310 cairoSurfaceSetFilter(3);
1312 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1313 cairoSurfaceSetFilter(4);
1316 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1317 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1320 public Object getRenderingHint(RenderingHints.Key hintKey)
1322 return hints.get(hintKey);
1325 public void setRenderingHints(Map hints)
1327 this.hints = new RenderingHints(getDefaultHints());
1328 this.hints.add(new RenderingHints(hints));
1330 if (hints.containsKey(RenderingHints.KEY_INTERPOLATION))
1332 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1333 cairoSurfaceSetFilter(0);
1335 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1336 cairoSurfaceSetFilter(1);
1339 if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION))
1341 if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1342 cairoSurfaceSetFilter(2);
1344 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1345 cairoSurfaceSetFilter(3);
1347 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1348 cairoSurfaceSetFilter(4);
1351 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1352 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1355 public void setRenderingHintsUnlocked(Map hints)
1357 this.hints = new RenderingHints(getDefaultHints());
1358 this.hints.add(new RenderingHints(hints));
1360 if (hints.containsKey(RenderingHints.KEY_INTERPOLATION))
1362 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1363 cairoSurfaceSetFilterUnlocked(0);
1365 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1366 cairoSurfaceSetFilterUnlocked(1);
1369 if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION))
1371 if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1372 cairoSurfaceSetFilterUnlocked(2);
1374 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1375 cairoSurfaceSetFilterUnlocked(3);
1377 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1378 cairoSurfaceSetFilterUnlocked(4);
1381 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1382 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1385 public void addRenderingHints(Map hints)
1387 this.hints.add(new RenderingHints(hints));
1390 public RenderingHints getRenderingHints()
1392 return hints;
1395 public Composite getComposite()
1397 if (comp == null)
1398 return AlphaComposite.SrcOver;
1399 else
1400 return comp;
1403 public FontRenderContext getFontRenderContext()
1405 return new FontRenderContext(transform, true, true);
1408 public void copyArea(int x, int y, int width, int height, int dx, int dy)
1410 GdkGraphics2D g = (GdkGraphics2D) create(x, y, width, height);
1411 gdkDrawDrawable(g, x + dx, y + dy);
1414 public void drawArc(int x, int y, int width, int height, int startAngle,
1415 int arcAngle)
1417 draw(new Arc2D.Double((double) x, (double) y, (double) width,
1418 (double) height, (double) startAngle,
1419 (double) arcAngle, Arc2D.OPEN));
1422 public boolean drawImage(Image img, int x, int y, Color bgcolor,
1423 ImageObserver observer)
1425 return drawImage(img, x, y, img.getWidth(observer),
1426 img.getHeight(observer), bgcolor, observer);
1429 public boolean drawImage(Image img, int x, int y, int width, int height,
1430 Color bgcolor, ImageObserver observer)
1432 double scaleX = width / (double) img.getWidth(observer);
1433 double scaleY = height / (double) img.getHeight(observer);
1435 return drawImage(img, new AffineTransform(scaleX, 0f, 0f, scaleY, x, y),
1436 bgcolor, observer);
1439 public boolean drawImage(Image img, int x, int y, int width, int height,
1440 ImageObserver observer)
1442 return drawImage(img, x, y, width, height, bg, observer);
1445 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1446 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1447 ImageObserver observer)
1449 if (img == null)
1450 return false;
1452 Image subImage;
1454 int sourceWidth = sx2 - sx1;
1455 int sourceHeight = sy2 - sy1;
1457 int destWidth = dx2 - dx1;
1458 int destHeight = dy2 - dy1;
1460 double scaleX = destWidth / (double) sourceWidth;
1461 double scaleY = destHeight / (double) sourceHeight;
1463 // Get the subimage of the source enclosed in the
1464 // rectangle specified by sx1, sy1, sx2, sy2
1466 if (img instanceof BufferedImage)
1468 BufferedImage b = (BufferedImage) img;
1469 subImage = b.getSubimage(sx1, sy1, sx2, sy2);
1471 else
1473 // FIXME: This code currently doesn't work. Null Pointer
1474 // exception is thrown in this case. This happens
1475 // because img.getSource() always returns null, since source gets
1476 // never initialized when it is created with the help of
1477 // createImage(int width, int height).
1478 CropImageFilter filter = new CropImageFilter(sx1, sx2, sx2, sy2);
1479 FilteredImageSource src = new FilteredImageSource(img.getSource(),
1480 filter);
1482 subImage = Toolkit.getDefaultToolkit().createImage(src);
1485 return drawImage(subImage,
1486 new AffineTransform(scaleX, 0, 0, scaleY, dx1, dy1),
1487 bgcolor, observer);
1490 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1491 int sx1, int sy1, int sx2, int sy2,
1492 ImageObserver observer)
1494 return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bg, observer);
1497 public void drawOval(int x, int y, int width, int height)
1499 drawArc(x, y, width, height, 0, 360);
1502 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1503 int arcHeight)
1505 if (arcWidth > width)
1506 arcWidth = width;
1507 if (arcHeight > height)
1508 arcHeight = height;
1510 int xx = x + width - arcWidth;
1511 int yy = y + height - arcHeight;
1513 drawArc(x, y, arcWidth, arcHeight, 90, 90);
1514 drawArc(xx, y, arcWidth, arcHeight, 0, 90);
1515 drawArc(xx, yy, arcWidth, arcHeight, 270, 90);
1516 drawArc(x, yy, arcWidth, arcHeight, 180, 90);
1518 int y1 = y + arcHeight / 2;
1519 int y2 = y + height - arcHeight / 2;
1520 drawLine(x, y1, x, y2);
1521 drawLine(x + width, y1, x + width, y2);
1523 int x1 = x + arcWidth / 2;
1524 int x2 = x + width - arcWidth / 2;
1525 drawLine(x1, y, x2, y);
1526 drawLine(x1, y + height, x2, y + height);
1529 // these are the most accelerated painting paths
1530 native void cairoDrawGlyphVector(GdkFontPeer font,
1531 float x, float y, int n,
1532 int[] codes, float[] positions);
1534 native void cairoDrawGdkTextLayout(GdkTextLayout gl,
1535 float x, float y);
1537 GdkFontPeer getFontPeer()
1539 return (GdkFontPeer) getFont().getPeer();
1542 public void drawGdkTextLayout(GdkTextLayout gl, float x, float y)
1544 cairoDrawGdkTextLayout (gl, x, y);
1545 updateBufferedImage ();
1548 public void drawString(String str, float x, float y)
1550 if (str == null || str.length() == 0)
1551 return;
1553 drawGlyphVector(getFont().createGlyphVector(null, str), x, y);
1554 updateBufferedImage ();
1557 public void drawString(String str, int x, int y)
1559 drawString (str, (float) x, (float) y);
1562 public void drawString(AttributedCharacterIterator ci, int x, int y)
1564 drawString (ci, (float) x, (float) y);
1567 public void drawGlyphVector(GlyphVector gv, float x, float y)
1569 int n = gv.getNumGlyphs ();
1570 int[] codes = gv.getGlyphCodes (0, n, null);
1571 float[] positions = gv.getGlyphPositions (0, n, null);
1573 setFont (gv.getFont ());
1574 cairoDrawGlyphVector (getFontPeer(), x, y, n, codes, positions);
1575 updateBufferedImage ();
1578 public void drawString(AttributedCharacterIterator ci, float x, float y)
1580 GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), ci);
1581 drawGlyphVector(gv, x, y);
1584 public void fillArc(int x, int y, int width, int height, int startAngle,
1585 int arcAngle)
1587 fill(new Arc2D.Double((double) x, (double) y, (double) width,
1588 (double) height, (double) startAngle,
1589 (double) arcAngle, Arc2D.OPEN));
1592 public void fillOval(int x, int y, int width, int height)
1594 fillArc(x, y, width, height, 0, 360);
1597 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1598 int arcHeight)
1600 if (arcWidth > width)
1601 arcWidth = width;
1602 if (arcHeight > height)
1603 arcHeight = height;
1605 int xx = x + width - arcWidth;
1606 int yy = y + height - arcHeight;
1608 fillArc(x, y, arcWidth, arcHeight, 90, 90);
1609 fillArc(xx, y, arcWidth, arcHeight, 0, 90);
1610 fillArc(xx, yy, arcWidth, arcHeight, 270, 90);
1611 fillArc(x, yy, arcWidth, arcHeight, 180, 90);
1613 fillRect(x, y + arcHeight / 2, width, height - arcHeight + 1);
1614 fillRect(x + arcWidth / 2, y, width - arcWidth + 1, height);
1617 public Font getFont()
1619 if (font == null)
1620 return new Font("SansSerif", Font.PLAIN, 12);
1621 return font;
1624 // Until such time as pango is happy to talk directly to cairo, we
1625 // actually need to redirect some calls from the GtkFontPeer and
1626 // GtkFontMetrics into the drawing kit and ask cairo ourselves.
1628 static native void releasePeerGraphicsResource(GdkFontPeer f);
1630 public FontMetrics getFontMetrics()
1632 return getFontMetrics(getFont());
1635 public FontMetrics getFontMetrics(Font f)
1637 // the reason we go via the toolkit here is to try to get
1638 // a cached object. the toolkit keeps such a cache.
1639 return Toolkit.getDefaultToolkit().getFontMetrics(f);
1642 public void setFont(Font f)
1644 // Sun's JDK does not throw NPEs, instead it leaves the current setting
1645 // unchanged. So do we.
1646 if (f == null)
1647 return;
1649 if (f.getPeer() instanceof GdkFontPeer)
1650 font = f;
1651 else
1652 font =
1653 ((ClasspathToolkit)(Toolkit.getDefaultToolkit()))
1654 .getFont(f.getName(), f.getAttributes());
1657 public void setFontUnlocked(Font f)
1659 setFont (f);
1662 public String toString()
1664 return (getClass().getName()
1665 + "[font=" + getFont().toString()
1666 + ",color=" + fg.toString()
1667 + "]");