1 /* ComponentGraphics.java --
2 Copyright (C) 2006 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.Pointer;
43 import java.awt.AlphaComposite;
44 import java.awt.Color;
45 import java.awt.Graphics;
46 import java.awt.Graphics2D;
47 import java.awt.GraphicsConfiguration;
48 import java.awt.Image;
49 import java.awt.Point;
50 import java.awt.Rectangle;
51 import java.awt.Shape;
52 import java.awt.Toolkit;
53 import java.awt.font.GlyphVector;
54 import java.awt.geom.AffineTransform;
55 import java.awt.geom.Line2D;
56 import java.awt.geom.Point2D;
57 import java.awt.geom.Rectangle2D;
58 import java.awt.image.BufferedImage;
59 import java.awt.image.ColorModel;
60 import java.awt.image.ImageObserver;
61 import java.awt.image.ImageProducer;
62 import java.awt.image.Raster;
63 import java.awt.image.RenderedImage;
64 import java.awt.image.WritableRaster;
65 import java.util.Hashtable;
68 * ComponentGraphics - context for drawing directly to a component,
69 * as this is an X drawable, it requires that we use GTK locks.
71 * This context draws directly to the drawable and requires xrender.
73 public class ComponentGraphics extends CairoGraphics2D
75 private static final boolean hasXRenderExtension = hasXRender();
77 private GtkComponentPeer component;
78 protected long cairo_t;
79 private BufferedImage buffer, componentBuffer;
81 private static ThreadLocal hasLock = new ThreadLocal();
82 private static Integer ONE = Integer.valueOf(1);
88 private ComponentGraphics(GtkComponentPeer component)
90 this.component = component;
91 cairo_t = initState(component);
93 Rectangle bounds = component.awtComponent.getBounds();
94 setClip( new Rectangle( 0, 0, bounds.width, bounds.height) );
95 setBackground(component.awtComponent.getBackground());
96 setColor(component.awtComponent.getForeground());
99 private ComponentGraphics(ComponentGraphics cg)
101 component = cg.component;
102 cairo_t = initState(component);
104 Rectangle bounds = component.awtComponent.getBounds();
105 setClip( new Rectangle( 0, 0, bounds.width, bounds.height) );
106 setBackground(component.awtComponent.getBackground());
107 setColor(component.awtComponent.getForeground());
111 * Creates a cairo_t for the component surface and return it.
113 private native long initState(GtkComponentPeer component);
117 Integer i = (Integer) hasLock.get();
124 hasLock.set(Integer.valueOf(i.intValue() + 1));
127 private void unlock()
129 Integer i = (Integer) hasLock.get();
131 throw new IllegalStateException();
138 hasLock.set(Integer.valueOf(i.intValue() - 1));
142 * Destroys the component surface and calls dispose on the cairo
143 * graphics2d to destroy any super class resources.
145 public void dispose()
148 disposeSurface(nativePointer);
152 * Destroys the component surface.
154 private native void disposeSurface(long nativePointer);
157 * Creates a cairo_t for a volatile image
159 protected native long initFromVolatile( long pixmapPtr, int width, int height);
164 private native void start_gdk_drawing();
169 private native void end_gdk_drawing();
172 * Query if the system has the XRender extension.
174 public static native boolean hasXRender();
177 * This is a utility method (used by GtkComponentPeer) for grabbing the
178 * image of a component.
180 private static native Pointer nativeGrab(GtkComponentPeer component);
182 private native void copyAreaNative(GtkComponentPeer component, int x, int y,
183 int width, int height, int dx, int dy);
185 private native void drawVolatile(GtkComponentPeer component,
186 long vimg, int x, int y,
187 int width, int height, int cx, int cy,
191 * Not really related (moveme?). Utility method used by GtkComponent.
193 public static GtkImage grab( GtkComponentPeer component )
195 return new GtkImage( nativeGrab( component ) );
199 * Returns a Graphics2D object for a component, either an instance of this
200 * class (if xrender is supported), or a context which copies.
202 public static Graphics2D getComponentGraphics(GtkComponentPeer component)
204 if( hasXRenderExtension )
205 return new ComponentGraphics(component);
207 Rectangle r = component.awtComponent.getBounds();
208 return new ComponentGraphicsCopy(r.width, r.height, component);
211 public GraphicsConfiguration getDeviceConfiguration()
213 return component.getGraphicsConfiguration();
216 public Graphics create()
218 return new ComponentGraphics(this);
221 protected Rectangle2D getRealBounds()
223 return component.awtComponent.getBounds();
226 public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy)
228 copyAreaNative(component, x, y, width, height, dx, dy);
232 * Overloaded methods that do actual drawing need to enter the gdk threads
233 * and also do certain things before and after.
235 public void draw(Shape s)
240 if (comp == null || comp instanceof AlphaComposite)
247 Graphics2D g2d = (Graphics2D)buffer.getGraphics();
248 g2d.setStroke(this.getStroke());
249 g2d.setColor(this.getColor());
252 drawComposite(s.getBounds2D(), null);
261 public void fill(Shape s)
266 if (comp == null || comp instanceof AlphaComposite)
273 Graphics2D g2d = (Graphics2D)buffer.getGraphics();
274 g2d.setPaint(this.getPaint());
275 g2d.setColor(this.getColor());
278 drawComposite(s.getBounds2D(), null);
287 public void drawRenderedImage(RenderedImage image, AffineTransform xform)
292 if (comp == null || comp instanceof AlphaComposite)
293 super.drawRenderedImage(image, xform);
299 Graphics2D g2d = (Graphics2D)buffer.getGraphics();
300 g2d.setRenderingHints(this.getRenderingHints());
301 g2d.drawRenderedImage(image, xform);
303 drawComposite(buffer.getRaster().getBounds(), null);
312 protected boolean drawImage(Image img, AffineTransform xform,
313 Color bgcolor, ImageObserver obs)
319 if (comp == null || comp instanceof AlphaComposite)
320 rv = super.drawImage(img, xform, bgcolor, obs);
324 // Get buffered image of source
325 if( !(img instanceof BufferedImage) )
327 ImageProducer source = img.getSource();
330 img = Toolkit.getDefaultToolkit().createImage(source);
332 BufferedImage bImg = (BufferedImage) img;
334 // Find translated bounds
335 Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY());
336 Point2D pt = new Point2D.Double(bImg.getWidth() + bImg.getMinX(),
337 bImg.getHeight() + bImg.getMinY());
340 origin = xform.transform(origin, origin);
341 pt = xform.transform(pt, pt);
344 // Create buffer and draw image
347 Graphics2D g2d = (Graphics2D)buffer.getGraphics();
348 g2d.setRenderingHints(this.getRenderingHints());
349 g2d.drawImage(img, xform, obs);
351 // Perform compositing
352 rv = drawComposite(new Rectangle2D.Double(origin.getX(),
354 pt.getX(), pt.getY()),
365 public void drawGlyphVector(GlyphVector gv, float x, float y)
370 if (comp == null || comp instanceof AlphaComposite)
371 super.drawGlyphVector(gv, x, y);
377 Graphics2D g2d = (Graphics2D)buffer.getGraphics();
378 g2d.setPaint(this.getPaint());
379 g2d.setStroke(this.getStroke());
380 g2d.drawGlyphVector(gv, x, y);
382 Rectangle2D bounds = gv.getLogicalBounds();
383 bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
384 bounds.getWidth(), bounds.getHeight());
385 drawComposite(bounds, null);
394 public boolean drawImage(Image img, int x, int y, ImageObserver observer)
396 // If it is a GtkVolatileImage with an "easy" transform then
397 // draw directly. Always pass a BufferedImage to super to avoid
398 // deadlock (see Note in CairoGraphics.drawImage()).
399 if (img instanceof GtkVolatileImage)
401 GtkVolatileImage vimg = (GtkVolatileImage) img;
402 int type = transform.getType();
403 if ((type == AffineTransform.TYPE_IDENTITY
404 || type == AffineTransform.TYPE_TRANSLATION)
405 && (clip == null || clip instanceof Rectangle2D))
407 Rectangle2D r = (Rectangle2D) clip;
410 x += transform.getTranslateX();
411 y += transform.getTranslateY();
412 drawVolatile(component, vimg.nativePointer,
413 x, y, vimg.width, vimg.height,
414 (int) (r.getX() + transform.getTranslateX()),
415 (int) (r.getY() + transform.getTranslateY()),
417 (int) r.getHeight());
421 return super.drawImage(vimg.getSnapshot(), x, y, observer);
425 if (img instanceof BufferedImage)
426 bimg = (BufferedImage) img;
429 ImageProducer source = img.getSource();
432 bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source);
434 return super.drawImage(bimg, x, y, observer);
437 public boolean drawImage(Image img, int x, int y, int width, int height,
438 ImageObserver observer)
440 // If it is a GtkVolatileImage with an "easy" transform then
441 // draw directly. Always pass a BufferedImage to super to avoid
442 // deadlock (see Note in CairoGraphics.drawImage()).
443 if (img instanceof GtkVolatileImage
444 && (clip == null || clip instanceof Rectangle2D))
446 GtkVolatileImage vimg = (GtkVolatileImage) img;
447 int type = transform.getType();
448 if ((type == AffineTransform.TYPE_IDENTITY
449 || type == AffineTransform.TYPE_TRANSLATION)
450 && (clip == null || clip instanceof Rectangle2D))
452 Rectangle2D r = (Rectangle2D) clip;
455 x += transform.getTranslateX();
456 y += transform.getTranslateY();
457 drawVolatile(component, vimg.nativePointer,
459 (int) (r.getX() + transform.getTranslateX()),
460 (int) (r.getY() + transform.getTranslateY()),
462 (int) r.getHeight());
466 return super.drawImage(vimg.getSnapshot(), x, y,
467 width, height, observer);
471 img = AsyncImage.realImage(img, observer);
472 if (img instanceof BufferedImage)
473 bimg = (BufferedImage) img;
476 ImageProducer source = img.getSource();
479 bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source);
481 return super.drawImage(bimg, x, y, width, height, observer);
484 public void setClip(Shape s)
498 private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
500 // Clip source to visible areas that need updating
501 Rectangle2D clip = this.getClipBounds();
502 Rectangle2D.intersect(bounds, clip, bounds);
503 clip = new Rectangle(buffer.getMinX(), buffer.getMinY(),
504 buffer.getWidth(), buffer.getHeight());
505 Rectangle2D.intersect(bounds, clip, bounds);
507 BufferedImage buffer2 = buffer;
508 if (!bounds.equals(buffer2.getRaster().getBounds()))
509 buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(),
510 (int)bounds.getWidth(),
511 (int)bounds.getHeight());
513 // Get destination clip to bounds
514 double[] points = new double[] {bounds.getX(), bounds.getY(),
515 bounds.getMaxX(), bounds.getMaxY()};
516 transform.transform(points, 0, points, 0, 2);
518 Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1],
519 points[2] - points[0],
520 points[3] - points[1]);
522 Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds);
524 // Get current image on the component
526 GtkImage img = grab(component);
527 Graphics gr = componentBuffer.createGraphics();
528 gr.drawImage(img, 0, 0, null);
532 BufferedImage cBuffer = componentBuffer;
533 if (!deviceBounds.equals(cBuffer.getRaster().getBounds()))
534 cBuffer = cBuffer.getSubimage((int)deviceBounds.getX(),
535 (int)deviceBounds.getY(),
536 (int)deviceBounds.getWidth(),
537 (int)deviceBounds.getHeight());
539 // Perform actual composite operation
540 compCtx.compose(buffer2.getRaster(), cBuffer.getRaster(),
541 cBuffer.getRaster());
543 // This MUST call directly into the "action" method in CairoGraphics2D,
544 // not one of the wrappers, to ensure that the composite isn't processed
546 boolean rv = super.drawImage(cBuffer,
547 AffineTransform.getTranslateInstance(bounds.getX(),
553 private void createBuffer()
558 rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(),
559 component.awtComponent.getHeight()),
562 buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
563 GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
568 Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
570 g2d.setBackground(new Color(0,0,0,0));
571 g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
574 if (componentBuffer == null)
577 rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(),
578 component.awtComponent.getHeight()),
581 componentBuffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
582 GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
587 protected ColorModel getNativeCM()
589 return GtkVolatileImage.gdkColorModel;