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.");
   }

Reply via email to