Alright, the first part of custom composites is attached and committed.
They should now work with the VolatileImageGraphics peer, with patches
for the other peers coming soon.
Cheers,
Francis
2006-10-03 Francis Kung <[EMAIL PROTECTED]>
* gnu/java/awt/peer/gtk/CairoGraphics2D.java
(compCtx): New field for composite context.
(copy): Copy composite.
(dispose): Dispose of composite context.
(getNativeCM): New method.
(setComposite): Discard old composite context and set up new context.
(setRenderingHints): Update composite context.
* gnu/java/awt/peer/gtk/CairoSurface.java
(nativeColorModel): New field, renamed from nativeModel.
(nativeModel): Renamed field to nativeColorModel.
(CairoSurface(int, int)): Call new method to create sample model.
(createNativeSampleModel): New method.
(getBufferedImage): Updated variable name.
* gnu/java/awt/peer/gtk/VolatileImageGraphics.java
(buffer): New field.
(createBuffer): New method.
(draw): New method.
(drawComposite): New method.
(drawGlyphVector): New method.
(drawImage(Image, AffineTransform, Color, ImageObserver)): New method.
(drawImage(Image, int, int, ImageObserver)): Check composite.
(drawImage(Image, int, int, int, int, ImageObserver)): Check composite.
(fill): New method.
(getNativeCM): New method.
* native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c
(Java_gnu_java_awt_peer_gtk_GtkVolatileImage_nativeGetPixels): Use
intermediary pixbuf to grab on-screen pixels.
Index: gnu/java/awt/peer/gtk/CairoSurface.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoSurface.java,v
retrieving revision 1.18
diff -u -r1.18 CairoSurface.java
--- gnu/java/awt/peer/gtk/CairoSurface.java 2 Oct 2006 21:36:21 -0000 1.18
+++ gnu/java/awt/peer/gtk/CairoSurface.java 3 Oct 2006 19:11:41 -0000
@@ -40,16 +40,16 @@
import gnu.java.awt.Buffers;
-import java.awt.Point;
import java.awt.Graphics2D;
+import java.awt.Point;
import java.awt.color.ColorSpace;
-import java.awt.image.DataBuffer;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
+import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
import java.nio.ByteOrder;
import java.util.Hashtable;
@@ -73,7 +73,7 @@
long bufferPointer;
// nativeGetPixels will return [0]=red, [1]=green, [2]=blue, [3]=alpha
- static ColorModel nativeModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
+ static ColorModel nativeColorModel = new DirectColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
32,
0x000000FF,
0x0000FF00,
@@ -147,10 +147,8 @@
*/
public CairoSurface(int width, int height)
{
- super( new SinglePixelPackedSampleModel
- (DataBuffer.TYPE_INT, width, height,
- new int[]{ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }),
- null, new Point(0, 0) );
+ super(createNativeSampleModel(width, height),
+ null, new Point(0, 0));
if(width <= 0 || height <= 0)
throw new IllegalArgumentException("Image must be at least 1x1 pixels.");
@@ -262,7 +260,7 @@
*/
public static BufferedImage getBufferedImage(CairoSurface surface)
{
- return new BufferedImage(nativeModel, surface, true, new Hashtable());
+ return new BufferedImage(nativeColorModel, surface, true, new Hashtable());
}
private class CairoDataBuffer extends DataBuffer
@@ -324,4 +322,14 @@
{
copyAreaNative2(bufferPointer, x, y, width, height, dx, dy, stride);
}
+
+ /**
+ * Creates a SampleModel that matches Cairo's native format
+ */
+ protected static SampleModel createNativeSampleModel(int w, int h)
+ {
+ return new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h,
+ new int[]{0x000000FF, 0x0000FF00,
+ 0x00FF0000, 0xFF000000});
+ }
}
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.41
diff -u -r1.41 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java 2 Oct 2006 21:36:21 -0000 1.41
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java 3 Oct 2006 19:11:40 -0000
@@ -45,6 +45,7 @@
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
+import java.awt.CompositeContext;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
@@ -163,6 +164,7 @@
* The current compositing context, if any.
*/
Composite comp;
+ CompositeContext compCtx;
/**
* Rendering hint map.
@@ -234,6 +236,7 @@
nativePointer = init(cairo_t_pointer);
paint = g.paint;
stroke = g.stroke;
+ comp = g.comp;
setRenderingHints(g.hints);
Color foreground;
@@ -268,6 +271,7 @@
setStroke(stroke);
setTransformImpl(transform);
setClip(clip);
+ setComposite(comp);
}
/**
@@ -288,6 +292,8 @@
{
disposeNative(nativePointer);
nativePointer = 0;
+ if (compCtx != null)
+ compCtx.dispose();
}
/**
@@ -951,7 +957,13 @@
*/
public void setComposite(Composite comp)
{
+ if (this.comp == comp)
+ return;
+
this.comp = comp;
+ if (compCtx != null)
+ compCtx.dispose();
+ compCtx = null;
if (comp == null)
comp = AlphaComposite.SrcOver;
@@ -961,6 +973,7 @@
AlphaComposite a = (AlphaComposite) comp;
cairoSetOperator(nativePointer, a.getRule());
}
+
else
{
// FIXME: this check is only required "if this Graphics2D
@@ -970,9 +983,26 @@
sm.checkPermission(new AWTPermission("readDisplayPixels"));
// FIXME: implement general Composite support
- throw new java.lang.UnsupportedOperationException();
+ //throw new java.lang.UnsupportedOperationException();
+ // this is in progress! yay!
+ compCtx = comp.createContext(getNativeCM(), getNativeCM(), hints);
}
}
+
+ /**
+ * Returns the Colour Model describing the native, raw image data for this
+ * specific peer.
+ *
+ * @return ColorModel the ColorModel of native data in this peer
+ */
+ /* protected abstract ColorModel getNativeCM(); */
+ protected ColorModel getNativeCM()
+ {
+ // This stub should be removed and the method made abstract once I'm done
+ // implementing custom composites across all the peers... but we need it
+ // for now, so that the build doesn't break.
+ return null;
+ }
///////////////////////// DRAWING PRIMITIVES ///////////////////////////////////
@@ -1237,6 +1267,12 @@
shiftDrawCalls = hints.containsValue(RenderingHints.VALUE_STROKE_NORMALIZE)
|| hints.containsValue(RenderingHints.VALUE_STROKE_DEFAULT);
+
+ if (compCtx != null)
+ {
+ compCtx.dispose();
+ compCtx = comp.createContext(getNativeCM(), getNativeCM(), this.hints);
+ }
}
public void addRenderingHints(Map hints)
Index: gnu/java/awt/peer/gtk/VolatileImageGraphics.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/VolatileImageGraphics.java,v
retrieving revision 1.7
diff -u -r1.7 VolatileImageGraphics.java
--- gnu/java/awt/peer/gtk/VolatileImageGraphics.java 17 Jul 2006 22:41:03 -0000 1.7
+++ gnu/java/awt/peer/gtk/VolatileImageGraphics.java 3 Oct 2006 19:11:41 -0000
@@ -38,15 +38,31 @@
package gnu.java.awt.peer.gtk;
+import java.awt.AlphaComposite;
+import java.awt.Color;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
+import java.awt.Point;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
+import java.awt.image.ImageProducer;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.util.Hashtable;
public class VolatileImageGraphics extends ComponentGraphics
{
private GtkVolatileImage owner;
+ private BufferedImage buffer;
public VolatileImageGraphics(GtkVolatileImage img)
{
@@ -77,10 +93,118 @@
return new VolatileImageGraphics( this );
}
+ public void draw(Shape s)
+ {
+ if (comp == null || comp instanceof AlphaComposite)
+ super.draw(s);
+
+ // Custom composite
+ else
+ {
+ // Draw operation to temporary buffer
+ createBuffer();
+
+ Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+ g2d.setColor(this.getColor());
+ g2d.setStroke(this.getStroke());
+ g2d.draw(s);
+
+ drawComposite(s.getBounds2D(), null);
+ }
+ }
+
+ public void fill(Shape s)
+ {
+ if (comp == null || comp instanceof AlphaComposite)
+ super.fill(s);
+
+ // Custom composite
+ else
+ {
+ // Draw operation to temporary buffer
+ createBuffer();
+
+ Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+ g2d.setPaint(this.getPaint());
+ g2d.setColor(this.getColor());
+ g2d.fill(s);
+
+ drawComposite(s.getBounds2D(), null);
+ }
+ }
+
+ public void drawGlyphVector(GlyphVector gv, float x, float y)
+ {
+ if (comp == null || comp instanceof AlphaComposite)
+ super.drawGlyphVector(gv, x, y);
+
+ // Custom composite
+ else
+ {
+ // Draw operation to temporary buffer
+ createBuffer();
+
+ Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+
+ g2d.setPaint(this.getPaint());
+ g2d.setColor(this.getColor());
+ g2d.drawGlyphVector(gv, x, y);
+
+ Rectangle2D bounds = gv.getLogicalBounds();
+ bounds = new Rectangle2D.Double(x + bounds.getX(), y + bounds.getY(),
+ bounds.getWidth(), bounds.getHeight());
+ drawComposite(bounds, null);
+ }
+ }
+
+ protected boolean drawImage(Image img, AffineTransform xform,
+ Color bgcolor, ImageObserver obs)
+ {
+ if (comp == null || comp instanceof AlphaComposite)
+ return super.drawImage(img, xform, bgcolor, obs);
+
+ // Custom composite
+ else
+ {
+ // Get buffered image of source
+ if( !(img instanceof BufferedImage) )
+ {
+ ImageProducer source = img.getSource();
+ if (source == null)
+ return false;
+ img = Toolkit.getDefaultToolkit().createImage(source);
+ }
+ BufferedImage bImg = (BufferedImage) img;
+
+ // Find dimensions of translation
+ Point2D origin = new Point2D.Double(bImg.getMinX(), bImg.getMinY());
+ Point2D pt = new Point2D.Double(bImg.getWidth(), bImg.getHeight());
+ if (xform != null)
+ {
+ origin = xform.transform(origin, origin);
+ pt = xform.transform(pt, pt);
+ }
+
+ // Create buffer and draw image
+ createBuffer();
+
+ Graphics2D g2d = (Graphics2D)buffer.getGraphics();
+ g2d.setRenderingHints(this.getRenderingHints());
+ g2d.drawImage(img, xform, obs);
+ // Perform compositing from buffer to screen
+ return drawComposite(new Rectangle2D.Double((int)origin.getX(),
+ (int)origin.getY(),
+ (int)pt.getX(),
+ (int)pt.getY()),
+ obs);
+ }
+ }
+
public boolean drawImage(Image img, int x, int y, ImageObserver observer)
{
- if( img instanceof GtkVolatileImage )
+ if (img instanceof GtkVolatileImage
+ && (comp == null || comp instanceof AlphaComposite))
{
owner.drawVolatile( ((GtkVolatileImage)img).nativePointer,
x, y,
@@ -94,7 +218,8 @@
public boolean drawImage(Image img, int x, int y, int width, int height,
ImageObserver observer)
{
- if( img instanceof GtkVolatileImage )
+ if ((img instanceof GtkVolatileImage)
+ && (comp == null || comp instanceof AlphaComposite))
{
owner.drawVolatile( ((GtkVolatileImage)img).nativePointer,
x, y, width, height );
@@ -107,5 +232,76 @@
{
return new Rectangle2D.Double(0, 0, owner.width, owner.height);
}
+
+ private boolean drawComposite(Rectangle2D bounds, ImageObserver observer)
+ {
+ // Clip source to visible areas that need updating
+ Rectangle2D clip = this.getClipBounds();
+ Rectangle2D.intersect(bounds, clip, bounds);
+
+ BufferedImage buffer2 = buffer;
+ if (!bounds.equals(buffer2.getRaster().getBounds()))
+ buffer2 = buffer2.getSubimage((int)bounds.getX(), (int)bounds.getY(),
+ (int)bounds.getWidth(),
+ (int)bounds.getHeight());
+
+ // Get current on-screen pixels (destination) and clip to bounds
+ BufferedImage current = owner.getSnapshot();
+
+ double[] points = new double[] {bounds.getX(), bounds.getY(),
+ bounds.getMaxX(), bounds.getMaxY()};
+ transform.transform(points, 0, points, 0, 2);
+
+ Rectangle2D deviceBounds = new Rectangle2D.Double(points[0], points[1],
+ points[2] - points[0],
+ points[3] - points[1]);
+ Rectangle2D.intersect(deviceBounds, this.getClipInDevSpace(), deviceBounds);
+
+ current = current.getSubimage((int)deviceBounds.getX(),
+ (int)deviceBounds.getY(),
+ (int)deviceBounds.getWidth(),
+ (int)deviceBounds.getHeight());
+
+ // Perform actual composite operation
+ compCtx.compose(buffer2.getRaster(), current.getRaster(),
+ buffer2.getRaster());
+
+ // This MUST call directly into the "action" method in CairoGraphics2D,
+ // not one of the wrappers, to ensure that the composite isn't processed
+ // more than once!
+ boolean rv = super.drawImage(buffer2,
+ AffineTransform.getTranslateInstance(bounds.getX(),
+ bounds.getY()),
+ null, null);
+
+ return rv;
+ }
+
+ private void createBuffer()
+ {
+ if (buffer == null)
+ {
+ WritableRaster rst;
+ rst = Raster.createWritableRaster(CairoSurface.
+ createNativeSampleModel(owner.width,
+ owner.height),
+ new Point(0,0));
+
+ buffer = new BufferedImage(CairoSurface.nativeColorModel, rst, true,
+ new Hashtable());
+ }
+ else
+ {
+ Graphics2D g2d = ((Graphics2D)buffer.getGraphics());
+
+ g2d.setBackground(new Color(0,0,0,0));
+ g2d.clearRect(0, 0, buffer.getWidth(), buffer.getHeight());
+ }
+ }
+
+ protected ColorModel getNativeCM()
+ {
+ return CairoSurface.nativeColorModel;
+ }
}
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c,v
retrieving revision 1.7
diff -u -r1.7 gnu_java_awt_peer_gtk_GtkVolatileImage.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c 5 Sep 2006 21:27:28 -0000 1.7
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkVolatileImage.c 3 Oct 2006 19:11:41 -0000
@@ -110,10 +110,12 @@
/* jint *pixeldata, *jpixdata; */
jint *jpixdata;
GdkPixmap *pixmap;
+ GdkPixbuf *pixbuf;
jintArray jpixels;
int width, height, depth, size;
jclass cls;
jfieldID field;
+ guchar *pixels;
cls = (*env)->GetObjectClass (env, obj);
field = (*env)->GetFieldID (env, cls, "width", "I");
@@ -131,11 +133,19 @@
/* get depth in bytes */
depth = gdk_drawable_get_depth( pixmap ) >> 3;
- size = width * height * 4;
+ size = width * height;
jpixels = (*env)->NewIntArray ( env, size );
jpixdata = (*env)->GetIntArrayElements (env, jpixels, NULL);
- /* memcpy (jpixdata, pixeldata, size * sizeof( jint )); */
-
+
+ pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE, 8, width, height );
+ gdk_pixbuf_get_from_drawable( pixbuf, pixmap, NULL, 0, 0, 0, 0, width, height );
+
+ if (pixbuf != NULL)
+ {
+ pixels = gdk_pixbuf_get_pixels(pixbuf);
+ memcpy (jpixdata, pixels, size * sizeof(jint));
+ }
+
(*env)->ReleaseIntArrayElements (env, jpixels, jpixdata, 0);
gdk_threads_leave();