Source for gnu.java.awt.peer.x.XGraphics2D

   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: 
 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
 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
 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
 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:    */
 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:    */
 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:    */
 153:   protected Rectangle getDeviceBounds()
 154:   {
 155:     return new Rectangle(0, 0, xdrawable.width, xdrawable.height);
 156:   }
 157: 
 158:   public GraphicsConfiguration getDeviceConfiguration()
 159:   {
 160:     // FIXME: Implement this.
 161:     throw new UnsupportedOperationException("Not yet implemented");
 162:   }
 163: 
 164:   public void dispose()
 165:   {
 166:     if (!disposed)
 167:       {
 168:         xgc.free();
 169:         xdrawable.display.flush();
 170:         disposed = true;
 171:       }
 172:   }
 173: 
 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: 
 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:    */
 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
 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: 
 304:   protected void init()
 305:   {
 306:     super.init();
 307:   }
 308: 
 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: 
 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: 
 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: 
 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: 
 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:    */
 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: }