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