mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-29 06:30:24 +01:00
4640 lines
137 KiB
C
4640 lines
137 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.
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/* Heed well the words of Owen Taylor:
|
|
* "Any patch that works around a render bug, or claims to, without a
|
|
* specific reference to the bug filed in bugzilla.freedesktop.org will
|
|
* never pass approval."
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-xlib-private.h"
|
|
#include "cairo-xlib-surface-private.h"
|
|
#include "cairo-clip-private.h"
|
|
#include "cairo-error-private.h"
|
|
#include "cairo-scaled-font-private.h"
|
|
#include "cairo-region-private.h"
|
|
|
|
#include <X11/Xutil.h> /* for XDestroyImage */
|
|
|
|
#define XLIB_COORD_MAX 32767
|
|
|
|
#define DEBUG 0
|
|
|
|
#if DEBUG
|
|
#define UNSUPPORTED(reason) \
|
|
fprintf (stderr, \
|
|
"cairo-xlib: hit unsupported operation %s(), line %d: %s\n", \
|
|
__FUNCTION__, __LINE__, reason), \
|
|
CAIRO_INT_STATUS_UNSUPPORTED
|
|
#else
|
|
#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
|
|
#endif
|
|
|
|
#if DEBUG
|
|
#include <X11/Xlibint.h>
|
|
static void CAIRO_PRINTF_FORMAT (2, 3)
|
|
_x_bread_crumb (Display *dpy,
|
|
const char *fmt,
|
|
...)
|
|
{
|
|
xReq *req;
|
|
char buf[2048];
|
|
unsigned int len, len_dwords;
|
|
va_list ap;
|
|
|
|
va_start (ap, fmt);
|
|
len = vsnprintf (buf, sizeof (buf), fmt, ap);
|
|
va_end (ap);
|
|
|
|
buf[len++] = '\0';
|
|
while (len & 3)
|
|
buf[len++] = '\0';
|
|
|
|
LockDisplay (dpy);
|
|
GetEmptyReq (NoOperation, req);
|
|
|
|
len_dwords = len >> 2;
|
|
SetReqLen (req, len_dwords, len_dwords);
|
|
Data (dpy, buf, len);
|
|
|
|
UnlockDisplay (dpy);
|
|
SyncHandle ();
|
|
}
|
|
#define X_DEBUG(x) _x_bread_crumb x
|
|
#else
|
|
#define X_DEBUG(x)
|
|
#endif
|
|
|
|
/* Xlib doesn't define a typedef, so define one ourselves */
|
|
typedef int (*cairo_xlib_error_func_t) (Display *display,
|
|
XErrorEvent *event);
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
|
|
Drawable drawable,
|
|
Visual *visual,
|
|
XRenderPictFormat *xrender_format,
|
|
int width,
|
|
int height,
|
|
int depth);
|
|
|
|
static cairo_bool_t
|
|
_cairo_surface_is_xlib (cairo_surface_t *surface);
|
|
|
|
static cairo_bool_t
|
|
_native_byte_order_lsb (void);
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_show_glyphs (void *abstract_dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_clip_t *clip,
|
|
int *remaining_glyphs);
|
|
|
|
/* XXX temporarily used by cairo-qt-surface.c */
|
|
slim_hidden_proto (cairo_xlib_surface_create);
|
|
slim_hidden_proto (cairo_xlib_surface_create_with_xrender_format);
|
|
|
|
/*
|
|
* Instead of taking two round trips for each blending request,
|
|
* assume that if a particular drawable fails GetImage that it will
|
|
* fail for a "while"; use temporary pixmaps to avoid the errors
|
|
*/
|
|
|
|
#define CAIRO_ASSUME_PIXMAP 20
|
|
|
|
static const XTransform identity = { {
|
|
{ 1 << 16, 0x00000, 0x00000 },
|
|
{ 0x00000, 1 << 16, 0x00000 },
|
|
{ 0x00000, 0x00000, 1 << 16 },
|
|
} };
|
|
|
|
#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \
|
|
(((surface)->render_major > major) || \
|
|
(((surface)->render_major == major) && ((surface)->render_minor >= minor)))
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
|
|
#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
|
|
#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
|
|
#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
|
|
#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
|
|
#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10)
|
|
#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10)
|
|
|
|
#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11)
|
|
|
|
#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \
|
|
((op) <= CAIRO_OPERATOR_SATURATE || \
|
|
(CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \
|
|
(op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface,
|
|
cairo_region_t *region)
|
|
{
|
|
cairo_bool_t had_clip_rects = surface->clip_region != NULL;
|
|
|
|
if (had_clip_rects == FALSE && region == NULL)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (surface->clip_region == region)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (cairo_region_equal (surface->clip_region, region))
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
cairo_region_destroy (surface->clip_region);
|
|
surface->clip_region = cairo_region_reference (region);
|
|
|
|
if (surface->clip_rects != surface->embedded_clip_rects) {
|
|
free (surface->clip_rects);
|
|
surface->clip_rects = surface->embedded_clip_rects;
|
|
}
|
|
surface->num_clip_rects = 0;
|
|
|
|
if (region != NULL) {
|
|
XRectangle *rects = NULL;
|
|
int n_rects, i;
|
|
|
|
n_rects = cairo_region_num_rectangles (region);
|
|
if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) {
|
|
rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
|
|
if (unlikely (rects == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
} else {
|
|
rects = surface->embedded_clip_rects;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
surface->clip_rects = rects;
|
|
surface->num_clip_rects = n_rects;
|
|
}
|
|
|
|
surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_content_t
|
|
_xrender_format_to_content (XRenderPictFormat *xrender_format)
|
|
{
|
|
cairo_bool_t xrender_format_has_alpha;
|
|
cairo_bool_t xrender_format_has_color;
|
|
|
|
/* This only happens when using a non-Render server. Let's punt
|
|
* and say there's no alpha here. */
|
|
if (xrender_format == NULL)
|
|
return CAIRO_CONTENT_COLOR;
|
|
|
|
xrender_format_has_alpha = (xrender_format->direct.alphaMask != 0);
|
|
xrender_format_has_color = (xrender_format->direct.redMask != 0 ||
|
|
xrender_format->direct.greenMask != 0 ||
|
|
xrender_format->direct.blueMask != 0);
|
|
|
|
if (xrender_format_has_alpha)
|
|
if (xrender_format_has_color)
|
|
return CAIRO_CONTENT_COLOR_ALPHA;
|
|
else
|
|
return CAIRO_CONTENT_ALPHA;
|
|
else
|
|
return CAIRO_CONTENT_COLOR;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xlib_surface_create_similar (void *abstract_src,
|
|
cairo_content_t content,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_xlib_surface_t *src = abstract_src;
|
|
XRenderPictFormat *xrender_format;
|
|
cairo_xlib_surface_t *surface;
|
|
cairo_xlib_display_t *display;
|
|
Pixmap pix;
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
|
|
return NULL;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src))
|
|
return NULL;
|
|
|
|
if (_cairo_xlib_display_acquire (src->base.device, &display))
|
|
return NULL;
|
|
|
|
/* If we never found an XRenderFormat or if it isn't compatible
|
|
* with the content being requested, then we fallback to just
|
|
* constructing a cairo_format_t instead, (which will fairly
|
|
* arbitrarily pick a visual/depth for the similar surface.
|
|
*/
|
|
xrender_format = src->xrender_format;
|
|
if ((xrender_format != NULL &&
|
|
_xrender_format_to_content (xrender_format) == content) ||
|
|
(xrender_format =
|
|
_cairo_xlib_display_get_xrender_format (display,
|
|
_cairo_format_from_content (content))))
|
|
{
|
|
Visual *visual;
|
|
|
|
/* We've got a compatible XRenderFormat now, which means the
|
|
* similar surface will match the existing surface as closely in
|
|
* visual/depth etc. as possible. */
|
|
pix = XCreatePixmap (display->display, src->drawable,
|
|
width <= 0 ? 1 : width, height <= 0 ? 1 : height,
|
|
xrender_format->depth);
|
|
|
|
visual = NULL;
|
|
if (xrender_format == src->xrender_format)
|
|
visual = src->visual;
|
|
|
|
surface = (cairo_xlib_surface_t *)
|
|
_cairo_xlib_surface_create_internal (src->screen, pix,
|
|
visual,
|
|
xrender_format,
|
|
width, height,
|
|
xrender_format->depth);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_FORCE_FALLBACKS
|
|
Screen *screen = src->screen->screen;
|
|
int depth;
|
|
|
|
/* No compatabile XRenderFormat, see if we can make an ordinary pixmap,
|
|
* so that we can still accelerate blits with XCopyArea(). */
|
|
if (content != CAIRO_CONTENT_COLOR) {
|
|
cairo_device_release (&display->base);
|
|
return NULL;
|
|
}
|
|
|
|
depth = DefaultDepthOfScreen (screen);
|
|
|
|
pix = XCreatePixmap (display->display, RootWindowOfScreen (screen),
|
|
width <= 0 ? 1 : width, height <= 0 ? 1 : height,
|
|
depth);
|
|
|
|
surface = (cairo_xlib_surface_t *)
|
|
_cairo_xlib_surface_create_internal (src->screen, pix,
|
|
DefaultVisualOfScreen (screen),
|
|
NULL,
|
|
width, height, depth);
|
|
#else
|
|
/* No compatabile XRenderFormat, just say no. */
|
|
cairo_device_release (&display->base);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
if (unlikely (surface->base.status)) {
|
|
XFreePixmap (display->display, pix);
|
|
cairo_device_release (&display->base);
|
|
return &surface->base;
|
|
}
|
|
|
|
surface->owns_pixmap = TRUE;
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
return &surface->base;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_finish (void *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
cairo_xlib_display_t *display;
|
|
|
|
X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (surface->owns_pixmap) {
|
|
cairo_status_t status2;
|
|
|
|
if (surface->dst_picture != None) {
|
|
status2 = _cairo_xlib_display_queue_resource (display,
|
|
XRenderFreePicture,
|
|
surface->dst_picture);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = status2;
|
|
}
|
|
|
|
if (surface->src_picture != None) {
|
|
status2 = _cairo_xlib_display_queue_resource (display,
|
|
XRenderFreePicture,
|
|
surface->src_picture);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = status2;
|
|
}
|
|
|
|
status2 = _cairo_xlib_display_queue_resource (display,
|
|
(cairo_xlib_notify_resource_func) XFreePixmap,
|
|
surface->drawable);
|
|
if (status == CAIRO_STATUS_SUCCESS)
|
|
status = status2;
|
|
} else {
|
|
if (surface->dst_picture != None)
|
|
XRenderFreePicture (display->display, surface->dst_picture);
|
|
|
|
if (surface->src_picture != None)
|
|
XRenderFreePicture (display->display, surface->src_picture);
|
|
}
|
|
|
|
if (surface->clip_rects != surface->embedded_clip_rects)
|
|
free (surface->clip_rects);
|
|
|
|
if (display->display != NULL)
|
|
_cairo_xlib_remove_close_display_hook (display,
|
|
&surface->close_display_hook);
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
cairo_region_destroy (surface->clip_region);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface,
|
|
GC *gc)
|
|
{
|
|
*gc = _cairo_xlib_screen_get_gc (display,
|
|
surface->screen,
|
|
surface->depth,
|
|
surface->drawable);
|
|
if (unlikely (*gc == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface,
|
|
GC gc)
|
|
{
|
|
_cairo_xlib_screen_put_gc (display,
|
|
surface->screen,
|
|
surface->depth,
|
|
gc);
|
|
}
|
|
|
|
static int
|
|
_noop_error_handler (Display *display,
|
|
XErrorEvent *event)
|
|
{
|
|
return False; /* return value is ignored */
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_2bytes (XImage *ximage)
|
|
{
|
|
int i, j;
|
|
char *line = ximage->data;
|
|
|
|
for (j = ximage->height; j; j--) {
|
|
uint16_t *p = (uint16_t *) line;
|
|
for (i = ximage->width; i; i--) {
|
|
*p = bswap_16 (*p);
|
|
p++;
|
|
}
|
|
|
|
line += ximage->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_3bytes (XImage *ximage)
|
|
{
|
|
int i, j;
|
|
char *line = ximage->data;
|
|
|
|
for (j = ximage->height; j; j--) {
|
|
uint8_t *p = (uint8_t *) line;
|
|
for (i = ximage->width; i; i--) {
|
|
uint8_t tmp;
|
|
tmp = p[2];
|
|
p[2] = p[0];
|
|
p[0] = tmp;
|
|
p += 3;
|
|
}
|
|
|
|
line += ximage->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_4bytes (XImage *ximage)
|
|
{
|
|
int i, j;
|
|
char *line = ximage->data;
|
|
|
|
for (j = ximage->height; j; j--) {
|
|
uint32_t *p = (uint32_t *) line;
|
|
for (i = ximage->width; i; i--) {
|
|
*p = bswap_32 (*p);
|
|
p++;
|
|
}
|
|
|
|
line += ximage->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_nibbles (XImage *ximage)
|
|
{
|
|
int i, j;
|
|
char *line = ximage->data;
|
|
|
|
for (j = ximage->height; j; j--) {
|
|
uint8_t *p = (uint8_t *) line;
|
|
for (i = (ximage->width + 1) / 2; i; i--) {
|
|
*p = ((*p >> 4) & 0xf) | ((*p << 4) & ~0xf);
|
|
p++;
|
|
}
|
|
|
|
line += ximage->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_bits (XImage *ximage)
|
|
{
|
|
int i, j;
|
|
char *line = ximage->data;
|
|
int unit = ximage->bitmap_unit;
|
|
int line_bytes = ((ximage->width + unit - 1) & ~(unit - 1)) / 8;
|
|
|
|
for (j = ximage->height; j; j--) {
|
|
char *p = line;
|
|
|
|
for (i = line_bytes; i; i--) {
|
|
char b = *p;
|
|
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
|
|
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
|
|
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
|
|
*p = b;
|
|
|
|
p++;
|
|
}
|
|
|
|
line += ximage->bytes_per_line;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_swap_ximage_to_native (XImage *ximage)
|
|
{
|
|
int unit_bytes = 0;
|
|
int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
|
|
|
|
if (ximage->bits_per_pixel == 1 &&
|
|
ximage->bitmap_bit_order != native_byte_order)
|
|
{
|
|
_swap_ximage_bits (ximage);
|
|
if (ximage->bitmap_bit_order == ximage->byte_order)
|
|
return;
|
|
}
|
|
|
|
if (ximage->byte_order == native_byte_order)
|
|
return;
|
|
|
|
switch (ximage->bits_per_pixel) {
|
|
case 1:
|
|
unit_bytes = ximage->bitmap_unit / 8;
|
|
break;
|
|
case 4:
|
|
_swap_ximage_nibbles (ximage);
|
|
/* fall-through */
|
|
case 8:
|
|
case 16:
|
|
case 20:
|
|
case 24:
|
|
case 28:
|
|
case 30:
|
|
case 32:
|
|
unit_bytes = (ximage->bits_per_pixel + 7) / 8;
|
|
break;
|
|
default:
|
|
/* This could be hit on some rare but possible cases. */
|
|
ASSERT_NOT_REACHED;
|
|
}
|
|
|
|
switch (unit_bytes) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
_swap_ximage_2bytes (ximage);
|
|
break;
|
|
case 3:
|
|
_swap_ximage_3bytes (ximage);
|
|
break;
|
|
case 4:
|
|
_swap_ximage_4bytes (ximage);
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
}
|
|
}
|
|
|
|
|
|
/* Given a mask, (with a single sequence of contiguous 1 bits), return
|
|
* the number of 1 bits in 'width' and the number of 0 bits to its
|
|
* right in 'shift'. */
|
|
static void
|
|
_characterize_field (uint32_t mask, int *width, int *shift)
|
|
{
|
|
*width = _cairo_popcount (mask);
|
|
/* The final '& 31' is to force a 0 mask to result in 0 shift. */
|
|
*shift = _cairo_popcount ((mask - 1) & ~mask) & 31;
|
|
}
|
|
|
|
|
|
/* Convert a field of 'width' bits to 'new_width' bits with correct
|
|
* rounding. */
|
|
static inline uint32_t
|
|
_resize_field (uint32_t field, int width, int new_width)
|
|
{
|
|
if (width == 0)
|
|
return 0;
|
|
|
|
if (width >= new_width) {
|
|
return field >> (width - new_width);
|
|
} else {
|
|
uint32_t result = field << (new_width - width);
|
|
|
|
while (width < new_width) {
|
|
result |= result >> width;
|
|
width <<= 1;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static inline uint32_t
|
|
_adjust_field (uint32_t field, int adjustment)
|
|
{
|
|
return MIN (255, MAX(0, (int)field + adjustment));
|
|
}
|
|
|
|
/* Given a shifted field value, (described by 'width' and 'shift),
|
|
* resize it 8-bits and return that value.
|
|
*
|
|
* Note that the original field value must not have any non-field bits
|
|
* set.
|
|
*/
|
|
static inline uint32_t
|
|
_field_to_8 (uint32_t field, int width, int shift)
|
|
{
|
|
return _resize_field (field >> shift, width, 8);
|
|
}
|
|
|
|
static inline uint32_t
|
|
_field_to_8_undither (uint32_t field, int width, int shift,
|
|
int dither_adjustment)
|
|
{
|
|
return _adjust_field (_field_to_8 (field, width, shift), - dither_adjustment>>width);
|
|
}
|
|
|
|
/* Given an 8-bit value, convert it to a field of 'width', shift it up
|
|
* to 'shift, and return it. */
|
|
static inline uint32_t
|
|
_field_from_8 (uint32_t field, int width, int shift)
|
|
{
|
|
return _resize_field (field, 8, width) << shift;
|
|
}
|
|
|
|
static inline uint32_t
|
|
_field_from_8_dither (uint32_t field, int width, int shift,
|
|
int8_t dither_adjustment)
|
|
{
|
|
return _field_from_8 (_adjust_field (field, dither_adjustment>>width), width, shift);
|
|
}
|
|
|
|
static inline uint32_t
|
|
_pseudocolor_from_rgb888_dither (cairo_xlib_visual_info_t *visual_info,
|
|
uint32_t r, uint32_t g, uint32_t b,
|
|
int8_t dither_adjustment)
|
|
{
|
|
if (r == g && g == b) {
|
|
dither_adjustment /= RAMP_SIZE;
|
|
return visual_info->gray8_to_pseudocolor[_adjust_field (r, dither_adjustment)];
|
|
} else {
|
|
dither_adjustment = visual_info->dither8_to_cube[dither_adjustment+128];
|
|
return visual_info->cube_to_pseudocolor[visual_info->field8_to_cube[_adjust_field (r, dither_adjustment)]]
|
|
[visual_info->field8_to_cube[_adjust_field (g, dither_adjustment)]]
|
|
[visual_info->field8_to_cube[_adjust_field (b, dither_adjustment)]];
|
|
}
|
|
}
|
|
|
|
static inline uint32_t
|
|
_pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info,
|
|
uint32_t pixel)
|
|
{
|
|
uint32_t r, g, b;
|
|
pixel &= 0xff;
|
|
r = visual_info->colors[pixel].r;
|
|
g = visual_info->colors[pixel].g;
|
|
b = visual_info->colors[pixel].b;
|
|
return (r << 16) |
|
|
(g << 8) |
|
|
(b );
|
|
}
|
|
|
|
|
|
/* should range from -128 to 127 */
|
|
#define X 16
|
|
static const int8_t dither_pattern[4][4] = {
|
|
{-8*X, +0*X, -6*X, +2*X},
|
|
{+4*X, -4*X, +6*X, -2*X},
|
|
{-5*X, +4*X, -7*X, +1*X},
|
|
{+7*X, -1*X, +5*X, -3*X}
|
|
};
|
|
#undef X
|
|
|
|
|
|
static cairo_status_t
|
|
_get_image_surface (cairo_xlib_surface_t *surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_rectangle_int_t *image_rect)
|
|
{
|
|
cairo_int_status_t status;
|
|
cairo_image_surface_t *image = NULL;
|
|
XImage *ximage;
|
|
cairo_rectangle_int_t extents;
|
|
pixman_format_code_t pixman_format;
|
|
cairo_format_masks_t xlib_masks;
|
|
cairo_xlib_display_t *display;
|
|
|
|
extents.x = 0;
|
|
extents.y = 0;
|
|
extents.width = surface->width;
|
|
extents.height = surface->height;
|
|
|
|
if (interest_rect) {
|
|
if (! _cairo_rectangle_intersect (&extents, interest_rect)) {
|
|
*image_out = NULL;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (status)
|
|
return status;
|
|
|
|
if (image_rect)
|
|
*image_rect = extents;
|
|
|
|
/* XXX: This should try to use the XShm extension if available */
|
|
|
|
if (surface->use_pixmap == 0)
|
|
{
|
|
cairo_xlib_error_func_t old_handler;
|
|
|
|
old_handler = XSetErrorHandler (_noop_error_handler);
|
|
|
|
ximage = XGetImage (display->display,
|
|
surface->drawable,
|
|
extents.x, extents.y,
|
|
extents.width, extents.height,
|
|
AllPlanes, ZPixmap);
|
|
|
|
XSetErrorHandler (old_handler);
|
|
|
|
/* If we get an error, the surface must have been a window,
|
|
* so retry with the safe code path.
|
|
*/
|
|
if (!ximage)
|
|
surface->use_pixmap = CAIRO_ASSUME_PIXMAP;
|
|
}
|
|
else
|
|
{
|
|
surface->use_pixmap--;
|
|
ximage = NULL;
|
|
}
|
|
|
|
if (ximage == NULL) {
|
|
/* XGetImage from a window is dangerous because it can
|
|
* produce errors if the window is unmapped or partially
|
|
* outside the screen. We could check for errors and
|
|
* retry, but to keep things simple, we just create a
|
|
* temporary pixmap
|
|
*/
|
|
Pixmap pixmap;
|
|
GC gc;
|
|
|
|
status = _cairo_xlib_surface_get_gc (display, surface, &gc);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
pixmap = XCreatePixmap (display->display,
|
|
surface->drawable,
|
|
extents.width, extents.height,
|
|
surface->depth);
|
|
if (pixmap) {
|
|
XCopyArea (display->display, surface->drawable, pixmap, gc,
|
|
extents.x, extents.y,
|
|
extents.width, extents.height,
|
|
0, 0);
|
|
|
|
ximage = XGetImage (display->display,
|
|
pixmap,
|
|
0, 0,
|
|
extents.width, extents.height,
|
|
AllPlanes, ZPixmap);
|
|
|
|
XFreePixmap (display->display, pixmap);
|
|
}
|
|
|
|
_cairo_xlib_surface_put_gc (display, surface, gc);
|
|
|
|
if (ximage == NULL) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
}
|
|
|
|
_swap_ximage_to_native (ximage);
|
|
|
|
xlib_masks.bpp = ximage->bits_per_pixel;
|
|
xlib_masks.alpha_mask = surface->a_mask;
|
|
xlib_masks.red_mask = surface->r_mask;
|
|
xlib_masks.green_mask = surface->g_mask;
|
|
xlib_masks.blue_mask = surface->b_mask;
|
|
|
|
/* We can't use pixman to simply write to image if:
|
|
* (a) the pixels are not appropriately aligned,
|
|
* (b) pixman does not the pixel format, or
|
|
* (c) if the image is palettized and we need to convert.
|
|
*/
|
|
if (ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
|
|
_pixman_format_from_masks (&xlib_masks, &pixman_format) &&
|
|
(surface->visual == NULL || surface->visual->class == TrueColor))
|
|
{
|
|
image = (cairo_image_surface_t*)
|
|
_cairo_image_surface_create_with_pixman_format ((unsigned char *) ximage->data,
|
|
pixman_format,
|
|
ximage->width,
|
|
ximage->height,
|
|
ximage->bytes_per_line);
|
|
status = image->base.status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
/* Let the surface take ownership of the data */
|
|
_cairo_image_surface_assume_ownership_of_data (image);
|
|
ximage->data = NULL;
|
|
} else {
|
|
/* The visual we are dealing with is not supported by the
|
|
* standard pixman formats. So we must first convert the data
|
|
* to a supported format. */
|
|
|
|
cairo_format_t format;
|
|
unsigned char *data;
|
|
uint32_t *row;
|
|
uint32_t in_pixel, out_pixel;
|
|
unsigned int rowstride;
|
|
uint32_t a_mask=0, r_mask=0, g_mask=0, b_mask=0;
|
|
int a_width=0, r_width=0, g_width=0, b_width=0;
|
|
int a_shift=0, r_shift=0, g_shift=0, b_shift=0;
|
|
int x, y, x0, y0, x_off, y_off;
|
|
cairo_xlib_visual_info_t *visual_info = NULL;
|
|
|
|
if (surface->visual == NULL || surface->visual->class == TrueColor) {
|
|
cairo_bool_t has_alpha;
|
|
cairo_bool_t has_color;
|
|
|
|
has_alpha = surface->a_mask;
|
|
has_color = (surface->r_mask ||
|
|
surface->g_mask ||
|
|
surface->b_mask);
|
|
|
|
if (has_color) {
|
|
if (has_alpha) {
|
|
format = CAIRO_FORMAT_ARGB32;
|
|
} else {
|
|
format = CAIRO_FORMAT_RGB24;
|
|
}
|
|
} else {
|
|
/* XXX: Using CAIRO_FORMAT_A8 here would be more
|
|
* efficient, but would require slightly different code in
|
|
* the image conversion to put the alpha channel values
|
|
* into the right place. */
|
|
format = CAIRO_FORMAT_ARGB32;
|
|
}
|
|
|
|
a_mask = surface->a_mask;
|
|
r_mask = surface->r_mask;
|
|
g_mask = surface->g_mask;
|
|
b_mask = surface->b_mask;
|
|
|
|
_characterize_field (a_mask, &a_width, &a_shift);
|
|
_characterize_field (r_mask, &r_width, &r_shift);
|
|
_characterize_field (g_mask, &g_width, &g_shift);
|
|
_characterize_field (b_mask, &b_width, &b_shift);
|
|
|
|
} else {
|
|
format = CAIRO_FORMAT_RGB24;
|
|
|
|
status = _cairo_xlib_screen_get_visual_info (display,
|
|
surface->screen,
|
|
surface->visual,
|
|
&visual_info);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
}
|
|
|
|
image = (cairo_image_surface_t *) cairo_image_surface_create
|
|
(format, ximage->width, ximage->height);
|
|
status = image->base.status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
data = cairo_image_surface_get_data (&image->base);
|
|
rowstride = cairo_image_surface_get_stride (&image->base) >> 2;
|
|
row = (uint32_t *) data;
|
|
x0 = extents.x + surface->base.device_transform.x0;
|
|
y0 = extents.y + surface->base.device_transform.y0;
|
|
for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
|
|
y < ximage->height;
|
|
y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern)) {
|
|
const int8_t *dither_row = dither_pattern[y_off];
|
|
for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
|
|
x < ximage->width;
|
|
x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0])) {
|
|
int dither_adjustment = dither_row[x_off];
|
|
|
|
in_pixel = XGetPixel (ximage, x, y);
|
|
if (visual_info == NULL) {
|
|
out_pixel = (
|
|
_field_to_8 (in_pixel & a_mask, a_width, a_shift) << 24 |
|
|
_field_to_8_undither (in_pixel & r_mask, r_width, r_shift, dither_adjustment) << 16 |
|
|
_field_to_8_undither (in_pixel & g_mask, g_width, g_shift, dither_adjustment) << 8 |
|
|
_field_to_8_undither (in_pixel & b_mask, b_width, b_shift, dither_adjustment));
|
|
} else {
|
|
/* Undithering pseudocolor does not look better */
|
|
out_pixel = _pseudocolor_to_rgb888 (visual_info, in_pixel);
|
|
}
|
|
row[x] = out_pixel;
|
|
}
|
|
row += rowstride;
|
|
}
|
|
cairo_surface_mark_dirty (&image->base);
|
|
}
|
|
|
|
BAIL:
|
|
if (ximage)
|
|
XDestroyImage (ximage);
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
if (unlikely (status)) {
|
|
if (image) {
|
|
cairo_surface_destroy (&image->base);
|
|
image = NULL;
|
|
}
|
|
}
|
|
*image_out = image;
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface)
|
|
{
|
|
if (!surface->src_picture) {
|
|
XRenderPictureAttributes pa;
|
|
int mask = 0;
|
|
|
|
pa.subwindow_mode = IncludeInferiors;
|
|
mask |= CPSubwindowMode;
|
|
|
|
surface->src_picture = XRenderCreatePicture (display->display,
|
|
surface->drawable,
|
|
surface->xrender_format,
|
|
mask, &pa);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface)
|
|
{
|
|
if (surface->clip_region != NULL) {
|
|
XRenderSetPictureClipRectangles (display->display, surface->dst_picture,
|
|
0, 0,
|
|
surface->clip_rects,
|
|
surface->num_clip_rects);
|
|
} else {
|
|
XRenderPictureAttributes pa;
|
|
pa.clip_mask = None;
|
|
XRenderChangePicture (display->display, surface->dst_picture,
|
|
CPClipMask, &pa);
|
|
}
|
|
|
|
surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface)
|
|
{
|
|
if (!surface->dst_picture) {
|
|
surface->dst_picture = XRenderCreatePicture (display->display,
|
|
surface->drawable,
|
|
surface->xrender_format,
|
|
0, NULL);
|
|
}
|
|
|
|
if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE)
|
|
_cairo_xlib_surface_set_picture_clip_rects (display, surface);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_draw_image_surface (cairo_xlib_surface_t *surface,
|
|
cairo_image_surface_t *image,
|
|
int src_x,
|
|
int src_y,
|
|
int width,
|
|
int height,
|
|
int dst_x,
|
|
int dst_y)
|
|
{
|
|
cairo_xlib_display_t *display;
|
|
XImage ximage;
|
|
cairo_format_masks_t image_masks;
|
|
int native_byte_order = _native_byte_order_lsb () ? LSBFirst : MSBFirst;
|
|
pixman_image_t *pixman_image = NULL;
|
|
cairo_status_t status;
|
|
cairo_bool_t own_data;
|
|
GC gc;
|
|
|
|
ximage.width = image->width;
|
|
ximage.height = image->height;
|
|
ximage.format = ZPixmap;
|
|
ximage.byte_order = native_byte_order;
|
|
ximage.bitmap_unit = 32; /* always for libpixman */
|
|
ximage.bitmap_bit_order = native_byte_order;
|
|
ximage.bitmap_pad = 32; /* always for libpixman */
|
|
ximage.depth = surface->depth;
|
|
ximage.red_mask = surface->r_mask;
|
|
ximage.green_mask = surface->g_mask;
|
|
ximage.blue_mask = surface->b_mask;
|
|
ximage.xoffset = 0;
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (!_pixman_format_to_masks (image->pixman_format, &image_masks))
|
|
{
|
|
pixman_format_code_t intermediate_format;
|
|
int ret;
|
|
|
|
image_masks.alpha_mask = surface->a_mask;
|
|
image_masks.red_mask = surface->r_mask;
|
|
image_masks.green_mask = surface->g_mask;
|
|
image_masks.blue_mask = surface->b_mask;
|
|
image_masks.bpp = surface->depth;
|
|
ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
|
|
assert (ret);
|
|
|
|
own_data = FALSE;
|
|
|
|
pixman_image = pixman_image_create_bits (intermediate_format,
|
|
image->width,
|
|
image->height,
|
|
NULL,
|
|
0);
|
|
if (pixman_image == NULL) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
|
|
pixman_image_composite32 (PIXMAN_OP_SRC,
|
|
image->pixman_image,
|
|
NULL,
|
|
pixman_image,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
image->width, image->height);
|
|
|
|
ximage.bits_per_pixel = image_masks.bpp;
|
|
ximage.data = (char *) pixman_image_get_data (pixman_image);
|
|
ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
|
|
|
|
ret = XInitImage (&ximage);
|
|
assert (ret != 0);
|
|
}
|
|
else if ((image_masks.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
|
|
(image_masks.red_mask == surface->r_mask || surface->r_mask == 0) &&
|
|
(image_masks.green_mask == surface->g_mask || surface->g_mask == 0) &&
|
|
(image_masks.blue_mask == surface->b_mask || surface->b_mask == 0))
|
|
{
|
|
int ret;
|
|
|
|
ximage.bits_per_pixel = image_masks.bpp;
|
|
ximage.bytes_per_line = image->stride;
|
|
ximage.data = (char *)image->data;
|
|
own_data = FALSE;
|
|
|
|
ret = XInitImage (&ximage);
|
|
assert (ret != 0);
|
|
}
|
|
else
|
|
{
|
|
unsigned int stride, rowstride;
|
|
int x, y, x0, y0, x_off, y_off;
|
|
uint32_t in_pixel, out_pixel, *row;
|
|
int i_a_width=0, i_r_width=0, i_g_width=0, i_b_width=0;
|
|
int i_a_shift=0, i_r_shift=0, i_g_shift=0, i_b_shift=0;
|
|
int o_a_width=0, o_r_width=0, o_g_width=0, o_b_width=0;
|
|
int o_a_shift=0, o_r_shift=0, o_g_shift=0, o_b_shift=0;
|
|
cairo_xlib_visual_info_t *visual_info = NULL;
|
|
cairo_bool_t true_color;
|
|
int ret;
|
|
|
|
if (surface->depth > 16)
|
|
ximage.bits_per_pixel = 32;
|
|
else if (surface->depth > 8)
|
|
ximage.bits_per_pixel = 16;
|
|
else if (surface->depth > 1)
|
|
ximage.bits_per_pixel = 8;
|
|
else
|
|
ximage.bits_per_pixel = 1;
|
|
stride = CAIRO_STRIDE_FOR_WIDTH_BPP (ximage.width,
|
|
ximage.bits_per_pixel);
|
|
ximage.bytes_per_line = stride;
|
|
ximage.data = _cairo_malloc_ab (stride, ximage.height);
|
|
if (unlikely (ximage.data == NULL)) {
|
|
own_data = FALSE;
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
|
|
own_data = TRUE;
|
|
|
|
ret = XInitImage (&ximage);
|
|
assert (ret != 0);
|
|
|
|
_characterize_field (image_masks.alpha_mask, &i_a_width, &i_a_shift);
|
|
_characterize_field (image_masks.red_mask , &i_r_width, &i_r_shift);
|
|
_characterize_field (image_masks.green_mask, &i_g_width, &i_g_shift);
|
|
_characterize_field (image_masks.blue_mask , &i_b_width, &i_b_shift);
|
|
|
|
true_color = surface->visual == NULL ||
|
|
surface->visual->class == TrueColor;
|
|
if (true_color) {
|
|
_characterize_field (surface->a_mask, &o_a_width, &o_a_shift);
|
|
_characterize_field (surface->r_mask, &o_r_width, &o_r_shift);
|
|
_characterize_field (surface->g_mask, &o_g_width, &o_g_shift);
|
|
_characterize_field (surface->b_mask, &o_b_width, &o_b_shift);
|
|
} else {
|
|
status = _cairo_xlib_screen_get_visual_info (display,
|
|
surface->screen,
|
|
surface->visual,
|
|
&visual_info);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
}
|
|
|
|
rowstride = image->stride >> 2;
|
|
row = (uint32_t *) image->data;
|
|
x0 = dst_x + surface->base.device_transform.x0;
|
|
y0 = dst_y + surface->base.device_transform.y0;
|
|
for (y = 0, y_off = y0 % ARRAY_LENGTH (dither_pattern);
|
|
y < ximage.height;
|
|
y++, y_off = (y_off+1) % ARRAY_LENGTH (dither_pattern))
|
|
{
|
|
const int8_t *dither_row = dither_pattern[y_off];
|
|
|
|
for (x = 0, x_off = x0 % ARRAY_LENGTH (dither_pattern[0]);
|
|
x < ximage.width;
|
|
x++, x_off = (x_off+1) % ARRAY_LENGTH (dither_pattern[0]))
|
|
{
|
|
int dither_adjustment = dither_row[x_off];
|
|
int a, r, g, b;
|
|
|
|
if (image_masks.bpp == 1)
|
|
in_pixel = !! (((uint8_t*)row)[x/8] & (1 << (x & 7)));
|
|
else if (image_masks.bpp <= 8)
|
|
in_pixel = ((uint8_t*)row)[x];
|
|
else if (image_masks.bpp <= 16)
|
|
in_pixel = ((uint16_t*)row)[x];
|
|
else if (image_masks.bpp <= 24)
|
|
#ifdef WORDS_BIGENDIAN
|
|
in_pixel = ((uint8_t*)row)[3 * x] << 16 |
|
|
((uint8_t*)row)[3 * x + 1] << 8 |
|
|
((uint8_t*)row)[3 * x + 2];
|
|
#else
|
|
in_pixel = ((uint8_t*)row)[3 * x] |
|
|
((uint8_t*)row)[3 * x + 1] << 8 |
|
|
((uint8_t*)row)[3 * x + 2] << 16;
|
|
#endif
|
|
else
|
|
in_pixel = row[x];
|
|
|
|
/* If the incoming image has no alpha channel, then the input
|
|
* is opaque and the output should have the maximum alpha value.
|
|
* For all other channels, their absence implies 0.
|
|
*/
|
|
if (image_masks.alpha_mask == 0x0)
|
|
a = 0xff;
|
|
else
|
|
a = _field_to_8 (in_pixel & image_masks.alpha_mask, i_a_width, i_a_shift);
|
|
r = _field_to_8 (in_pixel & image_masks.red_mask , i_r_width, i_r_shift);
|
|
g = _field_to_8 (in_pixel & image_masks.green_mask, i_g_width, i_g_shift);
|
|
b = _field_to_8 (in_pixel & image_masks.blue_mask , i_b_width, i_b_shift);
|
|
|
|
if (true_color) {
|
|
out_pixel = _field_from_8 (a, o_a_width, o_a_shift) |
|
|
_field_from_8_dither (r, o_r_width, o_r_shift, dither_adjustment) |
|
|
_field_from_8_dither (g, o_g_width, o_g_shift, dither_adjustment) |
|
|
_field_from_8_dither (b, o_b_width, o_b_shift, dither_adjustment);
|
|
} else {
|
|
out_pixel = _pseudocolor_from_rgb888_dither (visual_info, r, g, b, dither_adjustment);
|
|
}
|
|
|
|
XPutPixel (&ximage, x, y, out_pixel);
|
|
}
|
|
|
|
row += rowstride;
|
|
}
|
|
}
|
|
|
|
status = _cairo_xlib_surface_get_gc (display, surface, &gc);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
XPutImage (display->display, surface->drawable, gc,
|
|
&ximage, src_x, src_y, dst_x, dst_y,
|
|
width, height);
|
|
|
|
_cairo_xlib_surface_put_gc (display, surface, gc);
|
|
|
|
BAIL:
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
if (own_data)
|
|
free (ximage.data);
|
|
if (pixman_image)
|
|
pixman_image_unref (pixman_image);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_acquire_source_image (void *abstract_surface,
|
|
cairo_image_surface_t **image_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_image_surface_t *image;
|
|
cairo_status_t status;
|
|
|
|
status = _get_image_surface (surface, NULL, &image, NULL);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
*image_out = image;
|
|
*image_extra = NULL;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xlib_surface_snapshot (void *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_image_surface_t *image;
|
|
cairo_status_t status;
|
|
|
|
status = _get_image_surface (surface, NULL, &image, NULL);
|
|
if (unlikely (status))
|
|
return _cairo_surface_create_in_error (status);
|
|
|
|
return &image->base;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_release_source_image (void *abstract_surface,
|
|
cairo_image_surface_t *image,
|
|
void *image_extra)
|
|
{
|
|
cairo_surface_destroy (&image->base);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_acquire_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t **image_out,
|
|
cairo_rectangle_int_t *image_rect_out,
|
|
void **image_extra)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_image_surface_t *image;
|
|
cairo_status_t status;
|
|
|
|
status = _get_image_surface (surface, interest_rect, &image, image_rect_out);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
*image_out = image;
|
|
*image_extra = NULL;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_release_dest_image (void *abstract_surface,
|
|
cairo_rectangle_int_t *interest_rect,
|
|
cairo_image_surface_t *image,
|
|
cairo_rectangle_int_t *image_rect,
|
|
void *image_extra)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
status = _draw_image_surface (surface, image,
|
|
0, 0, image->width, image->height,
|
|
image_rect->x, image_rect->y);
|
|
status = _cairo_surface_set_error (&surface->base, status);
|
|
|
|
cairo_surface_destroy (&image->base);
|
|
}
|
|
|
|
/*
|
|
* Return whether two xlib surfaces share the same
|
|
* screen. Both core and Render drawing require this
|
|
* when using multiple drawables in an operation.
|
|
*/
|
|
static inline cairo_bool_t
|
|
_cairo_xlib_surface_same_screen (cairo_xlib_surface_t *dst,
|
|
cairo_xlib_surface_t *src)
|
|
{
|
|
return dst->screen == src->screen;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_clone_similar (void *abstract_surface,
|
|
cairo_surface_t *src,
|
|
int src_x,
|
|
int src_y,
|
|
int width,
|
|
int height,
|
|
int *clone_offset_x,
|
|
int *clone_offset_y,
|
|
cairo_surface_t **clone_out)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
cairo_xlib_surface_t *clone;
|
|
cairo_status_t status;
|
|
|
|
if (src->backend == surface->base.backend ) {
|
|
cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src;
|
|
|
|
if (_cairo_xlib_surface_same_screen (surface, xlib_src)) {
|
|
*clone_offset_x = 0;
|
|
*clone_offset_y = 0;
|
|
*clone_out = cairo_surface_reference (src);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
} else if (_cairo_surface_is_image (src)) {
|
|
cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
|
|
return UNSUPPORTED ("roi too large for xlib");
|
|
|
|
clone = (cairo_xlib_surface_t *)
|
|
_cairo_xlib_surface_create_similar (surface,
|
|
image_src->base.content,
|
|
width, height);
|
|
if (clone == NULL)
|
|
return UNSUPPORTED ("unhandled image format, no similar surface");
|
|
|
|
if (unlikely (clone->base.status))
|
|
return clone->base.status;
|
|
|
|
status = _draw_image_surface (clone, image_src,
|
|
src_x, src_y,
|
|
width, height,
|
|
0, 0);
|
|
if (unlikely (status)) {
|
|
cairo_surface_destroy (&clone->base);
|
|
return status;
|
|
}
|
|
|
|
*clone_offset_x = src_x;
|
|
*clone_offset_y = src_y;
|
|
*clone_out = &clone->base;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface,
|
|
const cairo_solid_pattern_t *solid_pattern)
|
|
{
|
|
/* This function's only responsibility is to create a proper surface
|
|
* for when XRender is not available. The proper surface is a xlib
|
|
* surface (as opposed to image surface which is what create_similar
|
|
* returns in those cases) and the size of the dithering pattern, not
|
|
* 1x1. This surface can then be used in
|
|
* _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid"
|
|
* fills using core protocol */
|
|
|
|
cairo_xlib_surface_t *other = abstract_surface;
|
|
cairo_image_surface_t *image;
|
|
cairo_xlib_surface_t *surface = NULL;
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_xlib_display_t *display;
|
|
|
|
int width = ARRAY_LENGTH (dither_pattern[0]);
|
|
int height = ARRAY_LENGTH (dither_pattern);
|
|
|
|
Pixmap pixmap = None;
|
|
|
|
if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other))
|
|
return NULL;
|
|
|
|
image = (cairo_image_surface_t *)
|
|
_cairo_image_surface_create_with_content (solid_pattern->content,
|
|
width, height);
|
|
status = image->base.status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
status = _cairo_xlib_display_acquire (other->base.device, &display);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
pixmap = XCreatePixmap (display->display,
|
|
other->drawable,
|
|
width, height,
|
|
other->depth);
|
|
cairo_device_release (&display->base);
|
|
|
|
surface = (cairo_xlib_surface_t *)
|
|
_cairo_xlib_surface_create_internal (other->screen,
|
|
pixmap,
|
|
other->visual,
|
|
other->xrender_format,
|
|
width, height,
|
|
other->depth);
|
|
status = surface->base.status;
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
status = _cairo_surface_paint (&image->base,
|
|
CAIRO_OPERATOR_SOURCE,
|
|
&solid_pattern->base,
|
|
NULL);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
status = _draw_image_surface (surface, image,
|
|
0, 0,
|
|
width, height,
|
|
0, 0);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
BAIL:
|
|
cairo_surface_destroy (&image->base);
|
|
|
|
if (status) {
|
|
if (pixmap != None) {
|
|
if (!_cairo_xlib_display_acquire (other->base.device, &display)) {
|
|
XFreePixmap (display->display, pixmap);
|
|
cairo_device_release (&display->base);
|
|
}
|
|
}
|
|
cairo_surface_destroy (&surface->base);
|
|
|
|
return _cairo_surface_create_in_error (status);
|
|
}
|
|
|
|
surface->owns_pixmap = TRUE;
|
|
return &surface->base;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface,
|
|
const cairo_solid_pattern_t *solid_pattern)
|
|
{
|
|
cairo_xlib_surface_t *other = abstract_surface;
|
|
return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other);
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface,
|
|
const cairo_matrix_t *matrix,
|
|
double xc,
|
|
double yc)
|
|
{
|
|
XTransform xtransform;
|
|
|
|
/* Casting between pixman_transform_t and XTransform is safe because
|
|
* they happen to be the exact same type.
|
|
*/
|
|
_cairo_matrix_to_pixman_matrix (matrix,
|
|
(pixman_transform_t *) &xtransform,
|
|
xc, yc);
|
|
|
|
if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface))
|
|
return UNSUPPORTED ("XRender does not support picture transforms");
|
|
|
|
XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform);
|
|
surface->xtransform = xtransform;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface,
|
|
cairo_filter_t filter)
|
|
{
|
|
const char *render_filter;
|
|
|
|
if (surface->filter == filter)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) {
|
|
if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
return UNSUPPORTED ("XRender does not support filter");
|
|
}
|
|
|
|
switch (filter) {
|
|
case CAIRO_FILTER_FAST:
|
|
render_filter = FilterFast;
|
|
break;
|
|
case CAIRO_FILTER_GOOD:
|
|
render_filter = FilterGood;
|
|
break;
|
|
case CAIRO_FILTER_BEST:
|
|
render_filter = FilterBest;
|
|
break;
|
|
case CAIRO_FILTER_NEAREST:
|
|
render_filter = FilterNearest;
|
|
break;
|
|
case CAIRO_FILTER_BILINEAR:
|
|
render_filter = FilterBilinear;
|
|
break;
|
|
case CAIRO_FILTER_GAUSSIAN:
|
|
/* XXX: The GAUSSIAN value has no implementation in cairo
|
|
* whatsoever, so it was really a mistake to have it in the
|
|
* API. We could fix this by officially deprecating it, or
|
|
* else inventing semantics and providing an actual
|
|
* implementation for it. */
|
|
default:
|
|
render_filter = FilterBest;
|
|
break;
|
|
}
|
|
|
|
XRenderSetPictureFilter (display->display, surface->src_picture,
|
|
(char *) render_filter, NULL, 0);
|
|
surface->filter = filter;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface,
|
|
cairo_extend_t extend,
|
|
unsigned long *mask,
|
|
XRenderPictureAttributes *pa)
|
|
{
|
|
int repeat;
|
|
|
|
if (surface->extend == extend)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
switch (extend) {
|
|
case CAIRO_EXTEND_NONE:
|
|
repeat = RepeatNone;
|
|
break;
|
|
case CAIRO_EXTEND_REPEAT:
|
|
repeat = RepeatNormal;
|
|
break;
|
|
case CAIRO_EXTEND_REFLECT:
|
|
if (surface->buggy_pad_reflect)
|
|
return UNSUPPORTED ("buggy reflect");
|
|
|
|
repeat = RepeatReflect;
|
|
break;
|
|
case CAIRO_EXTEND_PAD:
|
|
if (surface->buggy_pad_reflect)
|
|
return UNSUPPORTED ("buggy pad");
|
|
|
|
repeat = RepeatPad;
|
|
break;
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
*mask |= CPRepeat;
|
|
pa->repeat = repeat;
|
|
|
|
surface->extend = extend;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface,
|
|
cairo_bool_t ca,
|
|
unsigned long *mask,
|
|
XRenderPictureAttributes *pa)
|
|
{
|
|
if (surface->has_component_alpha == ca)
|
|
return CAIRO_STATUS_SUCCESS;
|
|
|
|
*mask |= CPComponentAlpha;
|
|
pa->component_alpha = ca;
|
|
|
|
surface->has_component_alpha = ca;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *surface,
|
|
const cairo_surface_attributes_t *attributes,
|
|
double xc,
|
|
double yc)
|
|
{
|
|
cairo_int_status_t status;
|
|
XRenderPictureAttributes pa;
|
|
unsigned long mask = 0;
|
|
|
|
_cairo_xlib_surface_ensure_src_picture (display, surface);
|
|
|
|
status = _cairo_xlib_surface_set_matrix (display, surface,
|
|
&attributes->matrix, xc, yc);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xlib_surface_set_repeat (surface, attributes->extend,
|
|
&mask, &pa);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xlib_surface_set_component_alpha (surface,
|
|
attributes->has_component_alpha,
|
|
&mask, &pa);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xlib_surface_set_filter (display, surface, attributes->filter);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (mask)
|
|
XRenderChangePicture (display->display, surface->src_picture, mask, &pa);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Checks whether we can can directly draw from src to dst with
|
|
* the core protocol: either with CopyArea or using src as a
|
|
* a tile in a GC.
|
|
*/
|
|
static cairo_bool_t
|
|
_surfaces_compatible (cairo_xlib_surface_t *dst,
|
|
cairo_xlib_surface_t *src)
|
|
{
|
|
/* same screen */
|
|
if (! _cairo_xlib_surface_same_screen (dst, src))
|
|
return FALSE;
|
|
|
|
/* same depth (for core) */
|
|
if (src->depth != dst->depth)
|
|
return FALSE;
|
|
|
|
/* if Render is supported, match picture formats */
|
|
if (src->xrender_format != dst->xrender_format)
|
|
return FALSE;
|
|
else if (src->xrender_format != NULL)
|
|
return TRUE;
|
|
|
|
/* Without Render, match visuals instead */
|
|
if (src->visual == dst->visual)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_surface_has_alpha (cairo_xlib_surface_t *surface)
|
|
{
|
|
if (surface->xrender_format) {
|
|
if (surface->xrender_format->type == PictTypeDirect &&
|
|
surface->xrender_format->direct.alphaMask != 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
} else {
|
|
/* In the no-render case, we never have alpha */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Returns true if the given operator and source-alpha combination
|
|
* requires alpha compositing to complete.
|
|
*/
|
|
static cairo_bool_t
|
|
_operator_needs_alpha_composite (cairo_operator_t op,
|
|
cairo_bool_t destination_has_alpha,
|
|
cairo_bool_t source_has_alpha)
|
|
{
|
|
if (op == CAIRO_OPERATOR_SOURCE ||
|
|
(! source_has_alpha &&
|
|
(op == CAIRO_OPERATOR_OVER ||
|
|
op == CAIRO_OPERATOR_ATOP ||
|
|
op == CAIRO_OPERATOR_IN)))
|
|
return destination_has_alpha;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* There is a bug in most older X servers with compositing using a
|
|
* untransformed repeating source pattern when the source is in off-screen
|
|
* video memory, and another with repeated transformed images using a
|
|
* general transform matrix. When these bugs could be triggered, we need a
|
|
* fallback: in the common case where we have no transformation and the
|
|
* source and destination have the same format/visual, we can do the
|
|
* operation using the core protocol for the first bug, otherwise, we need
|
|
* a software fallback.
|
|
*
|
|
* We can also often optimize a compositing operation by calling XCopyArea
|
|
* for some common cases where there is no alpha compositing to be done.
|
|
* We figure that out here as well.
|
|
*/
|
|
typedef enum {
|
|
DO_RENDER, /* use render */
|
|
DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */
|
|
DO_XTILE, /* core protocol XSetTile optimization/fallback */
|
|
DO_UNSUPPORTED /* software fallback */
|
|
} composite_operation_t;
|
|
|
|
/* Initial check for the render bugs; we need to recheck for the
|
|
* offscreen-memory bug after we turn patterns into surfaces, since that
|
|
* may introduce a repeating pattern for gradient patterns. We don't need
|
|
* to check for the repeat+transform bug because gradient surfaces aren't
|
|
* transformed.
|
|
*
|
|
* All we do here is reject cases where we *know* are going to
|
|
* hit the bug and won't be able to use a core protocol fallback.
|
|
*/
|
|
static composite_operation_t
|
|
_categorize_composite_operation (cairo_xlib_surface_t *dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
cairo_bool_t have_mask)
|
|
|
|
{
|
|
if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op))
|
|
return DO_UNSUPPORTED;
|
|
|
|
if (! dst->buggy_repeat)
|
|
return DO_RENDER;
|
|
|
|
if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
|
|
src_pattern->extend == CAIRO_EXTEND_REPEAT)
|
|
{
|
|
/* Check for the bug with repeat patterns nad general transforms. */
|
|
if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix,
|
|
NULL, NULL))
|
|
{
|
|
return DO_UNSUPPORTED;
|
|
}
|
|
|
|
if (have_mask ||
|
|
!(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
|
|
{
|
|
return DO_UNSUPPORTED;
|
|
}
|
|
|
|
if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
|
|
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern;
|
|
|
|
/* This is the case where we have the bug involving
|
|
* untransformed repeating source patterns with off-screen
|
|
* video memory; reject some cases where a core protocol
|
|
* fallback is impossible.
|
|
*/
|
|
if (_cairo_surface_is_xlib (surface_pattern->surface)) {
|
|
cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface;
|
|
|
|
if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src))
|
|
return DO_UNSUPPORTED;
|
|
|
|
/* If these are on the same screen but otherwise incompatible,
|
|
* make a copy as core drawing can't cross depths and doesn't
|
|
* work right across visuals of the same depth
|
|
*/
|
|
if (_cairo_xlib_surface_same_screen (dst, src) &&
|
|
!_surfaces_compatible (dst, src))
|
|
{
|
|
return DO_UNSUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return DO_RENDER;
|
|
}
|
|
|
|
/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces
|
|
* If we end up returning DO_UNSUPPORTED here, we're throwing away work we
|
|
* did to turn gradients into a pattern, but most of the time we can handle
|
|
* that case with core protocol fallback.
|
|
*
|
|
* Also check here if we can just use XCopyArea, instead of going through
|
|
* Render.
|
|
*/
|
|
static composite_operation_t
|
|
_recategorize_composite_operation (cairo_xlib_surface_t *dst,
|
|
cairo_operator_t op,
|
|
cairo_xlib_surface_t *src,
|
|
cairo_surface_attributes_t *src_attr,
|
|
cairo_bool_t have_mask)
|
|
{
|
|
/* Can we use the core protocol? */
|
|
if (! have_mask &&
|
|
src->owns_pixmap &&
|
|
src->depth == dst->depth &&
|
|
_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
|
|
! _operator_needs_alpha_composite (op,
|
|
_surface_has_alpha (dst),
|
|
_surface_has_alpha (src)))
|
|
{
|
|
if (src_attr->extend == CAIRO_EXTEND_NONE)
|
|
return DO_XCOPYAREA;
|
|
|
|
if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT)
|
|
return DO_XTILE;
|
|
}
|
|
|
|
if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT)
|
|
return DO_UNSUPPORTED;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src))
|
|
return DO_UNSUPPORTED;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
|
|
return DO_UNSUPPORTED;
|
|
|
|
return DO_RENDER;
|
|
}
|
|
|
|
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_int_status_t
|
|
_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *dst,
|
|
const cairo_pattern_t *pattern,
|
|
int x, int y,
|
|
int width, int height,
|
|
cairo_xlib_surface_t **surface_out,
|
|
cairo_surface_attributes_t *attributes)
|
|
{
|
|
switch (pattern->type) {
|
|
case CAIRO_PATTERN_TYPE_LINEAR:
|
|
case CAIRO_PATTERN_TYPE_RADIAL:
|
|
{
|
|
cairo_gradient_pattern_t *gradient =
|
|
(cairo_gradient_pattern_t *) pattern;
|
|
cairo_matrix_t matrix = pattern->matrix;
|
|
cairo_xlib_surface_t *surface;
|
|
char buf[CAIRO_STACK_BUFFER_SIZE];
|
|
XFixed *stops;
|
|
XRenderColor *colors;
|
|
XRenderPictFormat *format;
|
|
Picture picture;
|
|
unsigned int i;
|
|
|
|
if (dst->buggy_gradients)
|
|
break;
|
|
|
|
if (gradient->n_stops < 2) /* becomes a solid */
|
|
break;
|
|
|
|
if (gradient->n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor)))
|
|
{
|
|
stops = (XFixed *) buf;
|
|
}
|
|
else
|
|
{
|
|
stops =
|
|
_cairo_malloc_ab (gradient->n_stops,
|
|
sizeof (XFixed) + sizeof (XRenderColor));
|
|
if (unlikely (stops == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
}
|
|
|
|
colors = (XRenderColor *) (stops + gradient->n_stops);
|
|
for (i = 0; i < gradient->n_stops; i++) {
|
|
stops[i] =
|
|
_cairo_fixed_16_16_from_double (gradient->stops[i].offset);
|
|
|
|
colors[i].red = gradient->stops[i].color.red_short;
|
|
colors[i].green = gradient->stops[i].color.green_short;
|
|
colors[i].blue = gradient->stops[i].color.blue_short;
|
|
colors[i].alpha = gradient->stops[i].color.alpha_short;
|
|
}
|
|
|
|
#if 0
|
|
/* For some weird reason the X server is sometimes getting
|
|
* CreateGradient requests with bad length. So far I've only seen
|
|
* XRenderCreateLinearGradient request with 4 stops sometime end up
|
|
* with length field matching 0 stops at the server side. I've
|
|
* looked at the libXrender code and I can't see anything that
|
|
* could cause this behavior. However, for some reason having a
|
|
* XSync call here seems to avoid the issue so I'll keep it here
|
|
* until it's solved.
|
|
*/
|
|
XSync (display->display, False);
|
|
#endif
|
|
|
|
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
|
|
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
|
|
XLinearGradient grad;
|
|
|
|
cairo_fixed_t xdim, ydim;
|
|
|
|
xdim = linear->p2.x - linear->p1.x;
|
|
ydim = linear->p2.y - linear->p1.y;
|
|
|
|
/*
|
|
* Transform the matrix to avoid overflow when converting between
|
|
* cairo_fixed_t and pixman_fixed_t (without incurring performance
|
|
* loss when the transformation is unnecessary).
|
|
*
|
|
* XXX: Consider converting out-of-range co-ordinates and transforms.
|
|
* Having a function to compute the required transformation to
|
|
* "normalize" a given bounding box would be generally useful -
|
|
* cf linear patterns, gradient patterns, surface patterns...
|
|
*/
|
|
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
|
|
if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
|
|
_cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
|
|
{
|
|
double sf;
|
|
|
|
if (xdim > ydim)
|
|
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
|
|
else
|
|
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
|
|
|
|
grad.p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
|
|
grad.p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
|
|
grad.p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
|
|
grad.p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
|
|
|
|
cairo_matrix_scale (&matrix, sf, sf);
|
|
}
|
|
else
|
|
{
|
|
grad.p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
|
|
grad.p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
|
|
grad.p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
|
|
grad.p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
|
|
}
|
|
|
|
picture = XRenderCreateLinearGradient (display->display, &grad,
|
|
stops, colors,
|
|
gradient->n_stops);
|
|
} else {
|
|
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
|
|
XRadialGradient grad;
|
|
|
|
grad.inner.x = _cairo_fixed_to_16_16 (radial->c1.x);
|
|
grad.inner.y = _cairo_fixed_to_16_16 (radial->c1.y);
|
|
grad.inner.radius = _cairo_fixed_to_16_16 (radial->r1);
|
|
|
|
grad.outer.x = _cairo_fixed_to_16_16 (radial->c2.x);
|
|
grad.outer.y = _cairo_fixed_to_16_16 (radial->c2.y);
|
|
grad.outer.radius = _cairo_fixed_to_16_16 (radial->r2);
|
|
|
|
picture = XRenderCreateRadialGradient (display->display, &grad,
|
|
stops, colors,
|
|
gradient->n_stops);
|
|
|
|
}
|
|
|
|
if (stops != (XFixed *) buf)
|
|
free (stops);
|
|
|
|
if (unlikely (picture == None))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
/* Wrap the remote Picture in an xlib surface. */
|
|
format = _cairo_xlib_display_get_xrender_format (display,
|
|
CAIRO_FORMAT_ARGB32);
|
|
|
|
surface = (cairo_xlib_surface_t *)
|
|
_cairo_xlib_surface_create_internal (dst->screen, None,
|
|
NULL, format,
|
|
/* what could possibly go wrong? */
|
|
XLIB_COORD_MAX, XLIB_COORD_MAX, 32);
|
|
if (unlikely (surface->base.status)) {
|
|
XRenderFreePicture (display->display, picture);
|
|
return surface->base.status;
|
|
}
|
|
|
|
surface->src_picture = picture;
|
|
|
|
attributes->matrix = matrix;
|
|
attributes->extend = pattern->extend;
|
|
attributes->filter = CAIRO_FILTER_NEAREST;
|
|
attributes->x_offset = 0;
|
|
attributes->y_offset = 0;
|
|
attributes->has_component_alpha = FALSE;
|
|
|
|
*surface_out = surface;
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
case CAIRO_PATTERN_TYPE_SOLID:
|
|
case CAIRO_PATTERN_TYPE_SURFACE:
|
|
break;
|
|
}
|
|
|
|
return _cairo_pattern_acquire_surface (pattern, &dst->base,
|
|
x, y, width, height,
|
|
dst->buggy_pad_reflect ?
|
|
CAIRO_PATTERN_ACQUIRE_NO_REFLECT :
|
|
CAIRO_PATTERN_ACQUIRE_NONE,
|
|
(cairo_surface_t **) surface_out,
|
|
attributes);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *dst,
|
|
const cairo_pattern_t *src,
|
|
const cairo_pattern_t *mask,
|
|
int src_x,
|
|
int src_y,
|
|
int mask_x,
|
|
int mask_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_xlib_surface_t **src_out,
|
|
cairo_xlib_surface_t **mask_out,
|
|
cairo_surface_attributes_t *src_attr,
|
|
cairo_surface_attributes_t *mask_attr)
|
|
{
|
|
if (! dst->buggy_gradients &&
|
|
(src->type == CAIRO_PATTERN_TYPE_LINEAR ||
|
|
src->type == CAIRO_PATTERN_TYPE_RADIAL ||
|
|
(mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR ||
|
|
mask->type == CAIRO_PATTERN_TYPE_RADIAL))))
|
|
{
|
|
cairo_int_status_t status;
|
|
|
|
status = _cairo_xlib_surface_acquire_pattern_surface (display,
|
|
dst, src,
|
|
src_x, src_y,
|
|
width, height,
|
|
src_out,
|
|
src_attr);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
if (mask) {
|
|
status = _cairo_xlib_surface_acquire_pattern_surface (display,
|
|
dst, mask,
|
|
mask_x,
|
|
mask_y,
|
|
width,
|
|
height,
|
|
mask_out,
|
|
mask_attr);
|
|
if (unlikely (status)) {
|
|
_cairo_pattern_release_surface (src, &(*src_out)->base,
|
|
src_attr);
|
|
return status;
|
|
}
|
|
} else {
|
|
*mask_out = NULL;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
return _cairo_pattern_acquire_surfaces (src, mask,
|
|
&dst->base,
|
|
src_x, src_y,
|
|
mask_x, mask_y,
|
|
width, height,
|
|
dst->buggy_pad_reflect ?
|
|
CAIRO_PATTERN_ACQUIRE_NO_REFLECT :
|
|
CAIRO_PATTERN_ACQUIRE_NONE,
|
|
(cairo_surface_t **) src_out,
|
|
(cairo_surface_t **) mask_out,
|
|
src_attr, mask_attr);
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_composite (cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
const cairo_pattern_t *mask_pattern,
|
|
void *abstract_dst,
|
|
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_region_t *clip_region)
|
|
{
|
|
cairo_surface_attributes_t src_attr, mask_attr;
|
|
cairo_xlib_surface_t *dst = abstract_dst;
|
|
cairo_xlib_surface_t *src;
|
|
cairo_xlib_surface_t *mask;
|
|
cairo_xlib_display_t *display;
|
|
cairo_int_status_t status;
|
|
composite_operation_t operation;
|
|
int itx, ity;
|
|
cairo_bool_t is_integer_translation;
|
|
GC gc;
|
|
|
|
if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
|
|
return UNSUPPORTED ("no support for masks");
|
|
|
|
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));
|
|
|
|
status = _cairo_xlib_display_acquire (dst-> base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status =
|
|
_cairo_xlib_surface_acquire_pattern_surfaces (display, dst,
|
|
src_pattern, mask_pattern,
|
|
src_x, src_y,
|
|
mask_x, mask_y,
|
|
width, height,
|
|
&src, &mask,
|
|
&src_attr, &mask_attr);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
|
|
/* check for fallback surfaces that we cannot handle ... */
|
|
assert (_cairo_surface_is_xlib (&src->base));
|
|
assert (mask == NULL || _cairo_surface_is_xlib (&mask->base));
|
|
|
|
if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) {
|
|
status = UNSUPPORTED ("unsupported mask");
|
|
goto BAIL;
|
|
}
|
|
|
|
operation = _recategorize_composite_operation (dst, op, src, &src_attr,
|
|
mask_pattern != NULL);
|
|
if (operation == DO_UNSUPPORTED) {
|
|
status = UNSUPPORTED ("unsupported operation");
|
|
goto BAIL;
|
|
}
|
|
|
|
switch (operation)
|
|
{
|
|
case DO_RENDER:
|
|
status = _cairo_xlib_surface_set_attributes (display,
|
|
src, &src_attr,
|
|
dst_x + width / 2.,
|
|
dst_y + height / 2.);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
_cairo_xlib_surface_ensure_dst_picture (display, dst);
|
|
if (mask) {
|
|
status = _cairo_xlib_surface_set_attributes (display,
|
|
mask, &mask_attr,
|
|
dst_x + width / 2.,
|
|
dst_y + height/ 2.);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
XRenderComposite (display->display,
|
|
_render_operator (op),
|
|
src->src_picture,
|
|
mask->src_picture,
|
|
dst->dst_picture,
|
|
src_x + src_attr.x_offset,
|
|
src_y + src_attr.y_offset,
|
|
mask_x + mask_attr.x_offset,
|
|
mask_y + mask_attr.y_offset,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
} else {
|
|
XRenderComposite (display->display,
|
|
_render_operator (op),
|
|
src->src_picture,
|
|
0,
|
|
dst->dst_picture,
|
|
src_x + src_attr.x_offset,
|
|
src_y + src_attr.y_offset,
|
|
0, 0,
|
|
dst_x, dst_y,
|
|
width, height);
|
|
}
|
|
|
|
break;
|
|
|
|
case DO_XCOPYAREA:
|
|
status = _cairo_xlib_surface_get_gc (display, dst, &gc);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
is_integer_translation =
|
|
_cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity);
|
|
/* This is a pre-condition for DO_XCOPYAREA. */
|
|
assert (is_integer_translation);
|
|
|
|
if (clip_region == NULL) {
|
|
XCopyArea (display->display, src->drawable, dst->drawable, gc,
|
|
src_x + src_attr.x_offset + itx,
|
|
src_y + src_attr.y_offset + ity,
|
|
width, height,
|
|
dst_x, dst_y);
|
|
} else {
|
|
int n, num_rects, x, y;
|
|
|
|
x = src_x + src_attr.x_offset + itx - dst_x;
|
|
y = src_y + src_attr.y_offset + ity - dst_y;
|
|
|
|
num_rects = cairo_region_num_rectangles (clip_region);
|
|
for (n = 0; n < num_rects; n++) {
|
|
cairo_rectangle_int_t rect;
|
|
|
|
cairo_region_get_rectangle (clip_region, n, &rect);
|
|
XCopyArea (display->display, src->drawable, dst->drawable, gc,
|
|
rect.x + x, rect.y + y,
|
|
rect.width, rect.height,
|
|
rect.x, rect.y);
|
|
}
|
|
}
|
|
|
|
_cairo_xlib_surface_put_gc (display, dst, gc);
|
|
break;
|
|
|
|
case DO_XTILE:
|
|
/* This case is only used for bug fallbacks, though we also use it for
|
|
* the case where we don't have the RENDER extension, by forcing
|
|
* buggy_repeat to TRUE.
|
|
*
|
|
* We've checked that we have a repeating unscaled source in
|
|
* _recategorize_composite_operation.
|
|
*/
|
|
|
|
status = _cairo_xlib_surface_get_gc (display, dst, &gc);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
is_integer_translation =
|
|
_cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity);
|
|
/* This is a pre-condition for DO_XTILE. */
|
|
assert (is_integer_translation);
|
|
|
|
XSetTSOrigin (display->display, gc,
|
|
- (itx + src_attr.x_offset), - (ity + src_attr.y_offset));
|
|
XSetTile (display->display, gc, src->drawable);
|
|
|
|
if (clip_region == NULL) {
|
|
XFillRectangle (display->display, dst->drawable, gc,
|
|
dst_x, dst_y, width, height);
|
|
} else {
|
|
int n, num_rects;
|
|
|
|
num_rects = cairo_region_num_rectangles (clip_region);
|
|
for (n = 0; n < num_rects; n++) {
|
|
cairo_rectangle_int_t rect;
|
|
|
|
cairo_region_get_rectangle (clip_region, n, &rect);
|
|
XFillRectangle (display->display, dst->drawable, gc,
|
|
rect.x, rect.y, rect.width, rect.height);
|
|
}
|
|
}
|
|
|
|
_cairo_xlib_surface_put_gc (display, dst, gc);
|
|
break;
|
|
|
|
case DO_UNSUPPORTED:
|
|
default:
|
|
ASSERT_NOT_REACHED;
|
|
}
|
|
|
|
if (!_cairo_operator_bounded_by_source (op))
|
|
status = _cairo_surface_composite_fixup_unbounded (&dst->base,
|
|
&src_attr, src->width, src->height,
|
|
mask ? &mask_attr : NULL,
|
|
mask ? mask->width : 0,
|
|
mask ? mask->height : 0,
|
|
src_x, src_y,
|
|
mask_x, mask_y,
|
|
dst_x, dst_y, width, height,
|
|
clip_region);
|
|
|
|
BAIL:
|
|
if (mask)
|
|
_cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr);
|
|
|
|
_cairo_pattern_release_surface (src_pattern, &src->base, &src_attr);
|
|
|
|
BAIL0:
|
|
cairo_device_release (&display->base);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* XXX move this out of core and into acquire_pattern_surface() above. */
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface,
|
|
const cairo_color_t *color,
|
|
cairo_rectangle_int_t *rects,
|
|
int num_rects)
|
|
{
|
|
cairo_status_t status;
|
|
cairo_solid_pattern_t solid;
|
|
cairo_surface_t *solid_surface = NULL;
|
|
cairo_surface_attributes_t attrs;
|
|
cairo_xlib_display_t *display;
|
|
GC gc;
|
|
int i;
|
|
|
|
_cairo_pattern_init_solid (&solid, color, CAIRO_CONTENT_COLOR);
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
status = _cairo_xlib_surface_get_gc (display, surface, &gc);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
|
|
|
|
status = _cairo_pattern_acquire_surface (&solid.base, &surface->base,
|
|
0, 0,
|
|
ARRAY_LENGTH (dither_pattern[0]),
|
|
ARRAY_LENGTH (dither_pattern),
|
|
CAIRO_PATTERN_ACQUIRE_NONE,
|
|
&solid_surface,
|
|
&attrs);
|
|
if (unlikely (status)) {
|
|
_cairo_xlib_surface_put_gc (display, surface, gc);
|
|
cairo_device_release (&display->base);
|
|
return status;
|
|
}
|
|
|
|
assert (_cairo_surface_is_xlib (solid_surface));
|
|
|
|
XSetTSOrigin (display->display, gc,
|
|
- (surface->base.device_transform.x0 + attrs.x_offset),
|
|
- (surface->base.device_transform.y0 + attrs.y_offset));
|
|
XSetTile (display->display, gc,
|
|
((cairo_xlib_surface_t *) solid_surface)->drawable);
|
|
|
|
for (i = 0; i < num_rects; i++) {
|
|
XFillRectangle (display->display, surface->drawable, gc,
|
|
rects[i].x, rects[i].y,
|
|
rects[i].width, rects[i].height);
|
|
}
|
|
|
|
_cairo_xlib_surface_put_gc (display, surface, gc);
|
|
|
|
_cairo_pattern_release_surface (&solid.base, solid_surface, &attrs);
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_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 *surface = abstract_surface;
|
|
cairo_xlib_display_t *display;
|
|
XRenderColor render_color;
|
|
cairo_status_t status;
|
|
int i;
|
|
|
|
if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op))
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) {
|
|
if (op == CAIRO_OPERATOR_CLEAR ||
|
|
((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) &&
|
|
CAIRO_COLOR_IS_OPAQUE (color)))
|
|
{
|
|
return _cairo_xlib_surface_solid_fill_rectangles (surface, color,
|
|
rects, num_rects);
|
|
}
|
|
|
|
return UNSUPPORTED ("no support for FillRectangles with this op");
|
|
}
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
|
|
|
|
render_color.red = color->red_short;
|
|
render_color.green = color->green_short;
|
|
render_color.blue = color->blue_short;
|
|
render_color.alpha = color->alpha_short;
|
|
|
|
status = _cairo_xlib_surface_set_clip_region (surface, NULL);
|
|
assert (status == CAIRO_STATUS_SUCCESS);
|
|
|
|
_cairo_xlib_surface_ensure_dst_picture (display, surface);
|
|
if (num_rects == 1) {
|
|
/* Take advantage of the protocol compaction that libXrender performs
|
|
* to amalgamate sequences of XRenderFillRectangle().
|
|
*/
|
|
XRenderFillRectangle (display->display,
|
|
_render_operator (op),
|
|
surface->dst_picture,
|
|
&render_color,
|
|
rects->x,
|
|
rects->y,
|
|
rects->width,
|
|
rects->height);
|
|
} else {
|
|
XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
|
|
XRectangle *xrects = static_xrects;
|
|
|
|
if (num_rects > ARRAY_LENGTH (static_xrects)) {
|
|
xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
|
|
if (unlikely (xrects == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
}
|
|
|
|
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 (display->display,
|
|
_render_operator (op),
|
|
surface->dst_picture,
|
|
&render_color, xrects, num_rects);
|
|
|
|
if (xrects != static_xrects)
|
|
free (xrects);
|
|
}
|
|
|
|
BAIL:
|
|
cairo_device_release (&display->base);
|
|
return status;
|
|
}
|
|
|
|
#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));
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_composite_trapezoids (cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
void *abstract_dst,
|
|
cairo_antialias_t antialias,
|
|
int src_x,
|
|
int src_y,
|
|
int dst_x,
|
|
int dst_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_trapezoid_t *traps,
|
|
int num_traps,
|
|
cairo_region_t *clip_region)
|
|
{
|
|
cairo_surface_attributes_t attributes;
|
|
cairo_xlib_surface_t *dst = abstract_dst;
|
|
cairo_xlib_surface_t *src;
|
|
cairo_xlib_display_t *display;
|
|
cairo_int_status_t status;
|
|
composite_operation_t operation;
|
|
int render_reference_x, render_reference_y;
|
|
int render_src_x, render_src_y;
|
|
XRenderPictFormat *pict_format;
|
|
XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
|
|
XTrapezoid *xtraps = xtraps_stack;
|
|
int i;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst))
|
|
return UNSUPPORTED ("XRender does not support CompositeTrapezoids");
|
|
|
|
operation = _categorize_composite_operation (dst, op, pattern, TRUE);
|
|
if (operation == DO_UNSUPPORTED)
|
|
return UNSUPPORTED ("unsupported operation");
|
|
|
|
status = _cairo_xlib_display_acquire (dst->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
|
|
|
|
status = _cairo_xlib_surface_acquire_pattern_surface (display,
|
|
dst,
|
|
pattern,
|
|
src_x, src_y,
|
|
width, height,
|
|
&src, &attributes);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
|
|
operation = _recategorize_composite_operation (dst, op, src,
|
|
&attributes, TRUE);
|
|
if (operation == DO_UNSUPPORTED) {
|
|
status = UNSUPPORTED ("unsupported operation");
|
|
goto BAIL;
|
|
}
|
|
|
|
switch (antialias) {
|
|
case CAIRO_ANTIALIAS_NONE:
|
|
pict_format =
|
|
_cairo_xlib_display_get_xrender_format (display,
|
|
CAIRO_FORMAT_A1);
|
|
break;
|
|
case CAIRO_ANTIALIAS_GRAY:
|
|
case CAIRO_ANTIALIAS_SUBPIXEL:
|
|
case CAIRO_ANTIALIAS_DEFAULT:
|
|
default:
|
|
pict_format =
|
|
_cairo_xlib_display_get_xrender_format (display,
|
|
CAIRO_FORMAT_A8);
|
|
break;
|
|
}
|
|
|
|
status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
_cairo_xlib_surface_ensure_dst_picture (display, dst);
|
|
|
|
status = _cairo_xlib_surface_set_attributes (display,
|
|
src, &attributes,
|
|
dst_x + width / 2.,
|
|
dst_y + height / 2.);
|
|
if (unlikely (status))
|
|
goto BAIL;
|
|
|
|
if (num_traps > ARRAY_LENGTH (xtraps_stack)) {
|
|
xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid));
|
|
if (unlikely (xtraps == NULL)) {
|
|
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
goto BAIL;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_traps; i++) {
|
|
/* top/bottom will be clamped to surface bounds */
|
|
xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top);
|
|
xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom);
|
|
|
|
/* 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 (&traps[i].left))) {
|
|
_project_line_x_onto_16_16 (&traps[i].left,
|
|
traps[i].top,
|
|
traps[i].bottom,
|
|
&xtraps[i].left);
|
|
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(traps[i].left.p1.x);
|
|
xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y);
|
|
xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x);
|
|
xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y);
|
|
}
|
|
|
|
if (unlikely (_line_exceeds_16_16 (&traps[i].right))) {
|
|
_project_line_x_onto_16_16 (&traps[i].right,
|
|
traps[i].top,
|
|
traps[i].bottom,
|
|
&xtraps[i].right);
|
|
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(traps[i].right.p1.x);
|
|
xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y);
|
|
xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x);
|
|
xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y);
|
|
}
|
|
}
|
|
|
|
if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
|
|
render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
|
|
render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
|
|
} else {
|
|
render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
|
|
render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
|
|
}
|
|
|
|
render_src_x = src_x + render_reference_x - dst_x;
|
|
render_src_y = src_y + render_reference_y - dst_y;
|
|
|
|
XRenderCompositeTrapezoids (display->display,
|
|
_render_operator (op),
|
|
src->src_picture, dst->dst_picture,
|
|
pict_format,
|
|
render_src_x + attributes.x_offset,
|
|
render_src_y + attributes.y_offset,
|
|
xtraps, num_traps);
|
|
|
|
if (xtraps != xtraps_stack)
|
|
free (xtraps);
|
|
|
|
if (! _cairo_operator_bounded_by_mask (op)) {
|
|
cairo_traps_t _traps;
|
|
cairo_box_t box;
|
|
cairo_rectangle_int_t extents;
|
|
|
|
/* XRenderCompositeTrapezoids() creates a mask only large enough for the
|
|
* trapezoids themselves, but if the operator is unbounded, then we need
|
|
* to actually composite all the way out to the bounds.
|
|
*/
|
|
/* XXX: update the interface to pass composite rects */
|
|
_traps.traps = traps;
|
|
_traps.num_traps = num_traps;
|
|
_cairo_traps_extents (&_traps, &box);
|
|
_cairo_box_round_to_rectangle (&box, &extents);
|
|
|
|
status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
|
|
&attributes,
|
|
src->width, src->height,
|
|
extents.width, extents.height,
|
|
src_x, src_y,
|
|
-extents.x + dst_x, -extents.y + dst_y,
|
|
dst_x, dst_y,
|
|
width, height,
|
|
clip_region);
|
|
}
|
|
|
|
BAIL:
|
|
_cairo_pattern_release_surface (pattern, &src->base, &attributes);
|
|
BAIL0:
|
|
cairo_device_release (&display->base);
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xlib_surface_get_extents (void *abstract_surface,
|
|
cairo_rectangle_int_t *rectangle)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
|
|
rectangle->x = 0;
|
|
rectangle->y = 0;
|
|
|
|
rectangle->width = surface->width;
|
|
rectangle->height = surface->height;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_get_font_options (void *abstract_surface,
|
|
cairo_font_options_t *options)
|
|
{
|
|
cairo_xlib_surface_t *surface = abstract_surface;
|
|
|
|
*options = *_cairo_xlib_screen_get_font_options (surface->screen);
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
|
|
|
|
static void
|
|
_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_scaled_font_t *scaled_font);
|
|
|
|
static cairo_bool_t
|
|
_cairo_xlib_surface_is_similar (void *surface_a,
|
|
void *surface_b,
|
|
cairo_content_t content)
|
|
{
|
|
cairo_xlib_surface_t *a = surface_a;
|
|
cairo_xlib_surface_t *b = surface_b;
|
|
XRenderPictFormat *xrender_format = b->xrender_format;
|
|
cairo_xlib_display_t *display;
|
|
|
|
if (! _cairo_xlib_surface_same_screen (a, b))
|
|
return FALSE;
|
|
|
|
if (_cairo_xlib_display_acquire (b->base.device, &display))
|
|
return FALSE;
|
|
|
|
/* now inspect the content to check that a is similar to b */
|
|
if (xrender_format == NULL && b->visual != NULL)
|
|
xrender_format = XRenderFindVisualFormat (display->display, b->visual);
|
|
|
|
if (xrender_format == NULL ||
|
|
_xrender_format_to_content (xrender_format) != content)
|
|
{
|
|
xrender_format = _cairo_xlib_display_get_xrender_format (
|
|
display,
|
|
_cairo_format_from_content (content));
|
|
}
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
return a->xrender_format == xrender_format;
|
|
}
|
|
|
|
static const cairo_surface_backend_t cairo_xlib_surface_backend = {
|
|
CAIRO_SURFACE_TYPE_XLIB,
|
|
_cairo_xlib_surface_create_similar,
|
|
_cairo_xlib_surface_finish,
|
|
_cairo_xlib_surface_acquire_source_image,
|
|
_cairo_xlib_surface_release_source_image,
|
|
_cairo_xlib_surface_acquire_dest_image,
|
|
_cairo_xlib_surface_release_dest_image,
|
|
_cairo_xlib_surface_clone_similar,
|
|
_cairo_xlib_surface_composite,
|
|
_cairo_xlib_surface_fill_rectangles,
|
|
_cairo_xlib_surface_composite_trapezoids,
|
|
NULL, /* create_span_renderer */
|
|
NULL, /* check_span_renderer */
|
|
NULL, /* copy_page */
|
|
NULL, /* show_page */
|
|
_cairo_xlib_surface_get_extents,
|
|
NULL, /* old_show_glyphs */
|
|
_cairo_xlib_surface_get_font_options,
|
|
NULL, /* flush */
|
|
NULL, /* mark_dirty_rectangle */
|
|
_cairo_xlib_surface_scaled_font_fini,
|
|
_cairo_xlib_surface_scaled_glyph_fini,
|
|
|
|
NULL, /* paint */
|
|
NULL, /* mask */
|
|
NULL, /* stroke */
|
|
NULL, /* fill */
|
|
_cairo_xlib_surface_show_glyphs,
|
|
|
|
_cairo_xlib_surface_snapshot,
|
|
_cairo_xlib_surface_is_similar,
|
|
|
|
NULL, /* fill_stroke */
|
|
|
|
_cairo_xlib_surface_create_solid_pattern_surface,
|
|
_cairo_xlib_surface_can_repaint_solid_pattern_surface
|
|
};
|
|
|
|
/**
|
|
* _cairo_surface_is_xlib:
|
|
* @surface: a #cairo_surface_t
|
|
*
|
|
* Checks if a surface is a #cairo_xlib_surface_t
|
|
*
|
|
* Return value: True if the surface is an xlib surface
|
|
**/
|
|
static cairo_bool_t
|
|
_cairo_surface_is_xlib (cairo_surface_t *surface)
|
|
{
|
|
return surface->backend == &cairo_xlib_surface_backend;
|
|
}
|
|
|
|
/* callback from CloseDisplay */
|
|
static void
|
|
_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data)
|
|
{
|
|
cairo_xlib_surface_t *surface = cairo_container_of (data,
|
|
cairo_xlib_surface_t,
|
|
close_display_hook);
|
|
Display *dpy;
|
|
|
|
dpy = display->display;
|
|
|
|
X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable));
|
|
|
|
if (surface->dst_picture != None) {
|
|
XRenderFreePicture (dpy, surface->dst_picture);
|
|
surface->dst_picture = None;
|
|
}
|
|
|
|
if (surface->src_picture != None) {
|
|
XRenderFreePicture (dpy, surface->src_picture);
|
|
surface->src_picture = None;
|
|
}
|
|
|
|
if (surface->owns_pixmap) {
|
|
XFreePixmap (dpy, surface->drawable);
|
|
surface->drawable = None;
|
|
surface->owns_pixmap = FALSE;
|
|
}
|
|
}
|
|
|
|
static cairo_surface_t *
|
|
_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen,
|
|
Drawable drawable,
|
|
Visual *visual,
|
|
XRenderPictFormat *xrender_format,
|
|
int width,
|
|
int height,
|
|
int depth)
|
|
{
|
|
cairo_xlib_surface_t *surface;
|
|
cairo_xlib_display_t *display;
|
|
cairo_status_t status;
|
|
|
|
if (depth == 0) {
|
|
if (xrender_format) {
|
|
depth = xrender_format->depth;
|
|
|
|
/* XXX find matching visual for core/dithering fallbacks? */
|
|
} else if (visual) {
|
|
Screen *scr = screen->screen;
|
|
|
|
if (visual == DefaultVisualOfScreen (scr)) {
|
|
depth = DefaultDepthOfScreen (scr);
|
|
} else {
|
|
int j, k;
|
|
|
|
/* This is ugly, but we have to walk over all visuals
|
|
* for the display to find the correct depth.
|
|
*/
|
|
depth = 0;
|
|
for (j = 0; j < scr->ndepths; j++) {
|
|
Depth *d = &scr->depths[j];
|
|
for (k = 0; k < d->nvisuals; k++) {
|
|
if (&d->visuals[k] == visual) {
|
|
depth = d->depth;
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (depth == 0)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
|
|
|
|
found:
|
|
;
|
|
}
|
|
|
|
surface = malloc (sizeof (cairo_xlib_surface_t));
|
|
if (unlikely (surface == NULL))
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
|
|
|
|
status = _cairo_xlib_display_acquire (screen->device, &display);
|
|
if (unlikely (status)) {
|
|
free (surface);
|
|
return _cairo_surface_create_in_error (_cairo_error (status));
|
|
}
|
|
|
|
_cairo_xlib_display_get_xrender_version (display,
|
|
&surface->render_major,
|
|
&surface->render_minor);
|
|
if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) {
|
|
if (!xrender_format) {
|
|
if (visual) {
|
|
xrender_format = XRenderFindVisualFormat (display->display, visual);
|
|
} else if (depth == 1) {
|
|
xrender_format =
|
|
_cairo_xlib_display_get_xrender_format (display,
|
|
CAIRO_FORMAT_A1);
|
|
}
|
|
}
|
|
} else {
|
|
/* we cannot use XRender for this surface, so ensure we don't try */
|
|
surface->render_major = -1;
|
|
surface->render_minor = -1;
|
|
}
|
|
|
|
/* initialize and hook into the CloseDisplay callback */
|
|
surface->close_display_hook.func = _cairo_xlib_surface_detach_display;
|
|
_cairo_xlib_add_close_display_hook (display,
|
|
&surface->close_display_hook);
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
_cairo_surface_init (&surface->base,
|
|
&cairo_xlib_surface_backend,
|
|
screen->device,
|
|
_xrender_format_to_content (xrender_format));
|
|
|
|
surface->screen = screen;
|
|
|
|
surface->drawable = drawable;
|
|
surface->owns_pixmap = FALSE;
|
|
surface->use_pixmap = 0;
|
|
surface->width = width;
|
|
surface->height = height;
|
|
|
|
surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device);
|
|
if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) {
|
|
/* so we can use the XTile fallback */
|
|
surface->buggy_repeat = TRUE;
|
|
}
|
|
|
|
surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device);
|
|
if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface))
|
|
surface->buggy_pad_reflect = TRUE;
|
|
|
|
surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device);
|
|
if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface))
|
|
surface->buggy_gradients = TRUE;
|
|
|
|
surface->dst_picture = None;
|
|
surface->src_picture = None;
|
|
|
|
surface->visual = visual;
|
|
surface->xrender_format = xrender_format;
|
|
surface->depth = depth;
|
|
surface->filter = CAIRO_FILTER_NEAREST;
|
|
surface->extend = CAIRO_EXTEND_NONE;
|
|
surface->has_component_alpha = FALSE;
|
|
surface->xtransform = identity;
|
|
|
|
surface->clip_region = NULL;
|
|
surface->clip_rects = surface->embedded_clip_rects;
|
|
surface->num_clip_rects = 0;
|
|
surface->clip_dirty = 0;
|
|
|
|
/*
|
|
* Compute the pixel format masks from either a XrenderFormat or
|
|
* else from a visual; failing that we assume the drawable is an
|
|
* alpha-only pixmap as it could only have been created that way
|
|
* through the cairo_xlib_surface_create_for_bitmap function.
|
|
*/
|
|
if (xrender_format) {
|
|
surface->a_mask = (unsigned long)
|
|
surface->xrender_format->direct.alphaMask
|
|
<< surface->xrender_format->direct.alpha;
|
|
surface->r_mask = (unsigned long)
|
|
surface->xrender_format->direct.redMask
|
|
<< surface->xrender_format->direct.red;
|
|
surface->g_mask = (unsigned long)
|
|
surface->xrender_format->direct.greenMask
|
|
<< surface->xrender_format->direct.green;
|
|
surface->b_mask = (unsigned long)
|
|
surface->xrender_format->direct.blueMask
|
|
<< surface->xrender_format->direct.blue;
|
|
} else if (visual) {
|
|
surface->a_mask = 0;
|
|
surface->r_mask = visual->red_mask;
|
|
surface->g_mask = visual->green_mask;
|
|
surface->b_mask = visual->blue_mask;
|
|
} else {
|
|
if (depth < 32)
|
|
surface->a_mask = (1 << depth) - 1;
|
|
else
|
|
surface->a_mask = 0xffffffff;
|
|
surface->r_mask = 0;
|
|
surface->g_mask = 0;
|
|
surface->b_mask = 0;
|
|
}
|
|
|
|
return &surface->base;
|
|
}
|
|
|
|
static Screen *
|
|
_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual)
|
|
{
|
|
int s, d, v;
|
|
|
|
for (s = 0; s < ScreenCount (dpy); s++) {
|
|
Screen *screen;
|
|
|
|
screen = ScreenOfDisplay (dpy, s);
|
|
if (visual == DefaultVisualOfScreen (screen))
|
|
return screen;
|
|
|
|
for (d = 0; d < screen->ndepths; d++) {
|
|
Depth *depth;
|
|
|
|
depth = &screen->depths[d];
|
|
for (v = 0; v < depth->nvisuals; v++)
|
|
if (visual == &depth->visuals[v])
|
|
return screen;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_create:
|
|
* @dpy: an X Display
|
|
* @drawable: an X Drawable, (a Pixmap or a Window)
|
|
* @visual: the visual to use for drawing to @drawable. The depth
|
|
* of the visual must match the depth of the drawable.
|
|
* Currently, only TrueColor visuals are fully supported.
|
|
* @width: the current width of @drawable.
|
|
* @height: the current height of @drawable.
|
|
*
|
|
* Creates an Xlib surface that draws to the given drawable.
|
|
* The way that colors are represented in the drawable is specified
|
|
* by the provided visual.
|
|
*
|
|
* Note: If @drawable is a Window, then the function
|
|
* cairo_xlib_surface_set_size() must be called whenever the size of the
|
|
* window changes.
|
|
*
|
|
* When @drawable is a Window containing child windows then drawing to
|
|
* the created surface will be clipped by those child windows. When
|
|
* the created surface is used as a source, the contents of the
|
|
* children will be included.
|
|
*
|
|
* Return value: the newly created surface
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_xlib_surface_create (Display *dpy,
|
|
Drawable drawable,
|
|
Visual *visual,
|
|
int width,
|
|
int height)
|
|
{
|
|
Screen *scr;
|
|
cairo_xlib_screen_t *screen;
|
|
cairo_status_t status;
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
|
|
/* you're lying, and you know it! */
|
|
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
|
|
}
|
|
|
|
scr = _cairo_xlib_screen_from_visual (dpy, visual);
|
|
if (scr == NULL)
|
|
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
|
|
|
|
status = _cairo_xlib_screen_get (dpy, scr, &screen);
|
|
if (unlikely (status))
|
|
return _cairo_surface_create_in_error (status);
|
|
|
|
X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable));
|
|
|
|
return _cairo_xlib_surface_create_internal (screen, drawable,
|
|
visual, NULL,
|
|
width, height, 0);
|
|
}
|
|
slim_hidden_def (cairo_xlib_surface_create);
|
|
|
|
/**
|
|
* cairo_xlib_surface_create_for_bitmap:
|
|
* @dpy: an X Display
|
|
* @bitmap: an X Drawable, (a depth-1 Pixmap)
|
|
* @screen: the X Screen associated with @bitmap
|
|
* @width: the current width of @bitmap.
|
|
* @height: the current height of @bitmap.
|
|
*
|
|
* Creates an Xlib surface that draws to the given bitmap.
|
|
* This will be drawn to as a %CAIRO_FORMAT_A1 object.
|
|
*
|
|
* Return value: the newly created surface
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_xlib_surface_create_for_bitmap (Display *dpy,
|
|
Pixmap bitmap,
|
|
Screen *scr,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_xlib_screen_t *screen;
|
|
cairo_status_t status;
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
|
|
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
|
|
|
|
status = _cairo_xlib_screen_get (dpy, scr, &screen);
|
|
if (unlikely (status))
|
|
return _cairo_surface_create_in_error (status);
|
|
|
|
X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap));
|
|
|
|
return _cairo_xlib_surface_create_internal (screen, bitmap,
|
|
NULL, NULL,
|
|
width, height, 1);
|
|
}
|
|
|
|
#if CAIRO_HAS_XLIB_XRENDER_SURFACE
|
|
/**
|
|
* cairo_xlib_surface_create_with_xrender_format:
|
|
* @dpy: an X Display
|
|
* @drawable: an X Drawable, (a Pixmap or a Window)
|
|
* @screen: the X Screen associated with @drawable
|
|
* @format: the picture format to use for drawing to @drawable. The depth
|
|
* of @format must match the depth of the drawable.
|
|
* @width: the current width of @drawable.
|
|
* @height: the current height of @drawable.
|
|
*
|
|
* Creates an Xlib surface that draws to the given drawable.
|
|
* The way that colors are represented in the drawable is specified
|
|
* by the provided picture format.
|
|
*
|
|
* Note: If @drawable is a Window, then the function
|
|
* cairo_xlib_surface_set_size() must be called whenever the size of the
|
|
* window changes.
|
|
*
|
|
* Return value: the newly created surface
|
|
**/
|
|
cairo_surface_t *
|
|
cairo_xlib_surface_create_with_xrender_format (Display *dpy,
|
|
Drawable drawable,
|
|
Screen *scr,
|
|
XRenderPictFormat *format,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_xlib_screen_t *screen;
|
|
cairo_status_t status;
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
|
|
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
|
|
|
|
status = _cairo_xlib_screen_get (dpy, scr, &screen);
|
|
if (unlikely (status))
|
|
return _cairo_surface_create_in_error (status);
|
|
|
|
X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable));
|
|
|
|
return _cairo_xlib_surface_create_internal (screen, drawable,
|
|
NULL, format,
|
|
width, height, 0);
|
|
}
|
|
slim_hidden_def (cairo_xlib_surface_create_with_xrender_format);
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_xrender_format:
|
|
* @surface: an xlib surface
|
|
*
|
|
* Gets the X Render picture format that @surface uses for rendering with the
|
|
* X Render extension. If the surface was created by
|
|
* cairo_xlib_surface_create_with_xrender_format() originally, the return
|
|
* value is the format passed to that constructor.
|
|
*
|
|
* Return value: the XRenderPictFormat* associated with @surface,
|
|
* or %NULL if the surface is not an xlib surface
|
|
* or if the X Render extension is not available.
|
|
*
|
|
* Since: 1.6
|
|
**/
|
|
XRenderPictFormat *
|
|
cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
|
|
{
|
|
cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
|
|
|
|
/* Throw an error for a non-xlib surface */
|
|
if (! _cairo_surface_is_xlib (surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return NULL;
|
|
}
|
|
|
|
return xlib_surface->xrender_format;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* cairo_xlib_surface_set_size:
|
|
* @surface: a #cairo_surface_t for the XLib backend
|
|
* @width: the new width of the surface
|
|
* @height: the new height of the surface
|
|
*
|
|
* Informs cairo of the new size of the X Drawable underlying the
|
|
* surface. For a surface created for a Window (rather than a Pixmap),
|
|
* this function must be called each time the size of the window
|
|
* changes. (For a subwindow, you are normally resizing the window
|
|
* yourself, but for a toplevel window, it is necessary to listen for
|
|
* ConfigureNotify events.)
|
|
*
|
|
* A Pixmap can never change size, so it is never necessary to call
|
|
* this function on a surface created for a Pixmap.
|
|
**/
|
|
void
|
|
cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (abstract_surface->status))
|
|
return;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
status = _cairo_surface_set_error (abstract_surface,
|
|
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return;
|
|
}
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
|
|
status = _cairo_surface_set_error (abstract_surface,
|
|
CAIRO_STATUS_INVALID_SIZE);
|
|
return;
|
|
}
|
|
|
|
surface->width = width;
|
|
surface->height = height;
|
|
}
|
|
/**
|
|
* cairo_xlib_surface_set_drawable:
|
|
* @surface: a #cairo_surface_t for the XLib backend
|
|
* @drawable: the new drawable for the surface
|
|
* @width: the width of the new drawable
|
|
* @height: the height of the new drawable
|
|
*
|
|
* Informs cairo of a new X Drawable underlying the
|
|
* surface. The drawable must match the display, screen
|
|
* and format of the existing drawable or the application
|
|
* will get X protocol errors and will probably terminate.
|
|
* No checks are done by this function to ensure this
|
|
* compatibility.
|
|
**/
|
|
void
|
|
cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface,
|
|
Drawable drawable,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface;
|
|
cairo_status_t status;
|
|
|
|
if (unlikely (abstract_surface->status))
|
|
return;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
status = _cairo_surface_set_error (abstract_surface,
|
|
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return;
|
|
}
|
|
|
|
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) {
|
|
status = _cairo_surface_set_error (abstract_surface,
|
|
CAIRO_STATUS_INVALID_SIZE);
|
|
return;
|
|
}
|
|
|
|
/* XXX: and what about this case? */
|
|
if (surface->owns_pixmap)
|
|
return;
|
|
|
|
if (surface->drawable != drawable) {
|
|
cairo_xlib_display_t *display;
|
|
|
|
status = _cairo_xlib_display_acquire (surface->base.device, &display);
|
|
if (unlikely (status))
|
|
return;
|
|
|
|
X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable));
|
|
|
|
if (surface->dst_picture != None) {
|
|
status = _cairo_xlib_display_queue_resource (
|
|
display,
|
|
XRenderFreePicture,
|
|
surface->dst_picture);
|
|
if (unlikely (status)) {
|
|
status = _cairo_surface_set_error (&surface->base, status);
|
|
return;
|
|
}
|
|
|
|
surface->dst_picture = None;
|
|
}
|
|
|
|
if (surface->src_picture != None) {
|
|
status = _cairo_xlib_display_queue_resource (
|
|
display,
|
|
XRenderFreePicture,
|
|
surface->src_picture);
|
|
if (unlikely (status)) {
|
|
status = _cairo_surface_set_error (&surface->base, status);
|
|
return;
|
|
}
|
|
|
|
surface->src_picture = None;
|
|
}
|
|
|
|
cairo_device_release (&display->base);
|
|
|
|
surface->drawable = drawable;
|
|
}
|
|
surface->width = width;
|
|
surface->height = height;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_display:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the X Display for the underlying X Drawable.
|
|
*
|
|
* Return value: the display.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
Display *
|
|
cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
|
|
{
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return NULL;
|
|
}
|
|
|
|
return ((cairo_xlib_display_t *) abstract_surface->device)->display;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_drawable:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the underlying X Drawable used for the surface.
|
|
*
|
|
* Return value: the drawable.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
Drawable
|
|
cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return 0;
|
|
}
|
|
|
|
return surface->drawable;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_screen:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the X Screen for the underlying X Drawable.
|
|
*
|
|
* Return value: the screen.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
Screen *
|
|
cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return NULL;
|
|
}
|
|
|
|
return surface->screen->screen;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_visual:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the X Visual used for underlying X Drawable.
|
|
*
|
|
* Return value: the visual.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
Visual *
|
|
cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return NULL;
|
|
}
|
|
|
|
return surface->visual;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_depth:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the number of bits used to represent each pixel value.
|
|
*
|
|
* Return value: the depth of the surface in bits.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
int
|
|
cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return 0;
|
|
}
|
|
|
|
return surface->depth;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_width:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the width of the X Drawable underlying the surface in pixels.
|
|
*
|
|
* Return value: the width of the surface in pixels.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
int
|
|
cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return 0;
|
|
}
|
|
|
|
return surface->width;
|
|
}
|
|
|
|
/**
|
|
* cairo_xlib_surface_get_height:
|
|
* @surface: a #cairo_xlib_surface_t
|
|
*
|
|
* Get the height of the X Drawable underlying the surface in pixels.
|
|
*
|
|
* Return value: the height of the surface in pixels.
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
int
|
|
cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
|
|
{
|
|
cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface;
|
|
|
|
if (! _cairo_surface_is_xlib (abstract_surface)) {
|
|
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
|
|
return 0;
|
|
}
|
|
|
|
return surface->height;
|
|
}
|
|
|
|
enum {
|
|
GLYPHSET_INDEX_ARGB32,
|
|
GLYPHSET_INDEX_A8,
|
|
GLYPHSET_INDEX_A1,
|
|
NUM_GLYPHSETS
|
|
};
|
|
|
|
typedef struct _cairo_xlib_font_glyphset_free_glyphs {
|
|
GlyphSet glyphset;
|
|
int glyph_count;
|
|
unsigned long glyph_indices[128];
|
|
} cairo_xlib_font_glyphset_free_glyphs_t;
|
|
|
|
typedef struct _cairo_xlib_font_glyphset_info {
|
|
GlyphSet glyphset;
|
|
cairo_format_t format;
|
|
XRenderPictFormat *xrender_format;
|
|
cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs;
|
|
} cairo_xlib_font_glyphset_info_t;
|
|
|
|
typedef struct _cairo_xlib_surface_font_private {
|
|
cairo_scaled_font_t *scaled_font;
|
|
cairo_xlib_hook_t close_display_hook;
|
|
cairo_device_t *device;
|
|
cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
|
|
} cairo_xlib_surface_font_private_t;
|
|
|
|
/* callback from CloseDisplay */
|
|
static void
|
|
_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display,
|
|
void *data)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
cairo_scaled_font_t *scaled_font;
|
|
|
|
font_private = cairo_container_of (data,
|
|
cairo_xlib_surface_font_private_t,
|
|
close_display_hook);
|
|
scaled_font = font_private->scaled_font;
|
|
|
|
CAIRO_MUTEX_LOCK (scaled_font->mutex);
|
|
font_private = scaled_font->surface_private;
|
|
scaled_font->surface_private = NULL;
|
|
|
|
_cairo_scaled_font_reset_cache (scaled_font);
|
|
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
|
|
|
|
if (font_private != NULL) {
|
|
int i;
|
|
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyphset_info = &font_private->glyphset_info[i];
|
|
if (glyphset_info->glyphset)
|
|
XRenderFreeGlyphSet (display->display, glyphset_info->glyphset);
|
|
|
|
if (glyphset_info->pending_free_glyphs != NULL)
|
|
free (glyphset_info->pending_free_glyphs);
|
|
}
|
|
|
|
cairo_device_destroy (font_private->device);
|
|
free (font_private);
|
|
}
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_font_init (cairo_xlib_display_t *display,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
int i;
|
|
|
|
font_private = malloc (sizeof (cairo_xlib_surface_font_private_t));
|
|
if (unlikely (font_private == NULL))
|
|
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
|
|
|
font_private->scaled_font = scaled_font;
|
|
font_private->device = cairo_device_reference (&display->base);
|
|
|
|
/* initialize and hook into the CloseDisplay callback */
|
|
font_private->close_display_hook.func =
|
|
_cairo_xlib_surface_remove_scaled_font;
|
|
_cairo_xlib_add_close_display_hook (display,
|
|
&font_private->close_display_hook);
|
|
|
|
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
|
|
switch (i) {
|
|
case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break;
|
|
case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break;
|
|
case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break;
|
|
default: ASSERT_NOT_REACHED; break;
|
|
}
|
|
glyphset_info->xrender_format = NULL;
|
|
glyphset_info->glyphset = None;
|
|
glyphset_info->pending_free_glyphs = NULL;
|
|
}
|
|
|
|
scaled_font->surface_private = font_private;
|
|
scaled_font->surface_backend = &cairo_xlib_surface_backend;
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
cairo_status_t status;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if (font_private != NULL) {
|
|
cairo_xlib_display_t *display;
|
|
int i;
|
|
|
|
status = _cairo_xlib_display_acquire (font_private->device, &display);
|
|
if (status)
|
|
goto BAIL;
|
|
|
|
_cairo_xlib_remove_close_display_hook (display,
|
|
&font_private->close_display_hook);
|
|
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyphset_info = &font_private->glyphset_info[i];
|
|
|
|
if (glyphset_info->pending_free_glyphs != NULL)
|
|
free (glyphset_info->pending_free_glyphs);
|
|
|
|
if (glyphset_info->glyphset) {
|
|
status = _cairo_xlib_display_queue_resource (display,
|
|
XRenderFreeGlyphSet,
|
|
glyphset_info->glyphset);
|
|
(void) status; /* XXX cannot propagate failure */
|
|
}
|
|
}
|
|
|
|
cairo_device_release (&display->base);
|
|
BAIL:
|
|
cairo_device_destroy (&display->base);
|
|
free (font_private);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_render_free_glyphs (Display *dpy,
|
|
cairo_xlib_font_glyphset_free_glyphs_t *to_free)
|
|
{
|
|
XRenderFreeGlyphs (dpy,
|
|
to_free->glyphset,
|
|
to_free->glyph_indices,
|
|
to_free->glyph_count);
|
|
}
|
|
|
|
static cairo_xlib_font_glyphset_info_t *
|
|
_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph)
|
|
{
|
|
return scaled_glyph->surface_private;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info)
|
|
{
|
|
scaled_glyph->surface_private = glyphset_info;
|
|
}
|
|
|
|
static void
|
|
_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info;
|
|
|
|
if (scaled_font->finished)
|
|
return;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
|
|
if (font_private != NULL && glyphset_info != NULL) {
|
|
cairo_xlib_font_glyphset_free_glyphs_t *to_free;
|
|
cairo_status_t status;
|
|
|
|
to_free = glyphset_info->pending_free_glyphs;
|
|
if (to_free != NULL &&
|
|
to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
|
|
{
|
|
cairo_xlib_display_t *display;
|
|
|
|
status = _cairo_xlib_display_acquire (font_private->device, &display);
|
|
if (status == CAIRO_STATUS_SUCCESS) {
|
|
status = _cairo_xlib_display_queue_work (display,
|
|
(cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
|
|
to_free,
|
|
free);
|
|
cairo_device_release (&display->base);
|
|
}
|
|
/* XXX cannot propagate failure */
|
|
if (unlikely (status))
|
|
free (to_free);
|
|
|
|
to_free = glyphset_info->pending_free_glyphs = NULL;
|
|
}
|
|
|
|
if (to_free == NULL) {
|
|
to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t));
|
|
if (unlikely (to_free == NULL)) {
|
|
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
|
|
return; /* XXX cannot propagate failure */
|
|
}
|
|
|
|
to_free->glyphset = glyphset_info->glyphset;
|
|
to_free->glyph_count = 0;
|
|
glyphset_info->pending_free_glyphs = to_free;
|
|
}
|
|
|
|
to_free->glyph_indices[to_free->glyph_count++] =
|
|
_cairo_scaled_glyph_index (scaled_glyph);
|
|
}
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_native_byte_order_lsb (void)
|
|
{
|
|
int x = 1;
|
|
|
|
return *((char *) &x) == 1;
|
|
}
|
|
|
|
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 cairo_xlib_font_glyphset_info_t *
|
|
_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font,
|
|
cairo_format_t format)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info;
|
|
int glyphset_index;
|
|
|
|
glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
|
|
font_private = scaled_font->surface_private;
|
|
glyphset_info = &font_private->glyphset_info[glyphset_index];
|
|
if (glyphset_info->glyphset == None) {
|
|
cairo_xlib_display_t *display;
|
|
|
|
if (_cairo_xlib_display_acquire (font_private->device, &display))
|
|
return NULL;
|
|
|
|
glyphset_info->xrender_format =
|
|
_cairo_xlib_display_get_xrender_format (display,
|
|
glyphset_info->format);
|
|
glyphset_info->glyphset = XRenderCreateGlyphSet (display->display,
|
|
glyphset_info->xrender_format);
|
|
|
|
cairo_device_release (&display->base);
|
|
}
|
|
|
|
return glyphset_info;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xlib_glyphset_info_has_pending_free_glyph (
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info,
|
|
unsigned long glyph_index)
|
|
{
|
|
if (glyphset_info->pending_free_glyphs != NULL) {
|
|
cairo_xlib_font_glyphset_free_glyphs_t *to_free;
|
|
int i;
|
|
|
|
to_free = glyphset_info->pending_free_glyphs;
|
|
for (i = 0; i < to_free->glyph_count; i++) {
|
|
if (to_free->glyph_indices[i] == glyph_index) {
|
|
to_free->glyph_count--;
|
|
memmove (&to_free->glyph_indices[i],
|
|
&to_free->glyph_indices[i+1],
|
|
(to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0]));
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static cairo_xlib_font_glyphset_info_t *
|
|
_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (
|
|
cairo_scaled_font_t *scaled_font,
|
|
unsigned long glyph_index,
|
|
cairo_image_surface_t *surface)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
int i;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if (font_private == NULL)
|
|
return NULL;
|
|
|
|
if (surface != NULL) {
|
|
i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
|
|
if (_cairo_xlib_glyphset_info_has_pending_free_glyph (
|
|
&font_private->glyphset_info[i],
|
|
glyph_index))
|
|
{
|
|
return &font_private->glyphset_info[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < NUM_GLYPHSETS; i++) {
|
|
if (_cairo_xlib_glyphset_info_has_pending_free_glyph (
|
|
&font_private->glyphset_info[i],
|
|
glyph_index))
|
|
{
|
|
return &font_private->glyphset_info[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static cairo_status_t
|
|
_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
|
|
cairo_scaled_font_t *scaled_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 *scaled_glyph = *pscaled_glyph;
|
|
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
|
|
cairo_bool_t already_had_glyph_surface;
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info;
|
|
|
|
glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
|
|
|
|
/* check to see if we have a pending XRenderFreeGlyph for this glyph */
|
|
glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface);
|
|
if (glyphset_info != NULL) {
|
|
_cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (!glyph_surface) {
|
|
status = _cairo_scaled_glyph_lookup (scaled_font,
|
|
glyph_index,
|
|
CAIRO_SCALED_GLYPH_INFO_METRICS |
|
|
CAIRO_SCALED_GLYPH_INFO_SURFACE,
|
|
pscaled_glyph);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
scaled_glyph = *pscaled_glyph;
|
|
glyph_surface = scaled_glyph->surface;
|
|
already_had_glyph_surface = FALSE;
|
|
} else {
|
|
already_had_glyph_surface = TRUE;
|
|
}
|
|
|
|
if (scaled_font->surface_private == NULL) {
|
|
status = _cairo_xlib_surface_font_init (display, scaled_font);
|
|
if (unlikely (status))
|
|
return status;
|
|
}
|
|
|
|
glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font,
|
|
glyph_surface->format);
|
|
|
|
/* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */
|
|
{
|
|
int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height;
|
|
int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
|
|
: XMaxRequestSize (display->display)) * 4 -
|
|
sz_xRenderAddGlyphsReq -
|
|
sz_xGlyphInfo -
|
|
8;
|
|
if (len >= max_request_size)
|
|
return UNSUPPORTED ("glyph too large for XRequest");
|
|
}
|
|
|
|
/* 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 (glyphset_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;
|
|
}
|
|
|
|
/* 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 != glyphset_info->format) {
|
|
cairo_surface_pattern_t pattern;
|
|
cairo_surface_t *tmp_surface;
|
|
|
|
tmp_surface = cairo_image_surface_create (glyphset_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 = scaled_glyph->x_advance;
|
|
glyph_info.yOff = scaled_glyph->y_advance;
|
|
|
|
data = glyph_surface->data;
|
|
|
|
/* flip formats around */
|
|
switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
|
|
case GLYPHSET_INDEX_A1:
|
|
/* local bitmaps are always stored with bit == byte */
|
|
if (_native_byte_order_lsb() != (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 (_native_byte_order_lsb() != (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, glyphset_info->glyphset,
|
|
&glyph_index, &glyph_info, 1,
|
|
(char *) data,
|
|
glyph_surface->stride * glyph_surface->height);
|
|
|
|
if (data != glyph_surface->data)
|
|
free (data);
|
|
|
|
_cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
|
|
|
|
BAIL:
|
|
if (glyph_surface != scaled_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 (scaled_glyph, scaled_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,
|
|
cairo_xlib_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_operator_t op,
|
|
cairo_xlib_surface_t *src,
|
|
cairo_surface_attributes_t *attributes,
|
|
/* info for this chunk */
|
|
int num_elts,
|
|
int width,
|
|
cairo_xlib_font_glyphset_info_t *glyphset_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 = 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,
|
|
_render_operator (op),
|
|
src->src_picture,
|
|
dst->dst_picture,
|
|
glyphset_info->xrender_format,
|
|
attributes->x_offset + elts[0].xOff,
|
|
attributes->y_offset + elts[0].yOff,
|
|
elts[0].xOff, elts[0].yOff,
|
|
(XGlyphElt8 *) elts, nelt);
|
|
|
|
if (elts != stack_elts)
|
|
free (elts);
|
|
|
|
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_status_t
|
|
_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display,
|
|
cairo_xlib_surface_t *dst,
|
|
cairo_xlib_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_operator_t op,
|
|
cairo_xlib_surface_t *src,
|
|
cairo_surface_attributes_t *attributes,
|
|
int *remaining_glyphs)
|
|
{
|
|
int i;
|
|
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_scaled_glyph_t *scaled_glyph;
|
|
cairo_fixed_t x = 0, y = 0;
|
|
cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
|
|
|
|
unsigned long max_index = 0;
|
|
int width = 1;
|
|
int num_elts = 0;
|
|
int num_out_glyphs = 0;
|
|
|
|
int max_request_size = XMaxRequestSize (display->display) * 4
|
|
- MAX (sz_xRenderCompositeGlyphs8Req,
|
|
MAX(sz_xRenderCompositeGlyphs16Req,
|
|
sz_xRenderCompositeGlyphs32Req));
|
|
int request_size = 0;
|
|
|
|
_cairo_xlib_surface_ensure_dst_picture (display, dst);
|
|
|
|
for (i = 0; i < num_glyphs; i++) {
|
|
int this_x, this_y;
|
|
int old_width;
|
|
|
|
status = _cairo_scaled_glyph_lookup (scaled_font,
|
|
glyphs[i].index,
|
|
CAIRO_SCALED_GLYPH_INFO_METRICS,
|
|
&scaled_glyph);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
this_x = _cairo_lround (glyphs[i].d.x);
|
|
this_y = _cairo_lround (glyphs[i].d.y);
|
|
|
|
/* Glyph skipping:
|
|
*
|
|
* We skip any glyphs that have troublesome coordinates. We want
|
|
* to make sure that (glyph2.x - (glyph1.x + glyph1.width)) fits in
|
|
* a signed 16bit integer, otherwise it will overflow in the render
|
|
* protocol.
|
|
* To ensure this, we'll make sure that (glyph2.x - glyph1.x) fits in
|
|
* a signed 15bit integer. The trivial option would be to allow
|
|
* coordinates -8192..8192, but that's kinda dull. It probably will
|
|
* take a decade or so to get monitors 8192x4096 or something. A
|
|
* negative value of -8192 on the other hand, is absolutely useless.
|
|
* Note that we do want to allow some negative positions. The glyph
|
|
* may start off the screen but part of it make it to the screen.
|
|
* Anyway, we will allow positions in the range -4096..122887. That
|
|
* will buy us a few more years before this stops working.
|
|
*
|
|
* Update: upon seeing weird glyphs, we just return and let fallback
|
|
* code do the job.
|
|
*/
|
|
if (((this_x+4096)|(this_y+4096))&~0x3fffu)
|
|
break;
|
|
|
|
/* Send unsent glyphs to the server */
|
|
if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) {
|
|
status = _cairo_xlib_surface_add_glyph (display,
|
|
scaled_font,
|
|
&scaled_glyph);
|
|
if (unlikely (status)) {
|
|
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
|
|
/* Break so we flush glyphs so far and let fallback code
|
|
* handle the rest */
|
|
break;
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
|
|
if (!glyphset_info)
|
|
glyphset_info = 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 (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
|
|
(this_glyphset_info != glyphset_info)) {
|
|
status = _emit_glyphs_chunk (display, dst, glyphs, i,
|
|
scaled_font, op, src, attributes,
|
|
num_elts, old_width, glyphset_info);
|
|
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_info = 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 + scaled_glyph->x_advance;
|
|
y = this_y + scaled_glyph->y_advance;
|
|
|
|
num_out_glyphs++;
|
|
request_size += width;
|
|
}
|
|
|
|
if (num_elts) {
|
|
status = _emit_glyphs_chunk (display, dst, glyphs, i,
|
|
scaled_font, op, src, attributes,
|
|
num_elts, width, glyphset_info);
|
|
}
|
|
|
|
*remaining_glyphs = num_glyphs - i;
|
|
if (*remaining_glyphs != 0 && status == CAIRO_STATUS_SUCCESS)
|
|
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
|
|
|
return status;
|
|
}
|
|
|
|
static cairo_bool_t
|
|
_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst,
|
|
cairo_scaled_font_t *scaled_font)
|
|
{
|
|
cairo_xlib_surface_font_private_t *font_private;
|
|
|
|
font_private = scaled_font->surface_private;
|
|
if ((scaled_font->surface_backend != NULL &&
|
|
scaled_font->surface_backend != &cairo_xlib_surface_backend) ||
|
|
(font_private != NULL && font_private->device != dst->base.device))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static cairo_int_status_t
|
|
_cairo_xlib_surface_show_glyphs (void *abstract_dst,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *src_pattern,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_scaled_font_t *scaled_font,
|
|
cairo_clip_t *clip,
|
|
int *remaining_glyphs)
|
|
{
|
|
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
|
|
cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst;
|
|
composite_operation_t operation;
|
|
cairo_surface_attributes_t attributes;
|
|
cairo_xlib_surface_t *src = NULL;
|
|
cairo_region_t *clip_region = NULL;
|
|
cairo_xlib_display_t *display;
|
|
|
|
if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst))
|
|
return UNSUPPORTED ("XRender does not support CompositeText");
|
|
|
|
/* Just let unbounded operators go through the fallback code
|
|
* instead of trying to do the fixups here */
|
|
if (! _cairo_operator_bounded_by_mask (op))
|
|
return UNSUPPORTED ("unsupported unbounded op");
|
|
|
|
/* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs --
|
|
* the solid source seems to be multiplied by the glyph mask, and
|
|
* then the entire thing is copied to the destination surface,
|
|
* including the fully transparent "background" of the rectangular
|
|
* glyph surface. */
|
|
if (op == CAIRO_OPERATOR_SOURCE &&
|
|
! CAIRO_SURFACE_RENDER_AT_LEAST(dst, 0, 11))
|
|
{
|
|
return UNSUPPORTED ("known bug in Render");
|
|
}
|
|
|
|
/* We can only use our code if we either have no clip or
|
|
* have a real native clip region set. If we're using
|
|
* fallback clip masking, we have to go through the full
|
|
* fallback path.
|
|
*/
|
|
if (clip != NULL) {
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
|
|
if (status)
|
|
return status;
|
|
}
|
|
|
|
operation = _categorize_composite_operation (dst, op, src_pattern, TRUE);
|
|
if (operation == DO_UNSUPPORTED)
|
|
return UNSUPPORTED ("unsupported op");
|
|
|
|
if (! _cairo_xlib_surface_owns_font (dst, scaled_font))
|
|
return UNSUPPORTED ("unowned font");
|
|
|
|
status = _cairo_xlib_display_acquire (dst->base.device, &display);
|
|
if (unlikely (status))
|
|
return status;
|
|
|
|
X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable));
|
|
|
|
if (clip_region != NULL &&
|
|
cairo_region_num_rectangles (clip_region) == 1)
|
|
{
|
|
cairo_rectangle_int_t glyph_extents;
|
|
const cairo_rectangle_int_t *clip_extents;
|
|
|
|
/* Can we do without the clip?
|
|
* Around 50% of the time the clip is redundant (firefox).
|
|
*/
|
|
_cairo_scaled_font_glyph_approximate_extents (scaled_font,
|
|
glyphs, num_glyphs,
|
|
&glyph_extents);
|
|
|
|
clip_extents = &clip->path->extents;
|
|
if (clip_extents->x <= glyph_extents.x &&
|
|
clip_extents->y <= glyph_extents.y &&
|
|
clip_extents->x + clip_extents->width >= glyph_extents.x + glyph_extents.width &&
|
|
clip_extents->y + clip_extents->height >= glyph_extents.y + glyph_extents.height)
|
|
{
|
|
clip_region = NULL;
|
|
}
|
|
}
|
|
|
|
status = _cairo_xlib_surface_set_clip_region (dst, clip_region);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
|
|
/* After passing all those tests, we're now committed to rendering
|
|
* these glyphs or to fail trying. We first upload any glyphs to
|
|
* the X server that it doesn't have already, then we draw
|
|
* them.
|
|
*/
|
|
|
|
/* PictOpClear doesn't seem to work with CompositeText; it seems to ignore
|
|
* the mask (the glyphs). This code below was executed as a side effect
|
|
* of going through the _clip_and_composite fallback code for old_show_glyphs,
|
|
* so PictOpClear was never used with CompositeText before.
|
|
*/
|
|
if (op == CAIRO_OPERATOR_CLEAR) {
|
|
src_pattern = &_cairo_pattern_white.base;
|
|
op = CAIRO_OPERATOR_DEST_OUT;
|
|
}
|
|
|
|
if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
|
|
status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
|
|
0, 0, 1, 1,
|
|
CAIRO_PATTERN_ACQUIRE_NONE,
|
|
(cairo_surface_t **) &src,
|
|
&attributes);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
} else {
|
|
cairo_rectangle_int_t glyph_extents;
|
|
|
|
status = _cairo_scaled_font_glyph_device_extents (scaled_font,
|
|
glyphs,
|
|
num_glyphs,
|
|
&glyph_extents,
|
|
NULL);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
|
|
if (clip != NULL) {
|
|
if (! _cairo_rectangle_intersect (&glyph_extents,
|
|
_cairo_clip_get_extents (clip)))
|
|
{
|
|
goto BAIL0;
|
|
}
|
|
}
|
|
|
|
status = _cairo_xlib_surface_acquire_pattern_surface (display,
|
|
dst, src_pattern,
|
|
glyph_extents.x,
|
|
glyph_extents.y,
|
|
glyph_extents.width,
|
|
glyph_extents.height,
|
|
&src, &attributes);
|
|
if (unlikely (status))
|
|
goto BAIL0;
|
|
}
|
|
|
|
operation = _recategorize_composite_operation (dst, op, src,
|
|
&attributes, TRUE);
|
|
if (operation == DO_UNSUPPORTED) {
|
|
status = UNSUPPORTED ("unsupported op");
|
|
goto BAIL1;
|
|
}
|
|
|
|
status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0);
|
|
if (unlikely (status))
|
|
goto BAIL1;
|
|
|
|
_cairo_scaled_font_freeze_cache (scaled_font);
|
|
if (_cairo_xlib_surface_owns_font (dst, scaled_font)) {
|
|
status = _cairo_xlib_surface_emit_glyphs (display,
|
|
dst,
|
|
(cairo_xlib_glyph_t *) glyphs,
|
|
num_glyphs,
|
|
scaled_font,
|
|
op,
|
|
src,
|
|
&attributes,
|
|
remaining_glyphs);
|
|
} else {
|
|
status = UNSUPPORTED ("unowned font");
|
|
}
|
|
_cairo_scaled_font_thaw_cache (scaled_font);
|
|
|
|
BAIL1:
|
|
if (src)
|
|
_cairo_pattern_release_surface (src_pattern, &src->base, &attributes);
|
|
BAIL0:
|
|
cairo_device_release (&display->base);
|
|
|
|
return status;
|
|
}
|