Merge from the pain train
[official-gcc.git] / libjava / gnu / java / awt / peer / gtk / GdkGraphics2D.java
blob513c75c094a3d112d9ca2fe2a4d4bd5d2b3eac57
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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.Font;
49 import java.awt.FontMetrics;
50 import java.awt.GradientPaint;
51 import java.awt.Graphics;
52 import java.awt.Graphics2D;
53 import java.awt.GraphicsConfiguration;
54 import java.awt.Image;
55 import java.awt.Paint;
56 import java.awt.Rectangle;
57 import java.awt.RenderingHints;
58 import java.awt.Shape;
59 import java.awt.Stroke;
60 import java.awt.TexturePaint;
61 import java.awt.Toolkit;
62 import java.awt.font.FontRenderContext;
63 import java.awt.font.GlyphVector;
64 import java.awt.geom.AffineTransform;
65 import java.awt.geom.Arc2D;
66 import java.awt.geom.GeneralPath;
67 import java.awt.geom.NoninvertibleTransformException;
68 import java.awt.geom.PathIterator;
69 import java.awt.geom.Point2D;
70 import java.awt.geom.Rectangle2D;
71 import java.awt.image.AffineTransformOp;
72 import java.awt.image.BufferedImage;
73 import java.awt.image.BufferedImageOp;
74 import java.awt.image.ColorModel;
75 import java.awt.image.CropImageFilter;
76 import java.awt.image.DataBuffer;
77 import java.awt.image.DataBufferInt;
78 import java.awt.image.DirectColorModel;
79 import java.awt.image.FilteredImageSource;
80 import java.awt.image.ImageObserver;
81 import java.awt.image.ImagingOpException;
82 import java.awt.image.MultiPixelPackedSampleModel;
83 import java.awt.image.Raster;
84 import java.awt.image.RenderedImage;
85 import java.awt.image.SampleModel;
86 import java.awt.image.WritableRaster;
87 import java.awt.image.renderable.RenderContext;
88 import java.awt.image.renderable.RenderableImage;
89 import java.text.AttributedCharacterIterator;
90 import java.util.HashMap;
91 import java.util.Map;
92 import java.util.Stack;
94 public class GdkGraphics2D extends Graphics2D
96 //////////////////////////////////////
97 ////// State Management Methods //////
98 //////////////////////////////////////
100 static
102 if (Configuration.INIT_LOAD_LIBRARY)
103 System.loadLibrary("gtkpeer");
105 if (GtkToolkit.useGraphics2D())
106 initStaticState();
109 static native void initStaticState();
111 private final int native_state = GtkGenericPeer.getUniqueInteger();
113 private Paint paint;
114 private Stroke stroke;
115 private Color fg;
116 private Color bg;
117 private Shape clip;
118 private AffineTransform transform;
119 private GtkComponentPeer component;
120 private Font font;
121 private RenderingHints hints;
122 private BufferedImage bimage;
123 private boolean pixelConversionRequired;
124 private int[] pixelBuffer;
125 private Composite comp;
126 private Stack stateStack;
128 private native void initState(GtkComponentPeer component);
129 private native void initState(int width, int height);
130 private native void initState(int[] pixes, int width, int height);
131 private native void copyState(GdkGraphics2D g);
132 public native void dispose();
133 private native void cairoSurfaceSetFilter(int filter);
134 native void connectSignals(GtkComponentPeer component);
136 public void finalize()
138 dispose();
141 public Graphics create()
143 return new GdkGraphics2D(this);
146 public Graphics create(int x, int y, int width, int height)
148 return new GdkGraphics2D(width, height);
151 GdkGraphics2D(GdkGraphics2D g)
153 paint = g.paint;
154 stroke = g.stroke;
155 setRenderingHints(g.hints);
157 if (g.fg.getAlpha() != -1)
158 fg = new Color(g.fg.getRed(), g.fg.getGreen(), g.fg.getBlue(),
159 g.fg.getAlpha());
160 else
161 fg = new Color(g.fg.getRGB());
163 if (g.bg.getAlpha() != -1)
164 bg = new Color(g.bg.getRed(), g.bg.getGreen(), g.bg.getBlue(),
165 g.bg.getAlpha());
166 else
167 bg = new Color(g.bg.getRGB());
169 if (g.clip == null)
170 clip = null;
171 else
172 clip = new Rectangle(g.getClipBounds());
174 if (g.transform == null)
175 transform = new AffineTransform();
176 else
177 transform = new AffineTransform(g.transform);
179 font = g.font;
180 component = g.component;
181 copyState(g);
183 setColor(fg);
184 setBackground(bg);
185 setPaint(paint);
186 setStroke(stroke);
187 setTransform(transform);
188 setClip(clip);
189 stateStack = new Stack();
192 GdkGraphics2D(int width, int height)
194 initState(width, height);
196 setColor(Color.black);
197 setBackground(Color.black);
198 setPaint(getColor());
199 setFont(new Font("SansSerif", Font.PLAIN, 12));
200 setTransform(new AffineTransform());
201 setStroke(new BasicStroke());
202 setRenderingHints(getDefaultHints());
204 stateStack = new Stack();
207 GdkGraphics2D(GtkComponentPeer component)
209 this.component = component;
211 if (component.isRealized())
212 initComponentGraphics2D();
213 else
214 connectSignals(component);
217 void initComponentGraphics2D()
219 initState(component);
221 setColor(component.awtComponent.getForeground());
222 setBackground(component.awtComponent.getBackground());
223 setPaint(getColor());
224 setTransform(new AffineTransform());
225 setStroke(new BasicStroke());
226 setRenderingHints(getDefaultHints());
227 setFont(new Font("SansSerif", Font.PLAIN, 12));
229 stateStack = new Stack();
232 GdkGraphics2D(BufferedImage bimage)
234 this.bimage = bimage;
235 this.pixelBuffer = findSimpleIntegerArray(bimage.getColorModel(),
236 bimage.getRaster());
237 if (this.pixelBuffer == null)
239 this.pixelBuffer = new int[bimage.getRaster().getWidth() * bimage.getRaster()
240 .getHeight()];
241 this.pixelConversionRequired = true;
243 else
245 this.pixelConversionRequired = false;
248 initState(this.pixelBuffer, bimage.getWidth(), bimage.getHeight());
250 setColor(Color.black);
251 setBackground(Color.black);
252 setPaint(getColor());
253 setFont(new Font("SansSerif", Font.PLAIN, 12));
254 setTransform(new AffineTransform());
255 setStroke(new BasicStroke());
256 setRenderingHints(getDefaultHints());
258 stateStack = new Stack();
260 // draw current buffered image to the pixmap associated
261 // with it, if the image is not equal to our paint buffer.
262 if (pixelConversionRequired)
263 drawImage(bimage, new AffineTransform(1, 0, 0, 1, 0, 0), bg, null);
266 ////////////////////////////////////
267 ////// Native Drawing Methods //////
268 ////////////////////////////////////
270 // GDK drawing methods
271 private native void gdkDrawDrawable(GdkGraphics2D other, int x, int y);
273 // drawing utility methods
274 private native void drawPixels(int[] pixels, int w, int h, int stride,
275 double[] i2u);
276 private native void setTexturePixels(int[] pixels, int w, int h, int stride);
277 private native void setGradient(double x1, double y1, double x2, double y2,
278 int r1, int g1, int b1, int a1, int r2,
279 int g2, int b2, int a2, boolean cyclic);
281 // simple passthroughs to cairo
282 private native void cairoSave();
283 private native void cairoRestore();
284 private native void cairoSetMatrix(double[] m);
285 private native void cairoSetOperator(int cairoOperator);
286 private native void cairoSetRGBColor(double red, double green, double blue);
287 private native void cairoSetAlpha(double alpha);
288 private native void cairoSetFillRule(int cairoFillRule);
289 private native void cairoSetLineWidth(double width);
290 private native void cairoSetLineCap(int cairoLineCap);
291 private native void cairoSetLineJoin(int cairoLineJoin);
292 private native void cairoSetDash(double[] dashes, int ndash, double offset);
294 private native void cairoSetMiterLimit(double limit);
295 private native void cairoNewPath();
296 private native void cairoMoveTo(double x, double y);
297 private native void cairoLineTo(double x, double y);
298 private native void cairoCurveTo(double x1, double y1, double x2, double y2,
299 double x3, double y3);
300 private native void cairoRelMoveTo(double dx, double dy);
301 private native void cairoRelLineTo(double dx, double dy);
302 private native void cairoRelCurveTo(double dx1, double dy1, double dx2,
303 double dy2, double dx3, double dy3);
304 private native void cairoRectangle(double x, double y, double width,
305 double height);
306 private native void cairoClosePath();
307 private native void cairoStroke();
308 private native void cairoFill();
309 private native void cairoClip();
311 /////////////////////////////////////////////
312 ////// General Drawing Support Methods //////
313 /////////////////////////////////////////////
315 private class DrawState
317 private Paint paint;
318 private Stroke stroke;
319 private Color fg;
320 private Color bg;
321 private Shape clip;
322 private AffineTransform transform;
323 private Font font;
324 private Composite comp;
326 DrawState(GdkGraphics2D g)
328 this.paint = g.paint;
329 this.stroke = g.stroke;
330 this.fg = g.fg;
331 this.bg = g.bg;
332 this.clip = g.clip;
333 if (g.transform != null)
334 this.transform = (AffineTransform) g.transform.clone();
335 this.font = g.font;
336 this.comp = g.comp;
339 public void restore(GdkGraphics2D g)
341 g.paint = this.paint;
342 g.stroke = this.stroke;
343 g.fg = this.fg;
344 g.bg = this.bg;
345 g.clip = this.clip;
346 g.transform = this.transform;
347 g.font = this.font;
348 g.comp = this.comp;
352 private void stateSave()
354 stateStack.push(new DrawState(this));
355 cairoSave();
358 private void stateRestore()
360 ((DrawState) (stateStack.pop())).restore(this);
361 cairoRestore();
364 // Some operations (drawing rather than filling) require that their
365 // coords be shifted to land on 0.5-pixel boundaries, in order to land on
366 // "middle of pixel" coordinates and light up complete pixels.
367 private boolean shiftDrawCalls = false;
369 private double shifted(double coord, boolean doShift)
371 if (doShift)
372 return Math.floor(coord) + 0.5;
373 else
374 return coord;
377 private void walkPath(PathIterator p, boolean doShift)
379 double x = 0;
380 double y = 0;
381 double[] coords = new double[6];
383 cairoSetFillRule(p.getWindingRule());
384 for (; ! p.isDone(); p.next())
386 int seg = p.currentSegment(coords);
387 switch (seg)
389 case PathIterator.SEG_MOVETO:
390 x = shifted(coords[0], doShift);
391 y = shifted(coords[1], doShift);
392 cairoMoveTo(x, y);
393 break;
394 case PathIterator.SEG_LINETO:
395 x = shifted(coords[0], doShift);
396 y = shifted(coords[1], doShift);
397 cairoLineTo(x, y);
398 break;
399 case PathIterator.SEG_QUADTO:
400 // splitting a quadratic bezier into a cubic:
401 // see: http://pfaedit.sourceforge.net/bezier.html
402 double x1 = x + (2.0 / 3.0) * (shifted(coords[0], doShift) - x);
403 double y1 = y + (2.0 / 3.0) * (shifted(coords[1], doShift) - y);
405 double x2 = x1 + (1.0 / 3.0) * (shifted(coords[2], doShift) - x);
406 double y2 = y1 + (1.0 / 3.0) * (shifted(coords[3], doShift) - y);
408 x = shifted(coords[2], doShift);
409 y = shifted(coords[3], doShift);
410 cairoCurveTo(x1, y1, x2, y2, x, y);
411 break;
412 case PathIterator.SEG_CUBICTO:
413 x = shifted(coords[4], doShift);
414 y = shifted(coords[5], doShift);
415 cairoCurveTo(shifted(coords[0], doShift),
416 shifted(coords[1], doShift),
417 shifted(coords[2], doShift),
418 shifted(coords[3], doShift), x, y);
419 break;
420 case PathIterator.SEG_CLOSE:
421 cairoClosePath();
422 break;
427 private Map getDefaultHints()
429 HashMap defaultHints = new HashMap();
431 defaultHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
432 RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
434 defaultHints.put(RenderingHints.KEY_STROKE_CONTROL,
435 RenderingHints.VALUE_STROKE_DEFAULT);
437 defaultHints.put(RenderingHints.KEY_FRACTIONALMETRICS,
438 RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
440 defaultHints.put(RenderingHints.KEY_ANTIALIASING,
441 RenderingHints.VALUE_ANTIALIAS_OFF);
443 defaultHints.put(RenderingHints.KEY_RENDERING,
444 RenderingHints.VALUE_RENDER_DEFAULT);
446 return defaultHints;
449 public static int[] findSimpleIntegerArray (ColorModel cm, Raster raster)
451 if (cm == null || raster == null)
452 return null;
454 if (! cm.getColorSpace().isCS_sRGB())
455 return null;
457 if (! (cm instanceof DirectColorModel))
458 return null;
460 DirectColorModel dcm = (DirectColorModel) cm;
462 if (dcm.getRedMask() != 0x00FF0000 || dcm.getGreenMask() != 0x0000FF00
463 || dcm.getBlueMask() != 0x000000FF)
464 return null;
466 if (! (raster instanceof WritableRaster))
467 return null;
469 if (raster.getSampleModel().getDataType() != DataBuffer.TYPE_INT)
470 return null;
472 if (! (raster.getDataBuffer() instanceof DataBufferInt))
473 return null;
475 DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
477 if (db.getNumBanks() != 1)
478 return null;
480 // Finally, we have determined that this is a single bank, [A]RGB-int
481 // buffer in sRGB space. It's worth checking all this, because it means
482 // that cairo can paint directly into the data buffer, which is very
483 // fast compared to all the normal copying and converting.
485 return db.getData();
488 private void updateBufferedImage()
490 if (bimage != null && pixelConversionRequired)
492 int height = bimage.getHeight();
493 int width = bimage.getWidth();
494 for (int y = 0; y < height; ++y)
495 for (int x = 0; x < width; ++x)
496 bimage.setRGB(x, y, pixelBuffer[y*width+height]);
500 private boolean drawImage(Image img, AffineTransform xform,
501 Color bgcolor, ImageObserver obs)
503 if (img == null)
504 return false;
506 if (img instanceof GtkOffScreenImage
507 && img.getGraphics() instanceof GdkGraphics2D
508 && (xform == null || xform.getType() == AffineTransform.TYPE_IDENTITY
509 || xform.getType() == AffineTransform.TYPE_TRANSLATION))
511 // we are being asked to flush a double buffer from Gdk
512 GdkGraphics2D g2 = (GdkGraphics2D) img.getGraphics();
513 gdkDrawDrawable(g2, (int) xform.getTranslateX(),
514 (int) xform.getTranslateY());
516 updateBufferedImage();
518 return true;
520 else
522 // In this case, xform is an AffineTransform that transforms bounding
523 // box of the specified image from image space to user space. However
524 // when we pass this transform to cairo, cairo will use this transform
525 // to map "user coordinates" to "pixel" coordinates, which is the
526 // other way around. Therefore to get the "user -> pixel" transform
527 // that cairo wants from "image -> user" transform that we currently
528 // have, we will need to invert the transformation matrix.
529 AffineTransform invertedXform = new AffineTransform();
533 invertedXform = xform.createInverse();
534 if (img instanceof BufferedImage)
536 // draw an image which has actually been loaded
537 // into memory fully
538 BufferedImage b = (BufferedImage) img;
539 return drawRaster(b.getColorModel(), b.getData(),
540 invertedXform, bgcolor);
542 else
543 return this.drawImage(GdkPixbufDecoder.createBufferedImage(img
544 .getSource()),
545 xform, bgcolor, obs);
547 catch (NoninvertibleTransformException e)
549 throw new ImagingOpException("Unable to invert transform "
550 + xform.toString());
555 //////////////////////////////////////////////////
556 ////// Implementation of Graphics2D Methods //////
557 //////////////////////////////////////////////////
559 public void draw(Shape s)
561 if (stroke != null && ! (stroke instanceof BasicStroke))
563 fill(stroke.createStrokedShape(s));
564 return;
567 cairoNewPath();
569 if (s instanceof Rectangle2D)
571 Rectangle2D r = (Rectangle2D) s;
572 cairoRectangle(shifted(r.getX(), shiftDrawCalls),
573 shifted(r.getY(), shiftDrawCalls), r.getWidth(),
574 r.getHeight());
576 else
577 walkPath(s.getPathIterator(null), shiftDrawCalls);
578 cairoStroke();
580 updateBufferedImage();
583 public void fill(Shape s)
585 cairoNewPath();
586 if (s instanceof Rectangle2D)
588 Rectangle2D r = (Rectangle2D) s;
589 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
591 else
592 walkPath(s.getPathIterator(null), false);
594 cairoFill();
596 updateBufferedImage();
599 public void clip(Shape s)
601 // update it
602 if (clip == null || s == null)
603 clip = s;
604 else if (s instanceof Rectangle2D && clip instanceof Rectangle2D)
606 Rectangle2D r = (Rectangle2D) s;
607 Rectangle2D curr = (Rectangle2D) clip;
608 clip = curr.createIntersection(r);
610 else
611 throw new UnsupportedOperationException();
613 // draw it
614 if (clip != null)
616 cairoNewPath();
617 if (clip instanceof Rectangle2D)
619 Rectangle2D r = (Rectangle2D) clip;
620 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
622 else
623 walkPath(clip.getPathIterator(null), false);
625 // cairoClosePath ();
626 cairoClip();
630 public Paint getPaint()
632 return paint;
635 public AffineTransform getTransform()
637 return (AffineTransform) transform.clone();
640 public void setPaint(Paint p)
642 if (paint == null)
643 return;
645 paint = p;
646 if (paint instanceof Color)
648 setColor((Color) paint);
650 else if (paint instanceof TexturePaint)
652 TexturePaint tp = (TexturePaint) paint;
653 BufferedImage img = tp.getImage();
655 // map the image to the anchor rectangle
656 int width = (int) tp.getAnchorRect().getWidth();
657 int height = (int) tp.getAnchorRect().getHeight();
659 double scaleX = width / (double) img.getWidth();
660 double scaleY = width / (double) img.getHeight();
662 AffineTransform at = new AffineTransform(scaleX, 0, 0, scaleY, 0, 0);
663 AffineTransformOp op = new AffineTransformOp(at, getRenderingHints());
664 BufferedImage texture = op.filter(img, null);
665 int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width);
666 setTexturePixels(pixels, width, height, width);
668 else if (paint instanceof GradientPaint)
670 GradientPaint gp = (GradientPaint) paint;
671 Point2D p1 = gp.getPoint1();
672 Point2D p2 = gp.getPoint2();
673 Color c1 = gp.getColor1();
674 Color c2 = gp.getColor2();
675 setGradient(p1.getX(), p1.getY(), p2.getX(), p2.getY(), c1.getRed(),
676 c1.getGreen(), c1.getBlue(), c1.getAlpha(), c2.getRed(),
677 c2.getGreen(), c2.getBlue(), c2.getAlpha(), gp.isCyclic());
679 else
680 throw new java.lang.UnsupportedOperationException();
683 public void setTransform(AffineTransform tx)
685 transform = tx;
686 if (transform != null)
688 double[] m = new double[6];
689 transform.getMatrix(m);
690 cairoSetMatrix(m);
694 public void transform(AffineTransform tx)
696 if (transform == null)
697 transform = new AffineTransform(tx);
698 else
699 transform.concatenate(tx);
700 setTransform(transform);
701 if (clip != null)
703 // FIXME: this should actuall try to transform the shape
704 // rather than degrade to bounds.
705 Rectangle2D r = clip.getBounds2D();
706 double[] coords = new double[]
708 r.getX(), r.getY(), r.getX() + r.getWidth(),
709 r.getY() + r.getHeight()
713 tx.createInverse().transform(coords, 0, coords, 0, 2);
714 r.setRect(coords[0], coords[1], coords[2] - coords[0],
715 coords[3] - coords[1]);
716 clip = r;
718 catch (java.awt.geom.NoninvertibleTransformException e)
724 public void rotate(double theta)
726 transform(AffineTransform.getRotateInstance(theta));
729 public void rotate(double theta, double x, double y)
731 transform(AffineTransform.getRotateInstance(theta, x, y));
734 public void scale(double sx, double sy)
736 transform(AffineTransform.getScaleInstance(sx, sy));
739 public void translate(double tx, double ty)
741 transform(AffineTransform.getTranslateInstance(tx, ty));
744 public void translate(int x, int y)
746 translate((double) x, (double) y);
749 public void shear(double shearX, double shearY)
751 transform(AffineTransform.getShearInstance(shearX, shearY));
754 public Stroke getStroke()
756 return stroke;
759 public void setStroke(Stroke st)
761 stroke = st;
762 if (stroke instanceof BasicStroke)
764 BasicStroke bs = (BasicStroke) stroke;
765 cairoSetLineCap(bs.getEndCap());
766 cairoSetLineWidth(bs.getLineWidth());
767 cairoSetLineJoin(bs.getLineJoin());
768 cairoSetMiterLimit(bs.getMiterLimit());
769 float[] dashes = bs.getDashArray();
770 if (dashes != null)
772 double[] double_dashes = new double[dashes.length];
773 for (int i = 0; i < dashes.length; i++)
774 double_dashes[i] = dashes[i];
775 cairoSetDash(double_dashes, double_dashes.length,
776 (double) bs.getDashPhase());
781 ////////////////////////////////////////////////
782 ////// Implementation of Graphics Methods //////
783 ////////////////////////////////////////////////
785 public void setPaintMode()
787 setComposite(java.awt.AlphaComposite.SrcOver);
790 public void setXORMode(Color c)
792 setComposite(new gnu.java.awt.BitwiseXORComposite(c));
795 public void setColor(Color c)
797 if (c == null)
798 c = Color.BLACK;
800 fg = c;
801 paint = c;
802 cairoSetRGBColor(fg.getRed() / 255.0, fg.getGreen() / 255.0,
803 fg.getBlue() / 255.0);
804 cairoSetAlpha((fg.getAlpha() & 255) / 255.0);
807 public Color getColor()
809 return fg;
812 public void clipRect(int x, int y, int width, int height)
814 clip(new Rectangle(x, y, width, height));
817 public Shape getClip()
819 return clip.getBounds2D(); //getClipInDevSpace();
822 public Rectangle getClipBounds()
824 if (clip == null)
825 return null;
826 else
827 return clip.getBounds();
830 protected Rectangle2D getClipInDevSpace()
832 Rectangle2D uclip = clip.getBounds2D();
833 if (transform == null)
834 return uclip;
835 else
837 Point2D pos = transform.transform(new Point2D.Double(uclip.getX(),
838 uclip.getY()),
839 (Point2D) null);
840 Point2D extent = transform.deltaTransform(new Point2D.Double(uclip
841 .getWidth(),
842 uclip
843 .getHeight()),
844 (Point2D) null);
845 return new Rectangle2D.Double(pos.getX(), pos.getY(), extent.getX(),
846 extent.getY());
850 public void setClip(int x, int y, int width, int height)
852 setClip(new Rectangle2D.Double((double) x, (double) y, (double) width,
853 (double) height));
856 public void setClip(Shape s)
858 clip = s;
859 if (s != null)
861 cairoNewPath();
862 if (s instanceof Rectangle2D)
864 Rectangle2D r = (Rectangle2D) s;
865 cairoRectangle(r.getX(), r.getY(), r.getWidth(), r.getHeight());
867 else
868 walkPath(s.getPathIterator(null), false);
870 // cairoClosePath ();
871 cairoClip();
875 private static BasicStroke draw3DRectStroke = new BasicStroke();
877 public void draw3DRect(int x, int y, int width, int height, boolean raised)
879 Stroke tmp = stroke;
880 setStroke(draw3DRectStroke);
881 super.draw3DRect(x, y, width, height, raised);
882 setStroke(tmp);
883 updateBufferedImage();
886 public void fill3DRect(int x, int y, int width, int height, boolean raised)
888 Stroke tmp = stroke;
889 setStroke(draw3DRectStroke);
890 super.fill3DRect(x, y, width, height, raised);
891 setStroke(tmp);
892 updateBufferedImage();
895 public void drawRect(int x, int y, int width, int height)
897 draw(new Rectangle(x, y, width, height));
900 public void fillRect(int x, int y, int width, int height)
902 cairoNewPath();
903 cairoRectangle(x, y, width, height);
904 cairoFill();
907 public void clearRect(int x, int y, int width, int height)
909 cairoSetRGBColor(bg.getRed() / 255.0, bg.getGreen() / 255.0,
910 bg.getBlue() / 255.0);
911 cairoSetAlpha(1.0);
912 cairoNewPath();
913 cairoRectangle(x, y, width, height);
914 cairoFill();
915 setColor(fg);
917 updateBufferedImage();
920 public void setBackground(Color c)
922 bg = c;
925 public Color getBackground()
927 return bg;
930 private void doPolygon(int[] xPoints, int[] yPoints, int nPoints,
931 boolean close, boolean fill)
933 if (nPoints < 1)
934 return;
935 GeneralPath gp = new GeneralPath(PathIterator.WIND_EVEN_ODD);
936 gp.moveTo((float) xPoints[0], (float) yPoints[0]);
937 for (int i = 1; i < nPoints; i++)
938 gp.lineTo((float) xPoints[i], (float) yPoints[i]);
940 if (close)
941 gp.closePath();
943 Shape sh = gp;
944 if (fill == false && stroke != null && ! (stroke instanceof BasicStroke))
946 sh = stroke.createStrokedShape(gp);
947 fill = true;
950 if (fill)
951 fill(sh);
952 else
953 draw(sh);
956 public void drawLine(int x1, int y1, int x2, int y2)
958 int[] xp = new int[2];
959 int[] yp = new int[2];
961 xp[0] = x1;
962 xp[1] = x2;
963 yp[0] = y1;
964 yp[1] = y2;
966 doPolygon(xp, yp, 2, false, false);
969 public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
971 doPolygon(xPoints, yPoints, nPoints, true, true);
974 public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
976 doPolygon(xPoints, yPoints, nPoints, true, false);
979 public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
981 doPolygon(xPoints, yPoints, nPoints, false, false);
984 private boolean drawRaster(ColorModel cm, Raster r,
985 AffineTransform imageToUser, Color bgcolor)
987 if (r == null)
988 return false;
990 SampleModel sm = r.getSampleModel();
991 DataBuffer db = r.getDataBuffer();
993 if (db == null || sm == null)
994 return false;
996 if (cm == null)
997 cm = ColorModel.getRGBdefault();
999 double[] i2u = new double[6];
1000 if (imageToUser != null)
1001 imageToUser.getMatrix(i2u);
1002 else
1004 i2u[0] = 1;
1005 i2u[1] = 0;
1006 i2u[2] = 0;
1007 i2u[3] = 1;
1008 i2u[4] = 0;
1009 i2u[5] = 0;
1012 int[] pixels = findSimpleIntegerArray(cm, r);
1014 if (pixels == null)
1016 // FIXME: I don't think this code will work correctly with a non-RGB
1017 // MultiPixelPackedSampleModel. Although this entire method should
1018 // probably be rewritten to better utilize Cairo's different supported
1019 // data formats.
1020 if (sm instanceof MultiPixelPackedSampleModel)
1022 pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), pixels);
1023 for (int i = 0; i < pixels.length; i++)
1024 pixels[i] = cm.getRGB(pixels[i]);
1026 else
1028 pixels = new int[r.getWidth() * r.getHeight()];
1029 for (int i = 0; i < pixels.length; i++)
1030 pixels[i] = cm.getRGB(db.getElem(i));
1034 // Change all transparent pixels in the image to the specified bgcolor,
1035 // or (if there's no alpha) fill in an alpha channel so that it paints
1036 // correctly.
1037 if (cm.hasAlpha())
1039 if (bgcolor != null && cm.hasAlpha())
1040 for (int i = 0; i < pixels.length; i++)
1042 if (cm.getAlpha(pixels[i]) == 0)
1043 pixels[i] = bgcolor.getRGB();
1046 else
1047 for (int i = 0; i < pixels.length; i++)
1048 pixels[i] |= 0xFF000000;
1050 drawPixels(pixels, r.getWidth(), r.getHeight(), r.getWidth(), i2u);
1052 updateBufferedImage();
1054 return true;
1057 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
1059 drawRaster(image.getColorModel(), image.getData(), xform, bg);
1062 public void drawRenderableImage(RenderableImage image, AffineTransform xform)
1064 drawRenderedImage(image.createRendering(new RenderContext(xform)), xform);
1067 public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
1069 return drawImage(img, xform, bg, obs);
1072 public void drawImage(BufferedImage image, BufferedImageOp op, int x, int y)
1074 Image filtered = op.filter(image, null);
1075 drawImage(filtered, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg, null);
1078 public boolean drawImage(Image img, int x, int y, ImageObserver observer)
1080 return drawImage(img, new AffineTransform(1f, 0f, 0f, 1f, x, y), bg,
1081 observer);
1084 ///////////////////////////////////////////////
1085 ////// Unimplemented Stubs and Overloads //////
1086 ///////////////////////////////////////////////
1088 public boolean hit(Rectangle rect, Shape text, boolean onStroke)
1090 throw new java.lang.UnsupportedOperationException();
1093 public GraphicsConfiguration getDeviceConfiguration()
1095 throw new java.lang.UnsupportedOperationException();
1098 public void setComposite(Composite comp)
1100 this.comp = comp;
1102 if (comp instanceof AlphaComposite)
1104 AlphaComposite a = (AlphaComposite) comp;
1105 cairoSetOperator(a.getRule());
1106 Color c = getColor();
1107 setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(),
1108 (int) (a.getAlpha() * ((float) c.getAlpha()))));
1110 else
1111 throw new java.lang.UnsupportedOperationException();
1114 public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
1116 hints.put(hintKey, hintValue);
1118 if (hintKey.equals(RenderingHints.KEY_INTERPOLATION)
1119 || hintKey.equals(RenderingHints.KEY_ALPHA_INTERPOLATION))
1121 if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1122 cairoSurfaceSetFilter(0);
1124 else if (hintValue.equals(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1125 cairoSurfaceSetFilter(1);
1127 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1128 cairoSurfaceSetFilter(2);
1130 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1131 cairoSurfaceSetFilter(3);
1133 else if (hintValue.equals(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1134 cairoSurfaceSetFilter(4);
1137 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1138 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1141 public Object getRenderingHint(RenderingHints.Key hintKey)
1143 return hints.get(hintKey);
1146 public void setRenderingHints(Map hints)
1148 this.hints = new RenderingHints(getDefaultHints());
1149 this.hints.add(new RenderingHints(hints));
1151 if (hints.containsKey(RenderingHints.KEY_INTERPOLATION))
1153 if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR))
1154 cairoSurfaceSetFilter(0);
1156 else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR))
1157 cairoSurfaceSetFilter(1);
1160 if (hints.containsKey(RenderingHints.KEY_ALPHA_INTERPOLATION))
1162 if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED))
1163 cairoSurfaceSetFilter(2);
1165 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY))
1166 cairoSurfaceSetFilter(3);
1168 else if (hints.containsValue(RenderingHints.VALUE_ALPHA_INTERPOLATION_DEFAULT))
1169 cairoSurfaceSetFilter(4);
1172 shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
1173 || hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
1176 public void addRenderingHints(Map hints)
1178 this.hints.add(new RenderingHints(hints));
1181 public RenderingHints getRenderingHints()
1183 return hints;
1186 public Composite getComposite()
1188 if (comp == null)
1189 return AlphaComposite.SrcOver;
1190 else
1191 return comp;
1194 public FontRenderContext getFontRenderContext()
1196 return new FontRenderContext(transform, true, true);
1199 public void copyArea(int x, int y, int width, int height, int dx, int dy)
1201 throw new java.lang.UnsupportedOperationException();
1204 public void drawArc(int x, int y, int width, int height, int startAngle,
1205 int arcAngle)
1207 draw(new Arc2D.Double((double) x, (double) y, (double) width,
1208 (double) height, (double) startAngle,
1209 (double) arcAngle, Arc2D.OPEN));
1212 public boolean drawImage(Image img, int x, int y, Color bgcolor,
1213 ImageObserver observer)
1215 return drawImage(img, x, y, img.getWidth(observer),
1216 img.getHeight(observer), bgcolor, observer);
1219 public boolean drawImage(Image img, int x, int y, int width, int height,
1220 Color bgcolor, ImageObserver observer)
1222 double scaleX = width / (double) img.getWidth(observer);
1223 double scaleY = height / (double) img.getHeight(observer);
1225 return drawImage(img, new AffineTransform(scaleX, 0f, 0f, scaleY, x, y),
1226 bgcolor, observer);
1229 public boolean drawImage(Image img, int x, int y, int width, int height,
1230 ImageObserver observer)
1232 return drawImage(img, x, y, width, height, bg, observer);
1235 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1236 int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1237 ImageObserver observer)
1239 if (img == null)
1240 return false;
1242 Image subImage;
1244 int sourceWidth = sx2 - sx1;
1245 int sourceHeight = sy2 - sy1;
1247 int destWidth = dx2 - dx1;
1248 int destHeight = dy2 - dy1;
1250 double scaleX = destWidth / (double) sourceWidth;
1251 double scaleY = destHeight / (double) sourceHeight;
1253 // Get the subimage of the source enclosed in the
1254 // rectangle specified by sx1, sy1, sx2, sy2
1256 if (img instanceof BufferedImage)
1258 BufferedImage b = (BufferedImage) img;
1259 subImage = b.getSubimage(sx1, sy1, sx2, sy2);
1261 else
1263 // FIXME: This code currently doesn't work. Null Pointer
1264 // exception is thrown in this case. This happens
1265 // because img.getSource() always returns null, since source gets
1266 // never initialized when it is created with the help of
1267 // createImage(int width, int height).
1268 CropImageFilter filter = new CropImageFilter(sx1, sx2, sx2, sy2);
1269 FilteredImageSource src = new FilteredImageSource(img.getSource(),
1270 filter);
1272 subImage = Toolkit.getDefaultToolkit().createImage(src);
1275 return drawImage(subImage,
1276 new AffineTransform(scaleX, 0, 0, scaleY, dx1, dy1),
1277 bgcolor, observer);
1280 public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
1281 int sx1, int sy1, int sx2, int sy2,
1282 ImageObserver observer)
1284 return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bg, observer);
1287 public void drawOval(int x, int y, int width, int height)
1289 drawArc(x, y, width, height, 0, 360);
1292 public void drawRoundRect(int x, int y, int width, int height, int arcWidth,
1293 int arcHeight)
1295 if (arcWidth > width)
1296 arcWidth = width;
1297 if (arcHeight > height)
1298 arcHeight = height;
1300 int xx = x + width - arcWidth;
1301 int yy = y + height - arcHeight;
1303 drawArc(x, y, arcWidth, arcHeight, 90, 90);
1304 drawArc(xx, y, arcWidth, arcHeight, 0, 90);
1305 drawArc(xx, yy, arcWidth, arcHeight, 270, 90);
1306 drawArc(x, yy, arcWidth, arcHeight, 180, 90);
1308 int y1 = y + arcHeight / 2;
1309 int y2 = y + height - arcHeight / 2;
1310 drawLine(x, y1, x, y2);
1311 drawLine(x + width, y1, x + width, y2);
1313 int x1 = x + arcWidth / 2;
1314 int x2 = x + width - arcWidth / 2;
1315 drawLine(x1, y, x2, y);
1316 drawLine(x1, y + height, x2, y + height);
1319 // these are the most accelerated painting paths
1320 native void cairoDrawGlyphVector(GdkFontPeer font,
1321 float x, float y, int n,
1322 int[] codes, float[] positions);
1324 native void cairoDrawGdkTextLayout(GdkTextLayout gl,
1325 float x, float y);
1327 GdkFontPeer getFontPeer()
1329 return (GdkFontPeer) getFont().getPeer();
1332 public void drawGdkTextLayout(GdkTextLayout gl, float x, float y)
1334 cairoDrawGdkTextLayout (gl, x, y);
1335 updateBufferedImage ();
1338 public void drawString(String str, float x, float y)
1340 drawGlyphVector(getFont().createGlyphVector(null, str), x, y);
1341 updateBufferedImage ();
1344 public void drawString(String str, int x, int y)
1346 drawString (str, (float) x, (float) y);
1349 public void drawString(AttributedCharacterIterator ci, int x, int y)
1351 drawString (ci, (float) x, (float) y);
1354 public void drawGlyphVector(GlyphVector gv, float x, float y)
1356 int n = gv.getNumGlyphs ();
1357 int[] codes = gv.getGlyphCodes (0, n, null);
1358 float[] positions = gv.getGlyphPositions (0, n, null);
1360 setFont (gv.getFont ());
1361 cairoDrawGlyphVector (getFontPeer(), x, y, n, codes, positions);
1362 updateBufferedImage ();
1365 public void drawString(AttributedCharacterIterator ci, float x, float y)
1367 GlyphVector gv = getFont().createGlyphVector(getFontRenderContext(), ci);
1368 drawGlyphVector(gv, x, y);
1371 public void fillArc(int x, int y, int width, int height, int startAngle,
1372 int arcAngle)
1374 fill(new Arc2D.Double((double) x, (double) y, (double) width,
1375 (double) height, (double) startAngle,
1376 (double) arcAngle, Arc2D.OPEN));
1379 public void fillOval(int x, int y, int width, int height)
1381 fillArc(x, y, width, height, 0, 360);
1384 public void fillRoundRect(int x, int y, int width, int height, int arcWidth,
1385 int arcHeight)
1387 if (arcWidth > width)
1388 arcWidth = width;
1389 if (arcHeight > height)
1390 arcHeight = height;
1392 int xx = x + width - arcWidth;
1393 int yy = y + height - arcHeight;
1395 fillArc(x, y, arcWidth, arcHeight, 90, 90);
1396 fillArc(xx, y, arcWidth, arcHeight, 0, 90);
1397 fillArc(xx, yy, arcWidth, arcHeight, 270, 90);
1398 fillArc(x, yy, arcWidth, arcHeight, 180, 90);
1400 fillRect(x, y + arcHeight / 2, width, height - arcHeight + 1);
1401 fillRect(x + arcWidth / 2, y, width - arcWidth + 1, height);
1404 public Font getFont()
1406 if (font == null)
1407 return new Font("SansSerif", Font.PLAIN, 12);
1408 return font;
1411 // Until such time as pango is happy to talk directly to cairo, we
1412 // actually need to redirect some calls from the GtkFontPeer and
1413 // GtkFontMetrics into the drawing kit and ask cairo ourselves.
1415 static native void releasePeerGraphicsResource(GdkFontPeer f);
1417 public FontMetrics getFontMetrics()
1419 return getFontMetrics(getFont());
1422 public FontMetrics getFontMetrics(Font f)
1424 // the reason we go via the toolkit here is to try to get
1425 // a cached object. the toolkit keeps such a cache.
1426 return Toolkit.getDefaultToolkit().getFontMetrics(f);
1429 public void setFont(Font f)
1431 if (f.getPeer() instanceof GdkFontPeer)
1432 font = f;
1433 else
1434 font =
1435 ((ClasspathToolkit)(Toolkit.getDefaultToolkit()))
1436 .getFont(f.getName(), f.getAttributes());
1439 public String toString()
1441 return (getClass().getName()
1442 + "[font=" + getFont().toString()
1443 + ",color=" + fg.toString()
1444 + "]");