forked from KolibriOS/kolibrios
44d189b67c
git-svn-id: svn://kolibrios.org@3959 a494cfbc-eb01-0410-851d-a64ba20cac60
2000 lines
58 KiB
C
2000 lines
58 KiB
C
/* -*- 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 */
|