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

Reply via email to