+#ifdef USE_CAIRO
+
+#define FRAME_CR_CONTEXT(f) ((f)->output_data.x->cr_context)
+#define FRAME_CR_SURFACE(f) ((f)->output_data.x->cr_surface)
+
+static struct x_gc_ext_data *
+x_gc_get_ext_data (struct frame *f, GC gc, int create_if_not_found_p)
+{
+ struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ XEDataObject object;
+ XExtData **head, *ext_data;
+
+ object.gc = gc;
+ head = XEHeadOfExtensionList (object);
+ ext_data = XFindOnExtensionList (head, dpyinfo->ext_codes->extension);
+ if (ext_data == NULL)
+ {
+ if (!create_if_not_found_p)
+ return NULL;
+ else
+ {
+ ext_data = xzalloc (sizeof (*ext_data));
+ ext_data->number = dpyinfo->ext_codes->extension;
+ ext_data->private_data = xzalloc (sizeof (struct x_gc_ext_data));
+ XAddToExtensionList (head, ext_data);
+ }
+ }
+ return (struct x_gc_ext_data *) ext_data->private_data;
+}
+
+static void
+x_extension_initialize (struct x_display_info *dpyinfo)
+{
+ XExtCodes *ext_codes = XAddExtension (dpyinfo->display);
+
+ dpyinfo->ext_codes = ext_codes;
+}
+
+static void
+x_cr_destroy_surface (struct frame *f)
+{
+ if (FRAME_CR_SURFACE (f))
+ {
+ cairo_t *cr = FRAME_CR_CONTEXT (f);
+ cairo_surface_destroy (FRAME_CR_SURFACE (f));
+ FRAME_CR_SURFACE (f) = 0;
+ if (cr) cairo_destroy (cr);
+ FRAME_CR_CONTEXT (f) = NULL;
+ }
+}
+
+cairo_t *
+x_begin_cr_clip (struct frame *f, GC gc)
+{
+ cairo_t *cr = FRAME_CR_CONTEXT (f);
+
+ if (!cr)
+ {
+
+ if (! FRAME_CR_SURFACE (f))
+ {
+ cairo_surface_t *surface;
+ surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
+ FRAME_X_WINDOW (f),
+ FRAME_DISPLAY_INFO (f)->visual,
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f));
+ cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+ }
+ else
+ cr = cairo_create (FRAME_CR_SURFACE (f));
+ FRAME_CR_CONTEXT (f) = cr;
+ }
+ cairo_save (cr);
+
+ if (gc)
+ {
+ struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 0);
+
+ if (gc_ext && gc_ext->n_clip_rects)
+ {
+ int i;
+
+ for (i = 0; i < gc_ext->n_clip_rects; i++)
+ cairo_rectangle (cr, gc_ext->clip_rects[i].x,
+ gc_ext->clip_rects[i].y,
+ gc_ext->clip_rects[i].width,
+ gc_ext->clip_rects[i].height);
+ cairo_clip (cr);
+ }
+ }
+
+ return cr;
+}
+
+void
+x_end_cr_clip (struct frame *f)
+{
+ cairo_restore (FRAME_CR_CONTEXT (f));
+}
+
+void
+x_set_cr_source_with_gc_foreground (struct frame *f, GC gc)
+{
+ XGCValues xgcv;
+ XColor color;
+
+ XGetGCValues (FRAME_X_DISPLAY (f), gc, GCForeground, &xgcv);
+ color.pixel = xgcv.foreground;
+ x_query_color (f, &color);
+ cairo_set_source_rgb (FRAME_CR_CONTEXT (f), color.red / 65535.0,
+ color.green / 65535.0, color.blue / 65535.0);
+}
+
+void
+x_set_cr_source_with_gc_background (struct frame *f, GC gc)
+{
+ XGCValues xgcv;
+ XColor color;
+
+ XGetGCValues (FRAME_X_DISPLAY (f), gc, GCBackground, &xgcv);
+ color.pixel = xgcv.background;
+ x_query_color (f, &color);
+ cairo_set_source_rgb (FRAME_CR_CONTEXT (f), color.red / 65535.0,
+ color.green / 65535.0, color.blue / 65535.0);
+}
+
+/* Fringe bitmaps. */
+
+static int max_fringe_bmp = 0;
+static cairo_pattern_t **fringe_bmp = 0;
+
+static void
+x_cr_define_fringe_bitmap (int which, unsigned short *bits, int h, int wd)
+{
+ int i, stride;
+ cairo_surface_t *surface;
+ unsigned char *data;
+ cairo_pattern_t *pattern;
+
+ if (which >= max_fringe_bmp)
+ {
+ i = max_fringe_bmp;
+ max_fringe_bmp = which + 20;
+ fringe_bmp = (cairo_pattern_t **) xrealloc (fringe_bmp, max_fringe_bmp * sizeof (cairo_pattern_t *));
+ while (i < max_fringe_bmp)
+ fringe_bmp[i++] = 0;
+ }
+
+ block_input ();
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_A1, wd, h);
+ stride = cairo_image_surface_get_stride (surface);
+ data = cairo_image_surface_get_data (surface);
+
+ for (i = 0; i < h; i++)
+ {
+ *((unsigned short *) data) = bits[i];
+ data += stride;
+ }
+
+ cairo_surface_mark_dirty (surface);
+ pattern = cairo_pattern_create_for_surface (surface);
+ cairo_surface_destroy (surface);
+
+ unblock_input ();
+
+ fringe_bmp[which] = pattern;
+}
+
+static void
+x_cr_destroy_fringe_bitmap (int which)
+{
+ if (which >= max_fringe_bmp)
+ return;
+
+ if (fringe_bmp[which])
+ {
+ block_input ();
+ cairo_pattern_destroy (fringe_bmp[which]);
+ unblock_input ();
+ }
+ fringe_bmp[which] = 0;
+}
+
+static void
+x_cr_draw_image (struct frame *f, GC gc, cairo_pattern_t *image,
+ int src_x, int src_y, int width, int height,
+ int dest_x, int dest_y, bool overlay_p)
+{
+ cairo_t *cr;
+ cairo_matrix_t matrix;
+ cairo_surface_t *surface;
+ cairo_format_t format;
+
+ cr = x_begin_cr_clip (f, gc);
+ if (overlay_p)
+ cairo_rectangle (cr, dest_x, dest_y, width, height);
+ else
+ {
+ x_set_cr_source_with_gc_background (f, gc);
+ cairo_rectangle (cr, dest_x, dest_y, width, height);
+ cairo_fill_preserve (cr);
+ }
+ cairo_clip (cr);
+ cairo_matrix_init_translate (&matrix, src_x - dest_x, src_y - dest_y);
+ cairo_pattern_set_matrix (image, &matrix);
+ cairo_pattern_get_surface (image, &surface);
+ format = cairo_image_surface_get_format (surface);
+ if (format != CAIRO_FORMAT_A8 && format != CAIRO_FORMAT_A1)
+ {
+ cairo_set_source (cr, image);
+ cairo_fill (cr);
+ }
+ else
+ {
+ x_set_cr_source_with_gc_foreground (f, gc);
+ cairo_mask (cr, image);
+ }
+ x_end_cr_clip (f);
+}
+
+void
+x_cr_draw_frame (cairo_t *cr, struct frame *f)
+{
+ int width, height;
+
+ width = FRAME_PIXEL_WIDTH (f);
+ height = FRAME_PIXEL_HEIGHT (f);
+
+ x_free_cr_resources (f);
+ FRAME_CR_CONTEXT (f) = cr;
+ x_clear_area (f, 0, 0, width, height);
+ expose_frame (f, 0, 0, width, height);
+ FRAME_CR_CONTEXT (f) = NULL;
+}
+
+static cairo_status_t
+x_cr_accumulate_data (void *closure, const unsigned char *data,
+ unsigned int length)
+{
+ Lisp_Object *acc = (Lisp_Object *) closure;
+
+ *acc = Fcons (make_unibyte_string ((char const *) data, length), *acc);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+x_cr_destroy (Lisp_Object arg)
+{
+ cairo_t *cr = (cairo_t *) XSAVE_POINTER (arg, 0);
+
+ block_input ();
+ cairo_destroy (cr);
+ unblock_input ();
+}
+
+Lisp_Object
+x_cr_export_frames (Lisp_Object frames, cairo_surface_type_t surface_type)
+{
+ struct frame *f;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int width, height;
+ void (*surface_set_size_func) (cairo_surface_t *, double, double) = NULL;
+ Lisp_Object acc = Qnil;
+ int count = SPECPDL_INDEX ();
+
+ specbind (Qredisplay_dont_pause, Qt);
+ redisplay_preserve_echo_area (31);
+
+ f = XFRAME (XCAR (frames));
+ frames = XCDR (frames);
+ width = FRAME_PIXEL_WIDTH (f);
+ height = FRAME_PIXEL_HEIGHT (f);
+
+ block_input ();
+#ifdef CAIRO_HAS_PDF_SURFACE
+ if (surface_type == CAIRO_SURFACE_TYPE_PDF)
+ {
+ surface = cairo_pdf_surface_create_for_stream (x_cr_accumulate_data, &acc,
+ width, height);
+ surface_set_size_func = cairo_pdf_surface_set_size;
+ }
+ else
+#endif
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+ if (surface_type == CAIRO_SURFACE_TYPE_IMAGE)
+ surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+ else
+#endif
+#ifdef CAIRO_HAS_PS_SURFACE
+ if (surface_type == CAIRO_SURFACE_TYPE_PS)
+ {
+ surface = cairo_ps_surface_create_for_stream (x_cr_accumulate_data, &acc,
+ width, height);
+ surface_set_size_func = cairo_ps_surface_set_size;
+ }
+ else
+#endif
+#ifdef CAIRO_HAS_SVG_SURFACE
+ if (surface_type == CAIRO_SURFACE_TYPE_SVG)
+ surface = cairo_svg_surface_create_for_stream (x_cr_accumulate_data, &acc,
+ width, height);
+ else
+#endif
+ abort ();
+
+ cr = cairo_create (surface);
+ cairo_surface_destroy (surface);
+ record_unwind_protect (x_cr_destroy, make_save_ptr (cr));
+
+ while (1)
+ {
+ x_free_cr_resources (f);
+ FRAME_CR_CONTEXT (f) = cr;
+ x_clear_area (f, 0, 0, width, height);
+ expose_frame (f, 0, 0, width, height);
+ FRAME_CR_CONTEXT (f) = NULL;
+
+ if (NILP (frames))
+ break;
+
+ cairo_surface_show_page (surface);
+ f = XFRAME (XCAR (frames));
+ frames = XCDR (frames);
+ width = FRAME_PIXEL_WIDTH (f);
+ height = FRAME_PIXEL_HEIGHT (f);
+ if (surface_set_size_func)
+ (*surface_set_size_func) (surface, width, height);
+
+ unblock_input ();
+ QUIT;
+ block_input ();
+ }
+
+#ifdef CAIRO_HAS_PNG_FUNCTIONS
+ if (surface_type == CAIRO_SURFACE_TYPE_IMAGE)
+ {
+ cairo_surface_flush (surface);
+ cairo_surface_write_to_png_stream (surface, x_cr_accumulate_data, &acc);
+ }
+#endif
+ unblock_input ();
+
+ unbind_to (count, Qnil);
+
+ return CALLN (Fapply, intern ("concat"), Fnreverse (acc));
+}
+
+#endif /* USE_CAIRO */
+
+static void
+x_free_cr_resources (struct frame *f)
+{
+#ifdef USE_CAIRO
+ if (f == NULL)
+ {
+ Lisp_Object rest, frame;
+ FOR_EACH_FRAME (rest, frame)
+ if (FRAME_X_P (XFRAME (frame)))
+ x_free_cr_resources (XFRAME (frame));
+ }
+ else
+ {
+ cairo_t *cr = FRAME_CR_CONTEXT (f);
+
+ if (cr)
+ {
+ cairo_surface_t *surface = cairo_get_target (cr);
+
+ if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB)
+ {
+ cairo_destroy (cr);
+ FRAME_CR_CONTEXT (f) = NULL;
+ }
+ }
+ }
+#endif
+}
+
+static void
+x_set_clip_rectangles (struct frame *f, GC gc, XRectangle *rectangles, int n)
+{
+ XSetClipRectangles (FRAME_X_DISPLAY (f), gc, 0, 0, rectangles, n, Unsorted);
+#ifdef USE_CAIRO
+ eassert (n >= 0 && n <= MAX_CLIP_RECTS);
+
+ {
+ struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 1);
+
+ gc_ext->n_clip_rects = n;
+ memcpy (gc_ext->clip_rects, rectangles, sizeof (XRectangle) * n);
+ }
+#endif
+}
+
+static void
+x_reset_clip_rectangles (struct frame *f, GC gc)
+{
+ XSetClipMask (FRAME_X_DISPLAY (f), gc, None);
+#ifdef USE_CAIRO
+ {
+ struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 0);
+
+ if (gc_ext)
+ gc_ext->n_clip_rects = 0;
+ }
+#endif
+}
+
+static void
+x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
+{
+#ifdef USE_CAIRO
+ cairo_t *cr;
+
+ cr = x_begin_cr_clip (f, gc);
+ x_set_cr_source_with_gc_foreground (f, gc);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_fill (cr);
+ x_end_cr_clip (f);
+#else
+ XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+ gc, x, y, width, height);
+#endif
+}
+
+static void
+x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
+{
+#ifdef USE_CAIRO
+ cairo_t *cr;
+
+ cr = x_begin_cr_clip (f, gc);
+ x_set_cr_source_with_gc_foreground (f, gc);
+ cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
+ cairo_set_line_width (cr, 1);
+ cairo_stroke (cr);
+ x_end_cr_clip (f);
+#else
+ XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+ gc, x, y, width, height);
+#endif
+}
+
+static void
+x_clear_window (struct frame *f)
+{
+#ifdef USE_CAIRO
+ cairo_t *cr;
+
+ cr = x_begin_cr_clip (f, NULL);
+ x_set_cr_source_with_gc_background (f, f->output_data.x->normal_gc);
+ cairo_paint (cr);
+ x_end_cr_clip (f);
+#else
+ XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+#endif
+}
+
+#ifdef USE_CAIRO
+static void
+x_fill_trapezoid_for_relief (struct frame *f, GC gc, int x, int y,
+ int width, int height, int top_p)
+{
+ cairo_t *cr;
+
+ cr = x_begin_cr_clip (f, gc);
+ x_set_cr_source_with_gc_foreground (f, gc);
+ cairo_move_to (cr, top_p ? x : x + height, y);
+ cairo_line_to (cr, x, y + height);
+ cairo_line_to (cr, top_p ? x + width - height : x + width, y + height);
+ cairo_line_to (cr, x + width, y);
+ cairo_fill (cr);
+ x_end_cr_clip (f);
+}
+
+enum corners
+ {
+ CORNER_BOTTOM_RIGHT, /* 0 -> pi/2 */
+ CORNER_BOTTOM_LEFT, /* pi/2 -> pi */
+ CORNER_TOP_LEFT, /* pi -> 3pi/2 */
+ CORNER_TOP_RIGHT, /* 3pi/2 -> 2pi */
+ CORNER_LAST
+ };
+
+static void
+x_erase_corners_for_relief (struct frame *f, GC gc, int x, int y,
+ int width, int height,
+ double radius, double margin, int corners)
+{
+ cairo_t *cr;
+ int i;
+
+ cr = x_begin_cr_clip (f, gc);
+ x_set_cr_source_with_gc_background (f, gc);
+ for (i = 0; i < CORNER_LAST; i++)
+ if (corners & (1 << i))
+ {
+ double xm, ym, xc, yc;
+
+ if (i == CORNER_TOP_LEFT || i == CORNER_BOTTOM_LEFT)
+ xm = x - margin, xc = xm + radius;
+ else
+ xm = x + width + margin, xc = xm - radius;
+ if (i == CORNER_TOP_LEFT || i == CORNER_TOP_RIGHT)
+ ym = y - margin, yc = ym + radius;
+ else
+ ym = y + height + margin, yc = ym - radius;
+
+ cairo_move_to (cr, xm, ym);
+ cairo_arc (cr, xc, yc, radius, i * M_PI_2, (i + 1) * M_PI_2);
+ }
+ cairo_clip (cr);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_fill (cr);
+ x_end_cr_clip (f);
+}
+
+static void
+x_draw_horizontal_wave (struct frame *f, GC gc, int x, int y,
+ int width, int height, int wave_length)
+{
+ cairo_t *cr;
+ double dx = wave_length, dy = height - 1;
+ int xoffset, n;
+
+ cr = x_begin_cr_clip (f, gc);
+ x_set_cr_source_with_gc_foreground (f, gc);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+
+ if (x >= 0)
+ {
+ xoffset = x % (wave_length * 2);
+ if (xoffset == 0)
+ xoffset = wave_length * 2;
+ }
+ else
+ xoffset = x % (wave_length * 2) + wave_length * 2;
+ n = (width + xoffset) / wave_length + 1;
+ if (xoffset > wave_length)
+ {
+ xoffset -= wave_length;
+ --n;
+ y += height - 1;
+ dy = -dy;
+ }
+
+ cairo_move_to (cr, x - xoffset + 0.5, y + 0.5);
+ while (--n >= 0)
+ {
+ cairo_rel_line_to (cr, dx, dy);
+ dy = -dy;
+ }
+ cairo_set_line_width (cr, 1);
+ cairo_stroke (cr);
+ x_end_cr_clip (f);
+}
+#endif