OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / peer / gtk / ComponentGraphics.java
1 /* ComponentGraphics.java --
2    Copyright (C) 2006  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
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.
25
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. */
37
38
39 package gnu.java.awt.peer.gtk;
40
41 import gnu.classpath.Pointer;
42
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;
66
67 /**
68  * ComponentGraphics - context for drawing directly to a component,
69  * as this is an X drawable, it requires that we use GTK locks.
70  *
71  * This context draws directly to the drawable and requires xrender.
72  */
73 public class ComponentGraphics extends CairoGraphics2D
74 {
75   private static final boolean hasXRenderExtension = hasXRender();
76
77   private GtkComponentPeer component;
78   protected long cairo_t;
79   private BufferedImage buffer, componentBuffer;
80
81   private static ThreadLocal hasLock = new ThreadLocal();
82   private static Integer ONE = Integer.valueOf(1);
83
84   ComponentGraphics()
85   {
86   }
87   
88   private ComponentGraphics(GtkComponentPeer component)
89   {
90     this.component = component;
91     cairo_t = initState(component);
92     setup( cairo_t );
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());
97   }
98
99   private ComponentGraphics(ComponentGraphics cg)
100   {
101     component = cg.component;
102     cairo_t = initState(component);
103     copy( cg, cairo_t );
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());
108   }
109
110   /**
111    * Creates a cairo_t for the component surface and return it.
112    */
113   private native long initState(GtkComponentPeer component);
114
115   private void lock()
116   {
117     Integer i = (Integer) hasLock.get();
118     if (i == null)
119       {
120         start_gdk_drawing();
121         hasLock.set(ONE);
122       }
123     else
124       hasLock.set(Integer.valueOf(i.intValue() + 1));
125   }
126
127   private void unlock()
128   {
129     Integer i = (Integer) hasLock.get();
130     if (i == null)
131       throw new IllegalStateException();
132     if (i == ONE)
133       {
134         hasLock.set(null);
135         end_gdk_drawing();
136       }
137     else
138       hasLock.set(Integer.valueOf(i.intValue() - 1));
139   }
140
141   /**
142    * Destroys the component surface and calls dispose on the cairo
143    * graphics2d to destroy any super class resources.
144    */
145   public void dispose()
146   {
147     super.dispose();
148     disposeSurface(nativePointer);
149   }
150
151   /**
152    * Destroys the component surface.
153    */
154   private native void disposeSurface(long nativePointer);
155
156   /**
157    * Creates a cairo_t for a volatile image
158    */
159   protected native long initFromVolatile( long pixmapPtr, int width, int height);
160
161   /**
162    * Grab lock
163    */
164   private native void start_gdk_drawing();
165
166   /**
167    * Release lock
168    */
169   private native void end_gdk_drawing();
170
171   /**
172    * Query if the system has the XRender extension.
173    */
174   public static native boolean hasXRender();
175
176   /**
177    * This is a utility method (used by GtkComponentPeer) for grabbing the
178    * image of a component.
179    */
180   private static native Pointer nativeGrab(GtkComponentPeer component);
181
182   private native void copyAreaNative(GtkComponentPeer component, int x, int y, 
183                                      int width, int height, int dx, int dy);
184
185   private native void drawVolatile(GtkComponentPeer component,
186                                    long vimg, int x, int y, 
187                                    int width, int height, int cx, int cy,
188                                    int cw, int ch);
189
190   /**
191    * Not really related (moveme?). Utility method used by GtkComponent.
192    */
193   public static GtkImage grab( GtkComponentPeer component )
194   {
195     return new GtkImage( nativeGrab( component ) );
196   }
197
198   /**
199    * Returns a Graphics2D object for a component, either an instance of this 
200    * class (if xrender is supported), or a context which copies.
201    */
202   public static Graphics2D getComponentGraphics(GtkComponentPeer component)
203   {
204     if( hasXRenderExtension )
205       return new ComponentGraphics(component);
206
207     Rectangle r = component.awtComponent.getBounds();
208     return new ComponentGraphicsCopy(r.width, r.height, component);
209   }
210
211   public GraphicsConfiguration getDeviceConfiguration()
212   {
213     return component.getGraphicsConfiguration();
214   }
215
216   public Graphics create()
217   {
218     return new ComponentGraphics(this);
219   }
220   
221   protected Rectangle2D getRealBounds()
222   {
223     return component.awtComponent.getBounds();
224   }
225
226   public void copyAreaImpl(int x, int y, int width, int height, int dx, int dy)
227   {
228     copyAreaNative(component, x, y, width, height, dx, dy);
229   }
230
231   /**
232    * Overloaded methods that do actual drawing need to enter the gdk threads 
233    * and also do certain things before and after.
234    */
235   public void draw(Shape s)
236   {
237     lock();
238     try
239       {
240         if (comp == null || comp instanceof AlphaComposite)
241           super.draw(s);
242         
243         else
244           {
245             createBuffer();
246             
247             Graphics2D g2d = (Graphics2D)buffer.getGraphics();
248             g2d.setStroke(this.getStroke());
249             g2d.setColor(this.getColor());
250             g2d.draw(s);
251             
252             drawComposite(s.getBounds2D(), null);
253           }
254       }
255     finally
256       {
257         unlock();
258       }
259   }
260
261   public void fill(Shape s)
262   {
263     lock();
264     try
265       {
266         if (comp == null || comp instanceof AlphaComposite)
267           super.fill(s);
268         
269         else
270           {
271             createBuffer();
272             
273             Graphics2D g2d = (Graphics2D)buffer.getGraphics();
274             g2d.setPaint(this.getPaint());
275             g2d.setColor(this.getColor());
276             g2d.fill(s);
277             
278             drawComposite(s.getBounds2D(), null);
279           }
280       }
281     finally
282       {
283         unlock();
284       }
285   }
286
287   public void drawRenderedImage(RenderedImage image, AffineTransform xform)
288   {
289     lock();
290     try
291       {
292         if (comp == null || comp instanceof AlphaComposite)
293           super.drawRenderedImage(image, xform);
294         
295         else
296           {
297             createBuffer();
298
299             Graphics2D g2d = (Graphics2D)buffer.getGraphics();
300             g2d.setRenderingHints(this.getRenderingHints());
301             g2d.drawRenderedImage(image, xform);
302             
303             drawComposite(buffer.getRaster().getBounds(), null);
304           }
305       }
306     finally
307       {
308         unlock();
309       }
310   }
311
312   protected boolean drawImage(Image img, AffineTransform xform,
313                               Color bgcolor, ImageObserver obs)
314   {
315     boolean rv;
316     lock();
317     try
318       {
319         if (comp == null || comp instanceof AlphaComposite)
320           rv = super.drawImage(img, xform, bgcolor, obs);
321         
322         else
323           {
324             // Get buffered image of source
325             if( !(img instanceof BufferedImage) )
326               {
327                 ImageProducer source = img.getSource();
328                 if (source == null)
329                   return false;
330                 img = Toolkit.getDefaultToolkit().createImage(source);
331               }
332             BufferedImage bImg = (BufferedImage) img;
333             
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());
338             if (xform != null)
339               {
340                 origin = xform.transform(origin, origin);
341                 pt = xform.transform(pt, pt);
342               }
343             
344             // Create buffer and draw image
345             createBuffer();
346             
347             Graphics2D g2d = (Graphics2D)buffer.getGraphics();
348             g2d.setRenderingHints(this.getRenderingHints());
349             g2d.drawImage(img, xform, obs);
350
351             // Perform compositing
352             rv = drawComposite(new Rectangle2D.Double(origin.getX(),
353                                                         origin.getY(),
354                                                         pt.getX(), pt.getY()),
355                                  obs);
356           }
357       }
358     finally
359       {
360         unlock();
361       }
362     return rv;
363   }
364
365   public void drawGlyphVector(GlyphVector gv, float x, float y)
366   {
367     lock();
368     try
369       {
370         if (comp == null || comp instanceof AlphaComposite)
371           super.drawGlyphVector(gv, x, y);
372         
373         else
374           {
375             createBuffer();
376
377             Graphics2D g2d = (Graphics2D)buffer.getGraphics();
378             g2d.setPaint(this.getPaint());
379             g2d.setStroke(this.getStroke());
380             g2d.drawGlyphVector(gv, x, y);
381             
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);
386           }
387       }
388     finally
389       {
390         unlock();
391       }
392   }
393   
394   public boolean drawImage(Image img, int x, int y, ImageObserver observer)
395   {
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)
400       {
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))
406           {
407             Rectangle2D r = (Rectangle2D) clip;
408             if (r == null)
409               r = getRealBounds();
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()),
416                          (int) r.getWidth(),
417                          (int) r.getHeight());
418             return true;
419           }
420         else
421           return super.drawImage(vimg.getSnapshot(), x, y, observer);
422       }
423
424     BufferedImage bimg;
425     if (img instanceof BufferedImage)
426       bimg = (BufferedImage) img;
427     else
428       {
429         ImageProducer source = img.getSource();
430         if (source == null)
431           return false;
432         bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source);
433       }
434     return super.drawImage(bimg, x, y, observer);
435   }
436   
437   public boolean drawImage(Image img, int x, int y, int width, int height,
438                            ImageObserver observer)
439   {
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))
445       {
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))
451           {
452             Rectangle2D r = (Rectangle2D) clip;
453             if (r == null)
454               r = getRealBounds();
455             x += transform.getTranslateX();
456             y += transform.getTranslateY();
457             drawVolatile(component, vimg.nativePointer,
458                          x, y, width, height,
459                          (int) (r.getX() + transform.getTranslateX()),
460                          (int) (r.getY() + transform.getTranslateY()),
461                          (int) r.getWidth(),
462                          (int) r.getHeight());
463             return true;
464           }
465         else
466           return super.drawImage(vimg.getSnapshot(), x, y,
467                                  width, height, observer);
468       }
469
470     BufferedImage bimg;
471     img = AsyncImage.realImage(img, observer);
472     if (img instanceof BufferedImage)
473       bimg = (BufferedImage) img;
474     else
475       {
476         ImageProducer source = img.getSource();
477         if (source == null)
478           return false;
479         bimg = (BufferedImage) Toolkit.getDefaultToolkit().createImage(source);
480       }
481     return super.drawImage(bimg, x, y, width, height, observer);
482   }
483
484   public void setClip(Shape s)
485   {
486     lock();
487     try
488       {
489         super.setClip(s);
490       }
491     finally
492       {
493         unlock();
494       }
495   }
496
497   
498   private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
499   {
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);
506     
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());
512     
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);
517     
518     Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1],
519                                                        points[2] - points[0],
520                                                        points[3] - points[1]);
521     
522     Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds);
523     
524     // Get current image on the component
525     unlock();
526     GtkImage img = grab(component);
527     Graphics gr = componentBuffer.createGraphics();
528     gr.drawImage(img, 0, 0, null);
529     gr.dispose();
530     lock();
531     
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());
538     
539     // Perform actual composite operation
540     compCtx.compose(buffer2.getRaster(), cBuffer.getRaster(),
541                     cBuffer.getRaster());
542     
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
545     // more than once!
546     boolean rv = super.drawImage(cBuffer,
547                                  AffineTransform.getTranslateInstance(bounds.getX(),
548                                                                       bounds.getY()),
549                                  null, null);
550     return rv;
551   }
552   
553   private void createBuffer()
554   {
555     if (buffer == null)
556       {
557         WritableRaster rst;
558         rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(),
559                                                                                 component.awtComponent.getHeight()),
560                                           new Point(0,0));
561         
562         buffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
563                                    GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
564                                    new Hashtable());
565       }
566     else
567       {
568         Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
569         
570         g2d.setBackground(new Color(0,0,0,0));
571         g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
572       }
573     
574     if (componentBuffer == null)
575       {
576         WritableRaster rst;
577         rst = Raster.createWritableRaster(GtkVolatileImage.createGdkSampleModel(component.awtComponent.getWidth(),
578                                                                                 component.awtComponent.getHeight()),
579                                           new Point(0,0));
580         
581         componentBuffer = new BufferedImage(GtkVolatileImage.gdkColorModel, rst,
582                                    GtkVolatileImage.gdkColorModel.isAlphaPremultiplied(),
583                                    new Hashtable());
584       }
585   }
586   
587   protected ColorModel getNativeCM()
588   {
589     return GtkVolatileImage.gdkColorModel;
590   }
591 }
592