mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-28 10:40:29 +01:00
The snapshot takes a reference to the target recording surface in order to enable it for use by multiple treads. In order to balance this, the other two sources of recording surface must also take a reference and for us to release that reference after the replay. Otherwise, we end up with a memory leak: ==1== 1,392 bytes in 3 blocks are definitely lost in loss record 1 of 7 ==1== at 0x4A06BCF: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==1== by 0x4C7042D: _cairo_recording_surface_snapshot (cairo/src/cairo-recording-surface.c:1427) ==1== by 0x4C842BE: _cairo_surface_snapshot_copy_on_write (cairo/src/cairo-surface-snapshot.c:189) ==1== by 0x4C7E7E0: _cairo_surface_detach_snapshot (cairo/src/cairo-surface.c:348) ==1== by 0x4C7E55B: _cairo_surface_detach_snapshots (cairo/src/cairo-surface.c:333) ==1== by 0x4C7E55B: _cairo_surface_flush (cairo/src/cairo-surface.c:1545) ==1== by 0x4C7E6CC: _cairo_surface_finish_snapshots (cairo/src/cairo-surface.c:1017) ==1== by 0x4C7E6CC: cairo_surface_destroy (cairo/src/cairo-surface.c:961) ==1== by 0x4C625A7: cairo_pattern_destroy (cairo/src/cairo-pattern.c:1131) ==1== by 0x4C3FAC6: _cairo_gstate_fini (cairo/src/cairo-gstate.c:225) ==1== by 0x4C3C68C: _cairo_default_context_fini (cairo/src/cairo-default-context.c:75) ==1== by 0x4C3C708: _cairo_default_context_destroy (cairo/src/cairo-default-context.c:93) ==1== by 0x43E576: record_get (cairo/test/record-extend.c:158) ==1== by 0x43E576: record_replay (cairo/test/record-extend.c:173) ==1== by 0x40E22D: cairo_test_for_target (cairo/test/cairo-test.c:929) ==1== by 0x40E22D: _cairo_test_context_run_for_target (cairo/test/cairo-test.c:1532) ==1== by 0x40B6C0: _cairo_test_runner_draw (cairo/test/cairo-test-runner.c:255) ==1== by 0x40B6C0: main (cairo/test/cairo-test-runner.c:937) Reported-by: Massimo Valentini <mvalentini@src.gnome.org> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=87898 Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
1168 lines
35 KiB
C
1168 lines
35 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
|
|
*/
|
|
#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;
|
|
|
|
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;
|
|
proxy->source.picture = XRenderCreatePicture (proxy->source.dpy,
|
|
_cairo_xlib_shm_surface_get_pixmap (src),
|
|
_cairo_xlib_shm_surface_get_xrender_format (src),
|
|
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 */
|