cairo-xcb-surface.c [plain text]
#include "cairoint.h"
#include "cairo-xcb.h"
#include "cairo-xcb-xrender.h"
#include "cairo-clip-private.h"
#include <xcb/xcb_renderutil.h>
#define AllPlanes ((unsigned long)~0L)
slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
#define CAIRO_ASSUME_PIXMAP 20
typedef struct cairo_xcb_surface {
cairo_surface_t base;
xcb_connection_t *dpy;
xcb_screen_t *screen;
xcb_gcontext_t gc;
xcb_drawable_t drawable;
cairo_bool_t owns_pixmap;
xcb_visualtype_t *visual;
int use_pixmap;
int render_major;
int render_minor;
int width;
int height;
int depth;
cairo_bool_t have_clip_rects;
xcb_rectangle_t *clip_rects;
int num_clip_rects;
xcb_render_picture_t src_picture, dst_picture;
xcb_render_pictforminfo_t xrender_format;
} cairo_xcb_surface_t;
#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \
(((surface)->render_major > major) || \
(((surface)->render_major == major) && ((surface)->render_minor >= minor)))
#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
static void
_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface);
static int
_CAIRO_FORMAT_DEPTH (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_A1:
return 1;
case CAIRO_FORMAT_A8:
return 8;
case CAIRO_FORMAT_RGB24:
return 24;
case CAIRO_FORMAT_ARGB32:
default:
return 32;
}
}
static xcb_render_pictforminfo_t *
_CAIRO_FORMAT_TO_XRENDER_FORMAT(xcb_connection_t *dpy, cairo_format_t format)
{
xcb_pict_standard_t std_format;
switch (format) {
case CAIRO_FORMAT_A1:
std_format = XCB_PICT_STANDARD_A_1; break;
case CAIRO_FORMAT_A8:
std_format = XCB_PICT_STANDARD_A_8; break;
case CAIRO_FORMAT_RGB24:
std_format = XCB_PICT_STANDARD_RGB_24; break;
case CAIRO_FORMAT_ARGB32:
default:
std_format = XCB_PICT_STANDARD_ARGB_32; break;
}
return xcb_render_util_find_standard_format (xcb_render_util_query_formats (dpy), std_format);
}
static cairo_content_t
_xcb_render_format_to_content (xcb_render_pictforminfo_t *xrender_format)
{
cairo_bool_t xrender_format_has_alpha;
cairo_bool_t xrender_format_has_color;
if (xrender_format == NULL)
return CAIRO_CONTENT_COLOR;
xrender_format_has_alpha = (xrender_format->direct.alpha_mask != 0);
xrender_format_has_color = (xrender_format->direct.red_mask != 0 ||
xrender_format->direct.green_mask != 0 ||
xrender_format->direct.blue_mask != 0);
if (xrender_format_has_alpha)
if (xrender_format_has_color)
return CAIRO_CONTENT_COLOR_ALPHA;
else
return CAIRO_CONTENT_ALPHA;
else
return CAIRO_CONTENT_COLOR;
}
static cairo_surface_t *
_cairo_xcb_surface_create_similar (void *abstract_src,
cairo_content_t content,
int width,
int height)
{
cairo_xcb_surface_t *src = abstract_src;
xcb_connection_t *dpy = src->dpy;
xcb_pixmap_t pixmap;
cairo_xcb_surface_t *surface;
cairo_format_t format = _cairo_format_from_content (content);
xcb_render_pictforminfo_t *xrender_format;
if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) {
return cairo_image_surface_create (format, width, height);
}
pixmap = xcb_generate_id (dpy);
xcb_create_pixmap (dpy, _CAIRO_FORMAT_DEPTH (format),
pixmap, src->drawable,
width <= 0 ? 1 : width,
height <= 0 ? 1 : height);
xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dpy, format);
surface = (cairo_xcb_surface_t *)
cairo_xcb_surface_create_with_xrender_format (dpy, pixmap, src->screen,
xrender_format,
width, height);
if (surface->base.status)
return &surface->base;
surface->owns_pixmap = TRUE;
return &surface->base;
}
static cairo_status_t
_cairo_xcb_surface_finish (void *abstract_surface)
{
cairo_xcb_surface_t *surface = abstract_surface;
if (surface->dst_picture != XCB_NONE)
xcb_render_free_picture (surface->dpy, surface->dst_picture);
if (surface->src_picture != XCB_NONE)
xcb_render_free_picture (surface->dpy, surface->src_picture);
if (surface->owns_pixmap)
xcb_free_pixmap (surface->dpy, surface->drawable);
if (surface->gc != XCB_NONE)
xcb_free_gc (surface->dpy, surface->gc);
free (surface->clip_rects);
surface->dpy = NULL;
return CAIRO_STATUS_SUCCESS;
}
static int
_bits_per_pixel(xcb_connection_t *c, int depth)
{
xcb_format_t *fmt = xcb_setup_pixmap_formats(xcb_get_setup(c));
xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(xcb_get_setup(c));
for(; fmt != fmtend; ++fmt)
if(fmt->depth == depth)
return fmt->bits_per_pixel;
if(depth <= 4)
return 4;
if(depth <= 8)
return 8;
if(depth <= 16)
return 16;
return 32;
}
static int
_bytes_per_line(xcb_connection_t *c, int width, int bpp)
{
int bitmap_pad = xcb_get_setup(c)->bitmap_format_scanline_pad;
return ((bpp * width + bitmap_pad - 1) & -bitmap_pad) >> 3;
}
static cairo_bool_t
_CAIRO_MASK_FORMAT (cairo_format_masks_t *masks, cairo_format_t *format)
{
switch (masks->bpp) {
case 32:
if (masks->alpha_mask == 0xff000000 &&
masks->red_mask == 0x00ff0000 &&
masks->green_mask == 0x0000ff00 &&
masks->blue_mask == 0x000000ff)
{
*format = CAIRO_FORMAT_ARGB32;
return TRUE;
}
if (masks->alpha_mask == 0x00000000 &&
masks->red_mask == 0x00ff0000 &&
masks->green_mask == 0x0000ff00 &&
masks->blue_mask == 0x000000ff)
{
*format = CAIRO_FORMAT_RGB24;
return TRUE;
}
break;
case 8:
if (masks->alpha_mask == 0xff)
{
*format = CAIRO_FORMAT_A8;
return TRUE;
}
break;
case 1:
if (masks->alpha_mask == 0x1)
{
*format = CAIRO_FORMAT_A1;
return TRUE;
}
break;
}
return FALSE;
}
static cairo_status_t
_get_image_surface (cairo_xcb_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *image_rect)
{
cairo_image_surface_t *image;
xcb_get_image_reply_t *imagerep;
int bpp, bytes_per_line;
cairo_rectangle_int_t extents;
unsigned char *data;
cairo_format_masks_t masks;
cairo_format_t format;
extents.x = 0;
extents.y = 0;
extents.width = surface->width;
extents.height = surface->height;
if (interest_rect) {
if (! _cairo_rectangle_intersect (&extents, interest_rect)) {
*image_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
}
if (image_rect)
*image_rect = extents;
if (surface->use_pixmap == 0)
{
xcb_generic_error_t *error;
imagerep = xcb_get_image_reply(surface->dpy,
xcb_get_image(surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP,
surface->drawable,
extents.x, extents.y,
extents.width, extents.height,
AllPlanes), &error);
if (error)
surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
}
else
{
surface->use_pixmap--;
imagerep = NULL;
}
if (!imagerep)
{
xcb_pixmap_t pixmap;
pixmap = xcb_generate_id (surface->dpy);
xcb_create_pixmap (surface->dpy,
surface->depth,
pixmap,
surface->drawable,
extents.width, extents.height);
_cairo_xcb_surface_ensure_gc (surface);
xcb_copy_area (surface->dpy, surface->drawable, pixmap, surface->gc,
extents.x, extents.y, 0, 0, extents.width, extents.height);
imagerep = xcb_get_image_reply(surface->dpy,
xcb_get_image(surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP,
pixmap,
extents.x, extents.y,
extents.width, extents.height,
AllPlanes), 0);
xcb_free_pixmap (surface->dpy, pixmap);
}
if (!imagerep)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
bpp = _bits_per_pixel(surface->dpy, imagerep->depth);
bytes_per_line = _bytes_per_line(surface->dpy, surface->width, bpp);
data = _cairo_malloc_ab (surface->height, bytes_per_line);
if (data == NULL) {
free (imagerep);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memcpy (data, xcb_get_image_data (imagerep), bytes_per_line * surface->height);
free (imagerep);
if (surface->visual) {
masks.bpp = bpp;
masks.alpha_mask = 0;
masks.red_mask = surface->visual->red_mask;
masks.green_mask = surface->visual->green_mask;
masks.blue_mask = surface->visual->blue_mask;
} else if (surface->xrender_format.id != XCB_NONE) {
masks.bpp = bpp;
masks.red_mask = (unsigned long)surface->xrender_format.direct.red_mask << surface->xrender_format.direct.red_shift;
masks.green_mask = (unsigned long)surface->xrender_format.direct.green_mask << surface->xrender_format.direct.green_shift;
masks.blue_mask = (unsigned long)surface->xrender_format.direct.blue_mask << surface->xrender_format.direct.blue_shift;
masks.alpha_mask = (unsigned long)surface->xrender_format.direct.alpha_mask << surface->xrender_format.direct.alpha_shift;
} else {
masks.bpp = bpp;
masks.red_mask = 0;
masks.green_mask = 0;
masks.blue_mask = 0;
if (surface->depth < 32)
masks.alpha_mask = (1 << surface->depth) - 1;
else
masks.alpha_mask = 0xffffffff;
}
if (_CAIRO_MASK_FORMAT (&masks, &format)) {
image = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (data,
format,
extents.width,
extents.height,
bytes_per_line);
if (image->base.status)
goto FAIL;
} else {
image = (cairo_image_surface_t *)
_cairo_image_surface_create_with_masks (data,
&masks,
extents.width,
extents.height,
bytes_per_line);
if (image->base.status)
goto FAIL;
}
_cairo_image_surface_assume_ownership_of_data (image);
*image_out = image;
return CAIRO_STATUS_SUCCESS;
FAIL:
free (data);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
static void
_cairo_xcb_surface_ensure_src_picture (cairo_xcb_surface_t *surface)
{
if (!surface->src_picture) {
surface->src_picture = xcb_generate_id(surface->dpy);
xcb_render_create_picture (surface->dpy,
surface->src_picture,
surface->drawable,
surface->xrender_format.id,
0, NULL);
}
}
static void
_cairo_xcb_surface_set_picture_clip_rects (cairo_xcb_surface_t *surface)
{
if (surface->have_clip_rects)
xcb_render_set_picture_clip_rectangles (surface->dpy, surface->dst_picture,
0, 0,
surface->num_clip_rects,
surface->clip_rects);
}
static void
_cairo_xcb_surface_set_gc_clip_rects (cairo_xcb_surface_t *surface)
{
if (surface->have_clip_rects)
xcb_set_clip_rectangles(surface->dpy, XCB_CLIP_ORDERING_YX_SORTED, surface->gc,
0, 0,
surface->num_clip_rects,
surface->clip_rects );
}
static void
_cairo_xcb_surface_ensure_dst_picture (cairo_xcb_surface_t *surface)
{
if (!surface->dst_picture) {
surface->dst_picture = xcb_generate_id(surface->dpy);
xcb_render_create_picture (surface->dpy,
surface->dst_picture,
surface->drawable,
surface->xrender_format.id,
0, NULL);
_cairo_xcb_surface_set_picture_clip_rects (surface);
}
}
static void
_cairo_xcb_surface_ensure_gc (cairo_xcb_surface_t *surface)
{
if (surface->gc)
return;
surface->gc = xcb_generate_id(surface->dpy);
xcb_create_gc (surface->dpy, surface->gc, surface->drawable, 0, 0);
_cairo_xcb_surface_set_gc_clip_rects(surface);
}
static cairo_status_t
_draw_image_surface (cairo_xcb_surface_t *surface,
cairo_image_surface_t *image,
int src_x,
int src_y,
int width,
int height,
int dst_x,
int dst_y)
{
int bpp, bpl;
uint32_t data_len;
uint8_t *data, left_pad=0;
if (src_x < 0) {
width += src_x;
src_x = 0;
}
if (src_y < 0) {
height += src_y;
src_y = 0;
}
if (width + src_x > image->width)
width = image->width - src_x;
if (height + src_y > image->height)
height = image->height - src_y;
if (width <= 0 || height <= 0)
return CAIRO_STATUS_SUCCESS;
bpp = _bits_per_pixel(surface->dpy, image->depth);
bpl = _bytes_per_line(surface->dpy, image->width, bpp);
if (src_x == 0 && width == image->width) {
data_len = height * bpl;
data = image->data + src_y * bpl;
} else {
int line = 0;
uint8_t *data_line, *image_line;
int data_bpl = _bytes_per_line(surface->dpy, width, bpp);
data_len = height * data_bpl;
data_line = data = malloc(data_len);
if (data == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
image_line = image->data + src_y * bpl + (src_x * bpp / 8);
while (line++ < height) {
memcpy(data_line, image_line, data_bpl);
data_line += data_bpl;
image_line += bpl;
}
}
_cairo_xcb_surface_ensure_gc (surface);
xcb_put_image (surface->dpy, XCB_IMAGE_FORMAT_Z_PIXMAP,
surface->drawable, surface->gc,
width, height,
dst_x, dst_y,
left_pad, image->depth,
data_len, data);
if (data < image->data || data >= image->data + image->height * bpl)
free(data);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_xcb_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_image_surface_t *image;
cairo_status_t status;
status = _get_image_surface (surface, NULL, &image, NULL);
if (status)
return status;
*image_out = image;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_xcb_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
}
static cairo_status_t
_cairo_xcb_surface_acquire_dest_image (void *abstract_surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *image_rect_out,
void **image_extra)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_image_surface_t *image;
cairo_status_t status;
status = _get_image_surface (surface, interest_rect, &image, image_rect_out);
if (status)
return status;
*image_out = image;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_xcb_surface_release_dest_image (void *abstract_surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t *image,
cairo_rectangle_int_t *image_rect,
void *image_extra)
{
cairo_xcb_surface_t *surface = abstract_surface;
_draw_image_surface (surface, image, 0, 0, image->width, image->height,
image_rect->x, image_rect->y);
cairo_surface_destroy (&image->base);
}
static cairo_bool_t
_cairo_xcb_surface_same_screen (cairo_xcb_surface_t *dst,
cairo_xcb_surface_t *src)
{
return dst->dpy == src->dpy && dst->screen == src->screen;
}
static cairo_status_t
_cairo_xcb_surface_clone_similar (void *abstract_surface,
cairo_surface_t *src,
int src_x,
int src_y,
int width,
int height,
int *clone_offset_x,
int *clone_offset_y,
cairo_surface_t **clone_out)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_xcb_surface_t *clone;
if (src->backend == surface->base.backend ) {
cairo_xcb_surface_t *xcb_src = (cairo_xcb_surface_t *)src;
if (_cairo_xcb_surface_same_screen(surface, xcb_src)) {
*clone_offset_x = 0;
*clone_offset_y = 0;
*clone_out = cairo_surface_reference (src);
return CAIRO_STATUS_SUCCESS;
}
} else if (_cairo_surface_is_image (src)) {
cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
cairo_content_t content = _cairo_content_from_format (image_src->format);
cairo_status_t status;
if (surface->base.status)
return surface->base.status;
clone = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_similar (surface, content, width, height);
if (clone == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (clone->base.status)
return clone->base.status;
status = _draw_image_surface (clone, image_src,
src_x, src_y,
width, height,
0, 0);
if (status) {
cairo_surface_destroy (&clone->base);
return status;
}
*clone_offset_x = src_x;
*clone_offset_y = src_y;
*clone_out = &clone->base;
return CAIRO_STATUS_SUCCESS;
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
static cairo_status_t
_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface,
cairo_matrix_t *matrix)
{
xcb_render_transform_t xtransform;
if (!surface->src_picture)
return CAIRO_STATUS_SUCCESS;
xtransform.matrix11 = _cairo_fixed_16_16_from_double (matrix->xx);
xtransform.matrix12 = _cairo_fixed_16_16_from_double (matrix->xy);
xtransform.matrix13 = _cairo_fixed_16_16_from_double (matrix->x0);
xtransform.matrix21 = _cairo_fixed_16_16_from_double (matrix->yx);
xtransform.matrix22 = _cairo_fixed_16_16_from_double (matrix->yy);
xtransform.matrix23 = _cairo_fixed_16_16_from_double (matrix->y0);
xtransform.matrix31 = 0;
xtransform.matrix32 = 0;
xtransform.matrix33 = 1 << 16;
if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface))
{
static const xcb_render_transform_t identity = {
1 << 16, 0x00000, 0x00000,
0x00000, 1 << 16, 0x00000,
0x00000, 0x00000, 1 << 16
};
if (memcmp (&xtransform, &identity, sizeof (xcb_render_transform_t)) == 0)
return CAIRO_STATUS_SUCCESS;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
xcb_render_set_picture_transform (surface->dpy, surface->src_picture, xtransform);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_xcb_surface_set_filter (cairo_xcb_surface_t *surface,
cairo_filter_t filter)
{
const char *render_filter;
if (!surface->src_picture)
return CAIRO_STATUS_SUCCESS;
if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface))
{
if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST)
return CAIRO_STATUS_SUCCESS;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
switch (filter) {
case CAIRO_FILTER_FAST:
render_filter = "fast";
break;
case CAIRO_FILTER_GOOD:
render_filter = "good";
break;
case CAIRO_FILTER_BEST:
render_filter = "best";
break;
case CAIRO_FILTER_NEAREST:
render_filter = "nearest";
break;
case CAIRO_FILTER_BILINEAR:
render_filter = "bilinear";
break;
case CAIRO_FILTER_GAUSSIAN:
default:
render_filter = "best";
break;
}
xcb_render_set_picture_filter(surface->dpy, surface->src_picture,
strlen(render_filter), render_filter, 0, NULL);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_xcb_surface_set_repeat (cairo_xcb_surface_t *surface, int repeat)
{
uint32_t mask = XCB_RENDER_CP_REPEAT;
uint32_t pa[] = { repeat };
if (!surface->src_picture)
return CAIRO_STATUS_SUCCESS;
xcb_render_change_picture (surface->dpy, surface->src_picture, mask, pa);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_xcb_surface_set_attributes (cairo_xcb_surface_t *surface,
cairo_surface_attributes_t *attributes)
{
cairo_int_status_t status;
_cairo_xcb_surface_ensure_src_picture (surface);
status = _cairo_xcb_surface_set_matrix (surface, &attributes->matrix);
if (status)
return status;
switch (attributes->extend) {
case CAIRO_EXTEND_NONE:
_cairo_xcb_surface_set_repeat (surface, 0);
break;
case CAIRO_EXTEND_REPEAT:
_cairo_xcb_surface_set_repeat (surface, 1);
break;
case CAIRO_EXTEND_REFLECT:
case CAIRO_EXTEND_PAD:
return CAIRO_INT_STATUS_UNSUPPORTED;
}
status = _cairo_xcb_surface_set_filter (surface, attributes->filter);
if (status)
return status;
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
_surfaces_compatible (cairo_xcb_surface_t *dst,
cairo_xcb_surface_t *src)
{
if (!_cairo_xcb_surface_same_screen (dst, src))
return FALSE;
if (src->depth != dst->depth)
return FALSE;
if (src->xrender_format.id != dst->xrender_format.id)
return FALSE;
else if (src->xrender_format.id != XCB_NONE)
return TRUE;
if (src->visual == dst->visual)
return TRUE;
return FALSE;
}
static cairo_bool_t
_surface_has_alpha (cairo_xcb_surface_t *surface)
{
if (surface->xrender_format.id != XCB_NONE) {
if (surface->xrender_format.type == XCB_RENDER_PICT_TYPE_DIRECT &&
surface->xrender_format.direct.alpha_mask != 0)
return TRUE;
else
return FALSE;
} else {
return FALSE;
}
}
static cairo_bool_t
_operator_needs_alpha_composite (cairo_operator_t op,
cairo_bool_t surface_has_alpha)
{
if (op == CAIRO_OPERATOR_SOURCE ||
(!surface_has_alpha &&
(op == CAIRO_OPERATOR_OVER ||
op == CAIRO_OPERATOR_ATOP ||
op == CAIRO_OPERATOR_IN)))
return FALSE;
return TRUE;
}
typedef enum {
DO_RENDER,
DO_XCOPYAREA,
DO_XTILE,
DO_UNSUPPORTED
} composite_operation_t;
static composite_operation_t
_categorize_composite_operation (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_pattern_t *src_pattern,
cairo_bool_t have_mask)
{
#if XXX_BUGGY_REPEAT
if (!dst->buggy_repeat)
return DO_RENDER;
if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
{
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src_pattern;
if (_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) &&
src_pattern->extend == CAIRO_EXTEND_REPEAT)
{
if (have_mask ||
!(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
return DO_UNSUPPORTED;
if (_cairo_surface_is_xcb (surface_pattern->surface)) {
cairo_xcb_surface_t *src = (cairo_xcb_surface_t *)surface_pattern->surface;
if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src))
return DO_UNSUPPORTED;
if (_cairo_xcb_surface_same_screen (dst, src) &&
!_surfaces_compatible (dst, src))
return DO_UNSUPPORTED;
}
}
if (!_cairo_matrix_is_integer_translation (&src_pattern->matrix, NULL, NULL) &&
src_pattern->extend == CAIRO_EXTEND_REPEAT)
return DO_UNSUPPORTED;
}
#endif
return DO_RENDER;
}
static composite_operation_t
_recategorize_composite_operation (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_xcb_surface_t *src,
cairo_surface_attributes_t *src_attr,
cairo_bool_t have_mask)
{
cairo_bool_t is_integer_translation =
_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL);
cairo_bool_t needs_alpha_composite =
_operator_needs_alpha_composite (op, _surface_has_alpha (src));
if (!have_mask &&
is_integer_translation &&
src_attr->extend == CAIRO_EXTEND_NONE &&
!needs_alpha_composite &&
_surfaces_compatible(src, dst))
{
return DO_XCOPYAREA;
}
#if XXX_BUGGY_REPEAT
if (!dst->buggy_repeat)
return DO_RENDER;
if (is_integer_translation &&
src_attr->extend == CAIRO_EXTEND_REPEAT &&
(src->width != 1 || src->height != 1))
{
if (!have_mask &&
!needs_alpha_composite &&
_surfaces_compatible (dst, src))
{
return DO_XTILE;
}
return DO_UNSUPPORTED;
}
#endif
return DO_RENDER;
}
static int
_render_operator (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return XCB_RENDER_PICT_OP_CLEAR;
case CAIRO_OPERATOR_SOURCE:
return XCB_RENDER_PICT_OP_SRC;
case CAIRO_OPERATOR_DEST:
return XCB_RENDER_PICT_OP_DST;
case CAIRO_OPERATOR_OVER:
return XCB_RENDER_PICT_OP_OVER;
case CAIRO_OPERATOR_DEST_OVER:
return XCB_RENDER_PICT_OP_OVER_REVERSE;
case CAIRO_OPERATOR_IN:
return XCB_RENDER_PICT_OP_IN;
case CAIRO_OPERATOR_DEST_IN:
return XCB_RENDER_PICT_OP_IN_REVERSE;
case CAIRO_OPERATOR_OUT:
return XCB_RENDER_PICT_OP_OUT;
case CAIRO_OPERATOR_DEST_OUT:
return XCB_RENDER_PICT_OP_OUT_REVERSE;
case CAIRO_OPERATOR_ATOP:
return XCB_RENDER_PICT_OP_ATOP;
case CAIRO_OPERATOR_DEST_ATOP:
return XCB_RENDER_PICT_OP_ATOP_REVERSE;
case CAIRO_OPERATOR_XOR:
return XCB_RENDER_PICT_OP_XOR;
case CAIRO_OPERATOR_ADD:
return XCB_RENDER_PICT_OP_ADD;
case CAIRO_OPERATOR_SATURATE:
return XCB_RENDER_PICT_OP_SATURATE;
default:
return XCB_RENDER_PICT_OP_OVER;
}
}
static cairo_int_status_t
_cairo_xcb_surface_composite (cairo_operator_t op,
cairo_pattern_t *src_pattern,
cairo_pattern_t *mask_pattern,
void *abstract_dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_surface_attributes_t src_attr, mask_attr;
cairo_xcb_surface_t *dst = abstract_dst;
cairo_xcb_surface_t *src;
cairo_xcb_surface_t *mask;
cairo_int_status_t status;
composite_operation_t operation;
int itx, ity;
cairo_bool_t is_integer_translation;
if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
return CAIRO_INT_STATUS_UNSUPPORTED;
operation = _categorize_composite_operation (dst, op, src_pattern,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED)
return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_pattern_acquire_surfaces (src_pattern, mask_pattern,
&dst->base,
src_x, src_y,
mask_x, mask_y,
width, height,
(cairo_surface_t **) &src,
(cairo_surface_t **) &mask,
&src_attr, &mask_attr);
if (status)
return status;
operation = _recategorize_composite_operation (dst, op, src, &src_attr,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto BAIL;
}
status = _cairo_xcb_surface_set_attributes (src, &src_attr);
if (status)
goto BAIL;
switch (operation)
{
case DO_RENDER:
_cairo_xcb_surface_ensure_dst_picture (dst);
if (mask) {
status = _cairo_xcb_surface_set_attributes (mask, &mask_attr);
if (status)
goto BAIL;
xcb_render_composite (dst->dpy,
_render_operator (op),
src->src_picture,
mask->src_picture,
dst->dst_picture,
src_x + src_attr.x_offset,
src_y + src_attr.y_offset,
mask_x + mask_attr.x_offset,
mask_y + mask_attr.y_offset,
dst_x, dst_y,
width, height);
} else {
static xcb_render_picture_t maskpict = { XCB_NONE };
xcb_render_composite (dst->dpy,
_render_operator (op),
src->src_picture,
maskpict,
dst->dst_picture,
src_x + src_attr.x_offset,
src_y + src_attr.y_offset,
0, 0,
dst_x, dst_y,
width, height);
}
break;
case DO_XCOPYAREA:
_cairo_xcb_surface_ensure_gc (dst);
xcb_copy_area (dst->dpy,
src->drawable,
dst->drawable,
dst->gc,
src_x + src_attr.x_offset,
src_y + src_attr.y_offset,
dst_x, dst_y,
width, height);
break;
case DO_XTILE:
_cairo_xcb_surface_ensure_gc (dst);
is_integer_translation =
_cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity);
assert (is_integer_translation == TRUE);
{
uint32_t mask = XCB_GC_FILL_STYLE | XCB_GC_TILE
| XCB_GC_TILE_STIPPLE_ORIGIN_X
| XCB_GC_TILE_STIPPLE_ORIGIN_Y;
uint32_t values[] = {
XCB_FILL_STYLE_TILED, src->drawable,
- (itx + src_attr.x_offset),
- (ity + src_attr.y_offset)
};
xcb_rectangle_t rect = { dst_x, dst_y, width, height };
xcb_change_gc( dst->dpy, dst->gc, mask, values );
xcb_poly_fill_rectangle(dst->dpy, dst->drawable, dst->gc, 1, &rect);
}
break;
case DO_UNSUPPORTED:
default:
ASSERT_NOT_REACHED;
}
if (!_cairo_operator_bounded_by_source (op))
status = _cairo_surface_composite_fixup_unbounded (&dst->base,
&src_attr, src->width, src->height,
mask ? &mask_attr : NULL,
mask ? mask->width : 0,
mask ? mask->height : 0,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y, width, height);
BAIL:
if (mask)
_cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr);
_cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
return status;
}
static cairo_int_status_t
_cairo_xcb_surface_fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t * color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_xcb_surface_t *surface = abstract_surface;
xcb_render_color_t render_color;
xcb_rectangle_t static_xrects[16];
xcb_rectangle_t *xrects = static_xrects;
int i;
if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface))
return CAIRO_INT_STATUS_UNSUPPORTED;
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
if (num_rects > ARRAY_LENGTH(static_xrects)) {
xrects = _cairo_malloc_ab (num_rects, sizeof(xcb_rectangle_t));
if (xrects == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < num_rects; i++) {
xrects[i].x = rects[i].x;
xrects[i].y = rects[i].y;
xrects[i].width = rects[i].width;
xrects[i].height = rects[i].height;
}
_cairo_xcb_surface_ensure_dst_picture (surface);
xcb_render_fill_rectangles (surface->dpy,
_render_operator (op),
surface->dst_picture,
render_color, num_rects, xrects);
if (xrects != static_xrects)
free(xrects);
return CAIRO_STATUS_SUCCESS;
}
static xcb_render_picture_t
_create_a8_picture (cairo_xcb_surface_t *surface,
xcb_render_color_t *color,
int width,
int height,
cairo_bool_t repeat)
{
uint32_t values[] = { TRUE };
uint32_t mask = repeat ? XCB_RENDER_CP_REPEAT : 0;
xcb_pixmap_t pixmap = xcb_generate_id (surface->dpy);
xcb_render_picture_t picture = xcb_generate_id (surface->dpy);
xcb_render_pictforminfo_t *format
= _CAIRO_FORMAT_TO_XRENDER_FORMAT (surface->dpy, CAIRO_FORMAT_A8);
xcb_rectangle_t rect = { 0, 0, width, height };
xcb_create_pixmap (surface->dpy, 8, pixmap, surface->drawable,
width <= 0 ? 1 : width,
height <= 0 ? 1 : height);
xcb_render_create_picture (surface->dpy, picture, pixmap, format->id, mask, values);
xcb_render_fill_rectangles (surface->dpy, XCB_RENDER_PICT_OP_SRC, picture, *color, 1, &rect);
xcb_free_pixmap (surface->dpy, pixmap);
return picture;
}
static cairo_status_t
_create_trapezoid_mask (cairo_xcb_surface_t *dst,
cairo_trapezoid_t *traps,
int num_traps,
int dst_x,
int dst_y,
int width,
int height,
xcb_render_pictforminfo_t *pict_format,
xcb_render_picture_t *mask_picture_out)
{
xcb_render_color_t transparent = { 0, 0, 0, 0 };
xcb_render_color_t solid = { 0xffff, 0xffff, 0xffff, 0xffff };
xcb_render_picture_t mask_picture, solid_picture;
xcb_render_trapezoid_t *offset_traps;
int i;
mask_picture = _create_a8_picture (dst, &transparent, width, height, FALSE);
solid_picture = _create_a8_picture (dst, &solid, width, height, TRUE);
offset_traps = _cairo_malloc_ab (num_traps, sizeof (xcb_render_trapezoid_t));
if (offset_traps == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
for (i = 0; i < num_traps; i++) {
offset_traps[i].top = _cairo_fixed_to_16_16(traps[i].top) - 0x10000 * dst_y;
offset_traps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom) - 0x10000 * dst_y;
offset_traps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x) - 0x10000 * dst_x;
offset_traps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y) - 0x10000 * dst_y;
offset_traps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x) - 0x10000 * dst_x;
offset_traps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y) - 0x10000 * dst_y;
offset_traps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x) - 0x10000 * dst_x;
offset_traps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y) - 0x10000 * dst_y;
offset_traps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x) - 0x10000 * dst_x;
offset_traps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y) - 0x10000 * dst_y;
}
xcb_render_trapezoids (dst->dpy, XCB_RENDER_PICT_OP_ADD,
solid_picture, mask_picture,
pict_format->id,
0, 0,
num_traps, offset_traps);
xcb_render_free_picture (dst->dpy, solid_picture);
free (offset_traps);
*mask_picture_out = mask_picture;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_xcb_surface_composite_trapezoids (cairo_operator_t op,
cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias,
int src_x,
int src_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height,
cairo_trapezoid_t *traps,
int num_traps)
{
cairo_surface_attributes_t attributes;
cairo_xcb_surface_t *dst = abstract_dst;
cairo_xcb_surface_t *src;
cairo_int_status_t status;
composite_operation_t operation;
int render_reference_x, render_reference_y;
int render_src_x, render_src_y;
int cairo_format;
xcb_render_pictforminfo_t *render_format;
if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst))
return CAIRO_INT_STATUS_UNSUPPORTED;
operation = _categorize_composite_operation (dst, op, pattern, TRUE);
if (operation == DO_UNSUPPORTED)
return CAIRO_INT_STATUS_UNSUPPORTED;
status = _cairo_pattern_acquire_surface (pattern, &dst->base,
src_x, src_y, width, height,
(cairo_surface_t **) &src,
&attributes);
if (status)
return status;
operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE);
if (operation == DO_UNSUPPORTED) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto BAIL;
}
switch (antialias) {
case CAIRO_ANTIALIAS_NONE:
cairo_format = CAIRO_FORMAT_A1;
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_DEFAULT:
default:
cairo_format = CAIRO_FORMAT_A8;
break;
}
render_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dst->dpy, cairo_format);
if (traps[0].left.p1.y < traps[0].left.p2.y) {
render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p1.x);
render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p1.y);
} else {
render_reference_x = _cairo_fixed_integer_floor (traps[0].left.p2.x);
render_reference_y = _cairo_fixed_integer_floor (traps[0].left.p2.y);
}
render_src_x = src_x + render_reference_x - dst_x;
render_src_y = src_y + render_reference_y - dst_y;
_cairo_xcb_surface_ensure_dst_picture (dst);
status = _cairo_xcb_surface_set_attributes (src, &attributes);
if (status)
goto BAIL;
if (!_cairo_operator_bounded_by_mask (op)) {
xcb_render_picture_t mask_picture = 0;
status = _create_trapezoid_mask (dst, traps, num_traps,
dst_x, dst_y, width, height,
render_format,
&mask_picture);
if (status)
goto BAIL;
xcb_render_composite (dst->dpy,
_render_operator (op),
src->src_picture,
mask_picture,
dst->dst_picture,
src_x + attributes.x_offset,
src_y + attributes.y_offset,
0, 0,
dst_x, dst_y,
width, height);
xcb_render_free_picture (dst->dpy, mask_picture);
status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
&attributes, src->width, src->height,
width, height,
src_x, src_y,
0, 0,
dst_x, dst_y, width, height);
} else {
xcb_render_trapezoid_t xtraps_stack[16];
xcb_render_trapezoid_t *xtraps = xtraps_stack;
int i;
if (num_traps > ARRAY_LENGTH(xtraps_stack)) {
xtraps = _cairo_malloc_ab (num_traps, sizeof(xcb_render_trapezoid_t));
if (xtraps == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
}
for (i = 0; i < num_traps; i++) {
xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top);
xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom);
xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x);
xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y);
xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x);
xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y);
xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x);
xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y);
xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x);
xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y);
}
xcb_render_trapezoids (dst->dpy,
_render_operator (op),
src->src_picture, dst->dst_picture,
render_format->id,
render_src_x + attributes.x_offset,
render_src_y + attributes.y_offset,
num_traps, xtraps);
if (xtraps != xtraps_stack)
free(xtraps);
}
BAIL:
_cairo_pattern_release_surface (pattern, &src->base, &attributes);
return status;
}
static cairo_int_status_t
_cairo_xcb_surface_set_clip_region (void *abstract_surface,
cairo_region_t *region)
{
cairo_xcb_surface_t *surface = abstract_surface;
if (surface->clip_rects) {
free (surface->clip_rects);
surface->clip_rects = NULL;
}
surface->have_clip_rects = FALSE;
surface->num_clip_rects = 0;
if (region == NULL) {
uint32_t none[] = { XCB_NONE };
if (surface->gc)
xcb_change_gc (surface->dpy, surface->gc, XCB_GC_CLIP_MASK, none);
if (surface->xrender_format.id != XCB_NONE && surface->dst_picture)
xcb_render_change_picture (surface->dpy, surface->dst_picture,
XCB_RENDER_CP_CLIP_MASK, none);
} else {
cairo_box_int_t *boxes;
cairo_status_t status;
xcb_rectangle_t *rects = NULL;
int n_boxes, i;
status = _cairo_region_get_boxes (region, &n_boxes, &boxes);
if (status)
return status;
if (n_boxes > 0) {
rects = _cairo_malloc_ab (n_boxes, sizeof(xcb_rectangle_t));
if (rects == NULL) {
_cairo_region_boxes_fini (region, boxes);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
} else {
rects = NULL;
}
for (i = 0; i < n_boxes; i++) {
rects[i].x = boxes[i].p1.x;
rects[i].y = boxes[i].p1.y;
rects[i].width = boxes[i].p2.x - boxes[i].p1.x;
rects[i].height = boxes[i].p2.y - boxes[i].p1.y;
}
_cairo_region_boxes_fini (region, boxes);
surface->have_clip_rects = TRUE;
surface->clip_rects = rects;
surface->num_clip_rects = n_boxes;
if (surface->gc)
_cairo_xcb_surface_set_gc_clip_rects (surface);
if (surface->dst_picture)
_cairo_xcb_surface_set_picture_clip_rects (surface);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_xcb_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_xcb_surface_t *surface = abstract_surface;
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
static void
_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font);
static cairo_int_status_t
_cairo_xcb_surface_show_glyphs (void *abstract_dst,
cairo_operator_t op,
cairo_pattern_t *src_pattern,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
int *remaining_glyphs);
static cairo_bool_t
_cairo_xcb_surface_is_similar (void *surface_a,
void *surface_b,
cairo_content_t content)
{
cairo_xcb_surface_t *a = surface_a;
cairo_xcb_surface_t *b = surface_b;
xcb_render_pictforminfo_t *xrender_format;
return FALSE;
if (! _cairo_xcb_surface_same_screen (a, b))
return FALSE;
xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (b->dpy,
_cairo_format_from_content (content));
return a->xrender_format.id == xrender_format->id;
}
static cairo_status_t
_cairo_xcb_surface_reset (void *abstract_surface)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_status_t status;
status = _cairo_xcb_surface_set_clip_region (surface, NULL);
if (status)
return status;
return CAIRO_STATUS_SUCCESS;
}
static const cairo_surface_backend_t cairo_xcb_surface_backend = {
CAIRO_SURFACE_TYPE_XCB,
_cairo_xcb_surface_create_similar,
_cairo_xcb_surface_finish,
_cairo_xcb_surface_acquire_source_image,
_cairo_xcb_surface_release_source_image,
_cairo_xcb_surface_acquire_dest_image,
_cairo_xcb_surface_release_dest_image,
_cairo_xcb_surface_clone_similar,
_cairo_xcb_surface_composite,
_cairo_xcb_surface_fill_rectangles,
_cairo_xcb_surface_composite_trapezoids,
NULL,
NULL,
_cairo_xcb_surface_set_clip_region,
NULL,
_cairo_xcb_surface_get_extents,
NULL,
NULL,
NULL,
NULL,
_cairo_xcb_surface_scaled_font_fini,
_cairo_xcb_surface_scaled_glyph_fini,
NULL,
NULL,
NULL,
NULL,
_cairo_xcb_surface_show_glyphs,
NULL,
_cairo_xcb_surface_is_similar,
_cairo_xcb_surface_reset
};
static cairo_bool_t
_cairo_surface_is_xcb (cairo_surface_t *surface)
{
return surface->backend == &cairo_xcb_surface_backend;
}
static cairo_surface_t *
_cairo_xcb_surface_create_internal (xcb_connection_t *dpy,
xcb_drawable_t drawable,
xcb_screen_t *screen,
xcb_visualtype_t *visual,
xcb_render_pictforminfo_t *xrender_format,
int width,
int height,
int depth)
{
cairo_xcb_surface_t *surface;
const xcb_query_extension_reply_t *er;
const xcb_render_query_version_reply_t *r = NULL;
surface = malloc (sizeof (cairo_xcb_surface_t));
if (surface == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
if (xrender_format) {
depth = xrender_format->depth;
} else if (visual) {
xcb_depth_iterator_t depths;
xcb_visualtype_iterator_t visuals;
depths = xcb_screen_allowed_depths_iterator(screen);
for(; depths.rem; xcb_depth_next(&depths))
{
visuals = xcb_depth_visuals_iterator(depths.data);
for(; visuals.rem; xcb_visualtype_next(&visuals))
{
if(visuals.data->visual_id == visual->visual_id)
{
depth = depths.data->depth;
goto found;
}
}
}
found:
;
}
er = xcb_get_extension_data(dpy, &xcb_render_id);
if(er && er->present) {
r = xcb_render_util_query_version(dpy);
}
if (r) {
surface->render_major = r->major_version;
surface->render_minor = r->minor_version;
} else {
surface->render_major = -1;
surface->render_minor = -1;
}
if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) {
if (!xrender_format) {
if (visual) {
const xcb_render_query_pict_formats_reply_t *formats;
xcb_render_pictvisual_t *pict_visual;
formats = xcb_render_util_query_formats (dpy);
pict_visual = xcb_render_util_find_visual_format (formats, visual->visual_id);
if (pict_visual) {
xcb_render_pictforminfo_t template;
template.id = pict_visual->format;
xrender_format = xcb_render_util_find_format (formats, XCB_PICT_FORMAT_ID, &template, 0);
}
} else if (depth == 1) {
xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dpy, CAIRO_FORMAT_A1);
}
}
} else {
xrender_format = NULL;
}
_cairo_surface_init (&surface->base, &cairo_xcb_surface_backend,
_xcb_render_format_to_content (xrender_format));
surface->dpy = dpy;
surface->gc = XCB_NONE;
surface->drawable = drawable;
surface->screen = screen;
surface->owns_pixmap = FALSE;
surface->use_pixmap = 0;
surface->width = width;
surface->height = height;
surface->dst_picture = XCB_NONE;
surface->src_picture = XCB_NONE;
surface->visual = visual;
surface->xrender_format.id = XCB_NONE;
if (xrender_format) surface->xrender_format = *xrender_format;
surface->depth = depth;
surface->have_clip_rects = FALSE;
surface->clip_rects = NULL;
surface->num_clip_rects = 0;
return (cairo_surface_t *) surface;
}
static xcb_screen_t *
_cairo_xcb_screen_from_visual (xcb_connection_t *c, xcb_visualtype_t *visual)
{
xcb_depth_iterator_t d;
xcb_screen_iterator_t s = xcb_setup_roots_iterator(xcb_get_setup(c));
for (; s.rem; xcb_screen_next(&s))
{
if (s.data->root_visual == visual->visual_id)
return s.data;
d = xcb_screen_allowed_depths_iterator(s.data);
for (; d.rem; xcb_depth_next(&d))
{
xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator(d.data);
for (; v.rem; xcb_visualtype_next(&v))
{
if (v.data->visual_id == visual->visual_id)
return s.data;
}
}
}
return NULL;
}
cairo_surface_t *
cairo_xcb_surface_create (xcb_connection_t *c,
xcb_drawable_t drawable,
xcb_visualtype_t *visual,
int width,
int height)
{
xcb_screen_t *screen = _cairo_xcb_screen_from_visual (c, visual);
if (screen == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
return _cairo_xcb_surface_create_internal (c, drawable, screen,
visual, NULL,
width, height, 0);
}
cairo_surface_t *
cairo_xcb_surface_create_for_bitmap (xcb_connection_t *c,
xcb_pixmap_t bitmap,
xcb_screen_t *screen,
int width,
int height)
{
return _cairo_xcb_surface_create_internal (c, bitmap, screen,
NULL, NULL,
width, height, 1);
}
cairo_surface_t *
cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *c,
xcb_drawable_t drawable,
xcb_screen_t *screen,
xcb_render_pictforminfo_t *format,
int width,
int height)
{
return _cairo_xcb_surface_create_internal (c, drawable, screen,
NULL, format,
width, height, 0);
}
slim_hidden_def (cairo_xcb_surface_create_with_xrender_format);
void
cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
int width,
int height)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) abstract_surface;
cairo_status_t status_ignored;
if (! _cairo_surface_is_xcb (abstract_surface)) {
status_ignored = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
surface->width = width;
surface->height = height;
}
typedef struct _cairo_xcb_surface_font_private {
xcb_connection_t *dpy;
xcb_render_glyphset_t glyphset;
cairo_format_t format;
xcb_render_pictforminfo_t *xrender_format;
} cairo_xcb_surface_font_private_t;
static cairo_status_t
_cairo_xcb_surface_font_init (xcb_connection_t *dpy,
cairo_scaled_font_t *scaled_font,
cairo_format_t format)
{
cairo_xcb_surface_font_private_t *font_private;
font_private = malloc (sizeof (cairo_xcb_surface_font_private_t));
if (!font_private)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
font_private->dpy = dpy;
font_private->format = format;
font_private->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(dpy, format);
font_private->glyphset = xcb_generate_id(dpy);
xcb_render_create_glyph_set (dpy, font_private->glyphset, font_private->xrender_format->id);
scaled_font->surface_private = font_private;
scaled_font->surface_backend = &cairo_xcb_surface_backend;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private;
if (font_private) {
xcb_render_free_glyph_set (font_private->dpy, font_private->glyphset);
free (font_private);
}
}
static void
_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private;
if (font_private != NULL && scaled_glyph->surface_private != NULL) {
xcb_render_glyph_t glyph_index = _cairo_scaled_glyph_index(scaled_glyph);
xcb_render_free_glyphs (font_private->dpy,
font_private->glyphset,
1, &glyph_index);
}
}
static cairo_bool_t
_native_byte_order_lsb (void)
{
int x = 1;
return *((char *) &x) == 1;
}
static cairo_status_t
_cairo_xcb_surface_add_glyph (xcb_connection_t *dpy,
cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
xcb_render_glyphinfo_t glyph_info;
xcb_render_glyph_t glyph_index;
unsigned char *data;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_xcb_surface_font_private_t *font_private;
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
if (scaled_font->surface_private == NULL) {
status = _cairo_xcb_surface_font_init (dpy, scaled_font,
glyph_surface->format);
if (status)
return status;
}
font_private = scaled_font->surface_private;
if (glyph_surface->format != font_private->format) {
cairo_t *cr;
cairo_surface_t *tmp_surface;
double x_offset, y_offset;
tmp_surface = cairo_image_surface_create (font_private->format,
glyph_surface->width,
glyph_surface->height);
cr = cairo_create (tmp_surface);
cairo_surface_get_device_offset (&glyph_surface->base, &x_offset, &y_offset);
cairo_set_source_surface (cr, &glyph_surface->base, x_offset, y_offset);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
status = cairo_status (cr);
cairo_destroy (cr);
tmp_surface->device_transform = glyph_surface->base.device_transform;
tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
glyph_surface = (cairo_image_surface_t *) tmp_surface;
if (status)
goto BAIL;
}
glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
glyph_info.width = glyph_surface->width;
glyph_info.height = glyph_surface->height;
glyph_info.x_off = 0;
glyph_info.y_off = 0;
data = glyph_surface->data;
switch (scaled_glyph->surface->format) {
case CAIRO_FORMAT_A1:
if (_native_byte_order_lsb() != (xcb_get_setup(dpy)->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
int c = glyph_surface->stride * glyph_surface->height;
unsigned char *d;
unsigned char *new, *n;
new = malloc (c);
if (!new) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
n = new;
d = data;
while (c--)
{
char b = *d++;
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
*n++ = b;
}
data = new;
}
break;
case CAIRO_FORMAT_A8:
break;
case CAIRO_FORMAT_ARGB32:
if (_native_byte_order_lsb() != (xcb_get_setup(dpy)->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
unsigned int c = glyph_surface->stride * glyph_surface->height;
unsigned char *d;
unsigned char *new, *n;
new = malloc (c);
if (new == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
n = new;
d = data;
while (c >= 4)
{
n[3] = d[0];
n[2] = d[1];
n[1] = d[2];
n[0] = d[3];
d += 4;
n += 4;
c -= 4;
}
data = new;
}
break;
case CAIRO_FORMAT_RGB24:
default:
ASSERT_NOT_REACHED;
break;
}
glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
xcb_render_add_glyphs (dpy, font_private->glyphset,
1, &glyph_index, &glyph_info,
glyph_surface->stride * glyph_surface->height,
data);
if (data != glyph_surface->data)
free (data);
BAIL:
if (glyph_surface != scaled_glyph->surface)
cairo_surface_destroy (&glyph_surface->base);
return status;
}
#define N_STACK_BUF 1024
static cairo_status_t
_cairo_xcb_surface_show_glyphs_8 (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_xcb_surface_t *src,
int src_x_offset, int src_y_offset,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private;
xcb_render_util_composite_text_stream_t *stream;
int i;
int thisX, thisY;
int lastX = 0, lastY = 0;
uint8_t glyph;
stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0);
for (i = 0; i < num_glyphs; ++i) {
thisX = _cairo_lround (glyphs[i].x);
thisY = _cairo_lround (glyphs[i].y);
glyph = glyphs[i].index;
xcb_render_util_glyphs_8 (stream, thisX - lastX, thisY - lastY, 1, &glyph);
lastX = thisX;
lastY = thisY;
}
xcb_render_util_composite_text (dst->dpy,
_render_operator (op),
src->src_picture,
dst->dst_picture,
font_private->xrender_format->id,
src_x_offset + _cairo_lround (glyphs[0].x),
src_y_offset + _cairo_lround (glyphs[0].y),
stream);
xcb_render_util_composite_text_free (stream);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_xcb_surface_show_glyphs_16 (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_xcb_surface_t *src,
int src_x_offset, int src_y_offset,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private;
xcb_render_util_composite_text_stream_t *stream;
int i;
int thisX, thisY;
int lastX = 0, lastY = 0;
uint16_t glyph;
stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0);
for (i = 0; i < num_glyphs; ++i) {
thisX = _cairo_lround (glyphs[i].x);
thisY = _cairo_lround (glyphs[i].y);
glyph = glyphs[i].index;
xcb_render_util_glyphs_16 (stream, thisX - lastX, thisY - lastY, 1, &glyph);
lastX = thisX;
lastY = thisY;
}
xcb_render_util_composite_text (dst->dpy,
_render_operator (op),
src->src_picture,
dst->dst_picture,
font_private->xrender_format->id,
src_x_offset + _cairo_lround (glyphs[0].x),
src_y_offset + _cairo_lround (glyphs[0].y),
stream);
xcb_render_util_composite_text_free (stream);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_xcb_surface_show_glyphs_32 (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_xcb_surface_t *src,
int src_x_offset, int src_y_offset,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private = scaled_font->surface_private;
xcb_render_util_composite_text_stream_t *stream;
int i;
int thisX, thisY;
int lastX = 0, lastY = 0;
uint32_t glyph;
stream = xcb_render_util_composite_text_stream (font_private->glyphset, num_glyphs, 0);
for (i = 0; i < num_glyphs; ++i) {
thisX = _cairo_lround (glyphs[i].x);
thisY = _cairo_lround (glyphs[i].y);
glyph = glyphs[i].index;
xcb_render_util_glyphs_32 (stream, thisX - lastX, thisY - lastY, 1, &glyph);
lastX = thisX;
lastY = thisY;
}
xcb_render_util_composite_text (dst->dpy,
_render_operator (op),
src->src_picture,
dst->dst_picture,
font_private->xrender_format->id,
src_x_offset + _cairo_lround (glyphs[0].x),
src_y_offset + _cairo_lround (glyphs[0].y),
stream);
xcb_render_util_composite_text_free (stream);
return CAIRO_STATUS_SUCCESS;
}
typedef cairo_status_t (*cairo_xcb_surface_show_glyphs_func_t)
(cairo_xcb_surface_t *, cairo_operator_t, cairo_xcb_surface_t *, int, int,
const cairo_glyph_t *, int, cairo_scaled_font_t *);
static cairo_bool_t
_cairo_xcb_surface_owns_font (cairo_xcb_surface_t *dst,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_surface_font_private_t *font_private;
font_private = scaled_font->surface_private;
if ((scaled_font->surface_backend != NULL &&
scaled_font->surface_backend != &cairo_xcb_surface_backend) ||
(font_private != NULL && font_private->dpy != dst->dpy))
{
return FALSE;
}
return TRUE;
}
static cairo_status_t
_cairo_xcb_surface_emit_glyphs (cairo_xcb_surface_t *dst,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_operator_t op,
cairo_xcb_surface_t *src,
cairo_surface_attributes_t *attributes,
int *remaining_glyphs)
{
cairo_scaled_glyph_t *scaled_glyph;
int i, o;
unsigned long max_index = 0;
cairo_status_t status;
cairo_glyph_t *output_glyphs;
const cairo_glyph_t *glyphs_chunk;
int glyphs_remaining, chunk_size, max_chunk_size;
cairo_xcb_surface_show_glyphs_func_t show_glyphs_func;
output_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (output_glyphs == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
for (i = 0, o = 0; i < num_glyphs; i++) {
if (glyphs[i].index > max_index)
max_index = glyphs[i].index;
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (status) {
free (output_glyphs);
return status;
}
if (scaled_glyph->surface->width && scaled_glyph->surface->height) {
output_glyphs[o++] = glyphs[i];
if (scaled_glyph->surface_private == NULL) {
_cairo_xcb_surface_add_glyph (dst->dpy, scaled_font, scaled_glyph);
scaled_glyph->surface_private = (void *) 1;
}
}
}
num_glyphs = o;
_cairo_xcb_surface_ensure_dst_picture (dst);
max_chunk_size = xcb_get_maximum_request_length (dst->dpy);
if (max_index < 256) {
max_chunk_size -= sizeof(xcb_render_composite_glyphs_8_request_t);
show_glyphs_func = _cairo_xcb_surface_show_glyphs_8;
} else if (max_index < 65536) {
max_chunk_size -= sizeof(xcb_render_composite_glyphs_16_request_t);
show_glyphs_func = _cairo_xcb_surface_show_glyphs_16;
} else {
max_chunk_size -= sizeof(xcb_render_composite_glyphs_32_request_t);
show_glyphs_func = _cairo_xcb_surface_show_glyphs_32;
}
max_chunk_size /= 3*sizeof(uint32_t);
for (glyphs_remaining = num_glyphs, glyphs_chunk = output_glyphs;
glyphs_remaining;
glyphs_remaining -= chunk_size, glyphs_chunk += chunk_size)
{
chunk_size = MIN (glyphs_remaining, max_chunk_size);
status = show_glyphs_func (dst, op, src,
attributes->x_offset, attributes->y_offset,
glyphs_chunk, chunk_size, scaled_font);
if (status) {
free (output_glyphs);
return status;
}
}
free(output_glyphs);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_xcb_surface_show_glyphs (void *abstract_dst,
cairo_operator_t op,
cairo_pattern_t *src_pattern,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
int *remaining_glyphs)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_xcb_surface_t *dst = abstract_dst;
composite_operation_t operation;
cairo_surface_attributes_t attributes;
cairo_xcb_surface_t *src = NULL;
cairo_solid_pattern_t solid_pattern;
if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst) || dst->xrender_format.id == XCB_NONE)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (!_cairo_operator_bounded_by_mask (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_SOURCE &&
!CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (dst->base.clip &&
(dst->base.clip->mode != CAIRO_CLIP_MODE_REGION ||
dst->base.clip->surface != NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
operation = _categorize_composite_operation (dst, op, src_pattern, TRUE);
if (operation == DO_UNSUPPORTED)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (! _cairo_xcb_surface_owns_font (dst, scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_CLEAR) {
_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
CAIRO_CONTENT_COLOR);
src_pattern = &solid_pattern.base;
op = CAIRO_OPERATOR_DEST_OUT;
}
if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
0, 0, 1, 1,
(cairo_surface_t **) &src,
&attributes);
} else {
cairo_rectangle_int_t glyph_extents;
status = _cairo_scaled_font_glyph_device_extents (scaled_font,
glyphs,
num_glyphs,
&glyph_extents);
if (status)
goto BAIL;
status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
glyph_extents.x, glyph_extents.y,
glyph_extents.width, glyph_extents.height,
(cairo_surface_t **) &src,
&attributes);
}
if (status)
goto BAIL;
operation = _recategorize_composite_operation (dst, op, src, &attributes, TRUE);
if (operation == DO_UNSUPPORTED) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto BAIL;
}
status = _cairo_xcb_surface_set_attributes (src, &attributes);
if (status)
goto BAIL;
_cairo_scaled_font_freeze_cache (scaled_font);
if (_cairo_xcb_surface_owns_font (dst, scaled_font))
status = _cairo_xcb_surface_emit_glyphs (dst,
glyphs, num_glyphs,
scaled_font,
op,
src,
&attributes,
remaining_glyphs);
else
status = CAIRO_INT_STATUS_UNSUPPORTED;
_cairo_scaled_font_thaw_cache (scaled_font);
BAIL:
if (src)
_cairo_pattern_release_surface (src_pattern, &src->base, &attributes);
if (src_pattern == &solid_pattern.base)
_cairo_pattern_fini (&solid_pattern.base);
return status;
}