1 /* Copyright (C) 2000, 2003  Free Software Foundation
2 
3    This file is part of libgcj.
4 
5 This software is copyrighted work licensed under the terms of the
6 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
7 details.  */
8 
9 package gnu.awt.xlib;
10 
11 import java.awt.GraphicsConfiguration;
12 import java.awt.Rectangle;
13 import java.awt.Graphics2D;
14 import java.awt.Graphics;
15 import java.awt.GraphicsDevice;
16 import java.awt.Point;
17 import java.awt.Color;
18 import java.awt.color.ColorSpace;
19 import java.awt.Font;
20 import java.awt.image.*;
21 import java.awt.geom.AffineTransform;
22 import gnu.gcj.xlib.GC;
23 import gnu.gcj.xlib.Drawable;
24 import gnu.gcj.xlib.Window;
25 import gnu.gcj.xlib.XImage;
26 import gnu.gcj.xlib.Visual;
27 import gnu.gcj.xlib.Colormap;
28 import gnu.gcj.xlib.XColor;
29 import gnu.gcj.xlib.Screen;
30 import gnu.gcj.xlib.Display;
31 import gnu.gcj.xlib.XException;
32 import gnu.java.awt.Buffers;
33 import java.util.Enumeration;
34 import java.util.Hashtable;
35 
36 public class XGraphicsConfiguration extends GraphicsConfiguration
37 {
38   //public abstract GraphicsDevice getDevice();
39 
40   Visual visual;
41   int format;
42   Colormap colormap;
43   ColorModel imageCM;
44   ColorModel pixelCM;
45   private static final int CACHE_SIZE_PER_DISPLAY = 10;
46   static FontMetricsCache fontMetricsCache = new FontMetricsCache ();
47 
48   /** Font metrics cache class.  Caches at most CACHE_SIZE_PER_DISPLAY
49    * XFontMetrics objects for each display device.  When a display's cache
50    * gets full, the least-recently used entry is overwritten.
51    * XXX: lruOrder rolls over after a few billion operations, so it might
52    * on very rare occasions misinterpret which is the oldest entry
53    */
54   static class FontMetricsCache
55   {
56     private java.util.Hashtable displays = new java.util.Hashtable ();
57 
58     /** Font metrics cache for a display device
59      */
60     class PerDisplayCache
61     {
62       private int lruCount = 0;
63       private java.util.Hashtable entries = new java.util.Hashtable ();
64 
65       class CacheEntry
66       {
67         int lruOrder;
68         XFontMetrics fm;
69         Font font;
70       }
71 
72       /** Get an entry (null if not there) and update LRU ordering
73        */
get(Font font)74       XFontMetrics get (Font font)
75       {
76         CacheEntry entry = (CacheEntry)entries.get (font);
77         if (entry != null)
78         {
79           entry.lruOrder = lruCount++;
80         }
81         return (entry==null) ? null : entry.fm;
82       }
83 
84       /** Put an entry in the cache, eliminating the oldest entry if
85        * the cache is at capacity.
86        */
put(Font font, XFontMetrics fontMetrics)87       void put (Font font, XFontMetrics fontMetrics)
88       {
89         if (entries.size () >= CACHE_SIZE_PER_DISPLAY)
90         {
91           // cache is full -- eliminate the oldest entry
92           // slow operation, but shouldn't happen very often
93           int maxAge = 0;
94           CacheEntry oldestEntry = null;
95           int referenceCount = lruCount;
96           for (Enumeration e = entries.elements (); e.hasMoreElements ();)
97           {
98             CacheEntry entry = (CacheEntry)e.nextElement ();
99             if ((referenceCount-entry.lruOrder) > maxAge)
100             {
101               maxAge = referenceCount-entry.lruOrder;
102               oldestEntry = entry;
103             }
104           }
105           if (oldestEntry != null)
106             entries.remove (oldestEntry.font);
107         }
108         CacheEntry newEntry = new CacheEntry ();
109         newEntry.lruOrder = lruCount++;
110         newEntry.fm = fontMetrics;
111         newEntry.font = font;
112         entries.put (font,newEntry);
113       }
114     }
115 
116     /** Get the font metrics for a font, if it is present in the cache.
117      * @param font The AWT font for which to find the font metrics
118      * @param display The display, to select the cached entries for that display
119      * @return The font metrics, or null if not cached
120      */
get(Font font, Display display)121     XFontMetrics get (Font font, Display display)
122     {
123       PerDisplayCache cache = (PerDisplayCache)displays.get (display);
124       return (cache==null) ? null : cache.get (font);
125     }
126 
127     /** Put a font in the cache
128      * @param font The font
129      * @param display The display
130      * @param fontMetrics The font metrics
131      */
put(Font font, Display display, XFontMetrics fontMetrics)132     void put (Font font, Display display, XFontMetrics fontMetrics)
133     {
134       PerDisplayCache cache = (PerDisplayCache)displays.get (display);
135       if (cache == null)
136       {
137         cache = new PerDisplayCache ();
138         displays.put (display,cache);
139       }
140       cache.put (font,fontMetrics);
141     }
142   }
143 
XGraphicsConfiguration(Visual visual)144   public XGraphicsConfiguration(Visual visual)
145   {
146     this.visual = visual;
147   }
148 
createCompatibleImage(int width, int height)149   public BufferedImage createCompatibleImage(int width, int height)
150   {
151     XImage ximg = new XImage(visual, width, height,
152 			     false // do not auto allocate memory
153 			     );
154 
155     Point origin = new Point(0, 0);
156     WritableRaster raster = createRasterForXImage(ximg, origin);
157 
158     /* This is not a good way of doing this. Multiple toolkits may
159        want to share the BufferedImage. */
160     Hashtable props = new Hashtable();
161     props.put("gnu.gcj.xlib.XImage", ximg);
162     props.put("java.awt.GraphicsConfiguration", this);
163 
164     BufferedImage bimg = new BufferedImage(imageCM,raster, false, props);
165 
166     DataBuffer dataB = raster.getDataBuffer();
167     attachData(ximg, dataB, 0);
168     return bimg;
169   }
170 
createRasterForXImage(XImage ximage, Point origin)171   WritableRaster createRasterForXImage(XImage ximage, Point origin)
172   {
173     if (imageCM == null) prepareColorModel(ximage);
174 
175     /*
176       This will not work, since it creates a sample model that
177       does not necessarily match the format of the XImage.
178 
179       WritableRaster raster =
180       imageCM.createCompatibleWritableRaster(width, height); */
181 
182     // Create a sample model matching the XImage:
183 
184     SampleModel imageSM = null;
185 
186     int width = ximage.getWidth();
187     int height = ximage.getHeight();
188     int bitsPerPixel = ximage.getBitsPerPixel();
189     int dataType =
190       Buffers.smallestAppropriateTransferType(bitsPerPixel);
191     int bitsPerDataElement = DataBuffer.getDataTypeSize(dataType);
192     int scanlineStride = ximage.getBytesPerLine()*8/bitsPerDataElement;
193 
194     if (imageCM instanceof IndexColorModel)
195       {
196 	int[] bandOffsets = {0};
197 	imageSM = new ComponentSampleModel(dataType,
198 					   width, height,
199 					   1, // pixel stride
200 					   scanlineStride,
201 					   bandOffsets);
202       }
203     else if (imageCM instanceof PackedColorModel)
204       {
205 	PackedColorModel pcm = (PackedColorModel) imageCM;
206 	int[] masks = pcm.getMasks();
207 
208 	imageSM = new SinglePixelPackedSampleModel(dataType,
209 						   width, height,
210 						   scanlineStride,
211 						   masks);
212       }
213 
214     if (imageSM == null)
215       {
216 	throw new UnsupportedOperationException("creating sample model " +
217 						"for " + imageCM +
218 						" not implemented");
219       }
220 
221     WritableRaster raster = Raster.createWritableRaster(imageSM, origin);
222     return raster;
223   }
224 
225 
226 
227   /**
228    * Attach a the memory of a data buffer to an XImage
229    * structure. [This method is not gnu.awt.xlib specific, and should
230    * maybe be moved to a different location.]
231    *
232    * @param offset Offset to data. The given offset does not include
233    * data buffer offset, which will also be added.
234    */
attachData(XImage ximage, DataBuffer dataB, int offset)235   static void attachData(XImage ximage, DataBuffer dataB, int offset)
236   {
237     offset += dataB.getOffset();
238     switch (dataB.getDataType())
239       {
240       case DataBuffer.TYPE_BYTE:
241 	ximage.setData(((DataBufferByte) dataB).getData(), offset);
242 	break;
243       case DataBuffer.TYPE_USHORT:
244 	ximage.setData(((DataBufferUShort) dataB).getData(), offset);
245 	break;
246       case DataBuffer.TYPE_INT:
247 	ximage.setData(((DataBufferInt) dataB).getData(), offset);
248 	break;
249       default:
250 	throw
251 	  new UnsupportedOperationException("Do not know how to " +
252 					    "set data for data " +
253 					    "type " +
254 					    dataB.getDataType());
255       }
256   }
257 
prepareColorModel(XImage ximage)258   void prepareColorModel(XImage ximage)
259   {
260     format = ximage.getFormat();
261     int bitsPerPixel = ximage.getBitsPerPixel();
262     switch (format) {
263     case XImage.ZPIXMAP_FORMAT:
264       calcZPixmapModels(bitsPerPixel);
265       break;
266 
267     default:
268       throw new UnsupportedOperationException("unimplemented format");
269     }
270   }
271 
calcZPixmapModels(int bitsPerPixel)272   void calcZPixmapModels(int bitsPerPixel)
273   {
274     switch (visual.getVisualClass())
275       {
276       case Visual.VC_TRUE_COLOR:
277 	calcDecomposedRGBModels(bitsPerPixel);
278 	break;
279       case Visual.VC_PSEUDO_COLOR:
280 	calcPseudoColorModels(bitsPerPixel);
281 	break;
282       default:
283 	String msg = "unimplemented visual class";
284 	throw new UnsupportedOperationException(msg);
285       }
286   }
287 
calcDecomposedRGBModels(int bitsPerPixel)288   void calcDecomposedRGBModels(int bitsPerPixel)
289   {
290     int dataType = Buffers.smallestAppropriateTransferType(bitsPerPixel);
291 
292 
293     if (DataBuffer.getDataTypeSize(dataType) == bitsPerPixel)
294       {
295 	ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
296 
297 	imageCM = new DirectColorModel(cs,
298 				       visual.getDepth(),
299 				       visual.getRedMask(),
300 				       visual.getGreenMask(),
301 				       visual.getBlueMask(),
302 				       0, // no alpha
303 				       false,
304 				       dataType);
305       }
306     else
307       {
308 	throw new
309 	  UnsupportedOperationException("unimplemented bits per pixel");
310       }
311     }
312 
calcPseudoColorModels(int bitsPerPixel)313   void calcPseudoColorModels(int bitsPerPixel)
314   {
315     if (colormap == null)
316       colormap = visual.getScreen().getDefaultColormap();
317 
318     XColor[] colArray = colormap.getXColors();
319 
320     int numCol = colArray.length;
321     byte[] rmap = new byte[numCol];
322     byte[] gmap = new byte[numCol];
323     byte[] bmap = new byte[numCol];
324     byte[] amap = new byte[numCol];
325 
326     for (int i=0; i < numCol; i++)
327       {
328 	XColor color = colArray[i];
329 	if (color.getFlags() == Colormap.FLAG_SHARED)
330 	  {
331 	    rmap[i] = (byte) (color.getRed()   >> 8);
332 	    gmap[i] = (byte) (color.getGreen() >> 8);
333 	    bmap[i] = (byte) (color.getBlue()  >> 8);
334 	    amap[i] = (byte) 0xff;
335 	  } // else, leave default zero values...
336       }
337 
338     imageCM = new IndexColorModel(visual.getDepth(), numCol,
339 				  rmap, gmap, bmap, amap);
340   }
341 
342   /**
343    * Gets the associated device that this configuration describes.
344    *
345    * @return the device
346    */
getDevice()347   public GraphicsDevice getDevice()
348   {
349     throw new UnsupportedOperationException("not implemented");
350   }
351 
352   /**
353    * Returns a buffered image optimized to this device, so that blitting can
354    * be supported in the buffered image.
355    *
356    * @param w the width of the buffer
357    * @param h the height of the buffer
358    * @return the buffered image, or null if none is supported
359    */
createCompatibleImage(int width, int height, int transparency)360   public BufferedImage createCompatibleImage(int width,
361 					     int height,
362 					     int transparency)
363   {
364     throw new UnsupportedOperationException("not implemented");
365   }
366 
367   /**
368    * Returns a buffered volatile image optimized to this device, so that
369    * blitting can be supported in the buffered image. Because the buffer is
370    * volatile, it can be optimized by native graphics accelerators.
371    *
372    * @param w the width of the buffer
373    * @param h the height of the buffer
374    * @return the buffered image, or null if none is supported
375    * @see Component#createVolatileImage(int, int)
376    * @since 1.4
377    */
createCompatibleVolatileImage(int w, int h)378   public VolatileImage createCompatibleVolatileImage(int w, int h)
379   {
380     throw new UnsupportedOperationException("not implemented");
381   }
382 
383   /**
384    * FIXME: I'm not sure which color model that should be returned here.
385    */
getColorModel()386   public ColorModel getColorModel()
387   {
388     if (pixelCM == null)
389       preparePixelCM();
390     return pixelCM;
391   }
392 
preparePixelCM()393   void preparePixelCM()
394   {
395     switch (visual.getVisualClass())
396       {
397       case Visual.VC_TRUE_COLOR:
398 	pixelCM = new DirectColorModel(visual.getDepth(),
399 				       visual.getRedMask(),
400 				       visual.getGreenMask(),
401 				       visual.getBlueMask());
402 	break;
403       case Visual.VC_PSEUDO_COLOR:
404 
405 	if (colormap == null)
406 	  colormap = visual.getScreen().getDefaultColormap();
407 
408 	XColor[] colArray = colormap.getXColors();
409 
410 	int numCol = colArray.length;
411 	byte[] rmap = new byte[numCol];
412 	byte[] gmap = new byte[numCol];
413 	byte[] bmap = new byte[numCol];
414 	byte[] amap = new byte[numCol];
415 
416 	for (int i=0; i < numCol; i++)
417 	  {
418 	    XColor color = colArray[i];
419 	    if (color.getFlags() == Colormap.FLAG_SHARED) {
420 	      rmap[i] = (byte) (color.getRed()   >> 8);
421 	      gmap[i] = (byte) (color.getGreen() >> 8);
422 	      bmap[i] = (byte) (color.getBlue()  >> 8);
423 	      amap[i] = (byte) 0xff;
424 	    } // else, leave default zero values...
425 
426 	  }
427 
428 	pixelCM = new IndexColorModel(visual.getDepth(), numCol,
429 				      rmap, gmap, bmap, amap);
430 	break;
431       default:
432 	throw new UnsupportedOperationException("not implemented");
433       }
434   }
435 
getColorModel(int transparency)436   public ColorModel getColorModel(int transparency)
437   {
438     throw new UnsupportedOperationException("not implemented");
439   }
440 
getDefaultTransform()441   public AffineTransform getDefaultTransform()
442   {
443     throw new UnsupportedOperationException("not implemented");
444   }
445 
getNormalizingTransform()446   public AffineTransform getNormalizingTransform()
447   {
448     throw new UnsupportedOperationException("not implemented");
449   }
450 
getBounds()451   public Rectangle getBounds()
452   {
453     throw new UnsupportedOperationException("not implemented");
454   }
455 
getVisual()456   Visual getVisual()
457   {
458     return visual;
459   }
460 
461   /* FIXME: This should be moved to XGraphicsDevice... */
getXFontMetrics(java.awt.Font awtFont)462   XFontMetrics getXFontMetrics (java.awt.Font awtFont)
463   {
464     // If the metrics object for this font is already cached, use it.
465     // Otherwise create and cache it.
466     Display display = visual.getScreen ().getDisplay ();
467     XFontMetrics fm = fontMetricsCache.get (awtFont,display);
468     if (fm == null)
469     {
470       String foundry      = "*";
471       String family       = awtFont.getName ();
472       String weight       = awtFont.isBold () ? "bold" : "medium";
473       String slant        = awtFont.isItalic () ? "i" : "r";
474       String sWidth       = "*";
475       String addStyle     = "";
476       String pixelSize    = "*";
477       String pointSize    = awtFont.getSize () + "0";
478       String xres         = "*";
479       String yres         = "*";
480       String spacing      = "*";
481       String averageWidth = "*";
482       String charset      = "iso10646-1"; // because we use functions like XDrawString16
483 
484       String logicalFontDescription =
485         "-" + // FontNameRegistry prefix
486         foundry   + "-" + family    + "-" + weight       + "-" +
487         slant     + "-" + sWidth    + "-" + addStyle     + "-" +
488         pixelSize + "-" + pointSize + "-" + xres         + "-" +
489         yres      + "-" + spacing   + "-" + averageWidth + "-";
490 
491       // Try to load a Unicode font.  If that doesn't work, try again, without
492       // specifying the character set.
493       try
494       {
495         gnu.gcj.xlib.Font xfont = new gnu.gcj.xlib.Font (display, logicalFontDescription + charset);
496         fm = new XFontMetrics (xfont, awtFont);
497       }
498       catch (XException e)
499       {
500         gnu.gcj.xlib.Font xfont = new gnu.gcj.xlib.Font (display, logicalFontDescription + "*-*");
501         fm = new XFontMetrics (xfont, awtFont);
502       }
503       fontMetricsCache.put (awtFont,display,fm);
504     }
505     return fm;
506   }
507 
getPixel(Color color)508   int getPixel(Color color)
509   {
510     /* FIXME: consider an integer technique whenever
511      * the ColorModel is 8 bits per color.
512      * The problem with using integers is that it doesn't work unless
513      * the colors are 8 bits each (as in the array), since ColorModel.getDataElement(int[],int)
514      * expects non-normalized values.  For example, in a 16-bit display mode, you
515      * would typically have 5 bits each for red and blue, and 6 bits for green.
516     int[] components =
517     {
518       color.getRed (),
519       color.getGreen (),
520       color.getBlue (),
521       0xff
522     };
523      */
524 
525     int[] unnormalizedComponents = { 0, 0, 0, 0xff };
526     ColorModel cm = getColorModel ();
527     if (color != null)
528     {
529       float[] normalizedComponents =
530       {
531         ((float)color.getRed ()) / 255F,
532         ((float)color.getGreen ()) / 255F,
533         ((float)color.getBlue ()) / 255F,
534         1
535       };
536       cm.getUnnormalizedComponents(normalizedComponents, 0,
537            unnormalizedComponents, 0);
538     }
539     return cm.getDataElement (unnormalizedComponents, 0);
540   }
541 
542   /**
543    * @since 1.5
544    */
createCompatibleVolatileImage(int width, int height, int transparency)545   public VolatileImage createCompatibleVolatileImage (int width, int height,
546                                                       int transparency)
547   {
548     return null;
549   }
550 }
551