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