mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-03 06:38:06 +02:00
Introduce cairo_xlib_device_debug_set_precision() to override the automatic selection of rendering precision and force the Xorg/DDX to strictly adhere to the precise rendering mode of the Render specification. This allows us to test drivers without worrying, too much, about minor discrepancies in antialiasing. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
4476 lines
128 KiB
C
4476 lines
128 KiB
C
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2009 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* Contributor(s):
|
|
* Chris Wilson <chris@chris-wilson.co.uk>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-boxes-private.h"
|
|
#include "cairo-clip-private.h"
|
|
#include "cairo-composite-rectangles-private.h"
|
|
#include "cairo-region-private.h"
|
|
#include "cairo-surface-offset-private.h"
|
|
#include "cairo-surface-snapshot-private.h"
|
|
#include "cairo-surface-subsurface-private.h"
|
|
#include "cairo-xcb-private.h"
|
|
|
|
#if CAIRO_HAS_XCB_DRM_FUNCTIONS && CAIRO_HAS_DRM_SURFACE
|
|
#include "drm/cairo-drm-private.h"
|
|
#endif
|
|
|
|
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
|
|
|
|
/**
|
|
* SECTION:cairo-xcb-xrender
|
|
* @Title: XCB Surfaces
|
|
* @Short_Description: X Window System rendering using the XCB library and the X Render extension
|
|
* @See_Also: #cairo_surface_t
|
|
*
|
|
* The XCB surface is used to render cairo graphics to X Window System
|
|
* windows and pixmaps using the XCB library and its X Render extension.
|
|
*
|
|
* Note that the XCB surface automatically takes advantage of the X Render
|
|
* extension if it is available.
|
|
*/
|
|
|
|
typedef struct _cairo_xcb_picture {
|
|
cairo_surface_t base;
|
|
|
|
cairo_xcb_screen_t *screen;
|
|
xcb_render_picture_t picture;
|
|
xcb_render_pictformat_t xrender_format;
|
|
pixman_format_code_t pixman_format;
|
|
|
|
int width, height;
|
|
|
|
cairo_extend_t extend;
|
|
cairo_filter_t filter;
|
|
cairo_bool_t has_component_alpha;
|
|
xcb_render_transform_t transform;
|
|
|
|
int x0, y0;
|
|
int x, y;
|
|
} cairo_xcb_picture_t;
|
|
|
|
static inline cairo_xcb_connection_t *
|
|
_picture_to_connection (cairo_xcb_picture_t *picture)
|
|
{
|
|
return (cairo_xcb_connection_t *) picture->base.device;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface);
|
|
|
|
static uint32_t
|
|
hars_petruska_f54_1_random (void)
|
|
{
|
|
#define rol(x,k) ((x << k) | (x >> (32-k)))
|
|
static uint32_t x;
|
|
return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
|
|
#undef rol
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_picture_finish (void *abstract_surface)
|
|
{
|
|
cairo_xcb_picture_t *surface = abstract_surface;
|
|
cairo_xcb_connection_t *connection = _picture_to_connection (surface);
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_xcb_connection_acquire (connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xcb_connection_render_free_picture (connection, surface->picture);
|
|
|
|
_cairo_xcb_connection_release (connection);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const cairo_surface_backend_t _cairo_xcb_picture_backend = {
|
|
CAIRO_SURFACE_TYPE_XCB,
|
|
NULL,
|
|
_cairo_xcb_picture_finish,
|
|
};
|
|
|
|
static const struct xcb_render_transform_t identity_transform = {
|
|
1 << 16, 0, 0,
|
|
0, 1 << 16, 0,
|
|
0, 0, 1 << 16,
|
|
};
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_picture_create (cairo_xcb_screen_t *screen,
|
|
pixman_format_code_t pixman_format,
|
|
xcb_render_pictformat_t xrender_format,
|
|
int width, int height)
|
|
{
|
|
cairo_xcb_picture_t *surface;
|
|
|
|
surface = malloc (sizeof (cairo_xcb_picture_t));
|
|
if (unlikely (surface == NULL))
|
|
return (cairo_xcb_picture_t *)
|
|
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&_cairo_xcb_picture_backend,
|
|
&screen->connection->device,
|
|
_cairo_content_from_pixman_format (pixman_format));
|
|
|
|
surface->screen = screen;
|
|
surface->picture = _cairo_xcb_connection_get_xid (screen->connection);
|
|
surface->pixman_format = pixman_format;
|
|
surface->xrender_format = xrender_format;
|
|
|
|
surface->x0 = surface->y0 = 0;
|
|
surface->x = surface->y = 0;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
|
|
surface->transform = identity_transform;
|
|
surface->extend = CAIRO_EXTEND_NONE;
|
|
surface->filter = CAIRO_FILTER_NEAREST;
|
|
surface->has_component_alpha = FALSE;
|
|
|
|
return surface;
|
|
}
|
|
|
|
static inline cairo_bool_t
|
|
_operator_is_supported (uint32_t flags, cairo_operator_t op)
|
|
{
|
|
if (op <= CAIRO_OPERATOR_SATURATE)
|
|
return TRUE;
|
|
|
|
/* TODO Support pdf operators via CAIRO_XCB_RENDER_HAS_PDF_OPERATORS */
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
_render_operator (cairo_operator_t op)
|
|
{
|
|
#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y
|
|
switch (op) {
|
|
C(CLEAR, CLEAR);
|
|
C(SOURCE, SRC);
|
|
|
|
C(OVER, OVER);
|
|
C(IN, IN);
|
|
C(OUT, OUT);
|
|
C(ATOP, ATOP);
|
|
|
|
C(DEST, DST);
|
|
C(DEST_OVER, OVER_REVERSE);
|
|
C(DEST_IN, IN_REVERSE);
|
|
C(DEST_OUT, OUT_REVERSE);
|
|
C(DEST_ATOP, ATOP_REVERSE);
|
|
|
|
C(XOR, XOR);
|
|
C(ADD, ADD);
|
|
C(SATURATE, SATURATE);
|
|
|
|
case CAIRO_OPERATOR_MULTIPLY:
|
|
case CAIRO_OPERATOR_SCREEN:
|
|
case CAIRO_OPERATOR_OVERLAY:
|
|
case CAIRO_OPERATOR_DARKEN:
|
|
case CAIRO_OPERATOR_LIGHTEN:
|
|
case CAIRO_OPERATOR_COLOR_DODGE:
|
|
case CAIRO_OPERATOR_COLOR_BURN:
|
|
case CAIRO_OPERATOR_HARD_LIGHT:
|
|
case CAIRO_OPERATOR_SOFT_LIGHT:
|
|
case CAIRO_OPERATOR_DIFFERENCE:
|
|
case CAIRO_OPERATOR_EXCLUSION:
|
|
case CAIRO_OPERATOR_HSL_HUE:
|
|
case CAIRO_OPERATOR_HSL_SATURATION:
|
|
case CAIRO_OPERATOR_HSL_COLOR:
|
|
case CAIRO_OPERATOR_HSL_LUMINOSITY:
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
return XCB_RENDER_PICT_OP_OVER;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface,
|
|
cairo_region_t *region)
|
|
{
|
|
xcb_rectangle_t rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
|
|
int i, num_rects;
|
|
|
|
num_rects = cairo_region_num_rectangles (region);
|
|
assert (num_rects < ARRAY_LENGTH (rects)); /* XXX somebody will! */
|
|
|
|
for (i = 0; i < num_rects; i++) {
|
|
cairo_rectangle_int_t rect;
|
|
|
|
cairo_region_get_rectangle (region, i, &rect);
|
|
|
|
rects[i].x = rect.x;
|
|
rects[i].y = rect.y;
|
|
rects[i].width = rect.width;
|
|
rects[i].height = rect.height;
|
|
}
|
|
|
|
_cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection,
|
|
surface->picture,
|
|
0, 0,
|
|
num_rects, rects);
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface)
|
|
{
|
|
uint32_t values[] = { XCB_NONE };
|
|
_cairo_xcb_connection_render_change_picture (surface->connection,
|
|
surface->picture,
|
|
XCB_RENDER_CP_CLIP_MASK,
|
|
values);
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_surface_set_precision (cairo_xcb_surface_t *surface,
|
|
cairo_antialias_t antialias)
|
|
{
|
|
cairo_xcb_connection_t *connection = surface->connection;
|
|
uint32_t precision;
|
|
|
|
if (connection->force_precision != -1)
|
|
precision = connection->force_precision;
|
|
else switch (antialias) {
|
|
default:
|
|
case CAIRO_ANTIALIAS_DEFAULT:
|
|
case CAIRO_ANTIALIAS_GRAY:
|
|
case CAIRO_ANTIALIAS_NONE:
|
|
precision = 1;
|
|
break;
|
|
case CAIRO_ANTIALIAS_SUBPIXEL:
|
|
precision = 0;
|
|
break;
|
|
}
|
|
|
|
if (surface->precision != precision) {
|
|
_cairo_xcb_connection_render_change_picture (connection,
|
|
surface->picture,
|
|
XCB_RENDER_CP_POLY_MODE,
|
|
&precision);
|
|
surface->precision = precision;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface)
|
|
{
|
|
assert (surface->fallback == NULL);
|
|
if (surface->picture == XCB_NONE) {
|
|
surface->picture = _cairo_xcb_connection_get_xid (surface->connection);
|
|
_cairo_xcb_connection_render_create_picture (surface->connection,
|
|
surface->picture,
|
|
surface->drawable,
|
|
surface->xrender_format,
|
|
0, NULL);
|
|
}
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_picture_from_image (cairo_xcb_surface_t *target,
|
|
xcb_render_pictformat_t format,
|
|
cairo_image_surface_t *image,
|
|
cairo_xcb_shm_info_t *shm_info)
|
|
{
|
|
xcb_pixmap_t pixmap;
|
|
xcb_gcontext_t gc;
|
|
cairo_xcb_picture_t *picture;
|
|
|
|
pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
|
|
image->depth,
|
|
target->drawable,
|
|
image->width, image->height);
|
|
|
|
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth);
|
|
|
|
if (shm_info != NULL) {
|
|
_cairo_xcb_connection_shm_put_image (target->connection,
|
|
pixmap, gc,
|
|
image->width, image->height,
|
|
0, 0,
|
|
image->width, image->height,
|
|
0, 0,
|
|
image->depth,
|
|
shm_info->shm,
|
|
shm_info->offset);
|
|
} else {
|
|
int len;
|
|
|
|
/* Do we need to trim the image? */
|
|
len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format));
|
|
if (len == image->stride) {
|
|
_cairo_xcb_connection_put_image (target->connection,
|
|
pixmap, gc,
|
|
image->width, image->height,
|
|
0, 0,
|
|
image->depth,
|
|
image->stride,
|
|
image->data);
|
|
} else {
|
|
_cairo_xcb_connection_put_subimage (target->connection,
|
|
pixmap, gc,
|
|
0, 0,
|
|
image->width, image->height,
|
|
PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
|
|
image->stride,
|
|
0, 0,
|
|
image->depth,
|
|
image->data);
|
|
|
|
}
|
|
}
|
|
|
|
_cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
|
|
|
|
picture = _cairo_xcb_picture_create (target->screen,
|
|
image->pixman_format, format,
|
|
image->width, image->height);
|
|
if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) {
|
|
_cairo_xcb_connection_render_create_picture (target->connection,
|
|
picture->picture, pixmap, format,
|
|
0, 0);
|
|
}
|
|
|
|
_cairo_xcb_connection_free_pixmap (target->connection, pixmap);
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_pattern_is_supported (uint32_t flags,
|
|
const cairo_pattern_t *pattern)
|
|
|
|
{
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
|
|
return TRUE;
|
|
|
|
if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) {
|
|
if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0)
|
|
return FALSE;
|
|
}
|
|
|
|
switch (pattern->extend) {
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_EXTEND_NONE:
|
|
case CAIRO_EXTEND_REPEAT:
|
|
break;
|
|
case CAIRO_EXTEND_PAD:
|
|
case CAIRO_EXTEND_REFLECT:
|
|
if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0)
|
|
return FALSE;
|
|
}
|
|
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
|
|
cairo_filter_t filter;
|
|
|
|
filter = pattern->filter;
|
|
if (_cairo_matrix_has_unity_scale (&pattern->matrix) &&
|
|
_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
|
|
{
|
|
filter = CAIRO_FILTER_NEAREST;
|
|
}
|
|
|
|
if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) {
|
|
if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0)
|
|
return FALSE;
|
|
}
|
|
} else { /* gradient */
|
|
if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
|
|
return FALSE;
|
|
|
|
/* The RENDER specification says that the inner circle has to be
|
|
* completely contained inside the outer one. */
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
|
|
! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return pattern->type != CAIRO_PATTERN_TYPE_MESH;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture,
|
|
const cairo_matrix_t *matrix,
|
|
cairo_filter_t filter,
|
|
double xc, double yc)
|
|
{
|
|
xcb_render_transform_t transform;
|
|
pixman_transform_t *pixman_transform;
|
|
cairo_status_t ignored;
|
|
|
|
/* Casting between pixman_transform_t and xcb_render_transform_t is safe
|
|
* because they happen to be the exact same type.
|
|
*/
|
|
pixman_transform = (pixman_transform_t *) &transform;
|
|
|
|
picture->x = picture->x0;
|
|
picture->y = picture->y0;
|
|
ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
|
|
pixman_transform,
|
|
&picture->x, &picture->y);
|
|
|
|
if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) {
|
|
_cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture),
|
|
picture->picture,
|
|
&transform);
|
|
|
|
picture->transform = transform;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture,
|
|
cairo_filter_t filter)
|
|
{
|
|
const char *render_filter;
|
|
int len;
|
|
|
|
if (picture->filter == filter)
|
|
return;
|
|
|
|
switch (filter) {
|
|
case CAIRO_FILTER_FAST:
|
|
render_filter = "fast";
|
|
len = strlen ("fast");
|
|
break;
|
|
|
|
case CAIRO_FILTER_GOOD:
|
|
render_filter = "good";
|
|
len = strlen ("good");
|
|
break;
|
|
|
|
case CAIRO_FILTER_BEST:
|
|
render_filter = "best";
|
|
len = strlen ("best");
|
|
break;
|
|
|
|
case CAIRO_FILTER_NEAREST:
|
|
render_filter = "nearest";
|
|
len = strlen ("nearest");
|
|
break;
|
|
|
|
case CAIRO_FILTER_BILINEAR:
|
|
render_filter = "bilinear";
|
|
len = strlen ("bilinear");
|
|
break;
|
|
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_FILTER_GAUSSIAN:
|
|
render_filter = "best";
|
|
len = strlen ("best");
|
|
break;
|
|
}
|
|
|
|
_cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture),
|
|
picture->picture,
|
|
len, (char *) render_filter);
|
|
picture->filter = filter;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture,
|
|
cairo_extend_t extend)
|
|
{
|
|
uint32_t pa[1];
|
|
|
|
if (picture->extend == extend)
|
|
return;
|
|
|
|
switch (extend) {
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_EXTEND_NONE:
|
|
pa[0] = XCB_RENDER_REPEAT_NONE;
|
|
break;
|
|
|
|
case CAIRO_EXTEND_REPEAT:
|
|
pa[0] = XCB_RENDER_REPEAT_NORMAL;
|
|
break;
|
|
|
|
case CAIRO_EXTEND_REFLECT:
|
|
pa[0] = XCB_RENDER_REPEAT_REFLECT;
|
|
break;
|
|
|
|
case CAIRO_EXTEND_PAD:
|
|
pa[0] = XCB_RENDER_REPEAT_PAD;
|
|
break;
|
|
}
|
|
|
|
_cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
|
|
picture->picture,
|
|
XCB_RENDER_CP_REPEAT, pa);
|
|
picture->extend = extend;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture,
|
|
cairo_bool_t ca)
|
|
{
|
|
uint32_t pa[1];
|
|
|
|
if (picture->has_component_alpha == ca)
|
|
return;
|
|
|
|
pa[0] = ca;
|
|
|
|
_cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
|
|
picture->picture,
|
|
XCB_RENDER_CP_COMPONENT_ALPHA,
|
|
pa);
|
|
picture->has_component_alpha = ca;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_solid_picture (cairo_xcb_surface_t *target,
|
|
const cairo_color_t *color)
|
|
{
|
|
xcb_render_color_t xcb_color;
|
|
xcb_render_pictformat_t xrender_format;
|
|
cairo_xcb_picture_t *picture;
|
|
|
|
xcb_color.red = color->red_short;
|
|
xcb_color.green = color->green_short;
|
|
xcb_color.blue = color->blue_short;
|
|
xcb_color.alpha = color->alpha_short;
|
|
|
|
xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
|
|
picture = _cairo_xcb_picture_create (target->screen,
|
|
PIXMAN_a8r8g8b8,
|
|
xrender_format,
|
|
-1, -1);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
if (target->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) {
|
|
_cairo_xcb_connection_render_create_solid_fill (target->connection,
|
|
picture->picture,
|
|
xcb_color);
|
|
} else {
|
|
xcb_pixmap_t pixmap;
|
|
uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL };
|
|
|
|
pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
|
|
32, target->drawable, 1, 1);
|
|
_cairo_xcb_connection_render_create_picture (target->connection,
|
|
picture->picture,
|
|
pixmap,
|
|
xrender_format,
|
|
XCB_RENDER_CP_REPEAT,
|
|
values);
|
|
if (target->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
|
|
xcb_rectangle_t rect;
|
|
|
|
rect.x = rect.y = 0;
|
|
rect.width = rect.height = 1;
|
|
|
|
_cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture),
|
|
XCB_RENDER_PICT_OP_SRC,
|
|
picture->picture,
|
|
xcb_color, 1, &rect);
|
|
} else {
|
|
xcb_gcontext_t gc;
|
|
uint32_t pixel;
|
|
|
|
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32);
|
|
|
|
/* XXX byte ordering? */
|
|
pixel = ((color->alpha_short >> 8) << 24) |
|
|
((color->red_short >> 8) << 16) |
|
|
((color->green_short >> 8) << 8) |
|
|
((color->blue_short >> 8) << 0);
|
|
|
|
_cairo_xcb_connection_put_image (target->connection,
|
|
pixmap, gc,
|
|
1, 1, 0, 0,
|
|
32, 4, &pixel);
|
|
|
|
_cairo_xcb_screen_put_gc (target->screen, 32, gc);
|
|
}
|
|
|
|
_cairo_xcb_connection_free_pixmap (target->connection, pixmap);
|
|
}
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target)
|
|
{
|
|
cairo_xcb_picture_t *picture;
|
|
|
|
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT];
|
|
if (picture == NULL) {
|
|
picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT);
|
|
target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base;
|
|
}
|
|
|
|
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_black_picture (cairo_xcb_surface_t *target)
|
|
{
|
|
cairo_xcb_picture_t *picture;
|
|
|
|
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK];
|
|
if (picture == NULL) {
|
|
picture = _solid_picture (target, CAIRO_COLOR_BLACK);
|
|
target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base;
|
|
}
|
|
|
|
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_white_picture (cairo_xcb_surface_t *target)
|
|
{
|
|
cairo_xcb_picture_t *picture;
|
|
|
|
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE];
|
|
if (picture == NULL) {
|
|
picture = _solid_picture (target, CAIRO_COLOR_WHITE);
|
|
target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base;
|
|
}
|
|
|
|
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_solid_picture (cairo_xcb_surface_t *target,
|
|
const cairo_solid_pattern_t *pattern)
|
|
{
|
|
cairo_xcb_picture_t *picture;
|
|
cairo_xcb_screen_t *screen;
|
|
int i, n_cached;
|
|
|
|
if (pattern->color.alpha_short <= 0x00ff)
|
|
return _cairo_xcb_transparent_picture (target);
|
|
|
|
if (pattern->color.alpha_short >= 0xff00) {
|
|
if (pattern->color.red_short <= 0x00ff &&
|
|
pattern->color.green_short <= 0x00ff &&
|
|
pattern->color.blue_short <= 0x00ff)
|
|
{
|
|
return _cairo_xcb_black_picture (target);
|
|
}
|
|
|
|
if (pattern->color.red_short >= 0xff00 &&
|
|
pattern->color.green_short >= 0xff00 &&
|
|
pattern->color.blue_short >= 0xff00)
|
|
{
|
|
return _cairo_xcb_white_picture (target);
|
|
}
|
|
}
|
|
|
|
screen = target->screen;
|
|
n_cached = screen->solid_cache_size;
|
|
for (i = 0; i < n_cached; i++) {
|
|
if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) {
|
|
return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture);
|
|
}
|
|
}
|
|
|
|
picture = _solid_picture (target, &pattern->color);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) {
|
|
i = screen->solid_cache_size++;
|
|
} else {
|
|
i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache);
|
|
cairo_surface_destroy (screen->solid_cache[i].picture);
|
|
}
|
|
screen->solid_cache[i].picture = cairo_surface_reference (&picture->base);
|
|
screen->solid_cache[i].color = pattern->color;
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_render_to_picture (cairo_xcb_surface_t *target,
|
|
const cairo_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_image_surface_t *image;
|
|
cairo_xcb_shm_info_t *shm_info;
|
|
cairo_pattern_union_t copy;
|
|
cairo_status_t status;
|
|
cairo_xcb_picture_t *picture;
|
|
pixman_format_code_t pixman_format;
|
|
xcb_render_pictformat_t xrender_format;
|
|
|
|
/* XXX handle extend modes via tiling? */
|
|
/* XXX alpha-only masks? */
|
|
|
|
pixman_format = PIXMAN_a8r8g8b8;
|
|
xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
|
|
|
|
status = _cairo_xcb_shm_image_create (target->screen->connection,
|
|
pixman_format,
|
|
extents->width, extents->height,
|
|
&image, &shm_info);
|
|
if (unlikely (status))
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
|
|
_cairo_pattern_init_static_copy (©.base, pattern);
|
|
cairo_matrix_translate (©.base.matrix, extents->x, extents->y);
|
|
status = _cairo_surface_paint (&image->base,
|
|
CAIRO_OPERATOR_SOURCE,
|
|
©.base,
|
|
NULL);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&image->base);
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
picture = _picture_from_image (target, xrender_format, image, shm_info);
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
_cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha);
|
|
picture->x = -extents->x;
|
|
picture->y = -extents->y;
|
|
|
|
return picture;
|
|
}
|
|
|
|
static xcb_render_fixed_t *
|
|
_gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
|
|
unsigned int *n_stops,
|
|
char *buf, unsigned int buflen)
|
|
{
|
|
xcb_render_fixed_t *stops;
|
|
xcb_render_color_t *colors;
|
|
unsigned int i;
|
|
|
|
assert (gradient->n_stops > 0);
|
|
*n_stops = MAX (gradient->n_stops, 2);
|
|
|
|
if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
|
|
{
|
|
stops = (xcb_render_fixed_t *) buf;
|
|
}
|
|
else
|
|
{
|
|
stops =
|
|
_cairo_malloc_ab (*n_stops,
|
|
sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t));
|
|
if (unlikely (stops == NULL))
|
|
return NULL;
|
|
}
|
|
|
|
colors = (xcb_render_color_t *) (stops + *n_stops);
|
|
for (i = 0; i < gradient->n_stops; i++) {
|
|
stops[i] =
|
|
_cairo_fixed_16_16_from_double (gradient->stops[i].offset);
|
|
|
|
colors[i].red = gradient->stops[i].color.red_short;
|
|
colors[i].green = gradient->stops[i].color.green_short;
|
|
colors[i].blue = gradient->stops[i].color.blue_short;
|
|
colors[i].alpha = gradient->stops[i].color.alpha_short;
|
|
}
|
|
|
|
/* RENDER does not support gradients with less than 2 stops. If a
|
|
* gradient has only a single stop, duplicate it to make RENDER
|
|
* happy. */
|
|
if (gradient->n_stops == 1) {
|
|
stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset);
|
|
|
|
colors[1].red = gradient->stops[0].color.red_short;
|
|
colors[1].green = gradient->stops[0].color.green_short;
|
|
colors[1].blue = gradient->stops[0].color.blue_short;
|
|
colors[1].alpha = gradient->stops[0].color.alpha_short;
|
|
}
|
|
|
|
return stops;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
|
|
const cairo_linear_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
char buf[CAIRO_STACK_BUFFER_SIZE];
|
|
xcb_render_fixed_t *stops;
|
|
xcb_render_color_t *colors;
|
|
xcb_render_pointfix_t p1, p2;
|
|
cairo_matrix_t matrix;
|
|
cairo_circle_double_t extremes[2];
|
|
cairo_xcb_picture_t *picture;
|
|
cairo_status_t status;
|
|
unsigned int n_stops;
|
|
|
|
_cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
|
|
|
|
picture = (cairo_xcb_picture_t *)
|
|
_cairo_xcb_screen_lookup_linear_picture (target->screen, pattern);
|
|
if (picture != NULL)
|
|
goto setup_picture;
|
|
|
|
stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
|
|
if (unlikely (stops == NULL))
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
picture = _cairo_xcb_picture_create (target->screen,
|
|
target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
|
|
PIXMAN_a8r8g8b8,
|
|
-1, -1);
|
|
if (unlikely (picture->base.status)) {
|
|
if (stops != (xcb_render_fixed_t *) buf)
|
|
free (stops);
|
|
return picture;
|
|
}
|
|
picture->filter = CAIRO_FILTER_DEFAULT;
|
|
|
|
colors = (xcb_render_color_t *) (stops + n_stops);
|
|
|
|
p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
|
|
p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
|
|
p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
|
|
p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
|
|
|
|
_cairo_xcb_connection_render_create_linear_gradient (target->connection,
|
|
picture->picture,
|
|
p1, p2,
|
|
n_stops,
|
|
stops, colors);
|
|
|
|
if (stops != (xcb_render_fixed_t *) buf)
|
|
free (stops);
|
|
|
|
status = _cairo_xcb_screen_store_linear_picture (target->screen,
|
|
pattern,
|
|
&picture->base);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&picture->base);
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
setup_picture:
|
|
_cairo_xcb_picture_set_matrix (picture, &matrix,
|
|
pattern->base.base.filter,
|
|
extents->x + extents->width/2.,
|
|
extents->y + extents->height/2.);
|
|
_cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
|
|
_cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
|
|
_cairo_xcb_picture_set_component_alpha (picture,
|
|
pattern->base.base.has_component_alpha);
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
|
|
const cairo_radial_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
char buf[CAIRO_STACK_BUFFER_SIZE];
|
|
xcb_render_fixed_t *stops;
|
|
xcb_render_color_t *colors;
|
|
xcb_render_pointfix_t p1, p2;
|
|
xcb_render_fixed_t r1, r2;
|
|
cairo_matrix_t matrix;
|
|
cairo_circle_double_t extremes[2];
|
|
cairo_xcb_picture_t *picture;
|
|
cairo_status_t status;
|
|
unsigned int n_stops;
|
|
|
|
_cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
|
|
|
|
picture = (cairo_xcb_picture_t *)
|
|
_cairo_xcb_screen_lookup_radial_picture (target->screen, pattern);
|
|
if (picture != NULL)
|
|
goto setup_picture;
|
|
|
|
stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
|
|
if (unlikely (stops == NULL))
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
picture = _cairo_xcb_picture_create (target->screen,
|
|
target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
|
|
PIXMAN_a8r8g8b8,
|
|
-1, -1);
|
|
if (unlikely (picture->base.status)) {
|
|
if (stops != (xcb_render_fixed_t *) buf)
|
|
free (stops);
|
|
return picture;
|
|
}
|
|
picture->filter = CAIRO_FILTER_DEFAULT;
|
|
|
|
colors = (xcb_render_color_t *) (stops + n_stops);
|
|
|
|
p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
|
|
p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
|
|
p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
|
|
p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
|
|
|
|
r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
|
|
r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
|
|
|
|
_cairo_xcb_connection_render_create_radial_gradient (target->connection,
|
|
picture->picture,
|
|
p1, p2, r1, r2,
|
|
n_stops,
|
|
stops, colors);
|
|
|
|
if (stops != (xcb_render_fixed_t *) buf)
|
|
free (stops);
|
|
|
|
status = _cairo_xcb_screen_store_radial_picture (target->screen,
|
|
pattern,
|
|
&picture->base);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&picture->base);
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
setup_picture:
|
|
_cairo_xcb_picture_set_matrix (picture, &matrix,
|
|
pattern->base.base.filter,
|
|
extents->x + extents->width/2.,
|
|
extents->y + extents->height/2.);
|
|
_cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
|
|
_cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
|
|
_cairo_xcb_picture_set_component_alpha (picture,
|
|
pattern->base.base.has_component_alpha);
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_copy_to_picture (cairo_xcb_surface_t *source,
|
|
cairo_bool_t force)
|
|
{
|
|
cairo_xcb_picture_t *picture;
|
|
uint32_t values[] = { 0, 1 };
|
|
|
|
if (source->deferred_clear) {
|
|
cairo_status_t status = _cairo_xcb_surface_clear (source);
|
|
if (unlikely (status))
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
/* XXX two level device locking, ensure we release the xcb device mutex? */
|
|
if (source->drm != NULL)
|
|
cairo_surface_flush (source->drm);
|
|
|
|
picture = _cairo_xcb_picture_create (source->screen,
|
|
source->xrender_format,
|
|
source->pixman_format,
|
|
source->width,
|
|
source->height);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
_cairo_xcb_connection_render_create_picture (source->connection,
|
|
picture->picture,
|
|
source->drawable,
|
|
source->xrender_format,
|
|
XCB_RENDER_CP_GRAPHICS_EXPOSURE |
|
|
XCB_RENDER_CP_SUBWINDOW_MODE,
|
|
values);
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_surface_picture (cairo_xcb_surface_t *target,
|
|
const cairo_surface_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_surface_t *source = pattern->surface;
|
|
cairo_xcb_picture_t *picture;
|
|
cairo_filter_t filter;
|
|
|
|
{
|
|
cairo_xcb_surface_t *snapshot;
|
|
|
|
snapshot = (cairo_xcb_surface_t *)
|
|
_cairo_surface_has_snapshot (source, &_cairo_xcb_surface_backend);
|
|
if (snapshot != NULL) {
|
|
if (snapshot->screen == target->screen)
|
|
source = &snapshot->base;
|
|
}
|
|
}
|
|
|
|
picture = (cairo_xcb_picture_t *)
|
|
_cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend);
|
|
if (picture != NULL) {
|
|
if (picture->screen == target->screen) {
|
|
picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
|
|
goto setup_picture;
|
|
}
|
|
picture = NULL;
|
|
}
|
|
|
|
if (source->type == CAIRO_SURFACE_TYPE_XCB && ((cairo_xcb_surface_t *) source)->fallback == NULL)
|
|
{
|
|
if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) {
|
|
if (((cairo_xcb_surface_t *) source)->screen == target->screen) {
|
|
picture = _copy_to_picture ((cairo_xcb_surface_t *) source, FALSE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
} else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
|
|
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
|
|
cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target;
|
|
|
|
/* XXX repeat interval with source clipping? */
|
|
if (FALSE && xcb->screen == target->screen) {
|
|
xcb_rectangle_t rect;
|
|
|
|
picture = _copy_to_picture (xcb, TRUE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
rect.x = sub->extents.x;
|
|
rect.y = sub->extents.y;
|
|
rect.width = sub->extents.width;
|
|
rect.height = sub->extents.height;
|
|
|
|
_cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
|
|
picture->picture,
|
|
0, 0,
|
|
1, &rect);
|
|
picture->x0 = rect.x;
|
|
picture->y0 = rect.y;
|
|
picture->width = rect.width;
|
|
picture->height = rect.height;
|
|
}
|
|
} else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
|
|
cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
|
|
cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target;
|
|
|
|
if (xcb->screen == target->screen) {
|
|
picture = _copy_to_picture (xcb, TRUE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
}
|
|
}
|
|
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
|
|
else if (source->type == CAIRO_SURFACE_TYPE_XLIB && ((cairo_xlib_xcb_surface_t *) source)->xcb->fallback == NULL)
|
|
{
|
|
if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) {
|
|
if (((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen) {
|
|
picture = _copy_to_picture (((cairo_xlib_xcb_surface_t *) source)->xcb,
|
|
FALSE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
} else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
|
|
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
|
|
cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb;
|
|
|
|
if (FALSE && xcb->screen == target->screen) {
|
|
xcb_rectangle_t rect;
|
|
|
|
picture = _copy_to_picture (xcb, TRUE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
|
|
rect.x = sub->extents.x;
|
|
rect.y = sub->extents.y;
|
|
rect.width = sub->extents.width;
|
|
rect.height = sub->extents.height;
|
|
|
|
_cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
|
|
picture->picture,
|
|
0, 0,
|
|
1, &rect);
|
|
picture->x0 = rect.x;
|
|
picture->y0 = rect.y;
|
|
picture->width = rect.width;
|
|
picture->height = rect.height;
|
|
}
|
|
} else if (source->backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT) {
|
|
cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
|
|
cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb;
|
|
|
|
if (xcb->screen == target->screen) {
|
|
picture = _copy_to_picture (xcb, TRUE);
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if CAIRO_HAS_XCB_DRM_FUNCTIONS && CAIRO_HAS_DRM_SURFACE
|
|
else if (source->type == CAIRO_SURFACE_TYPE_DRM &&
|
|
target->drm != NULL &&
|
|
target->drm->device == source->device)
|
|
{
|
|
cairo_drm_surface_t *drm = (cairo_drm_surface_t *) source;
|
|
cairo_xcb_surface_t *tmp;
|
|
xcb_pixmap_t pixmap;
|
|
pixman_format_code_t pixman_format;
|
|
cairo_surface_pattern_t pattern;
|
|
cairo_status_t status;
|
|
|
|
/* XXX XRenderCreatePictureForNative:
|
|
* Copy the source to a temporary pixmap if possible.
|
|
* Still cheaper than pushing the image via the CPU.
|
|
*/
|
|
|
|
switch (drm->format) {
|
|
case CAIRO_FORMAT_A1:
|
|
pixman_format = PIXMAN_a1;
|
|
break;
|
|
case CAIRO_FORMAT_A8:
|
|
pixman_format = PIXMAN_a8;
|
|
break;
|
|
case CAIRO_FORMAT_RGB24:
|
|
pixman_format = PIXMAN_x8r8g8b8;
|
|
break;
|
|
default:
|
|
case CAIRO_FORMAT_ARGB32:
|
|
pixman_format = PIXMAN_a8r8g8b8;
|
|
break;
|
|
}
|
|
|
|
pixmap =
|
|
_cairo_xcb_connection_create_pixmap (target->connection,
|
|
PIXMAN_FORMAT_DEPTH (pixman_format),
|
|
target->drawable,
|
|
drm->width, drm->height);
|
|
|
|
tmp = (cairo_xcb_surface_t *)
|
|
_cairo_xcb_surface_create_internal (target->screen,
|
|
pixmap, TRUE,
|
|
pixman_format,
|
|
target->connection->standard_formats[drm->format],
|
|
drm->width, drm->height);
|
|
if (unlikely (tmp->base.status)) {
|
|
_cairo_xcb_connection_free_pixmap (target->connection, pixmap);
|
|
return (cairo_xcb_picture_t *) tmp;
|
|
}
|
|
|
|
_cairo_pattern_init_for_surface (&pattern, source);
|
|
status = _cairo_surface_paint (&tmp->base,
|
|
CAIRO_OPERATOR_SOURCE,
|
|
&pattern.base,
|
|
NULL);
|
|
_cairo_pattern_fini (&pattern.base);
|
|
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&tmp->base);
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
picture = _copy_to_picture (tmp, FALSE);
|
|
cairo_surface_destroy (&tmp->base);
|
|
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
#endif
|
|
#if CAIRO_HAS_GL_FUNCTIONS
|
|
else if (source->type == CAIRO_SURFACE_TYPE_GL)
|
|
{
|
|
/* pixmap from texture */
|
|
}
|
|
#endif
|
|
|
|
if (picture == NULL) {
|
|
cairo_image_surface_t *image;
|
|
void *image_extra;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
|
|
if (unlikely (status))
|
|
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
|
|
|
|
if (image->format != CAIRO_FORMAT_INVALID) {
|
|
xcb_render_pictformat_t format;
|
|
|
|
format = target->screen->connection->standard_formats[image->format];
|
|
|
|
picture = _picture_from_image (target, format, image, NULL);
|
|
_cairo_surface_release_source_image (source, image, image_extra);
|
|
} else {
|
|
cairo_image_surface_t *conv;
|
|
xcb_render_pictformat_t render_format;
|
|
|
|
/* XXX XRenderPutImage! */
|
|
|
|
conv = _cairo_image_surface_coerce (image);
|
|
_cairo_surface_release_source_image (source, image, image_extra);
|
|
if (unlikely (conv->base.status))
|
|
return (cairo_xcb_picture_t *) conv;
|
|
|
|
render_format = target->screen->connection->standard_formats[conv->format];
|
|
picture = _picture_from_image (target, render_format, conv, NULL);
|
|
cairo_surface_destroy (&conv->base);
|
|
}
|
|
|
|
if (unlikely (picture->base.status))
|
|
return picture;
|
|
}
|
|
|
|
_cairo_surface_attach_snapshot (source,
|
|
&picture->base,
|
|
NULL);
|
|
|
|
setup_picture:
|
|
filter = pattern->base.filter;
|
|
if (filter != CAIRO_FILTER_NEAREST &&
|
|
_cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
|
|
_cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) &&
|
|
_cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0)))
|
|
{
|
|
filter = CAIRO_FILTER_NEAREST;
|
|
}
|
|
_cairo_xcb_picture_set_filter (picture, filter);
|
|
|
|
_cairo_xcb_picture_set_matrix (picture,
|
|
&pattern->base.matrix, filter,
|
|
extents->x + extents->width/2.,
|
|
extents->y + extents->height/2.);
|
|
|
|
|
|
_cairo_xcb_picture_set_extend (picture, pattern->base.extend);
|
|
_cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha);
|
|
|
|
return picture;
|
|
}
|
|
|
|
static cairo_xcb_picture_t *
|
|
_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target,
|
|
const cairo_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
if (pattern == NULL)
|
|
return _cairo_xcb_white_picture (target);
|
|
|
|
if (! _pattern_is_supported (target->flags, pattern))
|
|
return _render_to_picture (target, pattern, extents);
|
|
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern);
|
|
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
return _cairo_xcb_linear_picture (target,
|
|
(cairo_linear_pattern_t *) pattern,
|
|
extents);
|
|
|
|
case CAIRO_PATTERN_TYPE_RADIAL:
|
|
return _cairo_xcb_radial_picture (target,
|
|
(cairo_radial_pattern_t *) pattern,
|
|
extents);
|
|
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
return _cairo_xcb_surface_picture (target,
|
|
(cairo_surface_pattern_t *) pattern,
|
|
extents);
|
|
case CAIRO_PATTERN_TYPE_MESH:
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
return _render_to_picture (target, pattern, extents);
|
|
}
|
|
}
|
|
|
|
COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t));
|
|
|
|
static cairo_status_t
|
|
_render_fill_boxes (void *abstract_dst,
|
|
cairo_operator_t op,
|
|
const cairo_color_t *color,
|
|
cairo_boxes_t *boxes)
|
|
{
|
|
cairo_xcb_surface_t *dst = abstract_dst;
|
|
xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))];
|
|
xcb_rectangle_t *xrects = stack_xrects;
|
|
xcb_render_color_t render_color;
|
|
int render_op = _render_operator (op);
|
|
struct _cairo_boxes_chunk *chunk;
|
|
int max_count;
|
|
|
|
render_color.red = color->red_short;
|
|
render_color.green = color->green_short;
|
|
render_color.blue = color->blue_short;
|
|
render_color.alpha = color->alpha_short;
|
|
|
|
max_count = 0;
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
if (chunk->count > max_count)
|
|
max_count = chunk->count;
|
|
}
|
|
if (max_count > ARRAY_LENGTH (stack_xrects)) {
|
|
xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t));
|
|
if (unlikely (xrects == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
int i, j;
|
|
|
|
for (i = j = 0; i < chunk->count; i++) {
|
|
int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x);
|
|
int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y);
|
|
int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x);
|
|
int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y);
|
|
|
|
if (x2 > x1 && y2 > y1) {
|
|
xrects[j].x = x1;
|
|
xrects[j].y = y1;
|
|
xrects[j].width = x2 - x1;
|
|
xrects[j].height = y2 - y1;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
if (j) {
|
|
_cairo_xcb_connection_render_fill_rectangles
|
|
(dst->connection,
|
|
render_op, dst->picture,
|
|
render_color, j, xrects);
|
|
}
|
|
}
|
|
|
|
if (xrects != stack_xrects)
|
|
free (xrects);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* pixel aligned, non-overlapping boxes */
|
|
static cairo_int_status_t
|
|
_render_composite_boxes (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
const cairo_pattern_t *mask_pattern,
|
|
const cairo_rectangle_int_t *extents,
|
|
const cairo_boxes_t *boxes)
|
|
{
|
|
cairo_xcb_picture_t *src, *mask;
|
|
const struct _cairo_boxes_chunk *chunk;
|
|
xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
|
|
xcb_rectangle_t *clip_boxes;
|
|
cairo_rectangle_int_t stack_extents;
|
|
cairo_status_t status;
|
|
int num_boxes;
|
|
int render_op;
|
|
|
|
render_op = _render_operator (op);
|
|
|
|
if (src_pattern == NULL) {
|
|
src_pattern = mask_pattern;
|
|
mask_pattern = NULL;
|
|
}
|
|
|
|
/* amalgamate into a single Composite call by setting a clip region */
|
|
clip_boxes = stack_boxes;
|
|
if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) {
|
|
clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t));
|
|
if (unlikely (clip_boxes == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
|
|
status = src->base.status;
|
|
if (unlikely (status))
|
|
goto cleanup_boxes;
|
|
|
|
num_boxes = 0;
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
const cairo_box_t *box = chunk->base;
|
|
int i;
|
|
|
|
for (i = 0; i < chunk->count; i++) {
|
|
int x = _cairo_fixed_integer_round_down (box[i].p1.x);
|
|
int y = _cairo_fixed_integer_round_down (box[i].p1.y);
|
|
int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x;
|
|
int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y;
|
|
|
|
if (width && height) {
|
|
clip_boxes[num_boxes].x = x;
|
|
clip_boxes[num_boxes].y = y;
|
|
clip_boxes[num_boxes].width = width;
|
|
clip_boxes[num_boxes].height = height;
|
|
num_boxes++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (num_boxes) {
|
|
if (num_boxes > 1) {
|
|
_cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection,
|
|
dst->picture,
|
|
0, 0,
|
|
num_boxes,
|
|
clip_boxes);
|
|
} else {
|
|
stack_extents.x = clip_boxes[0].x;
|
|
stack_extents.y = clip_boxes[0].y;
|
|
stack_extents.width = clip_boxes[0].width;
|
|
stack_extents.height = clip_boxes[0].height;
|
|
extents = &stack_extents;
|
|
}
|
|
|
|
if (mask_pattern != NULL) {
|
|
mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
|
|
status = mask->base.status;
|
|
if (unlikely (status))
|
|
goto cleanup_clip;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
render_op,
|
|
src->picture,
|
|
mask->picture,
|
|
dst->picture,
|
|
src->x + extents->x, src->y + extents->y,
|
|
mask->x + extents->x, mask->y + extents->y,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
|
|
cairo_surface_destroy (&mask->base);
|
|
} else {
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
render_op,
|
|
src->picture,
|
|
XCB_NONE,
|
|
dst->picture,
|
|
src->x + extents->x, src->y + extents->y,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
}
|
|
|
|
cleanup_clip:
|
|
|
|
if (num_boxes > 1)
|
|
_cairo_xcb_surface_clear_clip_region (dst);
|
|
}
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
|
|
cleanup_boxes:
|
|
|
|
if (clip_boxes != stack_boxes)
|
|
free (clip_boxes);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
|
|
#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
|
|
|
|
static cairo_bool_t
|
|
_line_exceeds_16_16 (const cairo_line_t *line)
|
|
{
|
|
return
|
|
line->p1.x <= CAIRO_FIXED_16_16_MIN ||
|
|
line->p1.x >= CAIRO_FIXED_16_16_MAX ||
|
|
|
|
line->p2.x <= CAIRO_FIXED_16_16_MIN ||
|
|
line->p2.x >= CAIRO_FIXED_16_16_MAX ||
|
|
|
|
line->p1.y <= CAIRO_FIXED_16_16_MIN ||
|
|
line->p1.y >= CAIRO_FIXED_16_16_MAX ||
|
|
|
|
line->p2.y <= CAIRO_FIXED_16_16_MIN ||
|
|
line->p2.y >= CAIRO_FIXED_16_16_MAX;
|
|
}
|
|
|
|
static void
|
|
_project_line_x_onto_16_16 (const cairo_line_t *line,
|
|
cairo_fixed_t top,
|
|
cairo_fixed_t bottom,
|
|
xcb_render_linefix_t *out)
|
|
{
|
|
cairo_point_double_t p1, p2;
|
|
double m;
|
|
|
|
p1.x = _cairo_fixed_to_double (line->p1.x);
|
|
p1.y = _cairo_fixed_to_double (line->p1.y);
|
|
|
|
p2.x = _cairo_fixed_to_double (line->p2.x);
|
|
p2.y = _cairo_fixed_to_double (line->p2.y);
|
|
|
|
m = (p2.x - p1.x) / (p2.y - p1.y);
|
|
out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
|
|
out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
|
|
}
|
|
|
|
typedef struct {
|
|
cairo_traps_t traps;
|
|
cairo_antialias_t antialias;
|
|
} composite_traps_info_t;
|
|
|
|
COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t));
|
|
|
|
static cairo_status_t
|
|
_composite_traps (void *closure,
|
|
cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
int dst_x, int dst_y,
|
|
const cairo_rectangle_int_t *extents,
|
|
cairo_region_t *clip_region)
|
|
{
|
|
composite_traps_info_t *info = closure;
|
|
const cairo_traps_t *traps = &info->traps;
|
|
cairo_xcb_picture_t *src;
|
|
cairo_format_t format;
|
|
xcb_render_pictformat_t xrender_format;
|
|
xcb_render_trapezoid_t *xtraps;
|
|
int render_reference_x, render_reference_y;
|
|
int i;
|
|
|
|
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
|
|
if (unlikely (src->base.status))
|
|
return src->base.status;
|
|
|
|
if (info->antialias == CAIRO_ANTIALIAS_NONE)
|
|
format = CAIRO_FORMAT_A1;
|
|
else
|
|
format = CAIRO_FORMAT_A8;
|
|
xrender_format = dst->screen->connection->standard_formats[format];
|
|
|
|
xtraps = (xcb_render_trapezoid_t *) traps->traps;
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
cairo_trapezoid_t t = traps->traps[i];
|
|
|
|
/* top/bottom will be clamped to surface bounds */
|
|
xtraps[i].top = _cairo_fixed_to_16_16 (t.top);
|
|
xtraps[i].top -= dst_y << 16;
|
|
xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom);
|
|
xtraps[i].bottom -= dst_y << 16;
|
|
|
|
/* However, all the other coordinates will have been left untouched so
|
|
* as not to introduce numerical error. Recompute them if they
|
|
* exceed the 16.16 limits.
|
|
*/
|
|
if (unlikely (_line_exceeds_16_16 (&t.left))) {
|
|
_project_line_x_onto_16_16 (&t.left,
|
|
t.top,
|
|
t.bottom,
|
|
&xtraps[i].left);
|
|
xtraps[i].left.p1.y = xtraps[i].top;
|
|
xtraps[i].left.p2.y = xtraps[i].bottom;
|
|
} else {
|
|
xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x);
|
|
xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y);
|
|
xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x);
|
|
xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y);
|
|
}
|
|
xtraps[i].left.p1.x -= dst_x << 16;
|
|
xtraps[i].left.p1.y -= dst_y << 16;
|
|
xtraps[i].left.p2.x -= dst_x << 16;
|
|
xtraps[i].left.p2.y -= dst_y << 16;
|
|
|
|
if (unlikely (_line_exceeds_16_16 (&t.right))) {
|
|
_project_line_x_onto_16_16 (&t.right,
|
|
t.top,
|
|
t.bottom,
|
|
&xtraps[i].right);
|
|
xtraps[i].right.p1.y = xtraps[i].top;
|
|
xtraps[i].right.p2.y = xtraps[i].bottom;
|
|
} else {
|
|
xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x);
|
|
xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y);
|
|
xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x);
|
|
xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y);
|
|
}
|
|
xtraps[i].right.p1.x -= dst_x << 16;
|
|
xtraps[i].right.p1.y -= dst_y << 16;
|
|
xtraps[i].right.p2.x -= dst_x << 16;
|
|
xtraps[i].right.p2.y -= dst_y << 16;
|
|
}
|
|
|
|
if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
|
|
render_reference_x = xtraps[0].left.p1.x >> 16;
|
|
render_reference_y = xtraps[0].left.p1.y >> 16;
|
|
} else {
|
|
render_reference_x = xtraps[0].left.p2.x >> 16;
|
|
render_reference_y = xtraps[0].left.p2.y >> 16;
|
|
}
|
|
|
|
_cairo_xcb_surface_set_precision (dst, info->antialias);
|
|
_cairo_xcb_connection_render_trapezoids (dst->connection,
|
|
_render_operator (op),
|
|
src->picture,
|
|
dst->picture,
|
|
xrender_format,
|
|
src->x + render_reference_x,
|
|
src->y + render_reference_y,
|
|
traps->num_traps, xtraps);
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* low-level composite driver */
|
|
|
|
typedef cairo_status_t
|
|
(*xcb_draw_func_t) (void *closure,
|
|
cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src,
|
|
int dst_x,
|
|
int dst_y,
|
|
const cairo_rectangle_int_t *extents,
|
|
cairo_region_t *clip_region);
|
|
|
|
static cairo_xcb_surface_t *
|
|
_create_composite_mask (cairo_clip_t *clip,
|
|
xcb_draw_func_t draw_func,
|
|
void *draw_closure,
|
|
cairo_xcb_surface_t *dst,
|
|
const cairo_rectangle_int_t*extents)
|
|
{
|
|
cairo_xcb_surface_t *surface;
|
|
cairo_bool_t clip_surface = FALSE;
|
|
cairo_status_t status;
|
|
|
|
if (clip != NULL) {
|
|
cairo_region_t *clip_region;
|
|
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
assert (! _cairo_status_is_error (status));
|
|
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
surface = (cairo_xcb_surface_t *)
|
|
_cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA,
|
|
extents->width, extents->height);
|
|
if (unlikely (surface->base.status))
|
|
return surface;
|
|
|
|
_cairo_xcb_surface_ensure_picture (surface);
|
|
|
|
if (surface->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
|
|
xcb_render_color_t clear;
|
|
xcb_rectangle_t xrect;
|
|
|
|
clear.red = clear.green = clear.blue = clear.alpha = 0;
|
|
|
|
xrect.x = xrect.y = 0;
|
|
xrect.width = extents->width;
|
|
xrect.height = extents->height;
|
|
|
|
_cairo_xcb_connection_render_fill_rectangles (surface->connection,
|
|
XCB_RENDER_PICT_OP_CLEAR,
|
|
surface->picture,
|
|
clear, 1, &xrect);
|
|
} else {
|
|
status = _cairo_xcb_surface_clear (surface);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&surface->base);
|
|
return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
}
|
|
|
|
/* Is it worth setting the clip region here? */
|
|
status = draw_func (draw_closure, surface,
|
|
CAIRO_OPERATOR_ADD, NULL,
|
|
extents->x, extents->y,
|
|
extents, NULL);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&surface->base);
|
|
return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
if (clip_surface) {
|
|
status = _cairo_clip_combine_with_surface (clip, &surface->base,
|
|
extents->x, extents->y);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&surface->base);
|
|
return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
/* Handles compositing with a clip surface when the operator allows
|
|
* us to combine the clip with the mask
|
|
*/
|
|
static cairo_status_t
|
|
_clip_and_composite_with_mask (cairo_clip_t *clip,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
xcb_draw_func_t draw_func,
|
|
void *draw_closure,
|
|
cairo_xcb_surface_t *dst,
|
|
const cairo_rectangle_int_t*extents)
|
|
{
|
|
cairo_xcb_surface_t *mask;
|
|
cairo_xcb_picture_t *src;
|
|
|
|
mask = _create_composite_mask (clip, draw_func, draw_closure, dst, extents);
|
|
if (unlikely (mask->base.status))
|
|
return mask->base.status;
|
|
|
|
if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) {
|
|
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
|
|
if (unlikely (src->base.status)) {
|
|
cairo_surface_destroy (&mask->base);
|
|
return src->base.status;
|
|
}
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
_render_operator (op),
|
|
src->picture,
|
|
mask->picture,
|
|
dst->picture,
|
|
extents->x + src->x, extents->y + src->y,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
} else {
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
_render_operator (op),
|
|
mask->picture,
|
|
XCB_NONE,
|
|
dst->picture,
|
|
0, 0,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
}
|
|
cairo_surface_destroy (&mask->base);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Handles compositing with a clip surface when we have to do the operation
|
|
* in two pieces and combine them together.
|
|
*/
|
|
static cairo_status_t
|
|
_clip_and_composite_combine (cairo_clip_t *clip,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
xcb_draw_func_t draw_func,
|
|
void *draw_closure,
|
|
cairo_xcb_surface_t *dst,
|
|
const cairo_rectangle_int_t*extents)
|
|
{
|
|
cairo_xcb_surface_t *tmp;
|
|
cairo_surface_t *clip_surface;
|
|
int clip_x, clip_y;
|
|
xcb_render_picture_t clip_picture;
|
|
cairo_status_t status;
|
|
|
|
tmp = (cairo_xcb_surface_t *)
|
|
_cairo_xcb_surface_create_similar (dst, dst->base.content,
|
|
extents->width, extents->height);
|
|
if (unlikely (tmp->base.status))
|
|
return tmp->base.status;
|
|
|
|
_cairo_xcb_surface_ensure_picture (tmp);
|
|
|
|
if (pattern == NULL) {
|
|
status = (*draw_func) (draw_closure, tmp,
|
|
CAIRO_OPERATOR_ADD, NULL,
|
|
extents->x, extents->y,
|
|
extents, NULL);
|
|
} else {
|
|
/* Initialize the temporary surface from the destination surface */
|
|
if (! dst->base.is_clear ||
|
|
(dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0)
|
|
{
|
|
/* XCopyArea may actually be quicker here.
|
|
* A good driver should translate if appropriate.
|
|
*/
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_SRC,
|
|
dst->picture,
|
|
XCB_NONE,
|
|
tmp->picture,
|
|
extents->x, extents->y,
|
|
0, 0,
|
|
0, 0,
|
|
extents->width, extents->height);
|
|
}
|
|
else
|
|
{
|
|
xcb_render_color_t clear;
|
|
xcb_rectangle_t xrect;
|
|
|
|
clear.red = clear.green = clear.blue = clear.alpha = 0;
|
|
|
|
xrect.x = xrect.y = 0;
|
|
xrect.width = extents->width;
|
|
xrect.height = extents->height;
|
|
|
|
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
|
|
XCB_RENDER_PICT_OP_CLEAR,
|
|
dst->picture,
|
|
clear, 1, &xrect);
|
|
}
|
|
|
|
status = (*draw_func) (draw_closure, tmp, op, pattern,
|
|
extents->x, extents->y,
|
|
extents, NULL);
|
|
}
|
|
if (unlikely (status))
|
|
goto CLEANUP_SURFACE;
|
|
|
|
clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
|
|
if (unlikely (clip_surface->status))
|
|
goto CLEANUP_SURFACE;
|
|
|
|
clip_picture = ((cairo_xcb_surface_t *) clip_surface)->picture;
|
|
assert (clip_picture != XCB_NONE);
|
|
|
|
if (dst->base.is_clear) {
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_SRC,
|
|
tmp->picture, clip_picture, dst->picture,
|
|
0, 0,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
} else {
|
|
/* Punch the clip out of the destination */
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
clip_picture, XCB_NONE, dst->picture,
|
|
extents->x - clip_x,
|
|
extents->y - clip_y,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
|
|
/* Now add the two results together */
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_ADD,
|
|
tmp->picture, clip_picture, dst->picture,
|
|
0, 0,
|
|
extents->x - clip_x,
|
|
extents->y - clip_y,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
}
|
|
|
|
CLEANUP_SURFACE:
|
|
cairo_surface_destroy (&tmp->base);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
|
|
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
|
|
*/
|
|
static cairo_status_t
|
|
_clip_and_composite_source (cairo_clip_t *clip,
|
|
const cairo_pattern_t *pattern,
|
|
xcb_draw_func_t draw_func,
|
|
void *draw_closure,
|
|
cairo_xcb_surface_t *dst,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
cairo_xcb_surface_t *mask;
|
|
cairo_xcb_picture_t *src;
|
|
|
|
/* Create a surface that is mask IN clip */
|
|
mask = _create_composite_mask (clip, draw_func, draw_closure, dst, extents);
|
|
if (unlikely (mask->base.status))
|
|
return mask->base.status;
|
|
|
|
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
|
|
if (unlikely (src->base.status)) {
|
|
cairo_surface_destroy (&mask->base);
|
|
return src->base.status;
|
|
}
|
|
|
|
if (dst->base.is_clear) {
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_SRC,
|
|
src->picture,
|
|
mask->picture,
|
|
dst->picture,
|
|
extents->x + src->x, extents->y + src->y,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
} else {
|
|
/* Compute dest' = dest OUT (mask IN clip) */
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
mask->picture,
|
|
XCB_NONE,
|
|
dst->picture,
|
|
0, 0, 0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
|
|
/* Now compute (src IN (mask IN clip)) ADD dest' */
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_ADD,
|
|
src->picture,
|
|
mask->picture,
|
|
dst->picture,
|
|
extents->x + src->x, extents->y + src->y,
|
|
0, 0,
|
|
extents->x, extents->y,
|
|
extents->width, extents->height);
|
|
}
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
cairo_surface_destroy (&mask->base);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
can_reduce_alpha_op (cairo_operator_t op)
|
|
{
|
|
int iop = op;
|
|
switch (iop) {
|
|
case CAIRO_OPERATOR_OVER:
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
case CAIRO_OPERATOR_ADD:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static cairo_bool_t
|
|
reduce_alpha_op (cairo_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern)
|
|
{
|
|
return dst->is_clear &&
|
|
dst->content == CAIRO_CONTENT_ALPHA &&
|
|
_cairo_pattern_is_opaque_solid (pattern) &&
|
|
can_reduce_alpha_op (op);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst,
|
|
const cairo_composite_rectangles_t *rects)
|
|
{
|
|
xcb_rectangle_t xrects[4];
|
|
int n;
|
|
|
|
if (rects->bounded.width == rects->unbounded.width &&
|
|
rects->bounded.height == rects->unbounded.height)
|
|
{
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
n = 0;
|
|
if (rects->bounded.width == 0 || rects->bounded.height == 0) {
|
|
xrects[n].x = rects->unbounded.x;
|
|
xrects[n].width = rects->unbounded.width;
|
|
xrects[n].y = rects->unbounded.y;
|
|
xrects[n].height = rects->unbounded.height;
|
|
n++;
|
|
} else {
|
|
/* top */
|
|
if (rects->bounded.y != rects->unbounded.y) {
|
|
xrects[n].x = rects->unbounded.x;
|
|
xrects[n].width = rects->unbounded.width;
|
|
xrects[n].y = rects->unbounded.y;
|
|
xrects[n].height = rects->bounded.y - rects->unbounded.y;
|
|
n++;
|
|
}
|
|
/* left */
|
|
if (rects->bounded.x != rects->unbounded.x) {
|
|
xrects[n].x = rects->unbounded.x;
|
|
xrects[n].width = rects->bounded.x - rects->unbounded.x;
|
|
xrects[n].y = rects->bounded.y;
|
|
xrects[n].height = rects->bounded.height;
|
|
n++;
|
|
}
|
|
/* right */
|
|
if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
|
|
xrects[n].x = rects->bounded.x + rects->bounded.width;
|
|
xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x;
|
|
xrects[n].y = rects->bounded.y;
|
|
xrects[n].height = rects->bounded.height;
|
|
n++;
|
|
}
|
|
/* bottom */
|
|
if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
|
|
xrects[n].x = rects->unbounded.x;
|
|
xrects[n].width = rects->unbounded.width;
|
|
xrects[n].y = rects->bounded.y + rects->bounded.height;
|
|
xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if (dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
|
|
xcb_render_color_t color;
|
|
|
|
color.red = 0;
|
|
color.green = 0;
|
|
color.blue = 0;
|
|
color.alpha = 0;
|
|
|
|
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
|
|
XCB_RENDER_PICT_OP_CLEAR,
|
|
dst->picture,
|
|
color, n, xrects);
|
|
} else {
|
|
int i;
|
|
cairo_xcb_picture_t *src;
|
|
|
|
src = _cairo_xcb_transparent_picture (dst);
|
|
if (unlikely (src->base.status))
|
|
return src->base.status;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_CLEAR,
|
|
src->picture, XCB_NONE, dst->picture,
|
|
0, 0,
|
|
0, 0,
|
|
xrects[i].x, xrects[i].y,
|
|
xrects[i].width, xrects[i].height);
|
|
}
|
|
cairo_surface_destroy (&src->base);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst,
|
|
const cairo_composite_rectangles_t *rects,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_xcb_surface_t *mask;
|
|
int mask_x, mask_y;
|
|
|
|
mask = (cairo_xcb_surface_t *) _cairo_clip_get_surface (clip, &dst->base, &mask_x, &mask_y);
|
|
if (unlikely (mask->base.status))
|
|
return mask->base.status;
|
|
|
|
/* top */
|
|
if (rects->bounded.y != rects->unbounded.y) {
|
|
int x = rects->unbounded.x;
|
|
int y = rects->unbounded.y;
|
|
int width = rects->unbounded.width;
|
|
int height = rects->bounded.y - y;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
mask->picture, XCB_NONE, dst->picture,
|
|
x - mask_x, y - mask_y,
|
|
0, 0,
|
|
x, y,
|
|
width, height);
|
|
}
|
|
|
|
/* left */
|
|
if (rects->bounded.x != rects->unbounded.x) {
|
|
int x = rects->unbounded.x;
|
|
int y = rects->bounded.y;
|
|
int width = rects->bounded.x - x;
|
|
int height = rects->bounded.height;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
mask->picture, XCB_NONE, dst->picture,
|
|
x - mask_x, y - mask_y,
|
|
0, 0,
|
|
x, y,
|
|
width, height);
|
|
}
|
|
|
|
/* right */
|
|
if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
|
|
int x = rects->bounded.x + rects->bounded.width;
|
|
int y = rects->bounded.y;
|
|
int width = rects->unbounded.x + rects->unbounded.width - x;
|
|
int height = rects->bounded.height;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
mask->picture, XCB_NONE, dst->picture,
|
|
x - mask_x, y - mask_y,
|
|
0, 0,
|
|
x, y,
|
|
width, height);
|
|
}
|
|
|
|
/* bottom */
|
|
if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
|
|
int x = rects->unbounded.x;
|
|
int y = rects->bounded.y + rects->bounded.height;
|
|
int width = rects->unbounded.width;
|
|
int height = rects->unbounded.y + rects->unbounded.height - y;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
XCB_RENDER_PICT_OP_OUT_REVERSE,
|
|
mask->picture, XCB_NONE, dst->picture,
|
|
x - mask_x, y - mask_y,
|
|
0, 0,
|
|
x, y,
|
|
width, height);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst,
|
|
const cairo_composite_rectangles_t *extents,
|
|
cairo_region_t *clip_region,
|
|
cairo_boxes_t *boxes)
|
|
{
|
|
cairo_boxes_t clear;
|
|
cairo_box_t box;
|
|
cairo_status_t status;
|
|
struct _cairo_boxes_chunk *chunk;
|
|
int i;
|
|
|
|
if (boxes->num_boxes <= 1 && clip_region == NULL)
|
|
return _cairo_xcb_surface_fixup_unbounded (dst, extents);
|
|
|
|
_cairo_boxes_init (&clear);
|
|
|
|
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
|
|
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
|
|
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
|
|
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
|
|
|
|
if (clip_region == NULL) {
|
|
cairo_boxes_t tmp;
|
|
|
|
_cairo_boxes_init (&tmp);
|
|
|
|
status = _cairo_boxes_add (&tmp, &box);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
tmp.chunks.next = &boxes->chunks;
|
|
tmp.num_boxes += boxes->num_boxes;
|
|
|
|
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
|
|
CAIRO_FILL_RULE_WINDING,
|
|
&clear);
|
|
|
|
tmp.chunks.next = NULL;
|
|
} else {
|
|
pixman_box32_t *pbox;
|
|
|
|
pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
|
|
_cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
|
|
|
|
status = _cairo_boxes_add (&clear, &box);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
for (i = 0; i < chunk->count; i++) {
|
|
status = _cairo_boxes_add (&clear, &chunk->base[i]);
|
|
if (unlikely (status)) {
|
|
_cairo_boxes_fini (&clear);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
|
|
CAIRO_FILL_RULE_WINDING,
|
|
&clear);
|
|
}
|
|
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
status = _render_fill_boxes (dst,
|
|
CAIRO_OPERATOR_CLEAR,
|
|
CAIRO_COLOR_TRANSPARENT,
|
|
&clear);
|
|
}
|
|
|
|
_cairo_boxes_fini (&clear);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst)
|
|
{
|
|
xcb_gcontext_t gc;
|
|
xcb_rectangle_t rect;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_xcb_connection_acquire (dst->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
rect.x = rect.y = 0;
|
|
rect.width = dst->width;
|
|
rect.height = dst->height;
|
|
|
|
if (dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
|
|
xcb_render_color_t transparent = { 0 };
|
|
|
|
_cairo_xcb_surface_ensure_picture (dst);
|
|
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
|
|
XCB_RENDER_PICT_OP_CLEAR,
|
|
dst->picture,
|
|
transparent,
|
|
1, &rect);
|
|
} else {
|
|
gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
|
|
|
|
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
|
|
dst->drawable, gc,
|
|
1, &rect);
|
|
|
|
_cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
|
|
}
|
|
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
|
|
dst->deferred_clear = FALSE;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_clip_and_composite (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src,
|
|
xcb_draw_func_t draw_func,
|
|
void *draw_closure,
|
|
cairo_composite_rectangles_t*extents,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_region_t *clip_region = NULL;
|
|
cairo_region_t extents_region;
|
|
cairo_bool_t need_clip_surface = FALSE;
|
|
|
|
if (clip != NULL) {
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
assert (! _cairo_status_is_error (status));
|
|
need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (clip_region != NULL) {
|
|
cairo_rectangle_int_t rect;
|
|
cairo_bool_t is_empty;
|
|
|
|
cairo_region_get_extents (clip_region, &rect);
|
|
is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect);
|
|
if (unlikely (is_empty))
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect);
|
|
if (unlikely (is_empty && extents->is_bounded))
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
} else if (!extents->is_bounded) {
|
|
/* The X server will estimate the affected region of the unbounded
|
|
* operation and will apply the operation to that rectangle.
|
|
* However, there are cases where this estimate is too high (e.g.
|
|
* the test suite's clip-fill-{eo,nz}-unbounded tests).
|
|
*/
|
|
_cairo_region_init_rectangle (&extents_region, &extents->unbounded);
|
|
clip_region = &extents_region;
|
|
}
|
|
|
|
status = _cairo_xcb_connection_acquire (dst->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (dst->deferred_clear) {
|
|
status = _cairo_xcb_surface_clear (dst);
|
|
if (unlikely (status)) {
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
_cairo_xcb_surface_ensure_picture (dst);
|
|
|
|
if (clip_region != NULL)
|
|
_cairo_xcb_surface_set_clip_region (dst, clip_region);
|
|
|
|
if (reduce_alpha_op (&dst->base, op, src)) {
|
|
op = CAIRO_OPERATOR_ADD;
|
|
src = NULL;
|
|
}
|
|
|
|
if (op == CAIRO_OPERATOR_SOURCE) {
|
|
status = _clip_and_composite_source (clip, src,
|
|
draw_func, draw_closure,
|
|
dst, &extents->bounded);
|
|
} else {
|
|
if (op == CAIRO_OPERATOR_CLEAR) {
|
|
op = CAIRO_OPERATOR_DEST_OUT;
|
|
src = NULL;
|
|
}
|
|
|
|
if (need_clip_surface) {
|
|
if (extents->is_bounded) {
|
|
status = _clip_and_composite_with_mask (clip, op, src,
|
|
draw_func, draw_closure,
|
|
dst, &extents->bounded);
|
|
} else {
|
|
status = _clip_and_composite_combine (clip, op, src,
|
|
draw_func, draw_closure,
|
|
dst, &extents->bounded);
|
|
}
|
|
} else {
|
|
status = draw_func (draw_closure,
|
|
dst, op, src,
|
|
0, 0,
|
|
&extents->bounded,
|
|
clip_region);
|
|
}
|
|
}
|
|
|
|
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
|
|
if (need_clip_surface)
|
|
status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, clip);
|
|
else
|
|
status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
|
|
}
|
|
|
|
if (clip_region != NULL)
|
|
_cairo_xcb_surface_clear_clip_region (dst);
|
|
if (clip_region == &extents_region)
|
|
_cairo_region_fini (&extents_region);
|
|
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_core_boxes (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src,
|
|
cairo_boxes_t *boxes,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
const cairo_composite_rectangles_t *extents)
|
|
{
|
|
if (antialias != CAIRO_ANTIALIAS_NONE) {
|
|
if (! boxes->is_pixel_aligned)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (clip != NULL) {
|
|
cairo_region_t *clip_region;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
assert (status == CAIRO_STATUS_SUCCESS ||
|
|
status == CAIRO_INT_STATUS_UNSUPPORTED);
|
|
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (op == CAIRO_OPERATOR_CLEAR)
|
|
return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes);
|
|
|
|
if (op == CAIRO_OPERATOR_OVER) {
|
|
if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded))
|
|
op = CAIRO_OPERATOR_SOURCE;
|
|
}
|
|
if (op != CAIRO_OPERATOR_SOURCE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
|
|
return _cairo_xcb_surface_core_fill_boxes (dst,
|
|
&((cairo_solid_pattern_t *) src)->color,
|
|
boxes);
|
|
}
|
|
|
|
return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_composite_boxes (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src,
|
|
cairo_boxes_t *boxes,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
const cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_bool_t need_clip_mask = FALSE;
|
|
cairo_region_t *clip_region = NULL;
|
|
cairo_status_t status;
|
|
|
|
/* If the boxes are not pixel-aligned, we will need to compute a real mask */
|
|
if (antialias != CAIRO_ANTIALIAS_NONE) {
|
|
if (! boxes->is_pixel_aligned)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (clip != NULL) {
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
assert (status == CAIRO_STATUS_SUCCESS ||
|
|
status == CAIRO_INT_STATUS_UNSUPPORTED);
|
|
|
|
need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED;
|
|
if (need_clip_mask &&
|
|
(! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
|
|
clip_region = NULL;
|
|
}
|
|
|
|
status = _cairo_xcb_connection_acquire (dst->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xcb_surface_ensure_picture (dst);
|
|
if (dst->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask &&
|
|
(op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID))
|
|
{
|
|
const cairo_color_t *color;
|
|
|
|
if (op == CAIRO_OPERATOR_CLEAR)
|
|
color = CAIRO_COLOR_TRANSPARENT;
|
|
else
|
|
color = &((cairo_solid_pattern_t *) src)->color;
|
|
|
|
status = _render_fill_boxes (dst, op, color, boxes);
|
|
}
|
|
else
|
|
{
|
|
cairo_surface_pattern_t mask;
|
|
|
|
if (need_clip_mask) {
|
|
cairo_surface_t *clip_surface;
|
|
int clip_x, clip_y;
|
|
|
|
clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
|
|
if (unlikely (clip_surface->status))
|
|
return clip_surface->status;
|
|
|
|
_cairo_pattern_init_for_surface (&mask, clip_surface);
|
|
mask.base.filter = CAIRO_FILTER_NEAREST;
|
|
cairo_matrix_init_translate (&mask.base.matrix,
|
|
-clip_x,
|
|
-clip_y);
|
|
|
|
if (op == CAIRO_OPERATOR_CLEAR) {
|
|
src = NULL;
|
|
op = CAIRO_OPERATOR_DEST_OUT;
|
|
}
|
|
}
|
|
|
|
status = _render_composite_boxes (dst, op, src,
|
|
need_clip_mask ? &mask.base : NULL,
|
|
&extents->bounded, boxes);
|
|
|
|
if (need_clip_mask)
|
|
_cairo_pattern_fini (&mask.base);
|
|
}
|
|
|
|
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
|
|
status =
|
|
_cairo_xcb_surface_fixup_unbounded_boxes (dst, extents,
|
|
clip_region, boxes);
|
|
}
|
|
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_upload_image_inplace (cairo_xcb_surface_t *surface,
|
|
const cairo_pattern_t *source,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
const cairo_surface_pattern_t *pattern;
|
|
cairo_image_surface_t *image;
|
|
xcb_gcontext_t gc;
|
|
cairo_status_t status;
|
|
int tx, ty;
|
|
int len, bpp;
|
|
|
|
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
pattern = (const cairo_surface_pattern_t *) source;
|
|
if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
{
|
|
cairo_xcb_surface_t *snapshot;
|
|
|
|
snapshot = (cairo_xcb_surface_t *)
|
|
_cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_surface_backend);
|
|
if (snapshot != NULL) {
|
|
if (snapshot->screen == surface->screen)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
{
|
|
cairo_xcb_picture_t *snapshot;
|
|
|
|
snapshot = (cairo_xcb_picture_t *)
|
|
_cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend);
|
|
if (snapshot != NULL) {
|
|
if (snapshot->screen == surface->screen)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
image = (cairo_image_surface_t *) pattern->surface;
|
|
if (image->format == CAIRO_FORMAT_INVALID)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (image->depth != surface->depth)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
image = (cairo_image_surface_t *) pattern->surface;
|
|
if (source->extend != CAIRO_EXTEND_NONE &&
|
|
(extents->x + tx < 0 ||
|
|
extents->y + ty < 0 ||
|
|
extents->x + tx + extents->width > image->width ||
|
|
extents->y + ty + extents->height > image->height))
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
status = _cairo_xcb_connection_acquire (surface->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
gc = _cairo_xcb_screen_get_gc (surface->screen, surface->drawable, image->depth);
|
|
|
|
/* Do we need to trim the image? */
|
|
bpp = PIXMAN_FORMAT_BPP (image->pixman_format);
|
|
len = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, bpp);
|
|
if (len == image->stride) {
|
|
_cairo_xcb_connection_put_image (surface->connection,
|
|
surface->drawable, gc,
|
|
extents->width, extents->height,
|
|
extents->x, extents->y,
|
|
image->depth,
|
|
image->stride,
|
|
image->data +
|
|
(extents->y + ty) * image->stride +
|
|
(extents->x + tx) * bpp/8);
|
|
} else {
|
|
_cairo_xcb_connection_put_subimage (surface->connection,
|
|
surface->drawable, gc,
|
|
extents->x + tx, extents->y + ty,
|
|
extents->width, extents->height,
|
|
bpp / 8,
|
|
image->stride,
|
|
extents->x, extents->y,
|
|
image->depth,
|
|
image->data);
|
|
|
|
}
|
|
|
|
_cairo_xcb_screen_put_gc (surface->screen, image->depth, gc);
|
|
_cairo_xcb_connection_release (surface->connection);
|
|
|
|
if (surface->width == image->width && surface->height == image->height &&
|
|
extents->width == image->width && extents->height == image->height)
|
|
{
|
|
_cairo_surface_attach_snapshot (&image->base, &surface->base, NULL);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src,
|
|
cairo_boxes_t *boxes,
|
|
cairo_antialias_t antialias,
|
|
cairo_composite_rectangles_t *extents,
|
|
cairo_clip_t *clip)
|
|
{
|
|
composite_traps_info_t info;
|
|
cairo_status_t status;
|
|
|
|
if (boxes->num_boxes == 0 && extents->is_bounded)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (clip == NULL &&
|
|
(op == CAIRO_OPERATOR_SOURCE || (op == CAIRO_OPERATOR_OVER && dst->base.is_clear)) &&
|
|
boxes->num_boxes == 1 &&
|
|
extents->bounded.width == dst->width &&
|
|
extents->bounded.height == dst->height)
|
|
{
|
|
op = CAIRO_OPERATOR_SOURCE;
|
|
dst->deferred_clear = FALSE;
|
|
|
|
status = _upload_image_inplace (dst, src, &extents->bounded);
|
|
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
|
return status;
|
|
}
|
|
|
|
if (dst->deferred_clear) {
|
|
status = _cairo_xcb_surface_clear (dst);
|
|
if (unlikely (status)) {
|
|
return status;
|
|
}
|
|
|
|
if (op == CAIRO_OPERATOR_OVER)
|
|
op = CAIRO_OPERATOR_SOURCE;
|
|
}
|
|
|
|
if (clip == NULL && op == CAIRO_OPERATOR_SOURCE && boxes->num_boxes == 1) {
|
|
status = _upload_image_inplace (dst, src, &extents->bounded);
|
|
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
|
return status;
|
|
}
|
|
|
|
if ((dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
|
|
return _core_boxes (dst, op, src, boxes, antialias, clip, extents);
|
|
|
|
if (dst->deferred_clear) {
|
|
status = _cairo_xcb_surface_clear (dst);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
/* Use a fast path if the boxes are pixel aligned */
|
|
status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
|
|
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
|
|
return status;
|
|
|
|
if ((dst->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
/* Otherwise render via a mask and composite in the usual fashion. */
|
|
status = _cairo_traps_init_boxes (&info.traps, boxes);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
info.antialias = antialias;
|
|
status = _clip_and_composite (dst, op, src,
|
|
_composite_traps, &info,
|
|
extents, clip);
|
|
|
|
_cairo_traps_fini (&info.traps);
|
|
return status;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_mono_edge_is_vertical (const cairo_line_t *line)
|
|
{
|
|
return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_traps_are_pixel_aligned (cairo_traps_t *traps,
|
|
cairo_antialias_t antialias)
|
|
{
|
|
int i;
|
|
|
|
if (antialias == CAIRO_ANTIALIAS_NONE) {
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
|
|
! _mono_edge_is_vertical (&traps->traps[i].right))
|
|
{
|
|
traps->maybe_region = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
|
|
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
|
|
! _cairo_fixed_is_integer (traps->traps[i].top) ||
|
|
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
|
|
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
|
|
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
|
|
{
|
|
traps->maybe_region = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_boxes_for_traps (cairo_boxes_t *boxes,
|
|
cairo_traps_t *traps,
|
|
cairo_antialias_t antialias)
|
|
{
|
|
int i;
|
|
|
|
_cairo_boxes_init (boxes);
|
|
|
|
boxes->num_boxes = traps->num_traps;
|
|
boxes->chunks.base = (cairo_box_t *) traps->traps;
|
|
boxes->chunks.count = traps->num_traps;
|
|
boxes->chunks.size = traps->num_traps;
|
|
|
|
if (antialias != CAIRO_ANTIALIAS_NONE) {
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
/* Note the traps and boxes alias so we need to take the local copies first. */
|
|
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
|
|
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
|
|
cairo_fixed_t y1 = traps->traps[i].top;
|
|
cairo_fixed_t y2 = traps->traps[i].bottom;
|
|
|
|
boxes->chunks.base[i].p1.x = x1;
|
|
boxes->chunks.base[i].p1.y = y1;
|
|
boxes->chunks.base[i].p2.x = x2;
|
|
boxes->chunks.base[i].p2.y = y2;
|
|
|
|
if (boxes->is_pixel_aligned) {
|
|
boxes->is_pixel_aligned =
|
|
_cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
|
|
_cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
|
|
}
|
|
}
|
|
} else {
|
|
boxes->is_pixel_aligned = TRUE;
|
|
|
|
for (i = 0; i < traps->num_traps; i++) {
|
|
/* Note the traps and boxes alias so we need to take the local copies first. */
|
|
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
|
|
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
|
|
cairo_fixed_t y1 = traps->traps[i].top;
|
|
cairo_fixed_t y2 = traps->traps[i].bottom;
|
|
|
|
/* round down here to match Pixman's behavior when using traps. */
|
|
boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
|
|
boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
|
|
boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
|
|
boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static cairo_status_t
|
|
_composite_mask (void *closure,
|
|
cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
int dst_x,
|
|
int dst_y,
|
|
const cairo_rectangle_int_t *extents,
|
|
cairo_region_t *clip_region)
|
|
{
|
|
const cairo_pattern_t *mask_pattern = closure;
|
|
cairo_xcb_picture_t *src, *mask = NULL;
|
|
|
|
if (src_pattern != NULL) {
|
|
src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
|
|
if (unlikely (src->base.status))
|
|
return src->base.status;
|
|
|
|
mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
|
|
if (unlikely (mask->base.status)) {
|
|
cairo_surface_destroy (&src->base);
|
|
return mask->base.status;
|
|
}
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
_render_operator (op),
|
|
src->picture,
|
|
mask->picture,
|
|
dst->picture,
|
|
extents->x + src->x, extents->y + src->y,
|
|
extents->x + mask->x, extents->y + mask->y,
|
|
extents->x - dst_x, extents->y - dst_y,
|
|
extents->width, extents->height);
|
|
cairo_surface_destroy (&mask->base);
|
|
cairo_surface_destroy (&src->base);
|
|
} else {
|
|
src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
|
|
if (unlikely (src->base.status))
|
|
return src->base.status;
|
|
|
|
_cairo_xcb_connection_render_composite (dst->connection,
|
|
_render_operator (op),
|
|
src->picture,
|
|
XCB_NONE,
|
|
dst->picture,
|
|
extents->x + src->x, extents->y + src->y,
|
|
0, 0,
|
|
extents->x - dst_x, extents->y - dst_y,
|
|
extents->width, extents->height);
|
|
cairo_surface_destroy (&src->base);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* high level rasteriser -> compositor */
|
|
|
|
static cairo_clip_path_t *
|
|
_clip_get_single_path (cairo_clip_t *clip)
|
|
{
|
|
cairo_clip_path_t *iter = clip->path;
|
|
cairo_clip_path_t *path = NULL;
|
|
|
|
do {
|
|
if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) {
|
|
if (path != NULL)
|
|
return FALSE;
|
|
|
|
path = iter;
|
|
}
|
|
iter = iter->prev;
|
|
} while (iter != NULL);
|
|
|
|
return path;
|
|
}
|
|
|
|
cairo_int_status_t
|
|
_cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_composite_rectangles_t extents;
|
|
cairo_boxes_t boxes;
|
|
cairo_box_t *clip_boxes = boxes.boxes_embedded;
|
|
cairo_clip_t local_clip;
|
|
cairo_clip_path_t *clip_path;
|
|
cairo_bool_t have_clip = FALSE;
|
|
int num_boxes = ARRAY_LENGTH (boxes.boxes_embedded);
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (! _operator_is_supported (surface->flags, op)))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
|
|
surface->deferred_clear = TRUE;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
status = _cairo_composite_rectangles_init_for_paint (&extents,
|
|
surface->width,
|
|
surface->height,
|
|
op, source,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (_cairo_clip_contains_extents (clip, &extents))
|
|
clip = NULL;
|
|
|
|
if (clip != NULL) {
|
|
clip = _cairo_clip_init_copy (&local_clip, clip);
|
|
have_clip = TRUE;
|
|
}
|
|
|
|
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
|
|
if (unlikely (status)) {
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
if (clip != NULL &&
|
|
extents.is_bounded &&
|
|
(clip_path = _clip_get_single_path (clip)) != NULL)
|
|
{
|
|
status = _cairo_xcb_surface_render_fill (surface, op, source,
|
|
&clip_path->path,
|
|
clip_path->fill_rule,
|
|
clip_path->tolerance,
|
|
clip_path->antialias,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
_cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
|
|
status = _clip_and_composite_boxes (surface, op, source,
|
|
&boxes, CAIRO_ANTIALIAS_DEFAULT,
|
|
&extents, clip);
|
|
if (clip_boxes != boxes.boxes_embedded)
|
|
free (clip_boxes);
|
|
}
|
|
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_int_status_t
|
|
_cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
const cairo_pattern_t *mask,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_composite_rectangles_t extents;
|
|
cairo_clip_t local_clip;
|
|
cairo_bool_t have_clip = FALSE;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (! _operator_is_supported (surface->flags, op)))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if ((surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
status = _cairo_composite_rectangles_init_for_mask (&extents,
|
|
surface->width, surface->height,
|
|
op, source, mask, clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (_cairo_clip_contains_extents (clip, &extents))
|
|
clip = NULL;
|
|
|
|
if (clip != NULL && extents.is_bounded) {
|
|
clip = _cairo_clip_init_copy (&local_clip, clip);
|
|
status = _cairo_clip_rectangle (clip, &extents.bounded);
|
|
if (unlikely (status)) {
|
|
_cairo_clip_fini (&local_clip);
|
|
return status;
|
|
}
|
|
have_clip = TRUE;
|
|
}
|
|
|
|
status = _clip_and_composite (surface, op, source,
|
|
_composite_mask, (void *) mask,
|
|
&extents, clip);
|
|
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_render_composite_polygon (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_polygon_t *polygon,
|
|
cairo_antialias_t antialias,
|
|
cairo_fill_rule_t fill_rule,
|
|
cairo_composite_rectangles_t *extents,
|
|
cairo_clip_t *clip)
|
|
{
|
|
composite_traps_info_t traps;
|
|
cairo_bool_t clip_surface = FALSE;
|
|
cairo_status_t status;
|
|
cairo_bool_t is_not_empty;
|
|
|
|
if (polygon->num_edges == 0) {
|
|
status = CAIRO_STATUS_SUCCESS;
|
|
|
|
if (! extents->is_bounded) {
|
|
cairo_region_t *clip_region = NULL;
|
|
|
|
if (clip != NULL) {
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
|
|
clip_region = NULL;
|
|
}
|
|
|
|
if (clip_surface == FALSE) {
|
|
if (clip_region != NULL)
|
|
_cairo_xcb_surface_set_clip_region (dst, clip_region);
|
|
|
|
status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
|
|
|
|
if (clip_region != NULL)
|
|
_cairo_xcb_surface_clear_clip_region (dst);
|
|
} else {
|
|
status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst,
|
|
extents,
|
|
clip);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
|
|
is_not_empty = _cairo_rectangle_intersect (&extents->bounded, &extents->mask);
|
|
if (extents->is_bounded && ! is_not_empty)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
_cairo_traps_init (&traps.traps);
|
|
|
|
status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
|
|
if (unlikely (status))
|
|
goto CLEANUP_TRAPS;
|
|
|
|
if (clip != NULL) {
|
|
cairo_region_t *clip_region;
|
|
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
if (traps.traps.has_intersections) {
|
|
if (traps.traps.is_rectangular)
|
|
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
|
|
else if (traps.traps.is_rectilinear)
|
|
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
|
|
else
|
|
status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
|
|
if (unlikely (status))
|
|
goto CLEANUP_TRAPS;
|
|
}
|
|
|
|
/* Use a fast path if the trapezoids consist of a simple region,
|
|
* but we can only do this if we do not have a clip surface, or can
|
|
* substitute the mask with the clip.
|
|
*/
|
|
if (traps.traps.maybe_region &&
|
|
_traps_are_pixel_aligned (&traps.traps, antialias) &&
|
|
(! clip_surface ||
|
|
(extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
|
|
{
|
|
cairo_boxes_t boxes;
|
|
|
|
_boxes_for_traps (&boxes, &traps.traps, antialias);
|
|
status = _clip_and_composite_boxes (dst, op, source,
|
|
&boxes, antialias,
|
|
extents, clip);
|
|
}
|
|
else
|
|
{
|
|
/* Otherwise render the trapezoids to a mask and composite in the usual
|
|
* fashion.
|
|
*/
|
|
traps.antialias = antialias;
|
|
status = _clip_and_composite (dst, op, source,
|
|
_composite_traps, &traps,
|
|
extents, clip);
|
|
}
|
|
|
|
CLEANUP_TRAPS:
|
|
_cairo_traps_fini (&traps.traps);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *stroke_style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
const cairo_box_t *clip_boxes,
|
|
int num_boxes,
|
|
cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_polygon_t polygon;
|
|
cairo_status_t status;
|
|
|
|
_cairo_polygon_init (&polygon, clip_boxes, num_boxes);
|
|
|
|
status = _cairo_path_fixed_stroke_to_polygon (path,
|
|
stroke_style,
|
|
ctm, ctm_inverse,
|
|
tolerance,
|
|
&polygon);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
status = _cairo_xcb_surface_render_composite_polygon (dst, op, source,
|
|
&polygon, antialias,
|
|
CAIRO_FILL_RULE_WINDING,
|
|
extents, clip);
|
|
}
|
|
|
|
_cairo_polygon_fini (&polygon);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
_clear_image (cairo_surface_t *surface)
|
|
{
|
|
cairo_image_surface_t *image = (cairo_image_surface_t *) surface;
|
|
memset (image->data, 0, image->stride * image->height);
|
|
surface->is_clear = TRUE;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *stroke_style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_surface_t *image;
|
|
cairo_status_t status;
|
|
int x, y;
|
|
|
|
x = extents->bounded.x;
|
|
y = extents->bounded.y;
|
|
image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_CONTENT_ALPHA,
|
|
extents->bounded.width,
|
|
extents->bounded.height);
|
|
if (unlikely (image->status))
|
|
return image->status;
|
|
|
|
_clear_image (image);
|
|
|
|
status = _cairo_surface_offset_stroke (image, x, y,
|
|
CAIRO_OPERATOR_ADD,
|
|
&_cairo_pattern_white.base,
|
|
path, stroke_style,
|
|
ctm, ctm_inverse,
|
|
tolerance, antialias,
|
|
NULL);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
cairo_surface_pattern_t mask;
|
|
|
|
_cairo_pattern_init_for_surface (&mask, image);
|
|
mask.base.filter = CAIRO_FILTER_NEAREST;
|
|
|
|
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
|
|
status = _clip_and_composite (dst, op, source,
|
|
_composite_mask, (void *) &mask.base,
|
|
extents, clip);
|
|
_cairo_pattern_fini (&mask.base);
|
|
}
|
|
|
|
cairo_surface_finish (image);
|
|
cairo_surface_destroy (image);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_int_status_t
|
|
_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_path_fixed_t *path,
|
|
const cairo_stroke_style_t *style,
|
|
const cairo_matrix_t *ctm,
|
|
const cairo_matrix_t *ctm_inverse,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_composite_rectangles_t extents;
|
|
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
|
|
int num_boxes = ARRAY_LENGTH (boxes_stack);
|
|
cairo_clip_t local_clip;
|
|
cairo_bool_t have_clip = FALSE;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (! _operator_is_supported (surface->flags, op)))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
status = _cairo_composite_rectangles_init_for_stroke (&extents,
|
|
surface->width,
|
|
surface->height,
|
|
op, source,
|
|
path, style, ctm,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (_cairo_clip_contains_extents (clip, &extents))
|
|
clip = NULL;
|
|
|
|
if (clip != NULL) {
|
|
clip = _cairo_clip_init_copy (&local_clip, clip);
|
|
have_clip = TRUE;
|
|
}
|
|
|
|
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
|
|
if (unlikely (status)) {
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
|
|
cairo_boxes_t boxes;
|
|
|
|
_cairo_boxes_init (&boxes);
|
|
_cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
|
|
|
|
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
|
|
style,
|
|
ctm,
|
|
&boxes);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
status = _clip_and_composite_boxes (surface, op, source,
|
|
&boxes, antialias,
|
|
&extents, clip);
|
|
}
|
|
|
|
_cairo_boxes_fini (&boxes);
|
|
}
|
|
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
|
if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
|
|
status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
|
|
path, style,
|
|
ctm, ctm_inverse,
|
|
tolerance, antialias,
|
|
clip, clip_boxes, num_boxes,
|
|
&extents);
|
|
} else if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
|
|
status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
|
|
path, style,
|
|
ctm, ctm_inverse,
|
|
tolerance, antialias,
|
|
have_clip ? &local_clip : NULL,
|
|
&extents);
|
|
} else {
|
|
ASSERT_NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
if (clip_boxes != boxes_stack)
|
|
free (clip_boxes);
|
|
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t*source,
|
|
cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
cairo_box_t *clip_boxes,
|
|
int num_boxes,
|
|
cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_polygon_t polygon;
|
|
cairo_status_t status;
|
|
|
|
_cairo_polygon_init (&polygon, clip_boxes, num_boxes);
|
|
|
|
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
status = _cairo_xcb_surface_render_composite_polygon (dst, op, source,
|
|
&polygon, antialias,
|
|
fill_rule,
|
|
extents, clip);
|
|
}
|
|
|
|
_cairo_polygon_fini (&polygon);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip,
|
|
cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_surface_t *image;
|
|
cairo_status_t status;
|
|
int x, y;
|
|
|
|
x = extents->bounded.x;
|
|
y = extents->bounded.y;
|
|
image = _cairo_xcb_surface_create_similar_image (dst,
|
|
CAIRO_CONTENT_ALPHA,
|
|
extents->bounded.width,
|
|
extents->bounded.height);
|
|
if (unlikely (image->status))
|
|
return image->status;
|
|
|
|
_clear_image (image);
|
|
|
|
status = _cairo_surface_offset_fill (image, x, y,
|
|
CAIRO_OPERATOR_ADD,
|
|
&_cairo_pattern_white.base,
|
|
path, fill_rule, tolerance, antialias,
|
|
NULL);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
cairo_surface_pattern_t mask;
|
|
|
|
_cairo_pattern_init_for_surface (&mask, image);
|
|
mask.base.filter = CAIRO_FILTER_NEAREST;
|
|
|
|
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
|
|
status = _clip_and_composite (dst, op, source,
|
|
_composite_mask, (void *) &mask.base,
|
|
extents, clip);
|
|
|
|
_cairo_pattern_fini (&mask.base);
|
|
}
|
|
|
|
cairo_surface_finish (image);
|
|
cairo_surface_destroy (image);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_int_status_t
|
|
_cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_path_fixed_t *path,
|
|
cairo_fill_rule_t fill_rule,
|
|
double tolerance,
|
|
cairo_antialias_t antialias,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_composite_rectangles_t extents;
|
|
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
|
|
int num_boxes = ARRAY_LENGTH (boxes_stack);
|
|
cairo_clip_t local_clip;
|
|
cairo_bool_t have_clip = FALSE;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (! _operator_is_supported (surface->flags, op)))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
|
|
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
|
|
{
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
status = _cairo_composite_rectangles_init_for_fill (&extents,
|
|
surface->width,
|
|
surface->height,
|
|
op, source, path,
|
|
clip);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (_cairo_clip_contains_extents (clip, &extents))
|
|
clip = NULL;
|
|
|
|
if (clip != NULL) {
|
|
clip = _cairo_clip_init_copy (&local_clip, clip);
|
|
have_clip = TRUE;
|
|
}
|
|
|
|
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
|
|
if (unlikely (status)) {
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
|
|
cairo_boxes_t boxes;
|
|
|
|
_cairo_boxes_init (&boxes);
|
|
_cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
|
|
|
|
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
|
|
fill_rule,
|
|
&boxes);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
status = _clip_and_composite_boxes (surface, op, source,
|
|
&boxes, antialias,
|
|
&extents, clip);
|
|
}
|
|
|
|
_cairo_boxes_fini (&boxes);
|
|
}
|
|
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
|
if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
|
|
status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path,
|
|
fill_rule, tolerance, antialias,
|
|
clip, clip_boxes, num_boxes,
|
|
&extents);
|
|
} else if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
|
|
status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path,
|
|
fill_rule, tolerance, antialias,
|
|
have_clip ? &local_clip : NULL,
|
|
&extents);
|
|
} else {
|
|
ASSERT_NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
if (clip_boxes != boxes_stack)
|
|
free (clip_boxes);
|
|
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_clip_t *clip,
|
|
cairo_composite_rectangles_t *extents)
|
|
{
|
|
cairo_surface_t *image;
|
|
cairo_content_t content;
|
|
cairo_status_t status;
|
|
int x, y;
|
|
|
|
content = CAIRO_CONTENT_ALPHA;
|
|
if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
|
|
content = CAIRO_CONTENT_COLOR_ALPHA;
|
|
|
|
x = extents->bounded.x;
|
|
y = extents->bounded.y;
|
|
image = _cairo_xcb_surface_create_similar_image (dst, content,
|
|
extents->bounded.width,
|
|
extents->bounded.height);
|
|
if (unlikely (image->status))
|
|
return image->status;
|
|
|
|
_clear_image (image);
|
|
|
|
status = _cairo_surface_offset_glyphs (image, x, y,
|
|
CAIRO_OPERATOR_ADD,
|
|
&_cairo_pattern_white.base,
|
|
scaled_font, glyphs, num_glyphs,
|
|
NULL);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
cairo_surface_pattern_t mask;
|
|
|
|
_cairo_pattern_init_for_surface (&mask, image);
|
|
mask.base.filter = CAIRO_FILTER_NEAREST;
|
|
if (content & CAIRO_CONTENT_COLOR)
|
|
mask.base.has_component_alpha = TRUE;
|
|
|
|
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
|
|
status = _clip_and_composite (dst, op, source,
|
|
_composite_mask, (void *) &mask.base,
|
|
extents, clip);
|
|
|
|
_cairo_pattern_fini (&mask.base);
|
|
}
|
|
|
|
cairo_surface_finish (image);
|
|
cairo_surface_destroy (image);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Build a struct of the same size of #cairo_glyph_t that can be used both as
|
|
* an input glyph with double coordinates, and as "working" glyph with
|
|
* integer from-current-point offsets. */
|
|
typedef union {
|
|
cairo_glyph_t d;
|
|
unsigned long index;
|
|
struct {
|
|
unsigned long index;
|
|
int x;
|
|
int y;
|
|
} i;
|
|
} cairo_xcb_glyph_t;
|
|
|
|
/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */
|
|
COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t));
|
|
|
|
typedef struct {
|
|
cairo_scaled_font_t *font;
|
|
cairo_xcb_glyph_t *glyphs;
|
|
int num_glyphs;
|
|
cairo_bool_t use_mask;
|
|
} composite_glyphs_info_t;
|
|
|
|
static cairo_status_t
|
|
_can_composite_glyphs (cairo_xcb_surface_t *dst,
|
|
cairo_rectangle_int_t *extents,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_glyph_t *glyphs,
|
|
int *num_glyphs)
|
|
{
|
|
#define GLYPH_CACHE_SIZE 64
|
|
cairo_box_t bbox_cache[GLYPH_CACHE_SIZE];
|
|
unsigned long glyph_cache[GLYPH_CACHE_SIZE];
|
|
#undef GLYPH_CACHE_SIZE
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_glyph_t *glyphs_end, *valid_glyphs;
|
|
const int max_glyph_size = dst->connection->maximum_request_length - 64;
|
|
|
|
/* We must initialize the cache with values that cannot match the
|
|
* "hash" to guarantee that when compared for the first time they
|
|
* will result in a mismatch. The hash function is simply modulus,
|
|
* so we cannot use 0 in glyph_cache[0], but we can use it in all
|
|
* other array cells.
|
|
*/
|
|
memset (glyph_cache, 0, sizeof (glyph_cache));
|
|
glyph_cache[0] = 1;
|
|
|
|
/* Scan for oversized glyphs or glyphs outside the representable
|
|
* range and fallback in that case, discard glyphs outside of the
|
|
* image.
|
|
*/
|
|
valid_glyphs = glyphs;
|
|
for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) {
|
|
double x1, y1, x2, y2;
|
|
cairo_scaled_glyph_t *scaled_glyph;
|
|
cairo_box_t *bbox;
|
|
int width, height, len;
|
|
int g;
|
|
|
|
g = glyphs->index % ARRAY_LENGTH (glyph_cache);
|
|
if (glyph_cache[g] != glyphs->index) {
|
|
status = _cairo_scaled_glyph_lookup (scaled_font,
|
|
glyphs->index,
|
|
CAIRO_SCALED_GLYPH_INFO_METRICS,
|
|
&scaled_glyph);
|
|
if (unlikely (status))
|
|
break;
|
|
|
|
glyph_cache[g] = glyphs->index;
|
|
bbox_cache[g] = scaled_glyph->bbox;
|
|
}
|
|
bbox = &bbox_cache[g];
|
|
|
|
/* Drop glyphs outside the clipping */
|
|
x1 = _cairo_fixed_to_double (bbox->p1.x);
|
|
y1 = _cairo_fixed_to_double (bbox->p1.y);
|
|
y2 = _cairo_fixed_to_double (bbox->p2.y);
|
|
x2 = _cairo_fixed_to_double (bbox->p2.x);
|
|
if (unlikely (glyphs->x + x2 <= extents->x ||
|
|
glyphs->y + y2 <= extents->y ||
|
|
glyphs->x + x1 >= extents->x + extents->width ||
|
|
glyphs->y + y1 >= extents->y + extents->height))
|
|
{
|
|
(*num_glyphs)--;
|
|
continue;
|
|
}
|
|
|
|
/* XRenderAddGlyph does not handle a glyph surface larger than
|
|
* the extended maximum XRequest size.
|
|
*/
|
|
width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x);
|
|
height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y);
|
|
len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height;
|
|
if (unlikely (len >= max_glyph_size)) {
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
/* The glyph coordinates must be representable in an int16_t.
|
|
* When possible, they will be expressed as an offset from the
|
|
* previous glyph, otherwise they will be an offset from the
|
|
* operation extents or from the surface origin. If the last
|
|
* two options are not valid, fallback.
|
|
*/
|
|
if (unlikely (glyphs->x > INT16_MAX ||
|
|
glyphs->y > INT16_MAX ||
|
|
glyphs->x - extents->x < INT16_MIN ||
|
|
glyphs->y - extents->y < INT16_MIN))
|
|
{
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
|
|
if (unlikely (valid_glyphs != glyphs))
|
|
*valid_glyphs = *glyphs;
|
|
valid_glyphs++;
|
|
}
|
|
|
|
if (unlikely (valid_glyphs != glyphs)) {
|
|
for (; glyphs != glyphs_end; glyphs++) {
|
|
*valid_glyphs = *glyphs;
|
|
valid_glyphs++;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Start a new element for the first glyph,
|
|
* or for any glyph that has unexpected position,
|
|
* or if current element has too many glyphs
|
|
* (Xrender limits each element to 252 glyphs, we limit them to 128)
|
|
*
|
|
* These same conditions need to be mirrored between
|
|
* _cairo_xcb_surface_emit_glyphs and _emit_glyph_chunks
|
|
*/
|
|
#define _start_new_glyph_elt(count, glyph) \
|
|
(((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
|
|
|
|
/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
|
|
* enough room for padding */
|
|
typedef struct {
|
|
uint8_t len;
|
|
uint8_t pad1;
|
|
uint16_t pad2;
|
|
int16_t deltax;
|
|
int16_t deltay;
|
|
} x_glyph_elt_t;
|
|
#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4)
|
|
|
|
static cairo_xcb_font_glyphset_info_t *
|
|
_cairo_xcb_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph)
|
|
{
|
|
return scaled_glyph->surface_private;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info)
|
|
{
|
|
scaled_glyph->surface_private = glyphset_info;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_font_init (cairo_xcb_connection_t *connection,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
int i;
|
|
|
|
font_private = malloc (sizeof (cairo_xcb_font_t));
|
|
if (unlikely (font_private == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font_private->scaled_font = scaled_font;
|
|
font_private->connection = _cairo_xcb_connection_reference (connection);
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
|
|
switch (i) {
|
|
case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break;
|
|
case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break;
|
|
case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break;
|
|
default: ASSERT_NOT_REACHED; break;
|
|
}
|
|
glyphset_info->xrender_format = 0;
|
|
glyphset_info->glyphset = XCB_NONE;
|
|
glyphset_info->pending_free_glyphs = NULL;
|
|
}
|
|
|
|
scaled_font->surface_private = font_private;
|
|
scaled_font->surface_backend = &_cairo_xcb_surface_backend;
|
|
|
|
cairo_list_add (&font_private->link, &connection->fonts);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_font_destroy (cairo_xcb_font_t *font)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyphset_info = &font->glyphset_info[i];
|
|
|
|
if (glyphset_info->pending_free_glyphs != NULL)
|
|
free (glyphset_info->pending_free_glyphs);
|
|
}
|
|
|
|
cairo_list_del (&font->link);
|
|
_cairo_xcb_connection_destroy (font->connection);
|
|
|
|
free (font);
|
|
}
|
|
|
|
void
|
|
_cairo_xcb_font_finish (cairo_xcb_font_t *font)
|
|
{
|
|
cairo_scaled_font_t *scaled_font;
|
|
|
|
scaled_font = font->scaled_font;
|
|
|
|
CAIRO_MUTEX_LOCK (scaled_font->mutex);
|
|
scaled_font->surface_private = NULL;
|
|
_cairo_scaled_font_reset_cache (scaled_font);
|
|
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
|
|
|
|
_cairo_xcb_font_destroy (font);
|
|
}
|
|
|
|
void
|
|
_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
cairo_xcb_connection_t *connection;
|
|
cairo_bool_t have_connection;
|
|
cairo_status_t status;
|
|
int i;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if (font_private == NULL)
|
|
return;
|
|
|
|
connection = font_private->connection;
|
|
|
|
status = _cairo_xcb_connection_acquire (connection);
|
|
have_connection = status == CAIRO_STATUS_SUCCESS;
|
|
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyphset_info = &font_private->glyphset_info[i];
|
|
if (glyphset_info->glyphset && status == CAIRO_STATUS_SUCCESS) {
|
|
_cairo_xcb_connection_render_free_glyph_set (connection,
|
|
glyphset_info->glyphset);
|
|
}
|
|
}
|
|
|
|
if (have_connection)
|
|
_cairo_xcb_connection_release (connection);
|
|
|
|
|
|
_cairo_xcb_font_destroy (font_private);
|
|
}
|
|
|
|
static void
|
|
_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection,
|
|
cairo_xcb_font_glyphset_free_glyphs_t *to_free)
|
|
{
|
|
_cairo_xcb_connection_render_free_glyphs (connection,
|
|
to_free->glyphset,
|
|
to_free->glyph_count,
|
|
to_free->glyph_indices);
|
|
}
|
|
|
|
void
|
|
_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info;
|
|
|
|
if (scaled_font->finished)
|
|
return;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
glyphset_info = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph);
|
|
if (font_private != NULL && glyphset_info != NULL) {
|
|
cairo_xcb_font_glyphset_free_glyphs_t *to_free;
|
|
|
|
to_free = glyphset_info->pending_free_glyphs;
|
|
if (to_free != NULL &&
|
|
to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
|
|
{
|
|
_cairo_xcb_render_free_glyphs (font_private->connection, to_free);
|
|
to_free = glyphset_info->pending_free_glyphs = NULL;
|
|
}
|
|
|
|
if (to_free == NULL) {
|
|
to_free = malloc (sizeof (cairo_xcb_font_glyphset_free_glyphs_t));
|
|
if (unlikely (to_free == NULL)) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
return; /* XXX cannot propagate failure */
|
|
}
|
|
|
|
to_free->glyphset = glyphset_info->glyphset;
|
|
to_free->glyph_count = 0;
|
|
glyphset_info->pending_free_glyphs = to_free;
|
|
}
|
|
|
|
to_free->glyph_indices[to_free->glyph_count++] =
|
|
_cairo_scaled_glyph_index (scaled_glyph);
|
|
}
|
|
}
|
|
|
|
static int
|
|
_cairo_xcb_get_glyphset_index_for_format (cairo_format_t format)
|
|
{
|
|
if (format == CAIRO_FORMAT_A8)
|
|
return GLYPHSET_INDEX_A8;
|
|
if (format == CAIRO_FORMAT_A1)
|
|
return GLYPHSET_INDEX_A1;
|
|
|
|
assert (format == CAIRO_FORMAT_ARGB32);
|
|
return GLYPHSET_INDEX_ARGB32;
|
|
}
|
|
|
|
static cairo_xcb_font_glyphset_info_t *
|
|
_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font,
|
|
cairo_format_t format)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info;
|
|
int glyphset_index;
|
|
|
|
glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format);
|
|
font_private = scaled_font->surface_private;
|
|
glyphset_info = &font_private->glyphset_info[glyphset_index];
|
|
if (glyphset_info->glyphset == XCB_NONE) {
|
|
cairo_xcb_connection_t *connection = font_private->connection;
|
|
|
|
glyphset_info->glyphset = _cairo_xcb_connection_get_xid (font_private->connection);
|
|
glyphset_info->xrender_format =
|
|
connection->standard_formats[glyphset_info->format];
|
|
|
|
_cairo_xcb_connection_render_create_glyph_set (font_private->connection,
|
|
glyphset_info->glyphset,
|
|
glyphset_info->xrender_format);
|
|
}
|
|
|
|
return glyphset_info;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xcb_glyphset_info_has_pending_free_glyph (
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info,
|
|
unsigned long glyph_index)
|
|
{
|
|
if (glyphset_info->pending_free_glyphs != NULL) {
|
|
cairo_xcb_font_glyphset_free_glyphs_t *to_free;
|
|
int i;
|
|
|
|
to_free = glyphset_info->pending_free_glyphs;
|
|
for (i = 0; i < to_free->glyph_count; i++) {
|
|
if (to_free->glyph_indices[i] == glyph_index) {
|
|
to_free->glyph_count--;
|
|
memmove (&to_free->glyph_indices[i],
|
|
&to_free->glyph_indices[i+1],
|
|
(to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0]));
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static cairo_xcb_font_glyphset_info_t *
|
|
_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (
|
|
cairo_scaled_font_t *scaled_font,
|
|
unsigned long glyph_index,
|
|
cairo_image_surface_t *surface)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
int i;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if (font_private == NULL)
|
|
return NULL;
|
|
|
|
if (surface != NULL) {
|
|
i = _cairo_xcb_get_glyphset_index_for_format (surface->format);
|
|
|
|
if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
|
|
&font_private->glyphset_info[i],
|
|
glyph_index))
|
|
{
|
|
return &font_private->glyphset_info[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
|
|
&font_private->glyphset_info[i],
|
|
glyph_index))
|
|
{
|
|
return &font_private->glyphset_info[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
static cairo_status_t
|
|
_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_scaled_glyph_t **scaled_glyph_out)
|
|
{
|
|
xcb_render_glyphinfo_t glyph_info;
|
|
uint32_t glyph_index;
|
|
uint8_t *data;
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out;
|
|
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
|
|
cairo_bool_t already_had_glyph_surface;
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
|
|
|
|
/* check to see if we have a pending XRenderFreeGlyph for this glyph */
|
|
glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface);
|
|
if (glyphset_info != NULL) {
|
|
_cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (glyph_surface == NULL) {
|
|
status = _cairo_scaled_glyph_lookup (scaled_font,
|
|
glyph_index,
|
|
CAIRO_SCALED_GLYPH_INFO_METRICS |
|
|
CAIRO_SCALED_GLYPH_INFO_SURFACE,
|
|
scaled_glyph_out);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
scaled_glyph = *scaled_glyph_out;
|
|
glyph_surface = scaled_glyph->surface;
|
|
already_had_glyph_surface = FALSE;
|
|
} else {
|
|
already_had_glyph_surface = TRUE;
|
|
}
|
|
|
|
if (scaled_font->surface_private == NULL) {
|
|
status = _cairo_xcb_surface_font_init (connection, scaled_font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (scaled_font,
|
|
glyph_surface->format);
|
|
|
|
/* If the glyph surface has zero height or width, we create
|
|
* a clear 1x1 surface, to avoid various X server bugs.
|
|
*/
|
|
if (glyph_surface->width == 0 || glyph_surface->height == 0) {
|
|
cairo_surface_t *tmp_surface;
|
|
|
|
tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1);
|
|
status = tmp_surface->status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
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 the glyph format does not match the font format, then we
|
|
* create a temporary surface for the glyph image with the font's
|
|
* format.
|
|
*/
|
|
if (glyph_surface->format != glyphset_info->format) {
|
|
glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface,
|
|
glyphset_info->format);
|
|
status = glyph_surface->base.status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
}
|
|
|
|
/* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
|
|
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 = scaled_glyph->x_advance;
|
|
glyph_info.y_off = scaled_glyph->y_advance;
|
|
|
|
data = glyph_surface->data;
|
|
|
|
/* flip formats around */
|
|
switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
|
|
case GLYPHSET_INDEX_A1:
|
|
/* local bitmaps are always stored with bit == byte */
|
|
if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
|
|
int c = glyph_surface->stride * glyph_surface->height;
|
|
const uint8_t *d;
|
|
uint8_t *new, *n;
|
|
|
|
new = malloc (c);
|
|
if (unlikely (new == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
|
|
n = new;
|
|
d = data;
|
|
do {
|
|
uint8_t 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;
|
|
} while (--c);
|
|
data = new;
|
|
}
|
|
break;
|
|
|
|
case GLYPHSET_INDEX_A8:
|
|
break;
|
|
|
|
case GLYPHSET_INDEX_ARGB32:
|
|
if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
|
|
unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
|
|
const uint32_t *d;
|
|
uint32_t *new, *n;
|
|
|
|
new = malloc (4 * c);
|
|
if (unlikely (new == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
|
|
n = new;
|
|
d = (uint32_t *) data;
|
|
do {
|
|
*n++ = bswap_32 (*d);
|
|
d++;
|
|
} while (--c);
|
|
data = (uint8_t *) new;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
break;
|
|
}
|
|
/* XXX assume X server wants pixman padding. Xft assumes this as well */
|
|
|
|
_cairo_xcb_connection_render_add_glyphs (connection,
|
|
glyphset_info->glyphset,
|
|
1, &glyph_index, &glyph_info,
|
|
glyph_surface->stride * glyph_surface->height,
|
|
data);
|
|
|
|
if (data != glyph_surface->data)
|
|
free (data);
|
|
|
|
_cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
|
|
|
|
BAIL:
|
|
if (glyph_surface != scaled_glyph->surface)
|
|
cairo_surface_destroy (&glyph_surface->base);
|
|
|
|
/* If the scaled glyph didn't already have a surface attached
|
|
* to it, release the created surface now that we have it
|
|
* uploaded to the X server. If the surface has already been
|
|
* there (e.g. because image backend requested it), leave it in
|
|
* the cache
|
|
*/
|
|
if (! already_had_glyph_surface)
|
|
_cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL);
|
|
|
|
return status;
|
|
}
|
|
|
|
typedef void (*cairo_xcb_render_composite_text_func_t)
|
|
(cairo_xcb_connection_t *connection,
|
|
uint8_t op,
|
|
xcb_render_picture_t src,
|
|
xcb_render_picture_t dst,
|
|
xcb_render_pictformat_t mask_format,
|
|
xcb_render_glyphset_t glyphset,
|
|
int16_t src_x,
|
|
int16_t src_y,
|
|
uint32_t len,
|
|
uint8_t *cmd);
|
|
|
|
|
|
static cairo_status_t
|
|
_emit_glyphs_chunk (cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
cairo_xcb_picture_t *src,
|
|
/* info for this chunk */
|
|
cairo_xcb_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
int width,
|
|
int estimated_req_size,
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info,
|
|
xcb_render_pictformat_t mask_format)
|
|
{
|
|
cairo_xcb_render_composite_text_func_t composite_text_func;
|
|
uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE];
|
|
uint8_t *buf = stack_buf;
|
|
x_glyph_elt_t *elt = NULL; /* silence compiler */
|
|
uint32_t len;
|
|
int i;
|
|
|
|
if (estimated_req_size > ARRAY_LENGTH (stack_buf)) {
|
|
buf = malloc (estimated_req_size);
|
|
if (unlikely (buf == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
len = 0;
|
|
for (i = 0; i < num_glyphs; i++) {
|
|
if (_start_new_glyph_elt (i, &glyphs[i])) {
|
|
if (len & 3)
|
|
len += 4 - (len & 3);
|
|
|
|
elt = (x_glyph_elt_t *) (buf + len);
|
|
elt->len = 0;
|
|
elt->deltax = glyphs[i].i.x;
|
|
elt->deltay = glyphs[i].i.y;
|
|
len += sizeof (x_glyph_elt_t);
|
|
}
|
|
|
|
switch (width) {
|
|
case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break;
|
|
case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break;
|
|
default:
|
|
case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break;
|
|
}
|
|
len += width;
|
|
elt->len++;
|
|
}
|
|
if (len & 3)
|
|
len += 4 - (len & 3);
|
|
|
|
switch (width) {
|
|
case 1:
|
|
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8;
|
|
break;
|
|
case 2:
|
|
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16;
|
|
break;
|
|
default:
|
|
case 4:
|
|
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32;
|
|
break;
|
|
}
|
|
composite_text_func (dst->connection,
|
|
_render_operator (op),
|
|
src->picture,
|
|
dst->picture,
|
|
mask_format,
|
|
glyphset_info->glyphset,
|
|
src->x + glyphs[0].i.x,
|
|
src->y + glyphs[0].i.y,
|
|
len, buf);
|
|
|
|
if (buf != stack_buf)
|
|
free (buf);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_composite_glyphs (void *closure,
|
|
cairo_xcb_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
int dst_x,
|
|
int dst_y,
|
|
const cairo_rectangle_int_t *extents,
|
|
cairo_region_t *clip_region)
|
|
{
|
|
composite_glyphs_info_t *info = closure;
|
|
cairo_scaled_glyph_t *glyph_cache[64];
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_fixed_t x = 0, y = 0;
|
|
cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
|
|
const unsigned int max_request_size = dst->connection->maximum_request_length - 64;
|
|
cairo_xcb_picture_t *src;
|
|
|
|
unsigned long max_index = 0;
|
|
int width = 1;
|
|
|
|
unsigned int request_size = 0;
|
|
int i;
|
|
|
|
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
|
|
if (unlikely (src->base.status))
|
|
return src->base.status;
|
|
|
|
memset (glyph_cache, 0, sizeof (glyph_cache));
|
|
|
|
for (i = 0; i < info->num_glyphs; i++) {
|
|
cairo_scaled_glyph_t *scaled_glyph;
|
|
unsigned long glyph_index = info->glyphs[i].index;
|
|
int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
|
|
int old_width = width;
|
|
int this_x, this_y;
|
|
|
|
scaled_glyph = glyph_cache[cache_index];
|
|
if (scaled_glyph == NULL ||
|
|
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
|
|
{
|
|
status = _cairo_scaled_glyph_lookup (info->font,
|
|
glyph_index,
|
|
CAIRO_SCALED_GLYPH_INFO_METRICS,
|
|
&scaled_glyph);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&src->base);
|
|
return status;
|
|
}
|
|
|
|
/* Send unseen glyphs to the server */
|
|
if (_cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) {
|
|
status = _cairo_xcb_surface_add_glyph (dst->connection,
|
|
info->font,
|
|
&scaled_glyph);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&src->base);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
glyph_cache[cache_index] = scaled_glyph;
|
|
}
|
|
|
|
this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x;
|
|
this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y;
|
|
|
|
this_glyphset_info = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph);
|
|
if (glyphset_info == NULL)
|
|
glyphset_info = this_glyphset_info;
|
|
|
|
/* Update max glyph index */
|
|
if (glyph_index > max_index) {
|
|
max_index = glyph_index;
|
|
if (max_index >= 65536)
|
|
width = 4;
|
|
else if (max_index >= 256)
|
|
width = 2;
|
|
if (width != old_width)
|
|
request_size += (width - old_width) * i;
|
|
}
|
|
|
|
/* If we will pass the max request size by adding this glyph,
|
|
* flush current glyphs. Note that we account for a
|
|
* possible element being added below.
|
|
*
|
|
* Also flush if changing glyphsets, as Xrender limits one mask
|
|
* format per request, so we can either break up, or use a
|
|
* wide-enough mask format. We do the former. One reason to
|
|
* prefer the latter is the fact that Xserver ADDs all glyphs
|
|
* to the mask first, and then composes that to final surface,
|
|
* though it's not a big deal.
|
|
*
|
|
* If the glyph has a coordinate which cannot be represented
|
|
* as a 16-bit offset from the previous glyph, flush the
|
|
* current chunk. The current glyph will be the first one in
|
|
* the next chunk, thus its coordinates will be an offset from
|
|
* the destination origin. This offset is guaranteed to be
|
|
* representable as 16-bit offset in _can_composite_glyphs().
|
|
*/
|
|
if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t ||
|
|
this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
|
|
this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
|
|
this_glyphset_info != glyphset_info)
|
|
{
|
|
status = _emit_glyphs_chunk (dst, op, src,
|
|
info->glyphs, i,
|
|
old_width, request_size,
|
|
glyphset_info,
|
|
info->use_mask ? glyphset_info->xrender_format : 0);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&src->base);
|
|
return status;
|
|
}
|
|
|
|
info->glyphs += i;
|
|
info->num_glyphs -= i;
|
|
i = 0;
|
|
|
|
max_index = info->glyphs[0].index;
|
|
width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
|
|
|
|
request_size = 0;
|
|
|
|
x = y = 0;
|
|
glyphset_info = this_glyphset_info;
|
|
}
|
|
|
|
/* Convert absolute glyph position to relative-to-current-point
|
|
* position */
|
|
info->glyphs[i].i.x = this_x - x;
|
|
info->glyphs[i].i.y = this_y - y;
|
|
|
|
/* Start a new element for the first glyph,
|
|
* or for any glyph that has unexpected position,
|
|
* or if current element has too many glyphs.
|
|
*
|
|
* These same conditions are mirrored in _emit_glyphs_chunk().
|
|
*/
|
|
if (_start_new_glyph_elt (i, &info->glyphs[i]))
|
|
request_size += _cairo_sz_x_glyph_elt_t;
|
|
|
|
/* adjust current-position */
|
|
x = this_x + scaled_glyph->x_advance;
|
|
y = this_y + scaled_glyph->y_advance;
|
|
|
|
request_size += width;
|
|
}
|
|
|
|
if (i) {
|
|
status = _emit_glyphs_chunk (dst, op, src,
|
|
info->glyphs, i,
|
|
width, request_size,
|
|
glyphset_info,
|
|
info->use_mask ? glyphset_info->xrender_format : 0);
|
|
}
|
|
|
|
cairo_surface_destroy (&src->base);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_surface_owns_font (cairo_xcb_surface_t *dst,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xcb_font_t *font_private;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if ((scaled_font->surface_backend != NULL &&
|
|
scaled_font->surface_backend != dst->base.backend) ||
|
|
(font_private != NULL && font_private->connection != dst->connection))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
cairo_int_status_t
|
|
_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *source,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_clip_t *clip)
|
|
{
|
|
cairo_composite_rectangles_t extents;
|
|
cairo_clip_t local_clip;
|
|
cairo_bool_t have_clip = FALSE;
|
|
cairo_status_t status;
|
|
cairo_bool_t overlap;
|
|
|
|
if (unlikely (! _operator_is_supported (surface->flags, op)))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if ((surface->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
status = _cairo_composite_rectangles_init_for_glyphs (&extents,
|
|
surface->width,
|
|
surface->height,
|
|
op, source,
|
|
scaled_font,
|
|
glyphs, num_glyphs,
|
|
clip, &overlap);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (_cairo_clip_contains_rectangle (clip, &extents.mask))
|
|
clip = NULL;
|
|
|
|
if (clip != NULL && extents.is_bounded) {
|
|
clip = _cairo_clip_init_copy (&local_clip, clip);
|
|
status = _cairo_clip_rectangle (clip, &extents.bounded);
|
|
if (unlikely (status)) {
|
|
_cairo_clip_fini (&local_clip);
|
|
return status;
|
|
}
|
|
|
|
have_clip = TRUE;
|
|
}
|
|
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
if (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) {
|
|
_cairo_scaled_font_freeze_cache (scaled_font);
|
|
|
|
if (_surface_owns_font (surface, scaled_font)) {
|
|
status = _can_composite_glyphs (surface, &extents.bounded,
|
|
scaled_font, glyphs, &num_glyphs);
|
|
if (likely (status == CAIRO_STATUS_SUCCESS)) {
|
|
composite_glyphs_info_t info;
|
|
|
|
info.font = scaled_font;
|
|
info.glyphs = (cairo_xcb_glyph_t *) glyphs;
|
|
info.num_glyphs = num_glyphs;
|
|
info.use_mask = overlap || clip != NULL || ! extents.is_bounded;
|
|
|
|
status = _clip_and_composite (surface, op, source,
|
|
_composite_glyphs, &info,
|
|
&extents, clip);
|
|
}
|
|
}
|
|
|
|
_cairo_scaled_font_thaw_cache (scaled_font);
|
|
}
|
|
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
|
|
assert (surface->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
|
|
status =
|
|
_cairo_xcb_surface_render_glyphs_via_mask (surface, op, source,
|
|
scaled_font, glyphs, num_glyphs,
|
|
clip, &extents);
|
|
}
|
|
|
|
if (have_clip)
|
|
_cairo_clip_fini (&local_clip);
|
|
|
|
return status;
|
|
}
|