This implements rudimentary support for drawing (semi-) transparent
images for the X peers. This is quite ugly, because X doesn't support
any transparency in itself. So I have to fetch the actual pixels, do
some compositing and push the composited pixels back to the server.
Makes things slow. In the future I'll try to do this using the X render
extension...
2006-07-20 Roman Kennke <[EMAIL PROTECTED]>
* gnu/java/awt/peer/x/XToolkit.java
(createImage(InputStream)): Only copy image to Pixmap if
it's actually opaque. Transparent images are left as
BufferedImage and composited later onto the screen.
* gnu/java/awt/peer/x/XGraphics.java
(XGraphics): Fetch some parameters for image rendering.
(drawImage): Added special handling of transparent images.
(getRGB): New helper method.
(setRGB): New helper method.
/Roman
Index: gnu/java/awt/peer/x/XGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/x/XGraphics.java,v
retrieving revision 1.6
diff -u -1 -2 -r1.6 XGraphics.java
--- gnu/java/awt/peer/x/XGraphics.java 19 Jul 2006 19:21:43 -0000 1.6
+++ gnu/java/awt/peer/x/XGraphics.java 20 Jul 2006 13:06:48 -0000
@@ -30,38 +30,43 @@
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package gnu.java.awt.peer.x;
import gnu.x11.Colormap;
+import gnu.x11.Data;
+import gnu.x11.Display;
import gnu.x11.Drawable;
import gnu.x11.GC;
import gnu.x11.Pixmap;
import gnu.x11.Point;
+import gnu.x11.image.ZPixmap;
import java.awt.AWTError;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.text.AttributedCharacterIterator;
import java.util.HashMap;
public class XGraphics
extends Graphics
implements Cloneable
{
/**
* The X Drawable to draw on.
@@ -90,36 +95,46 @@
private Font font;
/**
* The current foreground color, possibly null.
*/
private Color foreground;
/**
* Indicates if this object has been disposed.
*/
private boolean disposed = false;
+ // TODO: Workaround for limitation in current Escher.
+ private Pixmap.Format pixmapFormat;
+ private int imageByteOrder;
+ private int pixelByteCount;
+
/**
* Creates a new XGraphics on the specified X Drawable.
*
* @param d the X Drawable for which we create the Graphics
*/
XGraphics(Drawable d)
{
xdrawable = d;
xgc = new GC(d);
translateX = 0;
translateY = 0;
clip = new Rectangle(0, 0, d.width, d.height);
+
+ Display display = xdrawable.display;
+ pixmapFormat = display.default_pixmap_format;
+ imageByteOrder = display.image_byte_order;
+ pixelByteCount = pixmapFormat.bits_per_pixel () / 8;
}
/**
* Creates an exact copy of this graphics context.
*
* @return an exact copy of this graphics context
*/
public Graphics create()
{
XGraphics copy = (XGraphics) clone();
return copy;
}
@@ -521,64 +536,136 @@
/**
* Draws the specified image on the drawable at position (x,y).
*/
public boolean drawImage(Image image, int x, int y, ImageObserver observer)
{
if (image instanceof XImage)
{
XImage xim = (XImage) image;
Pixmap pm = xim.pixmap;
xdrawable.copy_area(pm, xgc, 0, 0, pm.width, pm.height,
x + translateX, y + translateY);
}
-// else if (image instanceof BufferedImage)
-// {
-// BufferedImage bufferedImage = (BufferedImage) image;
-// Raster raster = bufferedImage.getData();
-// int w = bufferedImage.getWidth();
-// int h = bufferedImage.getHeight();
-// // Push data to X server.
-// ZPixmap zPixmap = new ZPixmap(xdrawable.display, w, h,
-// xdrawable.display.default_pixmap_format);
-// System.err.println("data buffer length: " + zPixmap.data.length);
-// int[] pixel = new int[4];
-// for (int tx = 0; tx < w; tx++)
-// {
-// for (int ty = 0; ty < h; ty++)
-// {
-// pixel = raster.getPixel(tx, ty, pixel);
-//// System.err.print("r: " + pixel[0]);
-//// System.err.print(", g: " + pixel[1]);
-//// System.err.println(", b: " + pixel[2]);
-// zPixmap.set_red(tx, ty, pixel[0]);
-// zPixmap.set_green(tx, ty, pixel[1]);
-// zPixmap.set_blue(tx, ty, pixel[2]);
-// }
-// }
-// xdrawable.put_image(xgc, zPixmap, x, y);
-// }
+ else if (image instanceof BufferedImage
+ && ((BufferedImage) image).getTransparency() != Transparency.OPAQUE)
+ {
+ BufferedImage bi = (BufferedImage) image;
+ int width = bi.getWidth();
+ int height = bi.getHeight();
+ Data img = xdrawable.image(x + translateX, y + translateY,
+ width, height, 0xFFFFFFFF, 2);
+
+ // Compute line byte count.
+ int lineBitCount = width * pixmapFormat.bits_per_pixel ();
+ int rem = lineBitCount % pixmapFormat.scanline_pad ();
+ int linePadCount = lineBitCount / pixmapFormat.scanline_pad ()
+ + (rem == 0 ? 0 : 1);
+ int lineByteCount = linePadCount * pixmapFormat.scanline_pad () / 8;
+
+ // Composite source and destination pixel data.
+ int[] trgb = new int[3]; // The device rgb pixels.
+ for (int yy = 0; yy < height; yy++)
+ {
+ for (int xx = 0; xx < width; xx++)
+ {
+ getRGB(xx, yy, img, trgb, lineByteCount);
+ int srgb = bi.getRGB(xx, yy);
+ float alpha = ((srgb >> 24) & 0xff) / 256F;
+ float tAlpha = 1.F - alpha;
+ int red = (srgb >> 16) & 0xFF;
+ int green = (srgb >> 8) & 0xFF;
+ int blue = (srgb) & 0xFF;
+ trgb[0] = (int) (trgb[0] * tAlpha + red * alpha);
+ trgb[1] = (int) (trgb[1] * tAlpha + green * alpha);
+ trgb[2] = (int) (trgb[2] * tAlpha + blue * alpha);
+ setRGB(xx, yy, img, trgb, lineByteCount);
+ }
+ }
+
+ // Now we have the transparent image composited onto the target
+ // Image, now we only must copy it to the Drawable.
+ ZPixmap pm = new ZPixmap(xdrawable.display);
+ pm.width = width;
+ pm.height = height;
+ pm.init();
+ System.arraycopy(img.data, 32, pm.data, 0, img.data.length - 32);
+ xdrawable.put_image(xgc, pm, x + translateX, y + translateY);
+ }
else
{
// Pre-render the image into an XImage.
ImageProducer source = image.getSource();
ImageConverter conv = new ImageConverter();
source.startProduction(conv);
XImage xim = conv.getXImage();
Pixmap pm = xim.pixmap;
xdrawable.copy_area(pm, xgc, 0, 0, pm.width, pm.height,
x + translateX, y + translateY);
}
return true;
}
+ /**
+ * Helper method to work around limitation in the current Escher impl.
+ *
+ * @param x the x position
+ * @param y the y position
+ * @param img the image data
+ * @param rgb an 3-size array that holds the rgb values on method exit
+ */
+ private void getRGB(int x, int y, Data img, int[] rgb, int lineByteCount)
+ {
+ // TODO: Does this also work on non-RGB devices?
+ int i = y * lineByteCount + pixelByteCount * x;
+ if (imageByteOrder == gnu.x11.image.Image.LSB_FIRST)
+ {//if (i >= 5716-33) System.err.println("lbc: " + lineByteCount + ", " + pixelByteCount);
+ rgb[2] = img.data[32 + i];
+ rgb[1] = img.data[32 + i + 1];
+ rgb[0] = img.data[32 + i + 2];
+ }
+ else
+ { // MSB_FIRST
+ rgb[0] = img.data[32 + i];
+ rgb[1] = img.data[32 + i + 1];
+ rgb[2] = img.data[32 + i + 2];
+ }
+
+ }
+
+ /**
+ * Helper method to work around limitation in the current Escher impl.
+ *
+ * @param x the x position
+ * @param y the y position
+ * @param img the image data
+ * @param rgb an 3-size array that holds the rgb values on method exit
+ */
+ private void setRGB(int x, int y, Data img, int[] rgb, int lineByteCount)
+ {
+ // TODO: Does this also work on non-RGB devices?
+ int i = y * lineByteCount + pixelByteCount * x;
+ if (imageByteOrder == gnu.x11.image.Image.LSB_FIRST)
+ {
+ img.data[32 + i] = (byte) rgb[2];
+ img.data[32 + i + 1] = (byte) rgb[1];
+ img.data[32 + i + 2] = (byte) rgb[0];
+ }
+ else
+ { // MSB_FIRST
+ img.data[32 + i] = (byte) rgb[0];
+ img.data[32 + i + 1] = (byte) rgb[1];
+ img.data[32 + i + 2] = (byte) rgb[2];
+ }
+ }
+
public boolean drawImage(Image image, int x, int y, int width, int height,
ImageObserver observer)
{
// FIXME: Implement this.
throw new UnsupportedOperationException("Not yet implemented");
}
public boolean drawImage(Image image, int x, int y, Color bgcolor,
ImageObserver observer)
{
// FIXME: Implement this.
throw new UnsupportedOperationException("Not yet implemented");
Index: gnu/java/awt/peer/x/XToolkit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/x/XToolkit.java,v
retrieving revision 1.5
diff -u -1 -2 -r1.5 XToolkit.java
--- gnu/java/awt/peer/x/XToolkit.java 18 Jul 2006 10:23:20 -0000 1.5
+++ gnu/java/awt/peer/x/XToolkit.java 20 Jul 2006 13:06:48 -0000
@@ -58,24 +58,25 @@
import java.awt.Label;
import java.awt.List;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.PopupMenu;
import java.awt.PrintJob;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
import java.awt.TextArea;
import java.awt.TextField;
+import java.awt.Transparency;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.im.InputMethodHighlight;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
@@ -519,29 +520,35 @@
catch (IOException ex)
{
image = createErrorImage();
}
return image;
}
private Image createImage(InputStream i)
throws IOException
{
Image image;
BufferedImage buffered = ImageIO.read(i);
- if (buffered != null)
+ // If the bufferedimage is opaque, then we can copy it over to an
+ // X Pixmap for faster drawing.
+ if (buffered != null && buffered.getTransparency() == Transparency.OPAQUE)
{
ImageProducer source = buffered.getSource();
image = createImage(source);
}
+ else if (buffered != null)
+ {
+ image = buffered;
+ }
else
{
image = createErrorImage();
}
return image;
}
public PrintJob getPrintJob(Frame frame, String title, Properties props)
{
// TODO: Implement this.
throw new UnsupportedOperationException("Not yet implemented.");
}