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)
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
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
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
;
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
;
93 import java
.util
.Stack
;
95 public class GdkGraphics2D
extends Graphics2D
97 //////////////////////////////////////
98 ////// State Management Methods //////
99 //////////////////////////////////////
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");
113 static native void initStaticState();
115 private final int native_state
= GtkGenericPeer
.getUniqueInteger();
117 // These are package-private to avoid accessor methods.
123 AffineTransform transform
;
124 private GtkComponentPeer component
;
125 // This is package-private to avoid an accessor method.
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.
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()
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");
168 GdkGraphics2D(GdkGraphics2D g
)
170 if (!GtkToolkit
.useGraphics2D ())
175 setRenderingHints(g
.hints
);
177 if (g
.fg
.getAlpha() != -1)
178 fg
= new Color(g
.fg
.getRed(), g
.fg
.getGreen(), g
.fg
.getBlue(),
181 fg
= new Color(g
.fg
.getRGB());
185 if (g
.bg
.getAlpha() != -1)
186 bg
= new Color(g
.bg
.getRed(), g
.bg
.getGreen(), g
.bg
.getBlue(),
189 bg
= new Color(g
.bg
.getRGB());
195 clip
= new Rectangle(g
.getClipBounds());
197 if (g
.transform
== null)
198 transform
= new AffineTransform();
200 transform
= new AffineTransform(g
.transform
);
203 component
= g
.component
;
210 setTransform(transform
);
212 stateStack
= new Stack();
215 GdkGraphics2D(GdkGraphics2D g
, int x
, int y
, int widht
, int height
)
219 clipRect(0, 0, widht
, height
);
222 GdkGraphics2D(int width
, int height
)
224 if (!GtkToolkit
.useGraphics2D ())
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 ())
245 this.component
= component
;
247 if (component
.isRealized())
248 initComponentGraphics2D();
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(),
288 if (this.pixelBuffer
== null)
290 this.pixelBuffer
= new int[bimage
.getRaster().getWidth() * bimage
.getRaster()
292 this.pixelConversionRequired
= true;
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
,
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
,
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
381 private Stroke stroke
;
385 private AffineTransform transform
;
387 private Composite comp
;
389 DrawState(GdkGraphics2D g
)
391 this.paint
= g
.paint
;
392 this.stroke
= g
.stroke
;
396 if (g
.transform
!= null)
397 this.transform
= (AffineTransform
) g
.transform
.clone();
402 public void restore(GdkGraphics2D g
)
404 g
.paint
= this.paint
;
405 g
.stroke
= this.stroke
;
409 g
.transform
= this.transform
;
415 private void stateSave()
417 stateStack
.push(new DrawState(this));
421 private void stateRestore()
423 ((DrawState
) (stateStack
.pop())).restore(this);
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
)
435 return Math
.floor(coord
) + 0.5;
440 private void walkPath(PathIterator p
, boolean doShift
)
444 double[] coords
= new double[6];
446 cairoSetFillRule(p
.getWindingRule());
447 for (; ! p
.isDone(); p
.next())
449 int seg
= p
.currentSegment(coords
);
452 case PathIterator
.SEG_MOVETO
:
453 x
= shifted(coords
[0], doShift
);
454 y
= shifted(coords
[1], doShift
);
457 case PathIterator
.SEG_LINETO
:
458 x
= shifted(coords
[0], doShift
);
459 y
= shifted(coords
[1], doShift
);
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
);
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
);
483 case PathIterator
.SEG_CLOSE
:
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
);
512 public static int[] findSimpleIntegerArray (ColorModel cm
, Raster raster
)
514 if (cm
== null || raster
== null)
517 if (! cm
.getColorSpace().isCS_sRGB())
520 if (! (cm
instanceof DirectColorModel
))
523 DirectColorModel dcm
= (DirectColorModel
) cm
;
525 if (dcm
.getRedMask() != 0x00FF0000 || dcm
.getGreenMask() != 0x0000FF00
526 || dcm
.getBlueMask() != 0x000000FF)
529 if (! (raster
instanceof WritableRaster
))
532 if (raster
.getSampleModel().getDataType() != DataBuffer
.TYPE_INT
)
535 if (! (raster
.getDataBuffer() instanceof DataBufferInt
))
538 DataBufferInt db
= (DataBufferInt
) raster
.getDataBuffer();
540 if (db
.getNumBanks() != 1)
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.
551 private void updateBufferedImage()
553 if (bimage
!= null && pixelConversionRequired
)
555 int height
= bimage
.getHeight();
556 int width
= bimage
.getWidth();
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
)
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))
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();
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
603 BufferedImage b
= (BufferedImage
) img
;
604 return drawRaster(b
.getColorModel(), b
.getTile(0, 0),
605 invertedXform
, bgcolor
);
608 return this.drawImage(GdkPixbufDecoder
.createBufferedImage(img
610 xform
, bgcolor
, obs
);
612 catch (NoninvertibleTransformException e
)
614 throw new ImagingOpException("Unable to invert transform "
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
));
634 if (s
instanceof Rectangle2D
)
636 Rectangle2D r
= (Rectangle2D
) s
;
637 cairoRectangle(shifted(r
.getX(), shiftDrawCalls
),
638 shifted(r
.getY(), shiftDrawCalls
), r
.getWidth(),
642 walkPath(s
.getPathIterator(null), shiftDrawCalls
);
645 updateBufferedImage();
648 public void fill(Shape s
)
651 if (s
instanceof Rectangle2D
)
653 Rectangle2D r
= (Rectangle2D
) s
;
654 cairoRectangle(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
657 walkPath(s
.getPathIterator(null), false);
661 updateBufferedImage();
664 public void clip(Shape s
)
667 if (clip
== null || s
== null)
669 else if (s
instanceof Rectangle2D
&& clip
instanceof Rectangle2D
)
671 Rectangle2D r
= (Rectangle2D
) s
;
672 Rectangle2D curr
= (Rectangle2D
) clip
;
673 clip
= curr
.createIntersection(r
);
676 throw new UnsupportedOperationException();
682 if (clip
instanceof Rectangle2D
)
684 Rectangle2D r
= (Rectangle2D
) clip
;
685 cairoRectangle(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
688 walkPath(clip
.getPathIterator(null), false);
690 // cairoClosePath ();
695 public Paint
getPaint()
700 public AffineTransform
getTransform()
702 return (AffineTransform
) transform
.clone();
705 public void setPaint(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());
745 throw new java
.lang
.UnsupportedOperationException();
748 public void setPaintUnlocked(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());
788 throw new java
.lang
.UnsupportedOperationException();
791 public void setTransform(AffineTransform tx
)
794 if (transform
!= null)
796 double[] m
= new double[6];
797 transform
.getMatrix(m
);
802 public void setTransformUnlocked(AffineTransform 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
);
818 transform
.concatenate(tx
);
819 setTransform(transform
);
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]);
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()
878 public void setStroke(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();
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());
898 cairoSetDash(new double[0], 0, 0.0);
902 public void setStrokeUnlocked(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();
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());
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
)
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
)
958 cairoSetRGBAColorUnlocked(fg
.getRed() / 255.0, fg
.getGreen() / 255.0,
959 fg
.getBlue() / 255.0, fg
.getAlpha() / 255.0);
962 public Color
getColor()
967 public Color
getColorUnlocked()
972 public void clipRect(int x
, int y
, int width
, int height
)
974 clip(new Rectangle(x
, y
, width
, height
));
977 public Shape
getClip()
982 return clip
.getBounds2D(); //getClipInDevSpace();
985 public Rectangle
getClipBounds()
990 return clip
.getBounds();
993 protected Rectangle2D
getClipInDevSpace()
995 Rectangle2D uclip
= clip
.getBounds2D();
996 if (transform
== null)
1000 Point2D pos
= transform
.transform(new Point2D
.Double(uclip
.getX(),
1003 Point2D extent
= transform
.deltaTransform(new Point2D
.Double(uclip
1008 return new Rectangle2D
.Double(pos
.getX(), pos
.getY(), extent
.getX(),
1013 public void setClip(int x
, int y
, int width
, int height
)
1015 setClip(new Rectangle2D
.Double((double) x
, (double) y
, (double) width
,
1019 public void setClip(Shape s
)
1025 if (component
!= null)
1027 Dimension d
= component
.awtComponent
.getSize();
1028 setClip(0, 0, d
.width
, d
.height
);
1034 if (s
instanceof Rectangle2D
)
1036 Rectangle2D r
= (Rectangle2D
) s
;
1037 cairoRectangle(r
.getX(), r
.getY(), r
.getWidth(), r
.getHeight());
1040 walkPath(s
.getPathIterator(null), false);
1042 // cairoClosePath ();
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
);
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
);
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
)
1075 cairoRectangle(x
, y
, width
, height
);
1079 public void clearRect(int x
, int y
, int width
, int height
)
1082 cairoSetRGBAColor(bg
.getRed() / 255.0, bg
.getGreen() / 255.0,
1083 bg
.getBlue() / 255.0, 1.0);
1085 cairoRectangle(x
, y
, width
, height
);
1089 updateBufferedImage();
1092 public void setBackground(Color c
)
1099 public void setBackgroundUnlocked(Color c
)
1104 public Color
getBackground()
1109 private void doPolygon(int[] xPoints
, int[] yPoints
, int nPoints
,
1110 boolean close
, boolean fill
)
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
]);
1123 if (fill
== false && stroke
!= null && ! (stroke
instanceof BasicStroke
))
1125 sh
= stroke
.createStrokedShape(gp
);
1135 public void drawLine(int x1
, int y1
, int x2
, int y2
)
1137 int[] xp
= new int[2];
1138 int[] yp
= new int[2];
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
)
1169 SampleModel sm
= r
.getSampleModel();
1170 DataBuffer db
= r
.getDataBuffer();
1172 if (db
== null || sm
== null)
1176 cm
= ColorModel
.getRGBdefault();
1178 double[] i2u
= new double[6];
1179 if (imageToUser
!= null)
1180 imageToUser
.getMatrix(i2u
);
1191 int[] pixels
= findSimpleIntegerArray(cm
, r
);
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
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
]);
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
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();
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();
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
,
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
)
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()))));
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()
1395 public Composite
getComposite()
1398 return AlphaComposite
.SrcOver
;
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
,
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
),
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
)
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
);
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(),
1482 subImage
= Toolkit
.getDefaultToolkit().createImage(src
);
1485 return drawImage(subImage
,
1486 new AffineTransform(scaleX
, 0, 0, scaleY
, dx1
, dy1
),
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
,
1505 if (arcWidth
> width
)
1507 if (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
,
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)
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
,
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
,
1600 if (arcWidth
> width
)
1602 if (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()
1620 return new Font("SansSerif", Font
.PLAIN
, 12);
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.
1649 if (f
.getPeer() instanceof GdkFontPeer
)
1653 ((ClasspathToolkit
)(Toolkit
.getDefaultToolkit()))
1654 .getFont(f
.getName(), f
.getAttributes());
1657 public void setFontUnlocked(Font f
)
1662 public String
toString()
1664 return (getClass().getName()
1665 + "[font=" + getFont().toString()
1666 + ",color=" + fg
.toString()