0,0 → 1,1999 |
/* -*- 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 (sizeof (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; |
|
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; |
|
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 */ |