Hey,
Attached is an updated patch to fix the alpha composite stroking bug.
It works by drawing the stroked shape (using cairo's stroke function) on
a new surface, then using this surface as an alpha mask. Only the mask
and paint functions in cairo let us specify an alpha value.
Currently (pre-patch), we attempt to stroke the ship ourselves before
passing it to cairo to paint, but this is buggy and also results in a
lot of JNI overhead. The masking solution avoids most of the JNI, at
the expense of creating a new cairo surface for each stroke-with-alpha
operation in native code (I can't find any way to clear or reset a
surface between operations).
Cheers,
Francis
2006-07-12 Francis Kung <[EMAIL PROTECTED]>
* gnu/java/awt/peer/gtk/CairoGraphics2D.java
(cairoStroke): Added parameter for alpha value.
(copy): Pass surface size into init().
(drawPixels): API documentation clean-up.
(draw): Do stroking in native code when transparency is set.
(init): Add parameters for surface size.
(setup): Pass surface size into init().
* include/gnu_java_awt_peer_gtk_CairoGraphics2D.h: Added
function declarations.
* native/jni/gtk-peer/cairographics2d.h
(cairographics2d): Added fields for the alpha mask.
* native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoSetDash):
Save dash setting in alpha mask.
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoStroke):
Use alpha mask for transparent stroking.
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_disposeNative):
Destroy alpha mask.
(Java_gnu_java_awt_peer_gtk_CairoGraphics2D_init): Add
parameters for surface size and initialize alpha mask.
Index: gnu/java/awt/peer/gtk/CairoGraphics2D.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/CairoGraphics2D.java,v
retrieving revision 1.30
diff -u -r1.30 CairoGraphics2D.java
--- gnu/java/awt/peer/gtk/CairoGraphics2D.java 12 Jul 2006 20:28:43 -0000 1.30
+++ gnu/java/awt/peer/gtk/CairoGraphics2D.java 12 Jul 2006 20:51:04 -0000
@@ -202,7 +202,8 @@
*/
public void setup(long cairo_t_pointer)
{
- nativePointer = init(cairo_t_pointer);
+ nativePointer = init(cairo_t_pointer, getRealBounds().getWidth(),
+ getRealBounds().getWidth());
setRenderingHints(new RenderingHints(getDefaultHints()));
font = new Font("SansSerif", Font.PLAIN, 12);
setColor(Color.black);
@@ -217,7 +218,8 @@
*/
public void copy(CairoGraphics2D g, long cairo_t_pointer)
{
- nativePointer = init(cairo_t_pointer);
+ nativePointer = init(cairo_t_pointer, getRealBounds().getWidth(),
+ getRealBounds().getWidth());
paint = g.paint;
stroke = g.stroke;
setRenderingHints(g.hints);
@@ -279,8 +281,10 @@
/**
* Allocate the cairographics2d structure and set the cairo_t pointer in it.
* @param pointer - a cairo_t pointer, casted to a long.
+ * @param x the width of the surface
+ * @param y the height of the surface
*/
- private native long init(long pointer);
+ private native long init(long pointer, double x, double y);
/**
* These are declared abstract as there may be context-specific issues.
@@ -304,7 +308,8 @@
/**
* Draw pixels as an RGBA int matrix
- * @param w, h - width and height
+ * @param w - width
+ * @param h - height
* @param stride - stride of the array width
* @param i2u - affine transform array
*/
@@ -418,7 +423,7 @@
/**
* Stroke current path
*/
- private native void cairoStroke(long pointer);
+ private native void cairoStroke(long pointer, double alpha);
/**
* Fill current path
@@ -920,26 +925,18 @@
public void draw(Shape s)
{
- if ((stroke != null && ! (stroke instanceof BasicStroke))
- || (comp instanceof AlphaComposite
- && ((AlphaComposite) comp).getAlpha() != 1.0))
- {
- // FIXME: This is a hack to work around BasicStrokes's current
- // limitations wrt cubic curves.
- // See CubicSegment.getDisplacedSegments().
- if (stroke instanceof BasicStroke)
- {
- PathIterator flatten = s.getPathIterator(null, 1.0);
- GeneralPath p = new GeneralPath();
- p.append(flatten, false);
- s = p;
- }
+ if (stroke != null && ! (stroke instanceof BasicStroke))
+ {
fill(stroke.createStrokedShape(s));
return;
}
createPath(s);
- cairoStroke(nativePointer);
+
+ double alpha = 1.0;
+ if (comp instanceof AlphaComposite)
+ alpha = ((AlphaComposite) comp).getAlpha();
+ cairoStroke(nativePointer, alpha);
}
public void fill(Shape s)
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.8
diff -u -r1.8 gnu_java_awt_peer_gtk_CairoGraphics2D.h
--- include/gnu_java_awt_peer_gtk_CairoGraphics2D.h 12 Jul 2006 20:28:43 -0000 1.8
+++ include/gnu_java_awt_peer_gtk_CairoGraphics2D.h 12 Jul 2006 20:51:05 -0000
@@ -10,7 +10,7 @@
{
#endif
-JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_init (JNIEnv *env, jobject, jlong);
+JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_init (JNIEnv *env, jobject, jlong, jdouble, jdouble);
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);
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);
@@ -35,7 +35,7 @@
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoLineTo (JNIEnv *env, jobject, jlong, jdouble, jdouble);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoRelLineTo (JNIEnv *env, jobject, jlong, jdouble, jdouble);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoCurveTo (JNIEnv *env, jobject, jlong, jdouble, jdouble, jdouble, jdouble, jdouble, jdouble);
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoStroke (JNIEnv *env, jobject, jlong);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoStroke (JNIEnv *env, jobject, jlong, jdouble);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoFill (JNIEnv *env, jobject, jlong, jdouble);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoClip (JNIEnv *env, jobject, jlong);
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoPreserveClip (JNIEnv *env, jobject, jlong);
Index: native/jni/gtk-peer/cairographics2d.h
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/cairographics2d.h,v
retrieving revision 1.4
diff -u -r1.4 cairographics2d.h
--- native/jni/gtk-peer/cairographics2d.h 10 Jun 2006 14:16:09 -0000 1.4
+++ native/jni/gtk-peer/cairographics2d.h 12 Jul 2006 20:51:07 -0000
@@ -103,7 +103,7 @@
/**
* A structure which basically contains the cairo_t pointer.
- * The rest is for gradient and texture fills.
+ * The rest is for gradient and texture fills, and transparent strokes.
*/
struct cairographics2d
{
@@ -111,6 +111,9 @@
cairo_surface_t *pattern_surface;
cairo_pattern_t *pattern;
char *pattern_pixels;
+ cairo_t *alpha_mask;
+ double x;
+ double y;
};
#endif
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.12
diff -u -r1.12 gnu_java_awt_peer_gtk_CairoGraphics2D.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c 12 Jul 2006 20:28:43 -0000 1.12
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_CairoGraphics2D.c 12 Jul 2006 20:51:07 -0000
@@ -51,6 +51,7 @@
static void install_font_peer(cairo_t *cr, struct peerfont *pfont);
static void update_pattern_transform (struct cairographics2d *gr);
+static void create_alpha_mask (struct cairographics2d *gr);
/**
* Allocates the cairographics2d structure.
@@ -59,7 +60,7 @@
Java_gnu_java_awt_peer_gtk_CairoGraphics2D_init
(JNIEnv *env __attribute__ ((unused)),
jobject obj __attribute__ ((unused)),
- jlong cairo_t_pointer)
+ jlong cairo_t_pointer, jdouble x, jdouble y)
{
struct cairographics2d *g = NULL;
cairo_t *cr = JLONG_TO_PTR(cairo_t, cairo_t_pointer);
@@ -70,7 +71,11 @@
g_assert (g != NULL);
memset (g, 0, sizeof(struct cairographics2d));
g->cr = cr;
-
+
+ g->alpha_mask = NULL;
+ g->x = x;
+ g->y = y;
+
return PTR_TO_JLONG(g);
}
@@ -102,6 +107,10 @@
g_free(gr->pattern_pixels);
gr->pattern_pixels = NULL;
+ if (gr->alpha_mask)
+ cairo_destroy(gr->alpha_mask);
+ gr->alpha_mask = NULL;
+
g_free( gr );
}
@@ -480,6 +489,10 @@
cairo_set_dash (gr->cr, dasharr, ndash, offset);
+ if (gr->alpha_mask == NULL)
+ create_alpha_mask(gr);
+
+ cairo_set_dash(gr->alpha_mask, dasharr, ndash, offset);
(*env)->ReleaseDoubleArrayElements (env, dashes, dasharr, 0);
}
@@ -616,14 +629,39 @@
}
JNIEXPORT void JNICALL
-Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoStroke
+Java_gnu_java_awt_peer_gtk_CairoGraphics2D_cairoStroke
(JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)),
- jlong pointer)
+ jlong pointer, jdouble alpha)
{
+ cairo_path_t *path = NULL;
+
struct cairographics2d *gr = JLONG_TO_PTR(struct cairographics2d, pointer);
g_assert (gr != NULL);
- cairo_stroke (gr->cr);
+ if (alpha == 1.0)
+ cairo_stroke(gr->cr);
+ else
+ {
+ if (gr->alpha_mask == NULL)
+ create_alpha_mask(gr);
+
+ cairo_set_antialias(gr->alpha_mask, cairo_get_antialias(gr->cr));
+ cairo_set_line_cap(gr->alpha_mask, cairo_get_line_cap(gr->cr));
+ cairo_set_line_join(gr->alpha_mask, cairo_get_line_join(gr->cr));
+ cairo_set_miter_limit(gr->alpha_mask, cairo_get_miter_limit(gr->cr));
+ cairo_set_line_width(gr->alpha_mask, cairo_get_line_width(gr->cr));
+
+ cairo_set_source_rgba (gr->alpha_mask, 1, 1, 1, alpha);
+ path = cairo_copy_path(gr->cr);
+ cairo_append_path(gr->alpha_mask, path);
+ cairo_path_destroy(path);
+
+ cairo_stroke(gr->alpha_mask);
+ cairo_mask_surface(gr->cr, cairo_get_target(gr->alpha_mask), 0, 0);
+
+ cairo_destroy(gr->alpha_mask);
+ gr->alpha_mask = NULL;
+ }
}
JNIEXPORT void JNICALL
@@ -800,3 +838,15 @@
cairo_pattern_set_matrix (gr->pattern, &mat);
}
+static void
+create_alpha_mask (struct cairographics2d *gr)
+{
+ cairo_surface_t *surface = NULL;
+ g_assert (gr != NULL);
+
+ surface = cairo_surface_create_similar(cairo_get_target(gr->cr),
+ CAIRO_CONTENT_ALPHA,
+ gr->x, gr->y);
+ gr->alpha_mask = cairo_create(surface);
+ cairo_surface_destroy(surface);
+}