Hi,
The attached patch, committed, includes a number of fixes to our
handling of custom paint contexts.
This significantly improves our compliance to the Batik test-suite.
I've also included some drastic performance enhancements when doing
java2d operations with a custom paint context; our performance on some
Batik tests is now comparable to Sun's.
Cheers,
Francis
2006-11-21 Francis Kung <[EMAIL PROTECTED]>
* gnu/java/awt/java2d/TexturePaintContext.java
(getRaster): Handle negative coordinate values.
* gnu/java/awt/peer/gtk/CairoGraphics2D.java
(setPaint): Moved custom paint processing to a new method.
(setPaintPixels): Added x, y parameters.
(getRealBounds): Added documentation.
(copy): Copy clipping information.
(drawLine): Process custom paints.
(setCustomPaint): New method.
(fill): Process custom paints.
(drawGlyphVector): Process custom paints.
(drawRect): Process custom paints.
(draw): Process custom paints.
* gnu/java/awt/peer/gtk/CairoSurface.java
(cairoCM_opaque): New constant.
* gnu/java/awt/peer/gtk/BufferedImageGraphics.java
(argb32): Removed constant.
(rgb32): Removed constant.
(BufferedImageGraphics(BufferedImage)): Updated constant names.
(BufferedImageGraphics(BufferedImageGraphics)): Copy color model flags.
(updateBufferedImage): Transform to device-space before updating.
* include/gnu_java_awt_peer_gtk_CairoGraphics2D.h
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setPaintPixels): Added x, y
parameters.
* native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setPaintPixels): Set
pattern
source at designated x, y origin.
------------------------------------------------------------------------
Index: gnu/java/awt/peer/gtk/BufferedImageGraphics.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/BufferedImageGraphics.java,v
retrieving revision 1.15
diff -u -r1.15 BufferedImageGraphics.java
--- gnu/java/awt/peer/gtk/BufferedImageGraphics.java 19 Oct 2006 20:50:52
-0000 1.15
+++ gnu/java/awt/peer/gtk/BufferedImageGraphics.java 21 Nov 2006 16:21:15
-0000
@@ -54,7 +54,6 @@
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
-import java.awt.image.DirectColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.Raster;
@@ -100,12 +99,6 @@
*/
private long cairo_t;
- /**
- * Colormodels we recognize for fast copying.
- */
- static ColorModel rgb32 = new DirectColorModel(24, 0xFF0000, 0xFF00, 0xFF);
- static ColorModel argb32 = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF,
- 0xFF000000);
private boolean hasFastCM;
private boolean hasAlpha;
@@ -117,12 +110,12 @@
imageHeight = bi.getHeight();
locked = false;
- if(bi.getColorModel().equals(rgb32))
+ if(bi.getColorModel().equals(CairoSurface.cairoCM_opaque))
{
hasFastCM = true;
hasAlpha = false;
}
- else if(bi.getColorModel().equals(argb32))
+ else if(bi.getColorModel().equals(CairoSurface.cairoColorModel))
{
hasFastCM = true;
hasAlpha = true;
@@ -176,8 +169,11 @@
imageWidth = copyFrom.imageWidth;
imageHeight = copyFrom.imageHeight;
locked = false;
+
+ hasFastCM = copyFrom.hasFastCM;
+ hasAlpha = copyFrom.hasAlpha;
+
copy( copyFrom, cairo_t );
- setClip(0, 0, surface.width, surface.height);
}
/**
@@ -188,6 +184,13 @@
if (locked)
return;
+ double[] points = new double[]{x, y, width+x, height+y};
+ transform.transform(points, 0, points, 0, 2);
+ x = (int)points[0];
+ y = (int)points[1];
+ width = (int)(points[2] - x);
+ height = (int)(points[3] - y);
+
int[] pixels = surface.getPixels(imageWidth * imageHeight);
if( x > imageWidth || y > imageHeight )
Index: gnu/java/awt/peer/gtk/CairoSurface.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoSurface.java,v
retrieving revision 1.21
diff -u -r1.21 CairoSurface.java
--- gnu/java/awt/peer/gtk/CairoSurface.java 18 Oct 2006 19:00:31 -0000
1.21
+++ gnu/java/awt/peer/gtk/CairoSurface.java 21 Nov 2006 16:21:16 -0000
@@ -88,6 +88,11 @@
0xFF000000,
true,
Buffers.smallestAppropriateTransferType(32));
+
+ // This CM corresponds to the CAIRO_FORMAT_RGB24 type in Cairo
+ static ColorModel cairoCM_opaque = new DirectColorModel(24, 0x00FF0000,
+ 0x0000FF00,
+ 0x000000FF);
/**
* Allocates and clears the buffer and creates the cairo surface.
* @param width, height - the image size
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.50
diff -u -r1.50 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java 21 Nov 2006 14:14:26 -0000
1.50
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java 21 Nov 2006 16:21:16 -0000
@@ -134,6 +134,7 @@
* The current paint
*/
Paint paint;
+ boolean customPaint;
/**
* The current stroke
@@ -255,6 +256,8 @@
bg = new Color(g.bg.getRGB());
}
+ firstClip = g.firstClip;
+ originalClip = g.originalClip;
clip = g.getClip();
if (g.transform == null)
@@ -311,6 +314,11 @@
int width, int height, int dx, int dy);
+ /**
+ * Find the bounds of this graphics context, in device space.
+ *
+ * @return the bounds in device-space
+ */
protected abstract Rectangle2D getRealBounds();
////// Native Methods ////////////////////////////////////////////////////
@@ -336,7 +344,8 @@
int g2, int b2, int a2, boolean cyclic);
private native void setPaintPixels(long pointer, int[] pixels, int w,
- int h, int stride, boolean repeat);
+ int h, int stride, boolean repeat,
+ int x, int y);
/**
* Set the current transform matrix
@@ -691,6 +700,7 @@
if (paint instanceof Color)
{
setColor((Color) paint);
+ customPaint = false;
}
else if (paint instanceof TexturePaint)
{
@@ -708,7 +718,8 @@
AffineTransformOp op = new AffineTransformOp(at, getRenderingHints());
BufferedImage texture = op.filter(img, null);
int[] pixels = texture.getRGB(0, 0, width, height, null, 0, width);
- setPaintPixels(nativePointer, pixels, width, height, width, true);
+ setPaintPixels(nativePointer, pixels, width, height, width, true, 0, 0);
+ customPaint = false;
}
else if (paint instanceof GradientPaint)
{
@@ -721,36 +732,90 @@
c1.getRed(), c1.getGreen(), c1.getBlue(), c1.getAlpha(),
c2.getRed(), c2.getGreen(), c2.getBlue(), c2.getAlpha(),
gp.isCyclic());
+ customPaint = false;
}
else
{
- // Get bounds in device space
- int minX = 0;
- int minY = 0;
- int width = (int)getRealBounds().getWidth();
- int height = (int)getRealBounds().getHeight();
-
- Point2D origin = transform.transform(new Point2D.Double(minX, minY),
- null);
- Point2D extreme = transform.transform(new Point2D.Double(width + minX,
- height +
minY),
- null);
- minX = (int)origin.getX();
- minY = (int)origin.getY();
- width = (int)extreme.getX() - minX;
- height = (int)extreme.getY() - minY;
-
- // Get raster of the paint background
- PaintContext pc = paint.createContext(ColorModel.getRGBdefault(),
- new Rectangle(minX, minY,
- width, height),
- getRealBounds(),
- transform, hints);
+ customPaint = true;
+ }
+ }
+
+ /**
+ * Sets a custom paint
+ *
+ * @param bounds the bounding box, in user space
+ */
+ protected void setCustomPaint(Rectangle bounds)
+ {
+ if (paint instanceof Color || paint instanceof TexturePaint
+ || paint instanceof GradientPaint)
+ return;
+
+ int userX = bounds.x;
+ int userY = bounds.y;
+ int userWidth = bounds.width;
+ int userHeight = bounds.height;
+
+ // Find bounds in device space
+ Point2D origin = transform.transform(new Point2D.Double(userX, userY),
+ null);
+ Point2D extreme = transform.transform(new Point2D.Double(userWidth + userX,
+ userHeight +
userY),
+ null);
+ int deviceX = (int)origin.getX();
+ int deviceY = (int)origin.getY();
+ int deviceWidth = (int)extreme.getX() - deviceX;
+ int deviceHeight = (int)extreme.getY() - deviceY;
+
+ // Get raster of the paint background
+ PaintContext pc = paint.createContext(CairoSurface.cairoColorModel,
+ new Rectangle(deviceX, deviceY,
+ deviceWidth,
+ deviceHeight),
+ bounds,
+ transform, hints);
+
+ Raster raster = pc.getRaster(deviceX, deviceY, deviceWidth,
+ deviceHeight);
+
+ // Clear the transform matrix in Cairo, since the raster returned by the
+ // PaintContext is already in device-space
+ AffineTransform oldTx = new AffineTransform(transform);
+ setTransformImpl(new AffineTransform());
+
+ // Set pixels in cairo, aligning the top-left of the background image
+ // to the top-left corner in device space
+ if (pc.getColorModel().equals(CairoSurface.cairoColorModel)
+ && raster.getSampleModel().getTransferType() == DataBuffer.TYPE_INT)
+ {
+ // Use a fast copy if the paint context can uses a Cairo-compatible
+ // color model
+ setPaintPixels(nativePointer,
+ (int[])raster.getDataElements(0, 0, deviceWidth,
+ deviceHeight, null),
+ deviceWidth, deviceHeight, deviceWidth, false,
+ deviceX, deviceY);
+ }
+
+ else if (pc.getColorModel().equals(CairoSurface.cairoCM_opaque)
+ && raster.getSampleModel().getTransferType() ==
DataBuffer.TYPE_INT)
+ {
+ // We can also optimize if the context uses a similar color model
+ // but without an alpha channel; we just add the alpha
+ int[] pixels = (int[])raster.getDataElements(0, 0, deviceWidth,
+ deviceHeight, null);
- Raster raster = pc.getRaster(minX, minY, width, height);
+ for (int i = 0; i < pixels.length; i++)
+ pixels[i] = 0xff000000 | (pixels[i] & 0x00ffffff);
- // Work around colorspace issues, and force use of the
- // BufferedImage.getRGB method... this can be improved upon.
+ setPaintPixels(nativePointer, pixels, deviceWidth, deviceHeight,
+ deviceWidth, false, deviceX, deviceY);
+ }
+
+ else
+ {
+ // Fall back on wrapping the raster in a BufferedImage, and
+ // use BufferedImage.getRGB() to do color-model conversion
WritableRaster wr = Raster.createWritableRaster(raster.getSampleModel(),
new
Point(raster.getMinX(),
raster.getMinY()));
@@ -760,15 +825,15 @@
pc.getColorModel().isAlphaPremultiplied(),
null);
- // Set pixels in cairo
setPaintPixels(nativePointer,
- img2.getRGB(0, 0, width, height, null, 0, width),
- width, height, width, false);
- // setPaintPixels(nativePointer,
- // raster.getPixels(0, 0, width, height, (int[])null),
- // width, height, width, false);
- // doesn't work... but would be much more efficient!
+ img2.getRGB(0, 0, deviceWidth, deviceHeight, null, 0,
+ deviceWidth),
+ deviceWidth, deviceHeight, deviceWidth, false,
+ deviceX, deviceY);
}
+
+ // Restore transform
+ setTransformImpl(oldTx);
}
public Stroke getStroke()
@@ -1023,6 +1088,9 @@
return;
}
+ if (customPaint)
+ setCustomPaint(s.getBounds());
+
createPath(s, true);
cairoStroke(nativePointer);
}
@@ -1031,6 +1099,9 @@
{
createPath(s, false);
+ if (customPaint)
+ setCustomPaint(s.getBounds());
+
double alpha = 1.0;
if (comp instanceof AlphaComposite)
alpha = ((AlphaComposite) comp).getAlpha();
@@ -1132,6 +1203,9 @@
// The coordinates being pairwise identical means one wants
// to draw a single pixel. This is emulated by drawing
// a one pixel sized rectangle.
+ if (customPaint)
+ setCustomPaint(new Rectangle(x1, y1, x2 - x1, y2 - y1));
+
if (x1 == x2 && y1 == y2)
cairoFillRect(nativePointer, x1, y1, 1, 1);
else
@@ -1140,6 +1214,9 @@
public void drawRect(int x, int y, int width, int height)
{
+ if (customPaint)
+ setCustomPaint(new Rectangle(x, y, width, height));
+
cairoDrawRect(nativePointer, shifted(x, shiftDrawCalls),
shifted(y, shiftDrawCalls), width, height);
}
@@ -1550,6 +1627,9 @@
if( gv.getNumGlyphs() <= 0 )
return;
+ if (customPaint)
+ setCustomPaint(gv.getOutline().getBounds());
+
if (comp instanceof AlphaComposite)
alpha = ((AlphaComposite) comp).getAlpha();
if (gv instanceof FreetypeGlyphVector && alpha == 1.0)
@@ -1908,4 +1988,4 @@
return rect;
}
-}
+}
\ No newline at end of file
Index: include/gnu_java_awt_peer_gtk_CairoGraphics2D.h
===================================================================
RCS file:
/cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_CairoGraphics2D.h,v
retrieving revision 1.13
diff -u -r1.13 gnu_java_awt_peer_gtk_CairoGraphics2D.h
--- include/gnu_java_awt_peer_gtk_CairoGraphics2D.h 25 Oct 2006 21:07:59
-0000 1.13
+++ include/gnu_java_awt_peer_gtk_CairoGraphics2D.h 21 Nov 2006 16:21:16
-0000
@@ -14,7 +14,7 @@
JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_disposeNative (JNIEnv *env, jobject,
jlong);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_drawPixels
(JNIEnv *env, jobject, jlong, jintArray, jint, jint, jint, jdoubleArray,
jdouble, jint);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setGradient
(JNIEnv *env, jobject, jlong, jdouble, jdouble, jdouble, jdouble, jint, jint,
jint, jint, jint, jint, jint, jint, jboolean);
-JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setPaintPixels (JNIEnv *env,
jobject, jlong, jintArray, jint, jint, jint, jboolean);
+JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setPaintPixels (JNIEnv *env,
jobject, jlong, jintArray, jint, jint, jint, jboolean, jint, jint);
JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoSetMatrix (JNIEnv *env,
jobject, jlong, jdoubleArray);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoScale
(JNIEnv *env, jobject, jlong, jdouble, jdouble);
JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoSetOperator (JNIEnv *env,
jobject, jlong, jint);
Index: gnu/java/awt/java2d/TexturePaintContext.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/awt/java2d/TexturePaintContext.java,v
retrieving revision 1.2
diff -u -r1.2 TexturePaintContext.java
--- gnu/java/awt/java2d/TexturePaintContext.java 12 Sep 2006 19:05:38
-0000 1.2
+++ gnu/java/awt/java2d/TexturePaintContext.java 21 Nov 2006 16:21:15
-0000
@@ -177,6 +177,12 @@
// The modulo operation gives us the replication effect.
dx = ((dx - minX) % width) + minX;
dy = ((dy - minY) % height) + minY;
+
+ // Handle possible negative values (replicating above the top-left)
+ if (dx < 0)
+ dx += width;
+ if (dy < 0)
+ dy += height;
// Copy the pixel.
pixel = source.getDataElements(dx, dy, pixel);
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c
===================================================================
RCS file:
/cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c,v
retrieving revision 1.18
diff -u -r1.18 gnu_java_awt_peer_gtk_CairoGraphics2D.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c 23 Oct 2006
19:55:38 -0000 1.18
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c 21 Nov 2006
16:21:16 -0000
@@ -148,7 +148,8 @@
JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_setPaintPixels
(JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)),
- jlong pointer, jintArray jarr, jint w, jint h, jint stride, jboolean repeat)
+ jlong pointer, jintArray jarr, jint w, jint h, jint stride, jboolean repeat,
+ jint x, jint y)
{
struct cairographics2d *gr = NULL;
jint *jpixels = NULL;
@@ -184,12 +185,12 @@
gr->pattern = cairo_pattern_create_for_surface (gr->pattern_surface);
g_assert (gr->pattern != NULL);
+ cairo_set_source_surface(gr->cr, gr->pattern_surface, x, y);
+
if (repeat)
- cairo_pattern_set_extend (gr->pattern, CAIRO_EXTEND_REPEAT);
+ cairo_pattern_set_extend(cairo_get_source(gr->cr), CAIRO_EXTEND_REPEAT);
else
- cairo_pattern_set_extend (gr->pattern, CAIRO_EXTEND_NONE);
-
- cairo_set_source (gr->cr, gr->pattern);
+ cairo_pattern_set_extend(cairo_get_source(gr->cr), CAIRO_EXTEND_NONE);
}
JNIEXPORT void JNICALL