blob: 8a1ec7bd731561fbcbfdff9f53823c54be0ca609 [file] [log] [blame]
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 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.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Behdad Esfahbod <behdad@behdad.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
#include "cairoint.h"
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
#include "cairo-xlib-private.h"
#include "cairo-compositor-private.h"
#include "cairo-damage-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-pixman-private.h"
#include "cairo-traps-private.h"
#include "cairo-tristrip-private.h"
static cairo_int_status_t
check_composite (const cairo_composite_rectangles_t *extents)
{
cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display;
if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
return CAIRO_INT_STATUS_UNSUPPORTED;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
acquire (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_int_status_t status;
status = _cairo_xlib_display_acquire (dst->base.device, &dst->display);
if (unlikely (status))
return status;
dst->dpy = dst->display->display;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
release (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_device_release (&dst->display->base);
dst->dpy = NULL;
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
set_clip_region (void *_surface,
cairo_region_t *region)
{
cairo_xlib_surface_t *surface = _surface;
_cairo_xlib_surface_ensure_picture (surface);
if (region != NULL) {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
int n_rects, i;
n_rects = cairo_region_num_rectangles (region);
if (n_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < n_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;
}
XRenderSetPictureClipRectangles (surface->dpy,
surface->picture,
0, 0,
rects, n_rects);
if (rects != stack_rects)
free (rects);
} else {
XRenderPictureAttributes pa;
pa.clip_mask = None;
XRenderChangePicture (surface->dpy,
surface->picture,
CPClipMask, &pa);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
copy_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
cairo_int_status_t status;
Pixmap src;
GC gc;
int i, j;
assert (image->depth == dst->depth);
status = acquire (dst);
if (unlikely (status))
return status;
status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
if (unlikely (status)) {
release (dst);
return status;
}
src = _cairo_xlib_shm_surface_get_pixmap (&image->base);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
_cairo_xlib_shm_surface_mark_active (&image->base);
XCopyArea (dst->dpy, src, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
} else {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
if (x2 > x1 && y2 > y1) {
rects[j].x = x1;
rects[j].y = y1;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
}
XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
_cairo_xlib_shm_surface_mark_active (&image->base);
XCopyArea (dst->dpy, src, dst->drawable, gc,
0, 0, image->width, image->height, -dx, -dy);
XSetClipMask (dst->dpy, gc, None);
if (rects != stack_rects)
free (rects);
}
_cairo_xlib_surface_put_gc (dst->display, dst, gc);
release (dst);
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
boxes_cover_surface (cairo_boxes_t *boxes,
cairo_xlib_surface_t *surface)
{
cairo_box_t *b;
if (boxes->num_boxes != 1)
return FALSE;
b = &boxes->chunks.base[0];
if (_cairo_fixed_integer_part (b->p1.x) > 0 ||
_cairo_fixed_integer_part (b->p1.y) > 0)
return FALSE;
if (_cairo_fixed_integer_part (b->p2.x) < surface->width ||
_cairo_fixed_integer_part (b->p2.y) < surface->height)
return FALSE;
return TRUE;
}
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
cairo_image_surface_t *shm = NULL;
cairo_int_status_t status;
int i;
if (image->base.device == dst->base.device) {
if (image->depth != dst->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_xlib_shm_surface_get_pixmap (&image->base))
return copy_image_boxes (dst, image, boxes, dx, dy);
goto draw_image_boxes;
}
if (boxes_cover_surface (boxes, dst))
shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE);
if (shm) {
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
cairo_rectangle_int_t r;
r.x = _cairo_fixed_integer_part (b->p1.x);
r.y = _cairo_fixed_integer_part (b->p1.y);
r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
if (shm->pixman_format != image->pixman_format ||
! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
image->stride / sizeof (uint32_t),
shm->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (image->pixman_format),
PIXMAN_FORMAT_BPP (shm->pixman_format),
r.x + dx, r.y + dy,
r.x, r.y,
r.width, r.height))
{
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, shm->pixman_image,
r.x + dx, r.y + dy,
0, 0,
r.x, r.y,
r.width, r.height);
}
shm->base.damage =
_cairo_damage_add_rectangle (shm->base.damage, &r);
}
}
dst->base.is_clear = FALSE;
dst->fallback++;
dst->base.serial++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
if (image->depth == dst->depth &&
((cairo_xlib_display_t *)dst->display)->shm) {
cairo_box_t extents;
cairo_rectangle_int_t r;
_cairo_boxes_extents (boxes, &extents);
_cairo_box_round_to_rectangle (&extents, &r);
shm = (cairo_image_surface_t *)
_cairo_xlib_surface_create_shm (dst, image->pixman_format,
r.width, r.height);
if (shm) {
int tx = -r.x, ty = -r.y;
assert (shm->pixman_format == image->pixman_format);
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
r.x = _cairo_fixed_integer_part (b->p1.x);
r.y = _cairo_fixed_integer_part (b->p1.y);
r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
image->stride / sizeof (uint32_t),
shm->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (image->pixman_format),
PIXMAN_FORMAT_BPP (shm->pixman_format),
r.x + dx, r.y + dy,
r.x + tx, r.y + ty,
r.width, r.height))
{
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, shm->pixman_image,
r.x + dx, r.y + dy,
0, 0,
r.x + tx, r.y + ty,
r.width, r.height);
}
}
}
dx = tx;
dy = ty;
image = shm;
if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) {
status = copy_image_boxes (dst, image, boxes, dx, dy);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto out;
}
}
}
draw_image_boxes:
status = CAIRO_STATUS_SUCCESS;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x1 = _cairo_fixed_integer_part (b->p1.x);
int y1 = _cairo_fixed_integer_part (b->p1.y);
int x2 = _cairo_fixed_integer_part (b->p2.x);
int y2 = _cairo_fixed_integer_part (b->p2.y);
if (_cairo_xlib_surface_draw_image (dst, image,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto out;
}
}
}
out:
cairo_surface_destroy (&shm->base);
return status;
}
static cairo_int_status_t
copy_boxes (void *_dst,
cairo_surface_t *_src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src;
struct _cairo_boxes_chunk *chunk;
cairo_int_status_t status;
GC gc;
Drawable d;
int i, j;
if (! _cairo_xlib_surface_same_screen (dst, src))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (dst->depth != src->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
status = acquire (dst);
if (unlikely (status))
return status;
status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
if (unlikely (status)) {
release (dst);
return status;
}
if (src->fallback && src->shm->damage->dirty) {
assert (src != dst);
d = _cairo_xlib_shm_surface_get_pixmap (src->shm);
assert (d != 0);
} else {
if (! src->owns_pixmap) {
XGCValues gcv;
gcv.subwindow_mode = IncludeInferiors;
XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
}
d = src->drawable;
}
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
XCopyArea (dst->dpy, d, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
} else {
/* We can only have a single control for subwindow_mode on the
* GC. If we have a Window destination, we need to set ClipByChildren,
* but if we have a Window source, we need IncludeInferiors. If we have
* both a Window destination and source, we must fallback. There is
* no convenient way to detect if a drawable is a Pixmap or Window,
* therefore we can only rely on those surfaces that we created
* ourselves to be Pixmaps, and treat everything else as a potential
* Window.
*/
if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) {
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
XCopyArea (dst->dpy, d, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
}
}
} else {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
rects[j].x = x1;
rects[j].y = y1;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
assert (j == boxes->num_boxes);
XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
XCopyArea (dst->dpy, d, dst->drawable, gc,
extents->x + dx, extents->y + dy,
extents->width, extents->height,
extents->x, extents->y);
XSetClipMask (dst->dpy, gc, None);
if (rects != stack_rects)
free (rects);
}
}
if (src->fallback && src->shm->damage->dirty) {
_cairo_xlib_shm_surface_mark_active (src->shm);
} else if (! src->owns_pixmap) {
XGCValues gcv;
gcv.subwindow_mode = ClipByChildren;
XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
}
_cairo_xlib_surface_put_gc (dst->display, dst, gc);
release (dst);
return CAIRO_STATUS_SUCCESS;
}
static int
_render_operator (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return PictOpClear;
case CAIRO_OPERATOR_SOURCE:
return PictOpSrc;
case CAIRO_OPERATOR_OVER:
return PictOpOver;
case CAIRO_OPERATOR_IN:
return PictOpIn;
case CAIRO_OPERATOR_OUT:
return PictOpOut;
case CAIRO_OPERATOR_ATOP:
return PictOpAtop;
case CAIRO_OPERATOR_DEST:
return PictOpDst;
case CAIRO_OPERATOR_DEST_OVER:
return PictOpOverReverse;
case CAIRO_OPERATOR_DEST_IN:
return PictOpInReverse;
case CAIRO_OPERATOR_DEST_OUT:
return PictOpOutReverse;
case CAIRO_OPERATOR_DEST_ATOP:
return PictOpAtopReverse;
case CAIRO_OPERATOR_XOR:
return PictOpXor;
case CAIRO_OPERATOR_ADD:
return PictOpAdd;
case CAIRO_OPERATOR_SATURATE:
return PictOpSaturate;
case CAIRO_OPERATOR_MULTIPLY:
return PictOpMultiply;
case CAIRO_OPERATOR_SCREEN:
return PictOpScreen;
case CAIRO_OPERATOR_OVERLAY:
return PictOpOverlay;
case CAIRO_OPERATOR_DARKEN:
return PictOpDarken;
case CAIRO_OPERATOR_LIGHTEN:
return PictOpLighten;
case CAIRO_OPERATOR_COLOR_DODGE:
return PictOpColorDodge;
case CAIRO_OPERATOR_COLOR_BURN:
return PictOpColorBurn;
case CAIRO_OPERATOR_HARD_LIGHT:
return PictOpHardLight;
case CAIRO_OPERATOR_SOFT_LIGHT:
return PictOpSoftLight;
case CAIRO_OPERATOR_DIFFERENCE:
return PictOpDifference;
case CAIRO_OPERATOR_EXCLUSION:
return PictOpExclusion;
case CAIRO_OPERATOR_HSL_HUE:
return PictOpHSLHue;
case CAIRO_OPERATOR_HSL_SATURATION:
return PictOpHSLSaturation;
case CAIRO_OPERATOR_HSL_COLOR:
return PictOpHSLColor;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return PictOpHSLLuminosity;
default:
ASSERT_NOT_REACHED;
return PictOpOver;
}
}
static cairo_bool_t
fill_reduces_to_source (cairo_operator_t op,
const cairo_color_t *color,
cairo_xlib_surface_t *dst)
{
if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) {
if (op == CAIRO_OPERATOR_OVER)
return TRUE;
if (op == CAIRO_OPERATOR_ADD)
return (dst->base.content & CAIRO_CONTENT_COLOR) == 0;
}
return FALSE;
}
static cairo_int_status_t
fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_xlib_surface_t *dst = abstract_surface;
XRenderColor render_color;
int i;
//X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
if (fill_reduces_to_source (op, color, dst))
op = CAIRO_OPERATOR_SOURCE;
if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
cairo_int_status_t status;
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_SOURCE)
status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects);
return status;
}
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
_cairo_xlib_surface_ensure_picture (dst);
if (num_rects == 1) {
/* Take advantage of the protocol compaction that libXrender performs
* to amalgamate sequences of XRenderFillRectangle().
*/
XRenderFillRectangle (dst->dpy,
_render_operator (op),
dst->picture,
&render_color,
rects->x, rects->y,
rects->width, rects->height);
} else {
XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *xrects = stack_xrects;
if (num_rects > ARRAY_LENGTH (stack_xrects)) {
xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
if (unlikely (xrects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < num_rects; i++) {
xrects[i].x = rects[i].x;
xrects[i].y = rects[i].y;
xrects[i].width = rects[i].width;
xrects[i].height = rects[i].height;
}
XRenderFillRectangles (dst->dpy,
_render_operator (op),
dst->picture,
&render_color, xrects, num_rects);
if (xrects != stack_xrects)
free (xrects);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
fill_boxes (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_xlib_surface_t *dst = abstract_surface;
XRenderColor render_color;
if (fill_reduces_to_source (op, color, dst))
op = CAIRO_OPERATOR_SOURCE;
if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
cairo_int_status_t status;
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_SOURCE)
status = _cairo_xlib_core_fill_boxes (dst, color, boxes);
return status;
}
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
_cairo_xlib_surface_ensure_picture (dst);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
/* Take advantage of the protocol compaction that libXrender performs
* to amalgamate sequences of XRenderFillRectangle().
*/
XRenderFillRectangle (dst->dpy,
_render_operator (op),
dst->picture,
&render_color,
x1, y1,
x2 - x1, y2 - y1);
} else {
XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *xrects = stack_xrects;
struct _cairo_boxes_chunk *chunk;
int i, j;
if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) {
xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (xrects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
xrects[j].x = x1;
xrects[j].y = y1;
xrects[j].width = x2 - x1;
xrects[j].height = y2 - y1;
j++;
}
}
XRenderFillRectangles (dst->dpy,
_render_operator (op),
dst->picture,
&render_color, xrects, j);
if (xrects != stack_xrects)
free (xrects);
}
return CAIRO_STATUS_SUCCESS;
}
#if 0
check_composite ()
operation = _categorize_composite_operation (dst, op, src_pattern,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED)
return UNSUPPORTED ("unsupported operation");
//X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable));
operation = _recategorize_composite_operation (dst, op, src, &src_attr,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED) {
status = UNSUPPORTED ("unsupported operation");
goto BAIL;
}
#endif
static cairo_int_status_t
composite (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
op = _render_operator (op);
_cairo_xlib_surface_ensure_picture (dst);
if (abstract_mask) {
cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
XRenderComposite (dst->dpy, op,
src->picture, mask->picture, dst->picture,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
} else {
XRenderComposite (dst->dpy, op,
src->picture, 0, dst->picture,
src_x, src_y,
0, 0,
dst_x, dst_y,
width, height);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
lerp (void *abstract_dst,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
_cairo_xlib_surface_ensure_picture (dst);
XRenderComposite (dst->dpy, PictOpOutReverse,
mask->picture, None, dst->picture,
mask_x, mask_y,
0, 0,
dst_x, dst_y,
width, height);
XRenderComposite (dst->dpy, PictOpAdd,
src->picture, mask->picture, dst->picture,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
composite_boxes (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
cairo_xlib_surface_t *dst = abstract_dst;
Picture src = ((cairo_xlib_source_t *)abstract_src)->picture;
Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0;
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
struct _cairo_boxes_chunk *chunk;
int i, j;
op = _render_operator (op);
_cairo_xlib_surface_ensure_picture (dst);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
XRenderComposite (dst->dpy, op,
src, mask, dst->picture,
x1 + src_x, y1 + src_y,
x1 + mask_x, y1 + mask_y,
x1 - dst_x, y1 - dst_y,
x2 - x1, y2 - y1);
return CAIRO_STATUS_SUCCESS;
}
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
rects[j].x = x1 - dst_x;
rects[j].y = y1 - dst_y;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
assert (j == boxes->num_boxes);
XRenderSetPictureClipRectangles (dst->dpy,
dst->picture,
0, 0,
rects, j);
if (rects != stack_rects)
free (rects);
XRenderComposite (dst->dpy, op,
src, mask, 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);
set_clip_region (dst, NULL);
return CAIRO_STATUS_SUCCESS;
}
/* font rendering */
void
_cairo_xlib_font_close (cairo_xlib_font_t *priv)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key;
int i;
/* XXX All I really want is to do is zap my glyphs... */
_cairo_scaled_font_reset_cache (priv->font);
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info;
info = &priv->glyphset[i];
if (info->glyphset)
XRenderFreeGlyphSet (display->display, info->glyphset);
}
/* XXX locking */
cairo_list_del (&priv->link);
cairo_list_del (&priv->base.link);
free (priv);
}
static void
_cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private,
cairo_scaled_font_t *font)
{
cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private;
cairo_status_t status;
cairo_xlib_display_t *display;
int i;
cairo_list_del (&priv->base.link);
cairo_list_del (&priv->link);
status = _cairo_xlib_display_acquire (priv->device, &display);
if (status)
goto BAIL;
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info;
info = &priv->glyphset[i];
if (info->glyphset)
XRenderFreeGlyphSet (display->display, info->glyphset);
}
cairo_device_release (&display->base);
BAIL:
cairo_device_destroy (&display->base);
free (priv);
}
static cairo_xlib_font_t *
_cairo_xlib_font_create (cairo_xlib_display_t *display,
cairo_scaled_font_t *font)
{
cairo_xlib_font_t *priv;
int i;
priv = malloc (sizeof (cairo_xlib_font_t));
if (unlikely (priv == NULL))
return NULL;
_cairo_scaled_font_attach_private (font, &priv->base, display,
_cairo_xlib_font_fini);
priv->device = cairo_device_reference (&display->base);
priv->font = font;
cairo_list_add (&priv->link, &display->fonts);
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info = &priv->glyphset[i];
switch (i) {
case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break;
case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break;
default: ASSERT_NOT_REACHED; break;
}
info->xrender_format = NULL;
info->glyphset = None;
info->to_free.count = 0;
}
return priv;
}
static int
_cairo_xlib_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 inline cairo_xlib_font_t *
_cairo_xlib_font_get (const cairo_xlib_display_t *display,
cairo_scaled_font_t *font)
{
return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display);
}
typedef struct {
cairo_scaled_glyph_private_t base;
cairo_xlib_font_glyphset_t *glyphset;
} cairo_xlib_glyph_private_t;
static void
_cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
cairo_scaled_glyph_t *glyph,
cairo_scaled_font_t *font)
{
cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private;
if (! font->finished) {
cairo_xlib_font_t *font_private;
struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
cairo_xlib_font_glyphset_t *info;
font_private = _cairo_xlib_font_get (glyph_private->key, font);
assert (font_private);
info = priv->glyphset;
to_free = &info->to_free;
if (to_free->count == ARRAY_LENGTH (to_free->indices)) {
cairo_xlib_display_t *display;
if (_cairo_xlib_display_acquire (font_private->device,
&display) == CAIRO_STATUS_SUCCESS) {
XRenderFreeGlyphs (display->display,
info->glyphset,
to_free->indices,
to_free->count);
cairo_device_release (&display->base);
}
to_free->count = 0;
}
to_free->indices[to_free->count++] =
_cairo_scaled_glyph_index (glyph);
}
cairo_list_del (&glyph_private->link);
free (glyph_private);
}
static cairo_status_t
_cairo_xlib_glyph_attach (cairo_xlib_display_t *display,
cairo_scaled_glyph_t *glyph,
cairo_xlib_font_glyphset_t *info)
{
cairo_xlib_glyph_private_t *priv;
priv = malloc (sizeof (*priv));
if (unlikely (priv == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
_cairo_scaled_glyph_attach_private (glyph, &priv->base, display,
_cairo_xlib_glyph_fini);
priv->glyphset = info;
glyph->dev_private = info;
glyph->dev_private_key = display;
return CAIRO_STATUS_SUCCESS;
}
static cairo_xlib_font_glyphset_t *
_cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
cairo_format_t format)
{
cairo_xlib_font_t *priv;
cairo_xlib_font_glyphset_t *info;
int glyphset_index;
glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
priv = _cairo_xlib_font_get (display, font);
if (priv == NULL) {
priv = _cairo_xlib_font_create (display, font);
if (priv == NULL)
return NULL;
}
info = &priv->glyphset[glyphset_index];
if (info->glyphset == None) {
info->xrender_format =
_cairo_xlib_display_get_xrender_format (display, info->format);
info->glyphset = XRenderCreateGlyphSet (display->display,
info->xrender_format);
}
return info;
}
static cairo_bool_t
has_pending_free_glyph (cairo_xlib_font_glyphset_t *info,
unsigned long glyph_index)
{
struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
int i;
to_free = &info->to_free;
for (i = 0; i < to_free->count; i++) {
if (to_free->indices[i] == glyph_index) {
to_free->count--;
memmove (&to_free->indices[i],
&to_free->indices[i+1],
(to_free->count - i) * sizeof (to_free->indices[0]));
return TRUE;
}
}
return FALSE;
}
static cairo_xlib_font_glyphset_t *
find_pending_free_glyph (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
unsigned long glyph_index,
cairo_image_surface_t *surface)
{
cairo_xlib_font_t *priv;
int i;
priv = _cairo_xlib_font_get (display, font);
if (priv == NULL)
return NULL;
if (surface != NULL) {
i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
return &priv->glyphset[i];
} else {
for (i = 0; i < NUM_GLYPHSETS; i++) {
if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
return &priv->glyphset[i];
}
}
return NULL;
}
static cairo_status_t
_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
cairo_scaled_glyph_t **pscaled_glyph)
{
XGlyphInfo glyph_info;
unsigned long glyph_index;
unsigned char *data;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_scaled_glyph_t *glyph = *pscaled_glyph;
cairo_image_surface_t *glyph_surface = glyph->surface;
cairo_bool_t already_had_glyph_surface;
cairo_xlib_font_glyphset_t *info;
glyph_index = _cairo_scaled_glyph_index (glyph);
/* check to see if we have a pending XRenderFreeGlyph for this glyph */
info = find_pending_free_glyph (display, font, glyph_index, glyph_surface);
if (info != NULL)
return _cairo_xlib_glyph_attach (display, glyph, info);
if (glyph_surface == NULL) {
status = _cairo_scaled_glyph_lookup (font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_SURFACE,
pscaled_glyph);
if (unlikely (status))
return status;
glyph = *pscaled_glyph;
glyph_surface = glyph->surface;
already_had_glyph_surface = FALSE;
} else {
already_had_glyph_surface = TRUE;
}
info = _cairo_xlib_font_get_glyphset_info_for_format (display, font,
glyph_surface->format);
#if 0
/* 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 (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;
}
#endif
/* 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 != info->format) {
cairo_surface_pattern_t pattern;
cairo_surface_t *tmp_surface;
tmp_surface = cairo_image_surface_create (info->format,
glyph_surface->width,
glyph_surface->height);
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;
_cairo_pattern_init_for_surface (&pattern, &glyph_surface->base);
status = _cairo_surface_paint (tmp_surface,
CAIRO_OPERATOR_SOURCE, &pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
glyph_surface = (cairo_image_surface_t *) tmp_surface;
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.xOff = glyph->x_advance;
glyph_info.yOff = glyph->y_advance;
data = glyph_surface->data;
/* flip formats around */
switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) {
case GLYPHSET_INDEX_A1:
/* local bitmaps are always stored with bit == byte */
if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) {
int c = glyph_surface->stride * glyph_surface->height;
unsigned char *d;
unsigned char *new, *n;
if (c == 0)
break;
new = malloc (c);
if (!new) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
n = new;
d = data;
do {
char b = *d++;
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
*n++ = b;
} while (--c);
data = new;
}
break;
case GLYPHSET_INDEX_A8:
break;
case GLYPHSET_INDEX_ARGB32:
if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) {
unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
const uint32_t *d;
uint32_t *new, *n;
if (c == 0)
break;
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 */
XRenderAddGlyphs (display->display, info->glyphset,
&glyph_index, &glyph_info, 1,
(char *) data,
glyph_surface->stride * glyph_surface->height);
if (data != glyph_surface->data)
free (data);
status = _cairo_xlib_glyph_attach (display, glyph, info);
BAIL:
if (glyph_surface != 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 (eg. because image backend requested it), leave it in
* the cache
*/
if (!already_had_glyph_surface)
_cairo_scaled_glyph_set_surface (glyph, font, NULL);
return status;
}
typedef void (*cairo_xrender_composite_text_func_t)
(Display *dpy,
int op,
Picture src,
Picture dst,
_Xconst XRenderPictFormat *maskFormat,
int xSrc,
int ySrc,
int xDst,
int yDst,
_Xconst XGlyphElt8 *elts,
int nelt);
/* 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_xlib_glyph_t;
/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */
COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t));
/* 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_xlib_surface_emit_glyphs and _emit_glyph_chunks
*/
#define _start_new_glyph_elt(count, glyph) \
(((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
static cairo_status_t
_emit_glyphs_chunk (cairo_xlib_display_t *display,
cairo_xlib_surface_t *dst,
int dst_x, int dst_y,
cairo_xlib_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *font,
cairo_bool_t use_mask,
cairo_operator_t op,
cairo_xlib_source_t *src,
int src_x, int src_y,
/* info for this chunk */
int num_elts,
int width,
cairo_xlib_font_glyphset_t *info)
{
/* Which XRenderCompositeText function to use */
cairo_xrender_composite_text_func_t composite_text_func;
int size;
/* Element buffer stuff */
XGlyphElt8 *elts;
XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)];
/* Reuse the input glyph array for output char generation */
char *char8 = (char *) glyphs;
unsigned short *char16 = (unsigned short *) glyphs;
unsigned int *char32 = (unsigned int *) glyphs;
int i;
int nelt; /* Element index */
int n; /* Num output glyphs in current element */
int j; /* Num output glyphs so far */
switch (width) {
case 1:
/* don't cast the 8-variant, to catch possible mismatches */
composite_text_func = XRenderCompositeText8;
size = sizeof (char);
break;
case 2:
composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16;
size = sizeof (unsigned short);
break;
default:
case 4:
composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32;
size = sizeof (unsigned int);
}
/* Allocate element array */
if (num_elts <= ARRAY_LENGTH (stack_elts)) {
elts = stack_elts;
} else {
elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8));
if (unlikely (elts == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
/* Fill them in */
nelt = 0;
n = 0;
j = 0;
for (i = 0; i < num_glyphs; i++) {
/* Start a new element for first output glyph,
* or for any glyph that has unexpected position,
* or if current element has too many glyphs.
*
* These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs()
*/
if (_start_new_glyph_elt (j, &glyphs[i])) {
if (j) {
elts[nelt].nchars = n;
nelt++;
n = 0;
}
elts[nelt].chars = char8 + size * j;
elts[nelt].glyphset = info->glyphset;
elts[nelt].xOff = glyphs[i].i.x;
elts[nelt].yOff = glyphs[i].i.y;
}
switch (width) {
case 1: char8 [j] = (char) glyphs[i].index; break;
case 2: char16[j] = (unsigned short) glyphs[i].index; break;
default:
case 4: char32[j] = (unsigned int) glyphs[i].index; break;
}
n++;
j++;
}
if (n) {
elts[nelt].nchars = n;
nelt++;
}
/* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
* expected number of xGlyphElts. */
assert (nelt == num_elts);
composite_text_func (display->display, op,
src->picture,
dst->picture,
use_mask ? info->xrender_format : NULL,
src_x + elts[0].xOff + dst_x,
src_y + elts[0].yOff + dst_y,
elts[0].xOff, elts[0].yOff,
(XGlyphElt8 *) elts, nelt);
if (elts != stack_elts)
free (elts);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
cairo_xlib_display_t *display = dst->display;
int max_request_size, size;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
return CAIRO_INT_STATUS_UNSUPPORTED;
/* 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
* surface origin. If we can't guarantee this to be possible,
* fallback.
*/
if (extents->bounded.x + extents->bounded.width > INT16_MAX ||
extents->bounded.y + extents->bounded.height> INT16_MAX ||
extents->bounded.x < INT16_MIN ||
extents->bounded.y < INT16_MIN)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* Approximate the size of the largest glyph and fallback if we can not
* upload it to the xserver.
*/
size = ceil (font->max_scale);
size = 4 * size * size;
max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
: XMaxRequestSize (display->display)) * 4 -
sz_xRenderAddGlyphsReq -
sz_xGlyphInfo -
8;
if (size >= max_request_size)
return CAIRO_INT_STATUS_UNSUPPORTED;
return CAIRO_STATUS_SUCCESS;
}
/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
* enough room for padding */
#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4)
static cairo_int_status_t
composite_glyphs (void *surface,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_xlib_surface_t *dst = surface;
cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src;
cairo_xlib_display_t *display = dst->display;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_scaled_glyph_t *glyph;
cairo_fixed_t x = dst_x, y = dst_y;
cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info;
unsigned long max_index = 0;
int width = 1;
int num_elts = 0;
int num_out_glyphs = 0;
int num_glyphs = info->num_glyphs;
int max_request_size = XMaxRequestSize (display->display) * 4
- MAX (sz_xRenderCompositeGlyphs8Req,
MAX(sz_xRenderCompositeGlyphs16Req,
sz_xRenderCompositeGlyphs32Req));
int request_size = 0;
int i;
op = _render_operator (op),
_cairo_xlib_surface_ensure_picture (dst);
for (i = 0; i < num_glyphs; i++) {
int this_x, this_y;
int old_width;
status = _cairo_scaled_glyph_lookup (info->font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&glyph);
if (unlikely (status))
return status;
this_x = _cairo_lround (glyphs[i].d.x);
this_y = _cairo_lround (glyphs[i].d.y);
/* Send unsent glyphs to the server */
if (glyph->dev_private_key != display) {
status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph);
if (unlikely (status))
return status;
}
this_glyphset_info = glyph->dev_private;
if (!glyphset)
glyphset = this_glyphset_info;
/* The invariant here is that we can always flush the glyphs
* accumulated before this one, using old_width, and they
* would fit in the request.
*/
old_width = width;
/* Update max glyph index */
if (glyphs[i].index > max_index) {
max_index = glyphs[i].index;
if (max_index >= 65536)
width = 4;
else if (max_index >= 256)
width = 2;
if (width != old_width)
request_size += (width - old_width) * num_out_glyphs;
}
/* 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 (otherwise we would have
* fallen back).
*/
if (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
(this_glyphset_info != glyphset)) {
status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
glyphs, i, info->font, info->use_mask,
op, src, src_x, src_y,
num_elts, old_width, glyphset);
if (unlikely (status))
return status;
glyphs += i;
num_glyphs -= i;
i = 0;
max_index = glyphs[i].index;
width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
request_size = 0;
num_elts = 0;
num_out_glyphs = 0;
x = y = 0;
glyphset = this_glyphset_info;
}
/* Convert absolute glyph position to relative-to-current-point
* position */
glyphs[i].i.x = this_x - x;
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 (num_out_glyphs, &glyphs[i])) {
num_elts++;
request_size += _cairo_sz_xGlyphElt;
}
/* adjust current-position */
x = this_x + glyph->x_advance;
y = this_y + glyph->y_advance;
num_out_glyphs++;
request_size += width;
}
if (num_elts) {
status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
glyphs, i, info->font, info->use_mask,
op, src, src_x, src_y,
num_elts, width, glyphset);
}
return status;
}
const cairo_compositor_t *
_cairo_xlib_mask_compositor_get (void)
{
static cairo_mask_compositor_t compositor;
if (compositor.base.delegate == NULL) {
_cairo_mask_compositor_init (&compositor,
_cairo_xlib_fallback_compositor_get ());
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
compositor.fill_rectangles = fill_rectangles;
compositor.fill_boxes = fill_boxes;
compositor.copy_boxes = copy_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
return &compositor.base;
}
#define CAIRO_FIXED_16_16_MIN -32768
#define CAIRO_FIXED_16_16_MAX 32767
static cairo_bool_t
line_exceeds_16_16 (const cairo_line_t *line)
{
return
line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p2.y > _cairo_fixed_from_int (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,
XLineFixed *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));
}
#if 0
static cairo_int_status_T
check_composite_trapezoids ()
{
operation = _categorize_composite_operation (dst, op, pattern, TRUE);
if (operation == DO_UNSUPPORTED)
return UNSUPPORTED ("unsupported operation");
operation = _recategorize_composite_operation (dst, op, src,
&attributes, TRUE);
if (operation == DO_UNSUPPORTED) {
status = UNSUPPORTED ("unsupported operation");
goto BAIL;
}
}
#endif
static cairo_int_status_t
composite_traps (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_display_t *display = dst->display;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
XRenderPictFormat *pict_format;
XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
XTrapezoid *xtraps = xtraps_stack;
int dx, dy;
int i;
//X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
if (dst->base.is_clear &&
(op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))
{
op = CAIRO_OPERATOR_SOURCE;
}
pict_format =
_cairo_xlib_display_get_xrender_format (display,
antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) {
xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid));
if (unlikely (xtraps == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
dx = -dst_x << 16;
dy = -dst_y << 16;
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) + dy;
xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy;
/* 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.x += dx;
xtraps[i].left.p2.x += dx;
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) + dx;
xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy;
xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx;
xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy;
}
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.x += dx;
xtraps[i].right.p2.x += dx;
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) + dx;
xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy;
xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx;
xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy;
}
}
if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
} else {
src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
}
src_x += dst_x;
src_y += dst_y;
_cairo_xlib_surface_ensure_picture (dst);
_cairo_xlib_surface_set_precision (dst, antialias);
XRenderCompositeTrapezoids (dst->dpy,
_render_operator (op),
src->picture, dst->picture,
pict_format,
src_x, src_y,
xtraps, traps->num_traps);
if (xtraps != xtraps_stack)
free (xtraps);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
composite_tristrip (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *strip)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_display_t *display = dst->display;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
XRenderPictFormat *pict_format;
XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)];
XPointFixed *points = points_stack;
int dx, dy;
int i;
//X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
pict_format =
_cairo_xlib_display_get_xrender_format (display,
antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
if (strip->num_points > ARRAY_LENGTH (points_stack)) {
points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed));
if (unlikely (points == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
dx = -dst_x << 16;
dy = -dst_y << 16;
for (i = 0; i < strip->num_points; i++) {
cairo_point_t *p = &strip->points[i];
points[i].x = _cairo_fixed_to_16_16(p->x) + dx;
points[i].y = _cairo_fixed_to_16_16(p->y) + dy;
}
src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x;
src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y;
_cairo_xlib_surface_ensure_picture (dst);
_cairo_xlib_surface_set_precision (dst, antialias);
XRenderCompositeTriStrip (dst->dpy,
_render_operator (op),
src->picture, dst->picture,
pict_format,
src_x, src_y,
points, strip->num_points);
if (points != points_stack)
free (points);
return CAIRO_STATUS_SUCCESS;
}
const cairo_compositor_t *
_cairo_xlib_traps_compositor_get (void)
{
static cairo_traps_compositor_t compositor;
if (compositor.base.delegate == NULL) {
_cairo_traps_compositor_init (&compositor,
_cairo_xlib_mask_compositor_get ());
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
compositor.copy_boxes = copy_boxes;
compositor.fill_boxes = fill_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_composite_traps = check_composite_traps;
compositor.composite_traps = composite_traps;
//compositor.check_composite_tristrip = check_composite_tristrip;
compositor.composite_tristrip = composite_tristrip;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
return &compositor.base;
}
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */