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