[cairo] Any chance to get trapezoids into public API?

Clemens Eisserer linuxhippy at gmail.com
Sat Oct 31 03:47:22 PDT 2009


Hello,

I am writing a Java2D plugin, which allows to use Cairo's tesselator+
pixman's trapezoid-rasterizer for antialiased rendering. It works
quite well, however it uses a lot of cairo-internal stuff like:

_cairo_path_fixed_stroke_to_traps, _cairo_path_fixed_fill_to_traps,
cairo_path_fixed_t, cairo_path_buf_t, cairo_traps_t,
cairo_stroke_style_t

I know by completly relying on cairo, I wouldn't have to mess with its
internals - however there is a certain impedance mismatch between
Java2D and Cairo and thats why I choose to use xrender back then.

The ideas I have to deal with this situation would be:

- Get that structures/functions public. I guess thats unlikely to
happen, as its backend-specific.
Would it be possible to some extents?

- Fork cairo specifically for this project (just call it different),
compile it to a shared library and check at run-time wether its
available or not. (fallbacks are available).
This would guarantee compatibility with all the assumptions the binding makes.

How do you think should this be solved?

Thanks, Clemens

PS: The file attached is basically the binding.
-------------- next part --------------
#include <cairo.h>
#include <config.h>
#include <X11/extensions/Xrender.h>
#include <cairoint.h>
#include <cairo-path-fixed-private.h>
#include <jni.h>
#include <jlong.h>
#include <pixman.h>
#include <math.h>

jintArray tesselate(JNIEnv *env, jintArray pointArray, jbyteArray opArray, jint pointCnt, jint opCnt, jintArray xTrapArray, jint xTrapArrayLength, jint windingRule, cairo_stroke_style_t *strokeStyle, cairo_matrix_t *tr, jint clipLowX, jint clipLowY, jint clipHiX, jint clipHiY) {
   jint* points;
   jbyte* ops;

   if((points = (jint *) (*env)->GetPrimitiveArrayCritical(env, pointArray, NULL)) == NULL) {
     return xTrapArray;
   }
   if((ops = (jbyte *) (*env)->GetPrimitiveArrayCritical(env, opArray, NULL)) == NULL) {
     return xTrapArray;
   }
   
   cairo_traps_t traps;
   _cairo_traps_init (&traps);

   traps.has_limits = 1;
   traps.limits.p1.x = _cairo_fixed_from_double(clipLowX);
   traps.limits.p1.y = _cairo_fixed_from_double(clipLowY);
   traps.limits.p2.x = _cairo_fixed_from_double(clipHiX);
   traps.limits.p2.y = _cairo_fixed_from_double(clipHiY);
   
   cairo_path_buf_t buf;
   cairo_path_fixed_t path;
   buf.num_ops = opCnt;
   buf.num_points = pointCnt;
   buf.buf_size = pointCnt*2;
   buf.op = (unsigned char*) ops;
   buf.points = points;
   buf.next = NULL;
   buf.prev = &path.buf_head;
   
   path.has_current_point = 0;
   path.has_curve_to = 1;
   path.buf_tail = &buf;
   path.buf_head.base.next = &buf;
   path.buf_head.base.num_ops = 0;
   path.buf_head.base.num_points = 0;
   path.buf_head.base.buf_size = 0;
   
   if(strokeStyle == NULL) {
     _cairo_path_fixed_fill_to_traps (&path, windingRule, 0.1, &traps);
   }else {
      cairo_matrix_t tr_inv = *tr;
      cairo_matrix_invert(&tr_inv);
     _cairo_path_fixed_stroke_to_traps (&path, strokeStyle, tr, &tr_inv, 0.1, &traps);
   }

   (*env)->ReleasePrimitiveArrayCritical(env, pointArray, points, JNI_ABORT);
   (*env)->ReleasePrimitiveArrayCritical(env, opArray, ops, JNI_ABORT);

   cairo_trapezoid_t *trapPtr = traps.traps;
   jint requiredArraySize = traps.num_traps*10 + 5;
   
   if(requiredArraySize > xTrapArrayLength) {
    xTrapArray = (jintArray) (*env)->NewIntArray(env, requiredArraySize);
   }
   
   jint* xTraps;
   if((xTraps = (jint *) (*env)->GetPrimitiveArrayCritical(env, xTrapArray, NULL)) == NULL) {
     return xTrapArray;
   }
   
   xTraps[0] = traps.num_traps;
   xTraps[1] = (jint) _cairo_fixed_to_double(traps.extents.p1.x); 
   xTraps[2] = (jint) _cairo_fixed_to_double(traps.extents.p1.y);
   xTraps[3] = (jint) ceil(_cairo_fixed_to_double(traps.extents.p2.x));
   xTraps[4] = (jint) ceil(_cairo_fixed_to_double(traps.extents.p2.y));
   
   int i; 
   int intCnt = 5;
   for (i = 0; i < traps.num_traps; i++) {
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].top);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].bottom);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].left.p1.x);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].left.p1.y);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].left.p2.x);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].left.p2.y);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].right.p1.x);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].right.p1.y);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].right.p2.x);
	  xTraps[intCnt++] = _cairo_fixed_to_16_16(trapPtr[i].right.p2.y);
    } 
   
   (*env)->ReleasePrimitiveArrayCritical(env, xTrapArray, xTraps, 0);
   
   _cairo_traps_fini(&traps);
   
  return xTrapArray;
}

JNIEXPORT jintArray JNICALL
Java_sun_java2d_xr_CairoPathBuf_tesselateFillNative
    (JNIEnv *env, jclass xsd, jintArray pointArray, jbyteArray opArray, jint pointCnt, jint opCnt, jintArray xTrapArray, jint xTrapArrayLength, jint windingRule, jint clipLowX, jint clipLowY, jint clipHiX, jint clipHiY) {
  return tesselate(env, pointArray, opArray, pointCnt, opCnt, xTrapArray, xTrapArrayLength, windingRule, NULL, NULL, clipLowX, clipLowY, clipHiX, clipHiY);
}

JNIEXPORT jintArray JNICALL
Java_sun_java2d_xr_CairoPathBuf_tesselateStrokeNative
    (JNIEnv *env, jclass xsd, jintArray pointArray, jbyteArray opArray, jint pointCnt, jint opCnt, jintArray xTrapArray, jint xTrapArrayLength, jdouble lineWidth, jint lineCap, jint lineJoin, jdouble miterLimit, jdoubleArray dashArray, jint dashCnt, jdouble dashOffset,
     jdouble m00, jdouble m01, jdouble m02, jdouble m10, jdouble m11, jdouble m12, jint clipLowX, jint clipLowY, jint clipHiX, jint clipHiY) {
 
      jdouble *dash = NULL;
      if(dashCnt > 0) {
	   if((dash = (jdouble *) (*env)->GetPrimitiveArrayCritical(env, dashArray, NULL)) == NULL) {
	      return xTrapArray;
	}
      }
      
      cairo_matrix_t tr;
      tr.xx = m00;
      tr.xy = m01;
      tr.x0 = m02;
      tr.yx = m10;
      tr.yy = m11;
      tr.y0 = m12;

      
      cairo_stroke_style_t style;
      style.line_width = lineWidth;
      style.line_cap = lineCap;
      style.line_join = lineJoin;
      style.miter_limit = miterLimit;
      style.num_dashes = dashCnt;
      style.dash_offset = dashOffset;
      style.dash = dash;
      
      jintArray result = tesselate(env, pointArray, opArray, pointCnt, opCnt, xTrapArray, xTrapArrayLength, 0, &style, &tr, clipLowX, clipLowY, clipHiX, clipHiY);
      
      if(dashCnt > 0) {
	    (*env)->ReleasePrimitiveArrayCritical(env, dashArray, dash, JNI_ABORT);
      }

      return result;
}

