Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / java / awt / peer / gtk / GdkGraphics2D.java
blob323d5614aa4770c82b769c05d42b568b9d29f596
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 // Cairo seems loosing the current color.
1234 setColor(fg);
1236 return true;
1239 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
1241 drawRaster(image.getColorModel(), image.getData(), xform, bg);
1244 public void drawRenderableImage(RenderableImage image, AffineTransform xform)
1246 drawRenderedImage(image.createRendering(new RenderContext(xform)), xform);
1249 public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
1251 return drawImage(img, xform, bg, obs);
1254 public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
1256 Image filtered = op.filter(image, null);
1257 drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg, null);
1260 public boolean drawImage(Image img, int x, int y, ImageObserver observer)
1262 return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg,
1263 observer);
1266 ///////////////////////////////////////////////
1267 ////// Unimplemented Stubs and Overloads //////
1268 ///////////////////////////////////////////////
1270 public boolean hit(Rectangle rect, Shape text, boolean onStroke)
1272 throw new java.lang.UnsupportedOperationException();
1275 public GraphicsConfiguration getDeviceConfiguration()
1277 throw new java.lang.UnsupportedOperationException();
1280 public void setComposite(Composite comp)
1282 this.comp = comp;
1284 if (comp instanceof AlphaComposite)
1286 AlphaComposite a = (AlphaComposite) comp;
1287 cairoSetOperator(a.getRule());
1288 Color c = getColor();
1289 setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(),
1290 (int) (a.getAlpha() * ((float) c.getAlpha()))));
1292 else
1293 throw new java.lang.UnsupportedOperationException();
1296 public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
1298 hints.put(hintKey, hintValue);
1300 if (hintKey.equals(RenderingHints.KEY_INTERPOLATION)
1301 || hintKey.equals(RenderingHints.KEY_ALPHA_INTERPOLATION))
1303 if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1304 cairoSurfaceSetFilter(0);
1306 else if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1307 cairoSurfaceSetFilter(1);
1309 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1310 cairoSurfaceSetFilter(2);
1312 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1313 cairoSurfaceSetFilter(3);
1315 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1316 cairoSurfaceSetFilter(4);
1319 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1320 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1323 public Object getRenderingHint(RenderingHints.Key hintKey)
1325 return hints.get(hintKey);
1328 public void setRenderingHints(Map hints)
1330 this.hints = new RenderingHints(getDefaultHints());
1331 this.hints.add(new RenderingHints(hints));
1333 if (hints.containsKey(RenderingHints.KEY_INTERPOLATION))
1335 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1336 cairoSurfaceSetFilter(0);
1338 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1339 cairoSurfaceSetFilter(1);
1342 if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION))
1344 if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1345 cairoSurfaceSetFilter(2);
1347 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1348 cairoSurfaceSetFilter(3);
1350 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1351 cairoSurfaceSetFilter(4);
1354 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1355 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1358 public void setRenderingHintsUnlocked(Map hints)
1360 this.hints = new RenderingHints(getDefaultHints());
1361 this.hints.add(new RenderingHints(hints));
1363 if (hints.containsKey(RenderingHints.KEY_INTERPOLATION))
1365 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1366 cairoSurfaceSetFilterUnlocked(0);
1368 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1369 cairoSurfaceSetFilterUnlocked(1);
1372 if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION))
1374 if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1375 cairoSurfaceSetFilterUnlocked(2);
1377 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1378 cairoSurfaceSetFilterUnlocked(3);
1380 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1381 cairoSurfaceSetFilterUnlocked(4);
1384 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1385 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1388 public void addRenderingHints(Map hints)
1390 this.hints.add(new RenderingHints(hints));
1393 public RenderingHints getRenderingHints()
1395 return hints;
1398 public Composite getComposite()
1400 if (comp == null)
1401 return AlphaComposite.SrcOver;
1402 else
1403 return comp;
1406 public FontRenderContext getFontRenderContext()
1408 return new FontRenderContext(transform, true, true);
1411 public void copyArea(int x, int y, int width, int height, int dx, int dy)
1413 GdkGraphics2D g = (GdkGraphics2D) create(x, y, width, height);
1414 gdkDrawDrawable(g, x + dx, y + dy);
1417 public void drawArc(int x, int y, int width, int height, int startAngle,
1418 int arcAngle)
1420 draw(new Arc2D.Double((double) x, (double) y, (double) width,
1421 (double) height, (double) startAngle,
1422 (double) arcAngle, Arc2D.OPEN));
1425 public boolean drawImage(Image img, int x, int y, Color bgcolor,
1426 ImageObserver observer)
1428 return drawImage(img, x, y, img.getWidth(observer),
1429 img.getHeight(observer), bgcolor, observer);
1432 public boolean drawImage(Image img, int x, int y, int width, int height,
1433 Color bgcolor, ImageObserver observer)
1435 double scaleX = width / (double) img.getWidth(observer);
1436 double scaleY = height / (double) img.getHeight(observer);
1438 return drawImage(img, new AffineTransform(scaleX, 0f, 0f, scaleY, x, y),
1439 bgcolor, observer);
1442 public boolean drawImage(Image img, int x, int y, int width, int height,
1443 ImageObserver observer)
1445 return drawImage(img, x, y, width, height, bg, observer);
1448 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1449 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1450 ImageObserver observer)
1452 if (img == null)
1453 return false;
1455 Image subImage;
1457 int sourceWidth = sx2 - sx1;
1458 int sourceHeight = sy2 - sy1;
1460 int destWidth = dx2 - dx1;
1461 int destHeight = dy2 - dy1;
1463 double scaleX = destWidth / (double) sourceWidth;
1464 double scaleY = destHeight / (double) sourceHeight;
1466 // Get the subimage of the source enclosed in the
1467 // rectangle specified by sx1, sy1, sx2, sy2
1469 if (img instanceof BufferedImage)
1471 BufferedImage b = (BufferedImage) img;
1472 subImage = b.getSubimage(sx1, sy1, sx2, sy2);
1474 else
1476 // FIXME: This code currently doesn't work. Null Pointer
1477 // exception is thrown in this case. This happens
1478 // because img.getSource() always returns null, since source gets
1479 // never initialized when it is created with the help of
1480 // createImage(int width, int height).
1481 CropImageFilter filter = new CropImageFilter(sx1, sx2, sx2, sy2);
1482 FilteredImageSource src = new FilteredImageSource(img.getSource(),
1483 filter);
1485 subImage = Toolkit.getDefaultToolkit().createImage(src);
1488 return drawImage(subImage,
1489 new AffineTransform(scaleX, 0, 0, scaleY, dx1, dy1),
1490 bgcolor, observer);
1493 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1494 int sx1, int sy1, int sx2, int sy2,
1495 ImageObserver observer)
1497 return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bg, observer);
1500 public void drawOval(int x, int y, int width, int height)
1502 drawArc(x, y, width, height, 0, 360);
1505 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1506 int arcHeight)
1508 if (arcWidth > width)
1509 arcWidth = width;
1510 if (arcHeight > height)
1511 arcHeight = height;
1513 int xx = x + width - arcWidth;
1514 int yy = y + height - arcHeight;
1516 drawArc(x, y, arcWidth, arcHeight, 90, 90);
1517 drawArc(xx, y, arcWidth, arcHeight, 0, 90);
1518 drawArc(xx, yy, arcWidth, arcHeight, 270, 90);
1519 drawArc(x, yy, arcWidth, arcHeight, 180, 90);
1521 int y1 = y + arcHeight / 2;
1522 int y2 = y + height - arcHeight / 2;
1523 drawLine(x, y1, x, y2);
1524 drawLine(x + width, y1, x + width, y2);
1526 int x1 = x + arcWidth / 2;
1527 int x2 = x + width - arcWidth / 2;
1528 drawLine(x1, y, x2, y);
1529 drawLine(x1, y + height, x2, y + height);
1532 // these are the most accelerated painting paths
1533 native void cairoDrawGlyphVector(GdkFontPeer font,
1534 float x, float y, int n,
1535 int[] codes, float[] positions);
1537 native void cairoDrawGdkTextLayout(GdkTextLayout gl,
1538 float x, float y);
1540 GdkFontPeer getFontPeer()
1542 return (GdkFontPeer) getFont().getPeer();
1545 public void drawGdkTextLayout(GdkTextLayout gl, float x, float y)
1547 cairoDrawGdkTextLayout (gl, x, y);
1548 updateBufferedImage ();
1551 public void drawString(String str, float x, float y)
1553 if (str == null || str.length() == 0)
1554 return;
1556 drawGlyphVector(getFont().createGlyphVector(null, str), x, y);
1557 updateBufferedImage ();
1560 public void drawString(String str, int x, int y)
1562 drawString (str, (float) x, (float) y);
1565 public void drawString(AttributedCharacterIterator ci, int x, int y)
1567 drawString (ci, (float) x, (float) y);
1570 public void drawGlyphVector(GlyphVector gv, float x, float y)
1572 int n = gv.getNumGlyphs ();
1573 int[] codes = gv.getGlyphCodes (0, n, null);
1574 float[] positions = gv.getGlyphPositions (0, n, null);
1576 setFont (gv.getFont ());
1577 cairoDrawGlyphVector (getFontPeer(), x, y, n, codes, positions);
1578 updateBufferedImage ();
1581 public void drawString(AttributedCharacterIterator ci, float x, float y)
1583 GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), ci);
1584 drawGlyphVector(gv, x, y);
1587 public void fillArc(int x, int y, int width, int height, int startAngle,
1588 int arcAngle)
1590 fill(new Arc2D.Double((double) x, (double) y, (double) width,
1591 (double) height, (double) startAngle,
1592 (double) arcAngle, Arc2D.OPEN));
1595 public void fillOval(int x, int y, int width, int height)
1597 fillArc(x, y, width, height, 0, 360);
1600 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1601 int arcHeight)
1603 if (arcWidth > width)
1604 arcWidth = width;
1605 if (arcHeight > height)
1606 arcHeight = height;
1608 int xx = x + width - arcWidth;
1609 int yy = y + height - arcHeight;
1611 fillArc(x, y, arcWidth, arcHeight, 90, 90);
1612 fillArc(xx, y, arcWidth, arcHeight, 0, 90);
1613 fillArc(xx, yy, arcWidth, arcHeight, 270, 90);
1614 fillArc(x, yy, arcWidth, arcHeight, 180, 90);
1616 fillRect(x, y + arcHeight / 2, width, height - arcHeight + 1);
1617 fillRect(x + arcWidth / 2, y, width - arcWidth + 1, height);
1620 public Font getFont()
1622 if (font == null)
1623 return new Font("SansSerif", Font.PLAIN, 12);
1624 return font;
1627 // Until such time as pango is happy to talk directly to cairo, we
1628 // actually need to redirect some calls from the GtkFontPeer and
1629 // GtkFontMetrics into the drawing kit and ask cairo ourselves.
1631 static native void releasePeerGraphicsResource(GdkFontPeer f);
1633 public FontMetrics getFontMetrics()
1635 return getFontMetrics(getFont());
1638 public FontMetrics getFontMetrics(Font f)
1640 // the reason we go via the toolkit here is to try to get
1641 // a cached object. the toolkit keeps such a cache.
1642 return Toolkit.getDefaultToolkit().getFontMetrics(f);
1645 public void setFont(Font f)
1647 // Sun's JDK does not throw NPEs, instead it leaves the current setting
1648 // unchanged. So do we.
1649 if (f == null)
1650 return;
1652 if (f.getPeer() instanceof GdkFontPeer)
1653 font = f;
1654 else
1655 font =
1656 ((ClasspathToolkit)(Toolkit.getDefaultToolkit()))
1657 .getFont(f.getName(), f.getAttributes());
1660 public void setFontUnlocked(Font f)
1662 setFont (f);
1665 public String toString()
1667 return (getClass().getName()
1668 + "[font=" + getFont().toString()
1669 + ",color=" + fg.toString()
1670 + "]");