/* -*- 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 * Behdad Esfahbod * Chris Wilson * Karl Tomlinson , Mozilla Corporation */ #include "cairoint.h" #if !CAIRO_HAS_XLIB_XCB_FUNCTIONS #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-inline.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-backend-private.h" #include "cairo-surface-offset-private.h" #include "cairo-surface-observer-private.h" #include "cairo-surface-snapshot-inline.h" #include "cairo-surface-subsurface-inline.h" #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ static cairo_xlib_surface_t * unwrap_source (const cairo_surface_pattern_t *pattern) { cairo_rectangle_int_t limits; return (cairo_xlib_surface_t *)_cairo_pattern_get_source (pattern, &limits); } static cairo_status_t _cairo_xlib_source_finish (void *abstract_surface) { cairo_xlib_source_t *source = abstract_surface; XRenderFreePicture (source->dpy, source->picture); if (source->pixmap) XFreePixmap (source->dpy, source->pixmap); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xlib_source_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_source_finish, NULL, /* read-only wrapper */ }; static cairo_status_t _cairo_xlib_proxy_finish (void *abstract_surface) { cairo_xlib_proxy_t *proxy = abstract_surface; _cairo_xlib_shm_surface_mark_active (proxy->owner); XRenderFreePicture (proxy->source.dpy, proxy->source.picture); if (proxy->source.pixmap) XFreePixmap (proxy->source.dpy, proxy->source.pixmap); cairo_surface_destroy (proxy->owner); return CAIRO_STATUS_SUCCESS; } static const cairo_surface_backend_t cairo_xlib_proxy_backend = { CAIRO_SURFACE_TYPE_XLIB, _cairo_xlib_proxy_finish, NULL, /* read-only wrapper */ }; static cairo_surface_t * source (cairo_xlib_surface_t *dst, Picture picture, Pixmap pixmap) { cairo_xlib_source_t *source; if (picture == None) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); source = malloc (sizeof (*source)); if (unlikely (source == NULL)) { XRenderFreePicture (dst->display->display, picture); if (pixmap) XFreePixmap (dst->display->display, pixmap); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } _cairo_surface_init (&source->base, &cairo_xlib_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); /* The source exists only within an operation */ source->picture = picture; source->pixmap = pixmap; source->dpy = dst->display->display; return &source->base; } static uint32_t hars_petruska_f54_1_random (void) { #define rol(x,k) ((x << k) | (x >> (32-k))) static uint32_t x; return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849; #undef rol } static const XTransform identity = { { { 1 << 16, 0x00000, 0x00000 }, { 0x00000, 1 << 16, 0x00000 }, { 0x00000, 0x00000, 1 << 16 }, } }; static cairo_bool_t picture_set_matrix (cairo_xlib_display_t *display, Picture picture, const cairo_matrix_t *matrix, cairo_filter_t filter, double xc, double yc, int *x_offset, int *y_offset) { XTransform xtransform; pixman_transform_t *pixman_transform; cairo_int_status_t status; /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ pixman_transform = (pixman_transform_t *) &xtransform; status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, pixman_transform, x_offset, y_offset); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return TRUE; if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) return FALSE; if (memcmp (&xtransform, &identity, sizeof (XTransform)) == 0) return TRUE; /* a late check in case we perturb the matrix too far */ if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) return FALSE; XRenderSetPictureTransform (display->display, picture, &xtransform); return TRUE; } static cairo_status_t picture_set_filter (Display *dpy, Picture picture, cairo_filter_t filter) { const char *render_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 (dpy, picture, (char *) render_filter, NULL, 0); return CAIRO_STATUS_SUCCESS; } static int extend_to_repeat (cairo_extend_t extend) { switch (extend) { default: ASSERT_NOT_REACHED; case CAIRO_EXTEND_NONE: return RepeatNone; case CAIRO_EXTEND_REPEAT: return RepeatNormal; case CAIRO_EXTEND_REFLECT: return RepeatReflect; case CAIRO_EXTEND_PAD: return RepeatPad; } } static cairo_bool_t picture_set_properties (cairo_xlib_display_t *display, Picture picture, const cairo_pattern_t *pattern, const cairo_matrix_t *matrix, const cairo_rectangle_int_t *extents, int *x_off, int *y_off) { XRenderPictureAttributes pa; int mask = 0; if (! picture_set_matrix (display, picture, matrix, pattern->filter, extents->x + extents->width / 2, extents->y + extents->height / 2, x_off, y_off)) return FALSE; picture_set_filter (display->display, picture, pattern->filter); if (pattern->has_component_alpha) { pa.component_alpha = 1; mask |= CPComponentAlpha; } if (pattern->extend != CAIRO_EXTEND_NONE) { pa.repeat = extend_to_repeat (pattern->extend); mask |= CPRepeat; } if (mask) XRenderChangePicture (display->display, picture, mask, &pa); return TRUE; } static cairo_surface_t * render_pattern (cairo_xlib_surface_t *dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, int *src_x, int *src_y) { Display *dpy = dst->display->display; cairo_xlib_surface_t *src; cairo_image_surface_t *image; cairo_status_t status; cairo_rectangle_int_t map_extents; src = (cairo_xlib_surface_t *) _cairo_surface_create_scratch (&dst->base, is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA, extents->width, extents->height, NULL); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } map_extents = *extents; map_extents.x = map_extents.y = 0; image = _cairo_surface_map_to_image (&src->base, &map_extents); status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y, CAIRO_OPERATOR_SOURCE, pattern, NULL); status = _cairo_surface_unmap_image (&src->base, image); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } status = _cairo_xlib_surface_put_shm (src); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } src->picture = XRenderCreatePicture (dpy, src->drawable, src->xrender_format, 0, NULL); *src_x = -extents->x; *src_y = -extents->y; return &src->base; } static cairo_surface_t * gradient_source (cairo_xlib_surface_t *dst, const cairo_gradient_pattern_t *gradient, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, int *src_x, int *src_y) { cairo_xlib_display_t *display = dst->display; cairo_matrix_t matrix = gradient->base.matrix; char buf[CAIRO_STACK_BUFFER_SIZE]; cairo_circle_double_t extremes[2]; XFixed *stops; XRenderColor *colors; Picture picture; unsigned int i, n_stops; /* The RENDER specification says that the inner circle has * to be completely contained inside the outer one. */ if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL && ! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) gradient)) return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); assert (gradient->n_stops > 0); n_stops = MAX (gradient->n_stops, 2); if (n_stops < sizeof (buf) / (sizeof (XFixed) + sizeof (XRenderColor))) { stops = (XFixed *) buf; } else { stops = _cairo_malloc_ab (n_stops, sizeof (XFixed) + sizeof (XRenderColor)); if (unlikely (stops == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } colors = (XRenderColor *) (stops + 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; } /* RENDER does not support gradients with less than 2 * stops. If a gradient has only a single stop, duplicate * it to make RENDER happy. */ if (gradient->n_stops == 1) { stops[1] = _cairo_fixed_16_16_from_double (gradient->stops[0].offset); colors[1].red = gradient->stops[0].color.red_short; colors[1].green = gradient->stops[0].color.green_short; colors[1].blue = gradient->stops[0].color.blue_short; colors[1].alpha = gradient->stops[0].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 _cairo_gradient_pattern_fit_to_range (gradient, PIXMAN_MAX_INT >> 1, &matrix, extremes); if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { XLinearGradient grad; grad.p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); grad.p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); grad.p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); grad.p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); picture = XRenderCreateLinearGradient (display->display, &grad, stops, colors, n_stops); } else { XRadialGradient grad; grad.inner.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); grad.inner.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); grad.inner.radius = _cairo_fixed_16_16_from_double (extremes[0].radius); grad.outer.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); grad.outer.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); grad.outer.radius = _cairo_fixed_16_16_from_double (extremes[1].radius); picture = XRenderCreateRadialGradient (display->display, &grad, stops, colors, n_stops); } if (stops != (XFixed *) buf) free (stops); *src_x = *src_y = 0; if (! picture_set_properties (display, picture, &gradient->base, &gradient->base.matrix, extents, src_x, src_y)) { XRenderFreePicture (display->display, picture); return render_pattern (dst, &gradient->base, is_mask, extents, src_x, src_y); } return source (dst, picture, None); } static cairo_surface_t * color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { Display *dpy = dst->display->display; XRenderColor xcolor; Picture picture; Pixmap pixmap = None; xcolor.red = color->red_short; xcolor.green = color->green_short; xcolor.blue = color->blue_short; xcolor.alpha = color->alpha_short; if (CAIRO_RENDER_HAS_GRADIENTS(dst->display)) { picture = XRenderCreateSolidFill (dpy, &xcolor); } else { XRenderPictureAttributes pa; int mask = 0; pa.repeat = RepeatNormal; mask |= CPRepeat; pixmap = XCreatePixmap (dpy, dst->drawable, 1, 1, 32); picture = XRenderCreatePicture (dpy, pixmap, _cairo_xlib_display_get_xrender_format (dst->display, CAIRO_FORMAT_ARGB32), mask, &pa); if (CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) { XRectangle r = { 0, 0, 1, 1}; XRenderFillRectangles (dpy, PictOpSrc, picture, &xcolor, &r, 1); } else { XGCValues gcv; GC gc; gc = _cairo_xlib_screen_get_gc (dst->display, dst->screen, 32, pixmap); if (unlikely (gc == NULL)) { XFreePixmap (dpy, pixmap); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } gcv.foreground = 0; gcv.foreground |= color->alpha_short >> 8 << 24; gcv.foreground |= color->red_short >> 8 << 16; gcv.foreground |= color->green_short >> 8 << 8; gcv.foreground |= color->blue_short >> 8 << 0; gcv.fill_style = FillSolid; XChangeGC (dpy, gc, GCFillStyle | GCForeground, &gcv); XFillRectangle (dpy, pixmap, gc, 0, 0, 1, 1); _cairo_xlib_screen_put_gc (dst->display, dst->screen, 32, gc); } } return source (dst, picture, pixmap); } static cairo_surface_t * alpha_source (cairo_xlib_surface_t *dst, uint8_t alpha) { cairo_xlib_display_t *display = dst->display; if (display->alpha[alpha] == NULL) { cairo_color_t color; color.red_short = color.green_short = color.blue_short = 0; color.alpha_short = alpha << 8 | alpha; display->alpha[alpha] = color_source (dst, &color); } return cairo_surface_reference (display->alpha[alpha]); } static cairo_surface_t * white_source (cairo_xlib_surface_t *dst) { cairo_xlib_display_t *display = dst->display; if (display->white == NULL) display->white = color_source (dst, CAIRO_COLOR_WHITE); return cairo_surface_reference (display->white); } static cairo_surface_t * opaque_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { cairo_xlib_display_t *display = dst->display; uint32_t pixel = 0xff000000 | color->red_short >> 8 << 16 | color->green_short >> 8 << 8 | color->blue_short >> 8 << 0; int i; if (display->last_solid_cache[0].color == pixel) return cairo_surface_reference (display->solid[display->last_solid_cache[0].index]); for (i = 0; i < 16; i++) { if (display->solid_cache[i] == pixel) goto done; } i = hars_petruska_f54_1_random () % 16; cairo_surface_destroy (display->solid[i]); display->solid[i] = color_source (dst, color); display->solid_cache[i] = pixel; done: display->last_solid_cache[0].color = pixel; display->last_solid_cache[0].index = i; return cairo_surface_reference (display->solid[i]); } static cairo_surface_t * transparent_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { cairo_xlib_display_t *display = dst->display; uint32_t pixel = color->alpha_short >> 8 << 24 | color->red_short >> 8 << 16 | color->green_short >> 8 << 8 | color->blue_short >> 8 << 0; int i; if (display->last_solid_cache[1].color == pixel) { assert (display->solid[display->last_solid_cache[1].index]); return cairo_surface_reference (display->solid[display->last_solid_cache[1].index]); } for (i = 16; i < 32; i++) { if (display->solid_cache[i] == pixel) goto done; } i = 16 + (hars_petruska_f54_1_random () % 16); cairo_surface_destroy (display->solid[i]); display->solid[i] = color_source (dst, color); display->solid_cache[i] = pixel; done: display->last_solid_cache[1].color = pixel; display->last_solid_cache[1].index = i; assert (display->solid[i]); return cairo_surface_reference (display->solid[i]); } static cairo_surface_t * solid_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) { if ((color->red_short | color->green_short | color->blue_short) <= 0xff) return alpha_source (dst, color->alpha_short >> 8); if (CAIRO_ALPHA_SHORT_IS_OPAQUE (color->alpha_short)) { if (color->red_short >= 0xff00 && color->green_short >= 0xff00 && color->blue_short >= 0xff00) return white_source (dst); return opaque_source (dst, color); } else return transparent_source (dst, color); } static cairo_xlib_source_t *init_source (cairo_xlib_surface_t *dst, cairo_xlib_surface_t *src) { Display *dpy = dst->display->display; cairo_xlib_source_t *source = &src->embedded_source; /* As these are frequent and meant to be fast, we track pictures for * native surface and minimise update requests. */ if (source->picture == None) { XRenderPictureAttributes pa; _cairo_surface_init (&source->base, &cairo_xlib_source_backend, NULL, /* device */ CAIRO_CONTENT_COLOR_ALPHA); pa.subwindow_mode = IncludeInferiors; source->picture = XRenderCreatePicture (dpy, src->drawable, src->xrender_format, CPSubwindowMode, &pa); source->has_component_alpha = 0; source->has_matrix = 0; source->filter = CAIRO_FILTER_NEAREST; source->extend = CAIRO_EXTEND_NONE; } return (cairo_xlib_source_t *) cairo_surface_reference (&source->base); } static cairo_surface_t * embedded_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, const cairo_rectangle_int_t *extents, int *src_x, int *src_y, cairo_xlib_source_t *source) { Display *dpy = dst->display->display; cairo_int_status_t status; XTransform xtransform; XRenderPictureAttributes pa; unsigned mask = 0; status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, pattern->base.filter, extents->x + extents->width / 2, extents->y + extents->height / 2, (pixman_transform_t *)&xtransform, src_x, src_y); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { if (source->has_matrix) { source->has_matrix = 0; memcpy (&xtransform, &identity, sizeof (identity)); status = CAIRO_INT_STATUS_SUCCESS; } } else source->has_matrix = 1; if (status == CAIRO_INT_STATUS_SUCCESS) XRenderSetPictureTransform (dpy, source->picture, &xtransform); if (source->filter != pattern->base.filter) { picture_set_filter (dpy, source->picture, pattern->base.filter); source->filter = pattern->base.filter; } if (source->has_component_alpha != pattern->base.has_component_alpha) { pa.component_alpha = pattern->base.has_component_alpha; mask |= CPComponentAlpha; source->has_component_alpha = pattern->base.has_component_alpha; } if (source->extend != pattern->base.extend) { pa.repeat = extend_to_repeat (pattern->base.extend); mask |= CPRepeat; source->extend = pattern->base.extend; } if (mask) XRenderChangePicture (dpy, source->picture, mask, &pa); return &source->base; } static cairo_surface_t * subsurface_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_surface_subsurface_t *sub; cairo_xlib_surface_t *src; cairo_xlib_source_t *source; Display *dpy = dst->display->display; cairo_int_status_t status; cairo_surface_pattern_t local_pattern; XTransform xtransform; XRenderPictureAttributes pa; unsigned mask = 0; sub = (cairo_surface_subsurface_t *) pattern->surface; if (sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= sub->extents.width && sample->y + sample->height <= sub->extents.height) { src = (cairo_xlib_surface_t *) sub->target; status = _cairo_surface_flush (&src->base, 0); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (pattern->base.filter == CAIRO_FILTER_NEAREST && _cairo_matrix_is_translation (&pattern->base.matrix)) { *src_x += pattern->base.matrix.x0 + sub->extents.x; *src_y += pattern->base.matrix.y0 + sub->extents.y; _cairo_xlib_surface_ensure_picture (src); return cairo_surface_reference (&src->base); } else { cairo_surface_pattern_t local_pattern = *pattern; local_pattern.base.matrix.x0 += sub->extents.x; local_pattern.base.matrix.y0 += sub->extents.y; local_pattern.base.extend = CAIRO_EXTEND_NONE; return embedded_source (dst, &local_pattern, extents, src_x, src_y, init_source (dst, src)); } } if (sub->snapshot && sub->snapshot->type == CAIRO_SURFACE_TYPE_XLIB) { src = (cairo_xlib_surface_t *) cairo_surface_reference (sub->snapshot); source = &src->embedded_source; } else { src = (cairo_xlib_surface_t *) _cairo_surface_create_scratch (&dst->base, sub->base.content, sub->extents.width, sub->extents.height, NULL); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } _cairo_pattern_init_for_surface (&local_pattern, sub->target); cairo_matrix_init_translate (&local_pattern.base.matrix, sub->extents.x, sub->extents.y); local_pattern.base.filter = CAIRO_FILTER_NEAREST; status = _cairo_surface_paint (&src->base, CAIRO_OPERATOR_SOURCE, &local_pattern.base, NULL); _cairo_pattern_fini (&local_pattern.base); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } _cairo_xlib_surface_ensure_picture (src); _cairo_surface_subsurface_set_snapshot (&sub->base, &src->base); source = &src->embedded_source; source->has_component_alpha = 0; source->has_matrix = 0; source->filter = CAIRO_FILTER_NEAREST; source->extend = CAIRO_EXTEND_NONE; } status = _cairo_matrix_to_pixman_matrix_offset (&pattern->base.matrix, pattern->base.filter, extents->x + extents->width / 2, extents->y + extents->height / 2, (pixman_transform_t *)&xtransform, src_x, src_y); if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { if (source->has_matrix) { source->has_matrix = 0; memcpy (&xtransform, &identity, sizeof (identity)); status = CAIRO_INT_STATUS_SUCCESS; } } else source->has_matrix = 1; if (status == CAIRO_INT_STATUS_SUCCESS) XRenderSetPictureTransform (dpy, src->picture, &xtransform); if (source->filter != pattern->base.filter) { picture_set_filter (dpy, src->picture, pattern->base.filter); source->filter = pattern->base.filter; } if (source->has_component_alpha != pattern->base.has_component_alpha) { pa.component_alpha = pattern->base.has_component_alpha; mask |= CPComponentAlpha; source->has_component_alpha = pattern->base.has_component_alpha; } if (source->extend != pattern->base.extend) { pa.repeat = extend_to_repeat (pattern->base.extend); mask |= CPRepeat; source->extend = pattern->base.extend; } if (mask) XRenderChangePicture (dpy, src->picture, mask, &pa); return &src->base; } static cairo_surface_t * native_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *src; cairo_int_status_t status; if (_cairo_surface_is_subsurface (pattern->surface)) return subsurface_source (dst, pattern, is_mask, extents, sample, src_x, src_y); src = unwrap_source (pattern); status = _cairo_surface_flush (&src->base, 0); if (unlikely (status)) return _cairo_surface_create_in_error (status); if (pattern->base.filter == CAIRO_FILTER_NEAREST && sample->x >= 0 && sample->y >= 0 && sample->x + sample->width <= src->width && sample->y + sample->height <= src->height && _cairo_matrix_is_translation (&pattern->base.matrix)) { *src_x += pattern->base.matrix.x0; *src_y += pattern->base.matrix.y0; _cairo_xlib_surface_ensure_picture (src); return cairo_surface_reference (&src->base); } return embedded_source (dst, pattern, extents, src_x, src_y, init_source (dst, src)); } static cairo_surface_t * recording_pattern_get_surface (const cairo_pattern_t *pattern) { cairo_surface_t *surface; surface = ((const cairo_surface_pattern_t *) pattern)->surface; if (_cairo_surface_is_paginated (surface)) return cairo_surface_reference (_cairo_paginated_surface_get_recording (surface)); if (_cairo_surface_is_snapshot (surface)) return _cairo_surface_snapshot_get_target (surface); return cairo_surface_reference (surface); } static cairo_surface_t * record_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *src; cairo_surface_t *recording; cairo_matrix_t matrix, m; cairo_status_t status; cairo_rectangle_int_t upload, limit; upload = *sample; if (_cairo_surface_get_extents (pattern->surface, &limit) && ! _cairo_rectangle_intersect (&upload, &limit)) { if (pattern->base.extend == CAIRO_EXTEND_NONE) return alpha_source (dst, 0); upload = limit; } src = (cairo_xlib_surface_t *) _cairo_surface_create_scratch (&dst->base, pattern->surface->content, upload.width, upload.height, NULL); if (src->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); } cairo_matrix_init_translate (&matrix, upload.x, upload.y); recording = recording_pattern_get_surface (&pattern->base), status = _cairo_recording_surface_replay_with_clip (recording, &matrix, &src->base, NULL); cairo_surface_destroy (recording); if (unlikely (status)) { cairo_surface_destroy (&src->base); return _cairo_surface_create_in_error (status); } matrix = pattern->base.matrix; if (upload.x | upload.y) { cairo_matrix_init_translate (&m, -upload.x, -upload.y); cairo_matrix_multiply (&matrix, &matrix, &m); } _cairo_xlib_surface_ensure_picture (src); if (! picture_set_properties (src->display, src->picture, &pattern->base, &matrix, extents, src_x, src_y)) { cairo_surface_destroy (&src->base); return render_pattern (dst, &pattern->base, is_mask, extents, src_x, src_y); } return &src->base; } static cairo_surface_t * surface_source (cairo_xlib_surface_t *dst, const cairo_surface_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_surface_t *src; cairo_xlib_surface_t *xsrc; cairo_surface_pattern_t local_pattern; cairo_status_t status; cairo_rectangle_int_t upload, limit; XRenderPictFormat *format = NULL; src = pattern->surface; if (src->type == CAIRO_SURFACE_TYPE_IMAGE && src->device == dst->base.device && _cairo_xlib_shm_surface_get_pixmap (src)) { cairo_xlib_proxy_t *proxy; proxy = malloc (sizeof(*proxy)); if (unlikely (proxy == NULL)) return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); _cairo_surface_init (&proxy->source.base, &cairo_xlib_proxy_backend, dst->base.device, src->content); proxy->source.dpy = dst->display->display; format = _cairo_xlib_shm_surface_get_xrender_format(src); if (format == NULL) { free (proxy); return _cairo_surface_create_in_error (CAIRO_STATUS_NULL_POINTER); } proxy->source.picture = XRenderCreatePicture (proxy->source.dpy, _cairo_xlib_shm_surface_get_pixmap (src), format, 0, NULL); proxy->source.pixmap = None; proxy->source.has_component_alpha = 0; proxy->source.has_matrix = 0; proxy->source.filter = CAIRO_FILTER_NEAREST; proxy->source.extend = CAIRO_EXTEND_NONE; proxy->owner = cairo_surface_reference (src); return embedded_source (dst, pattern, extents, src_x, src_y, &proxy->source); } upload = *sample; if (_cairo_surface_get_extents (pattern->surface, &limit)) { if (pattern->base.extend == CAIRO_EXTEND_NONE) { if (! _cairo_rectangle_intersect (&upload, &limit)) return alpha_source (dst, 0); } else if (pattern->base.extend == CAIRO_EXTEND_PAD) { if (! _cairo_rectangle_intersect (&upload, &limit)) upload = limit; } else { if (upload.x < limit.x || upload.x + upload.width > limit.x + limit.width || upload.y < limit.y || upload.y + upload.height > limit.y + limit.height) { upload = limit; } } } xsrc = (cairo_xlib_surface_t *) _cairo_surface_create_scratch (&dst->base, src->content, upload.width, upload.height, NULL); if (xsrc->base.type != CAIRO_SURFACE_TYPE_XLIB) { cairo_surface_destroy (src); cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); } if (_cairo_surface_is_image (src)) { status = _cairo_xlib_surface_draw_image (xsrc, (cairo_image_surface_t *)src, upload.x, upload.y, upload.width, upload.height, 0, 0); } else { cairo_image_surface_t *image; cairo_rectangle_int_t map_extents = { 0,0, upload.width,upload.height }; image = _cairo_surface_map_to_image (&xsrc->base, &map_extents); _cairo_pattern_init_for_surface (&local_pattern, pattern->surface); cairo_matrix_init_translate (&local_pattern.base.matrix, upload.x, upload.y); status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, &local_pattern.base, NULL); _cairo_pattern_fini (&local_pattern.base); status = _cairo_surface_unmap_image (&xsrc->base, image); if (unlikely (status)) { cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (status); } status = _cairo_xlib_surface_put_shm (xsrc); if (unlikely (status)) { cairo_surface_destroy (&xsrc->base); return _cairo_surface_create_in_error (status); } } _cairo_pattern_init_static_copy (&local_pattern.base, &pattern->base); if (upload.x | upload.y) { cairo_matrix_t m; cairo_matrix_init_translate (&m, -upload.x, -upload.y); cairo_matrix_multiply (&local_pattern.base.matrix, &local_pattern.base.matrix, &m); } *src_x = *src_y = 0; _cairo_xlib_surface_ensure_picture (xsrc); if (! picture_set_properties (xsrc->display, xsrc->picture, &local_pattern.base, &local_pattern.base.matrix, extents, src_x, src_y)) { cairo_surface_destroy (&xsrc->base); return render_pattern (dst, &pattern->base, is_mask, extents, src_x, src_y); } return &xsrc->base; } static cairo_bool_t pattern_is_supported (cairo_xlib_display_t *display, const cairo_pattern_t *pattern) { if (pattern->type == CAIRO_PATTERN_TYPE_MESH) return FALSE; if (display->buggy_pad_reflect) { if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_PAD) return FALSE; } if (display->buggy_gradients) { if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) return FALSE; } switch (pattern->filter) { case CAIRO_FILTER_FAST: case CAIRO_FILTER_NEAREST: return CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display) || _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL); case CAIRO_FILTER_GOOD: return CAIRO_RENDER_HAS_FILTER_GOOD (display); case CAIRO_FILTER_BEST: return CAIRO_RENDER_HAS_FILTER_BEST (display); case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: default: return CAIRO_RENDER_HAS_FILTERS (display); } } cairo_surface_t * _cairo_xlib_source_create_for_pattern (cairo_surface_t *_dst, const cairo_pattern_t *pattern, cairo_bool_t is_mask, const cairo_rectangle_int_t *extents, const cairo_rectangle_int_t *sample, int *src_x, int *src_y) { cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)_dst; *src_x = *src_y = 0; if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) { if (pattern == NULL) pattern = &_cairo_pattern_white.base; return solid_source (dst, &((cairo_solid_pattern_t *)pattern)->color); } if (pattern_is_supported (dst->display, pattern)) { if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t *)pattern; if (spattern->surface->type == CAIRO_SURFACE_TYPE_XLIB && _cairo_xlib_surface_same_screen (dst, unwrap_source (spattern))) return native_source (dst, spattern, is_mask, extents, sample, src_x, src_y); if (spattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) return record_source (dst, spattern, is_mask, extents, sample, src_x, src_y); return surface_source (dst, spattern, is_mask, extents, sample, src_x, src_y); } if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR || pattern->type == CAIRO_PATTERN_TYPE_RADIAL) { cairo_gradient_pattern_t *gpattern = (cairo_gradient_pattern_t *)pattern; return gradient_source (dst, gpattern, is_mask, extents, src_x, src_y); } } return render_pattern (dst, pattern, is_mask, extents, src_x, src_y); } #endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */