JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_drawPixels 
  (JNIEnv *env, jobject obj, jintArray java_pixels, 
   jint w, jint h, jint stride, jdoubleArray java_matrix)
{
  struct graphics2d *gr = NULL;
  jint *native_pixels = NULL;
  jdouble *native_matrix = NULL;

  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
  g_assert (gr != NULL);

  if (gr->debug) printf ("drawPixels (%d pixels, %dx%d, stride: %d)\n",
			 (*env)->GetArrayLength (env, java_pixels), w, h, stride);

  native_pixels = (*env)->GetIntArrayElements (env, java_pixels, NULL);
  native_matrix = (*env)->GetDoubleArrayElements (env, java_matrix, NULL);
  g_assert (native_pixels != NULL);
  g_assert (native_matrix != NULL);
  g_assert ((*env)->GetArrayLength (env, java_matrix) == 6);

  begin_drawing_operation (gr);
  
 {
   cairo_matrix_t *mat = NULL;
   cairo_surface_t *surf = cairo_surface_create_for_image ((char *)native_pixels, 
							   CAIRO_FORMAT_ARGB32, 
							   w, h, stride * 4);   
   mat = cairo_matrix_create ();
   cairo_matrix_set_affine (mat, 
			    native_matrix[0], native_matrix[1],
			    native_matrix[2], native_matrix[3],
			    native_matrix[4], native_matrix[5]);
   cairo_surface_set_matrix (surf, mat);
   if (native_matrix[0] != 1.
       || native_matrix[1] != 0.
       || native_matrix[2] != 0.
       || native_matrix[3] != 1.)
     {
       cairo_surface_set_filter (surf, CAIRO_FILTER_BILINEAR);
       cairo_surface_set_filter (gr->surface, CAIRO_FILTER_BILINEAR);
     }
   else
     {
       cairo_surface_set_filter (surf, CAIRO_FILTER_FAST);
       cairo_surface_set_filter (gr->surface, CAIRO_FILTER_FAST);
     }
   cairo_show_surface (gr->cr, surf, w, h);
   cairo_surface_set_filter (gr->surface, CAIRO_FILTER_FAST);
   cairo_matrix_destroy (mat);
   cairo_surface_destroy (surf);
 }
  
 end_drawing_operation (gr);

  (*env)->ReleaseIntArrayElements (env, java_pixels, native_pixels, 0);
  (*env)->ReleaseDoubleArrayElements (env, java_matrix, native_matrix, 0);

}
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill 
   (JNIEnv *env, jobject obj)
{
  struct graphics2d *gr = NULL;
  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
  g_assert (gr != NULL);
  if (gr->debug) printf ("cairo_fill\n");
  begin_drawing_operation (gr);
  cairo_fill (gr->cr);
  end_drawing_operation (gr);
}
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoShowGlyphs
   (JNIEnv *env, jobject obj, jintArray java_codes, jfloatArray java_posns)
{
  struct graphics2d *gr = NULL;
  cairo_glyph_t *glyphs = NULL;
  jfloat *native_posns = NULL;
  jint *native_codes = NULL;
  jint i;
  jint ncodes, nposns;

  gdk_threads_enter();
  if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }

  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
  g_assert (gr != NULL);

  native_codes = (*env)->GetIntArrayElements (env, java_codes, NULL);  
  native_posns = (*env)->GetFloatArrayElements (env, java_posns, NULL);  
  g_assert (native_codes != NULL);
  g_assert (native_posns != NULL);

  ncodes = (*env)->GetArrayLength (env, java_codes);
  nposns = (*env)->GetArrayLength (env, java_posns);
  g_assert (2 * ncodes == nposns);

  if (gr->debug) printf ("cairo_show_glyphs (%d glyphs)\n", ncodes);

  glyphs = malloc (sizeof(cairo_glyph_t) * ncodes);
  g_assert (glyphs);

  for (i = 0; i < ncodes; ++i)
    {
      glyphs[i].index = native_codes[i];
      glyphs[i].x = (double) native_posns[2*i];
      glyphs[i].y = (double) native_posns[2*i + 1];
      if (gr->debug) printf ("cairo_show_glyphs (glyph %d (code %d) : %f,%f)\n", 
			     i, glyphs[i].index, glyphs[i].x, glyphs[i].y);
    }

  (*env)->ReleaseIntArrayElements (env, java_codes, native_codes, 0);
  (*env)->ReleaseFloatArrayElements (env, java_posns, native_posns, 0);

  begin_drawing_operation (gr);
  cairo_show_glyphs (gr->cr, glyphs, ncodes);
  end_drawing_operation (gr);

  free(glyphs);
  gdk_threads_leave();
}
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_cairoFill 
   (JNIEnv *env, jobject obj)
{
  struct graphics2d *gr = NULL;

  gdk_threads_enter();
  if (peer_is_disposed(env, obj)) { gdk_threads_leave(); return; }

  gr = (struct graphics2d *) NSA_GET_G2D_PTR (env, obj);
  g_assert (gr != NULL);
  if (gr->debug) printf ("cairo_fill\n");
  begin_drawing_operation (gr);
  cairo_fill (gr->cr);
  end_drawing_operation (gr);
  gdk_threads_leave();
}
JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GdkGraphics2D_gdkDrawDrawable
  (JNIEnv *env, jobject self, jobject other, jint x, jint y)
{
  struct graphics2d *src = NULL, *dst = NULL;
  gint s_height, s_width, d_height, d_width, height, width;
  cairo_matrix_t *matrix;
  cairo_operator_t tmp_op;

  gdk_threads_enter();
  if (peer_is_disposed(env, self)) { gdk_threads_leave(); return; }
  src = (struct graphics2d *)NSA_GET_G2D_PTR (env, other);
  dst = (struct graphics2d *)NSA_GET_G2D_PTR (env, self);
  g_assert (src != NULL);
  g_assert (dst != NULL);  

  if (src->debug) printf ("copying from offscreen drawable\n");

  begin_drawing_operation(dst); 

  gdk_flush();

  gdk_drawable_get_size (src->drawable, &s_width, &s_height);
  gdk_drawable_get_size (dst->drawable, &d_width, &d_height);
  width = min (s_width, d_width);
  height = min (s_height, d_height);

  matrix = cairo_matrix_create ();
  cairo_surface_get_matrix (src->surface, matrix);
  cairo_matrix_translate (matrix, (double)-x, (double)-y);
  cairo_surface_set_matrix (src->surface, matrix);

  tmp_op = cairo_current_operator (dst->cr); 
  cairo_set_operator(dst->cr, CAIRO_OPERATOR_SRC); 
  cairo_show_surface (dst->cr, src->surface, width, height);
  cairo_set_operator(dst->cr, tmp_op);

  cairo_matrix_translate (matrix, (double)x, (double)y);
  cairo_surface_set_matrix (src->surface, matrix);
  cairo_matrix_destroy (matrix);

  gdk_flush();

  end_drawing_operation(dst);

  if (src->debug) printf ("copied %d x %d pixels from offscreen drawable\n", width, height);
  gdk_threads_leave();
}