1 /* XGraphics2D.java -- A Java based Graphics2D impl for X
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 package gnu.java.awt.peer.x;
39 
40 import java.awt.Color;
41 import java.awt.Font;
42 import java.awt.Graphics;
43 import java.awt.GraphicsConfiguration;
44 import java.awt.Image;
45 import java.awt.Paint;
46 import java.awt.Rectangle;
47 import java.awt.Shape;
48 import java.awt.Toolkit;
49 import java.awt.Transparency;
50 import java.awt.geom.AffineTransform;
51 import java.awt.image.BufferedImage;
52 import java.awt.image.ColorModel;
53 import java.awt.image.DataBuffer;
54 import java.awt.image.ImageObserver;
55 import java.awt.image.Raster;
56 import java.awt.peer.FontPeer;
57 import java.util.HashMap;
58 import java.util.WeakHashMap;
59 
60 import gnu.java.awt.image.AsyncImage;
61 import gnu.java.awt.java2d.AbstractGraphics2D;
62 import gnu.java.awt.java2d.ScanlineCoverage;
63 import gnu.x11.Colormap;
64 import gnu.x11.Drawable;
65 import gnu.x11.GC;
66 import gnu.x11.image.ZPixmap;
67 
68 public class XGraphics2D
69   extends AbstractGraphics2D
70 {
71 
72   /**
73    * When this property is set to true, then images are always rendered as
74    * opaque images, ignoring their translucence. This is intended for
75    * debugging and demonstration purposes.
76    */
77   private static final boolean RENDER_OPAQUE =
78     Boolean.getBoolean("escherpeer.renderopaque");
79 
80   /**
81    * The X Drawable to draw on.
82    */
83   private Drawable xdrawable;
84 
85   /**
86    * The X graphics context (GC).
87    */
88   private GC xgc;
89 
90   /**
91    * Indicates if this graphics has already been disposed.
92    */
93   private boolean disposed;
94 
95   /**
96    * The current foreground color, possibly null.
97    */
98   private Color foreground;
99 
XGraphics2D(Drawable d)100   XGraphics2D(Drawable d)
101   {
102     super();
103     xdrawable = d;
104     xgc = new GC(d);
105     init();
106     disposed = false;
107     //setClip(new Rectangle(0, 0, xdrawable.width, xdrawable.height));
108   }
109 
110   @Override
rawDrawLine(int x0, int y0, int x1, int y1)111   protected void rawDrawLine(int x0, int y0, int x1, int y1)
112   {
113     xdrawable.segment(xgc, x0, y0, x1, y1);
114   }
115 
116   @Override
rawDrawRect(int x, int y, int w, int h)117   protected void rawDrawRect(int x, int y, int w, int h)
118   {
119     xdrawable.rectangle(xgc, x, y, w, h, false);
120   }
121 
122   @Override
rawFillRect(int x, int y, int w, int h)123   protected void rawFillRect(int x, int y, int w, int h)
124   {
125     xdrawable.rectangle(xgc, x, y, w, h, true);
126   }
127 
128   /**
129    * Returns the color model of this Graphics object.
130    *
131    * @return the color model of this Graphics object
132    */
getColorModel()133   protected ColorModel getColorModel()
134   {
135     return Toolkit.getDefaultToolkit().getColorModel();
136   }
137 
138   /**
139    * Returns the color model of the target device.
140    *
141    * @return the color model of the target device
142    */
getDestinationColorModel()143   protected ColorModel getDestinationColorModel()
144   {
145     return Toolkit.getDefaultToolkit().getColorModel();
146   }
147 
148   /**
149    * Returns the bounds of the target.
150    *
151    * @return the bounds of the target
152    */
getDeviceBounds()153   protected Rectangle getDeviceBounds()
154   {
155     return new Rectangle(0, 0, xdrawable.width, xdrawable.height);
156   }
157 
getDeviceConfiguration()158   public GraphicsConfiguration getDeviceConfiguration()
159   {
160     // FIXME: Implement this.
161     throw new UnsupportedOperationException("Not yet implemented");
162   }
163 
dispose()164   public void dispose()
165   {
166     if (!disposed)
167       {
168         xgc.free();
169         xdrawable.display.flush();
170         disposed = true;
171       }
172   }
173 
create()174   public Graphics create()
175   {
176     // super.create() returns a copy created by clone(), so it should
177     // be a XGraphics2D.
178     XGraphics2D copy = (XGraphics2D) super.create();
179     copy.xgc = xgc.copy();
180     return copy;
181   }
182 
setClip(Shape c)183   public void setClip(Shape c)
184   {
185     super.setClip(c);
186     if (c instanceof Rectangle)
187       {
188         Rectangle r = (Rectangle) c;
189         AffineTransform t = getTransform();
190         int translateX = (int) t.getTranslateX();
191         //System.err.println("translateX: " + translateX);
192         int translateY = (int) t.getTranslateY();
193         //System.err.println("translateY: " + translateY);
194         //System.err.println("clip: " + c);
195         gnu.x11.Rectangle clip = new gnu.x11.Rectangle(r.x, r.y, r.width,
196                                                        r.height);
197         xgc.set_clip_rectangles(translateX, translateY,
198                                 new gnu.x11.Rectangle[]{clip}, GC.UN_SORTED);
199       }
200   }
201 
202   /**
203    * Notifies the backend that the raster has changed in the specified
204    * rectangular area. The raster that is provided in this method is always
205    * the same as the one returned in {@link #getDestinationRaster}.
206    * Backends that reflect changes to this raster directly don't need to do
207    * anything here.
208    *
209    * @param raster the updated raster, identical to the raster returned
210    *        by {@link #getDestinationRaster()}
211    * @param x the upper left corner of the updated region, X coordinate
212    * @param y the upper lef corner of the updated region, Y coordinate
213    * @param w the width of the updated region
214    * @param h the height of the updated region
215    */
updateRaster(Raster raster, int x, int y, int w, int h)216   protected void updateRaster(Raster raster, int x, int y, int w, int h)
217   {
218     if (w > 0 && h > 0)
219       {
220         ZPixmap zPixmap = new ZPixmap(xdrawable.display, w, h,
221                                       xdrawable.display.default_pixmap_format);
222         int[] pixel = null;
223         int x1 = x + w;
224         int y1 = y + h;
225         for (int tx = x; tx < x1; tx++)
226           {
227             for (int ty = y; ty < y1; ty++)
228               {
229                 pixel = raster.getPixel(tx, ty, pixel);
230                 //System.err.println("tx: " + tx + ", ty: " + ty + ", pixel: " + pixel[0] + ", " + pixel[1] + ", " + pixel[2]);
231 //              System.err.print("r: " + pixel[0]);
232 //              System.err.print(", g: " + pixel[1]);
233 //              System.err.println(", b: " + pixel[2]);
234                 zPixmap.set_red(tx - x, ty - y, pixel[0]);
235                 zPixmap.set_green(tx - x, ty - y, pixel[1]);
236                 zPixmap.set_blue(tx - x, ty - y, pixel[2]);
237               }
238           }
239         xdrawable.put_image(xgc, zPixmap, x, y);
240       }
241   }
242 
243   @Override
renderScanline(int y, ScanlineCoverage c)244   public void renderScanline(int y, ScanlineCoverage c)
245   {
246     if (y >= xdrawable.height)
247       return;
248 
249     // TODO: Handle Composite and Paint.
250     ScanlineCoverage.Iterator iter = c.iterate();
251     int coverageAlpha = 0;
252     int maxCoverage = c.getMaxCoverage();
253     while (iter.hasNext())
254       {
255         ScanlineCoverage.Range range = iter.next();
256 
257         coverageAlpha = range.getCoverage();
258         int x0 = range.getXPos();
259         int l = range.getLength();
260         if (coverageAlpha == c.getMaxCoverage())
261           {
262             // Simply paint the current color over the existing pixels.
263             xdrawable.fill_rectangle(xgc, x0, y, l, 1);
264           }
265         else if (coverageAlpha > 0)
266           {
267             // Composite the current color with the existing pixels.
268             int x1 = x0 + l;
269             x0 = Math.min(Math.max(0, x0), xdrawable.width - 1);
270             x1 = Math.min(Math.max(0, x1), xdrawable.width - 1);
271             if ((x1 - x0) < 1)
272               continue;
273             l = x1 - x0;
274             gnu.x11.image.ZPixmap existing = (ZPixmap)
275             xdrawable.image(x0, y, l, 1, 0xFFFFFFFF,
276                             gnu.x11.image.Image.Format.ZPIXMAP);
277             for (int x = 0; x < l; x++)
278               {
279                 Color col = getColor();
280                 if (col == null)
281                   {
282                     col = Color.BLACK;
283                   }
284                 int red = col.getRed();
285                 int green = col.getGreen();
286                 int blue = col.getBlue();
287                 int redOut = existing.get_red(x, 0);
288                 int greenOut = existing.get_green(x, 0);
289                 int blueOut = existing.get_blue(x, 0);
290                 int outAlpha = maxCoverage - coverageAlpha;
291                 redOut = redOut * outAlpha + red * coverageAlpha;
292                 redOut = redOut / maxCoverage;
293                 greenOut = greenOut * outAlpha + green * coverageAlpha;
294                 greenOut = greenOut / maxCoverage;
295                 blueOut = blueOut * outAlpha + blue * coverageAlpha;
296                 blueOut = blueOut / maxCoverage;
297                 existing.set(x, 0, redOut, greenOut, blueOut);
298               }
299             xdrawable.put_image(xgc, existing, x0, y);
300           }
301       }
302   }
303 
init()304   protected void init()
305   {
306     super.init();
307   }
308 
setPaint(Paint p)309   public void setPaint(Paint p)
310   {
311     super.setPaint(p);
312     if (p instanceof Color)
313       {
314         // TODO: Optimize for different standard bit-depths.
315         Color c = (Color) p;
316        /* XToolkit tk = (XToolkit) Toolkit.getDefaultToolkit();
317         HashMap colorMap = tk.colorMap;
318         gnu.x11.Color col = (gnu.x11.Color) colorMap.get(c);
319         if (col == null)
320           {
321             Colormap map = xdrawable.display.default_colormap;
322             col = map.alloc_color (c.getRed() * 256,
323                                    c.getGreen() * 256,
324                                    c.getBlue() * 256);
325             colorMap.put(c, col);
326           }*/
327         //xgc.set_foreground(col);
328 
329         xgc.set_foreground(c.getRGB());
330         foreground = c;
331       }
332   }
333 
fillShape(Shape s, boolean isFont)334   protected void fillShape(Shape s, boolean isFont)
335   {
336     synchronized (xdrawable.display) {
337       super.fillShape(s, isFont);
338     }
339   }
340 
341   private static WeakHashMap<Image,ZPixmap> imageCache = new WeakHashMap<Image,ZPixmap>();
342 
rawDrawImage(Image image, int x, int y, ImageObserver obs)343   protected boolean rawDrawImage(Image image, int x, int y, ImageObserver obs)
344   {
345     image = unwrap(image);
346     boolean ret;
347     if (image instanceof XImage)
348       {
349         XImage xImage = (XImage) image;
350         xdrawable.copy_area(xImage.pixmap, xgc, 0, 0, xImage.getWidth(obs),
351                             xImage.getHeight(obs), x, y);
352         ret = true;
353       }
354     else if (image instanceof PixmapVolatileImage)
355       {
356         PixmapVolatileImage pvi = (PixmapVolatileImage) image;
357         xdrawable.copy_area(pvi.getPixmap(), xgc, 0, 0, pvi.getWidth(obs),
358                             pvi.getHeight(obs), x, y);
359         ret = true;
360       }
361     else if (image instanceof BufferedImage)
362       {
363         BufferedImage bi = (BufferedImage) image;
364         DataBuffer db = bi.getRaster().getDataBuffer();
365         if (db instanceof ZPixmapDataBuffer)
366           {
367             ZPixmapDataBuffer zpmdb = (ZPixmapDataBuffer) db;
368             ZPixmap zpixmap = zpmdb.getZPixmap();
369             xdrawable.put_image(xgc, zpixmap, x, y);
370             ret = true;
371           }
372         else
373           {
374             int transparency = bi.getTransparency();
375             int w = bi.getWidth();
376             int h = bi.getHeight();
377             if (imageCache.containsKey(image))
378               {
379                 ZPixmap zpixmap = imageCache.get(image);
380                 xdrawable.put_image(xgc, zpixmap, x, y);
381               }
382             else if (transparency == Transparency.OPAQUE || RENDER_OPAQUE)
383               {
384                 XGraphicsDevice gd = XToolkit.getDefaultDevice();
385                 ZPixmap zpixmap = new ZPixmap(gd.getDisplay(), w, h);
386                 for (int yy = 0; yy < h; yy++)
387                   {
388                     for (int xx = 0; xx < w; xx++)
389                       {
390                         int rgb = bi.getRGB(xx, yy);
391                         zpixmap.set(xx, yy, rgb);
392                       }
393                   }
394                 xdrawable.put_image(xgc, zpixmap, x, y);
395                 imageCache.put(image, zpixmap);
396               } else {
397 
398                 // TODO optimize reusing the rectangles
399                 Rectangle source =
400                   new Rectangle(0, 0, xdrawable.width, xdrawable.height);
401                 Rectangle target = new Rectangle(x, y, w, h);
402 
403                 Rectangle destination = source.intersection(target);
404 
405                 x = destination.x;
406                 y = destination.y;
407                 w = destination.width;
408                 h = destination.height;
409 
410                 ZPixmap zpixmap =
411                   (ZPixmap) xdrawable.image(x, y, w, h,
412                                             0xffffffff,
413                                             gnu.x11.image.Image.Format.ZPIXMAP);
414                 for (int yy = 0; yy < h; yy++)
415                   {
416                     for (int xx = 0; xx < w; xx++)
417                       {
418                         int rgb = bi.getRGB(xx, yy);
419                         int alpha = 0xff & (rgb >> 24);
420                         if (alpha == 0)
421                           {
422                             // Completely translucent.
423                             rgb = zpixmap.get_red(xx, yy) << 16
424                                   | zpixmap.get_green(xx, yy) << 8
425                                   | zpixmap.get_blue(xx, yy);
426                           }
427                         else if (alpha < 255)
428                           {
429                             // Composite pixels.
430                             int red = 0xff & (rgb >> 16);
431                             red = red * alpha
432                                      + (255 - alpha) * zpixmap.get_red(xx, yy);
433                             red = red / 255;
434                             int green = 0xff & (rgb >> 8);
435                             green = green * alpha
436                                    + (255 - alpha) * zpixmap.get_green(xx, yy);
437                             green = green / 255;
438                             int blue = 0xff & rgb;
439                             blue = blue * alpha
440                                     + (255 - alpha) * zpixmap.get_blue(xx, yy);
441                             blue = blue / 255;
442                             rgb = red << 16 | green << 8 | blue;
443                           }
444                         // else keep rgb value from source image.
445 
446                         zpixmap.set(xx, yy, rgb);
447                       }
448                   }
449                 xdrawable.put_image(xgc, zpixmap, x, y);
450                 // We can't cache prerendered translucent images, because
451                 // we never know how the background changes.
452               }
453             ret = true;
454           }
455       }
456     else
457       {
458         ret = super.rawDrawImage(image, x, y, obs);
459       }
460     return ret;
461   }
462 
setFont(Font f)463   public void setFont(Font f)
464   {
465     super.setFont(f);
466     FontPeer p = getFont().getPeer();
467     if (p instanceof XFontPeer)
468       {
469         XFontPeer xFontPeer = (XFontPeer) p;
470         xgc.set_font(xFontPeer.getXFont());
471       }
472   }
473 
drawString(String s, int x, int y)474   public void drawString(String s, int x, int y)
475   {
476     FontPeer p = getFont().getPeer();
477     if (p instanceof XFontPeer)
478       {
479         int tx = (int) transform.getTranslateX();
480         int ty = (int) transform.getTranslateY();
481         xdrawable.text(xgc, x + tx, y + ty, s);
482       }
483     else
484       {
485         super.drawString(s, x, y);
486       }
487   }
488 
489   /**
490    * Extracts an image instance out of an AsyncImage. If the image isn't
491    * an AsyncImage, then the original instance is returned.
492    *
493    * @param im the image
494    *
495    * @return the image to render
496    */
unwrap(Image im)497   private Image unwrap(Image im)
498   {
499     Image image = im;
500     if (image instanceof AsyncImage)
501       {
502         AsyncImage aIm = (AsyncImage) image;
503         image = aIm.getRealImage();
504       }
505     return image;
506   }
507 
508 }
509