JNIEXPORT jlong JNICALL
Java_sun_java2d_xr_CairoAATileGenerator_rasterizeTrapezoidsNative
  (JNIEnv *env, jclass xsd, jlong maskBitPtr, jintArray xTrapArray, jintArray trapPosArray, jint trapCnt, jbyteArray bufferArray, jint xOff, jint yOff) {
    jint i;
    jint *xTraps;
    jint *trapPos;
    jbyte *buffer;  
    if((xTraps = (jint *) (*env)->GetPrimitiveArrayCritical(env, xTrapArray, NULL)) == NULL) {
     return maskBitPtr;
    }
    if((trapPos = (jint *) (*env)->GetPrimitiveArrayCritical(env, trapPosArray, NULL)) == NULL) {
     return maskBitPtr;
    }
    if((buffer = (jbyte *) (*env)->GetPrimitiveArrayCritical(env, bufferArray, NULL)) == NULL) {
     return maskBitPtr;
    }
    
    memset(buffer, 0, 32*32);
    
    //TODO: we can optimize this, by comparing wether buffer adress stays the same.
    //Warning: multithreading!!
   // pixman_image_t *mask = pixman_image_create_bits(PIXMAN_a8, 32, 32, buffer, 32);
    
    pixman_image_t *mask = (pixman_image_t *) jlong_to_ptr(maskBitPtr);
    
    if(mask == NULL || pixman_image_get_data(mask) != buffer) {
      if(mask != NULL) {
	pixman_image_unref(mask);
	//printf("Old Mask Freed!\n");
      }
      
      mask = pixman_image_create_bits(PIXMAN_a8, 32, 32, buffer, 32);
      maskBitPtr = ptr_to_jlong(mask);
      
     // printf("New Mask created!\n");
      //fflush(stdout);
    }
    

    pixman_fixed_t xTileStart = pixman_double_to_fixed(xOff);
    pixman_fixed_t yTileStart = pixman_double_to_fixed(yOff);
    pixman_fixed_t xTileEnd = xTileStart + pixman_double_to_fixed(32);
    pixman_fixed_t yTileEnd = yTileStart + pixman_double_to_fixed(32);
    
    for(i=0; i < trapCnt; i++) {
      pixman_trapezoid_t trap;
            
      int arrayIndex = trapPos[i]*10 + 5;
      
     /* trap.top = xTraps[arrayIndex + 0];
      trap.bottom = xTraps[arrayIndex + 1];
      trap.left.p1.x = xTraps[arrayIndex + 2];
      trap.left.p1.y = xTraps[arrayIndex + 3];
      trap.left.p2.x = xTraps[arrayIndex + 4];
      trap.left.p2.y = xTraps[arrayIndex + 5];
      trap.right.p1.x = xTraps[arrayIndex + 6];
      trap.right.p1.y = xTraps[arrayIndex + 7];
      trap.right.p2.x = xTraps[arrayIndex + 8];
      trap.right.p2.y = xTraps[arrayIndex + 9];    */
      
      memcpy(&trap, &xTraps[arrayIndex], 40);
     
      //printf("top:%d, tilestart: %d", trap.top, yTileStart);
      //printf(" - bottom:%d, tileend %d", trap.bottom, yTileEnd);
      
      /*Clip trapezoid to mask bounds*/
      if(trap.top < yTileStart) {
	trap.top = yTileStart;
	//printf("- start limited");
      }
      
      if(trap.bottom > yTileEnd) {
	trap.bottom = yTileEnd;
	//printf("- bottom limited");
      }
  
      
      if (trap.left.p1.x <= xTileStart && trap.left.p2.x <= xTileStart) {
	  trap.left.p1.x = xTileStart;
	  trap.left.p2.x = xTileStart;
	 // printf("- left limited");
      }

      if (trap.right.p1.x >= xTileEnd && trap.right.p2.x >= xTileEnd) {
	    trap.right.p1.x = xTileEnd;
	    trap.right.p2.x = xTileEnd;
	//    printf("- right limited");
	}
     
     // printf("\n");
     // fflush(stdout);
     
      pixman_rasterize_trapezoid (mask, &trap, -xOff, -yOff);
    }  
   
    (*env)->ReleasePrimitiveArrayCritical(env, xTrapArray, xTraps, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, trapPosArray, trapPos, JNI_ABORT);
    (*env)->ReleasePrimitiveArrayCritical(env, bufferArray, buffer, 0);
    
    return maskBitPtr;
  }


/*TODO: Manage tiles statically, not re-allocaing everything, makes that obsolete.*/
JNIEXPORT void JNICALL
Java_sun_java2d_xr_CairoAATileGenerator_freePixmanImgPtr
  (JNIEnv *env, jclass xsd, jlong maskBitPtr) {
    pixman_image_t *mask = (pixman_image_t *) jlong_to_ptr(maskBitPtr);
    
    if(mask == NULL) {
	pixman_image_unref(mask);
      }
  }



More information about the cairo mailing list