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); +}