mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-02-19 04:10:31 +01:00
599 lines
16 KiB
C
599 lines
16 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-xcb-private.h"
|
|
|
|
/* XXX dithering */
|
|
|
|
typedef struct _cairo_xcb_pixmap {
|
|
cairo_surface_t base;
|
|
|
|
cairo_xcb_connection_t *connection;
|
|
cairo_xcb_screen_t *screen;
|
|
|
|
cairo_surface_t *owner;
|
|
xcb_pixmap_t pixmap;
|
|
int width;
|
|
int height;
|
|
int depth;
|
|
int x0, y0;
|
|
cairo_bool_t repeat;
|
|
} cairo_xcb_pixmap_t;
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_pixmap_finish (void *abstract_surface)
|
|
{
|
|
cairo_xcb_pixmap_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
if (surface->owner != NULL) {
|
|
cairo_surface_destroy (surface->owner);
|
|
} else {
|
|
status = _cairo_xcb_connection_acquire (surface->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
_cairo_xcb_connection_free_pixmap (surface->connection,
|
|
surface->pixmap);
|
|
_cairo_xcb_connection_release (surface->connection);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = {
|
|
CAIRO_SURFACE_TYPE_XCB,
|
|
NULL,
|
|
_cairo_xcb_pixmap_finish,
|
|
};
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target,
|
|
int width, int height)
|
|
{
|
|
cairo_xcb_pixmap_t *surface;
|
|
|
|
surface = malloc (sizeof (cairo_xcb_pixmap_t));
|
|
if (unlikely (surface == NULL))
|
|
return (cairo_xcb_pixmap_t *)
|
|
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&_cairo_xcb_pixmap_backend,
|
|
NULL,
|
|
target->base.content);
|
|
|
|
surface->connection = target->connection;
|
|
surface->screen = target->screen;
|
|
surface->owner = NULL;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
surface->depth = target->depth;
|
|
surface->x0 = surface->y0 = 0;
|
|
surface->repeat = FALSE;
|
|
|
|
surface->pixmap =
|
|
_cairo_xcb_connection_create_pixmap (surface->connection,
|
|
surface->depth,
|
|
target->drawable,
|
|
width, height);
|
|
|
|
return surface;
|
|
}
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target)
|
|
{
|
|
cairo_xcb_pixmap_t *surface;
|
|
|
|
surface = malloc (sizeof (cairo_xcb_pixmap_t));
|
|
if (unlikely (surface == NULL))
|
|
return (cairo_xcb_pixmap_t *)
|
|
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&_cairo_xcb_pixmap_backend,
|
|
NULL,
|
|
target->base.content);
|
|
|
|
surface->connection = target->connection;
|
|
surface->screen = target->screen;
|
|
surface->pixmap = target->drawable;
|
|
surface->owner = cairo_surface_reference (&target->base);
|
|
surface->width = target->width;
|
|
surface->height = target->height;
|
|
surface->depth = target->depth;
|
|
surface->x0 = surface->y0 = 0;
|
|
surface->repeat = FALSE;
|
|
|
|
return surface;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
|
|
pixman_format_code_t pixman_format,
|
|
int width, int height,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_xcb_shm_info_t **shm_info_out)
|
|
{
|
|
cairo_surface_t *image = NULL;
|
|
cairo_xcb_shm_info_t *shm_info = NULL;
|
|
cairo_status_t status;
|
|
|
|
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
|
|
if ((connection->flags & CAIRO_XCB_HAS_SHM)) {
|
|
size_t size, stride;
|
|
|
|
stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format));
|
|
size = stride * height;
|
|
if (size > CAIRO_XCB_SHM_SMALL_IMAGE) {
|
|
status = _cairo_xcb_connection_allocate_shm_info (connection,
|
|
size, &shm_info);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
|
|
pixman_format,
|
|
width, height,
|
|
stride);
|
|
status = image->status;
|
|
if (unlikely (status)) {
|
|
_cairo_xcb_shm_info_destroy (shm_info);
|
|
return status;
|
|
}
|
|
|
|
status = _cairo_user_data_array_set_data (&image->user_data,
|
|
(const cairo_user_data_key_t *) connection,
|
|
shm_info,
|
|
(cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (image);
|
|
_cairo_xcb_shm_info_destroy (shm_info);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (image == NULL) {
|
|
image = _cairo_image_surface_create_with_pixman_format (NULL,
|
|
pixman_format,
|
|
width, height,
|
|
0);
|
|
status = image->status;
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
|
|
*image_out = (cairo_image_surface_t *) image;
|
|
*shm_info_out = shm_info;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_pixmap_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_gcontext_t gc;
|
|
cairo_xcb_pixmap_t *pixmap;
|
|
|
|
pixmap = _cairo_xcb_pixmap_create (target,
|
|
image->width,
|
|
image->height);
|
|
if (unlikely (pixmap->base.status))
|
|
return pixmap;
|
|
|
|
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth);
|
|
|
|
if (shm_info != NULL) {
|
|
_cairo_xcb_connection_shm_put_image (target->connection,
|
|
pixmap->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->pixmap, gc,
|
|
image->width, image->height,
|
|
0, 0,
|
|
image->depth,
|
|
image->stride,
|
|
image->data);
|
|
} else {
|
|
_cairo_xcb_connection_put_subimage (target->connection,
|
|
pixmap->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);
|
|
|
|
return pixmap;
|
|
}
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_render_to_pixmap (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_pixmap_t *pixmap;
|
|
|
|
status = _cairo_xcb_shm_image_create (target->screen->connection,
|
|
target->pixman_format,
|
|
extents->width, extents->height,
|
|
&image, &shm_info);
|
|
if (unlikely (status))
|
|
return (cairo_xcb_pixmap_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_pixmap_t *) _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info);
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
if (unlikely (pixmap->base.status))
|
|
return pixmap;
|
|
|
|
pixmap->x0 = -extents->x;
|
|
pixmap->y0 = -extents->y;
|
|
return pixmap;
|
|
}
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_copy_to_pixmap (cairo_xcb_surface_t *source)
|
|
{
|
|
cairo_xcb_pixmap_t *pixmap;
|
|
|
|
/* If the source may be a window, we need to copy it and its children
|
|
* via a temporary pixmap so that we can IncludeInferiors on the source
|
|
* and use ClipByChildren on the destination.
|
|
*/
|
|
if (source->owns_pixmap) {
|
|
pixmap = _cairo_xcb_pixmap_copy (source);
|
|
if (unlikely (pixmap->base.status))
|
|
return pixmap;
|
|
} else {
|
|
uint32_t values[1];
|
|
xcb_gcontext_t gc;
|
|
|
|
pixmap = _cairo_xcb_pixmap_create (source,
|
|
source->width,
|
|
source->height);
|
|
if (unlikely (pixmap->base.status))
|
|
return pixmap;
|
|
|
|
gc = _cairo_xcb_screen_get_gc (source->screen,
|
|
pixmap->pixmap,
|
|
pixmap->depth);
|
|
|
|
values[0] = TRUE;
|
|
_cairo_xcb_connection_change_gc (pixmap->connection, gc,
|
|
XCB_GC_SUBWINDOW_MODE, values);
|
|
|
|
_cairo_xcb_connection_copy_area (pixmap->connection,
|
|
source->drawable,
|
|
pixmap->pixmap, gc,
|
|
0, 0,
|
|
0, 0,
|
|
source->width,
|
|
source->height);
|
|
|
|
values[0] = FALSE;
|
|
_cairo_xcb_connection_change_gc (pixmap->connection, gc,
|
|
XCB_GC_SUBWINDOW_MODE, values);
|
|
|
|
_cairo_xcb_screen_put_gc (source->screen,
|
|
pixmap->depth,
|
|
gc);
|
|
}
|
|
|
|
return pixmap;
|
|
}
|
|
static cairo_xcb_pixmap_t *
|
|
_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target,
|
|
const cairo_surface_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents,
|
|
int tx, int ty)
|
|
{
|
|
cairo_surface_t *source;
|
|
cairo_xcb_pixmap_t *pixmap;
|
|
|
|
source = pattern->surface;
|
|
pixmap = (cairo_xcb_pixmap_t *)
|
|
_cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend);
|
|
if (pixmap != NULL && pixmap->screen == target->screen)
|
|
return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base);
|
|
|
|
if (source->type == CAIRO_SURFACE_TYPE_XCB &&
|
|
((cairo_xcb_surface_t *) source)->screen == target->screen)
|
|
{
|
|
cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source;
|
|
|
|
if (xcb_source->depth == target->depth)
|
|
pixmap = _copy_to_pixmap (xcb_source);
|
|
}
|
|
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
|
|
else if (source->type == CAIRO_SURFACE_TYPE_XLIB &&
|
|
((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen)
|
|
{
|
|
cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb;
|
|
|
|
if (xcb_source->depth == target->depth)
|
|
pixmap = _copy_to_pixmap (xcb_source);
|
|
}
|
|
#endif
|
|
|
|
if (pixmap == NULL) {
|
|
cairo_rectangle_int_t rect;
|
|
|
|
if (! _cairo_surface_get_extents (source, &rect)) {
|
|
rect.x = rect.y = 0;
|
|
rect.width = target->width;
|
|
rect.height = target->height;
|
|
}
|
|
|
|
pixmap = _render_to_pixmap (target, &pattern->base, &rect);
|
|
}
|
|
|
|
if (unlikely (pixmap->base.status))
|
|
return pixmap;
|
|
|
|
_cairo_surface_attach_snapshot (source, &pixmap->base, NULL);
|
|
|
|
if (pattern->base.extend != CAIRO_EXTEND_NONE) {
|
|
if (extents->x < 0 || extents->y < 0 ||
|
|
extents->x + extents->width > pixmap->width ||
|
|
extents->y + extents->height > pixmap->height)
|
|
{
|
|
pixmap->repeat = TRUE;
|
|
}
|
|
}
|
|
|
|
pixmap->x0 += tx;
|
|
pixmap->y0 += ty;
|
|
|
|
return pixmap;
|
|
}
|
|
|
|
static cairo_xcb_pixmap_t *
|
|
_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target,
|
|
const cairo_pattern_t *pattern,
|
|
const cairo_rectangle_int_t *extents)
|
|
{
|
|
int tx, ty;
|
|
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
/* Core can only perform a native, unscaled blit, but can handle tiles */
|
|
if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) {
|
|
switch (pattern->extend) {
|
|
case CAIRO_EXTEND_NONE:
|
|
case CAIRO_EXTEND_REPEAT:
|
|
return _cairo_xcb_surface_pixmap (target,
|
|
(cairo_surface_pattern_t *) pattern,
|
|
extents, tx, ty);
|
|
|
|
default:
|
|
case CAIRO_EXTEND_PAD:
|
|
case CAIRO_EXTEND_REFLECT:
|
|
break;
|
|
}
|
|
}
|
|
/* fallthrough */
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
case CAIRO_PATTERN_TYPE_RADIAL:
|
|
case CAIRO_PATTERN_TYPE_MESH:
|
|
return _render_to_pixmap (target, pattern, extents);
|
|
|
|
default:
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
ASSERT_NOT_REACHED;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst,
|
|
const cairo_pattern_t *src_pattern,
|
|
const cairo_rectangle_int_t *extents,
|
|
const cairo_boxes_t *boxes)
|
|
{
|
|
cairo_xcb_pixmap_t *src;
|
|
const struct _cairo_boxes_chunk *chunk;
|
|
xcb_gcontext_t gc;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_xcb_connection_acquire (dst->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents);
|
|
status = src->base.status;
|
|
if (unlikely (status))
|
|
goto CLEANUP_CONNECTION;
|
|
|
|
assert (src->depth == dst->depth);
|
|
|
|
gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth);
|
|
|
|
if (src->repeat) {
|
|
uint32_t mask =
|
|
XCB_GC_FILL_STYLE |
|
|
XCB_GC_TILE |
|
|
XCB_GC_TILE_STIPPLE_ORIGIN_X |
|
|
XCB_GC_TILE_STIPPLE_ORIGIN_Y;
|
|
uint32_t values[] = {
|
|
XCB_FILL_STYLE_TILED,
|
|
src->pixmap,
|
|
- src->x0, - src->y0,
|
|
};
|
|
xcb_rectangle_t *xcb_rects;
|
|
|
|
_cairo_xcb_connection_change_gc (dst->connection, gc, mask, values);
|
|
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
int i;
|
|
|
|
xcb_rects = (xcb_rectangle_t *) chunk->base;
|
|
|
|
for (i = 0; i < chunk->count; i++) {
|
|
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
|
|
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
|
|
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
|
|
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
|
|
|
|
xcb_rects[i].x = x1;
|
|
xcb_rects[i].y = y1;
|
|
xcb_rects[i].width = x2 - x1;
|
|
xcb_rects[i].height = y2 - y1;
|
|
}
|
|
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
|
|
dst->drawable,
|
|
gc, chunk->count, xcb_rects);
|
|
}
|
|
|
|
values[0] = 0;
|
|
_cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values);
|
|
} else {
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
int i;
|
|
|
|
for (i = 0; i < chunk->count; i++) {
|
|
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
|
|
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
|
|
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
|
|
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
|
|
|
|
_cairo_xcb_connection_copy_area (dst->connection,
|
|
src->pixmap,
|
|
dst->drawable, gc,
|
|
src->x0 + x1,
|
|
src->y0 + y1,
|
|
x1, y1,
|
|
x2 - x2, y2 - x2);
|
|
}
|
|
}
|
|
}
|
|
|
|
_cairo_xcb_screen_put_gc (dst->screen, src->depth, gc);
|
|
cairo_surface_destroy (&src->base);
|
|
|
|
CLEANUP_CONNECTION:
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
|
|
return status;
|
|
}
|
|
|
|
cairo_status_t
|
|
_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
|
|
const cairo_color_t *color,
|
|
cairo_boxes_t *boxes)
|
|
{
|
|
struct _cairo_boxes_chunk *chunk;
|
|
xcb_gcontext_t gc;
|
|
cairo_status_t status;
|
|
|
|
status = _cairo_xcb_connection_acquire (dst->connection);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
|
|
|
|
#if 0
|
|
xcb_pixmap_t source;
|
|
|
|
source = _dither_source (dst, color);
|
|
XSetTSOrigin (surface->dpy, gc, 0, 0);
|
|
XSetTile (surface->dpy, gc, source);
|
|
#endif
|
|
|
|
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
|
|
xcb_rectangle_t *xcb_rects;
|
|
int i;
|
|
|
|
xcb_rects = (xcb_rectangle_t *) chunk->base;
|
|
for (i = 0; i < chunk->count; i++) {
|
|
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
|
|
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
|
|
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
|
|
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
|
|
|
|
xcb_rects[i].x = x1;
|
|
xcb_rects[i].y = y1;
|
|
xcb_rects[i].width = x2 - x1;
|
|
xcb_rects[i].height = y2 - y1;
|
|
}
|
|
|
|
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
|
|
dst->drawable, gc,
|
|
chunk->count, xcb_rects);
|
|
}
|
|
|
|
_cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
|
|
_cairo_xcb_connection_release (dst->connection);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|