Source for gnu.awt.xlib.XGraphicsConfiguration

   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:        */
  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:        */
  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:      */
 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:      */
 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:   
 144:   public XGraphicsConfiguration(Visual visual)
 145:   {
 146:     this.visual = visual;
 147:   }
 148: 
 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: 
 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:    */
 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:     
 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: 
 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:     
 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:     
 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:    */
 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:    */
 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:    */
 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:    */
 386:   public ColorModel getColorModel()
 387:   {
 388:     if (pixelCM == null)
 389:       preparePixelCM();
 390:     return pixelCM;
 391:   }
 392: 
 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:   
 436:   public ColorModel getColorModel(int transparency)
 437:   {
 438:     throw new UnsupportedOperationException("not implemented");
 439:   }
 440:     
 441:   public AffineTransform getDefaultTransform()
 442:   {
 443:     throw new UnsupportedOperationException("not implemented");
 444:   }
 445: 
 446:   public AffineTransform getNormalizingTransform()
 447:   {
 448:     throw new UnsupportedOperationException("not implemented");
 449:   }
 450:  
 451:   public Rectangle getBounds()
 452:   {
 453:     throw new UnsupportedOperationException("not implemented");
 454:   }
 455: 
 456:   Visual getVisual()
 457:   {
 458:     return visual;
 459:   }
 460:     
 461:   /* FIXME: This should be moved to XGraphicsDevice... */
 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: 
 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:    */
 545:   public VolatileImage createCompatibleVolatileImage (int width, int height,
 546:                                                       int transparency)
 547:   {
 548:     return null;
 549:   }
 550: }