Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 3945 → Rev 3959

/programs/develop/libraries/cairo/BUGS
File deleted
/programs/develop/libraries/cairo/COPYING
0,0 → 1,33
Cairo is free software.
 
Every source file in the implementation[*] of cairo is available to be
redistributed and/or modified under the terms of either the GNU Lesser
General Public License (LGPL) version 2.1 or the Mozilla Public
License (MPL) version 1.1. Some files are available under more
liberal terms, but we believe that in all cases, each file may be used
under either the LGPL or the MPL.
 
See the following files in this directory for the precise terms and
conditions of either license:
 
COPYING-LGPL-2.1
COPYING-MPL-1.1
 
Please see each file in the implementation for copyright and licensing
information, (in the opening comment of each file).
 
[*] The implementation of cairo is contained entirely within the "src"
directory of the cairo source distribution. There are other components
of the cairo source distribution (such as the "test", "util", and "perf")
that are auxiliary to the library itself. None of the source code in these
directories contributes to a build of the cairo library itself, (libcairo.so
or cairo.dll or similar).
 
These auxiliary components are also free software, but may be under
different license terms than cairo itself. For example, most of the
test cases in the perf and test directories are made available under
an MIT license to simplify any use of this code for reference purposes
in using cairo itself. Other files might be available under the GNU
General Public License (GPL), for example. Again, please see the COPYING
file under each directory and the opening comment of each file for copyright
and licensing information.
/programs/develop/libraries/cairo/Makefile
1,23 → 1,24
 
LIBRARY = cairo
LIBRARY = cairo2
 
CC = gcc
CFLAGS = -c -O2 -ffast-math -Wall -Winline -Wno-attributes -fomit-frame-pointer
 
CFLAGS = -c -O2 -fomit-frame-pointer -ffast-math
LD = ld
LDFLAGS = -shared -s -nostdlib -Map map -T ../newlib/dll.lds --entry _DllStartup --image-base=0 --output-def $(LIBRARY).orig.def --out-implib $(LIBRARY).dll.a
 
LDIMPORT:= -nostdlib --out-implib libcairoimp.a --exclude-libs libamz.a
STRIP = $(PREFIX)strip
 
LDFLAGS:= -shared -s -T ../newlib/dll.lds --image-base 0
DEFINES = -U__WIN32__ -U_Win32 -U_WIN32 -U__MINGW32__ -U_MSC_VER -DHAVE_CONFIG_H=1 -DCAIRO_NO_MUTEX=1
 
DEFINES = -DHAVE_CONFIG_H -DCAIRO_NO_MUTEX -U_WIN32 -U_MSC_VER -U__WIN32__
INCLUDES = -I. -I../newlib/include -I../pixman -I../zlib -I../libpng -I../freetype/include
 
INCLUDES = -I../newlib/include -I../pixman -I../zlib -I../libpng
 
LIBPATH:= -L../newlib -L../pixman -L../libpng
 
LIBS:= -lamz -lgcc -lcimp -lpiximp -lpngimp
LIBS:= -ldll -lgcc -lfreetype.dll -lz.dll -lpixman-1.dll -lpng.dll -lc.dll
 
CAIRO_SOURCES = \
cairo.c \
cairo-analysis-surface.c \
cairo-arc.c \
cairo-array.c \
29,64 → 30,106
cairo-bentley-ottmann-rectilinear.c \
cairo-botor-scan-converter.c \
cairo-boxes.c \
cairo.c \
cairo-boxes-intersect.c \
cairo-cache.c \
cairo-cff-subset.c \
cairo-clip.c \
cairo-clip-boxes.c \
cairo-clip-polygon.c \
cairo-clip-region.c \
cairo-clip-surface.c \
cairo-clip-tor-scan-converter.c \
cairo-color.c \
cairo-composite-rectangles.c \
cairo-compositor.c \
cairo-contour.c \
cairo-damage.c \
cairo-debug.c \
cairo-default-context.c \
cairo-deflate-stream.c \
cairo-device.c \
cairo-error.c \
cairo-fallback-compositor.c \
cairo-fixed.c \
cairo-font-face.c \
cairo-font-face-twin.c \
cairo-font-face-twin-data.c \
cairo-font-options.c \
cairo-freed-pool.c \
cairo-freelist.c \
cairo-freed-pool.c \
cairo-ft-font.c \
cairo-gstate.c \
cairo-hash.c \
cairo-hull.c \
cairo-image-compositor.c \
cairo-image-info.c \
cairo-image-source.c \
cairo-image-surface.c \
cairo-lzw.c \
cairo-mask-compositor.c \
cairo-matrix.c \
cairo-recording-surface.c \
cairo-mempool.c \
cairo-mesh-pattern-rasterizer.c \
cairo-misc.c \
cairo-mono-scan-converter.c \
cairo-mutex.c \
cairo-no-compositor.c \
cairo-observer.c \
cairo-output-stream.c \
cairo-paginated-surface.c \
cairo-path.c \
cairo-path-bounds.c \
cairo-path.c \
cairo-path-fill.c \
cairo-path-fixed.c \
cairo-path-in-fill.c \
cairo-path-stroke.c \
cairo-path-stroke-boxes.c \
cairo-path-stroke-polygon.c \
cairo-path-stroke-traps.c \
cairo-path-stroke-tristrip.c \
cairo-pattern.c \
cairo-pdf-operators.c \
cairo-pdf-shading.c \
cairo-pen.c \
cairo-png.c \
cairo-polygon.c \
cairo-polygon-intersect.c \
cairo-polygon-reduce.c \
cairo-raster-source-pattern.c \
cairo-recording-surface.c \
cairo-rectangle.c \
cairo-rectangular-scan-converter.c \
cairo-region.c \
cairo-rtree.c \
cairo-scaled-font.c \
cairo-scaled-font-subsets.c \
cairo-script-surface.c \
cairo-shape-mask-compositor.c \
cairo-slope.c \
cairo-spans.c \
cairo-spans-compositor.c \
cairo-spline.c \
cairo-stroke-dash.c \
cairo-stroke-style.c \
cairo-surface.c \
cairo-surface-clipper.c \
cairo-surface-fallback.c \
cairo-surface-clipper.c \
cairo-surface-offset.c \
cairo-surface-snapshot.c \
cairo-surface-subsurface.c \
cairo-surface-wrapper.c \
cairo-svg-surface.c \
cairo-tor22-scan-converter.c \
cairo-tor-scan-converter.c \
cairo-toy-font-face.c \
cairo-traps.c \
cairo-traps-compositor.c \
cairo-tristrip.c \
cairo-truetype-subset.c \
cairo-type1-fallback.c \
cairo-type1-glyph-names.c \
cairo-type1-subset.c \
cairo-type3-glyph-surface.c \
cairo-unicode.c \
cairo-user-font.c \
cairo-version.c \
94,18 → 137,8
$(NULL)
 
CAIRO_FONT = \
cairo-cff-subset.c \
cairo-scaled-font-subsets.c \
cairo-truetype-subset.c \
cairo-type1-fallback.c \
cairo-type1-subset.c \
cairo-type3-glyph-surface.c \
$(NULL)
SOURCES= $(CAIRO_SOURCES)
 
SOURCES= $(CAIRO_SOURCES) $(CAIRO_FONT)
 
OBJECTS = $(patsubst %.c, src/%.o, $(SOURCES))
 
 
112,7 → 145,7
# targets
 
 
all:$(LIBRARY).a $(LIBRARY).dll
all:$(LIBRARY).dll
 
 
$(LIBRARY).a: $(OBJECTS) Makefile
120,9 → 153,13
 
 
$(LIBRARY).dll: $(OBJECTS) Makefile
ld $(LDFLAGS) $(LDIMPORT) $(LIBPATH) -o $@ $(OBJECTS) $(LIBS)
$(LD) $(LDFLAGS) $(LIBPATH) -o $@ $(OBJECTS) $(LIBS)
$(STRIP) $@
sed -e "s/ @[^ ]*//" $(LIBRARY).orig.def > $(LIBRARY).def
sed -f ../newlib/cmd1.sed $(LIBRARY).def > mem
sed -f ../newlib/cmd2.sed mem >$(LIBRARY).inc
 
%.o : %.c Makefile
%.o : %.c
$(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o $@ $<
 
clean:
/programs/develop/libraries/cairo/cairo-version.h
2,7 → 2,7
#define CAIRO_VERSION_H
 
#define CAIRO_VERSION_MAJOR 1
#define CAIRO_VERSION_MINOR 10
#define CAIRO_VERSION_MICRO 2
#define CAIRO_VERSION_MINOR 12
#define CAIRO_VERSION_MICRO 16
 
#endif
/programs/develop/libraries/cairo/config.h
12,7 → 12,7
/* #undef CAIRO_CAN_TEST_PDF_SURFACE */
 
/* Define to 1 if the PS backend can be tested (needs ghostscript) */
/* #undef CAIRO_CAN_TEST_PS_SURFACE */
//#define CAIRO_CAN_TEST_PS_SURFACE 1
 
/* Define to 1 if the SVG backend can be tested */
/* #undef CAIRO_CAN_TEST_SVG_SURFACE */
24,7 → 24,8
/* Define to 1 to enable cairo's cairo-script-interpreter feature */
//#define CAIRO_HAS_INTERPRETER 1
 
#define CAIRO_NO_MUTEX 1
/* Define to 1 to enable cairo's cairo-perf-utils feature */
/* #undef CAIRO_HAS_PERF_UTILS */
 
/* Define to 1 to enable cairo's pthread feature */
/* #undef CAIRO_HAS_PTHREAD */
46,12 → 47,15
 
/* Define to 1 to disable certain code paths that rely heavily on double
precision floating-point calculation */
/* #undef DISABLE_SOME_FLOATING_POINT */
#define DISABLE_SOME_FLOATING_POINT 1
 
/* Define to 1 if your system stores words within floats with the most
significant word first */
/* #undef FLOAT_WORDS_BIGENDIAN */
 
/* Enable pixman glyph cache */
#define HAS_PIXMAN_GLYPHS 1
 
/* Define to 1 if you have the `alarm' function. */
//#define HAVE_ALARM 1
 
63,7 → 67,7
#define HAVE_BUILTIN_RETURN_ADDRESS 1
 
/* Define to 1 if you have the <byteswap.h> header file. */
#define HAVE_BYTESWAP_H 1
//#define HAVE_BYTESWAP_H 1
 
/* Define to 1 if you have the `clock_gettime' function. */
//#define HAVE_CLOCK_GETTIME 1
98,9 → 102,6
/* Define to 1 if you have the <fenv.h> header file. */
#define HAVE_FENV_H 1
 
/* Define to 1 if you have the `ffs' function. */
#define HAVE_FFS 1
 
/* Define to 1 if you have the `flockfile' function. */
#define HAVE_FLOCKFILE 1
 
107,23 → 108,32
/* Define to 1 if you have the `fork' function. */
//#define HAVE_FORK 1
 
/* FT_Bitmap_Size structure includes y_ppem field */
/* #undef HAVE_FT_BITMAP_SIZE_Y_PPEM */
/* Define to 1 if you have the `FT_Get_X11_Font_Format' function. */
//#define HAVE_FT_GET_X11_FONT_FORMAT 1
 
/* Define to 1 if you have the `FT_GlyphSlot_Embolden' function. */
/* #undef HAVE_FT_GLYPHSLOT_EMBOLDEN */
#define HAVE_FT_GLYPHSLOT_EMBOLDEN 1
 
/* Define to 1 if you have the `FT_GlyphSlot_Oblique' function. */
#define HAVE_FT_GLYPHSLOT_OBLIQUE 1
 
/* Define to 1 if you have the `FT_Library_SetLcdFilter' function. */
/* #undef HAVE_FT_LIBRARY_SETLCDFILTER */
//#define HAVE_FT_LIBRARY_SETLCDFILTER 1
 
/* Define to 1 if you have the `FT_Load_Sfnt_Table' function. */
/* #undef HAVE_FT_LOAD_SFNT_TABLE */
#define HAVE_FT_LOAD_SFNT_TABLE 1
 
/* Define to 1 if you have the `funlockfile' function. */
//#define HAVE_FUNLOCKFILE 1
 
/* Whether you have gcov */
/* #undef HAVE_GCOV */
 
/* Define to 1 if you have the `getline' function. */
//#define HAVE_GETLINE 1
 
/* Enable if your compiler supports the Intel __sync_* atomic primitives */
#define HAVE_INTEL_ATOMIC_PRIMITIVES 1
//#define HAVE_INTEL_ATOMIC_PRIMITIVES 1
 
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
132,8 → 142,11
/* #undef HAVE_IO_H */
 
/* Define to 1 if you have the <libgen.h> header file. */
#define HAVE_LIBGEN_H 1
#undef HAVE_LIBGEN_H
 
/* Define to 1 if you have the `rt' library (-lrt). */
//#define HAVE_LIBRT 1
 
/* Enable if you have libatomic-ops-dev installed */
/* #undef HAVE_LIB_ATOMIC_OPS */
 
143,6 → 156,9
/* Define to 1 if you have the Valgrind lockdep tool */
/* #undef HAVE_LOCKDEP */
 
/* Define to 1 if you have lzo available */
//#define HAVE_LZO 1
 
/* Define to 1 if you have the Valgrind memfault tool */
/* #undef HAVE_MEMFAULT */
 
192,17 → 208,23
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
 
/* Define to 1 if you have the `strndup' function. */
//#define HAVE_STRNDUP 1
 
/* Define to 1 if you have the <sys/int_types.h> header file. */
/* #undef HAVE_SYS_INT_TYPES_H */
 
/* Define to 1 if you have the <sys/ioctl.h> header file. */
//#define HAVE_SYS_IOCTL_H 1
 
/* Define to 1 if you have the <sys/mman.h> header file. */
//#define HAVE_SYS_MMAN_H 1
#define HAVE_SYS_MMAN_H 1
 
/* Define to 1 if you have the <sys/poll.h> header file. */
//#define HAVE_SYS_POLL_H 1
#define HAVE_SYS_POLL_H 1
 
/* Define to 1 if you have the <sys/socket.h> header file. */
//#define HAVE_SYS_SOCKET_H 1
#define HAVE_SYS_SOCKET_H 1
 
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
213,6 → 235,9
/* Define to 1 if you have the <sys/un.h> header file. */
#define HAVE_SYS_UN_H 1
 
/* Define to 1 if you have the <sys/wait.h> header file. */
//#define HAVE_SYS_WAIT_H 1
 
/* Define to 1 if you have the <time.h> header file. */
#define HAVE_TIME_H 1
 
228,8 → 253,6
/* Define to 1 if you have Valgrind */
/* #undef HAVE_VALGRIND */
 
/* Define to 1 if you have the `vasnprintf' function. */
/* #undef HAVE_VASNPRINTF */
 
/* Define to 1 if you have the `waitpid' function. */
//#define HAVE_WAITPID 1
247,9 → 270,6
*/
#define LT_OBJDIR ".libs/"
 
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
/* #undef NO_MINUS_C_MINUS_O */
 
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "http://bugs.freedesktop.org/enter_bug.cgi?product=cairo"
 
263,13 → 283,13
#define PACKAGE_TARNAME USE_cairo_INSTEAD
 
/* Define to the home page for this package. */
#define PACKAGE_URL ""
#define PACKAGE_URL "http://cairographics.org/"
 
/* Define to the version of this package. */
#define PACKAGE_VERSION USE_cairo_version_OR_cairo_version_string_INSTEAD
 
/* Shared library file extension */
#define SHARED_LIB_EXT "so"
#define SHARED_LIB_EXT "dll"
 
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
341,8 → 361,13
 
 
/* Define to 1 if the X Window System is missing or not being used. */
#define X_DISPLAY_MISSING 1
/* #undef X_DISPLAY_MISSING */
 
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
 
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
 
/programs/develop/libraries/cairo/src/cairo-combsort-private.h
File deleted
/programs/develop/libraries/cairo/src/cairo-win32-private.h
File deleted
/programs/develop/libraries/cairo/src/test-fallback-surface.h
File deleted
/programs/develop/libraries/cairo/src/test-null-surface.h
File deleted
/programs/develop/libraries/cairo/src/test-fallback16-surface.h
File deleted
/programs/develop/libraries/cairo/src/test-wrapping-surface.h
File deleted
/programs/develop/libraries/cairo/src/cairo-supported-features.h
File deleted
/programs/develop/libraries/cairo/src/cairo-drm-xr.h
File deleted
/programs/develop/libraries/cairo/src/cairo-analysis-surface.c
37,10 → 37,13
#include "cairoint.h"
 
#include "cairo-analysis-surface-private.h"
#include "cairo-box-inline.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-inline.h"
#include "cairo-region-private.h"
 
typedef struct {
66,8 → 69,8
cairo_int_status_t status_b)
{
/* fatal errors should be checked and propagated at source */
assert (! _cairo_status_is_error (status_a));
assert (! _cairo_status_is_error (status_b));
assert (! _cairo_int_status_is_error (status_a));
assert (! _cairo_int_status_is_error (status_b));
 
/* return the most important status */
if (status_a == CAIRO_INT_STATUS_UNSUPPORTED ||
87,48 → 90,98
return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
 
/* at this point we have checked all the valid internal codes, so... */
assert (status_a == CAIRO_STATUS_SUCCESS &&
status_b == CAIRO_STATUS_SUCCESS);
assert (status_a == CAIRO_INT_STATUS_SUCCESS &&
status_b == CAIRO_INT_STATUS_SUCCESS);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
struct proxy {
cairo_surface_t base;
cairo_surface_t *target;
};
 
static cairo_status_t
proxy_finish (void *abstract_surface)
{
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t proxy_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
proxy_finish,
};
 
static cairo_surface_t *
attach_proxy (cairo_surface_t *source,
cairo_surface_t *target)
{
struct proxy *proxy;
 
proxy = malloc (sizeof (*proxy));
if (unlikely (proxy == NULL))
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_surface_init (&proxy->base, &proxy_backend, NULL, target->content);
 
proxy->target = target;
_cairo_surface_attach_snapshot (source, &proxy->base, NULL);
 
return &proxy->base;
}
 
static void
detach_proxy (cairo_surface_t *proxy)
{
cairo_surface_finish (proxy);
cairo_surface_destroy (proxy);
}
 
static cairo_int_status_t
_analyze_recording_surface_pattern (cairo_analysis_surface_t *surface,
const cairo_pattern_t *pattern)
{
const cairo_surface_pattern_t *surface_pattern;
cairo_bool_t old_has_ctm;
cairo_matrix_t old_ctm, p2d;
cairo_status_t status;
cairo_surface_t *source;
cairo_analysis_surface_t *tmp;
cairo_surface_t *source, *proxy;
cairo_matrix_t p2d;
cairo_status_t status, analysis_status;
 
assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
surface_pattern = (const cairo_surface_pattern_t *) pattern;
assert (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING);
source = surface_pattern->surface;
 
old_ctm = surface->ctm;
old_has_ctm = surface->has_ctm;
proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
if (proxy != NULL) {
/* nothing untoward found so far */
return CAIRO_STATUS_SUCCESS;
}
 
tmp = (cairo_analysis_surface_t *)
_cairo_analysis_surface_create (surface->target);
if (unlikely (tmp->base.status))
return tmp->base.status;
proxy = attach_proxy (source, &tmp->base);
 
p2d = pattern->matrix;
status = cairo_matrix_invert (&p2d);
assert (status == CAIRO_STATUS_SUCCESS);
 
cairo_matrix_multiply (&surface->ctm, &p2d, &surface->ctm);
surface->has_ctm = ! _cairo_matrix_is_identity (&surface->ctm);
cairo_matrix_multiply (&tmp->ctm, &p2d, &surface->ctm);
tmp->has_ctm = ! _cairo_matrix_is_identity (&tmp->ctm);
 
source = surface_pattern->surface;
if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
source = sub->target;
}
source = _cairo_surface_get_source (source, NULL);
status = _cairo_recording_surface_replay_and_create_regions (source,
&tmp->base);
analysis_status = tmp->has_unsupported ? CAIRO_INT_STATUS_IMAGE_FALLBACK : CAIRO_INT_STATUS_SUCCESS;
detach_proxy (proxy);
cairo_surface_destroy (&tmp->base);
 
status = _cairo_recording_surface_replay_and_create_regions (source, &surface->base);
if (unlikely (status))
return status;
 
surface->ctm = old_ctm;
surface->has_ctm = old_has_ctm;
 
return status;
return analysis_status;
}
 
static cairo_int_status_t
143,10 → 196,11
/* Even though the operation is not visible we must be careful
* to not allow unsupported operations to be replayed to the
* backend during CAIRO_PAGINATED_MODE_RENDER */
if (backend_status == CAIRO_STATUS_SUCCESS ||
backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
{
return CAIRO_STATUS_SUCCESS;
return CAIRO_INT_STATUS_SUCCESS;
}
else
{
162,6 → 216,14
if (_cairo_matrix_is_integer_translation (&surface->ctm, &tx, &ty)) {
rect->x += tx;
rect->y += ty;
 
tx = _cairo_fixed_from_int (tx);
bbox.p1.x += tx;
bbox.p2.x += tx;
 
ty = _cairo_fixed_from_int (ty);
bbox.p1.y += ty;
bbox.p2.y += ty;
} else {
_cairo_matrix_transform_bounding_box_fixed (&surface->ctm,
&bbox, NULL);
171,10 → 233,11
* careful to not allow unsupported operations to be
* replayed to the backend during
* CAIRO_PAGINATED_MODE_RENDER */
if (backend_status == CAIRO_STATUS_SUCCESS ||
backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY)
if (backend_status == CAIRO_INT_STATUS_SUCCESS ||
backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY ||
backend_status == CAIRO_INT_STATUS_NOTHING_TO_DO)
{
return CAIRO_STATUS_SUCCESS;
return CAIRO_INT_STATUS_SUCCESS;
}
else
{
189,16 → 252,8
if (surface->first_op) {
surface->first_op = FALSE;
surface->page_bbox = bbox;
} else {
if (bbox.p1.x < surface->page_bbox.p1.x)
surface->page_bbox.p1.x = bbox.p1.x;
if (bbox.p1.y < surface->page_bbox.p1.y)
surface->page_bbox.p1.y = bbox.p1.y;
if (bbox.p2.x > surface->page_bbox.p2.x)
surface->page_bbox.p2.x = bbox.p2.x;
if (bbox.p2.y > surface->page_bbox.p2.y)
surface->page_bbox.p2.y = bbox.p2.y;
}
} else
_cairo_box_add_box(&surface->page_bbox, &bbox);
 
/* If the operation is completely enclosed within the fallback
* region there is no benefit in emitting a native operation as
216,10 → 271,10
* transparency into the white background.
*/
if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT)
backend_status = CAIRO_STATUS_SUCCESS;
backend_status = CAIRO_INT_STATUS_SUCCESS;
}
 
if (backend_status == CAIRO_STATUS_SUCCESS) {
if (backend_status == CAIRO_INT_STATUS_SUCCESS) {
/* Add the operation to the supported region. Operations in
* this region will be emitted as native operations.
*/
240,7 → 295,7
* invoke the cairo-surface-fallback path then return
* CAIRO_STATUS_SUCCESS.
*/
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
return CAIRO_INT_STATUS_IMAGE_FALLBACK;
else
return status;
269,17 → 324,10
}
 
static void
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, const cairo_clip_t *clip)
{
const cairo_rectangle_int_t *clip_extents;
cairo_bool_t is_empty;
 
clip_extents = NULL;
if (clip != NULL)
clip_extents = _cairo_clip_get_extents (clip);
 
if (clip_extents != NULL)
is_empty = _cairo_rectangle_intersect (extents, clip_extents);
_cairo_rectangle_intersect (extents, _cairo_clip_get_extents (clip));
}
 
static void
286,7 → 334,7
_cairo_analysis_surface_operation_extents (cairo_analysis_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip,
const cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_bool_t is_empty;
297,7 → 345,7
cairo_rectangle_int_t source_extents;
 
_cairo_pattern_get_extents (source, &source_extents);
is_empty = _cairo_rectangle_intersect (extents, &source_extents);
_cairo_rectangle_intersect (extents, &source_extents);
}
 
_rectangle_intersect_clip (extents, clip);
307,10 → 355,10
_cairo_analysis_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_status_t backend_status;
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
 
if (surface->target->backend->paint == NULL) {
319,7 → 367,7
backend_status =
surface->target->backend->paint (surface->target,
op, source, clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
 
338,12 → 386,11
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
cairo_bool_t is_empty;
 
if (surface->target->backend->mask == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
351,7 → 398,7
backend_status =
surface->target->backend->mask (surface->target,
op, source, mask, clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
 
360,21 → 407,23
cairo_int_status_t backend_mask_status = CAIRO_STATUS_SUCCESS;
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t *) source;
if (_cairo_surface_is_recording (surface_pattern->surface)) {
cairo_surface_t *src_surface = ((cairo_surface_pattern_t *)source)->surface;
src_surface = _cairo_surface_get_source (src_surface, NULL);
if (_cairo_surface_is_recording (src_surface)) {
backend_source_status =
_analyze_recording_surface_pattern (surface, source);
if (_cairo_status_is_error (backend_source_status))
if (_cairo_int_status_is_error (backend_source_status))
return backend_source_status;
}
}
 
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
if (_cairo_surface_is_recording (surface_pattern->surface)) {
cairo_surface_t *mask_surface = ((cairo_surface_pattern_t *)mask)->surface;
mask_surface = _cairo_surface_get_source (mask_surface, NULL);
if (_cairo_surface_is_recording (mask_surface)) {
backend_mask_status =
_analyze_recording_surface_pattern (surface, mask);
if (_cairo_status_is_error (backend_mask_status))
if (_cairo_int_status_is_error (backend_mask_status))
return backend_mask_status;
}
}
392,8 → 441,7
cairo_rectangle_int_t mask_extents;
 
_cairo_pattern_get_extents (mask, &mask_extents);
is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
 
_cairo_rectangle_intersect (&extents, &mask_extents);
}
 
return _add_operation (surface, &extents, backend_status);
403,18 → 451,17
_cairo_analysis_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_status_t backend_status;
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
cairo_bool_t is_empty;
 
if (surface->target->backend->stroke == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
425,7 → 472,7
ctm, ctm_inverse,
tolerance, antialias,
clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
 
438,7 → 485,7
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t mask_extents;
cairo_status_t status;
cairo_int_status_t status;
 
status = _cairo_path_fixed_stroke_extents (path, style,
ctm, ctm_inverse,
447,7 → 494,7
if (unlikely (status))
return status;
 
is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
_cairo_rectangle_intersect (&extents, &mask_extents);
}
 
return _add_operation (surface, &extents, backend_status);
457,16 → 504,15
_cairo_analysis_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_status_t backend_status;
cairo_int_status_t backend_status;
cairo_rectangle_int_t extents;
cairo_bool_t is_empty;
 
if (surface->target->backend->fill == NULL) {
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
476,7 → 522,7
source, path, fill_rule,
tolerance, antialias,
clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
 
493,7 → 539,7
_cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
&mask_extents);
 
is_empty = _cairo_rectangle_intersect (&extents, &mask_extents);
_cairo_rectangle_intersect (&extents, &mask_extents);
}
 
return _add_operation (surface, &extents, backend_status);
506,13 → 552,11
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_status_t status, backend_status;
cairo_int_status_t status, backend_status;
cairo_rectangle_int_t extents, glyph_extents;
cairo_bool_t is_empty;
 
/* Adapted from _cairo_surface_show_glyphs */
if (surface->target->backend->show_glyphs != NULL) {
521,9 → 565,8
source,
glyphs, num_glyphs,
scaled_font,
clip,
remaining_glyphs);
if (_cairo_status_is_error (backend_status))
clip);
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
else if (surface->target->backend->show_text_glyphs != NULL)
537,7 → 580,7
FALSE,
scaled_font,
clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
else
561,7 → 604,7
if (unlikely (status))
return status;
 
is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
_cairo_rectangle_intersect (&extents, &glyph_extents);
}
 
return _add_operation (surface, &extents, backend_status);
587,12 → 630,11
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_analysis_surface_t *surface = abstract_surface;
cairo_status_t status, backend_status;
cairo_int_status_t status, backend_status;
cairo_rectangle_int_t extents, glyph_extents;
cairo_bool_t is_empty;
 
/* Adapted from _cairo_surface_show_glyphs */
backend_status = CAIRO_INT_STATUS_UNSUPPORTED;
606,27 → 648,20
cluster_flags,
scaled_font,
clip);
if (_cairo_status_is_error (backend_status))
if (_cairo_int_status_is_error (backend_status))
return backend_status;
}
if (backend_status == CAIRO_INT_STATUS_UNSUPPORTED &&
surface->target->backend->show_glyphs != NULL)
{
int remaining_glyphs = num_glyphs;
backend_status =
surface->target->backend->show_glyphs (surface->target, op,
source,
glyphs, num_glyphs,
scaled_font,
clip,
&remaining_glyphs);
if (_cairo_status_is_error (backend_status))
clip);
if (_cairo_int_status_is_error (backend_status))
return backend_status;
 
glyphs += num_glyphs - remaining_glyphs;
num_glyphs = remaining_glyphs;
if (remaining_glyphs == 0)
backend_status = CAIRO_STATUS_SUCCESS;
}
 
if (backend_status == CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN)
645,7 → 680,7
if (unlikely (status))
return status;
 
is_empty = _cairo_rectangle_intersect (&extents, &glyph_extents);
_cairo_rectangle_intersect (&extents, &glyph_extents);
}
 
return _add_operation (surface, &extents, backend_status);
653,37 → 688,35
 
static const cairo_surface_backend_t cairo_analysis_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
 
_cairo_analysis_surface_finish,
NULL,
 
NULL, /* create_similar */
_cairo_analysis_surface_finish,
NULL, /* create_similar_image */
NULL, /* map_to_image */
NULL, /* unmap */
 
NULL, /* source */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_analysis_surface_get_extents,
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
_cairo_analysis_surface_paint,
_cairo_analysis_surface_mask,
_cairo_analysis_surface_stroke,
_cairo_analysis_surface_fill,
NULL, /* fill_stroke */
_cairo_analysis_surface_show_glyphs,
NULL, /* snapshot */
NULL, /* is_similar */
NULL, /* fill_stroke */
NULL, /* create_solid_pattern_surface */
NULL, /* can_repaint_solid_pattern_surface */
_cairo_analysis_surface_has_show_text_glyphs,
_cairo_analysis_surface_show_text_glyphs
};
807,7 → 840,7
(*_paint_func) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
typedef cairo_int_status_t
(*_mask_func) (void *surface,
814,29 → 847,29
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
typedef cairo_int_status_t
(*_stroke_func) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
typedef cairo_int_status_t
(*_fill_func) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
typedef cairo_int_status_t
(*_show_glyphs_func) (void *surface,
845,43 → 878,39
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs);
const cairo_clip_t *clip);
 
static const cairo_surface_backend_t cairo_null_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
NULL, /* finish */
 
NULL, /* only accessed through the surface functions */
 
NULL, /* create_similar */
NULL, /* finish */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image*/
 
NULL, /* source */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
NULL, /* get_extents */
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
(_paint_func) _return_success, /* paint */
(_mask_func) _return_success, /* mask */
(_stroke_func) _return_success, /* stroke */
(_fill_func) _return_success, /* fill */
NULL, /* fill_stroke */
(_show_glyphs_func) _return_success, /* show_glyphs */
NULL, /* snapshot */
NULL, /* is_similar */
NULL, /* fill_stroke */
NULL, /* create_solid_pattern_surface */
NULL, /* can_repaint_solid_pattern_surface */
NULL, /* has_show_text_glyphs */
NULL /* show_text_glyphs */
};
/programs/develop/libraries/cairo/src/cairo-arc-private.h
38,6 → 38,8
 
#include "cairoint.h"
 
CAIRO_BEGIN_DECLS
 
cairo_private void
_cairo_arc_path (cairo_t *cr,
double xc,
54,4 → 56,6
double angle1,
double angle2);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_ARC_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-arc.c
38,6 → 38,8
 
#include "cairo-arc-private.h"
 
#define MAX_FULL_CIRCLES 65536
 
/* Spline deviation from the circle in radius would be given by:
 
error = sqrt (x**2 + y**2) - 1
131,13 → 133,13
 
for some value of h.
 
"Approximation of circular arcs by cubic poynomials", Michael
"Approximation of circular arcs by cubic polynomials", Michael
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides
various values of h along with error analysis for each.
 
From that paper, a very practical value of h is:
 
h = 4/3 * tan(angle/4)
h = 4/3 * R * tan(angle/4)
 
This value does not give the spline with minimal error, but it does
provide a very good approximation, (6th-order convergence), and the
184,9 → 186,14
if (cairo_status (cr))
return;
 
while (angle_max - angle_min > 4 * M_PI)
angle_max -= 2 * M_PI;
assert (angle_max >= angle_min);
 
if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) {
angle_max = fmod (angle_max - angle_min, 2 * M_PI);
angle_min = fmod (angle_min, 2 * M_PI);
angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES;
}
 
/* Recurse if drawing arc larger than pi */
if (angle_max - angle_min > M_PI) {
double angle_mid = angle_min + (angle_max - angle_min) / 2.0;
210,27 → 217,32
} else if (angle_max != angle_min) {
cairo_matrix_t ctm;
int i, segments;
double angle, angle_step;
double step;
 
cairo_get_matrix (cr, &ctm);
segments = _arc_segments_needed (angle_max - angle_min,
radius, &ctm,
cairo_get_tolerance (cr));
angle_step = (angle_max - angle_min) / (double) segments;
step = (angle_max - angle_min) / segments;
segments -= 1;
 
if (dir == CAIRO_DIRECTION_FORWARD) {
angle = angle_min;
} else {
angle = angle_max;
angle_step = - angle_step;
if (dir == CAIRO_DIRECTION_REVERSE) {
double t;
 
t = angle_min;
angle_min = angle_max;
angle_max = t;
 
step = -step;
}
 
for (i = 0; i < segments; i++, angle += angle_step) {
_cairo_arc_segment (cr, xc, yc,
radius,
angle,
angle + angle_step);
for (i = 0; i < segments; i++, angle_min += step) {
_cairo_arc_segment (cr, xc, yc, radius,
angle_min, angle_min + step);
}
 
_cairo_arc_segment (cr, xc, yc, radius,
angle_min, angle_max);
} else {
cairo_line_to (cr,
xc + radius * cos (angle_min),
239,7 → 251,7
}
 
/**
* _cairo_arc_path
* _cairo_arc_path:
* @cr: a cairo context
* @xc: X position of the center of the arc
* @yc: Y position of the center of the arc
/programs/develop/libraries/cairo/src/cairo-array-private.h
0,0 → 1,90
/* 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>
*/
 
#ifndef CAIRO_ARRAY_PRIVATE_H
#define CAIRO_ARRAY_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-types-private.h"
 
CAIRO_BEGIN_DECLS
 
/* cairo-array.c structures and functions */
 
cairo_private void
_cairo_array_init (cairo_array_t *array, unsigned int element_size);
 
cairo_private void
_cairo_array_fini (cairo_array_t *array);
 
cairo_private cairo_status_t
_cairo_array_grow_by (cairo_array_t *array, unsigned int additional);
 
cairo_private void
_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements);
 
cairo_private cairo_status_t
_cairo_array_append (cairo_array_t *array, const void *element);
 
cairo_private cairo_status_t
_cairo_array_append_multiple (cairo_array_t *array,
const void *elements,
unsigned int num_elements);
 
cairo_private cairo_status_t
_cairo_array_allocate (cairo_array_t *array,
unsigned int num_elements,
void **elements);
 
cairo_private void *
_cairo_array_index (cairo_array_t *array, unsigned int index);
 
cairo_private const void *
_cairo_array_index_const (const cairo_array_t *array, unsigned int index);
 
cairo_private void
_cairo_array_copy_element (const cairo_array_t *array, unsigned int index, void *dst);
 
cairo_private unsigned int
_cairo_array_num_elements (const cairo_array_t *array);
 
cairo_private unsigned int
_cairo_array_size (const cairo_array_t *array);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_ARRAY_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-array.c
1,3 → 1,4
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc
36,6 → 37,7
*/
 
#include "cairoint.h"
#include "cairo-array-private.h"
#include "cairo-error-private.h"
 
/**
53,38 → 55,15
* called to free resources allocated during use of the array.
**/
void
_cairo_array_init (cairo_array_t *array, int element_size)
_cairo_array_init (cairo_array_t *array, unsigned int element_size)
{
array->size = 0;
array->num_elements = 0;
array->element_size = element_size;
array->elements = NULL;
 
array->is_snapshot = FALSE;
}
 
/**
* _cairo_array_init_snapshot:
* @array: A #cairo_array_t to be initialized as a snapshot
* @other: The #cairo_array_t from which to create the snapshot
*
* Initialize @array as an immutable copy of @other. It is an error to
* call an array-modifying function (other than _cairo_array_fini) on
* @array after calling this function.
**/
void
_cairo_array_init_snapshot (cairo_array_t *array,
const cairo_array_t *other)
{
array->size = other->size;
array->num_elements = other->num_elements;
array->element_size = other->element_size;
array->elements = other->elements;
 
array->is_snapshot = TRUE;
}
 
/**
* _cairo_array_fini:
* @array: A #cairo_array_t
*
95,14 → 74,8
void
_cairo_array_fini (cairo_array_t *array)
{
if (array->is_snapshot)
return;
 
if (array->elements) {
free (* array->elements);
free (array->elements);
}
}
 
/**
* _cairo_array_grow_by:
120,8 → 93,6
unsigned int required_size = array->num_elements + additional;
unsigned int new_size;
 
assert (! array->is_snapshot);
 
/* check for integer overflow */
if (required_size > INT_MAX || required_size < array->num_elements)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
140,16 → 111,8
while (new_size < required_size)
new_size = new_size * 2;
 
if (array->elements == NULL) {
array->elements = malloc (sizeof (char *));
if (unlikely (array->elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
*array->elements = NULL;
}
 
array->size = new_size;
new_elements = _cairo_realloc_ab (*array->elements,
new_elements = _cairo_realloc_ab (array->elements,
array->size, array->element_size);
 
if (unlikely (new_elements == NULL)) {
157,7 → 120,7
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
*array->elements = new_elements;
array->elements = new_elements;
 
return CAIRO_STATUS_SUCCESS;
}
173,8 → 136,6
void
_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements)
{
assert (! array->is_snapshot);
 
if (num_elements < array->num_elements)
array->num_elements = num_elements;
}
207,7 → 168,7
/* We allow an index of 0 for the no-elements case.
* This makes for cleaner calling code which will often look like:
*
* elements = _cairo_array_index (array, num_elements);
* elements = _cairo_array_index (array, 0);
* for (i=0; i < num_elements; i++) {
* ... use elements[i] here ...
* }
220,10 → 181,54
 
assert (index < array->num_elements);
 
return (void *) &(*array->elements)[index * array->element_size];
return array->elements + index * array->element_size;
}
 
/**
* _cairo_array_index_const:
* @array: a #cairo_array_t
* Returns: A pointer to the object stored at @index.
*
* If the resulting value is assigned to a pointer to an object of the same
* element_size as initially passed to _cairo_array_init() then that
* pointer may be used for further direct indexing with []. For
* example:
*
* <informalexample><programlisting>
* cairo_array_t array;
* const double *values;
*
* _cairo_array_init (&array, sizeof(double));
* ... calls to _cairo_array_append() here ...
*
* values = _cairo_array_index_const (&array, 0);
* for (i = 0; i < _cairo_array_num_elements (&array); i++)
* ... read values[i] here ...
* </programlisting></informalexample>
**/
const void *
_cairo_array_index_const (const cairo_array_t *array, unsigned int index)
{
/* We allow an index of 0 for the no-elements case.
* This makes for cleaner calling code which will often look like:
*
* elements = _cairo_array_index_const (array, 0);
* for (i=0; i < num_elements; i++) {
* ... read elements[i] here ...
* }
*
* which in the num_elements==0 case gets the NULL pointer here,
* but never dereferences it.
*/
if (index == 0 && array->num_elements == 0)
return NULL;
 
assert (index < array->num_elements);
 
return array->elements + index * array->element_size;
}
 
/**
* _cairo_array_copy_element:
* @array: a #cairo_array_t
*
231,9 → 236,11
* location pointed to by @dst.
**/
void
_cairo_array_copy_element (cairo_array_t *array, int index, void *dst)
_cairo_array_copy_element (const cairo_array_t *array,
unsigned int index,
void *dst)
{
memcpy (dst, _cairo_array_index (array, index), array->element_size);
memcpy (dst, _cairo_array_index_const (array, index), array->element_size);
}
 
/**
255,8 → 262,6
_cairo_array_append (cairo_array_t *array,
const void *element)
{
assert (! array->is_snapshot);
 
return _cairo_array_append_multiple (array, element, 1);
}
 
275,13 → 280,11
cairo_status_t
_cairo_array_append_multiple (cairo_array_t *array,
const void *elements,
int num_elements)
unsigned int num_elements)
{
cairo_status_t status;
void *dest;
 
assert (! array->is_snapshot);
 
status = _cairo_array_allocate (array, num_elements, &dest);
if (unlikely (status))
return status;
311,8 → 314,6
{
cairo_status_t status;
 
assert (! array->is_snapshot);
 
status = _cairo_array_grow_by (array, num_elements);
if (unlikely (status))
return status;
319,7 → 320,7
 
assert (array->num_elements + num_elements <= array->size);
 
*elements = &(*array->elements)[array->num_elements * array->element_size];
*elements = array->elements + array->num_elements * array->element_size;
 
array->num_elements += num_elements;
 
333,8 → 334,8
*
* This space was left intentionally blank, but gtk-doc filled it.
**/
int
_cairo_array_num_elements (cairo_array_t *array)
unsigned int
_cairo_array_num_elements (const cairo_array_t *array)
{
return array->num_elements;
}
347,8 → 348,8
*
* This space was left intentionally blank, but gtk-doc filled it.
**/
int
_cairo_array_size (cairo_array_t *array)
unsigned int
_cairo_array_size (const cairo_array_t *array)
{
return array->size;
}
385,12 → 386,12
cairo_user_data_slot_t *slots;
 
slots = _cairo_array_index (array, 0);
do {
if (slots->user_data != NULL && slots->destroy != NULL)
slots->destroy (slots->user_data);
slots++;
} while (--num_slots);
while (num_slots--) {
cairo_user_data_slot_t *s = &slots[num_slots];
if (s->user_data != NULL && s->destroy != NULL)
s->destroy (s->user_data);
}
}
 
_cairo_array_fini (array);
}
493,7 → 494,7
 
cairo_status_t
_cairo_user_data_array_copy (cairo_user_data_array_t *dst,
cairo_user_data_array_t *src)
const cairo_user_data_array_t *src)
{
/* discard any existing user-data */
if (dst->num_elements != 0) {
501,11 → 502,8
_cairo_user_data_array_init (dst);
}
 
if (src->num_elements == 0)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_array_append_multiple (dst,
_cairo_array_index (src, 0),
_cairo_array_index_const (src, 0),
src->num_elements);
}
 
/programs/develop/libraries/cairo/src/cairo-atomic-private.h
79,6 → 79,7
#endif
 
# define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1))
# define _cairo_atomic_int_dec(x) ((void) __sync_fetch_and_add(x, -1))
# define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1)
# define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv)
# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv)
111,6 → 112,7
# define _cairo_atomic_int_get(x) (AO_load_full (x))
 
# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x))
# define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_full(x))
# define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1)
# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv)
 
140,6 → 142,7
# define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x))
 
# define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x))
# define _cairo_atomic_int_dec(x) ((void) OSAtomicDecrement32Barrier (x))
# define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0)
# define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x)
 
178,6 → 181,8
cairo_private void
_cairo_atomic_int_inc (cairo_atomic_int_t *x);
 
#define _cairo_atomic_int_dec(x) _cairo_atomic_int_dec_and_test(x)
 
cairo_private cairo_bool_t
_cairo_atomic_int_dec_and_test (cairo_atomic_int_t *x);
 
254,9 → 259,11
_cairo_atomic_int_cmpxchg((cairo_atomic_int_t *)x, oldv, newv)
 
#define _cairo_status_set_error(status, err) do { \
int ret__; \
assert (err < CAIRO_STATUS_LAST_STATUS); \
/* hide compiler warnings about cairo_status_t != int (gcc treats its as \
* an unsigned integer instead, and about ignoring the return value. */ \
int ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \
ret__ = _cairo_atomic_int_cmpxchg ((cairo_atomic_int_t *) status, CAIRO_STATUS_SUCCESS, err); \
(void) ret__; \
} while (0)
 
/programs/develop/libraries/cairo/src/cairo-backend-private.h
0,0 → 1,201
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_BACKEND_PRIVATE_H
#define CAIRO_BACKEND_PRIVATE_H
 
#include "cairo-types-private.h"
#include "cairo-private.h"
 
typedef enum _cairo_backend_type {
CAIRO_TYPE_DEFAULT,
CAIRO_TYPE_SKIA,
} cairo_backend_type_t;
 
struct _cairo_backend {
cairo_backend_type_t type;
void (*destroy) (void *cr);
 
cairo_surface_t *(*get_original_target) (void *cr);
cairo_surface_t *(*get_current_target) (void *cr);
 
cairo_status_t (*save) (void *cr);
cairo_status_t (*restore) (void *cr);
 
cairo_status_t (*push_group) (void *cr, cairo_content_t content);
cairo_pattern_t *(*pop_group) (void *cr);
 
cairo_status_t (*set_source_rgba) (void *cr, double red, double green, double blue, double alpha);
cairo_status_t (*set_source_surface) (void *cr, cairo_surface_t *surface, double x, double y);
cairo_status_t (*set_source) (void *cr, cairo_pattern_t *source);
cairo_pattern_t *(*get_source) (void *cr);
 
cairo_status_t (*set_antialias) (void *cr, cairo_antialias_t antialias);
cairo_status_t (*set_dash) (void *cr, const double *dashes, int num_dashes, double offset);
cairo_status_t (*set_fill_rule) (void *cr, cairo_fill_rule_t fill_rule);
cairo_status_t (*set_line_cap) (void *cr, cairo_line_cap_t line_cap);
cairo_status_t (*set_line_join) (void *cr, cairo_line_join_t line_join);
cairo_status_t (*set_line_width) (void *cr, double line_width);
cairo_status_t (*set_miter_limit) (void *cr, double limit);
cairo_status_t (*set_opacity) (void *cr, double opacity);
cairo_status_t (*set_operator) (void *cr, cairo_operator_t op);
cairo_status_t (*set_tolerance) (void *cr, double tolerance);
 
cairo_antialias_t (*get_antialias) (void *cr);
void (*get_dash) (void *cr, double *dashes, int *num_dashes, double *offset);
cairo_fill_rule_t (*get_fill_rule) (void *cr);
cairo_line_cap_t (*get_line_cap) (void *cr);
cairo_line_join_t (*get_line_join) (void *cr);
double (*get_line_width) (void *cr);
double (*get_miter_limit) (void *cr);
double (*get_opacity) (void *cr);
cairo_operator_t (*get_operator) (void *cr);
double (*get_tolerance) (void *cr);
 
cairo_status_t (*translate) (void *cr, double tx, double ty);
cairo_status_t (*scale) (void *cr, double sx, double sy);
cairo_status_t (*rotate) (void *cr, double theta);
cairo_status_t (*transform) (void *cr, const cairo_matrix_t *matrix);
cairo_status_t (*set_matrix) (void *cr, const cairo_matrix_t *matrix);
cairo_status_t (*set_identity_matrix) (void *cr);
void (*get_matrix) (void *cr, cairo_matrix_t *matrix);
 
void (*user_to_device) (void *cr, double *x, double *y);
void (*user_to_device_distance) (void *cr, double *x, double *y);
void (*device_to_user) (void *cr, double *x, double *y);
void (*device_to_user_distance) (void *cr, double *x, double *y);
 
void (*user_to_backend) (void *cr, double *x, double *y);
void (*user_to_backend_distance) (void *cr, double *x, double *y);
void (*backend_to_user) (void *cr, double *x, double *y);
void (*backend_to_user_distance) (void *cr, double *x, double *y);
 
cairo_status_t (*new_path) (void *cr);
cairo_status_t (*new_sub_path) (void *cr);
cairo_status_t (*move_to) (void *cr, double x, double y);
cairo_status_t (*rel_move_to) (void *cr, double dx, double dy);
cairo_status_t (*line_to) (void *cr, double x, double y);
cairo_status_t (*rel_line_to) (void *cr, double dx, double dy);
cairo_status_t (*curve_to) (void *cr, double x1, double y1, double x2, double y2, double x3, double y3);
cairo_status_t (*rel_curve_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double dx3, double dy3);
cairo_status_t (*arc_to) (void *cr, double x1, double y1, double x2, double y2, double radius);
cairo_status_t (*rel_arc_to) (void *cr, double dx1, double dy1, double dx2, double dy2, double radius);
cairo_status_t (*close_path) (void *cr);
 
cairo_status_t (*arc) (void *cr, double xc, double yc, double radius, double angle1, double angle2, cairo_bool_t forward);
cairo_status_t (*rectangle) (void *cr, double x, double y, double width, double height);
 
void (*path_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
cairo_bool_t (*has_current_point) (void *cr);
cairo_bool_t (*get_current_point) (void *cr, double *x, double *y);
 
cairo_path_t *(*copy_path) (void *cr);
cairo_path_t *(*copy_path_flat) (void *cr);
cairo_status_t (*append_path) (void *cr, const cairo_path_t *path);
 
cairo_status_t (*stroke_to_path) (void *cr);
 
cairo_status_t (*clip) (void *cr);
cairo_status_t (*clip_preserve) (void *cr);
cairo_status_t (*in_clip) (void *cr, double x, double y, cairo_bool_t *inside);
cairo_status_t (*clip_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
cairo_status_t (*reset_clip) (void *cr);
cairo_rectangle_list_t *(*clip_copy_rectangle_list) (void *cr);
 
cairo_status_t (*paint) (void *cr);
cairo_status_t (*paint_with_alpha) (void *cr, double opacity);
cairo_status_t (*mask) (void *cr, cairo_pattern_t *pattern);
 
cairo_status_t (*stroke) (void *cr);
cairo_status_t (*stroke_preserve) (void *cr);
cairo_status_t (*in_stroke) (void *cr, double x, double y, cairo_bool_t *inside);
cairo_status_t (*stroke_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
 
cairo_status_t (*fill) (void *cr);
cairo_status_t (*fill_preserve) (void *cr);
cairo_status_t (*in_fill) (void *cr, double x, double y, cairo_bool_t *inside);
cairo_status_t (*fill_extents) (void *cr, double *x1, double *y1, double *x2, double *y2);
 
cairo_status_t (*set_font_face) (void *cr, cairo_font_face_t *font_face);
cairo_font_face_t *(*get_font_face) (void *cr);
cairo_status_t (*set_font_size) (void *cr, double size);
cairo_status_t (*set_font_matrix) (void *cr, const cairo_matrix_t *matrix);
void (*get_font_matrix) (void *cr, cairo_matrix_t *matrix);
cairo_status_t (*set_font_options) (void *cr, const cairo_font_options_t *options);
void (*get_font_options) (void *cr, cairo_font_options_t *options);
cairo_status_t (*set_scaled_font) (void *cr, cairo_scaled_font_t *scaled_font);
cairo_scaled_font_t *(*get_scaled_font) (void *cr);
cairo_status_t (*font_extents) (void *cr, cairo_font_extents_t *extents);
 
cairo_status_t (*glyphs) (void *cr,
const cairo_glyph_t *glyphs, int num_glyphs,
cairo_glyph_text_info_t *info);
cairo_status_t (*glyph_path) (void *cr,
const cairo_glyph_t *glyphs, int num_glyphs);
 
cairo_status_t (*glyph_extents) (void *cr,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents);
 
cairo_status_t (*copy_page) (void *cr);
cairo_status_t (*show_page) (void *cr);
};
 
static inline void
_cairo_backend_to_user (cairo_t *cr, double *x, double *y)
{
cr->backend->backend_to_user (cr, x, y);
}
 
static inline void
_cairo_backend_to_user_distance (cairo_t *cr, double *x, double *y)
{
cr->backend->backend_to_user_distance (cr, x, y);
}
 
static inline void
_cairo_user_to_backend (cairo_t *cr, double *x, double *y)
{
cr->backend->user_to_backend (cr, x, y);
}
 
static inline void
_cairo_user_to_backend_distance (cairo_t *cr, double *x, double *y)
{
cr->backend->user_to_backend_distance (cr, x, y);
}
 
#endif /* CAIRO_BACKEND_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-bentley-ottmann-rectangular.c
40,8 → 40,9
 
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-combsort-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-list-private.h"
#include "cairo-traps-private.h"
 
#include <setjmp.h>
 
69,23 → 70,20
/* left and right children are index * 2 and (index * 2) +1 respectively */
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
 
typedef struct _pqueue {
int size, max_size;
 
rectangle_t **elements;
rectangle_t *elements_embedded[1024];
} pqueue_t;
 
typedef struct _sweep_line {
rectangle_t **rectangles;
pqueue_t pq;
edge_t head, tail;
edge_t *insert_left, *insert_right;
rectangle_t **stop;
edge_t head, tail, *insert, *cursor;
int32_t current_y;
int32_t last_y;
int stop_size;
 
int32_t insert_x;
cairo_fill_rule_t fill_rule;
 
cairo_bool_t do_traps;
void *container;
 
jmp_buf unwind;
} sweep_line_t;
 
139,63 → 137,13
}
 
static inline void
pqueue_init (pqueue_t *pq)
{
pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
pq->size = 0;
 
pq->elements = pq->elements_embedded;
pq->elements[PQ_FIRST_ENTRY] = NULL;
}
 
static inline void
pqueue_fini (pqueue_t *pq)
{
if (pq->elements != pq->elements_embedded)
free (pq->elements);
}
 
static cairo_bool_t
pqueue_grow (pqueue_t *pq)
{
rectangle_t **new_elements;
pq->max_size *= 2;
 
if (pq->elements == pq->elements_embedded) {
new_elements = _cairo_malloc_ab (pq->max_size,
sizeof (rectangle_t *));
if (unlikely (new_elements == NULL))
return FALSE;
 
memcpy (new_elements, pq->elements_embedded,
sizeof (pq->elements_embedded));
} else {
new_elements = _cairo_realloc_ab (pq->elements,
pq->max_size,
sizeof (rectangle_t *));
if (unlikely (new_elements == NULL))
return FALSE;
}
 
pq->elements = new_elements;
return TRUE;
}
 
static inline void
pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
{
rectangle_t **elements;
int i, parent;
 
if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) {
if (unlikely (! pqueue_grow (&sweep->pq))) {
longjmp (sweep->unwind,
_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
}
 
elements = sweep->pq.elements;
for (i = ++sweep->pq.size;
elements = sweep->stop;
for (i = ++sweep->stop_size;
i != PQ_FIRST_ENTRY &&
rectangle_compare_stop (rectangle,
elements[parent = PQ_PARENT_INDEX (i)]) < 0;
208,23 → 156,23
}
 
static inline void
pqueue_pop (pqueue_t *pq)
rectangle_pop_stop (sweep_line_t *sweep)
{
rectangle_t **elements = pq->elements;
rectangle_t **elements = sweep->stop;
rectangle_t *tail;
int child, i;
 
tail = elements[pq->size--];
if (pq->size == 0) {
tail = elements[sweep->stop_size--];
if (sweep->stop_size == 0) {
elements[PQ_FIRST_ENTRY] = NULL;
return;
}
 
for (i = PQ_FIRST_ENTRY;
(child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
(child = PQ_LEFT_CHILD_INDEX (i)) <= sweep->stop_size;
i = child)
{
if (child != pq->size &&
if (child != sweep->stop_size &&
rectangle_compare_stop (elements[child+1],
elements[child]) < 0)
{
248,7 → 196,7
static inline rectangle_t *
rectangle_peek_stop (sweep_line_t *sweep_line)
{
return sweep_line->pq.elements[PQ_FIRST_ENTRY];
return sweep_line->stop[PQ_FIRST_ENTRY];
}
 
CAIRO_COMBSORT_DECLARE (_rectangle_sort,
259,50 → 207,48
sweep_line_init (sweep_line_t *sweep_line,
rectangle_t **rectangles,
int num_rectangles,
cairo_fill_rule_t fill_rule)
cairo_fill_rule_t fill_rule,
cairo_bool_t do_traps,
void *container)
{
_rectangle_sort (rectangles, num_rectangles);
rectangles[-2] = NULL;
rectangles[-1] = NULL;
rectangles[num_rectangles] = NULL;
sweep_line->rectangles = rectangles;
sweep_line->stop = rectangles - 2;
sweep_line->stop_size = 0;
 
sweep_line->insert = NULL;
sweep_line->insert_x = INT_MAX;
sweep_line->cursor = &sweep_line->tail;
 
sweep_line->head.dir = 0;
sweep_line->head.x = INT32_MIN;
sweep_line->head.right = NULL;
sweep_line->head.dir = 0;
sweep_line->head.prev = NULL;
sweep_line->head.next = &sweep_line->tail;
sweep_line->tail.prev = &sweep_line->head;
sweep_line->tail.next = NULL;
sweep_line->tail.right = NULL;
sweep_line->tail.x = INT32_MAX;
sweep_line->tail.right = NULL;
sweep_line->tail.dir = 0;
sweep_line->tail.prev = &sweep_line->head;
 
sweep_line->insert_left = &sweep_line->tail;
sweep_line->insert_right = &sweep_line->tail;
 
sweep_line->current_y = INT32_MIN;
sweep_line->last_y = INT32_MIN;
 
sweep_line->fill_rule = fill_rule;
 
pqueue_init (&sweep_line->pq);
sweep_line->container = container;
sweep_line->do_traps = do_traps;
}
 
static void
sweep_line_fini (sweep_line_t *sweep_line)
edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot)
{
pqueue_fini (&sweep_line->pq);
}
 
static void
edge_end_box (sweep_line_t *sweep_line,
edge_t *left,
int32_t bot,
cairo_bool_t do_traps,
void *container)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
/* Only emit (trivial) non-degenerate trapezoids with positive height. */
if (likely (left->top < bot)) {
if (do_traps) {
if (sweep_line->do_traps) {
cairo_line_t _left = {
{ left->x, left->top },
{ left->x, bot },
310,8 → 256,8
{ left->right->x, left->top },
{ left->right->x, bot },
};
_cairo_traps_add_trap (container, left->top, bot, &_left, &_right);
status = _cairo_traps_status ((cairo_traps_t *) container);
_cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right);
status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container);
} else {
cairo_box_t box;
 
320,7 → 266,9
box.p2.x = left->right->x;
box.p2.y = bot;
 
status = _cairo_boxes_add (container, &box);
status = _cairo_boxes_add (sweep_line->container,
CAIRO_ANTIALIAS_DEFAULT,
&box);
}
}
if (unlikely (status))
338,34 → 286,169
edge_start_or_continue_box (sweep_line_t *sweep_line,
edge_t *left,
edge_t *right,
int top,
cairo_bool_t do_traps,
void *container)
int top)
{
if (left->right == right)
return;
 
if (left->right != NULL) {
if (right != NULL && left->right->x == right->x) {
if (left->right->x == right->x) {
/* continuation on right, so just swap edges */
left->right = right;
return;
}
 
edge_end_box (sweep_line,
left, top, do_traps, container);
edge_end_box (sweep_line, left, top);
}
 
if (right != NULL && left->x != right->x) {
if (left->x != right->x) {
left->top = top;
left->right = right;
}
}
/*
* Merge two sorted edge lists.
* Input:
* - head_a: The head of the first list.
* - head_b: The head of the second list; head_b cannot be NULL.
* Output:
* Returns the head of the merged list.
*
* Implementation notes:
* To make it fast (in particular, to reduce to an insertion sort whenever
* one of the two input lists only has a single element) we iterate through
* a list until its head becomes greater than the head of the other list,
* then we switch their roles. As soon as one of the two lists is empty, we
* just attach the other one to the current list and exit.
* Writes to memory are only needed to "switch" lists (as it also requires
* attaching to the output list the list which we will be iterating next) and
* to attach the last non-empty list.
*/
static edge_t *
merge_sorted_edges (edge_t *head_a, edge_t *head_b)
{
edge_t *head, *prev;
int32_t x;
 
prev = head_a->prev;
if (head_a->x <= head_b->x) {
head = head_a;
} else {
head_b->prev = prev;
head = head_b;
goto start_with_b;
}
 
do {
x = head_b->x;
while (head_a != NULL && head_a->x <= x) {
prev = head_a;
head_a = head_a->next;
}
 
head_b->prev = prev;
prev->next = head_b;
if (head_a == NULL)
return head;
 
start_with_b:
x = head_a->x;
while (head_b != NULL && head_b->x <= x) {
prev = head_b;
head_b = head_b->next;
}
 
head_a->prev = prev;
prev->next = head_a;
if (head_b == NULL)
return head;
} while (1);
}
 
/*
* Sort (part of) a list.
* Input:
* - list: The list to be sorted; list cannot be NULL.
* - limit: Recursion limit.
* Output:
* - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
* input list; if the input list has fewer elements, head_out be a sorted list
* containing all the elements of the input list.
* Returns the head of the list of unprocessed elements (NULL if the sorted list contains
* all the elements of the input list).
*
* Implementation notes:
* Special case single element list, unroll/inline the sorting of the first two elements.
* Some tail recursion is used since we iterate on the bottom-up solution of the problem
* (we start with a small sorted list and keep merging other lists of the same size to it).
*/
static edge_t *
sort_edges (edge_t *list,
unsigned int level,
edge_t **head_out)
{
edge_t *head_other, *remaining;
unsigned int i;
 
head_other = list->next;
 
if (head_other == NULL) {
*head_out = list;
return NULL;
}
 
remaining = head_other->next;
if (list->x <= head_other->x) {
*head_out = list;
head_other->next = NULL;
} else {
*head_out = head_other;
head_other->prev = list->prev;
head_other->next = list;
list->prev = head_other;
list->next = NULL;
}
 
for (i = 0; i < level && remaining; i++) {
remaining = sort_edges (remaining, i, &head_other);
*head_out = merge_sorted_edges (*head_out, head_other);
}
 
return remaining;
}
 
static edge_t *
merge_unsorted_edges (edge_t *head, edge_t *unsorted)
{
sort_edges (unsorted, UINT_MAX, &unsorted);
return merge_sorted_edges (head, unsorted);
}
 
static void
active_edges_insert (sweep_line_t *sweep)
{
edge_t *prev;
int x;
 
x = sweep->insert_x;
prev = sweep->cursor;
if (prev->x > x) {
do {
prev = prev->prev;
} while (prev->x > x);
} else {
while (prev->next->x < x)
prev = prev->next;
}
 
prev->next = merge_unsorted_edges (prev->next, sweep->insert);
sweep->cursor = sweep->insert;
sweep->insert = NULL;
sweep->insert_x = INT_MAX;
}
 
static inline void
active_edges_to_traps (sweep_line_t *sweep,
cairo_bool_t do_traps,
void *container)
active_edges_to_traps (sweep_line_t *sweep)
{
int top = sweep->current_y;
edge_t *pos;
373,6 → 456,9
if (sweep->last_y == sweep->current_y)
return;
 
if (sweep->insert)
active_edges_insert (sweep);
 
pos = sweep->head.next;
if (pos == &sweep->tail)
return;
388,51 → 474,42
right = left->next;
 
/* Check if there is a co-linear edge with an existing trap */
if (left->right == NULL) {
while (unlikely (right->x == left->x)) {
winding += right->dir;
while (right->x == left->x) {
if (right->right != NULL) {
assert (left->right == NULL);
/* continuation on left */
left->top = right->top;
left->right = right->right;
right->right = NULL;
winding -= right->dir;
break;
}
 
winding += right->dir;
right = right->next;
}
 
if (winding == 0) {
if (left->right != NULL)
edge_end_box (sweep, left, top);
pos = right;
continue;
}
}
 
/* Greedily search for the closing edge, so that we generate the
* maximal span width with the minimal number of trapezoids.
*/
 
do {
/* End all subsumed traps */
if (unlikely (right->right != NULL)) {
edge_end_box (sweep,
right, top, do_traps, container);
}
if (unlikely (right->right != NULL))
edge_end_box (sweep, right, top);
 
/* Greedily search for the closing edge, so that we generate
* the * maximal span width with the minimal number of
* boxes.
*/
winding += right->dir;
if (winding == 0) {
/* skip co-linear edges */
if (likely (right->x != right->next->x))
if (winding == 0 && right->x != right->next->x)
break;
}
 
right = right->next;
} while (TRUE);
 
edge_start_or_continue_box (sweep,
left, right, top,
do_traps, container);
edge_start_or_continue_box (sweep, left, right, top);
 
pos = right->next;
} while (pos != &sweep->tail);
443,23 → 520,17
 
do {
/* End all subsumed traps */
if (unlikely (right->right != NULL)) {
edge_end_box (sweep,
right, top, do_traps, container);
}
if (unlikely (right->right != NULL))
edge_end_box (sweep, right, top);
 
if (++count & 1) {
/* skip co-linear edges */
if (likely (right->x != right->next->x))
if (++count & 1 && right->x != right->next->x)
break;
}
 
right = right->next;
} while (TRUE);
 
edge_start_or_continue_box (sweep,
pos, right, top,
do_traps, container);
edge_start_or_continue_box (sweep, pos, right, top);
 
pos = right->next;
} while (pos != &sweep->tail);
469,10 → 540,7
}
 
static inline void
sweep_line_delete_edge (sweep_line_t *sweep_line,
edge_t *edge,
cairo_bool_t do_traps,
void *container)
sweep_line_delete_edge (sweep_line_t *sweep, edge_t *edge)
{
if (edge->right != NULL) {
edge_t *next = edge->next;
479,18 → 547,12
if (next->x == edge->x) {
next->top = edge->top;
next->right = edge->right;
} else {
edge_end_box (sweep_line,
edge,
sweep_line->current_y,
do_traps, container);
} else
edge_end_box (sweep, edge, sweep->current_y);
}
}
 
if (sweep_line->insert_left == edge)
sweep_line->insert_left = edge->next;
if (sweep_line->insert_right == edge)
sweep_line->insert_right = edge->next;
if (sweep->cursor == edge)
sweep->cursor = edge->prev;
 
edge->prev->next = edge->next;
edge->next->prev = edge->prev;
497,10 → 559,7
}
 
static inline cairo_bool_t
sweep_line_delete (sweep_line_t *sweep,
rectangle_t *rectangle,
cairo_bool_t do_traps,
void *container)
sweep_line_delete (sweep_line_t *sweep, rectangle_t *rectangle)
{
cairo_bool_t update;
 
511,76 → 570,29
update = rectangle->left.next != &rectangle->right;
}
 
sweep_line_delete_edge (sweep,
&rectangle->left,
do_traps, container);
sweep_line_delete_edge (sweep, &rectangle->left);
sweep_line_delete_edge (sweep, &rectangle->right);
 
sweep_line_delete_edge (sweep,
&rectangle->right,
do_traps, container);
 
pqueue_pop (&sweep->pq);
rectangle_pop_stop (sweep);
return update;
}
 
static inline void
insert_edge (edge_t *edge, edge_t *pos)
sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle)
{
if (pos->x != edge->x) {
if (pos->x > edge->x) {
do {
UNROLL3({
if (pos->prev->x <= edge->x)
break;
pos = pos->prev;
})
} while (TRUE);
} else {
do {
UNROLL3({
pos = pos->next;
if (pos->x >= edge->x)
break;
})
} while (TRUE);
}
}
if (sweep->insert)
sweep->insert->prev = &rectangle->right;
rectangle->right.next = sweep->insert;
rectangle->right.prev = &rectangle->left;
rectangle->left.next = &rectangle->right;
rectangle->left.prev = NULL;
sweep->insert = &rectangle->left;
if (rectangle->left.x < sweep->insert_x)
sweep->insert_x = rectangle->left.x;
 
pos->prev->next = edge;
edge->prev = pos->prev;
edge->next = pos;
pos->prev = edge;
}
 
static inline cairo_bool_t
sweep_line_insert (sweep_line_t *sweep,
rectangle_t *rectangle)
{
edge_t *pos;
 
/* right edge */
pos = sweep->insert_right;
insert_edge (&rectangle->right, pos);
sweep->insert_right = &rectangle->right;
 
/* left edge */
pos = sweep->insert_left;
if (pos->x > sweep->insert_right->x)
pos = sweep->insert_right->prev;
insert_edge (&rectangle->left, pos);
sweep->insert_left = &rectangle->left;
 
pqueue_push (sweep, rectangle);
 
if (sweep->fill_rule == CAIRO_FILL_RULE_WINDING &&
rectangle->left.prev->dir == rectangle->left.dir)
{
return rectangle->left.next != &rectangle->right;
}
 
return TRUE;
}
 
static cairo_status_t
_cairo_bentley_ottmann_tessellate_rectangular (rectangle_t **rectangles,
int num_rectangles,
593,9 → 605,12
cairo_status_t status;
cairo_bool_t update = FALSE;
 
sweep_line_init (&sweep_line, rectangles, num_rectangles, fill_rule);
sweep_line_init (&sweep_line,
rectangles, num_rectangles,
fill_rule,
do_traps, container);
if ((status = setjmp (sweep_line.unwind)))
goto unwind;
return status;
 
rectangle = rectangle_pop_start (&sweep_line);
do {
606,8 → 621,7
while (stop != NULL && stop->bottom < rectangle->top) {
if (stop->bottom != sweep_line.current_y) {
if (update) {
active_edges_to_traps (&sweep_line,
do_traps, container);
active_edges_to_traps (&sweep_line);
update = FALSE;
}
 
614,13 → 628,12
sweep_line.current_y = stop->bottom;
}
 
update |= sweep_line_delete (&sweep_line, stop, do_traps, container);
 
update |= sweep_line_delete (&sweep_line, stop);
stop = rectangle_peek_stop (&sweep_line);
}
 
if (update) {
active_edges_to_traps (&sweep_line, do_traps, container);
active_edges_to_traps (&sweep_line);
update = FALSE;
}
 
627,25 → 640,26
sweep_line.current_y = rectangle->top;
}
 
update |= sweep_line_insert (&sweep_line, rectangle);
} while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL);
do {
sweep_line_insert (&sweep_line, rectangle);
} while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL &&
sweep_line.current_y == rectangle->top);
update = TRUE;
} while (rectangle);
 
while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
if (rectangle->bottom != sweep_line.current_y) {
if (update) {
active_edges_to_traps (&sweep_line, do_traps, container);
active_edges_to_traps (&sweep_line);
update = FALSE;
}
 
sweep_line.current_y = rectangle->bottom;
}
 
update |= sweep_line_delete (&sweep_line, rectangle, do_traps, container);
update |= sweep_line_delete (&sweep_line, rectangle);
}
 
unwind:
sweep_line_fini (&sweep_line);
return status;
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
653,9 → 667,8
cairo_fill_rule_t fill_rule)
{
rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
rectangle_t *rectangles;
rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
rectangle_t **rectangles_ptrs;
rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
rectangle_t *rectangles, **rectangles_ptrs;
cairo_status_t status;
int i;
 
672,7 → 685,7
rectangles = _cairo_malloc_ab_plus_c (traps->num_traps,
sizeof (rectangle_t) +
sizeof (rectangle_t *),
sizeof (rectangle_t *));
3*sizeof (rectangle_t *));
if (unlikely (rectangles == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
700,11 → 713,13
rectangles[i].top = traps->traps[i].top;
rectangles[i].bottom = traps->traps[i].bottom;
 
rectangles_ptrs[i] = &rectangles[i];
rectangles_ptrs[i+2] = &rectangles[i];
}
/* XXX incremental sort */
_rectangle_sort (rectangles_ptrs+2, i);
 
_cairo_traps_clear (traps);
status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, i,
status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i,
fill_rule,
TRUE, traps);
traps->is_rectilinear = TRUE;
724,16 → 739,68
cairo_boxes_t *out)
{
rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
rectangle_t *rectangles;
rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
rectangle_t **rectangles_ptrs;
rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3];
rectangle_t *rectangles, **rectangles_ptrs;
rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ];
rectangle_t **rectangles_chain = NULL;
const struct _cairo_boxes_chunk *chunk;
cairo_status_t status;
int i, j;
int i, j, y_min, y_max;
 
if (unlikely (in->num_boxes <= 1))
if (unlikely (in->num_boxes == 0)) {
_cairo_boxes_clear (out);
return CAIRO_STATUS_SUCCESS;
}
 
if (in->num_boxes == 1) {
if (in == out) {
cairo_box_t *box = &in->chunks.base[0];
 
if (box->p1.x > box->p2.x) {
cairo_fixed_t tmp = box->p1.x;
box->p1.x = box->p2.x;
box->p2.x = tmp;
}
} else {
cairo_box_t box = in->chunks.base[0];
 
if (box.p1.x > box.p2.x) {
cairo_fixed_t tmp = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = tmp;
}
 
_cairo_boxes_clear (out);
status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_STATUS_SUCCESS);
}
return CAIRO_STATUS_SUCCESS;
}
 
y_min = INT_MAX; y_max = INT_MIN;
for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
for (i = 0; i < chunk->count; i++) {
if (box[i].p1.y < y_min)
y_min = box[i].p1.y;
if (box[i].p1.y > y_max)
y_max = box[i].p1.y;
}
}
y_min = _cairo_fixed_integer_floor (y_min);
y_max = _cairo_fixed_integer_floor (y_max) + 1;
y_max -= y_min;
 
if (y_max < in->num_boxes) {
rectangles_chain = stack_rectangles_chain;
if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) {
rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *));
if (unlikely (rectangles_chain == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*));
}
 
rectangles = stack_rectangles;
rectangles_ptrs = stack_rectangles_ptrs;
if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) {
740,9 → 807,12
rectangles = _cairo_malloc_ab_plus_c (in->num_boxes,
sizeof (rectangle_t) +
sizeof (rectangle_t *),
sizeof (rectangle_t *));
if (unlikely (rectangles == NULL))
3*sizeof (rectangle_t *));
if (unlikely (rectangles == NULL)) {
if (rectangles_chain != stack_rectangles_chain)
free (rectangles_chain);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes);
}
751,6 → 821,8
for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
for (i = 0; i < chunk->count; i++) {
int h;
 
if (box[i].p1.x < box[i].p2.x) {
rectangles[j].left.x = box[i].p1.x;
rectangles[j].left.dir = 1;
771,13 → 843,38
rectangles[j].top = box[i].p1.y;
rectangles[j].bottom = box[i].p2.y;
 
rectangles_ptrs[j] = &rectangles[j];
if (rectangles_chain) {
h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min;
rectangles[j].left.next = (edge_t *)rectangles_chain[h];
rectangles_chain[h] = &rectangles[j];
} else {
rectangles_ptrs[j+2] = &rectangles[j];
}
j++;
}
}
 
if (rectangles_chain) {
j = 2;
for (y_min = 0; y_min < y_max; y_min++) {
rectangle_t *r;
int start = j;
for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next)
rectangles_ptrs[j++] = r;
if (j > start + 1)
_rectangle_sort (rectangles_ptrs + start, j - start);
}
 
if (rectangles_chain != stack_rectangles_chain)
free (rectangles_chain);
 
j -= 2;
} else {
_rectangle_sort (rectangles_ptrs + 2, j);
}
 
_cairo_boxes_clear (out);
status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs, j,
status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j,
fill_rule,
FALSE, out);
if (rectangles != stack_rectangles)
/programs/develop/libraries/cairo/src/cairo-bentley-ottmann-rectilinear.c
39,8 → 39,9
#include "cairoint.h"
 
#include "cairo-boxes-private.h"
#include "cairo-combsort-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-error-private.h"
#include "cairo-traps-private.h"
 
typedef struct _cairo_bo_edge cairo_bo_edge_t;
typedef struct _cairo_bo_trap cairo_bo_trap_t;
237,7 → 238,7
box.p1.y = trap->top;
box.p2.x = trap->right->edge.line.p1.x;
box.p2.y = bot;
status = _cairo_boxes_add (container, &box);
status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box);
}
}
 
437,74 → 438,6
}
 
cairo_status_t
_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps,
const cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule)
{
cairo_status_t status;
cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)];
cairo_bo_event_t *events;
cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
cairo_bo_event_t **event_ptrs;
cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)];
cairo_bo_edge_t *edges;
int num_events;
int i, j;
 
if (unlikely (polygon->num_edges == 0))
return CAIRO_STATUS_SUCCESS;
 
num_events = 2 * polygon->num_edges;
 
events = stack_events;
event_ptrs = stack_event_ptrs;
edges = stack_edges;
if (num_events > ARRAY_LENGTH (stack_events)) {
events = _cairo_malloc_ab_plus_c (num_events,
sizeof (cairo_bo_event_t) +
sizeof (cairo_bo_edge_t) +
sizeof (cairo_bo_event_t *),
sizeof (cairo_bo_event_t *));
if (unlikely (events == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
event_ptrs = (cairo_bo_event_t **) (events + num_events);
edges = (cairo_bo_edge_t *) (event_ptrs + num_events + 1);
}
 
for (i = j = 0; i < polygon->num_edges; i++) {
edges[i].edge = polygon->edges[i];
edges[i].deferred_trap.right = NULL;
edges[i].prev = NULL;
edges[i].next = NULL;
 
event_ptrs[j] = &events[j];
events[j].type = CAIRO_BO_EVENT_TYPE_START;
events[j].point.y = polygon->edges[i].top;
events[j].point.x = polygon->edges[i].line.p1.x;
events[j].edge = &edges[i];
j++;
 
event_ptrs[j] = &events[j];
events[j].type = CAIRO_BO_EVENT_TYPE_STOP;
events[j].point.y = polygon->edges[i].bottom;
events[j].point.x = polygon->edges[i].line.p1.x;
events[j].edge = &edges[i];
j++;
}
 
status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j,
fill_rule,
TRUE, traps);
if (events != stack_events)
free (events);
 
traps->is_rectilinear = TRUE;
 
return status;
}
 
cairo_status_t
_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_boxes_t *boxes)
/programs/develop/libraries/cairo/src/cairo-bentley-ottmann.c
40,7 → 40,8
 
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-traps-private.h"
 
#define DEBUG_PRINT_STATE 0
#define DEBUG_EVENTS 0
71,6 → 72,7
cairo_edge_t edge;
cairo_bo_edge_t *prev;
cairo_bo_edge_t *next;
cairo_bo_edge_t *colinear;
cairo_bo_trap_t deferred_trap;
};
 
560,8 → 562,8
a->p2.x == b->p2.x && a->p2.y == b->p2.y;
}
 
static int
_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line,
static inline int
_cairo_bo_sweep_line_compare_edges (const cairo_bo_sweep_line_t *sweep_line,
const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b)
{
569,6 → 571,13
 
/* compare the edges if not identical */
if (! _line_equal (&a->edge.line, &b->edge.line)) {
if (MAX (a->edge.line.p1.x, a->edge.line.p2.x) <
MIN (b->edge.line.p1.x, b->edge.line.p2.x))
return -1;
else if (MIN (a->edge.line.p1.x, a->edge.line.p2.x) >
MAX (b->edge.line.p1.x, b->edge.line.p2.x))
return 1;
 
cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
if (cmp)
return cmp;
1040,9 → 1049,6
cairo_bo_event_t **start_events,
int num_events)
{
_cairo_bo_event_queue_sort (start_events, num_events);
start_events[num_events] = NULL;
 
event_queue->start_events = start_events;
 
_cairo_freepool_init (&event_queue->pool,
1080,6 → 1086,10
{
cairo_bo_point32_t intersection;
 
if (MAX (left->edge.line.p1.x, left->edge.line.p2.x) <=
MIN (right->edge.line.p1.x, right->edge.line.p2.x))
return CAIRO_STATUS_SUCCESS;
 
if (_line_equal (&left->edge.line, &right->edge.line))
return CAIRO_STATUS_SUCCESS;
 
1109,7 → 1119,7
sweep_line->current_edge = NULL;
}
 
static cairo_status_t
static void
_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *edge)
{
1162,11 → 1172,10
}
} else {
sweep_line->head = edge;
edge->next = NULL;
}
 
sweep_line->current_edge = edge;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
1300,33 → 1309,61
}
#endif
 
#define HAS_COLINEAR(a, b) ((cairo_bo_edge_t *)(((uintptr_t)(a))&~1) == (b))
#define IS_COLINEAR(e) (((uintptr_t)(e))&1)
#define MARK_COLINEAR(e, v) ((cairo_bo_edge_t *)(((uintptr_t)(e))|(v)))
 
static inline cairo_bool_t
edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
edges_colinear (cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
{
if (_line_equal (&a->edge.line, &b->edge.line))
unsigned p;
 
if (HAS_COLINEAR(a->colinear, b))
return IS_COLINEAR(a->colinear);
 
if (HAS_COLINEAR(b->colinear, a)) {
p = IS_COLINEAR(b->colinear);
a->colinear = MARK_COLINEAR(b, p);
return p;
}
 
p = 0;
p |= (a->edge.line.p1.x == b->edge.line.p1.x) << 0;
p |= (a->edge.line.p1.y == b->edge.line.p1.y) << 1;
p |= (a->edge.line.p2.x == b->edge.line.p2.x) << 3;
p |= (a->edge.line.p2.y == b->edge.line.p2.y) << 4;
if (p == ((1 << 0) | (1 << 1) | (1 << 3) | (1 << 4))) {
a->colinear = MARK_COLINEAR(b, 1);
return TRUE;
}
 
if (_slope_compare (a, b))
if (_slope_compare (a, b)) {
a->colinear = MARK_COLINEAR(b, 0);
return FALSE;
}
 
/* The choice of y is not truly arbitrary since we must guarantee that it
* is greater than the start of either line.
*/
if (a->edge.line.p1.y == b->edge.line.p1.y) {
return a->edge.line.p1.x == b->edge.line.p1.x;
if (p != 0) {
/* colinear if either end-point are coincident */
p = (((p >> 1) & p) & 5) != 0;
} else if (a->edge.line.p1.y < b->edge.line.p1.y) {
return edge_compare_for_y_against_x (b,
p = edge_compare_for_y_against_x (b,
a->edge.line.p1.y,
a->edge.line.p1.x) == 0;
} else {
return edge_compare_for_y_against_x (a,
p = edge_compare_for_y_against_x (a,
b->edge.line.p1.y,
b->edge.line.p1.x) == 0;
}
 
a->colinear = MARK_COLINEAR(b, p);
return p;
}
 
/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */
static cairo_status_t
static void
_cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
int32_t bot,
cairo_traps_t *traps)
1358,8 → 1395,6
}
 
trap->right = NULL;
 
return _cairo_traps_status (traps);
}
 
 
1368,31 → 1403,28
* then either add it to the traps in `traps', if the trapezoid's
* right edge differs from `edge->next', or do nothing if the new
* trapezoid would be a continuation of the existing one. */
static inline cairo_status_t
static inline void
_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
cairo_bo_edge_t *right,
int top,
cairo_traps_t *traps)
{
cairo_status_t status;
 
if (left->deferred_trap.right == right)
return CAIRO_STATUS_SUCCESS;
return;
 
assert (right);
if (left->deferred_trap.right != NULL) {
if (right != NULL && edges_colinear (left->deferred_trap.right, right))
if (edges_colinear (left->deferred_trap.right, right))
{
/* continuation on right, so just swap edges */
left->deferred_trap.right = right;
return CAIRO_STATUS_SUCCESS;
return;
}
 
status = _cairo_bo_edge_end_trap (left, top, traps);
if (unlikely (status))
return status;
_cairo_bo_edge_end_trap (left, top, traps);
}
 
if (right != NULL && ! edges_colinear (left, right)) {
if (! edges_colinear (left, right)) {
left->deferred_trap.top = top;
left->deferred_trap.right = right;
 
1403,123 → 1435,55
top);
#endif
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline cairo_status_t
_active_edges_to_traps (cairo_bo_edge_t *left,
static inline void
_active_edges_to_traps (cairo_bo_edge_t *pos,
int32_t top,
cairo_fill_rule_t fill_rule,
unsigned mask,
cairo_traps_t *traps)
{
cairo_bo_edge_t *right;
cairo_status_t status;
cairo_bo_edge_t *left;
int in_out;
 
 
#if DEBUG_PRINT_STATE
printf ("Processing active edges for %x\n", top);
#endif
 
if (fill_rule == CAIRO_FILL_RULE_WINDING) {
while (left != NULL) {
int in_out;
 
/* Greedily search for the closing edge, so that we generate the
* maximal span width with the minimal number of trapezoids.
in_out = 0;
left = pos;
while (pos != NULL) {
if (pos != left && pos->deferred_trap.right) {
/* XXX It shouldn't be possible to here with 2 deferred traps
* on colinear edges... See bug-bo-rictoz.
*/
in_out = left->edge.dir;
 
/* Check if there is a co-linear edge with an existing trap */
right = left->next;
if (left->deferred_trap.right == NULL) {
while (right != NULL && right->deferred_trap.right == NULL)
right = right->next;
 
if (right != NULL && edges_colinear (left, right)) {
if (left->deferred_trap.right == NULL &&
edges_colinear (left, pos))
{
/* continuation on left */
left->deferred_trap = right->deferred_trap;
right->deferred_trap.right = NULL;
left->deferred_trap = pos->deferred_trap;
pos->deferred_trap.right = NULL;
}
else
{
_cairo_bo_edge_end_trap (pos, top, traps);
}
 
/* End all subsumed traps */
right = left->next;
while (right != NULL) {
if (right->deferred_trap.right != NULL) {
status = _cairo_bo_edge_end_trap (right, top, traps);
if (unlikely (status))
return status;
}
 
in_out += right->edge.dir;
if (in_out == 0) {
cairo_bo_edge_t *next;
cairo_bool_t skip = FALSE;
 
in_out += pos->edge.dir;
if ((in_out & mask) == 0) {
/* skip co-linear edges */
next = right->next;
if (next != NULL)
skip = edges_colinear (right, next);
 
if (! skip)
break;
if (pos->next == NULL || ! edges_colinear (pos, pos->next)) {
_cairo_bo_edge_start_or_continue_trap (left, pos, top, traps);
left = pos->next;
}
 
right = right->next;
}
 
status = _cairo_bo_edge_start_or_continue_trap (left, right,
top, traps);
if (unlikely (status))
return status;
 
left = right;
if (left != NULL)
left = left->next;
pos = pos->next;
}
} else {
while (left != NULL) {
int in_out = 0;
 
right = left->next;
while (right != NULL) {
if (right->deferred_trap.right != NULL) {
status = _cairo_bo_edge_end_trap (right, top, traps);
if (unlikely (status))
return status;
}
 
if ((in_out++ & 1) == 0) {
cairo_bo_edge_t *next;
cairo_bool_t skip = FALSE;
 
/* skip co-linear edges */
next = right->next;
if (next != NULL)
skip = edges_colinear (right, next);
 
if (! skip)
break;
}
 
right = right->next;
}
 
status = _cairo_bo_edge_start_or_continue_trap (left, right,
top, traps);
if (unlikely (status))
return status;
 
left = right;
if (left != NULL)
left = left->next;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
 
/* Execute a single pass of the Bentley-Ottmann algorithm on edges,
* generating trapezoids according to the fill_rule and appending them
* to traps. */
1526,11 → 1490,11
static cairo_status_t
_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events,
int num_events,
cairo_fill_rule_t fill_rule,
unsigned fill_rule,
cairo_traps_t *traps,
int *num_intersections)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
cairo_status_t status;
int intersection_count = 0;
cairo_bo_event_queue_t event_queue;
cairo_bo_sweep_line_t sweep_line;
1538,6 → 1502,12
cairo_bo_edge_t *left, *right;
cairo_bo_edge_t *e1, *e2;
 
/* convert the fill_rule into a winding mask */
if (fill_rule == CAIRO_FILL_RULE_WINDING)
fill_rule = (unsigned) -1;
else
fill_rule = 1;
 
#if DEBUG_EVENTS
{
int i;
1565,20 → 1535,16
if (event->point.y != sweep_line.current_y) {
for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
if (e1->deferred_trap.right != NULL) {
status = _cairo_bo_edge_end_trap (e1,
_cairo_bo_edge_end_trap (e1,
e1->edge.bottom,
traps);
if (unlikely (status))
goto unwind;
}
}
sweep_line.stopped = NULL;
 
status = _active_edges_to_traps (sweep_line.head,
_active_edges_to_traps (sweep_line.head,
sweep_line.current_y,
fill_rule, traps);
if (unlikely (status))
goto unwind;
 
sweep_line.current_y = event->point.y;
}
1596,9 → 1562,7
case CAIRO_BO_EVENT_TYPE_START:
e1 = &((cairo_bo_start_event_t *) event)->edge;
 
status = _cairo_bo_sweep_line_insert (&sweep_line, e1);
if (unlikely (status))
goto unwind;
_cairo_bo_sweep_line_insert (&sweep_line, e1);
 
status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
if (unlikely (status))
1701,11 → 1665,10
*num_intersections = intersection_count;
for (e1 = sweep_line.stopped; e1; e1 = e1->next) {
if (e1->deferred_trap.right != NULL) {
status = _cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps);
if (unlikely (status))
break;
_cairo_bo_edge_end_trap (e1, e1->edge.bottom, traps);
}
}
status = traps->status;
unwind:
_cairo_bo_event_queue_fini (&event_queue);
 
1722,18 → 1685,30
cairo_fill_rule_t fill_rule)
{
int intersections;
cairo_status_t status;
cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
cairo_bo_start_event_t *events;
cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
cairo_bo_event_t **event_ptrs;
int num_events;
int i;
cairo_bo_start_event_t *stack_event_y[64];
cairo_bo_start_event_t **event_y = NULL;
int i, num_events, y, ymin, ymax;
cairo_status_t status;
 
num_events = polygon->num_edges;
if (unlikely (0 == num_events))
return CAIRO_STATUS_SUCCESS;
 
if (polygon->num_limits) {
ymin = _cairo_fixed_integer_floor (polygon->limit.p1.y);
ymax = _cairo_fixed_integer_ceil (polygon->limit.p2.y) - ymin;
 
if (ymax > 64)
event_y = _cairo_malloc_ab(sizeof (cairo_bo_event_t*), ymax);
else
event_y = stack_event_y;
memset (event_y, 0, ymax * sizeof(cairo_bo_event_t *));
}
 
events = stack_events;
event_ptrs = stack_event_ptrs;
if (num_events > ARRAY_LENGTH (stack_events)) {
1748,8 → 1723,6
}
 
for (i = 0; i < num_events; i++) {
event_ptrs[i] = (cairo_bo_event_t *) &events[i];
 
events[i].type = CAIRO_BO_EVENT_TYPE_START;
events[i].point.y = polygon->edges[i].top;
events[i].point.x =
1760,8 → 1733,31
events[i].edge.deferred_trap.right = NULL;
events[i].edge.prev = NULL;
events[i].edge.next = NULL;
events[i].edge.colinear = NULL;
 
if (event_y) {
y = _cairo_fixed_integer_floor (events[i].point.y) - ymin;
events[i].edge.next = (cairo_bo_edge_t *) event_y[y];
event_y[y] = (cairo_bo_start_event_t *) &events[i];
} else
event_ptrs[i] = (cairo_bo_event_t *) &events[i];
}
 
if (event_y) {
for (y = i = 0; y < ymax && i < num_events; y++) {
cairo_bo_start_event_t *e;
int j = i;
for (e = event_y[y]; e; e = (cairo_bo_start_event_t *)e->edge.next)
event_ptrs[i++] = (cairo_bo_event_t *) e;
if (i > j + 1)
_cairo_bo_event_queue_sort (event_ptrs+j, i-j);
}
if (event_y != stack_event_y)
free (event_y);
} else
_cairo_bo_event_queue_sort (event_ptrs, i);
event_ptrs[i] = NULL;
 
#if DEBUG_TRAPS
dump_edges (events, num_events, "bo-polygon-edges.txt");
#endif
1770,8 → 1766,7
* passes of the Bentley-Ottmann algorithm. It would merely
* require storing the results of each pass into a temporary
* cairo_traps_t. */
status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
num_events,
status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events,
fill_rule, traps,
&intersections);
#if DEBUG_TRAPS
1799,8 → 1794,7
dump_traps (traps, "bo-traps-in.txt");
#endif
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
_cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
 
for (i = 0; i < traps->num_traps; i++) {
status = _cairo_polygon_add_line (&polygon,
/programs/develop/libraries/cairo/src/cairo-beos-surface.cpp
0,0 → 1,984
/* vim:set ts=8 sw=4 noet cin: */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Christian Biesinger <cbiesinger@web.de>
*
* 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 Christian Biesinger
* <cbiesinger@web.de>
*
* Contributor(s):
*/
 
// This is a C++ file in order to use the C++ BeOS API
 
#include "cairoint.h"
 
#include "cairo-beos.h"
 
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
 
#include <new>
 
#include <Bitmap.h>
#include <Region.h>
#if 0
#include <DirectWindow.h>
#endif
#include <Screen.h>
#include <Window.h>
#include <Locker.h>
 
/**
* SECTION:beos-surface
* @Title: BeOS Surfaces
* @Short_Description: BeOS surface support
* @See_Also: #cairo_surface_t
*
* The BeOS surface is used to render cairo graphics to BeOS views
* and bitmaps.
**/
 
#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)(CAIRO_STATUS_SUCCESS)
 
struct cairo_beos_surface_t {
cairo_surface_t base;
 
cairo_region_t *clip_region;
 
BView* view;
 
/*
* A view is either attached to a bitmap, a window, or unattached.
* If it is attached to a window, we can copy data out of it using BScreen.
* If it is attached to a bitmap, we can read the bitmap data.
* If it is not attached, it doesn't draw anything, we need not bother.
*
* Since there doesn't seem to be a way to get the bitmap from a view if it
* is attached to one, we have to use a special surface creation function.
*/
 
BBitmap* bitmap;
 
// If true, surface and view should be deleted when this surface is
// destroyed
bool owns_bitmap_view;
};
 
class AutoLockView {
public:
AutoLockView(BView* view) : mView(view) {
mOK = mView->LockLooper();
}
 
~AutoLockView() {
if (mOK)
mView->UnlockLooper();
}
 
operator bool() {
return mOK;
}
 
private:
BView* mView;
bool mOK;
};
 
static cairo_surface_t *
_cairo_beos_surface_create_internal (BView* view,
BBitmap* bmp,
bool owns_bitmap_view = false);
 
static inline BRect
_cairo_rectangle_to_brect (const cairo_rectangle_int_t* rect)
{
// A BRect is one pixel wider than you'd think
return BRect (rect->x, rect->y,
rect->x + rect->width - 1,
rect->y + rect->height - 1);
}
 
static inline cairo_rectangle_int_t
_brect_to_cairo_rectangle (const BRect &rect)
{
cairo_rectangle_int_t retval;
retval.x = floor (rect.left);
retval.y = floor (rect.top);
retval.width = ceil (rect.right) - retval.x + 1;
retval.height = ceil (rect.bottom) - rectval.y + 1;
return retval;
}
 
static inline rgb_color
_cairo_color_to_be_color (const cairo_color_t *color)
{
// This factor ensures a uniform distribution of numbers
const float factor = 256 - 1e-5;
// Using doubles to have non-premultiplied colors
rgb_color be_color = { uint8(color->red * factor),
uint8(color->green * factor),
uint8(color->blue * factor),
uint8(color->alpha * factor) };
 
return be_color;
}
 
enum ViewCopyStatus {
OK,
NOT_VISIBLE, // The view or the interest rect is not visible on screen
ERROR // The view was visible, but the rect could not be copied. Probably OOM
};
 
/**
* _cairo_beos_view_to_bitmap:
* @bitmap: [out] The resulting bitmap.
* @rect: [out] The rectangle that was copied, in the view's coordinate system
* @interestRect: If non-null, only this part of the view will be copied (view's coord system).
*
* Gets the contents of the view as a BBitmap*. Caller must delete the bitmap.
**/
static ViewCopyStatus
_cairo_beos_view_to_bitmap (BView* view,
BBitmap** bitmap,
BRect* rect = NULL,
const BRect* interestRect = NULL)
{
*bitmap = NULL;
 
BWindow* wnd = view->Window();
// If we have no window, can't do anything
if (!wnd)
return NOT_VISIBLE;
 
view->Sync();
wnd->Sync();
 
#if 0
// Is it a direct window?
BDirectWindow* directWnd = dynamic_cast<BDirectWindow*>(wnd);
if (directWnd) {
// WRITEME
}
#endif
 
// Is it visible? If so, we can copy the content off the screen
if (wnd->IsHidden())
return NOT_VISIBLE;
 
BRect rectToCopy(view->Bounds());
if (interestRect)
rectToCopy = rectToCopy & *interestRect;
 
if (!rectToCopy.IsValid())
return NOT_VISIBLE;
 
BScreen screen(wnd);
BRect screenRect(view->ConvertToScreen(rectToCopy));
screenRect = screenRect & screen.Frame();
 
if (!screen.IsValid())
return NOT_VISIBLE;
 
if (rect)
*rect = view->ConvertFromScreen(screenRect);
 
if (screen.GetBitmap(bitmap, false, &screenRect) == B_OK)
return OK;
 
return ERROR;
}
 
static void
unpremultiply_bgra (unsigned char* data,
int width,
int height,
int stride,
unsigned char* retdata)
{
unsigned char* end = data + stride * height;
for (unsigned char* in = data, *out = retdata;
in < end;
in += stride, out += stride)
{
for (int i = 0; i < width; i ++) {
uint8_t *b = &out[4*i];
uint32_t pixel;
uint8_t alpha;
 
memcpy (&pixel, &data[4*i], sizeof (uint32_t));
alpha = pixel & 0xff;
if (alpha == 0) {
b[0] = b[1] = b[2] = b[3] = 0;
} else {
b[0] = (((pixel >> 24) & 0xff) * 255 + alpha / 2) / alpha;
b[1] = (((pixel >> 16) & 0xff) * 255 + alpha / 2) / alpha;
b[2] = (((pixel >> 8) & 0xff) * 255 + alpha / 2) / alpha;
b[3] = alpha;
}
}
}
}
 
static inline int
multiply_alpha (int alpha, int color)
{
int temp = (alpha * color) + 0x80;
return ((temp + (temp >> 8)) >> 8);
}
 
static unsigned char*
premultiply_bgra (unsigned char* data,
int width,
int height,
int stride)
{
uint8_t * retdata = reinterpret_cast<unsigned char*>(_cairo_malloc_ab(height, stride));
if (!retdata)
return NULL;
 
uint8_t * end = data + stride * height;
for (uint8_t * in = data, *out = retdata;
in < end;
in += stride, out += stride)
{
for (int i = 0; i < width; i ++) {
uint8_t *base = &in[4*i];
uint8_t alpha = base[3];
uint32_t p;
 
if (alpha == 0) {
p = 0;
} else {
uint8_t blue = base[0];
uint8_t green = base[1];
uint8_t red = base[2];
 
if (alpha != 0xff) {
blue = multiply_alpha (alpha, blue);
green = multiply_alpha (alpha, green);
red = multiply_alpha (alpha, red);
}
p = (alpha << 0) | (red << 8) | (green << 16) | (blue << 24);
}
memcpy (&out[4*i], &p, sizeof (uint32_t));
}
}
return retdata;
}
 
static cairo_int_status_t
_cairo_beos_surface_set_clip_region (cairo_beos_surface_t *surface,
cairo_region_t *region)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
AutoLockView locker(surface->view);
assert (locker);
 
if (region == surface->clip_region)
return CAIRO_INT_STATUS_SUCCESS;
 
cairo_region_destroy (surface->clip_region);
surface->clip_region = cairo_region_reference (region);
 
if (region == NULL) {
// No clipping
surface->view->ConstrainClippingRegion(NULL);
return CAIRO_INT_STATUS_SUCCESS;
}
 
int count = cairo_region_num_rectangles (region);
BRegion bregion;
for (int i = 0; i < count; ++i) {
cairo_rectangle_int_t rect;
 
cairo_region_get_rectangle (region, i, &rect);
// Have to subtract one, because for pixman, the second coordinate
// lies outside the rectangle.
bregion.Include (_cairo_rectangle_to_brect (&rect));
}
surface->view->ConstrainClippingRegion(&bregion);
return CAIRO_INT_STATUS_SUCCESS;
}
 
 
/**
* _cairo_beos_bitmap_to_surface:
*
* Returns an addrefed image surface for a BBitmap. The bitmap need not outlive
* the surface.
**/
static cairo_image_surface_t*
_cairo_beos_bitmap_to_surface (BBitmap* bitmap)
{
color_space format = bitmap->ColorSpace();
if (format != B_RGB32 && format != B_RGBA32) {
BBitmap bmp(bitmap->Bounds(), B_RGB32, true);
BView view(bitmap->Bounds(), "Cairo bitmap drawing view",
B_FOLLOW_ALL_SIDES, 0);
bmp.AddChild(&view);
 
view.LockLooper();
 
view.DrawBitmap(bitmap, BPoint(0.0, 0.0));
view.Sync();
 
cairo_image_surface_t* imgsurf = _cairo_beos_bitmap_to_surface(&bmp);
 
view.UnlockLooper();
bmp.RemoveChild(&view);
return imgsurf;
}
 
cairo_format_t cformat = format == B_RGB32 ?
CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
 
BRect bounds(bitmap->Bounds());
unsigned char* bits = reinterpret_cast<unsigned char*>(bitmap->Bits());
int width = bounds.IntegerWidth() + 1;
int height = bounds.IntegerHeight() + 1;
unsigned char* premultiplied;
if (cformat == CAIRO_FORMAT_ARGB32) {
premultiplied = premultiply_bgra (bits, width, height,
bitmap->BytesPerRow());
} else {
premultiplied = reinterpret_cast<unsigned char*>(
_cairo_malloc_ab(bitmap->BytesPerRow(), height));
if (premultiplied)
memcpy(premultiplied, bits, bitmap->BytesPerRow() * height);
}
if (!premultiplied)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
cairo_image_surface_t* surf = reinterpret_cast<cairo_image_surface_t*>
(cairo_image_surface_create_for_data(premultiplied,
cformat,
width,
height,
bitmap->BytesPerRow()));
if (surf->base.status)
free(premultiplied);
else
_cairo_image_surface_assume_ownership_of_data(surf);
return surf;
}
 
/**
* _cairo_image_surface_to_bitmap:
*
* Converts an image surface to a BBitmap. The return value must be freed with
* delete.
**/
static BBitmap*
_cairo_image_surface_to_bitmap (cairo_image_surface_t* surface)
{
BRect size(0.0, 0.0, surface->width - 1, surface->height - 1);
switch (surface->format) {
case CAIRO_FORMAT_ARGB32: {
BBitmap* data = new BBitmap(size, B_RGBA32);
unpremultiply_bgra (surface->data,
surface->width,
surface->height,
surface->stride,
reinterpret_cast<unsigned char*>(data->Bits()));
return data;
}
case CAIRO_FORMAT_RGB24: {
BBitmap* data = new BBitmap(size, B_RGB32);
memcpy(data->Bits(), surface->data, surface->height * surface->stride);
return data;
}
default:
assert(0);
return NULL;
}
}
 
/**
* _cairo_op_to_be_op:
*
* Converts a cairo drawing operator to a beos drawing_mode. Returns true if
* the operator could be converted, false otherwise.
**/
static bool
_cairo_op_to_be_op (cairo_operator_t cairo_op,
drawing_mode* beos_op)
{
switch (cairo_op) {
case CAIRO_OPERATOR_SOURCE:
*beos_op = B_OP_COPY;
return true;
case CAIRO_OPERATOR_OVER:
*beos_op = B_OP_ALPHA;
return true;
 
case CAIRO_OPERATOR_ADD:
// Does not actually work
// XXX This is a fundamental compositing operator, it has to work!
#if 1
return false;
#else
*beos_op = B_OP_ADD;
return true;
#endif
 
case CAIRO_OPERATOR_CLEAR:
// Does not map to B_OP_ERASE - it replaces the dest with the low
// color, instead of transparency; could be done by setting low
// color appropriately.
 
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
 
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
 
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_SATURATE:
 
default:
return false;
}
}
 
static cairo_surface_t *
_cairo_beos_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
 
if (width <= 0)
width = 1;
if (height <= 0)
height = 1;
 
BRect rect(0.0, 0.0, width - 1, height - 1);
BBitmap* bmp;
switch (content) {
case CAIRO_CONTENT_ALPHA:
return NULL;
case CAIRO_CONTENT_COLOR_ALPHA:
bmp = new BBitmap(rect, B_RGBA32, true);
break;
case CAIRO_CONTENT_COLOR:
// Match the color depth
if (surface->bitmap) {
color_space space = surface->bitmap->ColorSpace();
// No alpha was requested -> make sure not to return
// a surface with alpha
if (space == B_RGBA32)
space = B_RGB32;
if (space == B_RGBA15)
space = B_RGB15;
bmp = new BBitmap(rect, space, true);
} else {
BScreen scr(surface->view->Window());
color_space space = B_RGB32;
if (scr.IsValid())
space = scr.ColorSpace();
bmp = new BBitmap(rect, space, true);
}
break;
default:
ASSERT_NOT_REACHED;
return NULL;
}
BView* view = new BView(rect, "Cairo bitmap view", B_FOLLOW_ALL_SIDES, 0);
bmp->AddChild(view);
return _cairo_beos_surface_create_internal(view, bmp, true);
}
 
static cairo_status_t
_cairo_beos_surface_finish (void *abstract_surface)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
if (surface->owns_bitmap_view) {
if (surface->bitmap)
surface->bitmap->RemoveChild(surface->view);
 
delete surface->view;
delete surface->bitmap;
 
surface->view = NULL;
surface->bitmap = NULL;
}
 
cairo_region_destroy (surface->clip_region);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_beos_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
AutoLockView locker(surface->view);
if (!locker)
return CAIRO_STATUS_NO_MEMORY; /// XXX not exactly right, but what can we do?
 
 
surface->view->Sync();
 
if (surface->bitmap) {
*image_out = _cairo_beos_bitmap_to_surface (surface->bitmap);
if (unlikely ((*image_out)->base.status))
return (*image_out)->base.status;
 
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
BBitmap* bmp;
if (_cairo_beos_view_to_bitmap(surface->view, &bmp) != OK)
return CAIRO_STATUS_NO_MEMORY; /// XXX incorrect if the error was NOT_VISIBLE
 
*image_out = _cairo_beos_bitmap_to_surface (bmp);
if (unlikely ((*image_out)->base.status)) {
delete bmp;
return (*image_out)->base.status;
}
*image_extra = bmp;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_beos_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
 
if (image_extra != NULL) {
BBitmap* bmp = static_cast<BBitmap*>(image_extra);
delete bmp;
}
}
 
static cairo_status_t
_cairo_beos_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,
void **image_extra)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
 
AutoLockView locker(surface->view);
if (!locker) {
*image_out = NULL;
*image_extra = NULL;
return (cairo_status_t) CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
if (surface->bitmap) {
surface->view->Sync();
*image_out = _cairo_beos_bitmap_to_surface(surface->bitmap);
if (unlikely ((*image_out)->base.status))
return (*image_out)->base.status;
 
image_rect->x = 0;
image_rect->y = 0;
image_rect->width = (*image_out)->width;
image_rect->height = (*image_out)->height;
 
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
BRect b_interest_rect (_cairo_rectangle_to_brect (interest_rect));
 
BRect rect;
BBitmap* bitmap;
ViewCopyStatus status = _cairo_beos_view_to_bitmap(surface->view, &bitmap,
&rect, &b_interest_rect);
if (status == NOT_VISIBLE) {
*image_out = NULL;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
if (status == ERROR)
return CAIRO_STATUS_NO_MEMORY;
 
*image_rect = _brect_to_cairo_rectangle(rect);
*image_out = _cairo_beos_bitmap_to_surface(bitmap);
delete bitmap;
if (unlikely ((*image_out)->base.status))
return (*image_out)->base.status;
 
*image_extra = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
 
static void
_cairo_beos_surface_release_dest_image (void *abstract_surface,
cairo_rectangle_int_t *intersect_rect,
cairo_image_surface_t *image,
cairo_rectangle_int_t *image_rect,
void *image_extra)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
 
AutoLockView locker(surface->view);
if (!locker)
return;
 
BBitmap* bitmap_to_draw = _cairo_image_surface_to_bitmap(image);
surface->view->PushState();
 
surface->view->SetDrawingMode(B_OP_COPY);
 
surface->view->DrawBitmap (bitmap_to_draw,
_cairo_rectangle_to_brect (image_rect));
 
surface->view->PopState();
 
delete bitmap_to_draw;
cairo_surface_destroy(&image->base);
}
 
static cairo_int_status_t
_cairo_beos_surface_composite (cairo_operator_t op,
cairo_pattern_t *src,
cairo_pattern_t *mask,
void *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_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
dst);
cairo_int_status_t status;
AutoLockView locker(surface->view);
if (!locker)
return CAIRO_INT_STATUS_SUCCESS;
 
drawing_mode mode;
if (!_cairo_op_to_be_op(op, &mode))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
// XXX Masks are not yet supported
if (mask)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
// XXX should eventually support the others
if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
src->extend != CAIRO_EXTEND_NONE)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
// Can we maybe support other matrices as well? (scale? if the filter is right)
int itx, ity;
if (!_cairo_matrix_is_integer_translation(&src->matrix, &itx, &ity))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_beos_surface_set_clip_region (surface, clip_region);
if (unlikely (status))
return status;
 
BRect srcRect(src_x + itx,
src_y + ity,
src_x + itx + width - 1,
src_y + ity + height - 1);
BRect dstRect(dst_x, dst_y, dst_x + width - 1, dst_y + height - 1);
 
cairo_surface_t* src_surface = reinterpret_cast<cairo_surface_pattern_t*>(src)->
surface;
 
// Get a bitmap
BBitmap* bmp = NULL;
bool free_bmp = false;
if (_cairo_surface_is_image(src_surface)) {
cairo_image_surface_t* img_surface =
reinterpret_cast<cairo_image_surface_t*>(src_surface);
 
bmp = _cairo_image_surface_to_bitmap(img_surface);
free_bmp = true;
} else if (src_surface->backend == surface->base.backend) {
cairo_beos_surface_t *beos_surface =
reinterpret_cast<cairo_beos_surface_t*>(src_surface);
if (beos_surface->bitmap) {
AutoLockView locker(beos_surface->view);
if (locker)
beos_surface->view->Sync();
bmp = beos_surface->bitmap;
} else {
_cairo_beos_view_to_bitmap(surface->view, &bmp);
free_bmp = true;
}
}
 
if (!bmp)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
// So, BeOS seems to screw up painting an opaque bitmap onto a
// translucent one (it makes them partly transparent). Just return
// unsupported.
if (bmp->ColorSpace() == B_RGB32 && surface->bitmap &&
surface->bitmap->ColorSpace() == B_RGBA32 &&
(mode == B_OP_COPY || mode == B_OP_ALPHA))
{
if (free_bmp)
delete bmp;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
// Draw it on screen.
surface->view->PushState();
 
// If our image rect is only a subrect of the desired size, and we
// aren't using B_OP_ALPHA, then we need to fill the rect first.
if (mode == B_OP_COPY && !bmp->Bounds().Contains(srcRect)) {
rgb_color black = { 0, 0, 0, 0 };
 
surface->view->SetDrawingMode(mode);
surface->view->SetHighColor(black);
surface->view->FillRect(dstRect);
}
 
if (mode == B_OP_ALPHA && bmp->ColorSpace() == B_RGB32) {
mode = B_OP_COPY;
}
surface->view->SetDrawingMode(mode);
 
if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
else
surface->view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
 
surface->view->DrawBitmap(bmp, srcRect, dstRect);
 
surface->view->PopState();
 
if (free_bmp)
delete bmp;
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
 
static cairo_int_status_t
_cairo_beos_surface_fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects,
cairo_region_t *clip_region)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
cairo_int_status_t status;
 
if (num_rects <= 0)
return CAIRO_INT_STATUS_SUCCESS;
 
AutoLockView locker(surface->view);
if (!locker)
return CAIRO_INT_STATUS_SUCCESS;
 
drawing_mode mode;
if (!_cairo_op_to_be_op(op, &mode))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_beos_surface_set_clip_region (surface, clip_region);
if (unlikely (status))
return status;
 
rgb_color be_color = _cairo_color_to_be_color(color);
 
if (mode == B_OP_ALPHA && be_color.alpha == 0xFF)
mode = B_OP_COPY;
 
// For CAIRO_OPERATOR_SOURCE, cairo expects us to use the premultiplied
// color info. This is only relevant when drawing into an rgb24 buffer
// (as for others, we can convert when asked for the image)
if (mode == B_OP_COPY && be_color.alpha != 0xFF &&
(!surface->bitmap || surface->bitmap->ColorSpace() != B_RGBA32))
{
be_color.red = color->red_short >> 8;
be_color.green = color->green_short >> 8;
be_color.blue = color->blue_short >> 8;
}
 
surface->view->PushState();
 
surface->view->SetDrawingMode(mode);
surface->view->SetHighColor(be_color);
if (surface->bitmap && surface->bitmap->ColorSpace() == B_RGBA32)
surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
else
surface->view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
 
for (int i = 0; i < num_rects; ++i)
surface->view->FillRect (_cairo_rectangle_to_brect (&rects[i]));
 
surface->view->PopState();
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_beos_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_beos_surface_t *surface = reinterpret_cast<cairo_beos_surface_t*>(
abstract_surface);
AutoLockView locker(surface->view);
if (!locker)
return FALSE;
 
*rectangle = _brect_to_cairo_rectangle (surface->view->Bounds());
return TRUE;
}
 
static const struct _cairo_surface_backend cairo_beos_surface_backend = {
CAIRO_SURFACE_TYPE_BEOS,
_cairo_beos_surface_create_similar,
_cairo_beos_surface_finish,
_cairo_beos_surface_acquire_source_image,
_cairo_beos_surface_release_source_image,
_cairo_beos_surface_acquire_dest_image,
_cairo_beos_surface_release_dest_image,
NULL, /* clone_similar */
_cairo_beos_surface_composite, /* composite */
_cairo_beos_surface_fill_rectangles,
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* copy_page */
NULL, /* show_page */
_cairo_beos_surface_get_extents,
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
NULL, /* paint */
NULL, /* mask */
NULL, /* stroke */
NULL, /* fill */
NULL /* show_glyphs */
};
 
static cairo_surface_t *
_cairo_beos_surface_create_internal (BView* view,
BBitmap* bmp,
bool owns_bitmap_view)
{
// Must use malloc, because cairo code will use free() on the surface
cairo_beos_surface_t *surface = static_cast<cairo_beos_surface_t*>(
malloc(sizeof(cairo_beos_surface_t)));
if (surface == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return const_cast<cairo_surface_t*>(&_cairo_surface_nil);
}
 
cairo_content_t content = CAIRO_CONTENT_COLOR;
if (bmp && (bmp->ColorSpace() == B_RGBA32 || bmp->ColorSpace() == B_RGBA15))
content = CAIRO_CONTENT_COLOR_ALPHA;
_cairo_surface_init (&surface->base,
&cairo_beos_surface_backend,
NULL, /* device */
content);
 
surface->view = view;
surface->bitmap = bmp;
surface->owns_bitmap_view = owns_bitmap_view;
 
surface->clip_region = NULL;
 
return &surface->base;
}
 
/**
* cairo_beos_surface_create:
* @view: The view to draw on
*
* Creates a Cairo surface that draws onto a BeOS BView.
* The caller must ensure that the view does not get deleted before the surface.
* If the view is attached to a bitmap rather than an on-screen window, use
* cairo_beos_surface_create_for_bitmap() instead of this function.
*
* Since: TBD
**/
cairo_surface_t *
cairo_beos_surface_create (BView* view)
{
return cairo_beos_surface_create_for_bitmap(view, NULL);
}
 
/**
* cairo_beos_surface_create_for_bitmap:
* @view: The view to draw on
* @bmp: The bitmap to which the view is attached
*
* Creates a Cairo surface that draws onto a BeOS BView which is attached to a
* BBitmap.
* The caller must ensure that the view and the bitmap do not get deleted
* before the surface.
*
* For views that draw to a bitmap (as opposed to a screen), use this function
* rather than cairo_beos_surface_create(). Not using this function WILL lead to
* incorrect behaviour.
*
* For now, only views that draw to the entire area of bmp are supported.
* The view must already be attached to the bitmap.
*
* Since: TBD
**/
cairo_surface_t *
cairo_beos_surface_create_for_bitmap (BView* view,
BBitmap* bmp)
{
return _cairo_beos_surface_create_internal(view, bmp);
}
/programs/develop/libraries/cairo/src/cairo-botor-scan-converter.c
43,9 → 43,9
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-list-private.h"
#include "cairo-list-inline.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-private.h"
#include "cairo-combsort-inline.h"
 
#include <setjmp.h>
 
1072,7 → 1072,7
coverage_rewind (cells);
}
 
inline static struct cell *
static struct cell *
coverage_alloc (sweep_line_t *sweep_line,
struct cell *tail,
int x)
1397,6 → 1397,7
 
if (x > prev_x) {
spans[num_spans].x = prev_x;
spans[num_spans].inverse = 0;
spans[num_spans].coverage = AREA_TO_ALPHA (cover);
++num_spans;
}
1413,6 → 1414,7
 
if (prev_x <= self->xmax) {
spans[num_spans].x = prev_x;
spans[num_spans].inverse = 0;
spans[num_spans].coverage = AREA_TO_ALPHA (cover);
++num_spans;
}
1419,6 → 1421,7
 
if (cover && prev_x < self->xmax) {
spans[num_spans].x = self->xmax;
spans[num_spans].inverse = 1;
spans[num_spans].coverage = 0;
++num_spans;
}
2125,42 → 2128,6
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_botor_scan_converter_add_edge (void *converter,
const cairo_point_t *p1,
const cairo_point_t *p2,
int top, int bottom,
int dir)
{
cairo_botor_scan_converter_t *self = converter;
cairo_edge_t edge;
 
edge.line.p1 = *p1;
edge.line.p2 = *p2;
edge.top = top;
edge.bottom = bottom;
edge.dir = dir;
 
return botor_add_edge (self, &edge);
}
 
static cairo_status_t
_cairo_botor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
cairo_botor_scan_converter_t *self = converter;
cairo_status_t status;
int i;
 
for (i = 0; i < polygon->num_edges; i++) {
status = botor_add_edge (self, &polygon->edges[i]);
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_botor_scan_converter_destroy (void *converter)
{
2179,8 → 2146,6
cairo_fill_rule_t fill_rule)
{
self->base.destroy = _cairo_botor_scan_converter_destroy;
self->base.add_edge = _cairo_botor_scan_converter_add_edge;
self->base.add_polygon = _cairo_botor_scan_converter_add_polygon;
self->base.generate = _cairo_botor_scan_converter_generate;
 
self->extents = *extents;
/programs/develop/libraries/cairo/src/cairo-box-inline.h
0,0 → 1,121
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Andrea Canciani
*
* 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.
*
* Contributor(s):
* Andrea Canciani <ranma42@gmail.com>
*/
 
#ifndef CAIRO_BOX_H
#define CAIRO_BOX_H
 
#include "cairo-types-private.h"
#include "cairo-compiler-private.h"
#include "cairo-fixed-private.h"
 
static inline void
_cairo_box_set (cairo_box_t *box,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
box->p1 = *p1;
box->p2 = *p2;
}
 
static inline void
_cairo_box_from_integers (cairo_box_t *box, int x, int y, int w, int h)
{
box->p1.x = _cairo_fixed_from_int (x);
box->p1.y = _cairo_fixed_from_int (y);
box->p2.x = _cairo_fixed_from_int (x + w);
box->p2.y = _cairo_fixed_from_int (y + h);
}
 
/* assumes box->p1 is top-left, p2 bottom-right */
static inline void
_cairo_box_add_point (cairo_box_t *box,
const cairo_point_t *point)
{
if (point->x < box->p1.x)
box->p1.x = point->x;
else if (point->x > box->p2.x)
box->p2.x = point->x;
 
if (point->y < box->p1.y)
box->p1.y = point->y;
else if (point->y > box->p2.y)
box->p2.y = point->y;
}
 
static inline void
_cairo_box_add_box (cairo_box_t *box,
const cairo_box_t *add)
{
if (add->p1.x < box->p1.x)
box->p1.x = add->p1.x;
if (add->p2.x > box->p2.x)
box->p2.x = add->p2.x;
 
if (add->p1.y < box->p1.y)
box->p1.y = add->p1.y;
if (add->p2.y > box->p2.y)
box->p2.y = add->p2.y;
}
 
/* assumes box->p1 is top-left, p2 bottom-right */
static inline cairo_bool_t
_cairo_box_contains_point (const cairo_box_t *box,
const cairo_point_t *point)
{
return box->p1.x <= point->x && point->x <= box->p2.x &&
box->p1.y <= point->y && point->y <= box->p2.y;
}
 
static inline cairo_bool_t
_cairo_box_is_pixel_aligned (const cairo_box_t *box)
{
#if CAIRO_FIXED_FRAC_BITS <= 8 && 0
return ((box->p1.x & CAIRO_FIXED_FRAC_MASK) << 24 |
(box->p1.y & CAIRO_FIXED_FRAC_MASK) << 16 |
(box->p2.x & CAIRO_FIXED_FRAC_MASK) << 8 |
(box->p2.y & CAIRO_FIXED_FRAC_MASK) << 0) == 0;
#else /* GCC on i7 prefers this variant (bizarrely according to the profiler) */
cairo_fixed_t f;
 
f = 0;
f |= box->p1.x & CAIRO_FIXED_FRAC_MASK;
f |= box->p1.y & CAIRO_FIXED_FRAC_MASK;
f |= box->p2.x & CAIRO_FIXED_FRAC_MASK;
f |= box->p2.y & CAIRO_FIXED_FRAC_MASK;
 
return f == 0;
#endif
}
 
#endif /* CAIRO_BOX_H */
/programs/develop/libraries/cairo/src/cairo-boxes-intersect.c
0,0 → 1,690
/*
* Copyright © 2004 Carl Worth
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2009 Chris Wilson
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Carl Worth
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* Provide definitions for standalone compilation */
#include "cairoint.h"
 
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-list-private.h"
 
#include <setjmp.h>
 
typedef struct _rectangle rectangle_t;
typedef struct _edge edge_t;
 
struct _edge {
edge_t *next, *prev;
edge_t *right;
cairo_fixed_t x, top;
int a_or_b;
int dir;
};
 
struct _rectangle {
edge_t left, right;
int32_t top, bottom;
};
 
#define UNROLL3(x) x x x
 
/* the parent is always given by index/2 */
#define PQ_PARENT_INDEX(i) ((i) >> 1)
#define PQ_FIRST_ENTRY 1
 
/* left and right children are index * 2 and (index * 2) +1 respectively */
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
 
typedef struct _pqueue {
int size, max_size;
 
rectangle_t **elements;
rectangle_t *elements_embedded[1024];
} pqueue_t;
 
typedef struct _sweep_line {
rectangle_t **rectangles;
pqueue_t pq;
edge_t head, tail;
edge_t *insert_left, *insert_right;
int32_t current_y;
int32_t last_y;
 
jmp_buf unwind;
} sweep_line_t;
 
#define DEBUG_TRAPS 0
 
#if DEBUG_TRAPS
static void
dump_traps (cairo_traps_t *traps, const char *filename)
{
FILE *file;
int n;
 
if (getenv ("CAIRO_DEBUG_TRAPS") == NULL)
return;
 
file = fopen (filename, "a");
if (file != NULL) {
for (n = 0; n < traps->num_traps; n++) {
fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
traps->traps[n].top,
traps->traps[n].bottom,
traps->traps[n].left.p1.x,
traps->traps[n].left.p1.y,
traps->traps[n].left.p2.x,
traps->traps[n].left.p2.y,
traps->traps[n].right.p1.x,
traps->traps[n].right.p1.y,
traps->traps[n].right.p2.x,
traps->traps[n].right.p2.y);
}
fprintf (file, "\n");
fclose (file);
}
}
#else
#define dump_traps(traps, filename)
#endif
 
static inline int
rectangle_compare_start (const rectangle_t *a,
const rectangle_t *b)
{
return a->top - b->top;
}
 
static inline int
rectangle_compare_stop (const rectangle_t *a,
const rectangle_t *b)
{
return a->bottom - b->bottom;
}
 
static inline void
pqueue_init (pqueue_t *pq)
{
pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
pq->size = 0;
 
pq->elements = pq->elements_embedded;
pq->elements[PQ_FIRST_ENTRY] = NULL;
}
 
static inline void
pqueue_fini (pqueue_t *pq)
{
if (pq->elements != pq->elements_embedded)
free (pq->elements);
}
 
static cairo_bool_t
pqueue_grow (pqueue_t *pq)
{
rectangle_t **new_elements;
pq->max_size *= 2;
 
if (pq->elements == pq->elements_embedded) {
new_elements = _cairo_malloc_ab (pq->max_size,
sizeof (rectangle_t *));
if (unlikely (new_elements == NULL))
return FALSE;
 
memcpy (new_elements, pq->elements_embedded,
sizeof (pq->elements_embedded));
} else {
new_elements = _cairo_realloc_ab (pq->elements,
pq->max_size,
sizeof (rectangle_t *));
if (unlikely (new_elements == NULL))
return FALSE;
}
 
pq->elements = new_elements;
return TRUE;
}
 
static inline void
pqueue_push (sweep_line_t *sweep, rectangle_t *rectangle)
{
rectangle_t **elements;
int i, parent;
 
if (unlikely (sweep->pq.size + 1 == sweep->pq.max_size)) {
if (unlikely (! pqueue_grow (&sweep->pq))) {
longjmp (sweep->unwind,
_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
}
 
elements = sweep->pq.elements;
for (i = ++sweep->pq.size;
i != PQ_FIRST_ENTRY &&
rectangle_compare_stop (rectangle,
elements[parent = PQ_PARENT_INDEX (i)]) < 0;
i = parent)
{
elements[i] = elements[parent];
}
 
elements[i] = rectangle;
}
 
static inline void
pqueue_pop (pqueue_t *pq)
{
rectangle_t **elements = pq->elements;
rectangle_t *tail;
int child, i;
 
tail = elements[pq->size--];
if (pq->size == 0) {
elements[PQ_FIRST_ENTRY] = NULL;
return;
}
 
for (i = PQ_FIRST_ENTRY;
(child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
i = child)
{
if (child != pq->size &&
rectangle_compare_stop (elements[child+1],
elements[child]) < 0)
{
child++;
}
 
if (rectangle_compare_stop (elements[child], tail) >= 0)
break;
 
elements[i] = elements[child];
}
elements[i] = tail;
}
 
static inline rectangle_t *
rectangle_pop_start (sweep_line_t *sweep_line)
{
return *sweep_line->rectangles++;
}
 
static inline rectangle_t *
rectangle_peek_stop (sweep_line_t *sweep_line)
{
return sweep_line->pq.elements[PQ_FIRST_ENTRY];
}
 
CAIRO_COMBSORT_DECLARE (_rectangle_sort,
rectangle_t *,
rectangle_compare_start)
 
static void
sweep_line_init (sweep_line_t *sweep_line,
rectangle_t **rectangles,
int num_rectangles)
{
_rectangle_sort (rectangles, num_rectangles);
rectangles[num_rectangles] = NULL;
sweep_line->rectangles = rectangles;
 
sweep_line->head.x = INT32_MIN;
sweep_line->head.right = NULL;
sweep_line->head.dir = 0;
sweep_line->head.next = &sweep_line->tail;
sweep_line->tail.x = INT32_MAX;
sweep_line->tail.right = NULL;
sweep_line->tail.dir = 0;
sweep_line->tail.prev = &sweep_line->head;
 
sweep_line->insert_left = &sweep_line->tail;
sweep_line->insert_right = &sweep_line->tail;
 
sweep_line->current_y = INT32_MIN;
sweep_line->last_y = INT32_MIN;
 
pqueue_init (&sweep_line->pq);
}
 
static void
sweep_line_fini (sweep_line_t *sweep_line)
{
pqueue_fini (&sweep_line->pq);
}
 
static void
end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out)
{
if (likely (left->top < bot)) {
cairo_status_t status;
cairo_box_t box;
 
box.p1.x = left->x;
box.p1.y = left->top;
box.p2.x = left->right->x;
box.p2.y = bot;
 
status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box);
if (unlikely (status))
longjmp (sweep_line->unwind, status);
}
 
left->right = NULL;
}
 
/* Start a new trapezoid at the given top y coordinate, whose edges
* are `edge' and `edge->next'. If `edge' already has a trapezoid,
* then either add it to the traps in `traps', if the trapezoid's
* right edge differs from `edge->next', or do nothing if the new
* trapezoid would be a continuation of the existing one. */
static inline void
start_or_continue_box (sweep_line_t *sweep_line,
edge_t *left,
edge_t *right,
int top,
cairo_boxes_t *out)
{
if (left->right == right)
return;
 
if (left->right != NULL) {
if (right != NULL && left->right->x == right->x) {
/* continuation on right, so just swap edges */
left->right = right;
return;
}
 
end_box (sweep_line, left, top, out);
}
 
if (right != NULL && left->x != right->x) {
left->top = top;
left->right = right;
}
}
 
static inline int is_zero(const int *winding)
{
return winding[0] == 0 || winding[1] == 0;
}
 
static inline void
active_edges (sweep_line_t *sweep, cairo_boxes_t *out)
{
int top = sweep->current_y;
int winding[2] = { 0 };
edge_t *pos;
 
if (sweep->last_y == sweep->current_y)
return;
 
pos = sweep->head.next;
if (pos == &sweep->tail)
return;
 
do {
edge_t *left, *right;
 
left = pos;
do {
winding[left->a_or_b] += left->dir;
if (!is_zero (winding))
break;
if (left->next == &sweep->tail)
goto out;
 
if (unlikely (left->right != NULL))
end_box (sweep, left, top, out);
 
left = left->next;
} while (1);
 
right = left->next;
do {
if (unlikely (right->right != NULL))
end_box (sweep, right, top, out);
 
winding[right->a_or_b] += right->dir;
if (is_zero (winding)) {
/* skip co-linear edges */
if (likely (right->x != right->next->x))
break;
}
 
right = right->next;
} while (TRUE);
 
start_or_continue_box (sweep, left, right, top, out);
 
pos = right->next;
} while (pos != &sweep->tail);
 
out:
sweep->last_y = sweep->current_y;
}
 
static inline void
sweep_line_delete_edge (sweep_line_t *sweep_line, edge_t *edge, cairo_boxes_t *out)
{
if (edge->right != NULL) {
edge_t *next = edge->next;
if (next->x == edge->x) {
next->top = edge->top;
next->right = edge->right;
} else {
end_box (sweep_line, edge, sweep_line->current_y, out);
}
}
 
if (sweep_line->insert_left == edge)
sweep_line->insert_left = edge->next;
if (sweep_line->insert_right == edge)
sweep_line->insert_right = edge->next;
 
edge->prev->next = edge->next;
edge->next->prev = edge->prev;
}
 
static inline void
sweep_line_delete (sweep_line_t *sweep,
rectangle_t *rectangle,
cairo_boxes_t *out)
{
sweep_line_delete_edge (sweep, &rectangle->left, out);
sweep_line_delete_edge (sweep, &rectangle->right, out);
 
pqueue_pop (&sweep->pq);
}
 
static inline void
insert_edge (edge_t *edge, edge_t *pos)
{
if (pos->x != edge->x) {
if (pos->x > edge->x) {
do {
UNROLL3({
if (pos->prev->x <= edge->x)
break;
pos = pos->prev;
})
} while (TRUE);
} else {
do {
UNROLL3({
pos = pos->next;
if (pos->x >= edge->x)
break;
})
} while (TRUE);
}
}
 
pos->prev->next = edge;
edge->prev = pos->prev;
edge->next = pos;
pos->prev = edge;
}
 
static inline void
sweep_line_insert (sweep_line_t *sweep, rectangle_t *rectangle)
{
edge_t *pos;
 
/* right edge */
pos = sweep->insert_right;
insert_edge (&rectangle->right, pos);
sweep->insert_right = &rectangle->right;
 
/* left edge */
pos = sweep->insert_left;
if (pos->x > sweep->insert_right->x)
pos = sweep->insert_right->prev;
insert_edge (&rectangle->left, pos);
sweep->insert_left = &rectangle->left;
 
pqueue_push (sweep, rectangle);
}
 
static cairo_status_t
intersect (rectangle_t **rectangles, int num_rectangles, cairo_boxes_t *out)
{
sweep_line_t sweep_line;
rectangle_t *rectangle;
cairo_status_t status;
 
sweep_line_init (&sweep_line, rectangles, num_rectangles);
if ((status = setjmp (sweep_line.unwind)))
goto unwind;
 
rectangle = rectangle_pop_start (&sweep_line);
do {
if (rectangle->top != sweep_line.current_y) {
rectangle_t *stop;
 
stop = rectangle_peek_stop (&sweep_line);
while (stop != NULL && stop->bottom < rectangle->top) {
if (stop->bottom != sweep_line.current_y) {
active_edges (&sweep_line, out);
sweep_line.current_y = stop->bottom;
}
 
sweep_line_delete (&sweep_line, stop, out);
 
stop = rectangle_peek_stop (&sweep_line);
}
 
active_edges (&sweep_line, out);
sweep_line.current_y = rectangle->top;
}
 
sweep_line_insert (&sweep_line, rectangle);
} while ((rectangle = rectangle_pop_start (&sweep_line)) != NULL);
 
while ((rectangle = rectangle_peek_stop (&sweep_line)) != NULL) {
if (rectangle->bottom != sweep_line.current_y) {
active_edges (&sweep_line, out);
sweep_line.current_y = rectangle->bottom;
}
 
sweep_line_delete (&sweep_line, rectangle, out);
}
 
unwind:
sweep_line_fini (&sweep_line);
return status;
}
 
static cairo_status_t
_cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes,
const cairo_box_t *box,
cairo_boxes_t *out)
{
cairo_status_t status;
int i, j;
 
if (out == boxes) { /* inplace update */
struct _cairo_boxes_chunk *chunk;
 
out->num_boxes = 0;
for (chunk = &out->chunks; chunk != NULL; chunk = chunk->next) {
for (i = j = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
 
b->p1.x = MAX (b->p1.x, box->p1.x);
b->p1.y = MAX (b->p1.y, box->p1.y);
b->p2.x = MIN (b->p2.x, box->p2.x);
b->p2.y = MIN (b->p2.y, box->p2.y);
if (b->p1.x < b->p2.x && b->p1.y < b->p2.y) {
if (i != j)
chunk->base[j] = *b;
j++;
}
}
/* XXX unlink empty chains? */
chunk->count = j;
out->num_boxes += j;
}
} else {
const struct _cairo_boxes_chunk *chunk;
 
_cairo_boxes_clear (out);
_cairo_boxes_limit (out, box, 1);
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
status = _cairo_boxes_add (out,
CAIRO_ANTIALIAS_DEFAULT,
&chunk->base[i]);
if (unlikely (status))
return status;
}
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_boxes_intersect (const cairo_boxes_t *a,
const cairo_boxes_t *b,
cairo_boxes_t *out)
{
rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)];
rectangle_t *rectangles;
rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 1];
rectangle_t **rectangles_ptrs;
const struct _cairo_boxes_chunk *chunk;
cairo_status_t status;
int i, j, count;
 
if (unlikely (a->num_boxes == 0 || b->num_boxes == 0)) {
_cairo_boxes_clear (out);
return CAIRO_STATUS_SUCCESS;
}
 
if (a->num_boxes == 1) {
cairo_box_t box = a->chunks.base[0];
return _cairo_boxes_intersect_with_box (b, &box, out);
}
if (b->num_boxes == 1) {
cairo_box_t box = b->chunks.base[0];
return _cairo_boxes_intersect_with_box (a, &box, out);
}
 
rectangles = stack_rectangles;
rectangles_ptrs = stack_rectangles_ptrs;
count = a->num_boxes + b->num_boxes;
if (count > ARRAY_LENGTH (stack_rectangles)) {
rectangles = _cairo_malloc_ab_plus_c (count,
sizeof (rectangle_t) +
sizeof (rectangle_t *),
sizeof (rectangle_t *));
if (unlikely (rectangles == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
rectangles_ptrs = (rectangle_t **) (rectangles + count);
}
 
j = 0;
for (chunk = &a->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
for (i = 0; i < chunk->count; i++) {
if (box[i].p1.x < box[i].p2.x) {
rectangles[j].left.x = box[i].p1.x;
rectangles[j].left.dir = 1;
 
rectangles[j].right.x = box[i].p2.x;
rectangles[j].right.dir = -1;
} else {
rectangles[j].right.x = box[i].p1.x;
rectangles[j].right.dir = 1;
 
rectangles[j].left.x = box[i].p2.x;
rectangles[j].left.dir = -1;
}
 
rectangles[j].left.a_or_b = 0;
rectangles[j].left.right = NULL;
rectangles[j].right.a_or_b = 0;
rectangles[j].right.right = NULL;
 
rectangles[j].top = box[i].p1.y;
rectangles[j].bottom = box[i].p2.y;
 
rectangles_ptrs[j] = &rectangles[j];
j++;
}
}
for (chunk = &b->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
for (i = 0; i < chunk->count; i++) {
if (box[i].p1.x < box[i].p2.x) {
rectangles[j].left.x = box[i].p1.x;
rectangles[j].left.dir = 1;
 
rectangles[j].right.x = box[i].p2.x;
rectangles[j].right.dir = -1;
} else {
rectangles[j].right.x = box[i].p1.x;
rectangles[j].right.dir = 1;
 
rectangles[j].left.x = box[i].p2.x;
rectangles[j].left.dir = -1;
}
 
rectangles[j].left.a_or_b = 1;
rectangles[j].left.right = NULL;
rectangles[j].right.a_or_b = 1;
rectangles[j].right.right = NULL;
 
rectangles[j].top = box[i].p1.y;
rectangles[j].bottom = box[i].p2.y;
 
rectangles_ptrs[j] = &rectangles[j];
j++;
}
}
assert (j == count);
 
_cairo_boxes_clear (out);
status = intersect (rectangles_ptrs, j, out);
if (rectangles != stack_rectangles)
free (rectangles);
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-boxes-private.h
37,14 → 37,20
#include "cairo-types-private.h"
#include "cairo-compiler-private.h"
 
#include <stdio.h>
#include <stdlib.h>
 
struct _cairo_boxes_t {
cairo_status_t status;
 
cairo_box_t limit;
const cairo_box_t *limits;
int num_limits;
 
int num_boxes;
unsigned int is_pixel_aligned : 1;
 
unsigned int is_pixel_aligned;
 
struct _cairo_boxes_chunk {
struct _cairo_boxes_chunk *next;
cairo_box_t *base;
58,11 → 64,19
_cairo_boxes_init (cairo_boxes_t *boxes);
 
cairo_private void
_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
cairo_clip_t *clip);
 
cairo_private void
_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
cairo_box_t *array,
int num_boxes);
 
cairo_private void
_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
int x, int y, int w, int h);
 
cairo_private void
_cairo_boxes_limit (cairo_boxes_t *boxes,
const cairo_box_t *limits,
int num_limits);
69,16 → 83,41
 
cairo_private cairo_status_t
_cairo_boxes_add (cairo_boxes_t *boxes,
cairo_antialias_t antialias,
const cairo_box_t *box);
 
cairo_private void
_cairo_boxes_extents (const cairo_boxes_t *boxes,
cairo_rectangle_int_t *extents);
cairo_box_t *box);
 
cairo_private cairo_box_t *
_cairo_boxes_to_array (const cairo_boxes_t *boxes,
int *num_boxes,
cairo_bool_t force_allocation);
 
cairo_private cairo_status_t
_cairo_boxes_intersect (const cairo_boxes_t *a,
const cairo_boxes_t *b,
cairo_boxes_t *out);
 
cairo_private void
_cairo_boxes_clear (cairo_boxes_t *boxes);
 
cairo_private_no_warn cairo_bool_t
_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
cairo_bool_t (*func) (cairo_box_t *box, void *data),
void *data);
 
cairo_private cairo_status_t
_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_boxes_t *boxes);
 
cairo_private void
_cairo_boxes_fini (cairo_boxes_t *boxes);
 
cairo_private void
_cairo_debug_print_boxes (FILE *stream,
const cairo_boxes_t *boxes);
 
#endif /* CAIRO_BOXES_H */
/programs/develop/libraries/cairo/src/cairo-boxes.c
33,6 → 33,7
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
 
53,6 → 54,25
}
 
void
_cairo_boxes_init_from_rectangle (cairo_boxes_t *boxes,
int x, int y, int w, int h)
{
_cairo_boxes_init (boxes);
 
_cairo_box_from_integers (&boxes->chunks.base[0], x, y, w, h);
boxes->num_boxes = 1;
}
 
void
_cairo_boxes_init_with_clip (cairo_boxes_t *boxes,
cairo_clip_t *clip)
{
_cairo_boxes_init (boxes);
if (clip)
_cairo_boxes_limit (boxes, clip->boxes, clip->num_boxes);
}
 
void
_cairo_boxes_init_for_array (cairo_boxes_t *boxes,
cairo_box_t *array,
int num_boxes)
145,19 → 165,25
chunk->base[chunk->count++] = *box;
boxes->num_boxes++;
 
if (boxes->is_pixel_aligned) {
boxes->is_pixel_aligned =
_cairo_fixed_is_integer (box->p1.x) &&
_cairo_fixed_is_integer (box->p1.y) &&
_cairo_fixed_is_integer (box->p2.x) &&
_cairo_fixed_is_integer (box->p2.y);
if (boxes->is_pixel_aligned)
boxes->is_pixel_aligned = _cairo_box_is_pixel_aligned (box);
}
}
 
cairo_status_t
_cairo_boxes_add (cairo_boxes_t *boxes,
cairo_antialias_t antialias,
const cairo_box_t *box)
{
cairo_box_t b;
 
if (antialias == CAIRO_ANTIALIAS_NONE) {
b.p1.x = _cairo_fixed_round_down (box->p1.x);
b.p1.y = _cairo_fixed_round_down (box->p1.y);
b.p2.x = _cairo_fixed_round_down (box->p2.x);
b.p2.y = _cairo_fixed_round_down (box->p2.y);
box = &b;
}
 
if (box->p1.y == box->p2.y)
return CAIRO_STATUS_SUCCESS;
 
241,33 → 267,34
 
void
_cairo_boxes_extents (const cairo_boxes_t *boxes,
cairo_rectangle_int_t *extents)
cairo_box_t *box)
{
const struct _cairo_boxes_chunk *chunk;
cairo_box_t box;
cairo_box_t b;
int i;
 
box.p1.y = box.p1.x = INT_MAX;
box.p2.y = box.p2.x = INT_MIN;
if (boxes->num_boxes == 0) {
box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0;
return;
}
 
b = boxes->chunks.base[0];
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *b = chunk->base;
for (i = 0; i < chunk->count; i++) {
if (b[i].p1.x < box.p1.x)
box.p1.x = b[i].p1.x;
if (chunk->base[i].p1.x < b.p1.x)
b.p1.x = chunk->base[i].p1.x;
 
if (b[i].p1.y < box.p1.y)
box.p1.y = b[i].p1.y;
if (chunk->base[i].p1.y < b.p1.y)
b.p1.y = chunk->base[i].p1.y;
 
if (b[i].p2.x > box.p2.x)
box.p2.x = b[i].p2.x;
if (chunk->base[i].p2.x > b.p2.x)
b.p2.x = chunk->base[i].p2.x;
 
if (b[i].p2.y > box.p2.y)
box.p2.y = b[i].p2.y;
if (chunk->base[i].p2.y > b.p2.y)
b.p2.y = chunk->base[i].p2.y;
}
}
 
_cairo_box_round_to_rectangle (&box, extents);
*box = b;
}
 
void
283,11 → 310,41
boxes->tail = &boxes->chunks;
boxes->chunks.next = 0;
boxes->chunks.count = 0;
boxes->chunks.base = boxes->boxes_embedded;
boxes->chunks.size = ARRAY_LENGTH (boxes->boxes_embedded);
boxes->num_boxes = 0;
 
boxes->is_pixel_aligned = TRUE;
}
 
cairo_box_t *
_cairo_boxes_to_array (const cairo_boxes_t *boxes,
int *num_boxes,
cairo_bool_t force_allocation)
{
const struct _cairo_boxes_chunk *chunk;
cairo_box_t *box;
int i, j;
 
*num_boxes = boxes->num_boxes;
if (boxes->chunks.next == NULL && ! force_allocation)
return boxes->chunks.base;
 
box = _cairo_malloc_ab (boxes->num_boxes, sizeof (cairo_box_t));
if (box == NULL) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
j = 0;
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++)
box[j++] = chunk->base[i];
}
 
return box;
}
 
void
_cairo_boxes_fini (cairo_boxes_t *boxes)
{
298,3 → 355,106
free (chunk);
}
}
 
cairo_bool_t
_cairo_boxes_for_each_box (cairo_boxes_t *boxes,
cairo_bool_t (*func) (cairo_box_t *box, void *data),
void *data)
{
struct _cairo_boxes_chunk *chunk;
int i;
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++)
if (! func (&chunk->base[i], data))
return FALSE;
}
 
return TRUE;
}
 
struct cairo_box_renderer {
cairo_span_renderer_t base;
cairo_boxes_t *boxes;
};
 
static cairo_status_t
span_to_boxes (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
struct cairo_box_renderer *r = abstract_renderer;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_box_t box;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
box.p1.y = _cairo_fixed_from_int (y);
box.p2.y = _cairo_fixed_from_int (y + h);
do {
if (spans[0].coverage) {
box.p1.x = _cairo_fixed_from_int(spans[0].x);
box.p2.x = _cairo_fixed_from_int(spans[1].x);
status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
}
spans++;
} while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS);
 
return status;
}
 
cairo_status_t
_cairo_rasterise_polygon_to_boxes (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_boxes_t *boxes)
{
struct cairo_box_renderer renderer;
cairo_scan_converter_t *converter;
cairo_int_status_t status;
cairo_rectangle_int_t r;
 
TRACE ((stderr, "%s: fill_rule=%d\n", __FUNCTION__, fill_rule));
 
_cairo_box_round_to_rectangle (&polygon->extents, &r);
converter = _cairo_mono_scan_converter_create (r.x, r.y,
r.x + r.width,
r.y + r.height,
fill_rule);
status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
if (unlikely (status))
goto cleanup_converter;
 
renderer.boxes = boxes;
renderer.base.render_rows = span_to_boxes;
 
status = converter->generate (converter, &renderer.base);
cleanup_converter:
converter->destroy (converter);
return status;
}
 
void
_cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes)
{
const struct _cairo_boxes_chunk *chunk;
cairo_box_t extents;
int i;
 
_cairo_boxes_extents (boxes, &extents);
fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n",
boxes->num_boxes,
_cairo_fixed_to_double (extents.p1.x),
_cairo_fixed_to_double (extents.p1.y),
_cairo_fixed_to_double (extents.p2.x),
_cairo_fixed_to_double (extents.p2.y));
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
fprintf (stderr, " box[%d]: (%f, %f), (%f, %f)\n", i,
_cairo_fixed_to_double (chunk->base[i].p1.x),
_cairo_fixed_to_double (chunk->base[i].p1.y),
_cairo_fixed_to_double (chunk->base[i].p2.x),
_cairo_fixed_to_double (chunk->base[i].p2.y));
}
}
}
/programs/develop/libraries/cairo/src/cairo-cff-subset.c
1,3 → 1,4
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 Adrian Johnson
36,11 → 37,14
 
/*
* Useful links:
* http://www.adobe.com/devnet/font/pdfs/5176.CFF.pdf
* http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
* http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5177.Type2.pdf
*/
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
48,6 → 52,7
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-truetype-subset-private.h"
#include <string.h>
#include <locale.h>
 
/* CFF Dict Operators. If the high byte is 0 the command is encoded
* with a single byte. */
56,14 → 61,17
#define CHARSET_OP 0x000f
#define CHARSTRINGS_OP 0x0011
#define COPYRIGHT_OP 0x0c00
#define DEFAULTWIDTH_OP 0x0014
#define ENCODING_OP 0x0010
#define FAMILYNAME_OP 0x0003
#define FDARRAY_OP 0x0c24
#define FDSELECT_OP 0x0c25
#define FONTBBOX_OP 0x0005
#define FONTMATRIX_OP 0x0c07
#define FONTNAME_OP 0x0c26
#define FULLNAME_OP 0x0002
#define LOCAL_SUB_OP 0x0013
#define NOMINALWIDTH_OP 0x0015
#define NOTICE_OP 0x0001
#define POSTSCRIPT_OP 0x0c15
#define PRIVATE_OP 0x0012
75,6 → 83,28
 
#define NUM_STD_STRINGS 391
 
/* Type 2 Charstring operators */
#define TYPE2_hstem 0x0001
#define TYPE2_vstem 0x0003
#define TYPE2_callsubr 0x000a
 
#define TYPE2_return 0x000b
#define TYPE2_endchar 0x000e
 
#define TYPE2_hstemhm 0x0012
#define TYPE2_hintmask 0x0013
#define TYPE2_cntrmask 0x0014
#define TYPE2_vstemhm 0x0017
#define TYPE2_callgsubr 0x001d
 
#define TYPE2_rmoveto 0x0015
#define TYPE2_hmoveto 0x0016
#define TYPE2_vmoveto 0x0004
 
 
#define MAX_SUBROUTINE_NESTING 10 /* From Type2 Charstring spec */
 
 
typedef struct _cff_header {
uint8_t major;
uint8_t minor;
116,9 → 146,15
cairo_array_t charstrings_index;
cairo_array_t global_sub_index;
cairo_array_t local_sub_index;
unsigned char *charset;
int num_glyphs;
cairo_bool_t is_cid;
cairo_bool_t is_opentype;
int units_per_em;
int global_sub_bias;
int local_sub_bias;
double default_width;
double nominal_width;
 
/* CID Font Data */
int *fdselect;
126,15 → 162,23
cairo_hash_table_t **fd_dict;
cairo_hash_table_t **fd_private_dict;
cairo_array_t *fd_local_sub_index;
int *fd_local_sub_bias;
double *fd_default_width;
double *fd_nominal_width;
 
/* Subsetted Font Data */
char *subset_font_name;
cairo_array_t charstrings_subset_index;
cairo_array_t strings_subset_index;
int euro_sid;
int *fdselect_subset;
unsigned int num_subset_fontdicts;
int *fd_subset_map;
int *private_dict_offset;
cairo_bool_t subset_subroutines;
cairo_bool_t *global_subs_used;
cairo_bool_t *local_subs_used;
cairo_bool_t **fd_local_subs_used;
cairo_array_t output;
 
/* Subset Metrics */
142,6 → 186,19
int x_min, y_min, x_max, y_max;
int ascent, descent;
 
/* Type 2 charstring data */
int type2_stack_size;
int type2_stack_top_value;
cairo_bool_t type2_stack_top_is_int;
int type2_num_hints;
int type2_hintmask_bytes;
int type2_nesting_level;
cairo_bool_t type2_seen_first_int;
cairo_bool_t type2_find_width;
cairo_bool_t type2_found_width;
int type2_width;
cairo_bool_t type2_has_path;
 
} cairo_cff_font_t;
 
/* Encoded integer using maximum sized encoding. This is required for
204,7 → 261,102
return p;
}
 
static char *
decode_nibble (int n, char *buf)
{
switch (n)
{
case 0xa:
*buf++ = '.';
break;
case 0xb:
*buf++ = 'E';
break;
case 0xc:
*buf++ = 'E';
*buf++ = '-';
break;
case 0xd:
*buf++ = '-';
break;
case 0xe:
*buf++ = '-';
break;
case 0xf:
break;
default:
*buf++ = '0' + n;
break;
}
 
return buf;
}
 
static unsigned char *
decode_real (unsigned char *p, double *real)
{
struct lconv *locale_data;
const char *decimal_point;
int decimal_point_len;
int n;
char buffer[100];
char buffer2[200];
char *q;
char *buf = buffer;
char *buf_end = buffer + sizeof (buffer);
 
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
 
assert (decimal_point_len != 0);
assert (sizeof(buffer) + decimal_point_len < sizeof(buffer2));
 
p++;
while (buf + 2 < buf_end) {
n = *p >> 4;
buf = decode_nibble (n, buf);
n = *p & 0x0f;
buf = decode_nibble (n, buf);
if ((*p & 0x0f) == 0x0f) {
p++;
break;
}
p++;
};
*buf = 0;
 
buf = buffer;
if (strchr (buffer, '.')) {
q = strchr (buffer, '.');
strncpy (buffer2, buffer, q - buffer);
buf = buffer2 + (q - buffer);
strncpy (buf, decimal_point, decimal_point_len);
buf += decimal_point_len;
strcpy (buf, q + 1);
buf = buffer2;
}
 
if (sscanf(buf, "%lf", real) != 1)
*real = 0.0;
 
return p;
}
 
static unsigned char *
decode_number (unsigned char *p, double *number)
{
if (*p == 30) {
p = decode_real (p, number);
} else {
int i;
p = decode_integer (p, &i);
*number = i;
}
return p;
}
 
static unsigned char *
decode_operator (unsigned char *p, unsigned short *operator)
{
unsigned short op = 0;
370,9 → 522,11
 
for (i = 0; i < num_elem; i++) {
element = _cairo_array_index (index, i);
if (element->length > 0) {
status = _cairo_array_append_multiple (output,
element->data,
element->length);
}
if (unlikely (status))
return status;
}
379,6 → 533,21
return CAIRO_STATUS_SUCCESS;
}
 
static void
cff_index_set_object (cairo_array_t *index, int obj_index,
unsigned char *object , int length)
{
cff_index_element_t *element;
 
element = _cairo_array_index (index, obj_index);
if (element->is_copy)
free (element->data);
 
element->data = object;
element->length = length;
element->is_copy = FALSE;
}
 
static cairo_status_t
cff_index_append (cairo_array_t *index, unsigned char *object , int length)
{
420,11 → 589,11
cff_index_fini (cairo_array_t *index)
{
cff_index_element_t *element;
int i;
unsigned int i;
 
for (i = 0; i < _cairo_array_num_elements (index); i++) {
element = _cairo_array_index (index, i);
if (element->is_copy)
if (element->is_copy && element->data)
free (element->data);
}
_cairo_array_fini (index);
691,6 → 860,7
if (font->data_length < sizeof (cff_header_t))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
 
font->header = (cff_header_t *) font->data;
font->current_ptr = font->data + font->header->header_size;
 
702,11 → 872,34
{
cairo_array_t index;
cairo_int_status_t status;
cff_index_element_t *element;
unsigned char *p;
int i, len;
 
/* The original font name is not used in the subset. Read the name
* index to skip over it. */
cff_index_init (&index);
status = cff_index_read (&index, &font->current_ptr, font->data_end);
if (!font->is_opentype) {
element = _cairo_array_index (&index, 0);
p = element->data;
len = element->length;
 
/* If font name is prefixed with a subset tag, strip it off. */
if (len > 7 && p[6] == '+') {
for (i = 0; i < 6; i++)
if (p[i] < 'A' || p[i] > 'Z')
break;
if (i == 6) {
p += 7;
len -= 7;
}
}
font->ps_name = malloc (len + 1);
if (unlikely (font->ps_name == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (font->ps_name, p, len);
font->ps_name[len] = 0;
}
cff_index_fini (&index);
 
return status;
716,6 → 909,10
cairo_cff_font_read_private_dict (cairo_cff_font_t *font,
cairo_hash_table_t *private_dict,
cairo_array_t *local_sub_index,
int *local_sub_bias,
cairo_bool_t **local_subs_used,
double *default_width,
double *nominal_width,
unsigned char *ptr,
int size)
{
726,6 → 923,7
int i;
unsigned char *operand;
unsigned char *p;
int num_subs;
 
status = cff_dict_read (private_dict, ptr, size);
if (unlikely (status))
746,6 → 944,28
return status;
}
 
*default_width = 0;
operand = cff_dict_get_operands (private_dict, DEFAULTWIDTH_OP, &i);
if (operand)
decode_number (operand, default_width);
 
*nominal_width = 0;
operand = cff_dict_get_operands (private_dict, NOMINALWIDTH_OP, &i);
if (operand)
decode_number (operand, nominal_width);
 
num_subs = _cairo_array_num_elements (local_sub_index);
*local_subs_used = calloc (num_subs, sizeof (cairo_bool_t));
if (unlikely (*local_subs_used == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (num_subs < 1240)
*local_sub_bias = 107;
else if (num_subs < 33900)
*local_sub_bias = 1131;
else
*local_sub_bias = 32768;
 
return CAIRO_STATUS_SUCCESS;
}
 
820,6 → 1040,30
goto fail;
}
 
font->fd_local_sub_bias = calloc (sizeof (int), font->num_fontdicts);
if (unlikely (font->fd_local_sub_bias == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
 
font->fd_local_subs_used = calloc (sizeof (cairo_bool_t *), font->num_fontdicts);
if (unlikely (font->fd_local_subs_used == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
 
font->fd_default_width = calloc (font->num_fontdicts, sizeof (double));
if (unlikely (font->fd_default_width == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
 
font->fd_nominal_width = calloc (font->num_fontdicts, sizeof (double));
if (unlikely (font->fd_nominal_width == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
 
for (i = 0; i < font->num_fontdicts; i++) {
status = cff_dict_init (&font->fd_dict[i]);
if (unlikely (status))
845,6 → 1089,10
status = cairo_cff_font_read_private_dict (font,
font->fd_private_dict[i],
&font->fd_local_sub_index[i],
&font->fd_local_sub_bias[i],
&font->fd_local_subs_used[i],
&font->fd_default_width[i],
&font->fd_nominal_width[i],
font->data + offset,
size);
if (unlikely (status))
867,6 → 1115,58
return status;
}
 
static void
cairo_cff_font_read_font_metrics (cairo_cff_font_t *font, cairo_hash_table_t *top_dict)
{
unsigned char *p;
unsigned char *end;
int size;
double x_min, y_min, x_max, y_max;
double xx, yx, xy, yy;
 
x_min = 0.0;
y_min = 0.0;
x_max = 0.0;
y_max = 0.0;
p = cff_dict_get_operands (font->top_dict, FONTBBOX_OP, &size);
if (p) {
end = p + size;
if (p < end)
p = decode_number (p, &x_min);
if (p < end)
p = decode_number (p, &y_min);
if (p < end)
p = decode_number (p, &x_max);
if (p < end)
p = decode_number (p, &y_max);
}
font->x_min = floor (x_min);
font->y_min = floor (y_min);
font->x_max = floor (x_max);
font->y_max = floor (y_max);
font->ascent = font->y_max;
font->descent = font->y_min;
 
xx = 0.001;
yx = 0.0;
xy = 0.0;
yy = 0.001;
p = cff_dict_get_operands (font->top_dict, FONTMATRIX_OP, &size);
if (p) {
end = p + size;
if (p < end)
p = decode_number (p, &xx);
if (p < end)
p = decode_number (p, &yx);
if (p < end)
p = decode_number (p, &xy);
if (p < end)
p = decode_number (p, &yy);
}
/* Freetype uses 1/yy to get units per EM */
font->units_per_em = _cairo_round(1.0/yy);
}
 
static cairo_int_status_t
cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
{
904,6 → 1204,20
font->num_glyphs = _cairo_array_num_elements (&font->charstrings_index);
 
if (font->is_cid) {
operand = cff_dict_get_operands (font->top_dict, CHARSET_OP, &size);
if (!operand)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
decode_integer (operand, &offset);
font->charset = font->data + offset;
if (font->charset >= font->data_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (!font->is_opentype)
cairo_cff_font_read_font_metrics (font, font->top_dict);
 
if (font->is_cid) {
operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
decode_integer (operand, &offset);
status = cairo_cff_font_read_fdselect (font, font->data + offset);
922,6 → 1236,10
status = cairo_cff_font_read_private_dict (font,
font->private_dict,
&font->local_sub_index,
&font->local_sub_bias,
&font->local_subs_used,
&font->default_width,
&font->nominal_width,
font->data + offset,
size);
if (unlikely (status))
936,22 → 1254,36
goto fail;
 
status = cff_dict_set_operands (font->top_dict,
FDSELECT_OP, buf, end_buf - buf);
CHARSET_OP, buf, end_buf - buf);
if (unlikely (status))
goto fail;
 
if (font->scaled_font_subset->is_latin) {
status = cff_dict_set_operands (font->top_dict,
FDARRAY_OP, buf, end_buf - buf);
ENCODING_OP, buf, end_buf - buf);
if (unlikely (status))
goto fail;
 
/* Private has two operands - size and offset */
end_buf = encode_integer_max (end_buf, 0);
cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf - buf);
if (unlikely (status))
goto fail;
 
} else {
status = cff_dict_set_operands (font->top_dict,
CHARSET_OP, buf, end_buf - buf);
FDSELECT_OP, buf, end_buf - buf);
if (unlikely (status))
goto fail;
 
status = cff_dict_set_operands (font->top_dict,
FDARRAY_OP, buf, end_buf - buf);
if (unlikely (status))
goto fail;
 
cff_dict_remove (font->top_dict, ENCODING_OP);
cff_dict_remove (font->top_dict, PRIVATE_OP);
}
 
/* Remove the unique identifier operators as the subsetted font is
* not the same is the original font. */
973,7 → 1305,26
static cairo_int_status_t
cairo_cff_font_read_global_subroutines (cairo_cff_font_t *font)
{
return cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
cairo_int_status_t status;
int num_subs;
 
status = cff_index_read (&font->global_sub_index, &font->current_ptr, font->data_end);
if (unlikely (status))
return status;
 
num_subs = _cairo_array_num_elements (&font->global_sub_index);
font->global_subs_used = calloc (num_subs, sizeof(cairo_bool_t));
if (unlikely (font->global_subs_used == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (num_subs < 1240)
font->global_sub_bias = 107;
else if (num_subs < 33900)
font->global_sub_bias = 1131;
else
font->global_sub_bias = 32768;
 
return CAIRO_STATUS_SUCCESS;
}
 
typedef cairo_int_status_t
1103,22 → 1454,364
return CAIRO_STATUS_SUCCESS;
}
 
static unsigned char *
type2_decode_integer (unsigned char *p, int *integer)
{
if (*p == 28) {
*integer = p[1] << 8 | p[2];
p += 3;
} else if (*p <= 246) {
*integer = *p++ - 139;
} else if (*p <= 250) {
*integer = (p[0] - 247) * 256 + p[1] + 108;
p += 2;
} else if (*p <= 254) {
*integer = -(p[0] - 251) * 256 - p[1] - 108;
p += 2;
} else { /* *p == 255 */
/* 16.16 fixed-point number. The fraction is ignored. */
*integer = (int16_t)((p[1] << 8) | p[2]);
p += 5;
}
return p;
}
 
/* Type 2 charstring parser for finding calls to local or global
* subroutines. For non Opentype CFF fonts it also gets the glyph
* widths.
*
* When we find a subroutine operator, the subroutine is marked as in
* use and recursively followed. The subroutine number is the value on
* the top of the stack when the subroutine operator is executed. In
* most fonts the subroutine number is encoded in an integer
* immediately preceding the subroutine operator. However it is
* possible for the subroutine number on the stack to be the result of
* a computation (in which case there will be an operator preceding
* the subroutine operator). If this occurs, subroutine subsetting is
* disabled since we can't easily determine which subroutines are
* used.
*
* The width, if present, is the first integer in the charstring. The
* only way to confirm if the integer at the start of the charstring is
* the width is when the first stack clearing operator is parsed,
* check if there is an extra integer left over on the stack.
*
* When the first stack clearing operator is encountered
* type2_find_width is set to FALSE and type2_found_width is set to
* TRUE if an extra argument is found, otherwise FALSE.
*/
static cairo_status_t
cairo_cff_font_subset_charstrings (cairo_cff_font_t *font)
cairo_cff_parse_charstring (cairo_cff_font_t *font,
unsigned char *charstring, int length,
int glyph_id,
cairo_bool_t need_width)
{
unsigned char *p = charstring;
unsigned char *end = charstring + length;
int integer;
int hint_bytes;
int sub_num;
cff_index_element_t *element;
unsigned int i;
int fd;
 
while (p < end) {
if (*p == 28 || *p >= 32) {
/* Integer value */
p = type2_decode_integer (p, &integer);
font->type2_stack_size++;
font->type2_stack_top_value = integer;
font->type2_stack_top_is_int = TRUE;
if (!font->type2_seen_first_int) {
font->type2_width = integer;
font->type2_seen_first_int = TRUE;
}
} else if (*p == TYPE2_hstem || *p == TYPE2_vstem ||
*p == TYPE2_hstemhm || *p == TYPE2_vstemhm) {
/* Hint operator. The number of hints declared by the
* operator depends on the size of the stack. */
font->type2_stack_top_is_int = FALSE;
font->type2_num_hints += font->type2_stack_size/2;
if (font->type2_find_width && font->type2_stack_size % 2)
font->type2_found_width = TRUE;
 
font->type2_stack_size = 0;
font->type2_find_width = FALSE;
p++;
} else if (*p == TYPE2_hintmask || *p == TYPE2_cntrmask) {
/* Hintmask operator. These operators are followed by a
* variable length mask where the length depends on the
* number of hints declared. The first time this is called
* it is also an implicit vstem if there are arguments on
* the stack. */
if (font->type2_hintmask_bytes == 0) {
font->type2_stack_top_is_int = FALSE;
font->type2_num_hints += font->type2_stack_size/2;
if (font->type2_find_width && font->type2_stack_size % 2)
font->type2_found_width = TRUE;
 
font->type2_stack_size = 0;
font->type2_find_width = FALSE;
font->type2_hintmask_bytes = (font->type2_num_hints+7)/8;
}
 
hint_bytes = font->type2_hintmask_bytes;
p++;
p += hint_bytes;
} else if (*p == TYPE2_rmoveto) {
if (font->type2_find_width && font->type2_stack_size > 2)
font->type2_found_width = TRUE;
 
font->type2_stack_size = 0;
font->type2_find_width = FALSE;
font->type2_has_path = TRUE;
p++;
} else if (*p == TYPE2_hmoveto || *p == TYPE2_vmoveto) {
if (font->type2_find_width && font->type2_stack_size > 1)
font->type2_found_width = TRUE;
 
font->type2_stack_size = 0;
font->type2_find_width = FALSE;
font->type2_has_path = TRUE;
p++;
} else if (*p == TYPE2_endchar) {
if (!font->type2_has_path && font->type2_stack_size > 3)
return CAIRO_INT_STATUS_UNSUPPORTED; /* seac (Ref Appendix C of Type 2 Charstring Format */
 
if (font->type2_find_width && font->type2_stack_size > 0)
font->type2_found_width = TRUE;
 
return CAIRO_STATUS_SUCCESS;
} else if (*p == TYPE2_callsubr) {
/* call to local subroutine */
if (! font->type2_stack_top_is_int)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
p++;
font->type2_stack_top_is_int = FALSE;
font->type2_stack_size--;
if (font->type2_find_width && font->type2_stack_size == 0)
font->type2_seen_first_int = FALSE;
 
if (font->is_cid) {
fd = font->fdselect[glyph_id];
sub_num = font->type2_stack_top_value + font->fd_local_sub_bias[fd];
element = _cairo_array_index (&font->fd_local_sub_index[fd], sub_num);
if (! font->fd_local_subs_used[fd][sub_num]) {
font->fd_local_subs_used[fd][sub_num] = TRUE;
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
}
} else {
sub_num = font->type2_stack_top_value + font->local_sub_bias;
element = _cairo_array_index (&font->local_sub_index, sub_num);
if (! font->local_subs_used[sub_num] ||
(need_width && !font->type2_found_width))
{
font->local_subs_used[sub_num] = TRUE;
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
}
}
font->type2_nesting_level--;
} else if (*p == TYPE2_callgsubr) {
/* call to global subroutine */
if (! font->type2_stack_top_is_int)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (++font->type2_nesting_level > MAX_SUBROUTINE_NESTING)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
p++;
font->type2_stack_size--;
font->type2_stack_top_is_int = FALSE;
if (font->type2_find_width && font->type2_stack_size == 0)
font->type2_seen_first_int = FALSE;
 
sub_num = font->type2_stack_top_value + font->global_sub_bias;
element = _cairo_array_index (&font->global_sub_index, sub_num);
if (! font->global_subs_used[sub_num] ||
(need_width && !font->type2_found_width))
{
font->global_subs_used[sub_num] = TRUE;
cairo_cff_parse_charstring (font, element->data, element->length, glyph_id, need_width);
}
font->type2_nesting_level--;
} else if (*p == 12) {
/* 2 byte instruction */
 
/* All the 2 byte operators are either not valid before a
* stack clearing operator or they are one of the
* arithmetic, storage, or conditional operators. */
if (need_width && font->type2_find_width)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
p += 2;
font->type2_stack_top_is_int = FALSE;
} else {
/* 1 byte instruction */
p++;
font->type2_stack_top_is_int = FALSE;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_cff_find_width_and_subroutines_used (cairo_cff_font_t *font,
unsigned char *charstring, int length,
int glyph_id, int subset_id)
{
cairo_status_t status;
int width;
int fd;
 
font->type2_stack_size = 0;
font->type2_stack_top_value = 0;;
font->type2_stack_top_is_int = FALSE;
font->type2_num_hints = 0;
font->type2_hintmask_bytes = 0;
font->type2_nesting_level = 0;
font->type2_seen_first_int = FALSE;
font->type2_find_width = TRUE;
font->type2_found_width = FALSE;
font->type2_width = 0;
font->type2_has_path = FALSE;
 
status = cairo_cff_parse_charstring (font, charstring, length, glyph_id, TRUE);
if (status)
return status;
 
if (!font->is_opentype) {
if (font->is_cid) {
fd = font->fdselect[glyph_id];
if (font->type2_found_width)
width = font->fd_nominal_width[fd] + font->type2_width;
else
width = font->fd_default_width[fd];
} else {
if (font->type2_found_width)
width = font->nominal_width + font->type2_width;
else
width = font->default_width;
}
font->widths[subset_id] = width;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
cairo_cff_font_get_gid_for_cid (cairo_cff_font_t *font, unsigned long cid, unsigned long *gid)
{
unsigned char *p;
unsigned long first_gid;
unsigned long first_cid;
int num_left;
unsigned long c, g;
 
if (cid == 0) {
*gid = 0;
return CAIRO_STATUS_SUCCESS;
}
 
switch (font->charset[0]) {
/* Format 0 */
case 0:
p = font->charset + 1;
g = 1;
while (g <= (unsigned)font->num_glyphs && p < font->data_end) {
c = be16_to_cpu( *((uint16_t *)p) );
if (c == cid) {
*gid = g;
return CAIRO_STATUS_SUCCESS;
}
g++;
p += 2;
}
break;
 
/* Format 1 */
case 1:
first_gid = 1;
p = font->charset + 1;
while (first_gid <= (unsigned)font->num_glyphs && p + 2 < font->data_end) {
first_cid = be16_to_cpu( *((uint16_t *)p) );
num_left = p[2];
if (cid >= first_cid && cid <= first_cid + num_left) {
*gid = first_gid + cid - first_cid;
return CAIRO_STATUS_SUCCESS;
}
first_gid += num_left + 1;
p += 3;
}
break;
 
/* Format 2 */
case 2:
first_gid = 1;
p = font->charset + 1;
while (first_gid <= (unsigned)font->num_glyphs && p + 3 < font->data_end) {
first_cid = be16_to_cpu( *((uint16_t *)p) );
num_left = be16_to_cpu( *((uint16_t *)(p+2)) );
if (cid >= first_cid && cid <= first_cid + num_left) {
*gid = first_gid + cid - first_cid;
return CAIRO_STATUS_SUCCESS;
}
first_gid += num_left + 1;
p += 4;
}
break;
 
default:
break;
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_int_status_t
cairo_cff_font_subset_charstrings_and_subroutines (cairo_cff_font_t *font)
{
cff_index_element_t *element;
unsigned int i;
cairo_int_status_t status;
unsigned long glyph, cid;
 
font->subset_subroutines = TRUE;
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
element = _cairo_array_index (&font->charstrings_index,
font->scaled_font_subset->glyphs[i]);
if (font->is_cid) {
cid = font->scaled_font_subset->glyphs[i];
status = cairo_cff_font_get_gid_for_cid (font, cid, &glyph);
if (unlikely (status))
return status;
} else {
glyph = font->scaled_font_subset->glyphs[i];
}
element = _cairo_array_index (&font->charstrings_index, glyph);
status = cff_index_append (&font->charstrings_subset_index,
element->data,
element->length);
if (unlikely (status))
return status;
 
if (font->subset_subroutines) {
status = cairo_cff_find_width_and_subroutines_used (font,
element->data, element->length,
glyph, i);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* If parsing the charstrings fails we embed all the
* subroutines. But if the font is not opentype we
* need to successfully parse all charstrings to get
* the widths. */
font->subset_subroutines = FALSE;
if (!font->is_opentype)
return status;
} else if (unlikely (status)) {
return status;
}
}
}
 
return CAIRO_STATUS_SUCCESS;
}
1129,6 → 1822,8
unsigned int i;
int fd;
int *reverse_map;
unsigned long cid, gid;
cairo_int_status_t status;
 
font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
sizeof (int));
1152,7 → 1847,12
 
font->num_subset_fontdicts = 0;
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
fd = font->fdselect[font->scaled_font_subset->glyphs[i]];
cid = font->scaled_font_subset->glyphs[i];
status = cairo_cff_font_get_gid_for_cid (font, cid, &gid);
if (unlikely (status))
return status;
 
fd = font->fdselect[gid];
if (reverse_map[fd] < 0) {
font->fd_subset_map[font->num_subset_fontdicts] = fd;
reverse_map[fd] = font->num_subset_fontdicts++;
1233,19 → 1933,49
return status;
}
 
/* The Euro is the only the only character in the winansi encoding
* with a glyph name that is not a CFF standard string. As the strings
* are written before the charset, we need to check during the
* subsetting phase if the Euro glyph is required and add the
* glyphname to the list of strings to write out.
*/
static cairo_status_t
cairo_cff_font_add_euro_charset_string (cairo_cff_font_t *font)
{
cairo_status_t status;
unsigned int i;
int ch;
const char *euro = "Euro";
 
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
ch = font->scaled_font_subset->to_latin_char[i];
if (ch == 128) {
font->euro_sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
status = cff_index_append_copy (&font->strings_subset_index,
(unsigned char *)euro, strlen(euro));
return status;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_cff_font_subset_font (cairo_cff_font_t *font)
{
cairo_status_t status;
 
if (!font->scaled_font_subset->is_latin) {
status = cairo_cff_font_set_ros_strings (font);
if (unlikely (status))
return status;
}
 
status = cairo_cff_font_subset_charstrings (font);
status = cairo_cff_font_subset_charstrings_and_subroutines (font);
if (unlikely (status))
return status;
 
if (!font->scaled_font_subset->is_latin) {
if (font->is_cid)
status = cairo_cff_font_subset_fontdict (font);
else
1252,11 → 1982,19
status = cairo_cff_font_create_cid_fontdict (font);
if (unlikely (status))
return status;
} else {
font->private_dict_offset = malloc (sizeof (int));
if (unlikely (font->private_dict_offset == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
status = cairo_cff_font_subset_strings (font);
if (unlikely (status))
return status;
 
if (font->scaled_font_subset->is_latin)
status = cairo_cff_font_add_euro_charset_string (font);
 
return status;
}
 
1300,8 → 2038,8
cff_index_init (&index);
 
status = cff_index_append_copy (&index,
(unsigned char *) font->subset_font_name,
strlen(font->subset_font_name));
(unsigned char *) font->ps_name,
strlen(font->ps_name));
if (unlikely (status))
goto FAIL;
 
1370,10 → 2108,46
static cairo_status_t
cairo_cff_font_write_global_subrs (cairo_cff_font_t *font)
{
unsigned int i;
unsigned char return_op = TYPE2_return;
 
/* poppler and fontforge don't like zero length subroutines so we
* replace unused subroutines with a 'return' instruction. */
if (font->subset_subroutines) {
for (i = 0; i < _cairo_array_num_elements (&font->global_sub_index); i++) {
if (! font->global_subs_used[i])
cff_index_set_object (&font->global_sub_index, i, &return_op, 1);
}
}
 
return cff_index_write (&font->global_sub_index, &font->output);
}
 
static cairo_status_t
cairo_cff_font_write_encoding (cairo_cff_font_t *font)
{
unsigned char buf[2];
cairo_status_t status;
unsigned int i;
 
cairo_cff_font_set_topdict_operator_to_cur_pos (font, ENCODING_OP);
buf[0] = 0; /* Format 0 */
buf[1] = font->scaled_font_subset->num_glyphs - 1;
status = _cairo_array_append_multiple (&font->output, buf, 2);
if (unlikely (status))
return status;
 
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
unsigned char ch = font->scaled_font_subset->to_latin_char[i];
status = _cairo_array_append (&font->output, &ch);
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_cff_font_write_fdselect (cairo_cff_font_t *font)
{
unsigned char data;
1404,31 → 2178,116
 
byte = 3;
status = _cairo_array_append (&font->output, &byte);
assert (status == CAIRO_STATUS_SUCCESS);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
word = cpu_to_be16 (1);
status = _cairo_array_append_multiple (&font->output, &word, 2);
assert (status == CAIRO_STATUS_SUCCESS);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
word = cpu_to_be16 (0);
status = _cairo_array_append_multiple (&font->output, &word, 2);
assert (status == CAIRO_STATUS_SUCCESS);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
byte = 0;
status = _cairo_array_append (&font->output, &byte);
assert (status == CAIRO_STATUS_SUCCESS);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
word = cpu_to_be16 (font->scaled_font_subset->num_glyphs);
status = _cairo_array_append_multiple (&font->output, &word, 2);
assert (status == CAIRO_STATUS_SUCCESS);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Winansi to CFF standard strings mapping for characters 128 to 255 */
static const int winansi_to_cff_std_string[] = {
/* 128 */
0, 0, 117, 101, 118, 121, 112, 113,
126, 122, 192, 107, 142, 0, 199, 0,
/* 144 */
0, 65, 8, 105, 119, 116, 111, 137,
127, 153, 221, 108, 148, 0, 228, 198,
/* 160 */
0, 96, 97, 98, 103, 100, 160, 102,
131, 170, 139, 106, 151, 0, 165, 128,
/* 176 */
161, 156, 164, 169, 125, 152, 115, 114,
133, 150, 143, 120, 158, 155, 163, 123,
/* 192 */
174, 171, 172, 176, 173, 175, 138, 177,
181, 178, 179, 180, 185, 182, 183, 184,
/* 208 */
154, 186, 190, 187, 188, 191, 189, 168,
141, 196, 193, 194, 195, 197, 157, 149,
/* 224 */
203, 200, 201, 205, 202, 204, 144, 206,
210, 207, 208, 209, 214, 211, 212, 213,
/* 240 */
167, 215, 219, 216, 217, 220, 218, 159,
147, 225, 222, 223, 224, 226, 162, 227,
};
 
static int
cairo_cff_font_get_sid_for_winansi_char (cairo_cff_font_t *font, int ch)
{
int sid;
 
if (ch == 39) {
sid = 104;
 
} else if (ch == 96) {
sid = 124;
 
} else if (ch >= 32 && ch <= 126) {
sid = ch - 31;
 
} else if (ch == 128) {
assert (font->euro_sid >= NUM_STD_STRINGS);
sid = font->euro_sid;
 
} else if (ch >= 128 && ch <= 255) {
sid = winansi_to_cff_std_string[ch - 128];
 
} else {
sid = 0;
}
 
return sid;
}
 
static cairo_status_t
cairo_cff_font_write_charset (cairo_cff_font_t *font)
cairo_cff_font_write_type1_charset (cairo_cff_font_t *font)
{
unsigned char format = 0;
unsigned int i;
int ch, sid;
cairo_status_t status;
uint16_t sid_be16;
 
cairo_cff_font_set_topdict_operator_to_cur_pos (font, CHARSET_OP);
status = _cairo_array_append (&font->output, &format);
if (unlikely (status))
return status;
 
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
ch = font->scaled_font_subset->to_latin_char[i];
sid = cairo_cff_font_get_sid_for_winansi_char (font, ch);
if (unlikely (status))
return status;
 
sid_be16 = cpu_to_be16(sid);
status = _cairo_array_append_multiple (&font->output, &sid_be16, sizeof(sid_be16));
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_cff_font_write_cid_charset (cairo_cff_font_t *font)
{
unsigned char byte;
uint16_t word;
cairo_status_t status;
1466,7 → 2325,8
{
unsigned int i;
cairo_int_status_t status;
uint32_t *offset_array;
unsigned int offset_array;
uint32_t *offset_array_ptr;
int offset_base;
uint16_t count;
uint8_t offset_size = 4;
1479,19 → 2339,25
status = _cairo_array_append (&font->output, &offset_size);
if (unlikely (status))
return status;
 
offset_array = _cairo_array_num_elements (&font->output);
status = _cairo_array_allocate (&font->output,
(font->num_subset_fontdicts + 1)*offset_size,
(void **) &offset_array);
(void **) &offset_array_ptr);
if (unlikely (status))
return status;
offset_base = _cairo_array_num_elements (&font->output) - 1;
*offset_array++ = cpu_to_be32(1);
*offset_array_ptr = cpu_to_be32(1);
offset_array += sizeof(uint32_t);
for (i = 0; i < font->num_subset_fontdicts; i++) {
status = cff_dict_write (font->fd_dict[font->fd_subset_map[i]],
&font->output);
if (unlikely (status))
return status;
*offset_array++ = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
 
offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
*offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
offset_array += sizeof(uint32_t);
}
 
return CAIRO_STATUS_SUCCESS;
1532,7 → 2398,8
cairo_cff_font_write_local_sub (cairo_cff_font_t *font,
int dict_num,
cairo_hash_table_t *private_dict,
cairo_array_t *local_sub_index)
cairo_array_t *local_sub_index,
cairo_bool_t *local_subs_used)
{
int offset;
int size;
1540,6 → 2407,8
unsigned char *buf_end;
unsigned char *p;
cairo_status_t status;
unsigned int i;
unsigned char return_op = TYPE2_return;
 
if (_cairo_array_num_elements (local_sub_index) > 0) {
/* Write local subroutines and update offset in private
1551,6 → 2420,16
assert (offset > 0);
p = _cairo_array_index (&font->output, offset);
memcpy (p, buf, buf_end - buf);
 
/* poppler and fontforge don't like zero length subroutines so
* we replace unused subroutines with a 'return' instruction.
*/
if (font->subset_subroutines) {
for (i = 0; i < _cairo_array_num_elements (local_sub_index); i++) {
if (! local_subs_used[i])
cff_index_set_object (local_sub_index, i, &return_op, 1);
}
}
status = cff_index_write (local_sub_index, &font->output);
if (unlikely (status))
return status;
1582,7 → 2461,8
font,
i,
font->fd_private_dict[font->fd_subset_map[i]],
&font->fd_local_sub_index[font->fd_subset_map[i]]);
&font->fd_local_sub_index[font->fd_subset_map[i]],
font->fd_local_subs_used[font->fd_subset_map[i]]);
if (unlikely (status))
return status;
}
1597,7 → 2477,8
status = cairo_cff_font_write_local_sub (font,
0,
font->private_dict,
&font->local_sub_index);
&font->local_sub_index,
font->local_subs_used);
if (unlikely (status))
return status;
}
1605,16 → 2486,40
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_cff_font_write_type1_private_dict_and_local_sub (cairo_cff_font_t *font)
{
cairo_int_status_t status;
 
status = cairo_cff_font_write_private_dict (font,
0,
font->top_dict,
font->private_dict);
if (unlikely (status))
return status;
 
status = cairo_cff_font_write_local_sub (font,
0,
font->private_dict,
&font->local_sub_index,
font->local_subs_used);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
 
typedef cairo_status_t
(*font_write_t) (cairo_cff_font_t *font);
 
static const font_write_t font_write_funcs[] = {
static const font_write_t font_write_cid_funcs[] = {
cairo_cff_font_write_header,
cairo_cff_font_write_name,
cairo_cff_font_write_top_dict,
cairo_cff_font_write_strings,
cairo_cff_font_write_global_subrs,
cairo_cff_font_write_charset,
cairo_cff_font_write_cid_charset,
cairo_cff_font_write_fdselect,
cairo_cff_font_write_charstrings,
cairo_cff_font_write_cid_fontdict,
1621,6 → 2526,18
cairo_cff_font_write_cid_private_dict_and_local_sub,
};
 
static const font_write_t font_write_type1_funcs[] = {
cairo_cff_font_write_header,
cairo_cff_font_write_name,
cairo_cff_font_write_top_dict,
cairo_cff_font_write_strings,
cairo_cff_font_write_global_subrs,
cairo_cff_font_write_encoding,
cairo_cff_font_write_type1_charset,
cairo_cff_font_write_charstrings,
cairo_cff_font_write_type1_private_dict_and_local_sub,
};
 
static cairo_status_t
cairo_cff_font_write_subset (cairo_cff_font_t *font)
{
1627,11 → 2544,19
cairo_int_status_t status;
unsigned int i;
 
for (i = 0; i < ARRAY_LENGTH (font_write_funcs); i++) {
status = font_write_funcs[i] (font);
if (font->scaled_font_subset->is_latin) {
for (i = 0; i < ARRAY_LENGTH (font_write_type1_funcs); i++) {
status = font_write_type1_funcs[i] (font);
if (unlikely (status))
return status;
}
} else {
for (i = 0; i < ARRAY_LENGTH (font_write_cid_funcs); i++) {
status = font_write_cid_funcs[i] (font);
if (unlikely (status))
return status;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
1647,6 → 2572,17
if (unlikely (status))
return status;
 
/* If the PS name is not found, create a CairoFont-x-y name. */
if (font->ps_name == NULL) {
font->ps_name = malloc (30);
if (unlikely (font->ps_name == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
font->scaled_font_subset->font_id,
font->scaled_font_subset->subset_id);
}
 
status = cairo_cff_font_subset_font (font);
if (unlikely (status))
return status;
1655,6 → 2591,7
if (unlikely (status))
return status;
 
 
*data = _cairo_array_index (&font->output, 0);
*length = _cairo_array_num_elements (&font->output);
 
1682,7 → 2619,7
return status;
num_hmetrics = be16_to_cpu (hhea.num_hmetrics);
 
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
glyph_index = font->scaled_font_subset->glyphs[i];
long_entry_size = 2 * sizeof (int16_t);
short_entry_size = sizeof (int16_t);
1709,30 → 2646,45
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
check_fontdata_is_cff (const unsigned char *data, long length)
{
cff_header_t *header;
 
if (length < (long)sizeof (cff_header_t))
return FALSE;
 
header = (cff_header_t *) data;
if (header->major == 1 &&
header->minor == 0 &&
header->header_size == 4)
{
return TRUE;
}
 
return FALSE;
}
 
static cairo_int_status_t
_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_cff_font_t **font_return,
const char *subset_name)
_cairo_cff_font_load_opentype_cff (cairo_cff_font_t *font)
{
const cairo_scaled_font_backend_t *backend;
const cairo_scaled_font_backend_t *backend = font->backend;
cairo_status_t status;
cairo_cff_font_t *font;
tt_head_t head;
tt_hhea_t hhea;
unsigned long size, data_length;
 
backend = scaled_font_subset->scaled_font->backend;
if (!backend->load_truetype_table)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
data_length = 0;
status = backend->load_truetype_table( scaled_font_subset->scaled_font,
status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_CFF, 0, NULL, &data_length);
if (unlikely (status))
if (status)
return status;
 
size = sizeof (tt_head_t);
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_head, 0,
(unsigned char *) &head, &size);
if (unlikely (status))
1739,7 → 2691,7
return status;
 
size = sizeof (tt_hhea_t);
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_hhea, 0,
(unsigned char *) &hhea, &size);
if (unlikely (status))
1746,28 → 2698,11
return status;
 
size = 0;
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
status = backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_hmtx, 0, NULL, &size);
if (unlikely (status))
return status;
 
font = malloc (sizeof (cairo_cff_font_t));
if (unlikely (font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font->backend = backend;
font->scaled_font_subset = scaled_font_subset;
 
_cairo_array_init (&font->output, sizeof (char));
status = _cairo_array_grow_by (&font->output, 4096);
if (unlikely (status))
goto fail2;
 
font->subset_font_name = strdup (subset_name);
if (unlikely (font->subset_font_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
font->x_min = (int16_t) be16_to_cpu (head.x_min);
font->y_min = (int16_t) be16_to_cpu (head.y_min);
font->x_max = (int16_t) be16_to_cpu (head.x_max);
1779,56 → 2714,123
font->units_per_em = 1000;
 
font->font_name = NULL;
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
status = _cairo_truetype_read_font_name (font->scaled_font_subset->scaled_font,
&font->ps_name,
&font->font_name);
if (_cairo_status_is_error (status))
goto fail3;
return status;
 
/* If the PS name is not found, create a CairoFont-x-y name. */
if (font->ps_name == NULL) {
font->ps_name = malloc (30);
if (unlikely (font->ps_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
font->is_opentype = TRUE;
font->data_length = data_length;
font->data = malloc (data_length);
if (unlikely (font->data == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_CFF, 0, font->data,
&font->data_length);
if (unlikely (status))
return status;
 
if (!check_fontdata_is_cff (font->data, data_length))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
scaled_font_subset->font_id,
scaled_font_subset->subset_id);
static cairo_int_status_t
_cairo_cff_font_load_cff (cairo_cff_font_t *font)
{
const cairo_scaled_font_backend_t *backend = font->backend;
cairo_status_t status;
unsigned long data_length;
 
if (!backend->load_type1_data)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
data_length = 0;
status = backend->load_type1_data (font->scaled_font_subset->scaled_font,
0, NULL, &data_length);
if (unlikely (status))
return status;
 
font->font_name = NULL;
font->is_opentype = FALSE;
font->data_length = data_length;
font->data = malloc (data_length);
if (unlikely (font->data == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = font->backend->load_type1_data (font->scaled_font_subset->scaled_font,
0, font->data, &font->data_length);
if (unlikely (status))
return status;
 
if (!check_fontdata_is_cff (font->data, data_length))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_cff_font_t **font_return,
const char *subset_name)
{
const cairo_scaled_font_backend_t *backend;
cairo_int_status_t status;
cairo_cff_font_t *font;
 
backend = scaled_font_subset->scaled_font->backend;
 
/* We need to use a fallback font generated from the synthesized outlines. */
if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font = calloc (1, sizeof (cairo_cff_font_t));
if (unlikely (font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font->backend = backend;
font->scaled_font_subset = scaled_font_subset;
 
status = _cairo_cff_font_load_opentype_cff (font);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status = _cairo_cff_font_load_cff (font);
if (status)
goto fail1;
 
font->data_end = font->data + font->data_length;
_cairo_array_init (&font->output, sizeof (char));
status = _cairo_array_grow_by (&font->output, 4096);
if (unlikely (status))
goto fail2;
 
font->subset_font_name = strdup (subset_name);
if (unlikely (font->subset_font_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
 
font->widths = calloc (font->scaled_font_subset->num_glyphs, sizeof (int));
if (unlikely (font->widths == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail4;
goto fail3;
}
 
if (font->is_opentype) {
status = cairo_cff_font_create_set_widths (font);
if (unlikely (status))
goto fail5;
 
font->data_length = data_length;
font->data = malloc (data_length);
if (unlikely (font->data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail5;
goto fail4;
}
status = font->backend->load_truetype_table ( font->scaled_font_subset->scaled_font,
TT_TAG_CFF, 0, font->data,
&font->data_length);
if (unlikely (status))
goto fail6;
 
font->data_end = font->data + font->data_length;
 
status = cff_dict_init (&font->top_dict);
if (unlikely (status))
goto fail6;
goto fail4;
 
status = cff_dict_init (&font->private_dict);
if (unlikely (status))
goto fail7;
goto fail5;
 
cff_index_init (&font->strings_index);
cff_index_init (&font->charstrings_index);
1836,31 → 2838,35
cff_index_init (&font->local_sub_index);
cff_index_init (&font->charstrings_subset_index);
cff_index_init (&font->strings_subset_index);
font->euro_sid = 0;
font->fdselect = NULL;
font->fd_dict = NULL;
font->fd_private_dict = NULL;
font->fd_local_sub_index = NULL;
font->fd_local_sub_bias = NULL;
font->fdselect_subset = NULL;
font->fd_subset_map = NULL;
font->private_dict_offset = NULL;
font->global_subs_used = NULL;
font->local_subs_used = NULL;
font->fd_local_subs_used = NULL;
 
*font_return = font;
 
return CAIRO_STATUS_SUCCESS;
 
fail7:
fail5:
_cairo_hash_table_destroy (font->top_dict);
fail6:
free (font->data);
fail5:
fail4:
free (font->widths);
fail4:
if (font->font_name)
free (font->font_name);
fail3:
free (font->subset_font_name);
fail2:
free (font->ps_name);
_cairo_array_fini (&font->output);
fail1:
free (font->data);
free (font->font_name);
free (font);
 
return status;
1872,7 → 2878,6
unsigned int i;
 
free (font->widths);
if (font->font_name)
free (font->font_name);
free (font->ps_name);
free (font->subset_font_name);
1895,15 → 2900,13
}
free (font->fd_dict);
}
if (font->fd_subset_map)
free (font->global_subs_used);
free (font->local_subs_used);
free (font->fd_subset_map);
if (font->private_dict_offset)
free (font->private_dict_offset);
 
if (font->is_cid) {
if (font->fdselect)
free (font->fdselect);
if (font->fdselect_subset)
free (font->fdselect_subset);
if (font->fd_private_dict) {
for (i = 0; i < font->num_fontdicts; i++) {
1917,9 → 2920,17
cff_index_fini (&font->fd_local_sub_index[i]);
free (font->fd_local_sub_index);
}
free (font->fd_local_sub_bias);
if (font->fd_local_subs_used) {
for (i = 0; i < font->num_fontdicts; i++) {
free (font->fd_local_subs_used[i]);
}
free (font->fd_local_subs_used);
}
free (font->fd_default_width);
free (font->fd_nominal_width);
}
 
if (font->data)
free (font->data);
 
free (font);
1951,13 → 2962,13
}
 
if (font->font_name) {
cff_subset->font_name = strdup (font->font_name);
if (cff_subset->font_name == NULL) {
cff_subset->family_name_utf8 = strdup (font->font_name);
if (cff_subset->family_name_utf8 == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
} else {
cff_subset->font_name = NULL;
cff_subset->family_name_utf8 = NULL;
}
 
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
1991,8 → 3002,7
fail4:
free (cff_subset->widths);
fail3:
if (cff_subset->font_name)
free (cff_subset->font_name);
free (cff_subset->family_name_utf8);
fail2:
free (cff_subset->ps_name);
fail1:
2005,12 → 3015,118
_cairo_cff_subset_fini (cairo_cff_subset_t *subset)
{
free (subset->ps_name);
if (subset->font_name)
free (subset->font_name);
free (subset->family_name_utf8);
free (subset->widths);
free (subset->data);
}
 
cairo_bool_t
_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font)
{
const cairo_scaled_font_backend_t *backend;
cairo_int_status_t status;
unsigned char *data;
unsigned long data_length;
unsigned char *current_ptr;
unsigned char *data_end;
cff_header_t *header;
cff_index_element_t *element;
cairo_hash_table_t *top_dict;
cairo_array_t index;
int size;
cairo_bool_t is_cid = FALSE;
 
backend = scaled_font->backend;
data = NULL;
data_length = 0;
status = CAIRO_INT_STATUS_UNSUPPORTED;
/* Try to load an OpenType/CFF font */
if (backend->load_truetype_table &&
(status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
{
data = malloc (data_length);
if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return FALSE;
}
 
status = backend->load_truetype_table (scaled_font, TT_TAG_CFF,
0, data, &data_length);
if (unlikely (status))
goto fail1;
}
/* Try to load a CFF font */
if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
backend->load_type1_data &&
(status = backend->load_type1_data (scaled_font,
0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS)
{
data = malloc (data_length);
if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return FALSE;
}
 
status = backend->load_type1_data (scaled_font, 0, data, &data_length);
if (unlikely (status))
goto fail1;
}
if (status)
goto fail1;
 
/* Check if it looks like a CFF font */
if (!check_fontdata_is_cff (data, data_length))
goto fail1;
 
data_end = data + data_length;
 
/* skip header */
if (data_length < sizeof (cff_header_t))
goto fail1;
 
header = (cff_header_t *) data;
current_ptr = data + header->header_size;
 
/* skip name */
cff_index_init (&index);
status = cff_index_read (&index, &current_ptr, data_end);
cff_index_fini (&index);
 
if (status)
goto fail1;
 
/* read top dict */
cff_index_init (&index);
status = cff_index_read (&index, &current_ptr, data_end);
if (unlikely (status))
goto fail2;
 
status = cff_dict_init (&top_dict);
if (unlikely (status))
goto fail2;
 
element = _cairo_array_index (&index, 0);
status = cff_dict_read (top_dict, element->data, element->length);
if (unlikely (status))
goto fail3;
 
/* check for ROS operator indicating a CID font */
if (cff_dict_get_operands (top_dict, ROS_OP, &size) != NULL)
is_cid = TRUE;
 
fail3:
cff_dict_fini (top_dict);
 
fail2:
cff_index_fini (&index);
 
fail1:
free (data);
 
return is_cid;
}
 
static cairo_int_status_t
_cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_cff_font_t **font_return,
2075,6 → 3191,9
cff_index_init (&font->local_sub_index);
cff_index_init (&font->charstrings_subset_index);
cff_index_init (&font->strings_subset_index);
font->global_subs_used = NULL;
font->local_subs_used = NULL;
font->subset_subroutines = FALSE;
font->fdselect = NULL;
font->fd_dict = NULL;
font->fd_private_dict = NULL;
2092,7 → 3211,6
fail4:
free (font->widths);
fail3:
if (font->font_name)
free (font->font_name);
free (font->ps_name);
fail2:
2113,8 → 3231,9
cff_header_t header;
cairo_array_t *charstring;
unsigned char buf[40];
unsigned char *end_buf;
unsigned char *end_buf, *end_buf2;
unsigned int i;
int sid;
 
/* Create header */
header.major = 1;
2125,6 → 3244,28
 
/* Create Top Dict */
font->is_cid = FALSE;
 
snprintf((char*)buf, sizeof(buf), "CairoFont-%u-%u",
font->scaled_font_subset->font_id,
font->scaled_font_subset->subset_id);
sid = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
status = cff_index_append_copy (&font->strings_subset_index,
(unsigned char *)buf,
strlen((char*)buf));
if (unlikely (status))
return status;
 
end_buf = encode_integer (buf, sid);
status = cff_dict_set_operands (font->top_dict, FULLNAME_OP,
buf, end_buf - buf);
if (unlikely (status))
return status;
 
status = cff_dict_set_operands (font->top_dict, FAMILYNAME_OP,
buf, end_buf - buf);
if (unlikely (status))
return status;
 
end_buf = encode_integer (buf, type2_subset->x_min);
end_buf = encode_integer (end_buf, type2_subset->y_min);
end_buf = encode_integer (end_buf, type2_subset->x_max);
2140,7 → 3281,21
if (unlikely (status))
return status;
 
 
if (font->scaled_font_subset->is_latin) {
status = cff_dict_set_operands (font->top_dict,
ENCODING_OP, buf, end_buf - buf);
if (unlikely (status))
return status;
 
/* Private has two operands - size and offset */
end_buf2 = encode_integer_max (end_buf, 0);
cff_dict_set_operands (font->top_dict, PRIVATE_OP, buf, end_buf2 - buf);
if (unlikely (status))
return status;
 
} else {
status = cff_dict_set_operands (font->top_dict,
FDSELECT_OP, buf, end_buf - buf);
if (unlikely (status))
return status;
2149,6 → 3304,7
FDARRAY_OP, buf, end_buf - buf);
if (unlikely (status))
return status;
}
 
status = cff_dict_set_operands (font->top_dict,
CHARSET_OP, buf, end_buf - buf);
2155,6 → 3311,7
if (unlikely (status))
return status;
 
if (!font->scaled_font_subset->is_latin) {
status = cairo_cff_font_set_ros_strings (font);
if (unlikely (status))
return status;
2163,6 → 3320,11
status = cairo_cff_font_create_cid_fontdict (font);
if (unlikely (status))
return status;
} else {
font->private_dict_offset = malloc (sizeof (int));
if (unlikely (font->private_dict_offset == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* Create charstrings */
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
2176,6 → 3338,9
return status;
}
 
if (font->scaled_font_subset->is_latin)
status = cairo_cff_font_add_euro_charset_string (font);
 
status = cairo_cff_font_write_subset (font);
if (unlikely (status))
return status;
2210,7 → 3375,7
if (unlikely (status))
goto fail2;
 
cff_subset->font_name = NULL;
cff_subset->family_name_utf8 = NULL;
cff_subset->ps_name = strdup (font->ps_name);
if (unlikely (cff_subset->ps_name == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2241,7 → 3406,6
 
memcpy (cff_subset->data, data, length);
cff_subset->data_length = length;
cff_subset->data_length = length;
 
_cairo_type2_charstrings_fini (&type2_subset);
cairo_cff_font_destroy (font);
/programs/develop/libraries/cairo/src/cairo-clip-boxes.c
0,0 → 1,594
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2009 Chris Wilson
*
* 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>
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-pattern-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-region-private.h"
 
static inline int
pot (int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
 
static cairo_bool_t
_cairo_clip_contains_rectangle_box (const cairo_clip_t *clip,
const cairo_rectangle_int_t *rect,
const cairo_box_t *box)
{
int i;
 
/* clip == NULL means no clip, so the clip contains everything */
if (clip == NULL)
return TRUE;
 
if (_cairo_clip_is_all_clipped (clip))
return FALSE;
 
/* If we have a non-trivial path, just say no */
if (clip->path)
return FALSE;
 
if (! _cairo_rectangle_contains_rectangle (&clip->extents, rect))
return FALSE;
 
if (clip->num_boxes == 0)
return TRUE;
 
/* Check for a clip-box that wholly contains the rectangle */
for (i = 0; i < clip->num_boxes; i++) {
if (box->p1.x >= clip->boxes[i].p1.x &&
box->p1.y >= clip->boxes[i].p1.y &&
box->p2.x <= clip->boxes[i].p2.x &&
box->p2.y <= clip->boxes[i].p2.y)
{
return TRUE;
}
}
 
return FALSE;
}
 
cairo_bool_t
_cairo_clip_contains_box (const cairo_clip_t *clip,
const cairo_box_t *box)
{
cairo_rectangle_int_t rect;
 
_cairo_box_round_to_rectangle (box, &rect);
return _cairo_clip_contains_rectangle_box(clip, &rect, box);
}
 
cairo_bool_t
_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
const cairo_rectangle_int_t *rect)
{
cairo_box_t box;
 
box.p1.x = _cairo_fixed_from_int (rect->x);
box.p1.y = _cairo_fixed_from_int (rect->y);
box.p2.x = _cairo_fixed_from_int (rect->x + rect->width);
box.p2.y = _cairo_fixed_from_int (rect->y + rect->height);
 
return _cairo_clip_contains_rectangle_box (clip, rect, &box);
}
 
cairo_clip_t *
_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_status_t status;
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_STATUS_SUCCESS && boxes.num_boxes))
clip = _cairo_clip_intersect_boxes (clip, &boxes);
else
clip = _cairo_clip_set_all_clipped (clip);
_cairo_boxes_fini (&boxes);
 
return clip;
}
 
static cairo_clip_t *
_cairo_clip_intersect_rectangle_box (cairo_clip_t *clip,
const cairo_rectangle_int_t *r,
const cairo_box_t *box)
{
cairo_box_t extents_box;
cairo_bool_t changed = FALSE;
int i, j;
 
if (clip == NULL) {
clip = _cairo_clip_create ();
if (clip == NULL)
return _cairo_clip_set_all_clipped (clip);
}
 
if (clip->num_boxes == 0) {
clip->boxes = &clip->embedded_box;
clip->boxes[0] = *box;
clip->num_boxes = 1;
if (clip->path == NULL) {
clip->extents = *r;
} else {
if (! _cairo_rectangle_intersect (&clip->extents, r))
clip = _cairo_clip_set_all_clipped (clip);
}
if (clip->path == NULL)
clip->is_region = _cairo_box_is_pixel_aligned (box);
return clip;
}
 
/* Does the new box wholly subsume the clip? Perform a cheap check
* for the common condition of a single clip rectangle.
*/
if (clip->num_boxes == 1 &&
clip->boxes[0].p1.x >= box->p1.x &&
clip->boxes[0].p1.y >= box->p1.y &&
clip->boxes[0].p2.x <= box->p2.x &&
clip->boxes[0].p2.y <= box->p2.y)
{
return clip;
}
 
for (i = j = 0; i < clip->num_boxes; i++) {
cairo_box_t *b = &clip->boxes[j];
 
if (j != i)
*b = clip->boxes[i];
 
if (box->p1.x > b->p1.x)
b->p1.x = box->p1.x, changed = TRUE;
if (box->p2.x < b->p2.x)
b->p2.x = box->p2.x, changed = TRUE;
 
if (box->p1.y > b->p1.y)
b->p1.y = box->p1.y, changed = TRUE;
if (box->p2.y < b->p2.y)
b->p2.y = box->p2.y, changed = TRUE;
 
j += b->p2.x > b->p1.x && b->p2.y > b->p1.y;
}
clip->num_boxes = j;
 
if (clip->num_boxes == 0)
return _cairo_clip_set_all_clipped (clip);
 
if (! changed)
return clip;
 
extents_box = clip->boxes[0];
for (i = 1; i < clip->num_boxes; i++) {
if (clip->boxes[i].p1.x < extents_box.p1.x)
extents_box.p1.x = clip->boxes[i].p1.x;
 
if (clip->boxes[i].p1.y < extents_box.p1.y)
extents_box.p1.y = clip->boxes[i].p1.y;
 
if (clip->boxes[i].p2.x > extents_box.p2.x)
extents_box.p2.x = clip->boxes[i].p2.x;
 
if (clip->boxes[i].p2.y > extents_box.p2.y)
extents_box.p2.y = clip->boxes[i].p2.y;
}
 
if (clip->path == NULL) {
_cairo_box_round_to_rectangle (&extents_box, &clip->extents);
} else {
cairo_rectangle_int_t extents_rect;
 
_cairo_box_round_to_rectangle (&extents_box, &extents_rect);
if (! _cairo_rectangle_intersect (&clip->extents, &extents_rect))
return _cairo_clip_set_all_clipped (clip);
}
 
if (clip->region) {
cairo_region_destroy (clip->region);
clip->region = NULL;
}
 
clip->is_region = FALSE;
return clip;
}
 
cairo_clip_t *
_cairo_clip_intersect_box (cairo_clip_t *clip,
const cairo_box_t *box)
{
cairo_rectangle_int_t r;
 
_cairo_box_round_to_rectangle (box, &r);
if (r.width == 0 || r.height == 0)
return _cairo_clip_set_all_clipped (clip);
 
return _cairo_clip_intersect_rectangle_box (clip, &r, box);
}
 
cairo_clip_t *
_cairo_clip_intersect_boxes (cairo_clip_t *clip,
const cairo_boxes_t *boxes)
{
cairo_boxes_t clip_boxes;
cairo_box_t limits;
cairo_rectangle_int_t extents;
 
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
if (boxes->num_boxes == 0)
return _cairo_clip_set_all_clipped (clip);
 
if (boxes->num_boxes == 1)
return _cairo_clip_intersect_box (clip, boxes->chunks.base);
 
if (clip == NULL)
clip = _cairo_clip_create ();
 
if (clip->num_boxes) {
_cairo_boxes_init_for_array (&clip_boxes, clip->boxes, clip->num_boxes);
if (unlikely (_cairo_boxes_intersect (&clip_boxes, boxes, &clip_boxes))) {
clip = _cairo_clip_set_all_clipped (clip);
goto out;
}
 
if (clip->boxes != &clip->embedded_box)
free (clip->boxes);
 
clip->boxes = NULL;
boxes = &clip_boxes;
}
 
if (boxes->num_boxes == 0) {
clip = _cairo_clip_set_all_clipped (clip);
goto out;
} else if (boxes->num_boxes == 1) {
clip->boxes = &clip->embedded_box;
clip->boxes[0] = boxes->chunks.base[0];
clip->num_boxes = 1;
} else {
clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
}
_cairo_boxes_extents (boxes, &limits);
 
_cairo_box_round_to_rectangle (&limits, &extents);
if (clip->path == NULL)
clip->extents = extents;
else if (! _cairo_rectangle_intersect (&clip->extents, &extents))
clip = _cairo_clip_set_all_clipped (clip);
 
if (clip->region) {
cairo_region_destroy (clip->region);
clip->region = NULL;
}
clip->is_region = FALSE;
 
out:
if (boxes == &clip_boxes)
_cairo_boxes_fini (&clip_boxes);
 
return clip;
}
 
cairo_clip_t *
_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *r)
{
cairo_box_t box;
 
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
if (r->width == 0 || r->height == 0)
return _cairo_clip_set_all_clipped (clip);
 
box.p1.x = _cairo_fixed_from_int (r->x);
box.p1.y = _cairo_fixed_from_int (r->y);
box.p2.x = _cairo_fixed_from_int (r->x + r->width);
box.p2.y = _cairo_fixed_from_int (r->y + r->height);
 
return _cairo_clip_intersect_rectangle_box (clip, r, &box);
}
 
struct reduce {
cairo_clip_t *clip;
cairo_box_t limit;
cairo_box_t extents;
cairo_bool_t inside;
 
cairo_point_t current_point;
cairo_point_t last_move_to;
};
 
static void
_add_clipped_edge (struct reduce *r,
const cairo_point_t *p1,
const cairo_point_t *p2,
int y1, int y2)
{
cairo_fixed_t x;
 
x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y1);
if (x < r->extents.p1.x)
r->extents.p1.x = x;
 
x = _cairo_edge_compute_intersection_x_for_y (p1, p2, y2);
if (x > r->extents.p2.x)
r->extents.p2.x = x;
 
if (y1 < r->extents.p1.y)
r->extents.p1.y = y1;
 
if (y2 > r->extents.p2.y)
r->extents.p2.y = y2;
 
r->inside = TRUE;
}
 
static void
_add_edge (struct reduce *r,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
int top, bottom;
int top_y, bot_y;
int n;
 
if (p1->y < p2->y) {
top = p1->y;
bottom = p2->y;
} else {
top = p2->y;
bottom = p1->y;
}
 
if (bottom < r->limit.p1.y || top > r->limit.p2.y)
return;
 
if (p1->x > p2->x) {
const cairo_point_t *t = p1;
p1 = p2;
p2 = t;
}
 
if (p2->x <= r->limit.p1.x || p1->x >= r->limit.p2.x)
return;
 
for (n = 0; n < r->clip->num_boxes; n++) {
const cairo_box_t *limits = &r->clip->boxes[n];
 
if (bottom < limits->p1.y || top > limits->p2.y)
continue;
 
if (p2->x <= limits->p1.x || p1->x >= limits->p2.x)
continue;
 
if (p1->x >= limits->p1.x && p2->x <= limits->p1.x) {
top_y = top;
bot_y = bottom;
} else {
int p1_y, p2_y;
 
p1_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p1.x);
p2_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p2.x);
if (p1_y < p2_y) {
top_y = p1_y;
bot_y = p2_y;
} else {
top_y = p2_y;
bot_y = p1_y;
}
 
if (top_y < top)
top_y = top;
if (bot_y > bottom)
bot_y = bottom;
}
 
if (top_y < limits->p1.y)
top_y = limits->p1.y;
 
if (bot_y > limits->p2.y)
bot_y = limits->p2.y;
if (bot_y > top_y)
_add_clipped_edge (r, p1, p2, top_y, bot_y);
}
}
 
static cairo_status_t
_reduce_line_to (void *closure,
const cairo_point_t *point)
{
struct reduce *r = closure;
 
_add_edge (r, &r->current_point, point);
r->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_reduce_close (void *closure)
{
struct reduce *r = closure;
 
return _reduce_line_to (r, &r->last_move_to);
}
 
static cairo_status_t
_reduce_move_to (void *closure,
const cairo_point_t *point)
{
struct reduce *r = closure;
cairo_status_t status;
 
/* close current subpath */
status = _reduce_close (closure);
 
/* make sure that the closure represents a degenerate path */
r->current_point = *point;
r->last_move_to = *point;
 
return status;
}
 
static cairo_clip_t *
_cairo_clip_reduce_to_boxes (cairo_clip_t *clip)
{
struct reduce r;
cairo_clip_path_t *clip_path;
cairo_status_t status;
 
return clip;
if (clip->path == NULL)
return clip;
 
r.clip = clip;
r.extents.p1.x = r.extents.p1.y = INT_MAX;
r.extents.p2.x = r.extents.p2.y = INT_MIN;
r.inside = FALSE;
 
r.limit.p1.x = _cairo_fixed_from_int (clip->extents.x);
r.limit.p1.y = _cairo_fixed_from_int (clip->extents.y);
r.limit.p2.x = _cairo_fixed_from_int (clip->extents.x + clip->extents.width);
r.limit.p2.y = _cairo_fixed_from_int (clip->extents.y + clip->extents.height);
 
clip_path = clip->path;
do {
r.current_point.x = 0;
r.current_point.y = 0;
r.last_move_to = r.current_point;
 
status = _cairo_path_fixed_interpret_flat (&clip_path->path,
_reduce_move_to,
_reduce_line_to,
_reduce_close,
&r,
clip_path->tolerance);
assert (status == CAIRO_STATUS_SUCCESS);
_reduce_close (&r);
} while ((clip_path = clip_path->prev));
 
if (! r.inside) {
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
}
 
return _cairo_clip_intersect_box (clip, &r.extents);
}
 
cairo_clip_t *
_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
const cairo_rectangle_int_t *r)
{
cairo_clip_t *copy;
 
if (_cairo_clip_is_all_clipped (clip))
return (cairo_clip_t *) clip;
 
if (_cairo_clip_contains_rectangle (clip, r))
return _cairo_clip_intersect_rectangle (NULL, r);
 
copy = _cairo_clip_copy_intersect_rectangle (clip, r);
if (_cairo_clip_is_all_clipped (copy))
return copy;
 
return _cairo_clip_reduce_to_boxes (copy);
}
 
cairo_clip_t *
_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
cairo_composite_rectangles_t *extents)
{
const cairo_rectangle_int_t *r;
 
r = extents->is_bounded ? &extents->bounded : &extents->unbounded;
return _cairo_clip_reduce_to_rectangle (clip, r);
}
 
cairo_clip_t *
_cairo_clip_from_boxes (const cairo_boxes_t *boxes)
{
cairo_box_t extents;
cairo_clip_t *clip = _cairo_clip_create ();
if (clip == NULL)
return _cairo_clip_set_all_clipped (clip);
 
/* XXX cow-boxes? */
if(boxes->num_boxes == 1) {
clip->boxes = &clip->embedded_box;
clip->boxes[0] = boxes->chunks.base[0];
clip->num_boxes = 1;
} else {
clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE);
if (clip->boxes == NULL)
return _cairo_clip_set_all_clipped (clip);
}
 
_cairo_boxes_extents (boxes, &extents);
_cairo_box_round_to_rectangle (&extents, &clip->extents);
 
return clip;
}
/programs/develop/libraries/cairo/src/cairo-clip-inline.h
0,0 → 1,83
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_CLIP_INLINE_H
#define CAIRO_CLIP_INLINE_H
 
#include "cairo-clip-private.h"
 
static inline cairo_bool_t _cairo_clip_is_all_clipped(const cairo_clip_t *clip)
{
return clip == &__cairo_clip_all;
}
 
static inline cairo_clip_t *
_cairo_clip_set_all_clipped (cairo_clip_t *clip)
{
_cairo_clip_destroy (clip);
return (cairo_clip_t *) &__cairo_clip_all;
}
 
static inline cairo_clip_t *
_cairo_clip_copy_intersect_rectangle (const cairo_clip_t *clip,
const cairo_rectangle_int_t *r)
{
return _cairo_clip_intersect_rectangle (_cairo_clip_copy (clip), r);
}
 
static inline cairo_clip_t *
_cairo_clip_copy_intersect_clip (const cairo_clip_t *clip,
const cairo_clip_t *other)
{
return _cairo_clip_intersect_clip (_cairo_clip_copy (clip), other);
}
 
static inline void
_cairo_clip_steal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
{
_cairo_boxes_init_for_array (boxes, clip->boxes, clip->num_boxes);
clip->boxes = NULL;
clip->num_boxes = 0;
}
 
static inline void
_cairo_clip_unsteal_boxes (cairo_clip_t *clip, cairo_boxes_t *boxes)
{
clip->boxes = boxes->chunks.base;
clip->num_boxes = boxes->num_boxes;
}
 
#endif /* CAIRO_CLIP_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-clip-polygon.c
0,0 → 1,156
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-pattern-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-region-private.h"
 
static cairo_bool_t
can_convert_to_polygon (const cairo_clip_t *clip)
{
cairo_clip_path_t *clip_path = clip->path;
cairo_antialias_t antialias = clip_path->antialias;
 
while ((clip_path = clip_path->prev) != NULL) {
if (clip_path->antialias != antialias)
return FALSE;
}
 
return TRUE;
}
 
cairo_int_status_t
_cairo_clip_get_polygon (const cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t *fill_rule,
cairo_antialias_t *antialias)
{
cairo_status_t status;
cairo_clip_path_t *clip_path;
 
if (_cairo_clip_is_all_clipped (clip)) {
_cairo_polygon_init (polygon, NULL, 0);
return CAIRO_INT_STATUS_SUCCESS;
}
 
/* If there is no clip, we need an infinite polygon */
assert (clip && (clip->path || clip->num_boxes));
 
if (clip->path == NULL) {
*fill_rule = CAIRO_FILL_RULE_WINDING;
*antialias = CAIRO_ANTIALIAS_DEFAULT;
return _cairo_polygon_init_box_array (polygon,
clip->boxes,
clip->num_boxes);
}
 
/* check that residual is all of the same type/tolerance */
if (! can_convert_to_polygon (clip))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (clip->num_boxes < 2)
_cairo_polygon_init_with_clip (polygon, clip);
else
_cairo_polygon_init_with_clip (polygon, NULL);
 
clip_path = clip->path;
*fill_rule = clip_path->fill_rule;
*antialias = clip_path->antialias;
 
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
polygon);
if (unlikely (status))
goto err;
 
if (clip->num_boxes > 1) {
status = _cairo_polygon_intersect_with_boxes (polygon, fill_rule,
clip->boxes, clip->num_boxes);
if (unlikely (status))
goto err;
}
 
polygon->limits = NULL;
polygon->num_limits = 0;
 
while ((clip_path = clip_path->prev) != NULL) {
cairo_polygon_t next;
 
_cairo_polygon_init (&next, NULL, 0);
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
&next);
if (likely (status == CAIRO_STATUS_SUCCESS))
status = _cairo_polygon_intersect (polygon, *fill_rule,
&next, clip_path->fill_rule);
_cairo_polygon_fini (&next);
if (unlikely (status))
goto err;
 
*fill_rule = CAIRO_FILL_RULE_WINDING;
}
 
return CAIRO_STATUS_SUCCESS;
 
err:
_cairo_polygon_fini (polygon);
return status;
}
 
cairo_bool_t
_cairo_clip_is_polygon (const cairo_clip_t *clip)
{
if (_cairo_clip_is_all_clipped (clip))
return TRUE;
 
/* If there is no clip, we need an infinite polygon */
if (clip == NULL)
return FALSE;
 
if (clip->path == NULL)
return TRUE;
 
/* check that residual is all of the same type/tolerance */
return can_convert_to_polygon (clip);
}
/programs/develop/libraries/cairo/src/cairo-clip-private.h
31,6 → 31,7
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_CLIP_PRIVATE_H
37,18 → 38,16
#define CAIRO_CLIP_PRIVATE_H
 
#include "cairo-types-private.h"
 
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-reference-count-private.h"
 
extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
 
enum {
CAIRO_CLIP_PATH_HAS_REGION = 0x1,
CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED = 0x2,
CAIRO_CLIP_PATH_IS_BOX = 0x4
};
 
struct _cairo_clip_path {
cairo_reference_count_t ref_count;
cairo_path_fixed_t path;
56,96 → 55,144
double tolerance;
cairo_antialias_t antialias;
cairo_clip_path_t *prev;
};
 
struct _cairo_clip {
cairo_rectangle_int_t extents;
cairo_clip_path_t *path;
 
/* partial caches */
unsigned int flags;
cairo_box_t *boxes;
int num_boxes;
 
cairo_region_t *region;
cairo_surface_t *surface;
cairo_bool_t is_region;
 
cairo_box_t embedded_box;
};
 
struct _cairo_clip {
/* can be used as a cairo_hash_entry_t for live clips */
cairo_clip_path_t *path;
cairo_private cairo_clip_t *
_cairo_clip_create (void);
 
cairo_bool_t all_clipped;
cairo_private cairo_clip_path_t *
_cairo_clip_path_reference (cairo_clip_path_t *clip_path);
 
};
cairo_private void
_cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
 
cairo_private void
_cairo_clip_init (cairo_clip_t *clip);
_cairo_clip_destroy (cairo_clip_t *clip);
 
cairo_private_no_warn cairo_clip_t *
_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other);
cairo_private extern const cairo_clip_t __cairo_clip_all;
 
cairo_private cairo_status_t
_cairo_clip_init_copy_transformed (cairo_clip_t *clip,
cairo_clip_t *other,
const cairo_matrix_t *matrix);
cairo_private cairo_clip_t *
_cairo_clip_copy (const cairo_clip_t *clip);
 
cairo_private void
_cairo_clip_reset (cairo_clip_t *clip);
cairo_private cairo_clip_t *
_cairo_clip_copy_region (const cairo_clip_t *clip);
 
cairo_private cairo_clip_t *
_cairo_clip_copy_path (const cairo_clip_t *clip);
 
cairo_private cairo_clip_t *
_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty);
 
cairo_private cairo_clip_t *
_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m);
 
cairo_private cairo_clip_t *
_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty);
 
cairo_private cairo_bool_t
_cairo_clip_equal (const cairo_clip_t *clip_a,
const cairo_clip_t *clip_b);
 
#define _cairo_clip_fini(clip) _cairo_clip_reset (clip)
 
cairo_private cairo_status_t
_cairo_clip_rectangle (cairo_clip_t *clip,
cairo_private cairo_clip_t *
_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *rectangle);
 
cairo_private cairo_status_t
_cairo_clip_clip (cairo_clip_t *clip,
cairo_private cairo_clip_t *
_cairo_clip_intersect_clip (cairo_clip_t *clip,
const cairo_clip_t *other);
 
cairo_private cairo_clip_t *
_cairo_clip_intersect_box (cairo_clip_t *clip,
const cairo_box_t *box);
 
cairo_private cairo_clip_t *
_cairo_clip_intersect_boxes (cairo_clip_t *clip,
const cairo_boxes_t *boxes);
 
cairo_private cairo_clip_t *
_cairo_clip_intersect_rectilinear_path (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
 
cairo_private cairo_clip_t *
_cairo_clip_intersect_path (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias);
 
cairo_private cairo_status_t
_cairo_clip_apply_clip (cairo_clip_t *clip,
const cairo_clip_t *other);
 
cairo_private const cairo_rectangle_int_t *
_cairo_clip_get_extents (const cairo_clip_t *clip);
 
cairo_private cairo_surface_t *
_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty);
_cairo_clip_get_surface (const cairo_clip_t *clip, cairo_surface_t *dst, int *tx, int *ty);
 
cairo_private cairo_surface_t *
_cairo_clip_get_image (const cairo_clip_t *clip,
cairo_surface_t *target,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_clip_combine_with_surface (cairo_clip_t *clip,
_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
cairo_surface_t *dst,
int dst_x, int dst_y);
 
cairo_private cairo_int_status_t
_cairo_clip_get_region (cairo_clip_t *clip,
cairo_region_t **region);
cairo_private cairo_clip_t *
_cairo_clip_from_boxes (const cairo_boxes_t *boxes);
 
cairo_private cairo_int_status_t
_cairo_clip_get_boxes (cairo_clip_t *clip,
cairo_box_t **boxes,
int *count);
cairo_private cairo_region_t *
_cairo_clip_get_region (const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_clip_to_boxes (cairo_clip_t **clip,
cairo_composite_rectangles_t *extents,
cairo_box_t **boxes,
int *num_boxes);
cairo_private cairo_bool_t
_cairo_clip_is_region (const cairo_clip_t *clip);
 
cairo_private cairo_clip_t *
_cairo_clip_reduce_to_rectangle (const cairo_clip_t *clip,
const cairo_rectangle_int_t *r);
 
cairo_private cairo_clip_t *
_cairo_clip_reduce_for_composite (const cairo_clip_t *clip,
cairo_composite_rectangles_t *extents);
 
cairo_private cairo_bool_t
_cairo_clip_contains_rectangle (cairo_clip_t *clip,
_cairo_clip_contains_rectangle (const cairo_clip_t *clip,
const cairo_rectangle_int_t *rect);
 
cairo_private cairo_bool_t
_cairo_clip_contains_extents (cairo_clip_t *clip,
_cairo_clip_contains_box (const cairo_clip_t *clip,
const cairo_box_t *box);
 
cairo_private cairo_bool_t
_cairo_clip_contains_extents (const cairo_clip_t *clip,
const cairo_composite_rectangles_t *extents);
 
cairo_private void
_cairo_clip_drop_cache (cairo_clip_t *clip);
 
cairo_private cairo_rectangle_list_t*
_cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate);
 
cairo_private cairo_rectangle_list_t *
_cairo_rectangle_list_create_in_error (cairo_status_t status);
 
cairo_private cairo_bool_t
_cairo_clip_is_polygon (const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_clip_get_polygon (const cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t *fill_rule,
cairo_antialias_t *antialias);
 
#endif /* CAIRO_CLIP_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-clip-region.c
0,0 → 1,123
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2009 Chris Wilson
*
* 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>
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-pattern-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-region-private.h"
 
static void
_cairo_clip_extract_region (cairo_clip_t *clip)
{
cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
cairo_rectangle_int_t *r = stack_rects;
cairo_bool_t is_region;
int i;
 
if (clip->num_boxes == 0)
return;
 
if (clip->num_boxes > ARRAY_LENGTH (stack_rects)) {
r = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_rectangle_int_t));
if (r == NULL){
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return;
}
}
 
is_region = clip->path == NULL;
for (i = 0; i < clip->num_boxes; i++) {
cairo_box_t *b = &clip->boxes[i];
if (is_region)
is_region =
_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y);
r[i].x = _cairo_fixed_integer_floor (b->p1.x);
r[i].y = _cairo_fixed_integer_floor (b->p1.y);
r[i].width = _cairo_fixed_integer_ceil (b->p2.x) - r[i].x;
r[i].height = _cairo_fixed_integer_ceil (b->p2.y) - r[i].y;
}
clip->is_region = is_region;
 
clip->region = cairo_region_create_rectangles (r, i);
 
if (r != stack_rects)
free (r);
}
 
cairo_region_t *
_cairo_clip_get_region (const cairo_clip_t *clip)
{
if (clip == NULL)
return NULL;
 
if (clip->region == NULL)
_cairo_clip_extract_region ((cairo_clip_t *) clip);
 
return clip->region;
}
 
cairo_bool_t
_cairo_clip_is_region (const cairo_clip_t *clip)
{
if (clip == NULL)
return TRUE;
 
if (clip->is_region)
return TRUE;
 
/* XXX Geometric reduction? */
 
if (clip->path)
return FALSE;
 
if (clip->num_boxes == 0)
return TRUE;
 
if (clip->region == NULL)
_cairo_clip_extract_region ((cairo_clip_t *) clip);
 
return clip->is_region;
}
/programs/develop/libraries/cairo/src/cairo-clip-surface.c
0,0 → 1,240
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2009 Chris Wilson
*
* 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>
* Kristian Høgsberg <krh@redhat.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-pattern-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-region-private.h"
 
cairo_status_t
_cairo_clip_combine_with_surface (const cairo_clip_t *clip,
cairo_surface_t *dst,
int dst_x, int dst_y)
{
cairo_clip_path_t *copy_path;
cairo_clip_path_t *clip_path;
cairo_clip_t *copy;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
copy = _cairo_clip_copy_with_translation (clip, -dst_x, -dst_y);
copy_path = copy->path;
copy->path = NULL;
 
if (copy->boxes) {
status = _cairo_surface_paint (dst,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
copy);
}
 
clip = NULL;
if (_cairo_clip_is_region (copy))
clip = copy;
clip_path = copy_path;
while (status == CAIRO_STATUS_SUCCESS && clip_path) {
status = _cairo_surface_fill (dst,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
clip);
clip_path = clip_path->prev;
}
 
copy->path = copy_path;
_cairo_clip_destroy (copy);
return status;
}
 
static cairo_status_t
_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
const cairo_box_t *box,
cairo_fixed_t fx,
cairo_fixed_t fy)
{
cairo_status_t status;
 
status = _cairo_path_fixed_move_to (path, box->p1.x + fx, box->p1.y + fy);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p1.y + fy);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p2.x + fx, box->p2.y + fy);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p1.x + fx, box->p2.y + fy);
if (unlikely (status))
return status;
 
return _cairo_path_fixed_close_path (path);
}
 
cairo_surface_t *
_cairo_clip_get_surface (const cairo_clip_t *clip,
cairo_surface_t *target,
int *tx, int *ty)
{
cairo_surface_t *surface;
cairo_status_t status;
cairo_clip_t *copy, *region;
cairo_clip_path_t *copy_path, *clip_path;
 
if (clip->num_boxes) {
cairo_path_fixed_t path;
int i;
 
surface = _cairo_surface_create_similar_solid (target,
CAIRO_CONTENT_ALPHA,
clip->extents.width,
clip->extents.height,
CAIRO_COLOR_TRANSPARENT);
if (unlikely (surface->status))
return surface;
 
_cairo_path_fixed_init (&path);
status = CAIRO_STATUS_SUCCESS;
for (i = 0; status == CAIRO_STATUS_SUCCESS && i < clip->num_boxes; i++) {
status = _cairo_path_fixed_add_box (&path, &clip->boxes[i],
-_cairo_fixed_from_int (clip->extents.x),
-_cairo_fixed_from_int (clip->extents.y));
}
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_surface_fill (surface,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
&path,
CAIRO_FILL_RULE_WINDING,
1.,
CAIRO_ANTIALIAS_DEFAULT,
NULL);
_cairo_path_fixed_fini (&path);
if (unlikely (status)) {
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
}
} else {
surface = _cairo_surface_create_similar_solid (target,
CAIRO_CONTENT_ALPHA,
clip->extents.width,
clip->extents.height,
CAIRO_COLOR_WHITE);
if (unlikely (surface->status))
return surface;
}
 
copy = _cairo_clip_copy_with_translation (clip,
-clip->extents.x,
-clip->extents.y);
copy_path = copy->path;
copy->path = NULL;
 
region = copy;
if (! _cairo_clip_is_region (copy))
region = _cairo_clip_copy_region (copy);
 
status = CAIRO_STATUS_SUCCESS;
clip_path = copy_path;
while (status == CAIRO_STATUS_SUCCESS && clip_path) {
status = _cairo_surface_fill (surface,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
region);
clip_path = clip_path->prev;
}
 
copy->path = copy_path;
_cairo_clip_destroy (copy);
if (region != copy)
_cairo_clip_destroy (region);
 
if (unlikely (status)) {
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
}
 
*tx = clip->extents.x;
*ty = clip->extents.y;
return surface;
}
 
cairo_surface_t *
_cairo_clip_get_image (const cairo_clip_t *clip,
cairo_surface_t *target,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *surface;
cairo_status_t status;
 
surface = cairo_surface_create_similar_image (target,
CAIRO_FORMAT_A8,
extents->width,
extents->height);
if (unlikely (surface->status))
return surface;
 
status = _cairo_surface_paint (surface, CAIRO_OPERATOR_SOURCE,
&_cairo_pattern_white.base, NULL);
if (likely (status == CAIRO_STATUS_SUCCESS))
status = _cairo_clip_combine_with_surface (clip, surface,
extents->x, extents->y);
 
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
 
return surface;
}
/programs/develop/libraries/cairo/src/cairo-clip-tor-scan-converter.c
0,0 → 1,1845
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* glitter-paths - polygon scan converter
*
* Copyright (c) 2008 M Joonas Pihlaja
* Copyright (c) 2007 David Turner
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* This is the Glitter paths scan converter incorporated into cairo.
* The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
* of
*
* http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
*/
/* Glitter-paths is a stand alone polygon rasteriser derived from
* David Turner's reimplementation of Tor Anderssons's 15x17
* supersampling rasteriser from the Apparition graphics library. The
* main new feature here is cheaply choosing per-scan line between
* doing fully analytical coverage computation for an entire row at a
* time vs. using a supersampling approach.
*
* David Turner's code can be found at
*
* http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
*
* In particular this file incorporates large parts of ftgrays_tor10.h
* from raster-comparison-20070813.tar.bz2
*/
/* Overview
*
* A scan converter's basic purpose to take polygon edges and convert
* them into an RLE compressed A8 mask. This one works in two phases:
* gathering edges and generating spans.
*
* 1) As the user feeds the scan converter edges they are vertically
* clipped and bucketted into a _polygon_ data structure. The edges
* are also snapped from the user's coordinates to the subpixel grid
* coordinates used during scan conversion.
*
* user
* |
* | edges
* V
* polygon buckets
*
* 2) Generating spans works by performing a vertical sweep of pixel
* rows from top to bottom and maintaining an _active_list_ of edges
* that intersect the row. From the active list the fill rule
* determines which edges are the left and right edges of the start of
* each span, and their contribution is then accumulated into a pixel
* coverage list (_cell_list_) as coverage deltas. Once the coverage
* deltas of all edges are known we can form spans of constant pixel
* coverage by summing the deltas during a traversal of the cell list.
* At the end of a pixel row the cell list is sent to a coverage
* blitter for rendering to some target surface.
*
* The pixel coverages are computed by either supersampling the row
* and box filtering a mono rasterisation, or by computing the exact
* coverages of edges in the active list. The supersampling method is
* used whenever some edge starts or stops within the row or there are
* edge intersections in the row.
*
* polygon bucket for \
* current pixel row |
* | |
* | activate new edges | Repeat GRID_Y times if we
* V \ are supersampling this row,
* active list / or just once if we're computing
* | | analytical coverage.
* | coverage deltas |
* V |
* pixel coverage list /
* |
* V
* coverage blitter
*/
#include "cairoint.h"
#include "cairo-spans-private.h"
#include "cairo-error-private.h"
 
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <setjmp.h>
 
/* The input coordinate scale and the rasterisation grid scales. */
#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
#define GRID_Y 15
 
/* Set glitter up to use a cairo span renderer to do the coverage
* blitting. */
struct pool;
struct cell_list;
 
/*-------------------------------------------------------------------------
* glitter-paths.h
*/
 
/* "Input scaled" numbers are fixed precision reals with multiplier
* 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
* pixel scaled numbers. These get converted to the internal grid
* scaled numbers as soon as possible. Internal overflow is possible
* if GRID_X/Y inside glitter-paths.c is larger than
* 1<<GLITTER_INPUT_BITS. */
#ifndef GLITTER_INPUT_BITS
# define GLITTER_INPUT_BITS 8
#endif
#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
typedef int glitter_input_scaled_t;
 
/* Opaque type for scan converting. */
typedef struct glitter_scan_converter glitter_scan_converter_t;
 
/*-------------------------------------------------------------------------
* glitter-paths.c: Implementation internal types
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
 
/* All polygon coordinates are snapped onto a subsample grid. "Grid
* scaled" numbers are fixed precision reals with multiplier GRID_X or
* GRID_Y. */
typedef int grid_scaled_t;
typedef int grid_scaled_x_t;
typedef int grid_scaled_y_t;
 
/* Default x/y scale factors.
* You can either define GRID_X/Y_BITS to get a power-of-two scale
* or define GRID_X/Y separately. */
#if !defined(GRID_X) && !defined(GRID_X_BITS)
# define GRID_X_BITS 8
#endif
#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
# define GRID_Y 15
#endif
 
/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
#ifdef GRID_X_BITS
# define GRID_X (1 << GRID_X_BITS)
#endif
#ifdef GRID_Y_BITS
# define GRID_Y (1 << GRID_Y_BITS)
#endif
 
/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
* integer and fractional parts. The integer part is floored. */
#if defined(GRID_X_TO_INT_FRAC)
/* do nothing */
#elif defined(GRID_X_BITS)
# define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
#else
# define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
#endif
 
#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
(i) = (t) / (m); \
(f) = (t) % (m); \
if ((f) < 0) { \
--(i); \
(f) += (m); \
} \
} while (0)
 
#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
(f) = (t) & ((1 << (b)) - 1); \
(i) = (t) >> (b); \
} while (0)
 
/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want
* to be able to represent exactly areas of subpixel trapezoids whose
* vertices are given in grid scaled coordinates. The scale factor
* comes from needing to accurately represent the area 0.5*dx*dy of a
* triangle with base dx and height dy in grid scaled numbers. */
typedef int grid_area_t;
#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
 
/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
#if GRID_XY == 510
# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
#elif GRID_XY == 255
# define GRID_AREA_TO_ALPHA(c) (c)
#elif GRID_XY == 64
# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
#elif GRID_XY == 128
# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
#elif GRID_XY == 256
# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
#elif GRID_XY == 15
# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
#elif GRID_XY == 2*256*15
# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
#else
# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
#endif
 
#define UNROLL3(x) x x x
 
struct quorem {
int32_t quo;
int32_t rem;
};
 
/* Header for a chunk of memory in a memory pool. */
struct _pool_chunk {
/* # bytes used in this chunk. */
size_t size;
 
/* # bytes total in this chunk */
size_t capacity;
 
/* Pointer to the previous chunk or %NULL if this is the sentinel
* chunk in the pool header. */
struct _pool_chunk *prev_chunk;
 
/* Actual data starts here. Well aligned for pointers. */
};
 
/* A memory pool. This is supposed to be embedded on the stack or
* within some other structure. It may optionally be followed by an
* embedded array from which requests are fulfilled until
* malloc needs to be called to allocate a first real chunk. */
struct pool {
/* Chunk we're allocating from. */
struct _pool_chunk *current;
 
jmp_buf *jmp;
 
/* Free list of previously allocated chunks. All have >= default
* capacity. */
struct _pool_chunk *first_free;
 
/* The default capacity of a chunk. */
size_t default_capacity;
 
/* Header for the sentinel chunk. Directly following the pool
* struct should be some space for embedded elements from which
* the sentinel chunk allocates from. */
struct _pool_chunk sentinel[1];
};
 
/* A polygon edge. */
struct edge {
/* Next in y-bucket or active list. */
struct edge *next;
 
/* Current x coordinate while the edge is on the active
* list. Initialised to the x coordinate of the top of the
* edge. The quotient is in grid_scaled_x_t units and the
* remainder is mod dy in grid_scaled_y_t units.*/
struct quorem x;
 
/* Advance of the current x when moving down a subsample line. */
struct quorem dxdy;
 
/* Advance of the current x when moving down a full pixel
* row. Only initialised when the height of the edge is large
* enough that there's a chance the edge could be stepped by a
* full row's worth of subsample rows at a time. */
struct quorem dxdy_full;
 
/* The clipped y of the top of the edge. */
grid_scaled_y_t ytop;
 
/* y2-y1 after orienting the edge downwards. */
grid_scaled_y_t dy;
 
/* Number of subsample rows remaining to scan convert of this
* edge. */
grid_scaled_y_t height_left;
 
/* Original sign of the edge: +1 for downwards, -1 for upwards
* edges. */
int dir;
int vertical;
int clip;
};
 
/* Number of subsample rows per y-bucket. Must be GRID_Y. */
#define EDGE_Y_BUCKET_HEIGHT GRID_Y
 
#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
 
/* A collection of sorted and vertically clipped edges of the polygon.
* Edges are moved from the polygon to an active list while scan
* converting. */
struct polygon {
/* The vertical clip extents. */
grid_scaled_y_t ymin, ymax;
 
/* Array of edges all starting in the same bucket. An edge is put
* into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
* it is added to the polygon. */
struct edge **y_buckets;
struct edge *y_buckets_embedded[64];
 
struct {
struct pool base[1];
struct edge embedded[32];
} edge_pool;
};
 
/* A cell records the effect on pixel coverage of polygon edges
* passing through a pixel. It contains two accumulators of pixel
* coverage.
*
* Consider the effects of a polygon edge on the coverage of a pixel
* it intersects and that of the following one. The coverage of the
* following pixel is the height of the edge multiplied by the width
* of the pixel, and the coverage of the pixel itself is the area of
* the trapezoid formed by the edge and the right side of the pixel.
*
* +-----------------------+-----------------------+
* | | |
* | | |
* |_______________________|_______________________|
* | \...................|.......................|\
* | \..................|.......................| |
* | \.................|.......................| |
* | \....covered.....|.......................| |
* | \....area.......|.......................| } covered height
* | \..............|.......................| |
* |uncovered\.............|.......................| |
* | area \............|.......................| |
* |___________\...........|.......................|/
* | | |
* | | |
* | | |
* +-----------------------+-----------------------+
*
* Since the coverage of the following pixel will always be a multiple
* of the width of the pixel, we can store the height of the covered
* area instead. The coverage of the pixel itself is the total
* coverage minus the area of the uncovered area to the left of the
* edge. As it's faster to compute the uncovered area we only store
* that and subtract it from the total coverage later when forming
* spans to blit.
*
* The heights and areas are signed, with left edges of the polygon
* having positive sign and right edges having negative sign. When
* two edges intersect they swap their left/rightness so their
* contribution above and below the intersection point must be
* computed separately. */
struct cell {
struct cell *next;
int x;
grid_area_t uncovered_area;
grid_scaled_y_t covered_height;
grid_scaled_y_t clipped_height;
};
 
/* A cell list represents the scan line sparsely as cells ordered by
* ascending x. It is geared towards scanning the cells in order
* using an internal cursor. */
struct cell_list {
/* Sentinel nodes */
struct cell head, tail;
 
/* Cursor state for iterating through the cell list. */
struct cell *cursor;
 
/* Cells in the cell list are owned by the cell list and are
* allocated from this pool. */
struct {
struct pool base[1];
struct cell embedded[32];
} cell_pool;
};
 
struct cell_pair {
struct cell *cell1;
struct cell *cell2;
};
 
/* The active list contains edges in the current scan line ordered by
* the x-coordinate of the intercept of the edge and the scan line. */
struct active_list {
/* Leftmost edge on the current scan line. */
struct edge *head;
 
/* A lower bound on the height of the active edges is used to
* estimate how soon some active edge ends. We can't advance the
* scan conversion by a full pixel row if an edge ends somewhere
* within it. */
grid_scaled_y_t min_height;
};
 
struct glitter_scan_converter {
struct polygon polygon[1];
struct active_list active[1];
struct cell_list coverages[1];
 
/* Clip box. */
grid_scaled_y_t ymin, ymax;
};
 
/* Compute the floored division a/b. Assumes / and % perform symmetric
* division. */
inline static struct quorem
floored_divrem(int a, int b)
{
struct quorem qr;
qr.quo = a/b;
qr.rem = a%b;
if ((a^b)<0 && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
* division. */
static struct quorem
floored_muldivrem(int x, int a, int b)
{
struct quorem qr;
long long xa = (long long)x*a;
qr.quo = xa/b;
qr.rem = xa%b;
if ((xa>=0) != (b>=0) && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
static struct _pool_chunk *
_pool_chunk_init(
struct _pool_chunk *p,
struct _pool_chunk *prev_chunk,
size_t capacity)
{
p->prev_chunk = prev_chunk;
p->size = 0;
p->capacity = capacity;
return p;
}
 
static struct _pool_chunk *
_pool_chunk_create(struct pool *pool, size_t size)
{
struct _pool_chunk *p;
 
p = malloc(size + sizeof(struct _pool_chunk));
if (unlikely (NULL == p))
longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
 
return _pool_chunk_init(p, pool->current, size);
}
 
static void
pool_init(struct pool *pool,
jmp_buf *jmp,
size_t default_capacity,
size_t embedded_capacity)
{
pool->jmp = jmp;
pool->current = pool->sentinel;
pool->first_free = NULL;
pool->default_capacity = default_capacity;
_pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
}
 
static void
pool_fini(struct pool *pool)
{
struct _pool_chunk *p = pool->current;
do {
while (NULL != p) {
struct _pool_chunk *prev = p->prev_chunk;
if (p != pool->sentinel)
free(p);
p = prev;
}
p = pool->first_free;
pool->first_free = NULL;
} while (NULL != p);
}
 
/* Satisfy an allocation by first allocating a new large enough chunk
* and adding it to the head of the pool's chunk list. This function
* is called as a fallback if pool_alloc() couldn't do a quick
* allocation from the current chunk in the pool. */
static void *
_pool_alloc_from_new_chunk(
struct pool *pool,
size_t size)
{
struct _pool_chunk *chunk;
void *obj;
size_t capacity;
 
/* If the allocation is smaller than the default chunk size then
* try getting a chunk off the free list. Force alloc of a new
* chunk for large requests. */
capacity = size;
chunk = NULL;
if (size < pool->default_capacity) {
capacity = pool->default_capacity;
chunk = pool->first_free;
if (chunk) {
pool->first_free = chunk->prev_chunk;
_pool_chunk_init(chunk, pool->current, chunk->capacity);
}
}
 
if (NULL == chunk)
chunk = _pool_chunk_create (pool, capacity);
pool->current = chunk;
 
obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
chunk->size += size;
return obj;
}
 
/* Allocate size bytes from the pool. The first allocated address
* returned from a pool is aligned to sizeof(void*). Subsequent
* addresses will maintain alignment as long as multiples of void* are
* allocated. Returns the address of a new memory area or %NULL on
* allocation failures. The pool retains ownership of the returned
* memory. */
inline static void *
pool_alloc (struct pool *pool, size_t size)
{
struct _pool_chunk *chunk = pool->current;
 
if (size <= chunk->capacity - chunk->size) {
void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
chunk->size += size;
return obj;
} else {
return _pool_alloc_from_new_chunk(pool, size);
}
}
 
/* Relinquish all pool_alloced memory back to the pool. */
static void
pool_reset (struct pool *pool)
{
/* Transfer all used chunks to the chunk free list. */
struct _pool_chunk *chunk = pool->current;
if (chunk != pool->sentinel) {
while (chunk->prev_chunk != pool->sentinel) {
chunk = chunk->prev_chunk;
}
chunk->prev_chunk = pool->first_free;
pool->first_free = pool->current;
}
/* Reset the sentinel as the current chunk. */
pool->current = pool->sentinel;
pool->sentinel->size = 0;
}
 
/* Rewinds the cell list's cursor to the beginning. After rewinding
* we're good to cell_list_find() the cell any x coordinate. */
inline static void
cell_list_rewind (struct cell_list *cells)
{
cells->cursor = &cells->head;
}
 
/* Rewind the cell list if its cursor has been advanced past x. */
inline static void
cell_list_maybe_rewind (struct cell_list *cells, int x)
{
struct cell *tail = cells->cursor;
if (tail->x > x)
cell_list_rewind (cells);
}
 
static void
cell_list_init(struct cell_list *cells, jmp_buf *jmp)
{
pool_init(cells->cell_pool.base, jmp,
256*sizeof(struct cell),
sizeof(cells->cell_pool.embedded));
cells->tail.next = NULL;
cells->tail.x = INT_MAX;
cells->head.x = INT_MIN;
cells->head.next = &cells->tail;
cell_list_rewind (cells);
}
 
static void
cell_list_fini(struct cell_list *cells)
{
pool_fini (cells->cell_pool.base);
}
 
/* Empty the cell list. This is called at the start of every pixel
* row. */
inline static void
cell_list_reset (struct cell_list *cells)
{
cell_list_rewind (cells);
cells->head.next = &cells->tail;
pool_reset (cells->cell_pool.base);
}
 
static struct cell *
cell_list_alloc (struct cell_list *cells,
struct cell *tail,
int x)
{
struct cell *cell;
 
cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
cell->next = tail->next;
tail->next = cell;
cell->x = x;
cell->uncovered_area = 0;
cell->covered_height = 0;
cell->clipped_height = 0;
return cell;
}
 
/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
* needed to be allocated but couldn't be. Cells must be found with
* non-decreasing x-coordinate until the cell list is rewound using
* cell_list_rewind(). Ownership of the returned cell is retained by
* the cell list. */
inline static struct cell *
cell_list_find (struct cell_list *cells, int x)
{
struct cell *tail = cells->cursor;
 
while (1) {
UNROLL3({
if (tail->next->x > x)
break;
tail = tail->next;
});
}
 
if (tail->x != x)
tail = cell_list_alloc (cells, tail, x);
return cells->cursor = tail;
 
}
 
/* Find two cells at x1 and x2. This is exactly equivalent
* to
*
* pair.cell1 = cell_list_find(cells, x1);
* pair.cell2 = cell_list_find(cells, x2);
*
* except with less function call overhead. */
inline static struct cell_pair
cell_list_find_pair(struct cell_list *cells, int x1, int x2)
{
struct cell_pair pair;
 
pair.cell1 = cells->cursor;
while (1) {
UNROLL3({
if (pair.cell1->next->x > x1)
break;
pair.cell1 = pair.cell1->next;
});
}
if (pair.cell1->x != x1) {
struct cell *cell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
cell->x = x1;
cell->uncovered_area = 0;
cell->covered_height = 0;
cell->clipped_height = 0;
cell->next = pair.cell1->next;
pair.cell1->next = cell;
pair.cell1 = cell;
}
 
pair.cell2 = pair.cell1;
while (1) {
UNROLL3({
if (pair.cell2->next->x > x2)
break;
pair.cell2 = pair.cell2->next;
});
}
if (pair.cell2->x != x2) {
struct cell *cell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
cell->uncovered_area = 0;
cell->covered_height = 0;
cell->clipped_height = 0;
cell->x = x2;
cell->next = pair.cell2->next;
pair.cell2->next = cell;
pair.cell2 = cell;
}
 
cells->cursor = pair.cell2;
return pair;
}
 
/* Add a subpixel span covering [x1, x2) to the coverage cells. */
inline static void
cell_list_add_subspan(struct cell_list *cells,
grid_scaled_x_t x1,
grid_scaled_x_t x2)
{
int ix1, fx1;
int ix2, fx2;
 
GRID_X_TO_INT_FRAC(x1, ix1, fx1);
GRID_X_TO_INT_FRAC(x2, ix2, fx2);
 
if (ix1 != ix2) {
struct cell_pair p;
p = cell_list_find_pair(cells, ix1, ix2);
p.cell1->uncovered_area += 2*fx1;
++p.cell1->covered_height;
p.cell2->uncovered_area -= 2*fx2;
--p.cell2->covered_height;
} else {
struct cell *cell = cell_list_find(cells, ix1);
cell->uncovered_area += 2*(fx1-fx2);
}
}
 
/* Adds the analytical coverage of an edge crossing the current pixel
* row to the coverage cells and advances the edge's x position to the
* following row.
*
* This function is only called when we know that during this pixel row:
*
* 1) The relative order of all edges on the active list doesn't
* change. In particular, no edges intersect within this row to pixel
* precision.
*
* 2) No new edges start in this row.
*
* 3) No existing edges end mid-row.
*
* This function depends on being called with all edges from the
* active list in the order they appear on the list (i.e. with
* non-decreasing x-coordinate.) */
static void
cell_list_render_edge(
struct cell_list *cells,
struct edge *edge,
int sign)
{
grid_scaled_y_t y1, y2, dy;
grid_scaled_x_t dx;
int ix1, ix2;
grid_scaled_x_t fx1, fx2;
 
struct quorem x1 = edge->x;
struct quorem x2 = x1;
 
if (! edge->vertical) {
x2.quo += edge->dxdy_full.quo;
x2.rem += edge->dxdy_full.rem;
if (x2.rem >= 0) {
++x2.quo;
x2.rem -= edge->dy;
}
 
edge->x = x2;
}
 
GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
 
/* Edge is entirely within a column? */
if (ix1 == ix2) {
/* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */
struct cell *cell = cell_list_find(cells, ix1);
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
return;
}
 
/* Orient the edge left-to-right. */
dx = x2.quo - x1.quo;
if (dx >= 0) {
y1 = 0;
y2 = GRID_Y;
} else {
int tmp;
tmp = ix1; ix1 = ix2; ix2 = tmp;
tmp = fx1; fx1 = fx2; fx2 = tmp;
dx = -dx;
sign = -sign;
y1 = GRID_Y;
y2 = 0;
}
dy = y2 - y1;
 
/* Add coverage for all pixels [ix1,ix2] on this row crossed
* by the edge. */
{
struct cell_pair pair;
struct quorem y = floored_divrem((GRID_X - fx1)*dy, dx);
 
/* When rendering a previous edge on the active list we may
* advance the cell list cursor past the leftmost pixel of the
* current edge even though the two edges don't intersect.
* e.g. consider two edges going down and rightwards:
*
* --\_+---\_+-----+-----+----
* \_ \_ | |
* | \_ | \_ | |
* | \_| \_| |
* | \_ \_ |
* ----+-----+-\---+-\---+----
*
* The left edge touches cells past the starting cell of the
* right edge. Fortunately such cases are rare.
*
* The rewinding is never necessary if the current edge stays
* within a single column because we've checked before calling
* this function that the active list order won't change. */
cell_list_maybe_rewind(cells, ix1);
 
pair = cell_list_find_pair(cells, ix1, ix1+1);
pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
pair.cell1->covered_height += sign*y.quo;
y.quo += y1;
 
if (ix1+1 < ix2) {
struct quorem dydx_full = floored_divrem(GRID_X*dy, dx);
struct cell *cell = pair.cell2;
 
++ix1;
do {
grid_scaled_y_t y_skip = dydx_full.quo;
y.rem += dydx_full.rem;
if (y.rem >= dx) {
++y_skip;
y.rem -= dx;
}
 
y.quo += y_skip;
 
y_skip *= sign;
cell->uncovered_area += y_skip*GRID_X;
cell->covered_height += y_skip;
 
++ix1;
cell = cell_list_find(cells, ix1);
} while (ix1 != ix2);
 
pair.cell2 = cell;
}
pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
pair.cell2->covered_height += sign*(y2 - y.quo);
}
}
 
static void
polygon_init (struct polygon *polygon, jmp_buf *jmp)
{
polygon->ymin = polygon->ymax = 0;
polygon->y_buckets = polygon->y_buckets_embedded;
pool_init (polygon->edge_pool.base, jmp,
8192 - sizeof (struct _pool_chunk),
sizeof (polygon->edge_pool.embedded));
}
 
static void
polygon_fini (struct polygon *polygon)
{
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
 
pool_fini (polygon->edge_pool.base);
}
 
/* Empties the polygon of all edges. The polygon is then prepared to
* receive new edges and clip them to the vertical range
* [ymin,ymax). */
static cairo_status_t
polygon_reset (struct polygon *polygon,
grid_scaled_y_t ymin,
grid_scaled_y_t ymax)
{
unsigned h = ymax - ymin;
unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
ymin);
 
pool_reset(polygon->edge_pool.base);
 
if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
 
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
 
polygon->y_buckets = polygon->y_buckets_embedded;
if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
polygon->y_buckets = _cairo_malloc_ab (num_buckets,
sizeof (struct edge *));
if (unlikely (NULL == polygon->y_buckets))
goto bail_no_mem;
}
memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
 
polygon->ymin = ymin;
polygon->ymax = ymax;
return CAIRO_STATUS_SUCCESS;
 
bail_no_mem:
polygon->ymin = 0;
polygon->ymax = 0;
return CAIRO_STATUS_NO_MEMORY;
}
 
static void
_polygon_insert_edge_into_its_y_bucket(
struct polygon *polygon,
struct edge *e)
{
unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
struct edge **ptail = &polygon->y_buckets[ix];
e->next = *ptail;
*ptail = e;
}
 
inline static void
polygon_add_edge (struct polygon *polygon,
const cairo_edge_t *edge,
int clip)
{
struct edge *e;
grid_scaled_x_t dx;
grid_scaled_y_t dy;
grid_scaled_y_t ytop, ybot;
grid_scaled_y_t ymin = polygon->ymin;
grid_scaled_y_t ymax = polygon->ymax;
 
assert (edge->bottom > edge->top);
 
if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
return;
 
e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
 
dx = edge->line.p2.x - edge->line.p1.x;
dy = edge->line.p2.y - edge->line.p1.y;
e->dy = dy;
e->dir = edge->dir;
e->clip = clip;
 
ytop = edge->top >= ymin ? edge->top : ymin;
ybot = edge->bottom <= ymax ? edge->bottom : ymax;
e->ytop = ytop;
e->height_left = ybot - ytop;
 
if (dx == 0) {
e->vertical = TRUE;
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
e->dxdy.quo = 0;
e->dxdy.rem = 0;
e->dxdy_full.quo = 0;
e->dxdy_full.rem = 0;
} else {
e->vertical = FALSE;
e->dxdy = floored_divrem (dx, dy);
if (ytop == edge->line.p1.y) {
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
} else {
e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
e->x.quo += edge->line.p1.x;
}
 
if (e->height_left >= GRID_Y) {
e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
} else {
e->dxdy_full.quo = 0;
e->dxdy_full.rem = 0;
}
}
 
_polygon_insert_edge_into_its_y_bucket (polygon, e);
 
e->x.rem -= dy; /* Bias the remainder for faster
* edge advancement. */
}
 
static void
active_list_reset (struct active_list *active)
{
active->head = NULL;
active->min_height = 0;
}
 
static void
active_list_init(struct active_list *active)
{
active_list_reset(active);
}
 
/*
* Merge two sorted edge lists.
* Input:
* - head_a: The head of the first list.
* - head_b: The head of the second list; head_b cannot be NULL.
* Output:
* Returns the head of the merged list.
*
* Implementation notes:
* To make it fast (in particular, to reduce to an insertion sort whenever
* one of the two input lists only has a single element) we iterate through
* a list until its head becomes greater than the head of the other list,
* then we switch their roles. As soon as one of the two lists is empty, we
* just attach the other one to the current list and exit.
* Writes to memory are only needed to "switch" lists (as it also requires
* attaching to the output list the list which we will be iterating next) and
* to attach the last non-empty list.
*/
static struct edge *
merge_sorted_edges (struct edge *head_a, struct edge *head_b)
{
struct edge *head, **next;
int32_t x;
 
if (head_a == NULL)
return head_b;
 
next = &head;
if (head_a->x.quo <= head_b->x.quo) {
head = head_a;
} else {
head = head_b;
goto start_with_b;
}
 
do {
x = head_b->x.quo;
while (head_a != NULL && head_a->x.quo <= x) {
next = &head_a->next;
head_a = head_a->next;
}
 
*next = head_b;
if (head_a == NULL)
return head;
 
start_with_b:
x = head_a->x.quo;
while (head_b != NULL && head_b->x.quo <= x) {
next = &head_b->next;
head_b = head_b->next;
}
 
*next = head_a;
if (head_b == NULL)
return head;
} while (1);
}
 
/*
* Sort (part of) a list.
* Input:
* - list: The list to be sorted; list cannot be NULL.
* - limit: Recursion limit.
* Output:
* - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
* input list; if the input list has fewer elements, head_out be a sorted list
* containing all the elements of the input list.
* Returns the head of the list of unprocessed elements (NULL if the sorted list contains
* all the elements of the input list).
*
* Implementation notes:
* Special case single element list, unroll/inline the sorting of the first two elements.
* Some tail recursion is used since we iterate on the bottom-up solution of the problem
* (we start with a small sorted list and keep merging other lists of the same size to it).
*/
static struct edge *
sort_edges (struct edge *list,
unsigned int level,
struct edge **head_out)
{
struct edge *head_other, *remaining;
unsigned int i;
 
head_other = list->next;
 
/* Single element list -> return */
if (head_other == NULL) {
*head_out = list;
return NULL;
}
 
/* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
* - Initialize remaining to be the list containing the elements after the second in the input list.
* - Initialize *head_out to be the sorted list containing the first two element.
*/
remaining = head_other->next;
if (list->x.quo <= head_other->x.quo) {
*head_out = list;
/* list->next = head_other; */ /* The input list is already like this. */
head_other->next = NULL;
} else {
*head_out = head_other;
head_other->next = list;
list->next = NULL;
}
 
for (i = 0; i < level && remaining; i++) {
/* Extract a sorted list of the same size as *head_out
* (2^(i+1) elements) from the list of remaining elements. */
remaining = sort_edges (remaining, i, &head_other);
*head_out = merge_sorted_edges (*head_out, head_other);
}
 
/* *head_out now contains (at most) 2^(level+1) elements. */
 
return remaining;
}
 
/* Test if the edges on the active list can be safely advanced by a
* full row without intersections or any edges ending. */
inline static int
active_list_can_step_full_row (struct active_list *active)
{
const struct edge *e;
int prev_x = INT_MIN;
 
/* Recomputes the minimum height of all edges on the active
* list if we have been dropping edges. */
if (active->min_height <= 0) {
int min_height = INT_MAX;
 
e = active->head;
while (NULL != e) {
if (e->height_left < min_height)
min_height = e->height_left;
e = e->next;
}
 
active->min_height = min_height;
}
 
if (active->min_height < GRID_Y)
return 0;
 
/* Check for intersections as no edges end during the next row. */
e = active->head;
while (NULL != e) {
struct quorem x = e->x;
 
if (! e->vertical) {
x.quo += e->dxdy_full.quo;
x.rem += e->dxdy_full.rem;
if (x.rem >= 0)
++x.quo;
}
 
if (x.quo <= prev_x)
return 0;
 
prev_x = x.quo;
e = e->next;
}
 
return 1;
}
 
/* Merges edges on the given subpixel row from the polygon to the
* active_list. */
inline static void
active_list_merge_edges_from_polygon(struct active_list *active,
struct edge **ptail,
grid_scaled_y_t y,
struct polygon *polygon)
{
/* Split off the edges on the current subrow and merge them into
* the active list. */
int min_height = active->min_height;
struct edge *subrow_edges = NULL;
struct edge *tail = *ptail;
 
do {
struct edge *next = tail->next;
 
if (y == tail->ytop) {
tail->next = subrow_edges;
subrow_edges = tail;
 
if (tail->height_left < min_height)
min_height = tail->height_left;
 
*ptail = next;
} else
ptail = &tail->next;
 
tail = next;
} while (tail);
 
if (subrow_edges) {
sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
active->head = merge_sorted_edges (active->head, subrow_edges);
active->min_height = min_height;
}
}
 
/* Advance the edges on the active list by one subsample row by
* updating their x positions. Drop edges from the list that end. */
inline static void
active_list_substep_edges(struct active_list *active)
{
struct edge **cursor = &active->head;
grid_scaled_x_t prev_x = INT_MIN;
struct edge *unsorted = NULL;
struct edge *edge = *cursor;
 
do {
UNROLL3({
struct edge *next;
 
if (NULL == edge)
break;
 
next = edge->next;
if (--edge->height_left) {
edge->x.quo += edge->dxdy.quo;
edge->x.rem += edge->dxdy.rem;
if (edge->x.rem >= 0) {
++edge->x.quo;
edge->x.rem -= edge->dy;
}
 
if (edge->x.quo < prev_x) {
*cursor = next;
edge->next = unsorted;
unsorted = edge;
} else {
prev_x = edge->x.quo;
cursor = &edge->next;
}
} else {
*cursor = next;
}
edge = next;
})
} while (1);
 
if (unsorted) {
sort_edges (unsorted, UINT_MAX, &unsorted);
active->head = merge_sorted_edges (active->head, unsorted);
}
}
 
inline static void
apply_nonzero_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
{
struct edge *edge = active->head;
int winding = 0;
int xstart;
int xend;
 
cell_list_rewind (coverages);
 
while (NULL != edge) {
xstart = edge->x.quo;
winding = edge->dir;
while (1) {
edge = edge->next;
if (NULL == edge) {
ASSERT_NOT_REACHED;
return;
}
 
winding += edge->dir;
if (0 == winding) {
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
}
}
 
xend = edge->x.quo;
cell_list_add_subspan (coverages, xstart, xend);
 
edge = edge->next;
}
}
 
static void
apply_evenodd_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
{
struct edge *edge = active->head;
int xstart;
int xend;
 
cell_list_rewind (coverages);
 
while (NULL != edge) {
xstart = edge->x.quo;
 
while (1) {
edge = edge->next;
if (NULL == edge) {
ASSERT_NOT_REACHED;
return;
}
 
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
 
edge = edge->next;
}
 
xend = edge->x.quo;
cell_list_add_subspan (coverages, xstart, xend);
 
edge = edge->next;
}
}
 
static void
apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
 
left_edge = *cursor;
while (NULL != left_edge) {
struct edge *right_edge;
int winding = left_edge->dir;
 
left_edge->height_left -= GRID_Y;
if (left_edge->height_left)
cursor = &left_edge->next;
else
*cursor = left_edge->next;
 
while (1) {
right_edge = *cursor;
if (NULL == right_edge) {
cell_list_render_edge (coverages, left_edge, +1);
return;
}
 
right_edge->height_left -= GRID_Y;
if (right_edge->height_left)
cursor = &right_edge->next;
else
*cursor = right_edge->next;
 
winding += right_edge->dir;
if (0 == winding) {
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
break;
}
}
 
if (! right_edge->vertical) {
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
if (right_edge->x.rem >= 0) {
++right_edge->x.quo;
right_edge->x.rem -= right_edge->dy;
}
}
}
 
cell_list_render_edge (coverages, left_edge, +1);
cell_list_render_edge (coverages, right_edge, -1);
 
left_edge = *cursor;
}
}
 
static void
apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
 
left_edge = *cursor;
while (NULL != left_edge) {
struct edge *right_edge;
 
left_edge->height_left -= GRID_Y;
if (left_edge->height_left)
cursor = &left_edge->next;
else
*cursor = left_edge->next;
 
while (1) {
right_edge = *cursor;
if (NULL == right_edge) {
cell_list_render_edge (coverages, left_edge, +1);
return;
}
 
right_edge->height_left -= GRID_Y;
if (right_edge->height_left)
cursor = &right_edge->next;
else
*cursor = right_edge->next;
 
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
break;
}
 
if (! right_edge->vertical) {
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
if (right_edge->x.rem >= 0) {
++right_edge->x.quo;
right_edge->x.rem -= right_edge->dy;
}
}
}
 
cell_list_render_edge (coverages, left_edge, +1);
cell_list_render_edge (coverages, right_edge, -1);
 
left_edge = *cursor;
}
}
 
static void
_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
{
polygon_init(converter->polygon, jmp);
active_list_init(converter->active);
cell_list_init(converter->coverages, jmp);
converter->ymin=0;
converter->ymax=0;
}
 
static void
_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
{
polygon_fini(converter->polygon);
cell_list_fini(converter->coverages);
converter->ymin=0;
converter->ymax=0;
}
 
static grid_scaled_t
int_to_grid_scaled(int i, int scale)
{
/* Clamp to max/min representable scaled number. */
if (i >= 0) {
if (i >= INT_MAX/scale)
i = INT_MAX/scale;
}
else {
if (i <= INT_MIN/scale)
i = INT_MIN/scale;
}
return i*scale;
}
 
#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
 
static cairo_status_t
glitter_scan_converter_reset(glitter_scan_converter_t *converter,
int ymin, int ymax)
{
cairo_status_t status;
 
converter->ymin = 0;
converter->ymax = 0;
 
ymin = int_to_grid_scaled_y(ymin);
ymax = int_to_grid_scaled_y(ymax);
 
active_list_reset(converter->active);
cell_list_reset(converter->coverages);
status = polygon_reset(converter->polygon, ymin, ymax);
if (status)
return status;
 
converter->ymin = ymin;
converter->ymax = ymax;
return CAIRO_STATUS_SUCCESS;
}
 
/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
* These macros convert an input coordinate in the client's
* device space to the rasterisation grid.
*/
/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
* shifts if possible, and something saneish if not.
*/
#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
#else
# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
#endif
 
#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
#else
# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
#endif
 
#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
long long tmp__ = (long long)(grid_scale) * (in); \
tmp__ >>= GLITTER_INPUT_BITS; \
(out) = tmp__; \
} while (0)
 
static void
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge,
int clip)
{
cairo_edge_t e;
 
INPUT_TO_GRID_Y (edge->top, e.top);
INPUT_TO_GRID_Y (edge->bottom, e.bottom);
if (e.top >= e.bottom)
return;
 
/* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
if (e.line.p1.y == e.line.p2.y)
return;
 
INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
 
e.dir = edge->dir;
 
polygon_add_edge (converter->polygon, &e, clip);
}
 
static cairo_bool_t
active_list_is_vertical (struct active_list *active)
{
struct edge *e;
 
for (e = active->head; e != NULL; e = e->next) {
if (! e->vertical)
return FALSE;
}
 
return TRUE;
}
 
static void
step_edges (struct active_list *active, int count)
{
struct edge **cursor = &active->head;
struct edge *edge;
 
for (edge = *cursor; edge != NULL; edge = *cursor) {
edge->height_left -= GRID_Y * count;
if (edge->height_left)
cursor = &edge->next;
else
*cursor = edge->next;
}
}
 
static cairo_status_t
blit_coverages (struct cell_list *cells,
cairo_span_renderer_t *renderer,
struct pool *span_pool,
int y, int height)
{
struct cell *cell = cells->head.next;
int prev_x = -1;
int cover = 0, last_cover = 0;
int clip = 0;
cairo_half_open_span_t *spans;
unsigned num_spans;
 
assert (cell != &cells->tail);
 
/* Count number of cells remaining. */
{
struct cell *next = cell;
num_spans = 2;
while (next->next) {
next = next->next;
++num_spans;
}
num_spans = 2*num_spans;
}
 
/* Allocate enough spans for the row. */
pool_reset (span_pool);
spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
num_spans = 0;
 
/* Form the spans from the coverages and areas. */
for (; cell->next; cell = cell->next) {
int x = cell->x;
int area;
 
if (x > prev_x && cover != last_cover) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
spans[num_spans].inverse = 0;
last_cover = cover;
++num_spans;
}
 
cover += cell->covered_height*GRID_X*2;
clip += cell->covered_height*GRID_X*2;
area = cover - cell->uncovered_area;
 
if (area != last_cover) {
spans[num_spans].x = x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
spans[num_spans].inverse = 0;
last_cover = area;
++num_spans;
}
 
prev_x = x+1;
}
 
/* Dump them into the renderer. */
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
 
static void
glitter_scan_converter_render(glitter_scan_converter_t *converter,
int nonzero_fill,
cairo_span_renderer_t *span_renderer,
struct pool *span_pool)
{
int i, j;
int ymax_i = converter->ymax / GRID_Y;
int ymin_i = converter->ymin / GRID_Y;
int h = ymax_i - ymin_i;
struct polygon *polygon = converter->polygon;
struct cell_list *coverages = converter->coverages;
struct active_list *active = converter->active;
 
/* Render each pixel row. */
for (i = 0; i < h; i = j) {
int do_full_step = 0;
 
j = i + 1;
 
/* Determine if we can ignore this row or use the full pixel
* stepper. */
if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
if (! active->head) {
for (; j < h && ! polygon->y_buckets[j]; j++)
;
continue;
}
 
do_full_step = active_list_can_step_full_row (active);
}
 
if (do_full_step) {
/* Step by a full pixel row's worth. */
if (nonzero_fill)
apply_nonzero_fill_rule_and_step_edges (active, coverages);
else
apply_evenodd_fill_rule_and_step_edges (active, coverages);
 
if (active_list_is_vertical (active)) {
while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
{
active->min_height -= GRID_Y;
j++;
}
if (j != i + 1)
step_edges (active, j - (i + 1));
}
} else {
grid_scaled_y_t suby;
 
/* Subsample this row. */
for (suby = 0; suby < GRID_Y; suby++) {
grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
 
if (polygon->y_buckets[i]) {
active_list_merge_edges_from_polygon (active,
&polygon->y_buckets[i], y,
polygon);
}
 
if (nonzero_fill)
apply_nonzero_fill_rule_for_subrow (active, coverages);
else
apply_evenodd_fill_rule_for_subrow (active, coverages);
 
active_list_substep_edges(active);
}
}
 
blit_coverages (coverages, span_renderer, span_pool, i+ymin_i, j -i);
cell_list_reset (coverages);
 
if (! active->head)
active->min_height = INT_MAX;
else
active->min_height -= GRID_Y;
}
}
 
struct _cairo_clip_tor_scan_converter {
cairo_scan_converter_t base;
 
glitter_scan_converter_t converter[1];
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
 
cairo_fill_rule_t clip_fill_rule;
cairo_antialias_t clip_antialias;
 
jmp_buf jmp;
 
struct {
struct pool base[1];
cairo_half_open_span_t embedded[32];
} span_pool;
};
 
typedef struct _cairo_clip_tor_scan_converter cairo_clip_tor_scan_converter_t;
 
static void
_cairo_clip_tor_scan_converter_destroy (void *converter)
{
cairo_clip_tor_scan_converter_t *self = converter;
if (self == NULL) {
return;
}
_glitter_scan_converter_fini (self->converter);
pool_fini (self->span_pool.base);
free(self);
}
 
static cairo_status_t
_cairo_clip_tor_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
cairo_clip_tor_scan_converter_t *self = converter;
cairo_status_t status;
 
if ((status = setjmp (self->jmp)))
return _cairo_scan_converter_set_error (self, _cairo_error (status));
 
glitter_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING,
renderer,
self->span_pool.base);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_scan_converter_t *
_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_clip_tor_scan_converter_t *self;
cairo_polygon_t clipper;
cairo_status_t status;
int i;
 
self = calloc (1, sizeof(struct _cairo_clip_tor_scan_converter));
if (unlikely (self == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail_nomem;
}
 
self->base.destroy = _cairo_clip_tor_scan_converter_destroy;
self->base.generate = _cairo_clip_tor_scan_converter_generate;
 
pool_init (self->span_pool.base, &self->jmp,
250 * sizeof(self->span_pool.embedded[0]),
sizeof(self->span_pool.embedded));
 
_glitter_scan_converter_init (self->converter, &self->jmp);
status = glitter_scan_converter_reset (self->converter,
clip->extents.y,
clip->extents.y + clip->extents.height);
if (unlikely (status))
goto bail;
 
self->fill_rule = fill_rule;
self->antialias = antialias;
 
for (i = 0; i < polygon->num_edges; i++)
glitter_scan_converter_add_edge (self->converter,
&polygon->edges[i],
FALSE);
 
status = _cairo_clip_get_polygon (clip,
&clipper,
&self->clip_fill_rule,
&self->clip_antialias);
if (unlikely (status))
goto bail;
 
for (i = 0; i < clipper.num_edges; i++)
glitter_scan_converter_add_edge (self->converter,
&clipper.edges[i],
TRUE);
_cairo_polygon_fini (&clipper);
 
return &self->base;
 
bail:
self->base.destroy(&self->base);
bail_nomem:
return _cairo_scan_converter_create_in_error (status);
}
 
/programs/develop/libraries/cairo/src/cairo-clip.c
40,18 → 40,21
*/
 
#include "cairoint.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-pattern-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-region-private.h"
 
#if HAS_FREED_POOL
static freed_pool_t clip_path_pool;
#endif
static freed_pool_t clip_pool;
 
const cairo_clip_t __cairo_clip_all;
 
static cairo_clip_path_t *
_cairo_clip_path_create (cairo_clip_t *clip)
{
66,10 → 69,6
 
CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
 
clip_path->flags = 0;
clip_path->region = NULL;
clip_path->surface = NULL;
 
clip_path->prev = clip->path;
clip->path = clip_path;
 
76,7 → 75,7
return clip_path;
}
 
static cairo_clip_path_t *
cairo_clip_path_t *
_cairo_clip_path_reference (cairo_clip_path_t *clip_path)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
86,7 → 85,7
return clip_path;
}
 
static void
void
_cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
95,10 → 94,6
return;
 
_cairo_path_fixed_fini (&clip_path->path);
if (clip_path->region != NULL)
cairo_region_destroy (clip_path->region);
if (clip_path->surface != NULL)
cairo_surface_destroy (clip_path->surface);
 
if (clip_path->prev != NULL)
_cairo_clip_path_destroy (clip_path->prev);
106,115 → 101,134
_freed_pool_put (&clip_path_pool, clip_path);
}
 
void
_cairo_clip_init (cairo_clip_t *clip)
cairo_clip_t *
_cairo_clip_create (void)
{
clip->all_clipped = FALSE;
cairo_clip_t *clip;
 
clip = _freed_pool_get (&clip_pool);
if (unlikely (clip == NULL)) {
clip = malloc (sizeof (cairo_clip_t));
if (unlikely (clip == NULL))
return NULL;
}
 
clip->extents = _cairo_unbounded_rectangle;
 
clip->path = NULL;
clip->boxes = NULL;
clip->num_boxes = 0;
clip->region = NULL;
clip->is_region = FALSE;
 
return clip;
}
 
static void
_cairo_clip_set_all_clipped (cairo_clip_t *clip)
void
_cairo_clip_destroy (cairo_clip_t *clip)
{
clip->all_clipped = TRUE;
if (clip->path != NULL) {
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return;
 
if (clip->path != NULL)
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
 
if (clip->boxes != &clip->embedded_box)
free (clip->boxes);
cairo_region_destroy (clip->region);
 
_freed_pool_put (&clip_pool, clip);
}
}
 
static cairo_status_t
_cairo_clip_intersect_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *rect)
cairo_clip_t *
_cairo_clip_copy (const cairo_clip_t *clip)
{
cairo_clip_path_t *clip_path;
cairo_status_t status;
cairo_clip_t *copy;
 
if (clip->path != NULL) {
if (rect->x <= clip->path->extents.x &&
rect->y <= clip->path->extents.y &&
rect->x + rect->width >= clip->path->extents.x + clip->path->extents.width &&
rect->y + rect->height >= clip->path->extents.y + clip->path->extents.height)
{
return CAIRO_STATUS_SUCCESS;
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return (cairo_clip_t *) clip;
 
copy = _cairo_clip_create ();
 
if (clip->path)
copy->path = _cairo_clip_path_reference (clip->path);
 
if (clip->num_boxes) {
if (clip->num_boxes == 1) {
copy->boxes = &copy->embedded_box;
} else {
copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
if (unlikely (copy->boxes == NULL))
return _cairo_clip_set_all_clipped (copy);
}
 
memcpy (copy->boxes, clip->boxes,
clip->num_boxes * sizeof (cairo_box_t));
copy->num_boxes = clip->num_boxes;
}
 
clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
copy->extents = clip->extents;
copy->region = cairo_region_reference (clip->region);
copy->is_region = clip->is_region;
 
_cairo_path_fixed_init (&clip_path->path);
return copy;
}
 
status = _cairo_path_fixed_move_to (&clip_path->path,
_cairo_fixed_from_int (rect->x),
_cairo_fixed_from_int (rect->y));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (&clip_path->path,
_cairo_fixed_from_int (rect->width),
_cairo_fixed_from_int (0));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (&clip_path->path,
_cairo_fixed_from_int (0),
_cairo_fixed_from_int (rect->height));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (&clip_path->path,
_cairo_fixed_from_int (-rect->width),
_cairo_fixed_from_int (0));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_close_path (&clip_path->path);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_clip_t *
_cairo_clip_copy_path (const cairo_clip_t *clip)
{
cairo_clip_t *copy;
 
clip_path->fill_rule = CAIRO_FILL_RULE_WINDING;
clip_path->tolerance = 1;
clip_path->antialias = CAIRO_ANTIALIAS_DEFAULT;
clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return (cairo_clip_t *) clip;
 
clip_path->extents = *rect;
if (clip_path->prev != NULL) {
if (! _cairo_rectangle_intersect (&clip_path->extents,
&clip_path->prev->extents))
{
_cairo_clip_set_all_clipped (clip);
}
}
assert (clip->num_boxes);
 
/* could preallocate the region if it proves worthwhile */
copy = _cairo_clip_create ();
copy->extents = clip->extents;
if (clip->path)
copy->path = _cairo_clip_path_reference (clip->path);
 
return CAIRO_STATUS_SUCCESS;
return copy;
}
 
cairo_clip_t *
_cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other)
_cairo_clip_copy_region (const cairo_clip_t *clip)
{
if (other != NULL) {
clip->all_clipped = other->all_clipped;
if (other->path == NULL) {
clip->path = NULL;
if (! clip->all_clipped)
clip = NULL;
cairo_clip_t *copy;
int i;
 
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return (cairo_clip_t *) clip;
 
assert (clip->num_boxes);
 
copy = _cairo_clip_create ();
copy->extents = clip->extents;
 
if (clip->num_boxes == 1) {
copy->boxes = &copy->embedded_box;
} else {
clip->path = _cairo_clip_path_reference (other->path);
copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
if (unlikely (copy->boxes == NULL))
return _cairo_clip_set_all_clipped (copy);
}
} else {
_cairo_clip_init (clip);
clip = NULL;
}
 
return clip;
for (i = 0; i < clip->num_boxes; i++) {
copy->boxes[i].p1.x = _cairo_fixed_floor (clip->boxes[i].p1.x);
copy->boxes[i].p1.y = _cairo_fixed_floor (clip->boxes[i].p1.y);
copy->boxes[i].p2.x = _cairo_fixed_ceil (clip->boxes[i].p2.x);
copy->boxes[i].p2.y = _cairo_fixed_ceil (clip->boxes[i].p2.y);
}
copy->num_boxes = clip->num_boxes;
 
void
_cairo_clip_reset (cairo_clip_t *clip)
{
clip->all_clipped = FALSE;
if (clip->path != NULL) {
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
copy->region = cairo_region_reference (clip->region);
copy->is_region = TRUE;
 
return copy;
}
}
 
static cairo_status_t
cairo_clip_t *
_cairo_clip_intersect_path (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
225,941 → 239,412
cairo_status_t status;
cairo_rectangle_int_t extents;
cairo_box_t box;
cairo_bool_t is_box = FALSE;
 
if (clip->path != NULL) {
if (clip->path->fill_rule == fill_rule &&
(path->is_rectilinear || tolerance == clip->path->tolerance) &&
antialias == clip->path->antialias &&
_cairo_path_fixed_equal (&clip->path->path, path))
{
return CAIRO_STATUS_SUCCESS;
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
/* catch the empty clip path */
if (_cairo_path_fixed_fill_is_empty (path))
return _cairo_clip_set_all_clipped (clip);
 
if (_cairo_path_fixed_is_box (path, &box)) {
if (antialias == CAIRO_ANTIALIAS_NONE) {
box.p1.x = _cairo_fixed_round_down (box.p1.x);
box.p1.y = _cairo_fixed_round_down (box.p1.y);
box.p2.x = _cairo_fixed_round_down (box.p2.x);
box.p2.y = _cairo_fixed_round_down (box.p2.y);
}
 
return _cairo_clip_intersect_box (clip, &box);
}
if (_cairo_path_fixed_fill_is_rectilinear (path))
return _cairo_clip_intersect_rectilinear_path (clip, path,
fill_rule, antialias);
 
_cairo_path_fixed_approximate_clip_extents (path, &extents);
if (extents.width == 0 || extents.height == 0) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
}
if (extents.width == 0 || extents.height == 0)
return _cairo_clip_set_all_clipped (clip);
 
is_box = _cairo_path_fixed_is_box (path, &box);
if (clip->path != NULL) {
if (! _cairo_rectangle_intersect (&extents, &clip->path->extents)) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
}
clip = _cairo_clip_intersect_rectangle (clip, &extents);
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
/* does this clip wholly subsume the others? */
if (is_box &&
box.p1.x <= _cairo_fixed_from_int (clip->path->extents.x) &&
box.p2.x >= _cairo_fixed_from_int (clip->path->extents.x + clip->path->extents.width) &&
box.p1.y <= _cairo_fixed_from_int (clip->path->extents.y) &&
box.p2.y >= _cairo_fixed_from_int (clip->path->extents.y + clip->path->extents.height))
{
return CAIRO_STATUS_SUCCESS;
}
}
 
clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
return _cairo_clip_set_all_clipped (clip);
 
status = _cairo_path_fixed_init_copy (&clip_path->path, path);
if (unlikely (status)) {
clip->path = clip->path->prev;
_cairo_clip_path_destroy (clip_path);
return status;
}
if (unlikely (status))
return _cairo_clip_set_all_clipped (clip);
 
clip_path->extents = extents;
clip_path->fill_rule = fill_rule;
clip_path->tolerance = tolerance;
clip_path->antialias = antialias;
if (is_box)
clip_path->flags |= CAIRO_CLIP_PATH_IS_BOX;
 
return CAIRO_STATUS_SUCCESS;
if (clip->region) {
cairo_region_destroy (clip->region);
clip->region = NULL;
}
 
cairo_bool_t
_cairo_clip_equal (const cairo_clip_t *clip_a,
const cairo_clip_t *clip_b)
clip->is_region = FALSE;
return clip;
}
 
static cairo_clip_t *
_cairo_clip_intersect_clip_path (cairo_clip_t *clip,
const cairo_clip_path_t *clip_path)
{
const cairo_clip_path_t *clip_path_a, *clip_path_b;
if (clip_path->prev)
clip = _cairo_clip_intersect_clip_path (clip, clip_path->prev);
 
clip_path_a = clip_a->path;
clip_path_b = clip_b->path;
return _cairo_clip_intersect_path (clip,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias);
}
 
while (clip_path_a && clip_path_b) {
if (clip_path_a == clip_path_b)
return TRUE;
cairo_clip_t *
_cairo_clip_intersect_clip (cairo_clip_t *clip,
const cairo_clip_t *other)
{
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
if (clip_path_a->fill_rule != clip_path_b->fill_rule)
return FALSE;
if (other == NULL)
return clip;
 
if (clip_path_a->tolerance != clip_path_b->tolerance)
return FALSE;
if (clip == NULL)
return _cairo_clip_copy (other);
 
if (clip_path_a->antialias != clip_path_b->antialias)
return FALSE;
if (_cairo_clip_is_all_clipped (other))
return _cairo_clip_set_all_clipped (clip);
 
if (! _cairo_path_fixed_equal (&clip_path_a->path, &clip_path_b->path))
return FALSE;
if (! _cairo_rectangle_intersect (&clip->extents, &other->extents))
return _cairo_clip_set_all_clipped (clip);
 
clip_path_a = clip_path_a->prev;
clip_path_b = clip_path_b->prev;
if (other->num_boxes) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_for_array (&boxes, other->boxes, other->num_boxes);
clip = _cairo_clip_intersect_boxes (clip, &boxes);
}
 
return clip_path_a == clip_path_b; /* ie both NULL */
if (! _cairo_clip_is_all_clipped (clip)) {
if (other->path) {
if (clip->path == NULL)
clip->path = _cairo_clip_path_reference (other->path);
else
clip = _cairo_clip_intersect_clip_path (clip, other->path);
}
}
 
cairo_status_t
_cairo_clip_clip (cairo_clip_t *clip,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
if (clip->all_clipped)
return CAIRO_STATUS_SUCCESS;
 
/* catch the empty clip path */
if (_cairo_path_fixed_fill_is_empty (path)) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
if (clip->region) {
cairo_region_destroy (clip->region);
clip->region = NULL;
}
clip->is_region = FALSE;
 
return _cairo_clip_intersect_path (clip,
path, fill_rule, tolerance,
antialias);
return clip;
}
 
cairo_status_t
_cairo_clip_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *rectangle)
cairo_bool_t
_cairo_clip_equal (const cairo_clip_t *clip_a,
const cairo_clip_t *clip_b)
{
if (clip->all_clipped)
return CAIRO_STATUS_SUCCESS;
const cairo_clip_path_t *cp_a, *cp_b;
 
if (rectangle->width == 0 || rectangle->height == 0) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
}
/* are both all-clipped or no-clip? */
if (clip_a == clip_b)
return TRUE;
 
/* if a smaller clip has already been set, ignore the new path */
if (clip->path != NULL) {
if (rectangle->x <= clip->path->extents.x &&
rectangle->y <= clip->path->extents.y &&
rectangle->x + rectangle->width >= clip->path->extents.x + clip->path->extents.width &&
rectangle->y + rectangle->height >= clip->path->extents.y + clip->path->extents.height)
/* or just one of them? */
if (clip_a == NULL || clip_b == NULL ||
_cairo_clip_is_all_clipped (clip_a) ||
_cairo_clip_is_all_clipped (clip_b))
{
return CAIRO_STATUS_SUCCESS;
return FALSE;
}
}
 
return _cairo_clip_intersect_rectangle (clip, rectangle);
}
/* We have a pair of normal clips, check their contents */
 
static cairo_status_t
_cairo_clip_path_reapply_clip_path_transform (cairo_clip_t *clip,
cairo_clip_path_t *other_path,
const cairo_matrix_t *matrix)
{
cairo_status_t status;
cairo_clip_path_t *clip_path;
cairo_bool_t is_empty;
if (clip_a->num_boxes != clip_b->num_boxes)
return FALSE;
 
if (other_path->prev != NULL) {
status = _cairo_clip_path_reapply_clip_path_transform (clip,
other_path->prev,
matrix);
if (unlikely (status))
return status;
}
if (memcmp (clip_a->boxes, clip_b->boxes,
sizeof (cairo_box_t) * clip_a->num_boxes))
return FALSE;
 
clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
cp_a = clip_a->path;
cp_b = clip_b->path;
while (cp_a && cp_b) {
if (cp_a == cp_b)
return TRUE;
 
status = _cairo_path_fixed_init_copy (&clip_path->path,
&other_path->path);
if (unlikely (status)) {
clip->path = clip->path->prev;
_cairo_clip_path_destroy (clip_path);
return status;
}
/* XXX compare reduced polygons? */
 
_cairo_path_fixed_transform (&clip_path->path, matrix);
_cairo_path_fixed_approximate_clip_extents (&clip_path->path,
&clip_path->extents);
if (clip_path->prev != NULL) {
is_empty = _cairo_rectangle_intersect (&clip_path->extents,
&clip_path->prev->extents);
}
if (cp_a->antialias != cp_b->antialias)
return FALSE;
 
clip_path->fill_rule = other_path->fill_rule;
clip_path->tolerance = other_path->tolerance;
clip_path->antialias = other_path->antialias;
if (cp_a->tolerance != cp_b->tolerance)
return FALSE;
 
return CAIRO_STATUS_SUCCESS;
if (cp_a->fill_rule != cp_b->fill_rule)
return FALSE;
 
if (! _cairo_path_fixed_equal (&cp_a->path,
&cp_b->path))
return FALSE;
 
cp_a = cp_a->prev;
cp_b = cp_b->prev;
}
 
static cairo_status_t
_cairo_clip_path_reapply_clip_path_translate (cairo_clip_t *clip,
return cp_a == NULL && cp_b == NULL;
}
 
static cairo_clip_t *
_cairo_clip_path_copy_with_translation (cairo_clip_t *clip,
cairo_clip_path_t *other_path,
int tx, int ty)
int fx, int fy)
{
cairo_status_t status;
cairo_clip_path_t *clip_path;
 
if (other_path->prev != NULL) {
status = _cairo_clip_path_reapply_clip_path_translate (clip,
other_path->prev,
tx, ty);
if (unlikely (status))
return status;
}
if (other_path->prev != NULL)
clip = _cairo_clip_path_copy_with_translation (clip, other_path->prev,
fx, fy);
if (_cairo_clip_is_all_clipped (clip))
return clip;
 
clip_path = _cairo_clip_path_create (clip);
if (unlikely (clip_path == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
return _cairo_clip_set_all_clipped (clip);
 
status = _cairo_path_fixed_init_copy (&clip_path->path,
&other_path->path);
if (unlikely (status)) {
clip->path = clip->path->prev;
_cairo_clip_path_destroy (clip_path);
return status;
}
if (unlikely (status))
return _cairo_clip_set_all_clipped (clip);
 
_cairo_path_fixed_translate (&clip_path->path,
_cairo_fixed_from_int (tx),
_cairo_fixed_from_int (ty));
_cairo_path_fixed_translate (&clip_path->path, fx, fy);
 
clip_path->fill_rule = other_path->fill_rule;
clip_path->tolerance = other_path->tolerance;
clip_path->antialias = other_path->antialias;
 
clip_path->flags = other_path->flags;
if (other_path->region != NULL) {
clip_path->region = cairo_region_copy (other_path->region);
status = clip_path->region->status;
if (unlikely (status)) {
clip->path = clip->path->prev;
_cairo_clip_path_destroy (clip_path);
return status;
return clip;
}
 
cairo_region_translate (clip_path->region, tx, ty);
}
clip_path->surface = cairo_surface_reference (other_path->surface);
cairo_clip_t *
_cairo_clip_translate (cairo_clip_t *clip, int tx, int ty)
{
int fx, fy, i;
cairo_clip_path_t *clip_path;
 
clip_path->extents = other_path->extents;
clip_path->extents.x += tx;
clip_path->extents.y += ty;
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return clip;
 
return CAIRO_STATUS_SUCCESS;
}
if (tx == 0 && ty == 0)
return clip;
 
cairo_status_t
_cairo_clip_init_copy_transformed (cairo_clip_t *clip,
cairo_clip_t *other,
const cairo_matrix_t *matrix)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
int tx, ty;
fx = _cairo_fixed_from_int (tx);
fy = _cairo_fixed_from_int (ty);
 
if (other == NULL) {
_cairo_clip_init (clip);
return CAIRO_STATUS_SUCCESS;
for (i = 0; i < clip->num_boxes; i++) {
clip->boxes[i].p1.x += fx;
clip->boxes[i].p2.x += fx;
clip->boxes[i].p1.y += fy;
clip->boxes[i].p2.y += fy;
}
 
if (other->all_clipped) {
_cairo_clip_init (clip);
clip->all_clipped = TRUE;
return CAIRO_STATUS_SUCCESS;
}
clip->extents.x += tx;
clip->extents.y += ty;
 
if (_cairo_matrix_is_identity (matrix)) {
_cairo_clip_init_copy (clip, other);
return CAIRO_STATUS_SUCCESS;
}
if (clip->path == NULL)
return clip;
 
if (other->path != NULL) {
_cairo_clip_init (clip);
clip_path = clip->path;
clip->path = NULL;
clip = _cairo_clip_path_copy_with_translation (clip, clip_path, fx, fy);
_cairo_clip_path_destroy (clip_path);
 
/* if we only need to translate, so we can reuse the caches... */
/* XXX we still loose the benefit of constructs when the copy is
* deleted though. Indirect clip_paths?
*/
if (_cairo_matrix_is_integer_translation (matrix, &tx, &ty)) {
status = _cairo_clip_path_reapply_clip_path_translate (clip,
other->path,
tx, ty);
} else {
status = _cairo_clip_path_reapply_clip_path_transform (clip,
other->path,
matrix);
if (clip->path->extents.width == 0 &&
clip->path->extents.height == 0)
{
_cairo_clip_set_all_clipped (clip);
return clip;
}
}
}
 
return status;
}
 
static cairo_status_t
_cairo_clip_apply_clip_path (cairo_clip_t *clip,
const cairo_clip_path_t *path)
_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
const cairo_box_t *box)
{
cairo_status_t status;
 
if (path->prev != NULL)
status = _cairo_clip_apply_clip_path (clip, path->prev);
 
return _cairo_clip_intersect_path (clip,
&path->path,
path->fill_rule,
path->tolerance,
path->antialias);
}
 
cairo_status_t
_cairo_clip_apply_clip (cairo_clip_t *clip,
const cairo_clip_t *other)
{
cairo_status_t status;
 
if (clip->all_clipped)
return CAIRO_STATUS_SUCCESS;
 
if (other->all_clipped) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_STATUS_SUCCESS;
}
 
status = CAIRO_STATUS_SUCCESS;
if (other->path != NULL)
status = _cairo_clip_apply_clip_path (clip, other->path);
 
return status;
}
 
static inline cairo_bool_t
_clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
{
while (clip_path != NULL) {
if (! clip_path->path.is_rectilinear)
return FALSE;
 
clip_path = clip_path->prev;
}
 
return TRUE;
}
 
static cairo_int_status_t
_cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
{
cairo_traps_t traps;
cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)];
cairo_box_t *boxes = stack_boxes;
cairo_status_t status;
int n;
 
/* If we have nothing to intersect with this path, then it cannot
* magically be reduced into a region.
*/
if (clip_path->prev == NULL)
goto UNSUPPORTED;
 
/* Start simple... Intersect some boxes with an arbitrary path. */
if (! clip_path->path.is_rectilinear)
goto UNSUPPORTED;
if (clip_path->prev->prev != NULL)
goto UNSUPPORTED;
 
_cairo_traps_init (&traps);
_cairo_box_from_rectangle (&boxes[0], &clip_path->extents);
_cairo_traps_limit (&traps, boxes, 1);
 
status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path,
clip_path->fill_rule,
&traps);
if (unlikely (_cairo_status_is_error (status)))
return status;
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
goto UNSUPPORTED;
 
if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) {
boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
if (unlikely (boxes == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (n = 0; n < traps.num_traps; n++) {
boxes[n].p1.x = traps.traps[n].left.p1.x;
boxes[n].p1.y = traps.traps[n].top;
boxes[n].p2.x = traps.traps[n].right.p1.x;
boxes[n].p2.y = traps.traps[n].bottom;
}
 
_cairo_traps_clear (&traps);
_cairo_traps_limit (&traps, boxes, n);
status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path,
clip_path->prev->fill_rule,
clip_path->prev->tolerance,
&traps);
if (boxes != stack_boxes)
free (boxes);
 
status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
if (unlikely (status))
return status;
 
status = _cairo_traps_extract_region (&traps, &clip_path->region);
_cairo_traps_fini (&traps);
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
goto UNSUPPORTED;
status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
if (unlikely (status))
return status;
 
clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
return CAIRO_STATUS_SUCCESS;
 
UNSUPPORTED:
clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_int_status_t
_cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
{
cairo_int_status_t status;
cairo_region_t *prev = NULL;
 
if (clip_path->flags &
(CAIRO_CLIP_PATH_HAS_REGION |
CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED))
{
return clip_path->flags & CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED ?
CAIRO_INT_STATUS_UNSUPPORTED :
CAIRO_STATUS_SUCCESS;
}
 
if (! clip_path->path.maybe_fill_region)
return _cairo_clip_path_to_region_geometric (clip_path);
 
/* first retrieve the region for our antecedents */
if (clip_path->prev != NULL) {
status = _cairo_clip_path_to_region (clip_path->prev);
if (status) {
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_clip_path_to_region_geometric (clip_path);
 
return status;
}
 
prev = clip_path->prev->region;
}
 
/* now extract the region for ourselves */
clip_path->region =
_cairo_path_fixed_fill_rectilinear_to_region (&clip_path->path,
clip_path->fill_rule,
&clip_path->extents);
assert (clip_path->region != NULL);
 
status = clip_path->region->status;
status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
if (unlikely (status))
return status;
 
if (prev != NULL) {
status = cairo_region_intersect (clip_path->region, prev);
status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
if (unlikely (status))
return status;
}
 
clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION;
return CAIRO_STATUS_SUCCESS;
return _cairo_path_fixed_close_path (path);
}
 
static inline int
pot (int v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
 
/* XXX there is likely a faster method! ;-) */
static cairo_status_t
_region_clip_to_boxes (const cairo_region_t *region,
cairo_box_t **boxes,
int *num_boxes,
int *size_boxes)
_cairo_path_fixed_init_from_boxes (cairo_path_fixed_t *path,
const cairo_boxes_t *boxes)
{
cairo_traps_t traps;
cairo_status_t status;
int n, num_rects;
 
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, *boxes, *num_boxes);
traps.is_rectilinear = TRUE;
traps.is_rectangular = TRUE;
 
num_rects = cairo_region_num_rectangles (region);
for (n = 0; n < num_rects; n++) {
cairo_rectangle_int_t rect;
cairo_point_t p1, p2;
 
cairo_region_get_rectangle (region, n, &rect);
 
p1.x = _cairo_fixed_from_int (rect.x);
p1.y = _cairo_fixed_from_int (rect.y);
p2.x = _cairo_fixed_from_int (rect.x + rect.width);
p2.y = _cairo_fixed_from_int (rect.y + rect.height);
 
status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2);
if (unlikely (status))
goto CLEANUP;
}
 
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
goto CLEANUP;
 
n = *size_boxes;
if (n < 0)
n = -n;
 
if (traps.num_traps > n) {
cairo_box_t *new_boxes;
 
new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t));
if (unlikely (new_boxes == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
if (*size_boxes > 0)
free (*boxes);
 
*boxes = new_boxes;
*size_boxes = traps.num_traps;
}
 
for (n = 0; n < traps.num_traps; n++) {
(*boxes)[n].p1.x = traps.traps[n].left.p1.x;
(*boxes)[n].p1.y = traps.traps[n].top;
(*boxes)[n].p2.x = traps.traps[n].right.p1.x;
(*boxes)[n].p2.y = traps.traps[n].bottom;
}
*num_boxes = n;
 
CLEANUP:
_cairo_traps_fini (&traps);
 
return status;
}
 
static cairo_status_t
_rectilinear_clip_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_box_t **boxes,
int *num_boxes,
int *size_boxes)
{
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_status_t status;
 
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, *boxes, *num_boxes);
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, *boxes, *num_boxes);
 
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
&traps);
if (unlikely (_cairo_status_is_error (status)))
goto CLEANUP;
if (status == CAIRO_STATUS_SUCCESS)
goto BOXES;
 
/* tolerance will be ignored as the path is rectilinear */
status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
if (unlikely (status))
goto CLEANUP;
 
if (polygon.num_edges == 0) {
*num_boxes = 0;
} else {
status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
&polygon,
fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
const struct _cairo_boxes_chunk *chunk;
int i;
 
BOXES:
i = *size_boxes;
if (i < 0)
i = -i;
_cairo_path_fixed_init (path);
if (boxes->num_boxes == 0)
return CAIRO_STATUS_SUCCESS;
 
if (traps.num_traps > i) {
cairo_box_t *new_boxes;
int new_size;
 
new_size = pot (traps.num_traps);
new_boxes = _cairo_malloc_ab (new_size, sizeof (cairo_box_t));
if (unlikely (new_boxes == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
if (*size_boxes > 0)
free (*boxes);
 
*boxes = new_boxes;
*size_boxes = new_size;
}
 
for (i = 0; i < traps.num_traps; i++) {
(*boxes)[i].p1.x = traps.traps[i].left.p1.x;
(*boxes)[i].p1.y = traps.traps[i].top;
(*boxes)[i].p2.x = traps.traps[i].right.p1.x;
(*boxes)[i].p2.y = traps.traps[i].bottom;
}
*num_boxes = i;
}
}
 
CLEANUP:
_cairo_polygon_fini (&polygon);
_cairo_traps_fini (&traps);
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
status = _cairo_path_fixed_add_box (path, &chunk->base[i]);
if (unlikely (status)) {
_cairo_path_fixed_fini (path);
return status;
}
 
static cairo_int_status_t
_cairo_clip_path_to_boxes (cairo_clip_path_t *clip_path,
cairo_box_t **boxes,
int *count)
{
int size = -*count;
int num_boxes = 0;
cairo_status_t status;
 
if (clip_path->region != NULL) {
int num_rects, n;
 
num_rects = cairo_region_num_rectangles (clip_path->region);
if (num_rects > -size) {
cairo_box_t *new_boxes;
 
new_boxes = _cairo_malloc_ab (num_rects, sizeof (cairo_box_t));
if (unlikely (new_boxes == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
*boxes = new_boxes;
}
 
for (n = 0; n < num_rects; n++) {
cairo_rectangle_int_t rect;
 
cairo_region_get_rectangle (clip_path->region, n, &rect);
(*boxes)[n].p1.x = _cairo_fixed_from_int (rect.x);
(*boxes)[n].p1.y = _cairo_fixed_from_int (rect.y);
(*boxes)[n].p2.x = _cairo_fixed_from_int (rect.x + rect.width);
(*boxes)[n].p2.y = _cairo_fixed_from_int (rect.y + rect.height);
}
 
*count = num_rects;
return CAIRO_STATUS_SUCCESS;
}
 
/* keep it simple at first */
if (! _clip_paths_are_rectilinear (clip_path))
return CAIRO_INT_STATUS_UNSUPPORTED;
static cairo_clip_t *
_cairo_clip_intersect_clip_path_transformed (cairo_clip_t *clip,
const cairo_clip_path_t *clip_path,
const cairo_matrix_t *m)
{
cairo_path_fixed_t path;
 
assert (-size >= 1);
if (_cairo_path_fixed_is_box (&clip_path->path, *boxes)) {
num_boxes = 1;
} else {
status = _rectilinear_clip_to_boxes (&clip_path->path,
clip_path->fill_rule,
boxes, &num_boxes, &size);
if (unlikely (status))
return status;
}
if (clip_path->prev)
clip = _cairo_clip_intersect_clip_path_transformed (clip,
clip_path->prev,
m);
 
while (num_boxes > 0 && (clip_path = clip_path->prev) != NULL) {
cairo_box_t box;
if (_cairo_path_fixed_init_copy (&path, &clip_path->path))
return _cairo_clip_set_all_clipped (clip);
 
if (clip_path->region != NULL) {
status = _region_clip_to_boxes (clip_path->region,
boxes, &num_boxes, &size);
if (unlikely (status))
return status;
_cairo_path_fixed_transform (&path, m);
 
break;
} else if (_cairo_path_fixed_is_box (&clip_path->path, &box)) {
int i, j;
 
for (i = j = 0; i < num_boxes; i++) {
if (j != i)
(*boxes)[j] = (*boxes)[i];
 
if (box.p1.x > (*boxes)[j].p1.x)
(*boxes)[j].p1.x = box.p1.x;
if (box.p2.x < (*boxes)[j].p2.x)
(*boxes)[j].p2.x = box.p2.x;
 
if (box.p1.y > (*boxes)[j].p1.y)
(*boxes)[j].p1.y = box.p1.y;
if (box.p2.y < (*boxes)[j].p2.y)
(*boxes)[j].p2.y = box.p2.y;
 
j += (*boxes)[j].p2.x > (*boxes)[j].p1.x &&
(*boxes)[j].p2.y > (*boxes)[j].p1.y;
}
 
num_boxes = j;
} else {
status = _rectilinear_clip_to_boxes (&clip_path->path,
clip = _cairo_clip_intersect_path (clip,
&path,
clip_path->fill_rule,
boxes, &num_boxes, &size);
if (unlikely (status))
return status;
}
}
clip_path->tolerance,
clip_path->antialias);
_cairo_path_fixed_fini (&path);
 
*count = num_boxes;
return CAIRO_STATUS_SUCCESS;
return clip;
}
 
static cairo_surface_t *
_cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
cairo_surface_t *target,
int *tx, int *ty)
cairo_clip_t *
_cairo_clip_transform (cairo_clip_t *clip, const cairo_matrix_t *m)
{
const cairo_rectangle_int_t *clip_extents = &clip_path->extents;
cairo_bool_t need_translate;
cairo_surface_t *surface;
cairo_clip_path_t *prev;
cairo_status_t status;
cairo_clip_t *copy;
 
while (clip_path->prev != NULL &&
clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
clip_path->path.maybe_fill_region)
{
clip_path = clip_path->prev;
}
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return clip;
 
clip_extents = &clip_path->extents;
if (clip_path->surface != NULL &&
clip_path->surface->backend == target->backend)
{
*tx = clip_extents->x;
*ty = clip_extents->y;
return clip_path->surface;
}
if (_cairo_matrix_is_translation (m))
return _cairo_clip_translate (clip, m->x0, m->y0);
 
surface = _cairo_surface_create_similar_scratch (target,
CAIRO_CONTENT_ALPHA,
clip_extents->width,
clip_extents->height);
if (surface == NULL) {
surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
clip_extents->width,
clip_extents->height);
}
if (unlikely (surface->status))
return surface;
copy = _cairo_clip_create ();
 
need_translate = clip_extents->x | clip_extents->y;
if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
clip_path->path.maybe_fill_region)
{
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_SOURCE,
&_cairo_pattern_white.base,
NULL);
if (unlikely (status))
goto BAIL;
}
else
{
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
NULL);
if (unlikely (status))
goto BAIL;
if (clip->num_boxes) {
cairo_path_fixed_t path;
cairo_boxes_t boxes;
 
if (need_translate) {
_cairo_path_fixed_translate (&clip_path->path,
_cairo_fixed_from_int (-clip_extents->x),
_cairo_fixed_from_int (-clip_extents->y));
}
status = _cairo_surface_fill (surface,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
NULL);
if (need_translate) {
_cairo_path_fixed_translate (&clip_path->path,
_cairo_fixed_from_int (clip_extents->x),
_cairo_fixed_from_int (clip_extents->y));
}
_cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
_cairo_path_fixed_init_from_boxes (&path, &boxes);
_cairo_path_fixed_transform (&path, m);
 
if (unlikely (status))
goto BAIL;
copy = _cairo_clip_intersect_path (copy, &path,
CAIRO_FILL_RULE_WINDING,
0.1,
CAIRO_ANTIALIAS_DEFAULT);
 
_cairo_path_fixed_fini (&path);
}
 
prev = clip_path->prev;
while (prev != NULL) {
if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
prev->path.maybe_fill_region)
{
/* a simple box only affects the extents */
if (clip->path)
copy = _cairo_clip_intersect_clip_path_transformed (copy, clip->path,m);
 
_cairo_clip_destroy (clip);
return copy;
}
else if (prev->path.is_rectilinear ||
prev->surface == NULL ||
prev->surface->backend != target->backend)
{
if (need_translate) {
_cairo_path_fixed_translate (&prev->path,
_cairo_fixed_from_int (-clip_extents->x),
_cairo_fixed_from_int (-clip_extents->y));
}
status = _cairo_surface_fill (surface,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
&prev->path,
prev->fill_rule,
prev->tolerance,
prev->antialias,
NULL);
if (need_translate) {
_cairo_path_fixed_translate (&prev->path,
_cairo_fixed_from_int (clip_extents->x),
_cairo_fixed_from_int (clip_extents->y));
}
 
if (unlikely (status))
goto BAIL;
}
else
cairo_clip_t *
_cairo_clip_copy_with_translation (const cairo_clip_t *clip, int tx, int ty)
{
cairo_surface_pattern_t pattern;
cairo_surface_t *prev_surface;
int prev_tx, prev_ty;
cairo_clip_t *copy;
int fx, fy, i;
 
prev_surface = _cairo_clip_path_get_surface (prev, target, &prev_tx, &prev_ty);
status = prev_surface->status;
if (unlikely (status))
goto BAIL;
if (clip == NULL || _cairo_clip_is_all_clipped (clip))
return (cairo_clip_t *)clip;
 
_cairo_pattern_init_for_surface (&pattern, prev_surface);
pattern.base.filter = CAIRO_FILTER_NEAREST;
cairo_matrix_init_translate (&pattern.base.matrix,
clip_extents->x - prev_tx,
clip_extents->y - prev_ty);
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_IN,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
if (tx == 0 && ty == 0)
return _cairo_clip_copy (clip);
 
if (unlikely (status))
goto BAIL;
copy = _cairo_clip_create ();
if (copy == NULL)
return _cairo_clip_set_all_clipped (copy);
 
break;
}
fx = _cairo_fixed_from_int (tx);
fy = _cairo_fixed_from_int (ty);
 
prev = prev->prev;
if (clip->num_boxes) {
if (clip->num_boxes == 1) {
copy->boxes = &copy->embedded_box;
} else {
copy->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t));
if (unlikely (copy->boxes == NULL))
return _cairo_clip_set_all_clipped (copy);
}
 
*tx = clip_extents->x;
*ty = clip_extents->y;
cairo_surface_destroy (clip_path->surface);
return clip_path->surface = surface;
 
BAIL:
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
for (i = 0; i < clip->num_boxes; i++) {
copy->boxes[i].p1.x = clip->boxes[i].p1.x + fx;
copy->boxes[i].p2.x = clip->boxes[i].p2.x + fx;
copy->boxes[i].p1.y = clip->boxes[i].p1.y + fy;
copy->boxes[i].p2.y = clip->boxes[i].p2.y + fy;
}
 
cairo_bool_t
_cairo_clip_contains_rectangle (cairo_clip_t *clip,
const cairo_rectangle_int_t *rect)
{
cairo_clip_path_t *clip_path;
 
if (clip == NULL)
return FALSE;
 
clip_path = clip->path;
if (clip_path->extents.x > rect->x ||
clip_path->extents.y > rect->y ||
clip_path->extents.x + clip_path->extents.width < rect->x + rect->width ||
clip_path->extents.y + clip_path->extents.height < rect->y + rect->height)
{
return FALSE;
copy->num_boxes = clip->num_boxes;
}
 
do {
cairo_box_t box;
copy->extents = clip->extents;
copy->extents.x += tx;
copy->extents.y += ty;
 
if ((clip_path->flags & CAIRO_CLIP_PATH_IS_BOX) == 0)
return FALSE;
if (clip->path == NULL)
return copy;
 
if (! _cairo_path_fixed_is_box (&clip_path->path, &box))
return FALSE;
 
if (box.p1.x > _cairo_fixed_from_int (rect->x) ||
box.p1.y > _cairo_fixed_from_int (rect->y) ||
box.p2.x < _cairo_fixed_from_int (rect->x + rect->width) ||
box.p2.y < _cairo_fixed_from_int (rect->y + rect->height))
{
return FALSE;
return _cairo_clip_path_copy_with_translation (copy, clip->path, fx, fy);
}
} while ((clip_path = clip_path->prev) != NULL);
 
return TRUE;
}
 
cairo_bool_t
_cairo_clip_contains_extents (cairo_clip_t *clip,
_cairo_clip_contains_extents (const cairo_clip_t *clip,
const cairo_composite_rectangles_t *extents)
{
const cairo_rectangle_int_t *rect;
 
if (clip == NULL)
return FALSE;
 
rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
return _cairo_clip_contains_rectangle (clip, rect);
}
 
void
_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip)
_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip)
{
cairo_clip_path_t *clip_path;
int i;
 
if (clip == NULL) {
fprintf (stream, "no clip\n");
1166,23 → 651,30
return;
}
 
if (clip->all_clipped) {
if (_cairo_clip_is_all_clipped (clip)) {
fprintf (stream, "clip: all-clipped\n");
return;
}
 
if (clip->path == NULL) {
fprintf (stream, "clip: empty\n");
return;
fprintf (stream, "clip:\n");
fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d",
clip->extents.x, clip->extents.y,
clip->extents.width, clip->extents.height,
clip->is_region);
 
fprintf (stream, " num_boxes = %d\n", clip->num_boxes);
for (i = 0; i < clip->num_boxes; i++) {
fprintf (stream, " [%d] = (%f, %f), (%f, %f)\n", i,
_cairo_fixed_to_double (clip->boxes[i].p1.x),
_cairo_fixed_to_double (clip->boxes[i].p1.y),
_cairo_fixed_to_double (clip->boxes[i].p2.x),
_cairo_fixed_to_double (clip->boxes[i].p2.y));
}
 
fprintf (stream, "clip:\n");
 
clip_path = clip->path;
if (clip->path) {
cairo_clip_path_t *clip_path = clip->path;
do {
fprintf (stream, "path: has region? %s, has surface? %s, aa=%d, tolerance=%f, rule=%d: ",
clip_path->region == NULL ? "no" : "yes",
clip_path->surface == NULL ? "no" : "yes",
fprintf (stream, "path: aa=%d, tolerance=%f, rule=%d: ",
clip_path->antialias,
clip_path->tolerance,
clip_path->fill_rule);
1190,118 → 682,20
fprintf (stream, "\n");
} while ((clip_path = clip_path->prev) != NULL);
}
 
cairo_surface_t *
_cairo_clip_get_surface (cairo_clip_t *clip, cairo_surface_t *target, int *tx, int *ty)
{
/* XXX is_clear -> all_clipped */
assert (clip->path != NULL);
return _cairo_clip_path_get_surface (clip->path, target, tx, ty);
}
 
cairo_status_t
_cairo_clip_combine_with_surface (cairo_clip_t *clip,
cairo_surface_t *dst,
int dst_x, int dst_y)
{
cairo_clip_path_t *clip_path = clip->path;
cairo_bool_t need_translate;
cairo_status_t status;
 
assert (clip_path != NULL);
 
need_translate = dst_x | dst_y;
do {
if (clip_path->surface != NULL &&
clip_path->surface->backend == dst->backend)
{
cairo_surface_pattern_t pattern;
 
_cairo_pattern_init_for_surface (&pattern, clip_path->surface);
cairo_matrix_init_translate (&pattern.base.matrix,
dst_x - clip_path->extents.x,
dst_y - clip_path->extents.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (dst,
CAIRO_OPERATOR_IN,
&pattern.base,
NULL);
 
_cairo_pattern_fini (&pattern.base);
 
return status;
}
 
if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
clip_path->path.maybe_fill_region)
{
continue;
}
 
if (need_translate) {
_cairo_path_fixed_translate (&clip_path->path,
_cairo_fixed_from_int (-dst_x),
_cairo_fixed_from_int (-dst_y));
}
status = _cairo_surface_fill (dst,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
NULL);
if (need_translate) {
_cairo_path_fixed_translate (&clip_path->path,
_cairo_fixed_from_int (dst_x),
_cairo_fixed_from_int (dst_y));
}
 
if (unlikely (status))
return status;
} while ((clip_path = clip_path->prev) != NULL);
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_rectangle_int_t _cairo_empty_rectangle_int = { 0, 0, 0, 0 };
 
const cairo_rectangle_int_t *
_cairo_clip_get_extents (const cairo_clip_t *clip)
{
if (clip->all_clipped)
return &_cairo_empty_rectangle_int;
if (clip == NULL)
return &_cairo_unbounded_rectangle;
 
if (clip->path == NULL)
return NULL;
if (_cairo_clip_is_all_clipped (clip))
return &_cairo_empty_rectangle;
 
return &clip->path->extents;
return &clip->extents;
}
 
void
_cairo_clip_drop_cache (cairo_clip_t *clip)
{
cairo_clip_path_t *clip_path;
 
if (clip->path == NULL)
return;
 
clip_path = clip->path;
do {
if (clip_path->region != NULL) {
cairo_region_destroy (clip_path->region);
clip_path->region = NULL;
}
 
if (clip_path->surface != NULL) {
cairo_surface_destroy (clip_path->surface);
clip_path->surface = NULL;
}
 
clip_path->flags &= ~CAIRO_CLIP_PATH_HAS_REGION;
} while ((clip_path = clip_path->prev) != NULL);
}
 
const cairo_rectangle_list_t _cairo_rectangles_nil =
{ CAIRO_STATUS_NO_MEMORY, NULL, 0 };
static const cairo_rectangle_list_t _cairo_rectangles_not_representable =
1331,145 → 725,7
return is_tight;
}
 
cairo_int_status_t
_cairo_clip_get_region (cairo_clip_t *clip,
cairo_region_t **region)
{
cairo_int_status_t status;
 
if (clip->all_clipped)
goto CLIPPED;
 
assert (clip->path != NULL);
 
status = _cairo_clip_path_to_region (clip->path);
if (status)
return status;
 
if (cairo_region_is_empty (clip->path->region)) {
_cairo_clip_set_all_clipped (clip);
goto CLIPPED;
}
 
if (region)
*region = clip->path->region;
return CAIRO_STATUS_SUCCESS;
 
CLIPPED:
if (region)
*region = NULL;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
cairo_int_status_t
_cairo_clip_get_boxes (cairo_clip_t *clip,
cairo_box_t **boxes,
int *count)
{
cairo_int_status_t status;
 
if (clip->all_clipped)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
assert (clip->path != NULL);
 
status = _cairo_clip_path_to_boxes (clip->path, boxes, count);
if (status)
return status;
 
if (*count == 0) {
_cairo_clip_set_all_clipped (clip);
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
box_is_aligned (const cairo_box_t *box)
{
return
_cairo_fixed_is_integer (box->p1.x) &&
_cairo_fixed_is_integer (box->p1.y) &&
_cairo_fixed_is_integer (box->p2.x) &&
_cairo_fixed_is_integer (box->p2.y);
}
 
static void
intersect_with_boxes (cairo_composite_rectangles_t *extents,
cairo_box_t *boxes,
int num_boxes)
{
cairo_rectangle_int_t rect;
cairo_box_t box;
cairo_bool_t is_empty;
 
box.p1.x = box.p1.y = INT_MIN;
box.p2.x = box.p2.y = INT_MAX;
while (num_boxes--) {
if (boxes->p1.x < box.p1.x)
box.p1.x = boxes->p1.x;
if (boxes->p1.y < box.p1.y)
box.p1.y = boxes->p1.y;
 
if (boxes->p2.x > box.p2.x)
box.p2.x = boxes->p2.x;
if (boxes->p2.y > box.p2.y)
box.p2.y = boxes->p2.y;
}
 
_cairo_box_round_to_rectangle (&box, &rect);
is_empty = _cairo_rectangle_intersect (&extents->bounded, &rect);
is_empty = _cairo_rectangle_intersect (&extents->unbounded, &rect);
}
 
cairo_status_t
_cairo_clip_to_boxes (cairo_clip_t **clip,
cairo_composite_rectangles_t *extents,
cairo_box_t **boxes,
int *num_boxes)
{
cairo_status_t status;
const cairo_rectangle_int_t *rect;
 
rect = extents->is_bounded ? &extents->bounded : &extents->unbounded;
 
if (*clip == NULL)
goto EXTENTS;
 
status = _cairo_clip_rectangle (*clip, rect);
if (unlikely (status))
return status;
 
status = _cairo_clip_get_boxes (*clip, boxes, num_boxes);
switch ((int) status) {
case CAIRO_STATUS_SUCCESS:
intersect_with_boxes (extents, *boxes, *num_boxes);
if (rect->width == 0 || rect->height == 0 ||
extents->is_bounded ||
(*num_boxes == 1 && box_is_aligned (*boxes)))
{
*clip = NULL;
}
goto DONE;
 
case CAIRO_INT_STATUS_UNSUPPORTED:
goto EXTENTS;
 
default:
return status;
}
 
EXTENTS:
status = CAIRO_STATUS_SUCCESS;
_cairo_box_from_rectangle (&(*boxes)[0], rect);
*num_boxes = 1;
DONE:
return status;
}
 
 
static cairo_rectangle_list_t *
cairo_rectangle_list_t *
_cairo_rectangle_list_create_in_error (cairo_status_t status)
{
cairo_rectangle_list_t *list;
1481,7 → 737,7
 
list = malloc (sizeof (*list));
if (unlikely (list == NULL)) {
_cairo_error_throw (status);
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
}
 
1500,25 → 756,22
cairo_rectangle_list_t *list;
cairo_rectangle_t *rectangles = NULL;
cairo_region_t *region = NULL;
cairo_int_status_t status;
int n_rects = 0;
int i;
 
if (clip->all_clipped)
goto DONE;
 
if (!clip->path)
if (clip == NULL)
return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
 
status = _cairo_clip_get_region (clip, &region);
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
if (_cairo_clip_is_all_clipped (clip))
goto DONE;
} else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 
if (! _cairo_clip_is_region (clip))
return ERROR_LIST (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE);
} else if (unlikely (status)) {
return ERROR_LIST (status);
}
 
region = _cairo_clip_get_region (clip);
if (region == NULL)
return ERROR_LIST (CAIRO_STATUS_NO_MEMORY);
 
n_rects = cairo_region_num_rectangles (region);
if (n_rects) {
rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t));
1558,7 → 811,7
 
/**
* cairo_rectangle_list_destroy:
* @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangles()
* @rectangle_list: a rectangle list, as obtained from cairo_copy_clip_rectangle_list()
*
* Unconditionally frees @rectangle_list and all associated
* references. After this call, the @rectangle_list pointer must not
1581,4 → 834,5
_cairo_clip_reset_static_data (void)
{
_freed_pool_reset (&clip_path_pool);
_freed_pool_reset (&clip_pool);
}
/programs/develop/libraries/cairo/src/cairo-cogl-context-private.h
0,0 → 1,52
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#ifndef CAIRO_COGL_CONTEXT_PRIVATE_H
#define CAIRO_COGL_CONTEXT_PRIVATE_H
 
#include "cairo-default-context-private.h"
#include "cairo-cogl-private.h"
 
typedef struct _cairo_cogl_context {
cairo_default_context_t base;
 
cairo_cogl_device_t *dev;
int path_ctm_age;
cairo_path_fixed_t user_path;
 
cairo_bool_t path_is_rectangle;
double x, y, width, height;
} cairo_cogl_context_t;
 
cairo_t *
_cairo_cogl_context_create (void *target);
 
#endif /* CAIRO_COGL_CONTEXT_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-cogl-context.c
0,0 → 1,822
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
/* so long as we can verify that the ctm doesn't change multiple times
* during the construction of a path we can build a shadow
* #cairo_path_fixed_t in user coordinates that we can use to create a
* hash value for caching tessellations of that path.
*
* We need to hook into all the points where the ctm can be changed
* so we can bump a cr->path_ctm_age counter.
*
* We need to hook into all the points where the path can be modified
* so we can catch the start of a path and reset the cr->path_ctm_age
* counter at that point.
*
* When a draw operation is hit we can then check that the
* path_ctm_age == 0 and if so we create a hash of the path.
*
* We use this hash to lookup a #cairo_cogl_path_meta_t struct which
* may contain tessellated triangles for the path or may just contain
* a count of how many times the path has been re-seen (we only cache
* tessellated triangles if there is evidence that the path is being
* used multiple times because there is a cost involved in allocating
* a separate buffer for the triangles).
*/
 
#include "cairoint.h"
 
#include "cairo-cogl-context-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-arc-private.h"
#include "cairo-path-fixed-private.h"
 
#include <glib.h>
 
static freed_pool_t context_pool;
 
void
_cairo_cogl_context_reset_static_data (void)
{
_freed_pool_reset (&context_pool);
}
 
static cairo_status_t
_cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr,
double x, double y,
double width, double height)
{
cairo_status_t status;
status = cr->dev->backend_parent.rectangle (cr, x, y, width, height);
if (unlikely (status))
return status;
 
return _cairo_cogl_path_fixed_rectangle (&cr->user_path,
_cairo_fixed_from_double (x),
_cairo_fixed_from_double (y),
_cairo_fixed_from_double (width),
_cairo_fixed_from_double (height));
}
 
/* The idea here is that we have a simplified way of tracking rectangle paths
* because rectangles are perhaps the most common shape drawn with cairo.
*
* Basically we have a speculative store for a rectangle path that doesn't
* need to use the #cairo_path_fixed_t api to describe a rectangle in terms of
* (move_to,rel_line_to,rel_line_to,_rel_line_to,close) because if you profile
* heavy rectangle drawing with Cairo that process can be overly expensive.
*
* If the user asks to add more than just a rectangle to their current path
* then we "flush" any speculative rectangle stored into the current path
* before continuing to append their operations.
*
* In addition to the speculative store cairo-cogl also has a fast-path
* fill_rectangle drawing operation that further aims to minimize the cost
* of drawing rectangles.
*/
static cairo_status_t
_flush_cr_rectangle (cairo_cogl_context_t *cr)
{
if (!cr->path_is_rectangle)
return CAIRO_STATUS_SUCCESS;
 
cr->path_is_rectangle = FALSE;
return _cairo_cogl_context_rectangle_real (cr, cr->x, cr->y, cr->width, cr->height);
}
 
static cairo_status_t
_cairo_cogl_context_restore (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.restore (abstract_cr);
}
 
static cairo_status_t
_cairo_cogl_context_translate (void *abstract_cr, double tx, double ty)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.translate (abstract_cr, tx, ty);
}
 
static cairo_status_t
_cairo_cogl_context_scale (void *abstract_cr, double sx, double sy)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.scale (abstract_cr, sx, sy);
}
 
static cairo_status_t
_cairo_cogl_context_rotate (void *abstract_cr, double theta)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.rotate (abstract_cr, theta);
}
 
static cairo_status_t
_cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.transform (abstract_cr, matrix);
}
 
static cairo_status_t
_cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.set_matrix (abstract_cr, matrix);
}
 
static cairo_status_t
_cairo_cogl_context_set_identity_matrix (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
cr->path_ctm_age++;
return cr->dev->backend_parent.set_identity_matrix (abstract_cr);
}
 
static cairo_status_t
_cairo_cogl_context_new_path (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.new_path (abstract_cr);
if (unlikely (status))
return status;
 
_cairo_path_fixed_fini (&cr->user_path);
_cairo_path_fixed_init (&cr->user_path);
cr->path_is_rectangle = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_new_sub_path (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.new_sub_path (abstract_cr);
if (unlikely (status))
return status;
 
_cairo_path_fixed_new_sub_path (&cr->user_path);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_move_to (void *abstract_cr, double x, double y)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t x_fixed, y_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.move_to (abstract_cr, x, y);
if (unlikely (status))
return status;
 
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
return _cairo_path_fixed_move_to (&cr->user_path, x_fixed, y_fixed);
}
 
static cairo_status_t
_cairo_cogl_context_line_to (void *abstract_cr, double x, double y)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t x_fixed, y_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.line_to (abstract_cr, x, y);
if (unlikely (status))
return status;
 
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
return _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
}
 
static cairo_status_t
_cairo_cogl_context_curve_to (void *abstract_cr,
double x1, double y1,
double x2, double y2,
double x3, double y3)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t x1_fixed, y1_fixed;
cairo_fixed_t x2_fixed, y2_fixed;
cairo_fixed_t x3_fixed, y3_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3);
if (unlikely (status))
return status;
 
x1_fixed = _cairo_fixed_from_double (x1);
y1_fixed = _cairo_fixed_from_double (y1);
 
x2_fixed = _cairo_fixed_from_double (x2);
y2_fixed = _cairo_fixed_from_double (y2);
 
x3_fixed = _cairo_fixed_from_double (x3);
y3_fixed = _cairo_fixed_from_double (y3);
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
return _cairo_path_fixed_curve_to (&cr->user_path,
x1_fixed, y1_fixed,
x2_fixed, y2_fixed,
x3_fixed, y3_fixed);
}
 
static cairo_status_t
_cairo_cogl_context_arc (void *abstract_cr,
double xc, double yc, double radius,
double angle1, double angle2,
cairo_bool_t forward)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward);
if (unlikely (status))
return status;
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0) {
cairo_fixed_t x_fixed, y_fixed;
 
x_fixed = _cairo_fixed_from_double (xc);
y_fixed = _cairo_fixed_from_double (yc);
status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
status = _cairo_cogl_context_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
 
if (unlikely (status))
return status;
 
if (forward)
_cairo_arc_path (&cr->base.base, xc, yc, radius, angle1, angle2);
else
_cairo_arc_path_negative (&cr->base.base, xc, yc, radius, angle1, angle2);
 
return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
}
 
static cairo_status_t
_cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t dx_fixed, dy_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.rel_move_to (abstract_cr, dx, dy);
if (unlikely (status))
return status;
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
return _cairo_path_fixed_rel_move_to (&cr->user_path, dx_fixed, dy_fixed);
}
 
static cairo_status_t
_cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t dx_fixed, dy_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.rel_line_to (abstract_cr, dx, dy);
if (unlikely (status))
return status;
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
return _cairo_path_fixed_rel_line_to (&cr->user_path, dx_fixed, dy_fixed);
}
 
 
static cairo_status_t
_cairo_cogl_context_rel_curve_to (void *abstract_cr,
double dx1, double dy1,
double dx2, double dy2,
double dx3, double dy3)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_fixed_t dx1_fixed, dy1_fixed;
cairo_fixed_t dx2_fixed, dy2_fixed;
cairo_fixed_t dx3_fixed, dy3_fixed;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3);
if (unlikely (status))
return status;
 
dx1_fixed = _cairo_fixed_from_double (dx1);
dy1_fixed = _cairo_fixed_from_double (dy1);
 
dx2_fixed = _cairo_fixed_from_double (dx2);
dy2_fixed = _cairo_fixed_from_double (dy2);
 
dx3_fixed = _cairo_fixed_from_double (dx3);
dy3_fixed = _cairo_fixed_from_double (dy3);
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
return _cairo_path_fixed_rel_curve_to (&cr->user_path,
dx1_fixed, dy1_fixed,
dx2_fixed, dy2_fixed,
dx3_fixed, dy3_fixed);
}
 
#if 0
static cairo_status_t
_cairo_cogl_context_arc_to (void *abstract_cr,
double x1, double y1,
double x2, double y2,
double radius)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius);
if (unlikely (status))
return status;
#warning "FIXME"
}
 
static cairo_status_t
_cairo_cogl_rel_arc_to (void *cr,
double dx1, double dy1,
double dx2, double dy2,
double radius)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius);
if (unlikely (status))
return status;
#warning "FIXME"
}
#endif
 
static cairo_status_t
_cairo_cogl_context_close_path (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
if (cr->path_is_rectangle) {
status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
status = cr->dev->backend_parent.close_path (abstract_cr);
if (unlikely (status))
return status;
 
if (cr->user_path.buf.base.num_ops == 0)
cr->path_ctm_age = 0;
 
return _cairo_path_fixed_close_path (&cr->user_path);
}
 
static cairo_status_t
_cairo_cogl_context_rectangle (void *abstract_cr,
double x, double y,
double width, double height)
{
cairo_cogl_context_t *cr = abstract_cr;
 
if (cr->user_path.buf.base.num_ops == 0) {
cr->path_ctm_age = 0;
 
#if 1
/* XXX: Since drawing rectangles is so common we have a
* fast-path for drawing a single rectangle. */
cr->x = x;
cr->y = y;
cr->width = width;
cr->height = height;
cr->path_is_rectangle = TRUE;
return CAIRO_STATUS_SUCCESS;
#endif
}
 
if (cr->path_is_rectangle) {
cairo_status_t status = _flush_cr_rectangle (cr);
if (unlikely (status))
return status;
}
 
return _cairo_cogl_context_rectangle_real (cr, x, y, width, height);
}
 
/* Since the surface backend drawing operator functions don't get
* passed the current #cairo_t context we don't have a good way
* to get our user-coordinates path into our surface operator
* functions.
*
* For now we use this function to set side band data on the surface
* itself.
*/
static void
_cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
cairo_cogl_context_t *cr)
{
 
if (cr->path_ctm_age <= 1) {
surface->user_path = &cr->user_path;
surface->ctm = &cr->base.gstate->ctm;
surface->ctm_inverse = &cr->base.gstate->ctm_inverse;
surface->path_is_rectangle = cr->path_is_rectangle;
if (surface->path_is_rectangle) {
surface->path_rectangle_x = cr->x;
surface->path_rectangle_y = cr->y;
surface->path_rectangle_width = cr->width;
surface->path_rectangle_height = cr->height;
}
} else {
surface->user_path = NULL;
surface->path_is_rectangle = FALSE;
}
}
 
static cairo_status_t
_cairo_cogl_context_fill (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
 
if (cr->path_is_rectangle) {
status = _cairo_cogl_surface_fill_rectangle (cr->base.gstate->target,
cr->base.gstate->op,
cr->base.gstate->source,
cr->x,
cr->y,
cr->width,
cr->height,
&cr->base.gstate->ctm,
cr->base.gstate->clip);
if (status == CAIRO_STATUS_SUCCESS)
goto DONE;
_flush_cr_rectangle (cr);
}
 
_cairo_cogl_surface_set_side_band_state (surface, cr);
 
status = cr->dev->backend_parent.fill (abstract_cr);
if (unlikely (status))
return status;
 
DONE:
_cairo_path_fixed_fini (&cr->user_path);
_cairo_path_fixed_init (&cr->user_path);
cr->path_is_rectangle = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_fill_preserve (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
 
_cairo_cogl_surface_set_side_band_state (surface, cr);
 
status = cr->dev->backend_parent.fill_preserve (abstract_cr);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_stroke (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
 
_cairo_cogl_surface_set_side_band_state (surface, cr);
 
status = cr->dev->backend_parent.stroke (abstract_cr);
if (unlikely (status))
return status;
 
_cairo_path_fixed_fini (&cr->user_path);
_cairo_path_fixed_init (&cr->user_path);
cr->path_is_rectangle = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_stroke_preserve (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
 
_cairo_cogl_surface_set_side_band_state (surface, cr);
 
status = cr->dev->backend_parent.stroke_preserve (abstract_cr);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_context_clip (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
cairo_status_t status;
 
status = cr->dev->backend_parent.clip (abstract_cr);
if (unlikely (status))
return status;
 
_cairo_path_fixed_fini (&cr->user_path);
_cairo_path_fixed_init (&cr->user_path);
cr->path_is_rectangle = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_cogl_context_destroy (void *abstract_cr)
{
cairo_cogl_context_t *cr = abstract_cr;
 
_cairo_default_context_fini (&cr->base);
 
_cairo_path_fixed_fini (&cr->user_path);
 
/* mark the context as invalid to protect against misuse */
cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
_freed_pool_put (&context_pool, cr);
}
 
/* We want to hook into the frontend of the path construction APIs so
* we can build up a path description in user coordinates instead of
* backend coordinates so that we can recognize user coordinate
* rectangles and so we can hash a user path independent of its
* transform. (With some care to catch unusual cases where the ctm
* changes mid-path) */
cairo_t *
_cairo_cogl_context_create (void *target)
{
cairo_cogl_surface_t *surface = target;
cairo_cogl_context_t *cr;
cairo_status_t status;
 
cr = _freed_pool_get (&context_pool);
if (unlikely (cr == NULL)) {
cr = malloc (sizeof (cairo_cogl_context_t));
if (unlikely (cr == NULL))
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
status = _cairo_default_context_init (&cr->base, target);
if (unlikely (status)) {
_freed_pool_put (&context_pool, cr);
return _cairo_create_in_error (status);
}
 
cr->dev = (cairo_cogl_device_t *)surface->base.device;
 
if (unlikely (cr->dev->backend_vtable_initialized == FALSE)) {
cairo_backend_t *backend = &cr->dev->backend;
memcpy (backend, cr->base.base.backend, sizeof (cairo_backend_t));
memcpy (&cr->dev->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
 
backend->destroy = _cairo_cogl_context_destroy;
 
backend->restore = _cairo_cogl_context_restore;
backend->translate = _cairo_cogl_context_translate;
backend->scale = _cairo_cogl_context_scale;
backend->rotate = _cairo_cogl_context_rotate;
backend->transform = _cairo_cogl_context_transform;
backend->set_matrix = _cairo_cogl_context_set_matrix;
backend->set_identity_matrix = _cairo_cogl_context_set_identity_matrix;
 
backend->new_path = _cairo_cogl_context_new_path;
backend->new_sub_path = _cairo_cogl_context_new_sub_path;
backend->move_to = _cairo_cogl_context_move_to;
backend->rel_move_to = _cairo_cogl_context_rel_move_to;
backend->line_to = _cairo_cogl_context_line_to;
backend->rel_line_to = _cairo_cogl_context_rel_line_to;
backend->curve_to = _cairo_cogl_context_curve_to;
backend->rel_curve_to = _cairo_cogl_context_rel_curve_to;
#if 0
backend->arc_to = _cairo_cogl_context_arc_to;
backend->rel_arc_to = _cairo_cogl_context_rel_arc_to;
#endif
backend->close_path = _cairo_cogl_context_close_path;
//backend->arc = _cairo_cogl_context_arc;
backend->rectangle = _cairo_cogl_context_rectangle;
 
/* Try to automatically catch if any new path APIs are added that mean
* we may need to overload more functions... */
assert (((char *)&backend->path_extents - (char *)&backend->device_to_user_distance)
== (sizeof (void *) * 14));
 
backend->fill = _cairo_cogl_context_fill;
backend->fill_preserve = _cairo_cogl_context_fill_preserve;
backend->stroke = _cairo_cogl_context_stroke;
backend->stroke_preserve = _cairo_cogl_context_stroke_preserve;
backend->clip = _cairo_cogl_context_clip;
 
cr->dev->backend_vtable_initialized = TRUE;
}
 
cr->base.base.backend = &cr->dev->backend;
 
_cairo_path_fixed_init (&cr->user_path);
cr->path_is_rectangle = FALSE;
 
return &cr->base.base;
}
/programs/develop/libraries/cairo/src/cairo-cogl-gradient-private.h
0,0 → 1,89
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#ifndef CAIRO_COGL_GRADIENT_PRIVATE_H
#define CAIRO_COGL_GRADIENT_PRIVATE_H
 
#include "cairoint.h"
#include "cairo-pattern-private.h"
 
#include <cogl/cogl2-experimental.h>
 
#define CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE (1024 * 1024)
 
typedef enum _cairo_cogl_gradient_compatibility {
CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD = 1<<0,
CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT = 1<<1,
CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT = 1<<2,
CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE = 1<<3
} cairo_cogl_gradient_compatibility_t;
#define CAIRO_COGL_GRADIENT_CAN_EXTEND_ALL (CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD |\
CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT|\
CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT|\
CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE)
 
typedef struct _cairo_cogl_linear_texture_entry {
cairo_cogl_gradient_compatibility_t compatibility;
CoglTexture *texture;
float translate_x;
float scale_x;
} cairo_cogl_linear_texture_entry_t;
 
typedef struct _cairo_cogl_linear_gradient {
cairo_cache_entry_t cache_entry;
cairo_reference_count_t ref_count;
GList *textures;
int n_stops;
const cairo_gradient_stop_t *stops;
cairo_gradient_stop_t stops_embedded[1];
} cairo_cogl_linear_gradient_t;
 
cairo_int_status_t
_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *context,
cairo_extend_t extend_mode,
int n_stops,
const cairo_gradient_stop_t *stops,
cairo_cogl_linear_gradient_t **gradient_out);
 
cairo_cogl_linear_texture_entry_t *
_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
cairo_extend_t extend_mode);
 
cairo_cogl_linear_gradient_t *
_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient);
 
void
_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient);
 
cairo_bool_t
_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b);
 
#endif /* CAIRO_COGL_GRADIENT_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-cogl-gradient.c
0,0 → 1,642
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
//#include "cairoint.h"
 
#include "cairo-cogl-private.h"
#include "cairo-cogl-gradient-private.h"
#include "cairo-image-surface-private.h"
 
#include <cogl/cogl2-experimental.h>
#include <glib.h>
 
#define DUMP_GRADIENTS_TO_PNG
 
static unsigned long
_cairo_cogl_linear_gradient_hash (unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
return _cairo_hash_bytes (n_stops, stops,
sizeof (cairo_gradient_stop_t) * n_stops);
}
 
static cairo_cogl_linear_gradient_t *
_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t *ctx,
unsigned long hash,
unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
cairo_cogl_linear_gradient_t lookup;
 
lookup.cache_entry.hash = hash,
lookup.n_stops = n_stops;
lookup.stops = stops;
 
return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry);
}
 
cairo_bool_t
_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b)
{
const cairo_cogl_linear_gradient_t *a = key_a;
const cairo_cogl_linear_gradient_t *b = key_b;
 
if (a->n_stops != b->n_stops)
return FALSE;
 
return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
}
 
cairo_cogl_linear_gradient_t *
_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
 
_cairo_reference_count_inc (&gradient->ref_count);
 
return gradient;
}
 
void
_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient)
{
GList *l;
 
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
 
if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
return;
 
for (l = gradient->textures; l; l = l->next) {
cairo_cogl_linear_texture_entry_t *entry = l->data;
cogl_object_unref (entry->texture);
free (entry);
}
g_list_free (gradient->textures);
 
free (gradient);
}
 
static int
_cairo_cogl_util_next_p2 (int a)
{
int rval = 1;
 
while (rval < a)
rval <<= 1;
 
return rval;
}
 
static float
get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
{
float range;
float max = 0;
 
range = fabs (color0->red - color1->red);
max = MAX (range, max);
range = fabs (color0->green - color1->green);
max = MAX (range, max);
range = fabs (color0->blue - color1->blue);
max = MAX (range, max);
range = fabs (color0->alpha - color1->alpha);
max = MAX (range, max);
 
return max;
}
 
static int
_cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t extend,
unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
unsigned int n;
float max_texels_per_unit_offset = 0;
float total_offset_range;
 
/* Find the stop pair demanding the most precision because we are
* interpolating the largest color-component range.
*
* From that we can define the relative sizes of all the other
* stop pairs within our texture and thus the overall size.
*
* To determine the maximum number of texels for a given gap we
* look at the range of colors we are expected to interpolate (so
* long as the stop offsets are not degenerate) and we simply
* assume we want one texel for each unique color value possible
* for a one byte-per-component representation.
* XXX: maybe this is overkill and just allowing 128 levels
* instead of 256 would be enough and then we'd rely on the
* bilinear filtering to give the full range.
*
* XXX: potentially we could try and map offsets to pixels to come
* up with a more precise mapping, but we are aiming to cache
* the gradients so we can't make assumptions about how it will be
* scaled in the future.
*/
for (n = 1; n < n_stops; n++) {
float color_range;
float offset_range;
float texels;
float texels_per_unit_offset;
 
/* note: degenerate stops don't need to be represented in the
* texture but we want to be sure that solid gaps get at least
* one texel and all other gaps get at least 2 texels.
*/
 
if (stops[n].offset == stops[n-1].offset)
continue;
 
color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color);
if (color_range == 0)
texels = 1;
else
texels = MAX (2, 256.0f * color_range);
 
/* So how many texels would we need to map over the full [0,1]
* gradient range so this gap would have enough texels? ... */
offset_range = stops[n].offset - stops[n - 1].offset;
texels_per_unit_offset = texels / offset_range;
 
if (texels_per_unit_offset > max_texels_per_unit_offset)
max_texels_per_unit_offset = texels_per_unit_offset;
}
 
total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset);
return max_texels_per_unit_offset * total_offset_range;
}
 
/* Aim to create gradient textures without an alpha component so we can avoid
* needing to use blending... */
static CoglPixelFormat
_cairo_cogl_linear_gradient_format_for_stops (cairo_extend_t extend,
unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
unsigned int n;
 
/* We have to add extra transparent texels to the end of the gradient to
* handle CAIRO_EXTEND_NONE... */
if (extend == CAIRO_EXTEND_NONE)
return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
 
for (n = 1; n < n_stops; n++) {
if (stops[n].color.alpha != 1.0)
return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
}
 
return COGL_PIXEL_FORMAT_BGR_888;
}
 
static cairo_cogl_gradient_compatibility_t
_cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode)
{
switch (extend_mode)
{
case CAIRO_EXTEND_NONE:
return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE;
case CAIRO_EXTEND_PAD:
return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD;
case CAIRO_EXTEND_REPEAT:
return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
case CAIRO_EXTEND_REFLECT:
return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
}
 
assert (0); /* not reached */
return CAIRO_EXTEND_NONE;
}
 
cairo_cogl_linear_texture_entry_t *
_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
cairo_extend_t extend_mode)
{
GList *l;
cairo_cogl_gradient_compatibility_t compatibility =
_cairo_cogl_compatibility_from_extend_mode (extend_mode);
for (l = gradient->textures; l; l = l->next) {
cairo_cogl_linear_texture_entry_t *entry = l->data;
if (entry->compatibility & compatibility)
return entry;
}
return NULL;
}
 
static void
color_stop_lerp (const cairo_color_stop_t *c0,
const cairo_color_stop_t *c1,
float factor,
cairo_color_stop_t *dest)
{
/* NB: we always ignore the short members in this file so we don't need to
* worry about initializing them here. */
dest->red = c0->red * (1.0f-factor) + c1->red * factor;
dest->green = c0->green * (1.0f-factor) + c1->green * factor;
dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor;
dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor;
}
 
static size_t
_cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient)
{
GList *l;
size_t size = 0;
for (l = gradient->textures; l; l = l->next) {
cairo_cogl_linear_texture_entry_t *entry = l->data;
size += cogl_texture_get_width (entry->texture) * 4;
}
return size;
}
 
static void
emit_stop (CoglVertexP2C4 **position,
float left,
float right,
const cairo_color_stop_t *left_color,
const cairo_color_stop_t *right_color)
{
CoglVertexP2C4 *p = *position;
 
guint8 lr = left_color->red * 255;
guint8 lg = left_color->green * 255;
guint8 lb = left_color->blue * 255;
guint8 la = left_color->alpha * 255;
 
guint8 rr = right_color->red * 255;
guint8 rg = right_color->green * 255;
guint8 rb = right_color->blue * 255;
guint8 ra = right_color->alpha * 255;
 
p[0].x = left;
p[0].y = 0;
p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la;
p[1].x = left;
p[1].y = 1;
p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la;
p[2].x = right;
p[2].y = 1;
p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra;
 
p[3].x = left;
p[3].y = 0;
p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la;
p[4].x = right;
p[4].y = 1;
p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra;
p[5].x = right;
p[5].y = 0;
p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra;
 
*position = &p[6];
}
 
#ifdef DUMP_GRADIENTS_TO_PNG
static void
dump_gradient_to_png (CoglTexture *texture)
{
cairo_image_surface_t *image = (cairo_image_surface_t *)
cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
cogl_texture_get_width (texture),
cogl_texture_get_height (texture));
CoglPixelFormat format;
static int gradient_id = 0;
char *gradient_name;
 
if (image->base.status)
return;
 
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
#else
format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
#endif
cogl_texture_get_data (texture,
format,
0,
image->data);
gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++);
g_print ("writing gradient: %s\n", gradient_name);
cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name);
g_free (gradient_name);
}
#endif
 
cairo_int_status_t
_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
cairo_extend_t extend_mode,
int n_stops,
const cairo_gradient_stop_t *stops,
cairo_cogl_linear_gradient_t **gradient_out)
{
unsigned long hash;
cairo_cogl_linear_gradient_t *gradient;
cairo_cogl_linear_texture_entry_t *entry;
cairo_gradient_stop_t *internal_stops;
int stop_offset;
int n_internal_stops;
int n;
cairo_cogl_gradient_compatibility_t compatibilities;
int width;
int left_padding = 0;
cairo_color_stop_t left_padding_color;
int right_padding = 0;
cairo_color_stop_t right_padding_color;
CoglPixelFormat format;
CoglTexture2D *tex;
GError *error = NULL;
int un_padded_width;
CoglHandle offscreen;
cairo_int_status_t status;
int n_quads;
int n_vertices;
float prev;
float right;
CoglVertexP2C4 *vertices;
CoglVertexP2C4 *p;
CoglPrimitive *prim;
 
hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
 
gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops);
if (gradient) {
cairo_cogl_linear_texture_entry_t *entry =
_cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode);
if (entry) {
*gradient_out = _cairo_cogl_linear_gradient_reference (gradient);
return CAIRO_INT_STATUS_SUCCESS;
}
}
 
if (!gradient) {
gradient = malloc (sizeof (cairo_cogl_linear_gradient_t) +
sizeof (cairo_gradient_stop_t) * (n_stops - 1));
if (!gradient)
return CAIRO_INT_STATUS_NO_MEMORY;
 
CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
/* NB: we update the cache_entry size at the end before
* [re]adding it to the cache. */
gradient->cache_entry.hash = hash;
gradient->textures = NULL;
gradient->n_stops = n_stops;
gradient->stops = gradient->stops_embedded;
memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
} else
_cairo_cogl_linear_gradient_reference (gradient);
 
entry = malloc (sizeof (cairo_cogl_linear_texture_entry_t));
if (!entry) {
status = CAIRO_INT_STATUS_NO_MEMORY;
goto BAIL;
}
 
compatibilities = _cairo_cogl_compatibility_from_extend_mode (extend_mode);
 
n_internal_stops = n_stops;
stop_offset = 0;
 
/* We really need stops covering the full [0,1] range for repeat/reflect
* if we want to use sampler REPEAT/MIRROR wrap modes so we may need
* to add some extra stops... */
if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT)
{
/* If we don't need any extra stops then actually the texture
* will be shareable for repeat and reflect... */
compatibilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT |
CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT);
 
if (stops[0].offset != 0) {
n_internal_stops++;
stop_offset++;
}
 
if (stops[n_stops - 1].offset != 1)
n_internal_stops++;
}
 
internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t));
memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops);
 
/* cairo_color_stop_t values are all unpremultiplied but we need to
* interpolate premultiplied colors so we premultiply all the double
* components now. (skipping any extra stops added for repeat/reflect)
*
* Anothing thing to note is that by premultiplying the colors
* early we'll also reduce the range of colors to interpolate
* which can result in smaller gradient textures.
*/
for (n = stop_offset; n < n_stops; n++) {
cairo_color_stop_t *color = &internal_stops[n].color;
color->red *= color->alpha;
color->green *= color->alpha;
color->blue *= color->alpha;
}
 
if (n_internal_stops != n_stops)
{
if (extend_mode == CAIRO_EXTEND_REPEAT) {
compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
if (stops[0].offset != 0) {
/* what's the wrap-around distance between the user's end-stops? */
double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset;
internal_stops[0].offset = 0;
color_stop_lerp (&stops[0].color,
&stops[n_stops - 1].color,
stops[0].offset / dx,
&internal_stops[0].color);
}
if (stops[n_stops - 1].offset != 1) {
internal_stops[n_internal_stops - 1].offset = 1;
internal_stops[n_internal_stops - 1].color = internal_stops[0].color;
}
} else if (extend_mode == CAIRO_EXTEND_REFLECT) {
compatibilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
if (stops[0].offset != 0) {
internal_stops[0].offset = 0;
internal_stops[0].color = stops[n_stops - 1].color;
}
if (stops[n_stops - 1].offset != 1) {
internal_stops[n_internal_stops - 1].offset = 1;
internal_stops[n_internal_stops - 1].color = stops[0].color;
}
}
}
 
stops = internal_stops;
n_stops = n_internal_stops;
 
width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops);
 
if (extend_mode == CAIRO_EXTEND_PAD) {
 
/* Here we need to guarantee that the edge texels of our
* texture correspond to the desired padding color so we
* can use CLAMP_TO_EDGE.
*
* For short stop-gaps and especially for degenerate stops
* it's possible that without special consideration the
* user's end stop colors would not be present in our final
* texture.
*
* To handle this we forcibly add two extra padding texels
* at the edges which extend beyond the [0,1] range of the
* gradient itself and we will later report a translate and
* scale transform to compensate for this.
*/
 
/* XXX: If we consider generating a mipmap for our 1d texture
* at some point then we also need to consider how much
* padding to add to be sure lower mipmap levels still have
* the desired edge color (as opposed to a linear blend with
* other colors of the gradient).
*/
 
left_padding = 1;
left_padding_color = stops[0].color;
right_padding = 1;
right_padding_color = stops[n_stops - 1].color;
} else if (extend_mode == CAIRO_EXTEND_NONE) {
/* We handle EXTEND_NONE by adding two extra, transparent, texels at
* the ends of the texture and use CLAMP_TO_EDGE.
*
* We add a scale and translate transform so to account for our texels
* extending beyond the [0,1] range. */
 
left_padding = 1;
left_padding_color.red = 0;
left_padding_color.green = 0;
left_padding_color.blue = 0;
left_padding_color.alpha = 0;
right_padding = 1;
right_padding_color = left_padding_color;
}
 
/* If we still have stops that don't cover the full [0,1] range
* then we need to define a texture-coordinate scale + translate
* transform to account for that... */
if (stops[n_stops - 1].offset - stops[0].offset < 1) {
float range = stops[n_stops - 1].offset - stops[0].offset;
entry->scale_x = 1.0 / range;
entry->translate_x = -(stops[0].offset * entry->scale_x);
}
 
width += left_padding + right_padding;
 
width = _cairo_cogl_util_next_p2 (width);
width = MIN (4096, width); /* lets not go too stupidly big! */
format = _cairo_cogl_linear_gradient_format_for_stops (extend_mode, n_stops, stops);
 
do {
tex = cogl_texture_2d_new_with_size (device->cogl_context,
width,
1,
format,
&error);
if (!tex)
g_error_free (error);
} while (tex == NULL && width >> 1);
 
if (!tex) {
status = CAIRO_INT_STATUS_NO_MEMORY;
goto BAIL;
}
 
entry->texture = COGL_TEXTURE (tex);
entry->compatibility = compatibilities;
 
un_padded_width = width - left_padding - right_padding;
 
/* XXX: only when we know the final texture width can we calculate the
* scale and translate factors needed to account for padding... */
if (un_padded_width != width)
entry->scale_x *= (float)un_padded_width / (float)width;
if (left_padding)
entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
 
offscreen = cogl_offscreen_new_to_texture (tex);
cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
cogl_ortho (0, width, 1, 0, -1, 100);
cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
COGL_BUFFER_BIT_COLOR,
0, 0, 0, 0);
 
n_quads = n_stops - 1 + !!left_padding + !!right_padding;
n_vertices = 6 * n_quads;
vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
p = vertices;
if (left_padding)
emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
prev = (float)left_padding;
for (n = 1; n < n_stops; n++) {
right = (float)left_padding + (float)un_padded_width * stops[n].offset;
emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color);
prev = right;
}
if (right_padding)
emit_stop (&p, prev, width, &right_padding_color, &right_padding_color);
 
prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
n_vertices,
vertices);
/* Just use this as the simplest way to setup a default pipeline... */
cogl_set_source_color4f (0, 0, 0, 0);
cogl_primitive_draw (prim);
cogl_object_unref (prim);
 
cogl_pop_framebuffer ();
cogl_object_unref (offscreen);
 
gradient->textures = g_list_prepend (gradient->textures, entry);
gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
 
#ifdef DUMP_GRADIENTS_TO_PNG
dump_gradient_to_png (COGL_TEXTURE (tex));
#endif
 
#warning "FIXME:"
/* XXX: it seems the documentation of _cairo_cache_insert isn't true - it
* doesn't handle re-adding the same entry gracefully - the cache will
* just keep on growing and then it will start randomly evicting things
* pointlessly */
/* we ignore errors here and just return an uncached gradient */
if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry)))
_cairo_cogl_linear_gradient_reference (gradient);
 
*gradient_out = gradient;
return CAIRO_INT_STATUS_SUCCESS;
 
BAIL:
free (entry);
if (gradient)
_cairo_cogl_linear_gradient_destroy (gradient);
return status;
}
/programs/develop/libraries/cairo/src/cairo-cogl-private.h
0,0 → 1,164
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#ifndef CAIRO_COGL_PRIVATE_H
#define CAIRO_COGL_PRIVATE_H
 
#include "cairo-device-private.h"
#include "cairo-cache-private.h"
#include "cairo-backend-private.h"
#include "cairo-default-context-private.h"
#include "cairo-surface-private.h"
 
#include <cogl/cogl2-experimental.h>
 
typedef enum _cairo_cogl_template_type {
CAIRO_COGL_TEMPLATE_TYPE_SOLID,
CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID,
CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE,
CAIRO_COGL_TEMPLATE_TYPE_COUNT
} cairo_cogl_template_type;
 
typedef struct _cairo_cogl_device {
cairo_device_t base;
 
cairo_bool_t backend_vtable_initialized;
cairo_backend_t backend;
 
/* We save a copy of all the original backend methods that we override so
* we can chain up...
*/
cairo_backend_t backend_parent;
 
CoglContext *cogl_context;
 
CoglTexture *dummy_texture;
 
/* This is a sparsely filled set of templates because we don't support
* the full range of operators that cairo has. All entries corresponding
* to unsupported operators are NULL.
*
* The CAIRO_OPERATOR_ADD is the operator enum with the highest value that
* we support so we at least cap the size of the array by that.
*
* For each operator we have a template for when we have a solid source
* and another for each texture format that could be used as a source.
*/
CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT];
 
CoglMatrix identity;
 
/* Caches 1d linear gradient textures */
cairo_cache_t linear_cache;
 
cairo_cache_t path_fill_staging_cache;
cairo_cache_t path_fill_prim_cache;
cairo_cache_t path_stroke_staging_cache;
cairo_cache_t path_stroke_prim_cache;
} cairo_cogl_device_t;
 
typedef struct _cairo_cogl_clip_primitives {
cairo_t *clip;
CoglPrimitive **primitives;
} cairo_cogl_clip_primitives_t;
 
typedef struct _cairo_cogl_surface {
cairo_surface_t base;
 
CoglPixelFormat cogl_format;
cairo_bool_t ignore_alpha;
 
/* We currently have 3 basic kinds of Cogl surfaces:
* 1) A light surface simply wrapping a CoglTexture
* 2) A CoglOffscreen framebuffer that implicitly also wraps a CoglTexture
* 3) A CoglOnscreen framebuffer which could potentially be mapped to
* a CoglTexture (e.g. via tfp on X11) but we don't currently do
* that.
*/
 
CoglTexture *texture;
CoglFramebuffer *framebuffer;
 
int width;
int height;
 
GQueue *journal;
 
CoglAttributeBuffer *buffer_stack;
size_t buffer_stack_size;
size_t buffer_stack_offset;
guint8 *buffer_stack_pointer;
 
cairo_clip_t *last_clip;
 
/* A small fifo of recently used cairo_clip_ts paired with CoglPrimitives
* that can be used to mask the stencil buffer. */
GList *clips_fifo;
 
int n_clip_updates_per_frame;
 
/* Since the surface backend drawing operator functions don't get
* passed the current cairo_t context we don't have a good way
* to get our user-coordinates path into our surface_fill function.
*
* For now we use our _cairo_cogl_context_fill() wrapper to set this
* side band data on the surface...
*/
cairo_path_fixed_t *user_path;
cairo_matrix_t *ctm;
cairo_matrix_t *ctm_inverse;
cairo_bool_t path_is_rectangle;
double path_rectangle_x;
double path_rectangle_y;
double path_rectangle_width;
double path_rectangle_height;
} cairo_cogl_surface_t;
 
cairo_status_t
_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y,
cairo_fixed_t width,
cairo_fixed_t height);
 
cairo_int_status_t
_cairo_cogl_surface_fill_rectangle (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
double x,
double y,
double width,
double height,
cairo_matrix_t *ctm,
const cairo_clip_t *clip);
 
#endif /* CAIRO_COGL_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-cogl-surface.c
0,0 → 1,2805
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
#include "cairoint.h"
 
#include "cairo-cache-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-fixed-private.h"
#include "cairo-device-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-cogl-private.h"
#include "cairo-cogl-gradient-private.h"
#include "cairo-arc-private.h"
#include "cairo-traps-private.h"
#include "cairo-cogl-context-private.h"
#include "cairo-cogl-utils-private.h"
#include "cairo-box-inline.h"
#include "cairo-surface-subsurface-inline.h"
#include "cairo-surface-fallback-private.h"
#include "cairo-surface-offset-private.h"
 
#include "cairo-cogl.h"
 
#include <cogl/cogl2-experimental.h>
#include <glib.h>
 
#define CAIRO_COGL_DEBUG 0
//#define FILL_WITH_COGL_PATH
//#define USE_CAIRO_PATH_FLATTENER
#define ENABLE_PATH_CACHE
//#define DISABLE_BATCHING
#define USE_COGL_RECTANGLE_API
#define ENABLE_RECTANGLES_FASTPATH
 
#if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
#define NEED_COGL_CONTEXT
#endif
 
#if CAIRO_COGL_DEBUG && __GNUC__
#define UNSUPPORTED(reason) ({ \
g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \
CAIRO_INT_STATUS_UNSUPPORTED; \
})
#else
#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
#endif
 
#define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024)
 
typedef struct _cairo_cogl_texture_attributes {
/* nabbed from cairo_surface_attributes_t... */
cairo_matrix_t matrix;
cairo_extend_t extend;
cairo_filter_t filter;
cairo_bool_t has_component_alpha;
 
CoglPipelineWrapMode s_wrap;
CoglPipelineWrapMode t_wrap;
} cairo_cogl_texture_attributes_t;
 
typedef enum _cairo_cogl_journal_entry_type {
CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH,
CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP
} cairo_cogl_journal_entry_type_t;
 
typedef struct _cairo_cogl_journal_entry {
cairo_cogl_journal_entry_type_t type;
} cairo_cogl_journal_entry_t;
 
typedef struct _cairo_cogl_journal_clip_entry {
cairo_cogl_journal_entry_t base;
cairo_clip_t *clip;
} cairo_cogl_journal_clip_entry_t;
 
typedef struct _cairo_cogl_journal_rect_entry {
cairo_cogl_journal_entry_t base;
CoglPipeline *pipeline;
float x;
float y;
float width;
float height;
int n_layers;
cairo_matrix_t ctm;
} cairo_cogl_journal_rect_entry_t;
 
typedef struct _cairo_cogl_journal_prim_entry {
cairo_cogl_journal_entry_t base;
CoglPipeline *pipeline;
CoglPrimitive *primitive;
gboolean has_transform;
cairo_matrix_t transform;
} cairo_cogl_journal_prim_entry_t;
 
typedef struct _cairo_cogl_journal_path_entry {
cairo_cogl_journal_entry_t base;
CoglPipeline *pipeline;
CoglPath *path;
} cairo_cogl_journal_path_entry_t;
 
typedef struct _cairo_cogl_path_fill_meta {
cairo_cache_entry_t cache_entry;
cairo_reference_count_t ref_count;
int counter;
cairo_path_fixed_t *user_path;
cairo_matrix_t ctm_inverse;
 
/* TODO */
#if 0
/* A cached path tessellation should be re-usable with different rotations
* and translations but not for different scales.
*
* one idea is to track the diagonal lenghts of a unit rectangle
* transformed through the original ctm use to tesselate the geometry
* so we can check what the lengths are for any new ctm to know if
* this geometry is compatible.
*/
#endif
 
CoglPrimitive *prim;
} cairo_cogl_path_fill_meta_t;
 
typedef struct _cairo_cogl_path_stroke_meta {
cairo_cache_entry_t cache_entry;
cairo_reference_count_t ref_count;
int counter;
cairo_path_fixed_t *user_path;
cairo_matrix_t ctm_inverse;
cairo_stroke_style_t style;
double tolerance;
 
/* TODO */
#if 0
/* A cached path tessellation should be re-usable with different rotations
* and translations but not for different scales.
*
* one idea is to track the diagonal lenghts of a unit rectangle
* transformed through the original ctm use to tesselate the geometry
* so we can check what the lengths are for any new ctm to know if
* this geometry is compatible.
*/
#endif
 
CoglPrimitive *prim;
} cairo_cogl_path_stroke_meta_t;
 
static cairo_surface_t *
_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
cairo_bool_t ignore_alpha,
CoglFramebuffer *framebuffer,
CoglTexture *texture);
 
static cairo_int_status_t
_cairo_cogl_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
static void
_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
 
cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend;
 
slim_hidden_proto (cairo_cogl_device_create);
slim_hidden_proto (cairo_cogl_surface_create);
slim_hidden_proto (cairo_cogl_surface_get_framebuffer);
slim_hidden_proto (cairo_cogl_surface_get_texture);
slim_hidden_proto (cairo_cogl_surface_end_frame);
 
static cairo_cogl_device_t *
to_device (cairo_device_t *device)
{
return (cairo_cogl_device_t *)device;
}
 
/* moves trap points such that they become the actual corners of the trapezoid */
static void
_sanitize_trap (cairo_trapezoid_t *t)
{
cairo_trapezoid_t s = *t;
 
#define FIX(lr, tb, p) \
if (t->lr.p.y != t->tb) { \
t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
t->lr.p.y = s.tb; \
}
FIX (left, top, p1);
FIX (left, bottom, p2);
FIX (right, top, p1);
FIX (right, bottom, p2);
}
 
static cairo_status_t
_cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
{
GError *error = NULL;
 
if (surface->framebuffer)
return CAIRO_STATUS_SUCCESS;
 
surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture));
if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
g_error_free (error);
cogl_object_unref (surface->framebuffer);
surface->framebuffer = NULL;
return CAIRO_STATUS_NO_MEMORY;
}
 
cogl_push_framebuffer (surface->framebuffer);
cogl_ortho (0, surface->width,
surface->height, 0,
-1, 100);
cogl_pop_framebuffer ();
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_cogl_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_cogl_surface_t *reference_surface = abstract_surface;
cairo_cogl_surface_t *surface;
CoglTexture *texture;
cairo_status_t status;
 
texture = cogl_texture_new_with_size (width, height,
COGL_TEXTURE_NO_SLICING,
(content & CAIRO_CONTENT_COLOR) ?
COGL_PIXEL_FORMAT_BGRA_8888_PRE :
COGL_PIXEL_FORMAT_A_8);
if (!texture)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface = (cairo_cogl_surface_t *)
_cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
(content & CAIRO_CONTENT_ALPHA) == 0,
NULL,
texture);
if (unlikely (surface->base.status))
return &surface->base;
 
status = _cairo_cogl_surface_ensure_framebuffer (surface);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
 
return &surface->base;
}
 
static cairo_bool_t
_cairo_cogl_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_cogl_surface_t *surface = abstract_surface;
 
extents->x = 0;
extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
 
return TRUE;
}
 
static void
_cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
{
GList *l;
 
for (l = surface->journal->head; l; l = l->next) {
cairo_cogl_journal_entry_t *entry = l->data;
 
if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) {
cairo_cogl_journal_prim_entry_t *prim_entry =
(cairo_cogl_journal_prim_entry_t *)entry;
cogl_object_unref (prim_entry->primitive);
} else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) {
cairo_cogl_journal_path_entry_t *path_entry =
(cairo_cogl_journal_path_entry_t *)entry;
cogl_object_unref (path_entry->path);
}
}
 
g_queue_free (surface->journal);
surface->journal = NULL;
}
 
#ifdef FILL_WITH_COGL_PATH
static void
_cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
CoglPipeline *pipeline,
CoglPath *path)
{
cairo_cogl_journal_path_entry_t *entry;
 
if (unlikely (surface->journal == NULL))
surface->journal = g_queue_new ();
 
/* FIXME: Instead of a GList here we should stack allocate the journal
* entries so it would be cheaper to allocate and they can all be freed in
* one go after flushing! */
entry = g_slice_new (cairo_cogl_journal_path_entry_t);
entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH;
 
entry->pipeline = cogl_object_ref (pipeline);
entry->path = cogl_object_ref (path);
 
g_queue_push_tail (surface->journal, entry);
 
#ifdef DISABLE_BATCHING
_cairo_cogl_journal_flush (surface);
#endif
}
#endif /* FILL_WITH_COGL_PATH */
 
static void
_cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
CoglPipeline *pipeline,
CoglPrimitive *primitive,
cairo_matrix_t *transform)
{
cairo_cogl_journal_prim_entry_t *entry;
 
if (unlikely (surface->journal == NULL))
surface->journal = g_queue_new ();
 
/* FIXME: Instead of a GList here we should stack allocate the journal
* entries so it would be cheaper to allocate and they can all be freed in
* one go after flushing! */
entry = g_slice_new (cairo_cogl_journal_prim_entry_t);
entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
 
entry->pipeline = cogl_object_ref (pipeline);
 
if (transform) {
entry->transform = *transform;
entry->has_transform = TRUE;
} else
entry->has_transform = FALSE;
 
entry->primitive = cogl_object_ref (primitive);
 
g_queue_push_tail (surface->journal, entry);
 
#ifdef DISABLE_BATCHING
_cairo_cogl_journal_flush (surface);
#endif
}
 
static void
_cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
CoglPipeline *pipeline,
float x,
float y,
float width,
float height,
int n_layers,
cairo_matrix_t *ctm)
{
cairo_cogl_journal_rect_entry_t *entry;
 
if (unlikely (surface->journal == NULL))
surface->journal = g_queue_new ();
 
/* FIXME: Instead of a GList here we should stack allocate the journal
* entries so it would be cheaper to allocate and they can all be freed in
* one go after flushing! */
entry = g_slice_new (cairo_cogl_journal_rect_entry_t);
entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE;
 
entry->pipeline = cogl_object_ref (pipeline);
 
entry->x = x;
entry->y = y;
entry->width = width;
entry->height = height;
entry->ctm = *ctm;
 
entry->n_layers = n_layers;
 
g_queue_push_tail (surface->journal, entry);
 
#ifdef DISABLE_BATCHING
_cairo_cogl_journal_flush (surface);
#endif
}
 
static void
_cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
const cairo_clip_t *clip)
{
cairo_cogl_journal_clip_entry_t *entry;
 
if (unlikely (surface->journal == NULL))
surface->journal = g_queue_new ();
 
/* FIXME: Instead of a GList here we should stack allocate the journal
* entries so it would be cheaper to allocate and they can all be freed in
* one go after flushing! */
entry = g_slice_new (cairo_cogl_journal_clip_entry_t);
entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP;
entry->clip = _cairo_clip_copy (clip);
 
g_queue_push_tail (surface->journal, entry);
}
 
static void
_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
{
GList *l;
 
if (!surface->journal) {
assert (surface->last_clip == NULL);
return;
}
 
if (surface->buffer_stack && surface->buffer_stack_offset) {
cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
cogl_object_unref (surface->buffer_stack);
surface->buffer_stack = NULL;
}
 
for (l = surface->journal->head; l; l = l->next) {
cairo_cogl_journal_entry_t *entry = l->data;
gsize entry_size;
 
switch (entry->type)
{
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
cairo_cogl_journal_clip_entry_t *clip_entry =
(cairo_cogl_journal_clip_entry_t *)entry;
_cairo_clip_destroy (clip_entry->clip);
entry_size = sizeof (cairo_cogl_journal_clip_entry_t);
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
cairo_cogl_journal_rect_entry_t *rect_entry =
(cairo_cogl_journal_rect_entry_t *)entry;
cogl_object_unref (rect_entry->pipeline);
entry_size = sizeof (cairo_cogl_journal_rect_entry_t);
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
cairo_cogl_journal_prim_entry_t *prim_entry =
(cairo_cogl_journal_prim_entry_t *)entry;
cogl_object_unref (prim_entry->pipeline);
cogl_object_unref (prim_entry->primitive);
entry_size = sizeof (cairo_cogl_journal_prim_entry_t);
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
cairo_cogl_journal_path_entry_t *path_entry =
(cairo_cogl_journal_path_entry_t *)entry;
cogl_object_unref (path_entry->pipeline);
cogl_object_unref (path_entry->path);
entry_size = sizeof (cairo_cogl_journal_path_entry_t);
break;
}
default:
assert (0); /* not reached! */
entry_size = 0; /* avoid compiler warning */
}
g_slice_free1 (entry_size, entry);
}
 
g_queue_clear (surface->journal);
 
if (surface->last_clip) {
_cairo_clip_destroy (surface->last_clip);
surface->last_clip = NULL;
}
}
 
static CoglAttributeBuffer *
_cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
size_t size,
size_t *offset,
void **pointer)
{
/* XXX: In the Cogl journal we found it more efficient to have a pool of
* buffers that we re-cycle but for now we simply thow away our stack
* buffer each time we flush. */
if (unlikely (surface->buffer_stack &&
(surface->buffer_stack_size - surface->buffer_stack_offset) < size)) {
cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
cogl_object_unref (surface->buffer_stack);
surface->buffer_stack = NULL;
surface->buffer_stack_size *= 2;
}
 
if (unlikely (surface->buffer_stack_size < size))
surface->buffer_stack_size = size * 2;
 
if (unlikely (surface->buffer_stack == NULL)) {
surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL);
surface->buffer_stack_pointer =
cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
COGL_BUFFER_ACCESS_WRITE,
COGL_BUFFER_MAP_HINT_DISCARD);
surface->buffer_stack_offset = 0;
}
 
*pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset;
*offset = surface->buffer_stack_offset;
 
surface->buffer_stack_offset += size;
return cogl_object_ref (surface->buffer_stack);
}
 
 
static CoglAttributeBuffer *
_cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
cairo_traps_t *traps,
size_t *offset,
gboolean one_shot)
{
CoglAttributeBuffer *buffer;
int n_traps = traps->num_traps;
int i;
CoglVertexP2 *triangles;
 
if (one_shot) {
buffer = _cairo_cogl_surface_allocate_buffer_space (surface,
n_traps * sizeof (CoglVertexP2) * 6,
offset,
(void **)&triangles);
if (!buffer)
return NULL;
} else {
buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL);
if (!buffer)
return NULL;
triangles = cogl_buffer_map (COGL_BUFFER (buffer),
COGL_BUFFER_ACCESS_WRITE,
COGL_BUFFER_MAP_HINT_DISCARD);
if (!triangles)
return NULL;
*offset = 0;
}
 
/* XXX: This is can be very expensive. I'm not sure a.t.m if it's
* predominantly the bandwidth required or the cost of the fixed_to_float
* conversions but either way we should try using an index buffer to
* reduce the amount we upload by 1/3 (offset by allocating and uploading
* indices though) sadly though my experience with the intel mesa drivers
* is that slow paths can easily be hit when starting to use indices.
*/
for (i = 0; i < n_traps; i++)
{
CoglVertexP2 *p = &triangles[i * 6];
cairo_trapezoid_t *trap = &traps->traps[i];
 
p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
 
p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x);
p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y);
 
p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
 
p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
 
p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
 
p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x);
p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y);
}
 
if (!one_shot)
cogl_buffer_unmap (COGL_BUFFER (buffer));
 
return buffer;
}
 
/* Used for solid fills, in this case we just need a mesh made of
* a single (2-component) position attribute. */
static CoglPrimitive *
_cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface,
cairo_traps_t *traps,
gboolean one_shot)
{
size_t offset;
CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
CoglAttribute *pos = cogl_attribute_new (buffer,
"cogl_position_in",
sizeof (CoglVertexP2),
offset,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglPrimitive *prim;
 
/* The attribute will have taken a reference on the buffer */
cogl_object_unref (buffer);
 
prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
traps->num_traps * 6, pos, NULL);
 
/* The primitive will now keep the attribute alive... */
cogl_object_unref (pos);
 
return prim;
}
 
/* Used for surface fills, in this case we need a mesh made of a single
* (2-component) position attribute + we also alias the same attribute as
* (2-component) texture coordinates */
static CoglPrimitive *
_cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface,
cairo_traps_t *traps,
gboolean one_shot)
{
size_t offset;
CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
CoglAttribute *pos = cogl_attribute_new (buffer,
"cogl_position_in",
sizeof (CoglVertexP2),
offset,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglAttribute *tex_coords = cogl_attribute_new (buffer,
"cogl_tex_coord0_in",
sizeof (CoglVertexP2),
0,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglPrimitive *prim;
 
/* The attributes will have taken references on the buffer */
cogl_object_unref (buffer);
 
prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
traps->num_traps * 6, pos, tex_coords, NULL);
 
/* The primitive will now keep the attributes alive... */
cogl_object_unref (pos);
cogl_object_unref (tex_coords);
 
return prim;
}
 
static CoglPrimitive *
_cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
cairo_traps_t *traps,
int n_layers,
gboolean one_shot)
{
int n_traps = traps->num_traps;
int i;
 
/* XXX: Ideally we would skip tessellating to traps entirely since
* given their representation, conversion to triangles is quite expensive.
*
* This simplifies the conversion to triangles by making the end points of
* the two side lines actually just correspond to the corners of the
* traps.
*/
for (i = 0; i < n_traps; i++)
_sanitize_trap (&traps->traps[i]);
 
if (n_layers == 0)
return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot);
else {
assert (n_layers == 1);
return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot);
}
}
 
static cairo_int_status_t
_cairo_cogl_fill_to_primitive (cairo_cogl_surface_t *surface,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
int n_layers,
cairo_bool_t one_shot,
CoglPrimitive **primitive,
size_t *size)
{
cairo_traps_t traps;
cairo_int_status_t status;
 
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
if (unlikely (status))
goto BAIL;
 
if (traps.num_traps == 0) {
status = CAIRO_INT_STATUS_NOTHING_TO_DO;
goto BAIL;
}
 
*size = traps.num_traps * sizeof (CoglVertexP2) * 6;
 
*primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
if (!*primitive) {
status = CAIRO_INT_STATUS_NO_MEMORY;
goto BAIL;
}
 
BAIL:
_cairo_traps_fini (&traps);
return status;
}
 
static void
_cairo_cogl_clip_push_box (const cairo_box_t *box)
{
if (_cairo_box_is_pixel_aligned (box)) {
cairo_rectangle_int_t rect;
_cairo_box_round_to_rectangle (box, &rect);
cogl_clip_push_window_rectangle (rect.x, rect.y,
rect.width, rect.height);
} else {
double x1, y1, x2, y2;
_cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
cogl_clip_push_rectangle (x1, y1, x2, y2);
}
}
 
static void
_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
{
GList *l;
int clip_stack_depth = 0;
int i;
 
if (!surface->journal)
return;
 
if (surface->buffer_stack && surface->buffer_stack_offset) {
cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
cogl_object_unref (surface->buffer_stack);
surface->buffer_stack = NULL;
}
 
cogl_set_framebuffer (surface->framebuffer);
 
cogl_push_matrix ();
 
for (l = surface->journal->head; l; l = l->next) {
cairo_cogl_journal_entry_t *entry = l->data;
 
switch (entry->type)
{
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
cairo_cogl_journal_clip_entry_t *clip_entry =
(cairo_cogl_journal_clip_entry_t *)entry;
cairo_clip_path_t *path;
#if 0
cairo_bool_t checked_for_primitives = FALSE;
cairo_cogl_clip_primitives_t *clip_primitives;
#endif
 
for (i = 0; i < clip_stack_depth; i++)
cogl_clip_pop ();
clip_stack_depth = 0;
 
for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
cairo_rectangle_int_t extents;
cairo_int_status_t status;
CoglPrimitive *prim;
size_t prim_size;
 
_cairo_path_fixed_approximate_clip_extents (&path->path, &extents);
 
/* TODO - maintain a fifo of the last 10 used clips with cached
* primitives to see if we can avoid tesselating the path and
* uploading the vertices...
*/
#if 0
if (!checked_for_primitives) {
clip_primitives = find_clip_primitives (clip);
checked_for_primitives = TRUE;
}
if (clip_primitives)
prim = clip_primitives->primitives[i];
#endif
status = _cairo_cogl_fill_to_primitive (surface,
&path->path,
path->fill_rule,
path->tolerance,
0,
TRUE,
&prim,
&prim_size);
if (unlikely (status)) {
g_warning ("Failed to get primitive for clip path while flushing journal");
continue;
}
clip_stack_depth++;
cogl_clip_push_primitive (prim,
extents.x, extents.y,
extents.x + extents.width,
extents.y + extents.height);
cogl_object_unref (prim);
}
 
for (i = 0; i < clip_entry->clip->num_boxes; i++) {
clip_stack_depth++;
_cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]);
}
 
surface->n_clip_updates_per_frame++;
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
cairo_cogl_journal_rect_entry_t *rect_entry =
(cairo_cogl_journal_rect_entry_t *)entry;
float tex_coords[8];
float x1 = rect_entry->x;
float y1 = rect_entry->y;
float x2 = rect_entry->x + rect_entry->width;
float y2 = rect_entry->y + rect_entry->height;
cairo_matrix_t *ctm = &rect_entry->ctm;
float ctmfv[16] = {
ctm->xx, ctm->yx, 0, 0,
ctm->xy, ctm->yy, 0, 0,
0, 0, 1, 0,
ctm->x0, ctm->y0, 0, 1
};
CoglMatrix transform;
 
cogl_matrix_init_from_array (&transform, ctmfv);
 
if (rect_entry->n_layers) {
g_assert (rect_entry->n_layers <= 2);
tex_coords[0] = x1;
tex_coords[1] = y1;
tex_coords[2] = x2;
tex_coords[3] = y2;
if (rect_entry->n_layers > 1)
memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
}
 
cogl_set_source (rect_entry->pipeline);
cogl_push_matrix ();
cogl_transform (&transform);
cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
tex_coords, 4 * rect_entry->n_layers);
cogl_pop_matrix ();
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
cairo_cogl_journal_prim_entry_t *prim_entry =
(cairo_cogl_journal_prim_entry_t *)entry;
CoglMatrix transform;
 
cogl_push_matrix ();
if (prim_entry->has_transform) {
cairo_matrix_t *ctm = &prim_entry->transform;
float ctmfv[16] = {
ctm->xx, ctm->yx, 0, 0,
ctm->xy, ctm->yy, 0, 0,
0, 0, 1, 0,
ctm->x0, ctm->y0, 0, 1
};
cogl_matrix_init_from_array (&transform, ctmfv);
cogl_transform (&transform);
} else {
cogl_matrix_init_identity (&transform);
cogl_set_modelview_matrix (&transform);
}
 
cogl_set_source (prim_entry->pipeline);
cogl_primitive_draw (prim_entry->primitive);
cogl_pop_matrix ();
break;
}
case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
cairo_cogl_journal_path_entry_t *path_entry =
(cairo_cogl_journal_path_entry_t *)entry;
 
cogl_set_source (path_entry->pipeline);
cogl_path_fill (path_entry->path);
break;
}
default:
assert (0); /* not reached! */
}
}
 
cogl_pop_matrix ();
 
for (i = 0; i < clip_stack_depth; i++)
cogl_clip_pop ();
 
_cairo_cogl_journal_discard (surface);
}
 
static cairo_status_t
_cairo_cogl_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
_cairo_cogl_journal_flush (surface);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_surface_finish (void *abstract_surface)
{
cairo_cogl_surface_t *surface = abstract_surface;
 
if (surface->texture)
cogl_object_unref (surface->texture);
 
if (surface->framebuffer)
cogl_object_unref (surface->framebuffer);
 
if (surface->journal)
_cairo_cogl_journal_free (surface);
 
/*XXX wtf */
cairo_device_release (surface->base.device);
 
return CAIRO_STATUS_SUCCESS;
}
 
static CoglPixelFormat
get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
 
/* XXX: We often use RGBA format for onscreen framebuffers so make sure
* to handle CAIRO_FORMAT_INVALID sensibly */
static cairo_format_t
get_cairo_format_from_cogl_format (CoglPixelFormat format)
{
switch ((int)format)
{
case COGL_PIXEL_FORMAT_A_8:
return CAIRO_FORMAT_A8;
case COGL_PIXEL_FORMAT_RGB_565:
return CAIRO_FORMAT_RGB16_565;
 
case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
/* Note: this is ambiguous since CAIRO_FORMAT_RGB24
* would also map to the same CoglPixelFormat */
return CAIRO_FORMAT_ARGB32;
 
default:
g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
format,
format & COGL_A_BIT,
format & COGL_BGR_BIT,
format & COGL_PREMULT_BIT,
format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT));
return CAIRO_FORMAT_INVALID;
}
}
 
static CoglPixelFormat
get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
{
switch (cairo_format)
{
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
#else
return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
#endif
case CAIRO_FORMAT_A8:
return COGL_PIXEL_FORMAT_A_8;
case CAIRO_FORMAT_RGB16_565:
return COGL_PIXEL_FORMAT_RGB_565;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_A1:
case CAIRO_FORMAT_RGB30:
return 0;
}
 
g_warn_if_reached ();
return 0;
}
 
static cairo_status_t
_cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t *surface,
cairo_rectangle_int_t *interest,
cairo_image_surface_t **image_out)
{
cairo_image_surface_t *image;
cairo_status_t status;
cairo_format_t cairo_format;
CoglPixelFormat cogl_format;
 
/* TODO: Add cogl_texture_get_region() API so we don't have to ensure the
* surface is bound to an fbo to read back pixels */
status = _cairo_cogl_surface_ensure_framebuffer (surface);
if (unlikely (status))
return status;
 
cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format);
if (cairo_format == CAIRO_FORMAT_INVALID) {
cairo_format = CAIRO_FORMAT_ARGB32;
cogl_format = get_cogl_format_from_cairo_format (cairo_format);
} else {
cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer);
}
 
image = (cairo_image_surface_t *)
cairo_image_surface_create (cairo_format, surface->width, surface->height);
if (image->base.status)
return image->base.status;
 
/* TODO: Add cogl_framebuffer_read_pixels() API */
cogl_push_framebuffer (surface->framebuffer);
cogl_read_pixels (0, 0, surface->width, surface->height,
COGL_READ_PIXELS_COLOR_BUFFER,
cogl_format,
image->data);
cogl_pop_framebuffer ();
 
*image_out = image;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_cogl_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_cogl_surface_t *surface = abstract_surface;
cairo_status_t status;
 
if (surface->texture) {
cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format);
cairo_image_surface_t *image = (cairo_image_surface_t *)
cairo_image_surface_create (format, surface->width, surface->height);
if (image->base.status)
return image->base.status;
 
cogl_texture_get_data (surface->texture,
cogl_texture_get_format (surface->texture),
0,
image->data);
 
image->base.is_clear = FALSE;
*image_out = image;
} else {
cairo_rectangle_int_t extents = {
0, 0, surface->width, surface->height
};
status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents,
image_out);
if (unlikely (status))
return status;
}
 
*image_extra = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_cogl_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_cogl_surface_clear (cairo_cogl_surface_t *surface,
const cairo_color_t *color)
{
/* Anything batched in the journal up until now is redundant... */
_cairo_cogl_journal_discard (surface);
 
/* XXX: we currently implicitly clear the depth and stencil buffer here
* but since we use the framebuffer_discard extension when available I
* suppose this doesn't matter too much.
*
* The main concern is that we want to avoid re-loading an external z
* buffer at the start of each frame, but also many gpu architectures have
* optimizations for how they handle the depth/stencil buffers and can get
* upset if they aren't cleared together at the start of the frame.
*
* FIXME: we need a way to assert that the clip stack currently isn't
* using the stencil buffer before clearing it here!
*/
cogl_framebuffer_clear4f (surface->framebuffer,
COGL_BUFFER_BIT_COLOR |
COGL_BUFFER_BIT_DEPTH |
COGL_BUFFER_BIT_STENCIL,
color->red * color->alpha,
color->green * color->alpha,
color->blue * color->alpha,
color->alpha);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y,
cairo_fixed_t width,
cairo_fixed_t height)
{
cairo_status_t status;
 
status = _cairo_path_fixed_move_to (path, x, y);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_rel_line_to (path, width, 0);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_rel_line_to (path, 0, height);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_rel_line_to (path, -width, 0);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_close_path (path);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_cogl_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_cogl_surface_t *surface;
cairo_path_fixed_t path;
cairo_status_t status;
cairo_matrix_t identity;
 
if (clip == NULL) {
if (op == CAIRO_OPERATOR_CLEAR)
return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
(op == CAIRO_OPERATOR_SOURCE ||
(op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) {
return _cairo_cogl_surface_clear (abstract_surface,
&((cairo_solid_pattern_t *) source)->color);
}
}
 
/* fallback to handling the paint in terms of a fill... */
 
surface = abstract_surface;
 
_cairo_path_fixed_init (&path);
 
status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height);
if (unlikely (status))
goto BAIL;
 
#ifdef NEED_COGL_CONTEXT
/* XXX: in cairo-cogl-context.c we set some sideband data on the
* surface before issuing a fill so we need to do that here too... */
surface->user_path = &path;
cairo_matrix_init_identity (&identity);
surface->ctm = &identity;
surface->ctm_inverse = &identity;
surface->path_is_rectangle = TRUE;
surface->path_rectangle_x = 0;
surface->path_rectangle_y = 0;
surface->path_rectangle_width = surface->width;
surface->path_rectangle_height = surface->height;
#endif
 
status = _cairo_cogl_surface_fill (abstract_surface,
op,
source,
&path,
CAIRO_FILL_RULE_WINDING,
1,
CAIRO_ANTIALIAS_DEFAULT,
clip);
BAIL:
_cairo_path_fixed_fini (&path);
return status;
}
 
static CoglPipelineWrapMode
get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
{
switch (extend_mode)
{
case CAIRO_EXTEND_NONE:
return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
case CAIRO_EXTEND_PAD:
return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
case CAIRO_EXTEND_REPEAT:
return COGL_PIPELINE_WRAP_MODE_REPEAT;
case CAIRO_EXTEND_REFLECT:
/* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */
return CAIRO_EXTEND_REPEAT;
}
assert (0); /* not reached */
return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
}
 
#if 0
/* Given an arbitrary texture, check if it's already a pot texture and simply
* return it back if so. If not create a new pot texture, scale the old to
* fill it, unref the old and return a pointer to the new pot texture. */
static cairo_int_status_t
_cairo_cogl_get_pot_texture (CoglContext *context,
CoglTexture *texture,
CoglTexture **pot_texture)
{
int width = cogl_texture_get_width (texture);
int height = cogl_texture_get_height (texture);
int pot_width;
int pot_height;
CoglHandle offscreen = NULL;
CoglTexture2D *pot = NULL;
GError *error;
 
pot_width = _cairo_cogl_util_next_p2 (width);
pot_height = _cairo_cogl_util_next_p2 (height);
 
if (pot_width == width && pot_height == height)
return CAIRO_INT_STATUS_SUCCESS;
 
for (;;) {
error = NULL;
pot = cogl_texture_2d_new_with_size (context,
pot_width,
pot_height,
cogl_texture_get_format (texture),
&error);
if (pot)
break;
else
g_error_free (error);
 
if (pot_width > pot_height)
pot_width >>= 1;
else
pot_height >>= 1;
 
if (!pot_width || !pot_height)
break;
}
 
*pot_texture = COGL_TEXTURE (pot);
 
if (!pot)
return CAIRO_INT_STATUS_NO_MEMORY;
 
/* Use the GPU to do a bilinear filtered scale from npot to pot... */
offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot));
error = NULL;
if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) {
/* NB: if we don't pass an error then Cogl is allowed to simply abort
* automatically. */
g_error_free (error);
cogl_object_unref (pot);
*pot_texture = NULL;
return CAIRO_INT_STATUS_NO_MEMORY;
}
 
cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
cogl_set_source_texture (texture);
cogl_rectangle (-1, 1, 1, -1);
cogl_pop_framebuffer ();
 
cogl_object_unref (offscreen);
}
#endif
 
/* NB: a reference for the texture is transferred to the caller which should
* be unrefed */
static CoglTexture *
_cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t *reference_surface,
cairo_surface_t *abstract_surface)
{
cairo_image_surface_t *image;
cairo_image_surface_t *acquired_image = NULL;
void *image_extra;
CoglPixelFormat format;
cairo_image_surface_t *image_clone = NULL;
CoglTexture2D *texture;
GError *error = NULL;
cairo_surface_t *clone;
 
if (abstract_surface->device == reference_surface->base.device) {
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
_cairo_cogl_surface_flush (surface, 0);
return surface->texture ? cogl_object_ref (surface->texture) : NULL;
}
 
if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) {
if (_cairo_surface_is_subsurface (abstract_surface)) {
cairo_cogl_surface_t *surface;
 
surface = (cairo_cogl_surface_t *)
_cairo_surface_subsurface_get_target (abstract_surface);
if (surface->base.device == reference_surface->base.device)
return surface->texture ? cogl_object_ref (surface->texture) : NULL;
}
}
 
clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend);
if (clone) {
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone;
return surface->texture ? cogl_object_ref (surface->texture) : NULL;
}
 
g_warning ("Uploading image surface to texture");
 
if (_cairo_surface_is_image (abstract_surface)) {
image = (cairo_image_surface_t *)abstract_surface;
} else {
cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface,
&acquired_image, &image_extra);
if (unlikely (status)) {
g_warning ("acquire_source_image failed: %s [%d]\n",
cairo_status_to_string (status), status);
return NULL;
}
image = acquired_image;
}
 
format = get_cogl_format_from_cairo_format (image->format);
if (!format)
{
image_clone = _cairo_image_surface_coerce (image);
if (unlikely (image_clone->base.status)) {
g_warning ("image_surface_coerce failed");
texture = NULL;
goto BAIL;
}
 
format = get_cogl_format_from_cairo_format (image_clone->format);
assert (format);
}
 
texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
image->width,
image->height,
format, /* incoming */
format, /* desired */
image->stride,
image->data,
&error);
if (!texture) {
g_warning ("Failed to allocate texture: %s", error->message);
g_error_free (error);
goto BAIL;
}
 
clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
reference_surface->ignore_alpha,
NULL, COGL_TEXTURE (texture));
 
_cairo_surface_attach_snapshot (abstract_surface, clone, NULL);
 
/* Attaching the snapshot will take a reference on the clone surface... */
cairo_surface_destroy (clone);
 
BAIL:
if (image_clone)
cairo_surface_destroy (&image_clone->base);
if (acquired_image)
_cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra);
 
return COGL_TEXTURE (texture);
}
 
/* NB: a reference for the texture is transferred to the caller which should
* be unrefed */
static CoglTexture *
_cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
cairo_cogl_surface_t *destination,
const cairo_rectangle_int_t *extents,
const cairo_rectangle_int_t *sample,
cairo_cogl_texture_attributes_t *attributes)
{
CoglTexture *texture = NULL;
 
switch ((int)pattern->type)
{
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
texture = _cairo_cogl_acquire_surface_texture (destination, surface);
if (!texture)
return NULL;
 
/* XXX: determine if it would have no effect to change the
* extend mode to EXTEND_PAD instead since we can simply map
* EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader
* tricks or extra border texels. */
#if 0
/* TODO: We still need to consider HW such as SGX which doesn't have
* full support for NPOT textures. */
if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) {
_cairo_cogl_get_pot_texture ();
}
#endif
 
cairo_matrix_init_identity (&attributes->matrix);
 
/* Convert from un-normalized source coordinates in backend
* coordinates to normalized texture coordinates */
cairo_matrix_scale (&attributes->matrix,
1.0f / cogl_texture_get_width (texture),
1.0f / cogl_texture_get_height (texture));
 
/* XXX: need to multiply in the pattern->matrix */
 
attributes->extend = pattern->extend;
attributes->filter = CAIRO_FILTER_BILINEAR;
attributes->has_component_alpha = pattern->has_component_alpha;
 
attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
attributes->t_wrap = attributes->s_wrap;
 
return texture;
}
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH: {
cairo_surface_t *surface;
cairo_matrix_t texture_matrix;
 
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
extents->width, extents->height);
if (_cairo_surface_offset_paint (surface,
extents->x, extents->y,
CAIRO_OPERATOR_SOURCE,
pattern, NULL)) {
cairo_surface_destroy (surface);
return NULL;
}
 
texture = _cairo_cogl_acquire_surface_texture (destination, surface);
if (!texture)
goto BAIL;
 
cairo_matrix_init_identity (&texture_matrix);
 
/* Convert from un-normalized source coordinates in backend
* coordinates to normalized texture coordinates */
cairo_matrix_scale (&texture_matrix,
1.0f / cogl_texture_get_width (texture),
1.0f / cogl_texture_get_height (texture));
 
cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y);
 
attributes->matrix = texture_matrix;
attributes->extend = pattern->extend;
attributes->filter = CAIRO_FILTER_NEAREST;
attributes->has_component_alpha = pattern->has_component_alpha;
 
/* any pattern extend modes have already been dealt with... */
attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
attributes->t_wrap = attributes->s_wrap;
 
BAIL:
cairo_surface_destroy (surface);
 
return texture;
}
case CAIRO_PATTERN_TYPE_LINEAR: {
cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern;
cairo_cogl_linear_gradient_t *gradient;
cairo_cogl_linear_texture_entry_t *linear_texture;
cairo_int_status_t status;
float a, b;
float dist;
float scale;
float angle;
 
status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
pattern->extend,
linear_pattern->base.n_stops,
linear_pattern->base.stops,
&gradient);
if (unlikely (status))
return NULL;
 
linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
 
attributes->extend = pattern->extend;
attributes->filter = CAIRO_FILTER_BILINEAR;
attributes->has_component_alpha = pattern->has_component_alpha;
attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT;
 
cairo_matrix_init_identity (&attributes->matrix);
 
a = linear_pattern->pd2.x - linear_pattern->pd1.x;
b = linear_pattern->pd2.y - linear_pattern->pd1.y;
dist = sqrtf (a*a + b*b);
scale = 1.0f / dist;
angle = - atan2f (b, a);
 
cairo_matrix_rotate (&attributes->matrix, angle);
cairo_matrix_scale (&attributes->matrix, scale, scale);
 
cairo_matrix_translate (&attributes->matrix,
-linear_pattern->pd1.x,
-linear_pattern->pd1.y);
 
/* XXX: this caught me out: cairo doesn't follow the standard
* maths convention for multiplying two matrices A x B - cairo
* does B x A so the final matrix is as if A's transforms were
* applied first.
*/
cairo_matrix_multiply (&attributes->matrix,
&pattern->matrix,
&attributes->matrix);
 
return cogl_object_ref (linear_texture->texture);
}
default:
g_warning ("Un-supported source type");
return NULL;
}
}
 
static void
set_layer_texture_with_attributes (CoglPipeline *pipeline,
int layer_index,
CoglTexture *texture,
cairo_cogl_texture_attributes_t *attributes)
{
cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
 
if (!_cairo_matrix_is_identity (&attributes->matrix)) {
cairo_matrix_t *m = &attributes->matrix;
float texture_matrixfv[16] = {
m->xx, m->yx, 0, 0,
m->xy, m->yy, 0, 0,
0, 0, 1, 0,
m->x0, m->y0, 0, 1
};
CoglMatrix texture_matrix;
cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv);
cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix);
}
 
if (attributes->s_wrap != attributes->t_wrap) {
cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap);
cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap);
} else
cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap);
}
 
static CoglPipeline *
get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
const cairo_pattern_t *source,
cairo_operator_t op,
cairo_cogl_surface_t *destination,
cairo_composite_rectangles_t *extents)
{
cairo_cogl_template_type template_type;
CoglPipeline *pipeline;
 
switch ((int)source->type)
{
case CAIRO_PATTERN_TYPE_SOLID:
template_type = mask ?
CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID;
break;
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
template_type = mask ?
CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
break;
default:
g_warning ("Un-supported source type");
return NULL;
}
 
pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]);
 
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
cogl_pipeline_set_color4f (pipeline,
solid_pattern->color.red * solid_pattern->color.alpha,
solid_pattern->color.green * solid_pattern->color.alpha,
solid_pattern->color.blue * solid_pattern->color.alpha,
solid_pattern->color.alpha);
} else {
cairo_cogl_texture_attributes_t attributes;
CoglTexture *texture =
_cairo_cogl_acquire_pattern_texture (source, destination,
&extents->bounded,
&extents->source_sample_area,
&attributes);
if (!texture)
goto BAIL;
set_layer_texture_with_attributes (pipeline, 0, texture, &attributes);
cogl_object_unref (texture);
}
 
if (mask) {
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask;
CoglColor color;
cogl_color_init_from_4f (&color,
solid_pattern->color.red * solid_pattern->color.alpha,
solid_pattern->color.green * solid_pattern->color.alpha,
solid_pattern->color.blue * solid_pattern->color.alpha,
solid_pattern->color.alpha);
cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
} else {
cairo_cogl_texture_attributes_t attributes;
CoglTexture *texture =
_cairo_cogl_acquire_pattern_texture (mask, destination,
&extents->bounded,
&extents->mask_sample_area,
&attributes);
if (!texture)
goto BAIL;
set_layer_texture_with_attributes (pipeline, 1, texture, &attributes);
cogl_object_unref (texture);
}
}
 
return pipeline;
 
BAIL:
cogl_object_unref (pipeline);
return NULL;
}
 
#if 0
CoglPrimitive *
_cairo_cogl_rectangle_new_p2t2t2 (float x,
float y,
float width,
float height)
{
CoglVertexP2 vertices[] = {
{x, y}, {x, y + height}, {x + width, y + height},
{x, y}, {x + width, y + height}, {x + width, y}
};
CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices));
CoglAttribute *pos = cogl_attribute_new (buffer,
"cogl_position_in",
sizeof (CoglVertexP2),
0,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
"cogl_tex_coord0_in",
sizeof (CoglVertexP2),
0,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
"cogl_tex_coord0_in",
sizeof (CoglVertexP2),
0,
2,
COGL_ATTRIBUTE_TYPE_FLOAT);
CoglPrimitive *prim;
 
cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices));
 
/* The attributes will now keep the buffer alive... */
cogl_object_unref (buffer);
 
prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
6, pos, tex_coords, NULL);
 
/* The primitive will now keep the attribute alive... */
cogl_object_unref (pos);
 
return prim;
}
#endif
 
static void
_cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
const cairo_clip_t *clip)
{
if (!_cairo_clip_equal (clip, surface->last_clip)) {
_cairo_cogl_journal_log_clip (surface, clip);
_cairo_clip_destroy (surface->last_clip);
surface->last_clip = _cairo_clip_copy (clip);
}
}
 
static void
_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
cairo_composite_rectangles_t *composite)
{
cairo_clip_t *clip = composite->clip;
 
if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
clip = NULL;
 
if (clip == NULL) {
if (_cairo_composite_rectangles_can_reduce_clip (composite,
surface->last_clip))
return;
}
 
_cairo_cogl_log_clip (surface, clip);
}
 
static cairo_bool_t
is_operator_supported (cairo_operator_t op)
{
switch ((int)op) {
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_ADD:
return TRUE;
 
default:
return FALSE;
}
}
 
static cairo_int_status_t
_cairo_cogl_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_cogl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_status_t status;
CoglPipeline *pipeline;
cairo_matrix_t identity;
 
/* XXX: Use this to smoke test the acquire_source/dest_image fallback
* paths... */
//return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (!is_operator_supported (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_composite_rectangles_init_for_mask (&extents,
&surface->base,
op, source, mask, clip);
if (unlikely (status))
return status;
 
pipeline = get_source_mask_operator_destination_pipeline (mask, source,
op, surface, &extents);
if (!pipeline){
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto BAIL;
}
 
_cairo_cogl_maybe_log_clip (surface, &extents);
 
cairo_matrix_init_identity (&identity);
_cairo_cogl_journal_log_rectangle (surface, pipeline,
extents.bounded.x,
extents.bounded.y,
extents.bounded.width,
extents.bounded.height,
2,
&identity);
 
/* The journal will take a reference on the pipeline and clip_path... */
cogl_object_unref (pipeline);
 
BAIL:
return status;
}
 
static int
_cairo_cogl_source_n_layers (const cairo_pattern_t *source)
{
switch ((int)source->type)
{
case CAIRO_PATTERN_TYPE_SOLID:
return 0;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_SURFACE:
return 1;
default:
g_warning ("Unsupported source type");
return 0;
}
}
 
static cairo_bool_t
_cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b)
{
const cairo_cogl_path_fill_meta_t *meta0 = key_a;
const cairo_cogl_path_fill_meta_t *meta1 = key_b;
 
return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
}
 
static cairo_bool_t
_cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
const cairo_stroke_style_t *b)
{
if (a->line_width == b->line_width &&
a->line_cap == b->line_cap &&
a->line_join == b->line_join &&
a->miter_limit == b->miter_limit &&
a->num_dashes == b->num_dashes &&
a->dash_offset == b->dash_offset)
{
unsigned int i;
for (i = 0; i < a->num_dashes; i++) {
if (a->dash[i] != b->dash[i])
return FALSE;
}
}
return TRUE;
}
 
static cairo_bool_t
_cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b)
{
const cairo_cogl_path_stroke_meta_t *meta0 = key_a;
const cairo_cogl_path_stroke_meta_t *meta1 = key_b;
 
return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) &&
_cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
}
 
static cairo_cogl_path_stroke_meta_t *
_cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
 
_cairo_reference_count_inc (&meta->ref_count);
 
return meta;
}
 
static void
_cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
 
if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
return;
 
_cairo_path_fixed_fini (meta->user_path);
free (meta->user_path);
 
_cairo_stroke_style_fini (&meta->style);
 
if (meta->prim)
cogl_object_unref (meta->prim);
 
free (meta);
}
 
static cairo_cogl_path_stroke_meta_t *
_cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t *ctx,
unsigned long hash,
cairo_path_fixed_t *user_path,
const cairo_stroke_style_t *style,
double tolerance)
{
cairo_cogl_path_stroke_meta_t *ret;
cairo_cogl_path_stroke_meta_t lookup;
 
lookup.cache_entry.hash = hash;
lookup.user_path = user_path;
lookup.style = *style;
lookup.tolerance = tolerance;
 
ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry);
if (!ret)
ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry);
return ret;
}
 
static void
_cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
cairo_cogl_path_stroke_meta_t *meta,
size_t size)
{
/* now that we know the meta structure is associated with a primitive
* we promote it from the staging cache into the primitive cache.
*/
 
/* XXX: _cairo_cache borks if you try and remove an entry that's already
* been evicted so we explicitly look it up first... */
if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) {
_cairo_cogl_path_stroke_meta_reference (meta);
_cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry);
}
 
meta->cache_entry.size = size;
if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) !=
CAIRO_STATUS_SUCCESS)
_cairo_cogl_path_stroke_meta_destroy (meta);
}
 
static unsigned int
_cairo_cogl_stroke_style_hash (unsigned int hash,
const cairo_stroke_style_t *style)
{
unsigned int i;
hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width));
hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap));
hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join));
hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit));
hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes));
hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset));
for (i = 0; i < style->num_dashes; i++)
hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double));
return hash;
}
 
static cairo_cogl_path_stroke_meta_t *
_cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface,
const cairo_stroke_style_t *style,
double tolerance)
{
unsigned long hash;
cairo_cogl_path_stroke_meta_t *meta = NULL;
cairo_path_fixed_t *meta_path = NULL;
cairo_status_t status;
 
if (!surface->user_path)
return NULL;
 
hash = _cairo_path_fixed_hash (surface->user_path);
hash = _cairo_cogl_stroke_style_hash (hash, style);
hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
 
meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash,
surface->user_path, style, tolerance);
if (meta)
return meta;
 
meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t));
if (!meta)
goto BAIL;
CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
meta->cache_entry.hash = hash;
meta->counter = 0;
meta_path = malloc (sizeof (cairo_path_fixed_t));
if (!meta_path)
goto BAIL;
/* FIXME: we should add a ref-counted wrapper for our user_paths
* so we don't have to keep copying them here! */
status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
if (unlikely (status))
goto BAIL;
meta->user_path = meta_path;
meta->ctm_inverse = *surface->ctm_inverse;
 
status = _cairo_stroke_style_init_copy (&meta->style, style);
if (unlikely (status)) {
_cairo_path_fixed_fini (meta_path);
goto BAIL;
}
meta->tolerance = tolerance;
 
return meta;
 
BAIL:
free (meta_path);
free (meta);
return NULL;
}
 
static cairo_int_status_t
_cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t *surface,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
int n_layers,
cairo_bool_t one_shot,
CoglPrimitive **primitive,
size_t *size)
{
cairo_traps_t traps;
cairo_int_status_t status;
 
_cairo_traps_init (&traps);
 
status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
ctm, ctm_inverse,
tolerance,
&traps);
if (unlikely (status))
goto BAIL;
 
if (traps.num_traps == 0) {
status = CAIRO_INT_STATUS_NOTHING_TO_DO;
goto BAIL;
}
 
*size = traps.num_traps * sizeof (CoglVertexP2) * 6;
 
//g_print ("new stroke prim\n");
*primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
if (!*primitive) {
status = CAIRO_INT_STATUS_NO_MEMORY;
goto BAIL;
}
 
BAIL:
_cairo_traps_fini (&traps);
return status;
}
 
static cairo_int_status_t
_cairo_cogl_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
cairo_composite_rectangles_t extents;
CoglPipeline *pipeline;
cairo_status_t status;
#ifdef ENABLE_PATH_CACHE
cairo_cogl_path_stroke_meta_t *meta = NULL;
cairo_matrix_t transform_matrix;
#endif
cairo_matrix_t *transform = NULL;
gboolean one_shot = TRUE;
CoglPrimitive *prim = NULL;
cairo_bool_t new_prim = FALSE;
 
if (! is_operator_supported (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* FIXME - support unbounded operators */
if (!_cairo_operator_bounded_by_mask (op)) {
/* Currently IN this is the only unbounded operator we aim to support
* in cairo-cogl. */
assert (op == CAIRO_OPERATOR_IN);
g_warning ("FIXME: handle stroking with unbounded operators!");
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = _cairo_composite_rectangles_init_for_stroke (&extents,
&surface->base,
op, source, path,
style,
ctm,
clip);
if (unlikely (status))
return status;
 
#ifdef ENABLE_PATH_CACHE
/* FIXME: we are currently leaking the meta state if we don't reach
* the cache_insert at the end. */
meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance);
if (meta) {
prim = meta->prim;
if (prim) {
cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
transform = &transform_matrix;
} else if (meta->counter++ > 10)
one_shot = FALSE;
}
#endif
 
if (!prim) {
int n_layers = _cairo_cogl_source_n_layers (source);
size_t prim_size = 0;
status = _cairo_cogl_stroke_to_primitive (surface, path, style,
ctm, ctm_inverse, tolerance,
n_layers, one_shot,
&prim, &prim_size);
if (unlikely (status))
return status;
new_prim = TRUE;
#if defined (ENABLE_PATH_CACHE)
if (meta) {
meta->prim = cogl_object_ref (prim);
_cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
}
#endif
}
 
pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
op, surface, &extents);
if (!pipeline)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_cogl_maybe_log_clip (surface, &extents);
 
_cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
 
/* The journal will take a reference on the pipeline and primitive... */
cogl_object_unref (pipeline);
if (new_prim)
cogl_object_unref (prim);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_cogl_path_fill_meta_t *
_cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
 
_cairo_reference_count_inc (&meta->ref_count);
 
return meta;
}
 
static void
_cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
 
if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
return;
 
_cairo_path_fixed_fini (meta->user_path);
free (meta->user_path);
 
if (meta->prim)
cogl_object_unref (meta->prim);
 
free (meta);
}
 
static cairo_cogl_path_fill_meta_t *
_cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t *ctx,
unsigned long hash,
cairo_path_fixed_t *user_path)
{
cairo_cogl_path_fill_meta_t *ret;
cairo_cogl_path_fill_meta_t lookup;
 
lookup.cache_entry.hash = hash;
lookup.user_path = user_path;
 
ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry);
if (!ret)
ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry);
return ret;
}
 
static void
_cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface,
cairo_cogl_path_fill_meta_t *meta,
size_t size)
{
/* now that we know the meta structure is associated with a primitive
* we promote it from the staging cache into the primitive cache.
*/
 
/* XXX: _cairo_cache borks if you try and remove an entry that's already
* been evicted so we explicitly look it up first... */
if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) {
_cairo_cogl_path_fill_meta_reference (meta);
_cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry);
}
 
meta->cache_entry.size = size;
if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) !=
CAIRO_STATUS_SUCCESS)
_cairo_cogl_path_fill_meta_destroy (meta);
}
 
static cairo_cogl_path_fill_meta_t *
_cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
{
unsigned long hash;
cairo_cogl_path_fill_meta_t *meta = NULL;
cairo_path_fixed_t *meta_path = NULL;
cairo_status_t status;
 
if (!surface->user_path)
return NULL;
 
hash = _cairo_path_fixed_hash (surface->user_path);
 
meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device),
hash, surface->user_path);
if (meta)
return meta;
 
meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t));
if (!meta)
goto BAIL;
meta->cache_entry.hash = hash;
meta->counter = 0;
CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
meta_path = malloc (sizeof (cairo_path_fixed_t));
if (!meta_path)
goto BAIL;
/* FIXME: we should add a ref-counted wrapper for our user_paths
* so we don't have to keep copying them here! */
status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
if (unlikely (status))
goto BAIL;
meta->user_path = meta_path;
meta->ctm_inverse = *surface->ctm_inverse;
 
/* To start with - until we associate a CoglPrimitive with the meta
* structure - we keep the meta in a staging structure until we
* see whether it actually gets re-used. */
meta->cache_entry.size = 1;
if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) !=
CAIRO_STATUS_SUCCESS)
_cairo_cogl_path_fill_meta_destroy (meta);
 
return meta;
 
BAIL:
free (meta_path);
free (meta);
return NULL;
}
 
static cairo_int_status_t
_cairo_cogl_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_cogl_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_status_t status;
#ifdef ENABLE_PATH_CACHE
cairo_cogl_path_fill_meta_t *meta = NULL;
cairo_matrix_t transform_matrix;
#endif
cairo_matrix_t *transform = NULL;
cairo_bool_t one_shot = TRUE;
CoglPrimitive *prim = NULL;
cairo_bool_t new_prim = FALSE;
CoglPipeline *pipeline;
 
if (! is_operator_supported (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* FIXME - support unbounded operators */
if (!_cairo_operator_bounded_by_mask (op)) {
/* Currently IN this is the only unbounded operator we aim to support
* in cairo-cogl. */
assert (op == CAIRO_OPERATOR_IN);
g_warning ("FIXME: handle filling with unbounded operators!");
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = _cairo_composite_rectangles_init_for_fill (&extents,
&surface->base,
op, source, path,
clip);
if (unlikely (status))
return status;
 
#ifndef FILL_WITH_COGL_PATH
#ifdef ENABLE_PATH_CACHE
meta = _cairo_cogl_get_path_fill_meta (surface);
if (meta) {
prim = meta->prim;
if (prim) {
cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
transform = &transform_matrix;
} else if (meta->counter++ > 10)
one_shot = FALSE;
}
#endif /* ENABLE_PATH_CACHE */
 
if (!prim) {
int n_layers = _cairo_cogl_source_n_layers (source);
size_t prim_size;
status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance,
one_shot, n_layers, &prim, &prim_size);
if (unlikely (status))
return status;
new_prim = TRUE;
#ifdef ENABLE_PATH_CACHE
if (meta) {
meta->prim = cogl_object_ref (prim);
_cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
}
#endif /* ENABLE_PATH_CACHE */
}
 
#endif /* !FILL_WITH_COGL_PATH */
 
pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
op, surface, &extents);
if (!pipeline)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_cogl_maybe_log_clip (surface, &extents);
 
#ifndef FILL_WITH_COGL_PATH
_cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
/* The journal will take a reference on the prim */
if (new_prim)
cogl_object_unref (prim);
#else
CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
_cairo_cogl_journal_log_path (surface, pipeline, cogl_path);
cogl_object_unref (cogl_path);
#endif
 
/* The journal will take a reference on the pipeline... */
cogl_object_unref (pipeline);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_cogl_surface_fill_rectangle (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
double x,
double y,
double width,
double height,
cairo_matrix_t *ctm,
const cairo_clip_t *clip)
{
cairo_cogl_surface_t *surface = abstract_surface;
CoglPipeline *pipeline;
 
if (! is_operator_supported (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* FIXME - support unbounded operators */
if (!_cairo_operator_bounded_by_mask (op)) {
/* Currently IN this is the only unbounded operator we aim to support
* in cairo-cogl. */
assert (op == CAIRO_OPERATOR_IN);
g_warning ("FIXME: handle filling with unbounded operators!");
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* FIXME */
#if 0
status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents,
&surface->base,
op, source, path,
clip);
if (unlikely (status))
return status;
#endif
 
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
double x1 = x;
double y1 = y;
double x2 = x1 + width;
double y2 = y1 + height;
 
pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
op, surface, NULL);
if (!pipeline)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_cogl_log_clip (surface, clip);
 
_cairo_cogl_journal_log_rectangle (surface,
pipeline,
x1, y1, x2, y2,
0,
ctm);
return CAIRO_INT_STATUS_SUCCESS;
} else
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* TODO:
* We need to acquire the textures here, look at the corresponding
* attributes and see if this can be trivially handled by logging
* a textured rectangle only needing simple scaling or translation
* of texture coordinates.
*
* At this point we should also aim to remap the default
* EXTEND_NONE mode to EXTEND_PAD which is more efficient if we
* know it makes no difference either way since we can map that to
* CLAMP_TO_EDGE.
*/
}
 
static cairo_int_status_t
_cairo_cogl_surface_show_glyphs (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
const cairo_surface_backend_t _cairo_cogl_surface_backend = {
CAIRO_SURFACE_TYPE_COGL,
_cairo_cogl_surface_finish,
#ifdef NEED_COGL_CONTEXT
_cairo_cogl_context_create,
#else
_cairo_default_context_create,
#endif
 
_cairo_cogl_surface_create_similar,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
_cairo_cogl_surface_acquire_source_image,
_cairo_cogl_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_cogl_surface_get_extents,
NULL, /* get_font_options */
 
_cairo_cogl_surface_flush, /* flush */
NULL, /* mark_dirty_rectangle */
 
_cairo_cogl_surface_paint,
_cairo_cogl_surface_mask,
_cairo_cogl_surface_stroke,
_cairo_cogl_surface_fill,
NULL, /* fill_stroke*/
_cairo_surface_fallback_glyphs,
};
 
static cairo_surface_t *
_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
cairo_bool_t ignore_alpha,
CoglFramebuffer *framebuffer,
CoglTexture *texture)
{
cairo_cogl_surface_t *surface;
cairo_status_t status;
 
status = cairo_device_acquire (&dev->base);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
surface = malloc (sizeof (cairo_cogl_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface->ignore_alpha = ignore_alpha;
 
surface->framebuffer = framebuffer;
if (framebuffer) {
surface->width = cogl_framebuffer_get_width (framebuffer);
surface->height = cogl_framebuffer_get_height (framebuffer);
surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer);
cogl_object_ref (framebuffer);
}
 
/* FIXME: If texture == NULL and we are given an offscreen framebuffer
* then we want a way to poke inside the framebuffer to get a texture */
surface->texture = texture;
if (texture) {
if (!framebuffer) {
surface->width = cogl_texture_get_width (texture);
surface->height = cogl_texture_get_height (texture);
surface->cogl_format = cogl_texture_get_format (texture);
}
cogl_object_ref (texture);
}
 
assert(surface->width && surface->height);
 
surface->journal = NULL;
 
surface->buffer_stack = NULL;
surface->buffer_stack_size = 4096;
 
surface->last_clip = NULL;
 
surface->n_clip_updates_per_frame = 0;
 
_cairo_surface_init (&surface->base,
&_cairo_cogl_surface_backend,
&dev->base,
CAIRO_CONTENT_COLOR_ALPHA);
 
return &surface->base;
}
 
cairo_surface_t *
cairo_cogl_surface_create (cairo_device_t *abstract_device,
CoglFramebuffer *framebuffer)
{
cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
if (abstract_device == NULL)
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
 
if (abstract_device->status)
return _cairo_surface_create_in_error (abstract_device->status);
 
if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL);
}
slim_hidden_def (cairo_cogl_surface_create);
 
CoglFramebuffer *
cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
{
cairo_cogl_surface_t *surface;
 
if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
surface = (cairo_cogl_surface_t *) abstract_surface;
 
return surface->framebuffer;
}
slim_hidden_def (cairo_cogl_surface_get_framebuffer);
 
CoglTexture *
cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface)
{
cairo_cogl_surface_t *surface;
 
if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
surface = (cairo_cogl_surface_t *) abstract_surface;
 
return surface->texture;
}
slim_hidden_def (cairo_cogl_surface_get_texture);
 
static cairo_status_t
_cairo_cogl_device_flush (void *device)
{
cairo_status_t status;
 
status = cairo_device_acquire (device);
if (unlikely (status))
return status;
 
/* XXX: we don't need to flush Cogl here, we just need to flush
* any batching we do of compositing primitives. */
 
cairo_device_release (device);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_cogl_device_finish (void *device)
{
cairo_status_t status;
 
status = cairo_device_acquire (device);
if (unlikely (status))
return;
 
/* XXX: Drop references to external resources */
 
cairo_device_release (device);
}
 
static void
_cairo_cogl_device_destroy (void *device)
{
cairo_cogl_device_t *dev = device;
 
/* FIXME: Free stuff! */
 
g_free (dev);
}
 
static const cairo_device_backend_t _cairo_cogl_device_backend = {
CAIRO_DEVICE_TYPE_COGL,
 
NULL, /* lock */
NULL, /* unlock */
 
_cairo_cogl_device_flush,
_cairo_cogl_device_finish,
_cairo_cogl_device_destroy,
};
 
static cairo_bool_t
set_blend (CoglPipeline *pipeline, const char *blend_string)
{
GError *error = NULL;
if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) {
g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
g_error_free (error);
return FALSE;
}
return TRUE;
}
 
static cairo_bool_t
_cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
{
cairo_bool_t status = FALSE;
 
switch ((int)op)
{
case CAIRO_OPERATOR_SOURCE:
status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)");
break;
case CAIRO_OPERATOR_OVER:
status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
break;
case CAIRO_OPERATOR_IN:
status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)");
break;
case CAIRO_OPERATOR_DEST_OVER:
status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)");
break;
case CAIRO_OPERATOR_DEST_IN:
status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
break;
case CAIRO_OPERATOR_ADD:
status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
break;
}
 
return status;
}
 
static void
create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op)
{
CoglPipeline *base = cogl_pipeline_new ();
CoglPipeline *pipeline;
CoglColor color;
 
if (!_cairo_cogl_setup_op_state (base, op)) {
cogl_object_unref (base);
return;
}
 
dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
 
pipeline = cogl_pipeline_copy (base);
cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline;
 
pipeline = cogl_pipeline_copy (base);
cogl_pipeline_set_layer_combine (pipeline, 1,
"RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
NULL);
cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline;
 
pipeline = cogl_pipeline_copy (base);
cogl_pipeline_set_layer_combine (pipeline, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline;
}
 
cairo_device_t *
cairo_cogl_device_create (CoglContext *cogl_context)
{
cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1);
cairo_status_t status;
 
dev->backend_vtable_initialized = FALSE;
 
dev->cogl_context = cogl_context;
 
dev->dummy_texture = cogl_texture_new_with_size (1, 1,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY);
if (!dev->dummy_texture)
goto ERROR;
 
memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines));
create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE);
create_templates_for_op (dev, CAIRO_OPERATOR_OVER);
create_templates_for_op (dev, CAIRO_OPERATOR_IN);
create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER);
create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN);
create_templates_for_op (dev, CAIRO_OPERATOR_ADD);
 
status = _cairo_cache_init (&dev->linear_cache,
_cairo_cogl_linear_gradient_equal,
NULL,
(cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy,
CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE);
if (unlikely (status))
return _cairo_device_create_in_error(status);
 
status = _cairo_cache_init (&dev->path_fill_staging_cache,
_cairo_cogl_path_fill_meta_equal,
NULL,
(cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
1000);
 
status = _cairo_cache_init (&dev->path_stroke_staging_cache,
_cairo_cogl_path_stroke_meta_equal,
NULL,
(cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
1000);
 
status = _cairo_cache_init (&dev->path_fill_prim_cache,
_cairo_cogl_path_fill_meta_equal,
NULL,
(cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
CAIRO_COGL_PATH_META_CACHE_SIZE);
 
status = _cairo_cache_init (&dev->path_stroke_prim_cache,
_cairo_cogl_path_stroke_meta_equal,
NULL,
(cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
CAIRO_COGL_PATH_META_CACHE_SIZE);
 
_cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
return &dev->base;
 
ERROR:
g_free (dev);
return NULL;
}
slim_hidden_def (cairo_cogl_device_create);
 
void
cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
{
cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
cairo_surface_flush (abstract_surface);
 
//g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame);
surface->n_clip_updates_per_frame = 0;
}
slim_hidden_def (cairo_cogl_surface_end_frame);
/programs/develop/libraries/cairo/src/cairo-cogl-utils-private.h
0,0 → 1,54
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#ifndef CAIRO_COGL_UTILS_PRIVATE_H
#define CAIRO_COGL_UTILS_PRIVATE_H
 
#include "cairo-path-fixed-private.h"
#include <cogl/cogl2-experimental.h>
 
CoglPath *
_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
float tolerance);
 
int
_cairo_cogl_util_next_p2 (int a);
 
#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS))
 
static inline float
_cairo_cogl_util_fixed_to_float (cairo_fixed_t f)
{
return ((float) f) / CAIRO_FIXED_ONE_FLOAT;
}
 
#endif /* CAIRO_COGL_UTILS_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-cogl-utils.c
0,0 → 1,126
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.og/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.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#include "cairoint.h"
#include "cairo-cogl-utils-private.h"
 
#include <cogl/cogl.h>
#include <glib.h>
 
static cairo_status_t
_cogl_move_to (void *closure,
const cairo_point_t *point)
{
cogl_path_move_to (closure,
_cairo_cogl_util_fixed_to_float (point->x),
_cairo_cogl_util_fixed_to_float (point->y));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cogl_line_to (void *closure,
const cairo_point_t *point)
{
cogl_path_line_to (closure,
_cairo_cogl_util_fixed_to_float (point->x),
_cairo_cogl_util_fixed_to_float (point->y));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cogl_curve_to (void *closure,
const cairo_point_t *p0,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
cogl_path_curve_to (closure,
_cairo_cogl_util_fixed_to_float (p0->x),
_cairo_cogl_util_fixed_to_float (p0->y),
_cairo_cogl_util_fixed_to_float (p1->x),
_cairo_cogl_util_fixed_to_float (p1->y),
_cairo_cogl_util_fixed_to_float (p2->x),
_cairo_cogl_util_fixed_to_float (p2->y));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cogl_close_path (void *closure)
{
cogl_path_close (closure);
return CAIRO_STATUS_SUCCESS;
}
 
CoglPath *
_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
float tolerance)
{
CoglPath *cogl_path = cogl_path_new ();
cairo_status_t status;
 
if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD)
cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_EVEN_ODD);
else
cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_NON_ZERO);
 
#ifdef USE_CAIRO_PATH_FLATTENER
/* XXX: rely on cairo to do path flattening, since it seems Cogl's
* curve_to flattening is much slower */
status = _cairo_path_fixed_interpret_flat (path,
_cogl_move_to,
_cogl_line_to,
_cogl_close_path,
cogl_path,
tolerance);
#else
status = _cairo_path_fixed_interpret (path,
_cogl_move_to,
_cogl_line_to,
_cogl_curve_to,
_cogl_close_path,
cogl_path);
#endif
 
assert (status == CAIRO_STATUS_SUCCESS);
return cogl_path;
}
 
int
_cairo_cogl_util_next_p2 (int a)
{
int rval = 1;
 
while (rval < a)
rval <<= 1;
 
return rval;
}
 
/programs/develop/libraries/cairo/src/cairo-cogl.h
0,0 → 1,69
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
*
* Contributor(s):
* Robert Bragg <robert@linux.intel.com>
*/
 
#ifndef CAIRO_VG_H
#define CAIRO_VG_H
 
#include "cairo.h"
 
#if CAIRO_HAS_COGL_SURFACE
 
#include <cogl/cogl2-experimental.h>
 
CAIRO_BEGIN_DECLS
 
cairo_public cairo_device_t *
cairo_cogl_device_create (CoglContext *context);
 
cairo_public cairo_surface_t *
cairo_cogl_surface_create (cairo_device_t *device,
CoglFramebuffer *framebuffer);
 
cairo_public CoglFramebuffer *
cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
 
cairo_public CoglTexture *
cairo_cogl_surface_get_texture (cairo_surface_t *surface);
 
cairo_public void
cairo_cogl_surface_end_frame (cairo_surface_t *surface);
 
CAIRO_END_DECLS
 
#else /* CAIRO_HAS_COGL_SURFACE*/
# error Cairo was not compiled with support for the Cogl backend
#endif /* CAIRO_HAS_COGL_SURFACE*/
 
#endif /* CAIRO_COGL_H */
/programs/develop/libraries/cairo/src/cairo-color.c
77,19 → 77,6
}
}
 
void
_cairo_color_init (cairo_color_t *color)
{
*color = cairo_color_white;
}
 
void
_cairo_color_init_rgb (cairo_color_t *color,
double red, double green, double blue)
{
_cairo_color_init_rgba (color, red, green, blue, 1.0);
}
 
/* Convert a double in [0.0, 1.0] to an integer in [0, 65535]
* The conversion is designed to divide the input range into 65536
* equally-sized regions. This is achieved by multiplying by 65536 and
/programs/develop/libraries/cairo/src/cairo-combsort-inline.h
0,0 → 1,94
/*
* Copyright © 2008 Chris Wilson
*
* 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 Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* This fragment implements a comb sort (specifically combsort11) */
#ifndef _HAVE_CAIRO_COMBSORT_NEWGAP
#define _HAVE_CAIRO_COMBSORT_NEWGAP
static inline unsigned int
_cairo_combsort_newgap (unsigned int gap)
{
gap = 10 * gap / 13;
if (gap == 9 || gap == 10)
gap = 11;
if (gap < 1)
gap = 1;
return gap;
}
#endif
 
#define CAIRO_COMBSORT_DECLARE(NAME, TYPE, CMP) \
static void \
NAME (TYPE *base, unsigned int nmemb) \
{ \
unsigned int gap = nmemb; \
unsigned int i, j; \
int swapped; \
do { \
gap = _cairo_combsort_newgap (gap); \
swapped = gap > 1; \
for (i = 0; i < nmemb-gap ; i++) { \
j = i + gap; \
if (CMP (base[i], base[j]) > 0 ) { \
TYPE tmp; \
tmp = base[i]; \
base[i] = base[j]; \
base[j] = tmp; \
swapped = 1; \
} \
} \
} while (swapped); \
}
 
#define CAIRO_COMBSORT_DECLARE_WITH_DATA(NAME, TYPE, CMP) \
static void \
NAME (TYPE *base, unsigned int nmemb, void *data) \
{ \
unsigned int gap = nmemb; \
unsigned int i, j; \
int swapped; \
do { \
gap = _cairo_combsort_newgap (gap); \
swapped = gap > 1; \
for (i = 0; i < nmemb-gap ; i++) { \
j = i + gap; \
if (CMP (base[i], base[j], data) > 0 ) { \
TYPE tmp; \
tmp = base[i]; \
base[i] = base[j]; \
base[j] = tmp; \
swapped = 1; \
} \
} \
} while (swapped); \
}
/programs/develop/libraries/cairo/src/cairo-compiler-private.h
114,7 → 114,9
 
/* slim_internal.h */
#define CAIRO_HAS_HIDDEN_SYMBOLS 1
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && defined(__ELF__) && !defined(__sun)
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && \
(defined(__ELF__) || defined(__APPLE__)) && \
!defined(__sun)
#define cairo_private_no_warn __attribute__((__visibility__("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
#define cairo_private_no_warn __hidden
181,17 → 183,8
#endif
 
#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
#define _CAIRO_BOOLEAN_EXPR(expr) \
__extension__ ({ \
int _cairo_boolean_var_; \
if (expr) \
_cairo_boolean_var_ = 1; \
else \
_cairo_boolean_var_ = 0; \
_cairo_boolean_var_; \
})
#define likely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 1))
#define unlikely(expr) (__builtin_expect (_CAIRO_BOOLEAN_EXPR(expr), 0))
#define likely(expr) (__builtin_expect (!!(expr), 1))
#define unlikely(expr) (__builtin_expect (!!(expr), 0))
#else
#define likely(expr) (expr)
#define unlikely(expr) (expr)
203,31 → 196,23
#endif
 
#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
#define access _access
#define fdopen _fdopen
#define hypot _hypot
#define pclose _pclose
#define popen _popen
#define snprintf _snprintf
#define popen _popen
#define pclose _pclose
#define hypot _hypot
#define strdup _strdup
#define unlink _unlink
#define vsnprintf _vsnprintf
#endif
 
#ifdef _MSC_VER
#ifndef __cplusplus
#undef inline
#define inline __inline
 
/* Add a definition of ffs */
#include <intrin.h>
#pragma intrinsic(_BitScanForward)
static __forceinline int
ffs (int x)
{
unsigned long i;
 
if (_BitScanForward(&i, x) != 0)
return i + 1;
 
return 0;
}
 
#endif
#endif
 
#if defined(_MSC_VER) && defined(_M_IX86)
/* When compiling with /Gy and /OPT:ICF identical functions will be folded in together.
/programs/develop/libraries/cairo/src/cairo-composite-rectangles-private.h
38,6 → 38,8
#define CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H
 
#include "cairo-types-private.h"
#include "cairo-error-private.h"
#include "cairo-pattern-private.h"
 
CAIRO_BEGIN_DECLS
 
51,55 → 53,107
*
*/
struct _cairo_composite_rectangles {
cairo_surface_t *surface;
cairo_operator_t op;
 
cairo_rectangle_int_t source;
cairo_rectangle_int_t mask;
cairo_rectangle_int_t bounded; /* dst */
cairo_rectangle_int_t unbounded; /* clip */
cairo_rectangle_int_t destination;
 
cairo_rectangle_int_t bounded; /* source? IN mask? IN unbounded */
cairo_rectangle_int_t unbounded; /* destination IN clip */
uint32_t is_bounded;
 
cairo_rectangle_int_t source_sample_area;
cairo_rectangle_int_t mask_sample_area;
 
cairo_pattern_union_t source_pattern;
cairo_pattern_union_t mask_pattern;
const cairo_pattern_t *original_source_pattern;
const cairo_pattern_t *original_mask_pattern;
 
cairo_clip_t *clip; /* clip will be reduced to the minimal container */
};
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_clip_t *clip);
const cairo_path_fixed_t *path,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_boxes_t *boxes,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_polygon_t *polygon,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip,
const cairo_clip_t *clip,
cairo_bool_t *overlap);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
const cairo_box_t *box);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
const cairo_box_t *box);
 
cairo_private cairo_bool_t
_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
cairo_boxes_t *damage);
 
cairo_private void
_cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-composite-rectangles.c
35,65 → 35,116
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-error-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-pattern-private.h"
 
/* A collection of routines to facilitate writing compositors. */
 
void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents)
{
_cairo_clip_destroy (extents->clip);
}
 
static void
_cairo_composite_reduce_pattern (const cairo_pattern_t *src,
cairo_pattern_union_t *dst)
{
int tx, ty;
 
_cairo_pattern_init_static_copy (&dst->base, src);
if (dst->base.type == CAIRO_PATTERN_TYPE_SOLID)
return;
 
dst->base.filter = _cairo_pattern_analyze_filter (&dst->base, NULL),
 
tx = ty = 0;
if (_cairo_matrix_is_pixman_translation (&dst->base.matrix,
dst->base.filter,
&tx, &ty))
{
dst->base.matrix.x0 = tx;
dst->base.matrix.y0 = ty;
}
}
 
static inline cairo_bool_t
_cairo_composite_rectangles_init (cairo_composite_rectangles_t *extents,
int width, int height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
extents->unbounded.x = extents->unbounded.y = 0;
extents->unbounded.width = width;
extents->unbounded.height = height;
if (_cairo_clip_is_all_clipped (clip))
return FALSE;
 
if (clip != NULL) {
const cairo_rectangle_int_t *clip_extents;
extents->surface = surface;
extents->op = op;
 
clip_extents = _cairo_clip_get_extents (clip);
if (clip_extents == NULL)
return FALSE;
_cairo_surface_get_extents (surface, &extents->destination);
extents->clip = NULL;
 
if (! _cairo_rectangle_intersect (&extents->unbounded, clip_extents))
extents->unbounded = extents->destination;
if (clip && ! _cairo_rectangle_intersect (&extents->unbounded,
_cairo_clip_get_extents (clip)))
return FALSE;
}
 
extents->bounded = extents->unbounded;
extents->is_bounded = _cairo_operator_bounded_by_either (op);
 
_cairo_pattern_get_extents (source, &extents->source);
extents->original_source_pattern = source;
_cairo_composite_reduce_pattern (source, &extents->source_pattern);
 
_cairo_pattern_get_extents (&extents->source_pattern.base,
&extents->source);
if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE) {
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source))
return FALSE;
}
 
extents->original_mask_pattern = NULL;
extents->mask_pattern.base.type = CAIRO_PATTERN_TYPE_SOLID;
extents->mask_pattern.solid.color.alpha = 1.; /* XXX full initialisation? */
extents->mask_pattern.solid.color.alpha_short = 0xffff;
 
return TRUE;
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
if (! _cairo_composite_rectangles_init (extents,
surface_width, surface_height,
op, source, clip))
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
extents->mask = extents->bounded;
extents->mask = extents->destination;
 
extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
if (_cairo_clip_is_all_clipped (extents->clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (! _cairo_rectangle_intersect (&extents->unbounded,
_cairo_clip_get_extents (extents->clip)))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
_cairo_pattern_sampled_area (&extents->source_pattern.base,
&extents->bounded,
&extents->source_sample_area);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents)
_cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents,
const cairo_clip_t *clip)
{
cairo_bool_t ret;
 
101,42 → 152,203
if (! ret && extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
extents->unbounded = extents->bounded;
} else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
if (_cairo_clip_is_all_clipped (extents->clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (! _cairo_rectangle_intersect (&extents->unbounded,
_cairo_clip_get_extents (extents->clip)))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (! _cairo_rectangle_intersect (&extents->bounded,
_cairo_clip_get_extents (extents->clip)) &&
extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
_cairo_pattern_sampled_area (&extents->source_pattern.base,
&extents->bounded,
&extents->source_sample_area);
if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
_cairo_pattern_sampled_area (&extents->mask_pattern.base,
&extents->bounded,
&extents->mask_sample_area);
if (extents->mask_sample_area.width == 0 ||
extents->mask_sample_area.height == 0) {
_cairo_composite_rectangles_fini (extents);
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_composite_rectangles_intersect_source_extents (cairo_composite_rectangles_t *extents,
const cairo_box_t *box)
{
cairo_rectangle_int_t rect;
cairo_clip_t *clip;
 
_cairo_box_round_to_rectangle (box, &rect);
if (rect.x == extents->source.x &&
rect.y == extents->source.y &&
rect.width == extents->source.width &&
rect.height == extents->source.height)
{
return CAIRO_INT_STATUS_SUCCESS;
}
 
_cairo_rectangle_intersect (&extents->source, &rect);
 
rect = extents->bounded;
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->source) &&
extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (rect.width == extents->bounded.width &&
rect.height == extents->bounded.height)
return CAIRO_INT_STATUS_SUCCESS;
 
if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
extents->unbounded = extents->bounded;
} else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
clip = extents->clip;
extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
if (clip != extents->clip)
_cairo_clip_destroy (clip);
 
if (_cairo_clip_is_all_clipped (extents->clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (! _cairo_rectangle_intersect (&extents->unbounded,
_cairo_clip_get_extents (extents->clip)))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
_cairo_pattern_sampled_area (&extents->source_pattern.base,
&extents->bounded,
&extents->source_sample_area);
if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
_cairo_pattern_sampled_area (&extents->mask_pattern.base,
&extents->bounded,
&extents->mask_sample_area);
if (extents->mask_sample_area.width == 0 ||
extents->mask_sample_area.height == 0)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t *extents,
const cairo_box_t *box)
{
cairo_rectangle_int_t mask;
cairo_clip_t *clip;
 
_cairo_box_round_to_rectangle (box, &mask);
if (mask.x == extents->mask.x &&
mask.y == extents->mask.y &&
mask.width == extents->mask.width &&
mask.height == extents->mask.height)
{
return CAIRO_INT_STATUS_SUCCESS;
}
 
_cairo_rectangle_intersect (&extents->mask, &mask);
 
mask = extents->bounded;
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask) &&
extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (mask.width == extents->bounded.width &&
mask.height == extents->bounded.height)
return CAIRO_INT_STATUS_SUCCESS;
 
if (extents->is_bounded == (CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE)) {
extents->unbounded = extents->bounded;
} else if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK) {
if (!_cairo_rectangle_intersect (&extents->unbounded, &extents->mask))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
clip = extents->clip;
extents->clip = _cairo_clip_reduce_for_composite (clip, extents);
if (clip != extents->clip)
_cairo_clip_destroy (clip);
 
if (_cairo_clip_is_all_clipped (extents->clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (! _cairo_rectangle_intersect (&extents->unbounded,
_cairo_clip_get_extents (extents->clip)))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (extents->source_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
_cairo_pattern_sampled_area (&extents->source_pattern.base,
&extents->bounded,
&extents->source_sample_area);
if (extents->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID) {
_cairo_pattern_sampled_area (&extents->mask_pattern.base,
&extents->bounded,
&extents->mask_sample_area);
if (extents->mask_sample_area.width == 0 ||
extents->mask_sample_area.height == 0)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t*surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
if (! _cairo_composite_rectangles_init (extents,
surface_width, surface_height,
op, source, clip))
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
_cairo_pattern_get_extents (mask, &extents->mask);
extents->original_mask_pattern = mask;
_cairo_composite_reduce_pattern (mask, &extents->mask_pattern);
_cairo_pattern_get_extents (&extents->mask_pattern.base, &extents->mask);
 
return _cairo_composite_rectangles_intersect (extents);
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
if (! _cairo_composite_rectangles_init (extents,
surface_width, surface_height,
op, source, clip))
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
143,20 → 355,19
 
_cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &extents->mask);
 
return _cairo_composite_rectangles_intersect (extents);
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_clip_t *clip)
const cairo_path_fixed_t *path,
const cairo_clip_t *clip)
{
if (! _cairo_composite_rectangles_init (extents,
surface_width, surface_height,
op, source, clip))
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
163,26 → 374,73
 
_cairo_path_fixed_approximate_fill_extents (path, &extents->mask);
 
return _cairo_composite_rectangles_intersect (extents);
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_polygon (cairo_composite_rectangles_t *extents,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_polygon_t *polygon,
const cairo_clip_t *clip)
{
if (! _cairo_composite_rectangles_init (extents,
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
_cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_boxes (cairo_composite_rectangles_t *extents,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_boxes_t *boxes,
const cairo_clip_t *clip)
{
cairo_box_t box;
 
if (! _cairo_composite_rectangles_init (extents,
surface, op, source, clip))
{
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
_cairo_boxes_extents (boxes, &box);
_cairo_box_round_to_rectangle (&box, &extents->mask);
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_int_status_t
_cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *extents,
int surface_width, int surface_height,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip,
const cairo_clip_t *clip,
cairo_bool_t *overlap)
{
cairo_status_t status;
 
if (! _cairo_composite_rectangles_init (extents,
surface_width, surface_height,
op, source, clip))
if (! _cairo_composite_rectangles_init (extents, surface, op, source, clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
/* Computing the exact bbox and the overlap is expensive.
* First perform a cheap test to see if the glyphs are all clipped out.
*/
if (extents->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK &&
_cairo_scaled_font_glyph_approximate_extents (scaled_font,
glyphs, num_glyphs,
&extents->mask))
{
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
193,5 → 451,50
if (unlikely (status))
return status;
 
return _cairo_composite_rectangles_intersect (extents);
if (overlap && *overlap &&
scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE &&
_cairo_pattern_is_opaque_solid (&extents->source_pattern.base))
{
*overlap = FALSE;
}
 
return _cairo_composite_rectangles_intersect (extents, clip);
}
 
cairo_bool_t
_cairo_composite_rectangles_can_reduce_clip (cairo_composite_rectangles_t *composite,
cairo_clip_t *clip)
{
cairo_rectangle_int_t extents;
cairo_box_t box;
 
if (clip == NULL)
return TRUE;
 
extents = composite->destination;
if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_SOURCE)
_cairo_rectangle_intersect (&extents, &composite->source);
if (composite->is_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)
_cairo_rectangle_intersect (&extents, &composite->mask);
 
_cairo_box_from_rectangle (&box, &extents);
return _cairo_clip_contains_box (clip, &box);
}
 
cairo_int_status_t
_cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite,
cairo_boxes_t *damage)
{
cairo_int_status_t status;
int n;
 
for (n = 0; n < composite->clip->num_boxes; n++) {
status = _cairo_boxes_add (damage,
CAIRO_ANTIALIAS_NONE,
&composite->clip->boxes[n]);
if (unlikely (status))
return status;
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-compositor-private.h
0,0 → 1,365
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_COMPOSITOR_PRIVATE_H
#define CAIRO_COMPOSITOR_PRIVATE_H
 
#include "cairo-composite-rectangles-private.h"
 
CAIRO_BEGIN_DECLS
 
typedef struct {
cairo_scaled_font_t *font;
cairo_glyph_t *glyphs;
int num_glyphs;
cairo_bool_t use_mask;
cairo_rectangle_int_t extents;
} cairo_composite_glyphs_info_t;
 
struct cairo_compositor {
const cairo_compositor_t *delegate;
 
cairo_warn cairo_int_status_t
(*paint) (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents);
 
cairo_warn cairo_int_status_t
(*mask) (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents);
 
cairo_warn cairo_int_status_t
(*stroke) (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias);
 
cairo_warn cairo_int_status_t
(*fill) (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias);
 
cairo_warn cairo_int_status_t
(*glyphs) (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap);
};
 
struct cairo_mask_compositor {
cairo_compositor_t base;
 
cairo_int_status_t (*acquire) (void *surface);
cairo_int_status_t (*release) (void *surface);
 
cairo_int_status_t (*set_clip_region) (void *surface,
cairo_region_t *clip_region);
 
cairo_surface_t * (*pattern_to_surface) (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_int_status_t (*draw_image_boxes) (void *surface,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy);
 
cairo_int_status_t (*copy_boxes) (void *surface,
cairo_surface_t *src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy);
 
cairo_int_status_t
(*fill_rectangles) (void *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rectangles,
int num_rects);
 
cairo_int_status_t
(*fill_boxes) (void *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes);
 
cairo_int_status_t
(*check_composite) (const cairo_composite_rectangles_t *extents);
 
cairo_int_status_t
(*composite) (void *dst,
cairo_operator_t op,
cairo_surface_t *src,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height);
 
cairo_int_status_t
(*composite_boxes) (void *surface,
cairo_operator_t op,
cairo_surface_t *source,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents);
 
cairo_int_status_t
(*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs);
cairo_int_status_t
(*composite_glyphs) (void *surface,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info);
};
 
struct cairo_traps_compositor {
cairo_compositor_t base;
 
cairo_int_status_t
(*acquire) (void *surface);
 
cairo_int_status_t
(*release) (void *surface);
 
cairo_int_status_t
(*set_clip_region) (void *surface,
cairo_region_t *clip_region);
 
cairo_surface_t *
(*pattern_to_surface) (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_int_status_t (*draw_image_boxes) (void *surface,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy);
 
cairo_int_status_t (*copy_boxes) (void *surface,
cairo_surface_t *src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy);
 
cairo_int_status_t
(*fill_boxes) (void *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes);
 
cairo_int_status_t
(*check_composite) (const cairo_composite_rectangles_t *extents);
 
cairo_int_status_t
(*composite) (void *dst,
cairo_operator_t op,
cairo_surface_t *src,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height);
cairo_int_status_t
(*lerp) (void *_dst,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height);
 
cairo_int_status_t
(*composite_boxes) (void *surface,
cairo_operator_t op,
cairo_surface_t *source,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents);
 
cairo_int_status_t
(*composite_traps) (void *dst,
cairo_operator_t op,
cairo_surface_t *source,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps);
 
cairo_int_status_t
(*composite_tristrip) (void *dst,
cairo_operator_t op,
cairo_surface_t *source,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *tristrip);
 
cairo_int_status_t
(*check_composite_glyphs) (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs);
cairo_int_status_t
(*composite_glyphs) (void *surface,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info);
};
 
cairo_private extern const cairo_compositor_t __cairo_no_compositor;
cairo_private extern const cairo_compositor_t _cairo_fallback_compositor;
 
cairo_private void
_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
const cairo_compositor_t *delegate);
 
cairo_private void
_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor,
const cairo_compositor_t *delegate);
 
cairo_private void
_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
const cairo_compositor_t *delegate);
 
cairo_private cairo_int_status_t
_cairo_compositor_paint (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_compositor_mask (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_compositor_stroke (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_compositor_fill (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_COMPOSITOR_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-compositor.c
0,0 → 1,268
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-compositor-private.h"
#include "cairo-damage-private.h"
#include "cairo-error-private.h"
 
cairo_int_status_t
_cairo_compositor_paint (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = _cairo_composite_rectangles_init_for_paint (&extents, surface,
op, source,
clip);
if (unlikely (status))
return status;
 
do {
while (compositor->paint == NULL)
compositor = compositor->delegate;
 
status = compositor->paint (compositor, &extents);
 
compositor = compositor->delegate;
} while (status == CAIRO_INT_STATUS_UNSUPPORTED);
 
if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
__FUNCTION__,
extents.unbounded.x, extents.unbounded.y,
extents.unbounded.width, extents.unbounded.height));
surface->damage = _cairo_damage_add_rectangle (surface->damage,
&extents.unbounded);
}
 
_cairo_composite_rectangles_fini (&extents);
 
return status;
}
 
cairo_int_status_t
_cairo_compositor_mask (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = _cairo_composite_rectangles_init_for_mask (&extents, surface,
op, source, mask,
clip);
if (unlikely (status))
return status;
 
do {
while (compositor->mask == NULL)
compositor = compositor->delegate;
 
status = compositor->mask (compositor, &extents);
 
compositor = compositor->delegate;
} while (status == CAIRO_INT_STATUS_UNSUPPORTED);
 
if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
__FUNCTION__,
extents.unbounded.x, extents.unbounded.y,
extents.unbounded.width, extents.unbounded.height));
surface->damage = _cairo_damage_add_rectangle (surface->damage,
&extents.unbounded);
}
 
_cairo_composite_rectangles_fini (&extents);
 
return status;
}
 
cairo_int_status_t
_cairo_compositor_stroke (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (_cairo_pen_vertices_needed (tolerance, style->line_width/2, ctm) <= 1)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
status = _cairo_composite_rectangles_init_for_stroke (&extents, surface,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
 
do {
while (compositor->stroke == NULL)
compositor = compositor->delegate;
 
status = compositor->stroke (compositor, &extents,
path, style, ctm, ctm_inverse,
tolerance, antialias);
 
compositor = compositor->delegate;
} while (status == CAIRO_INT_STATUS_UNSUPPORTED);
 
if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
__FUNCTION__,
extents.unbounded.x, extents.unbounded.y,
extents.unbounded.width, extents.unbounded.height));
surface->damage = _cairo_damage_add_rectangle (surface->damage,
&extents.unbounded);
}
 
_cairo_composite_rectangles_fini (&extents);
 
return status;
}
 
cairo_int_status_t
_cairo_compositor_fill (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = _cairo_composite_rectangles_init_for_fill (&extents, surface,
op, source, path,
clip);
if (unlikely (status))
return status;
 
do {
while (compositor->fill == NULL)
compositor = compositor->delegate;
 
status = compositor->fill (compositor, &extents,
path, fill_rule, tolerance, antialias);
 
compositor = compositor->delegate;
} while (status == CAIRO_INT_STATUS_UNSUPPORTED);
 
if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
__FUNCTION__,
extents.unbounded.x, extents.unbounded.y,
extents.unbounded.width, extents.unbounded.height));
surface->damage = _cairo_damage_add_rectangle (surface->damage,
&extents.unbounded);
}
 
_cairo_composite_rectangles_fini (&extents);
 
return status;
}
 
cairo_int_status_t
_cairo_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_bool_t overlap;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = _cairo_composite_rectangles_init_for_glyphs (&extents, surface,
op, source,
scaled_font,
glyphs, num_glyphs,
clip, &overlap);
if (unlikely (status))
return status;
 
do {
while (compositor->glyphs == NULL)
compositor = compositor->delegate;
 
status = compositor->glyphs (compositor, &extents,
scaled_font, glyphs, num_glyphs, overlap);
 
compositor = compositor->delegate;
} while (status == CAIRO_INT_STATUS_UNSUPPORTED);
 
if (status == CAIRO_INT_STATUS_SUCCESS && surface->damage) {
TRACE ((stderr, "%s: applying damage (%d,%d)x(%d, %d)\n",
__FUNCTION__,
extents.unbounded.x, extents.unbounded.y,
extents.unbounded.width, extents.unbounded.height));
surface->damage = _cairo_damage_add_rectangle (surface->damage,
&extents.unbounded);
}
 
_cairo_composite_rectangles_fini (&extents);
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-contour-inline.h
0,0 → 1,80
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_CONTOUR_INLINE_H
#define CAIRO_CONTOUR_INLINE_H
 
#include "cairo-contour-private.h"
 
CAIRO_BEGIN_DECLS
 
static inline cairo_int_status_t
_cairo_contour_add_point (cairo_contour_t *contour,
const cairo_point_t *point)
{
struct _cairo_contour_chain *tail = contour->tail;
 
if (unlikely (tail->num_points == tail->size_points))
return __cairo_contour_add_point (contour, point);
 
tail->points[tail->num_points++] = *point;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static inline cairo_point_t *
_cairo_contour_first_point (cairo_contour_t *c)
{
return &c->chain.points[0];
}
 
static inline cairo_point_t *
_cairo_contour_last_point (cairo_contour_t *c)
{
return &c->tail->points[c->tail->num_points-1];
}
 
static inline void
_cairo_contour_remove_last_point (cairo_contour_t *contour)
{
if (contour->chain.num_points == 0)
return;
 
if (--contour->tail->num_points == 0)
__cairo_contour_remove_last_chain (contour);
}
 
CAIRO_END_DECLS
 
#endif /* CAIRO_CONTOUR_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-contour-private.h
0,0 → 1,124
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_CONTOUR_PRIVATE_H
#define CAIRO_CONTOUR_PRIVATE_H
 
#include "cairo-types-private.h"
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-list-private.h"
 
#include <stdio.h>
 
CAIRO_BEGIN_DECLS
 
/* A contour is simply a closed chain of points that divide the infinite plane
* into inside and outside. Each contour is a simple polygon, that is it
* contains no holes or self-intersections, but maybe either concave or convex.
*/
 
struct _cairo_contour_chain {
cairo_point_t *points;
int num_points, size_points;
struct _cairo_contour_chain *next;
};
 
struct _cairo_contour_iter {
cairo_point_t *point;
cairo_contour_chain_t *chain;
};
 
struct _cairo_contour {
cairo_list_t next;
int direction;
cairo_contour_chain_t chain, *tail;
 
cairo_point_t embedded_points[64];
};
 
/* Initial definition of a shape is a set of contours (some representing holes) */
struct _cairo_shape {
cairo_list_t contours;
};
 
typedef struct _cairo_shape cairo_shape_t;
 
#if 0
cairo_private cairo_status_t
_cairo_shape_init_from_polygon (cairo_shape_t *shape,
const cairo_polygon_t *polygon);
 
cairo_private cairo_status_t
_cairo_shape_reduce (cairo_shape_t *shape, double tolerance);
#endif
 
cairo_private void
_cairo_contour_init (cairo_contour_t *contour,
int direction);
 
cairo_private cairo_int_status_t
__cairo_contour_add_point (cairo_contour_t *contour,
const cairo_point_t *point);
 
cairo_private void
_cairo_contour_simplify (cairo_contour_t *contour, double tolerance);
 
cairo_private void
_cairo_contour_reverse (cairo_contour_t *contour);
 
cairo_private cairo_int_status_t
_cairo_contour_add (cairo_contour_t *dst,
const cairo_contour_t *src);
 
cairo_private cairo_int_status_t
_cairo_contour_add_reversed (cairo_contour_t *dst,
const cairo_contour_t *src);
 
cairo_private void
__cairo_contour_remove_last_chain (cairo_contour_t *contour);
 
cairo_private void
_cairo_contour_reset (cairo_contour_t *contour);
 
cairo_private void
_cairo_contour_fini (cairo_contour_t *contour);
 
cairo_private void
_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_CONTOUR_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-contour.c
0,0 → 1,456
/*
* Copyright © 2004 Carl Worth
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2008 Chris Wilson
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Carl Worth
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
typedef unsigned long long uint64_t;
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-contour-inline.h"
#include "cairo-contour-private.h"
 
void
_cairo_contour_init (cairo_contour_t *contour,
int direction)
{
contour->direction = direction;
contour->chain.points = contour->embedded_points;
contour->chain.next = NULL;
contour->chain.num_points = 0;
contour->chain.size_points = ARRAY_LENGTH (contour->embedded_points);
contour->tail = &contour->chain;
}
 
cairo_int_status_t
__cairo_contour_add_point (cairo_contour_t *contour,
const cairo_point_t *point)
{
cairo_contour_chain_t *tail = contour->tail;
cairo_contour_chain_t *next;
 
assert (tail->next == NULL);
 
next = _cairo_malloc_ab_plus_c (tail->size_points*2,
sizeof (cairo_point_t),
sizeof (cairo_contour_chain_t));
if (unlikely (next == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
next->size_points = tail->size_points*2;
next->num_points = 1;
next->points = (cairo_point_t *)(next+1);
next->next = NULL;
tail->next = next;
contour->tail = next;
 
next->points[0] = *point;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static void
first_inc (cairo_contour_t *contour,
cairo_point_t **p,
cairo_contour_chain_t **chain)
{
if (*p == (*chain)->points + (*chain)->num_points) {
assert ((*chain)->next);
*chain = (*chain)->next;
*p = &(*chain)->points[0];
} else
++*p;
}
 
static void
last_dec (cairo_contour_t *contour,
cairo_point_t **p,
cairo_contour_chain_t **chain)
{
if (*p == (*chain)->points) {
cairo_contour_chain_t *prev;
assert (*chain != &contour->chain);
for (prev = &contour->chain; prev->next != *chain; prev = prev->next)
;
*chain = prev;
*p = &(*chain)->points[(*chain)->num_points-1];
} else
--*p;
}
 
void
_cairo_contour_reverse (cairo_contour_t *contour)
{
cairo_contour_chain_t *first_chain, *last_chain;
cairo_point_t *first, *last;
 
contour->direction = -contour->direction;
 
if (contour->chain.num_points <= 1)
return;
 
first_chain = &contour->chain;
last_chain = contour->tail;
 
first = &first_chain->points[0];
last = &last_chain->points[last_chain->num_points-1];
 
while (first != last) {
cairo_point_t p;
 
p = *first;
*first = *last;
*last = p;
 
first_inc (contour, &first, &first_chain);
last_dec (contour, &last, &last_chain);
}
}
 
cairo_int_status_t
_cairo_contour_add (cairo_contour_t *dst,
const cairo_contour_t *src)
{
const cairo_contour_chain_t *chain;
cairo_int_status_t status;
int i;
 
for (chain = &src->chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
status = _cairo_contour_add_point (dst, &chain->points[i]);
if (unlikely (status))
return status;
}
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static inline cairo_bool_t
iter_next (cairo_contour_iter_t *iter)
{
if (iter->point == &iter->chain->points[iter->chain->size_points-1]) {
iter->chain = iter->chain->next;
if (iter->chain == NULL)
return FALSE;
 
iter->point = &iter->chain->points[0];
return TRUE;
} else {
iter->point++;
return TRUE;
}
}
 
static cairo_bool_t
iter_equal (const cairo_contour_iter_t *i1,
const cairo_contour_iter_t *i2)
{
return i1->chain == i2->chain && i1->point == i2->point;
}
 
static void
iter_init (cairo_contour_iter_t *iter, cairo_contour_t *contour)
{
iter->chain = &contour->chain;
iter->point = &contour->chain.points[0];
}
 
static void
iter_init_last (cairo_contour_iter_t *iter, cairo_contour_t *contour)
{
iter->chain = contour->tail;
iter->point = &contour->tail->points[contour->tail->num_points-1];
}
 
static const cairo_contour_chain_t *prev_const_chain(const cairo_contour_t *contour,
const cairo_contour_chain_t *chain)
{
const cairo_contour_chain_t *prev;
 
if (chain == &contour->chain)
return NULL;
 
for (prev = &contour->chain; prev->next != chain; prev = prev->next)
;
 
return prev;
}
 
cairo_int_status_t
_cairo_contour_add_reversed (cairo_contour_t *dst,
const cairo_contour_t *src)
{
const cairo_contour_chain_t *last;
cairo_int_status_t status;
int i;
 
if (src->chain.num_points == 0)
return CAIRO_INT_STATUS_SUCCESS;
 
for (last = src->tail; last; last = prev_const_chain (src, last)) {
for (i = last->num_points-1; i >= 0; i--) {
status = _cairo_contour_add_point (dst, &last->points[i]);
if (unlikely (status))
return status;
}
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_uint64_t
point_distance_sq (const cairo_point_t *p1,
const cairo_point_t *p2)
{
int32_t dx = p1->x - p2->x;
int32_t dy = p1->y - p2->y;
return _cairo_int64_add(_cairo_int32x32_64_mul (dx, dx), _cairo_int32x32_64_mul (dy, dy));
}
 
#define DELETED(p) ((p)->x == INT_MIN && (p)->y == INT_MAX)
#define MARK_DELETED(p) ((p)->x = INT_MIN, (p)->y = INT_MAX)
 
static cairo_bool_t
_cairo_contour_simplify_chain (cairo_contour_t *contour, const double tolerance,
const cairo_contour_iter_t *first,
const cairo_contour_iter_t *last)
{
cairo_contour_iter_t iter, furthest;
uint64_t max_error;
int x0, y0;
int nx, ny;
int count;
 
iter = *first;
iter_next (&iter);
if (iter_equal (&iter, last))
return FALSE;
 
x0 = first->point->x;
y0 = first->point->y;
nx = last->point->y - y0;
ny = x0 - last->point->x;
 
count = 0;
max_error = 0;
do {
cairo_point_t *p = iter.point;
if (! DELETED(p)) {
uint64_t d = (uint64_t)nx * (x0 - p->x) + (uint64_t)ny * (y0 - p->y);
if (d * d > max_error) {
max_error = d * d;
furthest = iter;
}
count++;
}
iter_next (&iter);
} while (! iter_equal (&iter, last));
if (count == 0)
return FALSE;
 
if (max_error > tolerance * ((uint64_t)nx * nx + (uint64_t)ny * ny)) {
cairo_bool_t simplified;
 
simplified = FALSE;
simplified |= _cairo_contour_simplify_chain (contour, tolerance,
first, &furthest);
simplified |= _cairo_contour_simplify_chain (contour, tolerance,
&furthest, last);
return simplified;
} else {
iter = *first;
iter_next (&iter);
do {
MARK_DELETED (iter.point);
iter_next (&iter);
} while (! iter_equal (&iter, last));
 
return TRUE;
}
}
 
void
_cairo_contour_simplify (cairo_contour_t *contour, double tolerance)
{
cairo_contour_chain_t *chain;
cairo_point_t *last = NULL;
cairo_contour_iter_t iter, furthest;
cairo_bool_t simplified;
uint64_t max = 0;
int i;
 
if (contour->chain.num_points <= 2)
return;
 
tolerance = tolerance * CAIRO_FIXED_ONE;
tolerance *= tolerance;
 
/* stage 1: vertex reduction */
for (chain = &contour->chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
if (last == NULL ||
_cairo_uint64_to_double(point_distance_sq (last, &chain->points[i])) > tolerance) {
last = &chain->points[i];
} else {
MARK_DELETED (&chain->points[i]);
}
}
}
 
/* stage2: polygon simplification using Douglas-Peucker */
simplified = FALSE;
do {
last = &contour->chain.points[0];
iter_init (&furthest, contour);
max = 0;
for (chain = &contour->chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
uint64_t d;
 
if (DELETED (&chain->points[i]))
continue;
 
d = point_distance_sq (last, &chain->points[i]);
if (d > max) {
furthest.chain = chain;
furthest.point = &chain->points[i];
max = d;
}
}
}
assert (max);
 
simplified = FALSE;
iter_init (&iter, contour);
simplified |= _cairo_contour_simplify_chain (contour, tolerance,
&iter, &furthest);
 
iter_init_last (&iter, contour);
if (! iter_equal (&furthest, &iter))
simplified |= _cairo_contour_simplify_chain (contour, tolerance,
&furthest, &iter);
} while (simplified);
 
iter_init (&iter, contour);
for (chain = &contour->chain; chain; chain = chain->next) {
int num_points = chain->num_points;
chain->num_points = 0;
for (i = 0; i < num_points; i++) {
if (! DELETED(&chain->points[i])) {
if (iter.point != &chain->points[i])
*iter.point = chain->points[i];
iter.chain->num_points++;
iter_next (&iter);
}
}
}
 
if (iter.chain) {
cairo_contour_chain_t *next;
 
for (chain = iter.chain->next; chain; chain = next) {
next = chain->next;
free (chain);
}
 
iter.chain->next = NULL;
contour->tail = iter.chain;
}
}
 
void
_cairo_contour_reset (cairo_contour_t *contour)
{
_cairo_contour_fini (contour);
_cairo_contour_init (contour, contour->direction);
}
 
void
_cairo_contour_fini (cairo_contour_t *contour)
{
cairo_contour_chain_t *chain, *next;
 
for (chain = contour->chain.next; chain; chain = next) {
next = chain->next;
free (chain);
}
}
 
void
_cairo_debug_print_contour (FILE *file, cairo_contour_t *contour)
{
cairo_contour_chain_t *chain;
int num_points, size_points;
int i;
 
num_points = 0;
size_points = 0;
for (chain = &contour->chain; chain; chain = chain->next) {
num_points += chain->num_points;
size_points += chain->size_points;
}
 
fprintf (file, "contour: direction=%d, num_points=%d / %d\n",
contour->direction, num_points, size_points);
 
num_points = 0;
for (chain = &contour->chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
fprintf (file, " [%d] = (%f, %f)\n",
num_points++,
_cairo_fixed_to_double (chain->points[i].x),
_cairo_fixed_to_double (chain->points[i].y));
}
}
}
 
void
__cairo_contour_remove_last_chain (cairo_contour_t *contour)
{
cairo_contour_chain_t *chain;
 
if (contour->tail == &contour->chain)
return;
 
for (chain = &contour->chain; chain->next != contour->tail; chain = chain->next)
;
free (contour->tail);
contour->tail = chain;
chain->next = NULL;
}
/programs/develop/libraries/cairo/src/cairo-damage-private.h
0,0 → 1,85
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_DAMAGE_PRIVATE_H
#define CAIRO_DAMAGE_PRIVATE_H
 
#include "cairo-types-private.h"
 
#include <pixman.h>
 
CAIRO_BEGIN_DECLS
 
struct _cairo_damage {
cairo_status_t status;
cairo_region_t *region;
 
int dirty, remain;
struct _cairo_damage_chunk {
struct _cairo_damage_chunk *next;
cairo_box_t *base;
int count;
int size;
} chunks, *tail;
cairo_box_t boxes[32];
};
 
cairo_private cairo_damage_t *
_cairo_damage_create (void);
 
cairo_private cairo_damage_t *
_cairo_damage_create_in_error (cairo_status_t status);
 
cairo_private cairo_damage_t *
_cairo_damage_add_box (cairo_damage_t *damage,
const cairo_box_t *box);
 
cairo_private cairo_damage_t *
_cairo_damage_add_rectangle (cairo_damage_t *damage,
const cairo_rectangle_int_t *rect);
 
cairo_private cairo_damage_t *
_cairo_damage_add_region (cairo_damage_t *damage,
const cairo_region_t *region);
 
cairo_private cairo_damage_t *
_cairo_damage_reduce (cairo_damage_t *damage);
 
cairo_private void
_cairo_damage_destroy (cairo_damage_t *damage);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_DAMAGE_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-damage.c
0,0 → 1,241
/*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-damage-private.h"
#include "cairo-region-private.h"
 
static const cairo_damage_t __cairo_damage__nil = { CAIRO_STATUS_NO_MEMORY };
 
cairo_damage_t *
_cairo_damage_create_in_error (cairo_status_t status)
{
_cairo_error_throw (status);
return (cairo_damage_t *) &__cairo_damage__nil;
}
 
cairo_damage_t *
_cairo_damage_create (void)
{
cairo_damage_t *damage;
 
damage = malloc (sizeof (*damage));
if (unlikely (damage == NULL)) {
_cairo_error_throw(CAIRO_STATUS_NO_MEMORY);
return (cairo_damage_t *) &__cairo_damage__nil;
}
 
damage->status = CAIRO_STATUS_SUCCESS;
damage->region = NULL;
damage->dirty = 0;
damage->tail = &damage->chunks;
damage->chunks.base = damage->boxes;
damage->chunks.size = ARRAY_LENGTH(damage->boxes);
damage->chunks.count = 0;
damage->chunks.next = NULL;
 
damage->remain = damage->chunks.size;
 
return damage;
}
 
void
_cairo_damage_destroy (cairo_damage_t *damage)
{
struct _cairo_damage_chunk *chunk, *next;
 
if (damage == (cairo_damage_t *) &__cairo_damage__nil)
return;
 
for (chunk = damage->chunks.next; chunk != NULL; chunk = next) {
next = chunk->next;
free (chunk);
}
cairo_region_destroy (damage->region);
free (damage);
}
 
static cairo_damage_t *
_cairo_damage_add_boxes(cairo_damage_t *damage,
const cairo_box_t *boxes,
int count)
{
struct _cairo_damage_chunk *chunk;
int n, size;
 
TRACE ((stderr, "%s x%d\n", __FUNCTION__, count));
 
if (damage == NULL)
damage = _cairo_damage_create ();
if (damage->status)
return damage;
 
damage->dirty += count;
 
n = count;
if (n > damage->remain)
n = damage->remain;
 
memcpy (damage->tail->base + damage->tail->count, boxes,
n * sizeof (cairo_box_t));
 
count -= n;
damage->tail->count += n;
damage->remain -= n;
 
if (count == 0)
return damage;
 
size = 2 * damage->tail->size;
if (size < count)
size = (count + 64) & ~63;
 
chunk = malloc (sizeof (*chunk) + sizeof (cairo_box_t) * size);
if (unlikely (chunk == NULL)) {
_cairo_damage_destroy (damage);
return (cairo_damage_t *) &__cairo_damage__nil;
}
 
chunk->next = NULL;
chunk->base = (cairo_box_t *) (chunk + 1);
chunk->size = size;
chunk->count = count;
 
damage->tail->next = chunk;
damage->tail = chunk;
 
memcpy (damage->tail->base, boxes + n,
count * sizeof (cairo_box_t));
damage->remain = size - count;
 
return damage;
}
 
cairo_damage_t *
_cairo_damage_add_box(cairo_damage_t *damage,
const cairo_box_t *box)
{
TRACE ((stderr, "%s: (%d, %d),(%d, %d)\n", __FUNCTION__,
box->p1.x, box->p1.y, box->p2.x, box->p2.y));
 
return _cairo_damage_add_boxes(damage, box, 1);
}
 
cairo_damage_t *
_cairo_damage_add_rectangle(cairo_damage_t *damage,
const cairo_rectangle_int_t *r)
{
cairo_box_t box;
 
TRACE ((stderr, "%s: (%d, %d)x(%d, %d)\n", __FUNCTION__,
r->x, r->y, r->width, r->height));
 
box.p1.x = r->x;
box.p1.y = r->y;
box.p2.x = r->x + r->width;
box.p2.y = r->y + r->height;
 
return _cairo_damage_add_boxes(damage, &box, 1);
}
 
cairo_damage_t *
_cairo_damage_add_region (cairo_damage_t *damage,
const cairo_region_t *region)
{
cairo_box_t *boxes;
int nbox;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
boxes = _cairo_region_get_boxes (region, &nbox);
return _cairo_damage_add_boxes(damage, boxes, nbox);
}
 
cairo_damage_t *
_cairo_damage_reduce (cairo_damage_t *damage)
{
cairo_box_t *free_boxes = NULL;
cairo_box_t *boxes, *b;
struct _cairo_damage_chunk *chunk, *last;
 
TRACE ((stderr, "%s: dirty=%d\n", __FUNCTION__,
damage ? damage->dirty : -1));
if (damage == NULL || damage->status || !damage->dirty)
return damage;
 
if (damage->region) {
cairo_region_t *region;
 
region = damage->region;
damage->region = NULL;
 
damage = _cairo_damage_add_region (damage, region);
cairo_region_destroy (region);
 
if (unlikely (damage->status))
return damage;
}
 
boxes = damage->tail->base;
if (damage->dirty > damage->tail->size) {
boxes = free_boxes = malloc (damage->dirty * sizeof (cairo_box_t));
if (unlikely (boxes == NULL)) {
_cairo_damage_destroy (damage);
return (cairo_damage_t *) &__cairo_damage__nil;
}
 
b = boxes;
last = NULL;
} else {
b = boxes + damage->tail->count;
last = damage->tail;
}
 
for (chunk = &damage->chunks; chunk != last; chunk = chunk->next) {
memcpy (b, chunk->base, chunk->count * sizeof (cairo_box_t));
b += chunk->count;
}
 
damage->region = _cairo_region_create_from_boxes (boxes, damage->dirty);
free (free_boxes);
 
if (unlikely (damage->region->status)) {
_cairo_damage_destroy (damage);
return (cairo_damage_t *) &__cairo_damage__nil;
}
 
damage->dirty = 0;
return damage;
}
/programs/develop/libraries/cairo/src/cairo-debug.c
34,6 → 34,7
*/
 
#include "cairoint.h"
#include "cairo-image-surface-private.h"
 
/**
* cairo_debug_reset_static_data:
55,6 → 56,8
* functions have been called as necessary). If there are active cairo
* objects, this call is likely to cause a crash, (eg. an assertion
* failure due to a hash table being destroyed when non-empty).
*
* Since: 1.0
**/
void
cairo_debug_reset_static_data (void)
69,6 → 72,10
_cairo_ft_font_reset_static_data ();
#endif
 
#if CAIRO_HAS_WIN32_FONT
_cairo_win32_font_reset_static_data ();
#endif
 
_cairo_intern_string_reset_static_data ();
 
_cairo_scaled_font_reset_static_data ();
83,8 → 90,12
_cairo_drm_device_reset_static_data ();
#endif
 
_cairo_reset_static_data ();
_cairo_default_context_reset_static_data ();
 
#if CAIRO_HAS_COGL_SURFACE
_cairo_cogl_context_reset_static_data ();
#endif
 
CAIRO_MUTEX_FINALIZE ();
}
 
114,6 → 125,7
width = image->width*2;
break;
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_ARGB32:
width = image->width*4;
break;
228,8 → 240,10
_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path)
{
cairo_status_t status;
cairo_box_t box;
 
printf ("path: extents=(%f, %f), (%f, %f)\n",
fprintf (stream,
"path: extents=(%f, %f), (%f, %f)\n",
_cairo_fixed_to_double (path->extents.p1.x),
_cairo_fixed_to_double (path->extents.p1.y),
_cairo_fixed_to_double (path->extents.p2.x),
236,7 → 250,6
_cairo_fixed_to_double (path->extents.p2.y));
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_print_move_to,
_print_line_to,
_print_curve_to,
244,5 → 257,48
stream);
assert (status == CAIRO_STATUS_SUCCESS);
 
if (_cairo_path_fixed_is_box (path, &box)) {
fprintf (stream, "[box (%d, %d), (%d, %d)]",
box.p1.x, box.p1.y, box.p2.x, box.p2.y);
}
 
printf ("\n");
}
 
void
_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon)
{
int n;
 
fprintf (stream,
"polygon: extents=(%f, %f), (%f, %f)\n",
_cairo_fixed_to_double (polygon->extents.p1.x),
_cairo_fixed_to_double (polygon->extents.p1.y),
_cairo_fixed_to_double (polygon->extents.p2.x),
_cairo_fixed_to_double (polygon->extents.p2.y));
if (polygon->num_limits) {
fprintf (stream,
" : limit=(%f, %f), (%f, %f) x %d\n",
_cairo_fixed_to_double (polygon->limit.p1.x),
_cairo_fixed_to_double (polygon->limit.p1.y),
_cairo_fixed_to_double (polygon->limit.p2.x),
_cairo_fixed_to_double (polygon->limit.p2.y),
polygon->num_limits);
}
 
for (n = 0; n < polygon->num_edges; n++) {
cairo_edge_t *edge = &polygon->edges[n];
 
fprintf (stream,
" [%d] = [(%f, %f), (%f, %f)], top=%f, bottom=%f, dir=%d\n",
n,
_cairo_fixed_to_double (edge->line.p1.x),
_cairo_fixed_to_double (edge->line.p1.y),
_cairo_fixed_to_double (edge->line.p2.x),
_cairo_fixed_to_double (edge->line.p2.y),
_cairo_fixed_to_double (edge->top),
_cairo_fixed_to_double (edge->bottom),
edge->dir);
 
}
}
/programs/develop/libraries/cairo/src/cairo-default-context-private.h
0,0 → 1,68
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl D. Worth <cworth@redhat.com>
*/
 
#ifndef CAIRO_DEFAULT_CONTEXT_PRIVATE_H
#define CAIRO_DEFAULT_CONTEXT_PRIVATE_H
 
#include "cairo-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_default_context cairo_default_context_t;
 
struct _cairo_default_context {
cairo_t base;
 
cairo_gstate_t *gstate;
cairo_gstate_t gstate_tail[2];
cairo_gstate_t *gstate_freelist;
 
cairo_path_fixed_t path[1];
};
 
cairo_private cairo_t *
_cairo_default_context_create (void *target);
 
cairo_private cairo_status_t
_cairo_default_context_init (cairo_default_context_t *cr, void *target);
 
cairo_private void
_cairo_default_context_fini (cairo_default_context_t *cr);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-default-context.c
0,0 → 1,1478
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-private.h"
#include "cairo-arc-private.h"
#include "cairo-backend-private.h"
#include "cairo-clip-inline.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
 
#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1)
 
#if !defined(INFINITY)
#define INFINITY HUGE_VAL
#endif
 
static freed_pool_t context_pool;
 
void
_cairo_default_context_reset_static_data (void)
{
_freed_pool_reset (&context_pool);
}
 
void
_cairo_default_context_fini (cairo_default_context_t *cr)
{
while (cr->gstate != &cr->gstate_tail[0]) {
if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
break;
}
 
_cairo_gstate_fini (cr->gstate);
cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */
while (cr->gstate_freelist != NULL) {
cairo_gstate_t *gstate = cr->gstate_freelist;
cr->gstate_freelist = gstate->next;
free (gstate);
}
 
_cairo_path_fixed_fini (cr->path);
 
_cairo_fini (&cr->base);
}
 
static void
_cairo_default_context_destroy (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_default_context_fini (cr);
 
/* mark the context as invalid to protect against misuse */
cr->base.status = CAIRO_STATUS_NULL_POINTER;
_freed_pool_put (&context_pool, cr);
}
 
static cairo_surface_t *
_cairo_default_context_get_original_target (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_original_target (cr->gstate);
}
 
static cairo_surface_t *
_cairo_default_context_get_current_target (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_target (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_save (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
}
 
static cairo_status_t
_cairo_default_context_restore (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
if (unlikely (_cairo_gstate_is_group (cr->gstate)))
return _cairo_error (CAIRO_STATUS_INVALID_RESTORE);
 
return _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
}
 
static cairo_status_t
_cairo_default_context_push_group (void *abstract_cr, cairo_content_t content)
{
cairo_default_context_t *cr = abstract_cr;
cairo_surface_t *group_surface;
cairo_clip_t *clip;
cairo_status_t status;
 
clip = _cairo_gstate_get_clip (cr->gstate);
if (_cairo_clip_is_all_clipped (clip)) {
group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
status = group_surface->status;
if (unlikely (status))
goto bail;
} else {
cairo_surface_t *parent_surface;
cairo_rectangle_int_t extents;
cairo_bool_t bounded, is_empty;
 
parent_surface = _cairo_gstate_get_target (cr->gstate);
 
if (unlikely (parent_surface->status))
return parent_surface->status;
if (unlikely (parent_surface->finished))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
/* Get the extents that we'll use in creating our new group surface */
bounded = _cairo_surface_get_extents (parent_surface, &extents);
if (clip)
/* XXX: This assignment just fixes a compiler warning? */
is_empty = _cairo_rectangle_intersect (&extents,
_cairo_clip_get_extents (clip));
 
if (!bounded) {
/* XXX: Generic solution? */
group_surface = cairo_recording_surface_create (content, NULL);
extents.x = extents.y = 0;
} else {
group_surface = _cairo_surface_create_similar_solid (parent_surface,
content,
extents.width,
extents.height,
CAIRO_COLOR_TRANSPARENT);
}
status = group_surface->status;
if (unlikely (status))
goto bail;
 
/* Set device offsets on the new surface so that logically it appears at
* the same location on the parent surface -- when we pop_group this,
* the source pattern will get fixed up for the appropriate target surface
* device offsets, so we want to set our own surface offsets from /that/,
* and not from the device origin. */
cairo_surface_set_device_offset (group_surface,
parent_surface->device_transform.x0 - extents.x,
parent_surface->device_transform.y0 - extents.y);
 
/* If we have a current path, we need to adjust it to compensate for
* the device offset just applied. */
_cairo_path_fixed_translate (cr->path,
_cairo_fixed_from_int (-extents.x),
_cairo_fixed_from_int (-extents.y));
}
 
/* create a new gstate for the redirect */
status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
if (unlikely (status))
goto bail;
 
status = _cairo_gstate_redirect_target (cr->gstate, group_surface);
 
bail:
cairo_surface_destroy (group_surface);
return status;
}
 
static cairo_pattern_t *
_cairo_default_context_pop_group (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_surface_t *group_surface;
cairo_pattern_t *group_pattern;
cairo_matrix_t group_matrix, device_transform_matrix;
cairo_status_t status;
 
/* Verify that we are at the right nesting level */
if (unlikely (! _cairo_gstate_is_group (cr->gstate)))
return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP);
 
/* Get a reference to the active surface before restoring */
group_surface = _cairo_gstate_get_target (cr->gstate);
group_surface = cairo_surface_reference (group_surface);
 
status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
assert (status == CAIRO_STATUS_SUCCESS);
 
group_pattern = cairo_pattern_create_for_surface (group_surface);
status = group_pattern->status;
if (unlikely (status))
goto done;
 
_cairo_gstate_get_matrix (cr->gstate, &group_matrix);
/* Transform by group_matrix centered around device_transform so that when
* we call _cairo_gstate_copy_transformed_pattern the result is a pattern
* with a matrix equivalent to the device_transform of group_surface. */
if (_cairo_surface_has_device_transform (group_surface)) {
cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
_cairo_pattern_transform (group_pattern, &group_matrix);
_cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
} else {
cairo_pattern_set_matrix (group_pattern, &group_matrix);
}
 
/* If we have a current path, we need to adjust it to compensate for
* the device offset just removed. */
cairo_matrix_multiply (&device_transform_matrix,
&_cairo_gstate_get_target (cr->gstate)->device_transform,
&group_surface->device_transform_inverse);
_cairo_path_fixed_transform (cr->path, &device_transform_matrix);
 
done:
cairo_surface_destroy (group_surface);
 
return group_pattern;
}
 
static cairo_status_t
_cairo_default_context_set_source (void *abstract_cr,
cairo_pattern_t *source)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_source (cr->gstate, source);
}
 
static cairo_bool_t
_current_source_matches_solid (const cairo_pattern_t *pattern,
double red,
double green,
double blue,
double alpha)
{
cairo_color_t color;
 
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
return FALSE;
 
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
 
_cairo_color_init_rgba (&color, red, green, blue, alpha);
return _cairo_color_equal (&color,
&((cairo_solid_pattern_t *) pattern)->color);
}
 
static cairo_status_t
_cairo_default_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
{
cairo_default_context_t *cr = abstract_cr;
cairo_pattern_t *pattern;
cairo_status_t status;
 
if (_current_source_matches_solid (cr->gstate->source,
red, green, blue, alpha))
return CAIRO_STATUS_SUCCESS;
 
/* push the current pattern to the freed lists */
_cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
 
pattern = cairo_pattern_create_rgba (red, green, blue, alpha);
if (unlikely (pattern->status))
return pattern->status;
 
status = _cairo_default_context_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
 
return status;
}
 
static cairo_status_t
_cairo_default_context_set_source_surface (void *abstract_cr,
cairo_surface_t *surface,
double x,
double y)
{
cairo_default_context_t *cr = abstract_cr;
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
cairo_status_t status;
 
/* push the current pattern to the freed lists */
_cairo_default_context_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
 
pattern = cairo_pattern_create_for_surface (surface);
if (unlikely (pattern->status))
return pattern->status;
 
cairo_matrix_init_translate (&matrix, -x, -y);
cairo_pattern_set_matrix (pattern, &matrix);
 
status = _cairo_default_context_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
 
return status;
}
 
static cairo_pattern_t *
_cairo_default_context_get_source (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_source (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_set_tolerance (void *abstract_cr,
double tolerance)
{
cairo_default_context_t *cr = abstract_cr;
 
if (tolerance < CAIRO_TOLERANCE_MINIMUM)
tolerance = CAIRO_TOLERANCE_MINIMUM;
 
return _cairo_gstate_set_tolerance (cr->gstate, tolerance);
}
 
static cairo_status_t
_cairo_default_context_set_operator (void *abstract_cr, cairo_operator_t op)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_operator (cr->gstate, op);
}
 
static cairo_status_t
_cairo_default_context_set_opacity (void *abstract_cr, double opacity)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_opacity (cr->gstate, opacity);
}
 
static cairo_status_t
_cairo_default_context_set_antialias (void *abstract_cr,
cairo_antialias_t antialias)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_antialias (cr->gstate, antialias);
}
 
static cairo_status_t
_cairo_default_context_set_fill_rule (void *abstract_cr,
cairo_fill_rule_t fill_rule)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_fill_rule (cr->gstate, fill_rule);
}
 
static cairo_status_t
_cairo_default_context_set_line_width (void *abstract_cr,
double line_width)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_line_width (cr->gstate, line_width);
}
 
static cairo_status_t
_cairo_default_context_set_line_cap (void *abstract_cr,
cairo_line_cap_t line_cap)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_line_cap (cr->gstate, line_cap);
}
 
static cairo_status_t
_cairo_default_context_set_line_join (void *abstract_cr,
cairo_line_join_t line_join)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_line_join (cr->gstate, line_join);
}
 
static cairo_status_t
_cairo_default_context_set_dash (void *abstract_cr,
const double *dashes,
int num_dashes,
double offset)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_dash (cr->gstate,
dashes, num_dashes, offset);
}
 
static cairo_status_t
_cairo_default_context_set_miter_limit (void *abstract_cr,
double limit)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_miter_limit (cr->gstate, limit);
}
 
static cairo_antialias_t
_cairo_default_context_get_antialias (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_antialias (cr->gstate);
}
 
static void
_cairo_default_context_get_dash (void *abstract_cr,
double *dashes,
int *num_dashes,
double *offset)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_get_dash (cr->gstate, dashes, num_dashes, offset);
}
 
static cairo_fill_rule_t
_cairo_default_context_get_fill_rule (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_fill_rule (cr->gstate);
}
 
static double
_cairo_default_context_get_line_width (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_line_width (cr->gstate);
}
 
static cairo_line_cap_t
_cairo_default_context_get_line_cap (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_line_cap (cr->gstate);
}
 
static cairo_line_join_t
_cairo_default_context_get_line_join (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_line_join (cr->gstate);
}
 
static double
_cairo_default_context_get_miter_limit (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_miter_limit (cr->gstate);
}
 
static cairo_operator_t
_cairo_default_context_get_operator (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_operator (cr->gstate);
}
 
static double
_cairo_default_context_get_opacity (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_opacity (cr->gstate);
}
 
static double
_cairo_default_context_get_tolerance (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_tolerance (cr->gstate);
}
 
 
/* Current tranformation matrix */
 
static cairo_status_t
_cairo_default_context_translate (void *abstract_cr,
double tx,
double ty)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_translate (cr->gstate, tx, ty);
}
 
static cairo_status_t
_cairo_default_context_scale (void *abstract_cr,
double sx,
double sy)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_scale (cr->gstate, sx, sy);
}
 
static cairo_status_t
_cairo_default_context_rotate (void *abstract_cr,
double theta)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_rotate (cr->gstate, theta);
}
 
static cairo_status_t
_cairo_default_context_transform (void *abstract_cr,
const cairo_matrix_t *matrix)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_transform (cr->gstate, matrix);
}
 
static cairo_status_t
_cairo_default_context_set_matrix (void *abstract_cr,
const cairo_matrix_t *matrix)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_matrix (cr->gstate, matrix);
}
 
static cairo_status_t
_cairo_default_context_set_identity_matrix (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_identity_matrix (cr->gstate);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_default_context_get_matrix (void *abstract_cr,
cairo_matrix_t *matrix)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_get_matrix (cr->gstate, matrix);
}
 
static void
_cairo_default_context_user_to_device (void *abstract_cr,
double *x,
double *y)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_user_to_device (cr->gstate, x, y);
}
 
static void
_cairo_default_context_user_to_device_distance (void *abstract_cr, double *dx, double *dy)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_user_to_device_distance (cr->gstate, dx, dy);
}
 
static void
_cairo_default_context_device_to_user (void *abstract_cr,
double *x,
double *y)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_device_to_user (cr->gstate, x, y);
}
 
static void
_cairo_default_context_device_to_user_distance (void *abstract_cr,
double *dx,
double *dy)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_device_to_user_distance (cr->gstate, dx, dy);
}
 
static void
_cairo_default_context_backend_to_user (void *abstract_cr,
double *x,
double *y)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_backend_to_user (cr->gstate, x, y);
}
 
static void
_cairo_default_context_backend_to_user_distance (void *abstract_cr, double *dx, double *dy)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_backend_to_user_distance (cr->gstate, dx, dy);
}
 
static void
_cairo_default_context_user_to_backend (void *abstract_cr,
double *x,
double *y)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_user_to_backend (cr->gstate, x, y);
}
 
static void
_cairo_default_context_user_to_backend_distance (void *abstract_cr,
double *dx,
double *dy)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_user_to_backend_distance (cr->gstate, dx, dy);
}
 
/* Path constructor */
 
static cairo_status_t
_cairo_default_context_new_path (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_path_fixed_fini (cr->path);
_cairo_path_fixed_init (cr->path);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_default_context_new_sub_path (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_path_fixed_new_sub_path (cr->path);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_default_context_move_to (void *abstract_cr, double x, double y)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t x_fixed, y_fixed;
 
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
return _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed);
}
 
static cairo_status_t
_cairo_default_context_line_to (void *abstract_cr, double x, double y)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t x_fixed, y_fixed;
 
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
return _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
}
 
static cairo_status_t
_cairo_default_context_curve_to (void *abstract_cr,
double x1, double y1,
double x2, double y2,
double x3, double y3)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t x1_fixed, y1_fixed;
cairo_fixed_t x2_fixed, y2_fixed;
cairo_fixed_t x3_fixed, y3_fixed;
 
_cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
_cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
_cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
 
x1_fixed = _cairo_fixed_from_double (x1);
y1_fixed = _cairo_fixed_from_double (y1);
 
x2_fixed = _cairo_fixed_from_double (x2);
y2_fixed = _cairo_fixed_from_double (y2);
 
x3_fixed = _cairo_fixed_from_double (x3);
y3_fixed = _cairo_fixed_from_double (y3);
 
return _cairo_path_fixed_curve_to (cr->path,
x1_fixed, y1_fixed,
x2_fixed, y2_fixed,
x3_fixed, y3_fixed);
}
 
static cairo_status_t
_cairo_default_context_arc (void *abstract_cr,
double xc, double yc, double radius,
double angle1, double angle2,
cairo_bool_t forward)
{
cairo_default_context_t *cr = abstract_cr;
cairo_status_t status;
 
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0) {
cairo_fixed_t x_fixed, y_fixed;
 
_cairo_gstate_user_to_backend (cr->gstate, &xc, &yc);
x_fixed = _cairo_fixed_from_double (xc);
y_fixed = _cairo_fixed_from_double (yc);
status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
status = _cairo_default_context_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
 
if (unlikely (status))
return status;
 
if (forward)
_cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
else
_cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);
 
return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
}
 
static cairo_status_t
_cairo_default_context_rel_move_to (void *abstract_cr, double dx, double dy)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t dx_fixed, dy_fixed;
 
_cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy);
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
return _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed);
}
 
static cairo_status_t
_cairo_default_context_rel_line_to (void *abstract_cr, double dx, double dy)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t dx_fixed, dy_fixed;
 
_cairo_gstate_user_to_backend_distance (cr->gstate, &dx, &dy);
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
return _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed);
}
 
 
static cairo_status_t
_cairo_default_context_rel_curve_to (void *abstract_cr,
double dx1, double dy1,
double dx2, double dy2,
double dx3, double dy3)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t dx1_fixed, dy1_fixed;
cairo_fixed_t dx2_fixed, dy2_fixed;
cairo_fixed_t dx3_fixed, dy3_fixed;
 
_cairo_gstate_user_to_backend_distance (cr->gstate, &dx1, &dy1);
_cairo_gstate_user_to_backend_distance (cr->gstate, &dx2, &dy2);
_cairo_gstate_user_to_backend_distance (cr->gstate, &dx3, &dy3);
 
dx1_fixed = _cairo_fixed_from_double (dx1);
dy1_fixed = _cairo_fixed_from_double (dy1);
 
dx2_fixed = _cairo_fixed_from_double (dx2);
dy2_fixed = _cairo_fixed_from_double (dy2);
 
dx3_fixed = _cairo_fixed_from_double (dx3);
dy3_fixed = _cairo_fixed_from_double (dy3);
 
return _cairo_path_fixed_rel_curve_to (cr->path,
dx1_fixed, dy1_fixed,
dx2_fixed, dy2_fixed,
dx3_fixed, dy3_fixed);
}
 
static cairo_status_t
_cairo_default_context_close_path (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_path_fixed_close_path (cr->path);
}
 
static cairo_status_t
_cairo_default_context_rectangle (void *abstract_cr,
double x, double y,
double width, double height)
{
cairo_default_context_t *cr = abstract_cr;
cairo_status_t status;
 
status = _cairo_default_context_move_to (cr, x, y);
if (unlikely (status))
return status;
 
status = _cairo_default_context_rel_line_to (cr, width, 0);
if (unlikely (status))
return status;
 
status = _cairo_default_context_rel_line_to (cr, 0, height);
if (unlikely (status))
return status;
 
status = _cairo_default_context_rel_line_to (cr, -width, 0);
if (unlikely (status))
return status;
 
return _cairo_default_context_close_path (cr);
}
 
static void
_cairo_default_context_path_extents (void *abstract_cr,
double *x1,
double *y1,
double *x2,
double *y2)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_path_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
}
 
static cairo_bool_t
_cairo_default_context_has_current_point (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return cr->path->has_current_point;
}
 
static cairo_bool_t
_cairo_default_context_get_current_point (void *abstract_cr,
double *x,
double *y)
{
cairo_default_context_t *cr = abstract_cr;
cairo_fixed_t x_fixed, y_fixed;
 
if (_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed))
{
*x = _cairo_fixed_to_double (x_fixed);
*y = _cairo_fixed_to_double (y_fixed);
_cairo_gstate_backend_to_user (cr->gstate, x, y);
 
return TRUE;
}
else
{
return FALSE;
}
}
 
static cairo_path_t *
_cairo_default_context_copy_path (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_path_create (cr->path, &cr->base);
}
 
static cairo_path_t *
_cairo_default_context_copy_path_flat (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_path_create_flat (cr->path, &cr->base);
}
 
static cairo_status_t
_cairo_default_context_append_path (void *abstract_cr,
const cairo_path_t *path)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_path_append_to_context (path, &cr->base);
}
 
static cairo_status_t
_cairo_default_context_paint (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_paint (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_paint_with_alpha (void *abstract_cr,
double alpha)
{
cairo_default_context_t *cr = abstract_cr;
cairo_solid_pattern_t pattern;
cairo_status_t status;
cairo_color_t color;
 
if (CAIRO_ALPHA_IS_OPAQUE (alpha))
return _cairo_gstate_paint (cr->gstate);
 
if (CAIRO_ALPHA_IS_ZERO (alpha) &&
_cairo_operator_bounded_by_mask (cr->gstate->op)) {
return CAIRO_STATUS_SUCCESS;
}
 
_cairo_color_init_rgba (&color, 0., 0., 0., alpha);
_cairo_pattern_init_solid (&pattern, &color);
 
status = _cairo_gstate_mask (cr->gstate, &pattern.base);
_cairo_pattern_fini (&pattern.base);
 
return status;
}
 
static cairo_status_t
_cairo_default_context_mask (void *abstract_cr,
cairo_pattern_t *mask)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_mask (cr->gstate, mask);
}
 
static cairo_status_t
_cairo_default_context_stroke_preserve (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_stroke (cr->gstate, cr->path);
}
 
static cairo_status_t
_cairo_default_context_stroke (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_status_t status;
 
status = _cairo_gstate_stroke (cr->gstate, cr->path);
if (unlikely (status))
return status;
 
return _cairo_default_context_new_path (cr);
}
 
static cairo_status_t
_cairo_default_context_in_stroke (void *abstract_cr,
double x, double y,
cairo_bool_t *inside)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_in_stroke (cr->gstate,
cr->path,
x, y,
inside);
}
 
static cairo_status_t
_cairo_default_context_stroke_extents (void *abstract_cr,
double *x1, double *y1, double *x2, double *y2)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_stroke_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
}
 
static cairo_status_t
_cairo_default_context_fill_preserve (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_fill (cr->gstate, cr->path);
}
 
static cairo_status_t
_cairo_default_context_fill (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_status_t status;
 
status = _cairo_gstate_fill (cr->gstate, cr->path);
if (unlikely (status))
return status;
 
return _cairo_default_context_new_path (cr);
}
 
static cairo_status_t
_cairo_default_context_in_fill (void *abstract_cr,
double x, double y,
cairo_bool_t *inside)
{
cairo_default_context_t *cr = abstract_cr;
 
*inside = _cairo_gstate_in_fill (cr->gstate,
cr->path,
x, y);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_default_context_fill_extents (void *abstract_cr,
double *x1, double *y1, double *x2, double *y2)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_fill_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
}
 
static cairo_status_t
_cairo_default_context_clip_preserve (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_clip (cr->gstate, cr->path);
}
 
static cairo_status_t
_cairo_default_context_clip (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_status_t status;
 
status = _cairo_gstate_clip (cr->gstate, cr->path);
if (unlikely (status))
return status;
 
return _cairo_default_context_new_path (cr);
}
 
static cairo_status_t
_cairo_default_context_in_clip (void *abstract_cr,
double x, double y,
cairo_bool_t *inside)
{
cairo_default_context_t *cr = abstract_cr;
 
*inside = _cairo_gstate_in_clip (cr->gstate, x, y);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_default_context_reset_clip (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_reset_clip (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_clip_extents (void *abstract_cr,
double *x1, double *y1, double *x2, double *y2)
{
cairo_default_context_t *cr = abstract_cr;
 
if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) {
*x1 = -INFINITY;
*y1 = -INFINITY;
*x2 = +INFINITY;
*y2 = +INFINITY;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_rectangle_list_t *
_cairo_default_context_copy_clip_rectangle_list (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_copy_clip_rectangle_list (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_copy_page (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_copy_page (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_show_page (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_show_page (cr->gstate);
}
 
static cairo_status_t
_cairo_default_context_set_font_face (void *abstract_cr,
cairo_font_face_t *font_face)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_font_face (cr->gstate, font_face);
}
 
static cairo_font_face_t *
_cairo_default_context_get_font_face (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_font_face_t *font_face;
cairo_status_t status;
 
status = _cairo_gstate_get_font_face (cr->gstate, &font_face);
if (unlikely (status)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *) &_cairo_font_face_nil;
}
 
return font_face;
}
 
static cairo_status_t
_cairo_default_context_font_extents (void *abstract_cr,
cairo_font_extents_t *extents)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_get_font_extents (cr->gstate, extents);
}
 
static cairo_status_t
_cairo_default_context_set_font_size (void *abstract_cr,
double size)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_font_size (cr->gstate, size);
}
 
static cairo_status_t
_cairo_default_context_set_font_matrix (void *abstract_cr,
const cairo_matrix_t *matrix)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_set_font_matrix (cr->gstate, matrix);
}
 
static void
_cairo_default_context_get_font_matrix (void *abstract_cr,
cairo_matrix_t *matrix)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_get_font_matrix (cr->gstate, matrix);
}
 
static cairo_status_t
_cairo_default_context_set_font_options (void *abstract_cr,
const cairo_font_options_t *options)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_set_font_options (cr->gstate, options);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_default_context_get_font_options (void *abstract_cr,
cairo_font_options_t *options)
{
cairo_default_context_t *cr = abstract_cr;
 
_cairo_gstate_get_font_options (cr->gstate, options);
}
 
static cairo_status_t
_cairo_default_context_set_scaled_font (void *abstract_cr,
cairo_scaled_font_t *scaled_font)
{
cairo_default_context_t *cr = abstract_cr;
cairo_bool_t was_previous;
cairo_status_t status;
 
if (scaled_font == cr->gstate->scaled_font)
return CAIRO_STATUS_SUCCESS;
 
was_previous = scaled_font == cr->gstate->previous_scaled_font;
 
status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
if (unlikely (status))
return status;
 
status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix);
if (unlikely (status))
return status;
 
_cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
 
if (was_previous)
cr->gstate->scaled_font = cairo_scaled_font_reference (scaled_font);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_scaled_font_t *
_cairo_default_context_get_scaled_font (void *abstract_cr)
{
cairo_default_context_t *cr = abstract_cr;
cairo_scaled_font_t *scaled_font;
cairo_status_t status;
 
status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font);
if (unlikely (status))
return _cairo_scaled_font_create_in_error (status);
 
return scaled_font;
}
 
static cairo_status_t
_cairo_default_context_glyphs (void *abstract_cr,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_glyph_text_info_t *info)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_show_text_glyphs (cr->gstate, glyphs, num_glyphs, info);
}
 
static cairo_status_t
_cairo_default_context_glyph_path (void *abstract_cr,
const cairo_glyph_t *glyphs,
int num_glyphs)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_glyph_path (cr->gstate,
glyphs, num_glyphs,
cr->path);
}
 
static cairo_status_t
_cairo_default_context_glyph_extents (void *abstract_cr,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_text_extents_t *extents)
{
cairo_default_context_t *cr = abstract_cr;
 
return _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs, extents);
}
 
static const cairo_backend_t _cairo_default_context_backend = {
CAIRO_TYPE_DEFAULT,
_cairo_default_context_destroy,
 
_cairo_default_context_get_original_target,
_cairo_default_context_get_current_target,
 
_cairo_default_context_save,
_cairo_default_context_restore,
 
_cairo_default_context_push_group,
_cairo_default_context_pop_group,
 
_cairo_default_context_set_source_rgba,
_cairo_default_context_set_source_surface,
_cairo_default_context_set_source,
_cairo_default_context_get_source,
 
_cairo_default_context_set_antialias,
_cairo_default_context_set_dash,
_cairo_default_context_set_fill_rule,
_cairo_default_context_set_line_cap,
_cairo_default_context_set_line_join,
_cairo_default_context_set_line_width,
_cairo_default_context_set_miter_limit,
_cairo_default_context_set_opacity,
_cairo_default_context_set_operator,
_cairo_default_context_set_tolerance,
_cairo_default_context_get_antialias,
_cairo_default_context_get_dash,
_cairo_default_context_get_fill_rule,
_cairo_default_context_get_line_cap,
_cairo_default_context_get_line_join,
_cairo_default_context_get_line_width,
_cairo_default_context_get_miter_limit,
_cairo_default_context_get_opacity,
_cairo_default_context_get_operator,
_cairo_default_context_get_tolerance,
 
_cairo_default_context_translate,
_cairo_default_context_scale,
_cairo_default_context_rotate,
_cairo_default_context_transform,
_cairo_default_context_set_matrix,
_cairo_default_context_set_identity_matrix,
_cairo_default_context_get_matrix,
 
_cairo_default_context_user_to_device,
_cairo_default_context_user_to_device_distance,
_cairo_default_context_device_to_user,
_cairo_default_context_device_to_user_distance,
 
_cairo_default_context_user_to_backend,
_cairo_default_context_user_to_backend_distance,
_cairo_default_context_backend_to_user,
_cairo_default_context_backend_to_user_distance,
 
_cairo_default_context_new_path,
_cairo_default_context_new_sub_path,
_cairo_default_context_move_to,
_cairo_default_context_rel_move_to,
_cairo_default_context_line_to,
_cairo_default_context_rel_line_to,
_cairo_default_context_curve_to,
_cairo_default_context_rel_curve_to,
NULL, /* arc-to */
NULL, /* rel-arc-to */
_cairo_default_context_close_path,
_cairo_default_context_arc,
_cairo_default_context_rectangle,
_cairo_default_context_path_extents,
_cairo_default_context_has_current_point,
_cairo_default_context_get_current_point,
_cairo_default_context_copy_path,
_cairo_default_context_copy_path_flat,
_cairo_default_context_append_path,
 
NULL, /* stroke-to-path */
 
_cairo_default_context_clip,
_cairo_default_context_clip_preserve,
_cairo_default_context_in_clip,
_cairo_default_context_clip_extents,
_cairo_default_context_reset_clip,
_cairo_default_context_copy_clip_rectangle_list,
 
_cairo_default_context_paint,
_cairo_default_context_paint_with_alpha,
_cairo_default_context_mask,
 
_cairo_default_context_stroke,
_cairo_default_context_stroke_preserve,
_cairo_default_context_in_stroke,
_cairo_default_context_stroke_extents,
 
_cairo_default_context_fill,
_cairo_default_context_fill_preserve,
_cairo_default_context_in_fill,
_cairo_default_context_fill_extents,
 
_cairo_default_context_set_font_face,
_cairo_default_context_get_font_face,
_cairo_default_context_set_font_size,
_cairo_default_context_set_font_matrix,
_cairo_default_context_get_font_matrix,
_cairo_default_context_set_font_options,
_cairo_default_context_get_font_options,
_cairo_default_context_set_scaled_font,
_cairo_default_context_get_scaled_font,
_cairo_default_context_font_extents,
 
_cairo_default_context_glyphs,
_cairo_default_context_glyph_path,
_cairo_default_context_glyph_extents,
 
_cairo_default_context_copy_page,
_cairo_default_context_show_page,
};
 
cairo_status_t
_cairo_default_context_init (cairo_default_context_t *cr, void *target)
{
_cairo_init (&cr->base, &_cairo_default_context_backend);
_cairo_path_fixed_init (cr->path);
 
cr->gstate = &cr->gstate_tail[0];
cr->gstate_freelist = &cr->gstate_tail[1];
cr->gstate_tail[1].next = NULL;
 
return _cairo_gstate_init (cr->gstate, target);
}
 
cairo_t *
_cairo_default_context_create (void *target)
{
cairo_default_context_t *cr;
cairo_status_t status;
 
cr = _freed_pool_get (&context_pool);
if (unlikely (cr == NULL)) {
cr = malloc (sizeof (cairo_default_context_t));
if (unlikely (cr == NULL))
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
status = _cairo_default_context_init (cr, target);
if (unlikely (status)) {
_freed_pool_put (&context_pool, cr);
return _cairo_create_in_error (status);
}
 
return &cr->base;
}
/programs/develop/libraries/cairo/src/cairo-deflate-stream.c
0,0 → 1,156
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 Adrian Johnson
*
* 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 Adrian Johnson.
*
* Author(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_DEFLATE_STREAM
 
#include "cairo-error-private.h"
#include "cairo-output-stream-private.h"
#include <zlib.h>
 
#define BUFFER_SIZE 16384
 
typedef struct _cairo_deflate_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output;
z_stream zlib_stream;
unsigned char input_buf[BUFFER_SIZE];
unsigned char output_buf[BUFFER_SIZE];
} cairo_deflate_stream_t;
 
static void
cairo_deflate_stream_deflate (cairo_deflate_stream_t *stream, cairo_bool_t flush)
{
int ret;
cairo_bool_t finished;
 
do {
ret = deflate (&stream->zlib_stream, flush ? Z_FINISH : Z_NO_FLUSH);
if (flush || stream->zlib_stream.avail_out == 0)
{
_cairo_output_stream_write (stream->output,
stream->output_buf,
BUFFER_SIZE - stream->zlib_stream.avail_out);
stream->zlib_stream.next_out = stream->output_buf;
stream->zlib_stream.avail_out = BUFFER_SIZE;
}
 
finished = TRUE;
if (stream->zlib_stream.avail_in != 0)
finished = FALSE;
if (flush && ret != Z_STREAM_END)
finished = FALSE;
 
} while (!finished);
 
stream->zlib_stream.next_in = stream->input_buf;
}
 
static cairo_status_t
_cairo_deflate_stream_write (cairo_output_stream_t *base,
const unsigned char *data,
unsigned int length)
{
cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base;
unsigned int count;
const unsigned char *p = data;
 
while (length) {
count = length;
if (count > BUFFER_SIZE - stream->zlib_stream.avail_in)
count = BUFFER_SIZE - stream->zlib_stream.avail_in;
memcpy (stream->input_buf + stream->zlib_stream.avail_in, p, count);
p += count;
stream->zlib_stream.avail_in += count;
length -= count;
 
if (stream->zlib_stream.avail_in == BUFFER_SIZE)
cairo_deflate_stream_deflate (stream, FALSE);
}
 
return _cairo_output_stream_get_status (stream->output);
}
 
static cairo_status_t
_cairo_deflate_stream_close (cairo_output_stream_t *base)
{
cairo_deflate_stream_t *stream = (cairo_deflate_stream_t *) base;
 
cairo_deflate_stream_deflate (stream, TRUE);
deflateEnd (&stream->zlib_stream);
 
return _cairo_output_stream_get_status (stream->output);
}
 
cairo_output_stream_t *
_cairo_deflate_stream_create (cairo_output_stream_t *output)
{
cairo_deflate_stream_t *stream;
 
if (output->status)
return _cairo_output_stream_create_in_error (output->status);
 
stream = malloc (sizeof (cairo_deflate_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
 
_cairo_output_stream_init (&stream->base,
_cairo_deflate_stream_write,
NULL,
_cairo_deflate_stream_close);
stream->output = output;
 
stream->zlib_stream.zalloc = Z_NULL;
stream->zlib_stream.zfree = Z_NULL;
stream->zlib_stream.opaque = Z_NULL;
 
if (deflateInit (&stream->zlib_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
free (stream);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
 
stream->zlib_stream.next_in = stream->input_buf;
stream->zlib_stream.avail_in = 0;
stream->zlib_stream.next_out = stream->output_buf;
stream->zlib_stream.avail_out = BUFFER_SIZE;
 
return &stream->base;
}
 
#endif /* CAIRO_HAS_DEFLATE_STREAM */
/programs/develop/libraries/cairo/src/cairo-device.c
92,7 → 92,7
* interactions with existing surface API of the device functions for
* surfaces of that type.
* </para></note>
*/
**/
 
static const cairo_device_t _nil_device = {
CAIRO_REFERENCE_COUNT_INVALID,
156,6 → 156,8
case CAIRO_STATUS_INVALID_WEIGHT:
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
case CAIRO_STATUS_INVALID_CONTENT:
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
case CAIRO_STATUS_DEVICE_FINISHED:
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_device_t *) &_nil_device;
254,6 → 256,9
if (device == NULL || device->status)
return;
 
if (device->finished)
return;
 
if (device->backend->flush != NULL) {
status = device->backend->flush (device);
if (unlikely (status))
295,10 → 300,14
 
cairo_device_flush (device);
 
device->finished = TRUE;
 
if (device->backend->finish != NULL)
device->backend->finish (device);
 
/* We only finish the device after the backend's callback returns because
* the device might still be needed during the callback
* (e.g. for cairo_device_acquire ()).
*/
device->finished = TRUE;
}
slim_hidden_def (cairo_device_finish);
 
360,7 → 369,7
if (device == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&device->ref_count))
{
return (cairo_device_type_t) -1;
return CAIRO_DEVICE_TYPE_INVALID;
}
 
return device->backend->type;
406,7 → 415,7
return device->status;
 
if (unlikely (device->finished))
return _cairo_device_set_error (device, CAIRO_STATUS_SURFACE_FINISHED); /* XXX */
return _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_FINISHED);
 
CAIRO_MUTEX_LOCK (device->mutex);
if (device->mutex_depth++ == 0) {
448,11 → 457,9
_cairo_device_set_error (cairo_device_t *device,
cairo_status_t status)
{
if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED)
return status;
if (status == CAIRO_STATUS_SUCCESS)
return CAIRO_STATUS_SUCCESS;
 
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&device->status, status);
 
return _cairo_error (status);
/programs/develop/libraries/cairo/src/cairo-directfb-surface.c
0,0 → 1,544
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-directfb.h"
 
#include "cairo-clip-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-fallback-private.h"
 
#include <pixman.h>
 
#include <directfb.h>
#include <direct/types.h>
#include <direct/debug.h>
#include <direct/memcpy.h>
#include <direct/util.h>
 
slim_hidden_proto(cairo_directfb_surface_create);
 
typedef struct _cairo_dfb_surface {
cairo_image_surface_t image;
 
IDirectFB *dfb;
IDirectFBSurface *dfb_surface;
 
unsigned blit_premultiplied : 1;
} cairo_dfb_surface_t;
 
static cairo_content_t
_directfb_format_to_content (DFBSurfacePixelFormat format)
{
cairo_content_t content = 0;
 
if (DFB_PIXELFORMAT_HAS_ALPHA (format))
content |= CAIRO_CONTENT_ALPHA;
if (DFB_COLOR_BITS_PER_PIXEL (format))
content |= CAIRO_CONTENT_COLOR_ALPHA;
 
assert(content);
return content;
}
 
static inline pixman_format_code_t
_directfb_to_pixman_format (DFBSurfacePixelFormat format)
{
switch (format) {
case DSPF_UNKNOWN: return 0;
case DSPF_ARGB1555: return PIXMAN_a1r5g5b5;
case DSPF_RGB16: return PIXMAN_r5g6b5;
case DSPF_RGB24: return PIXMAN_r8g8b8;
case DSPF_RGB32: return PIXMAN_x8r8g8b8;
case DSPF_ARGB: return PIXMAN_a8r8g8b8;
case DSPF_A8: return PIXMAN_a8;
case DSPF_YUY2: return PIXMAN_yuy2;
case DSPF_RGB332: return PIXMAN_r3g3b2;
case DSPF_UYVY: return 0;
case DSPF_I420: return 0;
case DSPF_YV12: return PIXMAN_yv12;
case DSPF_LUT8: return 0;
case DSPF_ALUT44: return 0;
case DSPF_AiRGB: return 0;
case DSPF_A1: return 0; /* bit reversed, oops */
case DSPF_NV12: return 0;
case DSPF_NV16: return 0;
case DSPF_ARGB2554: return 0;
case DSPF_ARGB4444: return PIXMAN_a4r4g4b4;
case DSPF_NV21: return 0;
case DSPF_AYUV: return 0;
case DSPF_A4: return PIXMAN_a4;
case DSPF_ARGB1666: return 0;
case DSPF_ARGB6666: return 0;
case DSPF_RGB18: return 0;
case DSPF_LUT2: return 0;
case DSPF_RGB444: return PIXMAN_x4r4g4b4;
case DSPF_RGB555: return PIXMAN_x1r5g5b5;
#if DFB_NUM_PIXELFORMATS >= 29
case DSPF_BGR555: return PIXMAN_x1b5g5r5;
#endif
}
return 0;
}
 
static cairo_surface_t *
_cairo_dfb_surface_create_similar (void *abstract_src,
cairo_content_t content,
int width,
int height)
{
cairo_dfb_surface_t *other = abstract_src;
DFBSurfacePixelFormat format;
IDirectFBSurface *buffer;
DFBSurfaceDescription dsc;
cairo_surface_t *surface;
 
if (width <= 0 || height <= 0)
return _cairo_image_surface_create_with_content (content, width, height);
 
switch (content) {
default:
ASSERT_NOT_REACHED;
case CAIRO_CONTENT_COLOR_ALPHA:
format = DSPF_ARGB;
break;
case CAIRO_CONTENT_COLOR:
format = DSPF_RGB32;
break;
case CAIRO_CONTENT_ALPHA:
format = DSPF_A8;
break;
}
 
dsc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_PIXELFORMAT;
dsc.caps = DSCAPS_PREMULTIPLIED;
dsc.width = width;
dsc.height = height;
dsc.pixelformat = format;
 
if (other->dfb->CreateSurface (other->dfb, &dsc, &buffer))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_ERROR));
 
surface = cairo_directfb_surface_create (other->dfb, buffer);
buffer->Release (buffer);
 
return surface;
}
 
static cairo_status_t
_cairo_dfb_surface_finish (void *abstract_surface)
{
cairo_dfb_surface_t *surface = abstract_surface;
 
surface->dfb_surface->Release (surface->dfb_surface);
return _cairo_image_surface_finish (abstract_surface);
}
 
static cairo_image_surface_t *
_cairo_dfb_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_dfb_surface_t *surface = abstract_surface;
 
if (surface->image.pixman_image == NULL) {
IDirectFBSurface *buffer = surface->dfb_surface;
pixman_image_t *image;
void *data;
int pitch;
 
if (buffer->Lock (buffer, DSLF_READ | DSLF_WRITE, &data, &pitch))
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
image = pixman_image_create_bits (surface->image.pixman_format,
surface->image.width,
surface->image.height,
data, pitch);
if (image == NULL) {
buffer->Unlock (buffer);
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
_cairo_image_surface_init (&surface->image, image, surface->image.pixman_format);
}
 
return _cairo_image_surface_map_to_image (&surface->image.base, extents);
}
 
static cairo_int_status_t
_cairo_dfb_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_dfb_surface_t *surface = abstract_surface;
return _cairo_image_surface_unmap_image (&surface->image.base, image);
}
 
static cairo_status_t
_cairo_dfb_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_dfb_surface_t *surface = abstract_surface;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
if (surface->image.pixman_image) {
surface->dfb_surface->Unlock (surface->dfb_surface);
 
pixman_image_unref (surface->image.pixman_image);
surface->image.pixman_image = NULL;
surface->image.data = NULL;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
#if 0
static inline DFBSurfacePixelFormat
_directfb_from_pixman_format (pixman_format_code_t format)
{
switch ((int) format) {
case PIXMAN_a1r5g5b5: return DSPF_ARGB1555;
case PIXMAN_r5g6b5: return DSPF_RGB16;
case PIXMAN_r8g8b8: return DSPF_RGB24;
case PIXMAN_x8r8g8b8: return DSPF_RGB32;
case PIXMAN_a8r8g8b8: return DSPF_ARGB;
case PIXMAN_a8: return DSPF_A8;
case PIXMAN_yuy2: return DSPF_YUY2;
case PIXMAN_r3g3b2: return DSPF_RGB332;
case PIXMAN_yv12: return DSPF_YV12;
case PIXMAN_a1: return DSPF_A1; /* bit reversed, oops */
case PIXMAN_a4r4g4b4: return DSPF_ARGB4444;
case PIXMAN_a4: return DSPF_A4;
case PIXMAN_x4r4g4b4: return DSPF_RGB444;
case PIXMAN_x1r5g5b5: return DSPF_RGB555;
#if DFB_NUM_PIXELFORMATS >= 29
case PIXMAN_x1b5g5r5: return DSPF_BGR555;
#endif
default: return 0;
}
}
 
static cairo_bool_t
_directfb_get_operator (cairo_operator_t operator,
DFBSurfaceBlendFunction *ret_srcblend,
DFBSurfaceBlendFunction *ret_dstblend)
{
DFBSurfaceBlendFunction srcblend = DSBF_ONE;
DFBSurfaceBlendFunction dstblend = DSBF_ZERO;
 
switch (operator) {
case CAIRO_OPERATOR_CLEAR:
srcblend = DSBF_ZERO;
dstblend = DSBF_ZERO;
break;
case CAIRO_OPERATOR_SOURCE:
srcblend = DSBF_ONE;
dstblend = DSBF_ZERO;
break;
case CAIRO_OPERATOR_OVER:
srcblend = DSBF_ONE;
dstblend = DSBF_INVSRCALPHA;
break;
case CAIRO_OPERATOR_IN:
srcblend = DSBF_DESTALPHA;
dstblend = DSBF_ZERO;
break;
case CAIRO_OPERATOR_OUT:
srcblend = DSBF_INVDESTALPHA;
dstblend = DSBF_ZERO;
break;
case CAIRO_OPERATOR_ATOP:
srcblend = DSBF_DESTALPHA;
dstblend = DSBF_INVSRCALPHA;
break;
case CAIRO_OPERATOR_DEST:
srcblend = DSBF_ZERO;
dstblend = DSBF_ONE;
break;
case CAIRO_OPERATOR_DEST_OVER:
srcblend = DSBF_INVDESTALPHA;
dstblend = DSBF_ONE;
break;
case CAIRO_OPERATOR_DEST_IN:
srcblend = DSBF_ZERO;
dstblend = DSBF_SRCALPHA;
break;
case CAIRO_OPERATOR_DEST_OUT:
srcblend = DSBF_ZERO;
dstblend = DSBF_INVSRCALPHA;
break;
case CAIRO_OPERATOR_DEST_ATOP:
srcblend = DSBF_INVDESTALPHA;
dstblend = DSBF_SRCALPHA;
break;
case CAIRO_OPERATOR_XOR:
srcblend = DSBF_INVDESTALPHA;
dstblend = DSBF_INVSRCALPHA;
break;
case CAIRO_OPERATOR_ADD:
srcblend = DSBF_ONE;
dstblend = DSBF_ONE;
break;
case CAIRO_OPERATOR_SATURATE:
/* XXX This does not work. */
#if 0
srcblend = DSBF_SRCALPHASAT;
dstblend = DSBF_ONE;
break;
#endif
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
default:
return FALSE;
}
 
*ret_srcblend = srcblend;
*ret_dstblend = dstblend;
 
return TRUE;
}
#define RUN_CLIPPED(surface, clip_region, clip, func) {\
if ((clip_region) != NULL) {\
int n_clips = cairo_region_num_rectangles (clip_region), n; \
for (n = 0; n < n_clips; n++) {\
if (clip) {\
DFBRegion reg, *cli = (clip); \
cairo_rectangle_int_t rect; \
cairo_region_get_rectangle (clip_region, n, &rect); \
reg.x1 = rect.x; \
reg.y1 = rect.y; \
reg.x2 = rect.x + rect.width - 1; \
reg.y2 = rect.y + rect.height - 1; \
if (reg.x2 < cli->x1 || reg.y2 < cli->y1 ||\
reg.x1 > cli->x2 || reg.y1 > cli->y2)\
continue;\
if (reg.x1 < cli->x1)\
reg.x1 = cli->x1;\
if (reg.y1 < cli->y1)\
reg.y1 = cli->y1;\
if (reg.x2 > cli->x2)\
reg.x2 = cli->x2;\
if (reg.y2 > cli->y2)\
reg.y2 = cli->y2;\
(surface)->dfbsurface->SetClip ((surface)->dfbsurface, &reg);\
} else {\
DFBRegion reg; \
cairo_rectangle_int_t rect; \
cairo_region_get_rectangle (clip_region, n, &rect); \
reg.x1 = rect.x; \
reg.y1 = rect.y; \
reg.x2 = rect.x + rect.width - 1; \
reg.y2 = rect.y + rect.height - 1; \
(surface)->dfbsurface->SetClip ((surface)->dfbsurface, &reg); \
}\
func;\
}\
} else {\
(surface)->dfbsurface->SetClip ((surface)->dfbsurface, clip);\
func;\
}\
}
 
static cairo_int_status_t
_cairo_dfb_surface_fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int n_rects)
{
cairo_dfb_surface_t *dst = abstract_surface;
DFBSurfaceDrawingFlags flags;
DFBSurfaceBlendFunction sblend;
DFBSurfaceBlendFunction dblend;
DFBRectangle r[n_rects];
int i;
 
D_DEBUG_AT (CairoDFB_Render,
"%s( dst=%p, op=%d, color=%p, rects=%p, n_rects=%d ).\n",
__FUNCTION__, dst, op, color, rects, n_rects);
 
if (! dst->supported_destination)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _directfb_get_operator (op, &sblend, &dblend))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (CAIRO_COLOR_IS_OPAQUE (color)) {
if (sblend == DSBF_SRCALPHA)
sblend = DSBF_ONE;
else if (sblend == DSBF_INVSRCALPHA)
sblend = DSBF_ZERO;
 
if (dblend == DSBF_SRCALPHA)
dblend = DSBF_ONE;
else if (dblend == DSBF_INVSRCALPHA)
dblend = DSBF_ZERO;
}
if ((dst->base.content & CAIRO_CONTENT_ALPHA) == 0) {
if (sblend == DSBF_DESTALPHA)
sblend = DSBF_ONE;
else if (sblend == DSBF_INVDESTALPHA)
sblend = DSBF_ZERO;
 
if (dblend == DSBF_DESTALPHA)
dblend = DSBF_ONE;
else if (dblend == DSBF_INVDESTALPHA)
dblend = DSBF_ZERO;
}
 
flags = (sblend == DSBF_ONE && dblend == DSBF_ZERO) ? DSDRAW_NOFX : DSDRAW_BLEND;
dst->dfbsurface->SetDrawingFlags (dst->dfbsurface, flags);
if (flags & DSDRAW_BLEND) {
dst->dfbsurface->SetSrcBlendFunction (dst->dfbsurface, sblend);
dst->dfbsurface->SetDstBlendFunction (dst->dfbsurface, dblend);
}
 
dst->dfbsurface->SetColor (dst->dfbsurface,
color->red_short >> 8,
color->green_short >> 8,
color->blue_short >> 8,
color->alpha_short >> 8);
 
for (i = 0; i < n_rects; i++) {
r[i].x = rects[i].x;
r[i].y = rects[i].y;
r[i].w = rects[i].width;
r[i].h = rects[i].height;
}
 
RUN_CLIPPED (dst, NULL, NULL,
dst->dfbsurface->FillRectangles (dst->dfbsurface, r, n_rects));
 
return CAIRO_STATUS_SUCCESS;
}
#endif
 
static cairo_surface_backend_t
_cairo_dfb_surface_backend = {
CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
_cairo_dfb_surface_finish, /*finish*/
_cairo_default_context_create,
 
_cairo_dfb_surface_create_similar,/*create_similar*/
NULL, /* create similar image */
_cairo_dfb_surface_map_to_image,
_cairo_dfb_surface_unmap_image,
 
_cairo_surface_default_source,
_cairo_surface_default_acquire_source_image,
_cairo_surface_default_release_source_image,
NULL,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_image_surface_get_extents,
_cairo_image_surface_get_font_options,
 
_cairo_dfb_surface_flush,
NULL, /* mark_dirty_rectangle */
 
_cairo_surface_fallback_paint,
_cairo_surface_fallback_mask,
_cairo_surface_fallback_stroke,
_cairo_surface_fallback_fill,
NULL, /* fill-stroke */
_cairo_surface_fallback_glyphs,
};
 
cairo_surface_t *
cairo_directfb_surface_create (IDirectFB *dfb, IDirectFBSurface *dfbsurface)
{
cairo_dfb_surface_t *surface;
DFBSurfacePixelFormat format;
DFBSurfaceCapabilities caps;
pixman_format_code_t pixman_format;
int width, height;
 
D_ASSERT (dfb != NULL);
D_ASSERT (dfbsurface != NULL);
 
dfbsurface->GetPixelFormat (dfbsurface, &format);
dfbsurface->GetSize (dfbsurface, &width, &height);
 
pixman_format = _directfb_to_pixman_format (format);
if (! pixman_format_supported_destination (pixman_format))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
surface = calloc (1, sizeof (cairo_dfb_surface_t));
if (surface == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
/* XXX dfb -> device */
_cairo_surface_init (&surface->image.base,
&_cairo_dfb_surface_backend,
NULL, /* device */
_directfb_format_to_content (format));
 
surface->image.pixman_format = pixman_format;
surface->image.format = _cairo_format_from_pixman_format (pixman_format);
 
surface->image.width = width;
surface->image.height = height;
surface->image.depth = PIXMAN_FORMAT_DEPTH(pixman_format);
 
surface->dfb = dfb;
surface->dfb_surface = dfbsurface;
dfbsurface->AddRef (dfbsurface);
 
dfbsurface->GetCapabilities (dfbsurface, &caps);
if (caps & DSCAPS_PREMULTIPLIED)
surface->blit_premultiplied = TRUE;
 
return &surface->image.base;
}
slim_hidden_def(cairo_directfb_surface_create);
/programs/develop/libraries/cairo/src/cairo-egl-context.c
0,0 → 1,311
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-error-private.h"
 
typedef struct _cairo_egl_context {
cairo_gl_context_t base;
 
EGLDisplay display;
EGLContext context;
 
EGLSurface dummy_surface;
 
EGLContext previous_context;
EGLSurface previous_surface;
} cairo_egl_context_t;
 
typedef struct _cairo_egl_surface {
cairo_gl_surface_t base;
 
EGLSurface egl;
} cairo_egl_surface_t;
 
 
static cairo_bool_t
_context_acquisition_changed_egl_state (cairo_egl_context_t *ctx,
EGLSurface current_surface)
{
return ctx->previous_context != ctx->context ||
ctx->previous_surface != current_surface;
}
 
static EGLSurface
_egl_get_current_surface (cairo_egl_context_t *ctx)
{
if (ctx->base.current_target == NULL ||
_cairo_gl_surface_is_texture (ctx->base.current_target)) {
return ctx->dummy_surface;
}
 
return ((cairo_egl_surface_t *) ctx->base.current_target)->egl;
}
 
static void
_egl_query_current_state (cairo_egl_context_t *ctx)
{
ctx->previous_surface = eglGetCurrentSurface (EGL_DRAW);
ctx->previous_context = eglGetCurrentContext ();
 
/* If any of the values were none, assume they are all none. Not all
drivers seem well behaved when it comes to using these values across
multiple threads. */
if (ctx->previous_surface == EGL_NO_SURFACE ||
ctx->previous_context == EGL_NO_CONTEXT) {
ctx->previous_surface = EGL_NO_SURFACE;
ctx->previous_context = EGL_NO_CONTEXT;
}
}
 
static void
_egl_acquire (void *abstract_ctx)
{
cairo_egl_context_t *ctx = abstract_ctx;
EGLSurface current_surface = _egl_get_current_surface (ctx);
 
_egl_query_current_state (ctx);
if (!_context_acquisition_changed_egl_state (ctx, current_surface))
return;
 
eglMakeCurrent (ctx->display,
current_surface, current_surface, ctx->context);
}
 
static void
_egl_release (void *abstract_ctx)
{
cairo_egl_context_t *ctx = abstract_ctx;
if (!ctx->base.thread_aware ||
!_context_acquisition_changed_egl_state (ctx,
_egl_get_current_surface (ctx))) {
return;
}
 
eglMakeCurrent (ctx->display,
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
 
static void
_egl_make_current (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
{
cairo_egl_context_t *ctx = abstract_ctx;
cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
 
eglMakeCurrent(ctx->display, surface->egl, surface->egl, ctx->context);
}
 
static void
_egl_swap_buffers (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
{
cairo_egl_context_t *ctx = abstract_ctx;
cairo_egl_surface_t *surface = (cairo_egl_surface_t *) abstract_surface;
 
eglSwapBuffers (ctx->display, surface->egl);
}
 
static void
_egl_destroy (void *abstract_ctx)
{
cairo_egl_context_t *ctx = abstract_ctx;
 
eglMakeCurrent (ctx->display,
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (ctx->dummy_surface != EGL_NO_SURFACE)
eglDestroySurface (ctx->display, ctx->dummy_surface);
}
 
static cairo_bool_t
_egl_make_current_surfaceless(cairo_egl_context_t *ctx)
{
const char *extensions;
 
extensions = eglQueryString(ctx->display, EGL_EXTENSIONS);
if (strstr(extensions, "EGL_KHR_surfaceless_context") == NULL &&
strstr(extensions, "EGL_KHR_surfaceless_opengl") == NULL)
return FALSE;
 
if (!eglMakeCurrent(ctx->display,
EGL_NO_SURFACE, EGL_NO_SURFACE, ctx->context))
return FALSE;
 
return TRUE;
}
 
cairo_device_t *
cairo_egl_device_create (EGLDisplay dpy, EGLContext egl)
{
cairo_egl_context_t *ctx;
cairo_status_t status;
int attribs[] = {
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE,
};
EGLConfig config;
EGLint numConfigs;
 
ctx = calloc (1, sizeof (cairo_egl_context_t));
if (unlikely (ctx == NULL))
return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
ctx->display = dpy;
ctx->context = egl;
 
ctx->base.acquire = _egl_acquire;
ctx->base.release = _egl_release;
ctx->base.make_current = _egl_make_current;
ctx->base.swap_buffers = _egl_swap_buffers;
ctx->base.destroy = _egl_destroy;
 
/* We are about the change the current state of EGL, so we should
* query the pre-existing surface now instead of later. */
_egl_query_current_state (ctx);
 
if (!_egl_make_current_surfaceless (ctx)) {
/* Fall back to dummy surface, meh. */
EGLint config_attribs[] = {
EGL_CONFIG_ID, 0,
EGL_NONE
};
 
/*
* In order to be able to make an egl context current when using a
* pbuffer surface, that surface must have been created with a config
* that is compatible with the context config. For Mesa, this means
* that the configs must be the same.
*/
eglQueryContext (dpy, egl, EGL_CONFIG_ID, &config_attribs[1]);
eglChooseConfig (dpy, config_attribs, &config, 1, &numConfigs);
 
ctx->dummy_surface = eglCreatePbufferSurface (dpy, config, attribs);
if (ctx->dummy_surface == NULL) {
free (ctx);
return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
if (!eglMakeCurrent (dpy, ctx->dummy_surface, ctx->dummy_surface, egl)) {
free (ctx);
return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
status = _cairo_gl_dispatch_init (&ctx->base.dispatch, eglGetProcAddress);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
status = _cairo_gl_context_init (&ctx->base);
if (unlikely (status)) {
if (ctx->dummy_surface != EGL_NO_SURFACE)
eglDestroySurface (dpy, ctx->dummy_surface);
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
 
return &ctx->base.base;
}
 
cairo_surface_t *
cairo_gl_surface_create_for_egl (cairo_device_t *device,
EGLSurface egl,
int width,
int height)
{
cairo_egl_surface_t *surface;
 
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
if (width <= 0 || height <= 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
surface = calloc (1, sizeof (cairo_egl_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_gl_surface_init (device, &surface->base,
CAIRO_CONTENT_COLOR_ALPHA, width, height);
surface->egl = egl;
 
return &surface->base.base;
}
 
static cairo_bool_t is_egl_device (cairo_device_t *device)
{
return (device->backend != NULL &&
device->backend->type == CAIRO_DEVICE_TYPE_GL);
}
 
static cairo_egl_context_t *to_egl_context (cairo_device_t *device)
{
return (cairo_egl_context_t *) device;
}
 
EGLDisplay
cairo_egl_device_get_display (cairo_device_t *device)
{
if (! is_egl_device (device)) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return EGL_NO_DISPLAY;
}
 
return to_egl_context (device)->display;
}
 
cairo_public EGLContext
cairo_egl_device_get_context (cairo_device_t *device)
{
if (! is_egl_device (device)) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return EGL_NO_CONTEXT;
}
 
return to_egl_context (device)->context;
}
/programs/develop/libraries/cairo/src/cairo-error-inline.h
0,0 → 1,52
/* 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>
*/
 
#ifndef _CAIRO_ERROR_INLINE_H_
#define _CAIRO_ERROR_INLINE_H_
 
#include "cairo-error-private.h"
 
CAIRO_BEGIN_DECLS
 
static inline cairo_status_t
_cairo_public_status (cairo_int_status_t status)
{
assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
return (cairo_status_t) status;
}
 
#endif /* _CAIRO_ERROR_INLINE_H_ */
/programs/develop/libraries/cairo/src/cairo-error-private.h
40,12 → 40,75
 
#include "cairo.h"
#include "cairo-compiler-private.h"
#include "cairo-types-private.h"
 
#include <assert.h>
 
CAIRO_BEGIN_DECLS
 
/* Sure wish C had a real enum type so that this would be distinct
* from #cairo_status_t. Oh well, without that, I'll use this bogus 100
* offset. We want to keep it fit in int8_t as the compiler may choose
* that for #cairo_status_t */
enum _cairo_int_status {
CAIRO_INT_STATUS_SUCCESS = 0,
 
CAIRO_INT_STATUS_NO_MEMORY,
CAIRO_INT_STATUS_INVALID_RESTORE,
CAIRO_INT_STATUS_INVALID_POP_GROUP,
CAIRO_INT_STATUS_NO_CURRENT_POINT,
CAIRO_INT_STATUS_INVALID_MATRIX,
CAIRO_INT_STATUS_INVALID_STATUS,
CAIRO_INT_STATUS_NULL_POINTER,
CAIRO_INT_STATUS_INVALID_STRING,
CAIRO_INT_STATUS_INVALID_PATH_DATA,
CAIRO_INT_STATUS_READ_ERROR,
CAIRO_INT_STATUS_WRITE_ERROR,
CAIRO_INT_STATUS_SURFACE_FINISHED,
CAIRO_INT_STATUS_SURFACE_TYPE_MISMATCH,
CAIRO_INT_STATUS_PATTERN_TYPE_MISMATCH,
CAIRO_INT_STATUS_INVALID_CONTENT,
CAIRO_INT_STATUS_INVALID_FORMAT,
CAIRO_INT_STATUS_INVALID_VISUAL,
CAIRO_INT_STATUS_FILE_NOT_FOUND,
CAIRO_INT_STATUS_INVALID_DASH,
CAIRO_INT_STATUS_INVALID_DSC_COMMENT,
CAIRO_INT_STATUS_INVALID_INDEX,
CAIRO_INT_STATUS_CLIP_NOT_REPRESENTABLE,
CAIRO_INT_STATUS_TEMP_FILE_ERROR,
CAIRO_INT_STATUS_INVALID_STRIDE,
CAIRO_INT_STATUS_FONT_TYPE_MISMATCH,
CAIRO_INT_STATUS_USER_FONT_IMMUTABLE,
CAIRO_INT_STATUS_USER_FONT_ERROR,
CAIRO_INT_STATUS_NEGATIVE_COUNT,
CAIRO_INT_STATUS_INVALID_CLUSTERS,
CAIRO_INT_STATUS_INVALID_SLANT,
CAIRO_INT_STATUS_INVALID_WEIGHT,
CAIRO_INT_STATUS_INVALID_SIZE,
CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED,
CAIRO_INT_STATUS_DEVICE_TYPE_MISMATCH,
CAIRO_INT_STATUS_DEVICE_ERROR,
CAIRO_INT_STATUS_INVALID_MESH_CONSTRUCTION,
CAIRO_INT_STATUS_DEVICE_FINISHED,
 
CAIRO_INT_STATUS_LAST_STATUS,
 
CAIRO_INT_STATUS_UNSUPPORTED = 100,
CAIRO_INT_STATUS_DEGENERATE,
CAIRO_INT_STATUS_NOTHING_TO_DO,
CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
CAIRO_INT_STATUS_IMAGE_FALLBACK,
CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
};
 
typedef enum _cairo_int_status cairo_int_status_t;
 
#define _cairo_status_is_error(status) \
(status != CAIRO_STATUS_SUCCESS && status <= CAIRO_STATUS_LAST_STATUS)
(status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS)
 
#define _cairo_int_status_is_error(status) \
(status != CAIRO_INT_STATUS_SUCCESS && status < CAIRO_INT_STATUS_LAST_STATUS)
 
cairo_private cairo_status_t
_cairo_error (cairo_status_t status);
 
/programs/develop/libraries/cairo/src/cairo-error.c
0,0 → 1,73
/* -*- 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>
*/
 
#include "cairoint.h"
#include "cairo-private.h"
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
 
#include <assert.h>
 
/**
* _cairo_error:
* @status: a status value indicating an error, (eg. not
* %CAIRO_STATUS_SUCCESS)
*
* Checks that status is an error status, but does nothing else.
*
* All assignments of an error status to any user-visible object
* within the cairo application should result in a call to
* _cairo_error().
*
* The purpose of this function is to allow the user to set a
* breakpoint in _cairo_error() to generate a stack trace for when the
* user causes cairo to detect an error.
*
* Return value: the error status.
**/
cairo_status_t
_cairo_error (cairo_status_t status)
{
CAIRO_ENSURE_UNIQUE;
assert (_cairo_status_is_error (status));
 
return status;
}
 
COMPILE_TIME_ASSERT ((int)CAIRO_INT_STATUS_LAST_STATUS == (int)CAIRO_STATUS_LAST_STATUS);
/programs/develop/libraries/cairo/src/cairo-fallback-compositor.c
0,0 → 1,185
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-compositor-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-offset-private.h"
 
/* high-level compositor interface */
 
static cairo_int_status_t
_cairo_fallback_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_image_surface_t *image;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
 
status = _cairo_surface_offset_paint (&image->base,
extents->unbounded.x,
extents->unbounded.y,
extents->op,
&extents->source_pattern.base,
extents->clip);
 
return _cairo_surface_unmap_image (extents->surface, image);
}
 
static cairo_int_status_t
_cairo_fallback_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_image_surface_t *image;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
 
status = _cairo_surface_offset_mask (&image->base,
extents->unbounded.x,
extents->unbounded.y,
extents->op,
&extents->source_pattern.base,
&extents->mask_pattern.base,
extents->clip);
 
return _cairo_surface_unmap_image (extents->surface, image);
}
 
static cairo_int_status_t
_cairo_fallback_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_image_surface_t *image;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
 
status = _cairo_surface_offset_stroke (&image->base,
extents->unbounded.x,
extents->unbounded.y,
extents->op,
&extents->source_pattern.base,
path, style,
ctm, ctm_inverse,
tolerance,
antialias,
extents->clip);
 
return _cairo_surface_unmap_image (extents->surface, image);
}
 
static cairo_int_status_t
_cairo_fallback_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_image_surface_t *image;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
 
status = _cairo_surface_offset_fill (&image->base,
extents->unbounded.x,
extents->unbounded.y,
extents->op,
&extents->source_pattern.base,
path,
fill_rule, tolerance, antialias,
extents->clip);
 
return _cairo_surface_unmap_image (extents->surface, image);
}
 
static cairo_int_status_t
_cairo_fallback_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_image_surface_t *image;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = _cairo_surface_map_to_image (extents->surface, &extents->unbounded);
 
status = _cairo_surface_offset_glyphs (&image->base,
extents->unbounded.x,
extents->unbounded.y,
extents->op,
&extents->source_pattern.base,
scaled_font, glyphs, num_glyphs,
extents->clip);
 
return _cairo_surface_unmap_image (extents->surface, image);
}
 
const cairo_compositor_t _cairo_fallback_compositor = {
&__cairo_no_compositor,
 
_cairo_fallback_compositor_paint,
_cairo_fallback_compositor_mask,
_cairo_fallback_compositor_stroke,
_cairo_fallback_compositor_fill,
_cairo_fallback_compositor_glyphs,
};
/programs/develop/libraries/cairo/src/cairo-features.h
2,26 → 2,14
#ifndef CAIRO_FEATURES_H
#define CAIRO_FEATURES_H
 
#define CAIRO_HAS_FT_FONT 1
#define CAIRO_HAS_PNG_FUNCTIONS 1
#define CAIRO_HAS_IMAGE_SURFACE 1
#define CAIRO_HAS_PNG_FUNCTIONS 1
#define CAIRO_HAS_RECORDING_SURFACE 1
#define CAIRO_HAS_SCRIPT_SURFACE 1
#define CAIRO_HAS_PDF_SURFACE 1
#define CAIRO_HAS_PS_SURFACE 1
//#define CAIRO_HAS_RECORDING_SURFACE 1
#define CAIRO_HAS_SVG_SURFACE 1
#define CAIRO_HAS_USER_FONT 1
 
/*#undef CAIRO_HAS_EGL_FUNCTIONS */
/*#undef CAIRO_HAS_FC_FONT */
/*#undef CAIRO_HAS_FT_FONT */
/*#undef CAIRO_HAS_GLX_FUNCTIONS */
/*#undef CAIRO_HAS_GOBJECT_FUNCTIONS */
/*#undef CAIRO_HAS_PDF_SURFACE */
/*#undef CAIRO_HAS_PS_SURFACE */
/*#undef CAIRO_HAS_QUARTZ_FONT */
/*#undef CAIRO_HAS_QUARTZ_SURFACE */
/*#undef CAIRO_HAS_WGL_FUNCTIONS */
/*#undef CAIRO_HAS_WIN32_FONT */
/*#undef CAIRO_HAS_WIN32_SURFACE */
/*#undef CAIRO_HAS_XCB_SHM_FUNCTIONS */
/*#undef CAIRO_HAS_XLIB_SURFACE */
/*#undef CAIRO_HAS_XLIB_XRENDER_SURFACE */
 
#endif
/programs/develop/libraries/cairo/src/cairo-fixed-private.h
40,6 → 40,7
#include "cairo-fixed-type-private.h"
 
#include "cairo-wideint-private.h"
#include "cairoint.h"
 
/* Implementation */
 
52,6 → 53,8
#define CAIRO_FIXED_ONE_DOUBLE ((double)(1 << CAIRO_FIXED_FRAC_BITS))
#define CAIRO_FIXED_EPSILON ((cairo_fixed_t)(1))
 
#define CAIRO_FIXED_ERROR_DOUBLE (1. / (2 * CAIRO_FIXED_ONE_DOUBLE))
 
#define CAIRO_FIXED_FRAC_MASK ((cairo_fixed_t)(((cairo_fixed_unsigned_t)(-1)) >> (CAIRO_FIXED_BITS - CAIRO_FIXED_FRAC_BITS)))
#define CAIRO_FIXED_WHOLE_MASK (~CAIRO_FIXED_FRAC_MASK)
 
164,6 → 167,12
}
 
static inline cairo_fixed_t
_cairo_fixed_ceil (cairo_fixed_t f)
{
return _cairo_fixed_floor (f + CAIRO_FIXED_FRAC_MASK);
}
 
static inline cairo_fixed_t
_cairo_fixed_round (cairo_fixed_t f)
{
return _cairo_fixed_floor (f + (CAIRO_FIXED_FRAC_MASK+1)/2);
344,6 → 353,40
return x;
}
 
/* Intersect two segments based on the algorithm described at
* http://paulbourke.net/geometry/pointlineplane/. This implementation
* uses floating point math. */
static inline cairo_bool_t
_slow_segment_intersection (const cairo_point_t *seg1_p1,
const cairo_point_t *seg1_p2,
const cairo_point_t *seg2_p1,
const cairo_point_t *seg2_p2,
cairo_point_t *intersection)
{
double denominator, u_a, u_b;
double seg1_dx, seg1_dy, seg2_dx, seg2_dy, seg_start_dx, seg_start_dy;
 
seg1_dx = _cairo_fixed_to_double (seg1_p2->x - seg1_p1->x);
seg1_dy = _cairo_fixed_to_double (seg1_p2->y - seg1_p1->y);
seg2_dx = _cairo_fixed_to_double (seg2_p2->x - seg2_p1->x);
seg2_dy = _cairo_fixed_to_double (seg2_p2->y - seg2_p1->y);
denominator = (seg2_dy * seg1_dx) - (seg2_dx * seg1_dy);
if (denominator == 0)
return FALSE;
 
seg_start_dx = _cairo_fixed_to_double (seg1_p1->x - seg2_p1->x);
seg_start_dy = _cairo_fixed_to_double (seg1_p1->y - seg2_p1->y);
u_a = ((seg2_dx * seg_start_dy) - (seg2_dy * seg_start_dx)) / denominator;
u_b = ((seg1_dx * seg_start_dy) - (seg1_dy * seg_start_dx)) / denominator;
 
if (u_a <= 0 || u_a >= 1 || u_b <= 0 || u_b >= 1)
return FALSE;
 
intersection->x = seg1_p1->x + _cairo_fixed_from_double ((u_a * seg1_dx));
intersection->y = seg1_p1->y + _cairo_fixed_from_double ((u_a * seg1_dy));
return TRUE;
}
 
#else
# error Please define multiplication and other operands for your fixed-point type size
#endif
/programs/develop/libraries/cairo/src/cairo-font-face-twin.c
283,16 → 283,14
parse_field (props, start, end - start);
}
 
static cairo_status_t
twin_font_face_create_properties (cairo_font_face_t *twin_face,
twin_face_properties_t **props_out)
static twin_face_properties_t *
twin_font_face_create_properties (cairo_font_face_t *twin_face)
{
twin_face_properties_t *props;
cairo_status_t status;
 
props = malloc (sizeof (twin_face_properties_t));
if (unlikely (props == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
return NULL;
 
props->stretch = TWIN_STRETCH_NORMAL;
props->slant = CAIRO_FONT_SLANT_NORMAL;
300,18 → 298,14
props->monospace = FALSE;
props->smallcaps = FALSE;
 
status = cairo_font_face_set_user_data (twin_face,
if (unlikely (cairo_font_face_set_user_data (twin_face,
&twin_properties_key,
props, free);
if (unlikely (status)) {
props, free))) {
free (props);
return status;
return NULL;
}
 
if (props_out)
*props_out = props;
 
return CAIRO_STATUS_SUCCESS;
return props;
}
 
static cairo_status_t
318,12 → 312,11
twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face,
cairo_toy_font_face_t *toy_face)
{
cairo_status_t status;
twin_face_properties_t *props;
 
status = twin_font_face_create_properties (twin_face, &props);
if (unlikely (status))
return status;
props = twin_font_face_create_properties (twin_face);
if (unlikely (props == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
props->slant = toy_face->slant;
props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ?
729,11 → 722,9
_cairo_font_face_twin_create_fallback (void)
{
cairo_font_face_t *twin_font_face;
cairo_status_t status;
 
twin_font_face = _cairo_font_face_twin_create_internal ();
status = twin_font_face_create_properties (twin_font_face, NULL);
if (status) {
if (! twin_font_face_create_properties (twin_font_face)) {
cairo_font_face_destroy (twin_font_face);
return (cairo_font_face_t *) &_cairo_font_face_nil;
}
/programs/develop/libraries/cairo/src/cairo-font-face.c
52,11 → 52,11
*
* Font faces are created using <firstterm>font-backend</firstterm>-specific
* constructors, typically of the form
* cairo_<emphasis>backend</emphasis>_font_face_create(), or implicitly
* using the <firstterm>toy</firstterm> text API by way of
* <function>cairo_<emphasis>backend</emphasis>_font_face_create(<!-- -->)</function>,
* or implicitly using the <firstterm>toy</firstterm> text API by way of
* cairo_select_font_face(). The resulting face can be accessed using
* cairo_get_font_face().
*/
**/
 
/* #cairo_font_face_t */
 
108,6 → 108,8
* cairo_font_face_get_reference_count().
*
* Return value: the referenced #cairo_font_face_t.
*
* Since: 1.0
**/
cairo_font_face_t *
cairo_font_face_reference (cairo_font_face_t *font_face)
133,6 → 135,8
* Decreases the reference count on @font_face by one. If the result
* is zero, then @font_face and all associated resources are freed.
* See cairo_font_face_reference().
*
* Since: 1.0
**/
void
cairo_font_face_destroy (cairo_font_face_t *font_face)
212,6 → 216,8
*
* Return value: %CAIRO_STATUS_SUCCESS or another error such as
* %CAIRO_STATUS_NO_MEMORY.
*
* Since: 1.0
**/
cairo_status_t
cairo_font_face_status (cairo_font_face_t *font_face)
230,6 → 236,8
* function returns %NULL.
*
* Return value: the user data previously attached or %NULL.
*
* Since: 1.0
**/
void *
cairo_font_face_get_user_data (cairo_font_face_t *font_face,
255,6 → 263,8
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
* slot could not be allocated for the user data.
*
* Since: 1.0
**/
cairo_status_t
cairo_font_face_set_user_data (cairo_font_face_t *font_face,
/programs/develop/libraries/cairo/src/cairo-font-options.c
47,7 → 47,7
* time the font options implied by a surface are just right and do not
* need any changes, but for pixel-based targets tweaking font options
* may result in superior output on a particular display.
*/
**/
 
static const cairo_font_options_t _cairo_font_options_nil = {
CAIRO_ANTIALIAS_DEFAULT,
54,7 → 54,8
CAIRO_SUBPIXEL_ORDER_DEFAULT,
CAIRO_LCD_FILTER_DEFAULT,
CAIRO_HINT_STYLE_DEFAULT,
CAIRO_HINT_METRICS_DEFAULT
CAIRO_HINT_METRICS_DEFAULT,
CAIRO_ROUND_GLYPH_POS_DEFAULT
};
 
/**
71,6 → 72,7
options->lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
options->hint_style = CAIRO_HINT_STYLE_DEFAULT;
options->hint_metrics = CAIRO_HINT_METRICS_DEFAULT;
options->round_glyph_positions = CAIRO_ROUND_GLYPH_POS_DEFAULT;
}
 
void
82,6 → 84,7
options->lcd_filter = other->lcd_filter;
options->hint_style = other->hint_style;
options->hint_metrics = other->hint_metrics;
options->round_glyph_positions = other->round_glyph_positions;
}
 
/**
95,6 → 98,8
* valid pointer; if memory cannot be allocated, then a special
* error object is returned where all operations on the object do nothing.
* You can check for this with cairo_font_options_status().
*
* Since: 1.0
**/
cairo_font_options_t *
cairo_font_options_create (void)
124,6 → 129,8
* valid pointer; if memory cannot be allocated, then a special
* error object is returned where all operations on the object do nothing.
* You can check for this with cairo_font_options_status().
*
* Since: 1.0
**/
cairo_font_options_t *
cairo_font_options_copy (const cairo_font_options_t *original)
150,6 → 157,8
*
* Destroys a #cairo_font_options_t object created with
* cairo_font_options_create() or cairo_font_options_copy().
*
* Since: 1.0
**/
void
cairo_font_options_destroy (cairo_font_options_t *options)
168,6 → 177,8
* font options object
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
*
* Since: 1.0
**/
cairo_status_t
cairo_font_options_status (cairo_font_options_t *options)
189,7 → 200,9
* Merges non-default options from @other into @options, replacing
* existing values. This operation can be thought of as somewhat
* similar to compositing @other onto @options with the operation
* of %CAIRO_OPERATION_OVER.
* of %CAIRO_OPERATOR_OVER.
*
* Since: 1.0
**/
void
cairo_font_options_merge (cairo_font_options_t *options,
211,6 → 224,8
options->hint_style = other->hint_style;
if (other->hint_metrics != CAIRO_HINT_METRICS_DEFAULT)
options->hint_metrics = other->hint_metrics;
if (other->round_glyph_positions != CAIRO_ROUND_GLYPH_POS_DEFAULT)
options->round_glyph_positions = other->round_glyph_positions;
}
slim_hidden_def (cairo_font_options_merge);
 
224,6 → 239,8
* Return value: %TRUE if all fields of the two font options objects match.
* Note that this function will return %FALSE if either object is in
* error.
*
* Since: 1.0
**/
cairo_bool_t
cairo_font_options_equal (const cairo_font_options_t *options,
241,7 → 258,8
options->subpixel_order == other->subpixel_order &&
options->lcd_filter == other->lcd_filter &&
options->hint_style == other->hint_style &&
options->hint_metrics == other->hint_metrics);
options->hint_metrics == other->hint_metrics &&
options->round_glyph_positions == other->round_glyph_positions);
}
slim_hidden_def (cairo_font_options_equal);
 
256,6 → 274,8
* Return value: the hash value for the font options object.
* The return value can be cast to a 32-bit type if a
* 32-bit hash value is needed.
*
* Since: 1.0
**/
unsigned long
cairo_font_options_hash (const cairo_font_options_t *options)
278,6 → 298,8
*
* Sets the antialiasing mode for the font options object. This
* specifies the type of antialiasing to do when rendering text.
*
* Since: 1.0
**/
void
cairo_font_options_set_antialias (cairo_font_options_t *options,
297,6 → 319,8
* Gets the antialiasing mode for the font options object.
*
* Return value: the antialiasing mode
*
* Since: 1.0
**/
cairo_antialias_t
cairo_font_options_get_antialias (const cairo_font_options_t *options)
317,6 → 341,8
* the display device when rendering with an antialiasing mode of
* %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
* #cairo_subpixel_order_t for full details.
*
* Since: 1.0
**/
void
cairo_font_options_set_subpixel_order (cairo_font_options_t *options,
337,6 → 363,8
* See the documentation for #cairo_subpixel_order_t for full details.
*
* Return value: the subpixel order for the font options object
*
* Since: 1.0
**/
cairo_subpixel_order_t
cairo_font_options_get_subpixel_order (const cairo_font_options_t *options)
356,8 → 384,6
* specifies how pixels are filtered when rendered with an antialiasing
* mode of %CAIRO_ANTIALIAS_SUBPIXEL. See the documentation for
* #cairo_lcd_filter_t for full details.
*
* Since: 1.8
**/
void
_cairo_font_options_set_lcd_filter (cairo_font_options_t *options,
377,8 → 403,6
* See the documentation for #cairo_lcd_filter_t for full details.
*
* Return value: the LCD filter for the font options object
*
* Since: 1.8
**/
cairo_lcd_filter_t
_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options)
390,6 → 414,41
}
 
/**
* _cairo_font_options_set_round_glyph_positions:
* @options: a #cairo_font_options_t
* @round: the new rounding value
*
* Sets the rounding options for the font options object. If rounding is set, a
* glyph's position will be rounded to integer values.
**/
void
_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options,
cairo_round_glyph_positions_t round)
{
if (cairo_font_options_status (options))
return;
 
options->round_glyph_positions = round;
}
 
/**
* _cairo_font_options_get_round_glyph_positions:
* @options: a #cairo_font_options_t
*
* Gets the glyph position rounding option for the font options object.
*
* Return value: The round glyph posistions flag for the font options object.
**/
cairo_round_glyph_positions_t
_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options)
{
if (cairo_font_options_status ((cairo_font_options_t *) options))
return CAIRO_ROUND_GLYPH_POS_DEFAULT;
 
return options->round_glyph_positions;
}
 
/**
* cairo_font_options_set_hint_style:
* @options: a #cairo_font_options_t
* @hint_style: the new hint style
398,6 → 457,8
* This controls whether to fit font outlines to the pixel grid,
* and if so, whether to optimize for fidelity or contrast.
* See the documentation for #cairo_hint_style_t for full details.
*
* Since: 1.0
**/
void
cairo_font_options_set_hint_style (cairo_font_options_t *options,
418,6 → 479,8
* See the documentation for #cairo_hint_style_t for full details.
*
* Return value: the hint style for the font options object
*
* Since: 1.0
**/
cairo_hint_style_t
cairo_font_options_get_hint_style (const cairo_font_options_t *options)
437,6 → 500,8
* controls whether metrics are quantized to integer values in
* device units.
* See the documentation for #cairo_hint_metrics_t for full details.
*
* Since: 1.0
**/
void
cairo_font_options_set_hint_metrics (cairo_font_options_t *options,
457,6 → 522,8
* See the documentation for #cairo_hint_metrics_t for full details.
*
* Return value: the metrics hinting mode for the font options object
*
* Since: 1.0
**/
cairo_hint_metrics_t
cairo_font_options_get_hint_metrics (const cairo_font_options_t *options)
/programs/develop/libraries/cairo/src/cairo-freed-pool-private.h
40,11 → 40,15
#include "cairoint.h"
#include "cairo-atomic-private.h"
 
#if HAS_ATOMIC_OPS
CAIRO_BEGIN_DECLS
 
#define DISABLE_FREED_POOLS 0
 
#if HAS_ATOMIC_OPS && ! DISABLE_FREED_POOLS
/* Keep a stash of recently freed clip_paths, since we need to
* reallocate them frequently.
*/
#define MAX_FREED_POOL_SIZE 4
#define MAX_FREED_POOL_SIZE 16
typedef struct {
void *pool[MAX_FREED_POOL_SIZE];
int top;
118,6 → 122,10
 
#else
 
/* A warning about an unused freed-pool in a build without atomics
* enabled usually indicates a missing _freed_pool_reset() in the
* static reset function */
 
typedef int freed_pool_t;
 
#define _freed_pool_get(pool) NULL
126,4 → 134,6
 
#endif
 
CAIRO_END_DECLS
 
#endif /* CAIRO_FREED_POOL_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-freelist-private.h
111,7 → 111,7
cairo_freelist_node_t *node;
 
node = freepool->first_free_node;
if (unlikely (node == NULL))
if (node == NULL)
return _cairo_freepool_alloc_from_pool (freepool);
 
VG (VALGRIND_MAKE_MEM_DEFINED (node, sizeof (node->next)));
/programs/develop/libraries/cairo/src/cairo-ft-font.c
0,0 → 1,3579
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2000 Keith Packard
* 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 Red Hat, Inc.
*
* Contributor(s):
* Graydon Hoare <graydon@redhat.com>
* Owen Taylor <otaylor@redhat.com>
* Keith Packard <keithp@keithp.com>
* Carl Worth <cworth@cworth.org>
*/
 
#define _BSD_SOURCE /* for strdup() */
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-ft-private.h"
#include "cairo-pattern-private.h"
#include "cairo-pixman-private.h"
 
#include <float.h>
 
#include "cairo-fontconfig-private.h"
 
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_IMAGE_H
#include FT_BITMAP_H
#include FT_TRUETYPE_TABLES_H
#include FT_XFREE86_H
#if HAVE_FT_GLYPHSLOT_EMBOLDEN
#include FT_SYNTHESIS_H
#endif
 
#if HAVE_FT_LIBRARY_SETLCDFILTER
#include FT_LCD_FILTER_H
#endif
 
#if HAVE_UNISTD_H
#include <unistd.h>
#else
#define access(p, m) 0
#endif
 
/* Fontconfig version older than 2.6 didn't have these options */
#ifndef FC_LCD_FILTER
#define FC_LCD_FILTER "lcdfilter"
#endif
/* Some Ubuntu versions defined FC_LCD_FILTER without defining the following */
#ifndef FC_LCD_NONE
#define FC_LCD_NONE 0
#define FC_LCD_DEFAULT 1
#define FC_LCD_LIGHT 2
#define FC_LCD_LEGACY 3
#endif
 
/* FreeType version older than 2.3.5(?) didn't have these options */
#ifndef FT_LCD_FILTER_NONE
#define FT_LCD_FILTER_NONE 0
#define FT_LCD_FILTER_DEFAULT 1
#define FT_LCD_FILTER_LIGHT 2
#define FT_LCD_FILTER_LEGACY 16
#endif
 
#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)
 
/* This is the max number of FT_face objects we keep open at once
*/
#define MAX_OPEN_FACES 10
 
/**
* SECTION:cairo-ft
* @Title: FreeType Fonts
* @Short_Description: Font support for FreeType
* @See_Also: #cairo_font_face_t
*
* The FreeType font backend is primarily used to render text on GNU/Linux
* systems, but can be used on other platforms too.
**/
 
/**
* CAIRO_HAS_FT_FONT:
*
* Defined if the FreeType font backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.0
**/
 
/**
* CAIRO_HAS_FC_FONT:
*
* Defined if the Fontconfig-specific functions of the FreeType font backend
* are available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.10
**/
 
/*
* The simple 2x2 matrix is converted into separate scale and shape
* factors so that hinting works right
*/
 
typedef struct _cairo_ft_font_transform {
double x_scale, y_scale;
double shape[2][2];
} cairo_ft_font_transform_t;
 
/*
* We create an object that corresponds to a single font on the disk;
* (identified by a filename/id pair) these are shared between all
* fonts using that file. For cairo_ft_font_face_create_for_ft_face(), we
* just create a one-off version with a permanent face value.
*/
 
typedef struct _cairo_ft_font_face cairo_ft_font_face_t;
 
struct _cairo_ft_unscaled_font {
cairo_unscaled_font_t base;
 
cairo_bool_t from_face; /* was the FT_Face provided by user? */
FT_Face face; /* provided or cached face */
 
/* only set if from_face is false */
char *filename;
int id;
 
/* We temporarily scale the unscaled font as needed */
cairo_bool_t have_scale;
cairo_matrix_t current_scale;
double x_scale; /* Extracted X scale factor */
double y_scale; /* Extracted Y scale factor */
cairo_bool_t have_shape; /* true if the current scale has a non-scale component*/
cairo_matrix_t current_shape;
FT_Matrix Current_Shape;
 
cairo_mutex_t mutex;
int lock_count;
 
cairo_ft_font_face_t *faces; /* Linked list of faces for this font */
};
 
static int
_cairo_ft_unscaled_font_keys_equal (const void *key_a,
const void *key_b);
 
static void
_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled);
 
typedef struct _cairo_ft_options {
cairo_font_options_t base;
unsigned int load_flags; /* flags for FT_Load_Glyph */
unsigned int synth_flags;
} cairo_ft_options_t;
 
struct _cairo_ft_font_face {
cairo_font_face_t base;
 
cairo_ft_unscaled_font_t *unscaled;
cairo_ft_options_t ft_options;
cairo_ft_font_face_t *next;
 
#if CAIRO_HAS_FC_FONT
FcPattern *pattern; /* if pattern is set, the above fields will be NULL */
cairo_font_face_t *resolved_font_face;
FcConfig *resolved_config;
#endif
};
 
static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend;
 
#if CAIRO_HAS_FC_FONT
static cairo_status_t
_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
FcPattern *pattern);
 
static cairo_font_face_t *
_cairo_ft_resolve_pattern (FcPattern *pattern,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options);
 
#endif
 
/*
* We maintain a hash table to map file/id => #cairo_ft_unscaled_font_t.
* The hash table itself isn't limited in size. However, we limit the
* number of FT_Face objects we keep around; when we've exceeded that
* limit and need to create a new FT_Face, we dump the FT_Face from a
* random #cairo_ft_unscaled_font_t which has an unlocked FT_Face, (if
* there are any).
*/
 
typedef struct _cairo_ft_unscaled_font_map {
cairo_hash_table_t *hash_table;
FT_Library ft_library;
int num_open_faces;
} cairo_ft_unscaled_font_map_t;
 
static cairo_ft_unscaled_font_map_t *cairo_ft_unscaled_font_map = NULL;
 
 
static FT_Face
_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled);
 
static void
_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled);
 
static cairo_bool_t
_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font);
 
 
static void
_font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
cairo_ft_unscaled_font_t *unscaled)
{
if (unscaled->face) {
FT_Done_Face (unscaled->face);
unscaled->face = NULL;
unscaled->have_scale = FALSE;
 
font_map->num_open_faces--;
}
}
 
static cairo_status_t
_cairo_ft_unscaled_font_map_create (void)
{
cairo_ft_unscaled_font_map_t *font_map;
 
/* This function is only intended to be called from
* _cairo_ft_unscaled_font_map_lock. So we'll crash if we can
* detect some other call path. */
assert (cairo_ft_unscaled_font_map == NULL);
 
font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t));
if (unlikely (font_map == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font_map->hash_table =
_cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal);
 
if (unlikely (font_map->hash_table == NULL))
goto FAIL;
 
if (unlikely (FT_Init_FreeType (&font_map->ft_library)))
goto FAIL;
 
font_map->num_open_faces = 0;
 
cairo_ft_unscaled_font_map = font_map;
return CAIRO_STATUS_SUCCESS;
 
FAIL:
if (font_map->hash_table)
_cairo_hash_table_destroy (font_map->hash_table);
free (font_map);
 
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
 
static void
_cairo_ft_unscaled_font_map_pluck_entry (void *entry, void *closure)
{
cairo_ft_unscaled_font_t *unscaled = entry;
cairo_ft_unscaled_font_map_t *font_map = closure;
 
_cairo_hash_table_remove (font_map->hash_table,
&unscaled->base.hash_entry);
 
if (! unscaled->from_face)
_font_map_release_face_lock_held (font_map, unscaled);
 
_cairo_ft_unscaled_font_fini (unscaled);
free (unscaled);
}
 
static void
_cairo_ft_unscaled_font_map_destroy (void)
{
cairo_ft_unscaled_font_map_t *font_map;
 
CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex);
font_map = cairo_ft_unscaled_font_map;
cairo_ft_unscaled_font_map = NULL;
CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
 
if (font_map != NULL) {
_cairo_hash_table_foreach (font_map->hash_table,
_cairo_ft_unscaled_font_map_pluck_entry,
font_map);
assert (font_map->num_open_faces == 0);
 
FT_Done_FreeType (font_map->ft_library);
 
_cairo_hash_table_destroy (font_map->hash_table);
 
free (font_map);
}
}
 
static cairo_ft_unscaled_font_map_t *
_cairo_ft_unscaled_font_map_lock (void)
{
CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex);
 
if (unlikely (cairo_ft_unscaled_font_map == NULL)) {
if (unlikely (_cairo_ft_unscaled_font_map_create ())) {
CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
return NULL;
}
}
 
return cairo_ft_unscaled_font_map;
}
 
static void
_cairo_ft_unscaled_font_map_unlock (void)
{
CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
}
 
static void
_cairo_ft_unscaled_font_init_key (cairo_ft_unscaled_font_t *key,
cairo_bool_t from_face,
char *filename,
int id,
FT_Face face)
{
unsigned long hash;
 
key->from_face = from_face;
key->filename = filename;
key->id = id;
key->face = face;
 
hash = _cairo_hash_string (filename);
/* the constants are just arbitrary primes */
hash += ((unsigned long) id) * 1607;
hash += ((unsigned long) face) * 2137;
 
key->base.hash_entry.hash = hash;
}
 
/**
* _cairo_ft_unscaled_font_init:
*
* Initialize a #cairo_ft_unscaled_font_t.
*
* There are two basic flavors of #cairo_ft_unscaled_font_t, one
* created from an FT_Face and the other created from a filename/id
* pair. These two flavors are identified as from_face and !from_face.
*
* To initialize a from_face font, pass filename==%NULL, id=0 and the
* desired face.
*
* To initialize a !from_face font, pass the filename/id as desired
* and face==%NULL.
*
* Note that the code handles these two flavors in very distinct
* ways. For example there is a hash_table mapping
* filename/id->#cairo_unscaled_font_t in the !from_face case, but no
* parallel in the from_face case, (where the calling code would have
* to do its own mapping to ensure similar sharing).
**/
static cairo_status_t
_cairo_ft_unscaled_font_init (cairo_ft_unscaled_font_t *unscaled,
cairo_bool_t from_face,
const char *filename,
int id,
FT_Face face)
{
_cairo_unscaled_font_init (&unscaled->base,
&cairo_ft_unscaled_font_backend);
 
if (from_face) {
unscaled->from_face = TRUE;
_cairo_ft_unscaled_font_init_key (unscaled, TRUE, NULL, 0, face);
} else {
char *filename_copy;
 
unscaled->from_face = FALSE;
unscaled->face = NULL;
 
filename_copy = strdup (filename);
if (unlikely (filename_copy == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_ft_unscaled_font_init_key (unscaled, FALSE, filename_copy, id, NULL);
}
 
unscaled->have_scale = FALSE;
CAIRO_MUTEX_INIT (unscaled->mutex);
unscaled->lock_count = 0;
 
unscaled->faces = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_ft_unscaled_font_fini:
*
* Free all data associated with a #cairo_ft_unscaled_font_t.
*
* CAUTION: The unscaled->face field must be %NULL before calling this
* function. This is because the #cairo_ft_unscaled_font_t_map keeps a
* count of these faces (font_map->num_open_faces) so it maintains the
* unscaled->face field while it has its lock held. See
* _font_map_release_face_lock_held().
**/
static void
_cairo_ft_unscaled_font_fini (cairo_ft_unscaled_font_t *unscaled)
{
assert (unscaled->face == NULL);
 
free (unscaled->filename);
unscaled->filename = NULL;
 
CAIRO_MUTEX_FINI (unscaled->mutex);
}
 
static int
_cairo_ft_unscaled_font_keys_equal (const void *key_a,
const void *key_b)
{
const cairo_ft_unscaled_font_t *unscaled_a = key_a;
const cairo_ft_unscaled_font_t *unscaled_b = key_b;
 
if (unscaled_a->id == unscaled_b->id &&
unscaled_a->from_face == unscaled_b->from_face)
{
if (unscaled_a->from_face)
return unscaled_a->face == unscaled_b->face;
 
if (unscaled_a->filename == NULL && unscaled_b->filename == NULL)
return TRUE;
else if (unscaled_a->filename == NULL || unscaled_b->filename == NULL)
return FALSE;
else
return (strcmp (unscaled_a->filename, unscaled_b->filename) == 0);
}
 
return FALSE;
}
 
/* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from
* pattern. Returns a new reference to the unscaled font.
*/
static cairo_status_t
_cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face,
char *filename,
int id,
FT_Face font_face,
cairo_ft_unscaled_font_t **out)
{
cairo_ft_unscaled_font_t key, *unscaled;
cairo_ft_unscaled_font_map_t *font_map;
cairo_status_t status;
 
font_map = _cairo_ft_unscaled_font_map_lock ();
if (unlikely (font_map == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face);
 
/* Return existing unscaled font if it exists in the hash table. */
unscaled = _cairo_hash_table_lookup (font_map->hash_table,
&key.base.hash_entry);
if (unscaled != NULL) {
_cairo_unscaled_font_reference (&unscaled->base);
goto DONE;
}
 
/* Otherwise create it and insert into hash table. */
unscaled = malloc (sizeof (cairo_ft_unscaled_font_t));
if (unlikely (unscaled == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto UNWIND_FONT_MAP_LOCK;
}
 
status = _cairo_ft_unscaled_font_init (unscaled, from_face, filename, id, font_face);
if (unlikely (status))
goto UNWIND_UNSCALED_MALLOC;
 
assert (unscaled->base.hash_entry.hash == key.base.hash_entry.hash);
status = _cairo_hash_table_insert (font_map->hash_table,
&unscaled->base.hash_entry);
if (unlikely (status))
goto UNWIND_UNSCALED_FONT_INIT;
 
DONE:
_cairo_ft_unscaled_font_map_unlock ();
*out = unscaled;
return CAIRO_STATUS_SUCCESS;
 
UNWIND_UNSCALED_FONT_INIT:
_cairo_ft_unscaled_font_fini (unscaled);
UNWIND_UNSCALED_MALLOC:
free (unscaled);
UNWIND_FONT_MAP_LOCK:
_cairo_ft_unscaled_font_map_unlock ();
return status;
}
 
 
#if CAIRO_HAS_FC_FONT
static cairo_status_t
_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern,
cairo_ft_unscaled_font_t **out)
{
FT_Face font_face = NULL;
char *filename = NULL;
int id = 0;
FcResult ret;
 
ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face);
if (ret == FcResultMatch)
goto DONE;
if (ret == FcResultOutOfMemory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
ret = FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename);
if (ret == FcResultOutOfMemory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (ret == FcResultMatch) {
if (access (filename, R_OK) == 0) {
/* If FC_INDEX is not set, we just use 0 */
ret = FcPatternGetInteger (pattern, FC_INDEX, 0, &id);
if (ret == FcResultOutOfMemory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
goto DONE;
} else
return _cairo_error (CAIRO_STATUS_FILE_NOT_FOUND);
}
 
/* The pattern contains neither a face nor a filename, resolve it later. */
*out = NULL;
return CAIRO_STATUS_SUCCESS;
 
DONE:
return _cairo_ft_unscaled_font_create_internal (font_face != NULL,
filename, id, font_face,
out);
}
#endif
 
static cairo_status_t
_cairo_ft_unscaled_font_create_from_face (FT_Face face,
cairo_ft_unscaled_font_t **out)
{
return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out);
}
 
static void
_cairo_ft_unscaled_font_destroy (void *abstract_font)
{
cairo_ft_unscaled_font_t *unscaled = abstract_font;
cairo_ft_unscaled_font_map_t *font_map;
 
if (unscaled == NULL)
return;
 
font_map = _cairo_ft_unscaled_font_map_lock ();
/* All created objects must have been mapped in the font map. */
assert (font_map != NULL);
 
if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) {
/* somebody recreated the font whilst we waited for the lock */
_cairo_ft_unscaled_font_map_unlock ();
return;
}
 
_cairo_hash_table_remove (font_map->hash_table,
&unscaled->base.hash_entry);
 
if (unscaled->from_face) {
/* See comments in _ft_font_face_destroy about the "zombie" state
* for a _ft_font_face.
*/
if (unscaled->faces && unscaled->faces->unscaled == NULL) {
assert (unscaled->faces->next == NULL);
cairo_font_face_destroy (&unscaled->faces->base);
}
} else {
_font_map_release_face_lock_held (font_map, unscaled);
}
unscaled->face = NULL;
 
_cairo_ft_unscaled_font_map_unlock ();
 
_cairo_ft_unscaled_font_fini (unscaled);
}
 
static cairo_bool_t
_has_unlocked_face (const void *entry)
{
const cairo_ft_unscaled_font_t *unscaled = entry;
 
return (!unscaled->from_face && unscaled->lock_count == 0 && unscaled->face);
}
 
/* Ensures that an unscaled font has a face object. If we exceed
* MAX_OPEN_FACES, try to close some.
*
* This differs from _cairo_ft_scaled_font_lock_face in that it doesn't
* set the scale on the face, but just returns it at the last scale.
*/
static cairo_warn FT_Face
_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled)
{
cairo_ft_unscaled_font_map_t *font_map;
FT_Face face = NULL;
 
CAIRO_MUTEX_LOCK (unscaled->mutex);
unscaled->lock_count++;
 
if (unscaled->face)
return unscaled->face;
 
/* If this unscaled font was created from an FT_Face then we just
* returned it above. */
assert (!unscaled->from_face);
 
font_map = _cairo_ft_unscaled_font_map_lock ();
{
assert (font_map != NULL);
 
while (font_map->num_open_faces >= MAX_OPEN_FACES)
{
cairo_ft_unscaled_font_t *entry;
 
entry = _cairo_hash_table_random_entry (font_map->hash_table,
_has_unlocked_face);
if (entry == NULL)
break;
 
_font_map_release_face_lock_held (font_map, entry);
}
}
_cairo_ft_unscaled_font_map_unlock ();
 
if (FT_New_Face (font_map->ft_library,
unscaled->filename,
unscaled->id,
&face) != FT_Err_Ok)
{
unscaled->lock_count--;
CAIRO_MUTEX_UNLOCK (unscaled->mutex);
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
unscaled->face = face;
 
font_map->num_open_faces++;
 
return face;
}
 
 
/* Unlock unscaled font locked with _cairo_ft_unscaled_font_lock_face
*/
static void
_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled)
{
assert (unscaled->lock_count > 0);
 
unscaled->lock_count--;
 
CAIRO_MUTEX_UNLOCK (unscaled->mutex);
}
 
 
static cairo_status_t
_compute_transform (cairo_ft_font_transform_t *sf,
cairo_matrix_t *scale,
cairo_ft_unscaled_font_t *unscaled)
{
cairo_status_t status;
double x_scale, y_scale;
cairo_matrix_t normalized = *scale;
 
/* The font matrix has x and y "scale" components which we extract and
* use as character scale values. These influence the way freetype
* chooses hints, as well as selecting different bitmaps in
* hand-rendered fonts. We also copy the normalized matrix to
* freetype's transformation.
*/
 
status = _cairo_matrix_compute_basis_scale_factors (scale,
&x_scale, &y_scale,
1);
if (unlikely (status))
return status;
 
/* FreeType docs say this about x_scale and y_scale:
* "A character width or height smaller than 1pt is set to 1pt;"
* So, we cap them from below at 1.0 and let the FT transform
* take care of sub-1.0 scaling. */
if (x_scale < 1.0)
x_scale = 1.0;
if (y_scale < 1.0)
y_scale = 1.0;
 
if (unscaled && (unscaled->face->face_flags & FT_FACE_FLAG_SCALABLE) == 0) {
double min_distance = DBL_MAX;
cairo_bool_t magnify = TRUE;
int i;
int best_i = 0;
double best_x_size = 0;
double best_y_size = 0;
 
for (i = 0; i < unscaled->face->num_fixed_sizes; i++) {
double x_size = unscaled->face->available_sizes[i].y_ppem / 64.;
double y_size = unscaled->face->available_sizes[i].y_ppem / 64.;
double distance = y_size - y_scale;
 
/*
* distance is positive if current strike is larger than desired
* size, and negative if smaller.
*
* We like to prefer down-scaling to upscaling.
*/
 
if ((magnify && distance >= 0) || fabs (distance) <= min_distance) {
magnify = distance < 0;
min_distance = fabs (distance);
best_i = i;
best_x_size = x_size;
best_y_size = y_size;
}
}
 
x_scale = best_x_size;
y_scale = best_y_size;
}
 
sf->x_scale = x_scale;
sf->y_scale = y_scale;
 
cairo_matrix_scale (&normalized, 1.0 / x_scale, 1.0 / y_scale);
 
_cairo_matrix_get_affine (&normalized,
&sf->shape[0][0], &sf->shape[0][1],
&sf->shape[1][0], &sf->shape[1][1],
NULL, NULL);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Temporarily scales an unscaled font to the give scale. We catch
* scaling to the same size, since changing a FT_Face is expensive.
*/
static cairo_status_t
_cairo_ft_unscaled_font_set_scale (cairo_ft_unscaled_font_t *unscaled,
cairo_matrix_t *scale)
{
cairo_status_t status;
cairo_ft_font_transform_t sf;
FT_Matrix mat;
FT_Error error;
 
assert (unscaled->face != NULL);
 
if (unscaled->have_scale &&
scale->xx == unscaled->current_scale.xx &&
scale->yx == unscaled->current_scale.yx &&
scale->xy == unscaled->current_scale.xy &&
scale->yy == unscaled->current_scale.yy)
return CAIRO_STATUS_SUCCESS;
 
unscaled->have_scale = TRUE;
unscaled->current_scale = *scale;
 
status = _compute_transform (&sf, scale, unscaled);
if (unlikely (status))
return status;
 
unscaled->x_scale = sf.x_scale;
unscaled->y_scale = sf.y_scale;
 
mat.xx = DOUBLE_TO_16_16(sf.shape[0][0]);
mat.yx = - DOUBLE_TO_16_16(sf.shape[0][1]);
mat.xy = - DOUBLE_TO_16_16(sf.shape[1][0]);
mat.yy = DOUBLE_TO_16_16(sf.shape[1][1]);
 
unscaled->have_shape = (mat.xx != 0x10000 ||
mat.yx != 0x00000 ||
mat.xy != 0x00000 ||
mat.yy != 0x10000);
 
unscaled->Current_Shape = mat;
cairo_matrix_init (&unscaled->current_shape,
sf.shape[0][0], sf.shape[0][1],
sf.shape[1][0], sf.shape[1][1],
0.0, 0.0);
 
FT_Set_Transform(unscaled->face, &mat, NULL);
 
error = FT_Set_Char_Size (unscaled->face,
sf.x_scale * 64.0 + .5,
sf.y_scale * 64.0 + .5,
0, 0);
if (error)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* we sometimes need to convert the glyph bitmap in a FT_GlyphSlot
* into a different format. For example, we want to convert a
* FT_PIXEL_MODE_LCD or FT_PIXEL_MODE_LCD_V bitmap into a 32-bit
* ARGB or ABGR bitmap.
*
* this function prepares a target descriptor for this operation.
*
* input :: target bitmap descriptor. The function will set its
* 'width', 'rows' and 'pitch' fields, and only these
*
* slot :: the glyph slot containing the source bitmap. this
* function assumes that slot->format == FT_GLYPH_FORMAT_BITMAP
*
* mode :: the requested final rendering mode. supported values are
* MONO, NORMAL (i.e. gray), LCD and LCD_V
*
* the function returns the size in bytes of the corresponding buffer,
* it's up to the caller to allocate the corresponding memory block
* before calling _fill_xrender_bitmap
*
* it also returns -1 in case of error (e.g. incompatible arguments,
* like trying to convert a gray bitmap into a monochrome one)
*/
static int
_compute_xrender_bitmap_size(FT_Bitmap *target,
FT_GlyphSlot slot,
FT_Render_Mode mode)
{
FT_Bitmap *ftbit;
int width, height, pitch;
 
if (slot->format != FT_GLYPH_FORMAT_BITMAP)
return -1;
 
/* compute the size of the final bitmap */
ftbit = &slot->bitmap;
 
width = ftbit->width;
height = ftbit->rows;
pitch = (width + 3) & ~3;
 
switch (ftbit->pixel_mode) {
case FT_PIXEL_MODE_MONO:
if (mode == FT_RENDER_MODE_MONO) {
pitch = (((width + 31) & ~31) >> 3);
break;
}
/* fall-through */
 
case FT_PIXEL_MODE_GRAY:
if (mode == FT_RENDER_MODE_LCD ||
mode == FT_RENDER_MODE_LCD_V)
{
/* each pixel is replicated into a 32-bit ARGB value */
pitch = width * 4;
}
break;
 
case FT_PIXEL_MODE_LCD:
if (mode != FT_RENDER_MODE_LCD)
return -1;
 
/* horz pixel triplets are packed into 32-bit ARGB values */
width /= 3;
pitch = width * 4;
break;
 
case FT_PIXEL_MODE_LCD_V:
if (mode != FT_RENDER_MODE_LCD_V)
return -1;
 
/* vert pixel triplets are packed into 32-bit ARGB values */
height /= 3;
pitch = width * 4;
break;
 
default: /* unsupported source format */
return -1;
}
 
target->width = width;
target->rows = height;
target->pitch = pitch;
target->buffer = NULL;
 
return pitch * height;
}
 
/* this functions converts the glyph bitmap found in a FT_GlyphSlot
* into a different format (see _compute_xrender_bitmap_size)
*
* you should call this function after _compute_xrender_bitmap_size
*
* target :: target bitmap descriptor. Note that its 'buffer' pointer
* must point to memory allocated by the caller
*
* slot :: the glyph slot containing the source bitmap
*
* mode :: the requested final rendering mode
*
* bgr :: boolean, set if BGR or VBGR pixel ordering is needed
*/
static void
_fill_xrender_bitmap(FT_Bitmap *target,
FT_GlyphSlot slot,
FT_Render_Mode mode,
int bgr)
{
FT_Bitmap *ftbit = &slot->bitmap;
unsigned char *srcLine = ftbit->buffer;
unsigned char *dstLine = target->buffer;
int src_pitch = ftbit->pitch;
int width = target->width;
int height = target->rows;
int pitch = target->pitch;
int subpixel;
int h;
 
subpixel = (mode == FT_RENDER_MODE_LCD ||
mode == FT_RENDER_MODE_LCD_V);
 
if (src_pitch < 0)
srcLine -= src_pitch * (ftbit->rows - 1);
 
target->pixel_mode = ftbit->pixel_mode;
 
switch (ftbit->pixel_mode) {
case FT_PIXEL_MODE_MONO:
if (subpixel) {
/* convert mono to ARGB32 values */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
int x;
 
for (x = 0; x < width; x++) {
if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
((unsigned int *) dstLine)[x] = 0xffffffffU;
}
}
target->pixel_mode = FT_PIXEL_MODE_LCD;
 
} else if (mode == FT_RENDER_MODE_NORMAL) {
/* convert mono to 8-bit gray */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
int x;
 
for (x = 0; x < width; x++) {
if (srcLine[(x >> 3)] & (0x80 >> (x & 7)))
dstLine[x] = 0xff;
}
}
target->pixel_mode = FT_PIXEL_MODE_GRAY;
 
} else {
/* copy mono to mono */
 
int bytes = (width + 7) >> 3;
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
memcpy (dstLine, srcLine, bytes);
}
break;
 
case FT_PIXEL_MODE_GRAY:
if (subpixel) {
/* convert gray to ARGB32 values */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
int x;
unsigned int *dst = (unsigned int *) dstLine;
 
for (x = 0; x < width; x++) {
unsigned int pix = srcLine[x];
 
pix |= (pix << 8);
pix |= (pix << 16);
 
dst[x] = pix;
}
}
target->pixel_mode = FT_PIXEL_MODE_LCD;
} else {
/* copy gray into gray */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch)
memcpy (dstLine, srcLine, width);
}
break;
 
case FT_PIXEL_MODE_LCD:
if (!bgr) {
/* convert horizontal RGB into ARGB32 */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
int x;
unsigned char *src = srcLine;
unsigned int *dst = (unsigned int *) dstLine;
 
for (x = 0; x < width; x++, src += 3) {
unsigned int pix;
 
pix = ((unsigned int)src[0] << 16) |
((unsigned int)src[1] << 8) |
((unsigned int)src[2] ) |
((unsigned int)src[1] << 24) ;
 
dst[x] = pix;
}
}
} else {
/* convert horizontal BGR into ARGB32 */
 
for (h = height; h > 0; h--, srcLine += src_pitch, dstLine += pitch) {
 
int x;
unsigned char *src = srcLine;
unsigned int *dst = (unsigned int *) dstLine;
 
for (x = 0; x < width; x++, src += 3) {
unsigned int pix;
 
pix = ((unsigned int)src[2] << 16) |
((unsigned int)src[1] << 8) |
((unsigned int)src[0] ) |
((unsigned int)src[1] << 24) ;
 
dst[x] = pix;
}
}
}
break;
 
default: /* FT_PIXEL_MODE_LCD_V */
/* convert vertical RGB into ARGB32 */
if (!bgr) {
 
for (h = height; h > 0; h--, srcLine += 3 * src_pitch, dstLine += pitch) {
int x;
unsigned char* src = srcLine;
unsigned int* dst = (unsigned int *) dstLine;
 
for (x = 0; x < width; x++, src += 1) {
unsigned int pix;
pix = ((unsigned int)src[0] << 16) |
((unsigned int)src[src_pitch] << 8) |
((unsigned int)src[src_pitch*2] ) |
((unsigned int)src[src_pitch] << 24) ;
dst[x] = pix;
}
}
} else {
 
for (h = height; h > 0; h--, srcLine += 3*src_pitch, dstLine += pitch) {
int x;
unsigned char *src = srcLine;
unsigned int *dst = (unsigned int *) dstLine;
 
for (x = 0; x < width; x++, src += 1) {
unsigned int pix;
 
pix = ((unsigned int)src[src_pitch * 2] << 16) |
((unsigned int)src[src_pitch] << 8) |
((unsigned int)src[0] ) |
((unsigned int)src[src_pitch] << 24) ;
 
dst[x] = pix;
}
}
}
}
}
 
 
/* Fills in val->image with an image surface created from @bitmap
*/
static cairo_status_t
_get_bitmap_surface (FT_Bitmap *bitmap,
FT_Library library,
cairo_bool_t own_buffer,
cairo_font_options_t *font_options,
cairo_image_surface_t **surface)
{
unsigned int width, height;
unsigned char *data;
int format = CAIRO_FORMAT_A8;
int stride;
cairo_image_surface_t *image;
cairo_bool_t component_alpha = FALSE;
 
width = bitmap->width;
height = bitmap->rows;
 
if (width == 0 || height == 0) {
*surface = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
return (*surface)->base.status;
}
 
switch (bitmap->pixel_mode) {
case FT_PIXEL_MODE_MONO:
stride = (((width + 31) & ~31) >> 3);
if (own_buffer) {
data = bitmap->buffer;
assert (stride == bitmap->pitch);
} else {
data = _cairo_malloc_ab (height, stride);
if (!data)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (stride == bitmap->pitch) {
memcpy (data, bitmap->buffer, stride * height);
} else {
int i;
unsigned char *source, *dest;
 
source = bitmap->buffer;
dest = data;
for (i = height; i; i--) {
memcpy (dest, source, stride);
source += bitmap->pitch;
dest += stride;
}
}
}
 
#ifndef WORDS_BIGENDIAN
{
uint8_t *d = data;
int count = stride * height;
 
while (count--) {
*d = CAIRO_BITSWAP8 (*d);
d++;
}
}
#endif
format = CAIRO_FORMAT_A1;
break;
 
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
case FT_PIXEL_MODE_GRAY:
if (font_options->antialias != CAIRO_ANTIALIAS_SUBPIXEL ||
bitmap->pixel_mode == FT_PIXEL_MODE_GRAY)
{
stride = bitmap->pitch;
 
/* We don't support stride not multiple of 4. */
if (stride & 3)
{
assert (!own_buffer);
goto convert;
}
 
if (own_buffer) {
data = bitmap->buffer;
} else {
data = _cairo_malloc_ab (height, stride);
if (!data)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (data, bitmap->buffer, stride * height);
}
 
format = CAIRO_FORMAT_A8;
} else {
data = bitmap->buffer;
stride = bitmap->pitch;
format = CAIRO_FORMAT_ARGB32;
component_alpha = TRUE;
}
break;
#ifdef FT_LOAD_COLOR
case FT_PIXEL_MODE_BGRA:
stride = width * 4;
if (own_buffer) {
data = bitmap->buffer;
} else {
data = _cairo_malloc_ab (height, stride);
if (!data)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (data, bitmap->buffer, stride * height);
}
format = CAIRO_FORMAT_ARGB32;
break;
#endif
case FT_PIXEL_MODE_GRAY2:
case FT_PIXEL_MODE_GRAY4:
convert:
if (!own_buffer && library)
{
/* This is pretty much the only case that we can get in here. */
/* Convert to 8bit grayscale. */
 
FT_Bitmap tmp;
FT_Int align;
 
format = CAIRO_FORMAT_A8;
 
align = cairo_format_stride_for_width (format, bitmap->width);
 
FT_Bitmap_New( &tmp );
 
if (FT_Bitmap_Convert( library, bitmap, &tmp, align ))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
FT_Bitmap_Done( library, bitmap );
*bitmap = tmp;
 
stride = bitmap->pitch;
data = _cairo_malloc_ab (height, stride);
if (!data)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (bitmap->num_grays != 256)
{
unsigned int x, y;
unsigned int mul = 255 / (bitmap->num_grays - 1);
FT_Byte *p = bitmap->buffer;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++)
p[x] *= mul;
p += bitmap->pitch;
}
}
 
memcpy (data, bitmap->buffer, stride * height);
break;
}
/* These could be triggered by very rare types of TrueType fonts */
default:
if (own_buffer)
free (bitmap->buffer);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* XXX */
*surface = image = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (data,
format,
width, height, stride);
if (image->base.status) {
free (data);
return (*surface)->base.status;
}
 
if (component_alpha)
pixman_image_set_component_alpha (image->pixman_image, TRUE);
 
_cairo_image_surface_assume_ownership_of_data (image);
 
_cairo_debug_check_image_surface_is_defined (&image->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Converts an outline FT_GlyphSlot into an image
*
* This could go through _render_glyph_bitmap as well, letting
* FreeType convert the outline to a bitmap, but doing it ourselves
* has two minor advantages: first, we save a copy of the bitmap
* buffer: we can directly use the buffer that FreeType renders
* into.
*
* Second, it may help when we add support for subpixel
* rendering: the Xft code does it this way. (Keith thinks that
* it may also be possible to get the subpixel rendering with
* FT_Render_Glyph: something worth looking into in more detail
* when we add subpixel support. If so, we may want to eliminate
* this version of the code path entirely.
*/
static cairo_status_t
_render_glyph_outline (FT_Face face,
cairo_font_options_t *font_options,
cairo_image_surface_t **surface)
{
int rgba = FC_RGBA_UNKNOWN;
int lcd_filter = FT_LCD_FILTER_LEGACY;
FT_GlyphSlot glyphslot = face->glyph;
FT_Outline *outline = &glyphslot->outline;
FT_Bitmap bitmap;
FT_BBox cbox;
unsigned int width, height;
cairo_status_t status;
FT_Error fterror;
FT_Library library = glyphslot->library;
FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
 
switch (font_options->antialias) {
case CAIRO_ANTIALIAS_NONE:
render_mode = FT_RENDER_MODE_MONO;
break;
 
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
switch (font_options->subpixel_order) {
case CAIRO_SUBPIXEL_ORDER_DEFAULT:
case CAIRO_SUBPIXEL_ORDER_RGB:
case CAIRO_SUBPIXEL_ORDER_BGR:
render_mode = FT_RENDER_MODE_LCD;
break;
 
case CAIRO_SUBPIXEL_ORDER_VRGB:
case CAIRO_SUBPIXEL_ORDER_VBGR:
render_mode = FT_RENDER_MODE_LCD_V;
break;
}
 
switch (font_options->lcd_filter) {
case CAIRO_LCD_FILTER_NONE:
lcd_filter = FT_LCD_FILTER_NONE;
break;
case CAIRO_LCD_FILTER_DEFAULT:
case CAIRO_LCD_FILTER_INTRA_PIXEL:
lcd_filter = FT_LCD_FILTER_LEGACY;
break;
case CAIRO_LCD_FILTER_FIR3:
lcd_filter = FT_LCD_FILTER_LIGHT;
break;
case CAIRO_LCD_FILTER_FIR5:
lcd_filter = FT_LCD_FILTER_DEFAULT;
break;
}
 
break;
 
case CAIRO_ANTIALIAS_DEFAULT:
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
render_mode = FT_RENDER_MODE_NORMAL;
}
 
FT_Outline_Get_CBox (outline, &cbox);
 
cbox.xMin &= -64;
cbox.yMin &= -64;
cbox.xMax = (cbox.xMax + 63) & -64;
cbox.yMax = (cbox.yMax + 63) & -64;
 
width = (unsigned int) ((cbox.xMax - cbox.xMin) >> 6);
height = (unsigned int) ((cbox.yMax - cbox.yMin) >> 6);
 
if (width * height == 0) {
cairo_format_t format;
/* Looks like fb handles zero-sized images just fine */
switch (render_mode) {
case FT_RENDER_MODE_MONO:
format = CAIRO_FORMAT_A1;
break;
case FT_RENDER_MODE_LCD:
case FT_RENDER_MODE_LCD_V:
format= CAIRO_FORMAT_ARGB32;
break;
case FT_RENDER_MODE_LIGHT:
case FT_RENDER_MODE_NORMAL:
case FT_RENDER_MODE_MAX:
default:
format = CAIRO_FORMAT_A8;
break;
}
 
(*surface) = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (NULL, format, 0, 0, 0);
if ((*surface)->base.status)
return (*surface)->base.status;
} else {
 
int bitmap_size;
 
switch (render_mode) {
case FT_RENDER_MODE_LCD:
if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_BGR)
rgba = FC_RGBA_BGR;
else
rgba = FC_RGBA_RGB;
break;
 
case FT_RENDER_MODE_LCD_V:
if (font_options->subpixel_order == CAIRO_SUBPIXEL_ORDER_VBGR)
rgba = FC_RGBA_VBGR;
else
rgba = FC_RGBA_VRGB;
break;
 
case FT_RENDER_MODE_MONO:
case FT_RENDER_MODE_LIGHT:
case FT_RENDER_MODE_NORMAL:
case FT_RENDER_MODE_MAX:
default:
break;
}
 
#if HAVE_FT_LIBRARY_SETLCDFILTER
FT_Library_SetLcdFilter (library, lcd_filter);
#endif
 
fterror = FT_Render_Glyph (face->glyph, render_mode);
 
#if HAVE_FT_LIBRARY_SETLCDFILTER
FT_Library_SetLcdFilter (library, FT_LCD_FILTER_NONE);
#endif
 
if (fterror != 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
bitmap_size = _compute_xrender_bitmap_size (&bitmap,
face->glyph,
render_mode);
if (bitmap_size < 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
bitmap.buffer = calloc (1, bitmap_size);
if (bitmap.buffer == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_fill_xrender_bitmap (&bitmap, face->glyph, render_mode,
(rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR));
 
/* Note:
* _get_bitmap_surface will free bitmap.buffer if there is an error
*/
status = _get_bitmap_surface (&bitmap, NULL, TRUE, font_options, surface);
if (unlikely (status))
return status;
 
/* Note: the font's coordinate system is upside down from ours, so the
* Y coordinate of the control box needs to be negated. Moreover, device
* offsets are position of glyph origin relative to top left while xMin
* and yMax are offsets of top left relative to origin. Another negation.
*/
cairo_surface_set_device_offset (&(*surface)->base,
(double)-glyphslot->bitmap_left,
(double)+glyphslot->bitmap_top);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Converts a bitmap (or other) FT_GlyphSlot into an image */
static cairo_status_t
_render_glyph_bitmap (FT_Face face,
cairo_font_options_t *font_options,
cairo_image_surface_t **surface)
{
FT_GlyphSlot glyphslot = face->glyph;
cairo_status_t status;
FT_Error error;
 
/* According to the FreeType docs, glyphslot->format could be
* something other than FT_GLYPH_FORMAT_OUTLINE or
* FT_GLYPH_FORMAT_BITMAP. Calling FT_Render_Glyph gives FreeType
* the opportunity to convert such to
* bitmap. FT_GLYPH_FORMAT_COMPOSITE will not be encountered since
* we avoid the FT_LOAD_NO_RECURSE flag.
*/
error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL);
/* XXX ignoring all other errors for now. They are not fatal, typically
* just a glyph-not-found. */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _get_bitmap_surface (&glyphslot->bitmap,
glyphslot->library,
FALSE, font_options,
surface);
if (unlikely (status))
return status;
 
/*
* Note: the font's coordinate system is upside down from ours, so the
* Y coordinate of the control box needs to be negated. Moreover, device
* offsets are position of glyph origin relative to top left while
* bitmap_left and bitmap_top are offsets of top left relative to origin.
* Another negation.
*/
cairo_surface_set_device_offset (&(*surface)->base,
-glyphslot->bitmap_left,
+glyphslot->bitmap_top);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_transform_glyph_bitmap (cairo_matrix_t * shape,
cairo_image_surface_t ** surface)
{
cairo_matrix_t original_to_transformed;
cairo_matrix_t transformed_to_original;
cairo_image_surface_t *old_image;
cairo_surface_t *image;
double x[4], y[4];
double origin_x, origin_y;
int orig_width, orig_height;
int i;
int x_min, y_min, x_max, y_max;
int width, height;
cairo_status_t status;
cairo_surface_pattern_t pattern;
 
/* We want to compute a transform that takes the origin
* (device_x_offset, device_y_offset) to 0,0, then applies
* the "shape" portion of the font transform
*/
original_to_transformed = *shape;
 
cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y);
orig_width = (*surface)->width;
orig_height = (*surface)->height;
 
cairo_matrix_translate (&original_to_transformed,
-origin_x, -origin_y);
 
/* Find the bounding box of the original bitmap under that
* transform
*/
x[0] = 0; y[0] = 0;
x[1] = orig_width; y[1] = 0;
x[2] = orig_width; y[2] = orig_height;
x[3] = 0; y[3] = orig_height;
 
for (i = 0; i < 4; i++)
cairo_matrix_transform_point (&original_to_transformed,
&x[i], &y[i]);
 
x_min = floor (x[0]); y_min = floor (y[0]);
x_max = ceil (x[0]); y_max = ceil (y[0]);
 
for (i = 1; i < 4; i++) {
if (x[i] < x_min)
x_min = floor (x[i]);
else if (x[i] > x_max)
x_max = ceil (x[i]);
if (y[i] < y_min)
y_min = floor (y[i]);
else if (y[i] > y_max)
y_max = ceil (y[i]);
}
 
/* Adjust the transform so that the bounding box starts at 0,0 ...
* this gives our final transform from original bitmap to transformed
* bitmap.
*/
original_to_transformed.x0 -= x_min;
original_to_transformed.y0 -= y_min;
 
/* Create the transformed bitmap */
width = x_max - x_min;
height = y_max - y_min;
 
transformed_to_original = original_to_transformed;
status = cairo_matrix_invert (&transformed_to_original);
if (unlikely (status))
return status;
 
if ((*surface)->format == CAIRO_FORMAT_ARGB32 &&
!pixman_image_get_component_alpha ((*surface)->pixman_image))
image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
else
image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
if (unlikely (image->status))
return image->status;
 
/* Draw the original bitmap transformed into the new bitmap
*/
_cairo_pattern_init_for_surface (&pattern, &(*surface)->base);
cairo_pattern_set_matrix (&pattern.base, &transformed_to_original);
 
status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
 
_cairo_pattern_fini (&pattern.base);
 
if (unlikely (status)) {
cairo_surface_destroy (image);
return status;
}
 
/* Now update the cache entry for the new bitmap, recomputing
* the origin based on the final transform.
*/
cairo_matrix_transform_point (&original_to_transformed,
&origin_x, &origin_y);
 
old_image = (*surface);
(*surface) = (cairo_image_surface_t *)image;
cairo_surface_destroy (&old_image->base);
 
cairo_surface_set_device_offset (&(*surface)->base,
_cairo_lround (origin_x),
_cairo_lround (origin_y));
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = {
_cairo_ft_unscaled_font_destroy,
#if 0
_cairo_ft_unscaled_font_create_glyph
#endif
};
 
/* #cairo_ft_scaled_font_t */
 
typedef struct _cairo_ft_scaled_font {
cairo_scaled_font_t base;
cairo_ft_unscaled_font_t *unscaled;
cairo_ft_options_t ft_options;
} cairo_ft_scaled_font_t;
 
static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend;
 
#if CAIRO_HAS_FC_FONT
/* The load flags passed to FT_Load_Glyph control aspects like hinting and
* antialiasing. Here we compute them from the fields of a FcPattern.
*/
static void
_get_pattern_ft_options (FcPattern *pattern, cairo_ft_options_t *ret)
{
FcBool antialias, vertical_layout, hinting, autohint, bitmap, embolden;
cairo_ft_options_t ft_options;
int rgba;
#ifdef FC_HINT_STYLE
int hintstyle;
#endif
 
_cairo_font_options_init_default (&ft_options.base);
ft_options.load_flags = FT_LOAD_DEFAULT;
ft_options.synth_flags = 0;
 
#ifndef FC_EMBEDDED_BITMAP
#define FC_EMBEDDED_BITMAP "embeddedbitmap"
#endif
 
/* Check whether to force use of embedded bitmaps */
if (FcPatternGetBool (pattern,
FC_EMBEDDED_BITMAP, 0, &bitmap) != FcResultMatch)
bitmap = FcFalse;
 
/* disable antialiasing if requested */
if (FcPatternGetBool (pattern,
FC_ANTIALIAS, 0, &antialias) != FcResultMatch)
antialias = FcTrue;
if (antialias) {
cairo_subpixel_order_t subpixel_order;
int lcd_filter;
 
/* disable hinting if requested */
if (FcPatternGetBool (pattern,
FC_HINTING, 0, &hinting) != FcResultMatch)
hinting = FcTrue;
 
if (FcPatternGetInteger (pattern,
FC_RGBA, 0, &rgba) != FcResultMatch)
rgba = FC_RGBA_UNKNOWN;
 
switch (rgba) {
case FC_RGBA_RGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
break;
case FC_RGBA_BGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
break;
case FC_RGBA_VRGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
break;
case FC_RGBA_VBGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
break;
case FC_RGBA_UNKNOWN:
case FC_RGBA_NONE:
default:
subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
break;
}
 
if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
ft_options.base.subpixel_order = subpixel_order;
ft_options.base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
}
 
if (FcPatternGetInteger (pattern,
FC_LCD_FILTER, 0, &lcd_filter) == FcResultMatch)
{
switch (lcd_filter) {
case FC_LCD_NONE:
ft_options.base.lcd_filter = CAIRO_LCD_FILTER_NONE;
break;
case FC_LCD_DEFAULT:
ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR5;
break;
case FC_LCD_LIGHT:
ft_options.base.lcd_filter = CAIRO_LCD_FILTER_FIR3;
break;
case FC_LCD_LEGACY:
ft_options.base.lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
break;
}
}
 
#ifdef FC_HINT_STYLE
if (FcPatternGetInteger (pattern,
FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch)
hintstyle = FC_HINT_FULL;
 
if (!hinting)
hintstyle = FC_HINT_NONE;
 
switch (hintstyle) {
case FC_HINT_NONE:
ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
break;
case FC_HINT_SLIGHT:
ft_options.base.hint_style = CAIRO_HINT_STYLE_SLIGHT;
break;
case FC_HINT_MEDIUM:
default:
ft_options.base.hint_style = CAIRO_HINT_STYLE_MEDIUM;
break;
case FC_HINT_FULL:
ft_options.base.hint_style = CAIRO_HINT_STYLE_FULL;
break;
}
#else /* !FC_HINT_STYLE */
if (!hinting) {
ft_options.base.hint_style = CAIRO_HINT_STYLE_NONE;
}
#endif /* FC_HINT_STYLE */
 
/* Force embedded bitmaps off if no hinting requested */
if (ft_options.base.hint_style == CAIRO_HINT_STYLE_NONE)
bitmap = FcFalse;
 
if (!bitmap)
ft_options.load_flags |= FT_LOAD_NO_BITMAP;
 
} else {
ft_options.base.antialias = CAIRO_ANTIALIAS_NONE;
}
 
/* force autohinting if requested */
if (FcPatternGetBool (pattern,
FC_AUTOHINT, 0, &autohint) != FcResultMatch)
autohint = FcFalse;
 
if (autohint)
ft_options.load_flags |= FT_LOAD_FORCE_AUTOHINT;
 
if (FcPatternGetBool (pattern,
FC_VERTICAL_LAYOUT, 0, &vertical_layout) != FcResultMatch)
vertical_layout = FcFalse;
 
if (vertical_layout)
ft_options.load_flags |= FT_LOAD_VERTICAL_LAYOUT;
 
#ifndef FC_EMBOLDEN
#define FC_EMBOLDEN "embolden"
#endif
if (FcPatternGetBool (pattern,
FC_EMBOLDEN, 0, &embolden) != FcResultMatch)
embolden = FcFalse;
 
if (embolden)
ft_options.synth_flags |= CAIRO_FT_SYNTHESIZE_BOLD;
 
*ret = ft_options;
}
#endif
 
static void
_cairo_ft_options_merge (cairo_ft_options_t *options,
cairo_ft_options_t *other)
{
int load_flags = other->load_flags;
int load_target = FT_LOAD_TARGET_NORMAL;
 
/* clear load target mode */
load_flags &= ~(FT_LOAD_TARGET_(FT_LOAD_TARGET_MODE(other->load_flags)));
 
if (load_flags & FT_LOAD_NO_HINTING)
other->base.hint_style = CAIRO_HINT_STYLE_NONE;
 
if (other->base.antialias == CAIRO_ANTIALIAS_NONE ||
options->base.antialias == CAIRO_ANTIALIAS_NONE) {
options->base.antialias = CAIRO_ANTIALIAS_NONE;
options->base.subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
 
if (other->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL &&
(options->base.antialias == CAIRO_ANTIALIAS_DEFAULT ||
options->base.antialias == CAIRO_ANTIALIAS_GRAY)) {
options->base.antialias = CAIRO_ANTIALIAS_SUBPIXEL;
options->base.subpixel_order = other->base.subpixel_order;
}
 
if (options->base.hint_style == CAIRO_HINT_STYLE_DEFAULT)
options->base.hint_style = other->base.hint_style;
 
if (other->base.hint_style == CAIRO_HINT_STYLE_NONE)
options->base.hint_style = CAIRO_HINT_STYLE_NONE;
 
if (options->base.lcd_filter == CAIRO_LCD_FILTER_DEFAULT)
options->base.lcd_filter = other->base.lcd_filter;
 
if (other->base.lcd_filter == CAIRO_LCD_FILTER_NONE)
options->base.lcd_filter = CAIRO_LCD_FILTER_NONE;
 
if (options->base.antialias == CAIRO_ANTIALIAS_NONE) {
if (options->base.hint_style == CAIRO_HINT_STYLE_NONE)
load_flags |= FT_LOAD_NO_HINTING;
else
load_target = FT_LOAD_TARGET_MONO;
load_flags |= FT_LOAD_MONOCHROME;
} else {
switch (options->base.hint_style) {
case CAIRO_HINT_STYLE_NONE:
load_flags |= FT_LOAD_NO_HINTING;
break;
case CAIRO_HINT_STYLE_SLIGHT:
load_target = FT_LOAD_TARGET_LIGHT;
break;
case CAIRO_HINT_STYLE_MEDIUM:
break;
case CAIRO_HINT_STYLE_FULL:
case CAIRO_HINT_STYLE_DEFAULT:
if (options->base.antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
switch (options->base.subpixel_order) {
case CAIRO_SUBPIXEL_ORDER_DEFAULT:
case CAIRO_SUBPIXEL_ORDER_RGB:
case CAIRO_SUBPIXEL_ORDER_BGR:
load_target = FT_LOAD_TARGET_LCD;
break;
case CAIRO_SUBPIXEL_ORDER_VRGB:
case CAIRO_SUBPIXEL_ORDER_VBGR:
load_target = FT_LOAD_TARGET_LCD_V;
break;
}
}
break;
}
}
 
options->load_flags = load_flags | load_target;
options->synth_flags = other->synth_flags;
}
 
static cairo_status_t
_cairo_ft_font_face_scaled_font_create (void *abstract_font_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options,
cairo_scaled_font_t **font_out)
{
cairo_ft_font_face_t *font_face = abstract_font_face;
cairo_ft_scaled_font_t *scaled_font;
FT_Face face;
FT_Size_Metrics *metrics;
cairo_font_extents_t fs_metrics;
cairo_status_t status;
cairo_ft_unscaled_font_t *unscaled;
 
assert (font_face->unscaled);
 
face = _cairo_ft_unscaled_font_lock_face (font_face->unscaled);
if (unlikely (face == NULL)) /* backend error */
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
scaled_font = malloc (sizeof (cairo_ft_scaled_font_t));
if (unlikely (scaled_font == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
 
scaled_font->unscaled = unscaled = font_face->unscaled;
_cairo_unscaled_font_reference (&unscaled->base);
 
_cairo_font_options_init_copy (&scaled_font->ft_options.base, options);
_cairo_ft_options_merge (&scaled_font->ft_options, &font_face->ft_options);
 
status = _cairo_scaled_font_init (&scaled_font->base,
&font_face->base,
font_matrix, ctm, options,
&_cairo_ft_scaled_font_backend);
if (unlikely (status))
goto CLEANUP_SCALED_FONT;
 
status = _cairo_ft_unscaled_font_set_scale (unscaled,
&scaled_font->base.scale);
if (unlikely (status)) {
/* This can only fail if we encounter an error with the underlying
* font, so propagate the error back to the font-face. */
_cairo_ft_unscaled_font_unlock_face (unscaled);
_cairo_unscaled_font_destroy (&unscaled->base);
free (scaled_font);
return status;
}
 
 
metrics = &face->size->metrics;
 
/*
* Get to unscaled metrics so that the upper level can get back to
* user space
*
* Also use this path for bitmap-only fonts. The other branch uses
* face members that are only relevant for scalable fonts. This is
* detected by simply checking for units_per_EM==0.
*/
if (scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF ||
face->units_per_EM == 0) {
double x_factor, y_factor;
 
if (unscaled->x_scale == 0)
x_factor = 0;
else
x_factor = 1 / unscaled->x_scale;
 
if (unscaled->y_scale == 0)
y_factor = 0;
else
y_factor = 1 / unscaled->y_scale;
 
fs_metrics.ascent = DOUBLE_FROM_26_6(metrics->ascender) * y_factor;
fs_metrics.descent = DOUBLE_FROM_26_6(- metrics->descender) * y_factor;
fs_metrics.height = DOUBLE_FROM_26_6(metrics->height) * y_factor;
if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) {
fs_metrics.max_x_advance = DOUBLE_FROM_26_6(metrics->max_advance) * x_factor;
fs_metrics.max_y_advance = 0;
} else {
fs_metrics.max_x_advance = 0;
fs_metrics.max_y_advance = DOUBLE_FROM_26_6(metrics->max_advance) * y_factor;
}
} else {
double scale = face->units_per_EM;
 
fs_metrics.ascent = face->ascender / scale;
fs_metrics.descent = - face->descender / scale;
fs_metrics.height = face->height / scale;
if (!_cairo_ft_scaled_font_is_vertical (&scaled_font->base)) {
fs_metrics.max_x_advance = face->max_advance_width / scale;
fs_metrics.max_y_advance = 0;
} else {
fs_metrics.max_x_advance = 0;
fs_metrics.max_y_advance = face->max_advance_height / scale;
}
}
 
status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics);
if (unlikely (status))
goto CLEANUP_SCALED_FONT;
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
*font_out = &scaled_font->base;
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_SCALED_FONT:
_cairo_unscaled_font_destroy (&unscaled->base);
free (scaled_font);
FAIL:
_cairo_ft_unscaled_font_unlock_face (font_face->unscaled);
*font_out = _cairo_scaled_font_create_in_error (status);
return CAIRO_STATUS_SUCCESS; /* non-backend error */
}
 
cairo_bool_t
_cairo_scaled_font_is_ft (cairo_scaled_font_t *scaled_font)
{
return scaled_font->backend == &_cairo_ft_scaled_font_backend;
}
 
static void
_cairo_ft_scaled_font_fini (void *abstract_font)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
 
if (scaled_font == NULL)
return;
 
_cairo_unscaled_font_destroy (&scaled_font->unscaled->base);
}
 
static int
_move_to (FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_fixed_t x, y;
 
x = _cairo_fixed_from_26_6 (to->x);
y = _cairo_fixed_from_26_6 (to->y);
 
if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS)
return 1;
if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS)
return 1;
 
return 0;
}
 
static int
_line_to (FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_fixed_t x, y;
 
x = _cairo_fixed_from_26_6 (to->x);
y = _cairo_fixed_from_26_6 (to->y);
 
if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS)
return 1;
 
return 0;
}
 
static int
_conic_to (FT_Vector *control, FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
 
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
cairo_fixed_t x3, y3;
cairo_point_t conic;
 
if (! _cairo_path_fixed_get_current_point (path, &x0, &y0))
return 1;
 
conic.x = _cairo_fixed_from_26_6 (control->x);
conic.y = _cairo_fixed_from_26_6 (control->y);
 
x3 = _cairo_fixed_from_26_6 (to->x);
y3 = _cairo_fixed_from_26_6 (to->y);
 
x1 = x0 + 2.0/3.0 * (conic.x - x0);
y1 = y0 + 2.0/3.0 * (conic.y - y0);
 
x2 = x3 + 2.0/3.0 * (conic.x - x3);
y2 = y3 + 2.0/3.0 * (conic.y - y3);
 
if (_cairo_path_fixed_curve_to (path,
x1, y1,
x2, y2,
x3, y3) != CAIRO_STATUS_SUCCESS)
return 1;
 
return 0;
}
 
static int
_cubic_to (FT_Vector *control1, FT_Vector *control2,
FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
 
x0 = _cairo_fixed_from_26_6 (control1->x);
y0 = _cairo_fixed_from_26_6 (control1->y);
 
x1 = _cairo_fixed_from_26_6 (control2->x);
y1 = _cairo_fixed_from_26_6 (control2->y);
 
x2 = _cairo_fixed_from_26_6 (to->x);
y2 = _cairo_fixed_from_26_6 (to->y);
 
if (_cairo_path_fixed_curve_to (path,
x0, y0,
x1, y1,
x2, y2) != CAIRO_STATUS_SUCCESS)
return 1;
 
return 0;
}
 
static cairo_status_t
_decompose_glyph_outline (FT_Face face,
cairo_font_options_t *options,
cairo_path_fixed_t **pathp)
{
static const FT_Outline_Funcs outline_funcs = {
(FT_Outline_MoveToFunc)_move_to,
(FT_Outline_LineToFunc)_line_to,
(FT_Outline_ConicToFunc)_conic_to,
(FT_Outline_CubicToFunc)_cubic_to,
0, /* shift */
0, /* delta */
};
static const FT_Matrix invert_y = {
DOUBLE_TO_16_16 (1.0), 0,
0, DOUBLE_TO_16_16 (-1.0),
};
 
FT_GlyphSlot glyph;
cairo_path_fixed_t *path;
cairo_status_t status;
 
path = _cairo_path_fixed_create ();
if (!path)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
glyph = face->glyph;
 
/* Font glyphs have an inverted Y axis compared to cairo. */
FT_Outline_Transform (&glyph->outline, &invert_y);
if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) {
_cairo_path_fixed_destroy (path);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
status = _cairo_path_fixed_close_path (path);
if (unlikely (status)) {
_cairo_path_fixed_destroy (path);
return status;
}
 
*pathp = path;
 
return CAIRO_STATUS_SUCCESS;
}
 
/*
* Translate glyph to match its metrics.
*/
static void
_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (void *abstract_font,
FT_GlyphSlot glyph)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
FT_Vector vector;
 
vector.x = glyph->metrics.vertBearingX - glyph->metrics.horiBearingX;
vector.y = -glyph->metrics.vertBearingY - glyph->metrics.horiBearingY;
 
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
FT_Vector_Transform (&vector, &scaled_font->unscaled->Current_Shape);
FT_Outline_Translate(&glyph->outline, vector.x, vector.y);
} else if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
glyph->bitmap_left += vector.x / 64;
glyph->bitmap_top += vector.y / 64;
}
}
 
static cairo_int_status_t
_cairo_ft_scaled_glyph_init (void *abstract_font,
cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_info_t info)
{
cairo_text_extents_t fs_metrics;
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_GlyphSlot glyph;
FT_Face face;
FT_Error error;
int load_flags = scaled_font->ft_options.load_flags;
FT_Glyph_Metrics *metrics;
double x_factor, y_factor;
cairo_bool_t vertical_layout = FALSE;
cairo_status_t status;
 
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
&scaled_font->base.scale);
if (unlikely (status))
goto FAIL;
 
/* Ignore global advance unconditionally */
load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0 &&
(info & CAIRO_SCALED_GLYPH_INFO_SURFACE) == 0)
load_flags |= FT_LOAD_NO_BITMAP;
 
/*
* Don't pass FT_LOAD_VERTICAL_LAYOUT to FT_Load_Glyph here as
* suggested by freetype people.
*/
if (load_flags & FT_LOAD_VERTICAL_LAYOUT) {
load_flags &= ~FT_LOAD_VERTICAL_LAYOUT;
vertical_layout = TRUE;
}
 
#ifdef FT_LOAD_COLOR
/* Color-glyph support:
*
* This flags needs plumbing through fontconfig (does it?), and
* maybe we should cache color and grayscale bitmaps separately
* such that users of the font (ie. the surface) can choose which
* version to use based on target content type.
*
* Moreover, none of our backends and compositors currently support
* color glyphs. As such, this is currently disabled.
*/
/* load_flags |= FT_LOAD_COLOR; */
#endif
 
error = FT_Load_Glyph (face,
_cairo_scaled_glyph_index(scaled_glyph),
load_flags);
/* XXX ignoring all other errors for now. They are not fatal, typically
* just a glyph-not-found. */
if (error == FT_Err_Out_Of_Memory) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
 
glyph = face->glyph;
 
/*
* synthesize glyphs if requested
*/
#if HAVE_FT_GLYPHSLOT_EMBOLDEN
if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD)
FT_GlyphSlot_Embolden (glyph);
#endif
 
#if HAVE_FT_GLYPHSLOT_OBLIQUE
if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE)
FT_GlyphSlot_Oblique (glyph);
#endif
 
if (vertical_layout)
_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph);
 
if (info & CAIRO_SCALED_GLYPH_INFO_METRICS) {
 
cairo_bool_t hint_metrics = scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF;
/*
* Compute font-space metrics
*/
metrics = &glyph->metrics;
 
if (unscaled->x_scale == 0)
x_factor = 0;
else
x_factor = 1 / unscaled->x_scale;
 
if (unscaled->y_scale == 0)
y_factor = 0;
else
y_factor = 1 / unscaled->y_scale;
 
/*
* Note: Y coordinates of the horizontal bearing need to be negated.
*
* Scale metrics back to glyph space from the scaled glyph space returned
* by FreeType
*
* If we want hinted metrics but aren't asking for hinted glyphs from
* FreeType, then we need to do the metric hinting ourselves.
*/
 
if (hint_metrics && (load_flags & FT_LOAD_NO_HINTING))
{
FT_Pos x1, x2;
FT_Pos y1, y2;
FT_Pos advance;
 
if (!vertical_layout) {
x1 = (metrics->horiBearingX) & -64;
x2 = (metrics->horiBearingX + metrics->width + 63) & -64;
y1 = (-metrics->horiBearingY) & -64;
y2 = (-metrics->horiBearingY + metrics->height + 63) & -64;
 
advance = ((metrics->horiAdvance + 32) & -64);
 
fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
 
fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
 
fs_metrics.x_advance = DOUBLE_FROM_26_6 (advance) * x_factor;
fs_metrics.y_advance = 0;
} else {
x1 = (metrics->vertBearingX) & -64;
x2 = (metrics->vertBearingX + metrics->width + 63) & -64;
y1 = (metrics->vertBearingY) & -64;
y2 = (metrics->vertBearingY + metrics->height + 63) & -64;
 
advance = ((metrics->vertAdvance + 32) & -64);
 
fs_metrics.x_bearing = DOUBLE_FROM_26_6 (x1) * x_factor;
fs_metrics.y_bearing = DOUBLE_FROM_26_6 (y1) * y_factor;
 
fs_metrics.width = DOUBLE_FROM_26_6 (x2 - x1) * x_factor;
fs_metrics.height = DOUBLE_FROM_26_6 (y2 - y1) * y_factor;
 
fs_metrics.x_advance = 0;
fs_metrics.y_advance = DOUBLE_FROM_26_6 (advance) * y_factor;
}
} else {
fs_metrics.width = DOUBLE_FROM_26_6 (metrics->width) * x_factor;
fs_metrics.height = DOUBLE_FROM_26_6 (metrics->height) * y_factor;
 
if (!vertical_layout) {
fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->horiBearingX) * x_factor;
fs_metrics.y_bearing = DOUBLE_FROM_26_6 (-metrics->horiBearingY) * y_factor;
 
if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
fs_metrics.x_advance = DOUBLE_FROM_26_6 (metrics->horiAdvance) * x_factor;
else
fs_metrics.x_advance = DOUBLE_FROM_16_16 (glyph->linearHoriAdvance) * x_factor;
fs_metrics.y_advance = 0 * y_factor;
} else {
fs_metrics.x_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingX) * x_factor;
fs_metrics.y_bearing = DOUBLE_FROM_26_6 (metrics->vertBearingY) * y_factor;
 
fs_metrics.x_advance = 0 * x_factor;
if (hint_metrics || glyph->format != FT_GLYPH_FORMAT_OUTLINE)
fs_metrics.y_advance = DOUBLE_FROM_26_6 (metrics->vertAdvance) * y_factor;
else
fs_metrics.y_advance = DOUBLE_FROM_16_16 (glyph->linearVertAdvance) * y_factor;
}
}
 
_cairo_scaled_glyph_set_metrics (scaled_glyph,
&scaled_font->base,
&fs_metrics);
}
 
if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
cairo_image_surface_t *surface;
 
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
status = _render_glyph_outline (face, &scaled_font->ft_options.base,
&surface);
} else {
status = _render_glyph_bitmap (face, &scaled_font->ft_options.base,
&surface);
if (likely (status == CAIRO_STATUS_SUCCESS) &&
unscaled->have_shape)
{
status = _transform_glyph_bitmap (&unscaled->current_shape,
&surface);
if (unlikely (status))
cairo_surface_destroy (&surface->base);
}
}
if (unlikely (status))
goto FAIL;
 
_cairo_scaled_glyph_set_surface (scaled_glyph,
&scaled_font->base,
surface);
}
 
if (info & CAIRO_SCALED_GLYPH_INFO_PATH) {
cairo_path_fixed_t *path = NULL; /* hide compiler warning */
 
/*
* A kludge -- the above code will trash the outline,
* so reload it. This will probably never occur though
*/
if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0) {
error = FT_Load_Glyph (face,
_cairo_scaled_glyph_index(scaled_glyph),
load_flags | FT_LOAD_NO_BITMAP);
/* XXX ignoring all other errors for now. They are not fatal, typically
* just a glyph-not-found. */
if (error == FT_Err_Out_Of_Memory) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
#if HAVE_FT_GLYPHSLOT_EMBOLDEN
if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_BOLD)
FT_GlyphSlot_Embolden (glyph);
#endif
#if HAVE_FT_GLYPHSLOT_OBLIQUE
if (scaled_font->ft_options.synth_flags & CAIRO_FT_SYNTHESIZE_OBLIQUE)
FT_GlyphSlot_Oblique (glyph);
#endif
if (vertical_layout)
_cairo_ft_scaled_glyph_vertical_layout_bearing_fix (scaled_font, glyph);
 
}
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
status = _decompose_glyph_outline (face, &scaled_font->ft_options.base,
&path);
else
status = CAIRO_INT_STATUS_UNSUPPORTED;
 
if (unlikely (status))
goto FAIL;
 
_cairo_scaled_glyph_set_path (scaled_glyph,
&scaled_font->base,
path);
}
FAIL:
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
return status;
}
 
static unsigned long
_cairo_ft_ucs4_to_index (void *abstract_font,
uint32_t ucs4)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_Face face;
FT_UInt index;
 
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return 0;
 
#if CAIRO_HAS_FC_FONT
index = FcFreeTypeCharIndex (face, ucs4);
#else
index = FT_Get_Char_Index (face, ucs4);
#endif
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
return index;
}
 
static cairo_int_status_t
_cairo_ft_load_truetype_table (void *abstract_font,
unsigned long tag,
long offset,
unsigned char *buffer,
unsigned long *length)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_Face face;
cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
/* We don't support the FreeType feature of loading a table
* without specifying the size since this may overflow our
* buffer. */
assert (length != NULL);
 
if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
#if HAVE_FT_LOAD_SFNT_TABLE
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (FT_IS_SFNT (face)) {
if (buffer == NULL)
*length = 0;
 
if (FT_Load_Sfnt_Table (face, tag, offset, buffer, length) == 0)
status = CAIRO_STATUS_SUCCESS;
}
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
#endif
 
return status;
}
 
static cairo_int_status_t
_cairo_ft_index_to_ucs4(void *abstract_font,
unsigned long index,
uint32_t *ucs4)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_Face face;
FT_ULong charcode;
FT_UInt gindex;
 
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
*ucs4 = (uint32_t) -1;
charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0) {
if (gindex == index) {
*ucs4 = charcode;
break;
}
charcode = FT_Get_Next_Char (face, charcode, &gindex);
}
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_ft_is_synthetic (void *abstract_font)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
return scaled_font->ft_options.synth_flags != 0;
}
 
static cairo_int_status_t
_cairo_index_to_glyph_name (void *abstract_font,
char **glyph_names,
int num_glyph_names,
unsigned long glyph_index,
unsigned long *glyph_array_index)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_Face face;
char buffer[256]; /* PLRM spcifies max name length of 127 */
FT_Error error;
int i;
 
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
error = FT_Get_Glyph_Name (face, glyph_index, buffer, sizeof buffer);
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
if (error != FT_Err_Ok) {
/* propagate fatal errors from FreeType */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* FT first numbers the glyphs in the order they are read from the
* Type 1 font. Then if .notdef is not the first glyph, the first
* glyph is swapped with .notdef to ensure that .notdef is at
* glyph index 0.
*
* As all but two glyphs in glyph_names already have the same
* index as the FT glyph index, we first check if
* glyph_names[glyph_index] is the name we are looking for. If not
* we fall back to searching the entire array.
*/
 
if ((long)glyph_index < num_glyph_names &&
strcmp (glyph_names[glyph_index], buffer) == 0)
{
*glyph_array_index = glyph_index;
 
return CAIRO_STATUS_SUCCESS;
}
 
for (i = 0; i < num_glyph_names; i++) {
if (strcmp (glyph_names[i], buffer) == 0) {
*glyph_array_index = i;
 
return CAIRO_STATUS_SUCCESS;
}
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_bool_t
_ft_is_type1 (FT_Face face)
{
#if HAVE_FT_GET_X11_FONT_FORMAT
const char *font_format = FT_Get_X11_Font_Format (face);
if (font_format &&
(strcmp (font_format, "Type 1") == 0 ||
strcmp (font_format, "CFF") == 0))
{
return TRUE;
}
#endif
 
return FALSE;
}
 
static cairo_int_status_t
_cairo_ft_load_type1_data (void *abstract_font,
long offset,
unsigned char *buffer,
unsigned long *length)
{
cairo_ft_scaled_font_t *scaled_font = abstract_font;
cairo_ft_unscaled_font_t *unscaled = scaled_font->unscaled;
FT_Face face;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
unsigned long available_length;
unsigned long ret;
 
assert (length != NULL);
 
if (_cairo_ft_scaled_font_is_vertical (&scaled_font->base))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
#if HAVE_FT_LOAD_SFNT_TABLE
if (FT_IS_SFNT (face)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto unlock;
}
#endif
 
if (! _ft_is_type1 (face)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto unlock;
}
 
available_length = MAX (face->stream->size - offset, 0);
if (!buffer) {
*length = available_length;
} else {
if (*length > available_length) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
} else if (face->stream->read != NULL) {
/* Note that read() may be implemented as a macro, thanks POSIX!, so we
* need to wrap the following usage in parentheses in order to
* disambiguate it for the pre-processor - using the verbose function
* pointer dereference for clarity.
*/
ret = (* face->stream->read) (face->stream,
offset,
buffer,
*length);
if (ret != *length)
status = _cairo_error (CAIRO_STATUS_READ_ERROR);
} else {
memcpy (buffer, face->stream->base + offset, *length);
}
}
 
unlock:
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
return status;
}
 
static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
CAIRO_FONT_TYPE_FT,
_cairo_ft_scaled_font_fini,
_cairo_ft_scaled_glyph_init,
NULL, /* text_to_glyphs */
_cairo_ft_ucs4_to_index,
_cairo_ft_load_truetype_table,
_cairo_ft_index_to_ucs4,
_cairo_ft_is_synthetic,
_cairo_index_to_glyph_name,
_cairo_ft_load_type1_data
};
 
/* #cairo_ft_font_face_t */
 
#if CAIRO_HAS_FC_FONT
static cairo_font_face_t *
_cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
 
static cairo_status_t
_cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
cairo_font_face_t **font_face_out)
{
cairo_font_face_t *font_face = (cairo_font_face_t *) &_cairo_font_face_nil;
FcPattern *pattern;
int fcslant;
int fcweight;
 
pattern = FcPatternCreate ();
if (!pattern) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return font_face->status;
}
 
if (!FcPatternAddString (pattern,
FC_FAMILY, (unsigned char *) toy_face->family))
{
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
goto FREE_PATTERN;
}
 
switch (toy_face->slant)
{
case CAIRO_FONT_SLANT_ITALIC:
fcslant = FC_SLANT_ITALIC;
break;
case CAIRO_FONT_SLANT_OBLIQUE:
fcslant = FC_SLANT_OBLIQUE;
break;
case CAIRO_FONT_SLANT_NORMAL:
default:
fcslant = FC_SLANT_ROMAN;
break;
}
 
if (!FcPatternAddInteger (pattern, FC_SLANT, fcslant)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
goto FREE_PATTERN;
}
 
switch (toy_face->weight)
{
case CAIRO_FONT_WEIGHT_BOLD:
fcweight = FC_WEIGHT_BOLD;
break;
case CAIRO_FONT_WEIGHT_NORMAL:
default:
fcweight = FC_WEIGHT_MEDIUM;
break;
}
 
if (!FcPatternAddInteger (pattern, FC_WEIGHT, fcweight)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
goto FREE_PATTERN;
}
 
font_face = _cairo_ft_font_face_create_for_pattern (pattern);
 
FREE_PATTERN:
FcPatternDestroy (pattern);
 
*font_face_out = font_face;
return font_face->status;
}
#endif
 
static void
_cairo_ft_font_face_destroy (void *abstract_face)
{
cairo_ft_font_face_t *font_face = abstract_face;
 
/* When destroying a face created by cairo_ft_font_face_create_for_ft_face,
* we have a special "zombie" state for the face when the unscaled font
* is still alive but there are no other references to a font face with
* the same FT_Face.
*
* We go from:
*
* font_face ------> unscaled
* <-....weak....../
*
* To:
*
* font_face <------- unscaled
*/
 
if (font_face->unscaled &&
font_face->unscaled->from_face &&
font_face->next == NULL &&
font_face->unscaled->faces == font_face &&
CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1)
{
cairo_font_face_reference (&font_face->base);
 
_cairo_unscaled_font_destroy (&font_face->unscaled->base);
font_face->unscaled = NULL;
 
return;
}
 
if (font_face->unscaled) {
cairo_ft_font_face_t *tmp_face = NULL;
cairo_ft_font_face_t *last_face = NULL;
 
/* Remove face from linked list */
for (tmp_face = font_face->unscaled->faces;
tmp_face;
tmp_face = tmp_face->next)
{
if (tmp_face == font_face) {
if (last_face)
last_face->next = tmp_face->next;
else
font_face->unscaled->faces = tmp_face->next;
}
 
last_face = tmp_face;
}
 
_cairo_unscaled_font_destroy (&font_face->unscaled->base);
font_face->unscaled = NULL;
}
 
#if CAIRO_HAS_FC_FONT
if (font_face->pattern) {
FcPatternDestroy (font_face->pattern);
cairo_font_face_destroy (font_face->resolved_font_face);
}
#endif
}
 
static cairo_font_face_t *
_cairo_ft_font_face_get_implementation (void *abstract_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options)
{
cairo_ft_font_face_t *font_face = abstract_face;
 
/* The handling of font options is different depending on how the
* font face was created. When the user creates a font face with
* cairo_ft_font_face_create_for_ft_face(), then the load flags
* passed in augment the load flags for the options. But for
* cairo_ft_font_face_create_for_pattern(), the load flags are
* derived from a pattern where the user has called
* cairo_ft_font_options_substitute(), so *just* use those load
* flags and ignore the options.
*/
 
#if CAIRO_HAS_FC_FONT
/* If we have an unresolved pattern, resolve it and create
* unscaled font. Otherwise, use the ones stored in font_face.
*/
if (font_face->pattern) {
cairo_font_face_t *resolved;
 
/* Cache the resolved font whilst the FcConfig remains consistent. */
resolved = font_face->resolved_font_face;
if (resolved != NULL) {
if (! FcInitBringUptoDate ()) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *) &_cairo_font_face_nil;
}
 
if (font_face->resolved_config == FcConfigGetCurrent ())
return cairo_font_face_reference (resolved);
 
cairo_font_face_destroy (resolved);
font_face->resolved_font_face = NULL;
}
 
resolved = _cairo_ft_resolve_pattern (font_face->pattern,
font_matrix,
ctm,
options);
if (unlikely (resolved->status))
return resolved;
 
font_face->resolved_font_face = cairo_font_face_reference (resolved);
font_face->resolved_config = FcConfigGetCurrent ();
 
return resolved;
}
#endif
 
return abstract_face;
}
 
const cairo_font_face_backend_t _cairo_ft_font_face_backend = {
CAIRO_FONT_TYPE_FT,
#if CAIRO_HAS_FC_FONT
_cairo_ft_font_face_create_for_toy,
#else
NULL,
#endif
_cairo_ft_font_face_destroy,
_cairo_ft_font_face_scaled_font_create,
_cairo_ft_font_face_get_implementation
};
 
#if CAIRO_HAS_FC_FONT
static cairo_font_face_t *
_cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
{
cairo_ft_font_face_t *font_face;
 
font_face = malloc (sizeof (cairo_ft_font_face_t));
if (unlikely (font_face == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *) &_cairo_font_face_nil;
}
 
font_face->unscaled = NULL;
font_face->next = NULL;
 
font_face->pattern = FcPatternDuplicate (pattern);
if (unlikely (font_face->pattern == NULL)) {
free (font_face);
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *) &_cairo_font_face_nil;
}
 
font_face->resolved_font_face = NULL;
font_face->resolved_config = NULL;
 
_cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
 
return &font_face->base;
}
#endif
 
static cairo_font_face_t *
_cairo_ft_font_face_create (cairo_ft_unscaled_font_t *unscaled,
cairo_ft_options_t *ft_options)
{
cairo_ft_font_face_t *font_face, **prev_font_face;
 
/* Looked for an existing matching font face */
for (font_face = unscaled->faces, prev_font_face = &unscaled->faces;
font_face;
prev_font_face = &font_face->next, font_face = font_face->next)
{
if (font_face->ft_options.load_flags == ft_options->load_flags &&
font_face->ft_options.synth_flags == ft_options->synth_flags &&
cairo_font_options_equal (&font_face->ft_options.base, &ft_options->base))
{
if (font_face->base.status) {
/* The font_face has been left in an error state, abandon it. */
*prev_font_face = font_face->next;
break;
}
 
if (font_face->unscaled == NULL) {
/* Resurrect this "zombie" font_face (from
* _cairo_ft_font_face_destroy), switching its unscaled_font
* from owner to ownee. */
font_face->unscaled = unscaled;
_cairo_unscaled_font_reference (&unscaled->base);
return &font_face->base;
} else
return cairo_font_face_reference (&font_face->base);
}
}
 
/* No match found, create a new one */
font_face = malloc (sizeof (cairo_ft_font_face_t));
if (unlikely (!font_face)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *)&_cairo_font_face_nil;
}
 
font_face->unscaled = unscaled;
_cairo_unscaled_font_reference (&unscaled->base);
 
font_face->ft_options = *ft_options;
 
if (unscaled->faces && unscaled->faces->unscaled == NULL) {
/* This "zombie" font_face (from _cairo_ft_font_face_destroy)
* is no longer needed. */
assert (unscaled->from_face && unscaled->faces->next == NULL);
cairo_font_face_destroy (&unscaled->faces->base);
unscaled->faces = NULL;
}
 
font_face->next = unscaled->faces;
unscaled->faces = font_face;
 
#if CAIRO_HAS_FC_FONT
font_face->pattern = NULL;
#endif
 
_cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
 
return &font_face->base;
}
 
/* implement the platform-specific interface */
 
#if CAIRO_HAS_FC_FONT
static cairo_status_t
_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
FcPattern *pattern)
{
FcValue v;
 
if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
{
if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch)
{
if (! FcPatternAddBool (pattern,
FC_ANTIALIAS,
options->antialias != CAIRO_ANTIALIAS_NONE))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
FcPatternDel (pattern, FC_RGBA);
if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
}
 
if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
{
if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch)
{
int rgba;
 
if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
switch (options->subpixel_order) {
case CAIRO_SUBPIXEL_ORDER_DEFAULT:
case CAIRO_SUBPIXEL_ORDER_RGB:
default:
rgba = FC_RGBA_RGB;
break;
case CAIRO_SUBPIXEL_ORDER_BGR:
rgba = FC_RGBA_BGR;
break;
case CAIRO_SUBPIXEL_ORDER_VRGB:
rgba = FC_RGBA_VRGB;
break;
case CAIRO_SUBPIXEL_ORDER_VBGR:
rgba = FC_RGBA_VBGR;
break;
}
} else {
rgba = FC_RGBA_NONE;
}
 
if (! FcPatternAddInteger (pattern, FC_RGBA, rgba))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
if (options->lcd_filter != CAIRO_LCD_FILTER_DEFAULT)
{
if (FcPatternGet (pattern, FC_LCD_FILTER, 0, &v) == FcResultNoMatch)
{
int lcd_filter;
 
switch (options->lcd_filter) {
case CAIRO_LCD_FILTER_NONE:
lcd_filter = FT_LCD_FILTER_NONE;
break;
case CAIRO_LCD_FILTER_DEFAULT:
case CAIRO_LCD_FILTER_INTRA_PIXEL:
lcd_filter = FT_LCD_FILTER_LEGACY;
break;
case CAIRO_LCD_FILTER_FIR3:
lcd_filter = FT_LCD_FILTER_LIGHT;
break;
default:
case CAIRO_LCD_FILTER_FIR5:
lcd_filter = FT_LCD_FILTER_DEFAULT;
break;
}
 
if (! FcPatternAddInteger (pattern, FC_LCD_FILTER, lcd_filter))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT)
{
if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch)
{
if (! FcPatternAddBool (pattern,
FC_HINTING,
options->hint_style != CAIRO_HINT_STYLE_NONE))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
#ifdef FC_HINT_STYLE
if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch)
{
int hint_style;
 
switch (options->hint_style) {
case CAIRO_HINT_STYLE_NONE:
hint_style = FC_HINT_NONE;
break;
case CAIRO_HINT_STYLE_SLIGHT:
hint_style = FC_HINT_SLIGHT;
break;
case CAIRO_HINT_STYLE_MEDIUM:
hint_style = FC_HINT_MEDIUM;
break;
case CAIRO_HINT_STYLE_FULL:
case CAIRO_HINT_STYLE_DEFAULT:
default:
hint_style = FC_HINT_FULL;
break;
}
 
if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
#endif
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_ft_font_options_substitute:
* @options: a #cairo_font_options_t object
* @pattern: an existing #FcPattern
*
* Add options to a #FcPattern based on a #cairo_font_options_t font
* options object. Options that are already in the pattern, are not overridden,
* so you should call this function after calling FcConfigSubstitute() (the
* user's settings should override options based on the surface type), but
* before calling FcDefaultSubstitute().
*
* Since: 1.0
**/
void
cairo_ft_font_options_substitute (const cairo_font_options_t *options,
FcPattern *pattern)
{
if (cairo_font_options_status ((cairo_font_options_t *) options))
return;
 
_cairo_ft_font_options_substitute (options, pattern);
}
 
static cairo_font_face_t *
_cairo_ft_resolve_pattern (FcPattern *pattern,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *font_options)
{
cairo_status_t status;
 
cairo_matrix_t scale;
FcPattern *resolved;
cairo_ft_font_transform_t sf;
FcResult result;
cairo_ft_unscaled_font_t *unscaled;
cairo_ft_options_t ft_options;
cairo_font_face_t *font_face;
 
scale = *ctm;
scale.x0 = scale.y0 = 0;
cairo_matrix_multiply (&scale,
font_matrix,
&scale);
 
status = _compute_transform (&sf, &scale, NULL);
if (unlikely (status))
return (cairo_font_face_t *)&_cairo_font_face_nil;
 
pattern = FcPatternDuplicate (pattern);
if (pattern == NULL)
return (cairo_font_face_t *)&_cairo_font_face_nil;
 
if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
goto FREE_PATTERN;
}
 
if (! FcConfigSubstitute (NULL, pattern, FcMatchPattern)) {
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
goto FREE_PATTERN;
}
 
status = _cairo_ft_font_options_substitute (font_options, pattern);
if (status) {
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
goto FREE_PATTERN;
}
 
FcDefaultSubstitute (pattern);
 
status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
if (unlikely (status)) {
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
goto FREE_PATTERN;
}
 
if (unscaled == NULL) {
resolved = FcFontMatch (NULL, pattern, &result);
if (!resolved) {
/* We failed to find any font. Substitute twin so that the user can
* see something (and hopefully recognise that the font is missing)
* and not just receive a NO_MEMORY error during rendering.
*/
font_face = _cairo_font_face_twin_create_fallback ();
goto FREE_PATTERN;
}
 
status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
if (unlikely (status || unscaled == NULL)) {
font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
goto FREE_RESOLVED;
}
} else
resolved = pattern;
 
_get_pattern_ft_options (resolved, &ft_options);
font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
_cairo_unscaled_font_destroy (&unscaled->base);
 
FREE_RESOLVED:
if (resolved != pattern)
FcPatternDestroy (resolved);
 
FREE_PATTERN:
FcPatternDestroy (pattern);
 
return font_face;
}
 
/**
* cairo_ft_font_face_create_for_pattern:
* @pattern: A fontconfig pattern. Cairo makes a copy of the pattern
* if it needs to. You are free to modify or free @pattern after this call.
*
* Creates a new font face for the FreeType font backend based on a
* fontconfig pattern. This font can then be used with
* cairo_set_font_face() or cairo_scaled_font_create(). The
* #cairo_scaled_font_t returned from cairo_scaled_font_create() is
* also for the FreeType backend and can be used with functions such
* as cairo_ft_scaled_font_lock_face().
*
* Font rendering options are represented both here and when you
* call cairo_scaled_font_create(). Font options that have a representation
* in a #FcPattern must be passed in here; to modify #FcPattern
* appropriately to reflect the options in a #cairo_font_options_t, call
* cairo_ft_font_options_substitute().
*
* The pattern's FC_FT_FACE element is inspected first and if that is set,
* that will be the FreeType font face associated with the returned cairo
* font face. Otherwise the FC_FILE element is checked. If it's set,
* that and the value of the FC_INDEX element (defaults to zero) of @pattern
* are used to load a font face from file.
*
* If both steps from the previous paragraph fails, @pattern will be passed
* to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch,
* and the resulting font pattern is used.
*
* If the FC_FT_FACE element of @pattern is set, the user is responsible
* for making sure that the referenced FT_Face remains valid for the life
* time of the returned #cairo_font_face_t. See
* cairo_ft_font_face_create_for_ft_face() for an example of how to couple
* the life time of the FT_Face to that of the cairo font-face.
*
* Return value: a newly created #cairo_font_face_t. Free with
* cairo_font_face_destroy() when you are done using it.
*
* Since: 1.0
**/
cairo_font_face_t *
cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
{
cairo_ft_unscaled_font_t *unscaled;
cairo_font_face_t *font_face;
cairo_ft_options_t ft_options;
cairo_status_t status;
 
status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled);
if (unlikely (status))
return (cairo_font_face_t *) &_cairo_font_face_nil;
if (unlikely (unscaled == NULL)) {
/* Store the pattern. We will resolve it and create unscaled
* font when creating scaled fonts */
return _cairo_ft_font_face_create_for_pattern (pattern);
}
 
_get_pattern_ft_options (pattern, &ft_options);
font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
_cairo_unscaled_font_destroy (&unscaled->base);
 
return font_face;
}
#endif
 
/**
* cairo_ft_font_face_create_for_ft_face:
* @face: A FreeType face object, already opened. This must
* be kept around until the face's ref_count drops to
* zero and it is freed. Since the face may be referenced
* internally to Cairo, the best way to determine when it
* is safe to free the face is to pass a
* #cairo_destroy_func_t to cairo_font_face_set_user_data()
* @load_flags: flags to pass to FT_Load_Glyph when loading
* glyphs from the font. These flags are OR'ed together with
* the flags derived from the #cairo_font_options_t passed
* to cairo_scaled_font_create(), so only a few values such
* as %FT_LOAD_VERTICAL_LAYOUT, and %FT_LOAD_FORCE_AUTOHINT
* are useful. You should not pass any of the flags affecting
* the load target, such as %FT_LOAD_TARGET_LIGHT.
*
* Creates a new font face for the FreeType font backend from a
* pre-opened FreeType face. This font can then be used with
* cairo_set_font_face() or cairo_scaled_font_create(). The
* #cairo_scaled_font_t returned from cairo_scaled_font_create() is
* also for the FreeType backend and can be used with functions such
* as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference
* to the FT_Face alive in a font-cache and the exact lifetime of the reference
* depends highly upon the exact usage pattern and is subject to external
* factors. You must not call FT_Done_Face() before the last reference to the
* #cairo_font_face_t has been dropped.
*
* As an example, below is how one might correctly couple the lifetime of
* the FreeType face object to the #cairo_font_face_t.
*
* <informalexample><programlisting>
* static const cairo_user_data_key_t key;
*
* font_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
* status = cairo_font_face_set_user_data (font_face, &key,
* ft_face, (cairo_destroy_func_t) FT_Done_Face);
* if (status) {
* cairo_font_face_destroy (font_face);
* FT_Done_Face (ft_face);
* return ERROR;
* }
* </programlisting></informalexample>
*
* Return value: a newly created #cairo_font_face_t. Free with
* cairo_font_face_destroy() when you are done using it.
*
* Since: 1.0
**/
cairo_font_face_t *
cairo_ft_font_face_create_for_ft_face (FT_Face face,
int load_flags)
{
cairo_ft_unscaled_font_t *unscaled;
cairo_font_face_t *font_face;
cairo_ft_options_t ft_options;
cairo_status_t status;
 
status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled);
if (unlikely (status))
return (cairo_font_face_t *)&_cairo_font_face_nil;
 
ft_options.load_flags = load_flags;
ft_options.synth_flags = 0;
_cairo_font_options_init_default (&ft_options.base);
 
font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
_cairo_unscaled_font_destroy (&unscaled->base);
 
return font_face;
}
 
/**
* cairo_ft_font_face_set_synthesize:
* @font_face: The #cairo_ft_font_face_t object to modify
* @synth_flags: the set of synthesis options to enable
*
* FreeType provides the ability to synthesize different glyphs from a base
* font, which is useful if you lack those glyphs from a true bold or oblique
* font. See also #cairo_ft_synthesize_t.
*
* Since: 1.12
**/
void
cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face,
unsigned int synth_flags)
{
cairo_ft_font_face_t *ft;
 
if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
return;
 
ft = (cairo_ft_font_face_t *) font_face;
ft->ft_options.synth_flags |= synth_flags;
}
 
/**
* cairo_ft_font_face_unset_synthesize:
* @font_face: The #cairo_ft_font_face_t object to modify
* @synth_flags: the set of synthesis options to disable
*
* See cairo_ft_font_face_set_synthesize().
*
* Since: 1.12
**/
void
cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face,
unsigned int synth_flags)
{
cairo_ft_font_face_t *ft;
 
if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
return;
 
ft = (cairo_ft_font_face_t *) font_face;
ft->ft_options.synth_flags &= ~synth_flags;
}
 
/**
* cairo_ft_font_face_get_synthesize:
* @font_face: The #cairo_ft_font_face_t object to query
*
* See #cairo_ft_synthesize_t.
*
* Returns: the current set of synthesis options.
*
* Since: 1.12
**/
unsigned int
cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face)
{
cairo_ft_font_face_t *ft;
 
if (font_face->backend->type != CAIRO_FONT_TYPE_FT)
return 0;
 
ft = (cairo_ft_font_face_t *) font_face;
return ft->ft_options.synth_flags;
}
 
/**
* cairo_ft_scaled_font_lock_face:
* @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
* object can be created by calling cairo_scaled_font_create() on a
* FreeType backend font face (see cairo_ft_font_face_create_for_pattern(),
* cairo_ft_font_face_create_for_ft_face()).
*
* cairo_ft_scaled_font_lock_face() gets the #FT_Face object from a FreeType
* backend font and scales it appropriately for the font. You must
* release the face with cairo_ft_scaled_font_unlock_face()
* when you are done using it. Since the #FT_Face object can be
* shared between multiple #cairo_scaled_font_t objects, you must not
* lock any other font objects until you unlock this one. A count is
* kept of the number of times cairo_ft_scaled_font_lock_face() is
* called. cairo_ft_scaled_font_unlock_face() must be called the same number
* of times.
*
* You must be careful when using this function in a library or in a
* threaded application, because freetype's design makes it unsafe to
* call freetype functions simultaneously from multiple threads, (even
* if using distinct FT_Face objects). Because of this, application
* code that acquires an FT_Face object with this call must add its
* own locking to protect any use of that object, (and which also must
* protect any other calls into cairo as almost any cairo function
* might result in a call into the freetype library).
*
* Return value: The #FT_Face object for @font, scaled appropriately,
* or %NULL if @scaled_font is in an error state (see
* cairo_scaled_font_status()) or there is insufficient memory.
*
* Since: 1.0
**/
FT_Face
cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *abstract_font)
{
cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
FT_Face face;
cairo_status_t status;
 
if (! _cairo_scaled_font_is_ft (abstract_font)) {
_cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
return NULL;
}
 
if (scaled_font->base.status)
return NULL;
 
face = _cairo_ft_unscaled_font_lock_face (scaled_font->unscaled);
if (unlikely (face == NULL)) {
status = _cairo_scaled_font_set_error (&scaled_font->base, CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
status = _cairo_ft_unscaled_font_set_scale (scaled_font->unscaled,
&scaled_font->base.scale);
if (unlikely (status)) {
_cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
status = _cairo_scaled_font_set_error (&scaled_font->base, status);
return NULL;
}
 
/* Note: We deliberately release the unscaled font's mutex here,
* so that we are not holding a lock across two separate calls to
* cairo function, (which would give the application some
* opportunity for creating deadlock. This is obviously unsafe,
* but as documented, the user must add manual locking when using
* this function. */
CAIRO_MUTEX_UNLOCK (scaled_font->unscaled->mutex);
 
return face;
}
 
/**
* cairo_ft_scaled_font_unlock_face:
* @scaled_font: A #cairo_scaled_font_t from the FreeType font backend. Such an
* object can be created by calling cairo_scaled_font_create() on a
* FreeType backend font face (see cairo_ft_font_face_create_for_pattern(),
* cairo_ft_font_face_create_for_ft_face()).
*
* Releases a face obtained with cairo_ft_scaled_font_lock_face().
*
* Since: 1.0
**/
void
cairo_ft_scaled_font_unlock_face (cairo_scaled_font_t *abstract_font)
{
cairo_ft_scaled_font_t *scaled_font = (cairo_ft_scaled_font_t *) abstract_font;
 
if (! _cairo_scaled_font_is_ft (abstract_font)) {
_cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH);
return;
}
 
if (scaled_font->base.status)
return;
 
/* Note: We released the unscaled font's mutex at the end of
* cairo_ft_scaled_font_lock_face, so we have to acquire it again
* as _cairo_ft_unscaled_font_unlock_face expects it to be held
* when we call into it. */
CAIRO_MUTEX_LOCK (scaled_font->unscaled->mutex);
 
_cairo_ft_unscaled_font_unlock_face (scaled_font->unscaled);
}
 
static cairo_bool_t
_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font)
{
cairo_ft_scaled_font_t *ft_scaled_font;
 
if (!_cairo_scaled_font_is_ft (scaled_font))
return FALSE;
 
ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
if (ft_scaled_font->ft_options.load_flags & FT_LOAD_VERTICAL_LAYOUT)
return TRUE;
return FALSE;
}
 
unsigned int
_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font)
{
cairo_ft_scaled_font_t *ft_scaled_font;
 
if (! _cairo_scaled_font_is_ft (scaled_font))
return 0;
 
ft_scaled_font = (cairo_ft_scaled_font_t *) scaled_font;
return ft_scaled_font->ft_options.load_flags;
}
 
void
_cairo_ft_font_reset_static_data (void)
{
_cairo_ft_unscaled_font_map_destroy ();
}
/programs/develop/libraries/cairo/src/cairo-ft-private.h
52,18 → 52,6
/* These functions are needed by the PDF backend, which needs to keep track of the
* the different fonts-on-disk used by a document, so it can embed them
*/
cairo_private cairo_unscaled_font_t *
_cairo_ft_scaled_font_get_unscaled_font (cairo_scaled_font_t *scaled_font);
 
cairo_private FT_Face
_cairo_ft_unscaled_font_lock_face (cairo_ft_unscaled_font_t *unscaled);
 
cairo_private void
_cairo_ft_unscaled_font_unlock_face (cairo_ft_unscaled_font_t *unscaled);
 
cairo_private cairo_bool_t
_cairo_ft_scaled_font_is_vertical (cairo_scaled_font_t *scaled_font);
 
cairo_private unsigned int
_cairo_ft_scaled_font_get_load_flags (cairo_scaled_font_t *scaled_font);
 
/programs/develop/libraries/cairo/src/cairo-ft.h
56,6 → 56,42
cairo_ft_font_face_create_for_ft_face (FT_Face face,
int load_flags);
 
/**
* cairo_ft_synthesize_t:
* @CAIRO_FT_SYNTHESIZE_BOLD: Embolden the glyphs (redraw with a pixel offset)
* @CAIRO_FT_SYNTHESIZE_OBLIQUE: Slant the glyph outline by 12 degrees to the
* right.
*
* A set of synthesis options to control how FreeType renders the glyphs
* for a particular font face.
*
* Individual synthesis features of a #cairo_ft_font_face_t can be set
* using cairo_ft_font_face_set_synthesize(), or disabled using
* cairo_ft_font_face_unset_synthesize(). The currently enabled set of
* synthesis options can be queried with cairo_ft_font_face_get_synthesize().
*
* Note: that when synthesizing glyphs, the font metrics returned will only
* be estimates.
*
* Since: 1.12
**/
typedef enum {
CAIRO_FT_SYNTHESIZE_BOLD = 1 << 0,
CAIRO_FT_SYNTHESIZE_OBLIQUE = 1 << 1
} cairo_ft_synthesize_t;
 
cairo_public void
cairo_ft_font_face_set_synthesize (cairo_font_face_t *font_face,
unsigned int synth_flags);
 
cairo_public void
cairo_ft_font_face_unset_synthesize (cairo_font_face_t *font_face,
unsigned int synth_flags);
 
cairo_public unsigned int
cairo_ft_font_face_get_synthesize (cairo_font_face_t *font_face);
 
 
cairo_public FT_Face
cairo_ft_scaled_font_lock_face (cairo_scaled_font_t *scaled_font);
 
/programs/develop/libraries/cairo/src/cairo-gl-composite.c
0,0 → 1,1264
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2011 Linaro Limited
* Copyright © 2011 Samsung Electronics
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
* Henry Song <hsong@sisa.samsung.com>
* Martin Robinson <mrobinson@igalia.com>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
 
cairo_int_status_t
_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
_cairo_gl_operand_destroy (&setup->src);
return _cairo_gl_operand_init (&setup->src, pattern, setup->dst,
sample, extents, use_texgen);
}
 
void
_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
const cairo_gl_operand_t *source)
{
_cairo_gl_operand_destroy (&setup->src);
_cairo_gl_operand_copy (&setup->src, source);
}
 
void
_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
const cairo_color_t *color)
{
_cairo_gl_operand_destroy (&setup->src);
_cairo_gl_solid_operand_init (&setup->src, color);
}
 
cairo_int_status_t
_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
_cairo_gl_operand_destroy (&setup->mask);
if (pattern == NULL)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_gl_operand_init (&setup->mask, pattern, setup->dst,
sample, extents, use_texgen);
}
 
void
_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
const cairo_gl_operand_t *mask)
{
_cairo_gl_operand_destroy (&setup->mask);
if (mask)
_cairo_gl_operand_copy (&setup->mask, mask);
}
 
void
_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup)
{
setup->spans = TRUE;
}
 
void
_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup)
{
setup->multisample = TRUE;
}
 
void
_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
cairo_region_t *clip_region)
{
setup->clip_region = clip_region;
}
 
void
_cairo_gl_composite_set_clip (cairo_gl_composite_t *setup,
cairo_clip_t *clip)
{
setup->clip = clip;
}
 
static void
_cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup)
{
_cairo_gl_shader_bind_matrix4f(ctx, ctx->current_shader->mvp_location,
ctx->modelviewprojection_matrix);
_cairo_gl_operand_bind_to_shader (ctx, &setup->src, CAIRO_GL_TEX_SOURCE);
_cairo_gl_operand_bind_to_shader (ctx, &setup->mask, CAIRO_GL_TEX_MASK);
}
 
static void
_cairo_gl_texture_set_filter (cairo_gl_context_t *ctx,
GLuint target,
cairo_filter_t filter)
{
switch (filter) {
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
break;
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
break;
default:
case CAIRO_FILTER_GAUSSIAN:
ASSERT_NOT_REACHED;
}
}
 
static void
_cairo_gl_texture_set_extend (cairo_gl_context_t *ctx,
GLuint target,
cairo_extend_t extend)
{
GLint wrap_mode;
assert (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base) ||
(extend != CAIRO_EXTEND_REPEAT && extend != CAIRO_EXTEND_REFLECT));
 
switch (extend) {
case CAIRO_EXTEND_NONE:
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES)
wrap_mode = GL_CLAMP_TO_EDGE;
else
wrap_mode = GL_CLAMP_TO_BORDER;
break;
case CAIRO_EXTEND_PAD:
wrap_mode = GL_CLAMP_TO_EDGE;
break;
case CAIRO_EXTEND_REPEAT:
if (ctx->has_npot_repeat)
wrap_mode = GL_REPEAT;
else
wrap_mode = GL_CLAMP_TO_EDGE;
break;
case CAIRO_EXTEND_REFLECT:
if (ctx->has_npot_repeat)
wrap_mode = GL_MIRRORED_REPEAT;
else
wrap_mode = GL_CLAMP_TO_EDGE;
break;
default:
wrap_mode = 0;
}
 
if (likely (wrap_mode)) {
glTexParameteri (target, GL_TEXTURE_WRAP_S, wrap_mode);
glTexParameteri (target, GL_TEXTURE_WRAP_T, wrap_mode);
}
}
 
 
static void
_cairo_gl_context_setup_operand (cairo_gl_context_t *ctx,
cairo_gl_tex_t tex_unit,
cairo_gl_operand_t *operand,
unsigned int vertex_offset,
cairo_bool_t vertex_size_changed)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
cairo_bool_t needs_setup;
 
/* XXX: we need to do setup when switching from shaders
* to no shaders (or back) */
needs_setup = vertex_size_changed;
needs_setup |= _cairo_gl_operand_needs_setup (&ctx->operands[tex_unit],
operand,
vertex_offset);
 
if (needs_setup) {
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_destroy_operand (ctx, tex_unit);
}
 
memcpy (&ctx->operands[tex_unit], operand, sizeof (cairo_gl_operand_t));
ctx->operands[tex_unit].vertex_offset = vertex_offset;
 
if (! needs_setup)
return;
 
switch (operand->type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
break;
/* fall through */
case CAIRO_GL_OPERAND_CONSTANT:
break;
case CAIRO_GL_OPERAND_TEXTURE:
glActiveTexture (GL_TEXTURE0 + tex_unit);
glBindTexture (ctx->tex_target, operand->texture.tex);
_cairo_gl_texture_set_extend (ctx, ctx->tex_target,
operand->texture.attributes.extend);
_cairo_gl_texture_set_filter (ctx, ctx->tex_target,
operand->texture.attributes.filter);
 
if (! operand->texture.texgen) {
dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
GL_FLOAT, GL_FALSE, ctx->vertex_size,
ctx->vb + vertex_offset);
dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
}
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
glActiveTexture (GL_TEXTURE0 + tex_unit);
glBindTexture (ctx->tex_target, operand->gradient.gradient->tex);
_cairo_gl_texture_set_extend (ctx, ctx->tex_target, operand->gradient.extend);
_cairo_gl_texture_set_filter (ctx, ctx->tex_target, CAIRO_FILTER_BILINEAR);
 
if (! operand->gradient.texgen) {
dispatch->VertexAttribPointer (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit, 2,
GL_FLOAT, GL_FALSE, ctx->vertex_size,
ctx->vb + vertex_offset);
dispatch->EnableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
}
break;
}
}
 
static void
_cairo_gl_context_setup_spans (cairo_gl_context_t *ctx,
cairo_bool_t spans_enabled,
unsigned int vertex_size,
unsigned int vertex_offset)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
 
if (! spans_enabled) {
dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
ctx->spans = FALSE;
return;
}
 
dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4,
GL_UNSIGNED_BYTE, GL_TRUE, vertex_size,
ctx->vb + vertex_offset);
dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX);
ctx->spans = TRUE;
}
 
void
_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
cairo_gl_tex_t tex_unit)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
 
if (!_cairo_gl_context_is_flushed (ctx))
_cairo_gl_composite_flush (ctx);
 
switch (ctx->operands[tex_unit].type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
break;
/* fall through */
case CAIRO_GL_OPERAND_CONSTANT:
break;
case CAIRO_GL_OPERAND_TEXTURE:
dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
dispatch->DisableVertexAttribArray (CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + tex_unit);
break;
}
 
memset (&ctx->operands[tex_unit], 0, sizeof (cairo_gl_operand_t));
}
 
static void
_cairo_gl_set_operator (cairo_gl_context_t *ctx,
cairo_operator_t op,
cairo_bool_t component_alpha)
{
struct {
GLenum src;
GLenum dst;
} blend_factors[] = {
{ GL_ZERO, GL_ZERO }, /* Clear */
{ GL_ONE, GL_ZERO }, /* Source */
{ GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* Over */
{ GL_DST_ALPHA, GL_ZERO }, /* In */
{ GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* Out */
{ GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Atop */
 
{ GL_ZERO, GL_ONE }, /* Dest */
{ GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* DestOver */
{ GL_ZERO, GL_SRC_ALPHA }, /* DestIn */
{ GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* DestOut */
{ GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /* DestAtop */
 
{ GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /* Xor */
{ GL_ONE, GL_ONE }, /* Add */
};
GLenum src_factor, dst_factor;
 
assert (op < ARRAY_LENGTH (blend_factors));
/* different dst and component_alpha changes cause flushes elsewhere */
if (ctx->current_operator != op)
_cairo_gl_composite_flush (ctx);
ctx->current_operator = op;
 
src_factor = blend_factors[op].src;
dst_factor = blend_factors[op].dst;
 
/* Even when the user requests CAIRO_CONTENT_COLOR, we use GL_RGBA
* due to texture filtering of GL_CLAMP_TO_BORDER. So fix those
* bits in that case.
*/
if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
if (src_factor == GL_ONE_MINUS_DST_ALPHA)
src_factor = GL_ZERO;
if (src_factor == GL_DST_ALPHA)
src_factor = GL_ONE;
}
 
if (component_alpha) {
if (dst_factor == GL_ONE_MINUS_SRC_ALPHA)
dst_factor = GL_ONE_MINUS_SRC_COLOR;
if (dst_factor == GL_SRC_ALPHA)
dst_factor = GL_SRC_COLOR;
}
 
if (ctx->current_target->base.content == CAIRO_CONTENT_ALPHA) {
glBlendFuncSeparate (GL_ZERO, GL_ZERO, src_factor, dst_factor);
} else if (ctx->current_target->base.content == CAIRO_CONTENT_COLOR) {
glBlendFuncSeparate (src_factor, dst_factor, GL_ONE, GL_ONE);
} else {
glBlendFunc (src_factor, dst_factor);
}
}
 
static cairo_status_t
_cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup)
{
cairo_gl_shader_t *pre_shader = NULL;
cairo_status_t status;
 
/* For CLEAR, cairo's rendering equation (quoting Owen's description in:
* http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
* is:
* mask IN clip ? src OP dest : dest
* or more simply:
* mask IN CLIP ? 0 : dest
*
* where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
*
* The model we use in _cairo_gl_set_operator() is Render's:
* src IN mask IN clip OP dest
* which would boil down to:
* 0 (bounded by the extents of the drawing).
*
* However, we can do a Render operation using an opaque source
* and DEST_OUT to produce:
* 1 IN mask IN clip DEST_OUT dest
* which is
* mask IN clip ? 0 : dest
*/
if (setup->op == CAIRO_OPERATOR_CLEAR) {
_cairo_gl_solid_operand_init (&setup->src, CAIRO_COLOR_WHITE);
setup->op = CAIRO_OPERATOR_DEST_OUT;
}
 
/*
* implements component-alpha %CAIRO_OPERATOR_OVER using two passes of
* the simpler operations %CAIRO_OPERATOR_DEST_OUT and %CAIRO_OPERATOR_ADD.
*
* From http://anholt.livejournal.com/32058.html:
*
* The trouble is that component-alpha rendering requires two different sources
* for blending: one for the source value to the blender, which is the
* per-channel multiplication of source and mask, and one for the source alpha
* for multiplying with the destination channels, which is the multiplication
* of the source channels by the mask alpha. So the equation for Over is:
*
* dst.A = src.A * mask.A + (1 - (src.A * mask.A)) * dst.A
* dst.R = src.R * mask.R + (1 - (src.A * mask.R)) * dst.R
* dst.G = src.G * mask.G + (1 - (src.A * mask.G)) * dst.G
* dst.B = src.B * mask.B + (1 - (src.A * mask.B)) * dst.B
*
* But we can do some simpler operations, right? How about PictOpOutReverse,
* which has a source factor of 0 and dest factor of (1 - source alpha). We
* can get the source alpha value (srca.X = src.A * mask.X) out of the texture
* blenders pretty easily. So we can do a component-alpha OutReverse, which
* gets us:
*
* dst.A = 0 + (1 - (src.A * mask.A)) * dst.A
* dst.R = 0 + (1 - (src.A * mask.R)) * dst.R
* dst.G = 0 + (1 - (src.A * mask.G)) * dst.G
* dst.B = 0 + (1 - (src.A * mask.B)) * dst.B
*
* OK. And if an op doesn't use the source alpha value for the destination
* factor, then we can do the channel multiplication in the texture blenders
* to get the source value, and ignore the source alpha that we wouldn't use.
* We've supported this in the Radeon driver for a long time. An example would
* be PictOpAdd, which does:
*
* dst.A = src.A * mask.A + dst.A
* dst.R = src.R * mask.R + dst.R
* dst.G = src.G * mask.G + dst.G
* dst.B = src.B * mask.B + dst.B
*
* Hey, this looks good! If we do a PictOpOutReverse and then a PictOpAdd right
* after it, we get:
*
* dst.A = src.A * mask.A + ((1 - (src.A * mask.A)) * dst.A)
* dst.R = src.R * mask.R + ((1 - (src.A * mask.R)) * dst.R)
* dst.G = src.G * mask.G + ((1 - (src.A * mask.G)) * dst.G)
* dst.B = src.B * mask.B + ((1 - (src.A * mask.B)) * dst.B)
*
* This two-pass trickery could be avoided using a new GL extension that
* lets two values come out of the shader and into the blend unit.
*/
if (setup->op == CAIRO_OPERATOR_OVER) {
setup->op = CAIRO_OPERATOR_ADD;
status = _cairo_gl_get_shader_by_type (ctx,
&setup->src,
&setup->mask,
setup->spans,
CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
&pre_shader);
if (unlikely (status))
return status;
}
 
if (ctx->pre_shader != pre_shader)
_cairo_gl_composite_flush (ctx);
ctx->pre_shader = pre_shader;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_scissor_to_doubles (cairo_gl_surface_t *surface,
double x1, double y1,
double x2, double y2)
{
double height;
 
height = y2 - y1;
if (_cairo_gl_surface_is_texture (surface) == FALSE)
y1 = surface->height - (y1 + height);
glScissor (x1, y1, x2 - x1, height);
glEnable (GL_SCISSOR_TEST);
}
 
void
_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface,
const cairo_rectangle_int_t *r)
{
_scissor_to_doubles (surface, r->x, r->y, r->x+r->width, r->y+r->height);
}
 
static void
_scissor_to_box (cairo_gl_surface_t *surface,
const cairo_box_t *box)
{
double x1, y1, x2, y2;
_cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
_scissor_to_doubles (surface, x1, y1, x2, y2);
}
 
static cairo_bool_t
_cairo_gl_composite_setup_vbo (cairo_gl_context_t *ctx,
unsigned int size_per_vertex)
{
cairo_bool_t vertex_size_changed = ctx->vertex_size != size_per_vertex;
if (vertex_size_changed) {
ctx->vertex_size = size_per_vertex;
_cairo_gl_composite_flush (ctx);
}
 
if (_cairo_gl_context_is_flushed (ctx)) {
ctx->dispatch.VertexAttribPointer (CAIRO_GL_VERTEX_ATTRIB_INDEX, 2,
GL_FLOAT, GL_FALSE, size_per_vertex,
ctx->vb);
ctx->dispatch.EnableVertexAttribArray (CAIRO_GL_VERTEX_ATTRIB_INDEX);
}
 
return vertex_size_changed;
}
 
static void
_disable_stencil_buffer (void)
{
glDisable (GL_STENCIL_TEST);
glDepthMask (GL_FALSE);
}
 
static cairo_int_status_t
_cairo_gl_composite_setup_painted_clipping (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx,
int vertex_size)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
 
cairo_gl_surface_t *dst = setup->dst;
cairo_clip_t *clip = setup->clip;
 
if (clip->num_boxes == 1 && clip->path == NULL) {
_scissor_to_box (dst, &clip->boxes[0]);
goto disable_stencil_buffer_and_return;
}
 
if (! _cairo_gl_ensure_stencil (ctx, setup->dst)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto disable_stencil_buffer_and_return;
}
 
/* We only want to clear the part of the stencil buffer
* that we are about to use. It also does not hurt to
* scissor around the painted clip. */
_cairo_gl_scissor_to_rectangle (dst, _cairo_clip_get_extents (clip));
 
/* The clip is not rectangular, so use the stencil buffer. */
glDepthMask (GL_TRUE);
glEnable (GL_STENCIL_TEST);
 
/* Texture surfaces have private depth/stencil buffers, so we can
* rely on any previous clip being cached there. */
if (_cairo_gl_surface_is_texture (setup->dst)) {
cairo_clip_t *old_clip = setup->dst->clip_on_stencil_buffer;
if (_cairo_clip_equal (old_clip, setup->clip))
goto activate_stencil_buffer_and_return;
 
if (old_clip) {
_cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
}
 
setup->dst->clip_on_stencil_buffer = _cairo_clip_copy (setup->clip);
}
 
glClearStencil (0);
glClear (GL_STENCIL_BUFFER_BIT);
 
glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
glColorMask (0, 0, 0, 0);
 
status = _cairo_gl_msaa_compositor_draw_clip (ctx, setup, clip);
 
if (unlikely (status)) {
glColorMask (1, 1, 1, 1);
goto disable_stencil_buffer_and_return;
}
 
/* We want to only render to the stencil buffer, so draw everything now.
Flushing also unbinds the VBO, which we want to rebind for regular
drawing. */
_cairo_gl_composite_flush (ctx);
_cairo_gl_composite_setup_vbo (ctx, vertex_size);
 
activate_stencil_buffer_and_return:
glColorMask (1, 1, 1, 1);
glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc (GL_EQUAL, 1, 0xffffffff);
return CAIRO_INT_STATUS_SUCCESS;
 
disable_stencil_buffer_and_return:
_disable_stencil_buffer ();
return status;
}
 
static cairo_int_status_t
_cairo_gl_composite_setup_clipping (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx,
int vertex_size)
{
cairo_bool_t clip_changing = TRUE;
cairo_bool_t clip_region_changing = TRUE;
 
if (! ctx->clip && ! setup->clip && ! setup->clip_region && ! ctx->clip_region)
goto disable_all_clipping;
 
clip_changing = ! _cairo_clip_equal (ctx->clip, setup->clip);
clip_region_changing = ! cairo_region_equal (ctx->clip_region, setup->clip_region);
if (! _cairo_gl_context_is_flushed (ctx) &&
(clip_region_changing || clip_changing))
_cairo_gl_composite_flush (ctx);
 
assert (!setup->clip_region || !setup->clip);
 
/* setup->clip is only used by the msaa compositor and setup->clip_region
* only by the other compositors, so it's safe to wait to clean up obsolete
* clips. */
if (clip_region_changing) {
cairo_region_destroy (ctx->clip_region);
ctx->clip_region = cairo_region_reference (setup->clip_region);
}
if (clip_changing) {
_cairo_clip_destroy (ctx->clip);
ctx->clip = _cairo_clip_copy (setup->clip);
}
 
/* For clip regions, we scissor right before drawing. */
if (setup->clip_region)
goto disable_all_clipping;
 
if (setup->clip)
return _cairo_gl_composite_setup_painted_clipping (setup, ctx,
vertex_size);
disable_all_clipping:
_disable_stencil_buffer ();
glDisable (GL_SCISSOR_TEST);
return CAIRO_INT_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx)
{
unsigned int dst_size, src_size, mask_size, vertex_size;
cairo_status_t status;
cairo_gl_shader_t *shader;
cairo_bool_t component_alpha;
cairo_bool_t vertex_size_changed;
 
component_alpha =
setup->mask.type == CAIRO_GL_OPERAND_TEXTURE &&
setup->mask.texture.attributes.has_component_alpha;
 
/* Do various magic for component alpha */
if (component_alpha) {
status = _cairo_gl_composite_begin_component_alpha (ctx, setup);
if (unlikely (status))
return status;
} else {
if (ctx->pre_shader) {
_cairo_gl_composite_flush (ctx);
ctx->pre_shader = NULL;
}
}
 
status = _cairo_gl_get_shader_by_type (ctx,
&setup->src,
&setup->mask,
setup->spans,
component_alpha ?
CAIRO_GL_SHADER_IN_CA_SOURCE :
CAIRO_GL_SHADER_IN_NORMAL,
&shader);
if (unlikely (status)) {
ctx->pre_shader = NULL;
return status;
}
if (ctx->current_shader != shader)
_cairo_gl_composite_flush (ctx);
 
status = CAIRO_STATUS_SUCCESS;
 
dst_size = 2 * sizeof (GLfloat);
src_size = _cairo_gl_operand_get_vertex_size (&setup->src);
mask_size = _cairo_gl_operand_get_vertex_size (&setup->mask);
vertex_size = dst_size + src_size + mask_size;
 
if (setup->spans)
vertex_size += sizeof (GLfloat);
 
vertex_size_changed = _cairo_gl_composite_setup_vbo (ctx, vertex_size);
 
_cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, dst_size, vertex_size_changed);
_cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, dst_size + src_size, vertex_size_changed);
 
_cairo_gl_context_setup_spans (ctx, setup->spans, vertex_size,
dst_size + src_size + mask_size);
 
_cairo_gl_set_operator (ctx, setup->op, component_alpha);
 
if (_cairo_gl_context_is_flushed (ctx)) {
if (ctx->pre_shader) {
_cairo_gl_set_shader (ctx, ctx->pre_shader);
_cairo_gl_composite_bind_to_shader (ctx, setup);
}
_cairo_gl_set_shader (ctx, shader);
_cairo_gl_composite_bind_to_shader (ctx, setup);
}
 
return status;
}
 
cairo_status_t
_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
cairo_gl_context_t **ctx_out)
{
cairo_gl_context_t *ctx;
cairo_status_t status;
 
assert (setup->dst);
 
status = _cairo_gl_context_acquire (setup->dst->base.device, &ctx);
if (unlikely (status))
return status;
 
_cairo_gl_context_set_destination (ctx, setup->dst, setup->multisample);
glEnable (GL_BLEND);
 
status = _cairo_gl_set_operands_and_operator (setup, ctx);
if (unlikely (status))
goto FAIL;
 
status = _cairo_gl_composite_setup_clipping (setup, ctx, ctx->vertex_size);
if (unlikely (status))
goto FAIL;
 
*ctx_out = ctx;
 
FAIL:
if (unlikely (status))
status = _cairo_gl_context_release (ctx, status);
 
return status;
}
 
static inline void
_cairo_gl_composite_draw_tristrip (cairo_gl_context_t *ctx)
{
cairo_array_t* indices = &ctx->tristrip_indices;
const unsigned short *indices_array = _cairo_array_index_const (indices, 0);
 
if (ctx->pre_shader) {
cairo_gl_shader_t *prev_shader = ctx->current_shader;
 
_cairo_gl_set_shader (ctx, ctx->pre_shader);
_cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
 
_cairo_gl_set_shader (ctx, prev_shader);
_cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
}
 
glDrawElements (GL_TRIANGLE_STRIP, _cairo_array_num_elements (indices), GL_UNSIGNED_SHORT, indices_array);
_cairo_array_truncate (indices, 0);
}
 
static inline void
_cairo_gl_composite_draw_triangles (cairo_gl_context_t *ctx,
unsigned int count)
{
if (! ctx->pre_shader) {
glDrawArrays (GL_TRIANGLES, 0, count);
} else {
cairo_gl_shader_t *prev_shader = ctx->current_shader;
 
_cairo_gl_set_shader (ctx, ctx->pre_shader);
_cairo_gl_set_operator (ctx, CAIRO_OPERATOR_DEST_OUT, TRUE);
glDrawArrays (GL_TRIANGLES, 0, count);
 
_cairo_gl_set_shader (ctx, prev_shader);
_cairo_gl_set_operator (ctx, CAIRO_OPERATOR_ADD, TRUE);
glDrawArrays (GL_TRIANGLES, 0, count);
}
}
 
static void
_cairo_gl_composite_draw_triangles_with_clip_region (cairo_gl_context_t *ctx,
unsigned int count)
{
int i, num_rectangles;
 
if (!ctx->clip_region) {
_cairo_gl_composite_draw_triangles (ctx, count);
return;
}
 
num_rectangles = cairo_region_num_rectangles (ctx->clip_region);
for (i = 0; i < num_rectangles; i++) {
cairo_rectangle_int_t rect;
 
cairo_region_get_rectangle (ctx->clip_region, i, &rect);
 
_cairo_gl_scissor_to_rectangle (ctx->current_target, &rect);
_cairo_gl_composite_draw_triangles (ctx, count);
}
}
 
static void
_cairo_gl_composite_unmap_vertex_buffer (cairo_gl_context_t *ctx)
{
ctx->vb_offset = 0;
}
 
void
_cairo_gl_composite_flush (cairo_gl_context_t *ctx)
{
unsigned int count;
int i;
 
if (_cairo_gl_context_is_flushed (ctx))
return;
 
count = ctx->vb_offset / ctx->vertex_size;
_cairo_gl_composite_unmap_vertex_buffer (ctx);
 
if (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS) {
_cairo_gl_composite_draw_tristrip (ctx);
} else {
assert (ctx->primitive_type == CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
_cairo_gl_composite_draw_triangles_with_clip_region (ctx, count);
}
 
for (i = 0; i < ARRAY_LENGTH (&ctx->glyph_cache); i++)
_cairo_gl_glyph_cache_unlock (&ctx->glyph_cache[i]);
}
 
static void
_cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx,
unsigned int n_vertices,
cairo_gl_primitive_type_t primitive_type)
{
if (ctx->primitive_type != primitive_type) {
_cairo_gl_composite_flush (ctx);
ctx->primitive_type = primitive_type;
}
 
if (ctx->vb_offset + n_vertices * ctx->vertex_size > CAIRO_GL_VBO_SIZE)
_cairo_gl_composite_flush (ctx);
}
 
static inline void
_cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx,
GLfloat x, GLfloat y)
{
GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
 
*vb++ = x;
*vb++ = y;
 
_cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
_cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y);
 
ctx->vb_offset += ctx->vertex_size;
}
 
static inline void
_cairo_gl_composite_emit_alpha_vertex (cairo_gl_context_t *ctx,
GLfloat x, GLfloat y, uint8_t alpha)
{
GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
union fi {
float f;
GLbyte bytes[4];
} fi;
 
*vb++ = x;
*vb++ = y;
 
_cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
_cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y);
 
fi.bytes[0] = 0;
fi.bytes[1] = 0;
fi.bytes[2] = 0;
fi.bytes[3] = alpha;
*vb++ = fi.f;
 
ctx->vb_offset += ctx->vertex_size;
}
 
static void
_cairo_gl_composite_emit_point (cairo_gl_context_t *ctx,
const cairo_point_t *point)
{
_cairo_gl_composite_emit_vertex (ctx,
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
}
 
static void
_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2)
{
_cairo_gl_composite_prepare_buffer (ctx, 6,
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
 
_cairo_gl_composite_emit_vertex (ctx, x1, y1);
_cairo_gl_composite_emit_vertex (ctx, x2, y1);
_cairo_gl_composite_emit_vertex (ctx, x1, y2);
 
_cairo_gl_composite_emit_vertex (ctx, x2, y1);
_cairo_gl_composite_emit_vertex (ctx, x2, y2);
_cairo_gl_composite_emit_vertex (ctx, x1, y2);
}
 
cairo_gl_emit_rect_t
_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx)
{
return _cairo_gl_composite_emit_rect;
}
 
void
_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2)
{
_cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2);
}
 
static void
_cairo_gl_composite_emit_span (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
uint8_t alpha)
{
_cairo_gl_composite_prepare_buffer (ctx, 6,
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
 
_cairo_gl_composite_emit_alpha_vertex (ctx, x1, y1, alpha);
_cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha);
_cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha);
 
_cairo_gl_composite_emit_alpha_vertex (ctx, x2, y1, alpha);
_cairo_gl_composite_emit_alpha_vertex (ctx, x2, y2, alpha);
_cairo_gl_composite_emit_alpha_vertex (ctx, x1, y2, alpha);
}
 
static void
_cairo_gl_composite_emit_solid_span (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
uint8_t alpha)
{
GLfloat *v;
union fi {
float f;
GLbyte bytes[4];
} fi;
 
_cairo_gl_composite_prepare_buffer (ctx, 6,
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
 
v[15] = v[ 6] = v[0] = x1;
v[10] = v[ 4] = v[1] = y1;
v[12] = v[ 9] = v[3] = x2;
v[16] = v[13] = v[7] = y2;
 
fi.bytes[0] = 0;
fi.bytes[1] = 0;
fi.bytes[2] = 0;
fi.bytes[3] = alpha;
v[17] =v[14] = v[11] = v[8] = v[5] = v[2] = fi.f;
 
ctx->vb_offset += 6*3 * sizeof(GLfloat);
}
 
cairo_gl_emit_span_t
_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx)
{
if (ctx->operands[CAIRO_GL_TEX_MASK].type != CAIRO_GL_OPERAND_NONE) {
switch (ctx->operands[CAIRO_GL_TEX_MASK].type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
break;
 
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
if (!ctx->operands[CAIRO_GL_TEX_MASK].gradient.texgen)
return _cairo_gl_composite_emit_span;
break;
 
case CAIRO_GL_OPERAND_TEXTURE:
if (!ctx->operands[CAIRO_GL_TEX_MASK].texture.texgen)
return _cairo_gl_composite_emit_span;
break;
}
}
 
switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
break;
 
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
if (!ctx->operands[CAIRO_GL_TEX_SOURCE].gradient.texgen)
return _cairo_gl_composite_emit_span;
break;
 
case CAIRO_GL_OPERAND_TEXTURE:
if (!ctx->operands[CAIRO_GL_TEX_SOURCE].texture.texgen)
return _cairo_gl_composite_emit_span;
}
 
return _cairo_gl_composite_emit_solid_span;
}
 
static inline void
_cairo_gl_composite_emit_glyph_vertex (cairo_gl_context_t *ctx,
GLfloat x, GLfloat y,
GLfloat glyph_x, GLfloat glyph_y)
{
GLfloat *vb = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
 
*vb++ = x;
*vb++ = y;
 
_cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y);
 
*vb++ = glyph_x;
*vb++ = glyph_y;
 
ctx->vb_offset += ctx->vertex_size;
}
 
static void
_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
GLfloat glyph_x1, GLfloat glyph_y1,
GLfloat glyph_x2, GLfloat glyph_y2)
{
_cairo_gl_composite_prepare_buffer (ctx, 6,
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
 
_cairo_gl_composite_emit_glyph_vertex (ctx, x1, y1, glyph_x1, glyph_y1);
_cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
_cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
 
_cairo_gl_composite_emit_glyph_vertex (ctx, x2, y1, glyph_x2, glyph_y1);
_cairo_gl_composite_emit_glyph_vertex (ctx, x2, y2, glyph_x2, glyph_y2);
_cairo_gl_composite_emit_glyph_vertex (ctx, x1, y2, glyph_x1, glyph_y2);
}
 
static void
_cairo_gl_composite_emit_solid_glyph (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
GLfloat glyph_x1, GLfloat glyph_y1,
GLfloat glyph_x2, GLfloat glyph_y2)
{
GLfloat *v;
 
_cairo_gl_composite_prepare_buffer (ctx, 6,
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES);
 
v = (GLfloat *) (void *) &ctx->vb[ctx->vb_offset];
 
v[20] = v[ 8] = v[0] = x1;
v[13] = v[ 5] = v[1] = y1;
v[22] = v[10] = v[2] = glyph_x1;
v[15] = v[ 7] = v[3] = glyph_y1;
 
v[16] = v[12] = v[4] = x2;
v[18] = v[14] = v[6] = glyph_x2;
 
v[21] = v[17] = v[ 9] = y2;
v[23] = v[19] = v[11] = glyph_y2;
 
ctx->vb_offset += 4 * 6 * sizeof (GLfloat);
}
 
cairo_gl_emit_glyph_t
_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx)
{
switch (ctx->operands[CAIRO_GL_TEX_SOURCE].type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
return _cairo_gl_composite_emit_solid_glyph;
 
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
case CAIRO_GL_OPERAND_TEXTURE:
return _cairo_gl_composite_emit_glyph;
}
}
 
void
_cairo_gl_composite_fini (cairo_gl_composite_t *setup)
{
_cairo_gl_operand_destroy (&setup->src);
_cairo_gl_operand_destroy (&setup->mask);
}
 
cairo_status_t
_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup,
cairo_operator_t op,
cairo_bool_t assume_component_alpha)
{
if (assume_component_alpha) {
if (op != CAIRO_OPERATOR_CLEAR &&
op != CAIRO_OPERATOR_OVER &&
op != CAIRO_OPERATOR_ADD)
return UNSUPPORTED ("unsupported component alpha operator");
} else {
if (! _cairo_gl_operator_is_supported (op))
return UNSUPPORTED ("unsupported operator");
}
 
setup->op = op;
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_gl_composite_init (cairo_gl_composite_t *setup,
cairo_operator_t op,
cairo_gl_surface_t *dst,
cairo_bool_t assume_component_alpha)
{
cairo_status_t status;
 
memset (setup, 0, sizeof (cairo_gl_composite_t));
 
status = _cairo_gl_composite_set_operator (setup, op,
assume_component_alpha);
if (status)
return status;
 
setup->dst = dst;
setup->clip_region = dst->clip_region;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_gl_composite_append_vertex_indices (cairo_gl_context_t *ctx,
int number_of_new_indices)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_array_t *indices = &ctx->tristrip_indices;
int number_of_indices = _cairo_array_num_elements (indices);
unsigned short current_vertex_index = 0;
int i;
 
assert (number_of_new_indices > 0);
 
/* If any preexisting triangle triangle strip indices exist on this
context, we insert a set of degenerate triangles from the last
preexisting vertex to our first one. */
if (number_of_indices > 0) {
const unsigned short *indices_array = _cairo_array_index_const (indices, 0);
current_vertex_index = indices_array[number_of_indices - 1];
 
status = _cairo_array_append (indices, &current_vertex_index);
if (unlikely (status))
return status;
 
current_vertex_index++;
status =_cairo_array_append (indices, &current_vertex_index);
if (unlikely (status))
return status;
}
 
for (i = 0; i < number_of_new_indices; i++) {
status = _cairo_array_append (indices, &current_vertex_index);
current_vertex_index++;
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_point_t quad[4])
{
_cairo_gl_composite_prepare_buffer (ctx, 4,
CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
 
_cairo_gl_composite_emit_point (ctx, &quad[0]);
_cairo_gl_composite_emit_point (ctx, &quad[1]);
 
/* Cairo stores quad vertices in counter-clockwise order, but we need to
emit them from top to bottom in the triangle strip, so we need to reverse
the order of the last two vertices. */
_cairo_gl_composite_emit_point (ctx, &quad[3]);
_cairo_gl_composite_emit_point (ctx, &quad[2]);
 
return _cairo_gl_composite_append_vertex_indices (ctx, 4);
}
 
cairo_int_status_t
_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_point_t triangle[3])
{
_cairo_gl_composite_prepare_buffer (ctx, 3,
CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS);
 
_cairo_gl_composite_emit_point (ctx, &triangle[0]);
_cairo_gl_composite_emit_point (ctx, &triangle[1]);
_cairo_gl_composite_emit_point (ctx, &triangle[2]);
return _cairo_gl_composite_append_vertex_indices (ctx, 3);
}
/programs/develop/libraries/cairo/src/cairo-gl-device.c
0,0 → 1,802
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2010 Linaro Limited
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-gl-private.h"
 
#define MAX_MSAA_SAMPLES 4
 
static void
_gl_lock (void *device)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
 
ctx->acquire (ctx);
}
 
static void
_gl_unlock (void *device)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *) device;
 
ctx->release (ctx);
}
 
static cairo_status_t
_gl_flush (void *device)
{
cairo_gl_context_t *ctx;
cairo_status_t status;
 
status = _cairo_gl_context_acquire (device, &ctx);
if (unlikely (status))
return status;
 
_cairo_gl_composite_flush (ctx);
 
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
 
if (ctx->clip_region) {
cairo_region_destroy (ctx->clip_region);
ctx->clip_region = NULL;
}
 
ctx->current_target = NULL;
ctx->current_operator = -1;
ctx->vertex_size = 0;
ctx->pre_shader = NULL;
_cairo_gl_set_shader (ctx, NULL);
 
ctx->dispatch.BindBuffer (GL_ARRAY_BUFFER, 0);
 
glDisable (GL_SCISSOR_TEST);
glDisable (GL_BLEND);
 
return _cairo_gl_context_release (ctx, status);
}
 
static void
_gl_finish (void *device)
{
cairo_gl_context_t *ctx = device;
int n;
 
_gl_lock (device);
 
_cairo_cache_fini (&ctx->gradients);
 
_cairo_gl_context_fini_shaders (ctx);
 
for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
_cairo_gl_glyph_cache_fini (ctx, &ctx->glyph_cache[n]);
 
_gl_unlock (device);
}
 
static void
_gl_destroy (void *device)
{
cairo_gl_context_t *ctx = device;
 
ctx->acquire (ctx);
 
while (! cairo_list_is_empty (&ctx->fonts)) {
cairo_gl_font_t *font;
 
font = cairo_list_first_entry (&ctx->fonts,
cairo_gl_font_t,
link);
 
cairo_list_del (&font->base.link);
cairo_list_del (&font->link);
free (font);
}
 
_cairo_array_fini (&ctx->tristrip_indices);
 
cairo_region_destroy (ctx->clip_region);
_cairo_clip_destroy (ctx->clip);
 
free (ctx->vb);
 
ctx->destroy (ctx);
 
free (ctx);
}
 
static const cairo_device_backend_t _cairo_gl_device_backend = {
CAIRO_DEVICE_TYPE_GL,
 
_gl_lock,
_gl_unlock,
 
_gl_flush, /* flush */
_gl_finish,
_gl_destroy,
};
 
static cairo_bool_t
_cairo_gl_msaa_compositor_enabled (void)
{
const char *env = getenv ("CAIRO_GL_COMPOSITOR");
return env && strcmp(env, "msaa") == 0;
}
 
static cairo_bool_t
test_can_read_bgra (cairo_gl_flavor_t gl_flavor)
{
/* Desktop GL always supports BGRA formats. */
if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
return TRUE;
 
assert (gl_flavor == CAIRO_GL_FLAVOR_ES);
 
/* For OpenGL ES we have to look for the specific extension and BGRA only
* matches cairo's integer packed bytes on little-endian machines. */
if (!_cairo_is_little_endian())
return FALSE;
return _cairo_gl_has_extension ("EXT_read_format_bgra");
}
 
cairo_status_t
_cairo_gl_context_init (cairo_gl_context_t *ctx)
{
cairo_status_t status;
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
int gl_version = _cairo_gl_get_version ();
cairo_gl_flavor_t gl_flavor = _cairo_gl_get_flavor ();
int n;
 
cairo_bool_t is_desktop = gl_flavor == CAIRO_GL_FLAVOR_DESKTOP;
cairo_bool_t is_gles = gl_flavor == CAIRO_GL_FLAVOR_ES;
 
_cairo_device_init (&ctx->base, &_cairo_gl_device_backend);
 
/* XXX The choice of compositor should be made automatically at runtime.
* However, it is useful to force one particular compositor whilst
* testing.
*/
if (_cairo_gl_msaa_compositor_enabled ())
ctx->compositor = _cairo_gl_msaa_compositor_get ();
else
ctx->compositor = _cairo_gl_span_compositor_get ();
 
 
ctx->thread_aware = TRUE;
 
memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
cairo_list_init (&ctx->fonts);
 
/* Support only GL version >= 1.3 */
if (gl_version < CAIRO_GL_VERSION_ENCODE (1, 3))
return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
 
/* Check for required extensions */
if (is_desktop) {
if (_cairo_gl_has_extension ("GL_ARB_texture_non_power_of_two")) {
ctx->tex_target = GL_TEXTURE_2D;
ctx->has_npot_repeat = TRUE;
} else if (_cairo_gl_has_extension ("GL_ARB_texture_rectangle")) {
ctx->tex_target = GL_TEXTURE_RECTANGLE;
ctx->has_npot_repeat = FALSE;
} else
return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
} else {
ctx->tex_target = GL_TEXTURE_2D;
if (_cairo_gl_has_extension ("GL_OES_texture_npot") ||
_cairo_gl_has_extension ("GL_IMG_texture_npot"))
ctx->has_npot_repeat = TRUE;
else
ctx->has_npot_repeat = FALSE;
}
 
if (is_desktop && gl_version < CAIRO_GL_VERSION_ENCODE (2, 1) &&
! _cairo_gl_has_extension ("GL_ARB_pixel_buffer_object"))
return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
 
if (is_gles && ! _cairo_gl_has_extension ("GL_EXT_texture_format_BGRA8888"))
return _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
 
ctx->has_map_buffer =
is_desktop || (is_gles && _cairo_gl_has_extension ("GL_OES_mapbuffer"));
 
ctx->can_read_bgra = test_can_read_bgra (gl_flavor);
 
ctx->has_mesa_pack_invert =
_cairo_gl_has_extension ("GL_MESA_pack_invert");
 
ctx->has_packed_depth_stencil =
(is_desktop && _cairo_gl_has_extension ("GL_EXT_packed_depth_stencil")) ||
(is_gles && _cairo_gl_has_extension ("GL_OES_packed_depth_stencil"));
 
ctx->num_samples = 1;
 
#if CAIRO_HAS_GL_SURFACE
if (is_desktop && ctx->has_packed_depth_stencil &&
(gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
_cairo_gl_has_extension ("GL_ARB_framebuffer_object") ||
(_cairo_gl_has_extension ("GL_EXT_framebuffer_blit") &&
_cairo_gl_has_extension ("GL_EXT_framebuffer_multisample")))) {
glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
}
#endif
 
#if CAIRO_HAS_GLESV2_SURFACE && defined(GL_MAX_SAMPLES_EXT)
if (is_gles && ctx->has_packed_depth_stencil &&
_cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture")) {
glGetIntegerv(GL_MAX_SAMPLES_EXT, &ctx->num_samples);
}
#endif
 
#if CAIRO_HAS_GLESV2_SURFACE && defined(GL_MAX_SAMPLES_IMG)
if (is_gles && ctx->has_packed_depth_stencil &&
_cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture")) {
glGetIntegerv(GL_MAX_SAMPLES_IMG, &ctx->num_samples);
}
#endif
 
ctx->supports_msaa = ctx->num_samples > 1;
if (ctx->num_samples > MAX_MSAA_SAMPLES)
ctx->num_samples = MAX_MSAA_SAMPLES;
 
 
ctx->current_operator = -1;
ctx->gl_flavor = gl_flavor;
 
status = _cairo_gl_context_init_shaders (ctx);
if (unlikely (status))
return status;
 
status = _cairo_cache_init (&ctx->gradients,
_cairo_gl_gradient_equal,
NULL,
(cairo_destroy_func_t) _cairo_gl_gradient_destroy,
CAIRO_GL_GRADIENT_CACHE_SIZE);
if (unlikely (status))
return status;
 
ctx->vb = malloc (CAIRO_GL_VBO_SIZE);
if (unlikely (ctx->vb == NULL)) {
_cairo_cache_fini (&ctx->gradients);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
ctx->primitive_type = CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES;
_cairo_array_init (&ctx->tristrip_indices, sizeof (unsigned short));
 
/* PBO for any sort of texture upload */
dispatch->GenBuffers (1, &ctx->texture_load_pbo);
 
ctx->max_framebuffer_size = 0;
glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
ctx->max_texture_size = 0;
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
ctx->max_textures = 0;
glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS, &ctx->max_textures);
 
for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
_cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
 
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_gl_context_activate (cairo_gl_context_t *ctx,
cairo_gl_tex_t tex_unit)
{
if (ctx->max_textures <= (GLint) tex_unit) {
if (tex_unit < 2) {
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_destroy_operand (ctx, ctx->max_textures - 1);
}
glActiveTexture (ctx->max_textures - 1);
} else {
glActiveTexture (GL_TEXTURE0 + tex_unit);
}
}
 
static GLenum
_get_depth_stencil_format (cairo_gl_context_t *ctx)
{
/* This is necessary to properly handle the situation where both
OpenGL and OpenGLES are active and returning a sane default. */
#if CAIRO_HAS_GL_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
return GL_DEPTH_STENCIL;
#endif
 
#if CAIRO_HAS_GLESV2_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
return GL_DEPTH24_STENCIL8_OES;
#endif
 
#if CAIRO_HAS_GL_SURFACE
return GL_DEPTH_STENCIL;
#elif CAIRO_HAS_GLESV2_SURFACE
return GL_DEPTH24_STENCIL8_OES;
#endif
}
 
#if CAIRO_HAS_GLESV2_SURFACE
static void
_cairo_gl_ensure_msaa_gles_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
if (surface->msaa_active)
return;
 
ctx->dispatch.FramebufferTexture2DMultisample(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
ctx->tex_target,
surface->tex,
0,
ctx->num_samples);
 
/* From now on MSAA will always be active on this surface. */
surface->msaa_active = TRUE;
}
#endif
 
static void
_cairo_gl_ensure_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
GLenum status;
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
 
if (likely (surface->fb))
return;
 
/* Create a framebuffer object wrapping the texture so that we can render
* to it.
*/
dispatch->GenFramebuffers (1, &surface->fb);
dispatch->BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
 
/* Unlike for desktop GL we only maintain one multisampling framebuffer
for OpenGLES since the EXT_multisampled_render_to_texture extension
does not require an explicit multisample resolution. */
#if CAIRO_HAS_GLESV2_SURFACE
if (surface->supports_msaa && _cairo_gl_msaa_compositor_enabled () &&
ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
_cairo_gl_ensure_msaa_gles_framebuffer (ctx, surface);
} else
#endif
dispatch->FramebufferTexture2D (GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
ctx->tex_target,
surface->tex,
0);
 
#if CAIRO_HAS_GL_SURFACE
glDrawBuffer (GL_COLOR_ATTACHMENT0);
glReadBuffer (GL_COLOR_ATTACHMENT0);
#endif
 
status = dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
const char *str;
switch (status) {
//case GL_FRAMEBUFFER_UNDEFINED: str= "undefined"; break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: str= "incomplete attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: str= "incomplete/missing attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: str= "incomplete draw buffer"; break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: str= "incomplete read buffer"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: str= "unsupported"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: str= "incomplete multiple"; break;
default: str = "unknown error"; break;
}
 
fprintf (stderr,
"destination is framebuffer incomplete: %s [%#x]\n",
str, status);
}
}
#if CAIRO_HAS_GL_SURFACE
static void
_cairo_gl_ensure_multisampling (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
assert (surface->supports_msaa);
assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP);
 
if (surface->msaa_fb)
return;
 
/* We maintain a separate framebuffer for multisampling operations.
This allows us to do a fast paint to the non-multisampling framebuffer
when mulitsampling is disabled. */
ctx->dispatch.GenFramebuffers (1, &surface->msaa_fb);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
ctx->dispatch.GenRenderbuffers (1, &surface->msaa_rb);
ctx->dispatch.BindRenderbuffer (GL_RENDERBUFFER, surface->msaa_rb);
 
/* FIXME: For now we assume that textures passed from the outside have GL_RGBA
format, but eventually we need to expose a way for the API consumer to pass
this information. */
ctx->dispatch.RenderbufferStorageMultisample (GL_RENDERBUFFER,
ctx->num_samples,
GL_RGBA,
surface->width,
surface->height);
ctx->dispatch.FramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
surface->msaa_rb);
 
/* Cairo surfaces start out initialized to transparent (black) */
glDisable (GL_SCISSOR_TEST);
glClearColor (0, 0, 0, 0);
glClear (GL_COLOR_BUFFER_BIT);
}
#endif
 
static cairo_bool_t
_cairo_gl_ensure_msaa_depth_stencil_buffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
if (surface->msaa_depth_stencil)
return TRUE;
 
_cairo_gl_ensure_framebuffer (ctx, surface);
#if CAIRO_HAS_GL_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
_cairo_gl_ensure_multisampling (ctx, surface);
#endif
 
dispatch->GenRenderbuffers (1, &surface->msaa_depth_stencil);
dispatch->BindRenderbuffer (GL_RENDERBUFFER,
surface->msaa_depth_stencil);
 
dispatch->RenderbufferStorageMultisample (GL_RENDERBUFFER,
ctx->num_samples,
_get_depth_stencil_format (ctx),
surface->width,
surface->height);
 
#if CAIRO_HAS_GL_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER,
surface->msaa_depth_stencil);
}
#endif
 
#if CAIRO_HAS_GLESV2_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
surface->msaa_depth_stencil);
dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER,
surface->msaa_depth_stencil);
}
#endif
 
if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
dispatch->DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
surface->msaa_depth_stencil = 0;
return FALSE;
}
 
return TRUE;
}
 
static cairo_bool_t
_cairo_gl_ensure_depth_stencil_buffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
 
if (surface->depth_stencil)
return TRUE;
 
_cairo_gl_ensure_framebuffer (ctx, surface);
 
dispatch->GenRenderbuffers (1, &surface->depth_stencil);
dispatch->BindRenderbuffer (GL_RENDERBUFFER, surface->depth_stencil);
dispatch->RenderbufferStorage (GL_RENDERBUFFER,
_get_depth_stencil_format (ctx),
surface->width, surface->height);
 
dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, surface->depth_stencil);
dispatch->FramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, surface->depth_stencil);
if (dispatch->CheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
dispatch->DeleteRenderbuffers (1, &surface->depth_stencil);
surface->depth_stencil = 0;
return FALSE;
}
 
return TRUE;
}
 
cairo_bool_t
_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
if (! _cairo_gl_surface_is_texture (surface))
return TRUE; /* best guess for now, will check later */
if (! ctx->has_packed_depth_stencil)
return FALSE;
 
if (surface->msaa_active)
return _cairo_gl_ensure_msaa_depth_stencil_buffer (ctx, surface);
else
return _cairo_gl_ensure_depth_stencil_buffer (ctx, surface);
}
 
/*
* Stores a parallel projection transformation in matrix 'm',
* using column-major order.
*
* This is equivalent to:
*
* glLoadIdentity()
* gluOrtho2D()
*
* The calculation for the ortho tranformation was taken from the
* mesa source code.
*/
static void
_gl_identity_ortho (GLfloat *m,
GLfloat left, GLfloat right,
GLfloat bottom, GLfloat top)
{
#define M(row,col) m[col*4+row]
M(0,0) = 2.f / (right - left);
M(0,1) = 0.f;
M(0,2) = 0.f;
M(0,3) = -(right + left) / (right - left);
 
M(1,0) = 0.f;
M(1,1) = 2.f / (top - bottom);
M(1,2) = 0.f;
M(1,3) = -(top + bottom) / (top - bottom);
 
M(2,0) = 0.f;
M(2,1) = 0.f;
M(2,2) = -1.f;
M(2,3) = 0.f;
 
M(3,0) = 0.f;
M(3,1) = 0.f;
M(3,2) = 0.f;
M(3,3) = 1.f;
#undef M
}
 
#if CAIRO_HAS_GL_SURFACE
static void
bind_multisample_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
cairo_bool_t stencil_test_enabled;
cairo_bool_t scissor_test_enabled;
 
assert (surface->supports_msaa);
assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP);
 
_cairo_gl_ensure_framebuffer (ctx, surface);
_cairo_gl_ensure_multisampling (ctx, surface);
 
if (surface->msaa_active) {
glEnable (GL_MULTISAMPLE);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
return;
}
 
_cairo_gl_composite_flush (ctx);
 
stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST);
scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST);
glDisable (GL_STENCIL_TEST);
glDisable (GL_SCISSOR_TEST);
 
glEnable (GL_MULTISAMPLE);
 
/* The last time we drew to the surface, we were not using multisampling,
so we need to blit from the non-multisampling framebuffer into the
multisampling framebuffer. */
ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->msaa_fb);
ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->fb);
ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
0, 0, surface->width, surface->height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->msaa_fb);
 
if (stencil_test_enabled)
glEnable (GL_STENCIL_TEST);
if (scissor_test_enabled)
glEnable (GL_SCISSOR_TEST);
}
#endif
 
#if CAIRO_HAS_GL_SURFACE
static void
bind_singlesample_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface)
{
cairo_bool_t stencil_test_enabled;
cairo_bool_t scissor_test_enabled;
 
assert (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP);
_cairo_gl_ensure_framebuffer (ctx, surface);
 
if (! surface->msaa_active) {
glDisable (GL_MULTISAMPLE);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
return;
}
 
_cairo_gl_composite_flush (ctx);
 
stencil_test_enabled = glIsEnabled (GL_STENCIL_TEST);
scissor_test_enabled = glIsEnabled (GL_SCISSOR_TEST);
glDisable (GL_STENCIL_TEST);
glDisable (GL_SCISSOR_TEST);
 
glDisable (GL_MULTISAMPLE);
 
/* The last time we drew to the surface, we were using multisampling,
so we need to blit from the multisampling framebuffer into the
non-multisampling framebuffer. */
ctx->dispatch.BindFramebuffer (GL_DRAW_FRAMEBUFFER, surface->fb);
ctx->dispatch.BindFramebuffer (GL_READ_FRAMEBUFFER, surface->msaa_fb);
ctx->dispatch.BlitFramebuffer (0, 0, surface->width, surface->height,
0, 0, surface->width, surface->height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
 
if (stencil_test_enabled)
glEnable (GL_STENCIL_TEST);
if (scissor_test_enabled)
glEnable (GL_SCISSOR_TEST);
}
#endif
 
void
_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface,
cairo_bool_t multisampling)
{
if (_cairo_gl_surface_is_texture (surface)) {
/* OpenGL ES surfaces only have either a multisample framebuffer or a
* singlesample framebuffer, so we cannot switch back and forth. */
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
_cairo_gl_ensure_framebuffer (ctx, surface);
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, surface->fb);
return;
}
 
#if CAIRO_HAS_GL_SURFACE
if (multisampling)
bind_multisample_framebuffer (ctx, surface);
else
bind_singlesample_framebuffer (ctx, surface);
#endif
} else {
ctx->dispatch.BindFramebuffer (GL_FRAMEBUFFER, 0);
 
#if CAIRO_HAS_GL_SURFACE
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP) {
if (multisampling)
glEnable (GL_MULTISAMPLE);
else
glDisable (GL_MULTISAMPLE);
}
#endif
}
 
surface->msaa_active = multisampling;
}
 
void
_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface,
cairo_bool_t multisampling)
{
cairo_bool_t changing_surface, changing_sampling;
 
/* The decision whether or not to use multisampling happens when
* we create an OpenGL ES surface, so we can never switch modes. */
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES)
multisampling = surface->msaa_active;
 
changing_surface = ctx->current_target != surface || surface->needs_update;
changing_sampling = surface->msaa_active != multisampling;
if (! changing_surface && ! changing_sampling)
return;
 
if (! changing_surface) {
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
return;
}
 
_cairo_gl_composite_flush (ctx);
 
ctx->current_target = surface;
surface->needs_update = FALSE;
 
if (! _cairo_gl_surface_is_texture (surface)) {
ctx->make_current (ctx, surface);
}
 
_cairo_gl_context_bind_framebuffer (ctx, surface, multisampling);
 
if (! _cairo_gl_surface_is_texture (surface)) {
#if CAIRO_HAS_GL_SURFACE
glDrawBuffer (GL_BACK_LEFT);
glReadBuffer (GL_BACK_LEFT);
#endif
}
 
glDisable (GL_DITHER);
glViewport (0, 0, surface->width, surface->height);
 
if (_cairo_gl_surface_is_texture (surface))
_gl_identity_ortho (ctx->modelviewprojection_matrix,
0, surface->width, 0, surface->height);
else
_gl_identity_ortho (ctx->modelviewprojection_matrix,
0, surface->width, surface->height, 0);
}
 
void
cairo_gl_device_set_thread_aware (cairo_device_t *device,
cairo_bool_t thread_aware)
{
if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return;
}
((cairo_gl_context_t *) device)->thread_aware = thread_aware;
}
/programs/develop/libraries/cairo/src/cairo-gl-dispatch-private.h
0,0 → 1,129
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Linaro Limited
*
* 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.
*
* Contributor(s):
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#ifndef CAIRO_GL_DISPATCH_PRIVATE_H
#define CAIRO_GL_DISPATCH_PRIVATE_H
 
#include "cairo-gl-private.h"
#include <stddef.h>
 
typedef enum _cairo_gl_dispatch_name {
CAIRO_GL_DISPATCH_NAME_CORE,
CAIRO_GL_DISPATCH_NAME_EXT,
CAIRO_GL_DISPATCH_NAME_ES,
CAIRO_GL_DISPATCH_NAME_COUNT
} cairo_gl_dispatch_name_t;
 
typedef struct _cairo_gl_dispatch_entry {
const char *name[CAIRO_GL_DISPATCH_NAME_COUNT];
size_t offset;
} cairo_gl_dispatch_entry_t;
 
#define DISPATCH_ENTRY_ARB(name) { { "gl"#name, "gl"#name"ARB", "gl"#name }, \
offsetof(cairo_gl_dispatch_t, name) }
#define DISPATCH_ENTRY_EXT(name) { { "gl"#name, "gl"#name"EXT", "gl"#name }, \
offsetof(cairo_gl_dispatch_t, name) }
#define DISPATCH_ENTRY_ARB_OES(name) { { "gl"#name, "gl"#name"ARB", "gl"#name"OES" }, \
offsetof(cairo_gl_dispatch_t, name) }
#define DISPATCH_ENTRY_EXT_IMG(name) { { "gl"#name, "gl"#name"EXT", "gl"#name"IMG" }, \
offsetof(cairo_gl_dispatch_t, name) }
#define DISPATCH_ENTRY_CUSTOM(name, name2) { { "gl"#name, "gl"#name2, "gl"#name }, \
offsetof(cairo_gl_dispatch_t, name)}
#define DISPATCH_ENTRY_LAST { { NULL, NULL, NULL }, 0 }
 
cairo_private cairo_gl_dispatch_entry_t dispatch_buffers_entries[] = {
DISPATCH_ENTRY_ARB (GenBuffers),
DISPATCH_ENTRY_ARB (BindBuffer),
DISPATCH_ENTRY_ARB (BufferData),
DISPATCH_ENTRY_ARB_OES (MapBuffer),
DISPATCH_ENTRY_ARB_OES (UnmapBuffer),
DISPATCH_ENTRY_LAST
};
 
cairo_private cairo_gl_dispatch_entry_t dispatch_shaders_entries[] = {
/* Shaders */
DISPATCH_ENTRY_CUSTOM (CreateShader, CreateShaderObjectARB),
DISPATCH_ENTRY_ARB (ShaderSource),
DISPATCH_ENTRY_ARB (CompileShader),
DISPATCH_ENTRY_CUSTOM (GetShaderiv, GetObjectParameterivARB),
DISPATCH_ENTRY_CUSTOM (GetShaderInfoLog, GetInfoLogARB),
DISPATCH_ENTRY_CUSTOM (DeleteShader, DeleteObjectARB),
 
/* Programs */
DISPATCH_ENTRY_CUSTOM (CreateProgram, CreateProgramObjectARB),
DISPATCH_ENTRY_CUSTOM (AttachShader, AttachObjectARB),
DISPATCH_ENTRY_CUSTOM (DeleteProgram, DeleteObjectARB),
DISPATCH_ENTRY_ARB (LinkProgram),
DISPATCH_ENTRY_CUSTOM (UseProgram, UseProgramObjectARB),
DISPATCH_ENTRY_CUSTOM (GetProgramiv, GetObjectParameterivARB),
DISPATCH_ENTRY_CUSTOM (GetProgramInfoLog, GetInfoLogARB),
 
/* Uniforms */
DISPATCH_ENTRY_ARB (GetUniformLocation),
DISPATCH_ENTRY_ARB (Uniform1f),
DISPATCH_ENTRY_ARB (Uniform2f),
DISPATCH_ENTRY_ARB (Uniform3f),
DISPATCH_ENTRY_ARB (Uniform4f),
DISPATCH_ENTRY_ARB (UniformMatrix3fv),
DISPATCH_ENTRY_ARB (UniformMatrix4fv),
DISPATCH_ENTRY_ARB (Uniform1i),
 
/* Attributes */
DISPATCH_ENTRY_ARB (BindAttribLocation),
DISPATCH_ENTRY_ARB (VertexAttribPointer),
DISPATCH_ENTRY_ARB (EnableVertexAttribArray),
DISPATCH_ENTRY_ARB (DisableVertexAttribArray),
 
DISPATCH_ENTRY_LAST
};
 
cairo_private cairo_gl_dispatch_entry_t dispatch_fbo_entries[] = {
DISPATCH_ENTRY_EXT (GenFramebuffers),
DISPATCH_ENTRY_EXT (BindFramebuffer),
DISPATCH_ENTRY_EXT (FramebufferTexture2D),
DISPATCH_ENTRY_EXT (CheckFramebufferStatus),
DISPATCH_ENTRY_EXT (DeleteFramebuffers),
DISPATCH_ENTRY_EXT (GenRenderbuffers),
DISPATCH_ENTRY_EXT (BindRenderbuffer),
DISPATCH_ENTRY_EXT (RenderbufferStorage),
DISPATCH_ENTRY_EXT (FramebufferRenderbuffer),
DISPATCH_ENTRY_EXT (DeleteRenderbuffers),
DISPATCH_ENTRY_EXT (BlitFramebuffer),
DISPATCH_ENTRY_LAST
};
 
cairo_private cairo_gl_dispatch_entry_t dispatch_multisampling_entries[] = {
DISPATCH_ENTRY_EXT_IMG (RenderbufferStorageMultisample),
DISPATCH_ENTRY_EXT_IMG (FramebufferTexture2DMultisample),
DISPATCH_ENTRY_LAST
};
 
#endif /* CAIRO_GL_DISPATCH_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-gl-dispatch.c
0,0 → 1,261
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Linaro Limited
*
* 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.
*
* Contributor(s):
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#include "cairoint.h"
#include "cairo-gl-private.h"
#include "cairo-gl-dispatch-private.h"
#if CAIRO_HAS_DLSYM
#include <dlfcn.h>
#endif
 
#if CAIRO_HAS_DLSYM
static void *
_cairo_gl_dispatch_open_lib (void)
{
return dlopen (NULL, RTLD_LAZY);
}
 
static void
_cairo_gl_dispatch_close_lib (void *handle)
{
dlclose (handle);
}
 
static cairo_gl_generic_func_t
_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
{
return (cairo_gl_generic_func_t) dlsym (handle, name);
}
#else
static void *
_cairo_gl_dispatch_open_lib (void)
{
return NULL;
}
 
static void
_cairo_gl_dispatch_close_lib (void *handle)
{
return;
}
 
static cairo_gl_generic_func_t
_cairo_gl_dispatch_get_proc_addr (void *handle, const char *name)
{
return NULL;
}
#endif /* CAIRO_HAS_DLSYM */
 
 
static void
_cairo_gl_dispatch_init_entries (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr,
cairo_gl_dispatch_entry_t *entries,
cairo_gl_dispatch_name_t dispatch_name)
{
cairo_gl_dispatch_entry_t *entry = entries;
void *handle = _cairo_gl_dispatch_open_lib ();
 
while (entry->name[CAIRO_GL_DISPATCH_NAME_CORE] != NULL) {
void *dispatch_ptr = &((char *) dispatch)[entry->offset];
const char *name = entry->name[dispatch_name];
 
/*
* In strictly conforming EGL implementations, eglGetProcAddress() can
* be used only to get extension functions, but some of the functions
* we want belong to core GL(ES). If the *GetProcAddress function
* provided by the context fails, try to get the address of the wanted
* GL function using standard system facilities (eg dlsym() in *nix
* systems).
*/
cairo_gl_generic_func_t func = get_proc_addr (name);
if (func == NULL)
func = _cairo_gl_dispatch_get_proc_addr (handle, name);
 
*((cairo_gl_generic_func_t *) dispatch_ptr) = func;
 
++entry;
}
 
_cairo_gl_dispatch_close_lib (handle);
}
 
static cairo_status_t
_cairo_gl_dispatch_init_buffers (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr,
int gl_version, cairo_gl_flavor_t gl_flavor)
{
cairo_gl_dispatch_name_t dispatch_name;
 
if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
{
if (gl_version >= CAIRO_GL_VERSION_ENCODE (1, 5))
dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
else if (_cairo_gl_has_extension ("GL_ARB_vertex_buffer_object"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
else
return CAIRO_STATUS_DEVICE_ERROR;
}
else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
{
dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
}
else
{
return CAIRO_STATUS_DEVICE_ERROR;
}
 
_cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
dispatch_buffers_entries, dispatch_name);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_dispatch_init_shaders (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr,
int gl_version, cairo_gl_flavor_t gl_flavor)
{
cairo_gl_dispatch_name_t dispatch_name;
 
if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
{
if (gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
else if (_cairo_gl_has_extension ("GL_ARB_shader_objects"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
else
return CAIRO_STATUS_DEVICE_ERROR;
}
else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
{
dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
}
else
{
return CAIRO_STATUS_DEVICE_ERROR;
}
 
_cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
dispatch_shaders_entries, dispatch_name);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_dispatch_init_fbo (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr,
int gl_version, cairo_gl_flavor_t gl_flavor)
{
cairo_gl_dispatch_name_t dispatch_name;
 
if (gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
{
if (gl_version >= CAIRO_GL_VERSION_ENCODE (3, 0) ||
_cairo_gl_has_extension ("GL_ARB_framebuffer_object"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
else if (_cairo_gl_has_extension ("GL_EXT_framebuffer_object"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
else
return CAIRO_STATUS_DEVICE_ERROR;
}
else if (gl_flavor == CAIRO_GL_FLAVOR_ES &&
gl_version >= CAIRO_GL_VERSION_ENCODE (2, 0))
{
dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
}
else
{
return CAIRO_STATUS_DEVICE_ERROR;
}
 
_cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
dispatch_fbo_entries, dispatch_name);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_dispatch_init_multisampling (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr,
int gl_version,
cairo_gl_flavor_t gl_flavor)
{
/* For the multisampling table, there are two GLES versions of the
* extension, so we put one in the EXT slot and one in the real ES slot.*/
cairo_gl_dispatch_name_t dispatch_name = CAIRO_GL_DISPATCH_NAME_CORE;
if (gl_flavor == CAIRO_GL_FLAVOR_ES) {
if (_cairo_gl_has_extension ("GL_EXT_multisampled_render_to_texture"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_EXT;
else if (_cairo_gl_has_extension ("GL_IMG_multisampled_render_to_texture"))
dispatch_name = CAIRO_GL_DISPATCH_NAME_ES;
}
_cairo_gl_dispatch_init_entries (dispatch, get_proc_addr,
dispatch_multisampling_entries,
dispatch_name);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_gl_dispatch_init (cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr)
{
cairo_status_t status;
int gl_version;
cairo_gl_flavor_t gl_flavor;
 
gl_version = _cairo_gl_get_version ();
gl_flavor = _cairo_gl_get_flavor ();
 
status = _cairo_gl_dispatch_init_buffers (dispatch, get_proc_addr,
gl_version, gl_flavor);
if (status != CAIRO_STATUS_SUCCESS)
return status;
 
status = _cairo_gl_dispatch_init_shaders (dispatch, get_proc_addr,
gl_version, gl_flavor);
if (status != CAIRO_STATUS_SUCCESS)
return status;
 
status = _cairo_gl_dispatch_init_fbo (dispatch, get_proc_addr,
gl_version, gl_flavor);
if (status != CAIRO_STATUS_SUCCESS)
return status;
 
status = _cairo_gl_dispatch_init_multisampling (dispatch, get_proc_addr,
gl_version, gl_flavor);
if (status != CAIRO_STATUS_SUCCESS)
return status;
 
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-gl-ext-def-private.h
0,0 → 1,143
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Linaro Limited
*
* 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.
*
* Contributor(s):
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#ifndef CAIRO_GL_EXT_DEF_PRIVATE_H
#define CAIRO_GL_EXT_DEF_PRIVATE_H
 
#ifndef GL_TEXTURE_RECTANGLE
#define GL_TEXTURE_RECTANGLE 0x84F5
#endif
 
#ifndef GL_ARRAY_BUFFER
#define GL_ARRAY_BUFFER 0x8892
#endif
 
#ifndef GL_STREAM_DRAW
#define GL_STREAM_DRAW 0x88E0
#endif
 
#ifndef GL_WRITE_ONLY
#define GL_WRITE_ONLY 0x88B9
#endif
 
#ifndef GL_PIXEL_UNPACK_BUFFER
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#endif
 
#ifndef GL_FRAMEBUFFER
#define GL_FRAMEBUFFER 0x8D40
#endif
 
#ifndef GL_COLOR_ATTACHMENT0
#define GL_COLOR_ATTACHMENT0 0x8CE0
#endif
 
#ifndef GL_FRAMEBUFFER_COMPLETE
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 0x8CDA
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
#endif
 
#ifndef GL_FRAMEBUFFER_UNSUPPORTED
#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
#endif
 
#ifndef GL_PACK_INVERT_MESA
#define GL_PACK_INVERT_MESA 0x8758
#endif
 
#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812D
#endif
 
#ifndef GL_BGR
#define GL_BGR 0x80E0
#endif
 
#ifndef GL_BGRA
#define GL_BGRA 0x80E1
#endif
 
#ifndef GL_RGBA8
#define GL_RGBA8 0x8058
#endif
 
#ifndef GL_UNSIGNED_INT_8_8_8_8
#define GL_UNSIGNED_INT_8_8_8_8 0x8035
#endif
 
#ifndef GL_UNSIGNED_SHORT_5_6_5_REV
#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
#endif
 
#ifndef GL_UNSIGNED_SHORT_1_5_5_5_REV
#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
#endif
 
#ifndef GL_UNSIGNED_INT_8_8_8_8_REV
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#endif
 
#ifndef GL_PACK_ROW_LENGTH
#define GL_PACK_ROW_LENGTH 0x0D02
#endif
 
#ifndef GL_UNPACK_ROW_LENGTH
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#endif
 
#ifndef GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE
#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
#endif
 
#endif /* CAIRO_GL_EXT_DEF_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-gl-glyphs.c
0,0 → 1,503
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Chris Wilson
* Copyright © 2010 Intel Corporation
* Copyright © 2010 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 Chris Wilson.
*
* Contributors:
* Benjamin Otte <otte@gnome.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-rtree-private.h"
 
#define GLYPH_CACHE_WIDTH 1024
#define GLYPH_CACHE_HEIGHT 1024
#define GLYPH_CACHE_MIN_SIZE 4
#define GLYPH_CACHE_MAX_SIZE 128
 
typedef struct _cairo_gl_glyph {
cairo_rtree_node_t node;
cairo_scaled_glyph_private_t base;
cairo_scaled_glyph_t *glyph;
cairo_gl_glyph_cache_t *cache;
struct { float x, y; } p1, p2;
} cairo_gl_glyph_t;
 
static void
_cairo_gl_node_destroy (cairo_rtree_node_t *node)
{
cairo_gl_glyph_t *priv = cairo_container_of (node, cairo_gl_glyph_t, node);
cairo_scaled_glyph_t *glyph;
 
glyph = priv->glyph;
if (glyph == NULL)
return;
 
if (glyph->dev_private_key == priv->cache) {
glyph->dev_private = NULL;
glyph->dev_private_key = NULL;
}
cairo_list_del (&priv->base.link);
priv->glyph = NULL;
}
 
static void
_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font)
{
cairo_gl_glyph_t *priv = cairo_container_of (glyph_private,
cairo_gl_glyph_t,
base);
 
assert (priv->glyph);
 
_cairo_gl_node_destroy (&priv->node);
 
/* XXX thread-safety? Probably ok due to the frozen scaled-font. */
if (! priv->node.pinned)
_cairo_rtree_node_remove (&priv->cache->rtree, &priv->node);
 
assert (priv->glyph == NULL);
}
 
static cairo_int_status_t
_cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx,
cairo_gl_glyph_cache_t *cache,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
cairo_gl_glyph_t *glyph_private;
cairo_rtree_node_t *node = NULL;
cairo_int_status_t status;
int width, height;
 
width = glyph_surface->width;
if (width < GLYPH_CACHE_MIN_SIZE)
width = GLYPH_CACHE_MIN_SIZE;
height = glyph_surface->height;
if (height < GLYPH_CACHE_MIN_SIZE)
height = GLYPH_CACHE_MIN_SIZE;
 
/* search for an available slot */
status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
/* search for an unlocked slot */
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_rtree_evict_random (&cache->rtree,
width, height, &node);
if (status == CAIRO_INT_STATUS_SUCCESS) {
status = _cairo_rtree_node_insert (&cache->rtree,
node, width, height, &node);
}
}
if (status)
return status;
 
/* XXX: Make sure we use the mask texture. This should work automagically somehow */
glActiveTexture (GL_TEXTURE1);
status = _cairo_gl_surface_draw_image (cache->surface, glyph_surface,
0, 0,
glyph_surface->width, glyph_surface->height,
node->x, node->y, FALSE);
if (unlikely (status))
return status;
 
glyph_private = (cairo_gl_glyph_t *) node;
glyph_private->cache = cache;
glyph_private->glyph = scaled_glyph;
_cairo_scaled_glyph_attach_private (scaled_glyph,
&glyph_private->base,
cache,
_cairo_gl_glyph_fini);
 
scaled_glyph->dev_private = glyph_private;
scaled_glyph->dev_private_key = cache;
 
/* compute tex coords */
glyph_private->p1.x = node->x;
glyph_private->p1.y = node->y;
glyph_private->p2.x = node->x + glyph_surface->width;
glyph_private->p2.y = node->y + glyph_surface->height;
if (! _cairo_gl_device_requires_power_of_two_textures (&ctx->base)) {
glyph_private->p1.x /= GLYPH_CACHE_WIDTH;
glyph_private->p2.x /= GLYPH_CACHE_WIDTH;
glyph_private->p1.y /= GLYPH_CACHE_HEIGHT;
glyph_private->p2.y /= GLYPH_CACHE_HEIGHT;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_gl_glyph_t *
_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
cairo_scaled_glyph_t *scaled_glyph)
{
return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private);
}
 
static cairo_status_t
cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
cairo_format_t format,
cairo_gl_glyph_cache_t **cache_out)
{
cairo_gl_glyph_cache_t *cache;
cairo_content_t content;
 
switch (format) {
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
cache = &ctx->glyph_cache[0];
content = CAIRO_CONTENT_COLOR_ALPHA;
break;
case CAIRO_FORMAT_A8:
case CAIRO_FORMAT_A1:
cache = &ctx->glyph_cache[1];
content = CAIRO_CONTENT_ALPHA;
break;
default:
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
}
 
if (unlikely (cache->surface == NULL)) {
cairo_surface_t *surface;
 
surface = _cairo_gl_surface_create_scratch_for_caching (ctx,
content,
GLYPH_CACHE_WIDTH,
GLYPH_CACHE_HEIGHT);
if (unlikely (surface->status))
return surface->status;
 
_cairo_surface_release_device_reference (surface);
 
cache->surface = (cairo_gl_surface_t *)surface;
cache->surface->operand.texture.attributes.has_component_alpha =
content == CAIRO_CONTENT_COLOR_ALPHA;
}
 
*cache_out = cache;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
render_glyphs (cairo_gl_surface_t *dst,
int dst_x, int dst_y,
cairo_operator_t op,
cairo_surface_t *source,
cairo_composite_glyphs_info_t *info,
cairo_bool_t *has_component_alpha,
cairo_clip_t *clip)
{
cairo_format_t last_format = CAIRO_FORMAT_INVALID;
cairo_gl_glyph_cache_t *cache = NULL;
cairo_gl_context_t *ctx;
cairo_gl_emit_glyph_t emit = NULL;
cairo_gl_composite_t setup;
cairo_int_status_t status;
int i = 0;
 
TRACE ((stderr, "%s (%d, %d)x(%d, %d)\n", __FUNCTION__,
info->extents.x, info->extents.y,
info->extents.width, info->extents.height));
 
*has_component_alpha = FALSE;
 
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
 
status = _cairo_gl_composite_init (&setup, op, dst, TRUE);
if (unlikely (status))
goto FINISH;
 
if (source == NULL) {
_cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_WHITE);
} else {
_cairo_gl_composite_set_source_operand (&setup,
source_to_operand (source));
 
}
 
_cairo_gl_composite_set_clip (&setup, clip);
 
for (i = 0; i < info->num_glyphs; i++) {
cairo_scaled_glyph_t *scaled_glyph;
cairo_gl_glyph_t *glyph;
double x_offset, y_offset;
double x1, x2, y1, y2;
 
status = _cairo_scaled_glyph_lookup (info->font,
info->glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status))
goto FINISH;
 
if (scaled_glyph->surface->width == 0 ||
scaled_glyph->surface->height == 0)
{
continue;
}
if (scaled_glyph->surface->format != last_format) {
status = cairo_gl_context_get_glyph_cache (ctx,
scaled_glyph->surface->format,
&cache);
if (unlikely (status))
goto FINISH;
 
last_format = scaled_glyph->surface->format;
 
_cairo_gl_composite_set_mask_operand (&setup, &cache->surface->operand);
*has_component_alpha |= cache->surface->operand.texture.attributes.has_component_alpha;
 
/* XXX Shoot me. */
status = _cairo_gl_composite_begin (&setup, &ctx);
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status))
goto FINISH;
 
emit = _cairo_gl_context_choose_emit_glyph (ctx);
}
 
if (scaled_glyph->dev_private_key != cache) {
cairo_scaled_glyph_private_t *priv;
 
priv = _cairo_scaled_glyph_find_private (scaled_glyph, cache);
if (priv) {
scaled_glyph->dev_private_key = cache;
scaled_glyph->dev_private = cairo_container_of (priv,
cairo_gl_glyph_t,
base);
} else {
status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* Cache is full, so flush existing prims and try again. */
_cairo_gl_composite_flush (ctx);
_cairo_gl_glyph_cache_unlock (cache);
status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph);
}
 
if (unlikely (_cairo_int_status_is_error (status)))
goto FINISH;
}
}
 
x_offset = scaled_glyph->surface->base.device_transform.x0;
y_offset = scaled_glyph->surface->base.device_transform.y0;
 
x1 = _cairo_lround (info->glyphs[i].x - x_offset - dst_x);
y1 = _cairo_lround (info->glyphs[i].y - y_offset - dst_y);
x2 = x1 + scaled_glyph->surface->width;
y2 = y1 + scaled_glyph->surface->height;
 
glyph = _cairo_gl_glyph_cache_lock (cache, scaled_glyph);
assert (emit);
emit (ctx,
x1, y1, x2, y2,
glyph->p1.x, glyph->p1.y,
glyph->p2.x, glyph->p2.y);
}
 
status = CAIRO_STATUS_SUCCESS;
FINISH:
status = _cairo_gl_context_release (ctx, status);
 
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
render_glyphs_via_mask (cairo_gl_surface_t *dst,
int dst_x, int dst_y,
cairo_operator_t op,
cairo_surface_t *source,
cairo_composite_glyphs_info_t *info,
cairo_clip_t *clip)
{
cairo_surface_t *mask;
cairo_status_t status;
cairo_bool_t has_component_alpha;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* XXX: For non-CA, this should be CAIRO_CONTENT_ALPHA to save memory */
mask = cairo_gl_surface_create (dst->base.device,
CAIRO_CONTENT_COLOR_ALPHA,
info->extents.width,
info->extents.height);
if (unlikely (mask->status))
return mask->status;
 
status = render_glyphs ((cairo_gl_surface_t *) mask,
info->extents.x, info->extents.y,
CAIRO_OPERATOR_ADD, NULL,
info, &has_component_alpha, NULL);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_surface_pattern_t mask_pattern;
cairo_surface_pattern_t source_pattern;
cairo_rectangle_int_t clip_extents;
 
mask->is_clear = FALSE;
_cairo_pattern_init_for_surface (&mask_pattern, mask);
mask_pattern.base.has_component_alpha = has_component_alpha;
mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
mask_pattern.base.extend = CAIRO_EXTEND_NONE;
 
cairo_matrix_init_translate (&mask_pattern.base.matrix,
dst_x-info->extents.x, dst_y-info->extents.y);
 
_cairo_pattern_init_for_surface (&source_pattern, source);
cairo_matrix_init_translate (&source_pattern.base.matrix,
dst_x-info->extents.x, dst_y-info->extents.y);
 
clip = _cairo_clip_copy (clip);
clip_extents.x = info->extents.x - dst_x;
clip_extents.y = info->extents.y - dst_y;
clip_extents.width = info->extents.width;
clip_extents.height = info->extents.height;
clip = _cairo_clip_intersect_rectangle (clip, &clip_extents);
 
status = _cairo_surface_mask (&dst->base, op,
&source_pattern.base,
&mask_pattern.base,
clip);
 
_cairo_clip_destroy (clip);
 
_cairo_pattern_fini (&mask_pattern.base);
_cairo_pattern_fini (&source_pattern.base);
}
 
cairo_surface_destroy (mask);
 
return status;
}
 
cairo_int_status_t
_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
if (! _cairo_gl_operator_is_supported (extents->op))
return UNSUPPORTED ("unsupported operator");
 
/* XXX use individual masks for large glyphs? */
if (ceil (scaled_font->max_scale) >= GLYPH_CACHE_MAX_SIZE)
return UNSUPPORTED ("glyphs too large");
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_gl_composite_glyphs_with_clip (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info,
cairo_clip_t *clip)
{
cairo_gl_surface_t *dst = _dst;
cairo_bool_t has_component_alpha;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* If any of the glyphs require component alpha, we have to go through
* a mask, since only _cairo_gl_surface_composite() currently supports
* component alpha.
*/
if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER &&
(info->font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL ||
info->font->options.antialias == CAIRO_ANTIALIAS_BEST))
{
info->use_mask = TRUE;
}
 
if (info->use_mask) {
return render_glyphs_via_mask (dst, dst_x, dst_y,
op, _src, info, clip);
} else {
return render_glyphs (dst, dst_x, dst_y,
op, _src, info,
&has_component_alpha,
clip);
}
 
}
 
cairo_int_status_t
_cairo_gl_composite_glyphs (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
return _cairo_gl_composite_glyphs_with_clip (_dst, op, _src, src_x, src_y,
dst_x, dst_y, info, NULL);
}
 
void
_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
{
_cairo_rtree_init (&cache->rtree,
GLYPH_CACHE_WIDTH,
GLYPH_CACHE_HEIGHT,
GLYPH_CACHE_MIN_SIZE,
sizeof (cairo_gl_glyph_t),
_cairo_gl_node_destroy);
}
 
void
_cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx,
cairo_gl_glyph_cache_t *cache)
{
_cairo_rtree_fini (&cache->rtree);
cairo_surface_destroy (&cache->surface->base);
}
/programs/develop/libraries/cairo/src/cairo-gl-gradient-private.h
41,18 → 41,23
#ifndef CAIRO_GL_GRADIENT_PRIVATE_H
#define CAIRO_GL_GRADIENT_PRIVATE_H
 
#define GL_GLEXT_PROTOTYPES
 
#include "cairo-cache-private.h"
#include "cairo-device-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-pattern-private.h"
#include "cairo-types-private.h"
 
#include <GL/glew.h>
 
#include "cairo-gl.h"
 
#if CAIRO_HAS_GL_SURFACE
#include <GL/gl.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/glext.h>
#elif CAIRO_HAS_GLESV2_SURFACE
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
 
#define CAIRO_GL_GRADIENT_CACHE_SIZE 4096
 
/programs/develop/libraries/cairo/src/cairo-gl-gradient.c
0,0 → 1,338
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 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 Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
*/
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-gl-gradient-private.h"
#include "cairo-gl-private.h"
 
 
static int
_cairo_gl_gradient_sample_width (unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
unsigned int n;
int width;
 
width = 8;
for (n = 1; n < n_stops; n++) {
double dx = stops[n].offset - stops[n-1].offset;
double delta, max;
int ramp;
 
if (dx == 0)
return 1024; /* we need to emulate an infinitely sharp step */
 
max = fabs (stops[n].color.red - stops[n-1].color.red);
 
delta = fabs (stops[n].color.green - stops[n-1].color.green);
if (delta > max)
max = delta;
 
delta = fabs (stops[n].color.blue - stops[n-1].color.blue);
if (delta > max)
max = delta;
 
delta = fabs (stops[n].color.alpha - stops[n-1].color.alpha);
if (delta > max)
max = delta;
 
ramp = 128 * max / dx;
if (ramp > width)
width = ramp;
}
 
return (width + 7) & -8;
}
 
static uint8_t premultiply(double c, double a)
{
int v = c * a * 256;
return v - (v >> 8);
}
 
static uint32_t color_stop_to_pixel(const cairo_gradient_stop_t *stop)
{
uint8_t a, r, g, b;
 
a = stop->color.alpha_short >> 8;
r = premultiply(stop->color.red, stop->color.alpha);
g = premultiply(stop->color.green, stop->color.alpha);
b = premultiply(stop->color.blue, stop->color.alpha);
 
if (_cairo_is_little_endian ())
return a << 24 | r << 16 | g << 8 | b << 0;
else
return a << 0 | r << 8 | g << 16 | b << 24;
}
 
static cairo_status_t
_cairo_gl_gradient_render (const cairo_gl_context_t *ctx,
unsigned int n_stops,
const cairo_gradient_stop_t *stops,
void *bytes,
int width)
{
pixman_image_t *gradient, *image;
pixman_gradient_stop_t pixman_stops_stack[32];
pixman_gradient_stop_t *pixman_stops;
pixman_point_fixed_t p1, p2;
unsigned int i;
pixman_format_code_t gradient_pixman_format;
 
/*
* Ensure that the order of the gradient's components in memory is BGRA.
* This is done so that the gradient's pixel data is always suitable for
* texture upload using format=GL_BGRA and type=GL_UNSIGNED_BYTE.
*/
if (_cairo_is_little_endian ())
gradient_pixman_format = PIXMAN_a8r8g8b8;
else
gradient_pixman_format = PIXMAN_b8g8r8a8;
 
pixman_stops = pixman_stops_stack;
if (unlikely (n_stops > ARRAY_LENGTH (pixman_stops_stack))) {
pixman_stops = _cairo_malloc_ab (n_stops,
sizeof (pixman_gradient_stop_t));
if (unlikely (pixman_stops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (i = 0; i < n_stops; i++) {
pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset);
pixman_stops[i].color.red = stops[i].color.red_short;
pixman_stops[i].color.green = stops[i].color.green_short;
pixman_stops[i].color.blue = stops[i].color.blue_short;
pixman_stops[i].color.alpha = stops[i].color.alpha_short;
}
 
p1.x = _cairo_fixed_16_16_from_double (0.5);
p1.y = 0;
p2.x = _cairo_fixed_16_16_from_double (width - 0.5);
p2.y = 0;
 
gradient = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
n_stops);
if (pixman_stops != pixman_stops_stack)
free (pixman_stops);
 
if (unlikely (gradient == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
pixman_image_set_filter (gradient, PIXMAN_FILTER_BILINEAR, NULL, 0);
pixman_image_set_repeat (gradient, PIXMAN_REPEAT_PAD);
 
image = pixman_image_create_bits (gradient_pixman_format, width, 1,
bytes, sizeof(uint32_t)*width);
if (unlikely (image == NULL)) {
pixman_image_unref (gradient);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (PIXMAN_OP_SRC,
gradient, NULL, image,
0, 0,
0, 0,
0, 0,
width, 1);
 
pixman_image_unref (gradient);
pixman_image_unref (image);
 
/* We need to fudge pixel 0 to hold the left-most color stop and not
* the neareset stop to the zeroth pixel centre in order to correctly
* populate the border color. For completeness, do both edges.
*/
((uint32_t*)bytes)[0] = color_stop_to_pixel(&stops[0]);
((uint32_t*)bytes)[width-1] = color_stop_to_pixel(&stops[n_stops-1]);
 
return CAIRO_STATUS_SUCCESS;
}
 
static unsigned long
_cairo_gl_gradient_hash (unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
return _cairo_hash_bytes (n_stops,
stops,
sizeof (cairo_gradient_stop_t) * n_stops);
}
 
static cairo_gl_gradient_t *
_cairo_gl_gradient_lookup (cairo_gl_context_t *ctx,
unsigned long hash,
unsigned int n_stops,
const cairo_gradient_stop_t *stops)
{
cairo_gl_gradient_t lookup;
 
lookup.cache_entry.hash = hash,
lookup.n_stops = n_stops;
lookup.stops = stops;
 
return _cairo_cache_lookup (&ctx->gradients, &lookup.cache_entry);
}
 
cairo_bool_t
_cairo_gl_gradient_equal (const void *key_a, const void *key_b)
{
const cairo_gl_gradient_t *a = key_a;
const cairo_gl_gradient_t *b = key_b;
 
if (a->n_stops != b->n_stops)
return FALSE;
 
return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
}
 
cairo_int_status_t
_cairo_gl_gradient_create (cairo_gl_context_t *ctx,
unsigned int n_stops,
const cairo_gradient_stop_t *stops,
cairo_gl_gradient_t **gradient_out)
{
unsigned long hash;
cairo_gl_gradient_t *gradient;
cairo_status_t status;
int tex_width;
GLint internal_format;
void *data;
 
if ((unsigned int) ctx->max_texture_size / 2 <= n_stops)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
hash = _cairo_gl_gradient_hash (n_stops, stops);
 
gradient = _cairo_gl_gradient_lookup (ctx, hash, n_stops, stops);
if (gradient) {
*gradient_out = _cairo_gl_gradient_reference (gradient);
return CAIRO_STATUS_SUCCESS;
}
 
gradient = malloc (sizeof (cairo_gl_gradient_t) + sizeof (cairo_gradient_stop_t) * (n_stops - 1));
if (gradient == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
tex_width = _cairo_gl_gradient_sample_width (n_stops, stops);
if (tex_width > ctx->max_texture_size)
tex_width = ctx->max_texture_size;
 
CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 2);
gradient->cache_entry.hash = hash;
gradient->cache_entry.size = tex_width;
gradient->device = &ctx->base;
gradient->n_stops = n_stops;
gradient->stops = gradient->stops_embedded;
memcpy (gradient->stops_embedded, stops, n_stops * sizeof (cairo_gradient_stop_t));
 
glGenTextures (1, &gradient->tex);
_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
glBindTexture (ctx->tex_target, gradient->tex);
 
data = _cairo_malloc_ab (tex_width, sizeof (uint32_t));
if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup_gradient;
}
 
status = _cairo_gl_gradient_render (ctx, n_stops, stops, data, tex_width);
if (unlikely (status))
goto cleanup_data;
 
/*
* In OpenGL ES 2.0 no format conversion is allowed i.e. 'internalFormat'
* must match 'format' in glTexImage2D.
*/
if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES)
internal_format = GL_BGRA;
else
internal_format = GL_RGBA;
 
glTexImage2D (ctx->tex_target, 0, internal_format, tex_width, 1, 0,
GL_BGRA, GL_UNSIGNED_BYTE, data);
 
free (data);
 
/* we ignore errors here and just return an uncached gradient */
if (unlikely (_cairo_cache_insert (&ctx->gradients, &gradient->cache_entry)))
CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
 
*gradient_out = gradient;
return CAIRO_STATUS_SUCCESS;
 
cleanup_data:
free (data);
cleanup_gradient:
free (gradient);
return status;
}
 
cairo_gl_gradient_t *
_cairo_gl_gradient_reference (cairo_gl_gradient_t *gradient)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
 
_cairo_reference_count_inc (&gradient->ref_count);
 
return gradient;
}
 
void
_cairo_gl_gradient_destroy (cairo_gl_gradient_t *gradient)
{
cairo_gl_context_t *ctx;
cairo_status_t ignore;
 
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
 
if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
return;
 
if (_cairo_gl_context_acquire (gradient->device, &ctx) == CAIRO_STATUS_SUCCESS) {
/* The gradient my still be active in the last operation, so flush */
_cairo_gl_composite_flush (ctx);
glDeleteTextures (1, &gradient->tex);
ignore = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
}
 
free (gradient);
}
/programs/develop/libraries/cairo/src/cairo-gl-info.c
0,0 → 1,91
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2010 Linaro Limited
*
* 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.
*
* Contributor(s):
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#include "cairoint.h"
#include "cairo-gl-private.h"
 
int
_cairo_gl_get_version (void)
{
int major, minor;
const char *version = (const char *) glGetString (GL_VERSION);
const char *dot = version == NULL ? NULL : strchr (version, '.');
const char *major_start = dot;
 
/* Sanity check */
if (dot == NULL || dot == version || *(dot + 1) == '\0') {
major = 0;
minor = 0;
} else {
/* Find the start of the major version in the string */
while (major_start > version && *major_start != ' ')
--major_start;
major = strtol (major_start, NULL, 10);
minor = strtol (dot + 1, NULL, 10);
}
 
return CAIRO_GL_VERSION_ENCODE (major, minor);
}
 
cairo_gl_flavor_t
_cairo_gl_get_flavor (void)
{
const char *version = (const char *) glGetString (GL_VERSION);
cairo_gl_flavor_t flavor;
 
if (version == NULL)
flavor = CAIRO_GL_FLAVOR_NONE;
else if (strstr (version, "OpenGL ES") != NULL)
flavor = CAIRO_GL_FLAVOR_ES;
else
flavor = CAIRO_GL_FLAVOR_DESKTOP;
 
return flavor;
}
 
cairo_bool_t
_cairo_gl_has_extension (const char *ext)
{
const char *extensions = (const char *) glGetString (GL_EXTENSIONS);
size_t len = strlen (ext);
const char *ext_ptr = extensions;
 
if (unlikely (ext_ptr == NULL))
return 0;
 
while ((ext_ptr = strstr (ext_ptr, ext)) != NULL) {
if (ext_ptr[len] == ' ' || ext_ptr[len] == '\0')
break;
ext_ptr += len;
}
 
return (ext_ptr != NULL);
}
/programs/develop/libraries/cairo/src/cairo-gl-msaa-compositor.c
0,0 → 1,944
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
* Copyright © 2011 Samsung Electronics
*
* 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):
* Henry Song <hsong@sisa.samsung.com>
* Martin Robinson <mrobinson@igalia.com>
*/
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-gl-private.h"
#include "cairo-path-private.h"
#include "cairo-traps-private.h"
 
static cairo_bool_t
can_use_msaa_compositor (cairo_gl_surface_t *surface,
cairo_antialias_t antialias);
 
static void
query_surface_capabilities (cairo_gl_surface_t *surface);
 
struct _tristrip_composite_info {
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
};
 
static cairo_int_status_t
_draw_trap (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_trapezoid_t *trap)
{
cairo_point_t quad[4];
 
quad[0].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1,
&trap->left.p2,
trap->top);
quad[0].y = trap->top;
 
quad[1].x = _cairo_edge_compute_intersection_x_for_y (&trap->left.p1,
&trap->left.p2,
trap->bottom);
quad[1].y = trap->bottom;
 
quad[2].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1,
&trap->right.p2,
trap->bottom);
quad[2].y = trap->bottom;
 
quad[3].x = _cairo_edge_compute_intersection_x_for_y (&trap->right.p1,
&trap->right.p2,
trap->top);
quad[3].y = trap->top;
return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad);
}
 
static cairo_int_status_t
_draw_traps (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_traps_t *traps)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
int i;
 
for (i = 0; i < traps->num_traps; i++) {
cairo_trapezoid_t *trap = traps->traps + i;
if (unlikely ((status = _draw_trap (ctx, setup, trap))))
return status;
}
 
return status;
}
 
static cairo_int_status_t
_draw_int_rect (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_rectangle_int_t *rect)
{
cairo_box_t box;
cairo_point_t quad[4];
 
_cairo_box_from_rectangle (&box, rect);
quad[0].x = box.p1.x;
quad[0].y = box.p1.y;
quad[1].x = box.p1.x;
quad[1].y = box.p2.y;
quad[2].x = box.p2.x;
quad[2].y = box.p2.y;
quad[3].x = box.p2.x;
quad[3].y = box.p1.y;
 
return _cairo_gl_composite_emit_quad_as_tristrip (ctx, setup, quad);
}
 
static cairo_int_status_t
_draw_triangle_fan (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_point_t *midpt,
const cairo_point_t *points,
int npoints)
{
int i;
 
/* Our strategy here is to not even try to build a triangle fan, but to
draw each triangle as if it was an unconnected member of a triangle strip. */
for (i = 1; i < npoints; i++) {
cairo_int_status_t status;
cairo_point_t triangle[3];
 
triangle[0] = *midpt;
triangle[1] = points[i - 1];
triangle[2] = points[i];
 
status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_clip_to_traps (cairo_clip_t *clip,
cairo_traps_t *traps)
{
cairo_int_status_t status;
cairo_polygon_t polygon;
cairo_antialias_t antialias;
cairo_fill_rule_t fill_rule;
 
_cairo_traps_init (traps);
 
if (clip->num_boxes == 1 && clip->path == NULL) {
cairo_boxes_t boxes;
_cairo_boxes_init_for_array (&boxes, clip->boxes, clip->num_boxes);
return _cairo_traps_init_boxes (traps, &boxes);
}
 
status = _cairo_clip_get_polygon (clip, &polygon, &fill_rule, &antialias);
if (unlikely (status))
return status;
 
/* We ignore the antialias mode of the clip here, since the user requested
* unantialiased rendering of their path and we expect that this stencil
* based rendering of the clip to be a reasonable approximation to
* the intersection between that clip and the path.
*
* In other words, what the user expects when they try to perform
* a geometric intersection between an unantialiased polygon and an
* antialiased polygon is open to interpretation. And we choose the fast
* option.
*/
 
_cairo_traps_init (traps);
status = _cairo_bentley_ottmann_tessellate_polygon (traps,
&polygon,
fill_rule);
_cairo_polygon_fini (&polygon);
 
return status;
}
 
cairo_int_status_t
_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip)
{
cairo_int_status_t status;
cairo_traps_t traps;
 
status = _clip_to_traps (clip, &traps);
if (unlikely (status))
return status;
status = _draw_traps (ctx, setup, &traps);
 
_cairo_traps_fini (&traps);
return status;
}
 
static cairo_bool_t
_should_use_unbounded_surface (cairo_composite_rectangles_t *composite)
{
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_rectangle_int_t *source = &composite->source;
 
if (composite->is_bounded)
return FALSE;
 
/* This isn't just an optimization. It also detects when painting is used
to paint back the unbounded surface, preventing infinite recursion. */
return ! (source->x <= 0 && source->y <= 0 &&
source->height + source->y >= dst->height &&
source->width + source->x >= dst->width);
}
 
static cairo_surface_t*
_prepare_unbounded_surface (cairo_gl_surface_t *dst)
{
 
cairo_surface_t* surface = cairo_gl_surface_create (dst->base.device,
dst->base.content,
dst->width,
dst->height);
if (surface == NULL)
return NULL;
if (unlikely (surface->status)) {
cairo_surface_destroy (surface);
return NULL;
}
return surface;
}
 
static cairo_int_status_t
_paint_back_unbounded_surface (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
cairo_surface_t *surface)
{
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_int_status_t status;
 
cairo_pattern_t *pattern = cairo_pattern_create_for_surface (surface);
if (unlikely (pattern->status)) {
status = pattern->status;
goto finish;
}
 
status = _cairo_compositor_paint (compositor, &dst->base,
composite->op, pattern,
composite->clip);
 
finish:
cairo_pattern_destroy (pattern);
cairo_surface_destroy (surface);
return status;
}
 
static cairo_bool_t
can_use_msaa_compositor (cairo_gl_surface_t *surface,
cairo_antialias_t antialias)
{
query_surface_capabilities (surface);
if (! surface->supports_stencil)
return FALSE;
 
/* Multisampling OpenGL ES surfaces only maintain one multisampling
framebuffer and thus must use the spans compositor to do non-antialiased
rendering. */
if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES
&& surface->supports_msaa
&& antialias == CAIRO_ANTIALIAS_NONE)
return FALSE;
 
/* The MSAA compositor has a single-sample mode, so we can
support non-antialiased rendering. */
if (antialias == CAIRO_ANTIALIAS_NONE)
return TRUE;
 
if (antialias == CAIRO_ANTIALIAS_FAST || antialias == CAIRO_ANTIALIAS_DEFAULT)
return surface->supports_msaa;
return FALSE;
}
 
static void
_cairo_gl_msaa_compositor_set_clip (cairo_composite_rectangles_t *composite,
cairo_gl_composite_t *setup)
{
if (_cairo_composite_rectangles_can_reduce_clip (composite, composite->clip))
return;
_cairo_gl_composite_set_clip (setup, composite->clip);
}
 
/* Masking with the SOURCE operator requires two passes. In the first
* pass we use the mask as the source to get:
* result = (1 - ma) * dst
* In the second pass we use the add operator to achieve:
* result = (src * ma) + dst
* Combined this produces:
* result = (src * ma) + (1 - ma) * dst
*/
static cairo_int_status_t
_cairo_gl_msaa_compositor_mask_source_operator (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
{
cairo_gl_composite_t setup;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_gl_context_t *ctx = NULL;
cairo_int_status_t status;
 
cairo_clip_t *clip = composite->clip;
cairo_traps_t traps;
 
/* If we have a non-rectangular clip, we can avoid using the stencil buffer
* for clipping and just draw the clip polygon. */
if (clip) {
status = _clip_to_traps (clip, &traps);
if (unlikely (status)) {
_cairo_traps_fini (&traps);
return status;
}
}
 
status = _cairo_gl_composite_init (&setup,
CAIRO_OPERATOR_DEST_OUT,
dst,
FALSE /* assume_component_alpha */);
if (unlikely (status))
return status;
status = _cairo_gl_composite_set_source (&setup,
&composite->mask_pattern.base,
&composite->mask_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto finish;
_cairo_gl_composite_set_multisample (&setup);
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto finish;
 
if (! clip)
status = _draw_int_rect (ctx, &setup, &composite->bounded);
else
status = _draw_traps (ctx, &setup, &traps);
if (unlikely (status))
goto finish;
 
/* Now draw the second pass. */
status = _cairo_gl_composite_set_operator (&setup, CAIRO_OPERATOR_ADD,
FALSE /* assume_component_alpha */);
if (unlikely (status))
goto finish;
status = _cairo_gl_composite_set_source (&setup,
&composite->source_pattern.base,
&composite->source_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto finish;
status = _cairo_gl_composite_set_mask (&setup,
&composite->mask_pattern.base,
&composite->source_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto finish;
status = _cairo_gl_set_operands_and_operator (&setup, ctx);
if (unlikely (status))
goto finish;
 
if (! clip)
status = _draw_int_rect (ctx, &setup, &composite->bounded);
else
status = _draw_traps (ctx, &setup, &traps);
 
finish:
_cairo_gl_composite_fini (&setup);
if (ctx)
status = _cairo_gl_context_release (ctx, status);
if (clip)
_cairo_traps_fini (&traps);
 
return status;
}
 
static cairo_int_status_t
_cairo_gl_msaa_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
{
cairo_gl_composite_t setup;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_gl_context_t *ctx = NULL;
cairo_int_status_t status;
cairo_operator_t op = composite->op;
cairo_clip_t *clip = composite->clip;
 
if (! can_use_msaa_compositor (dst, CAIRO_ANTIALIAS_DEFAULT))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (composite->op == CAIRO_OPERATOR_CLEAR &&
composite->original_mask_pattern != NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* GL compositing operators cannot properly represent a mask operation
using the SOURCE compositing operator in one pass. This only matters if
there actually is a mask (there isn't in a paint operation) and if the
mask isn't totally opaque. */
if (op == CAIRO_OPERATOR_SOURCE &&
composite->original_mask_pattern != NULL &&
! _cairo_pattern_is_opaque (&composite->mask_pattern.base,
&composite->mask_sample_area)) {
 
if (! _cairo_pattern_is_opaque (&composite->source_pattern.base,
&composite->source_sample_area)) {
return _cairo_gl_msaa_compositor_mask_source_operator (compositor, composite);
}
 
/* If the source is opaque the operation reduces to OVER. */
op = CAIRO_OPERATOR_OVER;
}
 
if (_should_use_unbounded_surface (composite)) {
cairo_surface_t* surface = _prepare_unbounded_surface (dst);
 
if (unlikely (surface == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* This may be a paint operation. */
if (composite->original_mask_pattern == NULL) {
status = _cairo_compositor_paint (compositor, surface,
CAIRO_OPERATOR_SOURCE,
&composite->source_pattern.base,
NULL);
} else {
status = _cairo_compositor_mask (compositor, surface,
CAIRO_OPERATOR_SOURCE,
&composite->source_pattern.base,
&composite->mask_pattern.base,
NULL);
}
 
if (unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
 
return _paint_back_unbounded_surface (compositor, composite, surface);
}
 
status = _cairo_gl_composite_init (&setup,
op,
dst,
FALSE /* assume_component_alpha */);
if (unlikely (status))
return status;
 
status = _cairo_gl_composite_set_source (&setup,
&composite->source_pattern.base,
&composite->source_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto finish;
 
if (composite->original_mask_pattern != NULL) {
status = _cairo_gl_composite_set_mask (&setup,
&composite->mask_pattern.base,
&composite->mask_sample_area,
&composite->bounded,
FALSE);
}
if (unlikely (status))
goto finish;
 
/* We always use multisampling here, because we do not yet have the smarts
to calculate when the clip or the source requires it. */
_cairo_gl_composite_set_multisample (&setup);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto finish;
 
if (! clip)
status = _draw_int_rect (ctx, &setup, &composite->bounded);
else
status = _cairo_gl_msaa_compositor_draw_clip (ctx, &setup, clip);
 
finish:
_cairo_gl_composite_fini (&setup);
 
if (ctx)
status = _cairo_gl_context_release (ctx, status);
 
return status;
}
 
static cairo_int_status_t
_cairo_gl_msaa_compositor_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
{
return _cairo_gl_msaa_compositor_mask (compositor, composite);
}
 
static cairo_status_t
_stroke_shaper_add_triangle (void *closure,
const cairo_point_t triangle[3])
{
struct _tristrip_composite_info *info = closure;
return _cairo_gl_composite_emit_triangle_as_tristrip (info->ctx,
&info->setup,
triangle);
}
 
static cairo_status_t
_stroke_shaper_add_triangle_fan (void *closure,
const cairo_point_t *midpoint,
const cairo_point_t *points,
int npoints)
{
struct _tristrip_composite_info *info = closure;
return _draw_triangle_fan (info->ctx, &info->setup,
midpoint, points, npoints);
}
 
static cairo_status_t
_stroke_shaper_add_quad (void *closure,
const cairo_point_t quad[4])
{
struct _tristrip_composite_info *info = closure;
return _cairo_gl_composite_emit_quad_as_tristrip (info->ctx, &info->setup,
quad);
}
 
static cairo_int_status_t
_prevent_overlapping_strokes (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_composite_rectangles_t *composite,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm)
{
cairo_rectangle_int_t stroke_extents;
 
if (! _cairo_gl_ensure_stencil (ctx, setup->dst))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (_cairo_pattern_is_opaque (&composite->source_pattern.base,
&composite->source_sample_area))
return CAIRO_INT_STATUS_SUCCESS;
 
if (glIsEnabled (GL_STENCIL_TEST) == FALSE) {
cairo_bool_t scissor_was_enabled;
 
/* In case we have pending operations we have to flush before
adding the stencil buffer. */
_cairo_gl_composite_flush (ctx);
 
/* Enable the stencil buffer, even if we are not using it for clipping,
so we can use it below to prevent overlapping shapes. We initialize
it all to one here which represents infinite clip. */
glDepthMask (GL_TRUE);
glEnable (GL_STENCIL_TEST);
 
/* We scissor here so that we don't have to clear the entire stencil
* buffer. If the scissor test is already enabled, it was enabled
* for clipping. In that case, instead of calculating an intersection,
* we just reuse it, and risk clearing too much. */
scissor_was_enabled = glIsEnabled (GL_SCISSOR_TEST);
if (! scissor_was_enabled) {
_cairo_path_fixed_approximate_stroke_extents (path, style, ctm,
&stroke_extents);
_cairo_gl_scissor_to_rectangle (setup->dst, &stroke_extents);
}
glClearStencil (1);
glClear (GL_STENCIL_BUFFER_BIT);
if (! scissor_was_enabled)
glDisable (GL_SCISSOR_TEST);
 
glStencilFunc (GL_EQUAL, 1, 1);
}
 
/* This means that once we draw to a particular pixel nothing else can
be drawn there until the stencil buffer is reset or the stencil test
is disabled. */
glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO);
 
_cairo_clip_destroy (setup->dst->clip_on_stencil_buffer);
setup->dst->clip_on_stencil_buffer = NULL;
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static void
query_surface_capabilities (cairo_gl_surface_t *surface)
{
GLint samples, stencil_bits;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
/* Texture surfaces are create in such a way that they always
have stencil and multisample bits if possible, so we don't
need to query their capabilities lazily. */
if (_cairo_gl_surface_is_texture (surface))
return;
if (surface->stencil_and_msaa_caps_initialized)
return;
 
surface->stencil_and_msaa_caps_initialized = TRUE;
surface->supports_stencil = FALSE;
surface->supports_msaa = FALSE;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return;
 
_cairo_gl_context_set_destination (ctx, surface, FALSE);
 
glGetIntegerv(GL_SAMPLES, &samples);
glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);
surface->supports_stencil = stencil_bits > 0;
surface->supports_msaa = samples > 1;
 
status = _cairo_gl_context_release (ctx, status);
}
 
static cairo_int_status_t
_cairo_gl_msaa_compositor_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_int_status_t status;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
struct _tristrip_composite_info info;
 
if (! can_use_msaa_compositor (dst, antialias))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (composite->is_bounded == FALSE) {
cairo_surface_t* surface = _prepare_unbounded_surface (dst);
 
if (unlikely (surface == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_stroke (compositor, surface,
CAIRO_OPERATOR_SOURCE,
&composite->source_pattern.base,
path, style, ctm, ctm_inverse,
tolerance, antialias, NULL);
if (unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
 
return _paint_back_unbounded_surface (compositor, composite, surface);
}
 
status = _cairo_gl_composite_init (&info.setup,
composite->op,
dst,
FALSE /* assume_component_alpha */);
if (unlikely (status))
return status;
 
info.ctx = NULL;
 
status = _cairo_gl_composite_set_source (&info.setup,
&composite->source_pattern.base,
&composite->source_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto finish;
 
_cairo_gl_msaa_compositor_set_clip (composite, &info.setup);
if (antialias != CAIRO_ANTIALIAS_NONE)
_cairo_gl_composite_set_multisample (&info.setup);
 
status = _cairo_gl_composite_begin (&info.setup, &info.ctx);
if (unlikely (status))
goto finish;
 
status = _prevent_overlapping_strokes (info.ctx, &info.setup,
composite, path, style, ctm);
if (unlikely (status))
goto finish;
 
status = _cairo_path_fixed_stroke_to_shaper ((cairo_path_fixed_t *) path,
style,
ctm,
ctm_inverse,
tolerance,
_stroke_shaper_add_triangle,
_stroke_shaper_add_triangle_fan,
_stroke_shaper_add_quad,
&info);
if (unlikely (status))
goto finish;
 
finish:
_cairo_gl_composite_fini (&info.setup);
 
if (info.ctx)
status = _cairo_gl_context_release (info.ctx, status);
 
return status;
}
 
static cairo_int_status_t
_draw_simple_quad_path (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_path_fixed_t *path)
{
cairo_point_t triangle[3];
cairo_int_status_t status;
const cairo_point_t *points;
 
points = cairo_path_head (path)->points;
triangle[0] = points[0];
triangle[1] = points[1];
triangle[2] = points[2];
status = _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
if (status)
return status;
 
triangle[0] = points[2];
triangle[1] = points[3];
triangle[2] = points[0];
return _cairo_gl_composite_emit_triangle_as_tristrip (ctx, setup, triangle);
}
 
static cairo_int_status_t
_cairo_gl_msaa_compositor_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_gl_composite_t setup;
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
cairo_gl_context_t *ctx = NULL;
cairo_int_status_t status;
cairo_traps_t traps;
cairo_bool_t draw_path_with_traps;
 
if (! can_use_msaa_compositor (dst, antialias))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (composite->is_bounded == FALSE) {
cairo_surface_t* surface = _prepare_unbounded_surface (dst);
 
if (unlikely (surface == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
 
status = _cairo_compositor_fill (compositor, surface,
CAIRO_OPERATOR_SOURCE,
&composite->source_pattern.base,
path, fill_rule, tolerance,
antialias, NULL);
 
if (unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
 
return _paint_back_unbounded_surface (compositor, composite, surface);
}
 
draw_path_with_traps = ! _cairo_path_fixed_is_simple_quad (path);
 
if (draw_path_with_traps) {
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
if (unlikely (status))
goto cleanup_traps;
}
 
status = _cairo_gl_composite_init (&setup,
composite->op,
dst,
FALSE /* assume_component_alpha */);
if (unlikely (status))
goto cleanup_traps;
 
status = _cairo_gl_composite_set_source (&setup,
&composite->source_pattern.base,
&composite->source_sample_area,
&composite->bounded,
FALSE);
if (unlikely (status))
goto cleanup_setup;
 
_cairo_gl_msaa_compositor_set_clip (composite, &setup);
if (antialias != CAIRO_ANTIALIAS_NONE)
_cairo_gl_composite_set_multisample (&setup);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto cleanup_setup;
 
if (! draw_path_with_traps)
status = _draw_simple_quad_path (ctx, &setup, path);
else
status = _draw_traps (ctx, &setup, &traps);
if (unlikely (status))
goto cleanup_setup;
 
cleanup_setup:
_cairo_gl_composite_fini (&setup);
 
if (ctx)
status = _cairo_gl_context_release (ctx, status);
 
cleanup_traps:
if (draw_path_with_traps)
_cairo_traps_fini (&traps);
 
return status;
}
 
static cairo_int_status_t
_cairo_gl_msaa_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_int_status_t status;
cairo_surface_t *src = NULL;
int src_x, src_y;
cairo_composite_glyphs_info_t info;
 
cairo_gl_surface_t *dst = (cairo_gl_surface_t *) composite->surface;
 
query_surface_capabilities (dst);
if (! dst->supports_stencil)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (composite->op == CAIRO_OPERATOR_CLEAR)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (composite->is_bounded == FALSE) {
cairo_surface_t* surface = _prepare_unbounded_surface (dst);
 
if (unlikely (surface == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_glyphs (compositor, surface,
CAIRO_OPERATOR_SOURCE,
&composite->source_pattern.base,
glyphs, num_glyphs,
scaled_font, composite->clip);
 
if (unlikely (status)) {
cairo_surface_destroy (surface);
return status;
}
 
return _paint_back_unbounded_surface (compositor, composite, surface);
}
 
src = _cairo_gl_pattern_to_source (&dst->base,
&composite->source_pattern.base,
FALSE,
&composite->bounded,
&composite->source_sample_area,
&src_x, &src_y);
if (unlikely (src->status)) {
status = src->status;
goto finish;
}
 
status = _cairo_gl_check_composite_glyphs (composite,
scaled_font, glyphs,
&num_glyphs);
if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
goto finish;
 
info.font = scaled_font;
info.glyphs = glyphs;
info.num_glyphs = num_glyphs;
info.use_mask = overlap || ! composite->is_bounded ||
composite->op == CAIRO_OPERATOR_SOURCE;
info.extents = composite->bounded;
 
_cairo_scaled_font_freeze_cache (scaled_font);
status = _cairo_gl_composite_glyphs_with_clip (dst, composite->op,
src, src_x, src_y,
0, 0, &info,
composite->clip);
 
_cairo_scaled_font_thaw_cache (scaled_font);
 
finish:
if (src)
cairo_surface_destroy (src);
 
return status;
}
 
static void
_cairo_gl_msaa_compositor_init (cairo_compositor_t *compositor,
const cairo_compositor_t *delegate)
{
compositor->delegate = delegate;
 
compositor->paint = _cairo_gl_msaa_compositor_paint;
compositor->mask = _cairo_gl_msaa_compositor_mask;
compositor->fill = _cairo_gl_msaa_compositor_fill;
compositor->stroke = _cairo_gl_msaa_compositor_stroke;
compositor->glyphs = _cairo_gl_msaa_compositor_glyphs;
}
 
const cairo_compositor_t *
_cairo_gl_msaa_compositor_get (void)
{
static cairo_compositor_t compositor;
if (compositor.delegate == NULL)
_cairo_gl_msaa_compositor_init (&compositor,
_cairo_gl_span_compositor_get ());
 
return &compositor;
}
/programs/develop/libraries/cairo/src/cairo-gl-operand.c
0,0 → 1,788
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-subsurface-inline.h"
 
static cairo_int_status_t
_cairo_gl_create_gradient_texture (cairo_gl_surface_t *dst,
const cairo_gradient_pattern_t *pattern,
cairo_gl_gradient_t **gradient)
{
cairo_gl_context_t *ctx;
cairo_status_t status;
 
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
 
status = _cairo_gl_gradient_create (ctx, pattern->n_stops, pattern->stops, gradient);
 
return _cairo_gl_context_release (ctx, status);
}
 
static cairo_status_t
_cairo_gl_subsurface_clone_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *_src,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
cairo_surface_pattern_t local_pattern;
cairo_surface_subsurface_t *sub;
cairo_gl_surface_t *surface;
cairo_gl_context_t *ctx;
cairo_surface_attributes_t *attributes;
cairo_status_t status;
 
sub = (cairo_surface_subsurface_t *) src->surface;
 
if (sub->snapshot &&
sub->snapshot->type == CAIRO_SURFACE_TYPE_GL &&
sub->snapshot->device == dst->base.device)
{
surface = (cairo_gl_surface_t *)
cairo_surface_reference (sub->snapshot);
}
else
{
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
 
/* XXX Trim surface to the sample area within the subsurface? */
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch (ctx,
sub->target->content,
sub->extents.width,
sub->extents.height);
if (surface->base.status)
return _cairo_gl_context_release (ctx, surface->base.status);
 
_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 (&surface->base,
CAIRO_OPERATOR_SOURCE,
&local_pattern.base,
NULL);
_cairo_pattern_fini (&local_pattern.base);
 
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return status;
}
 
_cairo_surface_subsurface_set_snapshot (&sub->base, &surface->base);
}
 
status = _cairo_gl_surface_resolve_multisampling (surface);
if (unlikely (status))
return status;
 
attributes = &operand->texture.attributes;
 
operand->type = CAIRO_GL_OPERAND_TEXTURE;
operand->texture.surface = surface;
operand->texture.owns_surface = surface;
operand->texture.tex = surface->tex;
 
if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) {
attributes->matrix = src->base.matrix;
} else {
cairo_matrix_t m;
 
cairo_matrix_init_scale (&m,
1.0 / surface->width,
1.0 / surface->height);
cairo_matrix_multiply (&attributes->matrix, &src->base.matrix, &m);
}
 
attributes->extend = src->base.extend;
attributes->filter = src->base.filter;
attributes->has_component_alpha = src->base.has_component_alpha;
 
operand->texture.texgen = use_texgen;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_subsurface_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *_src,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
cairo_surface_subsurface_t *sub;
cairo_gl_surface_t *surface;
cairo_surface_attributes_t *attributes;
cairo_int_status_t status;
 
sub = (cairo_surface_subsurface_t *) src->surface;
 
if (sample->x < 0 || sample->y < 0 ||
sample->x + sample->width > sub->extents.width ||
sample->y + sample->height > sub->extents.height)
{
return _cairo_gl_subsurface_clone_operand_init (operand, _src,
dst, sample, extents,
use_texgen);
}
 
surface = (cairo_gl_surface_t *) sub->target;
if (surface->base.device && surface->base.device != dst->base.device)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_gl_surface_is_texture (surface))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_gl_surface_resolve_multisampling (surface);
if (unlikely (status))
return status;
 
/* Translate the matrix from
* (unnormalized src -> unnormalized src) to
* (unnormalized dst -> unnormalized src)
*/
_cairo_gl_operand_copy(operand, &surface->operand);
 
attributes = &operand->texture.attributes;
attributes->matrix = src->base.matrix;
attributes->matrix.x0 += sub->extents.x;
attributes->matrix.y0 += sub->extents.y;
cairo_matrix_multiply (&attributes->matrix,
&attributes->matrix,
&surface->operand.texture.attributes.matrix);
 
attributes->extend = src->base.extend;
attributes->filter = src->base.filter;
attributes->has_component_alpha = src->base.has_component_alpha;
 
operand->texture.texgen = use_texgen;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_surface_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *_src,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
const cairo_surface_pattern_t *src = (cairo_surface_pattern_t *)_src;
cairo_gl_surface_t *surface;
cairo_surface_attributes_t *attributes;
cairo_int_status_t status;
 
surface = (cairo_gl_surface_t *) src->surface;
if (surface->base.type != CAIRO_SURFACE_TYPE_GL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (surface->base.backend->type != CAIRO_SURFACE_TYPE_GL) {
if (_cairo_surface_is_subsurface (&surface->base))
return _cairo_gl_subsurface_operand_init (operand, _src, dst,
sample, extents,
use_texgen);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (surface->base.device && surface->base.device != dst->base.device)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (surface->base.device && ! _cairo_gl_surface_is_texture (surface))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_gl_surface_resolve_multisampling (surface);
if (unlikely (status))
return status;
 
_cairo_gl_operand_copy(operand, &surface->operand);
 
attributes = &operand->texture.attributes;
cairo_matrix_multiply (&attributes->matrix,
&src->base.matrix,
&attributes->matrix);
 
attributes->extend = src->base.extend;
attributes->filter = src->base.filter;
attributes->has_component_alpha = src->base.has_component_alpha;
 
operand->texture.texgen = use_texgen;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand,
const cairo_pattern_t *_src,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_status_t status;
cairo_gl_surface_t *surface;
cairo_gl_context_t *ctx;
cairo_image_surface_t *image;
cairo_bool_t src_is_gl_surface = FALSE;
cairo_rectangle_int_t map_extents;
 
if (_src->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_t* src_surface = ((cairo_surface_pattern_t *) _src)->surface;
src_is_gl_surface = src_surface->type == CAIRO_SURFACE_TYPE_GL;
}
 
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
 
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch (ctx,
CAIRO_CONTENT_COLOR_ALPHA,
extents->width, extents->height);
map_extents = *extents;
map_extents.x = map_extents.y = 0;
image = _cairo_surface_map_to_image (&surface->base, &map_extents);
 
/* If the pattern is a GL surface, it belongs to some other GL context,
so we need to release this device while we paint it to the image. */
if (src_is_gl_surface) {
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status))
goto fail;
}
 
status = _cairo_surface_offset_paint (&image->base, extents->x, extents->y,
CAIRO_OPERATOR_SOURCE, _src, NULL);
 
if (src_is_gl_surface) {
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
goto fail;
}
 
status = _cairo_surface_unmap_image (&surface->base, image);
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status))
goto fail;
 
*operand = surface->operand;
operand->texture.owns_surface = surface;
operand->texture.attributes.matrix.x0 -= extents->x * operand->texture.attributes.matrix.xx;
operand->texture.attributes.matrix.y0 -= extents->y * operand->texture.attributes.matrix.yy;
return CAIRO_STATUS_SUCCESS;
 
fail:
cairo_surface_destroy (&surface->base);
return status;
}
 
void
_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
const cairo_color_t *color)
{
operand->type = CAIRO_GL_OPERAND_CONSTANT;
operand->constant.color[0] = color->red * color->alpha;
operand->constant.color[1] = color->green * color->alpha;
operand->constant.color[2] = color->blue * color->alpha;
operand->constant.color[3] = color->alpha;
}
 
void
_cairo_gl_operand_translate (cairo_gl_operand_t *operand,
double tx, double ty)
{
switch (operand->type) {
case CAIRO_GL_OPERAND_TEXTURE:
operand->texture.attributes.matrix.x0 -= tx * operand->texture.attributes.matrix.xx;
operand->texture.attributes.matrix.y0 -= ty * operand->texture.attributes.matrix.yy;
break;
 
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
operand->gradient.m.x0 -= tx * operand->gradient.m.xx;
operand->gradient.m.y0 -= ty * operand->gradient.m.yy;
break;
 
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
case CAIRO_GL_OPERAND_COUNT:
default:
break;
}
}
 
static cairo_status_t
_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *pattern,
cairo_gl_surface_t *dst,
cairo_bool_t use_texgen)
{
const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *)pattern;
cairo_status_t status;
 
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
if (! _cairo_gl_device_has_glsl (dst->base.device))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_gl_create_gradient_texture (dst,
gradient,
&operand->gradient.gradient);
if (unlikely (status))
return status;
 
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
double x0, y0, dx, dy, sf, offset;
 
dx = linear->pd2.x - linear->pd1.x;
dy = linear->pd2.y - linear->pd1.y;
sf = 1.0 / (dx * dx + dy * dy);
dx *= sf;
dy *= sf;
 
x0 = linear->pd1.x;
y0 = linear->pd1.y;
offset = dx * x0 + dy * y0;
 
operand->type = CAIRO_GL_OPERAND_LINEAR_GRADIENT;
 
cairo_matrix_init (&operand->gradient.m, dx, 0, dy, 1, -offset, 0);
if (! _cairo_matrix_is_identity (&pattern->matrix)) {
cairo_matrix_multiply (&operand->gradient.m,
&pattern->matrix,
&operand->gradient.m);
}
} else {
cairo_matrix_t m;
cairo_circle_double_t circles[2];
double x0, y0, r0, dx, dy, dr;
 
/*
* Some fragment shader implementations use half-floats to
* represent numbers, so the maximum number they can represent
* is about 2^14. Some intermediate computations used in the
* radial gradient shaders can produce results of up to 2*k^4.
* Setting k=8 makes the maximum result about 8192 (assuming
* that the extreme circles are not much smaller than the
* destination image).
*/
_cairo_gradient_pattern_fit_to_range (gradient, 8.,
&operand->gradient.m, circles);
 
x0 = circles[0].center.x;
y0 = circles[0].center.y;
r0 = circles[0].radius;
dx = circles[1].center.x - x0;
dy = circles[1].center.y - y0;
dr = circles[1].radius - r0;
 
operand->gradient.a = dx * dx + dy * dy - dr * dr;
operand->gradient.radius_0 = r0;
operand->gradient.circle_d.center.x = dx;
operand->gradient.circle_d.center.y = dy;
operand->gradient.circle_d.radius = dr;
 
if (operand->gradient.a == 0)
operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0;
else if (pattern->extend == CAIRO_EXTEND_NONE)
operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE;
else
operand->type = CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT;
 
cairo_matrix_init_translate (&m, -x0, -y0);
cairo_matrix_multiply (&operand->gradient.m,
&operand->gradient.m,
&m);
}
 
operand->gradient.extend = pattern->extend;
operand->gradient.texgen = use_texgen;
 
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
const cairo_gl_operand_t *src)
{
*dst = *src;
switch (dst->type) {
case CAIRO_GL_OPERAND_CONSTANT:
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
_cairo_gl_gradient_reference (dst->gradient.gradient);
break;
case CAIRO_GL_OPERAND_TEXTURE:
cairo_surface_reference (&dst->texture.owns_surface->base);
break;
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
break;
}
}
 
void
_cairo_gl_operand_destroy (cairo_gl_operand_t *operand)
{
switch (operand->type) {
case CAIRO_GL_OPERAND_CONSTANT:
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
_cairo_gl_gradient_destroy (operand->gradient.gradient);
break;
case CAIRO_GL_OPERAND_TEXTURE:
cairo_surface_destroy (&operand->texture.owns_surface->base);
break;
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
break;
}
 
operand->type = CAIRO_GL_OPERAND_NONE;
}
 
cairo_int_status_t
_cairo_gl_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *pattern,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen)
{
cairo_int_status_t status;
 
TRACE ((stderr, "%s: type=%d\n", __FUNCTION__, pattern->type));
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
_cairo_gl_solid_operand_init (operand,
&((cairo_solid_pattern_t *) pattern)->color);
return CAIRO_STATUS_SUCCESS;
case CAIRO_PATTERN_TYPE_SURFACE:
status = _cairo_gl_surface_operand_init (operand, pattern, dst,
sample, extents, use_texgen);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
break;
 
return status;
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
status = _cairo_gl_gradient_operand_init (operand, pattern, dst,
use_texgen);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
break;
 
return status;
 
default:
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
break;
}
 
return _cairo_gl_pattern_texture_setup (operand, pattern, dst, extents);
}
 
cairo_filter_t
_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand)
{
cairo_filter_t filter;
 
switch ((int) operand->type) {
case CAIRO_GL_OPERAND_TEXTURE:
filter = operand->texture.attributes.filter;
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
filter = CAIRO_FILTER_BILINEAR;
break;
default:
filter = CAIRO_FILTER_DEFAULT;
break;
}
 
return filter;
}
 
GLint
_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand)
{
cairo_filter_t filter = _cairo_gl_operand_get_filter (operand);
 
return filter != CAIRO_FILTER_FAST && filter != CAIRO_FILTER_NEAREST ?
GL_LINEAR :
GL_NEAREST;
}
 
cairo_extend_t
_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand)
{
cairo_extend_t extend;
 
switch ((int) operand->type) {
case CAIRO_GL_OPERAND_TEXTURE:
extend = operand->texture.attributes.extend;
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
extend = operand->gradient.extend;
break;
default:
extend = CAIRO_EXTEND_NONE;
break;
}
 
return extend;
}
 
 
void
_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
cairo_gl_operand_t *operand,
cairo_gl_tex_t tex_unit)
{
const cairo_matrix_t *texgen = NULL;
 
switch (operand->type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
return;
 
case CAIRO_GL_OPERAND_CONSTANT:
_cairo_gl_shader_bind_vec4 (ctx,
ctx->current_shader->constant_location[tex_unit],
operand->constant.color[0],
operand->constant.color[1],
operand->constant.color[2],
operand->constant.color[3]);
return;
 
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
_cairo_gl_shader_bind_float (ctx,
ctx->current_shader->a_location[tex_unit],
operand->gradient.a);
/* fall through */
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
_cairo_gl_shader_bind_vec3 (ctx,
ctx->current_shader->circle_d_location[tex_unit],
operand->gradient.circle_d.center.x,
operand->gradient.circle_d.center.y,
operand->gradient.circle_d.radius);
_cairo_gl_shader_bind_float (ctx,
ctx->current_shader->radius_0_location[tex_unit],
operand->gradient.radius_0);
/* fall through */
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_TEXTURE:
/*
* For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used
* with CAIRO_EXTEND_NONE). When bilinear filtering is enabled,
* these shaders need the texture dimensions for their calculations.
*/
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
_cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE &&
_cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR)
{
float width, height;
if (operand->type == CAIRO_GL_OPERAND_TEXTURE) {
width = operand->texture.surface->width;
height = operand->texture.surface->height;
}
else {
width = operand->gradient.gradient->cache_entry.size,
height = 1;
}
_cairo_gl_shader_bind_vec2 (ctx,
ctx->current_shader->texdims_location[tex_unit],
width, height);
}
break;
}
 
if (operand->type == CAIRO_GL_OPERAND_TEXTURE) {
if (operand->texture.texgen)
texgen = &operand->texture.attributes.matrix;
} else {
if (operand->gradient.texgen)
texgen = &operand->gradient.m;
}
if (texgen) {
_cairo_gl_shader_bind_matrix(ctx,
ctx->current_shader->texgen_location[tex_unit],
texgen);
}
}
 
 
cairo_bool_t
_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
cairo_gl_operand_t *source,
unsigned int vertex_offset)
{
if (dest->type != source->type)
return TRUE;
if (dest->vertex_offset != vertex_offset)
return TRUE;
 
switch (source->type) {
case CAIRO_GL_OPERAND_NONE:
return FALSE;
case CAIRO_GL_OPERAND_CONSTANT:
return dest->constant.color[0] != source->constant.color[0] ||
dest->constant.color[1] != source->constant.color[1] ||
dest->constant.color[2] != source->constant.color[2] ||
dest->constant.color[3] != source->constant.color[3];
case CAIRO_GL_OPERAND_TEXTURE:
return dest->texture.surface != source->texture.surface ||
dest->texture.attributes.extend != source->texture.attributes.extend ||
dest->texture.attributes.filter != source->texture.attributes.filter ||
dest->texture.attributes.has_component_alpha != source->texture.attributes.has_component_alpha;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
/* XXX: improve this */
return TRUE;
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
break;
}
return TRUE;
}
 
unsigned int
_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand)
{
switch (operand->type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
return 0;
case CAIRO_GL_OPERAND_TEXTURE:
return operand->texture.texgen ? 0 : 2 * sizeof (GLfloat);
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
return operand->gradient.texgen ? 0 : 2 * sizeof (GLfloat);
}
}
 
void
_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
GLfloat ** vb,
GLfloat x,
GLfloat y)
{
switch (operand->type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
if (! operand->gradient.texgen) {
double s = x;
double t = y;
 
cairo_matrix_transform_point (&operand->gradient.m, &s, &t);
 
*(*vb)++ = s;
*(*vb)++ = t;
}
break;
case CAIRO_GL_OPERAND_TEXTURE:
if (! operand->texture.texgen) {
cairo_surface_attributes_t *src_attributes = &operand->texture.attributes;
double s = x;
double t = y;
 
cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
*(*vb)++ = s;
*(*vb)++ = t;
}
break;
}
}
/programs/develop/libraries/cairo/src/cairo-gl-private.h
3,6 → 3,7
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2011 Linaro Limited
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
37,29 → 38,38
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
* T. Zachary Laine <whatwasthataddress@gmail.com>
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#ifndef CAIRO_GL_PRIVATE_H
#define CAIRO_GL_PRIVATE_H
 
#define GL_GLEXT_PROTOTYPES
 
#include "cairoint.h"
 
#include "cairo-gl.h"
#include "cairo-gl-gradient-private.h"
 
#include "cairo-device-private.h"
#include "cairo-error-private.h"
#include "cairo-rtree-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-spans-compositor-private.h"
#include "cairo-array-private.h"
 
#include <assert.h>
 
#include <GL/glew.h>
 
#include "cairo-gl.h"
 
#if CAIRO_HAS_GL_SURFACE
#include <GL/gl.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/glext.h>
#elif CAIRO_HAS_GLESV2_SURFACE
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
 
#include "cairo-gl-ext-def-private.h"
 
#define DEBUG_GL 0
 
#if DEBUG_GL && __GNUC__
73,27 → 83,113
#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
#endif
 
#define CAIRO_GL_VERSION_ENCODE(major, minor) ( \
((major) * 256) \
+ ((minor) * 1))
 
/* maximal number of shaders we keep in the cache.
* Random number that is hopefully big enough to not cause many cache evictions. */
#define CAIRO_GL_MAX_SHADERS_PER_CONTEXT 64
 
/* VBO size that we allocate, smaller size means we gotta flush more often */
#define CAIRO_GL_VBO_SIZE 16384
/* VBO size that we allocate, smaller size means we gotta flush more often,
* but larger means hogging more memory and can cause trouble for drivers
* (especially on embedded devices). */
#define CAIRO_GL_VBO_SIZE (16*1024)
 
typedef struct _cairo_gl_surface {
typedef struct _cairo_gl_surface cairo_gl_surface_t;
 
/* GL flavor */
typedef enum cairo_gl_flavor {
CAIRO_GL_FLAVOR_NONE = 0,
CAIRO_GL_FLAVOR_DESKTOP = 1,
CAIRO_GL_FLAVOR_ES = 2
} cairo_gl_flavor_t;
 
/* Indices for vertex attributes used by BindAttribLocation etc */
enum {
CAIRO_GL_VERTEX_ATTRIB_INDEX = 0,
CAIRO_GL_COLOR_ATTRIB_INDEX = 1,
CAIRO_GL_TEXCOORD0_ATTRIB_INDEX = 2,
CAIRO_GL_TEXCOORD1_ATTRIB_INDEX = CAIRO_GL_TEXCOORD0_ATTRIB_INDEX + 1
};
 
typedef enum cairo_gl_operand_type {
CAIRO_GL_OPERAND_NONE,
CAIRO_GL_OPERAND_CONSTANT,
CAIRO_GL_OPERAND_TEXTURE,
CAIRO_GL_OPERAND_LINEAR_GRADIENT,
CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0,
CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE,
CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT,
 
CAIRO_GL_OPERAND_COUNT
} cairo_gl_operand_type_t;
 
/* This union structure describes a potential source or mask operand to the
* compositing equation.
*/
typedef struct cairo_gl_operand {
cairo_gl_operand_type_t type;
union {
struct {
GLuint tex;
cairo_gl_surface_t *surface;
cairo_gl_surface_t *owns_surface;
cairo_surface_attributes_t attributes;
int texgen;
} texture;
struct {
GLfloat color[4];
} constant;
struct {
cairo_gl_gradient_t *gradient;
cairo_matrix_t m;
cairo_circle_double_t circle_d;
double radius_0, a;
cairo_extend_t extend;
int texgen;
} gradient;
};
unsigned int vertex_offset;
} cairo_gl_operand_t;
 
typedef struct cairo_gl_source {
cairo_surface_t base;
cairo_gl_operand_t operand;
} cairo_gl_source_t;
 
struct _cairo_gl_surface {
cairo_surface_t base;
cairo_gl_operand_t operand;
 
int width, height;
 
GLuint tex; /* GL texture object containing our data. */
GLuint fb; /* GL framebuffer object wrapping our data. */
GLuint depth; /* GL framebuffer object holding depth */
GLuint depth_stencil; /* GL renderbuffer object for holding stencil buffer clip. */
 
#if CAIRO_HAS_GL_SURFACE
GLuint msaa_rb; /* The ARB MSAA path uses a renderbuffer. */
GLuint msaa_fb;
#endif
GLuint msaa_depth_stencil;
 
cairo_bool_t stencil_and_msaa_caps_initialized;
cairo_bool_t supports_stencil; /* Stencil support for for non-texture surfaces. */
cairo_bool_t supports_msaa;
cairo_bool_t msaa_active; /* Whether the multisampling
framebuffer is active or not. */
cairo_clip_t *clip_on_stencil_buffer;
 
int owns_tex;
} cairo_gl_surface_t;
cairo_bool_t needs_update;
 
cairo_region_t *clip_region;
};
 
typedef struct cairo_gl_glyph_cache {
cairo_rtree_t rtree;
cairo_surface_pattern_t pattern;
cairo_gl_surface_t *surface;
} cairo_gl_glyph_cache_t;
 
typedef enum cairo_gl_tex {
102,22 → 198,16
CAIRO_GL_TEX_TEMP = 2
} cairo_gl_tex_t;
 
typedef enum cairo_gl_operand_type {
CAIRO_GL_OPERAND_NONE,
CAIRO_GL_OPERAND_CONSTANT,
CAIRO_GL_OPERAND_TEXTURE,
CAIRO_GL_OPERAND_LINEAR_GRADIENT,
CAIRO_GL_OPERAND_RADIAL_GRADIENT,
CAIRO_GL_OPERAND_SPANS,
 
CAIRO_GL_OPERAND_COUNT
} cairo_gl_operand_type_t;
 
typedef struct cairo_gl_shader_impl cairo_gl_shader_impl_t;
 
typedef struct cairo_gl_shader {
GLuint fragment_shader;
GLuint program;
GLint mvp_location;
GLint constant_location[2];
GLint a_location[2];
GLint circle_d_location[2];
GLint radius_0_location[2];
GLint texdims_location[2];
GLint texgen_location[2];
} cairo_gl_shader_t;
 
typedef enum cairo_gl_shader_in {
131,60 → 221,130
typedef enum cairo_gl_var_type {
CAIRO_GL_VAR_NONE,
CAIRO_GL_VAR_TEXCOORDS,
CAIRO_GL_VAR_COVERAGE
CAIRO_GL_VAR_TEXGEN,
} cairo_gl_var_type_t;
 
#define cairo_gl_var_type_hash(src,mask,dest) ((mask) << 2 | (src << 1) | (dest))
#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_COVERAGE << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS)
typedef enum cairo_gl_primitive_type {
CAIRO_GL_PRIMITIVE_TYPE_TRIANGLES,
CAIRO_GL_PRIMITIVE_TYPE_TRISTRIPS
} cairo_gl_primitive_type_t;
 
/* This union structure describes a potential source or mask operand to the
* compositing equation.
*/
typedef struct cairo_gl_operand {
cairo_gl_operand_type_t type;
union {
struct {
GLuint tex;
cairo_gl_surface_t *surface;
cairo_surface_attributes_t attributes;
} texture;
struct {
GLfloat color[4];
} constant;
struct {
cairo_gl_gradient_t *gradient;
cairo_matrix_t m;
float segment_x;
float segment_y;
cairo_extend_t extend;
} linear;
struct {
cairo_gl_gradient_t *gradient;
cairo_matrix_t m;
float circle_1_x;
float circle_1_y;
float radius_0;
float radius_1;
cairo_extend_t extend;
} radial;
};
unsigned int vertex_offset;
} cairo_gl_operand_t;
typedef void (*cairo_gl_emit_rect_t) (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2);
 
typedef void (*cairo_gl_emit_span_t) (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
uint8_t alpha);
 
typedef void (*cairo_gl_emit_glyph_t) (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2,
GLfloat glyph_x1, GLfloat glyph_y1,
GLfloat glyph_x2, GLfloat glyph_y2);
 
#define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 5) | ((mask) << 3 | (src << 1) | (dest))
#define CAIRO_GL_VAR_TYPE_MAX (1 << 6)
 
typedef void (*cairo_gl_generic_func_t)(void);
typedef cairo_gl_generic_func_t (*cairo_gl_get_proc_addr_func_t)(const char *procname);
 
typedef struct _cairo_gl_dispatch {
/* Buffers */
void (*GenBuffers) (GLsizei n, GLuint *buffers);
void (*BindBuffer) (GLenum target, GLuint buffer);
void (*BufferData) (GLenum target, GLsizeiptr size,
const GLvoid* data, GLenum usage);
GLvoid *(*MapBuffer) (GLenum target, GLenum access);
GLboolean (*UnmapBuffer) (GLenum target);
 
/* Shaders */
GLuint (*CreateShader) (GLenum type);
void (*ShaderSource) (GLuint shader, GLsizei count,
const GLchar** string, const GLint* length);
void (*CompileShader) (GLuint shader);
void (*GetShaderiv) (GLuint shader, GLenum pname, GLint *params);
void (*GetShaderInfoLog) (GLuint shader, GLsizei bufSize,
GLsizei *length, GLchar *infoLog);
void (*DeleteShader) (GLuint shader);
 
/* Programs */
GLuint (*CreateProgram) (void);
void (*AttachShader) (GLuint program, GLuint shader);
void (*DeleteProgram) (GLuint program);
void (*LinkProgram) (GLuint program);
void (*UseProgram) (GLuint program);
void (*GetProgramiv) (GLuint program, GLenum pname, GLint *params);
void (*GetProgramInfoLog) (GLuint program, GLsizei bufSize,
GLsizei *length, GLchar *infoLog);
 
/* Uniforms */
GLint (*GetUniformLocation) (GLuint program, const GLchar* name);
void (*Uniform1f) (GLint location, GLfloat x);
void (*Uniform2f) (GLint location, GLfloat x, GLfloat y);
void (*Uniform3f) (GLint location, GLfloat x, GLfloat y, GLfloat z);
void (*Uniform4f) (GLint location, GLfloat x, GLfloat y, GLfloat z,
GLfloat w);
void (*UniformMatrix3fv) (GLint location, GLsizei count,
GLboolean transpose, const GLfloat *value);
void (*UniformMatrix4fv) (GLint location, GLsizei count,
GLboolean transpose, const GLfloat *value);
void (*Uniform1i) (GLint location, GLint x);
 
/* Attributes */
void (*BindAttribLocation) (GLuint program, GLuint index,
const GLchar *name);
void (*VertexAttribPointer) (GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride,
const GLvoid *pointer);
void (*EnableVertexAttribArray) (GLuint index);
void (*DisableVertexAttribArray) (GLuint index);
 
/* Framebuffer objects */
void (*GenFramebuffers) (GLsizei n, GLuint* framebuffers);
void (*BindFramebuffer) (GLenum target, GLuint framebuffer);
void (*FramebufferTexture2D) (GLenum target, GLenum attachment,
GLenum textarget, GLuint texture,
GLint level);
GLenum (*CheckFramebufferStatus) (GLenum target);
void (*DeleteFramebuffers) (GLsizei n, const GLuint* framebuffers);
void (*GenRenderbuffers) (GLsizei n, GLuint *renderbuffers);
void (*BindRenderbuffer) (GLenum target, GLuint renderbuffer);
void (*RenderbufferStorage) (GLenum target, GLenum internal_format,
GLsizei width, GLsizei height);
void (*FramebufferRenderbuffer) (GLenum target, GLenum attachment,
GLenum renderbuffer_ttarget, GLuint renderbuffer);
void (*DeleteRenderbuffers) (GLsizei n, GLuint *renderbuffers);
void (*BlitFramebuffer) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter);
void (*RenderbufferStorageMultisample) (GLenum target, GLsizei samples,
GLenum internalformat,
GLsizei width, GLsizei height);
void (*FramebufferTexture2DMultisample) (GLenum target, GLenum attachment,
GLenum textarget, GLuint texture,
GLint level, GLsizei samples);
} cairo_gl_dispatch_t;
 
struct _cairo_gl_context {
cairo_device_t base;
 
GLuint dummy_tex;
const cairo_compositor_t *compositor;
 
GLuint texture_load_pbo;
GLuint vbo;
GLint max_framebuffer_size;
GLint max_texture_size;
GLint max_textures;
GLenum tex_target;
 
const cairo_gl_shader_impl_t *shader_impl;
GLint num_samples;
cairo_bool_t supports_msaa;
char *vb;
 
GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX + 1];
cairo_bool_t has_shader_support;
 
GLuint vertex_shaders[CAIRO_GL_VAR_TYPE_MAX];
cairo_gl_shader_t fill_rectangles_shader;
cairo_cache_t shaders;
 
199,12 → 359,27
cairo_gl_shader_t *current_shader;
 
cairo_gl_operand_t operands[2];
cairo_bool_t spans;
 
char *vb;
unsigned int vb_offset;
unsigned int vertex_size;
cairo_region_t *clip_region;
cairo_clip_t *clip;
 
cairo_gl_primitive_type_t primitive_type;
cairo_array_t tristrip_indices;
 
cairo_bool_t has_mesa_pack_invert;
cairo_gl_dispatch_t dispatch;
GLfloat modelviewprojection_matrix[16];
cairo_gl_flavor_t gl_flavor;
cairo_bool_t has_map_buffer;
cairo_bool_t has_packed_depth_stencil;
cairo_bool_t has_npot_repeat;
cairo_bool_t can_read_bgra;
 
cairo_bool_t thread_aware;
 
void (*acquire) (void *ctx);
void (*release) (void *ctx);
 
220,9 → 395,17
 
cairo_gl_operand_t src;
cairo_gl_operand_t mask;
cairo_bool_t spans;
 
cairo_clip_t *clip;
cairo_bool_t multisample;
} cairo_gl_composite_t;
 
cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
typedef struct _cairo_gl_font {
cairo_scaled_font_private_t base;
cairo_device_t *device;
cairo_list_t link;
} cairo_gl_font_t;
 
static cairo_always_inline GLenum
_cairo_gl_get_error (void)
261,18 → 444,22
cairo_image_surface_t *src,
int src_x, int src_y,
int width, int height,
int dst_x, int dst_y);
int dst_x, int dst_y,
cairo_bool_t force_flush);
 
cairo_private cairo_int_status_t
_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface);
 
static cairo_always_inline cairo_bool_t
_cairo_gl_device_has_glsl (cairo_device_t *device)
{
return ((cairo_gl_context_t *) device)->shader_impl != NULL;
return ((cairo_gl_context_t *) device)->has_shader_support;
}
 
static cairo_always_inline cairo_bool_t
_cairo_gl_device_requires_power_of_two_textures (cairo_device_t *device)
{
return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE_EXT;
return ((cairo_gl_context_t *) device)->tex_target == GL_TEXTURE_RECTANGLE;
}
 
static cairo_always_inline cairo_status_t cairo_warn
312,9 → 499,30
}
 
cairo_private void
_cairo_gl_context_set_destination (cairo_gl_context_t *ctx, cairo_gl_surface_t *surface);
_cairo_gl_context_set_destination (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface,
cairo_bool_t multisampling);
 
cairo_private void
_cairo_gl_context_bind_framebuffer (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface,
cairo_bool_t multisampling);
 
cairo_private cairo_gl_emit_rect_t
_cairo_gl_context_choose_emit_rect (cairo_gl_context_t *ctx);
 
cairo_private void
_cairo_gl_context_emit_rect (cairo_gl_context_t *ctx,
GLfloat x1, GLfloat y1,
GLfloat x2, GLfloat y2);
 
cairo_private cairo_gl_emit_span_t
_cairo_gl_context_choose_emit_span (cairo_gl_context_t *ctx);
 
cairo_private cairo_gl_emit_glyph_t
_cairo_gl_context_choose_emit_glyph (cairo_gl_context_t *ctx);
 
cairo_private void
_cairo_gl_context_activate (cairo_gl_context_t *ctx,
cairo_gl_tex_t tex_unit);
 
321,80 → 529,97
cairo_private cairo_bool_t
_cairo_gl_operator_is_supported (cairo_operator_t op);
 
cairo_private cairo_bool_t
_cairo_gl_ensure_stencil (cairo_gl_context_t *ctx,
cairo_gl_surface_t *surface);
 
cairo_private cairo_status_t
_cairo_gl_composite_init (cairo_gl_composite_t *setup,
cairo_operator_t op,
cairo_gl_surface_t *dst,
cairo_bool_t has_component_alpha,
const cairo_rectangle_int_t *rect);
cairo_bool_t has_component_alpha);
 
cairo_private void
_cairo_gl_composite_fini (cairo_gl_composite_t *setup);
 
cairo_private cairo_status_t
_cairo_gl_composite_set_operator (cairo_gl_composite_t *setup,
cairo_operator_t op,
cairo_bool_t assume_component_alpha);
 
cairo_private void
_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup,
cairo_region_t *clip_region);
 
cairo_private void
_cairo_gl_composite_set_clip(cairo_gl_composite_t *setup,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_set_source (cairo_gl_composite_t *setup,
const cairo_pattern_t *pattern,
int src_x, int src_y,
int dst_x, int dst_y,
int width, int height);
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen);
 
cairo_private void
_cairo_gl_composite_set_solid_source (cairo_gl_composite_t *setup,
const cairo_color_t *color);
 
cairo_private void
_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup,
const cairo_gl_operand_t *source);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_set_mask (cairo_gl_composite_t *setup,
const cairo_pattern_t *pattern,
int src_x, int src_y,
int dst_x, int dst_y,
int width, int height);
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen);
 
cairo_private void
_cairo_gl_composite_set_mask_spans (cairo_gl_composite_t *setup);
_cairo_gl_composite_set_mask_operand (cairo_gl_composite_t *setup,
const cairo_gl_operand_t *mask);
 
cairo_private void
_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup);
 
cairo_private void
_cairo_gl_composite_set_multisample (cairo_gl_composite_t *setup);
 
cairo_private cairo_status_t
_cairo_gl_composite_begin (cairo_gl_composite_t *setup,
cairo_gl_context_t **ctx);
 
cairo_private void
_cairo_gl_composite_emit_rect (cairo_gl_context_t *ctx,
GLfloat x1,
GLfloat y1,
GLfloat x2,
GLfloat y2,
uint8_t alpha);
cairo_private cairo_status_t
_cairo_gl_set_operands_and_operator (cairo_gl_composite_t *setup,
cairo_gl_context_t *ctx);
 
cairo_private void
_cairo_gl_composite_emit_glyph (cairo_gl_context_t *ctx,
GLfloat x1,
GLfloat y1,
GLfloat x2,
GLfloat y2,
GLfloat glyph_x1,
GLfloat glyph_y1,
GLfloat glyph_x2,
GLfloat glyph_y2);
 
cairo_private void
_cairo_gl_composite_flush (cairo_gl_context_t *ctx);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_emit_quad_as_tristrip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_point_t quad[4]);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_emit_triangle_as_tristrip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
const cairo_point_t triangle[3]);
 
cairo_private void
_cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx,
cairo_gl_tex_t tex_unit);
 
cairo_private cairo_bool_t
_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha);
GLenum *type, cairo_bool_t *has_alpha,
cairo_bool_t *needs_swap);
 
cairo_private void
_cairo_gl_surface_scaled_font_fini ( cairo_scaled_font_t *scaled_font);
 
cairo_private void
_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font);
 
cairo_private void
_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache);
 
cairo_private void
408,18 → 633,9
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
const cairo_clip_t *clip,
int *remaining_glyphs);
 
static inline int
_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
{
if (surface->fb)
return y;
else
return (surface->height - 1) - y;
}
 
cairo_private cairo_status_t
_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx);
 
429,29 → 645,30
static cairo_always_inline cairo_bool_t
_cairo_gl_context_is_flushed (cairo_gl_context_t *ctx)
{
return ctx->vb == NULL;
return ctx->vb_offset == 0;
}
 
cairo_private cairo_status_t
_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
cairo_gl_operand_type_t source,
cairo_gl_operand_type_t mask,
cairo_gl_operand_t *source,
cairo_gl_operand_t *mask,
cairo_bool_t use_coverage,
cairo_gl_shader_in_t in,
cairo_gl_shader_t **shader);
 
cairo_private void
_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
const char *name,
GLint location,
float value);
 
cairo_private void
_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
const char *name,
GLint location,
float value0, float value1);
 
cairo_private void
_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
const char *name,
GLint location,
float value0,
float value1,
float value2);
458,19 → 675,19
 
cairo_private void
_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
const char *name,
GLint location,
float value0, float value1,
float value2, float value3);
 
cairo_private void
_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
const char *name,
cairo_matrix_t* m);
GLint location,
const cairo_matrix_t* m);
 
cairo_private void
_cairo_gl_shader_bind_texture (cairo_gl_context_t *ctx,
const char *name,
GLuint tex_unit);
_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
GLint location,
GLfloat* gl_m);
 
cairo_private void
_cairo_gl_set_shader (cairo_gl_context_t *ctx,
479,6 → 696,152
cairo_private void
_cairo_gl_shader_fini (cairo_gl_context_t *ctx, cairo_gl_shader_t *shader);
 
cairo_private int
_cairo_gl_get_version (void);
 
cairo_private cairo_gl_flavor_t
_cairo_gl_get_flavor (void);
 
cairo_private cairo_bool_t
_cairo_gl_has_extension (const char *ext);
 
cairo_private cairo_status_t
_cairo_gl_dispatch_init(cairo_gl_dispatch_t *dispatch,
cairo_gl_get_proc_addr_func_t get_proc_addr);
 
cairo_private cairo_int_status_t
_cairo_gl_operand_init (cairo_gl_operand_t *operand,
const cairo_pattern_t *pattern,
cairo_gl_surface_t *dst,
const cairo_rectangle_int_t *sample,
const cairo_rectangle_int_t *extents,
cairo_bool_t use_texgen);
 
cairo_private void
_cairo_gl_solid_operand_init (cairo_gl_operand_t *operand,
const cairo_color_t *color);
 
cairo_private cairo_filter_t
_cairo_gl_operand_get_filter (cairo_gl_operand_t *operand);
 
cairo_private GLint
_cairo_gl_operand_get_gl_filter (cairo_gl_operand_t *operand);
 
cairo_private cairo_extend_t
_cairo_gl_operand_get_extend (cairo_gl_operand_t *operand);
 
cairo_private unsigned int
_cairo_gl_operand_get_vertex_size (const cairo_gl_operand_t *operand);
 
cairo_private cairo_bool_t
_cairo_gl_operand_needs_setup (cairo_gl_operand_t *dest,
cairo_gl_operand_t *source,
unsigned int vertex_offset);
 
cairo_private void
_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx,
cairo_gl_operand_t *operand,
cairo_gl_tex_t tex_unit);
 
cairo_private void
_cairo_gl_operand_emit (cairo_gl_operand_t *operand,
GLfloat ** vb,
GLfloat x,
GLfloat y);
 
cairo_private void
_cairo_gl_operand_copy (cairo_gl_operand_t *dst,
const cairo_gl_operand_t *src);
 
cairo_private void
_cairo_gl_operand_translate (cairo_gl_operand_t *operand,
double tx, double ty);
 
cairo_private void
_cairo_gl_operand_destroy (cairo_gl_operand_t *operand);
 
cairo_private const cairo_compositor_t *
_cairo_gl_msaa_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_gl_span_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_gl_traps_compositor_get (void);
 
cairo_private cairo_int_status_t
_cairo_gl_check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_glyphs (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info);
 
cairo_private cairo_int_status_t
_cairo_gl_composite_glyphs_with_clip (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info,
cairo_clip_t *clip);
 
cairo_private cairo_surface_t *
_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height);
 
cairo_private cairo_surface_t *
_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height);
 
cairo_private cairo_surface_t *
_cairo_gl_pattern_to_source (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_private cairo_int_status_t
_cairo_gl_msaa_compositor_draw_clip (cairo_gl_context_t *ctx,
cairo_gl_composite_t *setup,
cairo_clip_t *clip);
 
cairo_private cairo_surface_t *
_cairo_gl_white_source (void);
 
cairo_private void
_cairo_gl_scissor_to_rectangle (cairo_gl_surface_t *surface,
const cairo_rectangle_int_t *r);
 
static inline cairo_gl_operand_t *
source_to_operand (cairo_surface_t *surface)
{
cairo_gl_source_t *source = (cairo_gl_source_t *)surface;
return source ? &source->operand : NULL;
}
 
static inline void
_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache)
{
_cairo_rtree_unpin (&cache->rtree);
}
 
 
slim_hidden_proto (cairo_gl_surface_create);
slim_hidden_proto (cairo_gl_surface_create_for_texture);
 
/programs/develop/libraries/cairo/src/cairo-gl-shaders.c
0,0 → 1,1093
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 T. Zachary Laine
* Copyright © 2010 Eric Anholt
* Copyright © 2010 Red Hat, Inc
* Copyright © 2010 Linaro Limited
*
* 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 T. Zachary Laine.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Eric Anholt <eric@anholt.net>
* T. Zachary Laine <whatwasthataddress@gmail.com>
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
*/
 
#include "cairoint.h"
#include "cairo-gl-private.h"
#include "cairo-error-private.h"
#include "cairo-output-stream-private.h"
 
static cairo_status_t
_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
cairo_gl_var_type_t src,
cairo_gl_var_type_t mask,
cairo_bool_t use_coverage,
const char *fragment_text);
 
typedef struct _cairo_shader_cache_entry {
cairo_cache_entry_t base;
 
unsigned vertex;
 
cairo_gl_operand_type_t src;
cairo_gl_operand_type_t mask;
cairo_gl_operand_type_t dest;
cairo_bool_t use_coverage;
 
cairo_gl_shader_in_t in;
GLint src_gl_filter;
cairo_bool_t src_border_fade;
cairo_extend_t src_extend;
GLint mask_gl_filter;
cairo_bool_t mask_border_fade;
cairo_extend_t mask_extend;
 
cairo_gl_context_t *ctx; /* XXX: needed to destroy the program */
cairo_gl_shader_t shader;
} cairo_shader_cache_entry_t;
 
static cairo_bool_t
_cairo_gl_shader_cache_equal_desktop (const void *key_a, const void *key_b)
{
const cairo_shader_cache_entry_t *a = key_a;
const cairo_shader_cache_entry_t *b = key_b;
cairo_bool_t both_have_npot_repeat =
a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
 
return (a->vertex == b->vertex &&
a->src == b->src &&
a->mask == b->mask &&
a->dest == b->dest &&
a->use_coverage == b->use_coverage &&
a->in == b->in &&
(both_have_npot_repeat || a->src_extend == b->src_extend) &&
(both_have_npot_repeat || a->mask_extend == b->mask_extend));
}
 
/*
* For GLES2 we use more complicated shaders to implement missing GL
* features. In this case we need more parameters to uniquely identify
* a shader (vs _cairo_gl_shader_cache_equal_desktop()).
*/
static cairo_bool_t
_cairo_gl_shader_cache_equal_gles2 (const void *key_a, const void *key_b)
{
const cairo_shader_cache_entry_t *a = key_a;
const cairo_shader_cache_entry_t *b = key_b;
cairo_bool_t both_have_npot_repeat =
a->ctx->has_npot_repeat && b->ctx->has_npot_repeat;
 
return (a->vertex == b->vertex &&
a->src == b->src &&
a->mask == b->mask &&
a->dest == b->dest &&
a->use_coverage == b->use_coverage &&
a->in == b->in &&
a->src_gl_filter == b->src_gl_filter &&
a->src_border_fade == b->src_border_fade &&
(both_have_npot_repeat || a->src_extend == b->src_extend) &&
a->mask_gl_filter == b->mask_gl_filter &&
a->mask_border_fade == b->mask_border_fade &&
(both_have_npot_repeat || a->mask_extend == b->mask_extend));
}
 
static unsigned long
_cairo_gl_shader_cache_hash (const cairo_shader_cache_entry_t *entry)
{
return ((entry->src << 24) | (entry->mask << 16) | (entry->dest << 8) | (entry->in << 1) | entry->use_coverage) ^ entry->vertex;
}
 
static void
_cairo_gl_shader_cache_destroy (void *data)
{
cairo_shader_cache_entry_t *entry = data;
 
_cairo_gl_shader_fini (entry->ctx, &entry->shader);
if (entry->ctx->current_shader == &entry->shader)
entry->ctx->current_shader = NULL;
free (entry);
}
 
static void
_cairo_gl_shader_init (cairo_gl_shader_t *shader)
{
shader->fragment_shader = 0;
shader->program = 0;
}
 
cairo_status_t
_cairo_gl_context_init_shaders (cairo_gl_context_t *ctx)
{
static const char *fill_fs_source =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"uniform vec4 color;\n"
"void main()\n"
"{\n"
" gl_FragColor = color;\n"
"}\n";
cairo_status_t status;
 
if (_cairo_gl_get_version () >= CAIRO_GL_VERSION_ENCODE (2, 0) ||
(_cairo_gl_has_extension ("GL_ARB_shader_objects") &&
_cairo_gl_has_extension ("GL_ARB_fragment_shader") &&
_cairo_gl_has_extension ("GL_ARB_vertex_shader"))) {
ctx->has_shader_support = TRUE;
} else {
ctx->has_shader_support = FALSE;
fprintf (stderr, "Error: The cairo gl backend requires shader support!\n");
return CAIRO_STATUS_DEVICE_ERROR;
}
 
memset (ctx->vertex_shaders, 0, sizeof (ctx->vertex_shaders));
 
status = _cairo_cache_init (&ctx->shaders,
ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP ?
_cairo_gl_shader_cache_equal_desktop :
_cairo_gl_shader_cache_equal_gles2,
NULL,
_cairo_gl_shader_cache_destroy,
CAIRO_GL_MAX_SHADERS_PER_CONTEXT);
if (unlikely (status))
return status;
 
_cairo_gl_shader_init (&ctx->fill_rectangles_shader);
status = _cairo_gl_shader_compile_and_link (ctx,
&ctx->fill_rectangles_shader,
CAIRO_GL_VAR_NONE,
CAIRO_GL_VAR_NONE,
FALSE,
fill_fs_source);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_gl_context_fini_shaders (cairo_gl_context_t *ctx)
{
int i;
 
for (i = 0; i <= CAIRO_GL_VAR_TYPE_MAX; i++) {
if (ctx->vertex_shaders[i])
ctx->dispatch.DeleteShader (ctx->vertex_shaders[i]);
}
 
_cairo_cache_fini (&ctx->shaders);
}
 
void
_cairo_gl_shader_fini (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader)
{
if (shader->fragment_shader)
ctx->dispatch.DeleteShader (shader->fragment_shader);
 
if (shader->program)
ctx->dispatch.DeleteProgram (shader->program);
}
 
static const char *operand_names[] = { "source", "mask", "dest" };
 
static cairo_gl_var_type_t
cairo_gl_operand_get_var_type (cairo_gl_operand_t *operand)
{
switch (operand->type) {
default:
case CAIRO_GL_OPERAND_COUNT:
ASSERT_NOT_REACHED;
case CAIRO_GL_OPERAND_NONE:
case CAIRO_GL_OPERAND_CONSTANT:
return CAIRO_GL_VAR_NONE;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
return operand->gradient.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
case CAIRO_GL_OPERAND_TEXTURE:
return operand->texture.texgen ? CAIRO_GL_VAR_TEXGEN : CAIRO_GL_VAR_TEXCOORDS;
}
}
 
static void
cairo_gl_shader_emit_variable (cairo_output_stream_t *stream,
cairo_gl_var_type_t type,
cairo_gl_tex_t name)
{
switch (type) {
default:
ASSERT_NOT_REACHED;
case CAIRO_GL_VAR_NONE:
break;
case CAIRO_GL_VAR_TEXCOORDS:
_cairo_output_stream_printf (stream,
"attribute vec4 MultiTexCoord%d;\n"
"varying vec2 %s_texcoords;\n",
name,
operand_names[name]);
break;
case CAIRO_GL_VAR_TEXGEN:
_cairo_output_stream_printf (stream,
"uniform mat3 %s_texgen;\n"
"varying vec2 %s_texcoords;\n",
operand_names[name],
operand_names[name]);
break;
}
}
 
static void
cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream,
cairo_gl_var_type_t type,
cairo_gl_tex_t name)
{
switch (type) {
default:
ASSERT_NOT_REACHED;
case CAIRO_GL_VAR_NONE:
break;
case CAIRO_GL_VAR_TEXCOORDS:
_cairo_output_stream_printf (stream,
" %s_texcoords = MultiTexCoord%d.xy;\n",
operand_names[name], name);
break;
 
case CAIRO_GL_VAR_TEXGEN:
_cairo_output_stream_printf (stream,
" %s_texcoords = (%s_texgen * Vertex.xyw).xy;\n",
operand_names[name], operand_names[name]);
break;
}
}
 
static void
cairo_gl_shader_dcl_coverage (cairo_output_stream_t *stream)
{
_cairo_output_stream_printf (stream, "varying float coverage;\n");
}
 
static void
cairo_gl_shader_def_coverage (cairo_output_stream_t *stream)
{
_cairo_output_stream_printf (stream, " coverage = Color.a;\n");
}
 
static cairo_status_t
cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src,
cairo_gl_var_type_t mask,
cairo_bool_t use_coverage,
cairo_gl_var_type_t dest,
char **out)
{
cairo_output_stream_t *stream = _cairo_memory_stream_create ();
unsigned char *source;
unsigned long length;
cairo_status_t status;
 
cairo_gl_shader_emit_variable (stream, src, CAIRO_GL_TEX_SOURCE);
cairo_gl_shader_emit_variable (stream, mask, CAIRO_GL_TEX_MASK);
if (use_coverage)
cairo_gl_shader_dcl_coverage (stream);
 
_cairo_output_stream_printf (stream,
"attribute vec4 Vertex;\n"
"attribute vec4 Color;\n"
"uniform mat4 ModelViewProjectionMatrix;\n"
"void main()\n"
"{\n"
" gl_Position = ModelViewProjectionMatrix * Vertex;\n");
 
cairo_gl_shader_emit_vertex (stream, src, CAIRO_GL_TEX_SOURCE);
cairo_gl_shader_emit_vertex (stream, mask, CAIRO_GL_TEX_MASK);
if (use_coverage)
cairo_gl_shader_def_coverage (stream);
 
_cairo_output_stream_write (stream,
"}\n\0", 3);
 
status = _cairo_memory_stream_destroy (stream, &source, &length);
if (unlikely (status))
return status;
 
*out = (char *) source;
return CAIRO_STATUS_SUCCESS;
}
 
/*
* Returns whether an operand needs a special border fade fragment shader
* to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2.
*/
static cairo_bool_t
_cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand)
{
cairo_extend_t extend =_cairo_gl_operand_get_extend (operand);
 
return extend == CAIRO_EXTEND_NONE &&
(operand->type == CAIRO_GL_OPERAND_TEXTURE ||
operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT ||
operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE ||
operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0);
}
 
static void
cairo_gl_shader_emit_color (cairo_output_stream_t *stream,
cairo_gl_context_t *ctx,
cairo_gl_operand_t *op,
cairo_gl_tex_t name)
{
const char *namestr = operand_names[name];
const char *rectstr = (ctx->tex_target == GL_TEXTURE_RECTANGLE ? "Rect" : "");
 
switch (op->type) {
case CAIRO_GL_OPERAND_COUNT:
default:
ASSERT_NOT_REACHED;
break;
case CAIRO_GL_OPERAND_NONE:
_cairo_output_stream_printf (stream,
"vec4 get_%s()\n"
"{\n"
" return vec4 (0, 0, 0, 1);\n"
"}\n",
namestr);
break;
case CAIRO_GL_OPERAND_CONSTANT:
_cairo_output_stream_printf (stream,
"uniform vec4 %s_constant;\n"
"vec4 get_%s()\n"
"{\n"
" return %s_constant;\n"
"}\n",
namestr, namestr, namestr);
break;
case CAIRO_GL_OPERAND_TEXTURE:
_cairo_output_stream_printf (stream,
"uniform sampler2D%s %s_sampler;\n"
"uniform vec2 %s_texdims;\n"
"varying vec2 %s_texcoords;\n"
"vec4 get_%s()\n"
"{\n",
rectstr, namestr, namestr, namestr, namestr);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
_cairo_gl_shader_needs_border_fade (op))
{
_cairo_output_stream_printf (stream,
" vec2 border_fade = %s_border_fade (%s_texcoords, %s_texdims);\n"
" vec4 texel = texture2D%s (%s_sampler, %s_texcoords);\n"
" return texel * border_fade.x * border_fade.y;\n"
"}\n",
namestr, namestr, namestr, rectstr, namestr, namestr);
}
else
{
_cairo_output_stream_printf (stream,
" return texture2D%s (%s_sampler, %s_wrap (%s_texcoords));\n"
"}\n",
rectstr, namestr, namestr, namestr);
}
break;
case CAIRO_GL_OPERAND_LINEAR_GRADIENT:
_cairo_output_stream_printf (stream,
"varying vec2 %s_texcoords;\n"
"uniform vec2 %s_texdims;\n"
"uniform sampler2D%s %s_sampler;\n"
"\n"
"vec4 get_%s()\n"
"{\n",
namestr, namestr, rectstr, namestr, namestr);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
_cairo_gl_shader_needs_border_fade (op))
{
_cairo_output_stream_printf (stream,
" float border_fade = %s_border_fade (%s_texcoords.x, %s_texdims.x);\n"
" vec4 texel = texture2D%s (%s_sampler, vec2 (%s_texcoords.x, 0.5));\n"
" return texel * border_fade;\n"
"}\n",
namestr, namestr, namestr, rectstr, namestr, namestr);
}
else
{
_cairo_output_stream_printf (stream,
" return texture2D%s (%s_sampler, %s_wrap (vec2 (%s_texcoords.x, 0.5)));\n"
"}\n",
rectstr, namestr, namestr, namestr);
}
break;
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0:
_cairo_output_stream_printf (stream,
"varying vec2 %s_texcoords;\n"
"uniform vec2 %s_texdims;\n"
"uniform sampler2D%s %s_sampler;\n"
"uniform vec3 %s_circle_d;\n"
"uniform float %s_radius_0;\n"
"\n"
"vec4 get_%s()\n"
"{\n"
" vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
" \n"
" float B = dot (pos, %s_circle_d);\n"
" float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
" \n"
" float t = 0.5 * C / B;\n"
" float is_valid = step (-%s_radius_0, t * %s_circle_d.z);\n",
namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
namestr, namestr, namestr, namestr, namestr);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
_cairo_gl_shader_needs_border_fade (op))
{
_cairo_output_stream_printf (stream,
" float border_fade = %s_border_fade (t, %s_texdims.x);\n"
" vec4 texel = texture2D%s (%s_sampler, vec2 (t, 0.5));\n"
" return mix (vec4 (0.0), texel * border_fade, is_valid);\n"
"}\n",
namestr, namestr, rectstr, namestr);
}
else
{
_cairo_output_stream_printf (stream,
" vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2 (t, 0.5)));\n"
" return mix (vec4 (0.0), texel, is_valid);\n"
"}\n",
rectstr, namestr, namestr);
}
break;
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE:
_cairo_output_stream_printf (stream,
"varying vec2 %s_texcoords;\n"
"uniform vec2 %s_texdims;\n"
"uniform sampler2D%s %s_sampler;\n"
"uniform vec3 %s_circle_d;\n"
"uniform float %s_a;\n"
"uniform float %s_radius_0;\n"
"\n"
"vec4 get_%s()\n"
"{\n"
" vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
" \n"
" float B = dot (pos, %s_circle_d);\n"
" float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
" \n"
" float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
" float sqrtdet = sqrt (abs (det));\n"
" vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
" \n"
" vec2 is_valid = step (vec2 (0.0), t) * step (t, vec2(1.0));\n"
" float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
" \n"
" float upper_t = mix (t.y, t.x, is_valid.x);\n",
namestr, namestr, rectstr, namestr, namestr, namestr, namestr,
namestr, namestr, namestr, namestr, namestr, namestr);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
_cairo_gl_shader_needs_border_fade (op))
{
_cairo_output_stream_printf (stream,
" float border_fade = %s_border_fade (upper_t, %s_texdims.x);\n"
" vec4 texel = texture2D%s (%s_sampler, vec2 (upper_t, 0.5));\n"
" return mix (vec4 (0.0), texel * border_fade, has_color);\n"
"}\n",
namestr, namestr, rectstr, namestr);
}
else
{
_cairo_output_stream_printf (stream,
" vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
" return mix (vec4 (0.0), texel, has_color);\n"
"}\n",
rectstr, namestr, namestr);
}
break;
case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT:
_cairo_output_stream_printf (stream,
"varying vec2 %s_texcoords;\n"
"uniform sampler2D%s %s_sampler;\n"
"uniform vec3 %s_circle_d;\n"
"uniform float %s_a;\n"
"uniform float %s_radius_0;\n"
"\n"
"vec4 get_%s()\n"
"{\n"
" vec3 pos = vec3 (%s_texcoords, %s_radius_0);\n"
" \n"
" float B = dot (pos, %s_circle_d);\n"
" float C = dot (pos, vec3 (pos.xy, -pos.z));\n"
" \n"
" float det = dot (vec2 (B, %s_a), vec2 (B, -C));\n"
" float sqrtdet = sqrt (abs (det));\n"
" vec2 t = (B + vec2 (sqrtdet, -sqrtdet)) / %s_a;\n"
" \n"
" vec2 is_valid = step (vec2 (-%s_radius_0), t * %s_circle_d.z);\n"
" float has_color = step (0., det) * max (is_valid.x, is_valid.y);\n"
" \n"
" float upper_t = mix (t.y, t.x, is_valid.x);\n"
" vec4 texel = texture2D%s (%s_sampler, %s_wrap (vec2(upper_t, 0.5)));\n"
" return mix (vec4 (0.0), texel, has_color);\n"
"}\n",
namestr, rectstr, namestr, namestr, namestr, namestr,
namestr, namestr, namestr, namestr, namestr,
namestr, namestr, namestr, rectstr, namestr, namestr);
break;
}
}
 
/*
* Emits the border fade functions used by an operand.
*
* If bilinear filtering is used, the emitted function performs a linear
* fade to transparency effect in the intervals [-1/2n, 1/2n] and
* [1 - 1/2n, 1 + 1/2n] (n: texture size).
*
* If nearest filtering is used, the emitted function just returns
* 0.0 for all values outside [0, 1).
*/
static void
_cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream,
cairo_gl_operand_t *operand,
cairo_gl_tex_t name)
{
const char *namestr = operand_names[name];
GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand);
 
/* 2D version */
_cairo_output_stream_printf (stream,
"vec2 %s_border_fade (vec2 coords, vec2 dims)\n"
"{\n",
namestr);
 
if (gl_filter == GL_LINEAR)
_cairo_output_stream_printf (stream,
" return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n");
else
_cairo_output_stream_printf (stream,
" bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n"
" bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n"
" return vec2 (float (all (in_tex1) && all (in_tex2)));\n");
 
_cairo_output_stream_printf (stream, "}\n");
 
/* 1D version */
_cairo_output_stream_printf (stream,
"float %s_border_fade (float x, float dim)\n"
"{\n",
namestr);
if (gl_filter == GL_LINEAR)
_cairo_output_stream_printf (stream,
" return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n");
else
_cairo_output_stream_printf (stream,
" bool in_tex = x >= 0.0 && x < 1.0;\n"
" return float (in_tex);\n");
 
_cairo_output_stream_printf (stream, "}\n");
}
 
/*
* Emits the wrap function used by an operand.
*
* In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are
* only available for NPOT textures if the GL_OES_texture_npot is supported.
* If GL_OES_texture_npot is not supported, we need to implement the wrapping
* functionality in the shader.
*/
static void
_cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx,
cairo_output_stream_t *stream,
cairo_gl_operand_t *operand,
cairo_gl_tex_t name)
{
const char *namestr = operand_names[name];
cairo_extend_t extend = _cairo_gl_operand_get_extend (operand);
 
_cairo_output_stream_printf (stream,
"vec2 %s_wrap(vec2 coords)\n"
"{\n",
namestr);
 
if (! ctx->has_npot_repeat &&
(extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT))
{
if (extend == CAIRO_EXTEND_REPEAT) {
_cairo_output_stream_printf (stream,
" return fract(coords);\n");
} else { /* CAIRO_EXTEND_REFLECT */
_cairo_output_stream_printf (stream,
" return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n");
}
}
else
{
_cairo_output_stream_printf (stream, " return coords;\n");
}
 
_cairo_output_stream_printf (stream, "}\n");
}
 
static cairo_status_t
cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx,
cairo_gl_shader_in_t in,
cairo_gl_operand_t *src,
cairo_gl_operand_t *mask,
cairo_bool_t use_coverage,
cairo_gl_operand_type_t dest_type,
char **out)
{
cairo_output_stream_t *stream = _cairo_memory_stream_create ();
unsigned char *source;
unsigned long length;
cairo_status_t status;
const char *coverage_str;
 
_cairo_output_stream_printf (stream,
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n");
 
_cairo_gl_shader_emit_wrap (ctx, stream, src, CAIRO_GL_TEX_SOURCE);
_cairo_gl_shader_emit_wrap (ctx, stream, mask, CAIRO_GL_TEX_MASK);
 
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) {
if (_cairo_gl_shader_needs_border_fade (src))
_cairo_gl_shader_emit_border_fade (stream, src, CAIRO_GL_TEX_SOURCE);
if (_cairo_gl_shader_needs_border_fade (mask))
_cairo_gl_shader_emit_border_fade (stream, mask, CAIRO_GL_TEX_MASK);
}
 
cairo_gl_shader_emit_color (stream, ctx, src, CAIRO_GL_TEX_SOURCE);
cairo_gl_shader_emit_color (stream, ctx, mask, CAIRO_GL_TEX_MASK);
 
coverage_str = "";
if (use_coverage) {
_cairo_output_stream_printf (stream, "varying float coverage;\n");
coverage_str = " * coverage";
}
 
_cairo_output_stream_printf (stream,
"void main()\n"
"{\n");
switch (in) {
case CAIRO_GL_SHADER_IN_COUNT:
default:
ASSERT_NOT_REACHED;
case CAIRO_GL_SHADER_IN_NORMAL:
_cairo_output_stream_printf (stream,
" gl_FragColor = get_source() * get_mask().a%s;\n",
coverage_str);
break;
case CAIRO_GL_SHADER_IN_CA_SOURCE:
_cairo_output_stream_printf (stream,
" gl_FragColor = get_source() * get_mask()%s;\n",
coverage_str);
break;
case CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA:
_cairo_output_stream_printf (stream,
" gl_FragColor = get_source().a * get_mask()%s;\n",
coverage_str);
break;
}
 
_cairo_output_stream_write (stream,
"}\n\0", 3);
 
status = _cairo_memory_stream_destroy (stream, &source, &length);
if (unlikely (status))
return status;
 
*out = (char *) source;
return CAIRO_STATUS_SUCCESS;
}
 
static void
compile_shader (cairo_gl_context_t *ctx,
GLuint *shader,
GLenum type,
const char *source)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint success, log_size, num_chars;
char *log;
 
*shader = dispatch->CreateShader (type);
dispatch->ShaderSource (*shader, 1, &source, 0);
dispatch->CompileShader (*shader);
dispatch->GetShaderiv (*shader, GL_COMPILE_STATUS, &success);
 
if (success)
return;
 
dispatch->GetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
if (log_size < 0) {
printf ("OpenGL shader compilation failed.\n");
ASSERT_NOT_REACHED;
return;
}
 
log = _cairo_malloc (log_size + 1);
dispatch->GetShaderInfoLog (*shader, log_size, &num_chars, log);
log[num_chars] = '\0';
 
printf ("OpenGL shader compilation failed. Shader:\n%s\n", source);
printf ("OpenGL compilation log:\n%s\n", log);
 
free (log);
ASSERT_NOT_REACHED;
}
 
static void
link_shader_program (cairo_gl_context_t *ctx,
GLuint *program,
GLuint vert,
GLuint frag)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint success, log_size, num_chars;
char *log;
 
*program = dispatch->CreateProgram ();
dispatch->AttachShader (*program, vert);
dispatch->AttachShader (*program, frag);
 
dispatch->BindAttribLocation (*program, CAIRO_GL_VERTEX_ATTRIB_INDEX,
"Vertex");
dispatch->BindAttribLocation (*program, CAIRO_GL_COLOR_ATTRIB_INDEX,
"Color");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD0_ATTRIB_INDEX,
"MultiTexCoord0");
dispatch->BindAttribLocation (*program, CAIRO_GL_TEXCOORD1_ATTRIB_INDEX,
"MultiTexCoord1");
 
dispatch->LinkProgram (*program);
dispatch->GetProgramiv (*program, GL_LINK_STATUS, &success);
if (success)
return;
 
dispatch->GetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
if (log_size < 0) {
printf ("OpenGL shader link failed.\n");
ASSERT_NOT_REACHED;
return;
}
 
log = _cairo_malloc (log_size + 1);
dispatch->GetProgramInfoLog (*program, log_size, &num_chars, log);
log[num_chars] = '\0';
 
printf ("OpenGL shader link failed:\n%s\n", log);
free (log);
ASSERT_NOT_REACHED;
}
 
static GLint
_cairo_gl_get_op_uniform_location(cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
cairo_gl_tex_t tex_unit,
const char *suffix)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
char uniform_name[100];
const char *unit_name[2] = { "source", "mask" };
 
snprintf (uniform_name, sizeof (uniform_name), "%s_%s",
unit_name[tex_unit], suffix);
 
return dispatch->GetUniformLocation (shader->program, uniform_name);
}
 
static cairo_status_t
_cairo_gl_shader_compile_and_link (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader,
cairo_gl_var_type_t src,
cairo_gl_var_type_t mask,
cairo_bool_t use_coverage,
const char *fragment_text)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
unsigned int vertex_shader;
cairo_status_t status;
int i;
 
assert (shader->program == 0);
 
vertex_shader = cairo_gl_var_type_hash (src, mask, use_coverage,
CAIRO_GL_VAR_NONE);
if (ctx->vertex_shaders[vertex_shader] == 0) {
char *source;
 
status = cairo_gl_shader_get_vertex_source (src,
mask,
use_coverage,
CAIRO_GL_VAR_NONE,
&source);
if (unlikely (status))
goto FAILURE;
 
compile_shader (ctx, &ctx->vertex_shaders[vertex_shader],
GL_VERTEX_SHADER, source);
free (source);
}
 
compile_shader (ctx, &shader->fragment_shader,
GL_FRAGMENT_SHADER, fragment_text);
 
link_shader_program (ctx, &shader->program,
ctx->vertex_shaders[vertex_shader],
shader->fragment_shader);
 
shader->mvp_location =
dispatch->GetUniformLocation (shader->program,
"ModelViewProjectionMatrix");
 
for (i = 0; i < 2; i++) {
shader->constant_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "constant");
shader->a_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "a");
shader->circle_d_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "circle_d");
shader->radius_0_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "radius_0");
shader->texdims_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "texdims");
shader->texgen_location[i] =
_cairo_gl_get_op_uniform_location (ctx, shader, i, "texgen");
}
 
return CAIRO_STATUS_SUCCESS;
 
FAILURE:
_cairo_gl_shader_fini (ctx, shader);
shader->fragment_shader = 0;
shader->program = 0;
 
return status;
}
 
/* We always bind the source to texture unit 0 if present, and mask to
* texture unit 1 if present, so we can just initialize these once at
* compile time.
*/
static void
_cairo_gl_shader_set_samplers (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
GLint location;
GLint saved_program;
 
/* We have to save/restore the current program because we might be
* asked for a different program while a shader is bound. This shouldn't
* be a performance issue, since this is only called once per compile.
*/
glGetIntegerv (GL_CURRENT_PROGRAM, &saved_program);
dispatch->UseProgram (shader->program);
 
location = dispatch->GetUniformLocation (shader->program, "source_sampler");
if (location != -1) {
dispatch->Uniform1i (location, CAIRO_GL_TEX_SOURCE);
}
 
location = dispatch->GetUniformLocation (shader->program, "mask_sampler");
if (location != -1) {
dispatch->Uniform1i (location, CAIRO_GL_TEX_MASK);
}
 
dispatch->UseProgram (saved_program);
}
 
void
_cairo_gl_shader_bind_float (cairo_gl_context_t *ctx,
GLint location,
float value)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
assert (location != -1);
dispatch->Uniform1f (location, value);
}
 
void
_cairo_gl_shader_bind_vec2 (cairo_gl_context_t *ctx,
GLint location,
float value0,
float value1)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
assert (location != -1);
dispatch->Uniform2f (location, value0, value1);
}
 
void
_cairo_gl_shader_bind_vec3 (cairo_gl_context_t *ctx,
GLint location,
float value0,
float value1,
float value2)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
assert (location != -1);
dispatch->Uniform3f (location, value0, value1, value2);
}
 
void
_cairo_gl_shader_bind_vec4 (cairo_gl_context_t *ctx,
GLint location,
float value0, float value1,
float value2, float value3)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
assert (location != -1);
dispatch->Uniform4f (location, value0, value1, value2, value3);
}
 
void
_cairo_gl_shader_bind_matrix (cairo_gl_context_t *ctx,
GLint location,
const cairo_matrix_t* m)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
float gl_m[9] = {
m->xx, m->xy, m->x0,
m->yx, m->yy, m->y0,
0, 0, 1
};
assert (location != -1);
dispatch->UniformMatrix3fv (location, 1, GL_TRUE, gl_m);
}
 
void
_cairo_gl_shader_bind_matrix4f (cairo_gl_context_t *ctx,
GLint location, GLfloat* gl_m)
{
cairo_gl_dispatch_t *dispatch = &ctx->dispatch;
assert (location != -1);
dispatch->UniformMatrix4fv (location, 1, GL_FALSE, gl_m);
}
 
void
_cairo_gl_set_shader (cairo_gl_context_t *ctx,
cairo_gl_shader_t *shader)
{
if (ctx->current_shader == shader)
return;
 
if (shader)
ctx->dispatch.UseProgram (shader->program);
else
ctx->dispatch.UseProgram (0);
 
ctx->current_shader = shader;
}
 
cairo_status_t
_cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx,
cairo_gl_operand_t *source,
cairo_gl_operand_t *mask,
cairo_bool_t use_coverage,
cairo_gl_shader_in_t in,
cairo_gl_shader_t **shader)
{
cairo_shader_cache_entry_t lookup, *entry;
char *fs_source;
cairo_status_t status;
 
lookup.ctx = ctx;
 
lookup.vertex = cairo_gl_var_type_hash (cairo_gl_operand_get_var_type (source),
cairo_gl_operand_get_var_type (mask),
use_coverage,
CAIRO_GL_VAR_NONE);
 
lookup.src = source->type;
lookup.mask = mask->type;
lookup.dest = CAIRO_GL_OPERAND_NONE;
lookup.use_coverage = use_coverage;
lookup.in = in;
lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source);
lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source);
lookup.src_extend = _cairo_gl_operand_get_extend (source);
lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask);
lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask);
lookup.mask_extend = _cairo_gl_operand_get_extend (mask);
lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup);
lookup.base.size = 1;
 
entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base);
if (entry) {
assert (entry->shader.program);
*shader = &entry->shader;
return CAIRO_STATUS_SUCCESS;
}
 
status = cairo_gl_shader_get_fragment_source (ctx,
in,
source,
mask,
use_coverage,
CAIRO_GL_OPERAND_NONE,
&fs_source);
if (unlikely (status))
return status;
 
entry = malloc (sizeof (cairo_shader_cache_entry_t));
if (unlikely (entry == NULL)) {
free (fs_source);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t));
 
entry->ctx = ctx;
_cairo_gl_shader_init (&entry->shader);
status = _cairo_gl_shader_compile_and_link (ctx,
&entry->shader,
cairo_gl_operand_get_var_type (source),
cairo_gl_operand_get_var_type (mask),
use_coverage,
fs_source);
free (fs_source);
 
if (unlikely (status)) {
free (entry);
return status;
}
 
_cairo_gl_shader_set_samplers (ctx, &entry->shader);
 
status = _cairo_cache_insert (&ctx->shaders, &entry->base);
if (unlikely (status)) {
_cairo_gl_shader_fini (ctx, &entry->shader);
free (entry);
return status;
}
 
*shader = &entry->shader;
 
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-gl-source.c
0,0 → 1,111
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-surface-backend-private.h"
 
static cairo_status_t
_cairo_gl_source_finish (void *abstract_surface)
{
cairo_gl_source_t *source = abstract_surface;
 
_cairo_gl_operand_destroy (&source->operand);
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t cairo_gl_source_backend = {
CAIRO_SURFACE_TYPE_GL,
_cairo_gl_source_finish,
NULL, /* read-only wrapper */
};
 
cairo_surface_t *
_cairo_gl_pattern_to_source (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_gl_source_t *source;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (pattern == NULL)
return _cairo_gl_white_source ();
 
source = malloc (sizeof (*source));
if (unlikely (source == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&source->base,
&cairo_gl_source_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
*src_x = *src_y = 0;
status = _cairo_gl_operand_init (&source->operand, pattern,
(cairo_gl_surface_t *)dst,
sample, extents,
FALSE);
if (unlikely (status)) {
cairo_surface_destroy (&source->base);
return _cairo_surface_create_in_error (status);
}
 
return &source->base;
}
 
cairo_surface_t *
_cairo_gl_white_source (void)
{
cairo_gl_source_t *source;
 
source = malloc (sizeof (*source));
if (unlikely (source == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&source->base,
&cairo_gl_source_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
_cairo_gl_solid_operand_init (&source->operand, CAIRO_COLOR_WHITE);
 
return &source->base;
}
/programs/develop/libraries/cairo/src/cairo-gl-spans-compositor.c
0,0 → 1,553
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-spans-compositor-private.h"
#include "cairo-surface-backend-private.h"
 
typedef struct _cairo_gl_span_renderer {
cairo_span_renderer_t base;
 
cairo_gl_composite_t setup;
double opacity;
 
cairo_gl_emit_span_t emit;
 
int xmin, xmax;
int ymin, ymax;
 
cairo_gl_context_t *ctx;
} cairo_gl_span_renderer_t;
 
static cairo_status_t
_cairo_gl_bounded_opaque_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
cairo_gl_emit_span_t emit = r->emit;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage) {
emit (r->ctx,
spans[0].x, y,
spans[1].x, y + height,
spans[0].coverage);
}
 
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_bounded_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
cairo_gl_emit_span_t emit = r->emit;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage) {
emit (r->ctx,
spans[0].x, y,
spans[1].x, y + height,
r->opacity * spans[0].coverage);
}
 
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_unbounded_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
cairo_gl_emit_span_t emit = r->emit;
 
if (y > r->ymin) {
emit (r->ctx,
r->xmin, r->ymin,
r->xmax, y,
0);
}
 
if (num_spans == 0) {
emit (r->ctx,
r->xmin, y,
r->xmax, y + height,
0);
} else {
if (spans[0].x != r->xmin) {
emit (r->ctx,
r->xmin, y,
spans[0].x, y + height,
0);
}
 
do {
emit (r->ctx,
spans[0].x, y,
spans[1].x, y + height,
r->opacity * spans[0].coverage);
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != r->xmax) {
emit (r->ctx,
spans[0].x, y,
r->xmax, y + height,
0);
}
}
 
r->ymin = y + height;
return CAIRO_STATUS_SUCCESS;
}
 
/* XXX */
static cairo_status_t
_cairo_gl_clipped_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
cairo_gl_emit_span_t emit = r->emit;
 
if (y > r->ymin) {
emit (r->ctx,
r->xmin, r->ymin,
r->xmax, y,
0);
}
 
if (num_spans == 0) {
emit (r->ctx,
r->xmin, y,
r->xmax, y + height,
0);
} else {
if (spans[0].x != r->xmin) {
emit (r->ctx,
r->xmin, y,
spans[0].x, y + height,
0);
}
 
do {
emit (r->ctx,
spans[0].x, y,
spans[1].x, y + height,
r->opacity * spans[0].coverage);
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != r->xmax) {
emit (r->ctx,
spans[0].x, y,
r->xmax, y + height,
0);
}
}
 
r->ymin = y + height;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_gl_finish_unbounded_spans (void *abstract_renderer)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
cairo_gl_emit_span_t emit = r->emit;
 
if (r->ymax > r->ymin) {
emit (r->ctx,
r->xmin, r->ymin,
r->xmax, r->ymax,
0);
}
 
return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
}
 
static cairo_status_t
_cairo_gl_finish_bounded_spans (void *abstract_renderer)
{
cairo_gl_span_renderer_t *r = abstract_renderer;
 
return _cairo_gl_context_release (r->ctx, CAIRO_STATUS_SUCCESS);
}
 
static void
emit_aligned_boxes (cairo_gl_context_t *ctx,
const cairo_boxes_t *boxes)
{
const struct _cairo_boxes_chunk *chunk;
cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx);
int i;
 
TRACE ((stderr, "%s: num_boxes=%d\n", __FUNCTION__, boxes->num_boxes));
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
emit (ctx, x1, y1, x2, y2);
}
}
}
 
static cairo_int_status_t
fill_boxes (void *_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_solid_source (&setup, color);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
emit_aligned_boxes (ctx, boxes);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_gl_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
int i;
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x = _cairo_fixed_integer_part (b->p1.x);
int y = _cairo_fixed_integer_part (b->p1.y);
int w = _cairo_fixed_integer_part (b->p2.x) - x;
int h = _cairo_fixed_integer_part (b->p2.y) - y;
cairo_status_t status;
 
status = _cairo_gl_surface_draw_image (dst, image,
x + dx, y + dy,
w, h,
x, y, TRUE);
if (unlikely (status))
return status;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t copy_boxes (void *_dst,
cairo_surface_t *_src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy)
{
cairo_gl_surface_t *dst = _dst;
cairo_gl_surface_t *src = (cairo_gl_surface_t *)_src;
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (! _cairo_gl_surface_is_texture (src))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (src->base.device != dst->base.device)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup, &src->operand);
_cairo_gl_operand_translate (&setup.src, -dx, -dy);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
emit_aligned_boxes (ctx, boxes);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
composite_boxes (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
cairo_gl_operand_t tmp_operand;
cairo_gl_operand_t *src_operand;
 
TRACE ((stderr, "%s mask=(%d,%d), dst=(%d, %d)\n", __FUNCTION__,
mask_x, mask_y, dst_x, dst_y));
 
if (abstract_mask) {
if (op == CAIRO_OPERATOR_CLEAR) {
_cairo_gl_solid_operand_init (&tmp_operand, CAIRO_COLOR_WHITE);
src_operand = &tmp_operand;
op = CAIRO_OPERATOR_DEST_OUT;
} else if (op == CAIRO_OPERATOR_SOURCE) {
/* requires a LERP in the shader between dest and source */
return CAIRO_INT_STATUS_UNSUPPORTED;
} else
src_operand = source_to_operand (abstract_src);
} else
src_operand = source_to_operand (abstract_src);
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup,
src_operand);
_cairo_gl_operand_translate (&setup.src, -src_x, -src_y);
 
_cairo_gl_composite_set_mask_operand (&setup,
source_to_operand (abstract_mask));
_cairo_gl_operand_translate (&setup.mask, -mask_x, -mask_y);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
emit_aligned_boxes (ctx, boxes);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
if (src_operand == &tmp_operand)
_cairo_gl_operand_destroy (&tmp_operand);
return status;
}
 
static cairo_int_status_t
_cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t *_r,
const cairo_composite_rectangles_t *composite,
cairo_antialias_t antialias,
cairo_bool_t needs_clip)
{
cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *)_r;
const cairo_pattern_t *source = &composite->source_pattern.base;
cairo_operator_t op = composite->op;
cairo_int_status_t status;
 
if (op == CAIRO_OPERATOR_SOURCE) {
if (! _cairo_pattern_is_opaque (&composite->source_pattern.base,
&composite->source_sample_area))
return CAIRO_INT_STATUS_UNSUPPORTED;
op = CAIRO_OPERATOR_OVER;
}
 
/* XXX earlier! */
if (op == CAIRO_OPERATOR_CLEAR) {
source = &_cairo_pattern_white.base;
op = CAIRO_OPERATOR_DEST_OUT;
} else if (composite->surface->is_clear &&
(op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_OVER ||
op == CAIRO_OPERATOR_ADD)) {
op = CAIRO_OPERATOR_SOURCE;
} else if (op == CAIRO_OPERATOR_SOURCE) {
/* no lerp equivalent without some major PITA */
return CAIRO_INT_STATUS_UNSUPPORTED;
} else if (! _cairo_gl_operator_is_supported (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_gl_composite_init (&r->setup,
op, (cairo_gl_surface_t *)composite->surface,
FALSE);
if (unlikely (status))
goto FAIL;
 
status = _cairo_gl_composite_set_source (&r->setup, source,
&composite->source_sample_area,
&composite->unbounded,
TRUE);
if (unlikely (status))
goto FAIL;
 
r->opacity = 1.0;
if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
r->opacity = composite->mask_pattern.solid.color.alpha;
} else {
status = _cairo_gl_composite_set_mask (&r->setup,
&composite->mask_pattern.base,
&composite->mask_sample_area,
&composite->unbounded,
TRUE);
if (unlikely (status))
goto FAIL;
}
 
_cairo_gl_composite_set_spans (&r->setup);
 
status = _cairo_gl_composite_begin (&r->setup, &r->ctx);
if (unlikely (status))
goto FAIL;
 
r->emit = _cairo_gl_context_choose_emit_span (r->ctx);
if (composite->is_bounded) {
if (r->opacity == 1.)
r->base.render_rows = _cairo_gl_bounded_opaque_spans;
else
r->base.render_rows = _cairo_gl_bounded_spans;
r->base.finish = _cairo_gl_finish_bounded_spans;
} else {
if (needs_clip)
r->base.render_rows = _cairo_gl_clipped_spans;
else
r->base.render_rows = _cairo_gl_unbounded_spans;
r->base.finish = _cairo_gl_finish_unbounded_spans;
r->xmin = composite->unbounded.x;
r->xmax = composite->unbounded.x + composite->unbounded.width;
r->ymin = composite->unbounded.y;
r->ymax = composite->unbounded.y + composite->unbounded.height;
}
 
return CAIRO_STATUS_SUCCESS;
 
FAIL:
return status;
}
 
static void
_cairo_gl_span_renderer_fini (cairo_abstract_span_renderer_t *_r,
cairo_int_status_t status)
{
cairo_gl_span_renderer_t *r = (cairo_gl_span_renderer_t *) _r;
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return;
 
if (status == CAIRO_INT_STATUS_SUCCESS)
r->base.finish (r);
 
_cairo_gl_composite_fini (&r->setup);
}
 
const cairo_compositor_t *
_cairo_gl_span_compositor_get (void)
{
static cairo_spans_compositor_t spans;
static cairo_compositor_t shape;
 
if (spans.base.delegate == NULL) {
/* The fallback to traps here is essentially just for glyphs... */
_cairo_shape_mask_compositor_init (&shape,
_cairo_gl_traps_compositor_get());
shape.glyphs = NULL;
 
_cairo_spans_compositor_init (&spans, &shape);
spans.fill_boxes = fill_boxes;
spans.draw_image_boxes = draw_image_boxes;
spans.copy_boxes = copy_boxes;
//spans.check_composite_boxes = check_composite_boxes;
spans.pattern_to_surface = _cairo_gl_pattern_to_source;
spans.composite_boxes = composite_boxes;
//spans.check_span_renderer = check_span_renderer;
spans.renderer_init = _cairo_gl_span_renderer_init;
spans.renderer_fini = _cairo_gl_span_renderer_fini;
}
 
return &spans.base;
}
/programs/develop/libraries/cairo/src/cairo-gl-surface.c
0,0 → 1,1451
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 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 Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-surface-backend-private.h"
 
static const cairo_surface_backend_t _cairo_gl_surface_backend;
 
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags);
 
static cairo_bool_t _cairo_surface_is_gl (cairo_surface_t *surface)
{
return surface->backend == &_cairo_gl_surface_backend;
}
 
static cairo_bool_t
_cairo_gl_get_image_format_and_type_gles2 (pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha,
cairo_bool_t *needs_swap)
{
cairo_bool_t is_little_endian = _cairo_is_little_endian ();
 
*has_alpha = TRUE;
 
switch ((int) pixman_format) {
case PIXMAN_a8r8g8b8:
*internal_format = GL_BGRA;
*format = GL_BGRA;
*type = GL_UNSIGNED_BYTE;
*needs_swap = !is_little_endian;
return TRUE;
 
case PIXMAN_x8r8g8b8:
*internal_format = GL_BGRA;
*format = GL_BGRA;
*type = GL_UNSIGNED_BYTE;
*has_alpha = FALSE;
*needs_swap = !is_little_endian;
return TRUE;
 
case PIXMAN_a8b8g8r8:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
*needs_swap = !is_little_endian;
return TRUE;
 
case PIXMAN_x8b8g8r8:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_BYTE;
*has_alpha = FALSE;
*needs_swap = !is_little_endian;
return TRUE;
 
case PIXMAN_b8g8r8a8:
*internal_format = GL_BGRA;
*format = GL_BGRA;
*type = GL_UNSIGNED_BYTE;
*needs_swap = is_little_endian;
return TRUE;
 
case PIXMAN_b8g8r8x8:
*internal_format = GL_BGRA;
*format = GL_BGRA;
*type = GL_UNSIGNED_BYTE;
*has_alpha = FALSE;
*needs_swap = is_little_endian;
return TRUE;
 
case PIXMAN_r8g8b8:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
*needs_swap = is_little_endian;
return TRUE;
 
case PIXMAN_b8g8r8:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
*needs_swap = !is_little_endian;
return TRUE;
 
case PIXMAN_r5g6b5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5;
*needs_swap = FALSE;
return TRUE;
 
case PIXMAN_b5g6r5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5;
*needs_swap = TRUE;
return TRUE;
 
case PIXMAN_a1b5g5r5:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_5_5_5_1;
*needs_swap = TRUE;
return TRUE;
 
case PIXMAN_x1b5g5r5:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_5_5_5_1;
*has_alpha = FALSE;
*needs_swap = TRUE;
return TRUE;
 
case PIXMAN_a8:
*internal_format = GL_ALPHA;
*format = GL_ALPHA;
*type = GL_UNSIGNED_BYTE;
*needs_swap = FALSE;
return TRUE;
 
default:
return FALSE;
}
}
 
static cairo_bool_t
_cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha,
cairo_bool_t *needs_swap)
{
*has_alpha = TRUE;
*needs_swap = FALSE;
 
switch (pixman_format) {
case PIXMAN_a8r8g8b8:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
return TRUE;
case PIXMAN_x8r8g8b8:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a8b8g8r8:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
return TRUE;
case PIXMAN_x8b8g8r8:
*internal_format = GL_RGB;
*format = GL_RGBA;
*type = GL_UNSIGNED_INT_8_8_8_8_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_b8g8r8a8:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8;
return TRUE;
case PIXMAN_b8g8r8x8:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_INT_8_8_8_8;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_r8g8b8:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_BYTE;
return TRUE;
case PIXMAN_b8g8r8:
*internal_format = GL_RGB;
*format = GL_BGR;
*type = GL_UNSIGNED_BYTE;
return TRUE;
case PIXMAN_r5g6b5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5;
return TRUE;
case PIXMAN_b5g6r5:
*internal_format = GL_RGB;
*format = GL_RGB;
*type = GL_UNSIGNED_SHORT_5_6_5_REV;
return TRUE;
case PIXMAN_a1r5g5b5:
*internal_format = GL_RGBA;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
return TRUE;
case PIXMAN_x1r5g5b5:
*internal_format = GL_RGB;
*format = GL_BGRA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a1b5g5r5:
*internal_format = GL_RGBA;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
return TRUE;
case PIXMAN_x1b5g5r5:
*internal_format = GL_RGB;
*format = GL_RGBA;
*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
*has_alpha = FALSE;
return TRUE;
case PIXMAN_a8:
*internal_format = GL_ALPHA;
*format = GL_ALPHA;
*type = GL_UNSIGNED_BYTE;
return TRUE;
 
case PIXMAN_a2b10g10r10:
case PIXMAN_x2b10g10r10:
case PIXMAN_a4r4g4b4:
case PIXMAN_x4r4g4b4:
case PIXMAN_a4b4g4r4:
case PIXMAN_x4b4g4r4:
case PIXMAN_r3g3b2:
case PIXMAN_b2g3r3:
case PIXMAN_a2r2g2b2:
case PIXMAN_a2b2g2r2:
case PIXMAN_c8:
case PIXMAN_x4a4:
/* case PIXMAN_x4c4: */
case PIXMAN_x4g4:
case PIXMAN_a4:
case PIXMAN_r1g2b1:
case PIXMAN_b1g2r1:
case PIXMAN_a1r1g1b1:
case PIXMAN_a1b1g1r1:
case PIXMAN_c4:
case PIXMAN_g4:
case PIXMAN_a1:
case PIXMAN_g1:
case PIXMAN_yuy2:
case PIXMAN_yv12:
case PIXMAN_x2r10g10b10:
case PIXMAN_a2r10g10b10:
case PIXMAN_r8g8b8x8:
case PIXMAN_r8g8b8a8:
case PIXMAN_x14r6g6b6:
default:
return FALSE;
}
}
 
/*
* Extracts pixel data from an image surface.
*/
static cairo_status_t
_cairo_gl_surface_extract_image_data (cairo_image_surface_t *image,
int x, int y,
int width, int height,
void **output)
{
int cpp = PIXMAN_FORMAT_BPP (image->pixman_format) / 8;
char *data = _cairo_malloc_ab (width * height, cpp);
char *dst = data;
unsigned char *src = image->data + y * image->stride + x * cpp;
int i;
 
if (unlikely (data == NULL))
return CAIRO_STATUS_NO_MEMORY;
 
for (i = 0; i < height; i++) {
memcpy (dst, src, width * cpp);
src += image->stride;
dst += width * cpp;
}
 
*output = data;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_bool_t
_cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor,
pixman_format_code_t pixman_format,
GLenum *internal_format, GLenum *format,
GLenum *type, cairo_bool_t *has_alpha,
cairo_bool_t *needs_swap)
{
if (flavor == CAIRO_GL_FLAVOR_DESKTOP)
return _cairo_gl_get_image_format_and_type_gl (pixman_format,
internal_format, format,
type, has_alpha,
needs_swap);
else
return _cairo_gl_get_image_format_and_type_gles2 (pixman_format,
internal_format, format,
type, has_alpha,
needs_swap);
 
}
 
cairo_bool_t
_cairo_gl_operator_is_supported (cairo_operator_t op)
{
return op < CAIRO_OPERATOR_SATURATE;
}
 
static void
_cairo_gl_surface_embedded_operand_init (cairo_gl_surface_t *surface)
{
cairo_gl_operand_t *operand = &surface->operand;
cairo_surface_attributes_t *attributes = &operand->texture.attributes;
 
memset (operand, 0, sizeof (cairo_gl_operand_t));
 
operand->type = CAIRO_GL_OPERAND_TEXTURE;
operand->texture.surface = surface;
operand->texture.tex = surface->tex;
 
if (_cairo_gl_device_requires_power_of_two_textures (surface->base.device)) {
cairo_matrix_init_identity (&attributes->matrix);
} else {
cairo_matrix_init_scale (&attributes->matrix,
1.0 / surface->width,
1.0 / surface->height);
}
 
attributes->extend = CAIRO_EXTEND_NONE;
attributes->filter = CAIRO_FILTER_NEAREST;
}
 
void
_cairo_gl_surface_init (cairo_device_t *device,
cairo_gl_surface_t *surface,
cairo_content_t content,
int width, int height)
{
assert (width > 0 && height > 0);
 
_cairo_surface_init (&surface->base,
&_cairo_gl_surface_backend,
device,
content);
 
surface->width = width;
surface->height = height;
surface->needs_update = FALSE;
 
_cairo_gl_surface_embedded_operand_init (surface);
}
 
static cairo_bool_t
_cairo_gl_surface_size_valid_for_context (cairo_gl_context_t *ctx,
int width, int height)
{
return width > 0 && height > 0 &&
width <= ctx->max_framebuffer_size &&
height <= ctx->max_framebuffer_size;
}
 
static cairo_bool_t
_cairo_gl_surface_size_valid (cairo_gl_surface_t *surface,
int width, int height)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
return _cairo_gl_surface_size_valid_for_context (ctx, width, height);
}
 
static cairo_surface_t *
_cairo_gl_surface_create_scratch_for_texture (cairo_gl_context_t *ctx,
cairo_content_t content,
GLuint tex,
int width,
int height)
{
cairo_gl_surface_t *surface;
 
surface = calloc (1, sizeof (cairo_gl_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface->tex = tex;
_cairo_gl_surface_init (&ctx->base, surface, content, width, height);
 
surface->supports_msaa = ctx->supports_msaa;
surface->supports_stencil = TRUE;
 
/* Create the texture used to store the surface's data. */
_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
glBindTexture (ctx->tex_target, surface->tex);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
return &surface->base;
}
 
static cairo_surface_t *
_create_scratch_internal (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height,
cairo_bool_t for_caching)
{
cairo_gl_surface_t *surface;
GLenum format;
GLuint tex;
 
glGenTextures (1, &tex);
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch_for_texture (ctx, content,
tex, width, height);
if (unlikely (surface->base.status))
return &surface->base;
 
surface->owns_tex = TRUE;
 
/* adjust the texture size after setting our real extents */
if (width < 1)
width = 1;
if (height < 1)
height = 1;
 
switch (content) {
default:
ASSERT_NOT_REACHED;
case CAIRO_CONTENT_COLOR_ALPHA:
format = GL_RGBA;
break;
case CAIRO_CONTENT_ALPHA:
/* When using GL_ALPHA, compositing doesn't work properly, but for
* caching surfaces, we are just uploading pixel data, so it isn't
* an issue. */
if (for_caching)
format = GL_ALPHA;
else
format = GL_RGBA;
break;
case CAIRO_CONTENT_COLOR:
/* GL_RGB is almost what we want here -- sampling 1 alpha when
* texturing, using 1 as destination alpha factor in blending,
* etc. However, when filtering with GL_CLAMP_TO_BORDER, the
* alpha channel of the border color will also be clamped to
* 1, when we actually want the border color we explicitly
* specified. So, we have to store RGBA, and fill the alpha
* channel with 1 when blending.
*/
format = GL_RGBA;
break;
}
 
glTexImage2D (ctx->tex_target, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, NULL);
 
return &surface->base;
}
 
cairo_surface_t *
_cairo_gl_surface_create_scratch (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height)
{
return _create_scratch_internal (ctx, content, width, height, FALSE);
}
 
cairo_surface_t *
_cairo_gl_surface_create_scratch_for_caching (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height)
{
return _create_scratch_internal (ctx, content, width, height, TRUE);
}
 
static cairo_status_t
_cairo_gl_surface_clear (cairo_gl_surface_t *surface,
const cairo_color_t *color)
{
cairo_gl_context_t *ctx;
cairo_status_t status;
double r, g, b, a;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
 
_cairo_gl_context_set_destination (ctx, surface, surface->msaa_active);
if (surface->base.content & CAIRO_CONTENT_COLOR) {
r = color->red * color->alpha;
g = color->green * color->alpha;
b = color->blue * color->alpha;
} else {
r = g = b = 0;
}
if (surface->base.content & CAIRO_CONTENT_ALPHA) {
a = color->alpha;
} else {
a = 1.0;
}
 
glDisable (GL_SCISSOR_TEST);
glClearColor (r, g, b, a);
glClear (GL_COLOR_BUFFER_BIT);
 
if (a == 0)
surface->base.is_clear = TRUE;
 
return _cairo_gl_context_release (ctx, status);
}
 
static cairo_surface_t *
_cairo_gl_surface_create_and_clear_scratch (cairo_gl_context_t *ctx,
cairo_content_t content,
int width,
int height)
{
cairo_gl_surface_t *surface;
cairo_int_status_t status;
 
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch (ctx, content, width, height);
if (unlikely (surface->base.status))
return &surface->base;
 
/* Cairo surfaces start out initialized to transparent (black) */
status = _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
 
return &surface->base;
}
 
cairo_surface_t *
cairo_gl_surface_create (cairo_device_t *abstract_device,
cairo_content_t content,
int width,
int height)
{
cairo_gl_context_t *ctx;
cairo_gl_surface_t *surface;
cairo_status_t status;
 
if (! CAIRO_CONTENT_VALID (content))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
 
if (abstract_device == NULL)
return _cairo_image_surface_create_with_content (content, width, height);
 
if (abstract_device->status)
return _cairo_surface_create_in_error (abstract_device->status);
 
if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
status = _cairo_gl_context_acquire (abstract_device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (! _cairo_gl_surface_size_valid_for_context (ctx, width, height)) {
status = _cairo_gl_context_release (ctx, status);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
}
 
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);
if (unlikely (surface->base.status)) {
status = _cairo_gl_context_release (ctx, surface->base.status);
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
 
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
 
return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create);
 
/**
* cairo_gl_surface_create_for_texture:
* @content: type of content in the surface
* @tex: name of texture to use for storage of surface pixels
* @width: width of the surface, in pixels
* @height: height of the surface, in pixels
*
* Creates a GL surface for the specified texture with the specified
* content and dimensions. The texture must be kept around until the
* #cairo_surface_t is destroyed or cairo_surface_finish() is called
* on the surface. The initial contents of @tex will be used as the
* initial image contents; you must explicitly clear the buffer,
* using, for example, cairo_rectangle() and cairo_fill() if you want
* it cleared. The format of @tex should be compatible with @content,
* in the sense that it must have the color components required by
* @content.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: TBD
**/
cairo_surface_t *
cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device,
cairo_content_t content,
unsigned int tex,
int width,
int height)
{
cairo_gl_context_t *ctx;
cairo_gl_surface_t *surface;
cairo_status_t status;
 
if (! CAIRO_CONTENT_VALID (content))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
 
if (abstract_device == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
 
if (abstract_device->status)
return _cairo_surface_create_in_error (abstract_device->status);
 
if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH));
 
status = _cairo_gl_context_acquire (abstract_device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
surface = (cairo_gl_surface_t *)
_cairo_gl_surface_create_scratch_for_texture (ctx, content,
tex, width, height);
status = _cairo_gl_context_release (ctx, status);
 
return &surface->base;
}
slim_hidden_def (cairo_gl_surface_create_for_texture);
 
 
void
cairo_gl_surface_set_size (cairo_surface_t *abstract_surface,
int width,
int height)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (! _cairo_surface_is_gl (abstract_surface) ||
_cairo_gl_surface_is_texture (surface)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (surface->width != width || surface->height != height) {
surface->needs_update = TRUE;
surface->width = width;
surface->height = height;
}
}
 
int
cairo_gl_surface_get_width (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
 
if (! _cairo_surface_is_gl (abstract_surface))
return 0;
 
return surface->width;
}
 
int
cairo_gl_surface_get_height (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
 
if (! _cairo_surface_is_gl (abstract_surface))
return 0;
 
return surface->height;
}
 
void
cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface)
{
cairo_gl_surface_t *surface = (cairo_gl_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (! _cairo_surface_is_gl (abstract_surface)) {
_cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
 
if (! _cairo_gl_surface_is_texture (surface)) {
cairo_gl_context_t *ctx;
cairo_status_t status;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return;
 
/* For swapping on EGL, at least, we need a valid context/target. */
_cairo_gl_context_set_destination (ctx, surface, FALSE);
/* And in any case we should flush any pending operations. */
_cairo_gl_composite_flush (ctx);
 
ctx->swap_buffers (ctx, surface);
 
status = _cairo_gl_context_release (ctx, status);
if (status)
status = _cairo_surface_set_error (abstract_surface, status);
}
}
 
static cairo_surface_t *
_cairo_gl_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_surface_t *surface = abstract_surface;
cairo_gl_context_t *ctx;
cairo_status_t status;
 
if (! _cairo_gl_surface_size_valid (abstract_surface, width, height))
return _cairo_image_surface_create_with_content (content, width, height);
 
status = _cairo_gl_context_acquire (surface->device, &ctx);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
surface = _cairo_gl_surface_create_and_clear_scratch (ctx, content, width, height);
 
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
}
 
return surface;
}
 
static cairo_int_status_t
_cairo_gl_surface_fill_alpha_channel (cairo_gl_surface_t *dst,
cairo_gl_context_t *ctx,
int x, int y,
int width, int height)
{
cairo_gl_composite_t setup;
cairo_status_t status;
 
_cairo_gl_composite_flush (ctx);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
 
status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
dst, FALSE);
if (unlikely (status))
goto CLEANUP;
 
_cairo_gl_composite_set_solid_source (&setup, CAIRO_COLOR_BLACK);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto CLEANUP;
 
_cairo_gl_context_emit_rect (ctx, x, y, x + width, y + height);
 
status = _cairo_gl_context_release (ctx, status);
 
CLEANUP:
_cairo_gl_composite_fini (&setup);
 
_cairo_gl_composite_flush (ctx);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 
return status;
}
 
cairo_status_t
_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
cairo_image_surface_t *src,
int src_x, int src_y,
int width, int height,
int dst_x, int dst_y,
cairo_bool_t force_flush)
{
GLenum internal_format, format, type;
cairo_bool_t has_alpha, needs_swap;
cairo_image_surface_t *clone = NULL;
cairo_gl_context_t *ctx;
int cpp;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
 
status = _cairo_gl_context_acquire (dst->base.device, &ctx);
if (unlikely (status))
return status;
 
if (! _cairo_gl_get_image_format_and_type (ctx->gl_flavor,
src->pixman_format,
&internal_format,
&format,
&type,
&has_alpha,
&needs_swap))
{
cairo_bool_t is_supported;
 
clone = _cairo_image_surface_coerce (src);
if (unlikely (status = clone->base.status))
goto FAIL;
 
is_supported =
_cairo_gl_get_image_format_and_type (ctx->gl_flavor,
clone->pixman_format,
&internal_format,
&format,
&type,
&has_alpha,
&needs_swap);
assert (is_supported);
assert (!needs_swap);
src = clone;
}
 
cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
 
if (force_flush) {
status = _cairo_gl_surface_flush (&dst->base, 0);
if (unlikely (status))
goto FAIL;
}
 
if (_cairo_gl_surface_is_texture (dst)) {
void *data_start = src->data + src_y * src->stride + src_x * cpp;
void *data_start_gles2 = NULL;
 
/*
* Due to GL_UNPACK_ROW_LENGTH missing in GLES2 we have to extract the
* image data ourselves in some cases. In particular, we must extract
* the pixels if:
* a. we don't want full-length lines or
* b. the row stride cannot be handled by GL itself using a 4 byte
* alignment constraint
*/
if (src->stride < 0 ||
(ctx->gl_flavor == CAIRO_GL_FLAVOR_ES &&
(src->width * cpp < src->stride - 3 ||
width != src->width)))
{
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
status = _cairo_gl_surface_extract_image_data (src, src_x, src_y,
width, height,
&data_start_gles2);
if (unlikely (status))
goto FAIL;
 
data_start = data_start_gles2;
}
else
{
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
}
 
_cairo_gl_context_activate (ctx, CAIRO_GL_TEX_TEMP);
glBindTexture (ctx->tex_target, dst->tex);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (ctx->tex_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexSubImage2D (ctx->tex_target, 0,
dst_x, dst_y, width, height,
format, type, data_start);
 
free (data_start_gles2);
 
/* If we just treated some rgb-only data as rgba, then we have to
* go back and fix up the alpha channel where we filled in this
* texture data.
*/
if (!has_alpha) {
_cairo_gl_surface_fill_alpha_channel (dst, ctx,
dst_x, dst_y,
width, height);
}
} else {
cairo_surface_t *tmp;
 
tmp = _cairo_gl_surface_create_scratch (ctx,
dst->base.content,
width, height);
if (unlikely (tmp->status))
goto FAIL;
 
status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *) tmp,
src,
src_x, src_y,
width, height,
0, 0, force_flush);
if (status == CAIRO_INT_STATUS_SUCCESS) {
cairo_surface_pattern_t tmp_pattern;
cairo_rectangle_int_t r;
cairo_clip_t *clip;
 
_cairo_pattern_init_for_surface (&tmp_pattern, tmp);
cairo_matrix_init_translate (&tmp_pattern.base.matrix,
-dst_x, -dst_y);
tmp_pattern.base.filter = CAIRO_FILTER_NEAREST;
tmp_pattern.base.extend = CAIRO_EXTEND_NONE;
 
r.x = dst_x;
r.y = dst_y;
r.width = width;
r.height = height;
clip = _cairo_clip_intersect_rectangle (NULL, &r);
status = _cairo_surface_paint (&dst->base,
CAIRO_OPERATOR_SOURCE,
&tmp_pattern.base,
clip);
_cairo_clip_destroy (clip);
_cairo_pattern_fini (&tmp_pattern.base);
}
 
cairo_surface_destroy (tmp);
}
 
FAIL:
status = _cairo_gl_context_release (ctx, status);
 
if (clone)
cairo_surface_destroy (&clone->base);
 
return status;
}
 
static int _cairo_gl_surface_flavor (cairo_gl_surface_t *surface)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
return ctx->gl_flavor;
}
 
static cairo_status_t
_cairo_gl_surface_finish (void *abstract_surface)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_status_t status;
cairo_gl_context_t *ctx;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
 
if (ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface)
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_SOURCE);
if (ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface)
_cairo_gl_context_destroy_operand (ctx, CAIRO_GL_TEX_MASK);
if (ctx->current_target == surface)
ctx->current_target = NULL;
 
if (surface->fb)
ctx->dispatch.DeleteFramebuffers (1, &surface->fb);
if (surface->depth_stencil)
ctx->dispatch.DeleteRenderbuffers (1, &surface->depth_stencil);
if (surface->owns_tex)
glDeleteTextures (1, &surface->tex);
 
if (surface->msaa_depth_stencil)
ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_depth_stencil);
 
#if CAIRO_HAS_GL_SURFACE
if (surface->msaa_fb)
ctx->dispatch.DeleteFramebuffers (1, &surface->msaa_fb);
if (surface->msaa_rb)
ctx->dispatch.DeleteRenderbuffers (1, &surface->msaa_rb);
#endif
 
_cairo_clip_destroy (surface->clip_on_stencil_buffer);
 
return _cairo_gl_context_release (ctx, status);
}
 
static cairo_image_surface_t *
_cairo_gl_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_image_surface_t *image;
cairo_gl_context_t *ctx;
GLenum format, type;
pixman_format_code_t pixman_format;
unsigned int cpp;
cairo_bool_t flipped, mesa_invert;
cairo_status_t status;
int y;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status)) {
return _cairo_image_surface_create_in_error (status);
}
 
/* Want to use a switch statement here but the compiler gets whiny. */
if (surface->base.content == CAIRO_CONTENT_COLOR_ALPHA) {
format = GL_BGRA;
pixman_format = PIXMAN_a8r8g8b8;
type = GL_UNSIGNED_INT_8_8_8_8_REV;
cpp = 4;
} else if (surface->base.content == CAIRO_CONTENT_COLOR) {
format = GL_BGRA;
pixman_format = PIXMAN_x8r8g8b8;
type = GL_UNSIGNED_INT_8_8_8_8_REV;
cpp = 4;
} else if (surface->base.content == CAIRO_CONTENT_ALPHA) {
format = GL_ALPHA;
pixman_format = PIXMAN_a8;
type = GL_UNSIGNED_BYTE;
cpp = 1;
} else {
ASSERT_NOT_REACHED;
return NULL;
}
 
if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) {
/* If only RGBA is supported, we must download data in a compatible
* format. This means that pixman will convert the data on the CPU when
* interacting with other image surfaces. For ALPHA, GLES2 does not
* support GL_PACK_ROW_LENGTH anyway, and this makes sure that the
* pixman image that is created has row_stride = row_width * bpp. */
if (surface->base.content == CAIRO_CONTENT_ALPHA || !ctx->can_read_bgra) {
cairo_bool_t little_endian = _cairo_is_little_endian ();
format = GL_RGBA;
 
if (surface->base.content == CAIRO_CONTENT_COLOR) {
pixman_format = little_endian ?
PIXMAN_x8b8g8r8 : PIXMAN_r8g8b8x8;
} else {
pixman_format = little_endian ?
PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8;
}
}
 
/* GLES2 only supports GL_UNSIGNED_BYTE. */
type = GL_UNSIGNED_BYTE;
cpp = 4;
}
 
image = (cairo_image_surface_t*)
_cairo_image_surface_create_with_pixman_format (NULL,
pixman_format,
extents->width,
extents->height,
-1);
if (unlikely (image->base.status)) {
status = _cairo_gl_context_release (ctx, status);
return image;
}
 
cairo_surface_set_device_offset (&image->base, -extents->x, -extents->y);
 
/* If the original surface has not been modified or
* is clear, we can avoid downloading data. */
if (surface->base.is_clear || surface->base.serial == 0) {
status = _cairo_gl_context_release (ctx, status);
return image;
}
 
/* This is inefficient, as we'd rather just read the thing without making
* it the destination. But then, this is the fallback path, so let's not
* fall back instead.
*/
_cairo_gl_composite_flush (ctx);
_cairo_gl_context_set_destination (ctx, surface, FALSE);
 
flipped = ! _cairo_gl_surface_is_texture (surface);
mesa_invert = flipped && ctx->has_mesa_pack_invert;
 
glPixelStorei (GL_PACK_ALIGNMENT, 4);
if (ctx->gl_flavor == CAIRO_GL_FLAVOR_DESKTOP)
glPixelStorei (GL_PACK_ROW_LENGTH, image->stride / cpp);
if (mesa_invert)
glPixelStorei (GL_PACK_INVERT_MESA, 1);
 
y = extents->y;
if (flipped)
y = surface->height - extents->y - extents->height;
 
glReadPixels (extents->x, y,
extents->width, extents->height,
format, type, image->data);
if (mesa_invert)
glPixelStorei (GL_PACK_INVERT_MESA, 0);
 
status = _cairo_gl_context_release (ctx, status);
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return _cairo_image_surface_create_in_error (status);
}
 
/* We must invert the image manualy if we lack GL_MESA_pack_invert */
if (flipped && ! mesa_invert) {
uint8_t stack[1024], *row = stack;
uint8_t *top = image->data;
uint8_t *bot = image->data + (image->height-1)*image->stride;
 
if (image->stride > (int)sizeof(stack)) {
row = malloc (image->stride);
if (unlikely (row == NULL)) {
cairo_surface_destroy (&image->base);
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
}
 
while (top < bot) {
memcpy (row, top, image->stride);
memcpy (top, bot, image->stride);
memcpy (bot, row, image->stride);
top += image->stride;
bot -= image->stride;
}
 
if (row != stack)
free(row);
}
 
image->base.is_clear = FALSE;
return image;
}
 
static cairo_surface_t *
_cairo_gl_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_gl_surface_t *surface = abstract_surface;
 
if (extents) {
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
}
 
return &surface->base;
}
 
static cairo_status_t
_cairo_gl_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_rectangle_int_t extents;
 
*image_extra = NULL;
 
extents.x = extents.y = 0;
extents.width = surface->width;
extents.height = surface->height;
 
*image_out = (cairo_image_surface_t *)
_cairo_gl_surface_map_to_image (surface, &extents);
return (*image_out)->base.status;
}
 
static void
_cairo_gl_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
}
 
static cairo_int_status_t
_cairo_gl_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_int_status_t status;
 
status = _cairo_gl_surface_draw_image (abstract_surface, image,
0, 0,
image->width, image->height,
image->base.device_transform_inverse.x0,
image->base.device_transform_inverse.y0,
TRUE);
 
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return status;
}
 
static cairo_bool_t
_cairo_gl_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_gl_surface_t *surface = abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
 
return TRUE;
}
 
static cairo_status_t
_cairo_gl_surface_flush (void *abstract_surface, unsigned flags)
{
cairo_gl_surface_t *surface = abstract_surface;
cairo_status_t status;
cairo_gl_context_t *ctx;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
 
if ((ctx->operands[CAIRO_GL_TEX_SOURCE].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_SOURCE].texture.surface == surface) ||
(ctx->operands[CAIRO_GL_TEX_MASK].type == CAIRO_GL_OPERAND_TEXTURE &&
ctx->operands[CAIRO_GL_TEX_MASK].texture.surface == surface) ||
(ctx->current_target == surface))
_cairo_gl_composite_flush (ctx);
 
status = _cairo_gl_surface_resolve_multisampling (surface);
 
return _cairo_gl_context_release (ctx, status);
}
 
cairo_int_status_t
_cairo_gl_surface_resolve_multisampling (cairo_gl_surface_t *surface)
{
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
if (! surface->msaa_active)
return CAIRO_INT_STATUS_SUCCESS;
 
if (surface->base.device == NULL)
return CAIRO_INT_STATUS_SUCCESS;
 
/* GLES surfaces do not need explicit resolution. */
if (((cairo_gl_context_t *) surface->base.device)->gl_flavor == CAIRO_GL_FLAVOR_ES)
return CAIRO_INT_STATUS_SUCCESS;
 
if (! _cairo_gl_surface_is_texture (surface))
return CAIRO_INT_STATUS_SUCCESS;
 
status = _cairo_gl_context_acquire (surface->base.device, &ctx);
if (unlikely (status))
return status;
 
ctx->current_target = surface;
 
#if CAIRO_HAS_GL_SURFACE
_cairo_gl_context_bind_framebuffer (ctx, surface, FALSE);
#endif
 
status = _cairo_gl_context_release (ctx, status);
return status;
}
 
static const cairo_compositor_t *
get_compositor (cairo_gl_surface_t *surface)
{
cairo_gl_context_t *ctx = (cairo_gl_context_t *)surface->base.device;
return ctx->compositor;
}
 
static cairo_int_status_t
_cairo_gl_surface_paint (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
/* simplify the common case of clearing the surface */
if (clip == NULL) {
if (op == CAIRO_OPERATOR_CLEAR)
return _cairo_gl_surface_clear (surface, CAIRO_COLOR_TRANSPARENT);
else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
(op == CAIRO_OPERATOR_SOURCE ||
(op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque_solid (source)))) {
return _cairo_gl_surface_clear (surface,
&((cairo_solid_pattern_t *) source)->color);
}
}
 
return _cairo_compositor_paint (get_compositor (surface), surface,
op, source, clip);
}
 
static cairo_int_status_t
_cairo_gl_surface_mask (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
return _cairo_compositor_mask (get_compositor (surface), surface,
op, source, mask, clip);
}
 
static cairo_int_status_t
_cairo_gl_surface_stroke (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
return _cairo_compositor_stroke (get_compositor (surface), surface,
op, source, path, style,
ctm, ctm_inverse, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_gl_surface_fill (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
return _cairo_compositor_fill (get_compositor (surface), surface,
op, source, path,
fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_gl_surface_glyphs (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *font,
const cairo_clip_t *clip)
{
return _cairo_compositor_glyphs (get_compositor (surface), surface,
op, source, glyphs, num_glyphs, font,
clip);
}
 
static const cairo_surface_backend_t _cairo_gl_surface_backend = {
CAIRO_SURFACE_TYPE_GL,
_cairo_gl_surface_finish,
_cairo_default_context_create,
 
_cairo_gl_surface_create_similar,
NULL, /* similar image */
_cairo_gl_surface_map_to_image,
_cairo_gl_surface_unmap_image,
 
_cairo_gl_surface_source,
_cairo_gl_surface_acquire_source_image,
_cairo_gl_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_gl_surface_get_extents,
_cairo_image_surface_get_font_options,
 
_cairo_gl_surface_flush,
NULL, /* mark_dirty_rectangle */
 
_cairo_gl_surface_paint,
_cairo_gl_surface_mask,
_cairo_gl_surface_stroke,
_cairo_gl_surface_fill,
NULL, /* fill/stroke */
_cairo_gl_surface_glyphs,
};
/programs/develop/libraries/cairo/src/cairo-gl-traps-compositor.c
0,0 → 1,556
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* Copyright © 2005,2010 Red Hat, Inc
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Benjamin Otte <otte@gnome.org>
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Eric Anholt <eric@anholt.net>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-spans-compositor-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-offset-private.h"
 
static cairo_int_status_t
acquire (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
release (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
set_clip_region (void *_surface,
cairo_region_t *region)
{
cairo_gl_surface_t *surface = _surface;
 
surface->clip_region = region;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_gl_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
int i;
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x = _cairo_fixed_integer_part (b->p1.x);
int y = _cairo_fixed_integer_part (b->p1.y);
int w = _cairo_fixed_integer_part (b->p2.x) - x;
int h = _cairo_fixed_integer_part (b->p2.y) - y;
cairo_status_t status;
 
status = _cairo_gl_surface_draw_image (dst, image,
x + dx, y + dy,
w, h,
x, y,
TRUE);
if (unlikely (status))
return status;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
emit_aligned_boxes (cairo_gl_context_t *ctx,
const cairo_boxes_t *boxes)
{
const struct _cairo_boxes_chunk *chunk;
cairo_gl_emit_rect_t emit = _cairo_gl_context_choose_emit_rect (ctx);
int i;
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
emit (ctx, x1, y1, x2, y2);
}
}
}
 
static cairo_int_status_t
fill_boxes (void *_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_solid_source (&setup, color);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
emit_aligned_boxes (ctx, boxes);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
composite_boxes (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup,
source_to_operand (abstract_src));
_cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y);
 
_cairo_gl_composite_set_mask_operand (&setup,
source_to_operand (abstract_mask));
_cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
emit_aligned_boxes (ctx, boxes);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
composite (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup,
source_to_operand (abstract_src));
_cairo_gl_operand_translate (&setup.src, dst_x-src_x, dst_y-src_y);
 
_cairo_gl_composite_set_mask_operand (&setup,
source_to_operand (abstract_mask));
_cairo_gl_operand_translate (&setup.mask, dst_x-mask_x, dst_y-mask_y);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
/* XXX clip */
_cairo_gl_context_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_int_status_t
lerp (void *dst,
cairo_surface_t *src,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_int_status_t status;
 
/* we could avoid some repetition... */
status = composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
mask_x, mask_y,
0, 0,
dst_x, dst_y,
width, height);
if (unlikely (status))
return status;
 
status = composite (dst, CAIRO_OPERATOR_ADD, src, mask,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
traps_to_operand (void *_dst,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps,
cairo_gl_operand_t *operand,
int dst_x, int dst_y)
{
pixman_format_code_t pixman_format;
pixman_image_t *pixman_image;
cairo_surface_t *image, *mask;
cairo_surface_pattern_t pattern;
cairo_status_t status;
 
pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1;
pixman_image = pixman_image_create_bits (pixman_format,
extents->width,
extents->height,
NULL, 0);
if (unlikely (pixman_image == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_pixman_image_add_traps (pixman_image, extents->x, extents->y, traps);
image = _cairo_image_surface_create_for_pixman_image (pixman_image,
pixman_format);
if (unlikely (image->status)) {
pixman_image_unref (pixman_image);
return image->status;
}
 
/* GLES2 only supports RGB/RGBA when uploading */
if (_cairo_gl_get_flavor () == CAIRO_GL_FLAVOR_ES) {
cairo_surface_pattern_t pattern;
cairo_surface_t *rgba_image;
 
/* XXX perform this fixup inside _cairo_gl_draw_image() */
 
rgba_image =
_cairo_image_surface_create_with_pixman_format (NULL,
_cairo_is_little_endian () ? PIXMAN_a8b8g8r8 : PIXMAN_r8g8b8a8,
extents->width,
extents->height,
0);
if (unlikely (rgba_image->status))
return rgba_image->status;
 
_cairo_pattern_init_for_surface (&pattern, image);
status = _cairo_surface_paint (rgba_image, CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL);
_cairo_pattern_fini (&pattern.base);
 
cairo_surface_destroy (image);
image = rgba_image;
 
if (unlikely (status)) {
cairo_surface_destroy (image);
return status;
}
}
 
mask = _cairo_surface_create_similar_scratch (_dst,
CAIRO_CONTENT_COLOR_ALPHA,
extents->width,
extents->height);
if (unlikely (mask->status)) {
cairo_surface_destroy (image);
return mask->status;
}
 
status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
(cairo_image_surface_t *)image,
0, 0,
extents->width, extents->height,
0, 0,
TRUE);
cairo_surface_destroy (image);
 
if (unlikely (status))
goto error;
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->x+dst_x, -extents->y+dst_y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
status = _cairo_gl_operand_init (operand, &pattern.base, _dst,
&_cairo_unbounded_rectangle,
&_cairo_unbounded_rectangle,
FALSE);
_cairo_pattern_fini (&pattern.base);
 
if (unlikely (status))
goto error;
 
operand->texture.owns_surface = (cairo_gl_surface_t *)mask;
return CAIRO_STATUS_SUCCESS;
 
error:
cairo_surface_destroy (mask);
return status;
}
 
static cairo_int_status_t
composite_traps (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_int_status_t status;
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup,
source_to_operand (abstract_src));
_cairo_gl_operand_translate (&setup.src, -src_x-dst_x, -src_y-dst_y);
status = traps_to_operand (_dst, extents, antialias, traps, &setup.mask, dst_x, dst_y);
if (unlikely (status))
goto FAIL;
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
/* XXX clip */
_cairo_gl_context_emit_rect (ctx,
extents->x-dst_x, extents->y-dst_y,
extents->x-dst_x+extents->width,
extents->y-dst_y+extents->height);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
return status;
}
 
static cairo_gl_surface_t *
tristrip_to_surface (void *_dst,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *strip)
{
pixman_format_code_t pixman_format;
pixman_image_t *pixman_image;
cairo_surface_t *image, *mask;
cairo_status_t status;
 
pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1,
pixman_image = pixman_image_create_bits (pixman_format,
extents->width,
extents->height,
NULL, 0);
if (unlikely (pixman_image == NULL))
return (cairo_gl_surface_t *)_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_pixman_image_add_tristrip (pixman_image, extents->x, extents->y, strip);
image = _cairo_image_surface_create_for_pixman_image (pixman_image,
pixman_format);
if (unlikely (image->status)) {
pixman_image_unref (pixman_image);
return (cairo_gl_surface_t *)image;
}
 
mask = _cairo_surface_create_similar_scratch (_dst,
CAIRO_CONTENT_COLOR_ALPHA,
extents->width,
extents->height);
if (unlikely (mask->status)) {
cairo_surface_destroy (image);
return (cairo_gl_surface_t *)mask;
}
 
status = _cairo_gl_surface_draw_image ((cairo_gl_surface_t *)mask,
(cairo_image_surface_t *)image,
0, 0,
extents->width, extents->height,
0, 0,
TRUE);
cairo_surface_destroy (image);
if (unlikely (status)) {
cairo_surface_destroy (mask);
return (cairo_gl_surface_t*)_cairo_surface_create_in_error (status);
}
 
return (cairo_gl_surface_t*)mask;
}
 
static cairo_int_status_t
composite_tristrip (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *strip)
{
cairo_gl_composite_t setup;
cairo_gl_context_t *ctx;
cairo_gl_surface_t *mask;
cairo_int_status_t status;
 
mask = tristrip_to_surface (_dst, extents, antialias, strip);
if (unlikely (mask->base.status))
return mask->base.status;
 
status = _cairo_gl_composite_init (&setup, op, _dst, FALSE);
if (unlikely (status))
goto FAIL;
 
_cairo_gl_composite_set_source_operand (&setup,
source_to_operand (abstract_src));
 
//_cairo_gl_composite_set_mask_surface (&setup, mask, 0, 0);
 
status = _cairo_gl_composite_begin (&setup, &ctx);
if (unlikely (status))
goto FAIL;
 
/* XXX clip */
_cairo_gl_context_emit_rect (ctx,
dst_x, dst_y,
dst_x+extents->width,
dst_y+extents->height);
status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS);
 
FAIL:
_cairo_gl_composite_fini (&setup);
cairo_surface_destroy (&mask->base);
return status;
}
 
static cairo_int_status_t
check_composite (const cairo_composite_rectangles_t *extents)
{
if (! _cairo_gl_operator_is_supported (extents->op))
return UNSUPPORTED ("unsupported operator");
 
return CAIRO_STATUS_SUCCESS;
}
 
const cairo_compositor_t *
_cairo_gl_traps_compositor_get (void)
{
static cairo_traps_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_traps_compositor_init (&compositor, &_cairo_fallback_compositor);
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_gl_pattern_to_source;
compositor.draw_image_boxes = draw_image_boxes;
//compositor.copy_boxes = copy_boxes;
compositor.fill_boxes = fill_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_composite_traps = check_composite_traps;
compositor.composite_traps = composite_traps;
//compositor.check_composite_tristrip = check_composite_traps;
compositor.composite_tristrip = composite_tristrip;
compositor.check_composite_glyphs = _cairo_gl_check_composite_glyphs;
compositor.composite_glyphs = _cairo_gl_composite_glyphs;
}
 
return &compositor.base;
}
/programs/develop/libraries/cairo/src/cairo-gl.h
31,12 → 31,38
* The Initial Developer of the Original Code is Eric Anholt.
*/
 
/*
* cairo-gl.h:
*
* The cairo-gl backend provides an implementation of possibly
* hardware-accelerated cairo rendering by targeting the OpenGL API.
* The goal of the cairo-gl backend is to provide better performance
* with equal functionality to cairo-image where possible. It does
* not directly provide for applying additional OpenGL effects to
* cairo surfaces.
*
* Cairo-gl allows interoperability with other GL rendering through GL
* context sharing. Cairo-gl surfaces are created in reference to a
* #cairo_device_t, which represents a GL context created by the user.
* When that GL context is created with its sharePtr set to another
* context (or vice versa), its objects (textures backing cairo-gl
* surfaces) can be accessed in the other OpenGL context. This allows
* cairo-gl to maintain its drawing state in one context while the
* user's 3D rendering occurs in the user's other context.
*
* However, as only one context can be current to a thread at a time,
* cairo-gl may make its context current to the thread on any cairo
* call which interacts with a cairo-gl surface or the cairo-gl
* device. As a result, the user must make their own context current
* between any cairo calls and their own OpenGL rendering.
**/
 
#ifndef CAIRO_GL_H
#define CAIRO_GL_H
 
#include "cairo.h"
 
#if CAIRO_HAS_GL_SURFACE
#if CAIRO_HAS_GL_SURFACE || CAIRO_HAS_GLESV2_SURFACE
 
CAIRO_BEGIN_DECLS
 
62,6 → 88,10
cairo_public void
cairo_gl_surface_swapbuffers (cairo_surface_t *surface);
 
cairo_public void
cairo_gl_device_set_thread_aware (cairo_device_t *device,
cairo_bool_t thread_aware);
 
#if CAIRO_HAS_GLX_FUNCTIONS
#include <GL/glx.h>
 
108,6 → 138,12
int width,
int height);
 
cairo_public EGLDisplay
cairo_egl_device_get_display (cairo_device_t *device);
 
cairo_public EGLSurface
cairo_egl_device_get_context (cairo_device_t *device);
 
#endif
 
CAIRO_END_DECLS
/programs/develop/libraries/cairo/src/cairo-glx-context.c
0,0 → 1,324
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-error-private.h"
 
#include <X11/Xutil.h>
 
/* XXX needs hooking into XCloseDisplay() */
 
typedef struct _cairo_glx_context {
cairo_gl_context_t base;
 
Display *display;
Window dummy_window;
GLXContext context;
 
GLXDrawable previous_drawable;
GLXContext previous_context;
 
cairo_bool_t has_multithread_makecurrent;
} cairo_glx_context_t;
 
typedef struct _cairo_glx_surface {
cairo_gl_surface_t base;
 
Window win;
} cairo_glx_surface_t;
 
static cairo_bool_t
_context_acquisition_changed_glx_state (cairo_glx_context_t *ctx,
GLXDrawable current_drawable)
{
return ctx->previous_drawable != current_drawable ||
ctx->previous_context != ctx->context;
}
 
static GLXDrawable
_glx_get_current_drawable (cairo_glx_context_t *ctx)
{
if (ctx->base.current_target == NULL ||
_cairo_gl_surface_is_texture (ctx->base.current_target)) {
return ctx->dummy_window;
}
 
return ((cairo_glx_surface_t *) ctx->base.current_target)->win;
}
 
static void
_glx_query_current_state (cairo_glx_context_t * ctx)
{
ctx->previous_drawable = glXGetCurrentDrawable ();
ctx->previous_context = glXGetCurrentContext ();
 
/* If any of the values were none, assume they are all none. Not all
drivers seem well behaved when it comes to using these values across
multiple threads. */
if (ctx->previous_drawable == None ||
ctx->previous_context == None) {
ctx->previous_drawable = None;
ctx->previous_context = None;
}
}
 
static void
_glx_acquire (void *abstract_ctx)
{
cairo_glx_context_t *ctx = abstract_ctx;
GLXDrawable current_drawable = _glx_get_current_drawable (ctx);
 
_glx_query_current_state (ctx);
if (!_context_acquisition_changed_glx_state (ctx, current_drawable))
return;
 
glXMakeCurrent (ctx->display, current_drawable, ctx->context);
}
 
static void
_glx_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
{
cairo_glx_context_t *ctx = abstract_ctx;
cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
 
/* Set the window as the target of our context. */
glXMakeCurrent (ctx->display, surface->win, ctx->context);
}
 
static void
_glx_release (void *abstract_ctx)
{
cairo_glx_context_t *ctx = abstract_ctx;
 
if (ctx->has_multithread_makecurrent || !ctx->base.thread_aware ||
!_context_acquisition_changed_glx_state (ctx,
_glx_get_current_drawable (ctx))) {
return;
}
 
glXMakeCurrent (ctx->display, None, None);
}
 
static void
_glx_swap_buffers (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
{
cairo_glx_context_t *ctx = abstract_ctx;
cairo_glx_surface_t *surface = (cairo_glx_surface_t *) abstract_surface;
 
glXSwapBuffers (ctx->display, surface->win);
}
 
static void
_glx_destroy (void *abstract_ctx)
{
cairo_glx_context_t *ctx = abstract_ctx;
 
if (ctx->dummy_window != None)
XDestroyWindow (ctx->display, ctx->dummy_window);
 
glXMakeCurrent (ctx->display, None, None);
}
 
static cairo_status_t
_glx_dummy_window (Display *dpy, GLXContext gl_ctx, Window *dummy)
{
int attr[3] = { GLX_FBCONFIG_ID, 0, None };
GLXFBConfig *config;
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win = None;
int cnt;
 
/* Create a dummy window created for the target GLX context that we can
* use to query the available GL/GLX extensions.
*/
glXQueryContext (dpy, gl_ctx, GLX_FBCONFIG_ID, &attr[1]);
 
cnt = 0;
config = glXChooseFBConfig (dpy, DefaultScreen (dpy), attr, &cnt);
if (unlikely (cnt == 0))
return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
 
vi = glXGetVisualFromFBConfig (dpy, config[0]);
XFree (config);
 
if (unlikely (vi == NULL))
return _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
 
cmap = XCreateColormap (dpy,
RootWindow (dpy, vi->screen),
vi->visual,
AllocNone);
swa.colormap = cmap;
swa.border_pixel = 0;
win = XCreateWindow (dpy, RootWindow (dpy, vi->screen),
-1, -1, 1, 1, 0,
vi->depth,
InputOutput,
vi->visual,
CWBorderPixel | CWColormap, &swa);
XFreeColormap (dpy, cmap);
XFree (vi);
 
XFlush (dpy);
if (unlikely (! glXMakeCurrent (dpy, win, gl_ctx))) {
XDestroyWindow (dpy, win);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
*dummy = win;
return CAIRO_STATUS_SUCCESS;
}
 
cairo_device_t *
cairo_glx_device_create (Display *dpy, GLXContext gl_ctx)
{
cairo_glx_context_t *ctx;
cairo_status_t status;
Window dummy = None;
const char *glx_extensions;
 
ctx = calloc (1, sizeof (cairo_glx_context_t));
if (unlikely (ctx == NULL))
return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
/* glx_dummy_window will call glXMakeCurrent, so we need to
* query the current state of the context now. */
_glx_query_current_state (ctx);
 
status = _glx_dummy_window (dpy, gl_ctx, &dummy);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
ctx->display = dpy;
ctx->dummy_window = dummy;
ctx->context = gl_ctx;
 
ctx->base.acquire = _glx_acquire;
ctx->base.release = _glx_release;
ctx->base.make_current = _glx_make_current;
ctx->base.swap_buffers = _glx_swap_buffers;
ctx->base.destroy = _glx_destroy;
 
status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
(cairo_gl_get_proc_addr_func_t) glXGetProcAddress);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
status = _cairo_gl_context_init (&ctx->base);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
glx_extensions = glXQueryExtensionsString (dpy, DefaultScreen (dpy));
if (strstr(glx_extensions, "GLX_MESA_multithread_makecurrent")) {
ctx->has_multithread_makecurrent = TRUE;
}
 
ctx->base.release (ctx);
 
return &ctx->base.base;
}
 
Display *
cairo_glx_device_get_display (cairo_device_t *device)
{
cairo_glx_context_t *ctx;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return NULL;
}
 
ctx = (cairo_glx_context_t *) device;
 
return ctx->display;
}
 
GLXContext
cairo_glx_device_get_context (cairo_device_t *device)
{
cairo_glx_context_t *ctx;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return NULL;
}
 
ctx = (cairo_glx_context_t *) device;
 
return ctx->context;
}
 
cairo_surface_t *
cairo_gl_surface_create_for_window (cairo_device_t *device,
Window win,
int width,
int height)
{
cairo_glx_surface_t *surface;
 
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
if (width <= 0 || height <= 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
surface = calloc (1, sizeof (cairo_glx_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_gl_surface_init (device, &surface->base,
CAIRO_CONTENT_COLOR_ALPHA, width, height);
surface->win = win;
 
return &surface->base.base;
}
/programs/develop/libraries/cairo/src/cairo-gstate-private.h
41,6 → 41,7
struct _cairo_gstate {
cairo_operator_t op;
 
double opacity;
double tolerance;
cairo_antialias_t antialias;
 
54,7 → 55,7
cairo_matrix_t font_matrix;
cairo_font_options_t font_options;
 
cairo_clip_t clip;
cairo_clip_t *clip;
 
cairo_surface_t *target; /* The target to which all rendering is directed */
cairo_surface_t *parent_target; /* The previous target which was receiving rendering */
88,7 → 89,7
_cairo_gstate_restore (cairo_gstate_t **gstate, cairo_gstate_t **freelist);
 
cairo_private cairo_bool_t
_cairo_gstate_is_redirected (cairo_gstate_t *gstate);
_cairo_gstate_is_group (cairo_gstate_t *gstate);
 
cairo_private cairo_status_t
_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child);
97,9 → 98,6
_cairo_gstate_get_target (cairo_gstate_t *gstate);
 
cairo_private cairo_surface_t *
_cairo_gstate_get_parent_target (cairo_gstate_t *gstate);
 
cairo_private cairo_surface_t *
_cairo_gstate_get_original_target (cairo_gstate_t *gstate);
 
cairo_private cairo_clip_t *
118,6 → 116,12
_cairo_gstate_get_operator (cairo_gstate_t *gstate);
 
cairo_private cairo_status_t
_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double opacity);
 
cairo_private double
_cairo_gstate_get_opacity (cairo_gstate_t *gstate);
 
cairo_private cairo_status_t
_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance);
 
cairo_private double
205,6 → 209,16
}
 
cairo_private void
_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y);
 
static inline void
_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y)
{
if (! gstate->is_identity)
_do_cairo_gstate_user_to_backend_distance (gstate, x, y);
}
 
cairo_private void
_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y);
 
static inline void
215,6 → 229,16
}
 
cairo_private void
_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y);
 
static inline void
_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y)
{
if (! gstate->is_identity)
_do_cairo_gstate_backend_to_user_distance (gstate, x, y);
}
 
cairo_private void
_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
double *x1, double *y1,
double *x2, double *y2,
300,12 → 324,6
double height);
 
cairo_private cairo_status_t
_cairo_gstate_select_font_face (cairo_gstate_t *gstate,
const char *family,
cairo_font_slant_t slant,
cairo_font_weight_t weight);
 
cairo_private cairo_status_t
_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
double size);
 
342,18 → 360,6
cairo_font_face_t *font_face);
 
cairo_private cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_text_cluster_flags_t *cluster_flags);
 
cairo_private cairo_status_t
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate,
const cairo_glyph_t *glyphs,
int num_glyphs,
361,13 → 367,9
 
cairo_private cairo_status_t
_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
const char *utf8,
int utf8_len,
const cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags);
cairo_glyph_text_info_t *info);
 
cairo_private cairo_status_t
_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
/programs/develop/libraries/cairo/src/cairo-gstate.c
37,9 → 37,13
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
#include "cairo-gstate-private.h"
#include "cairo-pattern-private.h"
#include "cairo-traps-private.h"
 
#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
#define ISFINITE(x) isfinite (x)
59,7 → 63,7
static void
_cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate);
 
static cairo_status_t
static void
_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate,
const cairo_glyph_t *glyphs,
int num_glyphs,
86,13 → 90,12
_cairo_gstate_init (cairo_gstate_t *gstate,
cairo_surface_t *target)
{
cairo_status_t status;
 
VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
 
gstate->next = NULL;
 
gstate->op = CAIRO_GSTATE_OPERATOR_DEFAULT;
gstate->opacity = 1.;
 
gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
gstate->antialias = CAIRO_ANTIALIAS_DEFAULT;
111,7 → 114,7
 
_cairo_font_options_init_default (&gstate->font_options);
 
_cairo_clip_init (&gstate->clip);
gstate->clip = NULL;
 
gstate->target = cairo_surface_reference (target);
gstate->parent_target = NULL;
131,15 → 134,7
/* Now that the gstate is fully initialized and ready for the eventual
* _cairo_gstate_fini(), we can check for errors (and not worry about
* the resource deallocation). */
status = target->status;
if (unlikely (status))
return status;
 
status = gstate->source->status;
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
return target->status;
}
 
/**
157,6 → 152,7
VG (VALGRIND_MAKE_MEM_UNDEFINED (gstate, sizeof (cairo_gstate_t)));
 
gstate->op = other->op;
gstate->opacity = other->opacity;
 
gstate->tolerance = other->tolerance;
gstate->antialias = other->antialias;
176,7 → 172,7
 
_cairo_font_options_init_copy (&gstate->font_options , &other->font_options);
 
_cairo_clip_init_copy (&gstate->clip, &other->clip);
gstate->clip = _cairo_clip_copy (other->clip);
 
gstate->target = cairo_surface_reference (other->target);
/* parent_target is always set to NULL; it's only ever set by redirect_target */
213,7 → 209,7
cairo_scaled_font_destroy (gstate->scaled_font);
gstate->scaled_font = NULL;
 
_cairo_clip_reset (&gstate->clip);
_cairo_clip_destroy (gstate->clip);
 
cairo_list_del (&gstate->device_transform_observer.link);
 
303,16 → 299,10
* Redirect @gstate rendering to a "child" target. The original
* "parent" target with which the gstate was created will not be
* affected. See _cairo_gstate_get_target().
*
* Unless the redirected target has the same device offsets as the
* original #cairo_t target, the clip will be INVALID after this call,
* and the caller should either recreate or reset the clip.
**/
cairo_status_t
_cairo_gstate_redirect_target (cairo_gstate_t *gstate, cairo_surface_t *child)
{
cairo_matrix_t matrix;
 
/* If this gstate is already redirected, this is an error; we need a
* new gstate to be able to redirect */
assert (gstate->parent_target == NULL);
320,7 → 310,6
/* Set up our new parent_target based on our current target;
* gstate->parent_target will take the ref that is held by gstate->target
*/
cairo_surface_destroy (gstate->parent_target);
gstate->parent_target = gstate->target;
 
/* Now set up our new target; we overwrite gstate->target directly,
332,28 → 321,28
 
/* The clip is in surface backend coordinates for the previous target;
* translate it into the child's backend coordinates. */
cairo_matrix_init_translate (&matrix,
_cairo_clip_destroy (gstate->clip);
gstate->clip = _cairo_clip_copy_with_translation (gstate->next->clip,
child->device_transform.x0 - gstate->parent_target->device_transform.x0,
child->device_transform.y0 - gstate->parent_target->device_transform.y0);
_cairo_clip_reset (&gstate->clip);
return _cairo_clip_init_copy_transformed (&gstate->clip,
&gstate->next->clip,
&matrix);
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_gstate_is_redirected
* _cairo_gstate_is_group:
* @gstate: a #cairo_gstate_t
*
* This space left intentionally blank.
* Check if _cairo_gstate_redirect_target has been called on the head
* of the stack.
*
* Return value: %TRUE if the gstate is redirected to a target
* different than the original, %FALSE otherwise.
* Return value: %TRUE if @gstate is redirected to a target different
* than the previous state in the stack, %FALSE otherwise.
**/
cairo_bool_t
_cairo_gstate_is_redirected (cairo_gstate_t *gstate)
_cairo_gstate_is_group (cairo_gstate_t *gstate)
{
return (gstate->target != gstate->original_target);
return gstate->parent_target != NULL;
}
 
/**
372,19 → 361,6
}
 
/**
* _cairo_gstate_get_parent_target:
* @gstate: a #cairo_gstate_t
*
* Return the parent surface of the current drawing target surface;
* if this particular gstate isn't a redirect gstate, this will return %NULL.
**/
cairo_surface_t *
_cairo_gstate_get_parent_target (cairo_gstate_t *gstate)
{
return gstate->parent_target;
}
 
/**
* _cairo_gstate_get_original_target:
* @gstate: a #cairo_gstate_t
*
408,11 → 384,11
* This space left intentionally blank.
*
* Return value: a pointer to the gstate's #cairo_clip_t structure.
*/
**/
cairo_clip_t *
_cairo_gstate_get_clip (cairo_gstate_t *gstate)
{
return &gstate->clip;
return gstate->clip;
}
 
cairo_status_t
456,6 → 432,20
}
 
cairo_status_t
_cairo_gstate_set_opacity (cairo_gstate_t *gstate, double op)
{
gstate->opacity = op;
 
return CAIRO_STATUS_SUCCESS;
}
 
double
_cairo_gstate_get_opacity (cairo_gstate_t *gstate)
{
return gstate->opacity;
}
 
cairo_status_t
_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance)
{
gstate->tolerance = tolerance;
528,10 → 518,9
cairo_status_t
_cairo_gstate_set_dash (cairo_gstate_t *gstate, const double *dash, int num_dashes, double offset)
{
unsigned int i;
double dash_total;
double dash_total, on_total, off_total;
int i, j;
 
if (gstate->stroke_style.dash)
free (gstate->stroke_style.dash);
 
gstate->stroke_style.num_dashes = num_dashes;
548,15 → 537,28
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
memcpy (gstate->stroke_style.dash, dash, gstate->stroke_style.num_dashes * sizeof (double));
on_total = off_total = dash_total = 0.0;
for (i = j = 0; i < num_dashes; i++) {
if (dash[i] < 0)
return _cairo_error (CAIRO_STATUS_INVALID_DASH);
 
dash_total = 0.0;
for (i = 0; i < gstate->stroke_style.num_dashes; i++) {
if (gstate->stroke_style.dash[i] < 0)
if (dash[i] == 0 && i > 0 && i < num_dashes - 1) {
if (dash[++i] < 0)
return _cairo_error (CAIRO_STATUS_INVALID_DASH);
 
dash_total += gstate->stroke_style.dash[i];
gstate->stroke_style.dash[j-1] += dash[i];
gstate->stroke_style.num_dashes -= 2;
} else
gstate->stroke_style.dash[j++] = dash[i];
 
if (dash[i]) {
dash_total += dash[i];
if ((i & 1) == 0)
on_total += dash[i];
else
off_total += dash[i];
}
}
 
if (dash_total == 0.0)
return _cairo_error (CAIRO_STATUS_INVALID_DASH);
563,9 → 565,20
 
/* An odd dash value indicate symmetric repeating, so the total
* is twice as long. */
if (gstate->stroke_style.num_dashes & 1)
if (gstate->stroke_style.num_dashes & 1) {
dash_total *= 2;
on_total += off_total;
}
 
if (dash_total - on_total < CAIRO_FIXED_ERROR_DOUBLE) {
/* Degenerate dash -> solid line */
free (gstate->stroke_style.dash);
gstate->stroke_style.dash = NULL;
gstate->stroke_style.num_dashes = 0;
gstate->stroke_style.dash_offset = 0.0;
return CAIRO_STATUS_SUCCESS;
}
 
/* The dashing code doesn't like a negative offset or a big positive
* offset, so we compute an equivalent offset which is guaranteed to be
* positive and less than twice the pattern length. */
801,6 → 814,13
}
 
void
_do_cairo_gstate_user_to_backend_distance (cairo_gstate_t *gstate, double *x, double *y)
{
cairo_matrix_transform_distance (&gstate->ctm, x, y);
cairo_matrix_transform_distance (&gstate->target->device_transform, x, y);
}
 
void
_do_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
{
cairo_matrix_transform_point (&gstate->target->device_transform_inverse, x, y);
808,6 → 828,13
}
 
void
_do_cairo_gstate_backend_to_user_distance (cairo_gstate_t *gstate, double *x, double *y)
{
cairo_matrix_transform_distance (&gstate->target->device_transform_inverse, x, y);
cairo_matrix_transform_distance (&gstate->ctm_inverse, x, y);
}
 
void
_cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate,
double *x1, double *y1,
double *x2, double *y2,
815,6 → 842,9
{
cairo_matrix_t matrix_inverse;
 
if (! _cairo_matrix_is_identity (&gstate->target->device_transform_inverse) ||
! _cairo_matrix_is_identity (&gstate->ctm_inverse))
{
cairo_matrix_multiply (&matrix_inverse,
&gstate->target->device_transform_inverse,
&gstate->ctm_inverse);
822,6 → 852,13
x1, y1, x2, y2, is_tight);
}
 
else
{
if (is_tight)
*is_tight = TRUE;
}
}
 
/* XXX: NYI
cairo_status_t
_cairo_gstate_stroke_to_path (cairo_gstate_t *gstate)
948,39 → 985,6
&gstate->ctm_inverse);
}
 
/* We need to take a copy of the clip so that the lower layers may modify it
* by, perhaps, intersecting it with the operation extents and other paths.
*/
#define _gstate_get_clip(G, C) _cairo_clip_init_copy ((C), &(G)->clip)
 
static cairo_bool_t
_clipped (cairo_gstate_t *gstate)
{
cairo_rectangle_int_t extents;
 
if (gstate->clip.all_clipped)
return TRUE;
 
/* XXX consider applying a surface clip? */
 
if (gstate->clip.path == NULL)
return FALSE;
 
if (_cairo_surface_get_extents (gstate->target, &extents)) {
if (extents.width == 0 || extents.height == 0)
return TRUE;
 
if (! _cairo_rectangle_intersect (&extents,
&gstate->clip.path->extents))
{
return TRUE;
}
}
 
/* perform a simple query to exclude trivial all-clipped cases */
return _cairo_clip_get_region (&gstate->clip, NULL) == CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_operator_t
_reduce_op (cairo_gstate_t *gstate)
{
1020,22 → 1024,36
return op;
}
 
static cairo_status_t
_cairo_gstate_get_pattern_status (const cairo_pattern_t *pattern)
{
if (unlikely (pattern->type == CAIRO_PATTERN_TYPE_MESH &&
((const cairo_mesh_pattern_t *) pattern)->current_patch))
{
/* If current patch != NULL, the pattern is under construction
* and cannot be used as a source */
return CAIRO_STATUS_INVALID_MESH_CONSTRUCTION;
}
 
return pattern->status;
}
 
cairo_status_t
_cairo_gstate_paint (cairo_gstate_t *gstate)
{
cairo_pattern_union_t source_pattern;
const cairo_pattern_t *pattern;
cairo_clip_t clip;
cairo_status_t status;
cairo_operator_t op;
 
if (unlikely (gstate->source->status))
return gstate->source->status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
 
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (_clipped (gstate))
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
 
op = _reduce_op (gstate);
1046,12 → 1064,9
pattern = &source_pattern.base;
}
 
status = _cairo_surface_paint (gstate->target,
return _cairo_surface_paint (gstate->target,
op, pattern,
_gstate_get_clip (gstate, &clip));
_cairo_clip_fini (&clip);
 
return status;
gstate->clip);
}
 
cairo_status_t
1061,21 → 1076,24
cairo_pattern_union_t source_pattern, mask_pattern;
const cairo_pattern_t *source;
cairo_operator_t op;
cairo_clip_t clip;
cairo_status_t status;
 
if (unlikely (mask->status))
return mask->status;
status = _cairo_gstate_get_pattern_status (mask);
if (unlikely (status))
return status;
 
if (unlikely (gstate->source->status))
return gstate->source->status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
 
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (_clipped (gstate))
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
 
assert (gstate->opacity == 1.0);
 
if (_cairo_pattern_is_opaque (mask, NULL))
return _cairo_gstate_paint (gstate);
 
1095,7 → 1113,7
_cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask);
 
if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID &&
mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
_cairo_operator_bounded_by_source (op))
{
const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1117,7 → 1135,7
 
status = _cairo_surface_paint (gstate->target, op,
&source_pattern.base,
_gstate_get_clip (gstate, &clip));
gstate->clip);
}
else
{
1124,9 → 1142,8
status = _cairo_surface_mask (gstate->target, op,
source,
&mask_pattern.base,
_gstate_get_clip (gstate, &clip));
gstate->clip);
}
_cairo_clip_fini (&clip);
 
return status;
}
1137,11 → 1154,11
cairo_pattern_union_t source_pattern;
cairo_stroke_style_t style;
double dash[2];
cairo_clip_t clip;
cairo_status_t status;
 
if (unlikely (gstate->source->status))
return gstate->source->status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
 
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
1149,9 → 1166,11
if (gstate->stroke_style.line_width <= 0.0)
return CAIRO_STATUS_SUCCESS;
 
if (_clipped (gstate))
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
 
assert (gstate->opacity == 1.0);
 
memcpy (&style, &gstate->stroke_style, sizeof (gstate->stroke_style));
if (_cairo_stroke_style_dash_can_approximate (&gstate->stroke_style, &gstate->ctm, gstate->tolerance)) {
style.dash = dash;
1163,7 → 1182,7
 
_cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
 
status = _cairo_surface_stroke (gstate->target,
return _cairo_surface_stroke (gstate->target,
gstate->op,
&source_pattern.base,
path,
1172,10 → 1191,7
&gstate->ctm_inverse,
gstate->tolerance,
gstate->antialias,
_gstate_get_clip (gstate, &clip));
_cairo_clip_fini (&clip);
 
return status;
gstate->clip);
}
 
cairo_status_t
1219,7 → 1235,7
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, &limit, 1);
 
status = _cairo_path_fixed_stroke_to_traps (path,
status = _cairo_path_fixed_stroke_polygon_to_traps (path,
&gstate->stroke_style,
&gstate->ctm,
&gstate->ctm_inverse,
1239,18 → 1255,20
cairo_status_t
_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
cairo_clip_t clip;
cairo_status_t status;
 
if (unlikely (gstate->source->status))
return gstate->source->status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
 
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (_clipped (gstate))
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
 
assert (gstate->opacity == 1.0);
 
if (_cairo_path_fixed_fill_is_empty (path)) {
if (_cairo_operator_bounded_by_mask (gstate->op))
return CAIRO_STATUS_SUCCESS;
1258,7 → 1276,7
status = _cairo_surface_paint (gstate->target,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
_gstate_get_clip (gstate, &clip));
gstate->clip);
} else {
cairo_pattern_union_t source_pattern;
const cairo_pattern_t *pattern;
1283,7 → 1301,7
box.p2.y >= _cairo_fixed_from_int (extents.y + extents.height))
{
status = _cairo_surface_paint (gstate->target, op, pattern,
_gstate_get_clip (gstate, &clip));
gstate->clip);
}
else
{
1292,12 → 1310,10
gstate->fill_rule,
gstate->tolerance,
gstate->antialias,
_gstate_get_clip (gstate, &clip));
gstate->clip);
}
}
 
_cairo_clip_fini (&clip);
 
return status;
}
 
1320,25 → 1336,41
double x,
double y)
{
cairo_clip_path_t *clip_path;
cairo_clip_t *clip = gstate->clip;
int i;
 
if (gstate->clip.all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return FALSE;
 
clip_path = gstate->clip.path;
if (clip_path == NULL)
if (clip == NULL)
return TRUE;
 
_cairo_gstate_user_to_backend (gstate, &x, &y);
 
if (x < clip_path->extents.x ||
x >= clip_path->extents.x + clip_path->extents.width ||
y < clip_path->extents.y ||
y >= clip_path->extents.y + clip_path->extents.height)
if (x < clip->extents.x ||
x >= clip->extents.x + clip->extents.width ||
y < clip->extents.y ||
y >= clip->extents.y + clip->extents.height)
{
return FALSE;
}
 
if (clip->num_boxes) {
int fx, fy;
 
fx = _cairo_fixed_from_double (x);
fy = _cairo_fixed_from_double (y);
for (i = 0; i < clip->num_boxes; i++) {
if (fx >= clip->boxes[i].p1.x && fx <= clip->boxes[i].p2.x &&
fy >= clip->boxes[i].p1.y && fy <= clip->boxes[i].p2.y)
break;
}
if (i == clip->num_boxes)
return FALSE;
}
 
if (clip->path) {
cairo_clip_path_t *clip_path = clip->path;
do {
if (! _cairo_path_fixed_in_fill (&clip_path->path,
clip_path->fill_rule,
1346,6 → 1378,7
x, y))
return FALSE;
} while ((clip_path = clip_path->prev) != NULL);
}
 
return TRUE;
}
1365,33 → 1398,18
}
 
static void
_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate,
cairo_traps_t *traps,
_cairo_gstate_extents_to_user_rectangle (cairo_gstate_t *gstate,
const cairo_box_t *extents,
double *x1, double *y1,
double *x2, double *y2)
{
cairo_box_t extents;
 
if (traps->num_traps == 0) {
/* no traps, so we actually won't draw anything */
if (x1)
*x1 = 0.0;
if (y1)
*y1 = 0.0;
if (x2)
*x2 = 0.0;
if (y2)
*y2 = 0.0;
} else {
double px1, py1, px2, py2;
 
_cairo_traps_extents (traps, &extents);
px1 = _cairo_fixed_to_double (extents->p1.x);
py1 = _cairo_fixed_to_double (extents->p1.y);
px2 = _cairo_fixed_to_double (extents->p2.x);
py2 = _cairo_fixed_to_double (extents->p2.y);
 
px1 = _cairo_fixed_to_double (extents.p1.x);
py1 = _cairo_fixed_to_double (extents.p1.y);
px2 = _cairo_fixed_to_double (extents.p2.x);
py2 = _cairo_fixed_to_double (extents.p2.y);
 
_cairo_gstate_backend_to_user_rectangle (gstate,
&px1, &py1, &px2, &py2,
NULL);
1404,7 → 1422,6
if (y2)
*y2 = py2;
}
}
 
cairo_status_t
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
1412,10 → 1429,10
double *x1, double *y1,
double *x2, double *y2)
{
cairo_status_t status;
cairo_traps_t traps;
cairo_int_status_t status;
cairo_box_t extents;
cairo_bool_t empty;
 
if (gstate->stroke_style.line_width <= 0.0) {
if (x1)
*x1 = 0.0;
if (y1)
1424,24 → 1441,46
*x2 = 0.0;
if (y2)
*y2 = 0.0;
 
if (gstate->stroke_style.line_width <= 0.0)
return CAIRO_STATUS_SUCCESS;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
&gstate->stroke_style,
&gstate->ctm,
gstate->antialias,
&boxes);
empty = boxes.num_boxes == 0;
if (! empty)
_cairo_boxes_extents (&boxes, &extents);
_cairo_boxes_fini (&boxes);
}
 
_cairo_traps_init (&traps);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
 
status = _cairo_path_fixed_stroke_to_traps (path,
_cairo_polygon_init (&polygon, NULL, 0);
status = _cairo_path_fixed_stroke_to_polygon (path,
&gstate->stroke_style,
&gstate->ctm,
&gstate->ctm_inverse,
gstate->tolerance,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
&polygon);
empty = polygon.num_edges == 0;
if (! empty)
extents = polygon.extents;
_cairo_polygon_fini (&polygon);
}
if (! empty) {
_cairo_gstate_extents_to_user_rectangle (gstate, &extents,
x1, y1, x2, y2);
}
 
_cairo_traps_fini (&traps);
 
return status;
}
 
1452,9 → 1491,9
double *x2, double *y2)
{
cairo_status_t status;
cairo_traps_t traps;
cairo_box_t extents;
cairo_bool_t empty;
 
if (path->is_empty_fill) {
if (x1)
*x1 = 0.0;
if (y1)
1463,9 → 1502,26
*x2 = 0.0;
if (y2)
*y2 = 0.0;
 
if (_cairo_path_fixed_fill_is_empty (path))
return CAIRO_STATUS_SUCCESS;
}
 
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
gstate->fill_rule,
gstate->antialias,
&boxes);
empty = boxes.num_boxes == 0;
if (! empty)
_cairo_boxes_extents (&boxes, &extents);
 
_cairo_boxes_fini (&boxes);
} else {
cairo_traps_t traps;
 
_cairo_traps_init (&traps);
 
status = _cairo_path_fixed_fill_to_traps (path,
1472,13 → 1528,18
gstate->fill_rule,
gstate->tolerance,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps,
empty = traps.num_traps == 0;
if (! empty)
_cairo_traps_extents (&traps, &extents);
 
_cairo_traps_fini (&traps);
}
 
if (! empty) {
_cairo_gstate_extents_to_user_rectangle (gstate, &extents,
x1, y1, x2, y2);
}
 
_cairo_traps_fini (&traps);
 
return status;
}
 
1485,7 → 1546,8
cairo_status_t
_cairo_gstate_reset_clip (cairo_gstate_t *gstate)
{
_cairo_clip_reset (&gstate->clip);
_cairo_clip_destroy (gstate->clip);
gstate->clip = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
1493,9 → 1555,14
cairo_status_t
_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
return _cairo_clip_clip (&gstate->clip,
path, gstate->fill_rule,
gstate->tolerance, gstate->antialias);
gstate->clip =
_cairo_clip_intersect_path (gstate->clip,
path,
gstate->fill_rule,
gstate->tolerance,
gstate->antialias);
/* XXX */
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
1502,16 → 1569,13
_cairo_gstate_int_clip_extents (cairo_gstate_t *gstate,
cairo_rectangle_int_t *extents)
{
const cairo_rectangle_int_t *clip_extents;
cairo_bool_t is_bounded;
 
is_bounded = _cairo_surface_get_extents (gstate->target, extents);
 
clip_extents = _cairo_clip_get_extents (&gstate->clip);
if (clip_extents != NULL) {
cairo_bool_t is_empty;
 
is_empty = _cairo_rectangle_intersect (extents, clip_extents);
if (gstate->clip) {
_cairo_rectangle_intersect (extents,
_cairo_clip_get_extents (gstate->clip));
is_bounded = TRUE;
}
 
1555,18 → 1619,20
cairo_rectangle_list_t*
_cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
{
cairo_clip_t clip;
cairo_rectangle_int_t extents;
cairo_rectangle_list_t *list;
cairo_clip_t *clip;
 
_cairo_clip_init_copy (&clip, &gstate->clip);
 
if (_cairo_surface_get_extents (gstate->target, &extents))
_cairo_clip_rectangle (&clip, &extents);
clip = _cairo_clip_copy_intersect_rectangle (gstate->clip, &extents);
else
clip = gstate->clip;
 
list = _cairo_clip_copy_rectangle_list (&clip, gstate);
_cairo_clip_fini (&clip);
list = _cairo_clip_copy_rectangle_list (clip, gstate);
 
if (clip != gstate->clip)
_cairo_clip_destroy (clip);
 
return list;
}
 
1584,25 → 1650,6
}
 
cairo_status_t
_cairo_gstate_select_font_face (cairo_gstate_t *gstate,
const char *family,
cairo_font_slant_t slant,
cairo_font_weight_t weight)
{
cairo_font_face_t *font_face;
cairo_status_t status;
 
font_face = cairo_toy_font_face_create (family, slant, weight);
if (font_face->status)
return font_face->status;
 
status = _cairo_gstate_set_font_face (gstate, font_face);
cairo_font_face_destroy (font_face);
 
return status;
}
 
cairo_status_t
_cairo_gstate_set_font_size (cairo_gstate_t *gstate,
double size)
{
1620,9 → 1667,6
if (memcmp (matrix, &gstate->font_matrix, sizeof (cairo_matrix_t)) == 0)
return CAIRO_STATUS_SUCCESS;
 
if (! _cairo_matrix_is_invertible (matrix))
return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
 
_cairo_gstate_unset_scaled_font (gstate);
 
gstate->font_matrix = *matrix;
1828,31 → 1872,6
}
 
cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
double x,
double y,
const char *utf8,
int utf8_len,
cairo_glyph_t **glyphs,
int *num_glyphs,
cairo_text_cluster_t **clusters,
int *num_clusters,
cairo_text_cluster_flags_t *cluster_flags)
{
cairo_status_t status;
 
status = _cairo_gstate_ensure_scaled_font (gstate);
if (unlikely (status))
return status;
 
return cairo_scaled_font_text_to_glyphs (gstate->scaled_font, x, y,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags);
}
 
cairo_status_t
_cairo_gstate_set_font_face (cairo_gstate_t *gstate,
cairo_font_face_t *font_face)
{
1891,31 → 1910,27
 
cairo_status_t
_cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate,
const char *utf8,
int utf8_len,
const cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags)
cairo_glyph_text_info_t *info)
{
cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
cairo_pattern_union_t source_pattern;
cairo_glyph_t *transformed_glyphs;
const cairo_pattern_t *pattern;
cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
cairo_glyph_t *transformed_glyphs;
cairo_text_cluster_t stack_transformed_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
cairo_text_cluster_t *transformed_clusters;
cairo_operator_t op;
cairo_status_t status;
cairo_clip_t clip;
 
if (unlikely (gstate->source->status))
return gstate->source->status;
status = _cairo_gstate_get_pattern_status (gstate->source);
if (unlikely (status))
return status;
 
if (gstate->op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (_clipped (gstate))
if (_cairo_clip_is_all_clipped (gstate->clip))
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_gstate_ensure_scaled_font (gstate);
1927,18 → 1942,13
 
if (num_glyphs > ARRAY_LENGTH (stack_transformed_glyphs)) {
transformed_glyphs = cairo_glyph_allocate (num_glyphs);
if (unlikely (transformed_glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_GLYPHS;
if (unlikely (transformed_glyphs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
/* Just in case */
if (!clusters)
num_clusters = 0;
 
if (num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
transformed_clusters = cairo_text_cluster_allocate (num_clusters);
if (info != NULL) {
if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) {
transformed_clusters = cairo_text_cluster_allocate (info->num_clusters);
if (unlikely (transformed_clusters == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_GLYPHS;
1945,16 → 1955,24
}
}
 
status = _cairo_gstate_transform_glyphs_to_backend (gstate,
_cairo_gstate_transform_glyphs_to_backend (gstate,
glyphs, num_glyphs,
clusters,
num_clusters,
cluster_flags,
info->clusters,
info->num_clusters,
info->cluster_flags,
transformed_glyphs,
&num_glyphs,
transformed_clusters);
} else {
_cairo_gstate_transform_glyphs_to_backend (gstate,
glyphs, num_glyphs,
NULL, 0, 0,
transformed_glyphs,
&num_glyphs,
NULL);
}
 
if (status || num_glyphs == 0)
if (num_glyphs == 0)
goto CLEANUP_GLYPHS;
 
op = _reduce_op (gstate);
1978,14 → 1996,23
if (cairo_surface_has_show_text_glyphs (gstate->target) ||
_cairo_scaled_font_get_max_scale (gstate->scaled_font) <= 10240)
{
if (info != NULL) {
status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
utf8, utf8_len,
info->utf8, info->utf8_len,
transformed_glyphs, num_glyphs,
transformed_clusters, num_clusters,
cluster_flags,
transformed_clusters, info->num_clusters,
info->cluster_flags,
gstate->scaled_font,
_gstate_get_clip (gstate, &clip));
gstate->clip);
} else {
status = _cairo_surface_show_text_glyphs (gstate->target, op, pattern,
NULL, 0,
transformed_glyphs, num_glyphs,
NULL, 0, 0,
gstate->scaled_font,
gstate->clip);
}
}
else
{
cairo_path_fixed_t path;
2002,14 → 2029,12
CAIRO_FILL_RULE_WINDING,
gstate->tolerance,
gstate->scaled_font->options.antialias,
_gstate_get_clip (gstate, &clip));
gstate->clip);
}
 
_cairo_path_fixed_fini (&path);
}
 
_cairo_clip_fini (&clip);
 
CLEANUP_GLYPHS:
if (transformed_glyphs != stack_transformed_glyphs)
cairo_glyph_free (transformed_glyphs);
2025,9 → 2050,9
int num_glyphs,
cairo_path_fixed_t *path)
{
cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
cairo_glyph_t *transformed_glyphs;
cairo_status_t status;
cairo_glyph_t *transformed_glyphs;
cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
 
status = _cairo_gstate_ensure_scaled_font (gstate);
if (unlikely (status))
2041,19 → 2066,16
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
status = _cairo_gstate_transform_glyphs_to_backend (gstate,
_cairo_gstate_transform_glyphs_to_backend (gstate,
glyphs, num_glyphs,
NULL, 0, 0,
transformed_glyphs,
NULL, NULL);
if (unlikely (status))
goto CLEANUP_GLYPHS;
&num_glyphs, NULL);
 
status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
transformed_glyphs, num_glyphs,
path);
 
CLEANUP_GLYPHS:
if (transformed_glyphs != stack_transformed_glyphs)
cairo_glyph_free (transformed_glyphs);
 
2093,7 → 2115,7
* This also uses information from the scaled font and the surface to
* cull/drop glyphs that will not be visible.
**/
static cairo_status_t
static void
_cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate,
const cairo_glyph_t *glyphs,
int num_glyphs,
2104,16 → 2126,14
int *num_transformed_glyphs,
cairo_text_cluster_t *transformed_clusters)
{
int i, j, k;
cairo_rectangle_int_t surface_extents;
cairo_matrix_t *ctm = &gstate->ctm;
cairo_matrix_t *font_matrix = &gstate->font_matrix;
cairo_matrix_t *device_transform = &gstate->target->device_transform;
cairo_bool_t drop = FALSE;
double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
int i, j, k;
 
if (num_transformed_glyphs != NULL) {
cairo_rectangle_int_t surface_extents;
 
drop = TRUE;
if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) {
drop = FALSE; /* unbounded surface */
2122,7 → 2142,7
if (surface_extents.width == 0 || surface_extents.height == 0) {
/* No visible area. Don't draw anything */
*num_transformed_glyphs = 0;
return CAIRO_STATUS_SUCCESS;
return;
}
/* XXX We currently drop any glyphs that has its position outside
* of the surface boundaries by a safety margin depending on the
2140,8 → 2160,6
 
if (!drop)
*num_transformed_glyphs = num_glyphs;
} else
num_transformed_glyphs = &j;
 
#define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2)
 
2153,6 → 2171,8
if (! drop) {
memcpy (transformed_glyphs, glyphs,
num_glyphs * sizeof (cairo_glyph_t));
memcpy (transformed_clusters, clusters,
num_clusters * sizeof (cairo_text_cluster_t));
j = num_glyphs;
} else if (num_clusters == 0) {
for (i = 0; i < num_glyphs; i++) {
2208,6 → 2228,8
if (!drop || KEEP_GLYPH (transformed_glyphs[j]))
j++;
}
memcpy (transformed_clusters, clusters,
num_clusters * sizeof (cairo_text_cluster_t));
} else {
const cairo_glyph_t *cur_glyph;
 
2261,6 → 2283,8
if (! drop || KEEP_GLYPH (transformed_glyphs[j]))
j++;
}
memcpy (transformed_clusters, clusters,
num_clusters * sizeof (cairo_text_cluster_t));
} else {
const cairo_glyph_t *cur_glyph;
 
2304,6 → 2328,4
transformed_glyphs[j] = tmp;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-hash.c
59,21 → 59,20
#define ENTRY_IS_DEAD(entry) ((entry) == DEAD_ENTRY)
#define ENTRY_IS_LIVE(entry) ((entry) > DEAD_ENTRY)
 
/* We expect keys will not be destroyed frequently, so our table does not
* contain any explicit shrinking code nor any chain-coalescing code for
* entries randomly deleted by memory pressure (except during rehashing, of
* course). These assumptions are potentially bad, but they make the
* implementation straightforward.
/*
* This table is open-addressed with double hashing. Each table size
* is a prime and it makes for the "first" hash modulus; a second
* prime (2 less than the first prime) serves as the "second" hash
* modulus, which is smaller and thus guarantees a complete
* permutation of table indices.
*
* Revisit later if evidence appears that we're using excessive memory from
* a mostly-dead table.
* Hash tables are rehashed in order to keep between 12.5% and 50%
* entries in the hash table alive and at least 25% free. When table
* size is changed, the new table has about 25% live elements.
*
* This table is open-addressed with double hashing. Each table size is a
* prime chosen to be a little more than double the high water mark for a
* given arrangement, so the tables should remain < 50% full. The table
* size makes for the "first" hash modulus; a second prime (2 less than the
* first prime) serves as the "second" hash modulus, which is co-prime and
* thus guarantees a complete permutation of table indices.
* The free entries guarantee an expected constant-time lookup.
* Doubling/halving the table in the described fashion guarantees
* amortized O(1) insertion/removal.
*
* This structure, and accompanying table, is borrowed/modified from the
* file xserver/render/glyph.c in the freedesktop.org x server, with
81,53 → 80,67
* Packard.
*/
 
typedef struct _cairo_hash_table_arrangement {
unsigned long high_water_mark;
unsigned long size;
unsigned long rehash;
} cairo_hash_table_arrangement_t;
 
static const cairo_hash_table_arrangement_t hash_table_arrangements [] = {
{ 16, 43, 41 },
{ 32, 73, 71 },
{ 64, 151, 149 },
{ 128, 283, 281 },
{ 256, 571, 569 },
{ 512, 1153, 1151 },
{ 1024, 2269, 2267 },
{ 2048, 4519, 4517 },
{ 4096, 9013, 9011 },
{ 8192, 18043, 18041 },
{ 16384, 36109, 36107 },
{ 32768, 72091, 72089 },
{ 65536, 144409, 144407 },
{ 131072, 288361, 288359 },
{ 262144, 576883, 576881 },
{ 524288, 1153459, 1153457 },
{ 1048576, 2307163, 2307161 },
{ 2097152, 4613893, 4613891 },
{ 4194304, 9227641, 9227639 },
{ 8388608, 18455029, 18455027 },
{ 16777216, 36911011, 36911009 },
{ 33554432, 73819861, 73819859 },
{ 67108864, 147639589, 147639587 },
{ 134217728, 295279081, 295279079 },
{ 268435456, 590559793, 590559791 }
static const unsigned long hash_table_sizes[] = {
43,
73,
151,
283,
571,
1153,
2269,
4519,
9013,
18043,
36109,
72091,
144409,
288361,
576883,
1153459,
2307163,
4613893,
9227641,
18455029,
36911011,
73819861,
147639589,
295279081,
590559793
};
 
#define NUM_HASH_TABLE_ARRANGEMENTS ARRAY_LENGTH (hash_table_arrangements)
 
struct _cairo_hash_table {
cairo_hash_keys_equal_func_t keys_equal;
 
const cairo_hash_table_arrangement_t *arrangement;
cairo_hash_entry_t *cache[32];
 
const unsigned long *table_size;
cairo_hash_entry_t **entries;
 
unsigned long live_entries;
unsigned long free_entries;
unsigned long iterating; /* Iterating, no insert, no resize */
};
 
/**
* _cairo_hash_table_uid_keys_equal:
* @key_a: the first key to be compared
* @key_b: the second key to be compared
*
* Provides a #cairo_hash_keys_equal_func_t which always returns
* %TRUE. This is useful to create hash tables using keys whose hash
* completely describes the key, because in this special case
* comparing the hashes is sufficient to guarantee that the keys are
* equal.
*
* Return value: %TRUE.
**/
static cairo_bool_t
_cairo_hash_table_uid_keys_equal (const void *key_a, const void *key_b)
{
return TRUE;
}
 
/**
* _cairo_hash_table_create:
* @keys_equal: a function to return %TRUE if two keys are equal
*
139,6 → 152,9
* _cairo_hash_table_remove), and other times both a key and a value
* will be necessary, (as in _cairo_hash_table_insert).
*
* If @keys_equal is %NULL, two keys will be considered equal if and
* only if their hashes are equal.
*
* See #cairo_hash_entry_t for more details.
*
* Return value: the new hash table or %NULL if out of memory.
154,11 → 170,15
return NULL;
}
 
if (keys_equal == NULL)
hash_table->keys_equal = _cairo_hash_table_uid_keys_equal;
else
hash_table->keys_equal = keys_equal;
 
hash_table->arrangement = &hash_table_arrangements[0];
memset (&hash_table->cache, 0, sizeof (hash_table->cache));
hash_table->table_size = &hash_table_sizes[0];
 
hash_table->entries = calloc (hash_table->arrangement->size,
hash_table->entries = calloc (*hash_table->table_size,
sizeof(cairo_hash_entry_t *));
if (unlikely (hash_table->entries == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
167,6 → 187,7
}
 
hash_table->live_entries = 0;
hash_table->free_entries = *hash_table->table_size;
hash_table->iterating = 0;
 
return hash_table;
198,8 → 219,6
assert (hash_table->iterating == 0);
 
free (hash_table->entries);
hash_table->entries = NULL;
 
free (hash_table);
}
 
210,7 → 229,7
unsigned long table_size, i, idx, step;
cairo_hash_entry_t **entry;
 
table_size = hash_table->arrangement->size;
table_size = *hash_table->table_size;
idx = key->hash % table_size;
 
entry = &hash_table->entries[idx];
218,9 → 237,7
return entry;
 
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
step = 1 + key->hash % (table_size - 2);
do {
idx += step;
if (idx >= table_size)
236,52 → 253,62
}
 
/**
* _cairo_hash_table_resize:
* _cairo_hash_table_manage:
* @hash_table: a hash table
*
* Resize the hash table if the number of entries has gotten much
* bigger or smaller than the ideal number of entries for the current
* size.
* size and guarantee some free entries to be used as lookup
* termination points.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful or
* %CAIRO_STATUS_NO_MEMORY if out of memory.
**/
static cairo_status_t
_cairo_hash_table_resize (cairo_hash_table_t *hash_table)
_cairo_hash_table_manage (cairo_hash_table_t *hash_table)
{
cairo_hash_table_t tmp;
unsigned long new_size, i;
 
/* This keeps the hash table between 25% and 50% full. */
unsigned long high = hash_table->arrangement->high_water_mark;
unsigned long low = high >> 2;
/* Keep between 12.5% and 50% entries in the hash table alive and
* at least 25% free. */
unsigned long live_high = *hash_table->table_size >> 1;
unsigned long live_low = live_high >> 2;
unsigned long free_low = live_high >> 1;
 
if (hash_table->live_entries >= low && hash_table->live_entries <= high)
return CAIRO_STATUS_SUCCESS;
 
tmp = *hash_table;
 
if (hash_table->live_entries > high)
if (hash_table->live_entries > live_high)
{
tmp.arrangement = hash_table->arrangement + 1;
tmp.table_size = hash_table->table_size + 1;
/* This code is being abused if we can't make a table big enough. */
assert (tmp.arrangement - hash_table_arrangements <
NUM_HASH_TABLE_ARRANGEMENTS);
assert (tmp.table_size - hash_table_sizes <
ARRAY_LENGTH (hash_table_sizes));
}
else /* hash_table->live_entries < low */
else if (hash_table->live_entries < live_low)
{
/* Can't shrink if we're at the smallest size */
if (hash_table->arrangement == &hash_table_arrangements[0])
if (hash_table->table_size == &hash_table_sizes[0])
tmp.table_size = hash_table->table_size;
else
tmp.table_size = hash_table->table_size - 1;
}
 
if (tmp.table_size == hash_table->table_size &&
hash_table->free_entries > free_low)
{
/* The number of live entries is within the desired bounds
* (we're not going to resize the table) and we have enough
* free entries. Do nothing. */
return CAIRO_STATUS_SUCCESS;
tmp.arrangement = hash_table->arrangement - 1;
}
 
new_size = tmp.arrangement->size;
new_size = *tmp.table_size;
tmp.entries = calloc (new_size, sizeof (cairo_hash_entry_t*));
if (unlikely (tmp.entries == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
for (i = 0; i < hash_table->arrangement->size; ++i) {
for (i = 0; i < *hash_table->table_size; ++i) {
if (ENTRY_IS_LIVE (hash_table->entries[i])) {
*_cairo_hash_table_lookup_unique_key (&tmp, hash_table->entries[i])
= hash_table->entries[i];
290,7 → 317,8
 
free (hash_table->entries);
hash_table->entries = tmp.entries;
hash_table->arrangement = tmp.arrangement;
hash_table->table_size = tmp.table_size;
hash_table->free_entries = new_size - hash_table->live_entries;
 
return CAIRO_STATUS_SUCCESS;
}
312,21 → 340,24
{
cairo_hash_entry_t *entry;
unsigned long table_size, i, idx, step;
unsigned long hash = key->hash;
 
table_size = hash_table->arrangement->size;
idx = key->hash % table_size;
entry = hash_table->cache[hash & 31];
if (entry && entry->hash == hash && hash_table->keys_equal (key, entry))
return entry;
 
table_size = *hash_table->table_size;
idx = hash % table_size;
 
entry = hash_table->entries[idx];
if (ENTRY_IS_LIVE (entry)) {
if (hash_table->keys_equal (key, entry))
return entry;
if (entry->hash == hash && hash_table->keys_equal (key, entry))
goto insert_cache;
} else if (ENTRY_IS_FREE (entry))
return NULL;
 
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
step = 1 + hash % (table_size - 2);
do {
idx += step;
if (idx >= table_size)
334,13 → 365,18
 
entry = hash_table->entries[idx];
if (ENTRY_IS_LIVE (entry)) {
if (hash_table->keys_equal (key, entry))
return entry;
if (entry->hash == hash && hash_table->keys_equal (key, entry))
goto insert_cache;
} else if (ENTRY_IS_FREE (entry))
return NULL;
} while (++i < table_size);
 
ASSERT_NOT_REACHED;
return NULL;
 
insert_cache:
hash_table->cache[hash & 31] = entry;
return entry;
}
 
/**
372,7 → 408,7
 
assert (predicate != NULL);
 
table_size = hash_table->arrangement->size;
table_size = *hash_table->table_size;
hash = rand ();
idx = hash % table_size;
 
381,9 → 417,7
return entry;
 
i = 1;
step = hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
step = 1 + hash % (table_size - 2);
do {
idx += step;
if (idx >= table_size)
421,22 → 455,25
_cairo_hash_table_insert (cairo_hash_table_t *hash_table,
cairo_hash_entry_t *key_and_value)
{
cairo_hash_entry_t **entry;
cairo_status_t status;
 
/* Insert is illegal while an iterator is running. */
assert (hash_table->iterating == 0);
 
hash_table->live_entries++;
status = _cairo_hash_table_resize (hash_table);
if (unlikely (status)) {
/* abort the insert... */
hash_table->live_entries--;
status = _cairo_hash_table_manage (hash_table);
if (unlikely (status))
return status;
}
 
*_cairo_hash_table_lookup_unique_key (hash_table,
key_and_value) = key_and_value;
entry = _cairo_hash_table_lookup_unique_key (hash_table, key_and_value);
 
if (ENTRY_IS_FREE (*entry))
hash_table->free_entries--;
 
*entry = key_and_value;
hash_table->cache[key_and_value->hash & 31] = key_and_value;
hash_table->live_entries++;
 
return CAIRO_STATUS_SUCCESS;
}
 
447,7 → 484,7
unsigned long table_size, i, idx, step;
cairo_hash_entry_t **entry;
 
table_size = hash_table->arrangement->size;
table_size = *hash_table->table_size;
idx = key->hash % table_size;
 
entry = &hash_table->entries[idx];
455,9 → 492,7
return entry;
 
i = 1;
step = key->hash % hash_table->arrangement->rehash;
if (step == 0)
step = 1;
step = 1 + key->hash % (table_size - 2);
do {
idx += step;
if (idx >= table_size)
487,6 → 522,7
{
*_cairo_hash_table_lookup_exact_key (hash_table, key) = DEAD_ENTRY;
hash_table->live_entries--;
hash_table->cache[key->hash & 31] = NULL;
 
/* Check for table resize. Don't do this when iterating as this will
* reorder elements of the table and cause the iteration to potentially
496,7 → 532,7
* memory to shrink the hash table. It does leave the table in a
* consistent state, and we've already succeeded in removing the
* entry, so we don't examine the failure status of this call. */
_cairo_hash_table_resize (hash_table);
_cairo_hash_table_manage (hash_table);
}
}
 
525,7 → 561,7
 
/* Mark the table for iteration */
++hash_table->iterating;
for (i = 0; i < hash_table->arrangement->size; i++) {
for (i = 0; i < *hash_table->table_size; i++) {
entry = hash_table->entries[i];
if (ENTRY_IS_LIVE(entry))
hash_callback (entry, closure);
537,6 → 573,6
if (--hash_table->iterating == 0) {
/* Should we fail to shrink the hash table, it is left unaltered,
* and we don't need to propagate the error status. */
_cairo_hash_table_resize (hash_table);
_cairo_hash_table_manage (hash_table);
}
}
/programs/develop/libraries/cairo/src/cairo-image-compositor.c
0,0 → 1,3100
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
* Copyright © 2009,2010,2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* The primarily reason for keeping a traps-compositor around is
* for validating cairo-xlib (which currently also uses traps).
*/
 
#include "cairoint.h"
 
#include "cairo-image-surface-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-spans-compositor-private.h"
 
#include "cairo-region-private.h"
#include "cairo-traps-private.h"
#include "cairo-tristrip-private.h"
 
#include "cairo-pixman-private.h"
 
static pixman_image_t *
to_pixman_image (cairo_surface_t *s)
{
return ((cairo_image_surface_t *)s)->pixman_image;
}
 
static cairo_int_status_t
acquire (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
release (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
set_clip_region (void *_surface,
cairo_region_t *region)
{
cairo_image_surface_t *surface = _surface;
pixman_region32_t *rgn = region ? &region->rgn : NULL;
 
if (! pixman_image_set_clip_region32 (surface->pixman_image, rgn))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_image_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
int i;
 
TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x = _cairo_fixed_integer_part (b->p1.x);
int y = _cairo_fixed_integer_part (b->p1.y);
int w = _cairo_fixed_integer_part (b->p2.x) - x;
int h = _cairo_fixed_integer_part (b->p2.y) - y;
if (dst->pixman_format != image->pixman_format ||
! pixman_blt ((uint32_t *)image->data, (uint32_t *)dst->data,
image->stride / sizeof (uint32_t),
dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (image->pixman_format),
PIXMAN_FORMAT_BPP (dst->pixman_format),
x + dx, y + dy,
x, y,
w, h))
{
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, dst->pixman_image,
x + dx, y + dy,
0, 0,
x, y,
w, h);
}
}
}
return CAIRO_STATUS_SUCCESS;
}
 
static inline uint32_t
color_to_uint32 (const cairo_color_t *color)
{
return
(color->alpha_short >> 8 << 24) |
(color->red_short >> 8 << 16) |
(color->green_short & 0xff00) |
(color->blue_short >> 8);
}
 
static inline cairo_bool_t
color_to_pixel (const cairo_color_t *color,
pixman_format_code_t format,
uint32_t *pixel)
{
uint32_t c;
 
if (!(format == PIXMAN_a8r8g8b8 ||
format == PIXMAN_x8r8g8b8 ||
format == PIXMAN_a8b8g8r8 ||
format == PIXMAN_x8b8g8r8 ||
format == PIXMAN_b8g8r8a8 ||
format == PIXMAN_b8g8r8x8 ||
format == PIXMAN_r5g6b5 ||
format == PIXMAN_b5g6r5 ||
format == PIXMAN_a8))
{
return FALSE;
}
 
c = color_to_uint32 (color);
 
if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
c = ((c & 0xff000000) >> 0) |
((c & 0x00ff0000) >> 16) |
((c & 0x0000ff00) >> 0) |
((c & 0x000000ff) << 16);
}
 
if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
c = ((c & 0xff000000) >> 24) |
((c & 0x00ff0000) >> 8) |
((c & 0x0000ff00) << 8) |
((c & 0x000000ff) << 24);
}
 
if (format == PIXMAN_a8) {
c = c >> 24;
} else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
c = ((((c) >> 3) & 0x001f) |
(((c) >> 5) & 0x07e0) |
(((c) >> 8) & 0xf800));
}
 
*pixel = c;
return TRUE;
}
 
static pixman_op_t
_pixman_operator (cairo_operator_t op)
{
switch ((int) op) {
case CAIRO_OPERATOR_CLEAR:
return PIXMAN_OP_CLEAR;
 
case CAIRO_OPERATOR_SOURCE:
return PIXMAN_OP_SRC;
case CAIRO_OPERATOR_OVER:
return PIXMAN_OP_OVER;
case CAIRO_OPERATOR_IN:
return PIXMAN_OP_IN;
case CAIRO_OPERATOR_OUT:
return PIXMAN_OP_OUT;
case CAIRO_OPERATOR_ATOP:
return PIXMAN_OP_ATOP;
 
case CAIRO_OPERATOR_DEST:
return PIXMAN_OP_DST;
case CAIRO_OPERATOR_DEST_OVER:
return PIXMAN_OP_OVER_REVERSE;
case CAIRO_OPERATOR_DEST_IN:
return PIXMAN_OP_IN_REVERSE;
case CAIRO_OPERATOR_DEST_OUT:
return PIXMAN_OP_OUT_REVERSE;
case CAIRO_OPERATOR_DEST_ATOP:
return PIXMAN_OP_ATOP_REVERSE;
 
case CAIRO_OPERATOR_XOR:
return PIXMAN_OP_XOR;
case CAIRO_OPERATOR_ADD:
return PIXMAN_OP_ADD;
case CAIRO_OPERATOR_SATURATE:
return PIXMAN_OP_SATURATE;
 
case CAIRO_OPERATOR_MULTIPLY:
return PIXMAN_OP_MULTIPLY;
case CAIRO_OPERATOR_SCREEN:
return PIXMAN_OP_SCREEN;
case CAIRO_OPERATOR_OVERLAY:
return PIXMAN_OP_OVERLAY;
case CAIRO_OPERATOR_DARKEN:
return PIXMAN_OP_DARKEN;
case CAIRO_OPERATOR_LIGHTEN:
return PIXMAN_OP_LIGHTEN;
case CAIRO_OPERATOR_COLOR_DODGE:
return PIXMAN_OP_COLOR_DODGE;
case CAIRO_OPERATOR_COLOR_BURN:
return PIXMAN_OP_COLOR_BURN;
case CAIRO_OPERATOR_HARD_LIGHT:
return PIXMAN_OP_HARD_LIGHT;
case CAIRO_OPERATOR_SOFT_LIGHT:
return PIXMAN_OP_SOFT_LIGHT;
case CAIRO_OPERATOR_DIFFERENCE:
return PIXMAN_OP_DIFFERENCE;
case CAIRO_OPERATOR_EXCLUSION:
return PIXMAN_OP_EXCLUSION;
case CAIRO_OPERATOR_HSL_HUE:
return PIXMAN_OP_HSL_HUE;
case CAIRO_OPERATOR_HSL_SATURATION:
return PIXMAN_OP_HSL_SATURATION;
case CAIRO_OPERATOR_HSL_COLOR:
return PIXMAN_OP_HSL_COLOR;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return PIXMAN_OP_HSL_LUMINOSITY;
 
default:
ASSERT_NOT_REACHED;
return PIXMAN_OP_OVER;
}
}
 
static cairo_bool_t
__fill_reduces_to_source (cairo_operator_t op,
const cairo_color_t *color,
const cairo_image_surface_t *dst)
{
if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)
return TRUE;
if (op == CAIRO_OPERATOR_OVER && CAIRO_COLOR_IS_OPAQUE (color))
return TRUE;
if (dst->base.is_clear)
return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD;
 
return FALSE;
}
 
static cairo_bool_t
fill_reduces_to_source (cairo_operator_t op,
const cairo_color_t *color,
const cairo_image_surface_t *dst,
uint32_t *pixel)
{
if (__fill_reduces_to_source (op, color, dst)) {
color_to_pixel (color, dst->pixman_format, pixel);
return TRUE;
}
 
return FALSE;
}
 
static cairo_int_status_t
fill_rectangles (void *_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_image_surface_t *dst = _dst;
uint32_t pixel;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (fill_reduces_to_source (op, color, dst, &pixel)) {
for (i = 0; i < num_rects; i++) {
pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (dst->pixman_format),
rects[i].x, rects[i].y,
rects[i].width, rects[i].height,
pixel);
}
} else {
pixman_image_t *src = _pixman_image_for_color (color);
 
op = _pixman_operator (op);
for (i = 0; i < num_rects; i++) {
pixman_image_composite32 (op,
src, NULL, dst->pixman_image,
0, 0,
0, 0,
rects[i].x, rects[i].y,
rects[i].width, rects[i].height);
}
 
pixman_image_unref (src);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
fill_boxes (void *_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_image_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
uint32_t pixel;
int i;
 
TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
 
if (fill_reduces_to_source (op, color, dst, &pixel)) {
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int w = _cairo_fixed_integer_part (chunk->base[i].p2.x) - x;
int h = _cairo_fixed_integer_part (chunk->base[i].p2.y) - y;
pixman_fill ((uint32_t *) dst->data,
dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (dst->pixman_format),
x, y, w, h, pixel);
}
}
}
else
{
pixman_image_t *src = _pixman_image_for_color (color);
 
op = _pixman_operator (op);
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
pixman_image_composite32 (op,
src, NULL, dst->pixman_image,
0, 0,
0, 0,
x1, y1,
x2-x1, y2-y1);
}
}
 
pixman_image_unref (src);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (mask) {
pixman_image_composite32 (_pixman_operator (op),
src->pixman_image, mask->pixman_image, to_pixman_image (_dst),
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
} else {
pixman_image_composite32 (_pixman_operator (op),
src->pixman_image, NULL, to_pixman_image (_dst),
src_x, src_y,
0, 0,
dst_x, dst_y,
width, height);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
lerp (void *_dst,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_image_surface_t *dst = _dst;
cairo_image_source_t *src = (cairo_image_source_t *)abstract_src;
cairo_image_source_t *mask = (cairo_image_source_t *)abstract_mask;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
src->pixman_image, mask->pixman_image, dst->pixman_image,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
#else
/* Punch the clip out of the destination */
TRACE ((stderr, "%s - OUT_REVERSE (mask=%d/%p, dst=%d/%p)\n",
__FUNCTION__,
mask->base.unique_id, mask->pixman_image,
dst->base.unique_id, dst->pixman_image));
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask->pixman_image, NULL, dst->pixman_image,
mask_x, mask_y,
0, 0,
dst_x, dst_y,
width, height);
 
/* Now add the two results together */
TRACE ((stderr, "%s - ADD (src=%d/%p, mask=%d/%p, dst=%d/%p)\n",
__FUNCTION__,
src->base.unique_id, src->pixman_image,
mask->base.unique_id, mask->pixman_image,
dst->base.unique_id, dst->pixman_image));
pixman_image_composite32 (PIXMAN_OP_ADD,
src->pixman_image, mask->pixman_image, dst->pixman_image,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
#endif
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_boxes (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
pixman_image_t *dst = to_pixman_image (_dst);
pixman_image_t *src = ((cairo_image_source_t *)abstract_src)->pixman_image;
pixman_image_t *mask = abstract_mask ? ((cairo_image_source_t *)abstract_mask)->pixman_image : NULL;
pixman_image_t *free_src = NULL;
struct _cairo_boxes_chunk *chunk;
int i;
 
/* XXX consider using a region? saves multiple prepare-composite */
TRACE ((stderr, "%s x %d\n", __FUNCTION__, boxes->num_boxes));
 
if (((cairo_surface_t *)_dst)->is_clear &&
(op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_OVER ||
op == CAIRO_OPERATOR_ADD)) {
op = PIXMAN_OP_SRC;
} else if (mask) {
if (op == CAIRO_OPERATOR_CLEAR) {
#if PIXMAN_HAS_OP_LERP
op = PIXMAN_OP_LERP_CLEAR;
#else
free_src = src = _pixman_image_for_color (CAIRO_COLOR_WHITE);
op = PIXMAN_OP_OUT_REVERSE;
#endif
} else if (op == CAIRO_OPERATOR_SOURCE) {
#if PIXMAN_HAS_OP_LERP
op = PIXMAN_OP_LERP_SRC;
#else
return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
} else {
op = _pixman_operator (op);
}
} else {
op = _pixman_operator (op);
}
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
pixman_image_composite32 (op, src, mask, dst,
x1 + src_x, y1 + src_y,
x1 + mask_x, y1 + mask_y,
x1 + dst_x, y1 + dst_y,
x2 - x1, y2 - y1);
}
}
 
if (free_src)
pixman_image_unref (free_src);
 
return CAIRO_STATUS_SUCCESS;
}
 
#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
 
static cairo_bool_t
line_exceeds_16_16 (const cairo_line_t *line)
{
return
line->p1.x <= CAIRO_FIXED_16_16_MIN ||
line->p1.x >= CAIRO_FIXED_16_16_MAX ||
 
line->p2.x <= CAIRO_FIXED_16_16_MIN ||
line->p2.x >= CAIRO_FIXED_16_16_MAX ||
 
line->p1.y <= CAIRO_FIXED_16_16_MIN ||
line->p1.y >= CAIRO_FIXED_16_16_MAX ||
 
line->p2.y <= CAIRO_FIXED_16_16_MIN ||
line->p2.y >= 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,
pixman_line_fixed_t *out)
{
/* XXX use fixed-point arithmetic? */
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));
}
 
void
_pixman_image_add_traps (pixman_image_t *image,
int dst_x, int dst_y,
cairo_traps_t *traps)
{
cairo_trapezoid_t *t = traps->traps;
int num_traps = traps->num_traps;
while (num_traps--) {
pixman_trapezoid_t trap;
 
/* top/bottom will be clamped to surface bounds */
trap.top = _cairo_fixed_to_16_16 (t->top);
trap.bottom = _cairo_fixed_to_16_16 (t->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 (&t->left))) {
project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
trap.left.p1.y = trap.top;
trap.left.p2.y = trap.bottom;
} else {
trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
}
 
if (unlikely (line_exceeds_16_16 (&t->right))) {
project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
trap.right.p1.y = trap.top;
trap.right.p2.y = trap.bottom;
} else {
trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
}
 
pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
t++;
}
}
 
static cairo_int_status_t
composite_traps (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
pixman_image_t *mask;
pixman_format_code_t format;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* Special case adding trapezoids onto a mask surface; we want to avoid
* creating an intermediate temporary mask unnecessarily.
*
* We make the assumption here that the portion of the trapezoids
* contained within the surface is bounded by [dst_x,dst_y,width,height];
* the Cairo core code passes bounds based on the trapezoid extents.
*/
format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
if (dst->pixman_format == format &&
(abstract_src == NULL ||
(op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
{
_pixman_image_add_traps (dst->pixman_image, dst_x, dst_y, traps);
return CAIRO_STATUS_SUCCESS;
}
 
mask = pixman_image_create_bits (format,
extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_pixman_image_add_traps (mask, extents->x, extents->y, traps);
pixman_image_composite32 (_pixman_operator (op),
src->pixman_image, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0)
static void
set_point (pixman_point_fixed_t *p, cairo_point_t *c)
{
p->x = _cairo_fixed_to_16_16 (c->x);
p->y = _cairo_fixed_to_16_16 (c->y);
}
 
void
_pixman_image_add_tristrip (pixman_image_t *image,
int dst_x, int dst_y,
cairo_tristrip_t *strip)
{
pixman_triangle_t tri;
pixman_point_fixed_t *p[3] = {&tri.p1, &tri.p2, &tri.p3 };
int n;
 
set_point (p[0], &strip->points[0]);
set_point (p[1], &strip->points[1]);
set_point (p[2], &strip->points[2]);
pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
for (n = 3; n < strip->num_points; n++) {
set_point (p[n%3], &strip->points[n]);
pixman_add_triangles (image, -dst_x, -dst_y, 1, &tri);
}
}
 
static cairo_int_status_t
composite_tristrip (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *strip)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *) _dst;
cairo_image_source_t *src = (cairo_image_source_t *) abstract_src;
pixman_image_t *mask;
pixman_format_code_t format;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (strip->num_points < 3)
return CAIRO_STATUS_SUCCESS;
 
format = antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
if (dst->pixman_format == format &&
(abstract_src == NULL ||
(op == CAIRO_OPERATOR_ADD && src->is_opaque_solid)))
{
_pixman_image_add_tristrip (dst->pixman_image, dst_x, dst_y, strip);
return CAIRO_STATUS_SUCCESS;
}
 
mask = pixman_image_create_bits (format,
extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_pixman_image_add_tristrip (mask, extents->x, extents->y, strip);
pixman_image_composite32 (_pixman_operator (op),
src->pixman_image, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (mask);
 
return CAIRO_STATUS_SUCCESS;
}
#endif
 
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
return CAIRO_STATUS_SUCCESS;
}
 
#if HAS_PIXMAN_GLYPHS
static pixman_glyph_cache_t *global_glyph_cache;
 
static inline pixman_glyph_cache_t *
get_glyph_cache (void)
{
if (!global_glyph_cache)
global_glyph_cache = pixman_glyph_cache_create ();
 
return global_glyph_cache;
}
 
void
_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
if (global_glyph_cache) {
pixman_glyph_cache_remove (
global_glyph_cache, scaled_font,
(void *)_cairo_scaled_glyph_index (scaled_glyph));
}
 
CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
}
 
static cairo_int_status_t
composite_glyphs (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
pixman_glyph_cache_t *glyph_cache;
pixman_glyph_t pglyphs_stack[CAIRO_STACK_ARRAY_LENGTH (pixman_glyph_t)];
pixman_glyph_t *pglyphs = pglyphs_stack;
pixman_glyph_t *pg;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
glyph_cache = get_glyph_cache();
if (unlikely (glyph_cache == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto out_unlock;
}
 
pixman_glyph_cache_freeze (glyph_cache);
 
if (info->num_glyphs > ARRAY_LENGTH (pglyphs_stack)) {
pglyphs = _cairo_malloc_ab (info->num_glyphs, sizeof (pixman_glyph_t));
if (unlikely (pglyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto out_thaw;
}
}
 
pg = pglyphs;
for (i = 0; i < info->num_glyphs; i++) {
unsigned long index = info->glyphs[i].index;
const void *glyph;
 
glyph = pixman_glyph_cache_lookup (glyph_cache, info->font, (void *)index);
if (!glyph) {
cairo_scaled_glyph_t *scaled_glyph;
cairo_image_surface_t *glyph_surface;
 
/* This call can actually end up recursing, so we have to
* drop the mutex around it.
*/
CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
status = _cairo_scaled_glyph_lookup (info->font, index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
CAIRO_MUTEX_LOCK (_cairo_glyph_cache_mutex);
 
if (unlikely (status))
goto out_thaw;
 
glyph_surface = scaled_glyph->surface;
glyph = pixman_glyph_cache_insert (glyph_cache, info->font, (void *)index,
glyph_surface->base.device_transform.x0,
glyph_surface->base.device_transform.y0,
glyph_surface->pixman_image);
if (unlikely (!glyph)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto out_thaw;
}
}
 
pg->x = _cairo_lround (info->glyphs[i].x);
pg->y = _cairo_lround (info->glyphs[i].y);
pg->glyph = glyph;
pg++;
}
 
if (info->use_mask) {
pixman_format_code_t mask_format;
 
mask_format = pixman_glyph_get_mask_format (glyph_cache, pg - pglyphs, pglyphs);
 
pixman_composite_glyphs (_pixman_operator (op),
((cairo_image_source_t *)_src)->pixman_image,
to_pixman_image (_dst),
mask_format,
info->extents.x + src_x, info->extents.y + src_y,
info->extents.x, info->extents.y,
info->extents.x - dst_x, info->extents.y - dst_y,
info->extents.width, info->extents.height,
glyph_cache, pg - pglyphs, pglyphs);
} else {
pixman_composite_glyphs_no_mask (_pixman_operator (op),
((cairo_image_source_t *)_src)->pixman_image,
to_pixman_image (_dst),
src_x, src_y,
- dst_x, - dst_y,
glyph_cache, pg - pglyphs, pglyphs);
}
 
out_thaw:
pixman_glyph_cache_thaw (glyph_cache);
 
if (pglyphs != pglyphs_stack)
free(pglyphs);
 
out_unlock:
CAIRO_MUTEX_UNLOCK (_cairo_glyph_cache_mutex);
return status;
}
#else
void
_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
}
 
static cairo_int_status_t
composite_one_glyph (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_image_surface_t *glyph_surface;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status;
int x, y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = _cairo_scaled_glyph_lookup (info->font,
info->glyphs[0].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status))
return status;
 
glyph_surface = scaled_glyph->surface;
if (glyph_surface->width == 0 || glyph_surface->height == 0)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (info->glyphs[0].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (info->glyphs[0].y -
glyph_surface->base.device_transform.y0);
 
pixman_image_composite32 (_pixman_operator (op),
((cairo_image_source_t *)_src)->pixman_image,
glyph_surface->pixman_image,
to_pixman_image (_dst),
x + src_x, y + src_y,
0, 0,
x - dst_x, y - dst_y,
glyph_surface->width,
glyph_surface->height);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_glyphs_via_mask (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_scaled_glyph_t *glyph_cache[64];
pixman_image_t *white = _pixman_image_for_color (CAIRO_COLOR_WHITE);
cairo_scaled_glyph_t *scaled_glyph;
uint8_t buf[2048];
pixman_image_t *mask;
pixman_format_code_t format;
cairo_status_t status;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (unlikely (white == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* XXX convert the glyphs to common formats a8/a8r8g8b8 to hit
* optimised paths through pixman. Should we increase the bit
* depth of the target surface, we should reconsider the appropriate
* mask formats.
*/
 
status = _cairo_scaled_glyph_lookup (info->font,
info->glyphs[0].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (unlikely (status)) {
pixman_image_unref (white);
return status;
}
 
memset (glyph_cache, 0, sizeof (glyph_cache));
glyph_cache[info->glyphs[0].index % ARRAY_LENGTH (glyph_cache)] = scaled_glyph;
 
format = PIXMAN_a8;
i = (info->extents.width + 3) & ~3;
if (scaled_glyph->surface->base.content & CAIRO_CONTENT_COLOR) {
format = PIXMAN_a8r8g8b8;
i = info->extents.width * 4;
}
 
if (i * info->extents.height > (int) sizeof (buf)) {
mask = pixman_image_create_bits (format,
info->extents.width,
info->extents.height,
NULL, 0);
} else {
memset (buf, 0, i * info->extents.height);
mask = pixman_image_create_bits (format,
info->extents.width,
info->extents.height,
(uint32_t *)buf, i);
}
if (unlikely (mask == NULL)) {
pixman_image_unref (white);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
status = CAIRO_STATUS_SUCCESS;
for (i = 0; i < info->num_glyphs; i++) {
unsigned long glyph_index = info->glyphs[i].index;
int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
cairo_image_surface_t *glyph_surface;
int x, y;
 
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
{
status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status)) {
pixman_image_unref (mask);
pixman_image_unref (white);
return status;
}
 
glyph_cache[cache_index] = scaled_glyph;
}
 
glyph_surface = scaled_glyph->surface;
if (glyph_surface->width && glyph_surface->height) {
if (glyph_surface->base.content & CAIRO_CONTENT_COLOR &&
format == PIXMAN_a8) {
pixman_image_t *ca_mask;
 
format = PIXMAN_a8r8g8b8;
ca_mask = pixman_image_create_bits (format,
info->extents.width,
info->extents.height,
NULL, 0);
if (unlikely (ca_mask == NULL)) {
pixman_image_unref (mask);
pixman_image_unref (white);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (PIXMAN_OP_SRC,
white, mask, ca_mask,
0, 0,
0, 0,
0, 0,
info->extents.width,
info->extents.height);
pixman_image_unref (mask);
mask = ca_mask;
}
 
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (info->glyphs[i].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (info->glyphs[i].y -
glyph_surface->base.device_transform.y0);
 
if (glyph_surface->pixman_format == format) {
pixman_image_composite32 (PIXMAN_OP_ADD,
glyph_surface->pixman_image, NULL, mask,
0, 0,
0, 0,
x - info->extents.x, y - info->extents.y,
glyph_surface->width,
glyph_surface->height);
} else {
pixman_image_composite32 (PIXMAN_OP_ADD,
white, glyph_surface->pixman_image, mask,
0, 0,
0, 0,
x - info->extents.x, y - info->extents.y,
glyph_surface->width,
glyph_surface->height);
}
}
}
 
if (format == PIXMAN_a8r8g8b8)
pixman_image_set_component_alpha (mask, TRUE);
 
pixman_image_composite32 (_pixman_operator (op),
((cairo_image_source_t *)_src)->pixman_image,
mask,
to_pixman_image (_dst),
info->extents.x + src_x, info->extents.y + src_y,
0, 0,
info->extents.x - dst_x, info->extents.y - dst_y,
info->extents.width, info->extents.height);
pixman_image_unref (mask);
pixman_image_unref (white);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_glyphs (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_scaled_glyph_t *glyph_cache[64];
pixman_image_t *dst, *src;
cairo_status_t status;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (info->num_glyphs == 1)
return composite_one_glyph(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
 
if (info->use_mask)
return composite_glyphs_via_mask(_dst, op, _src, src_x, src_y, dst_x, dst_y, info);
 
op = _pixman_operator (op);
dst = to_pixman_image (_dst);
src = ((cairo_image_source_t *)_src)->pixman_image;
 
memset (glyph_cache, 0, sizeof (glyph_cache));
status = CAIRO_STATUS_SUCCESS;
 
for (i = 0; i < info->num_glyphs; i++) {
int x, y;
cairo_image_surface_t *glyph_surface;
cairo_scaled_glyph_t *scaled_glyph;
unsigned long glyph_index = info->glyphs[i].index;
int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
 
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
{
status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status))
break;
 
glyph_cache[cache_index] = scaled_glyph;
}
 
glyph_surface = scaled_glyph->surface;
if (glyph_surface->width && glyph_surface->height) {
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (info->glyphs[i].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (info->glyphs[i].y -
glyph_surface->base.device_transform.y0);
 
pixman_image_composite32 (op, src, glyph_surface->pixman_image, dst,
x + src_x, y + src_y,
0, 0,
x - dst_x, y - dst_y,
glyph_surface->width,
glyph_surface->height);
}
}
 
return status;
}
#endif
 
static cairo_int_status_t
check_composite (const cairo_composite_rectangles_t *extents)
{
return CAIRO_STATUS_SUCCESS;
}
 
const cairo_compositor_t *
_cairo_image_traps_compositor_get (void)
{
static cairo_traps_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_traps_compositor_init (&compositor,
&__cairo_no_compositor);
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
//compositor.copy_boxes = copy_boxes;
compositor.fill_boxes = fill_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_composite_traps = check_composite_traps;
compositor.composite_traps = composite_traps;
//compositor.check_composite_tristrip = check_composite_traps;
#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0)
compositor.composite_tristrip = composite_tristrip;
#endif
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
 
return &compositor.base;
}
 
const cairo_compositor_t *
_cairo_image_mask_compositor_get (void)
{
static cairo_mask_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_mask_compositor_init (&compositor,
_cairo_image_traps_compositor_get ());
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_image_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
compositor.fill_rectangles = fill_rectangles;
compositor.fill_boxes = fill_boxes;
//compositor.check_composite = check_composite;
compositor.composite = composite;
//compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
 
return &compositor.base;
}
 
#if PIXMAN_HAS_COMPOSITOR
typedef struct _cairo_image_span_renderer {
cairo_span_renderer_t base;
 
pixman_image_compositor_t *compositor;
pixman_image_t *src, *mask;
float opacity;
cairo_rectangle_int_t extents;
} cairo_image_span_renderer_t;
COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
 
static cairo_status_t
_cairo_image_bounded_opaque_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage)
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
spans[1].x - spans[0].x, height,
spans[0].coverage);
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_bounded_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage) {
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
spans[1].x - spans[0].x, height,
r->opacity * spans[0].coverage);
}
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_unbounded_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
assert (y + height <= r->extents.height);
if (y > r->extents.y) {
pixman_image_compositor_blt (r->compositor,
r->extents.x, r->extents.y,
r->extents.width, y - r->extents.y,
0);
}
 
if (num_spans == 0) {
pixman_image_compositor_blt (r->compositor,
r->extents.x, y,
r->extents.width, height,
0);
} else {
if (spans[0].x != r->extents.x) {
pixman_image_compositor_blt (r->compositor,
r->extents.x, y,
spans[0].x - r->extents.x,
height,
0);
}
 
do {
assert (spans[0].x < r->extents.x + r->extents.width);
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
spans[1].x - spans[0].x, height,
r->opacity * spans[0].coverage);
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != r->extents.x + r->extents.width) {
assert (spans[0].x < r->extents.x + r->extents.width);
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
r->extents.x + r->extents.width - spans[0].x, height,
0);
}
}
 
r->extents.y = y + height;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_clipped_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
assert (num_spans);
 
do {
if (! spans[0].inverse)
pixman_image_compositor_blt (r->compositor,
spans[0].x, y,
spans[1].x - spans[0].x, height,
r->opacity * spans[0].coverage);
spans++;
} while (--num_spans > 1);
 
r->extents.y = y + height;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_finish_unbounded_spans (void *abstract_renderer)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (r->extents.y < r->extents.height) {
pixman_image_compositor_blt (r->compositor,
r->extents.x, r->extents.y,
r->extents.width,
r->extents.height - r->extents.y,
0);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
span_renderer_init (cairo_abstract_span_renderer_t *_r,
const cairo_composite_rectangles_t *composite,
cairo_bool_t needs_clip)
{
cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
const cairo_pattern_t *source = &composite->source_pattern.base;
cairo_operator_t op = composite->op;
int src_x, src_y;
int mask_x, mask_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (op == CAIRO_OPERATOR_CLEAR) {
op = PIXMAN_OP_LERP_CLEAR;
} else if (dst->base.is_clear &&
(op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_OVER ||
op == CAIRO_OPERATOR_ADD)) {
op = PIXMAN_OP_SRC;
} else if (op == CAIRO_OPERATOR_SOURCE) {
op = PIXMAN_OP_LERP_SRC;
} else {
op = _pixman_operator (op);
}
 
r->compositor = NULL;
r->mask = NULL;
r->src = _pixman_image_for_pattern (dst, source, FALSE,
&composite->unbounded,
&composite->source_sample_area,
&src_x, &src_y);
if (unlikely (r->src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
r->opacity = 1.0;
if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
r->opacity = composite->mask_pattern.solid.color.alpha;
} else {
r->mask = _pixman_image_for_pattern (dst,
&composite->mask_pattern.base,
TRUE,
&composite->unbounded,
&composite->mask_sample_area,
&mask_x, &mask_y);
if (unlikely (r->mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* XXX Component-alpha? */
if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
_cairo_pattern_is_opaque (source, &composite->source_sample_area))
{
pixman_image_unref (r->src);
r->src = r->mask;
src_x = mask_x;
src_y = mask_y;
r->mask = NULL;
}
}
 
if (composite->is_bounded) {
if (r->opacity == 1.)
r->base.render_rows = _cairo_image_bounded_opaque_spans;
else
r->base.render_rows = _cairo_image_bounded_spans;
r->base.finish = NULL;
} else {
if (needs_clip)
r->base.render_rows = _cairo_image_clipped_spans;
else
r->base.render_rows = _cairo_image_unbounded_spans;
r->base.finish = _cairo_image_finish_unbounded_spans;
r->extents = composite->unbounded;
r->extents.height += r->extents.y;
}
 
r->compositor =
pixman_image_create_compositor (op, r->src, r->mask, dst->pixman_image,
composite->unbounded.x + src_x,
composite->unbounded.y + src_y,
composite->unbounded.x + mask_x,
composite->unbounded.y + mask_y,
composite->unbounded.x,
composite->unbounded.y,
composite->unbounded.width,
composite->unbounded.height);
if (unlikely (r->compositor == NULL))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
span_renderer_fini (cairo_abstract_span_renderer_t *_r,
cairo_int_status_t status)
{
cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (status == CAIRO_INT_STATUS_SUCCESS && r->base.finish)
r->base.finish (r);
 
if (r->compositor)
pixman_image_compositor_destroy (r->compositor);
 
if (r->src)
pixman_image_unref (r->src);
if (r->mask)
pixman_image_unref (r->mask);
}
#else
typedef struct _cairo_image_span_renderer {
cairo_span_renderer_t base;
 
const cairo_composite_rectangles_t *composite;
 
float opacity;
uint8_t op;
int bpp;
 
pixman_image_t *src, *mask;
union {
struct fill {
int stride;
uint8_t *data;
uint32_t pixel;
} fill;
struct blit {
int stride;
uint8_t *data;
int src_stride;
uint8_t *src_data;
} blit;
struct composite {
pixman_image_t *dst;
int src_x, src_y;
int mask_x, mask_y;
int run_length;
} composite;
struct finish {
cairo_rectangle_int_t extents;
int src_x, src_y;
int stride;
uint8_t *data;
} mask;
} u;
uint8_t _buf[0];
#define SZ_BUF (int)(sizeof (cairo_abstract_span_renderer_t) - sizeof (cairo_image_span_renderer_t))
} cairo_image_span_renderer_t;
COMPILE_TIME_ASSERT (sizeof (cairo_image_span_renderer_t) <= sizeof (cairo_abstract_span_renderer_t));
 
static cairo_status_t
_cairo_image_spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *mask, *row;
int len;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
mask = r->u.mask.data + (y - r->u.mask.extents.y) * r->u.mask.stride;
mask += spans[0].x - r->u.mask.extents.x;
row = mask;
 
do {
len = spans[1].x - spans[0].x;
if (spans[0].coverage) {
*row++ = r->opacity * spans[0].coverage;
if (--len)
memset (row, row[-1], len);
}
row += len;
spans++;
} while (--num_spans > 1);
 
len = row - mask;
row = mask;
while (--height) {
mask += r->u.mask.stride;
memcpy (mask, row, len);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_spans_and_zero (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *mask;
int len;
 
mask = r->u.mask.data;
if (y > r->u.mask.extents.y) {
len = (y - r->u.mask.extents.y) * r->u.mask.stride;
memset (mask, 0, len);
mask += len;
}
 
r->u.mask.extents.y = y + height;
r->u.mask.data = mask + height * r->u.mask.stride;
if (num_spans == 0) {
memset (mask, 0, height * r->u.mask.stride);
} else {
uint8_t *row = mask;
 
if (spans[0].x != r->u.mask.extents.x) {
len = spans[0].x - r->u.mask.extents.x;
memset (row, 0, len);
row += len;
}
 
do {
len = spans[1].x - spans[0].x;
*row++ = r->opacity * spans[0].coverage;
if (len > 1) {
memset (row, row[-1], --len);
row += len;
}
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != r->u.mask.extents.x + r->u.mask.extents.width) {
len = r->u.mask.extents.x + r->u.mask.extents.width - spans[0].x;
memset (row, 0, len);
}
 
row = mask;
while (--height) {
mask += r->u.mask.stride;
memcpy (mask, row, r->u.mask.extents.width);
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_finish_spans_and_zero (void *abstract_renderer)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (r->u.mask.extents.y < r->u.mask.extents.height)
memset (r->u.mask.data, 0, (r->u.mask.extents.height - r->u.mask.extents.y) * r->u.mask.stride);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill8_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
if (spans[0].coverage) {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
if (len == 1)
*d = r->u.fill.pixel;
else
memset(d, r->u.fill.pixel, len);
}
spans++;
} while (--num_spans > 1);
} else {
do {
if (spans[0].coverage) {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
if (len == 1)
*d = r->u.fill.pixel;
else
memset(d, r->u.fill.pixel, len);
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill16_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
if (spans[0].coverage) {
int len = spans[1].x - spans[0].x;
uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*2);
while (len--)
*d++ = r->u.fill.pixel;
}
spans++;
} while (--num_spans > 1);
} else {
do {
if (spans[0].coverage) {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint16_t *d = (uint16_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*2);
while (len--)
*d++ = r->u.fill.pixel;
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill32_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
if (spans[0].coverage) {
int len = spans[1].x - spans[0].x;
if (len > 32) {
pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
spans[0].x, y, len, 1, r->u.fill.pixel);
} else {
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
while (len--)
*d++ = r->u.fill.pixel;
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
if (spans[0].coverage) {
if (spans[1].x - spans[0].x > 16) {
pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), r->bpp,
spans[0].x, y, spans[1].x - spans[0].x, h,
r->u.fill.pixel);
} else {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
while (len--)
*d++ = r->u.fill.pixel;
yy++;
} while (--hh);
}
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
#if 0
static cairo_status_t
_fill_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage) {
pixman_fill ((uint32_t *) r->data, r->stride, r->bpp,
spans[0].x, y,
spans[1].x - spans[0].x, h,
r->pixel);
}
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
#endif
 
static cairo_status_t
_blit_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
int cpp;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
cpp = r->bpp/8;
if (likely (h == 1)) {
uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride;
uint8_t *dst = r->u.blit.data + y*r->u.blit.stride;
do {
if (spans[0].coverage) {
void *s = src + spans[0].x*cpp;
void *d = dst + spans[0].x*cpp;
int len = (spans[1].x - spans[0].x) * cpp;
switch (len) {
case 1:
*(uint8_t *)d = *(uint8_t *)s;
break;
case 2:
*(uint16_t *)d = *(uint16_t *)s;
break;
case 4:
*(uint32_t *)d = *(uint32_t *)s;
break;
#if HAVE_UINT64_T
case 8:
*(uint64_t *)d = *(uint64_t *)s;
break;
#endif
default:
memcpy(d, s, len);
break;
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
if (spans[0].coverage) {
int yy = y, hh = h;
do {
void *src = r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x*cpp;
void *dst = r->u.blit.data + yy*r->u.blit.stride + spans[0].x*cpp;
int len = (spans[1].x - spans[0].x) * cpp;
switch (len) {
case 1:
*(uint8_t *)dst = *(uint8_t *)src;
break;
case 2:
*(uint16_t *)dst = *(uint16_t *)src;
break;
case 4:
*(uint32_t *)dst = *(uint32_t *)src;
break;
#if HAVE_UINT64_T
case 8:
*(uint64_t *)dst = *(uint64_t *)src;
break;
#endif
default:
memcpy(dst, src, len);
break;
}
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_mono_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
do {
if (spans[0].coverage) {
pixman_image_composite32 (r->op,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
spans[1].x - spans[0].x, h);
}
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_mono_unbounded_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0) {
pixman_image_composite32 (PIXMAN_OP_CLEAR,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
r->composite->unbounded.x, y,
r->composite->unbounded.width, h);
r->u.composite.mask_y = y + h;
return CAIRO_STATUS_SUCCESS;
}
 
if (y != r->u.composite.mask_y) {
pixman_image_composite32 (PIXMAN_OP_CLEAR,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
r->composite->unbounded.x, r->u.composite.mask_y,
r->composite->unbounded.width, y - r->u.composite.mask_y);
}
 
if (spans[0].x != r->composite->unbounded.x) {
pixman_image_composite32 (PIXMAN_OP_CLEAR,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
r->composite->unbounded.x, y,
spans[0].x - r->composite->unbounded.x, h);
}
 
do {
int op = spans[0].coverage ? r->op : PIXMAN_OP_CLEAR;
pixman_image_composite32 (op,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
spans[1].x - spans[0].x, h);
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != r->composite->unbounded.x + r->composite->unbounded.width) {
pixman_image_composite32 (PIXMAN_OP_CLEAR,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x, y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
r->composite->unbounded.x + r->composite->unbounded.width - spans[0].x, h);
}
 
r->u.composite.mask_y = y + h;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_mono_finish_unbounded_spans (void *abstract_renderer)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (r->u.composite.mask_y < r->composite->unbounded.y + r->composite->unbounded.height) {
pixman_image_composite32 (PIXMAN_OP_CLEAR,
r->src, NULL, r->u.composite.dst,
r->composite->unbounded.x + r->u.composite.src_x, r->u.composite.mask_y + r->u.composite.src_y,
0, 0,
r->composite->unbounded.x, r->u.composite.mask_y,
r->composite->unbounded.width,
r->composite->unbounded.y + r->composite->unbounded.height - r->u.composite.mask_y);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
mono_renderer_init (cairo_image_span_renderer_t *r,
const cairo_composite_rectangles_t *composite,
cairo_antialias_t antialias,
cairo_bool_t needs_clip)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
 
if (antialias != CAIRO_ANTIALIAS_NONE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (!_cairo_pattern_is_opaque_solid (&composite->mask_pattern.base))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
r->base.render_rows = NULL;
if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
const cairo_color_t *color;
 
color = &composite->source_pattern.solid.color;
if (composite->op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
 
if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) {
/* Use plain C for the fill operations as the span length is
* typically small, too small to payback the startup overheads of
* using SSE2 etc.
*/
switch (PIXMAN_FORMAT_BPP(dst->pixman_format)) {
case 8: r->base.render_rows = _fill8_spans; break;
case 16: r->base.render_rows = _fill16_spans; break;
case 32: r->base.render_rows = _fill32_spans; break;
default: break;
}
r->u.fill.data = dst->data;
r->u.fill.stride = dst->stride;
}
} else if ((composite->op == CAIRO_OPERATOR_SOURCE ||
(composite->op == CAIRO_OPERATOR_OVER &&
(dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
{
cairo_image_surface_t *src =
to_image_surface(composite->source_pattern.surface.surface);
int tx, ty;
 
if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
&tx, &ty) &&
composite->bounded.x + tx >= 0 &&
composite->bounded.y + ty >= 0 &&
composite->bounded.x + composite->bounded.width + tx <= src->width &&
composite->bounded.y + composite->bounded.height + ty <= src->height) {
 
r->u.blit.stride = dst->stride;
r->u.blit.data = dst->data;
r->u.blit.src_stride = src->stride;
r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
r->base.render_rows = _blit_spans;
}
}
 
if (r->base.render_rows == NULL) {
r->src = _pixman_image_for_pattern (dst, &composite->source_pattern.base, FALSE,
&composite->unbounded,
&composite->source_sample_area,
&r->u.composite.src_x, &r->u.composite.src_y);
if (unlikely (r->src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
r->u.composite.dst = to_pixman_image (composite->surface);
r->op = _pixman_operator (composite->op);
if (composite->is_bounded == 0) {
r->base.render_rows = _mono_unbounded_spans;
r->base.finish = _mono_finish_unbounded_spans;
r->u.composite.mask_y = composite->unbounded.y;
} else
r->base.render_rows = _mono_spans;
}
r->bpp = PIXMAN_FORMAT_BPP(dst->pixman_format);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
#define ONE_HALF 0x7f
#define RB_MASK 0x00ff00ff
#define RB_ONE_HALF 0x007f007f
#define RB_MASK_PLUS_ONE 0x01000100
#define G_SHIFT 8
static inline uint32_t
mul8x2_8 (uint32_t a, uint8_t b)
{
uint32_t t = (a & RB_MASK) * b + RB_ONE_HALF;
return ((t + ((t >> G_SHIFT) & RB_MASK)) >> G_SHIFT) & RB_MASK;
}
 
static inline uint32_t
add8x2_8x2 (uint32_t a, uint32_t b)
{
uint32_t t = a + b;
t |= RB_MASK_PLUS_ONE - ((t >> G_SHIFT) & RB_MASK);
return t & RB_MASK;
}
 
static inline uint8_t
mul8_8 (uint8_t a, uint8_t b)
{
uint16_t t = a * (uint16_t)b + ONE_HALF;
return ((t >> G_SHIFT) + t) >> G_SHIFT;
}
 
static inline uint32_t
lerp8x4 (uint32_t src, uint8_t a, uint32_t dst)
{
return (add8x2_8x2 (mul8x2_8 (src, a),
mul8x2_8 (dst, ~a)) |
add8x2_8x2 (mul8x2_8 (src >> G_SHIFT, a),
mul8x2_8 (dst >> G_SHIFT, ~a)) << G_SHIFT);
}
 
static cairo_status_t
_fill_a8_lerp_opaque_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
uint8_t *d = r->u.fill.data + r->u.fill.stride*y;
do {
uint8_t a = spans[0].coverage;
if (a) {
int len = spans[1].x - spans[0].x;
if (a == 0xff) {
memset(d + spans[0].x, r->u.fill.pixel, len);
} else {
uint8_t s = mul8_8(a, r->u.fill.pixel);
uint8_t *dst = d + spans[0].x;
a = ~a;
while (len--) {
uint8_t t = mul8_8(*dst, a);
*dst++ = t + s;
}
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
uint8_t a = spans[0].coverage;
if (a) {
int yy = y, hh = h;
if (a == 0xff) {
do {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
memset(d, r->u.fill.pixel, len);
yy++;
} while (--hh);
} else {
uint8_t s = mul8_8(a, r->u.fill.pixel);
a = ~a;
do {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
while (len--) {
uint8_t t = mul8_8(*d, a);
*d++ = t + s;
}
yy++;
} while (--hh);
}
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill_xrgb32_lerp_opaque_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
uint8_t a = spans[0].coverage;
if (a) {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
if (a == 0xff) {
if (len > 31) {
pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
spans[0].x, y, len, 1, r->u.fill.pixel);
} else {
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
while (len--)
*d++ = r->u.fill.pixel;
}
} else while (len--) {
*d = lerp8x4 (r->u.fill.pixel, a, *d);
d++;
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
uint8_t a = spans[0].coverage;
if (a) {
if (a == 0xff) {
if (spans[1].x - spans[0].x > 16) {
pixman_fill ((uint32_t *)r->u.fill.data, r->u.fill.stride / sizeof(uint32_t), 32,
spans[0].x, y, spans[1].x - spans[0].x, h,
r->u.fill.pixel);
} else {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
while (len--)
*d++ = r->u.fill.pixel;
yy++;
} while (--hh);
}
} else {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
while (len--) {
*d = lerp8x4 (r->u.fill.pixel, a, *d);
d++;
}
yy++;
} while (--hh);
}
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill_a8_lerp_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*y + spans[0].x;
uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
uint16_t ia = ~a;
while (len--) {
uint16_t t = *d*ia + p;
*d++ = (t + (t>>8)) >> 8;
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
int yy = y, hh = h;
uint16_t p = (uint16_t)a * r->u.fill.pixel + 0x7f;
uint16_t ia = ~a;
do {
int len = spans[1].x - spans[0].x;
uint8_t *d = r->u.fill.data + r->u.fill.stride*yy + spans[0].x;
while (len--) {
uint16_t t = *d*ia + p;
*d++ = (t + (t>>8)) >> 8;
}
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_fill_xrgb32_lerp_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t*)(r->u.fill.data + r->u.fill.stride*y + spans[0].x*4);
while (len--) {
*d = lerp8x4 (r->u.fill.pixel, a, *d);
d++;
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
int yy = y, hh = h;
do {
int len = spans[1].x - spans[0].x;
uint32_t *d = (uint32_t *)(r->u.fill.data + r->u.fill.stride*yy + spans[0].x*4);
while (len--) {
*d = lerp8x4 (r->u.fill.pixel, a, *d);
d++;
}
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_blit_xrgb32_lerp_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (likely(h == 1)) {
uint8_t *src = r->u.blit.src_data + y*r->u.blit.src_stride;
uint8_t *dst = r->u.blit.data + y*r->u.blit.stride;
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
uint32_t *s = (uint32_t*)src + spans[0].x;
uint32_t *d = (uint32_t*)dst + spans[0].x;
int len = spans[1].x - spans[0].x;
if (a == 0xff) {
if (len == 1)
*d = *s;
else
memcpy(d, s, len*4);
} else {
while (len--) {
*d = lerp8x4 (*s, a, *d);
s++, d++;
}
}
}
spans++;
} while (--num_spans > 1);
} else {
do {
uint8_t a = mul8_8 (spans[0].coverage, r->bpp);
if (a) {
int yy = y, hh = h;
do {
uint32_t *s = (uint32_t *)(r->u.blit.src_data + yy*r->u.blit.src_stride + spans[0].x * 4);
uint32_t *d = (uint32_t *)(r->u.blit.data + yy*r->u.blit.stride + spans[0].x * 4);
int len = spans[1].x - spans[0].x;
if (a == 0xff) {
if (len == 1)
*d = *s;
else
memcpy(d, s, len * 4);
} else {
while (len--) {
*d = lerp8x4 (*s, a, *d);
s++, d++;
}
}
yy++;
} while (--hh);
}
spans++;
} while (--num_spans > 1);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_inplace_spans (void *abstract_renderer,
int y, int h,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *mask;
int x0, x1;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
if (num_spans == 2 && spans[0].coverage == 0xff) {
pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
spans[1].x - spans[0].x, h);
return CAIRO_STATUS_SUCCESS;
}
 
mask = (uint8_t *)pixman_image_get_data (r->mask);
x1 = x0 = spans[0].x;
do {
int len = spans[1].x - spans[0].x;
*mask++ = spans[0].coverage;
if (len > 1) {
if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
if (x1 != x0) {
pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
x1 - x0, h);
}
pixman_image_composite32 (r->op, r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
len, h);
mask = (uint8_t *)pixman_image_get_data (r->mask);
x0 = spans[1].x;
} else if (spans[0].coverage == 0x0 &&
x1 - x0 > r->u.composite.run_length) {
pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
x1 - x0, h);
mask = (uint8_t *)pixman_image_get_data (r->mask);
x0 = spans[1].x;
}else {
memset (mask, spans[0].coverage, --len);
mask += len;
}
}
x1 = spans[1].x;
spans++;
} while (--num_spans > 1);
 
if (x1 != x0) {
pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
x1 - x0, h);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_inplace_opacity_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *mask;
int x0, x1;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
mask = (uint8_t *)pixman_image_get_data (r->mask);
x1 = x0 = spans[0].x;
do {
int len = spans[1].x - spans[0].x;
uint8_t m = mul8_8(spans[0].coverage, r->bpp);
*mask++ = m;
if (len > 1) {
if (m == 0 &&
x1 - x0 > r->u.composite.run_length) {
pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
x1 - x0, h);
mask = (uint8_t *)pixman_image_get_data (r->mask);
x0 = spans[1].x;
}else {
memset (mask, m, --len);
mask += len;
}
}
x1 = spans[1].x;
spans++;
} while (--num_spans > 1);
 
if (x1 != x0) {
pixman_image_composite32 (r->op, r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
x1 - x0, h);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_inplace_src_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *m;
int x0;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
x0 = spans[0].x;
m = r->_buf;
do {
int len = spans[1].x - spans[0].x;
if (len >= r->u.composite.run_length && spans[0].coverage == 0xff) {
if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#else
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
r->mask, NULL, r->u.composite.dst,
0, 0,
0, 0,
x0, y,
spans[0].x - x0, h);
pixman_image_composite32 (PIXMAN_OP_ADD,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#endif
}
 
pixman_image_composite32 (PIXMAN_OP_SRC,
r->src, NULL, r->u.composite.dst,
spans[0].x + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
spans[0].x, y,
spans[1].x - spans[0].x, h);
 
m = r->_buf;
x0 = spans[1].x;
} else if (spans[0].coverage == 0x0) {
if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#else
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
r->mask, NULL, r->u.composite.dst,
0, 0,
0, 0,
x0, y,
spans[0].x - x0, h);
pixman_image_composite32 (PIXMAN_OP_ADD,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#endif
}
 
m = r->_buf;
x0 = spans[1].x;
} else {
*m++ = spans[0].coverage;
if (len > 1) {
memset (m, spans[0].coverage, --len);
m += len;
}
}
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#else
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
r->mask, NULL, r->u.composite.dst,
0, 0,
0, 0,
x0, y,
spans[0].x - x0, h);
pixman_image_composite32 (PIXMAN_OP_ADD,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#endif
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_inplace_src_opacity_spans (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
cairo_image_span_renderer_t *r = abstract_renderer;
uint8_t *mask;
int x0;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
x0 = spans[0].x;
mask = (uint8_t *)pixman_image_get_data (r->mask);
do {
int len = spans[1].x - spans[0].x;
uint8_t m = mul8_8(spans[0].coverage, r->bpp);
if (m == 0) {
if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#else
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
r->mask, NULL, r->u.composite.dst,
0, 0,
0, 0,
x0, y,
spans[0].x - x0, h);
pixman_image_composite32 (PIXMAN_OP_ADD,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#endif
}
 
mask = (uint8_t *)pixman_image_get_data (r->mask);
x0 = spans[1].x;
} else {
*mask++ = m;
if (len > 1) {
memset (mask, m, --len);
mask += len;
}
}
spans++;
} while (--num_spans > 1);
 
if (spans[0].x != x0) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP_SRC,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#else
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
r->mask, NULL, r->u.composite.dst,
0, 0,
0, 0,
x0, y,
spans[0].x - x0, h);
pixman_image_composite32 (PIXMAN_OP_ADD,
r->src, r->mask, r->u.composite.dst,
x0 + r->u.composite.src_x,
y + r->u.composite.src_y,
0, 0,
x0, y,
spans[0].x - x0, h);
#endif
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void free_pixels (pixman_image_t *image, void *data)
{
free (data);
}
 
static cairo_int_status_t
inplace_renderer_init (cairo_image_span_renderer_t *r,
const cairo_composite_rectangles_t *composite,
cairo_antialias_t antialias,
cairo_bool_t needs_clip)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
uint8_t *buf;
 
if (composite->mask_pattern.base.type != CAIRO_PATTERN_TYPE_SOLID)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
r->base.render_rows = NULL;
r->bpp = composite->mask_pattern.solid.color.alpha_short >> 8;
 
if (composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
const cairo_color_t *color;
 
color = &composite->source_pattern.solid.color;
if (composite->op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
 
if (fill_reduces_to_source (composite->op, color, dst, &r->u.fill.pixel)) {
/* Use plain C for the fill operations as the span length is
* typically small, too small to payback the startup overheads of
* using SSE2 etc.
*/
if (r->bpp == 0xff) {
switch (dst->format) {
case CAIRO_FORMAT_A8:
r->base.render_rows = _fill_a8_lerp_opaque_spans;
break;
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_ARGB32:
r->base.render_rows = _fill_xrgb32_lerp_opaque_spans;
break;
case CAIRO_FORMAT_A1:
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_INVALID:
default: break;
}
} else {
switch (dst->format) {
case CAIRO_FORMAT_A8:
r->base.render_rows = _fill_a8_lerp_spans;
break;
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_ARGB32:
r->base.render_rows = _fill_xrgb32_lerp_spans;
break;
case CAIRO_FORMAT_A1:
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_INVALID:
default: break;
}
}
r->u.fill.data = dst->data;
r->u.fill.stride = dst->stride;
}
} else if ((dst->format == CAIRO_FORMAT_ARGB32 || dst->format == CAIRO_FORMAT_RGB24) &&
(composite->op == CAIRO_OPERATOR_SOURCE ||
(composite->op == CAIRO_OPERATOR_OVER &&
(dst->base.is_clear || (dst->base.content & CAIRO_CONTENT_ALPHA) == 0))) &&
composite->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
composite->source_pattern.surface.surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE &&
to_image_surface(composite->source_pattern.surface.surface)->format == dst->format)
{
cairo_image_surface_t *src =
to_image_surface(composite->source_pattern.surface.surface);
int tx, ty;
 
if (_cairo_matrix_is_integer_translation(&composite->source_pattern.base.matrix,
&tx, &ty) &&
composite->bounded.x + tx >= 0 &&
composite->bounded.y + ty >= 0 &&
composite->bounded.x + composite->bounded.width + tx <= src->width &&
composite->bounded.y + composite->bounded.height + ty <= src->height) {
 
assert(PIXMAN_FORMAT_BPP(dst->pixman_format) == 32);
r->u.blit.stride = dst->stride;
r->u.blit.data = dst->data;
r->u.blit.src_stride = src->stride;
r->u.blit.src_data = src->data + src->stride * ty + tx * 4;
r->base.render_rows = _blit_xrgb32_lerp_spans;
}
}
if (r->base.render_rows == NULL) {
const cairo_pattern_t *src = &composite->source_pattern.base;
unsigned int width;
 
if (composite->is_bounded == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
r->base.render_rows = r->bpp == 0xff ? _inplace_spans : _inplace_opacity_spans;
width = (composite->bounded.width + 3) & ~3;
 
r->u.composite.run_length = 8;
if (src->type == CAIRO_PATTERN_TYPE_LINEAR ||
src->type == CAIRO_PATTERN_TYPE_RADIAL)
r->u.composite.run_length = 256;
if (dst->base.is_clear &&
(composite->op == CAIRO_OPERATOR_SOURCE ||
composite->op == CAIRO_OPERATOR_OVER ||
composite->op == CAIRO_OPERATOR_ADD)) {
r->op = PIXMAN_OP_SRC;
} else if (composite->op == CAIRO_OPERATOR_SOURCE) {
r->base.render_rows = r->bpp == 0xff ? _inplace_src_spans : _inplace_src_opacity_spans;
r->u.composite.mask_y = r->composite->unbounded.y;
width = (composite->unbounded.width + 3) & ~3;
} else if (composite->op == CAIRO_OPERATOR_CLEAR) {
r->op = PIXMAN_OP_OUT_REVERSE;
src = NULL;
} else {
r->op = _pixman_operator (composite->op);
}
 
r->src = _pixman_image_for_pattern (dst, src, FALSE,
&composite->bounded,
&composite->source_sample_area,
&r->u.composite.src_x, &r->u.composite.src_y);
if (unlikely (r->src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* Create an effectively unbounded mask by repeating the single line */
buf = r->_buf;
if (width > SZ_BUF) {
buf = malloc (width);
if (unlikely (buf == NULL)) {
pixman_image_unref (r->src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
r->mask = pixman_image_create_bits (PIXMAN_a8,
width, composite->unbounded.height,
(uint32_t *)buf, 0);
if (unlikely (r->mask == NULL)) {
pixman_image_unref (r->src);
if (buf != r->_buf)
free (buf);
return _cairo_error(CAIRO_STATUS_NO_MEMORY);
}
 
if (buf != r->_buf)
pixman_image_set_destroy_function (r->mask, free_pixels, buf);
 
r->u.composite.dst = dst->pixman_image;
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
span_renderer_init (cairo_abstract_span_renderer_t *_r,
const cairo_composite_rectangles_t *composite,
cairo_antialias_t antialias,
cairo_bool_t needs_clip)
{
cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r;
cairo_image_surface_t *dst = (cairo_image_surface_t *)composite->surface;
const cairo_pattern_t *source = &composite->source_pattern.base;
cairo_operator_t op = composite->op;
cairo_int_status_t status;
 
TRACE ((stderr, "%s: antialias=%d, needs_clip=%d\n", __FUNCTION__,
antialias, needs_clip));
 
if (needs_clip)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
r->composite = composite;
r->mask = NULL;
r->src = NULL;
r->base.finish = NULL;
 
status = mono_renderer_init (r, composite, antialias, needs_clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = inplace_renderer_init (r, composite, antialias, needs_clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
r->bpp = 0;
 
if (op == CAIRO_OPERATOR_CLEAR) {
#if PIXMAN_HAS_OP_LERP
op = PIXMAN_OP_LERP_CLEAR;
#else
source = &_cairo_pattern_white.base;
op = PIXMAN_OP_OUT_REVERSE;
#endif
} else if (dst->base.is_clear &&
(op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_OVER ||
op == CAIRO_OPERATOR_ADD)) {
op = PIXMAN_OP_SRC;
} else if (op == CAIRO_OPERATOR_SOURCE) {
if (_cairo_pattern_is_opaque (&composite->source_pattern.base,
&composite->source_sample_area))
{
op = PIXMAN_OP_OVER;
}
else
{
#if PIXMAN_HAS_OP_LERP
op = PIXMAN_OP_LERP_SRC;
#else
return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
}
} else {
op = _pixman_operator (op);
}
r->op = op;
 
r->src = _pixman_image_for_pattern (dst, source, FALSE,
&composite->unbounded,
&composite->source_sample_area,
&r->u.mask.src_x, &r->u.mask.src_y);
if (unlikely (r->src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
r->opacity = 1.0;
if (composite->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID) {
r->opacity = composite->mask_pattern.solid.color.alpha;
} else {
pixman_image_t *mask;
int mask_x, mask_y;
 
mask = _pixman_image_for_pattern (dst,
&composite->mask_pattern.base,
TRUE,
&composite->unbounded,
&composite->mask_sample_area,
&mask_x, &mask_y);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* XXX Component-alpha? */
if ((dst->base.content & CAIRO_CONTENT_COLOR) == 0 &&
_cairo_pattern_is_opaque (source, &composite->source_sample_area))
{
pixman_image_unref (r->src);
r->src = mask;
r->u.mask.src_x = mask_x;
r->u.mask.src_y = mask_y;
mask = NULL;
}
 
if (mask) {
pixman_image_unref (mask);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
r->u.mask.extents = composite->unbounded;
r->u.mask.stride = (r->u.mask.extents.width + 3) & ~3;
if (r->u.mask.extents.height * r->u.mask.stride > SZ_BUF) {
r->mask = pixman_image_create_bits (PIXMAN_a8,
r->u.mask.extents.width,
r->u.mask.extents.height,
NULL, 0);
 
r->base.render_rows = _cairo_image_spans;
r->base.finish = NULL;
} else {
r->mask = pixman_image_create_bits (PIXMAN_a8,
r->u.mask.extents.width,
r->u.mask.extents.height,
(uint32_t *)r->_buf, r->u.mask.stride);
 
r->base.render_rows = _cairo_image_spans_and_zero;
r->base.finish = _cairo_image_finish_spans_and_zero;
}
if (unlikely (r->mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
r->u.mask.data = (uint8_t *) pixman_image_get_data (r->mask);
r->u.mask.stride = pixman_image_get_stride (r->mask);
 
r->u.mask.extents.height += r->u.mask.extents.y;
return CAIRO_STATUS_SUCCESS;
}
 
static void
span_renderer_fini (cairo_abstract_span_renderer_t *_r,
cairo_int_status_t status)
{
cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) _r;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
if (r->base.finish)
r->base.finish (r);
}
if (likely (status == CAIRO_INT_STATUS_SUCCESS && r->bpp == 0)) {
const cairo_composite_rectangles_t *composite = r->composite;
 
pixman_image_composite32 (r->op, r->src, r->mask,
to_pixman_image (composite->surface),
composite->unbounded.x + r->u.mask.src_x,
composite->unbounded.y + r->u.mask.src_y,
0, 0,
composite->unbounded.x,
composite->unbounded.y,
composite->unbounded.width,
composite->unbounded.height);
}
 
if (r->src)
pixman_image_unref (r->src);
if (r->mask)
pixman_image_unref (r->mask);
}
#endif
 
const cairo_compositor_t *
_cairo_image_spans_compositor_get (void)
{
static cairo_spans_compositor_t spans;
static cairo_compositor_t shape;
 
if (spans.base.delegate == NULL) {
_cairo_shape_mask_compositor_init (&shape,
_cairo_image_traps_compositor_get());
shape.glyphs = NULL;
 
_cairo_spans_compositor_init (&spans, &shape);
 
spans.flags = 0;
#if PIXMAN_HAS_OP_LERP
spans.flags |= CAIRO_SPANS_COMPOSITOR_HAS_LERP;
#endif
 
//spans.acquire = acquire;
//spans.release = release;
spans.fill_boxes = fill_boxes;
spans.draw_image_boxes = draw_image_boxes;
//spans.copy_boxes = copy_boxes;
spans.pattern_to_surface = _cairo_image_source_create_for_pattern;
//spans.check_composite_boxes = check_composite_boxes;
spans.composite_boxes = composite_boxes;
//spans.check_span_renderer = check_span_renderer;
spans.renderer_init = span_renderer_init;
spans.renderer_fini = span_renderer_fini;
}
 
return &spans.base;
}
/programs/develop/libraries/cairo/src/cairo-image-info.c
34,6 → 34,8
*/
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
 
static uint32_t
/programs/develop/libraries/cairo/src/cairo-image-source.c
0,0 → 1,1187
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
* Copyright © 2009,2010,2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* The purpose of this file/surface is to simply translate a pattern
* to a pixman_image_t and thence to feed it back to the general
* compositor interface.
*/
 
#include "cairoint.h"
 
#include "cairo-image-surface-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-error-private.h"
#include "cairo-pattern-inline.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-observer-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
 
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 
#if CAIRO_NO_MUTEX
#define PIXMAN_HAS_ATOMIC_OPS 1
#endif
 
#if PIXMAN_HAS_ATOMIC_OPS
static pixman_image_t *__pixman_transparent_image;
static pixman_image_t *__pixman_black_image;
static pixman_image_t *__pixman_white_image;
 
static pixman_image_t *
_pixman_transparent_image (void)
{
pixman_image_t *image;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = __pixman_transparent_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0x00;
color.green = 0x00;
color.blue = 0x00;
color.alpha = 0x00;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
 
static pixman_image_t *
_pixman_black_image (void)
{
pixman_image_t *image;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = __pixman_black_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0x00;
color.green = 0x00;
color.blue = 0x00;
color.alpha = 0xffff;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
 
static pixman_image_t *
_pixman_white_image (void)
{
pixman_image_t *image;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
image = __pixman_white_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0xffff;
color.green = 0xffff;
color.blue = 0xffff;
color.alpha = 0xffff;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
 
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 struct {
cairo_color_t color;
pixman_image_t *image;
} cache[16];
static int n_cached;
 
#else /* !PIXMAN_HAS_ATOMIC_OPS */
static pixman_image_t *
_pixman_transparent_image (void)
{
TRACE ((stderr, "%s\n", __FUNCTION__));
return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT);
}
 
static pixman_image_t *
_pixman_black_image (void)
{
TRACE ((stderr, "%s\n", __FUNCTION__));
return _pixman_image_for_color (CAIRO_COLOR_BLACK);
}
 
static pixman_image_t *
_pixman_white_image (void)
{
TRACE ((stderr, "%s\n", __FUNCTION__));
return _pixman_image_for_color (CAIRO_COLOR_WHITE);
}
#endif /* !PIXMAN_HAS_ATOMIC_OPS */
 
 
pixman_image_t *
_pixman_image_for_color (const cairo_color_t *cairo_color)
{
pixman_color_t color;
pixman_image_t *image;
 
#if PIXMAN_HAS_ATOMIC_OPS
int i;
 
if (CAIRO_COLOR_IS_CLEAR (cairo_color))
return _pixman_transparent_image ();
 
if (CAIRO_COLOR_IS_OPAQUE (cairo_color)) {
if (cairo_color->red_short <= 0x00ff &&
cairo_color->green_short <= 0x00ff &&
cairo_color->blue_short <= 0x00ff)
{
return _pixman_black_image ();
}
 
if (cairo_color->red_short >= 0xff00 &&
cairo_color->green_short >= 0xff00 &&
cairo_color->blue_short >= 0xff00)
{
return _pixman_white_image ();
}
}
 
CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
for (i = 0; i < n_cached; i++) {
if (_cairo_color_equal (&cache[i].color, cairo_color)) {
image = pixman_image_ref (cache[i].image);
goto UNLOCK;
}
}
#endif
 
color.red = cairo_color->red_short;
color.green = cairo_color->green_short;
color.blue = cairo_color->blue_short;
color.alpha = cairo_color->alpha_short;
 
image = pixman_image_create_solid_fill (&color);
#if PIXMAN_HAS_ATOMIC_OPS
if (image == NULL)
goto UNLOCK;
 
if (n_cached < ARRAY_LENGTH (cache)) {
i = n_cached++;
} else {
i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
pixman_image_unref (cache[i].image);
}
cache[i].image = pixman_image_ref (image);
cache[i].color = *cairo_color;
 
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
#endif
return image;
}
 
 
void
_cairo_image_reset_static_data (void)
{
#if PIXMAN_HAS_ATOMIC_OPS
while (n_cached)
pixman_image_unref (cache[--n_cached].image);
 
if (__pixman_transparent_image) {
pixman_image_unref (__pixman_transparent_image);
__pixman_transparent_image = NULL;
}
 
if (__pixman_black_image) {
pixman_image_unref (__pixman_black_image);
__pixman_black_image = NULL;
}
 
if (__pixman_white_image) {
pixman_image_unref (__pixman_white_image);
__pixman_white_image = NULL;
}
#endif
}
 
static pixman_image_t *
_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int *ix, int *iy)
{
pixman_image_t *pixman_image;
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
pixman_transform_t pixman_transform;
cairo_matrix_t matrix;
cairo_circle_double_t extremes[2];
pixman_point_fixed_t p1, p2;
unsigned int i;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
pixman_stops = _cairo_malloc_ab (pattern->n_stops,
sizeof(pixman_gradient_stop_t));
if (unlikely (pixman_stops == NULL))
return NULL;
}
 
for (i = 0; i < pattern->n_stops; i++) {
pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
pixman_stops[i].color.red = pattern->stops[i].color.red_short;
pixman_stops[i].color.green = pattern->stops[i].color.green_short;
pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
}
 
_cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
 
p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
} else {
pixman_fixed_t r1, r2;
 
r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
 
pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
pixman_stops,
pattern->n_stops);
}
 
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
 
if (unlikely (pixman_image == NULL))
return NULL;
 
*ix = *iy = 0;
status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter,
extents->x + extents->width/2.,
extents->y + extents->height/2.,
&pixman_transform, ix, iy);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
if (unlikely (status != CAIRO_INT_STATUS_SUCCESS) ||
! pixman_image_set_transform (pixman_image, &pixman_transform))
{
pixman_image_unref (pixman_image);
return NULL;
}
}
 
{
pixman_repeat_t pixman_repeat;
 
switch (pattern->base.extend) {
default:
case CAIRO_EXTEND_NONE:
pixman_repeat = PIXMAN_REPEAT_NONE;
break;
case CAIRO_EXTEND_REPEAT:
pixman_repeat = PIXMAN_REPEAT_NORMAL;
break;
case CAIRO_EXTEND_REFLECT:
pixman_repeat = PIXMAN_REPEAT_REFLECT;
break;
case CAIRO_EXTEND_PAD:
pixman_repeat = PIXMAN_REPEAT_PAD;
break;
}
 
pixman_image_set_repeat (pixman_image, pixman_repeat);
}
 
return pixman_image;
}
 
static pixman_image_t *
_pixman_image_for_mesh (const cairo_mesh_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int *tx, int *ty)
{
pixman_image_t *image;
int width, height;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
*tx = -extents->x;
*ty = -extents->y;
width = extents->width;
height = extents->height;
 
image = pixman_image_create_bits (PIXMAN_a8r8g8b8, width, height, NULL, 0);
if (unlikely (image == NULL))
return NULL;
 
_cairo_mesh_pattern_rasterize (pattern,
pixman_image_get_data (image),
width, height,
pixman_image_get_stride (image),
*tx, *ty);
return image;
}
 
struct acquire_source_cleanup {
cairo_surface_t *surface;
cairo_image_surface_t *image;
void *image_extra;
};
 
static void
_acquire_source_cleanup (pixman_image_t *pixman_image,
void *closure)
{
struct acquire_source_cleanup *data = closure;
 
_cairo_surface_release_source_image (data->surface,
data->image,
data->image_extra);
free (data);
}
 
static void
_defer_free_cleanup (pixman_image_t *pixman_image,
void *closure)
{
cairo_surface_destroy (closure);
}
 
static uint16_t
expand_channel (uint16_t v, uint32_t bits)
{
int offset = 16 - bits;
while (offset > 0) {
v |= v >> bits;
offset -= bits;
bits += bits;
}
return v;
}
 
static pixman_image_t *
_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
{
uint32_t pixel;
pixman_color_t color;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
switch (image->format) {
default:
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
return NULL;
 
case CAIRO_FORMAT_A1:
pixel = *(uint8_t *) (image->data + y * image->stride + x/8);
return pixel & (1 << (x&7)) ? _pixman_black_image () : _pixman_transparent_image ();
 
case CAIRO_FORMAT_A8:
color.alpha = *(uint8_t *) (image->data + y * image->stride + x);
color.alpha |= color.alpha << 8;
if (color.alpha == 0)
return _pixman_transparent_image ();
if (color.alpha == 0xffff)
return _pixman_black_image ();
 
color.red = color.green = color.blue = 0;
return pixman_image_create_solid_fill (&color);
 
case CAIRO_FORMAT_RGB16_565:
pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x);
if (pixel == 0)
return _pixman_black_image ();
if (pixel == 0xffff)
return _pixman_white_image ();
 
color.alpha = 0xffff;
color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
return pixman_image_create_solid_fill (&color);
 
case CAIRO_FORMAT_RGB30:
pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
pixel &= 0x3fffffff; /* ignore alpha bits */
if (pixel == 0)
return _pixman_black_image ();
if (pixel == 0x3fffffff)
return _pixman_white_image ();
 
/* convert 10bpc to 16bpc */
color.alpha = 0xffff;
color.red = expand_channel((pixel >> 20) & 0x3fff, 10);
color.green = expand_channel((pixel >> 10) & 0x3fff, 10);
color.blue = expand_channel(pixel & 0x3fff, 10);
return pixman_image_create_solid_fill (&color);
 
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
if (color.alpha == 0)
return _pixman_transparent_image ();
if (pixel == 0xffffffff)
return _pixman_white_image ();
if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
return _pixman_black_image ();
 
color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
return pixman_image_create_solid_fill (&color);
}
}
 
static cairo_bool_t
_pixman_image_set_properties (pixman_image_t *pixman_image,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int *ix,int *iy)
{
pixman_transform_t pixman_transform;
cairo_int_status_t status;
 
status = _cairo_matrix_to_pixman_matrix_offset (&pattern->matrix,
pattern->filter,
extents->x + extents->width/2.,
extents->y + extents->height/2.,
&pixman_transform, ix, iy);
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
{
/* If the transform is an identity, we don't need to set it
* and we can use any filtering, so choose the fastest one. */
pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
}
else if (unlikely (status != CAIRO_INT_STATUS_SUCCESS ||
! pixman_image_set_transform (pixman_image,
&pixman_transform)))
{
return FALSE;
}
else
{
pixman_filter_t pixman_filter;
 
switch (pattern->filter) {
case CAIRO_FILTER_FAST:
pixman_filter = PIXMAN_FILTER_FAST;
break;
case CAIRO_FILTER_GOOD:
pixman_filter = PIXMAN_FILTER_GOOD;
break;
case CAIRO_FILTER_BEST:
pixman_filter = PIXMAN_FILTER_BEST;
break;
case CAIRO_FILTER_NEAREST:
pixman_filter = PIXMAN_FILTER_NEAREST;
break;
case CAIRO_FILTER_BILINEAR:
pixman_filter = PIXMAN_FILTER_BILINEAR;
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:
pixman_filter = PIXMAN_FILTER_BEST;
}
 
pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
}
 
{
pixman_repeat_t pixman_repeat;
 
switch (pattern->extend) {
default:
case CAIRO_EXTEND_NONE:
pixman_repeat = PIXMAN_REPEAT_NONE;
break;
case CAIRO_EXTEND_REPEAT:
pixman_repeat = PIXMAN_REPEAT_NORMAL;
break;
case CAIRO_EXTEND_REFLECT:
pixman_repeat = PIXMAN_REPEAT_REFLECT;
break;
case CAIRO_EXTEND_PAD:
pixman_repeat = PIXMAN_REPEAT_PAD;
break;
}
 
pixman_image_set_repeat (pixman_image, pixman_repeat);
}
 
if (pattern->has_component_alpha)
pixman_image_set_component_alpha (pixman_image, TRUE);
 
return TRUE;
}
 
struct proxy {
cairo_surface_t base;
cairo_surface_t *image;
};
 
static cairo_status_t
proxy_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
struct proxy *proxy = abstract_surface;
return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
}
 
static void
proxy_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
struct proxy *proxy = abstract_surface;
_cairo_surface_release_source_image (proxy->image, image, image_extra);
}
 
static cairo_status_t
proxy_finish (void *abstract_surface)
{
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t proxy_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
proxy_finish,
NULL,
 
NULL, /* create similar */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
proxy_acquire_source_image,
proxy_release_source_image,
};
 
static cairo_surface_t *
attach_proxy (cairo_surface_t *source,
cairo_surface_t *image)
{
struct proxy *proxy;
 
proxy = malloc (sizeof (*proxy));
if (unlikely (proxy == NULL))
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
 
proxy->image = image;
_cairo_surface_attach_snapshot (source, &proxy->base, NULL);
 
return &proxy->base;
}
 
static void
detach_proxy (cairo_surface_t *source,
cairo_surface_t *proxy)
{
cairo_surface_finish (proxy);
cairo_surface_destroy (proxy);
}
 
static cairo_surface_t *
get_proxy (cairo_surface_t *proxy)
{
return ((struct proxy *)proxy)->image;
}
 
static pixman_image_t *
_pixman_image_for_recording (cairo_image_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 *ix, int *iy)
{
cairo_surface_t *source, *clone, *proxy;
cairo_rectangle_int_t limit;
pixman_image_t *pixman_image;
cairo_status_t status;
cairo_extend_t extend;
cairo_matrix_t *m, matrix;
int tx = 0, ty = 0;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
*ix = *iy = 0;
 
source = _cairo_pattern_get_source (pattern, &limit);
 
extend = pattern->base.extend;
if (_cairo_rectangle_contains_rectangle (&limit, sample))
extend = CAIRO_EXTEND_NONE;
if (extend == CAIRO_EXTEND_NONE) {
if (! _cairo_rectangle_intersect (&limit, sample))
return _pixman_transparent_image ();
 
if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
double x1, y1, x2, y2;
 
matrix = pattern->base.matrix;
status = cairo_matrix_invert (&matrix);
assert (status == CAIRO_STATUS_SUCCESS);
 
x1 = limit.x;
y1 = limit.y;
x2 = limit.x + limit.width;
y2 = limit.y + limit.height;
 
_cairo_matrix_transform_bounding_box (&matrix,
&x1, &y1, &x2, &y2, NULL);
 
limit.x = floor (x1);
limit.y = floor (y1);
limit.width = ceil (x2) - limit.x;
limit.height = ceil (y2) - limit.y;
}
}
tx = limit.x;
ty = limit.y;
 
/* XXX transformations! */
proxy = _cairo_surface_has_snapshot (source, &proxy_backend);
if (proxy != NULL) {
clone = cairo_surface_reference (get_proxy (proxy));
goto done;
}
 
if (is_mask) {
clone = cairo_image_surface_create (CAIRO_FORMAT_A8,
limit.width, limit.height);
} else {
if (dst->base.content == source->content)
clone = cairo_image_surface_create (dst->format,
limit.width, limit.height);
else
clone = _cairo_image_surface_create_with_content (source->content,
limit.width,
limit.height);
}
 
m = NULL;
if (extend == CAIRO_EXTEND_NONE) {
matrix = pattern->base.matrix;
if (tx | ty)
cairo_matrix_translate (&matrix, tx, ty);
m = &matrix;
} else {
/* XXX extract scale factor for repeating patterns */
}
 
/* Handle recursion by returning future reads from the current image */
proxy = attach_proxy (source, clone);
status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL);
detach_proxy (source, proxy);
if (unlikely (status)) {
cairo_surface_destroy (clone);
return NULL;
}
 
done:
pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image);
cairo_surface_destroy (clone);
 
*ix = -limit.x;
*iy = -limit.y;
if (extend != CAIRO_EXTEND_NONE) {
if (! _pixman_image_set_properties (pixman_image,
&pattern->base, extents,
ix, iy)) {
pixman_image_unref (pixman_image);
pixman_image= NULL;
}
}
 
return pixman_image;
}
 
static pixman_image_t *
_pixman_image_for_surface (cairo_image_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 *ix, int *iy)
{
cairo_extend_t extend = pattern->base.extend;
pixman_image_t *pixman_image;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
*ix = *iy = 0;
pixman_image = NULL;
if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
return _pixman_image_for_recording(dst, pattern,
is_mask, extents, sample,
ix, iy);
 
if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
(! is_mask || ! pattern->base.has_component_alpha ||
(pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
{
cairo_surface_t *defer_free = NULL;
cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
cairo_surface_type_t type;
 
if (_cairo_surface_is_snapshot (&source->base)) {
defer_free = _cairo_surface_snapshot_get_target (&source->base);
source = (cairo_image_surface_t *) defer_free;
}
 
type = source->base.backend->type;
if (type == CAIRO_SURFACE_TYPE_IMAGE) {
if (extend != CAIRO_EXTEND_NONE &&
sample->x >= 0 &&
sample->y >= 0 &&
sample->x + sample->width <= source->width &&
sample->y + sample->height <= source->height)
{
extend = CAIRO_EXTEND_NONE;
}
 
if (sample->width == 1 && sample->height == 1) {
if (sample->x < 0 ||
sample->y < 0 ||
sample->x >= source->width ||
sample->y >= source->height)
{
if (extend == CAIRO_EXTEND_NONE) {
cairo_surface_destroy (defer_free);
return _pixman_transparent_image ();
}
}
else
{
pixman_image = _pixel_to_solid (source,
sample->x, sample->y);
if (pixman_image) {
cairo_surface_destroy (defer_free);
return pixman_image;
}
}
}
 
#if PIXMAN_HAS_ATOMIC_OPS
/* avoid allocating a 'pattern' image if we can reuse the original */
if (extend == CAIRO_EXTEND_NONE &&
_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
pattern->base.filter,
ix, iy))
{
cairo_surface_destroy (defer_free);
return pixman_image_ref (source->pixman_image);
}
#endif
 
pixman_image = pixman_image_create_bits (source->pixman_format,
source->width,
source->height,
(uint32_t *) source->data,
source->stride);
if (unlikely (pixman_image == NULL)) {
cairo_surface_destroy (defer_free);
return NULL;
}
 
if (defer_free) {
pixman_image_set_destroy_function (pixman_image,
_defer_free_cleanup,
defer_free);
}
} else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub;
cairo_bool_t is_contained = FALSE;
 
sub = (cairo_surface_subsurface_t *) source;
source = (cairo_image_surface_t *) sub->target;
 
if (sample->x >= 0 &&
sample->y >= 0 &&
sample->x + sample->width <= sub->extents.width &&
sample->y + sample->height <= sub->extents.height)
{
is_contained = TRUE;
}
 
if (sample->width == 1 && sample->height == 1) {
if (is_contained) {
pixman_image = _pixel_to_solid (source,
sub->extents.x + sample->x,
sub->extents.y + sample->y);
if (pixman_image)
return pixman_image;
} else {
if (extend == CAIRO_EXTEND_NONE)
return _pixman_transparent_image ();
}
}
 
#if PIXMAN_HAS_ATOMIC_OPS
*ix = sub->extents.x;
*iy = sub->extents.y;
if (is_contained &&
_cairo_matrix_is_pixman_translation (&pattern->base.matrix,
pattern->base.filter,
ix, iy))
{
return pixman_image_ref (source->pixman_image);
}
#endif
 
/* Avoid sub-byte offsets, force a copy in that case. */
if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
if (is_contained) {
void *data = source->data
+ sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8
+ sub->extents.y * source->stride;
pixman_image = pixman_image_create_bits (source->pixman_format,
sub->extents.width,
sub->extents.height,
data,
source->stride);
if (unlikely (pixman_image == NULL))
return NULL;
} else {
/* XXX for a simple translation and EXTEND_NONE we can
* fix up the pattern matrix instead.
*/
}
}
}
}
 
if (pixman_image == NULL) {
struct acquire_source_cleanup *cleanup;
cairo_image_surface_t *image;
void *extra;
cairo_status_t status;
 
status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
if (unlikely (status))
return NULL;
 
pixman_image = pixman_image_create_bits (image->pixman_format,
image->width,
image->height,
(uint32_t *) image->data,
image->stride);
if (unlikely (pixman_image == NULL)) {
_cairo_surface_release_source_image (pattern->surface, image, extra);
return NULL;
}
 
cleanup = malloc (sizeof (*cleanup));
if (unlikely (cleanup == NULL)) {
_cairo_surface_release_source_image (pattern->surface, image, extra);
pixman_image_unref (pixman_image);
return NULL;
}
 
cleanup->surface = pattern->surface;
cleanup->image = image;
cleanup->image_extra = extra;
pixman_image_set_destroy_function (pixman_image,
_acquire_source_cleanup, cleanup);
}
 
if (! _pixman_image_set_properties (pixman_image,
&pattern->base, extents,
ix, iy)) {
pixman_image_unref (pixman_image);
pixman_image= NULL;
}
 
return pixman_image;
}
 
struct raster_source_cleanup {
const cairo_pattern_t *pattern;
cairo_surface_t *surface;
cairo_image_surface_t *image;
void *image_extra;
};
 
static void
_raster_source_cleanup (pixman_image_t *pixman_image,
void *closure)
{
struct raster_source_cleanup *data = closure;
 
_cairo_surface_release_source_image (data->surface,
data->image,
data->image_extra);
 
_cairo_raster_source_pattern_release (data->pattern,
data->surface);
 
free (data);
}
 
static pixman_image_t *
_pixman_image_for_raster (cairo_image_surface_t *dst,
const cairo_raster_source_pattern_t *pattern,
cairo_bool_t is_mask,
const cairo_rectangle_int_t *extents,
const cairo_rectangle_int_t *sample,
int *ix, int *iy)
{
pixman_image_t *pixman_image;
struct raster_source_cleanup *cleanup;
cairo_image_surface_t *image;
void *extra;
cairo_status_t status;
cairo_surface_t *surface;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
*ix = *iy = 0;
 
surface = _cairo_raster_source_pattern_acquire (&pattern->base,
&dst->base, NULL);
if (unlikely (surface == NULL || surface->status))
return NULL;
 
status = _cairo_surface_acquire_source_image (surface, &image, &extra);
if (unlikely (status)) {
_cairo_raster_source_pattern_release (&pattern->base, surface);
return NULL;
}
 
assert (image->width == pattern->extents.width);
assert (image->height == pattern->extents.height);
 
pixman_image = pixman_image_create_bits (image->pixman_format,
image->width,
image->height,
(uint32_t *) image->data,
image->stride);
if (unlikely (pixman_image == NULL)) {
_cairo_surface_release_source_image (surface, image, extra);
_cairo_raster_source_pattern_release (&pattern->base, surface);
return NULL;
}
 
cleanup = malloc (sizeof (*cleanup));
if (unlikely (cleanup == NULL)) {
pixman_image_unref (pixman_image);
_cairo_surface_release_source_image (surface, image, extra);
_cairo_raster_source_pattern_release (&pattern->base, surface);
return NULL;
}
 
cleanup->pattern = &pattern->base;
cleanup->surface = surface;
cleanup->image = image;
cleanup->image_extra = extra;
pixman_image_set_destroy_function (pixman_image,
_raster_source_cleanup, cleanup);
 
if (! _pixman_image_set_properties (pixman_image,
&pattern->base, extents,
ix, iy)) {
pixman_image_unref (pixman_image);
pixman_image= NULL;
}
 
return pixman_image;
}
 
pixman_image_t *
_pixman_image_for_pattern (cairo_image_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 *tx, int *ty)
{
*tx = *ty = 0;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (pattern == NULL)
return _pixman_white_image ();
 
switch (pattern->type) {
default:
ASSERT_NOT_REACHED;
case CAIRO_PATTERN_TYPE_SOLID:
return _pixman_image_for_color (&((const cairo_solid_pattern_t *) pattern)->color);
 
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_LINEAR:
return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
extents, tx, ty);
 
case CAIRO_PATTERN_TYPE_MESH:
return _pixman_image_for_mesh ((const cairo_mesh_pattern_t *) pattern,
extents, tx, ty);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _pixman_image_for_surface (dst,
(const cairo_surface_pattern_t *) pattern,
is_mask, extents, sample,
tx, ty);
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _pixman_image_for_raster (dst,
(const cairo_raster_source_pattern_t *) pattern,
is_mask, extents, sample,
tx, ty);
}
}
 
static cairo_status_t
_cairo_image_source_finish (void *abstract_surface)
{
cairo_image_source_t *source = abstract_surface;
 
pixman_image_unref (source->pixman_image);
return CAIRO_STATUS_SUCCESS;
}
 
const cairo_surface_backend_t _cairo_image_source_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_source_finish,
NULL, /* read-only wrapper */
};
 
cairo_surface_t *
_cairo_image_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_image_source_t *source;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
source = malloc (sizeof (cairo_image_source_t));
if (unlikely (source == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
source->pixman_image =
_pixman_image_for_pattern ((cairo_image_surface_t *)dst,
pattern, is_mask,
extents, sample,
src_x, src_y);
if (unlikely (source->pixman_image == NULL)) {
free (source);
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_surface_init (&source->base,
&_cairo_image_source_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
source->is_opaque_solid =
pattern == NULL || _cairo_pattern_is_opaque_solid (pattern);
 
return &source->base;
}
/programs/develop/libraries/cairo/src/cairo-image-surface-inline.h
0,0 → 1,96
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
*/
 
#ifndef CAIRO_IMAGE_SURFACE_INLINE_H
#define CAIRO_IMAGE_SURFACE_INLINE_H
 
#include "cairo-surface-private.h"
#include "cairo-image-surface-private.h"
 
CAIRO_BEGIN_DECLS
 
static inline cairo_image_surface_t *
_cairo_image_surface_create_in_error (cairo_status_t status)
{
return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
}
 
static inline void
_cairo_image_surface_set_parent (cairo_image_surface_t *image,
cairo_surface_t *parent)
{
image->parent = parent;
}
 
static inline cairo_bool_t
_cairo_image_surface_is_clone (cairo_image_surface_t *image)
{
return image->parent != NULL;
}
 
/**
* _cairo_surface_is_image:
* @surface: a #cairo_surface_t
*
* Checks if a surface is an #cairo_image_surface_t
*
* Return value: %TRUE if the surface is an image surface
**/
static inline cairo_bool_t
_cairo_surface_is_image (const cairo_surface_t *surface)
{
/* _cairo_surface_nil sets a NULL backend so be safe */
return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_IMAGE;
}
 
/**
* _cairo_surface_is_image_source:
* @surface: a #cairo_surface_t
*
* Checks if a surface is an #cairo_image_source_t
*
* Return value: %TRUE if the surface is an image source
**/
static inline cairo_bool_t
_cairo_surface_is_image_source (const cairo_surface_t *surface)
{
return surface->backend == &_cairo_image_source_backend;
}
 
CAIRO_END_DECLS
 
#endif /* CAIRO_IMAGE_SURFACE_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-image-surface-private.h
0,0 → 1,238
/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
*/
 
#ifndef CAIRO_IMAGE_SURFACE_PRIVATE_H
#define CAIRO_IMAGE_SURFACE_PRIVATE_H
 
#include "cairo-surface-private.h"
 
#include <pixman.h>
 
CAIRO_BEGIN_DECLS
 
/* The canonical image backend */
struct _cairo_image_surface {
cairo_surface_t base;
 
pixman_image_t *pixman_image;
const cairo_compositor_t *compositor;
 
/* Parenting is tricky wrt lifetime tracking...
*
* One use for tracking the parent of an image surface is for
* create_similar_image() where we wish to create a device specific
* surface but return an image surface to the user. In such a case,
* the image may be owned by the device specific surface, its parent,
* but the user lifetime tracking is then performed on the image. So
* when the image is then finalized we call cairo_surface_destroy()
* on the parent. However, for normal usage where the lifetime tracking
* is done on the parent surface, we need to be careful to unhook
* the image->parent pointer before finalizing the image.
*/
cairo_surface_t *parent;
 
pixman_format_code_t pixman_format;
cairo_format_t format;
unsigned char *data;
 
int width;
int height;
int stride;
int depth;
 
unsigned owns_data : 1;
unsigned transparency : 2;
unsigned color : 2;
};
#define to_image_surface(S) ((cairo_image_surface_t *)(S))
 
/* A wrapper for holding pixman images returned by create_for_pattern */
typedef struct _cairo_image_source {
cairo_surface_t base;
 
pixman_image_t *pixman_image;
unsigned is_opaque_solid : 1;
} cairo_image_source_t;
 
cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend;
cairo_private extern const cairo_surface_backend_t _cairo_image_source_backend;
 
cairo_private const cairo_compositor_t *
_cairo_image_mask_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_image_traps_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_image_spans_compositor_get (void);
 
#define _cairo_image_default_compositor_get _cairo_image_spans_compositor_get
 
cairo_private cairo_int_status_t
_cairo_image_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_image_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_image_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_image_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_image_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip);
 
cairo_private void
_cairo_image_surface_init (cairo_image_surface_t *surface,
pixman_image_t *pixman_image,
pixman_format_code_t pixman_format);
 
cairo_private cairo_surface_t *
_cairo_image_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
int height);
 
cairo_private cairo_image_surface_t *
_cairo_image_surface_map_to_image (void *abstract_other,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_int_status_t
_cairo_image_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image);
 
cairo_private cairo_surface_t *
_cairo_image_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_image_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra);
 
cairo_private void
_cairo_image_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra);
 
cairo_private cairo_surface_t *
_cairo_image_surface_snapshot (void *abstract_surface);
 
cairo_private_no_warn cairo_bool_t
_cairo_image_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle);
 
cairo_private void
_cairo_image_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options);
 
cairo_private cairo_surface_t *
_cairo_image_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_private cairo_status_t
_cairo_image_surface_finish (void *abstract_surface);
 
cairo_private pixman_image_t *
_pixman_image_for_color (const cairo_color_t *cairo_color);
 
cairo_private pixman_image_t *
_pixman_image_for_pattern (cairo_image_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 *tx, int *ty);
 
cairo_private void
_pixman_image_add_traps (pixman_image_t *image,
int dst_x, int dst_y,
cairo_traps_t *traps);
 
cairo_private void
_pixman_image_add_tristrip (pixman_image_t *image,
int dst_x, int dst_y,
cairo_tristrip_t *strip);
 
cairo_private cairo_image_surface_t *
_cairo_image_surface_clone_subimage (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents);
 
/* Similar to clone; but allow format conversion */
cairo_private cairo_image_surface_t *
_cairo_image_surface_create_from_image (cairo_image_surface_t *other,
pixman_format_code_t format,
int x, int y, int width, int height,
int stride);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-image-surface.c
2,7 → 2,7
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
* Copyright © 2009,2010 Intel Corporation
* Copyright © 2009,2010,2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
42,7 → 42,14
#include "cairo-boxes-private.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-paginated-private.h"
#include "cairo-pattern-private.h"
#include "cairo-pixman-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-snapshot-private.h"
52,7 → 59,6
* mainly determined by coordinates of things sent to pixman at the
* moment being in 16.16 format. */
#define MAX_IMAGE_SIZE 32767
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 
/**
* SECTION:cairo-image
63,7 → 69,7
* Image surfaces provide the ability to render to memory buffers
* either allocated by cairo or by the calling code. The supported
* image formats are those defined in #cairo_format_t.
*/
**/
 
/**
* CAIRO_HAS_IMAGE_SURFACE:
72,22 → 78,9
* The image surface backend is always built in.
* This macro was added for completeness in cairo 1.8.
*
* @Since: 1.8
*/
* Since: 1.8
**/
 
static cairo_int_status_t
_cairo_image_surface_fill (void *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
 
static pixman_image_t *
_pixman_image_for_solid (const cairo_solid_pattern_t *pattern);
 
static cairo_bool_t
_cairo_image_surface_is_size_valid (int width, int height)
{
101,6 → 94,8
switch (pixman_format) {
case PIXMAN_a8r8g8b8:
return CAIRO_FORMAT_ARGB32;
case PIXMAN_x2r10g10b10:
return CAIRO_FORMAT_RGB30;
case PIXMAN_x8r8g8b8:
return CAIRO_FORMAT_RGB24;
case PIXMAN_a8:
109,6 → 104,9
return CAIRO_FORMAT_A1;
case PIXMAN_r5g6b5:
return CAIRO_FORMAT_RGB16_565;
#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0)
case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8:
#endif
case PIXMAN_a8b8g8r8: case PIXMAN_x8b8g8r8: case PIXMAN_r8g8b8:
case PIXMAN_b8g8r8: case PIXMAN_b5g6r5:
case PIXMAN_a1r5g5b5: case PIXMAN_x1r5g5b5: case PIXMAN_a1b5g5r5:
122,10 → 120,12
case PIXMAN_yuy2: case PIXMAN_yv12:
case PIXMAN_b8g8r8x8:
case PIXMAN_b8g8r8a8:
case PIXMAN_a2b10g10r10:
case PIXMAN_x2b10g10r10:
case PIXMAN_a2b10g10r10:
case PIXMAN_x2r10g10b10:
case PIXMAN_a2r10g10b10:
#if PIXMAN_VERSION >= PIXMAN_VERSION_ENCODE(0,22,0)
case PIXMAN_x14r6g6b6:
#endif
default:
return CAIRO_FORMAT_INVALID;
}
147,13 → 147,36
return content;
}
 
void
_cairo_image_surface_init (cairo_image_surface_t *surface,
pixman_image_t *pixman_image,
pixman_format_code_t pixman_format)
{
surface->parent = NULL;
surface->pixman_image = pixman_image;
 
surface->pixman_format = pixman_format;
surface->format = _cairo_format_from_pixman_format (pixman_format);
surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
surface->owns_data = FALSE;
surface->transparency = CAIRO_IMAGE_UNKNOWN;
surface->color = CAIRO_IMAGE_UNKNOWN_COLOR;
 
surface->width = pixman_image_get_width (pixman_image);
surface->height = pixman_image_get_height (pixman_image);
surface->stride = pixman_image_get_stride (pixman_image);
surface->depth = pixman_image_get_depth (pixman_image);
 
surface->base.is_clear = surface->width == 0 || surface->height == 0;
 
surface->compositor = _cairo_image_spans_compositor_get ();
}
 
cairo_surface_t *
_cairo_image_surface_create_for_pixman_image (pixman_image_t *pixman_image,
pixman_format_code_t pixman_format)
{
cairo_image_surface_t *surface;
int width = pixman_image_get_width (pixman_image);
int height = pixman_image_get_height (pixman_image);
 
surface = malloc (sizeof (cairo_image_surface_t));
if (unlikely (surface == NULL))
164,19 → 187,8
NULL, /* device */
_cairo_content_from_pixman_format (pixman_format));
 
surface->pixman_image = pixman_image;
_cairo_image_surface_init (surface, pixman_image, pixman_format);
 
surface->pixman_format = pixman_format;
surface->format = _cairo_format_from_pixman_format (pixman_format);
surface->data = (uint8_t *) pixman_image_get_data (pixman_image);
surface->owns_data = FALSE;
surface->transparency = CAIRO_IMAGE_UNKNOWN;
 
surface->width = width;
surface->height = height;
surface->stride = pixman_image_get_stride (pixman_image);
surface->depth = pixman_image_get_depth (pixman_image);
 
return &surface->base;
}
 
299,6 → 311,9
case CAIRO_FORMAT_RGB24:
ret = PIXMAN_x8r8g8b8;
break;
case CAIRO_FORMAT_RGB30:
ret = PIXMAN_x2r10g10b10;
break;
case CAIRO_FORMAT_RGB16_565:
ret = PIXMAN_r5g6b5;
break;
363,6 → 378,8
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_image_surface_create (cairo_format_t format,
478,6 → 495,8
*
* See cairo_surface_set_user_data() for a means of attaching a
* destroy-notification fallback to the surface if necessary.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_image_surface_create_for_data (unsigned char *data,
524,6 → 543,11
* Get a pointer to the data of the image surface, for direct
* inspection or modification.
*
* A call to cairo_surface_flush() is required before accessing the
* pixel data to ensure that all pending drawing operations are
* finished. A call to cairo_surface_mark_dirty() is required after
* the data is modified.
*
* Return value: a pointer to the image data of this surface or %NULL
* if @surface is not an image surface, or if cairo_surface_finish()
* has been called.
575,6 → 599,8
* Get the width of the image surface in pixels.
*
* Return value: the width of the surface in pixels.
*
* Since: 1.0
**/
int
cairo_image_surface_get_width (cairo_surface_t *surface)
597,6 → 623,8
* Get the height of the image surface in pixels.
*
* Return value: the height of the surface in pixels.
*
* Since: 1.0
**/
int
cairo_image_surface_get_height (cairo_surface_t *surface)
662,6 → 690,8
switch (format) {
case CAIRO_FORMAT_ARGB32:
return CAIRO_CONTENT_COLOR_ALPHA;
case CAIRO_FORMAT_RGB30:
return CAIRO_CONTENT_COLOR;
case CAIRO_FORMAT_RGB24:
return CAIRO_CONTENT_COLOR;
case CAIRO_FORMAT_RGB16_565:
682,7 → 712,7
{
switch (format) {
case CAIRO_FORMAT_ARGB32:
return 32;
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_RGB24:
return 32;
case CAIRO_FORMAT_RGB16_565:
698,7 → 728,7
}
}
 
static cairo_surface_t *
cairo_surface_t *
_cairo_image_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
706,6 → 736,8
{
cairo_image_surface_t *other = abstract_other;
 
TRACE ((stderr, "%s (other=%u)\n", __FUNCTION__, other->base.unique_id));
 
if (! _cairo_image_surface_is_size_valid (width, height))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
720,3301 → 752,240
width, height);
}
 
static cairo_status_t
_cairo_image_surface_finish (void *abstract_surface)
cairo_surface_t *
_cairo_image_surface_snapshot (void *abstract_surface)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_image_surface_t *image = abstract_surface;
cairo_image_surface_t *clone;
 
if (surface->pixman_image) {
pixman_image_unref (surface->pixman_image);
surface->pixman_image = NULL;
}
/* If we own the image, we can simply steal the memory for the snapshot */
if (image->owns_data && image->base._finishing) {
clone = (cairo_image_surface_t *)
_cairo_image_surface_create_for_pixman_image (image->pixman_image,
image->pixman_format);
if (unlikely (clone->base.status))
return &clone->base;
 
if (surface->owns_data) {
free (surface->data);
surface->data = NULL;
}
image->pixman_image = NULL;
image->owns_data = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
clone->transparency = image->transparency;
clone->color = image->color;
 
void
_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface)
{
surface->owns_data = TRUE;
clone->owns_data = TRUE;
return &clone->base;
}
 
static cairo_status_t
_cairo_image_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
*image_out = abstract_surface;
*image_extra = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_image_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
}
 
/* XXX: I think we should fix pixman to match the names/order of the
* cairo operators, but that will likely be better done at the same
* time the X server is ported to pixman, (which will change a lot of
* things in pixman I think).
*/
static pixman_op_t
_pixman_operator (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return PIXMAN_OP_CLEAR;
 
case CAIRO_OPERATOR_SOURCE:
return PIXMAN_OP_SRC;
case CAIRO_OPERATOR_OVER:
return PIXMAN_OP_OVER;
case CAIRO_OPERATOR_IN:
return PIXMAN_OP_IN;
case CAIRO_OPERATOR_OUT:
return PIXMAN_OP_OUT;
case CAIRO_OPERATOR_ATOP:
return PIXMAN_OP_ATOP;
 
case CAIRO_OPERATOR_DEST:
return PIXMAN_OP_DST;
case CAIRO_OPERATOR_DEST_OVER:
return PIXMAN_OP_OVER_REVERSE;
case CAIRO_OPERATOR_DEST_IN:
return PIXMAN_OP_IN_REVERSE;
case CAIRO_OPERATOR_DEST_OUT:
return PIXMAN_OP_OUT_REVERSE;
case CAIRO_OPERATOR_DEST_ATOP:
return PIXMAN_OP_ATOP_REVERSE;
 
case CAIRO_OPERATOR_XOR:
return PIXMAN_OP_XOR;
case CAIRO_OPERATOR_ADD:
return PIXMAN_OP_ADD;
case CAIRO_OPERATOR_SATURATE:
return PIXMAN_OP_SATURATE;
 
case CAIRO_OPERATOR_MULTIPLY:
return PIXMAN_OP_MULTIPLY;
case CAIRO_OPERATOR_SCREEN:
return PIXMAN_OP_SCREEN;
case CAIRO_OPERATOR_OVERLAY:
return PIXMAN_OP_OVERLAY;
case CAIRO_OPERATOR_DARKEN:
return PIXMAN_OP_DARKEN;
case CAIRO_OPERATOR_LIGHTEN:
return PIXMAN_OP_LIGHTEN;
case CAIRO_OPERATOR_COLOR_DODGE:
return PIXMAN_OP_COLOR_DODGE;
case CAIRO_OPERATOR_COLOR_BURN:
return PIXMAN_OP_COLOR_BURN;
case CAIRO_OPERATOR_HARD_LIGHT:
return PIXMAN_OP_HARD_LIGHT;
case CAIRO_OPERATOR_SOFT_LIGHT:
return PIXMAN_OP_SOFT_LIGHT;
case CAIRO_OPERATOR_DIFFERENCE:
return PIXMAN_OP_DIFFERENCE;
case CAIRO_OPERATOR_EXCLUSION:
return PIXMAN_OP_EXCLUSION;
case CAIRO_OPERATOR_HSL_HUE:
return PIXMAN_OP_HSL_HUE;
case CAIRO_OPERATOR_HSL_SATURATION:
return PIXMAN_OP_HSL_SATURATION;
case CAIRO_OPERATOR_HSL_COLOR:
return PIXMAN_OP_HSL_COLOR;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return PIXMAN_OP_HSL_LUMINOSITY;
 
default:
ASSERT_NOT_REACHED;
return PIXMAN_OP_OVER;
}
}
 
static cairo_status_t
_cairo_image_surface_set_clip_region (cairo_image_surface_t *surface,
cairo_region_t *region)
{
if (! pixman_image_set_clip_region32 (surface->pixman_image, &region->rgn))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface)
{
pixman_image_set_clip_region32 (surface->pixman_image, NULL);
}
 
static double
_pixman_nearest_sample (double d)
{
return ceil (d - .5);
}
 
static cairo_bool_t
_nearest_sample (cairo_filter_t filter, double *tx, double *ty)
{
if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
*tx = _pixman_nearest_sample (*tx);
*ty = _pixman_nearest_sample (*ty);
} else {
if (*tx != floor (*tx) || *ty != floor (*ty))
return FALSE;
}
return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT;
}
 
#if HAS_ATOMIC_OPS
static pixman_image_t *__pixman_transparent_image;
static pixman_image_t *__pixman_black_image;
static pixman_image_t *__pixman_white_image;
 
static pixman_image_t *
_pixman_transparent_image (void)
{
pixman_image_t *image;
 
image = __pixman_transparent_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0x00;
color.green = 0x00;
color.blue = 0x00;
color.alpha = 0x00;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_transparent_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
 
static pixman_image_t *
_pixman_black_image (void)
{
pixman_image_t *image;
 
image = __pixman_black_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0x00;
color.green = 0x00;
color.blue = 0x00;
color.alpha = 0xffff;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
 
static pixman_image_t *
_pixman_white_image (void)
{
pixman_image_t *image;
 
image = __pixman_white_image;
if (unlikely (image == NULL)) {
pixman_color_t color;
 
color.red = 0xffff;
color.green = 0xffff;
color.blue = 0xffff;
color.alpha = 0xffff;
 
image = pixman_image_create_solid_fill (&color);
if (unlikely (image == NULL))
return NULL;
 
if (_cairo_atomic_ptr_cmpxchg (&__pixman_white_image,
NULL, image))
{
pixman_image_ref (image);
}
} else {
pixman_image_ref (image);
}
 
return image;
}
#else
static pixman_image_t *
_pixman_transparent_image (void)
{
return _pixman_image_for_solid (&_cairo_pattern_clear);
}
static pixman_image_t *
_pixman_black_image (void)
{
return _pixman_image_for_solid (&_cairo_pattern_black);
}
static pixman_image_t *
_pixman_white_image (void)
{
return _pixman_image_for_solid (&_cairo_pattern_white);
}
#endif
 
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 struct {
cairo_color_t color;
pixman_image_t *image;
} cache[16];
static int n_cached;
 
void
_cairo_image_reset_static_data (void)
{
while (n_cached)
pixman_image_unref (cache[--n_cached].image);
 
#if HAS_ATOMIC_OPS
if (__pixman_transparent_image) {
pixman_image_unref (__pixman_transparent_image);
__pixman_transparent_image = NULL;
}
 
if (__pixman_black_image) {
pixman_image_unref (__pixman_black_image);
__pixman_black_image = NULL;
}
 
if (__pixman_white_image) {
pixman_image_unref (__pixman_white_image);
__pixman_white_image = NULL;
}
#endif
}
 
static pixman_image_t *
_pixman_image_for_solid (const cairo_solid_pattern_t *pattern)
{
pixman_color_t color;
pixman_image_t *image;
int i;
 
#if HAS_ATOMIC_OPS
if (pattern->color.alpha_short <= 0x00ff)
return _pixman_transparent_image ();
 
if (pattern->color.alpha_short >= 0xff00) {
if (pattern->color.red_short <= 0x00ff &&
pattern->color.green_short <= 0x00ff &&
pattern->color.blue_short <= 0x00ff)
{
return _pixman_black_image ();
}
 
if (pattern->color.red_short >= 0xff00 &&
pattern->color.green_short >= 0xff00 &&
pattern->color.blue_short >= 0xff00)
{
return _pixman_white_image ();
}
}
#endif
 
CAIRO_MUTEX_LOCK (_cairo_image_solid_cache_mutex);
for (i = 0; i < n_cached; i++) {
if (_cairo_color_equal (&cache[i].color, &pattern->color)) {
image = pixman_image_ref (cache[i].image);
goto UNLOCK;
}
}
 
color.red = pattern->color.red_short;
color.green = pattern->color.green_short;
color.blue = pattern->color.blue_short;
color.alpha = pattern->color.alpha_short;
 
image = pixman_image_create_solid_fill (&color);
if (image == NULL)
goto UNLOCK;
 
if (n_cached < ARRAY_LENGTH (cache)) {
i = n_cached++;
} else {
i = hars_petruska_f54_1_random () % ARRAY_LENGTH (cache);
pixman_image_unref (cache[i].image);
}
cache[i].image = pixman_image_ref (image);
cache[i].color = pattern->color;
 
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex);
return image;
}
 
static pixman_image_t *
_pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int *ix, int *iy)
{
pixman_image_t *pixman_image;
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
cairo_matrix_t matrix = pattern->base.matrix;
double tx, ty;
unsigned int i;
 
if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
pixman_stops = _cairo_malloc_ab (pattern->n_stops,
sizeof(pixman_gradient_stop_t));
if (unlikely (pixman_stops == NULL))
return NULL;
}
 
for (i = 0; i < pattern->n_stops; i++) {
pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
pixman_stops[i].color.red = pattern->stops[i].color.red_short;
pixman_stops[i].color.green = pattern->stops[i].color.green_short;
pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
}
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
pixman_point_fixed_t p1, p2;
cairo_fixed_t xdim, ydim;
 
xdim = fabs (linear->p2.x - linear->p1.x);
ydim = fabs (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...
*/
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);
 
p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
cairo_matrix_scale (&matrix, sf, sf);
}
else
{
p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
}
 
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
} else {
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
pixman_point_fixed_t c1, c2;
pixman_fixed_t r1, r2;
 
c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
r1 = _cairo_fixed_to_16_16 (radial->r1);
 
c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
r2 = _cairo_fixed_to_16_16 (radial->r2);
 
pixman_image = pixman_image_create_radial_gradient (&c1, &c2, r1, r2,
pixman_stops,
pattern->n_stops);
}
 
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
 
if (unlikely (pixman_image == NULL))
return NULL;
 
tx = pattern->base.matrix.x0;
ty = pattern->base.matrix.y0;
if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
! _nearest_sample (pattern->base.filter, &tx, &ty))
{
pixman_transform_t pixman_transform;
 
if (tx != 0. || ty != 0.) {
cairo_matrix_t m, inv;
cairo_status_t status;
double x, y;
 
/* pixman also limits the [xy]_offset to 16 bits so evenly
* spread the bits between the two.
*/
inv = pattern->base.matrix;
status = cairo_matrix_invert (&inv);
assert (status == CAIRO_STATUS_SUCCESS);
 
x = floor (inv.x0 / 2);
y = floor (inv.y0 / 2);
tx = -x;
ty = -y;
cairo_matrix_init_translate (&inv, x, y);
cairo_matrix_multiply (&m, &inv, &pattern->base.matrix);
_cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
} else {
tx = ty = 0;
_cairo_matrix_to_pixman_matrix (&pattern->base.matrix,
&pixman_transform,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
}
 
if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
pixman_image_unref (pixman_image);
return NULL;
}
}
*ix = tx;
*iy = ty;
 
{
pixman_repeat_t pixman_repeat;
 
switch (pattern->base.extend) {
default:
case CAIRO_EXTEND_NONE:
pixman_repeat = PIXMAN_REPEAT_NONE;
break;
case CAIRO_EXTEND_REPEAT:
pixman_repeat = PIXMAN_REPEAT_NORMAL;
break;
case CAIRO_EXTEND_REFLECT:
pixman_repeat = PIXMAN_REPEAT_REFLECT;
break;
case CAIRO_EXTEND_PAD:
pixman_repeat = PIXMAN_REPEAT_PAD;
break;
}
 
pixman_image_set_repeat (pixman_image, pixman_repeat);
}
 
return pixman_image;
}
 
struct acquire_source_cleanup {
cairo_surface_t *surface;
cairo_image_surface_t *image;
void *image_extra;
};
 
static void
_acquire_source_cleanup (pixman_image_t *pixman_image,
void *closure)
{
struct acquire_source_cleanup *data = closure;
 
_cairo_surface_release_source_image (data->surface,
data->image,
data->image_extra);
free (data);
}
 
static cairo_filter_t
sampled_area (const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *sample)
{
cairo_filter_t filter;
double x1, x2, y1, y2;
double pad;
 
x1 = extents->x;
y1 = extents->y;
x2 = extents->x + (int) extents->width;
y2 = extents->y + (int) extents->height;
 
_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
&x1, &y1, &x2, &y2,
NULL);
 
filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
sample->x = floor (x1 - pad);
sample->y = floor (y1 - pad);
sample->width = ceil (x2 + pad) - sample->x;
sample->height = ceil (y2 + pad) - sample->y;
 
return filter;
}
 
static uint16_t
expand_channel (uint16_t v, uint32_t bits)
{
int offset = 16 - bits;
while (offset > 0) {
v |= v >> bits;
offset -= bits;
bits += bits;
}
return v;
}
 
static pixman_image_t *
_pixel_to_solid (cairo_image_surface_t *image, int x, int y)
{
uint32_t pixel;
pixman_color_t color;
 
switch (image->format) {
default:
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
return NULL;
 
case CAIRO_FORMAT_A1:
pixel = *(uint8_t *) (image->data + y * image->stride + x/8);
return pixel & (1 << (x&7)) ? _pixman_white_image () : _pixman_transparent_image ();
 
case CAIRO_FORMAT_A8:
color.alpha = *(uint8_t *) (image->data + y * image->stride + x);
color.alpha |= color.alpha << 8;
if (color.alpha == 0)
return _pixman_transparent_image ();
 
color.red = color.green = color.blue = 0;
return pixman_image_create_solid_fill (&color);
 
case CAIRO_FORMAT_RGB16_565:
pixel = *(uint16_t *) (image->data + y * image->stride + 2 * x);
if (pixel == 0)
return _pixman_black_image ();
if (pixel == 0xffff)
return _pixman_white_image ();
 
color.alpha = 0xffff;
color.red = expand_channel ((pixel >> 11 & 0x1f) << 11, 5);
color.green = expand_channel ((pixel >> 5 & 0x3f) << 10, 6);
color.blue = expand_channel ((pixel & 0x1f) << 11, 5);
return pixman_image_create_solid_fill (&color);
 
case CAIRO_FORMAT_ARGB32:
case CAIRO_FORMAT_RGB24:
pixel = *(uint32_t *) (image->data + y * image->stride + 4 * x);
color.alpha = image->format == CAIRO_FORMAT_ARGB32 ? (pixel >> 24) | (pixel >> 16 & 0xff00) : 0xffff;
if (color.alpha == 0)
return _pixman_transparent_image ();
if (pixel == 0xffffffff)
return _pixman_white_image ();
if (color.alpha == 0xffff && (pixel & 0xffffff) == 0)
return _pixman_black_image ();
 
color.red = (pixel >> 16 & 0xff) | (pixel >> 8 & 0xff00);
color.green = (pixel >> 8 & 0xff) | (pixel & 0xff00);
color.blue = (pixel & 0xff) | (pixel << 8 & 0xff00);
return pixman_image_create_solid_fill (&color);
}
}
 
static pixman_image_t *
_pixman_image_for_surface (const cairo_surface_pattern_t *pattern,
cairo_bool_t is_mask,
const cairo_rectangle_int_t *extents,
int *ix, int *iy)
{
pixman_image_t *pixman_image;
cairo_rectangle_int_t sample;
cairo_extend_t extend;
cairo_filter_t filter;
double tx, ty;
 
tx = pattern->base.matrix.x0;
ty = pattern->base.matrix.y0;
 
extend = pattern->base.extend;
filter = sampled_area (pattern, extents, &sample);
 
pixman_image = NULL;
if (pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE &&
(! is_mask || ! pattern->base.has_component_alpha ||
(pattern->surface->content & CAIRO_CONTENT_COLOR) == 0))
{
cairo_image_surface_t *source = (cairo_image_surface_t *) pattern->surface;
cairo_surface_type_t type;
 
if (source->base.backend->type == CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT)
source = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) pattern->surface)->target;
 
type = source->base.backend->type;
if (type == CAIRO_SURFACE_TYPE_IMAGE) {
if (extend != CAIRO_EXTEND_NONE &&
sample.x >= 0 &&
sample.y >= 0 &&
sample.x + sample.width <= source->width &&
sample.y + sample.height <= source->height)
{
extend = CAIRO_EXTEND_NONE;
}
 
if (sample.width == 1 && sample.height == 1) {
if (sample.x < 0 ||
sample.y < 0 ||
sample.x >= source->width ||
sample.y >= source->height)
{
if (extend == CAIRO_EXTEND_NONE)
return _pixman_transparent_image ();
}
else
{
return _pixel_to_solid (source, sample.x, sample.y);
}
}
 
/* avoid allocating a 'pattern' image if we can reuse the original */
if (extend == CAIRO_EXTEND_NONE &&
_cairo_matrix_is_translation (&pattern->base.matrix) &&
_nearest_sample (filter, &tx, &ty))
{
*ix = tx;
*iy = ty;
return pixman_image_ref (source->pixman_image);
}
 
pixman_image = pixman_image_create_bits (source->pixman_format,
source->width,
source->height,
(uint32_t *) source->data,
source->stride);
if (unlikely (pixman_image == NULL))
return NULL;
} else if (type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub;
cairo_bool_t is_contained = FALSE;
 
sub = (cairo_surface_subsurface_t *) source;
source = (cairo_image_surface_t *) sub->target;
 
if (sample.x >= 0 &&
sample.y >= 0 &&
sample.x + sample.width <= sub->extents.width &&
sample.y + sample.height <= sub->extents.height)
{
is_contained = TRUE;
}
 
if (sample.width == 1 && sample.height == 1) {
if (is_contained) {
return _pixel_to_solid (source,
sub->extents.x + sample.x,
sub->extents.y + sample.y);
} else {
if (extend == CAIRO_EXTEND_NONE)
return _pixman_transparent_image ();
}
}
 
if (is_contained &&
_cairo_matrix_is_translation (&pattern->base.matrix) &&
_nearest_sample (filter, &tx, &ty))
{
*ix = tx + sub->extents.x;
*iy = ty + sub->extents.y;
return pixman_image_ref (source->pixman_image);
}
 
/* Avoid sub-byte offsets, force a copy in that case. */
if (PIXMAN_FORMAT_BPP (source->pixman_format) >= 8) {
pixman_image = pixman_image_create_bits (source->pixman_format,
sub->extents.width,
sub->extents.height,
(uint32_t *) (source->data + sub->extents.x * PIXMAN_FORMAT_BPP(source->pixman_format)/8 + sub->extents.y * source->stride),
source->stride);
if (unlikely (pixman_image == NULL))
return NULL;
}
}
}
 
if (pixman_image == NULL) {
struct acquire_source_cleanup *cleanup;
cairo_image_surface_t *image;
void *extra;
cairo_status_t status;
 
status = _cairo_surface_acquire_source_image (pattern->surface, &image, &extra);
if (unlikely (status))
return NULL;
 
if (sample.width == 1 && sample.height == 1) {
if (sample.x < 0 ||
sample.y < 0 ||
sample.x >= image->width ||
sample.y >= image->height)
{
if (extend == CAIRO_EXTEND_NONE) {
pixman_image = _pixman_transparent_image ();
_cairo_surface_release_source_image (pattern->surface, image, extra);
return pixman_image;
}
}
else
{
pixman_image = _pixel_to_solid (image, sample.x, sample.y);
_cairo_surface_release_source_image (pattern->surface, image, extra);
return pixman_image;
}
}
 
pixman_image = pixman_image_create_bits (image->pixman_format,
clone = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
image->pixman_format,
image->width,
image->height,
(uint32_t *) image->data,
image->stride);
if (unlikely (pixman_image == NULL)) {
_cairo_surface_release_source_image (pattern->surface, image, extra);
return NULL;
}
 
cleanup = malloc (sizeof (*cleanup));
if (unlikely (cleanup == NULL)) {
_cairo_surface_release_source_image (pattern->surface, image, extra);
pixman_image_unref (pixman_image);
return NULL;
}
 
cleanup->surface = pattern->surface;
cleanup->image = image;
cleanup->image_extra = extra;
pixman_image_set_destroy_function (pixman_image,
_acquire_source_cleanup, cleanup);
}
 
if (! _cairo_matrix_is_translation (&pattern->base.matrix) ||
! _nearest_sample (filter, &tx, &ty))
{
pixman_transform_t pixman_transform;
cairo_matrix_t m;
 
m = pattern->base.matrix;
if (m.x0 != 0. || m.y0 != 0.) {
cairo_matrix_t inv;
cairo_status_t status;
double x, y;
 
/* pixman also limits the [xy]_offset to 16 bits so evenly
* spread the bits between the two.
*/
inv = m;
status = cairo_matrix_invert (&inv);
assert (status == CAIRO_STATUS_SUCCESS);
 
x = floor (inv.x0 / 2);
y = floor (inv.y0 / 2);
tx = -x;
ty = -y;
cairo_matrix_init_translate (&inv, x, y);
cairo_matrix_multiply (&m, &inv, &m);
} else {
tx = ty = 0;
}
 
_cairo_matrix_to_pixman_matrix (&m, &pixman_transform,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
if (! pixman_image_set_transform (pixman_image, &pixman_transform)) {
pixman_image_unref (pixman_image);
return NULL;
}
}
*ix = tx;
*iy = ty;
 
if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
tx == pattern->base.matrix.x0 &&
ty == pattern->base.matrix.y0)
{
pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0);
}
else
{
pixman_filter_t pixman_filter;
 
switch (filter) {
case CAIRO_FILTER_FAST:
pixman_filter = PIXMAN_FILTER_FAST;
break;
case CAIRO_FILTER_GOOD:
pixman_filter = PIXMAN_FILTER_GOOD;
break;
case CAIRO_FILTER_BEST:
pixman_filter = PIXMAN_FILTER_BEST;
break;
case CAIRO_FILTER_NEAREST:
pixman_filter = PIXMAN_FILTER_NEAREST;
break;
case CAIRO_FILTER_BILINEAR:
pixman_filter = PIXMAN_FILTER_BILINEAR;
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:
pixman_filter = PIXMAN_FILTER_BEST;
}
 
pixman_image_set_filter (pixman_image, pixman_filter, NULL, 0);
}
 
{
pixman_repeat_t pixman_repeat;
 
switch (extend) {
default:
case CAIRO_EXTEND_NONE:
pixman_repeat = PIXMAN_REPEAT_NONE;
break;
case CAIRO_EXTEND_REPEAT:
pixman_repeat = PIXMAN_REPEAT_NORMAL;
break;
case CAIRO_EXTEND_REFLECT:
pixman_repeat = PIXMAN_REPEAT_REFLECT;
break;
case CAIRO_EXTEND_PAD:
pixman_repeat = PIXMAN_REPEAT_PAD;
break;
}
 
pixman_image_set_repeat (pixman_image, pixman_repeat);
}
 
if (pattern->base.has_component_alpha)
pixman_image_set_component_alpha (pixman_image, TRUE);
 
return pixman_image;
}
 
static pixman_image_t *
_pixman_image_for_pattern (const cairo_pattern_t *pattern,
cairo_bool_t is_mask,
const cairo_rectangle_int_t *extents,
int *tx, int *ty)
{
*tx = *ty = 0;
 
if (pattern == NULL)
return _pixman_white_image ();
 
switch (pattern->type) {
default:
ASSERT_NOT_REACHED;
case CAIRO_PATTERN_TYPE_SOLID:
return _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
 
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_LINEAR:
return _pixman_image_for_gradient ((const cairo_gradient_pattern_t *) pattern,
extents, tx, ty);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _pixman_image_for_surface ((const cairo_surface_pattern_t *) pattern,
is_mask, extents, tx, ty);
}
}
 
static cairo_status_t
_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst,
const cairo_composite_rectangles_t *rects,
cairo_clip_t *clip)
{
pixman_image_t *mask = NULL;
pixman_box32_t boxes[4];
int i, mask_x = 0, mask_y = 0, n_boxes = 0;
 
if (clip != NULL) {
cairo_surface_t *clip_surface;
int clip_x, clip_y;
 
clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
if (unlikely (clip_surface->status))
return clip_surface->status;
 
mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
mask_x = -clip_x;
mask_y = -clip_y;
} else {
if (rects->bounded.width == rects->unbounded.width &&
rects->bounded.height == rects->unbounded.height)
{
return CAIRO_STATUS_SUCCESS;
}
}
 
/* wholly unbounded? */
if (rects->bounded.width == 0 || rects->bounded.height == 0) {
int x = rects->unbounded.x;
int y = rects->unbounded.y;
int width = rects->unbounded.width;
int height = rects->unbounded.height;
 
if (mask != NULL) {
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
x + mask_x, y + mask_y,
0, 0,
x, y,
width, height);
} else {
pixman_color_t color = { 0, };
pixman_box32_t box = { x, y, x + width, y + height };
 
if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
dst->pixman_image,
&color,
1, &box))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/* top */
if (rects->bounded.y != rects->unbounded.y) {
boxes[n_boxes].x1 = rects->unbounded.x;
boxes[n_boxes].y1 = rects->unbounded.y;
boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
boxes[n_boxes].y2 = rects->bounded.y;
n_boxes++;
}
 
/* left */
if (rects->bounded.x != rects->unbounded.x) {
boxes[n_boxes].x1 = rects->unbounded.x;
boxes[n_boxes].y1 = rects->bounded.y;
boxes[n_boxes].x2 = rects->bounded.x;
boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
n_boxes++;
}
 
/* right */
if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
boxes[n_boxes].x1 = rects->bounded.x + rects->bounded.width;
boxes[n_boxes].y1 = rects->bounded.y;
boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
boxes[n_boxes].y2 = rects->bounded.y + rects->bounded.height;
n_boxes++;
}
 
/* bottom */
if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
boxes[n_boxes].x1 = rects->unbounded.x;
boxes[n_boxes].y1 = rects->bounded.y + rects->bounded.height;
boxes[n_boxes].x2 = rects->unbounded.x + rects->unbounded.width;
boxes[n_boxes].y2 = rects->unbounded.y + rects->unbounded.height;
n_boxes++;
}
 
if (mask != NULL) {
for (i = 0; i < n_boxes; i++) {
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
boxes[i].x1 + mask_x, boxes[i].y1 + mask_y,
0, 0,
boxes[i].x1, boxes[i].y1,
boxes[i].x2 - boxes[i].x1, boxes[i].y2 - boxes[i].y1);
}
} else {
pixman_color_t color = { 0, };
 
if (! pixman_image_fill_boxes (PIXMAN_OP_CLEAR,
dst->pixman_image,
&color,
n_boxes,
boxes))
{
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_image_surface_fixup_unbounded_boxes (cairo_image_surface_t *dst,
const cairo_composite_rectangles_t *extents,
cairo_region_t *clip_region,
cairo_boxes_t *boxes)
{
cairo_boxes_t clear;
cairo_box_t box;
cairo_status_t status;
struct _cairo_boxes_chunk *chunk;
int i;
 
if (boxes->num_boxes <= 1 && clip_region == NULL)
return _cairo_image_surface_fixup_unbounded (dst, extents, NULL);
 
_cairo_boxes_init (&clear);
 
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
 
if (clip_region == NULL) {
cairo_boxes_t tmp;
 
_cairo_boxes_init (&tmp);
 
status = _cairo_boxes_add (&tmp, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
tmp.chunks.next = &boxes->chunks;
tmp.num_boxes += boxes->num_boxes;
 
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
CAIRO_FILL_RULE_WINDING,
&clear);
 
tmp.chunks.next = NULL;
} else {
pixman_box32_t *pbox;
 
pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
_cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
 
status = _cairo_boxes_add (&clear, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
status = _cairo_boxes_add (&clear, &chunk->base[i]);
if (unlikely (status)) {
_cairo_boxes_fini (&clear);
return status;
}
}
}
 
status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
CAIRO_FILL_RULE_WINDING,
&clear);
}
 
if (likely (status == CAIRO_STATUS_SUCCESS)) {
for (chunk = &clear.chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (dst->pixman_format),
x1, y1, x2 - x1, y2 - y1,
0);
}
}
}
if (unlikely (clone->base.status))
return &clone->base;
 
_cairo_boxes_fini (&clear);
 
return status;
}
 
static cairo_bool_t
can_reduce_alpha_op (cairo_operator_t op)
{
int iop = op;
switch (iop) {
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_ADD:
return TRUE;
default:
return FALSE;
}
}
 
static cairo_bool_t
reduce_alpha_op (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern)
{
return dst->base.is_clear &&
dst->base.content == CAIRO_CONTENT_ALPHA &&
_cairo_pattern_is_opaque_solid (pattern) &&
can_reduce_alpha_op (op);
}
 
/* low level compositor */
typedef cairo_status_t
(*image_draw_func_t) (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *src,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region);
 
static pixman_image_t *
_create_composite_mask_pattern (cairo_clip_t *clip,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_region_t *clip_region = NULL;
pixman_image_t *mask;
cairo_status_t status;
cairo_bool_t need_clip_surface = FALSE;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
assert (! _cairo_status_is_error (status));
 
/* The all-clipped state should never propagate this far. */
assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
 
need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
 
if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
}
 
mask = pixman_image_create_bits (PIXMAN_a8, extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL))
return NULL;
 
/* Is it worth setting the clip region here? */
if (clip_region != NULL) {
pixman_bool_t ret;
 
pixman_region32_translate (&clip_region->rgn, -extents->x, -extents->y);
ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn);
pixman_region32_translate (&clip_region->rgn, extents->x, extents->y);
 
if (! ret) {
pixman_image_unref (mask);
return NULL;
}
}
 
status = draw_func (draw_closure,
mask, PIXMAN_a8,
CAIRO_OPERATOR_ADD, NULL,
extents->x, extents->y,
extents, NULL);
if (unlikely (status)) {
pixman_image_unref (mask);
return NULL;
}
 
if (need_clip_surface) {
cairo_surface_t *tmp;
 
tmp = _cairo_image_surface_create_for_pixman_image (mask, PIXMAN_a8);
if (unlikely (tmp->status)) {
pixman_image_unref (mask);
return NULL;
}
 
pixman_image_ref (mask);
 
status = _cairo_clip_combine_with_surface (clip, tmp, extents->x, extents->y);
cairo_surface_destroy (tmp);
if (unlikely (status)) {
pixman_image_unref (mask);
return NULL;
}
}
 
if (clip_region != NULL)
pixman_image_set_clip_region (mask, NULL);
 
return mask;
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
_clip_and_composite_with_mask (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *pattern,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
pixman_image_t *mask;
 
mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (pattern == NULL) {
if (dst->pixman_format == PIXMAN_a8) {
pixman_image_composite32 (_pixman_operator (op),
mask, NULL, dst->pixman_image,
0, 0, 0, 0,
extents->x, extents->y,
extents->width, extents->height);
if (clone->stride == image->stride) {
memcpy (clone->data, image->data, clone->stride * clone->height);
} else {
pixman_image_t *src;
 
src = _pixman_white_image ();
if (unlikely (src == NULL)) {
pixman_image_unref (mask);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
0, 0, 0, 0,
extents->x, extents->y,
extents->width, extents->height);
pixman_image_unref (src);
}
} else {
pixman_image_t *src;
int src_x, src_y;
 
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL)) {
pixman_image_unref (mask);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
pixman_image_unref (src);
}
 
pixman_image_unref (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
_clip_and_composite_combine (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
pixman_image_t *tmp;
cairo_surface_t *clip_surface;
int clip_x, clip_y;
cairo_status_t status;
 
tmp = pixman_image_create_bits (dst->pixman_format,
extents->width, extents->height,
NULL, 0);
if (unlikely (tmp == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (src == NULL) {
status = (*draw_func) (draw_closure,
tmp, dst->pixman_format,
CAIRO_OPERATOR_ADD, NULL,
extents->x, extents->y,
extents, NULL);
} else {
/* Initialize the temporary surface from the destination surface */
if (! dst->base.is_clear) {
pixman_image_composite32 (PIXMAN_OP_SRC,
dst->pixman_image, NULL, tmp,
extents->x, extents->y,
image->pixman_image, NULL, clone->pixman_image,
0, 0,
0, 0,
extents->width, extents->height);
}
 
status = (*draw_func) (draw_closure,
tmp, dst->pixman_format,
op, src,
extents->x, extents->y,
extents, NULL);
}
if (unlikely (status))
goto CLEANUP_SURFACE;
 
assert (clip->path != NULL);
clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
if (unlikely (clip_surface->status))
goto CLEANUP_SURFACE;
 
if (! dst->base.is_clear) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP,
tmp,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
extents->x - clip_x,
extents->y - clip_y,
extents->x, extents->y,
extents->width, extents->height);
#else
/* Punch the clip out of the destination */
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
((cairo_image_surface_t *) clip_surface)->pixman_image,
NULL, dst->pixman_image,
extents->x - clip_x,
extents->y - clip_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
 
/* Now add the two results together */
pixman_image_composite32 (PIXMAN_OP_ADD,
tmp,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
extents->x - clip_x,
extents->y - clip_y,
extents->x, extents->y,
extents->width, extents->height);
#endif
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
tmp,
((cairo_image_surface_t *) clip_surface)->pixman_image,
dst->pixman_image,
0, 0,
extents->x - clip_x,
extents->y - clip_y,
extents->x, extents->y,
extents->width, extents->height);
image->width, image->height);
}
 
CLEANUP_SURFACE:
pixman_image_unref (tmp);
 
return status;
clone->base.is_clear = FALSE;
return &clone->base;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
_clip_and_composite_source (cairo_clip_t *clip,
const cairo_pattern_t *pattern,
image_draw_func_t draw_func,
void *draw_closure,
cairo_image_surface_t *dst,
cairo_image_surface_t *
_cairo_image_surface_map_to_image (void *abstract_other,
const cairo_rectangle_int_t *extents)
{
pixman_image_t *mask, *src;
int src_x, src_y;
cairo_image_surface_t *other = abstract_other;
cairo_surface_t *surface;
uint8_t *data;
 
if (pattern == NULL) {
cairo_region_t *clip_region;
cairo_status_t status;
data = other->data;
data += extents->y * other->stride;
data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
 
status = draw_func (draw_closure,
dst->pixman_image, dst->pixman_format,
CAIRO_OPERATOR_SOURCE, NULL,
extents->x, extents->y,
extents, NULL);
if (unlikely (status))
return status;
surface =
_cairo_image_surface_create_with_pixman_format (data,
other->pixman_format,
extents->width,
extents->height,
other->stride);
 
if (_cairo_clip_get_region (clip, &clip_region) == CAIRO_INT_STATUS_UNSUPPORTED)
status = _cairo_clip_combine_with_surface (clip, &dst->base, 0, 0);
 
return status;
cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
return (cairo_image_surface_t *) surface;
}
 
/* Create a surface that is mask IN clip */
mask = _create_composite_mask_pattern (clip, draw_func, draw_closure, dst, extents);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL)) {
pixman_image_unref (mask);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
if (! dst->base.is_clear) {
#if PIXMAN_HAS_OP_LERP
pixman_image_composite32 (PIXMAN_OP_LERP,
src, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
#else
/* Compute dest' = dest OUT (mask IN clip) */
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
0, 0, 0, 0,
extents->x, extents->y,
extents->width, extents->height);
 
/* Now compute (src IN (mask IN clip)) ADD dest' */
pixman_image_composite32 (PIXMAN_OP_ADD,
src, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
#endif
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
src, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
}
 
pixman_image_unref (src);
pixman_image_unref (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_clip_and_composite (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
image_draw_func_t draw_func,
void *draw_closure,
cairo_composite_rectangles_t*extents,
cairo_clip_t *clip)
cairo_int_status_t
_cairo_image_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_status_t status;
cairo_region_t *clip_region = NULL;
cairo_bool_t need_clip_surface = FALSE;
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
if (unlikely (_cairo_status_is_error (status)))
return status;
 
need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
 
if (clip_region != NULL) {
cairo_rectangle_int_t rect;
cairo_bool_t is_empty;
 
cairo_region_get_extents (clip_region, &rect);
is_empty = ! _cairo_rectangle_intersect (&extents->unbounded, &rect);
if (unlikely (is_empty))
return CAIRO_STATUS_SUCCESS;
 
is_empty = ! _cairo_rectangle_intersect (&extents->bounded, &rect);
if (unlikely (is_empty && extents->is_bounded))
return CAIRO_STATUS_SUCCESS;
 
if (cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
return CAIRO_INT_STATUS_SUCCESS;
}
}
 
if (clip_region != NULL) {
status = _cairo_image_surface_set_clip_region (dst, clip_region);
if (unlikely (status))
return status;
}
 
if (reduce_alpha_op (dst, op, src)) {
op = CAIRO_OPERATOR_ADD;
src = NULL;
}
 
if (op == CAIRO_OPERATOR_SOURCE) {
status = _clip_and_composite_source (clip, src,
draw_func, draw_closure,
dst, &extents->bounded);
} else {
if (op == CAIRO_OPERATOR_CLEAR) {
src = NULL;
op = CAIRO_OPERATOR_DEST_OUT;
}
 
if (need_clip_surface) {
if (extents->is_bounded) {
status = _clip_and_composite_with_mask (clip, op, src,
draw_func, draw_closure,
dst, &extents->bounded);
} else {
status = _clip_and_composite_combine (clip, op, src,
draw_func, draw_closure,
dst, &extents->bounded);
}
} else {
status = draw_func (draw_closure,
dst->pixman_image, dst->pixman_format,
op, src,
0, 0,
&extents->bounded,
clip_region);
}
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
status = _cairo_image_surface_fixup_unbounded (dst, extents,
need_clip_surface ? clip : NULL);
}
 
if (clip_region != NULL)
_cairo_image_surface_unset_clip_region (dst);
 
return status;
}
 
#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
 
static cairo_bool_t
_line_exceeds_16_16 (const cairo_line_t *line)
cairo_status_t
_cairo_image_surface_finish (void *abstract_surface)
{
return
line->p1.x <= CAIRO_FIXED_16_16_MIN ||
line->p1.x >= CAIRO_FIXED_16_16_MAX ||
cairo_image_surface_t *surface = abstract_surface;
 
line->p2.x <= CAIRO_FIXED_16_16_MIN ||
line->p2.x >= CAIRO_FIXED_16_16_MAX ||
 
line->p1.y <= CAIRO_FIXED_16_16_MIN ||
line->p1.y >= CAIRO_FIXED_16_16_MAX ||
 
line->p2.y <= CAIRO_FIXED_16_16_MIN ||
line->p2.y >= CAIRO_FIXED_16_16_MAX;
if (surface->pixman_image) {
pixman_image_unref (surface->pixman_image);
surface->pixman_image = NULL;
}
 
static void
_project_line_x_onto_16_16 (const cairo_line_t *line,
cairo_fixed_t top,
cairo_fixed_t bottom,
pixman_line_fixed_t *out)
{
cairo_point_double_t p1, p2;
double m;
 
p1.x = _cairo_fixed_to_double (line->p1.x);
p1.y = _cairo_fixed_to_double (line->p1.y);
 
p2.x = _cairo_fixed_to_double (line->p2.x);
p2.y = _cairo_fixed_to_double (line->p2.y);
 
m = (p2.x - p1.x) / (p2.y - p1.y);
out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
if (surface->owns_data) {
free (surface->data);
surface->data = NULL;
}
 
 
typedef struct {
cairo_trapezoid_t *traps;
int num_traps;
cairo_antialias_t antialias;
} composite_traps_info_t;
 
static void
_pixman_image_add_traps (pixman_image_t *image,
int dst_x, int dst_y,
composite_traps_info_t *info)
{
cairo_trapezoid_t *t = info->traps;
int num_traps = info->num_traps;
while (num_traps--) {
pixman_trapezoid_t trap;
 
/* top/bottom will be clamped to surface bounds */
trap.top = _cairo_fixed_to_16_16 (t->top);
trap.bottom = _cairo_fixed_to_16_16 (t->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 (&t->left))) {
_project_line_x_onto_16_16 (&t->left, t->top, t->bottom, &trap.left);
trap.left.p1.y = trap.top;
trap.left.p2.y = trap.bottom;
} else {
trap.left.p1.x = _cairo_fixed_to_16_16 (t->left.p1.x);
trap.left.p1.y = _cairo_fixed_to_16_16 (t->left.p1.y);
trap.left.p2.x = _cairo_fixed_to_16_16 (t->left.p2.x);
trap.left.p2.y = _cairo_fixed_to_16_16 (t->left.p2.y);
if (surface->parent) {
cairo_surface_t *parent = surface->parent;
surface->parent = NULL;
cairo_surface_destroy (parent);
}
 
if (unlikely (_line_exceeds_16_16 (&t->right))) {
_project_line_x_onto_16_16 (&t->right, t->top, t->bottom, &trap.right);
trap.right.p1.y = trap.top;
trap.right.p2.y = trap.bottom;
} else {
trap.right.p1.x = _cairo_fixed_to_16_16 (t->right.p1.x);
trap.right.p1.y = _cairo_fixed_to_16_16 (t->right.p1.y);
trap.right.p2.x = _cairo_fixed_to_16_16 (t->right.p2.x);
trap.right.p2.y = _cairo_fixed_to_16_16 (t->right.p2.y);
}
 
pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
 
t++;
}
}
 
static cairo_status_t
_composite_traps (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
composite_traps_info_t *info = closure;
pixman_image_t *src, *mask;
pixman_format_code_t format;
int src_x = 0, src_y = 0;
cairo_status_t status;
 
/* Special case adding trapezoids onto a mask surface; we want to avoid
* creating an intermediate temporary mask unnecessarily.
*
* We make the assumption here that the portion of the trapezoids
* contained within the surface is bounded by [dst_x,dst_y,width,height];
* the Cairo core code passes bounds based on the trapezoid extents.
*/
format = info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8;
if (dst_format == format &&
(pattern == NULL ||
(op == CAIRO_OPERATOR_ADD && _cairo_pattern_is_opaque_solid (pattern))))
{
_pixman_image_add_traps (dst, dst_x, dst_y, info);
return CAIRO_STATUS_SUCCESS;
}
 
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
mask = pixman_image_create_bits (format, extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_SOURCE;
}
 
_pixman_image_add_traps (mask, extents->x, extents->y, info);
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (mask);
 
status = CAIRO_STATUS_SUCCESS;
CLEANUP_SOURCE:
pixman_image_unref (src);
 
return status;
}
 
static inline uint32_t
color_to_uint32 (const cairo_color_t *color)
void
_cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface)
{
return
(color->alpha_short >> 8 << 24) |
(color->red_short >> 8 << 16) |
(color->green_short & 0xff00) |
(color->blue_short >> 8);
surface->owns_data = TRUE;
}
 
static inline cairo_bool_t
color_to_pixel (const cairo_color_t *color,
pixman_format_code_t format,
uint32_t *pixel)
cairo_surface_t *
_cairo_image_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
uint32_t c;
cairo_image_surface_t *surface = abstract_surface;
 
if (!(format == PIXMAN_a8r8g8b8 ||
format == PIXMAN_x8r8g8b8 ||
format == PIXMAN_a8b8g8r8 ||
format == PIXMAN_x8b8g8r8 ||
format == PIXMAN_b8g8r8a8 ||
format == PIXMAN_b8g8r8x8 ||
format == PIXMAN_r5g6b5 ||
format == PIXMAN_b5g6r5 ||
format == PIXMAN_a8))
{
return FALSE;
if (extents) {
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
}
 
c = color_to_uint32 (color);
 
if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_ABGR) {
c = ((c & 0xff000000) >> 0) |
((c & 0x00ff0000) >> 16) |
((c & 0x0000ff00) >> 0) |
((c & 0x000000ff) << 16);
return &surface->base;
}
 
if (PIXMAN_FORMAT_TYPE (format) == PIXMAN_TYPE_BGRA) {
c = ((c & 0xff000000) >> 24) |
((c & 0x00ff0000) >> 8) |
((c & 0x0000ff00) << 8) |
((c & 0x000000ff) << 24);
}
 
if (format == PIXMAN_a8) {
c = c >> 24;
} else if (format == PIXMAN_r5g6b5 || format == PIXMAN_b5g6r5) {
c = ((((c) >> 3) & 0x001f) |
(((c) >> 5) & 0x07e0) |
(((c) >> 8) & 0xf800));
}
 
*pixel = c;
return TRUE;
}
 
static inline cairo_bool_t
pattern_to_pixel (const cairo_solid_pattern_t *solid,
cairo_operator_t op,
pixman_format_code_t format,
uint32_t *pixel)
cairo_status_t
_cairo_image_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
if (op == CAIRO_OPERATOR_CLEAR) {
*pixel = 0;
return TRUE;
}
*image_out = abstract_surface;
*image_extra = NULL;
 
if (solid->base.type != CAIRO_PATTERN_TYPE_SOLID)
return FALSE;
 
if (op == CAIRO_OPERATOR_OVER) {
if (solid->color.alpha_short >= 0xff00)
op = CAIRO_OPERATOR_SOURCE;
}
 
if (op != CAIRO_OPERATOR_SOURCE)
return FALSE;
 
return color_to_pixel (&solid->color, format, pixel);
}
 
typedef struct _fill_span {
cairo_span_renderer_t base;
 
uint8_t *mask_data;
pixman_image_t *src, *dst, *mask;
} fill_span_renderer_t;
 
static cairo_status_t
_fill_span (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
fill_span_renderer_t *renderer = abstract_renderer;
uint8_t *row;
unsigned i;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
row = renderer->mask_data - spans[0].x;
for (i = 0; i < num_spans - 1; i++) {
/* We implement setting the most common single pixel wide
* span case to avoid the overhead of a memset call.
* Open coding setting longer spans didn't show a
* noticeable improvement over memset.
*/
if (spans[i+1].x == spans[i].x + 1) {
row[spans[i].x] = spans[i].coverage;
} else {
memset (row + spans[i].x,
spans[i].coverage,
spans[i+1].x - spans[i].x);
}
}
 
do {
pixman_image_composite32 (PIXMAN_OP_OVER,
renderer->src, renderer->mask, renderer->dst,
0, 0, 0, 0,
spans[0].x, y++,
spans[i].x - spans[0].x, 1);
} while (--height);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* avoid using region code to re-validate boxes */
static cairo_status_t
_fill_unaligned_boxes (cairo_image_surface_t *dst,
const cairo_pattern_t *pattern,
uint32_t pixel,
const cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
void
_cairo_image_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
fill_span_renderer_t renderer;
cairo_rectangular_scan_converter_t converter;
const struct _cairo_boxes_chunk *chunk;
cairo_status_t status;
int i;
 
/* XXX
* using composite for fill:
* spiral-box-nonalign-evenodd-fill.512 2201957 2.202
* spiral-box-nonalign-nonzero-fill.512 336726 0.337
* spiral-box-pixalign-evenodd-fill.512 352256 0.352
* spiral-box-pixalign-nonzero-fill.512 147056 0.147
* using fill:
* spiral-box-nonalign-evenodd-fill.512 3174565 3.175
* spiral-box-nonalign-nonzero-fill.512 182710 0.183
* spiral-box-pixalign-evenodd-fill.512 353863 0.354
* spiral-box-pixalign-nonzero-fill.512 147402 0.147
*
* cairo-perf-trace seems to favour using fill.
*/
 
renderer.base.render_rows = _fill_span;
renderer.dst = dst->pixman_image;
 
if ((unsigned) extents->bounded.width <= sizeof (buf)) {
renderer.mask = pixman_image_create_bits (PIXMAN_a8,
extents->bounded.width, 1,
(uint32_t *) buf,
sizeof (buf));
} else {
renderer.mask = pixman_image_create_bits (PIXMAN_a8,
extents->bounded.width, 1,
NULL, 0);
}
if (unlikely (renderer.mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
renderer.mask_data = (uint8_t *) pixman_image_get_data (renderer.mask);
 
renderer.src = _pixman_image_for_solid ((const cairo_solid_pattern_t *) pattern);
if (unlikely (renderer.src == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_MASK;
}
 
_cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
 
/* first blit any aligned part of the boxes */
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
 
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_ceil (box[i].p1.x);
int y1 = _cairo_fixed_integer_ceil (box[i].p1.y);
int x2 = _cairo_fixed_integer_floor (box[i].p2.x);
int y2 = _cairo_fixed_integer_floor (box[i].p2.y);
 
if (x2 > x1 && y2 > y1) {
cairo_box_t b;
 
pixman_fill ((uint32_t *) dst->data,
dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (dst->pixman_format),
x1, y1, x2 - x1, y2 - y1,
pixel);
 
/*
* Corners have to be included only once if the rects
* are passed to the rectangular scan converter
* because it can only handle disjoint rectangles.
*/
 
/* top (including top-left and top-right corners) */
if (! _cairo_fixed_is_integer (box[i].p1.y)) {
b.p1.x = box[i].p1.x;
b.p1.y = box[i].p1.y;
b.p2.x = box[i].p2.x;
b.p2.y = _cairo_fixed_from_int (y1);
 
status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
 
/* left (no corners) */
if (! _cairo_fixed_is_integer (box[i].p1.x)) {
b.p1.x = box[i].p1.x;
b.p1.y = _cairo_fixed_from_int (y1);
b.p2.x = _cairo_fixed_from_int (x1);
b.p2.y = _cairo_fixed_from_int (y2);
 
status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
 
/* right (no corners) */
if (! _cairo_fixed_is_integer (box[i].p2.x)) {
b.p1.x = _cairo_fixed_from_int (x2);
b.p1.y = _cairo_fixed_from_int (y1);
b.p2.x = box[i].p2.x;
b.p2.y = _cairo_fixed_from_int (y2);
 
status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
 
/* bottom (including bottom-left and bottom-right corners) */
if (! _cairo_fixed_is_integer (box[i].p2.y)) {
b.p1.x = box[i].p1.x;
b.p1.y = _cairo_fixed_from_int (y2);
b.p2.x = box[i].p2.x;
b.p2.y = box[i].p2.y;
 
status = _cairo_rectangular_scan_converter_add_box (&converter, &b, 1);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
} else {
status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
if (unlikely (status))
goto CLEANUP_CONVERTER;
}
}
}
 
status = converter.base.generate (&converter.base, &renderer.base);
 
CLEANUP_CONVERTER:
converter.base.destroy (&converter.base);
pixman_image_unref (renderer.src);
CLEANUP_MASK:
pixman_image_unref (renderer.mask);
 
return status;
}
 
typedef struct _cairo_image_surface_span_renderer {
cairo_span_renderer_t base;
 
uint8_t *mask_data;
uint32_t mask_stride;
} cairo_image_surface_span_renderer_t;
 
static cairo_status_t
_cairo_image_surface_span (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
/* high level image interface */
cairo_bool_t
_cairo_image_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
uint8_t *row;
unsigned i;
cairo_image_surface_t *surface = abstract_surface;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
 
/* XXX will it be quicker to repeat the sparse memset,
* or perform a simpler memcpy?
* The fairly dense spiral benchmarks suggests that the sparse
* memset is a win there as well.
*/
row = renderer->mask_data + y * renderer->mask_stride;
do {
for (i = 0; i < num_spans - 1; i++) {
if (! spans[i].coverage)
continue;
 
/* We implement setting rendering the most common single
* pixel wide span case to avoid the overhead of a memset
* call. Open coding setting longer spans didn't show a
* noticeable improvement over memset. */
if (spans[i+1].x == spans[i].x + 1) {
row[spans[i].x] = spans[i].coverage;
} else {
memset (row + spans[i].x,
spans[i].coverage,
spans[i+1].x - spans[i].x);
}
}
row += renderer->mask_stride;
} while (--height);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_composite_unaligned_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
uint8_t buf[CAIRO_STACK_BUFFER_SIZE];
cairo_image_surface_span_renderer_t renderer;
cairo_rectangular_scan_converter_t converter;
pixman_image_t *mask, *src;
cairo_status_t status;
const struct _cairo_boxes_chunk *chunk;
int i, src_x, src_y;
 
i = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8) * extents->bounded.height;
if ((unsigned) i <= sizeof (buf)) {
mask = pixman_image_create_bits (PIXMAN_a8,
extents->bounded.width,
extents->bounded.height,
(uint32_t *) buf,
CAIRO_STRIDE_FOR_WIDTH_BPP (extents->bounded.width, 8));
memset (buf, 0, i);
} else {
mask = pixman_image_create_bits (PIXMAN_a8,
extents->bounded.width,
extents->bounded.height,
NULL, 0);
}
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
renderer.base.render_rows = _cairo_image_surface_span;
renderer.mask_stride = pixman_image_get_stride (mask);
renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
renderer.mask_data -= extents->bounded.y * renderer.mask_stride + extents->bounded.x;
 
_cairo_rectangular_scan_converter_init (&converter, &extents->bounded);
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
 
for (i = 0; i < chunk->count; i++) {
status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
if (unlikely (status))
goto CLEANUP;
}
}
 
status = converter.base.generate (&converter.base, &renderer.base);
if (unlikely (status))
goto CLEANUP;
 
src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y);
if (unlikely (src == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
pixman_image_unref (src);
 
CLEANUP:
converter.base.destroy (&converter.base);
pixman_image_unref (mask);
 
return status;
}
 
static cairo_status_t
_composite_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_boxes_t *boxes,
cairo_antialias_t antialias,
cairo_clip_t *clip,
const cairo_composite_rectangles_t *extents)
{
cairo_region_t *clip_region = NULL;
cairo_bool_t need_clip_mask = FALSE;
cairo_status_t status;
struct _cairo_boxes_chunk *chunk;
uint32_t pixel;
int i;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
need_clip_mask = status == CAIRO_INT_STATUS_UNSUPPORTED;
if (need_clip_mask &&
(op == CAIRO_OPERATOR_SOURCE || ! extents->is_bounded))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1)
clip_region = NULL;
}
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
if (! boxes->is_pixel_aligned) {
if (need_clip_mask)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op,
dst->pixman_format, &pixel))
{
return _fill_unaligned_boxes (dst, pattern, pixel, boxes, extents);
}
else
{
return _composite_unaligned_boxes (dst, op, pattern, boxes, extents);
}
}
}
 
status = CAIRO_STATUS_SUCCESS;
if (! need_clip_mask &&
pattern_to_pixel ((cairo_solid_pattern_t *) pattern, op, dst->pixman_format,
&pixel))
{
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
cairo_box_t *box = chunk->base;
 
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round_down (box[i].p1.x);
int y1 = _cairo_fixed_integer_round_down (box[i].p1.y);
int x2 = _cairo_fixed_integer_round_down (box[i].p2.x);
int y2 = _cairo_fixed_integer_round_down (box[i].p2.y);
 
if (x2 == x1 || y2 == y1)
continue;
 
pixman_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (dst->pixman_format),
x1, y1, x2 - x1, y2 - y1,
pixel);
}
}
}
else
{
pixman_image_t *src = NULL, *mask = NULL;
int src_x, src_y, mask_x = 0, mask_y = 0;
pixman_op_t pixman_op = _pixman_operator (op);
 
if (need_clip_mask) {
cairo_surface_t *clip_surface;
int clip_x, clip_y;
 
clip_surface = _cairo_clip_get_surface (clip, &dst->base, &clip_x, &clip_y);
if (unlikely (clip_surface->status))
return clip_surface->status;
 
mask_x = -clip_x;
mask_y = -clip_y;
 
if (op == CAIRO_OPERATOR_CLEAR) {
pattern = NULL;
pixman_op = PIXMAN_OP_OUT_REVERSE;
}
 
mask = ((cairo_image_surface_t *) clip_surface)->pixman_image;
}
 
if (pattern != NULL) {
src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, &src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else {
src = mask;
src_x = mask_x;
src_y = mask_y;
mask = NULL;
}
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
 
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round_down (box[i].p1.x);
int y1 = _cairo_fixed_integer_round_down (box[i].p1.y);
int x2 = _cairo_fixed_integer_round_down (box[i].p2.x);
int y2 = _cairo_fixed_integer_round_down (box[i].p2.y);
 
if (x2 == x1 || y2 == y1)
continue;
 
pixman_image_composite32 (pixman_op,
src, mask, dst->pixman_image,
x1 + src_x, y1 + src_y,
x1 + mask_x, y1 + mask_y,
x1, y1,
x2 - x1, y2 - y1);
}
}
 
if (pattern != NULL)
pixman_image_unref (src);
 
if (! extents->is_bounded) {
status =
_cairo_image_surface_fixup_unbounded_boxes (dst, extents,
clip_region, boxes);
}
}
 
return status;
}
 
static cairo_status_t
_clip_and_composite_boxes (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents,
cairo_clip_t *clip)
{
cairo_traps_t traps;
cairo_status_t status;
composite_traps_info_t info;
 
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
/* Use a fast path if the boxes are pixel aligned */
status = _composite_boxes (dst, op, src, boxes, antialias, clip, extents);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
/* Otherwise render via a mask and composite in the usual fashion. */
status = _cairo_traps_init_boxes (&traps, boxes);
if (unlikely (status))
return status;
 
info.num_traps = traps.num_traps;
info.traps = traps.traps;
info.antialias = antialias;
status = _clip_and_composite (dst, op, src,
_composite_traps, &info,
extents, clip);
 
_cairo_traps_fini (&traps);
return status;
}
 
static cairo_bool_t
_mono_edge_is_vertical (const cairo_line_t *line)
{
return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
}
 
static cairo_bool_t
_traps_are_pixel_aligned (cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
if (antialias == CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
! _mono_edge_is_vertical (&traps->traps[i].right))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
} else {
for (i = 0; i < traps->num_traps; i++) {
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
}
 
return TRUE;
}
 
static void
_boxes_for_traps (cairo_boxes_t *boxes,
cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
_cairo_boxes_init (boxes);
 
boxes->num_boxes = traps->num_traps;
boxes->chunks.base = (cairo_box_t *) traps->traps;
boxes->chunks.count = traps->num_traps;
boxes->chunks.size = traps->num_traps;
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
boxes->chunks.base[i].p1.x = x1;
boxes->chunks.base[i].p1.y = y1;
boxes->chunks.base[i].p2.x = x2;
boxes->chunks.base[i].p2.y = y2;
 
if (boxes->is_pixel_aligned) {
boxes->is_pixel_aligned =
_cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
_cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
}
}
} else {
boxes->is_pixel_aligned = TRUE;
 
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
/* round down here to match Pixman's behavior when using traps. */
boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
}
}
}
 
static cairo_status_t
_clip_and_composite_trapezoids (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents,
cairo_clip_t *clip)
{
composite_traps_info_t info;
cairo_bool_t need_clip_surface = FALSE;
cairo_status_t status;
 
if (traps->num_traps == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
if (clip != NULL) {
cairo_region_t *clip_region;
 
status = _cairo_clip_get_region (clip, &clip_region);
need_clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (traps->has_intersections) {
if (traps->is_rectangular)
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
else if (traps->is_rectilinear)
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
else
status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
return status;
}
 
/* Use a fast path if the trapezoids consist of a simple region,
* but we can only do this if we do not have a clip surface, or can
* substitute the mask with the clip.
*/
if (traps->maybe_region && _traps_are_pixel_aligned (traps, antialias) &&
(! need_clip_surface ||
(extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
{
cairo_boxes_t boxes;
 
_boxes_for_traps (&boxes, traps, antialias);
return _clip_and_composite_boxes (dst, op, src,
&boxes, antialias,
extents, clip);
}
 
/* No fast path, exclude self-intersections and clip trapezoids. */
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
info.traps = traps->traps;
info.num_traps = traps->num_traps;
info.antialias = antialias;
return _clip_and_composite (dst, op, src,
_composite_traps, &info,
extents, clip);
}
 
static cairo_clip_path_t *
_clip_get_single_path (cairo_clip_t *clip)
{
cairo_clip_path_t *iter = clip->path;
cairo_clip_path_t *path = NULL;
 
do {
if ((iter->flags & CAIRO_CLIP_PATH_IS_BOX) == 0) {
if (path != NULL)
return FALSE;
 
path = iter;
}
iter = iter->prev;
} while (iter != NULL);
 
return path;
}
 
/* high level image interface */
 
static cairo_int_status_t
cairo_int_status_t
_cairo_image_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_clip_path_t *clip_path;
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_paint (&extents,
surface->width,
surface->height,
op, source,
clip);
if (unlikely (status))
return status;
TRACE ((stderr, "%s (surface=%d)\n",
__FUNCTION__, surface->base.unique_id));
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
if (clip != NULL) {
clip = _cairo_clip_init_copy (&local_clip, clip);
have_clip = TRUE;
return _cairo_compositor_paint (surface->compositor,
&surface->base, op, source, clip);
}
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status)) {
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
/* If the clip cannot be reduced to a set of boxes, we will need to
* use a clipmask. Paint is special as it is the only operation that
* does not implicitly use a mask, so we may be able to reduce this
* operation to a fill...
*/
if (clip != NULL &&
extents.is_bounded &&
(clip_path = _clip_get_single_path (clip)) != NULL)
{
status = _cairo_image_surface_fill (surface, op, source,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
NULL);
}
else
{
cairo_boxes_t boxes;
 
_cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
status = _clip_and_composite_boxes (surface, op, source,
&boxes, CAIRO_ANTIALIAS_DEFAULT,
&extents, clip);
}
 
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
static cairo_status_t
_composite_mask (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
const cairo_pattern_t *mask_pattern = closure;
pixman_image_t *src, *mask = NULL;
int src_x = 0, src_y = 0;
int mask_x = 0, mask_y = 0;
 
if (src_pattern != NULL) {
src = _pixman_image_for_pattern (src_pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
mask = _pixman_image_for_pattern (mask_pattern, TRUE, extents, &mask_x, &mask_y);
if (unlikely (mask == NULL)) {
pixman_image_unref (src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
if (mask_pattern->has_component_alpha)
pixman_image_set_component_alpha (mask, TRUE);
} else {
src = _pixman_image_for_pattern (mask_pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
extents->x + src_x, extents->y + src_y,
extents->x + mask_x, extents->y + mask_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
if (mask != NULL)
pixman_image_unref (mask);
pixman_image_unref (src);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
cairo_int_status_t
_cairo_image_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_mask (&extents,
surface->width, surface->height,
op, source, mask, clip);
if (unlikely (status))
return status;
TRACE ((stderr, "%s (surface=%d)\n",
__FUNCTION__, surface->base.unique_id));
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
if (clip != NULL && extents.is_bounded) {
clip = _cairo_clip_init_copy (&local_clip, clip);
status = _cairo_clip_rectangle (clip, &extents.bounded);
if (unlikely (status)) {
_cairo_clip_fini (&local_clip);
return status;
return _cairo_compositor_mask (surface->compositor,
&surface->base, op, source, mask, clip);
}
 
have_clip = TRUE;
}
 
status = _clip_and_composite (surface, op, source,
_composite_mask, (void *) mask,
&extents, clip);
 
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
typedef struct {
cairo_polygon_t *polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
} composite_spans_info_t;
 
//#define USE_BOTOR_SCAN_CONVERTER
static cairo_status_t
_composite_spans (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
uint8_t mask_buf[CAIRO_STACK_BUFFER_SIZE];
composite_spans_info_t *info = closure;
cairo_image_surface_span_renderer_t renderer;
#if USE_BOTOR_SCAN_CONVERTER
cairo_box_t box;
cairo_botor_scan_converter_t converter;
#else
cairo_scan_converter_t *converter;
#endif
pixman_image_t *mask;
cairo_status_t status;
 
#if USE_BOTOR_SCAN_CONVERTER
box.p1.x = _cairo_fixed_from_int (extents->x);
box.p1.y = _cairo_fixed_from_int (extents->y);
box.p2.x = _cairo_fixed_from_int (extents->x + extents->width);
box.p2.y = _cairo_fixed_from_int (extents->y + extents->height);
_cairo_botor_scan_converter_init (&converter, &box, info->fill_rule);
status = converter.base.add_polygon (&converter.base, info->polygon);
#else
converter = _cairo_tor_scan_converter_create (extents->x, extents->y,
extents->x + extents->width,
extents->y + extents->height,
info->fill_rule);
status = converter->add_polygon (converter, info->polygon);
#endif
if (unlikely (status))
goto CLEANUP_CONVERTER;
 
/* TODO: support rendering to A1 surfaces (or: go add span
* compositing to pixman.) */
 
if (pattern == NULL &&
dst_format == PIXMAN_a8 &&
op == CAIRO_OPERATOR_SOURCE)
{
mask = dst;
dst = NULL;
}
else
{
int stride = CAIRO_STRIDE_FOR_WIDTH_BPP (extents->width, 8);
uint8_t *data = mask_buf;
 
if (extents->height * stride <= (int) sizeof (mask_buf))
memset (data, 0, extents->height * stride);
else
data = NULL, stride = 0;
 
mask = pixman_image_create_bits (PIXMAN_a8,
extents->width,
extents->height,
(uint32_t *) data,
stride);
if (unlikely (mask == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_CONVERTER;
}
}
 
renderer.base.render_rows = _cairo_image_surface_span;
renderer.mask_stride = pixman_image_get_stride (mask);
renderer.mask_data = (uint8_t *) pixman_image_get_data (mask);
if (dst != NULL)
renderer.mask_data -= extents->y * renderer.mask_stride + extents->x;
else
renderer.mask_data -= dst_y * renderer.mask_stride + dst_x;
 
#if USE_BOTOR_SCAN_CONVERTER
status = converter.base.generate (&converter.base, &renderer.base);
#else
status = converter->generate (converter, &renderer.base);
#endif
if (unlikely (status))
goto CLEANUP_RENDERER;
 
if (dst != NULL) {
pixman_image_t *src;
int src_x, src_y;
 
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_RENDERER;
}
 
pixman_image_composite32 (_pixman_operator (op), src, mask, dst,
extents->x + src_x, extents->y + src_y,
0, 0, /* mask.x, mask.y */
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
pixman_image_unref (src);
}
 
CLEANUP_RENDERER:
if (dst != NULL)
pixman_image_unref (mask);
CLEANUP_CONVERTER:
#if USE_BOTOR_SCAN_CONVERTER
converter.base.destroy (&converter.base);
#else
converter->destroy (converter);
#endif
return status;
}
 
static cairo_status_t
_clip_and_composite_polygon (cairo_image_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents,
cairo_clip_t *clip)
{
cairo_status_t status;
 
if (polygon->num_edges == 0) {
cairo_traps_t traps;
 
if (extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
_cairo_traps_init (&traps);
status = _clip_and_composite_trapezoids (dst, op, src,
&traps, antialias,
extents, clip);
_cairo_traps_fini (&traps);
 
return status;
}
 
_cairo_box_round_to_rectangle (&polygon->extents, &extents->mask);
if (! _cairo_rectangle_intersect (&extents->bounded, &extents->mask))
return CAIRO_STATUS_SUCCESS;
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
composite_spans_info_t info;
 
info.polygon = polygon;
info.fill_rule = fill_rule;
info.antialias = antialias;
 
status = _clip_and_composite (dst, op, src,
_composite_spans, &info,
extents, clip);
} else {
cairo_traps_t traps;
 
_cairo_traps_init (&traps);
 
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
polygon,
fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_trapezoids (dst, op, src,
&traps, antialias,
extents, clip);
}
 
_cairo_traps_fini (&traps);
}
 
return status;
}
 
static cairo_int_status_t
cairo_int_status_t
_cairo_image_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_stroke (&extents,
surface->width,
surface->height,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
TRACE ((stderr, "%s (surface=%d)\n",
__FUNCTION__, surface->base.unique_id));
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
if (clip != NULL) {
clip = _cairo_clip_init_copy (&local_clip, clip);
have_clip = TRUE;
return _cairo_compositor_stroke (surface->compositor, &surface->base,
op, source, path,
style, ctm, ctm_inverse,
tolerance, antialias, clip);
}
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status)) {
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (path->is_rectilinear) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
_cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
 
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
&boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source,
&boxes, antialias,
&extents, clip);
}
 
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
 
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_polygon (surface, op, source, &polygon,
CAIRO_FILL_RULE_WINDING, antialias,
&extents, clip);
}
 
_cairo_polygon_fini (&polygon);
}
 
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
static cairo_int_status_t
cairo_int_status_t
_cairo_image_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_fill (&extents,
surface->width,
surface->height,
TRACE ((stderr, "%s (surface=%d)\n",
__FUNCTION__, surface->base.unique_id));
 
return _cairo_compositor_fill (surface->compositor, &surface->base,
op, source, path,
fill_rule, tolerance, antialias,
clip);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
if (extents.is_bounded && clip != NULL) {
cairo_clip_path_t *clip_path;
 
if (((clip_path = _clip_get_single_path (clip)) != NULL) &&
_cairo_path_fixed_equal (&clip_path->path, path))
{
clip = NULL;
}
}
 
if (clip != NULL) {
clip = _cairo_clip_init_copy (&local_clip, clip);
have_clip = TRUE;
}
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status)) {
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
if (_cairo_path_fixed_is_rectilinear_fill (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
_cairo_boxes_limit (&boxes, clip_boxes, num_boxes);
 
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
&boxes);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source,
&boxes, antialias,
&extents, clip);
}
 
_cairo_boxes_fini (&boxes);
} else {
cairo_polygon_t polygon;
 
assert (! path->is_empty_fill);
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
 
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _clip_and_composite_polygon (surface, op, source, &polygon,
fill_rule, antialias,
&extents, clip);
}
 
_cairo_polygon_fini (&polygon);
}
 
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
if (have_clip)
_cairo_clip_fini (&local_clip);
 
return status;
}
 
typedef struct {
cairo_scaled_font_t *font;
cairo_glyph_t *glyphs;
int num_glyphs;
} composite_glyphs_info_t;
 
static cairo_status_t
_composite_glyphs_via_mask (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
composite_glyphs_info_t *info = closure;
cairo_scaled_font_t *font = info->font;
cairo_glyph_t *glyphs = info->glyphs;
int num_glyphs = info->num_glyphs;
pixman_image_t *mask = NULL;
pixman_image_t *src;
pixman_image_t *white;
pixman_format_code_t mask_format = 0; /* silence gcc */
cairo_status_t status;
int src_x, src_y;
int i;
 
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
white = _pixman_white_image ();
if (unlikely (white == NULL)) {
pixman_image_unref (src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_scaled_font_freeze_cache (font);
 
for (i = 0; i < num_glyphs; i++) {
int x, y;
cairo_image_surface_t *glyph_surface;
cairo_scaled_glyph_t *scaled_glyph;
 
status = _cairo_scaled_glyph_lookup (font, glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status))
goto CLEANUP;
 
glyph_surface = scaled_glyph->surface;
 
if (glyph_surface->width == 0 || glyph_surface->height == 0)
continue;
 
/* To start, create the mask using the format from the first
* glyph. Later we'll deal with different formats. */
if (mask == NULL) {
mask_format = glyph_surface->pixman_format;
mask = pixman_image_create_bits (mask_format,
extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
if (PIXMAN_FORMAT_RGB (mask_format))
pixman_image_set_component_alpha (mask, TRUE);
}
 
/* If we have glyphs of different formats, we "upgrade" the mask
* to the wider of the formats. */
if (glyph_surface->pixman_format != mask_format &&
PIXMAN_FORMAT_BPP (mask_format) <
PIXMAN_FORMAT_BPP (glyph_surface->pixman_format))
{
pixman_image_t *new_mask;
 
mask_format = glyph_surface->pixman_format;
new_mask = pixman_image_create_bits (mask_format,
extents->width, extents->height,
NULL, 0);
if (unlikely (new_mask == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
pixman_image_composite32 (PIXMAN_OP_SRC,
white, mask, new_mask,
0, 0, 0, 0, 0, 0,
extents->width, extents->height);
 
pixman_image_unref (mask);
mask = new_mask;
if (PIXMAN_FORMAT_RGB (mask_format))
pixman_image_set_component_alpha (mask, TRUE);
}
 
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (glyphs[i].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (glyphs[i].y -
glyph_surface->base.device_transform.y0);
if (glyph_surface->pixman_format == mask_format) {
pixman_image_composite32 (PIXMAN_OP_ADD,
glyph_surface->pixman_image, NULL, mask,
0, 0, 0, 0,
x - extents->x, y - extents->y,
glyph_surface->width,
glyph_surface->height);
} else {
pixman_image_composite32 (PIXMAN_OP_ADD,
white, glyph_surface->pixman_image, mask,
0, 0, 0, 0,
x - extents->x, y - extents->y,
glyph_surface->width,
glyph_surface->height);
}
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
CLEANUP:
_cairo_scaled_font_thaw_cache (font);
if (mask != NULL)
pixman_image_unref (mask);
pixman_image_unref (src);
pixman_image_unref (white);
 
return status;
}
 
static cairo_status_t
_composite_glyphs (void *closure,
pixman_image_t *dst,
pixman_format_code_t dst_format,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
composite_glyphs_info_t *info = closure;
cairo_scaled_glyph_t *glyph_cache[64];
pixman_op_t pixman_op = _pixman_operator (op);
pixman_image_t *src = NULL;
int src_x = 0, src_y = 0;
cairo_status_t status;
int i;
 
if (pattern != NULL) {
src = _pixman_image_for_pattern (pattern, FALSE, extents, &src_x, &src_y);
src_x -= dst_x;
src_y -= dst_y;
} else {
src = _pixman_white_image ();
}
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memset (glyph_cache, 0, sizeof (glyph_cache));
status = CAIRO_STATUS_SUCCESS;
 
_cairo_scaled_font_freeze_cache (info->font);
for (i = 0; i < info->num_glyphs; i++) {
int x, y;
cairo_image_surface_t *glyph_surface;
cairo_scaled_glyph_t *scaled_glyph;
unsigned long glyph_index = info->glyphs[i].index;
int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
 
scaled_glyph = glyph_cache[cache_index];
if (scaled_glyph == NULL ||
_cairo_scaled_glyph_index (scaled_glyph) != glyph_index)
{
status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status))
break;
 
glyph_cache[cache_index] = scaled_glyph;
}
 
glyph_surface = scaled_glyph->surface;
if (glyph_surface->width && glyph_surface->height) {
int x1, y1, x2, y2;
 
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (info->glyphs[i].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (info->glyphs[i].y -
glyph_surface->base.device_transform.y0);
 
x1 = x;
if (x1 < extents->x)
x1 = extents->x;
x2 = x + glyph_surface->width;
if (x2 > extents->x + extents->width)
x2 = extents->x + extents->width;
 
y1 = y;
if (y1 < extents->y)
y1 = extents->y;
y2 = y + glyph_surface->height;
if (y2 > extents->y + extents->height)
y2 = extents->y + extents->height;
 
pixman_image_composite32 (pixman_op,
src, glyph_surface->pixman_image, dst,
x1 + src_x, y1 + src_y,
x1 - x, y1 - y,
x1 - dst_x, y1 - dst_y,
x2 - x1, y2 - y1);
}
}
_cairo_scaled_font_thaw_cache (info->font);
 
pixman_image_unref (src);
 
return status;
}
 
static cairo_int_status_t
cairo_int_status_t
_cairo_image_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
4021,71 → 992,20
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *num_remaining)
const cairo_clip_t *clip)
{
cairo_image_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
composite_glyphs_info_t glyph_info;
cairo_clip_t local_clip;
cairo_bool_t have_clip = FALSE;
cairo_bool_t overlap;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_glyphs (&extents,
surface->width,
surface->height,
TRACE ((stderr, "%s (surface=%d)\n",
__FUNCTION__, surface->base.unique_id));
 
return _cairo_compositor_glyphs (surface->compositor, &surface->base,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
&overlap);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_rectangle (clip, &extents.mask))
clip = NULL;
 
if (clip != NULL && extents.is_bounded) {
clip = _cairo_clip_init_copy (&local_clip, clip);
status = _cairo_clip_rectangle (clip, &extents.bounded);
if (unlikely (status))
return status;
 
have_clip = TRUE;
glyphs, num_glyphs, scaled_font,
clip);
}
 
glyph_info.font = scaled_font;
glyph_info.glyphs = glyphs;
glyph_info.num_glyphs = num_glyphs;
 
status = _clip_and_composite (surface, op, source,
overlap || extents.is_bounded == 0 ? _composite_glyphs_via_mask : _composite_glyphs,
&glyph_info,
&extents, clip);
 
if (have_clip)
_cairo_clip_fini (&local_clip);
 
*num_remaining = 0;
return status;
}
 
static cairo_bool_t
_cairo_image_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_image_surface_t *surface = abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
 
return TRUE;
}
 
static void
void
_cairo_image_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
4092,523 → 1012,40
_cairo_font_options_init_default (options);
 
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
}
 
/* legacy interface kept for compatibility until surface-fallback is removed */
static cairo_status_t
_cairo_image_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_image_surface_t *surface = abstract_surface;
const cairo_surface_backend_t _cairo_image_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_surface_finish,
 
image_rect_out->x = 0;
image_rect_out->y = 0;
image_rect_out->width = surface->width;
image_rect_out->height = surface->height;
_cairo_default_context_create,
 
*image_out = surface;
*image_extra = NULL;
_cairo_image_surface_create_similar,
NULL, /* create similar image */
_cairo_image_surface_map_to_image,
_cairo_image_surface_unmap_image,
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_image_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)
{
}
 
static cairo_status_t
_cairo_image_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_image_surface_t *surface = abstract_surface;
 
if (src->backend == surface->base.backend) {
*clone_offset_x = *clone_offset_y = 0;
*clone_out = cairo_surface_reference (src);
 
return CAIRO_STATUS_SUCCESS;
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_int_status_t
_cairo_image_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_image_surface_t *dst = abstract_dst;
cairo_composite_rectangles_t extents;
pixman_image_t *src;
int src_offset_x, src_offset_y;
cairo_status_t status;
 
if (clip_region != NULL) {
status = _cairo_image_surface_set_clip_region (dst, clip_region);
if (unlikely (status))
return status;
}
 
extents.source.x = src_x;
extents.source.y = src_y;
extents.source.width = width;
extents.source.height = height;
 
extents.mask.x = mask_x;
extents.mask.y = mask_y;
extents.mask.width = width;
extents.mask.height = height;
 
extents.bounded.x = dst_x;
extents.bounded.y = dst_y;
extents.bounded.width = width;
extents.bounded.height = height;
 
extents.unbounded.x = 0;
extents.unbounded.y = 0;
extents.unbounded.width = dst->width;
extents.unbounded.height = dst->height;
 
if (clip_region != NULL) {
cairo_rectangle_int_t rect;
 
cairo_region_get_extents (clip_region, &rect);
if (! _cairo_rectangle_intersect (&extents.unbounded, &rect))
return CAIRO_STATUS_SUCCESS;
}
 
extents.is_bounded = _cairo_operator_bounded_by_either (op);
 
src = _pixman_image_for_pattern (src_pattern, FALSE, &extents.source, &src_offset_x, &src_offset_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = CAIRO_STATUS_SUCCESS;
if (mask_pattern != NULL) {
pixman_image_t *mask;
int mask_offset_x, mask_offset_y;
 
mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents.mask, &mask_offset_x, &mask_offset_y);
if (unlikely (mask == NULL)) {
pixman_image_unref (src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
src_x + src_offset_x,
src_y + src_offset_y,
mask_x + mask_offset_x,
mask_y + mask_offset_y,
dst_x, dst_y, width, height);
 
pixman_image_unref (mask);
} else {
pixman_image_composite32 (_pixman_operator (op),
src, NULL, dst->pixman_image,
src_x + src_offset_x,
src_y + src_offset_y,
0, 0,
dst_x, dst_y, width, height);
}
 
pixman_image_unref (src);
 
if (! extents.is_bounded)
status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
 
if (clip_region != NULL)
_cairo_image_surface_unset_clip_region (dst);
 
return status;
}
 
static cairo_int_status_t
_cairo_image_surface_fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_image_surface_t *surface = abstract_surface;
 
pixman_color_t pixman_color;
pixman_box32_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)];
pixman_box32_t *pixman_boxes = stack_boxes;
int i;
 
cairo_int_status_t status;
 
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
pixman_color.red = color->red_short;
pixman_color.green = color->green_short;
pixman_color.blue = color->blue_short;
pixman_color.alpha = color->alpha_short;
 
if (num_rects > ARRAY_LENGTH (stack_boxes)) {
pixman_boxes = _cairo_malloc_ab (num_rects, sizeof (pixman_box32_t));
if (unlikely (pixman_boxes == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (i = 0; i < num_rects; i++) {
pixman_boxes[i].x1 = rects[i].x;
pixman_boxes[i].y1 = rects[i].y;
pixman_boxes[i].x2 = rects[i].x + rects[i].width;
pixman_boxes[i].y2 = rects[i].y + rects[i].height;
}
 
status = CAIRO_STATUS_SUCCESS;
if (! pixman_image_fill_boxes (_pixman_operator (op),
surface->pixman_image,
&pixman_color,
num_rects,
pixman_boxes))
{
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
if (pixman_boxes != stack_boxes)
free (pixman_boxes);
 
return status;
}
 
static cairo_int_status_t
_cairo_image_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_image_surface_t *dst = abstract_dst;
cairo_composite_rectangles_t extents;
cairo_pattern_union_t source_pattern;
composite_traps_info_t info;
cairo_status_t status;
 
if (height == 0 || width == 0)
return CAIRO_STATUS_SUCCESS;
 
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
extents.source.x = src_x;
extents.source.y = src_y;
extents.source.width = width;
extents.source.height = height;
 
extents.mask.x = dst_x;
extents.mask.y = dst_y;
extents.mask.width = width;
extents.mask.height = height;
 
extents.bounded.x = dst_x;
extents.bounded.y = dst_y;
extents.bounded.width = width;
extents.bounded.height = height;
 
extents.unbounded.x = 0;
extents.unbounded.y = 0;
extents.unbounded.width = dst->width;
extents.unbounded.height = dst->height;
 
if (clip_region != NULL) {
cairo_rectangle_int_t rect;
 
cairo_region_get_extents (clip_region, &rect);
if (! _cairo_rectangle_intersect (&extents.unbounded, &rect))
return CAIRO_STATUS_SUCCESS;
}
 
extents.is_bounded = _cairo_operator_bounded_by_either (op);
 
if (clip_region != NULL) {
status = _cairo_image_surface_set_clip_region (dst, clip_region);
if (unlikely (status))
return status;
}
 
_cairo_pattern_init_static_copy (&source_pattern.base, pattern);
cairo_matrix_translate (&source_pattern.base.matrix,
src_x - extents.bounded.x,
src_y - extents.bounded.y);
 
info.traps = traps;
info.num_traps = num_traps;
info.antialias = antialias;
status = _composite_traps (&info,
dst->pixman_image,
dst->pixman_format,
op,
&source_pattern.base,
0, 0,
&extents.bounded,
clip_region);
 
if (status == CAIRO_STATUS_SUCCESS && ! extents.is_bounded)
status = _cairo_image_surface_fixup_unbounded (dst, &extents, NULL);
 
if (clip_region != NULL)
_cairo_image_surface_unset_clip_region (dst);
 
return status;
}
 
typedef struct _legacy_image_surface_span_renderer {
cairo_span_renderer_t base;
 
cairo_operator_t op;
const cairo_pattern_t *pattern;
cairo_antialias_t antialias;
cairo_region_t *clip_region;
 
pixman_image_t *mask;
uint8_t *mask_data;
uint32_t mask_stride;
 
cairo_image_surface_t *dst;
cairo_composite_rectangles_t composite_rectangles;
} legacy_image_surface_span_renderer_t;
 
void
_cairo_image_surface_span_render_row (
int y,
const cairo_half_open_span_t *spans,
unsigned num_spans,
uint8_t *data,
uint32_t stride)
{
uint8_t *row;
unsigned i;
 
if (num_spans == 0)
return;
 
row = data + y * stride;
for (i = 0; i < num_spans - 1; i++) {
if (! spans[i].coverage)
continue;
 
/* We implement setting the most common single pixel wide
* span case to avoid the overhead of a memset call.
* Open coding setting longer spans didn't show a
* noticeable improvement over memset.
*/
if (spans[i+1].x == spans[i].x + 1) {
row[spans[i].x] = spans[i].coverage;
} else {
memset (row + spans[i].x,
spans[i].coverage,
spans[i+1].x - spans[i].x);
}
}
}
 
static cairo_status_t
_cairo_image_surface_span_renderer_render_rows (
void *abstract_renderer,
int y,
int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
while (height--)
_cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_image_surface_span_renderer_destroy (void *abstract_renderer)
{
legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
if (renderer == NULL)
return;
 
pixman_image_unref (renderer->mask);
 
free (renderer);
}
 
static cairo_status_t
_cairo_image_surface_span_renderer_finish (void *abstract_renderer)
{
legacy_image_surface_span_renderer_t *renderer = abstract_renderer;
cairo_composite_rectangles_t *rects = &renderer->composite_rectangles;
cairo_image_surface_t *dst = renderer->dst;
pixman_image_t *src;
int src_x, src_y;
cairo_status_t status;
 
if (renderer->clip_region != NULL) {
status = _cairo_image_surface_set_clip_region (dst, renderer->clip_region);
if (unlikely (status))
return status;
}
 
src = _pixman_image_for_pattern (renderer->pattern, FALSE, &rects->bounded, &src_x, &src_y);
if (src == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = CAIRO_STATUS_SUCCESS;
pixman_image_composite32 (_pixman_operator (renderer->op),
src,
renderer->mask,
dst->pixman_image,
rects->bounded.x + src_x,
rects->bounded.y + src_y,
0, 0,
rects->bounded.x, rects->bounded.y,
rects->bounded.width, rects->bounded.height);
 
if (! rects->is_bounded)
status = _cairo_image_surface_fixup_unbounded (dst, rects, NULL);
 
if (renderer->clip_region != NULL)
_cairo_image_surface_unset_clip_region (dst);
 
return status;
}
 
static cairo_bool_t
_cairo_image_surface_check_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias)
{
return TRUE;
(void) op;
(void) pattern;
(void) abstract_dst;
(void) antialias;
}
 
static cairo_span_renderer_t *
_cairo_image_surface_create_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *abstract_dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region)
{
cairo_image_surface_t *dst = abstract_dst;
legacy_image_surface_span_renderer_t *renderer;
 
renderer = calloc(1, sizeof(*renderer));
if (unlikely (renderer == NULL))
return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
renderer->base.finish = _cairo_image_surface_span_renderer_finish;
renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows;
renderer->op = op;
renderer->pattern = pattern;
renderer->antialias = antialias;
renderer->dst = dst;
renderer->clip_region = clip_region;
 
renderer->composite_rectangles = *rects;
 
/* TODO: support rendering to A1 surfaces (or: go add span
* compositing to pixman.) */
renderer->mask = pixman_image_create_bits (PIXMAN_a8,
rects->bounded.width,
rects->bounded.height,
NULL, 0);
if (renderer->mask == NULL) {
free (renderer);
return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
renderer->mask_stride = pixman_image_get_stride (renderer->mask);
renderer->mask_data = (uint8_t *) pixman_image_get_data (renderer->mask) - rects->bounded.x - rects->bounded.y * renderer->mask_stride;
 
return &renderer->base;
}
 
/**
* _cairo_surface_is_image:
* @surface: a #cairo_surface_t
*
* Checks if a surface is an #cairo_image_surface_t
*
* Return value: %TRUE if the surface is an image surface
**/
cairo_bool_t
_cairo_surface_is_image (const cairo_surface_t *surface)
{
return surface->backend == &_cairo_image_surface_backend;
}
 
const cairo_surface_backend_t _cairo_image_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_surface_create_similar,
_cairo_image_surface_finish,
_cairo_image_surface_source,
_cairo_image_surface_acquire_source_image,
_cairo_image_surface_release_source_image,
_cairo_image_surface_acquire_dest_image,
_cairo_image_surface_release_dest_image,
_cairo_image_surface_clone_similar,
_cairo_image_surface_composite,
_cairo_image_surface_fill_rectangles,
_cairo_image_surface_composite_trapezoids,
_cairo_image_surface_create_span_renderer,
_cairo_image_surface_check_span_renderer,
_cairo_image_surface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_image_surface_get_extents,
NULL, /* old_show_glyphs */
_cairo_image_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark dirty */
NULL, /* font_fini */
NULL, /* glyph_fini */
NULL,
 
_cairo_image_surface_paint,
_cairo_image_surface_mask,
_cairo_image_surface_stroke,
_cairo_image_surface_fill,
NULL, /* fill-stroke */
_cairo_image_surface_glyphs,
NULL, /* show_text_glyphs */
NULL, /* snapshot */
NULL, /* is_similar */
};
 
/* A convenience function for when one needs to coerce an image
4618,7 → 1055,6
{
return _cairo_image_surface_coerce_to_format (surface,
_cairo_format_from_content (surface->base.content));
}
 
/* A convenience function for when one needs to coerce an image
4658,6 → 1094,61
return clone;
}
 
cairo_image_surface_t *
_cairo_image_surface_create_from_image (cairo_image_surface_t *other,
pixman_format_code_t format,
int x, int y,
int width, int height, int stride)
{
cairo_image_surface_t *surface;
cairo_status_t status;
pixman_image_t *image;
void *mem = NULL;
 
status = other->base.status;
if (unlikely (status))
goto cleanup;
 
if (stride) {
mem = _cairo_malloc_ab (height, stride);
if (unlikely (mem == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
}
 
image = pixman_image_create_bits (format, width, height, mem, stride);
if (unlikely (image == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup_mem;
}
 
surface = (cairo_image_surface_t *)
_cairo_image_surface_create_for_pixman_image (image, format);
if (unlikely (surface->base.status)) {
status = surface->base.status;
goto cleanup_image;
}
 
pixman_image_composite32 (PIXMAN_OP_SRC,
other->pixman_image, NULL, image,
x, y,
0, 0,
0, 0,
width, height);
surface->base.is_clear = FALSE;
surface->owns_data = mem != NULL;
 
return surface;
 
cleanup_image:
pixman_image_unref (image);
cleanup_mem:
free (mem);
cleanup:
return (cairo_image_surface_t *) _cairo_surface_create_in_error (status);
}
 
cairo_image_transparency_t
_cairo_image_analyze_transparency (cairo_image_surface_t *image)
{
4669,12 → 1160,26
if ((image->base.content & CAIRO_CONTENT_ALPHA) == 0)
return image->transparency = CAIRO_IMAGE_IS_OPAQUE;
 
if (image->base.is_clear)
return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
 
if ((image->base.content & CAIRO_CONTENT_COLOR) == 0) {
if (image->format == CAIRO_FORMAT_A1)
if (image->format == CAIRO_FORMAT_A1) {
return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
else
} else if (image->format == CAIRO_FORMAT_A8) {
for (y = 0; y < image->height; y++) {
uint8_t *alpha = (uint8_t *) (image->data + y * image->stride);
 
for (x = 0; x < image->width; x++, alpha++) {
if (*alpha > 0 && *alpha < 255)
return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
}
}
return image->transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
} else {
return image->transparency = CAIRO_IMAGE_HAS_ALPHA;
}
}
 
if (image->format == CAIRO_FORMAT_RGB16_565) {
image->transparency = CAIRO_IMAGE_IS_OPAQUE;
4700,3 → 1205,114
 
return image->transparency;
}
 
cairo_image_color_t
_cairo_image_analyze_color (cairo_image_surface_t *image)
{
int x, y;
 
if (image->color != CAIRO_IMAGE_UNKNOWN_COLOR)
return image->color;
 
if (image->format == CAIRO_FORMAT_A1)
return image->color = CAIRO_IMAGE_IS_MONOCHROME;
 
if (image->format == CAIRO_FORMAT_A8)
return image->color = CAIRO_IMAGE_IS_GRAYSCALE;
 
if (image->format == CAIRO_FORMAT_ARGB32) {
image->color = CAIRO_IMAGE_IS_MONOCHROME;
for (y = 0; y < image->height; y++) {
uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
 
for (x = 0; x < image->width; x++, pixel++) {
int a = (*pixel & 0xff000000) >> 24;
int r = (*pixel & 0x00ff0000) >> 16;
int g = (*pixel & 0x0000ff00) >> 8;
int b = (*pixel & 0x000000ff);
if (a == 0) {
r = g = b = 0;
} else {
r = (r * 255 + a / 2) / a;
g = (g * 255 + a / 2) / a;
b = (b * 255 + a / 2) / a;
}
if (!(r == g && g == b))
return image->color = CAIRO_IMAGE_IS_COLOR;
else if (r > 0 && r < 255)
image->color = CAIRO_IMAGE_IS_GRAYSCALE;
}
}
return image->color;
}
 
if (image->format == CAIRO_FORMAT_RGB24) {
image->color = CAIRO_IMAGE_IS_MONOCHROME;
for (y = 0; y < image->height; y++) {
uint32_t *pixel = (uint32_t *) (image->data + y * image->stride);
 
for (x = 0; x < image->width; x++, pixel++) {
int r = (*pixel & 0x00ff0000) >> 16;
int g = (*pixel & 0x0000ff00) >> 8;
int b = (*pixel & 0x000000ff);
if (!(r == g && g == b))
return image->color = CAIRO_IMAGE_IS_COLOR;
else if (r > 0 && r < 255)
image->color = CAIRO_IMAGE_IS_GRAYSCALE;
}
}
return image->color;
}
 
return image->color = CAIRO_IMAGE_IS_COLOR;
}
 
cairo_image_surface_t *
_cairo_image_surface_clone_subimage (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *image;
cairo_surface_pattern_t pattern;
cairo_status_t status;
 
image = cairo_surface_create_similar_image (surface,
_cairo_format_from_content (surface->content),
extents->width,
extents->height);
if (image->status)
return to_image_surface (image);
 
/* TODO: check me with non-identity device_transform. Should we
* clone the scaling, too? */
cairo_surface_set_device_offset (image,
-extents->x,
-extents->y);
 
_cairo_pattern_init_for_surface (&pattern, surface);
pattern.base.filter = CAIRO_FILTER_NEAREST;
 
status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
 
_cairo_pattern_fini (&pattern.base);
 
if (unlikely (status))
goto error;
 
/* We use the parent as a flag during map-to-image/umap-image that the
* resultant image came from a fallback rather than as direct call
* to the backend's map_to_image(). Whilst we use it as a simple flag,
* we need to make sure the parent surface obeys the reference counting
* semantics and is consistent for all callers.
*/
_cairo_image_surface_set_parent (to_image_surface (image),
cairo_surface_reference (surface));
 
return to_image_surface (image);
 
error:
cairo_surface_destroy (image);
return to_image_surface (_cairo_surface_create_in_error (status));
}
/programs/develop/libraries/cairo/src/cairo-list-inline.h
0,0 → 1,215
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Chris Wilson
*
* 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 Chris Wilson.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*
*/
 
#ifndef CAIRO_LIST_INLINE_H
#define CAIRO_LIST_INLINE_H
 
#include "cairo-list-private.h"
 
#define cairo_list_entry(ptr, type, member) \
cairo_container_of(ptr, type, member)
 
#define cairo_list_first_entry(ptr, type, member) \
cairo_list_entry((ptr)->next, type, member)
 
#define cairo_list_last_entry(ptr, type, member) \
cairo_list_entry((ptr)->prev, type, member)
 
#define cairo_list_foreach(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
 
#define cairo_list_foreach_entry(pos, type, head, member) \
for (pos = cairo_list_entry((head)->next, type, member);\
&pos->member != (head); \
pos = cairo_list_entry(pos->member.next, type, member))
 
#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \
for (pos = cairo_list_entry ((head)->next, type, member),\
n = cairo_list_entry (pos->member.next, type, member);\
&pos->member != (head); \
pos = n, n = cairo_list_entry (n->member.next, type, member))
 
#define cairo_list_foreach_entry_reverse(pos, type, head, member) \
for (pos = cairo_list_entry((head)->prev, type, member);\
&pos->member != (head); \
pos = cairo_list_entry(pos->member.prev, type, member))
 
#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \
for (pos = cairo_list_entry((head)->prev, type, member),\
n = cairo_list_entry (pos->member.prev, type, member);\
&pos->member != (head); \
pos = n, n = cairo_list_entry (n->member.prev, type, member))
 
#ifdef CAIRO_LIST_DEBUG
static inline void
_cairo_list_validate (const cairo_list_t *link)
{
assert (link->next->prev == link);
assert (link->prev->next == link);
}
static inline void
cairo_list_validate (const cairo_list_t *head)
{
cairo_list_t *link;
 
cairo_list_foreach (link, head)
_cairo_list_validate (link);
}
static inline cairo_bool_t
cairo_list_is_empty (const cairo_list_t *head);
static inline void
cairo_list_validate_is_empty (const cairo_list_t *head)
{
assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev));
}
#else
#define _cairo_list_validate(link)
#define cairo_list_validate(head)
#define cairo_list_validate_is_empty(head)
#endif
 
static inline void
cairo_list_init (cairo_list_t *entry)
{
entry->next = entry;
entry->prev = entry;
}
 
static inline void
__cairo_list_add (cairo_list_t *entry,
cairo_list_t *prev,
cairo_list_t *next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
 
static inline void
cairo_list_add (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
cairo_list_validate_is_empty (entry);
__cairo_list_add (entry, head, head->next);
cairo_list_validate (head);
}
 
static inline void
cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
cairo_list_validate_is_empty (entry);
__cairo_list_add (entry, head->prev, head);
cairo_list_validate (head);
}
 
static inline void
__cairo_list_del (cairo_list_t *prev, cairo_list_t *next)
{
next->prev = prev;
prev->next = next;
}
 
static inline void
_cairo_list_del (cairo_list_t *entry)
{
__cairo_list_del (entry->prev, entry->next);
}
 
static inline void
cairo_list_del (cairo_list_t *entry)
{
_cairo_list_del (entry);
cairo_list_init (entry);
}
 
static inline void
cairo_list_move (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
__cairo_list_del (entry->prev, entry->next);
__cairo_list_add (entry, head, head->next);
cairo_list_validate (head);
}
 
static inline void
cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
__cairo_list_del (entry->prev, entry->next);
__cairo_list_add (entry, head->prev, head);
cairo_list_validate (head);
}
 
static inline void
cairo_list_swap (cairo_list_t *entry, cairo_list_t *other)
{
__cairo_list_add (entry, other->prev, other->next);
cairo_list_init (other);
}
 
static inline cairo_bool_t
cairo_list_is_first (const cairo_list_t *entry,
const cairo_list_t *head)
{
cairo_list_validate (head);
return entry->prev == head;
}
 
static inline cairo_bool_t
cairo_list_is_last (const cairo_list_t *entry,
const cairo_list_t *head)
{
cairo_list_validate (head);
return entry->next == head;
}
 
static inline cairo_bool_t
cairo_list_is_empty (const cairo_list_t *head)
{
cairo_list_validate (head);
return head->next == head;
}
 
static inline cairo_bool_t
cairo_list_is_singular (const cairo_list_t *head)
{
cairo_list_validate (head);
return head->next == head || head->next == head->prev;
}
 
#endif /* CAIRO_LIST_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-list-private.h
45,171 → 45,4
struct _cairo_list *next, *prev;
} cairo_list_t;
 
#define cairo_list_entry(ptr, type, member) \
cairo_container_of(ptr, type, member)
 
#define cairo_list_first_entry(ptr, type, member) \
cairo_list_entry((ptr)->next, type, member)
 
#define cairo_list_last_entry(ptr, type, member) \
cairo_list_entry((ptr)->prev, type, member)
 
#define cairo_list_foreach(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
 
#define cairo_list_foreach_entry(pos, type, head, member) \
for (pos = cairo_list_entry((head)->next, type, member);\
&pos->member != (head); \
pos = cairo_list_entry(pos->member.next, type, member))
 
#define cairo_list_foreach_entry_safe(pos, n, type, head, member) \
for (pos = cairo_list_entry ((head)->next, type, member),\
n = cairo_list_entry (pos->member.next, type, member);\
&pos->member != (head); \
pos = n, n = cairo_list_entry (n->member.next, type, member))
 
#define cairo_list_foreach_entry_reverse(pos, type, head, member) \
for (pos = cairo_list_entry((head)->prev, type, member);\
&pos->member != (head); \
pos = cairo_list_entry(pos->member.prev, type, member))
 
#define cairo_list_foreach_entry_reverse_safe(pos, n, type, head, member) \
for (pos = cairo_list_entry((head)->prev, type, member),\
n = cairo_list_entry (pos->member.prev, type, member);\
&pos->member != (head); \
pos = n, n = cairo_list_entry (n->member.prev, type, member))
 
#ifdef CAIRO_LIST_DEBUG
static inline void
_cairo_list_validate (const cairo_list_t *link)
{
assert (link->next->prev == link);
assert (link->prev->next == link);
}
static inline void
cairo_list_validate (const cairo_list_t *head)
{
cairo_list_t *link;
 
cairo_list_foreach (link, head)
_cairo_list_validate (link);
}
static inline cairo_bool_t
cairo_list_is_empty (const cairo_list_t *head);
static inline void
cairo_list_validate_is_empty (const cairo_list_t *head)
{
assert (head->next == NULL || (cairo_list_is_empty (head) && head->next == head->prev));
}
#else
#define _cairo_list_validate(link)
#define cairo_list_validate(head)
#define cairo_list_validate_is_empty(head)
#endif
 
static inline void
cairo_list_init (cairo_list_t *entry)
{
entry->next = entry;
entry->prev = entry;
}
 
static inline void
__cairo_list_add (cairo_list_t *entry,
cairo_list_t *prev,
cairo_list_t *next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
 
static inline void
cairo_list_add (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
cairo_list_validate_is_empty (entry);
__cairo_list_add (entry, head, head->next);
cairo_list_validate (head);
}
 
static inline void
cairo_list_add_tail (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
cairo_list_validate_is_empty (entry);
__cairo_list_add (entry, head->prev, head);
cairo_list_validate (head);
}
 
static inline void
__cairo_list_del (cairo_list_t *prev, cairo_list_t *next)
{
next->prev = prev;
prev->next = next;
}
 
static inline void
cairo_list_del (cairo_list_t *entry)
{
__cairo_list_del (entry->prev, entry->next);
cairo_list_init (entry);
}
 
static inline void
cairo_list_move (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
__cairo_list_del (entry->prev, entry->next);
__cairo_list_add (entry, head, head->next);
cairo_list_validate (head);
}
 
static inline void
cairo_list_move_tail (cairo_list_t *entry, cairo_list_t *head)
{
cairo_list_validate (head);
__cairo_list_del (entry->prev, entry->next);
__cairo_list_add (entry, head->prev, head);
cairo_list_validate (head);
}
 
static inline void
cairo_list_swap (cairo_list_t *entry, cairo_list_t *other)
{
__cairo_list_add (entry, other->prev, other->next);
cairo_list_init (other);
}
 
static inline cairo_bool_t
cairo_list_is_first (const cairo_list_t *entry,
const cairo_list_t *head)
{
cairo_list_validate (head);
return entry->prev == head;
}
 
static inline cairo_bool_t
cairo_list_is_last (const cairo_list_t *entry,
const cairo_list_t *head)
{
cairo_list_validate (head);
return entry->next == head;
}
 
static inline cairo_bool_t
cairo_list_is_empty (const cairo_list_t *head)
{
cairo_list_validate (head);
return head->next == head;
}
 
static inline cairo_bool_t
cairo_list_is_singular (const cairo_list_t *head)
{
cairo_list_validate (head);
return head->next == head || head->next == head->prev;
}
 
#endif /* CAIRO_LIST_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-malloc-private.h
38,6 → 38,7
#define CAIRO_MALLOC_PRIVATE_H
 
#include "cairo-wideint-private.h"
#include <stdlib.h>
 
#if HAVE_MEMFAULT
#include <memfault.h>
56,7 → 57,7
*
* Return value: A pointer to the newly allocated memory, or %NULL in
* case of malloc() failure or size is 0.
*/
**/
 
#define _cairo_malloc(size) \
((size) ? malloc((unsigned) (size)) : NULL)
63,10 → 64,10
 
/**
* _cairo_malloc_ab:
* @n: number of elements to allocate
* @a: number of elements to allocate
* @size: size of each element
*
* Allocates @n*@size memory using _cairo_malloc(), taking care to not
* Allocates @a*@size memory using _cairo_malloc(), taking care to not
* overflow when doing the multiplication. Behaves much like
* calloc(), except that the returned memory is not set to zero.
* The memory should be freed using free().
76,7 → 77,7
*
* Return value: A pointer to the newly allocated memory, or %NULL in
* case of malloc() failure or overflow.
*/
**/
 
#define _cairo_malloc_ab(a, size) \
((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
85,10 → 86,10
/**
* _cairo_realloc_ab:
* @ptr: original pointer to block of memory to be resized
* @n: number of elements to allocate
* @a: number of elements to allocate
* @size: size of each element
*
* Reallocates @ptr a block of @n*@size memory using realloc(), taking
* Reallocates @ptr a block of @a*@size memory using realloc(), taking
* care to not overflow when doing the multiplication. The memory
* should be freed using free().
*
98,7 → 99,7
* Return value: A pointer to the newly allocated memory, or %NULL in
* case of realloc() failure or overflow (whereupon the original block
* of memory * is left untouched).
*/
**/
 
#define _cairo_realloc_ab(ptr, a, size) \
((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
106,11 → 107,11
 
/**
* _cairo_malloc_abc:
* @n: first factor of number of elements to allocate
* @a: first factor of number of elements to allocate
* @b: second factor of number of elements to allocate
* @size: size of each element
*
* Allocates @n*@b*@size memory using _cairo_malloc(), taking care to not
* Allocates @a*@b*@size memory using _cairo_malloc(), taking care to not
* overflow when doing the multiplication. Behaves like
* _cairo_malloc_ab(). The memory should be freed using free().
*
119,7 → 120,7
*
* Return value: A pointer to the newly allocated memory, or %NULL in
* case of malloc() failure or overflow.
*/
**/
 
#define _cairo_malloc_abc(a, b, size) \
((b) && (unsigned) (a) >= INT32_MAX / (unsigned) (b) ? NULL : \
128,21 → 129,21
 
/**
* _cairo_malloc_ab_plus_c:
* @n: number of elements to allocate
* @a: number of elements to allocate
* @size: size of each element
* @k: additional size to allocate
* @c: additional size to allocate
*
* Allocates @n*@ksize+@k memory using _cairo_malloc(), taking care to not
* overflow when doing the arithmetic. Behaves like
* Allocates @a*@size+@c memory using _cairo_malloc(), taking care to not
* overflow when doing the arithmetic. Behaves similar to
* _cairo_malloc_ab(). The memory should be freed using free().
*
* Return value: A pointer to the newly allocated memory, or %NULL in
* case of malloc() failure or overflow.
*/
**/
 
#define _cairo_malloc_ab_plus_c(n, size, k) \
((size) && (unsigned) (n) >= INT32_MAX / (unsigned) (size) ? NULL : \
(unsigned) (k) >= INT32_MAX - (unsigned) (n) * (unsigned) (size) ? NULL : \
_cairo_malloc((unsigned) (n) * (unsigned) (size) + (unsigned) (k)))
#define _cairo_malloc_ab_plus_c(a, size, c) \
((size) && (unsigned) (a) >= INT32_MAX / (unsigned) (size) ? NULL : \
(unsigned) (c) >= INT32_MAX - (unsigned) (a) * (unsigned) (size) ? NULL : \
_cairo_malloc((unsigned) (a) * (unsigned) (size) + (unsigned) (c)))
 
#endif /* CAIRO_MALLOC_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-mask-compositor.c
0,0 → 1,1479
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* This compositor renders the shape to a mask using an image surface
* then calls composite.
*/
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-compositor-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-pattern-inline.h"
#include "cairo-region-private.h"
#include "cairo-surface-observer-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
 
typedef cairo_int_status_t
(*draw_func_t) (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_rectangle_int_t *src_sample,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip);
 
static void do_unaligned_row(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b,
int tx, int y, int h,
uint16_t coverage)
{
int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
if (x2 > x1) {
if (! _cairo_fixed_is_integer (b->p1.x)) {
blt(closure, x1, y, 1, h,
coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
x1++;
}
 
if (x2 > x1)
blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
 
if (! _cairo_fixed_is_integer (b->p2.x))
blt(closure, x2, y, 1, h,
coverage * _cairo_fixed_fractional_part (b->p2.x));
} else
blt(closure, x1, y, 1, h,
coverage * (b->p2.x - b->p1.x));
}
 
static void do_unaligned_box(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b, int tx, int ty)
{
int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
if (y2 > y1) {
if (! _cairo_fixed_is_integer (b->p1.y)) {
do_unaligned_row(blt, closure, b, tx, y1, 1,
256 - _cairo_fixed_fractional_part (b->p1.y));
y1++;
}
 
if (y2 > y1)
do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
 
if (! _cairo_fixed_is_integer (b->p2.y))
do_unaligned_row(blt, closure, b, tx, y2, 1,
_cairo_fixed_fractional_part (b->p2.y));
} else
do_unaligned_row(blt, closure, b, tx, y1, 1,
b->p2.y - b->p1.y);
}
 
struct blt_in {
const cairo_mask_compositor_t *compositor;
cairo_surface_t *dst;
};
 
static void blt_in(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct blt_in *info = closure;
cairo_color_t color;
cairo_rectangle_int_t rect;
 
if (coverage == 0xffff)
return;
 
rect.x = x;
rect.y = y;
rect.width = w;
rect.height = h;
 
_cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
info->compositor->fill_rectangles (info->dst, CAIRO_OPERATOR_IN,
&color, &rect, 1);
}
 
static cairo_surface_t *
create_composite_mask (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
void *draw_closure,
draw_func_t draw_func,
draw_func_t mask_func,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *surface;
cairo_int_status_t status;
struct blt_in info;
int i;
 
surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA,
extents->bounded.width,
extents->bounded.height);
if (unlikely (surface->status))
return surface;
 
status = compositor->acquire (surface);
if (unlikely (status)) {
cairo_surface_destroy (surface);
return _cairo_int_surface_create_in_error (status);
}
 
if (!surface->is_clear) {
cairo_rectangle_int_t rect;
 
rect.x = rect.y = 0;
rect.width = extents->bounded.width;
rect.height = extents->bounded.height;
 
status = compositor->fill_rectangles (surface, CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&rect, 1);
if (unlikely (status))
goto error;
}
 
if (mask_func) {
status = mask_func (compositor, surface, draw_closure,
CAIRO_OPERATOR_SOURCE, NULL, NULL,
extents->bounded.x, extents->bounded.y,
&extents->bounded, extents->clip);
if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
goto out;
}
 
/* Is it worth setting the clip region here? */
status = draw_func (compositor, surface, draw_closure,
CAIRO_OPERATOR_ADD, NULL, NULL,
extents->bounded.x, extents->bounded.y,
&extents->bounded, NULL);
if (unlikely (status))
goto error;
 
info.compositor = compositor;
info.dst = surface;
for (i = 0; i < extents->clip->num_boxes; i++) {
cairo_box_t *b = &extents->clip->boxes[i];
 
if (! _cairo_fixed_is_integer (b->p1.x) ||
! _cairo_fixed_is_integer (b->p1.y) ||
! _cairo_fixed_is_integer (b->p2.x) ||
! _cairo_fixed_is_integer (b->p2.y))
{
do_unaligned_box(blt_in, &info, b,
extents->bounded.x,
extents->bounded.y);
}
}
 
if (extents->clip->path != NULL) {
status = _cairo_clip_combine_with_surface (extents->clip, surface,
extents->bounded.x,
extents->bounded.y);
if (unlikely (status))
goto error;
}
 
out:
compositor->release (surface);
surface->is_clear = FALSE;
return surface;
 
error:
compositor->release (surface);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
cairo_surface_destroy (surface);
surface = _cairo_int_surface_create_in_error (status);
}
return surface;
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
clip_and_composite_with_mask (const cairo_mask_compositor_t *compositor,
void *draw_closure,
draw_func_t draw_func,
draw_func_t mask_func,
cairo_operator_t op,
cairo_pattern_t *pattern,
const cairo_composite_rectangles_t*extents)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *mask, *src;
int src_x, src_y;
 
mask = create_composite_mask (compositor, dst, draw_closure,
draw_func, mask_func,
extents);
if (unlikely (mask->status))
return mask->status;
 
if (pattern != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
src = compositor->pattern_to_surface (dst,
&extents->source_pattern.base,
FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (unlikely (src->status)) {
cairo_surface_destroy (mask);
return src->status;
}
 
compositor->composite (dst, op, src, mask,
extents->bounded.x + src_x,
extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
cairo_surface_destroy (src);
} else {
compositor->composite (dst, op, mask, NULL,
0, 0,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
cairo_surface_destroy (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
get_clip_source (const cairo_mask_compositor_t *compositor,
cairo_clip_t *clip,
cairo_surface_t *dst,
const cairo_rectangle_int_t *bounds,
int *out_x, int *out_y)
{
cairo_surface_pattern_t pattern;
cairo_rectangle_int_t r;
cairo_surface_t *surface;
 
surface = _cairo_clip_get_image (clip, dst, bounds);
if (unlikely (surface->status))
return surface;
 
_cairo_pattern_init_for_surface (&pattern, surface);
pattern.base.filter = CAIRO_FILTER_NEAREST;
cairo_surface_destroy (surface);
 
r.x = r.y = 0;
r.width = bounds->width;
r.height = bounds->height;
 
surface = compositor->pattern_to_surface (dst, &pattern.base, TRUE,
&r, &r, out_x, out_y);
_cairo_pattern_fini (&pattern.base);
 
*out_x += -bounds->x;
*out_y += -bounds->y;
return surface;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
clip_and_composite_combine (const cairo_mask_compositor_t *compositor,
void *draw_closure,
draw_func_t draw_func,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_composite_rectangles_t*extents)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *tmp, *clip;
cairo_status_t status;
int clip_x, clip_y;
 
tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
extents->bounded.width,
extents->bounded.height);
if (unlikely (tmp->status))
return tmp->status;
 
compositor->composite (tmp, CAIRO_OPERATOR_SOURCE, dst, NULL,
extents->bounded.x, extents->bounded.y,
0, 0,
0, 0,
extents->bounded.width, extents->bounded.height);
 
status = draw_func (compositor, tmp, draw_closure, op,
pattern, &extents->source_sample_area,
extents->bounded.x, extents->bounded.y,
&extents->bounded, NULL);
if (unlikely (status))
goto cleanup;
 
clip = get_clip_source (compositor,
extents->clip, dst, &extents->bounded,
&clip_x, &clip_y);
if (unlikely ((status = clip->status)))
goto cleanup;
 
if (dst->is_clear) {
compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
0, 0,
clip_x, clip_y,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
} else {
/* Punch the clip out of the destination */
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL,
clip_x, clip_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
/* Now add the two results together */
compositor->composite (dst, CAIRO_OPERATOR_ADD, tmp, clip,
0, 0,
clip_x, clip_y,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
cairo_surface_destroy (clip);
 
cleanup:
cairo_surface_destroy (tmp);
return status;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
clip_and_composite_source (const cairo_mask_compositor_t *compositor,
void *draw_closure,
draw_func_t draw_func,
draw_func_t mask_func,
cairo_pattern_t *pattern,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *mask, *src;
int src_x, src_y;
 
/* Create a surface that is mask IN clip */
mask = create_composite_mask (compositor, dst, draw_closure,
draw_func, mask_func,
extents);
if (unlikely (mask->status))
return mask->status;
 
src = compositor->pattern_to_surface (dst,
pattern,
FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (unlikely (src->status)) {
cairo_surface_destroy (mask);
return src->status;
}
 
if (dst->is_clear) {
compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
} else {
/* Compute dest' = dest OUT (mask IN clip) */
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
0, 0, 0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
/* Now compute (src IN (mask IN clip)) ADD dest' */
compositor->composite (dst, CAIRO_OPERATOR_ADD, src, mask,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
 
cairo_surface_destroy (src);
cairo_surface_destroy (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
can_reduce_alpha_op (cairo_operator_t op)
{
int iop = op;
switch (iop) {
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_ADD:
return TRUE;
default:
return FALSE;
}
}
 
static cairo_bool_t
reduce_alpha_op (cairo_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern)
{
return dst->is_clear &&
dst->content == CAIRO_CONTENT_ALPHA &&
_cairo_pattern_is_opaque_solid (pattern) &&
can_reduce_alpha_op (op);
}
 
static cairo_status_t
fixup_unbounded (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
const cairo_composite_rectangles_t *extents)
{
cairo_rectangle_int_t rects[4];
int n;
 
if (extents->bounded.width == extents->unbounded.width &&
extents->bounded.height == extents->unbounded.height)
{
return CAIRO_STATUS_SUCCESS;
}
 
n = 0;
if (extents->bounded.width == 0 || extents->bounded.height == 0) {
rects[n].x = extents->unbounded.x;
rects[n].width = extents->unbounded.width;
rects[n].y = extents->unbounded.y;
rects[n].height = extents->unbounded.height;
n++;
} else {
/* top */
if (extents->bounded.y != extents->unbounded.y) {
rects[n].x = extents->unbounded.x;
rects[n].width = extents->unbounded.width;
rects[n].y = extents->unbounded.y;
rects[n].height = extents->bounded.y - extents->unbounded.y;
n++;
}
/* left */
if (extents->bounded.x != extents->unbounded.x) {
rects[n].x = extents->unbounded.x;
rects[n].width = extents->bounded.x - extents->unbounded.x;
rects[n].y = extents->bounded.y;
rects[n].height = extents->bounded.height;
n++;
}
/* right */
if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
rects[n].x = extents->bounded.x + extents->bounded.width;
rects[n].width = extents->unbounded.x + extents->unbounded.width - rects[n].x;
rects[n].y = extents->bounded.y;
rects[n].height = extents->bounded.height;
n++;
}
/* bottom */
if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
rects[n].x = extents->unbounded.x;
rects[n].width = extents->unbounded.width;
rects[n].y = extents->bounded.y + extents->bounded.height;
rects[n].height = extents->unbounded.y + extents->unbounded.height - rects[n].y;
n++;
}
}
 
return compositor->fill_rectangles (dst, CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
rects, n);
}
 
static cairo_status_t
fixup_unbounded_with_mask (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *mask;
int mask_x, mask_y;
 
mask = get_clip_source (compositor,
extents->clip, dst, &extents->unbounded,
&mask_x, &mask_y);
if (unlikely (mask->status))
return mask->status;
 
/* top */
if (extents->bounded.y != extents->unbounded.y) {
int x = extents->unbounded.x;
int y = extents->unbounded.y;
int width = extents->unbounded.width;
int height = extents->bounded.y - y;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
x + mask_x, y + mask_y,
0, 0,
x, y,
width, height);
}
 
/* left */
if (extents->bounded.x != extents->unbounded.x) {
int x = extents->unbounded.x;
int y = extents->bounded.y;
int width = extents->bounded.x - x;
int height = extents->bounded.height;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
x + mask_x, y + mask_y,
0, 0,
x, y,
width, height);
}
 
/* right */
if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
int x = extents->bounded.x + extents->bounded.width;
int y = extents->bounded.y;
int width = extents->unbounded.x + extents->unbounded.width - x;
int height = extents->bounded.height;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
x + mask_x, y + mask_y,
0, 0,
x, y,
width, height);
}
 
/* bottom */
if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
int x = extents->unbounded.x;
int y = extents->bounded.y + extents->bounded.height;
int width = extents->unbounded.width;
int height = extents->unbounded.y + extents->unbounded.height - y;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
x + mask_x, y + mask_y,
0, 0,
x, y,
width, height);
}
 
cairo_surface_destroy (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
fixup_unbounded_boxes (const cairo_mask_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_boxes_t clear;
cairo_region_t *clip_region;
cairo_box_t box;
cairo_status_t status;
struct _cairo_boxes_chunk *chunk;
int i;
 
assert (boxes->is_pixel_aligned);
 
clip_region = NULL;
if (_cairo_clip_is_region (extents->clip) &&
(clip_region = _cairo_clip_get_region (extents->clip)) &&
cairo_region_contains_rectangle (clip_region,
&extents->bounded) == CAIRO_REGION_OVERLAP_IN)
clip_region = NULL;
 
 
if (boxes->num_boxes <= 1 && clip_region == NULL)
return fixup_unbounded (compositor, dst, extents);
 
_cairo_boxes_init (&clear);
 
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
 
if (clip_region == NULL) {
cairo_boxes_t tmp;
 
_cairo_boxes_init (&tmp);
 
status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
tmp.chunks.next = &boxes->chunks;
tmp.num_boxes += boxes->num_boxes;
 
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
CAIRO_FILL_RULE_WINDING,
&clear);
 
tmp.chunks.next = NULL;
} else {
pixman_box32_t *pbox;
 
pbox = pixman_region32_rectangles (&clip_region->rgn, &i);
_cairo_boxes_limit (&clear, (cairo_box_t *) pbox, i);
 
status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
status = _cairo_boxes_add (&clear,
CAIRO_ANTIALIAS_DEFAULT,
&chunk->base[i]);
if (unlikely (status)) {
_cairo_boxes_fini (&clear);
return status;
}
}
}
 
status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
CAIRO_FILL_RULE_WINDING,
&clear);
}
 
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = compositor->fill_boxes (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
}
 
_cairo_boxes_fini (&clear);
 
return status;
}
 
enum {
NEED_CLIP_REGION = 0x1,
NEED_CLIP_SURFACE = 0x2,
FORCE_CLIP_REGION = 0x4,
};
 
static cairo_bool_t
need_bounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = NEED_CLIP_REGION;
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
return flags;
}
 
static cairo_bool_t
need_unbounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = 0;
if (! extents->is_bounded) {
flags |= NEED_CLIP_REGION;
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
}
if (extents->clip->path != NULL)
flags |= NEED_CLIP_SURFACE;
return flags;
}
 
static cairo_status_t
clip_and_composite (const cairo_mask_compositor_t *compositor,
draw_func_t draw_func,
draw_func_t mask_func,
void *draw_closure,
cairo_composite_rectangles_t*extents,
unsigned int need_clip)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
cairo_pattern_t *src = &extents->source_pattern.base;
cairo_region_t *clip_region = NULL;
cairo_status_t status;
 
compositor->acquire (dst);
 
if (need_clip & NEED_CLIP_REGION) {
clip_region = _cairo_clip_get_region (extents->clip);
if ((need_clip & FORCE_CLIP_REGION) == 0 &&
_cairo_composite_rectangles_can_reduce_clip (extents,
extents->clip))
clip_region = NULL;
if (clip_region != NULL) {
status = compositor->set_clip_region (dst, clip_region);
if (unlikely (status)) {
compositor->release (dst);
return status;
}
}
}
 
if (reduce_alpha_op (dst, op, &extents->source_pattern.base)) {
op = CAIRO_OPERATOR_ADD;
src = NULL;
}
 
if (op == CAIRO_OPERATOR_SOURCE) {
status = clip_and_composite_source (compositor,
draw_closure, draw_func, mask_func,
src, extents);
} else {
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
src = NULL;
}
 
if (need_clip & NEED_CLIP_SURFACE) {
if (extents->is_bounded) {
status = clip_and_composite_with_mask (compositor,
draw_closure,
draw_func,
mask_func,
op, src, extents);
} else {
status = clip_and_composite_combine (compositor,
draw_closure,
draw_func,
op, src, extents);
}
} else {
status = draw_func (compositor,
dst, draw_closure,
op, src, &extents->source_sample_area,
0, 0,
&extents->bounded,
extents->clip);
}
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
if (need_clip & NEED_CLIP_SURFACE)
status = fixup_unbounded_with_mask (compositor, dst, extents);
else
status = fixup_unbounded (compositor, dst, extents);
}
 
if (clip_region)
compositor->set_clip_region (dst, NULL);
 
compositor->release (dst);
 
return status;
}
 
static cairo_int_status_t
trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_box_t box;
 
_cairo_boxes_extents (boxes, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_status_t
upload_boxes (const cairo_mask_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
const cairo_pattern_t *source = &extents->source_pattern.base;
cairo_surface_t *src;
cairo_rectangle_int_t limit;
cairo_int_status_t status;
int tx, ty;
 
src = _cairo_pattern_get_source ((cairo_surface_pattern_t *)source, &limit);
if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Check that the data is entirely within the image */
if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
tx += limit.x;
ty += limit.y;
 
if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
status = compositor->draw_image_boxes (dst,
(cairo_image_surface_t *)src,
boxes, tx, ty);
else
status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
tx, ty);
 
return status;
}
 
static cairo_status_t
composite_boxes (const cairo_mask_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
const cairo_pattern_t *source = &extents->source_pattern.base;
cairo_bool_t need_clip_mask = extents->clip->path != NULL;
cairo_status_t status;
 
if (need_clip_mask &&
(! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = compositor->acquire (dst);
if (unlikely (status))
return status;
 
if (! need_clip_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
const cairo_color_t *color;
 
color = &((cairo_solid_pattern_t *) source)->color;
status = compositor->fill_boxes (dst, op, color, boxes);
} else {
cairo_surface_t *src, *mask = NULL;
int src_x, src_y;
int mask_x = 0, mask_y = 0;
 
if (need_clip_mask) {
mask = get_clip_source (compositor,
extents->clip, dst, &extents->bounded,
&mask_x, &mask_y);
if (unlikely (mask->status))
return mask->status;
 
if (op == CAIRO_OPERATOR_CLEAR) {
source = NULL;
op = CAIRO_OPERATOR_DEST_OUT;
}
}
 
if (source || mask == NULL) {
src = compositor->pattern_to_surface (dst, source, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
} else {
src = mask;
src_x = mask_x;
src_y = mask_y;
mask = NULL;
}
 
status = compositor->composite_boxes (dst, op, src, mask,
src_x, src_y,
mask_x, mask_y,
0, 0,
boxes, &extents->bounded);
 
cairo_surface_destroy (src);
cairo_surface_destroy (mask);
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
status = fixup_unbounded_boxes (compositor, extents, boxes);
 
compositor->release (dst);
 
return status;
}
 
static cairo_status_t
clip_and_composite_boxes (const cairo_mask_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_int_status_t status;
 
if (boxes->num_boxes == 0) {
if (extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
return fixup_unbounded_boxes (compositor, extents, boxes);
}
 
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = trim_extents_to_boxes (extents, boxes);
if (unlikely (status))
return status;
 
if (extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
extents->clip->path == NULL &&
(extents->op == CAIRO_OPERATOR_SOURCE ||
(dst->is_clear && (extents->op == CAIRO_OPERATOR_OVER ||
extents->op == CAIRO_OPERATOR_ADD))))
{
status = upload_boxes (compositor, extents, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
return composite_boxes (compositor, extents, boxes);
}
 
/* high-level compositor interface */
 
static cairo_int_status_t
_cairo_mask_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
cairo_boxes_t boxes;
cairo_int_status_t status;
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
_cairo_clip_steal_boxes (extents->clip, &boxes);
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_clip_unsteal_boxes (extents->clip, &boxes);
 
return status;
}
 
struct composite_opacity_info {
const cairo_mask_compositor_t *compositor;
uint8_t op;
cairo_surface_t *dst;
cairo_surface_t *src;
int src_x, src_y;
double opacity;
};
 
static void composite_opacity(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_opacity_info *info = closure;
const cairo_mask_compositor_t *compositor = info->compositor;
cairo_surface_t *mask;
int mask_x, mask_y;
cairo_color_t color;
cairo_solid_pattern_t solid;
 
_cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
_cairo_pattern_init_solid (&solid, &color);
mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
&_cairo_unbounded_rectangle,
&_cairo_unbounded_rectangle,
&mask_x, &mask_y);
if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
if (info->src) {
compositor->composite (info->dst, info->op, info->src, mask,
x + info->src_x, y + info->src_y,
mask_x, mask_y,
x, y,
w, h);
} else {
compositor->composite (info->dst, info->op, mask, NULL,
mask_x, mask_y,
0, 0,
x, y,
w, h);
}
}
 
cairo_surface_destroy (mask);
}
 
static cairo_int_status_t
composite_opacity_boxes (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
const cairo_rectangle_int_t *src_sample,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
const cairo_solid_pattern_t *mask_pattern = closure;
struct composite_opacity_info info;
int i;
 
assert (clip);
 
info.compositor = compositor;
info.op = op;
info.dst = dst;
 
if (src_pattern != NULL) {
info.src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
extents, src_sample,
&info.src_x, &info.src_y);
if (unlikely (info.src->status))
return info.src->status;
} else
info.src = NULL;
 
info.opacity = mask_pattern->color.alpha / (double) 0xffff;
 
/* XXX for lots of boxes create a clip region for the fully opaque areas */
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_opacity, &info,
&clip->boxes[i], dst_x, dst_y);
cairo_surface_destroy (info.src);
 
return CAIRO_STATUS_SUCCESS;
}
 
struct composite_box_info {
const cairo_mask_compositor_t *compositor;
cairo_surface_t *dst;
cairo_surface_t *src;
int src_x, src_y;
uint8_t op;
};
 
static void composite_box(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_box_info *info = closure;
const cairo_mask_compositor_t *compositor = info->compositor;
 
if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
cairo_surface_t *mask;
cairo_color_t color;
cairo_solid_pattern_t solid;
int mask_x, mask_y;
 
_cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
_cairo_pattern_init_solid (&solid, &color);
 
mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
&_cairo_unbounded_rectangle,
&_cairo_unbounded_rectangle,
&mask_x, &mask_y);
 
if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
compositor->composite (info->dst, info->op, info->src, mask,
x + info->src_x, y + info->src_y,
mask_x, mask_y,
x, y,
w, h);
}
 
cairo_surface_destroy (mask);
} else {
compositor->composite (info->dst, info->op, info->src, NULL,
x + info->src_x, y + info->src_y,
0, 0,
x, y,
w, h);
}
}
 
static cairo_int_status_t
composite_mask_clip_boxes (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
const cairo_rectangle_int_t *src_sample,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
cairo_composite_rectangles_t *composite = closure;
struct composite_box_info info;
int i;
 
assert (src_pattern == NULL);
assert (op == CAIRO_OPERATOR_SOURCE);
 
info.compositor = compositor;
info.op = CAIRO_OPERATOR_SOURCE;
info.dst = dst;
info.src = compositor->pattern_to_surface (dst,
&composite->mask_pattern.base,
FALSE, extents,
&composite->mask_sample_area,
&info.src_x, &info.src_y);
if (unlikely (info.src->status))
return info.src->status;
 
info.src_x += dst_x;
info.src_y += dst_y;
 
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
 
cairo_surface_destroy (info.src);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_mask (const cairo_mask_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
const cairo_rectangle_int_t *src_sample,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
cairo_composite_rectangles_t *composite = closure;
cairo_surface_t *src, *mask;
int src_x, src_y;
int mask_x, mask_y;
 
if (src_pattern != NULL) {
src = compositor->pattern_to_surface (dst, src_pattern, FALSE,
extents, src_sample,
&src_x, &src_y);
if (unlikely (src->status))
return src->status;
 
mask = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, TRUE,
extents, &composite->mask_sample_area,
&mask_x, &mask_y);
if (unlikely (mask->status)) {
cairo_surface_destroy (src);
return mask->status;
}
 
compositor->composite (dst, op, src, mask,
extents->x + src_x, extents->y + src_y,
extents->x + mask_x, extents->y + mask_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
cairo_surface_destroy (mask);
cairo_surface_destroy (src);
} else {
src = compositor->pattern_to_surface (dst, &composite->mask_pattern.base, FALSE,
extents, &composite->mask_sample_area,
&src_x, &src_y);
if (unlikely (src->status))
return src->status;
 
compositor->composite (dst, op, src, NULL,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
cairo_surface_destroy (src);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_mask_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
extents->clip->path == NULL &&
_cairo_clip_is_region (extents->clip)) {
status = clip_and_composite (compositor,
composite_opacity_boxes,
composite_opacity_boxes,
&extents->mask_pattern.solid,
extents, need_unbounded_clip (extents));
} else {
status = clip_and_composite (compositor,
composite_mask,
extents->clip->path == NULL ? composite_mask_clip_boxes : NULL,
extents,
extents, need_bounded_clip (extents));
}
 
return status;
}
 
static cairo_int_status_t
_cairo_mask_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
mask = cairo_surface_create_similar_image (extents->surface,
CAIRO_FORMAT_A8,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
status = _cairo_surface_offset_stroke (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, style, ctm, ctm_inverse,
tolerance, antialias,
extents->clip);
if (unlikely (status)) {
cairo_surface_destroy (mask);
return status;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_surface_destroy (mask);
 
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
extents->clip);
_cairo_pattern_fini (&pattern.base);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_mask_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
mask = cairo_surface_create_similar_image (extents->surface,
CAIRO_FORMAT_A8,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
status = _cairo_surface_offset_fill (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, fill_rule, tolerance, antialias,
extents->clip);
if (unlikely (status)) {
cairo_surface_destroy (mask);
return status;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_surface_destroy (mask);
 
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
extents->clip);
_cairo_pattern_fini (&pattern.base);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
const cairo_mask_compositor_t *compositor = (cairo_mask_compositor_t*)_compositor;
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status;
 
status = compositor->check_composite (extents);
if (unlikely (status))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
mask = cairo_surface_create_similar_image (extents->surface,
CAIRO_FORMAT_A8,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
status = _cairo_surface_offset_glyphs (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
scaled_font, glyphs, num_glyphs,
extents->clip);
if (unlikely (status)) {
cairo_surface_destroy (mask);
return status;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_surface_destroy (mask);
 
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
extents->clip);
_cairo_pattern_fini (&pattern.base);
 
return status;
}
 
void
_cairo_mask_compositor_init (cairo_mask_compositor_t *compositor,
const cairo_compositor_t *delegate)
{
compositor->base.delegate = delegate;
 
compositor->base.paint = _cairo_mask_compositor_paint;
compositor->base.mask = _cairo_mask_compositor_mask;
compositor->base.fill = _cairo_mask_compositor_fill;
compositor->base.stroke = _cairo_mask_compositor_stroke;
compositor->base.glyphs = _cairo_mask_compositor_glyphs;
}
/programs/develop/libraries/cairo/src/cairo-matrix.c
36,7 → 36,10
 
#include "cairoint.h"
#include "cairo-error-private.h"
#include <float.h>
 
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 
#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
#define ISFINITE(x) isfinite (x)
#else
64,7 → 67,7
* #cairo_matrix_t, defines the transformation from user-space
* coordinates to device-space coordinates. See cairo_get_matrix() and
* cairo_set_matrix().
*/
**/
 
static void
_cairo_matrix_scalar_multiply (cairo_matrix_t *matrix, double scalar);
77,6 → 80,8
* @matrix: a #cairo_matrix_t
*
* Modifies @matrix to be an identity transformation.
*
* Since: 1.0
**/
void
cairo_matrix_init_identity (cairo_matrix_t *matrix)
105,6 → 110,8
* x_new = xx * x + xy * y + x0;
* y_new = yx * x + yy * y + y0;
* </programlisting>
*
* Since: 1.0
**/
void
cairo_matrix_init (cairo_matrix_t *matrix,
165,6 → 172,8
*
* Initializes @matrix to a transformation that translates by @tx and
* @ty in the X and Y dimensions, respectively.
*
* Since: 1.0
**/
void
cairo_matrix_init_translate (cairo_matrix_t *matrix,
187,6 → 196,8
* @matrix. The effect of the new transformation is to first translate
* the coordinates by @tx and @ty, then apply the original transformation
* to the coordinates.
*
* Since: 1.0
**/
void
cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
207,6 → 218,8
*
* Initializes @matrix to a transformation that scales by @sx and @sy
* in the X and Y dimensions, respectively.
*
* Since: 1.0
**/
void
cairo_matrix_init_scale (cairo_matrix_t *matrix,
228,6 → 241,8
* Applies scaling by @sx, @sy to the transformation in @matrix. The
* effect of the new transformation is to first scale the coordinates
* by @sx and @sy, then apply the original transformation to the coordinates.
*
* Since: 1.0
**/
void
cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
250,6 → 265,8
* direction.
*
* Initialized @matrix to a transformation that rotates by @radians.
*
* Since: 1.0
**/
void
cairo_matrix_init_rotate (cairo_matrix_t *matrix,
281,6 → 298,8
* @matrix. The effect of the new transformation is to first rotate the
* coordinates by @radians, then apply the original transformation
* to the coordinates.
*
* Since: 1.0
**/
void
cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
305,6 → 324,8
* coordinates.
*
* It is allowable for @result to be identical to either @a or @b.
*
* Since: 1.0
**/
/*
* XXX: The ordering of the arguments to this function corresponds
330,6 → 351,21
}
slim_hidden_def(cairo_matrix_multiply);
 
void
_cairo_matrix_multiply (cairo_matrix_t *r,
const cairo_matrix_t *a,
const cairo_matrix_t *b)
{
r->xx = a->xx * b->xx + a->yx * b->xy;
r->yx = a->xx * b->yx + a->yx * b->yy;
 
r->xy = a->xy * b->xx + a->yy * b->xy;
r->yy = a->xy * b->yx + a->yy * b->yy;
 
r->x0 = a->x0 * b->xx + a->y0 * b->xy + b->x0;
r->y0 = a->x0 * b->yx + a->y0 * b->yy + b->y0;
}
 
/**
* cairo_matrix_transform_distance:
* @matrix: a #cairo_matrix_t
350,6 → 386,8
* always transforms to the same vector. If (@x1,@y1) transforms
* to (@x2,@y2) then (@x1+@dx1,@y1+@dy1) will transform to
* (@x1+@dx2,@y1+@dy2) for all values of @x1 and @x2.
*
* Since: 1.0
**/
void
cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy)
371,6 → 409,8
* @y: Y position. An in/out parameter
*
* Transforms the point (@x, @y) by @matrix.
*
* Since: 1.0
**/
void
cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y)
545,6 → 585,8
* Returns: If @matrix has an inverse, modifies @matrix to
* be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise,
* returns %CAIRO_STATUS_INVALID_MATRIX.
*
* Since: 1.0
**/
cairo_status_t
cairo_matrix_invert (cairo_matrix_t *matrix)
683,21 → 725,6
}
 
cairo_bool_t
_cairo_matrix_is_identity (const cairo_matrix_t *matrix)
{
return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
matrix->xy == 0.0 && matrix->yy == 1.0 &&
matrix->x0 == 0.0 && matrix->y0 == 0.0);
}
 
cairo_bool_t
_cairo_matrix_is_translation (const cairo_matrix_t *matrix)
{
return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
matrix->xy == 0.0 && matrix->yy == 1.0);
}
 
cairo_bool_t
_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
int *itx, int *ity)
{
886,6 → 913,9
{
double a, b, c, d, f, g, h, i, j;
 
if (_cairo_matrix_has_unity_scale (matrix))
return radius;
 
_cairo_matrix_get_affine (matrix,
&a, &b,
&c, &d,
906,12 → 936,6
*/
}
 
void
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
pixman_transform_t *pixman_transform,
double xc,
double yc)
{
static const pixman_transform_t pixman_identity_transform = {{
{1 << 16, 0, 0},
{ 0, 1 << 16, 0},
918,9 → 942,12
{ 0, 0, 1 << 16}
}};
 
if (_cairo_matrix_is_identity (matrix)) {
*pixman_transform = pixman_identity_transform;
} else {
static cairo_status_t
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
pixman_transform_t *pixman_transform,
double xc,
double yc)
{
cairo_matrix_t inv;
unsigned max_iterations;
 
948,15 → 975,24
*/
 
if (_cairo_matrix_has_unity_scale (matrix))
return;
return CAIRO_STATUS_SUCCESS;
 
if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT ||
fabs (matrix->xy) > PIXMAN_MAX_INT ||
fabs (matrix->x0) > PIXMAN_MAX_INT ||
fabs (matrix->yx) > PIXMAN_MAX_INT ||
fabs (matrix->yy) > PIXMAN_MAX_INT ||
fabs (matrix->y0) > PIXMAN_MAX_INT))
{
return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
}
 
/* Note: If we can't invert the transformation, skip the adjustment. */
inv = *matrix;
if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS)
return;
return CAIRO_STATUS_SUCCESS;
 
/* find the pattern space coordinate that maps to (xc, yc) */
xc += .5; yc += .5; /* offset for the pixel centre */
max_iterations = 5;
do {
double x,y;
967,8 → 1003,9
vector.vector[1] = _cairo_fixed_16_16_from_double (yc);
vector.vector[2] = 1 << 16;
 
/* If we can't transform the reference point, skip the adjustment. */
if (! pixman_transform_point_3d (pixman_transform, &vector))
return;
return CAIRO_STATUS_SUCCESS;
 
x = pixman_fixed_to_double (vector.vector[0]);
y = pixman_fixed_to_double (vector.vector[1]);
986,7 → 1023,181
pixman_transform->matrix[1][2] -= dy;
 
if (dx == 0 && dy == 0)
break;
return CAIRO_STATUS_SUCCESS;
} while (--max_iterations);
 
/* We didn't find an exact match between cairo and pixman, but
* the matrix should be mostly correct */
return CAIRO_STATUS_SUCCESS;
}
 
static inline double
_pixman_nearest_sample (double d)
{
return ceil (d - .5);
}
 
/**
* _cairo_matrix_is_pixman_translation:
* @matrix: a matrix
* @filter: the filter to be used on the pattern transformed by @matrix
* @x_offset: the translation in the X direction
* @y_offset: the translation in the Y direction
*
* Checks if @matrix translated by (x_offset, y_offset) can be
* represented using just an offset (within the range pixman can
* accept) and an identity matrix.
*
* Passing a non-zero value in x_offset/y_offset has the same effect
* as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
* setting x_offset and y_offset to 0.
*
* Upon return x_offset and y_offset contain the translation vector if
* the return value is %TRUE. If the return value is %FALSE, they will
* not be modified.
*
* Return value: %TRUE if @matrix can be represented as a pixman
* translation, %FALSE otherwise.
**/
cairo_bool_t
_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix,
cairo_filter_t filter,
int *x_offset,
int *y_offset)
{
double tx, ty;
 
if (!_cairo_matrix_is_translation (matrix))
return FALSE;
 
if (matrix->x0 == 0. && matrix->y0 == 0.)
return TRUE;
 
tx = matrix->x0 + *x_offset;
ty = matrix->y0 + *y_offset;
 
if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) {
tx = _pixman_nearest_sample (tx);
ty = _pixman_nearest_sample (ty);
} else if (tx != floor (tx) || ty != floor (ty)) {
return FALSE;
}
 
if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT)
return FALSE;
 
*x_offset = _cairo_lround (tx);
*y_offset = _cairo_lround (ty);
return TRUE;
}
 
/**
* _cairo_matrix_to_pixman_matrix_offset:
* @matrix: a matrix
* @filter: the filter to be used on the pattern transformed by @matrix
* @xc: the X coordinate of the point to fix in pattern space
* @yc: the Y coordinate of the point to fix in pattern space
* @out_transform: the transformation which best approximates @matrix
* @x_offset: the translation in the X direction
* @y_offset: the translation in the Y direction
*
* This function tries to represent @matrix translated by (x_offset,
* y_offset) as a %pixman_transform_t and an translation.
*
* Passing a non-zero value in x_offset/y_offset has the same effect
* as applying cairo_matrix_translate(matrix, x_offset, y_offset) and
* setting x_offset and y_offset to 0.
*
* If it is possible to represent the matrix with an identity
* %pixman_transform_t and a translation within the valid range for
* pixman, this function will set @out_transform to be the identity,
* @x_offset and @y_offset to be the translation vector and will
* return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to
* evenly divide the translational component of @matrix between
* @out_transform and (@x_offset, @y_offset).
*
* Upon return x_offset and y_offset contain the translation vector.
*
* Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform
* is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not
* possible to represent @matrix as a pixman_transform_t without
* overflows, %CAIRO_STATUS_SUCCESS otherwise.
**/
cairo_status_t
_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix,
cairo_filter_t filter,
double xc,
double yc,
pixman_transform_t *out_transform,
int *x_offset,
int *y_offset)
{
cairo_bool_t is_pixman_translation;
 
is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix,
filter,
x_offset,
y_offset);
 
if (is_pixman_translation) {
*out_transform = pixman_identity_transform;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
} else {
cairo_matrix_t m;
 
m = *matrix;
cairo_matrix_translate (&m, *x_offset, *y_offset);
if (m.x0 != 0.0 || m.y0 != 0.0) {
double tx, ty, norm;
int i, j;
 
/* pixman also limits the [xy]_offset to 16 bits so evenly
* spread the bits between the two.
*
* To do this, find the solutions of:
* |x| = |x*m.xx + y*m.xy + m.x0|
* |y| = |x*m.yx + y*m.yy + m.y0|
*
* and select the one whose maximum norm is smallest.
*/
tx = m.x0;
ty = m.y0;
norm = MAX (fabs (tx), fabs (ty));
 
for (i = -1; i < 2; i+=2) {
for (j = -1; j < 2; j+=2) {
double x, y, den, new_norm;
 
den = (m.xx + i) * (m.yy + j) - m.xy * m.yx;
if (fabs (den) < DBL_EPSILON)
continue;
 
x = m.y0 * m.xy - m.x0 * (m.yy + j);
y = m.x0 * m.yx - m.y0 * (m.xx + i);
 
den = 1 / den;
x *= den;
y *= den;
 
new_norm = MAX (fabs (x), fabs (y));
if (norm > new_norm) {
norm = new_norm;
tx = x;
ty = y;
}
}
}
 
tx = floor (tx);
ty = floor (ty);
*x_offset = -tx;
*y_offset = -ty;
cairo_matrix_translate (&m, tx, ty);
} else {
*x_offset = 0;
*y_offset = 0;
}
 
return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc);
}
}
/programs/develop/libraries/cairo/src/cairo-mempool-private.h
0,0 → 1,85
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 Chris Wilson
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributors(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_MEMPOOL_PRIVATE_H
#define CAIRO_MEMPOOL_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
 
#include <stddef.h> /* for size_t */
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_mempool cairo_mempool_t;
 
struct _cairo_mempool {
char *base;
struct _cairo_memblock {
int bits;
cairo_list_t link;
} *blocks;
cairo_list_t free[32];
unsigned char *map;
 
unsigned int num_blocks;
int min_bits; /* Minimum block size is 1 << min_bits */
int num_sizes;
int max_free_bits;
 
size_t free_bytes;
size_t max_bytes;
};
 
cairo_private cairo_status_t
_cairo_mempool_init (cairo_mempool_t *pool,
void *base,
size_t bytes,
int min_bits,
int num_sizes);
 
cairo_private void *
_cairo_mempool_alloc (cairo_mempool_t *pi, size_t bytes);
 
cairo_private void
_cairo_mempool_free (cairo_mempool_t *pi, void *storage);
 
cairo_private void
_cairo_mempool_fini (cairo_mempool_t *pool);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_MEMPOOL_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-mempool.c
0,0 → 1,369
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 Chris Wilson
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipoolent 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 Red Hat, Inc.
*
* Contributors(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-mempool-private.h"
#include "cairo-list-inline.h"
 
/* a simple buddy allocator for memory pools
* XXX fragmentation? use Doug Lea's malloc?
*/
 
#define BITTEST(p, n) ((p)->map[(n) >> 3] & (128 >> ((n) & 7)))
#define BITSET(p, n) ((p)->map[(n) >> 3] |= (128 >> ((n) & 7)))
#define BITCLEAR(p, n) ((p)->map[(n) >> 3] &= ~(128 >> ((n) & 7)))
 
static void
clear_bits (cairo_mempool_t *pool, size_t first, size_t last)
{
size_t i, n = last;
size_t first_full = (first + 7) & ~7;
size_t past_full = last & ~7;
size_t bytes;
 
if (n > first_full)
n = first_full;
for (i = first; i < n; i++)
BITCLEAR (pool, i);
 
if (past_full > first_full) {
bytes = past_full - first_full;
bytes = bytes >> 3;
memset (pool->map + (first_full >> 3), 0, bytes);
}
 
if (past_full < n)
past_full = n;
for (i = past_full; i < last; i++)
BITCLEAR (pool, i);
}
 
static void
free_bits (cairo_mempool_t *pool, size_t start, int bits, cairo_bool_t clear)
{
struct _cairo_memblock *block;
 
if (clear)
clear_bits (pool, start, start + (1 << bits));
 
block = pool->blocks + start;
block->bits = bits;
 
cairo_list_add (&block->link, &pool->free[bits]);
 
pool->free_bytes += 1 << (bits + pool->min_bits);
if (bits > pool->max_free_bits)
pool->max_free_bits = bits;
}
 
/* Add a chunk to the free list */
static void
free_blocks (cairo_mempool_t *pool,
size_t first,
size_t last,
cairo_bool_t clear)
{
size_t i, len;
int bits = 0;
 
for (i = first, len = 1; i < last; i += len) {
/* To avoid cost quadratic in the number of different
* blocks produced from this chunk of store, we have to
* use the size of the previous block produced from this
* chunk as the starting point to work out the size of the
* next block we can produce. If you look at the binary
* representation of the starting points of the blocks
* produced, you can see that you first of all increase the
* size of the blocks produced up to some maximum as the
* address dealt with gets offsets added on which zap out
* low order bits, then decrease as the low order bits of the
* final block produced get added in. E.g. as you go from
* 001 to 0111 you generate blocks
* of size 001 at 001 taking you to 010
* of size 010 at 010 taking you to 100
* of size 010 at 100 taking you to 110
* of size 001 at 110 taking you to 111
* So the maximum total cost of the loops below this comment
* is one trip from the lowest blocksize to the highest and
* back again.
*/
while (bits < pool->num_sizes - 1) {
size_t next_bits = bits + 1;
size_t next_len = len << 1;
 
if (i + next_bits > last) {
/* off end of chunk to be freed */
break;
}
 
if (i & (next_len - 1)) /* block would not be on boundary */
break;
 
bits = next_bits;
len = next_len;
}
 
do {
if (i + len <= last && /* off end of chunk to be freed */
(i & (len - 1)) == 0) /* block would not be on boundary */
break;
 
bits--; len >>=1;
} while (len);
 
if (len == 0)
break;
 
free_bits (pool, i, bits, clear);
}
}
 
static struct _cairo_memblock *
get_buddy (cairo_mempool_t *pool, size_t offset, int bits)
{
struct _cairo_memblock *block;
 
if (offset + (1 << bits) >= pool->num_blocks)
return NULL; /* invalid */
 
if (BITTEST (pool, offset + (1 << bits) - 1))
return NULL; /* buddy is allocated */
 
block = pool->blocks + offset;
if (block->bits != bits)
return NULL; /* buddy is partially allocated */
 
return block;
}
 
static void
merge_buddies (cairo_mempool_t *pool,
struct _cairo_memblock *block,
int max_bits)
{
size_t block_offset = block - pool->blocks;
int bits = block->bits;
 
while (bits < max_bits - 1) {
/* while you can, merge two blocks and get a legal block size */
size_t buddy_offset = block_offset ^ (1 << bits);
 
block = get_buddy (pool, buddy_offset, bits);
if (block == NULL)
break;
 
cairo_list_del (&block->link);
 
/* Merged block starts at buddy */
if (buddy_offset < block_offset)
block_offset = buddy_offset;
 
bits++;
}
 
block = pool->blocks + block_offset;
block->bits = bits;
cairo_list_add (&block->link, &pool->free[bits]);
 
if (bits > pool->max_free_bits)
pool->max_free_bits = bits;
}
 
/* attempt to merge all available buddies up to a particular size */
static int
merge_bits (cairo_mempool_t *pool, int max_bits)
{
struct _cairo_memblock *block, *buddy, *next;
int bits;
 
for (bits = 0; bits < max_bits - 1; bits++) {
cairo_list_foreach_entry_safe (block, next,
struct _cairo_memblock,
&pool->free[bits],
link)
{
size_t buddy_offset = (block - pool->blocks) ^ (1 << bits);
 
buddy = get_buddy (pool, buddy_offset, bits);
if (buddy == NULL)
continue;
 
if (buddy == next) {
next = cairo_container_of (buddy->link.next,
struct _cairo_memblock,
link);
}
 
cairo_list_del (&block->link);
merge_buddies (pool, block, max_bits);
}
}
 
return pool->max_free_bits;
}
 
/* find store for 1 << bits blocks */
static void *
buddy_malloc (cairo_mempool_t *pool, int bits)
{
size_t past, offset;
struct _cairo_memblock *block;
int b;
 
if (bits > pool->max_free_bits && bits > merge_bits (pool, bits))
return NULL;
 
/* Find a list with blocks big enough on it */
block = NULL;
for (b = bits; b <= pool->max_free_bits; b++) {
if (! cairo_list_is_empty (&pool->free[b])) {
block = cairo_list_first_entry (&pool->free[b],
struct _cairo_memblock,
link);
break;
}
}
assert (block != NULL);
 
cairo_list_del (&block->link);
 
while (cairo_list_is_empty (&pool->free[pool->max_free_bits])) {
if (--pool->max_free_bits == -1)
break;
}
 
/* Mark end of allocated area */
offset = block - pool->blocks;
past = offset + (1 << bits);
BITSET (pool, past - 1);
block->bits = bits;
 
/* If we used a larger free block than we needed, free the rest */
pool->free_bytes -= 1 << (b + pool->min_bits);
free_blocks (pool, past, offset + (1 << b), 0);
 
return pool->base + ((block - pool->blocks) << pool->min_bits);
}
 
cairo_status_t
_cairo_mempool_init (cairo_mempool_t *pool,
void *base, size_t bytes,
int min_bits, int num_sizes)
{
unsigned long tmp;
int num_blocks;
int i;
 
/* Align the start to an integral chunk */
tmp = ((unsigned long) base) & ((1 << min_bits) - 1);
if (tmp) {
tmp = (1 << min_bits) - tmp;
base = (char *)base + tmp;
bytes -= tmp;
}
 
assert ((((unsigned long) base) & ((1 << min_bits) - 1)) == 0);
assert (num_sizes < ARRAY_LENGTH (pool->free));
 
pool->base = base;
pool->free_bytes = 0;
pool->max_bytes = bytes;
pool->max_free_bits = -1;
 
num_blocks = bytes >> min_bits;
pool->blocks = calloc (num_blocks, sizeof (struct _cairo_memblock));
if (pool->blocks == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
pool->num_blocks = num_blocks;
pool->min_bits = min_bits;
pool->num_sizes = num_sizes;
 
for (i = 0; i < ARRAY_LENGTH (pool->free); i++)
cairo_list_init (&pool->free[i]);
 
pool->map = malloc ((num_blocks + 7) >> 3);
if (pool->map == NULL) {
free (pool->blocks);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
memset (pool->map, -1, (num_blocks + 7) >> 3);
clear_bits (pool, 0, num_blocks);
 
/* Now add all blocks to the free list */
free_blocks (pool, 0, num_blocks, 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
void *
_cairo_mempool_alloc (cairo_mempool_t *pool, size_t bytes)
{
size_t size;
int bits;
 
size = 1 << pool->min_bits;
for (bits = 0; size < bytes; bits++)
size <<= 1;
if (bits >= pool->num_sizes)
return NULL;
 
return buddy_malloc (pool, bits);
}
 
void
_cairo_mempool_free (cairo_mempool_t *pool, void *storage)
{
size_t block_offset;
struct _cairo_memblock *block;
 
block_offset = ((char *)storage - pool->base) >> pool->min_bits;
block = pool->blocks + block_offset;
 
BITCLEAR (pool, block_offset + ((1 << block->bits) - 1));
pool->free_bytes += 1 << (block->bits + pool->min_bits);
 
merge_buddies (pool, block, pool->num_sizes);
}
 
void
_cairo_mempool_fini (cairo_mempool_t *pool)
{
free (pool->map);
free (pool->blocks);
}
/programs/develop/libraries/cairo/src/cairo-mesh-pattern-rasterizer.c
0,0 → 1,940
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright 2009 Andrea Canciani
*
* 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 Andrea Canciani.
*
* Contributor(s):
* Andrea Canciani <ranma42@gmail.com>
*/
 
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-pattern-private.h"
 
/*
* Rasterizer for mesh patterns.
*
* This implementation is based on techniques derived from several
* papers (available from ACM):
*
* - Lien, Shantz and Pratt "Adaptive Forward Differencing for
* Rendering Curves and Surfaces" (discussion of the AFD technique,
* bound of 1/sqrt(2) on step length without proof)
*
* - Popescu and Rosen, "Forward rasterization" (description of
* forward rasterization, proof of the previous bound)
*
* - Klassen, "Integer Forward Differencing of Cubic Polynomials:
* Analysis and Algorithms"
*
* - Klassen, "Exact Integer Hybrid Subdivision and Forward
* Differencing of Cubics" (improving the bound on the minimum
* number of steps)
*
* - Chang, Shantz and Rocchetti, "Rendering Cubic Curves and Surfaces
* with Integer Adaptive Forward Differencing" (analysis of forward
* differencing applied to Bezier patches)
*
* Notes:
* - Poor performance expected in degenerate cases
*
* - Patches mostly outside the drawing area are drawn completely (and
* clipped), wasting time
*
* - Both previous problems are greatly reduced by splitting until a
* reasonably small size and clipping the new tiles: execution time
* is quadratic in the convex-hull diameter instead than linear to
* the painted area. Splitting the tiles doesn't change the painted
* area but (usually) reduces the bounding box area (bbox area can
* remain the same after splitting, but cannot grow)
*
* - The initial implementation used adaptive forward differencing,
* but simple forward differencing scored better in benchmarks
*
* Idea:
*
* We do a sampling over the cubic patch with step du and dv (in the
* two parameters) that guarantees that any point of our sampling will
* be at most at 1/sqrt(2) from its adjacent points. In formulae
* (assuming B is the patch):
*
* |B(u,v) - B(u+du,v)| < 1/sqrt(2)
* |B(u,v) - B(u,v+dv)| < 1/sqrt(2)
*
* This means that every pixel covered by the patch will contain at
* least one of the samples, thus forward rasterization can be
* performed. Sketch of proof (from Popescu and Rosen):
*
* Let's take the P pixel we're interested into. If we assume it to be
* square, its boundaries define 9 regions on the plane:
*
* 1|2|3
* -+-+-
* 8|P|4
* -+-+-
* 7|6|5
*
* Let's check that the pixel P will contain at least one point
* assuming that it is covered by the patch.
*
* Since the pixel is covered by the patch, its center will belong to
* (at least) one of the quads:
*
* {(B(u,v), B(u+du,v), B(u,v+dv), B(u+du,v+dv)) for u,v in [0,1]}
*
* If P doesn't contain any of the corners of the quad:
*
* - if one of the corners is in 1,3,5 or 7, other two of them have to
* be in 2,4,6 or 8, thus if the last corner is not in P, the length
* of one of the edges will be > 1/sqrt(2)
*
* - if none of the corners is in 1,3,5 or 7, all of them are in 2,4,6
* and/or 8. If they are all in different regions, they can't
* satisfy the distance constraint. If two of them are in the same
* region (let's say 2), no point is in 6 and again it is impossible
* to have the center of P in the quad respecting the distance
* constraint (both these assertions can be checked by continuity
* considering the length of the edges of a quad with the vertices
* on the edges of P)
*
* Each of the cases led to a contradiction, so P contains at least
* one of the corners of the quad.
*/
 
/*
* Make sure that errors are less than 1 in fixed point math if you
* change these values.
*
* The error is amplified by about steps^3/4 times.
* The rasterizer always uses a number of steps that is a power of 2.
*
* 256 is the maximum allowed number of steps (to have error < 1)
* using 8.24 for the differences.
*/
#define STEPS_MAX_V 256.0
#define STEPS_MAX_U 256.0
 
/*
* If the patch/curve is only partially visible, split it to a finer
* resolution to get higher chances to clip (part of) it.
*
* These values have not been computed, but simply obtained
* empirically (by benchmarking some patches). They should never be
* greater than STEPS_MAX_V (or STEPS_MAX_U), but they can be as small
* as 1 (depending on how much you want to spend time in splitting the
* patch/curve when trying to save some rasterization time).
*/
#define STEPS_CLIP_V 64.0
#define STEPS_CLIP_U 64.0
 
 
/* Utils */
static inline double
sqlen (cairo_point_double_t p0, cairo_point_double_t p1)
{
cairo_point_double_t delta;
 
delta.x = p0.x - p1.x;
delta.y = p0.y - p1.y;
 
return delta.x * delta.x + delta.y * delta.y;
}
 
static inline int16_t
_color_delta_to_shifted_short (int32_t from, int32_t to, int shift)
{
int32_t delta = to - from;
 
/* We need to round toward zero, because otherwise adding the
* delta 2^shift times can overflow */
if (delta >= 0)
return delta >> shift;
else
return -((-delta) >> shift);
}
 
/*
* Convert a number of steps to the equivalent shift.
*
* Input: the square of the minimum number of steps
*
* Output: the smallest integer x such that 2^x > steps
*/
static inline int
sqsteps2shift (double steps_sq)
{
int r;
frexp (MAX (1.0, steps_sq), &r);
return (r + 1) >> 1;
}
 
/*
* FD functions
*
* A Bezier curve is defined (with respect to a parameter t in
* [0,1]) from its nodes (x,y,z,w) like this:
*
* B(t) = x(1-t)^3 + 3yt(1-t)^2 + 3zt^2(1-t) + wt^3
*
* To efficiently evaluate a Bezier curve, the rasterizer uses forward
* differences. Given x, y, z, w (the 4 nodes of the Bezier curve), it
* is possible to convert them to forward differences form and walk
* over the curve using fd_init (), fd_down () and fd_fwd ().
*
* f[0] is always the value of the Bezier curve for "current" t.
*/
 
/*
* Initialize the coefficient for forward differences.
*
* Input: x,y,z,w are the 4 nodes of the Bezier curve
*
* Output: f[i] is the i-th difference of the curve
*
* f[0] is the value of the curve for t==0, i.e. f[0]==x.
*
* The initial step is 1; this means that each step increases t by 1
* (so fd_init () immediately followed by fd_fwd (f) n times makes
* f[0] be the value of the curve for t==n).
*/
static inline void
fd_init (double x, double y, double z, double w, double f[4])
{
f[0] = x;
f[1] = w - x;
f[2] = 6. * (w - 2. * z + y);
f[3] = 6. * (w - 3. * z + 3. * y - x);
}
 
/*
* Halve the step of the coefficients for forward differences.
*
* Input: f[i] is the i-th difference of the curve
*
* Output: f[i] is the i-th difference of the curve with half the
* original step
*
* f[0] is not affected, so the current t is not changed.
*
* The other coefficients are changed so that the step is half the
* original step. This means that doing fd_fwd (f) n times with the
* input f results in the same f[0] as doing fd_fwd (f) 2n times with
* the output f.
*/
static inline void
fd_down (double f[4])
{
f[3] *= 0.125;
f[2] = f[2] * 0.25 - f[3];
f[1] = (f[1] - f[2]) * 0.5;
}
 
/*
* Perform one step of forward differences along the curve.
*
* Input: f[i] is the i-th difference of the curve
*
* Output: f[i] is the i-th difference of the curve after one step
*/
static inline void
fd_fwd (double f[4])
{
f[0] += f[1];
f[1] += f[2];
f[2] += f[3];
}
 
/*
* Transform to integer forward differences.
*
* Input: d[n] is the n-th difference (in double precision)
*
* Output: i[n] is the n-th difference (in fixed point precision)
*
* i[0] is 9.23 fixed point, other differences are 4.28 fixed point.
*/
static inline void
fd_fixed (double d[4], int32_t i[4])
{
i[0] = _cairo_fixed_16_16_from_double (256 * 2 * d[0]);
i[1] = _cairo_fixed_16_16_from_double (256 * 16 * d[1]);
i[2] = _cairo_fixed_16_16_from_double (256 * 16 * d[2]);
i[3] = _cairo_fixed_16_16_from_double (256 * 16 * d[3]);
}
 
/*
* Perform one step of integer forward differences along the curve.
*
* Input: f[n] is the n-th difference
*
* Output: f[n] is the n-th difference
*
* f[0] is 9.23 fixed point, other differences are 4.28 fixed point.
*/
static inline void
fd_fixed_fwd (int32_t f[4])
{
f[0] += (f[1] >> 5) + ((f[1] >> 4) & 1);
f[1] += f[2];
f[2] += f[3];
}
 
/*
* Compute the minimum number of steps that guarantee that walking
* over a curve will leave no holes.
*
* Input: p[0..3] the nodes of the Bezier curve
*
* Returns: the square of the number of steps
*
* Idea:
*
* We want to make sure that at every step we move by less than
* 1/sqrt(2).
*
* The derivative of the cubic Bezier with nodes (p0, p1, p2, p3) is
* the quadratic Bezier with nodes (p1-p0, p2-p1, p3-p2) scaled by 3,
* so (since a Bezier curve is always bounded by its convex hull), we
* can say that:
*
* max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p1|, |p3-p2|)
*
* We can improve this by noticing that a quadratic Bezier (a,b,c) is
* bounded by the quad (a,lerp(a,b,t),lerp(b,c,t),c) for any t, so
* (substituting the previous values, using t=0.5 and simplifying):
*
* max(|B'(t)|) <= 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|)
*
* So, to guarantee a maximum step length of 1/sqrt(2) we must do:
*
* 3 max (|p1-p0|, |p2-p0|/2, |p3-p1|/2, |p3-p2|) sqrt(2) steps
*/
static inline double
bezier_steps_sq (cairo_point_double_t p[4])
{
double tmp = sqlen (p[0], p[1]);
tmp = MAX (tmp, sqlen (p[2], p[3]));
tmp = MAX (tmp, sqlen (p[0], p[2]) * .25);
tmp = MAX (tmp, sqlen (p[1], p[3]) * .25);
return 18.0 * tmp;
}
 
/*
* Split a 1D Bezier cubic using de Casteljau's algorithm.
*
* Input: x,y,z,w the nodes of the Bezier curve
*
* Output: x0,y0,z0,w0 and x1,y1,z1,w1 are respectively the nodes of
* the first half and of the second half of the curve
*
* The output control nodes have to be distinct.
*/
static inline void
split_bezier_1D (double x, double y, double z, double w,
double *x0, double *y0, double *z0, double *w0,
double *x1, double *y1, double *z1, double *w1)
{
double tmp;
 
*x0 = x;
*w1 = w;
 
tmp = 0.5 * (y + z);
*y0 = 0.5 * (x + y);
*z1 = 0.5 * (z + w);
 
*z0 = 0.5 * (*y0 + tmp);
*y1 = 0.5 * (tmp + *z1);
 
*w0 = *x1 = 0.5 * (*z0 + *y1);
}
 
/*
* Split a Bezier curve using de Casteljau's algorithm.
*
* Input: p[0..3] the nodes of the Bezier curve
*
* Output: fst_half[0..3] and snd_half[0..3] are respectively the
* nodes of the first and of the second half of the curve
*
* fst_half and snd_half must be different, but they can be the same as
* nodes.
*/
static void
split_bezier (cairo_point_double_t p[4],
cairo_point_double_t fst_half[4],
cairo_point_double_t snd_half[4])
{
split_bezier_1D (p[0].x, p[1].x, p[2].x, p[3].x,
&fst_half[0].x, &fst_half[1].x, &fst_half[2].x, &fst_half[3].x,
&snd_half[0].x, &snd_half[1].x, &snd_half[2].x, &snd_half[3].x);
 
split_bezier_1D (p[0].y, p[1].y, p[2].y, p[3].y,
&fst_half[0].y, &fst_half[1].y, &fst_half[2].y, &fst_half[3].y,
&snd_half[0].y, &snd_half[1].y, &snd_half[2].y, &snd_half[3].y);
}
 
 
typedef enum _intersection {
INSIDE = -1, /* the interval is entirely contained in the reference interval */
OUTSIDE = 0, /* the interval has no intersection with the reference interval */
PARTIAL = 1 /* the interval intersects the reference interval (but is not fully inside it) */
} intersection_t;
 
/*
* Check if an interval if inside another.
*
* Input: a,b are the extrema of the first interval
* c,d are the extrema of the second interval
*
* Returns: INSIDE iff [a,b) intersection [c,d) = [a,b)
* OUTSIDE iff [a,b) intersection [c,d) = {}
* PARTIAL otherwise
*
* The function assumes a < b and c < d
*
* Note: Bitwise-anding the results along each component gives the
* expected result for [a,b) x [A,B) intersection [c,d) x [C,D).
*/
static inline int
intersect_interval (double a, double b, double c, double d)
{
if (c <= a && b <= d)
return INSIDE;
else if (a >= d || b <= c)
return OUTSIDE;
else
return PARTIAL;
}
 
/*
* Set the color of a pixel.
*
* Input: data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
* x, y are the coordinates of the pixel to be colored
* r,g,b,a are the color components of the color to be set
*
* Output: the (x,y) pixel in data has the (r,g,b,a) color
*
* The input color components are not premultiplied, but the data
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
* premultiplied).
*
* If the pixel to be set is outside the image, this function does
* nothing.
*/
static inline void
draw_pixel (unsigned char *data, int width, int height, int stride,
int x, int y, uint16_t r, uint16_t g, uint16_t b, uint16_t a)
{
if (likely (0 <= x && 0 <= y && x < width && y < height)) {
uint32_t tr, tg, tb, ta;
 
/* Premultiply and round */
ta = a;
tr = r * ta + 0x8000;
tg = g * ta + 0x8000;
tb = b * ta + 0x8000;
 
tr += tr >> 16;
tg += tg >> 16;
tb += tb >> 16;
 
*((uint32_t*) (data + y*stride + 4*x)) = ((ta << 16) & 0xff000000) |
((tr >> 8) & 0xff0000) | ((tg >> 16) & 0xff00) | (tb >> 24);
}
}
 
/*
* Forward-rasterize a cubic curve using forward differences.
*
* Input: data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
* ushift is log2(n) if n is the number of desired steps
* dxu[i], dyu[i] are the x,y forward differences of the curve
* r0,g0,b0,a0 are the color components of the start point
* r3,g3,b3,a3 are the color components of the end point
*
* Output: data will be changed to have the requested curve drawn in
* the specified colors
*
* The input color components are not premultiplied, but the data
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
* premultiplied).
*
* The function draws n+1 pixels, that is from the point at step 0 to
* the point at step n, both included. This is the discrete equivalent
* to drawing the curve for values of the interpolation parameter in
* [0,1] (including both extremes).
*/
static inline void
rasterize_bezier_curve (unsigned char *data, int width, int height, int stride,
int ushift, double dxu[4], double dyu[4],
uint16_t r0, uint16_t g0, uint16_t b0, uint16_t a0,
uint16_t r3, uint16_t g3, uint16_t b3, uint16_t a3)
{
int32_t xu[4], yu[4];
int x0, y0, u, usteps = 1 << ushift;
 
uint16_t r = r0, g = g0, b = b0, a = a0;
int16_t dr = _color_delta_to_shifted_short (r0, r3, ushift);
int16_t dg = _color_delta_to_shifted_short (g0, g3, ushift);
int16_t db = _color_delta_to_shifted_short (b0, b3, ushift);
int16_t da = _color_delta_to_shifted_short (a0, a3, ushift);
 
fd_fixed (dxu, xu);
fd_fixed (dyu, yu);
 
/*
* Use (dxu[0],dyu[0]) as origin for the forward differences.
*
* This makes it possible to handle much larger coordinates (the
* ones that can be represented as cairo_fixed_t)
*/
x0 = _cairo_fixed_from_double (dxu[0]);
y0 = _cairo_fixed_from_double (dyu[0]);
xu[0] = 0;
yu[0] = 0;
 
for (u = 0; u <= usteps; ++u) {
/*
* This rasterizer assumes that pixels are integer aligned
* squares, so a generic (x,y) point belongs to the pixel with
* top-left coordinates (floor(x), floor(y))
*/
 
int x = _cairo_fixed_integer_floor (x0 + (xu[0] >> 15) + ((xu[0] >> 14) & 1));
int y = _cairo_fixed_integer_floor (y0 + (yu[0] >> 15) + ((yu[0] >> 14) & 1));
 
draw_pixel (data, width, height, stride, x, y, r, g, b, a);
 
fd_fixed_fwd (xu);
fd_fixed_fwd (yu);
r += dr;
g += dg;
b += db;
a += da;
}
}
 
/*
* Clip, split and rasterize a Bezier curve.
*
* Input: data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
* p[i] is the i-th node of the Bezier curve
* c0[i] is the i-th color component at the start point
* c3[i] is the i-th color component at the end point
*
* Output: data will be changed to have the requested curve drawn in
* the specified colors
*
* The input color components are not premultiplied, but the data
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
* premultiplied).
*
* The color components are red, green, blue and alpha, in this order.
*
* The function guarantees that it will draw the curve with a step
* small enough to never have a distance above 1/sqrt(2) between two
* consecutive points (which is needed to ensure that no hole can
* appear when using this function to rasterize a patch).
*/
static void
draw_bezier_curve (unsigned char *data, int width, int height, int stride,
cairo_point_double_t p[4], double c0[4], double c3[4])
{
double top, bottom, left, right, steps_sq;
int i, v;
 
top = bottom = p[0].y;
for (i = 1; i < 4; ++i) {
top = MIN (top, p[i].y);
bottom = MAX (bottom, p[i].y);
}
 
/* Check visibility */
v = intersect_interval (top, bottom, 0, height);
if (v == OUTSIDE)
return;
 
left = right = p[0].x;
for (i = 1; i < 4; ++i) {
left = MIN (left, p[i].x);
right = MAX (right, p[i].x);
}
 
v &= intersect_interval (left, right, 0, width);
if (v == OUTSIDE)
return;
 
steps_sq = bezier_steps_sq (p);
if (steps_sq >= (v == INSIDE ? STEPS_MAX_U * STEPS_MAX_U : STEPS_CLIP_U * STEPS_CLIP_U)) {
/*
* The number of steps is greater than the threshold. This
* means that either the error would become too big if we
* directly rasterized it or that we can probably save some
* time by splitting the curve and clipping part of it
*/
cairo_point_double_t first[4], second[4];
double midc[4];
split_bezier (p, first, second);
midc[0] = (c0[0] + c3[0]) * 0.5;
midc[1] = (c0[1] + c3[1]) * 0.5;
midc[2] = (c0[2] + c3[2]) * 0.5;
midc[3] = (c0[3] + c3[3]) * 0.5;
draw_bezier_curve (data, width, height, stride, first, c0, midc);
draw_bezier_curve (data, width, height, stride, second, midc, c3);
} else {
double xu[4], yu[4];
int ushift = sqsteps2shift (steps_sq), k;
 
fd_init (p[0].x, p[1].x, p[2].x, p[3].x, xu);
fd_init (p[0].y, p[1].y, p[2].y, p[3].y, yu);
 
for (k = 0; k < ushift; ++k) {
fd_down (xu);
fd_down (yu);
}
 
rasterize_bezier_curve (data, width, height, stride, ushift,
xu, yu,
_cairo_color_double_to_short (c0[0]),
_cairo_color_double_to_short (c0[1]),
_cairo_color_double_to_short (c0[2]),
_cairo_color_double_to_short (c0[3]),
_cairo_color_double_to_short (c3[0]),
_cairo_color_double_to_short (c3[1]),
_cairo_color_double_to_short (c3[2]),
_cairo_color_double_to_short (c3[3]));
 
/* Draw the end point, to make sure that we didn't leave it
* out because of rounding */
draw_pixel (data, width, height, stride,
_cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].x)),
_cairo_fixed_integer_floor (_cairo_fixed_from_double (p[3].y)),
_cairo_color_double_to_short (c3[0]),
_cairo_color_double_to_short (c3[1]),
_cairo_color_double_to_short (c3[2]),
_cairo_color_double_to_short (c3[3]));
}
}
 
/*
* Forward-rasterize a cubic Bezier patch using forward differences.
*
* Input: data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
* vshift is log2(n) if n is the number of desired steps
* p[i][j], p[i][j] are the the nodes of the Bezier patch
* col[i][j] is the j-th color component of the i-th corner
*
* Output: data will be changed to have the requested patch drawn in
* the specified colors
*
* The nodes of the patch are as follows:
*
* u\v 0 - > 1
* 0 p00 p01 p02 p03
* | p10 p11 p12 p13
* v p20 p21 p22 p23
* 1 p30 p31 p32 p33
*
* i.e. u varies along the first component (rows), v varies along the
* second one (columns).
*
* The color components are red, green, blue and alpha, in this order.
* c[0..3] are the colors in p00, p30, p03, p33 respectively
*
* The input color components are not premultiplied, but the data
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
* premultiplied).
*
* If the patch folds over itself, the part with the highest v
* parameter is considered above. If both have the same v, the one
* with the highest u parameter is above.
*
* The function draws n+1 curves, that is from the curve at step 0 to
* the curve at step n, both included. This is the discrete equivalent
* to drawing the patch for values of the interpolation parameter in
* [0,1] (including both extremes).
*/
static inline void
rasterize_bezier_patch (unsigned char *data, int width, int height, int stride, int vshift,
cairo_point_double_t p[4][4], double col[4][4])
{
double pv[4][2][4], cstart[4], cend[4], dcstart[4], dcend[4];
int vsteps, v, i, k;
 
vsteps = 1 << vshift;
 
/*
* pv[i][0] is the function (represented using forward
* differences) mapping v to the x coordinate of the i-th node of
* the Bezier curve with parameter u.
* (Likewise p[i][0] gives the y coordinate).
*
* This means that (pv[0][0][0],pv[0][1][0]),
* (pv[1][0][0],pv[1][1][0]), (pv[2][0][0],pv[2][1][0]) and
* (pv[3][0][0],pv[3][1][0]) are the nodes of the Bezier curve for
* the "current" v value (see the FD comments for more details).
*/
for (i = 0; i < 4; ++i) {
fd_init (p[i][0].x, p[i][1].x, p[i][2].x, p[i][3].x, pv[i][0]);
fd_init (p[i][0].y, p[i][1].y, p[i][2].y, p[i][3].y, pv[i][1]);
for (k = 0; k < vshift; ++k) {
fd_down (pv[i][0]);
fd_down (pv[i][1]);
}
}
 
for (i = 0; i < 4; ++i) {
cstart[i] = col[0][i];
cend[i] = col[1][i];
dcstart[i] = (col[2][i] - col[0][i]) / vsteps;
dcend[i] = (col[3][i] - col[1][i]) / vsteps;
}
 
for (v = 0; v <= vsteps; ++v) {
cairo_point_double_t nodes[4];
for (i = 0; i < 4; ++i) {
nodes[i].x = pv[i][0][0];
nodes[i].y = pv[i][1][0];
}
 
draw_bezier_curve (data, width, height, stride, nodes, cstart, cend);
 
for (i = 0; i < 4; ++i) {
fd_fwd (pv[i][0]);
fd_fwd (pv[i][1]);
cstart[i] += dcstart[i];
cend[i] += dcend[i];
}
}
}
 
/*
* Clip, split and rasterize a Bezier cubic patch.
*
* Input: data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
* p[i][j], p[i][j] are the nodes of the patch
* col[i][j] is the j-th color component of the i-th corner
*
* Output: data will be changed to have the requested patch drawn in
* the specified colors
*
* The nodes of the patch are as follows:
*
* u\v 0 - > 1
* 0 p00 p01 p02 p03
* | p10 p11 p12 p13
* v p20 p21 p22 p23
* 1 p30 p31 p32 p33
*
* i.e. u varies along the first component (rows), v varies along the
* second one (columns).
*
* The color components are red, green, blue and alpha, in this order.
* c[0..3] are the colors in p00, p30, p03, p33 respectively
*
* The input color components are not premultiplied, but the data
* stored in the image is assumed to be in CAIRO_FORMAT_ARGB32 (8 bpc,
* premultiplied).
*
* If the patch folds over itself, the part with the highest v
* parameter is considered above. If both have the same v, the one
* with the highest u parameter is above.
*
* The function guarantees that it will draw the patch with a step
* small enough to never have a distance above 1/sqrt(2) between two
* adjacent points (which guarantees that no hole can appear).
*
* This function can be used to rasterize a tile of PDF type 7
* shadings (see http://www.adobe.com/devnet/pdf/pdf_reference.html).
*/
static void
draw_bezier_patch (unsigned char *data, int width, int height, int stride,
cairo_point_double_t p[4][4], double c[4][4])
{
double top, bottom, left, right, steps_sq;
int i, j, v;
 
top = bottom = p[0][0].y;
for (i = 0; i < 4; ++i) {
for (j= 0; j < 4; ++j) {
top = MIN (top, p[i][j].y);
bottom = MAX (bottom, p[i][j].y);
}
}
 
v = intersect_interval (top, bottom, 0, height);
if (v == OUTSIDE)
return;
 
left = right = p[0][0].x;
for (i = 0; i < 4; ++i) {
for (j= 0; j < 4; ++j) {
left = MIN (left, p[i][j].x);
right = MAX (right, p[i][j].x);
}
}
 
v &= intersect_interval (left, right, 0, width);
if (v == OUTSIDE)
return;
 
steps_sq = 0;
for (i = 0; i < 4; ++i)
steps_sq = MAX (steps_sq, bezier_steps_sq (p[i]));
 
if (steps_sq >= (v == INSIDE ? STEPS_MAX_V * STEPS_MAX_V : STEPS_CLIP_V * STEPS_CLIP_V)) {
/* The number of steps is greater than the threshold. This
* means that either the error would become too big if we
* directly rasterized it or that we can probably save some
* time by splitting the curve and clipping part of it. The
* patch is only split in the v direction to guarantee that
* rasterizing each part will overwrite parts with low v with
* overlapping parts with higher v. */
 
cairo_point_double_t first[4][4], second[4][4];
double subc[4][4];
 
for (i = 0; i < 4; ++i)
split_bezier (p[i], first[i], second[i]);
 
for (i = 0; i < 4; ++i) {
subc[0][i] = c[0][i];
subc[1][i] = c[1][i];
subc[2][i] = 0.5 * (c[0][i] + c[2][i]);
subc[3][i] = 0.5 * (c[1][i] + c[3][i]);
}
 
draw_bezier_patch (data, width, height, stride, first, subc);
 
for (i = 0; i < 4; ++i) {
subc[0][i] = subc[2][i];
subc[1][i] = subc[3][i];
subc[2][i] = c[2][i];
subc[3][i] = c[3][i];
}
draw_bezier_patch (data, width, height, stride, second, subc);
} else {
rasterize_bezier_patch (data, width, height, stride, sqsteps2shift (steps_sq), p, c);
}
}
 
/*
* Draw a tensor product shading pattern.
*
* Input: mesh is the mesh pattern
* data is the base pointer of the image
* width, height are the dimensions of the image
* stride is the stride in bytes between adjacent rows
*
* Output: data will be changed to have the pattern drawn on it
*
* data is assumed to be clear and its content is assumed to be in
* CAIRO_FORMAT_ARGB32 (8 bpc, premultiplied).
*
* This function can be used to rasterize a PDF type 7 shading (see
* http://www.adobe.com/devnet/pdf/pdf_reference.html).
*/
void
_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
void *data,
int width,
int height,
int stride,
double x_offset,
double y_offset)
{
cairo_point_double_t nodes[4][4];
double colors[4][4];
cairo_matrix_t p2u;
unsigned int i, j, k, n;
cairo_status_t status;
const cairo_mesh_patch_t *patch;
const cairo_color_t *c;
 
assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
assert (mesh->current_patch == NULL);
 
p2u = mesh->base.matrix;
status = cairo_matrix_invert (&p2u);
assert (status == CAIRO_STATUS_SUCCESS);
 
n = _cairo_array_num_elements (&mesh->patches);
patch = _cairo_array_index_const (&mesh->patches, 0);
for (i = 0; i < n; i++) {
for (j = 0; j < 4; j++) {
for (k = 0; k < 4; k++) {
nodes[j][k] = patch->points[j][k];
cairo_matrix_transform_point (&p2u, &nodes[j][k].x, &nodes[j][k].y);
nodes[j][k].x += x_offset;
nodes[j][k].y += y_offset;
}
}
 
c = &patch->colors[0];
colors[0][0] = c->red;
colors[0][1] = c->green;
colors[0][2] = c->blue;
colors[0][3] = c->alpha;
 
c = &patch->colors[3];
colors[1][0] = c->red;
colors[1][1] = c->green;
colors[1][2] = c->blue;
colors[1][3] = c->alpha;
 
c = &patch->colors[1];
colors[2][0] = c->red;
colors[2][1] = c->green;
colors[2][2] = c->blue;
colors[2][3] = c->alpha;
 
c = &patch->colors[2];
colors[3][0] = c->red;
colors[3][1] = c->green;
colors[3][2] = c->blue;
colors[3][3] = c->alpha;
 
draw_bezier_patch (data, width, height, stride, nodes, colors);
patch++;
}
}
/programs/develop/libraries/cairo/src/cairo-misc.c
41,7 → 41,7
#include "cairoint.h"
#include "cairo-error-private.h"
 
COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED);
COMPILE_TIME_ASSERT ((int)CAIRO_STATUS_LAST_STATUS < (int)CAIRO_INT_STATUS_UNSUPPORTED);
COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127);
 
/**
62,7 → 62,7
* the mean time, it is safe to call all cairo functions normally even if the
* underlying object is in an error status. This means that no error handling
* code is required before or after each individual cairo function call.
*/
**/
 
/* Public stuff */
 
73,7 → 73,9
* Provides a human-readable description of a #cairo_status_t.
*
* Returns: a string representation of the status
*/
*
* Since: 1.0
**/
const char *
cairo_status_to_string (cairo_status_t status)
{
150,6 → 152,10
return "the device type is not appropriate for the operation";
case CAIRO_STATUS_DEVICE_ERROR:
return "an operation to the device caused an unspecified error";
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
return "invalid operation during mesh pattern construction";
case CAIRO_STATUS_DEVICE_FINISHED:
return "the target device has been finished";
default:
case CAIRO_STATUS_LAST_STATUS:
return "<unknown error status>";
176,7 → 182,7
* freed using cairo_glyph_free()
*
* Since: 1.8
*/
**/
cairo_glyph_t *
cairo_glyph_allocate (int num_glyphs)
{
199,11 → 205,10
* for glyphs.
*
* Since: 1.8
*/
**/
void
cairo_glyph_free (cairo_glyph_t *glyphs)
{
if (glyphs)
free (glyphs);
}
slim_hidden_def (cairo_glyph_free);
227,7 → 232,7
* freed using cairo_text_cluster_free()
*
* Since: 1.8
*/
**/
cairo_text_cluster_t *
cairo_text_cluster_allocate (int num_clusters)
{
250,11 → 255,10
* for text clusters.
*
* Since: 1.8
*/
**/
void
cairo_text_cluster_free (cairo_text_cluster_t *clusters)
{
if (clusters)
free (clusters);
}
slim_hidden_def (cairo_text_cluster_free);
279,7 → 283,7
* %CAIRO_STATUS_INVALID_CLUSTERS on error.
* The error is either invalid UTF-8 input,
* or bad cluster mapping.
*/
**/
cairo_status_t
_cairo_validate_text_clusters (const char *utf8,
int utf8_len,
/programs/develop/libraries/cairo/src/cairo-mono-scan-converter.c
0,0 → 1,612
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/*
* Copyright (c) 2011 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "cairoint.h"
#include "cairo-spans-private.h"
#include "cairo-error-private.h"
 
#include <stdlib.h>
#include <string.h>
#include <limits.h>
 
struct quorem {
int32_t quo;
int32_t rem;
};
 
struct edge {
struct edge *next, *prev;
 
int32_t height_left;
int32_t dir;
int32_t vertical;
 
int32_t dy;
struct quorem x;
struct quorem dxdy;
};
 
/* A collection of sorted and vertically clipped edges of the polygon.
* Edges are moved from the polygon to an active list while scan
* converting. */
struct polygon {
/* The vertical clip extents. */
int32_t ymin, ymax;
 
int num_edges;
struct edge *edges;
 
/* Array of edges all starting in the same bucket. An edge is put
* into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
* it is added to the polygon. */
struct edge **y_buckets;
 
struct edge *y_buckets_embedded[64];
struct edge edges_embedded[32];
};
 
struct mono_scan_converter {
struct polygon polygon[1];
 
/* Leftmost edge on the current scan line. */
struct edge head, tail;
int is_vertical;
 
cairo_half_open_span_t *spans;
cairo_half_open_span_t spans_embedded[64];
int num_spans;
 
/* Clip box. */
int32_t xmin, xmax;
int32_t ymin, ymax;
};
 
#define I(x) _cairo_fixed_integer_round_down(x)
 
/* Compute the floored division a/b. Assumes / and % perform symmetric
* division. */
inline static struct quorem
floored_divrem(int a, int b)
{
struct quorem qr;
qr.quo = a/b;
qr.rem = a%b;
if ((a^b)<0 && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
* division. */
static struct quorem
floored_muldivrem(int x, int a, int b)
{
struct quorem qr;
long long xa = (long long)x*a;
qr.quo = xa/b;
qr.rem = xa%b;
if ((xa>=0) != (b>=0) && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
static cairo_status_t
polygon_init (struct polygon *polygon, int ymin, int ymax)
{
unsigned h = ymax - ymin + 1;
 
polygon->y_buckets = polygon->y_buckets_embedded;
if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *));
if (unlikely (NULL == polygon->y_buckets))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memset (polygon->y_buckets, 0, h * sizeof (struct edge *));
polygon->y_buckets[h-1] = (void *)-1;
 
polygon->ymin = ymin;
polygon->ymax = ymax;
return CAIRO_STATUS_SUCCESS;
}
 
static void
polygon_fini (struct polygon *polygon)
{
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
 
if (polygon->edges != polygon->edges_embedded)
free (polygon->edges);
}
 
static void
_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
struct edge *e,
int y)
{
struct edge **ptail = &polygon->y_buckets[y - polygon->ymin];
if (*ptail)
(*ptail)->prev = e;
e->next = *ptail;
e->prev = NULL;
*ptail = e;
}
 
inline static void
polygon_add_edge (struct polygon *polygon,
const cairo_edge_t *edge)
{
struct edge *e;
cairo_fixed_t dx;
cairo_fixed_t dy;
int y, ytop, ybot;
int ymin = polygon->ymin;
int ymax = polygon->ymax;
 
y = I(edge->top);
ytop = MAX(y, ymin);
 
y = I(edge->bottom);
ybot = MIN(y, ymax);
 
if (ybot <= ytop)
return;
 
e = polygon->edges + polygon->num_edges++;
e->height_left = ybot - ytop;
e->dir = edge->dir;
 
dx = edge->line.p2.x - edge->line.p1.x;
dy = edge->line.p2.y - edge->line.p1.y;
 
if (dx == 0) {
e->vertical = TRUE;
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
e->dxdy.quo = 0;
e->dxdy.rem = 0;
e->dy = 0;
} else {
e->vertical = FALSE;
e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy);
e->dy = dy;
 
e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y,
dx, dy);
e->x.quo += edge->line.p1.x;
}
e->x.rem -= dy;
 
_polygon_insert_edge_into_its_y_bucket (polygon, e, ytop);
}
 
static struct edge *
merge_sorted_edges (struct edge *head_a, struct edge *head_b)
{
struct edge *head, **next, *prev;
int32_t x;
 
prev = head_a->prev;
next = &head;
if (head_a->x.quo <= head_b->x.quo) {
head = head_a;
} else {
head = head_b;
head_b->prev = prev;
goto start_with_b;
}
 
do {
x = head_b->x.quo;
while (head_a != NULL && head_a->x.quo <= x) {
prev = head_a;
next = &head_a->next;
head_a = head_a->next;
}
 
head_b->prev = prev;
*next = head_b;
if (head_a == NULL)
return head;
 
start_with_b:
x = head_a->x.quo;
while (head_b != NULL && head_b->x.quo <= x) {
prev = head_b;
next = &head_b->next;
head_b = head_b->next;
}
 
head_a->prev = prev;
*next = head_a;
if (head_b == NULL)
return head;
} while (1);
}
 
static struct edge *
sort_edges (struct edge *list,
unsigned int level,
struct edge **head_out)
{
struct edge *head_other, *remaining;
unsigned int i;
 
head_other = list->next;
 
if (head_other == NULL) {
*head_out = list;
return NULL;
}
 
remaining = head_other->next;
if (list->x.quo <= head_other->x.quo) {
*head_out = list;
head_other->next = NULL;
} else {
*head_out = head_other;
head_other->prev = list->prev;
head_other->next = list;
list->prev = head_other;
list->next = NULL;
}
 
for (i = 0; i < level && remaining; i++) {
remaining = sort_edges (remaining, i, &head_other);
*head_out = merge_sorted_edges (*head_out, head_other);
}
 
return remaining;
}
 
static struct edge *
merge_unsorted_edges (struct edge *head, struct edge *unsorted)
{
sort_edges (unsorted, UINT_MAX, &unsorted);
return merge_sorted_edges (head, unsorted);
}
 
inline static void
active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges)
{
struct edge *e;
 
for (e = edges; c->is_vertical && e; e = e->next)
c->is_vertical = e->vertical;
 
c->head.next = merge_unsorted_edges (c->head.next, edges);
}
 
inline static void
add_span (struct mono_scan_converter *c, int x1, int x2)
{
int n;
 
if (x1 < c->xmin)
x1 = c->xmin;
if (x2 > c->xmax)
x2 = c->xmax;
if (x2 <= x1)
return;
 
n = c->num_spans++;
c->spans[n].x = x1;
c->spans[n].coverage = 255;
 
n = c->num_spans++;
c->spans[n].x = x2;
c->spans[n].coverage = 0;
}
 
inline static void
row (struct mono_scan_converter *c, unsigned int mask)
{
struct edge *edge = c->head.next;
int xstart = INT_MIN, prev_x = INT_MIN;
int winding = 0;
 
c->num_spans = 0;
while (&c->tail != edge) {
struct edge *next = edge->next;
int xend = I(edge->x.quo);
 
if (--edge->height_left) {
if (!edge->vertical) {
edge->x.quo += edge->dxdy.quo;
edge->x.rem += edge->dxdy.rem;
if (edge->x.rem >= 0) {
++edge->x.quo;
edge->x.rem -= edge->dy;
}
}
 
if (edge->x.quo < prev_x) {
struct edge *pos = edge->prev;
pos->next = next;
next->prev = pos;
do {
pos = pos->prev;
} while (edge->x.quo < pos->x.quo);
pos->next->prev = edge;
edge->next = pos->next;
edge->prev = pos;
pos->next = edge;
} else
prev_x = edge->x.quo;
} else {
edge->prev->next = next;
next->prev = edge->prev;
}
 
winding += edge->dir;
if ((winding & mask) == 0) {
if (I(next->x.quo) > xend + 1) {
add_span (c, xstart, xend);
xstart = INT_MIN;
}
} else if (xstart == INT_MIN)
xstart = xend;
 
edge = next;
}
}
 
inline static void dec (struct edge *e, int h)
{
e->height_left -= h;
if (e->height_left == 0) {
e->prev->next = e->next;
e->next->prev = e->prev;
}
}
 
static cairo_status_t
_mono_scan_converter_init(struct mono_scan_converter *c,
int xmin, int ymin,
int xmax, int ymax)
{
cairo_status_t status;
int max_num_spans;
 
status = polygon_init (c->polygon, ymin, ymax);
if (unlikely (status))
return status;
 
max_num_spans = xmax - xmin + 1;
if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) {
c->spans = _cairo_malloc_ab (max_num_spans,
sizeof (cairo_half_open_span_t));
if (unlikely (c->spans == NULL)) {
polygon_fini (c->polygon);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
} else
c->spans = c->spans_embedded;
 
c->xmin = xmin;
c->xmax = xmax;
c->ymin = ymin;
c->ymax = ymax;
 
c->head.vertical = 1;
c->head.height_left = INT_MAX;
c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN));
c->head.prev = NULL;
c->head.next = &c->tail;
c->tail.prev = &c->head;
c->tail.next = NULL;
c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX));
c->tail.height_left = INT_MAX;
c->tail.vertical = 1;
 
c->is_vertical = 1;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_mono_scan_converter_fini(struct mono_scan_converter *self)
{
if (self->spans != self->spans_embedded)
free (self->spans);
 
polygon_fini(self->polygon);
}
 
static cairo_status_t
mono_scan_converter_allocate_edges(struct mono_scan_converter *c,
int num_edges)
 
{
c->polygon->num_edges = 0;
c->polygon->edges = c->polygon->edges_embedded;
if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) {
c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge));
if (unlikely (c->polygon->edges == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
mono_scan_converter_add_edge (struct mono_scan_converter *c,
const cairo_edge_t *edge)
{
polygon_add_edge (c->polygon, edge);
}
 
static void
step_edges (struct mono_scan_converter *c, int count)
{
struct edge *edge;
 
for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
edge->height_left -= count;
if (! edge->height_left) {
edge->prev->next = edge->next;
edge->next->prev = edge->prev;
}
}
}
 
static cairo_status_t
mono_scan_converter_render(struct mono_scan_converter *c,
unsigned int winding_mask,
cairo_span_renderer_t *renderer)
{
struct polygon *polygon = c->polygon;
int i, j, h = c->ymax - c->ymin;
cairo_status_t status;
 
for (i = 0; i < h; i = j) {
j = i + 1;
 
if (polygon->y_buckets[i])
active_list_merge_edges (c, polygon->y_buckets[i]);
 
if (c->is_vertical) {
int min_height;
struct edge *e;
 
e = c->head.next;
min_height = e->height_left;
while (e != &c->tail) {
if (e->height_left < min_height)
min_height = e->height_left;
e = e->next;
}
 
while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
j++;
if (j != i + 1)
step_edges (c, j - (i + 1));
}
 
row (c, winding_mask);
if (c->num_spans) {
status = renderer->render_rows (renderer, c->ymin+i, j-i,
c->spans, c->num_spans);
if (unlikely (status))
return status;
}
 
/* XXX recompute after dropping edges? */
if (c->head.next == &c->tail)
c->is_vertical = 1;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
struct _cairo_mono_scan_converter {
cairo_scan_converter_t base;
 
struct mono_scan_converter converter[1];
cairo_fill_rule_t fill_rule;
};
 
typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t;
 
static void
_cairo_mono_scan_converter_destroy (void *converter)
{
cairo_mono_scan_converter_t *self = converter;
_mono_scan_converter_fini (self->converter);
free(self);
}
 
cairo_status_t
_cairo_mono_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
cairo_mono_scan_converter_t *self = converter;
cairo_status_t status;
int i;
 
#if 0
FILE *file = fopen ("polygon.txt", "w");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
#endif
 
status = mono_scan_converter_allocate_edges (self->converter,
polygon->num_edges);
if (unlikely (status))
return status;
 
for (i = 0; i < polygon->num_edges; i++)
mono_scan_converter_add_edge (self->converter, &polygon->edges[i]);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_mono_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
cairo_mono_scan_converter_t *self = converter;
 
return mono_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
renderer);
}
 
cairo_scan_converter_t *
_cairo_mono_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule)
{
cairo_mono_scan_converter_t *self;
cairo_status_t status;
 
self = malloc (sizeof(struct _cairo_mono_scan_converter));
if (unlikely (self == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail_nomem;
}
 
self->base.destroy = _cairo_mono_scan_converter_destroy;
self->base.generate = _cairo_mono_scan_converter_generate;
 
status = _mono_scan_converter_init (self->converter,
xmin, ymin, xmax, ymax);
if (unlikely (status))
goto bail;
 
self->fill_rule = fill_rule;
 
return &self->base;
 
bail:
self->base.destroy(&self->base);
bail_nomem:
return _cairo_scan_converter_create_in_error (status);
}
/programs/develop/libraries/cairo/src/cairo-mutex-list-private.h
40,17 → 40,21
 
CAIRO_MUTEX_DECLARE (_cairo_image_solid_cache_mutex)
 
CAIRO_MUTEX_DECLARE (_cairo_error_mutex)
CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
CAIRO_MUTEX_DECLARE (_cairo_glyph_cache_mutex)
 
#if CAIRO_HAS_FT_FONT
CAIRO_MUTEX_DECLARE (_cairo_ft_unscaled_font_map_mutex)
#endif
 
#if CAIRO_HAS_WIN32_FONT
CAIRO_MUTEX_DECLARE (_cairo_win32_font_face_mutex)
#endif
 
#if CAIRO_HAS_XLIB_SURFACE
CAIRO_MUTEX_DECLARE (_cairo_xlib_display_mutex)
#endif
/programs/develop/libraries/cairo/src/cairo-no-compositor.c
0,0 → 1,107
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-compositor-private.h"
 
static cairo_int_status_t
_cairo_no_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_no_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_no_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_no_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_no_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
const cairo_compositor_t __cairo_no_compositor = {
NULL,
_cairo_no_compositor_paint,
_cairo_no_compositor_mask,
_cairo_no_compositor_stroke,
_cairo_no_compositor_fill,
_cairo_no_compositor_glyphs,
};
/programs/develop/libraries/cairo/src/cairo-observer.c
36,6 → 36,8
 
#include "cairoint.h"
 
#include "cairo-list-inline.h"
 
void
_cairo_observers_notify (cairo_list_t *observers, void *arg)
{
/programs/develop/libraries/cairo/src/cairo-os2-surface.c
0,0 → 1,1415
/* vim: set sw=4 sts=4 et cin: */
/* cairo - a vector graphics library with display and print output
*
* Copyright (c) 2005-2006 netlabs.org
*
* 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
* Doodle <doodle@scenergy.dfmk.hu>
*
* Contributor(s):
* Peter Weilbacher <mozilla@Weilbacher.org>
* Rich Walsh <dragtext@e-vertise.com>
*/
 
#include "cairoint.h"
 
#include "cairo-os2-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-surface-fallback-private.h"
#include "cairo-image-surface-private.h"
 
#if CAIRO_HAS_FC_FONT
#include <fontconfig/fontconfig.h>
#endif
 
#include <float.h>
#ifdef BUILD_CAIRO_DLL
# include "cairo-os2.h"
# ifndef __WATCOMC__
# include <emx/startup.h>
# endif
#endif
 
/*
* Here comes the extra API for the OS/2 platform. Currently it consists
* of two extra functions, the cairo_os2_init() and the
* cairo_os2_fini(). Both of them are called automatically if
* Cairo is compiled to be a DLL file, but you have to call them before
* using the Cairo API if you link to Cairo statically!
*
* You'll also find the code in here which deals with DLL initialization
* and termination, if the code is built to be a DLL.
* (if BUILD_CAIRO_DLL is defined)
*/
 
/* Initialization counter: */
static int cairo_os2_initialization_count = 0;
 
static inline void
DisableFPUException (void)
{
unsigned short usCW;
 
/* Some OS/2 PM API calls modify the FPU Control Word,
* but forget to restore it.
*
* This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
* so to be sure, we disable Invalid Opcode FPU exception
* before using FPU stuffs.
*/
usCW = _control87 (0, 0);
usCW = usCW | EM_INVALID | 0x80;
_control87 (usCW, MCW_EM | 0x80);
}
 
/**
* cairo_os2_init:
*
* Initializes the Cairo library. This function is automatically called if
* Cairo was compiled to be a DLL (however it's not a problem if it's called
* multiple times). But if you link to Cairo statically, you have to call it
* once to set up Cairo's internal structures and mutexes.
*
* Since: 1.4
**/
cairo_public void
cairo_os2_init (void)
{
/* This may initialize some stuffs, like create mutex semaphores etc.. */
 
cairo_os2_initialization_count++;
if (cairo_os2_initialization_count > 1) return;
 
DisableFPUException ();
 
#if CAIRO_HAS_FC_FONT
/* Initialize FontConfig */
FcInit ();
#endif
 
CAIRO_MUTEX_INITIALIZE ();
}
 
/**
* cairo_os2_fini:
*
* Uninitializes the Cairo library. This function is automatically called if
* Cairo was compiled to be a DLL (however it's not a problem if it's called
* multiple times). But if you link to Cairo statically, you have to call it
* once to shut down Cairo, to let it free all the resources it has allocated.
*
* Since: 1.4
**/
cairo_public void
cairo_os2_fini (void)
{
/* This has to uninitialize some stuffs, like destroy mutex semaphores etc.. */
 
if (cairo_os2_initialization_count <= 0) return;
cairo_os2_initialization_count--;
if (cairo_os2_initialization_count > 0) return;
 
DisableFPUException ();
 
cairo_debug_reset_static_data ();
 
#if CAIRO_HAS_FC_FONT
# if HAVE_FCFINI
/* Uninitialize FontConfig */
FcFini ();
# endif
#endif
 
#ifdef __WATCOMC__
/* It can happen that the libraries we use have memory leaks,
* so there are still memory chunks allocated at this point.
* In these cases, Watcom might still have a bigger memory chunk,
* called "the heap" allocated from the OS.
* As we want to minimize the memory we lose from the point of
* view of the OS, we call this function to shrink that heap
* as much as possible.
*/
_heapshrink ();
#else
/* GCC has a heapmin function that approximately corresponds to
* what the Watcom function does
*/
_heapmin ();
#endif
}
 
/*
* This function calls the allocation function depending on which
* method was compiled into the library: it can be native allocation
* (DosAllocMem/DosFreeMem) or C-Library based allocation (malloc/free).
* Actually, for pixel buffers that we use this function for, cairo
* uses _cairo_malloc_abc, so we use that here, too. And use the
* change to check the size argument
*/
void *_buffer_alloc (size_t a, size_t b, const unsigned int size)
{
size_t nbytes;
void *buffer = NULL;
 
if (!a || !b || !size ||
a >= INT32_MAX / b || a*b >= INT32_MAX / size) {
return NULL;
}
nbytes = a * b * size;
 
#ifdef OS2_USE_PLATFORM_ALLOC
/* Using OBJ_ANY on a machine that isn't configured for hi-mem
* will cause ERROR_INVALID_PARAMETER. If this occurs, or this
* build doesn't have hi-mem enabled, fall back to using lo-mem.
*/
#ifdef OS2_HIGH_MEMORY
if (!DosAllocMem (&buffer, nbytes,
OBJ_ANY | PAG_READ | PAG_WRITE | PAG_COMMIT))
return buffer;
#endif
if (DosAllocMem (&buffer, nbytes,
PAG_READ | PAG_WRITE | PAG_COMMIT))
return NULL;
#else
/* Clear the malloc'd buffer the way DosAllocMem() does. */
buffer = malloc (nbytes);
if (buffer) {
memset (buffer, 0, nbytes);
}
#endif
return buffer;
}
 
/*
* This function selects the free function depending on which
* allocation method was compiled into the library
*/
void _buffer_free (void *buffer)
{
#ifdef OS2_USE_PLATFORM_ALLOC
DosFreeMem (buffer);
#else
free (buffer);
#endif
}
 
/* XXX
* The cairo_os2_ini() and cairo_os2_fini() functions should be removed and
* the LibMain code moved to cairo-system.c. It should also call
* cairo_debug_reset_static_data() instead of duplicating its logic...
*/
 
#ifdef BUILD_CAIRO_DLL
/* The main DLL entry for DLL initialization and uninitialization */
/* Only include this code if we're about to build a DLL. */
 
#ifdef __WATCOMC__
unsigned _System
LibMain (unsigned hmod,
unsigned termination)
#else
unsigned long _System
_DLL_InitTerm (unsigned long hModule,
unsigned long termination)
#endif
{
if (termination) {
/* Unloading the DLL */
cairo_os2_fini ();
 
#ifndef __WATCOMC__
/* Uninitialize RTL of GCC */
__ctordtorTerm ();
_CRT_term ();
#endif
return 1;
} else {
/* Loading the DLL */
#ifndef __WATCOMC__
/* Initialize RTL of GCC */
if (_CRT_init () != 0)
return 0;
__ctordtorInit ();
#endif
 
cairo_os2_init ();
return 1;
}
}
 
#endif /* BUILD_CAIRO_DLL */
 
/*
* The following part of the source file contains the code which might
* be called the "core" of the OS/2 backend support. This contains the
* OS/2 surface support functions and structures.
*/
 
/* Forward declaration */
static const cairo_surface_backend_t cairo_os2_surface_backend;
 
/* Unpublished API:
* GpiEnableYInversion = PMGPI.723
* GpiQueryYInversion = PMGPI.726
* BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
* LONG APIENTRY GpiQueryYInversion (HPS hps);
*/
BOOL APIENTRY GpiEnableYInversion (HPS hps, LONG lHeight);
LONG APIENTRY GpiQueryYInversion (HPS hps);
 
#ifdef __WATCOMC__
/* Function declaration for GpiDrawBits () (missing from OpenWatcom headers) */
LONG APIENTRY GpiDrawBits (HPS hps,
PVOID pBits,
PBITMAPINFO2 pbmiInfoTable,
LONG lCount,
PPOINTL aptlPoints,
LONG lRop,
ULONG flOptions);
#endif
 
static void
_cairo_os2_surface_blit_pixels (cairo_os2_surface_t *surface,
HPS hps_begin_paint,
PRECTL prcl_begin_paint_rect)
{
POINTL aptlPoints[4];
LONG lOldYInversion;
LONG rc = GPI_OK;
 
/* Check the limits (may not be necessary) */
if (prcl_begin_paint_rect->xLeft < 0)
prcl_begin_paint_rect->xLeft = 0;
if (prcl_begin_paint_rect->yBottom < 0)
prcl_begin_paint_rect->yBottom = 0;
if (prcl_begin_paint_rect->xRight > (LONG) surface->bitmap_info.cx)
prcl_begin_paint_rect->xRight = (LONG) surface->bitmap_info.cx;
if (prcl_begin_paint_rect->yTop > (LONG) surface->bitmap_info.cy)
prcl_begin_paint_rect->yTop = (LONG) surface->bitmap_info.cy;
 
/* Exit if the rectangle is empty */
if (prcl_begin_paint_rect->xLeft >= prcl_begin_paint_rect->xRight ||
prcl_begin_paint_rect->yBottom >= prcl_begin_paint_rect->yTop)
return;
 
/* Set the Target & Source coordinates */
*((PRECTL)&aptlPoints[0]) = *prcl_begin_paint_rect;
*((PRECTL)&aptlPoints[2]) = *prcl_begin_paint_rect;
 
/* Make the Target coordinates non-inclusive */
aptlPoints[1].x -= 1;
aptlPoints[1].y -= 1;
 
/* Enable Y Inversion for the HPS, so GpiDrawBits will
* work with upside-top image, not with upside-down image!
*/
lOldYInversion = GpiQueryYInversion (hps_begin_paint);
GpiEnableYInversion (hps_begin_paint, surface->bitmap_info.cy-1);
 
/* Debug code to draw rectangle limits */
#if 0
{
int x, y;
unsigned char *pixels;
 
pixels = surface->pixels;
for (x = 0; x < surface->bitmap_info.cx; x++) {
for (y = 0; y < surface->bitmap_info.cy; y++) {
if ((x == 0) ||
(y == 0) ||
(x == y) ||
(x >= surface->bitmap_info.cx-1) ||
(y >= surface->bitmap_info.cy-1))
{
pixels[y*surface->bitmap_info.cx*4+x*4] = 255;
}
}
}
}
#endif
if (!surface->use_24bpp) {
rc = GpiDrawBits (hps_begin_paint,
surface->pixels,
&(surface->bitmap_info),
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
if (rc != GPI_OK)
surface->use_24bpp = TRUE;
}
 
if (surface->use_24bpp) {
/* If GpiDrawBits () failed then this is most likely because the
* display driver could not handle a 32bit bitmap. So we need to
* - create a buffer that only contains 3 bytes per pixel
* - change the bitmap info header to contain 24bit
* - pass the new buffer to GpiDrawBits () again
* - clean up the new buffer
*/
BITMAPINFO2 bmpinfo;
unsigned char *pchPixBuf;
unsigned char *pchTarget;
ULONG *pulSource;
ULONG ulX;
ULONG ulY;
ULONG ulPad;
 
/* Set up the bitmap header, but this time for 24bit depth. */
bmpinfo = surface->bitmap_info;
bmpinfo.cBitCount = 24;
 
/* The start of each row has to be DWORD aligned. Calculate the
* of number aligned bytes per row, the total size of the bitmap,
* and the number of padding bytes at the end of each row.
*/
ulX = (((bmpinfo.cx * bmpinfo.cBitCount) + 31) / 32) * 4;
bmpinfo.cbImage = ulX * bmpinfo.cy;
ulPad = ulX - bmpinfo.cx * 3;
 
/* Allocate temporary pixel buffer. If the rows don't need
* padding, it has to be 1 byte larger than the size of the
* bitmap or else the high-order byte from the last source
* row will end up in unallocated memory.
*/
pchPixBuf = (unsigned char *)_buffer_alloc (1, 1,
bmpinfo.cbImage + (ulPad ? 0 : 1));
 
if (pchPixBuf) {
/* Copy 4 bytes from the source but advance the target ptr only
* 3 bytes, so the high-order alpha byte will be overwritten by
* the next copy. At the end of each row, skip over the padding.
*/
pchTarget = pchPixBuf;
pulSource = (ULONG*)surface->pixels;
for (ulY = bmpinfo.cy; ulY; ulY--) {
for (ulX = bmpinfo.cx; ulX; ulX--) {
*((ULONG*)pchTarget) = *pulSource++;
pchTarget += 3;
}
pchTarget += ulPad;
}
 
rc = GpiDrawBits (hps_begin_paint,
pchPixBuf,
&bmpinfo,
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
if (rc != GPI_OK)
surface->use_24bpp = FALSE;
 
_buffer_free (pchPixBuf);
}
}
 
/* Restore Y inversion */
GpiEnableYInversion (hps_begin_paint, lOldYInversion);
}
 
static void
_cairo_os2_surface_get_pixels_from_screen (cairo_os2_surface_t *surface,
HPS hps_begin_paint,
PRECTL prcl_begin_paint_rect)
{
HPS hps;
HDC hdc;
SIZEL sizlTemp;
HBITMAP hbmpTemp;
BITMAPINFO2 bmi2Temp;
POINTL aptlPoints[4];
int y;
unsigned char *pchTemp;
 
/* To copy pixels from screen to our buffer, we do the following steps:
*
* - Blit pixels from screen to a HBITMAP:
* -- Create Memory Device Context
* -- Create a PS into it
* -- Create a HBITMAP
* -- Select HBITMAP into memory PS
* -- Blit dirty pixels from screen to HBITMAP
* - Copy HBITMAP lines (pixels) into our buffer
* - Free resources
*/
 
/* Create a memory device context */
hdc = DevOpenDC (0, OD_MEMORY,"*",0L, NULL, NULLHANDLE);
if (!hdc) {
return;
}
 
/* Create a memory PS */
sizlTemp.cx = prcl_begin_paint_rect->xRight - prcl_begin_paint_rect->xLeft;
sizlTemp.cy = prcl_begin_paint_rect->yTop - prcl_begin_paint_rect->yBottom;
hps = GpiCreatePS (0,
hdc,
&sizlTemp,
PU_PELS | GPIT_NORMAL | GPIA_ASSOC);
if (!hps) {
DevCloseDC (hdc);
return;
}
 
/* Create an uninitialized bitmap. */
/* Prepare BITMAPINFO2 structure for our buffer */
memset (&bmi2Temp, 0, sizeof (bmi2Temp));
bmi2Temp.cbFix = sizeof (BITMAPINFOHEADER2);
bmi2Temp.cx = sizlTemp.cx;
bmi2Temp.cy = sizlTemp.cy;
bmi2Temp.cPlanes = 1;
bmi2Temp.cBitCount = 32;
 
hbmpTemp = GpiCreateBitmap (hps,
(PBITMAPINFOHEADER2) &bmi2Temp,
0,
NULL,
NULL);
 
if (!hbmpTemp) {
GpiDestroyPS (hps);
DevCloseDC (hdc);
return;
}
 
/* Select the bitmap into the memory device context. */
GpiSetBitmap (hps, hbmpTemp);
 
/* Target coordinates (Noninclusive) */
aptlPoints[0].x = 0;
aptlPoints[0].y = 0;
 
aptlPoints[1].x = sizlTemp.cx;
aptlPoints[1].y = sizlTemp.cy;
 
/* Source coordinates (Inclusive) */
aptlPoints[2].x = prcl_begin_paint_rect->xLeft;
aptlPoints[2].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
 
aptlPoints[3].x = prcl_begin_paint_rect->xRight;
aptlPoints[3].y = surface->bitmap_info.cy - prcl_begin_paint_rect->yTop;
 
/* Blit pixels from screen to bitmap */
GpiBitBlt (hps,
hps_begin_paint,
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
 
/* Now we have to extract the pixels from the bitmap. */
pchTemp =
surface->pixels +
(prcl_begin_paint_rect->yBottom)*surface->bitmap_info.cx*4 +
prcl_begin_paint_rect->xLeft*4;
for (y = 0; y < sizlTemp.cy; y++) {
/* Get one line of pixels */
GpiQueryBitmapBits (hps,
sizlTemp.cy - y - 1, /* lScanStart */
1, /* lScans */
(PBYTE)pchTemp,
&bmi2Temp);
 
/* Go for next line */
pchTemp += surface->bitmap_info.cx*4;
}
 
/* Clean up resources */
GpiSetBitmap (hps, (HBITMAP) NULL);
GpiDeleteBitmap (hbmpTemp);
GpiDestroyPS (hps);
DevCloseDC (hdc);
}
 
static cairo_status_t
_cairo_os2_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
 
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
 
/* Increase lend counter */
surface->pixel_array_lend_count++;
 
*image_out = surface->image_surface;
*image_extra = NULL;
 
DosReleaseMutexSem (surface->hmtx_use_private_fields);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_os2_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
 
/* Decrease Lend counter! */
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
 
if (surface->pixel_array_lend_count > 0)
surface->pixel_array_lend_count--;
DosPostEventSem (surface->hev_pixel_array_came_back);
 
DosReleaseMutexSem (surface->hmtx_use_private_fields);
}
 
static cairo_image_surface_t *
_cairo_os2_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
 
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
/* Increase lend counter */
surface->pixel_array_lend_count++;
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
 
/* XXX: BROKEN! */
*image_out = _cairo_surface_create_for_rectangle_int (surface->image_surface,
extents);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_os2_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
 
/* So, we got back the image, and if all goes well, then
* something has been changed inside the interest_rect.
* So, we blit it to the screen!
*/
if (surface->blit_as_changes) {
RECTL rclToBlit;
 
/* Get mutex, we'll work with the pixel array! */
if (DosRequestMutexSem (surface->hmtx_use_private_fields,
SEM_INDEFINITE_WAIT) != NO_ERROR)
{
/* Could not get mutex! */
return;
}
 
rclToBlit.xLeft = image->base.device_transform_inverse.x0;
rclToBlit.xRight = rclToBlit.xLeft + image->width; /* Noninclusive */
rclToBlit.yTop = image->base.device_transform_inverse.y0;
rclToBlit.yBottom = rclToBlit.yTop + image->height; /* Noninclusive */
 
if (surface->hwnd_client_window) {
/* We know the HWND, so let's invalidate the window region,
* so the application will redraw itself, using the
* cairo_os2_surface_refresh_window () API from its own PM thread.
*
* This is the safe method, which should be preferred every time.
*/
rclToBlit.yTop = surface->bitmap_info.cy - rclToBlit.yTop;
rclToBlit.yBottom = surface->bitmap_info.cy - rclToBlit.yTop;
WinInvalidateRect (surface->hwnd_client_window,
&rclToBlit,
FALSE);
} else {
/* We don't know the HWND, so try to blit the pixels from here!
* Please note that it can be problematic if this is not the PM thread!
*
* It can cause internal PM stuffs to be screwed up, for some reason.
* Please always tell the HWND to the surface using the
* cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
* from your WM_PAINT, if it's possible!
*/
_cairo_os2_surface_blit_pixels (surface,
surface->hps_client_window,
&rclToBlit);
}
 
DosReleaseMutexSem (surface->hmtx_use_private_fields);
}
/* Also decrease Lend counter! */
DosRequestMutexSem (surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
 
if (surface->pixel_array_lend_count > 0)
surface->pixel_array_lend_count--;
DosPostEventSem (surface->hev_pixel_array_came_back);
 
DosReleaseMutexSem (surface->hmtx_use_private_fields);
}
 
static cairo_bool_t
_cairo_os2_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_os2_surface_t *surface = (cairo_os2_surface_t *) abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->bitmap_info.cx;
rectangle->height = surface->bitmap_info.cy;
 
return TRUE;
}
 
/**
* cairo_os2_surface_create:
* @hps_client_window: the presentation handle to bind the surface to
* @width: the width of the surface
* @height: the height of the surface
*
* Create a Cairo surface which is bound to a given presentation space (HPS).
* The caller retains ownership of the HPS and must dispose of it after the
* the surface has been destroyed. The surface will be created to have the
* given size. By default every change to the surface will be made visible
* immediately by blitting it into the window. This can be changed with
* cairo_os2_surface_set_manual_window_refresh().
* Note that the surface will contain garbage when created, so the pixels
* have to be initialized by hand first. You can use the Cairo functions to
* fill it with black, or use cairo_surface_mark_dirty() to fill the surface
* with pixels from the window/HPS.
*
* Return value: the newly created surface
*
* Since: 1.4
**/
cairo_surface_t *
cairo_os2_surface_create (HPS hps_client_window,
int width,
int height)
{
cairo_os2_surface_t *local_os2_surface = 0;
cairo_status_t status;
int rc;
 
/* Check the size of the window */
if ((width <= 0) || (height <= 0)) {
status = _cairo_error (CAIRO_STATUS_INVALID_SIZE);
goto error_exit;
}
 
/* Allocate an OS/2 surface structure. */
local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
if (!local_os2_surface) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto error_exit;
}
 
memset(local_os2_surface, 0, sizeof(cairo_os2_surface_t));
 
/* Allocate resources: mutex & event semaphores and the pixel buffer */
if (DosCreateMutexSem (NULL,
&(local_os2_surface->hmtx_use_private_fields),
0,
FALSE))
{
status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
goto error_exit;
}
 
if (DosCreateEventSem (NULL,
&(local_os2_surface->hev_pixel_array_came_back),
0,
FALSE))
{
status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR);
goto error_exit;
}
 
local_os2_surface->pixels = (unsigned char *) _buffer_alloc (height, width, 4);
if (!local_os2_surface->pixels) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto error_exit;
}
 
/* Create image surface from pixel array */
local_os2_surface->image_surface = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (local_os2_surface->pixels,
CAIRO_FORMAT_ARGB32,
width, /* Width */
height, /* Height */
width * 4); /* Rowstride */
status = local_os2_surface->image_surface->base.status;
if (status)
goto error_exit;
 
/* Set values for OS/2-specific data that aren't zero/NULL/FALSE.
* Note: hps_client_window may be null if this was called by
* cairo_os2_surface_create_for_window().
*/
local_os2_surface->hps_client_window = hps_client_window;
local_os2_surface->blit_as_changes = TRUE;
 
/* Prepare BITMAPINFO2 structure for our buffer */
local_os2_surface->bitmap_info.cbFix = sizeof (BITMAPINFOHEADER2);
local_os2_surface->bitmap_info.cx = width;
local_os2_surface->bitmap_info.cy = height;
local_os2_surface->bitmap_info.cPlanes = 1;
local_os2_surface->bitmap_info.cBitCount = 32;
 
/* Initialize base surface */
_cairo_surface_init (&local_os2_surface->base,
&cairo_os2_surface_backend,
NULL, /* device */
_cairo_content_from_format (CAIRO_FORMAT_ARGB32));
 
/* Successful exit */
return (cairo_surface_t *)local_os2_surface;
 
error_exit:
 
/* This point will only be reached if an error occurred */
 
if (local_os2_surface) {
if (local_os2_surface->pixels)
_buffer_free (local_os2_surface->pixels);
if (local_os2_surface->hev_pixel_array_came_back)
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
if (local_os2_surface->hmtx_use_private_fields)
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
free (local_os2_surface);
}
 
return _cairo_surface_create_in_error (status);
}
 
/**
* cairo_os2_surface_create_for_window:
* @hwnd_client_window: the window handle to bind the surface to
* @width: the width of the surface
* @height: the height of the surface
*
* Create a Cairo surface which is bound to a given window; the caller retains
* ownership of the window. This is a convenience function for use with
* windows that will only be updated when cairo_os2_surface_refresh_window()
* is called (usually in response to a WM_PAINT message). It avoids the need
* to create a persistent HPS for every window and assumes that one will be
* supplied by the caller when a cairo function needs one. If it isn't, an
* HPS will be created on-the-fly and released before the function which needs
* it returns.
*
* Return value: the newly created surface
*
* Since: 1.10
**/
cairo_surface_t *
cairo_os2_surface_create_for_window (HWND hwnd_client_window,
int width,
int height)
{
cairo_os2_surface_t *local_os2_surface;
 
/* A window handle must be provided. */
if (!hwnd_client_window) {
return _cairo_surface_create_in_error (
_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
/* Create the surface. */
local_os2_surface = (cairo_os2_surface_t *)
cairo_os2_surface_create (0, width, height);
 
/* If successful, save the hwnd & turn off automatic repainting. */
if (!local_os2_surface->image_surface->base.status) {
local_os2_surface->hwnd_client_window = hwnd_client_window;
local_os2_surface->blit_as_changes = FALSE;
}
 
return (cairo_surface_t *)local_os2_surface;
}
 
/**
* cairo_os2_surface_set_size:
* @surface: the cairo surface to resize
* @new_width: the new width of the surface
* @new_height: the new height of the surface
* @timeout: timeout value in milliseconds
*
* When the client window is resized, call this API to set the new size in the
* underlying surface accordingly. This function will reallocate everything,
* so you'll have to redraw everything in the surface after this call.
* The surface will contain garbage after the resizing. So the notes of
* cairo_os2_surface_create() apply here, too.
*
* The timeout value specifies how long the function should wait on other parts
* of the program to release the buffers. It is necessary, because it can happen
* that Cairo is just drawing something into the surface while we want to
* destroy and recreate it.
*
* Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
* %CAIRO_STATUS_INVALID_SIZE for invalid sizes
* %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
* timeout happened before all the buffers were released
*
* Since: 1.4
**/
int
cairo_os2_surface_set_size (cairo_surface_t *surface,
int new_width,
int new_height,
int timeout)
{
cairo_os2_surface_t *local_os2_surface;
unsigned char *pchNewPixels;
int rc;
cairo_image_surface_t *pNewImageSurface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}
 
if ((new_width <= 0) ||
(new_height <= 0))
{
/* Invalid size! */
return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
}
 
/* Allocate memory for new stuffs */
pchNewPixels = (unsigned char *) _buffer_alloc (new_height, new_width, 4);
if (!pchNewPixels) {
/* Not enough memory for the pixels!
* Everything remains the same!
*/
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* Create image surface from new pixel array */
pNewImageSurface = (cairo_image_surface_t *)
cairo_image_surface_create_for_data (pchNewPixels,
CAIRO_FORMAT_ARGB32,
new_width, /* Width */
new_height, /* Height */
new_width * 4); /* Rowstride */
 
if (pNewImageSurface->base.status) {
/* Could not create image surface!
* Everything remains the same!
*/
_buffer_free (pchNewPixels);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* Okay, new memory allocated, so it's time to swap old buffers
* to new ones!
*/
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)!=NO_ERROR) {
/* Could not get mutex!
* Everything remains the same!
*/
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
_buffer_free (pchNewPixels);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* We have to make sure that we won't destroy a surface which
* is lent to some other code (Cairo is drawing into it)!
*/
while (local_os2_surface->pixel_array_lend_count > 0) {
ULONG ulPostCount;
DosResetEventSem (local_os2_surface->hev_pixel_array_came_back, &ulPostCount);
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
/* Wait for somebody to return the pixels! */
rc = DosWaitEventSem (local_os2_surface->hev_pixel_array_came_back, timeout);
if (rc != NO_ERROR) {
/* Either timeout or something wrong... Exit. */
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
_buffer_free (pchNewPixels);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
/* Okay, grab mutex and check counter again! */
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
!= NO_ERROR)
{
/* Could not get mutex!
* Everything remains the same!
*/
cairo_surface_destroy ((cairo_surface_t *) pNewImageSurface);
_buffer_free (pchNewPixels);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
/* Destroy old image surface */
cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
/* Destroy old pixel buffer */
_buffer_free (local_os2_surface->pixels);
/* Set new image surface */
local_os2_surface->image_surface = pNewImageSurface;
/* Set new pixel buffer */
local_os2_surface->pixels = pchNewPixels;
/* Change bitmap2 structure */
local_os2_surface->bitmap_info.cx = new_width;
local_os2_surface->bitmap_info.cy = new_height;
 
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_os2_surface_refresh_window:
* @surface: the cairo surface to refresh
* @hps_begin_paint: the presentation handle of the window to refresh
* @prcl_begin_paint_rect: the rectangle to redraw
*
* This function can be used to force a repaint of a given area of the client
* window. It should usually be called from the WM_PAINT processing of the
* window procedure. However, it can be called any time a given part of the
* window has to be updated.
*
* The HPS and RECTL to be passed can be taken from the usual WinBeginPaint call
* of the window procedure, but you can also get the HPS using WinGetPS, and you
* can assemble your own update rectangle by hand.
* If hps_begin_paint is %NULL, the function will use the HPS passed into
* cairo_os2_surface_create(). If @prcl_begin_paint_rect is %NULL, the function
* will query the current window size and repaint the whole window.
*
* Cairo assumes that if you set the HWND to the surface using
* cairo_os2_surface_set_hwnd(), this function will be called by the application
* every time it gets a WM_PAINT for that HWND. If the HWND is set in the
* surface, Cairo uses this function to handle dirty areas too.
*
* Since: 1.4
**/
void
cairo_os2_surface_refresh_window (cairo_surface_t *surface,
HPS hps_begin_paint,
PRECTL prcl_begin_paint_rect)
{
cairo_os2_surface_t *local_os2_surface;
RECTL rclTemp;
HPS hpsTemp = 0;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return;
}
 
/* If an HPS wasn't provided, see if we can get one. */
if (!hps_begin_paint) {
hps_begin_paint = local_os2_surface->hps_client_window;
if (!hps_begin_paint) {
if (local_os2_surface->hwnd_client_window) {
hpsTemp = WinGetPS(local_os2_surface->hwnd_client_window);
hps_begin_paint = hpsTemp;
}
/* No HPS & no way to get one, so exit */
if (!hps_begin_paint)
return;
}
}
 
if (prcl_begin_paint_rect == NULL) {
/* Update the whole window! */
rclTemp.xLeft = 0;
rclTemp.xRight = local_os2_surface->bitmap_info.cx;
rclTemp.yTop = local_os2_surface->bitmap_info.cy;
rclTemp.yBottom = 0;
} else {
/* Use the rectangle we got passed as parameter! */
rclTemp.xLeft = prcl_begin_paint_rect->xLeft;
rclTemp.xRight = prcl_begin_paint_rect->xRight;
rclTemp.yTop = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yBottom;
rclTemp.yBottom = local_os2_surface->bitmap_info.cy - prcl_begin_paint_rect->yTop ;
}
 
/* Get mutex, we'll work with the pixel array! */
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
!= NO_ERROR)
{
/* Could not get mutex! */
if (hpsTemp)
WinReleasePS(hpsTemp);
return;
}
 
if ((local_os2_surface->dirty_area_present) &&
(local_os2_surface->rcl_dirty_area.xLeft == rclTemp.xLeft) &&
(local_os2_surface->rcl_dirty_area.xRight == rclTemp.xRight) &&
(local_os2_surface->rcl_dirty_area.yTop == rclTemp.yTop) &&
(local_os2_surface->rcl_dirty_area.yBottom == rclTemp.yBottom))
{
/* Aha, this call was because of a dirty area, so in this case we
* have to blit the pixels from the screen to the surface!
*/
local_os2_surface->dirty_area_present = FALSE;
_cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
hps_begin_paint,
&rclTemp);
} else {
/* Okay, we have the surface, have the HPS
* (might be from WinBeginPaint () or from WinGetPS () )
* Now blit there the stuffs!
*/
_cairo_os2_surface_blit_pixels (local_os2_surface,
hps_begin_paint,
&rclTemp);
}
 
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
 
if (hpsTemp)
WinReleasePS(hpsTemp);
}
 
static cairo_status_t
_cairo_os2_surface_finish (void *abstract_surface)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) abstract_surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}
 
DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT);
 
/* Destroy old image surface */
cairo_surface_destroy ((cairo_surface_t *) (local_os2_surface->image_surface));
/* Destroy old pixel buffer */
_buffer_free (local_os2_surface->pixels);
DosCloseMutexSem (local_os2_surface->hmtx_use_private_fields);
DosCloseEventSem (local_os2_surface->hev_pixel_array_came_back);
 
/* The memory itself will be free'd by the cairo_surface_destroy ()
* who called us.
*/
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_os2_surface_set_hwnd:
* @surface: the cairo surface to associate with the window handle
* @hwnd_client_window: the window handle of the client window
*
* Sets window handle for surface; the caller retains ownership of the window.
* If Cairo wants to blit into the window because it is set to blit as the
* surface changes (see cairo_os2_surface_set_manual_window_refresh()), then
* there are two ways it can choose:
* If it knows the HWND of the surface, then it invalidates that area, so the
* application will get a WM_PAINT message and it can call
* cairo_os2_surface_refresh_window() to redraw that area. Otherwise cairo itself
* will use the HPS it got at surface creation time, and blit the pixels itself.
* It's also a solution, but experience shows that if this happens from a non-PM
* thread, then it can screw up PM internals.
*
* So, best solution is to set the HWND for the surface after the surface
* creation, so every blit will be done from application's message processing
* loop, which is the safest way to do.
*
* Since: 1.4
**/
void
cairo_os2_surface_set_hwnd (cairo_surface_t *surface,
HWND hwnd_client_window)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return;
}
 
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
!= NO_ERROR)
{
/* Could not get mutex! */
return;
}
 
local_os2_surface->hwnd_client_window = hwnd_client_window;
 
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
}
 
/**
* cairo_os2_surface_set_manual_window_refresh:
* @surface: the cairo surface to set the refresh mode for
* @manual_refresh: the switch for manual surface refresh
*
* This API can tell Cairo if it should show every change to this surface
* immediately in the window or if it should be cached and will only be visible
* once the user calls cairo_os2_surface_refresh_window() explicitly. If the
* HWND was not set in the cairo surface, then the HPS will be used to blit the
* graphics. Otherwise it will invalidate the given window region so the user
* will get the WM_PAINT message to redraw that area of the window.
*
* So, if you're only interested in displaying the final result after several
* drawing operations, you might get better performance if you put the surface
* into manual refresh mode by passing a true value to this function. Then call
* cairo_os2_surface_refresh() whenever desired.
*
* Since: 1.4
**/
void
cairo_os2_surface_set_manual_window_refresh (cairo_surface_t *surface,
cairo_bool_t manual_refresh)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return;
}
 
local_os2_surface->blit_as_changes = !manual_refresh;
}
 
/**
* cairo_os2_surface_get_manual_window_refresh:
* @surface: the cairo surface to query the refresh mode from
*
* This space left intentionally blank.
*
* Return value: current refresh mode of the surface (true by default)
*
* Since: 1.4
**/
cairo_bool_t
cairo_os2_surface_get_manual_window_refresh (cairo_surface_t *surface)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return FALSE;
}
 
return !(local_os2_surface->blit_as_changes);
}
 
/**
* cairo_os2_surface_get_hps:
* @surface: the cairo surface to be querued
* @hps: HPS currently associated with the surface (if any)
*
* This API retrieves the HPS associated with the surface.
*
* Return value: %CAIRO_STATUS_SUCCESS if the hps could be retrieved,
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
* %CAIRO_STATUS_NULL_POINTER if the hps argument is null
*
* Since: 1.10
**/
cairo_status_t
cairo_os2_surface_get_hps (cairo_surface_t *surface,
HPS *hps)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}
if (!hps)
{
return _cairo_error (CAIRO_STATUS_NULL_POINTER);
}
*hps = local_os2_surface->hps_client_window;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_os2_surface_set_hps:
* @surface: the cairo surface to associate with the HPS
* @hps: new HPS to be associated with the surface (the HPS may be null)
*
* This API replaces the HPS associated with the surface with a new one.
* The caller retains ownership of the HPS and must dispose of it after
* the surface has been destroyed or it has been replaced by another
* call to this function.
*
* Return value: %CAIRO_STATUS_SUCCESS if the hps could be replaced,
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
*
* Since: 1.10
**/
cairo_status_t
cairo_os2_surface_set_hps (cairo_surface_t *surface,
HPS hps)
{
cairo_os2_surface_t *local_os2_surface;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}
local_os2_surface->hps_client_window = hps;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_os2_surface_mark_dirty_rectangle (void *surface,
int x,
int y,
int width,
int height)
{
cairo_os2_surface_t *local_os2_surface;
RECTL rclToBlit;
 
local_os2_surface = (cairo_os2_surface_t *) surface;
if ((!local_os2_surface) ||
(local_os2_surface->base.backend != &cairo_os2_surface_backend))
{
/* Invalid parameter (wrong surface)! */
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}
 
/* Get mutex, we'll work with the pixel array! */
if (DosRequestMutexSem (local_os2_surface->hmtx_use_private_fields, SEM_INDEFINITE_WAIT)
!= NO_ERROR)
{
/* Could not get mutex! */
return CAIRO_STATUS_NO_MEMORY;
}
 
/* Check for defaults */
if (width < 0)
width = local_os2_surface->bitmap_info.cx;
if (height < 0)
height = local_os2_surface->bitmap_info.cy;
 
if (local_os2_surface->hwnd_client_window) {
/* We know the HWND, so let's invalidate the window region,
* so the application will redraw itself, using the
* cairo_os2_surface_refresh_window () API from its own PM thread.
* From that function we'll note that it's not a redraw but a
* dirty-rectangle deal stuff, so we'll handle the things from
* there.
*
* This is the safe method, which should be preferred every time.
*/
rclToBlit.xLeft = x;
rclToBlit.xRight = x + width; /* Noninclusive */
rclToBlit.yTop = local_os2_surface->bitmap_info.cy - (y);
rclToBlit.yBottom = local_os2_surface->bitmap_info.cy - (y + height); /* Noninclusive */
 
#if 0
if (local_os2_surface->dirty_area_present) {
/* Yikes, there is already a dirty area which should be
* cleaned up, but we'll overwrite it. Sorry.
* TODO: Something clever should be done here.
*/
}
#endif
 
/* Set up dirty area reminder stuff */
memcpy (&(local_os2_surface->rcl_dirty_area), &rclToBlit, sizeof (RECTL));
local_os2_surface->dirty_area_present = TRUE;
 
/* Invalidate window area */
WinInvalidateRect (local_os2_surface->hwnd_client_window,
&rclToBlit,
FALSE);
} else {
/* We don't know the HWND, so try to blit the pixels from here!
* Please note that it can be problematic if this is not the PM thread!
*
* It can cause internal PM stuffs to be scewed up, for some reason.
* Please always tell the HWND to the surface using the
* cairo_os2_surface_set_hwnd () API, and call cairo_os2_surface_refresh_window ()
* from your WM_PAINT, if it's possible!
*/
 
rclToBlit.xLeft = x;
rclToBlit.xRight = x + width; /* Noninclusive */
rclToBlit.yBottom = y;
rclToBlit.yTop = y + height; /* Noninclusive */
/* Now get the pixels from the screen! */
_cairo_os2_surface_get_pixels_from_screen (local_os2_surface,
local_os2_surface->hps_client_window,
&rclToBlit);
}
 
DosReleaseMutexSem (local_os2_surface->hmtx_use_private_fields);
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t cairo_os2_surface_backend = {
CAIRO_SURFACE_TYPE_OS2,
_cairo_os2_surface_finish,
_cairo_default_context_create,
 
NULL, /* create_similar */
NULL, /* create_similar_image */
_cairo_os2_surface_map_to_image,
_cairo_os2_surface_unmap_image,
 
_cairo_surface_default_source,
_cairo_os2_surface_acquire_source_image,
_cairo_os2_surface_release_source_image,
NULL, /* snapshot */
 
_cairo_os2_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
_cairo_os2_surface_mark_dirty_rectangle,
 
_cairo_surface_fallback_paint,
_cairo_surface_fallback_mask,
_cairo_surface_fallback_fill,
_cairo_surface_fallback_stroke,
NULL, /* fill/stroke */
_cairo_surface_fallback_glyphs,
};
/programs/develop/libraries/cairo/src/cairo-output-stream.c
33,10 → 33,13
* Kristian Høgsberg <krh@redhat.com>
*/
 
#define _BSD_SOURCE /* for snprintf() */
 
//#define _BSD_SOURCE /* for snprintf() */
#include "cairoint.h"
 
#include "cairo-output-stream-private.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
#include "cairo-compiler-private.h"
 
/programs/develop/libraries/cairo/src/cairo-paginated-private.h
154,6 → 154,9
cairo_private cairo_surface_t *
_cairo_paginated_surface_get_target (cairo_surface_t *surface);
 
cairo_private cairo_surface_t *
_cairo_paginated_surface_get_recording (cairo_surface_t *surface);
 
cairo_private cairo_bool_t
_cairo_surface_is_paginated (cairo_surface_t *surface);
 
/programs/develop/libraries/cairo/src/cairo-paginated-surface.c
49,6 → 49,8
#include "cairo-recording-surface-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-subsurface-inline.h"
 
static const cairo_surface_backend_t cairo_paginated_surface_backend;
 
147,10 → 149,20
assert (_cairo_surface_is_paginated (surface));
 
paginated_surface = (cairo_paginated_surface_t *) surface;
 
return paginated_surface->target;
}
 
cairo_surface_t *
_cairo_paginated_surface_get_recording (cairo_surface_t *surface)
{
cairo_paginated_surface_t *paginated_surface;
 
assert (_cairo_surface_is_paginated (surface));
 
paginated_surface = (cairo_paginated_surface_t *) surface;
return paginated_surface->recording_surface;
}
 
cairo_status_t
_cairo_paginated_surface_set_size (cairo_surface_t *surface,
int width,
230,6 → 242,14
return image;
}
 
static cairo_surface_t *
_cairo_paginated_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_paginated_surface_t *surface = abstract_surface;
return _cairo_surface_get_source (surface->target, extents);
}
 
static cairo_status_t
_cairo_paginated_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
279,7 → 299,7
cairo_status_t status;
cairo_surface_t *image;
cairo_surface_pattern_t pattern;
cairo_clip_t clip;
cairo_clip_t *clip;
 
x = rect->x;
y = rect->y;
304,15 → 324,11
* filtering (if possible) to avoid introducing potential artifacts. */
pattern.base.filter = CAIRO_FILTER_NEAREST;
 
_cairo_clip_init (&clip);
status = _cairo_clip_rectangle (&clip, rect);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
clip = _cairo_clip_intersect_rectangle (NULL, rect);
status = _cairo_surface_paint (surface->target,
CAIRO_OPERATOR_SOURCE,
&pattern.base, &clip);
}
 
_cairo_clip_fini (&clip);
&pattern.base, clip);
_cairo_clip_destroy (clip);
_cairo_pattern_fini (&pattern.base);
 
CLEANUP_IMAGE:
325,7 → 341,7
_paint_page (cairo_paginated_surface_t *surface)
{
cairo_surface_t *analysis;
cairo_status_t status;
cairo_int_status_t status;
cairo_bool_t has_supported, has_page_fallback, has_finegrained_fallback;
 
if (unlikely (surface->target->status))
339,12 → 355,11
CAIRO_PAGINATED_MODE_ANALYZE);
status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface,
analysis);
if (status || analysis->status) {
if (status == CAIRO_STATUS_SUCCESS)
status = analysis->status;
if (status)
goto FAIL;
}
 
assert (analysis->status == CAIRO_STATUS_SUCCESS);
 
if (surface->backend->set_bounding_box) {
cairo_box_t bbox;
 
542,7 → 557,7
_cairo_paginated_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
554,7 → 569,7
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
565,13 → 580,13
_cairo_paginated_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
586,11 → 601,11
_cairo_paginated_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
620,7 → 635,7
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
633,47 → 648,69
clip);
}
 
static const char **
_cairo_paginated_surface_get_supported_mime_types (void *abstract_surface)
{
cairo_paginated_surface_t *surface = abstract_surface;
 
if (surface->target->backend->get_supported_mime_types)
return surface->target->backend->get_supported_mime_types (surface->target);
 
return NULL;
}
 
static cairo_surface_t *
_cairo_paginated_surface_snapshot (void *abstract_other)
{
cairo_paginated_surface_t *other = abstract_other;
 
return _cairo_surface_snapshot (other->recording_surface);
return other->recording_surface->backend->snapshot (other->recording_surface);
}
 
static cairo_t *
_cairo_paginated_context_create (void *target)
{
cairo_paginated_surface_t *surface = target;
 
if (_cairo_surface_is_subsurface (&surface->base))
surface = (cairo_paginated_surface_t *)
_cairo_surface_subsurface_get_target (&surface->base);
 
return surface->recording_surface->backend->create_context (target);
}
 
static const cairo_surface_backend_t cairo_paginated_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
_cairo_paginated_surface_finish,
 
_cairo_paginated_context_create,
 
_cairo_paginated_surface_create_similar,
_cairo_paginated_surface_finish,
NULL, /* create simlar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_paginated_surface_source,
_cairo_paginated_surface_acquire_source_image,
_cairo_paginated_surface_release_source_image,
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
_cairo_paginated_surface_snapshot,
 
_cairo_paginated_surface_copy_page,
_cairo_paginated_surface_show_page,
 
_cairo_paginated_surface_get_extents,
NULL, /* old_show_glyphs */
_cairo_paginated_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
_cairo_paginated_surface_paint,
_cairo_paginated_surface_mask,
_cairo_paginated_surface_stroke,
_cairo_paginated_surface_fill,
NULL, /* fill_stroke */
NULL, /* show_glyphs */
_cairo_paginated_surface_snapshot,
NULL, /* is_similar */
NULL, /* fill_stroke */
NULL, /* create_solid_pattern_surface */
NULL, /* can_repaint_solid_pattern_surface */
_cairo_paginated_surface_has_show_text_glyphs,
_cairo_paginated_surface_show_text_glyphs
_cairo_paginated_surface_show_text_glyphs,
_cairo_paginated_surface_get_supported_mime_types,
};
/programs/develop/libraries/cairo/src/cairo-path-bounds.c
1,3 → 1,4
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
35,48 → 36,16
*/
 
#include "cairoint.h"
#include "cairo-box-inline.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
 
typedef struct cairo_path_bounder {
typedef struct _cairo_path_bounder {
cairo_point_t current_point;
cairo_bool_t has_initial_point;
cairo_bool_t has_point;
 
cairo_bool_t has_extents;
cairo_box_t extents;
} cairo_path_bounder_t;
 
static void
_cairo_path_bounder_init (cairo_path_bounder_t *bounder)
{
bounder->has_initial_point = FALSE;
bounder->has_point = FALSE;
}
 
static void
_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder,
const cairo_point_t *point)
{
if (bounder->has_point) {
if (point->x < bounder->extents.p1.x)
bounder->extents.p1.x = point->x;
 
if (point->y < bounder->extents.p1.y)
bounder->extents.p1.y = point->y;
 
if (point->x > bounder->extents.p2.x)
bounder->extents.p2.x = point->x;
 
if (point->y > bounder->extents.p2.y)
bounder->extents.p2.y = point->y;
} else {
bounder->extents.p1.x = point->x;
bounder->extents.p1.y = point->y;
bounder->extents.p2.x = point->x;
bounder->extents.p2.y = point->y;
bounder->has_point = TRUE;
}
}
 
static cairo_status_t
_cairo_path_bounder_move_to (void *closure,
const cairo_point_t *point)
84,8 → 53,14
cairo_path_bounder_t *bounder = closure;
 
bounder->current_point = *point;
bounder->has_initial_point = TRUE;
 
if (likely (bounder->has_extents)) {
_cairo_box_add_point (&bounder->extents, point);
} else {
bounder->has_extents = TRUE;
_cairo_box_set (&bounder->extents, point, point);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
95,13 → 70,8
{
cairo_path_bounder_t *bounder = closure;
 
if (bounder->has_initial_point) {
_cairo_path_bounder_add_point (bounder, &bounder->current_point);
bounder->has_initial_point = FALSE;
}
 
_cairo_path_bounder_add_point (bounder, point);
bounder->current_point = *point;
_cairo_box_add_point (&bounder->extents, point);
 
return CAIRO_STATUS_SUCCESS;
}
114,28 → 84,13
{
cairo_path_bounder_t *bounder = closure;
 
/* If the bbox of the control points is entirely inside, then we
* do not need to further evaluate the spline.
*/
if (! bounder->has_point ||
b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x ||
b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y ||
c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x ||
c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y ||
d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x ||
d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y)
{
return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder,
&bounder->current_point, b, c, d);
}
else
{
/* All control points are within the current extents. */
_cairo_box_add_curve_to (&bounder->extents,
&bounder->current_point,
b, c, d);
bounder->current_point = *d;
 
return CAIRO_STATUS_SUCCESS;
}
}
 
static cairo_status_t
_cairo_path_bounder_close_path (void *closure)
143,53 → 98,40
return CAIRO_STATUS_SUCCESS;
}
 
/* This computes the extents of all the points in the path, not those of
* the damage area (i.e it does not consider winding and it only inspects
* the control points of the curves, not the flattened path).
*/
void
_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
cairo_rectangle_int_t *extents)
cairo_bool_t
_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
cairo_box_t *extents)
{
if (path->extents.p1.x < path->extents.p2.x) {
_cairo_box_round_to_rectangle (&path->extents, extents);
} else {
extents->x = extents->y = 0;
extents->width = extents->height = 0;
}
}
 
/* A slightly better approximation than above - we actually decompose the
* Bezier, but we continue to ignore winding.
*/
void
_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
cairo_rectangle_int_t *extents)
{
cairo_path_bounder_t bounder;
cairo_status_t status;
 
if (! path->has_curve_to) {
bounder.extents = path->extents;
bounder.has_point = path->extents.p1.x < path->extents.p2.x;
} else {
_cairo_path_bounder_init (&bounder);
 
status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
bounder.has_extents = FALSE;
status = _cairo_path_fixed_interpret (path,
_cairo_path_bounder_move_to,
_cairo_path_bounder_line_to,
_cairo_path_bounder_curve_to,
_cairo_path_bounder_close_path,
&bounder);
assert (status == CAIRO_STATUS_SUCCESS);
assert (!status);
 
if (bounder.has_extents)
*extents = bounder.extents;
 
return bounder.has_extents;
}
 
if (bounder.has_point) {
_cairo_box_round_to_rectangle (&bounder.extents, extents);
} else {
extents->x = extents->y = 0;
extents->width = extents->height = 0;
void
_cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
cairo_rectangle_int_t *extents)
{
_cairo_path_fixed_approximate_fill_extents (path, extents);
}
 
void
_cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
cairo_rectangle_int_t *extents)
{
_cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents);
}
 
void
198,26 → 140,10
double tolerance,
cairo_rectangle_int_t *extents)
{
cairo_path_bounder_t bounder;
cairo_status_t status;
 
if (! path->has_curve_to) {
bounder.extents = path->extents;
bounder.has_point = path->extents.p1.x < path->extents.p2.x;
if (path->extents.p1.x < path->extents.p2.x &&
path->extents.p1.y < path->extents.p2.y) {
_cairo_box_round_to_rectangle (&path->extents, extents);
} else {
_cairo_path_bounder_init (&bounder);
 
status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD,
_cairo_path_bounder_move_to,
_cairo_path_bounder_line_to,
_cairo_path_bounder_close_path,
&bounder, tolerance);
assert (status == CAIRO_STATUS_SUCCESS);
}
 
if (bounder.has_point) {
_cairo_box_round_to_rectangle (&bounder.extents, extents);
} else {
extents->x = extents->y = 0;
extents->width = extents->height = 0;
}
230,65 → 156,19
const cairo_matrix_t *ctm,
cairo_rectangle_int_t *extents)
{
cairo_path_bounder_t bounder;
cairo_status_t status;
 
if (! path->has_curve_to) {
bounder.extents = path->extents;
 
/* include trailing move-to for degenerate segments */
if (path->has_last_move_point) {
const cairo_point_t *point = &path->last_move_point;
 
if (point->x < bounder.extents.p1.x)
bounder.extents.p1.x = point->x;
if (point->y < bounder.extents.p1.y)
bounder.extents.p1.y = point->y;
 
if (point->x > bounder.extents.p2.x)
bounder.extents.p2.x = point->x;
if (point->y > bounder.extents.p2.y)
bounder.extents.p2.y = point->y;
}
 
bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x;
bounder.has_initial_point = FALSE;
} else {
_cairo_path_bounder_init (&bounder);
 
status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
_cairo_path_bounder_move_to,
_cairo_path_bounder_line_to,
_cairo_path_bounder_curve_to,
_cairo_path_bounder_close_path,
&bounder);
assert (status == CAIRO_STATUS_SUCCESS);
}
 
if (bounder.has_point) {
if (path->has_extents) {
cairo_box_t box_extents;
double dx, dy;
 
_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
_cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
 
bounder.extents.p1.x -= _cairo_fixed_from_double (dx);
bounder.extents.p2.x += _cairo_fixed_from_double (dx);
bounder.extents.p1.y -= _cairo_fixed_from_double (dy);
bounder.extents.p2.y += _cairo_fixed_from_double (dy);
box_extents = path->extents;
box_extents.p1.x -= _cairo_fixed_from_double (dx);
box_extents.p1.y -= _cairo_fixed_from_double (dy);
box_extents.p2.x += _cairo_fixed_from_double (dx);
box_extents.p2.y += _cairo_fixed_from_double (dy);
 
_cairo_box_round_to_rectangle (&bounder.extents, extents);
} else if (bounder.has_initial_point) {
double dx, dy;
 
/* accommodate capping of degenerate paths */
 
_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
 
bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);
 
_cairo_box_round_to_rectangle (&bounder.extents, extents);
_cairo_box_round_to_rectangle (&box_extents, extents);
} else {
extents->x = extents->y = 0;
extents->width = extents->height = 0;
303,24 → 183,18
double tolerance,
cairo_rectangle_int_t *extents)
{
cairo_traps_t traps;
cairo_box_t bbox;
cairo_polygon_t polygon;
cairo_status_t status;
 
_cairo_traps_init (&traps);
 
status = _cairo_path_fixed_stroke_to_traps (path,
_cairo_polygon_init (&polygon, NULL, 0);
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm,
ctm_inverse,
ctm, ctm_inverse,
tolerance,
&traps);
&polygon);
_cairo_box_round_to_rectangle (&polygon.extents, extents);
_cairo_polygon_fini (&polygon);
 
_cairo_traps_extents (&traps, &bbox);
_cairo_traps_fini (&traps);
 
_cairo_box_round_to_rectangle (&bbox, extents);
 
return status;
}
 
328,26 → 202,6
_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
cairo_box_t *box)
{
cairo_path_bounder_t bounder;
cairo_status_t status;
 
if (! path->has_curve_to) {
*box = path->extents;
/* empty extents should still have an origin and should not
* be {0, 0, 0, 0} */
return path->extents.p1.x <= path->extents.p2.x;
return path->has_extents;
}
 
_cairo_path_bounder_init (&bounder);
 
status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
_cairo_path_bounder_move_to,
_cairo_path_bounder_line_to,
_cairo_path_bounder_curve_to,
_cairo_path_bounder_close_path,
&bounder);
assert (status == CAIRO_STATUS_SUCCESS);
 
*box = bounder.extents;
return bounder.has_point;
}
/programs/develop/libraries/cairo/src/cairo-path-fill.c
1,3 → 1,4
/* -*- 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
39,71 → 40,88
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-region-private.h"
#include "cairo-traps-private.h"
 
typedef struct cairo_filler {
cairo_polygon_t *polygon;
double tolerance;
cairo_polygon_t *polygon;
 
cairo_box_t limit;
cairo_bool_t has_limits;
 
cairo_point_t current_point;
cairo_point_t last_move_to;
} cairo_filler_t;
 
static void
_cairo_filler_init (cairo_filler_t *filler,
double tolerance,
cairo_polygon_t *polygon)
static cairo_status_t
_cairo_filler_line_to (void *closure,
const cairo_point_t *point)
{
filler->tolerance = tolerance;
filler->polygon = polygon;
}
cairo_filler_t *filler = closure;
cairo_status_t status;
 
static void
_cairo_filler_fini (cairo_filler_t *filler)
{
status = _cairo_polygon_add_external_edge (filler->polygon,
&filler->current_point,
point);
 
filler->current_point = *point;
 
return status;
}
 
static cairo_status_t
_cairo_filler_move_to (void *closure,
const cairo_point_t *point)
_cairo_filler_close (void *closure)
{
cairo_filler_t *filler = closure;
cairo_polygon_t *polygon = filler->polygon;
 
return _cairo_polygon_close (polygon) ||
_cairo_polygon_move_to (polygon, point);
/* close the subpath */
return _cairo_filler_line_to (closure, &filler->last_move_to);
}
 
static cairo_status_t
_cairo_filler_line_to (void *closure,
_cairo_filler_move_to (void *closure,
const cairo_point_t *point)
{
cairo_filler_t *filler = closure;
return _cairo_polygon_line_to (filler->polygon, point);
cairo_status_t status;
 
/* close current subpath */
status = _cairo_filler_close (closure);
if (unlikely (status))
return status;
 
/* make sure that the closure represents a degenerate path */
filler->current_point = *point;
filler->last_move_to = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_filler_curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
const cairo_point_t *p1,
const cairo_point_t *p2,
const cairo_point_t *p3)
{
cairo_filler_t *filler = closure;
cairo_spline_t spline;
 
if (filler->has_limits) {
if (! _cairo_spline_intersects (&filler->current_point, p1, p2, p3,
&filler->limit))
return _cairo_filler_line_to (filler, p3);
}
 
if (! _cairo_spline_init (&spline,
_cairo_filler_line_to, filler,
&filler->polygon->current_point, b, c, d))
(cairo_spline_add_point_func_t)_cairo_filler_line_to, filler,
&filler->current_point, p1, p2, p3))
{
return _cairo_filler_line_to (closure, d);
return _cairo_filler_line_to (closure, p3);
}
 
return _cairo_spline_decompose (&spline, filler->tolerance);
}
 
static cairo_status_t
_cairo_filler_close_path (void *closure)
{
cairo_filler_t *filler = closure;
return _cairo_polygon_close (filler->polygon);
}
 
cairo_status_t
_cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path,
double tolerance,
112,302 → 130,158
cairo_filler_t filler;
cairo_status_t status;
 
_cairo_filler_init (&filler, tolerance, polygon);
filler.polygon = polygon;
filler.tolerance = tolerance;
 
filler.has_limits = FALSE;
if (polygon->num_limits) {
filler.has_limits = TRUE;
filler.limit = polygon->limit;
}
 
/* make sure that the closure represents a degenerate path */
filler.current_point.x = 0;
filler.current_point.y = 0;
filler.last_move_to = filler.current_point;
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_filler_move_to,
_cairo_filler_line_to,
_cairo_filler_curve_to,
_cairo_filler_close_path,
_cairo_filler_close,
&filler);
if (unlikely (status))
return status;
 
status = _cairo_polygon_close (polygon);
_cairo_filler_fini (&filler);
 
return status;
return _cairo_filler_close (&filler);
}
 
cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps)
typedef struct cairo_filler_rectilinear_aligned {
cairo_polygon_t *polygon;
 
cairo_point_t current_point;
cairo_point_t last_move_to;
} cairo_filler_ra_t;
 
static cairo_status_t
_cairo_filler_ra_line_to (void *closure,
const cairo_point_t *point)
{
cairo_polygon_t polygon;
cairo_filler_ra_t *filler = closure;
cairo_status_t status;
cairo_point_t p;
 
if (path->is_empty_fill)
return CAIRO_STATUS_SUCCESS;
p.x = _cairo_fixed_round_down (point->x);
p.y = _cairo_fixed_round_down (point->y);
 
_cairo_polygon_init (&polygon);
if (traps->num_limits)
_cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
status = _cairo_polygon_add_external_edge (filler->polygon,
&filler->current_point,
&p);
 
status = _cairo_path_fixed_fill_to_polygon (path,
tolerance,
&polygon);
if (unlikely (status || polygon.num_edges == 0))
goto CLEANUP;
filler->current_point = p;
 
if (path->is_rectilinear) {
status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps,
&polygon,
fill_rule);
} else {
status = _cairo_bentley_ottmann_tessellate_polygon (traps,
&polygon,
fill_rule);
return status;
}
 
CLEANUP:
_cairo_polygon_fini (&polygon);
return status;
static cairo_status_t
_cairo_filler_ra_close (void *closure)
{
cairo_filler_ra_t *filler = closure;
return _cairo_filler_ra_line_to (closure, &filler->last_move_to);
}
 
static cairo_region_t *
_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_rectangle_int_t *extents)
static cairo_status_t
_cairo_filler_ra_move_to (void *closure,
const cairo_point_t *point)
{
cairo_box_t box;
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_filler_ra_t *filler = closure;
cairo_status_t status;
cairo_region_t *region;
cairo_point_t p;
 
/* first try to bypass fill-to-polygon */
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
&traps);
if (_cairo_status_is_error (status))
goto CLEANUP_TRAPS;
 
if (status == CAIRO_STATUS_SUCCESS) {
status = _cairo_traps_extract_region (&traps, &region);
goto CLEANUP_TRAPS;
}
 
/* path is not rectangular, try extracting clipped rectilinear edges */
_cairo_polygon_init (&polygon);
if (extents != NULL) {
_cairo_box_from_rectangle (&box, extents);
_cairo_polygon_limit (&polygon, &box, 1);
}
 
/* tolerance will be ignored as the path is rectilinear */
status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
/* close current subpath */
status = _cairo_filler_ra_close (closure);
if (unlikely (status))
goto CLEANUP_POLYGON;
return status;
 
if (polygon.num_edges == 0) {
region = cairo_region_create ();
} else {
status =
_cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
&polygon,
fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS))
status = _cairo_traps_extract_region (&traps, &region);
}
p.x = _cairo_fixed_round_down (point->x);
p.y = _cairo_fixed_round_down (point->y);
 
CLEANUP_POLYGON:
_cairo_polygon_fini (&polygon);
/* make sure that the closure represents a degenerate path */
filler->current_point = p;
filler->last_move_to = p;
 
CLEANUP_TRAPS:
_cairo_traps_fini (&traps);
 
if (unlikely (status))
region = _cairo_region_create_in_error (status);
 
return region;
return CAIRO_STATUS_SUCCESS;
}
 
/* This special-case filler supports only a path that describes a
* device-axis aligned rectangle. It exists to avoid the overhead of
* the general tessellator when drawing very common rectangles.
*
* If the path described anything but a device-axis aligned rectangle,
* this function will abort.
*/
cairo_region_t *
_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_rectangle_int_t *extents)
cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
cairo_antialias_t antialias,
cairo_polygon_t *polygon)
{
cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
cairo_box_t box;
cairo_region_t *region = NULL;
cairo_filler_ra_t filler;
cairo_status_t status;
 
assert (path->maybe_fill_region);
assert (! path->is_empty_fill);
if (antialias != CAIRO_ANTIALIAS_NONE)
return _cairo_path_fixed_fill_to_polygon (path, 0., polygon);
 
if (_cairo_path_fixed_is_box (path, &box)) {
rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
rectangle_stack[0].x;
rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) -
rectangle_stack[0].y;
if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents))
region = cairo_region_create ();
else
region = cairo_region_create_rectangle (&rectangle_stack[0]);
} else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
cairo_rectangle_int_t *rects = rectangle_stack;
cairo_path_fixed_iter_t iter;
int last_cw = -1;
int size = ARRAY_LENGTH (rectangle_stack);
int count = 0;
filler.polygon = polygon;
 
/* Support a series of rectangles as can be expected to describe a
* GdkRegion clip region during exposes.
*/
_cairo_path_fixed_iter_init (&iter, path);
while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
int cw = 0;
/* make sure that the closure represents a degenerate path */
filler.current_point.x = 0;
filler.current_point.y = 0;
filler.last_move_to = filler.current_point;
 
if (box.p1.x > box.p2.x) {
cairo_fixed_t t;
status = _cairo_path_fixed_interpret_flat (path,
_cairo_filler_ra_move_to,
_cairo_filler_ra_line_to,
_cairo_filler_ra_close,
&filler,
0.);
if (unlikely (status))
return status;
 
t = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = t;
 
cw = ! cw;
return _cairo_filler_ra_close (&filler);
}
 
if (box.p1.y > box.p2.y) {
cairo_fixed_t t;
 
t = box.p1.y;
box.p1.y = box.p2.y;
box.p2.y = t;
 
cw = ! cw;
}
 
if (last_cw < 0)
last_cw = cw;
else if (last_cw != cw)
goto TESSELLATE;
 
if (count == size) {
cairo_rectangle_int_t *new_rects;
 
size *= 4;
if (rects == rectangle_stack) {
new_rects = _cairo_malloc_ab (size,
sizeof (cairo_rectangle_int_t));
if (unlikely (new_rects == NULL)) {
/* XXX _cairo_region_nil */
break;
}
memcpy (new_rects, rects, sizeof (rectangle_stack));
} else {
new_rects = _cairo_realloc_ab (rects, size,
sizeof (cairo_rectangle_int_t));
if (unlikely (new_rects == NULL)) {
/* XXX _cairo_region_nil */
break;
}
}
rects = new_rects;
}
 
rects[count].x = _cairo_fixed_integer_part (box.p1.x);
rects[count].y = _cairo_fixed_integer_part (box.p1.y);
rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y;
if (_cairo_rectangle_intersect (&rects[count], extents))
count++;
}
 
if (_cairo_path_fixed_iter_at_end (&iter))
region = cairo_region_create_rectangles (rects, count);
 
TESSELLATE:
if (rects != rectangle_stack)
free (rects);
}
 
if (region == NULL) {
/* Hmm, complex polygon */
region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path,
fill_rule,
extents);
 
 
}
 
return region;
}
 
cairo_int_status_t
_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
cairo_status_t
_cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_traps_t *traps)
{
cairo_box_t box;
cairo_polygon_t polygon;
cairo_status_t status;
 
traps->is_rectilinear = TRUE;
traps->is_rectangular = TRUE;
if (_cairo_path_fixed_fill_is_empty (path))
return CAIRO_STATUS_SUCCESS;
 
if (_cairo_path_fixed_is_box (path, &box)) {
return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
} else {
cairo_path_fixed_iter_t iter;
_cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (unlikely (status || polygon.num_edges == 0))
goto CLEANUP;
 
_cairo_path_fixed_iter_init (&iter, path);
while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
if (box.p1.y > box.p2.y) {
cairo_fixed_t t;
status = _cairo_bentley_ottmann_tessellate_polygon (traps,
&polygon, fill_rule);
 
t = box.p1.y;
box.p1.y = box.p2.y;
box.p2.y = t;
 
t = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = t;
}
 
status = _cairo_traps_tessellate_rectangle (traps,
&box.p1, &box.p2);
if (unlikely (status)) {
_cairo_traps_clear (traps);
CLEANUP:
_cairo_polygon_fini (&polygon);
return status;
}
}
 
if (_cairo_path_fixed_iter_at_end (&iter))
return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule);
 
_cairo_traps_clear (traps);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
static cairo_status_t
_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_boxes_t *boxes)
{
cairo_polygon_t polygon;
cairo_status_t status;
 
_cairo_polygon_init (&polygon);
if (boxes->num_limits) {
_cairo_polygon_limit (&polygon, boxes->limits, boxes->num_limits);
_cairo_polygon_init (&polygon, boxes->limits, boxes->num_limits);
boxes->num_limits = 0;
}
 
/* tolerance will be ignored as the path is rectilinear */
status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
status = _cairo_path_fixed_fill_rectilinear_to_polygon (path, antialias, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status =
_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (&polygon,
423,6 → 297,7
cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_boxes_t *boxes)
{
cairo_path_fixed_iter_t iter;
430,7 → 305,7
cairo_box_t box;
 
if (_cairo_path_fixed_is_box (path, &box))
return _cairo_boxes_add (boxes, &box);
return _cairo_boxes_add (boxes, antialias, &box);
 
_cairo_path_fixed_iter_init (&iter, path);
while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
449,7 → 324,7
box.p2.x = t;
}
 
status = _cairo_boxes_add (boxes, &box);
status = _cairo_boxes_add (boxes, antialias, &box);
if (unlikely (status))
return status;
}
461,5 → 336,6
_cairo_boxes_clear (boxes);
return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path,
fill_rule,
antialias,
boxes);
}
/programs/develop/libraries/cairo/src/cairo-path-fixed-private.h
59,6 → 59,20
#define CAIRO_PATH_BUF_SIZE ((512 - sizeof (cairo_path_buf_t)) \
/ (2 * sizeof (cairo_point_t) + sizeof (cairo_path_op_t)))
 
#define cairo_path_head(path__) (&(path__)->buf.base)
#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
 
#define cairo_path_buf_next(pos__) \
cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
#define cairo_path_buf_prev(pos__) \
cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
 
#define cairo_path_foreach_buf_start(pos__, path__) \
pos__ = cairo_path_head (path__); do
#define cairo_path_foreach_buf_end(pos__, path__) \
while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
 
 
typedef struct _cairo_path_buf {
cairo_list_t link;
unsigned int num_ops;
77,15 → 91,24
cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE];
} cairo_path_buf_fixed_t;
 
/*
NOTES:
has_curve_to => !stroke_is_rectilinear
fill_is_rectilinear => stroke_is_rectilinear
fill_is_empty => fill_is_rectilinear
fill_maybe_region => fill_is_rectilinear
*/
struct _cairo_path_fixed {
cairo_point_t last_move_point;
cairo_point_t current_point;
unsigned int has_current_point : 1;
unsigned int has_last_move_point : 1;
unsigned int needs_move_to : 1;
unsigned int has_extents : 1;
unsigned int has_curve_to : 1;
unsigned int is_rectilinear : 1;
unsigned int maybe_fill_region : 1;
unsigned int is_empty_fill : 1;
unsigned int stroke_is_rectilinear : 1;
unsigned int fill_is_rectilinear : 1;
unsigned int fill_maybe_region : 1;
unsigned int fill_is_empty : 1;
 
cairo_box_t extents;
 
100,7 → 123,6
cairo_private cairo_status_t
_cairo_path_fixed_append (cairo_path_fixed_t *path,
const cairo_path_fixed_t *other,
cairo_direction_t dir,
cairo_fixed_t tx,
cairo_fixed_t ty);
 
135,16 → 157,16
static inline cairo_bool_t
_cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
{
return path->is_empty_fill;
return path->fill_is_empty;
}
 
static inline cairo_bool_t
_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path)
_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
{
if (! path->is_rectilinear)
if (! path->fill_is_rectilinear)
return 0;
 
if (! path->has_current_point)
if (! path->has_current_point || path->needs_move_to)
return 1;
 
/* check whether the implicit close preserves the rectilinear property */
153,13 → 175,32
}
 
static inline cairo_bool_t
_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path)
_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
{
#if WATCH_PATH
fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n",
path->maybe_fill_region ? "true" : "false");
#endif
return path->maybe_fill_region;
return path->stroke_is_rectilinear;
}
 
static inline cairo_bool_t
_cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
{
if (! path->fill_maybe_region)
return 0;
 
if (! path->has_current_point || path->needs_move_to)
return 1;
 
/* check whether the implicit close preserves the rectilinear property
* (the integer point property is automatically preserved)
*/
return path->current_point.x == path->last_move_point.x ||
path->current_point.y == path->last_move_point.y;
}
 
cairo_private cairo_bool_t
_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
cairo_box_t *box);
 
cairo_private cairo_bool_t
_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path);
 
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-path-fixed.c
38,7 → 38,9
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
 
67,19 → 69,6
const cairo_point_t *points,
int num_points);
 
#define cairo_path_head(path__) (&(path__)->buf.base)
#define cairo_path_tail(path__) cairo_path_buf_prev (cairo_path_head (path__))
 
#define cairo_path_buf_next(pos__) \
cairo_list_entry ((pos__)->link.next, cairo_path_buf_t, link)
#define cairo_path_buf_prev(pos__) \
cairo_list_entry ((pos__)->link.prev, cairo_path_buf_t, link)
 
#define cairo_path_foreach_buf_start(pos__, path__) \
pos__ = cairo_path_head (path__); do
#define cairo_path_foreach_buf_end(pos__, path__) \
while ((pos__ = cairo_path_buf_next (pos__)) != cairo_path_head (path__))
 
void
_cairo_path_fixed_init (cairo_path_fixed_t *path)
{
97,15 → 86,18
path->current_point.x = 0;
path->current_point.y = 0;
path->last_move_point = path->current_point;
path->has_last_move_point = FALSE;
 
path->has_current_point = FALSE;
path->needs_move_to = TRUE;
path->has_extents = FALSE;
path->has_curve_to = FALSE;
path->is_rectilinear = TRUE;
path->maybe_fill_region = TRUE;
path->is_empty_fill = TRUE;
path->stroke_is_rectilinear = TRUE;
path->fill_is_rectilinear = TRUE;
path->fill_maybe_region = TRUE;
path->fill_is_empty = TRUE;
 
path->extents.p1.x = path->extents.p1.y = INT_MAX;
path->extents.p2.x = path->extents.p2.y = INT_MIN;
path->extents.p1.x = path->extents.p1.y = 0;
path->extents.p2.x = path->extents.p2.y = 0;
}
 
cairo_status_t
126,12 → 118,15
 
path->current_point = other->current_point;
path->last_move_point = other->last_move_point;
path->has_last_move_point = other->has_last_move_point;
 
path->has_current_point = other->has_current_point;
path->needs_move_to = other->needs_move_to;
path->has_extents = other->has_extents;
path->has_curve_to = other->has_curve_to;
path->is_rectilinear = other->is_rectilinear;
path->maybe_fill_region = other->maybe_fill_region;
path->is_empty_fill = other->is_empty_fill;
path->stroke_is_rectilinear = other->stroke_is_rectilinear;
path->fill_is_rectilinear = other->fill_is_rectilinear;
path->fill_maybe_region = other->fill_maybe_region;
path->fill_is_empty = other->fill_is_empty;
 
path->extents = other->extents;
 
182,24 → 177,24
{
unsigned long hash = _CAIRO_HASH_INIT_VALUE;
const cairo_path_buf_t *buf;
int num_points, num_ops;
unsigned int count;
 
hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
 
num_ops = num_points = 0;
count = 0;
cairo_path_foreach_buf_start (buf, path) {
hash = _cairo_hash_bytes (hash, buf->op,
buf->num_ops * sizeof (buf->op[0]));
count += buf->num_ops;
} cairo_path_foreach_buf_end (buf, path);
hash = _cairo_hash_bytes (hash, &count, sizeof (count));
 
count = 0;
cairo_path_foreach_buf_start (buf, path) {
hash = _cairo_hash_bytes (hash, buf->points,
buf->num_points * sizeof (buf->points[0]));
 
num_ops += buf->num_ops;
num_points += buf->num_points;
count += buf->num_points;
} cairo_path_foreach_buf_end (buf, path);
hash = _cairo_hash_bytes (hash, &count, sizeof (count));
 
hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
 
return hash;
}
 
233,10 → 228,7
return TRUE;
 
/* use the flags to quickly differentiate based on contents */
if (a->is_empty_fill != b->is_empty_fill ||
a->has_curve_to != b->has_curve_to ||
a->maybe_fill_region != b->maybe_fill_region ||
a->is_rectilinear != b->is_rectilinear)
if (a->has_curve_to != b->has_curve_to)
{
return FALSE;
}
366,79 → 358,98
}
 
static cairo_path_op_t
_cairo_path_last_op (cairo_path_fixed_t *path)
_cairo_path_fixed_last_op (cairo_path_fixed_t *path)
{
cairo_path_buf_t *buf;
 
buf = cairo_path_tail (path);
if (buf->num_ops == 0)
return -1;
assert (buf->num_ops != 0);
 
return buf->op[buf->num_ops - 1];
}
 
static inline void
_cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
const cairo_point_t *point)
static inline const cairo_point_t *
_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path)
{
if (point->x < path->extents.p1.x)
path->extents.p1.x = point->x;
if (point->y < path->extents.p1.y)
path->extents.p1.y = point->y;
cairo_path_buf_t *buf;
 
if (point->x > path->extents.p2.x)
path->extents.p2.x = point->x;
if (point->y > path->extents.p2.y)
path->extents.p2.y = point->y;
buf = cairo_path_tail (path);
if (likely (buf->num_points >= 2)) {
return &buf->points[buf->num_points - 2];
} else {
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
 
assert (prev_buf->num_points >= 2 - buf->num_points);
return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
}
}
 
static void
_cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path)
{
cairo_path_buf_t *buf;
 
assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO);
 
buf = cairo_path_tail (path);
buf->num_points--;
buf->num_ops--;
}
 
cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y)
{
cairo_status_t status;
cairo_point_t point;
_cairo_path_fixed_new_sub_path (path);
 
point.x = x;
point.y = y;
path->has_current_point = TRUE;
path->current_point.x = x;
path->current_point.y = y;
path->last_move_point = path->current_point;
 
/* If the previous op was also a MOVE_TO, then just change its
* point rather than adding a new op. */
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
cairo_path_buf_t *buf;
return CAIRO_STATUS_SUCCESS;
}
 
buf = cairo_path_tail (path);
buf->points[buf->num_points - 1] = point;
static cairo_status_t
_cairo_path_fixed_move_to_apply (cairo_path_fixed_t *path)
{
if (likely (! path->needs_move_to))
return CAIRO_STATUS_SUCCESS;
 
path->needs_move_to = FALSE;
 
if (path->has_extents) {
_cairo_box_add_point (&path->extents, &path->current_point);
} else {
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
if (unlikely (status))
return status;
_cairo_box_set (&path->extents, &path->current_point, &path->current_point);
path->has_extents = TRUE;
}
 
if (path->has_current_point && path->is_rectilinear) {
/* a move-to is first an implicit close */
path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
path->current_point.y == path->last_move_point.y;
path->maybe_fill_region &= path->is_rectilinear;
if (path->fill_maybe_region) {
path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) &&
_cairo_fixed_is_integer (path->current_point.y);
}
if (path->maybe_fill_region) {
path->maybe_fill_region =
_cairo_fixed_is_integer (path->last_move_point.x) &&
_cairo_fixed_is_integer (path->last_move_point.y);
}
}
 
path->current_point = point;
path->last_move_point = point;
path->has_last_move_point = TRUE;
path->has_current_point = TRUE;
path->last_move_point = path->current_point;
 
return CAIRO_STATUS_SUCCESS;
return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1);
}
 
void
_cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
{
if (! path->needs_move_to) {
/* If the current subpath doesn't need_move_to, it contains at least one command */
if (path->fill_is_rectilinear) {
/* Implicitly close for fill */
path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x ||
path->current_point.y == path->last_move_point.y;
path->fill_maybe_region &= path->fill_is_rectilinear;
}
path->needs_move_to = TRUE;
}
 
path->has_current_point = FALSE;
}
 
475,12 → 486,16
if (! path->has_current_point)
return _cairo_path_fixed_move_to (path, point.x, point.y);
 
status = _cairo_path_fixed_move_to_apply (path);
if (unlikely (status))
return status;
 
/* If the previous op was but the initial MOVE_TO and this segment
* is degenerate, then we can simply skip this point. Note that
* a move-to followed by a degenerate line-to is a valid path for
* stroking, but at all other times is simply a degenerate segment.
*/
if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
if (x == path->current_point.x && y == path->current_point.y)
return CAIRO_STATUS_SUCCESS;
}
488,22 → 503,13
/* If the previous op was also a LINE_TO with the same gradient,
* then just change its end-point rather than adding a new op.
*/
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
cairo_path_buf_t *buf;
if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
const cairo_point_t *p;
 
buf = cairo_path_tail (path);
if (likely (buf->num_points >= 2)) {
p = &buf->points[buf->num_points-2];
} else {
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
}
 
p = _cairo_path_fixed_penultimate_point (path);
if (p->x == path->current_point.x && p->y == path->current_point.y) {
/* previous line element was degenerate, replace */
buf->points[buf->num_points - 1] = point;
goto FLAGS;
_cairo_path_fixed_drop_line_to (path);
} else {
cairo_slope_t prev, self;
 
513,39 → 519,37
/* cannot trim anti-parallel segments whilst stroking */
! _cairo_slope_backwards (&prev, &self))
{
buf->points[buf->num_points - 1] = point;
goto FLAGS;
_cairo_path_fixed_drop_line_to (path);
/* In this case the flags might be more restrictive than
* what we actually need.
* When changing the flags definition we should check if
* changing the line_to point can affect them.
*/
}
}
}
 
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
if (unlikely (status))
return status;
 
FLAGS:
if (path->is_rectilinear) {
path->is_rectilinear = path->current_point.x == x ||
if (path->stroke_is_rectilinear) {
path->stroke_is_rectilinear = path->current_point.x == x ||
path->current_point.y == y;
path->maybe_fill_region &= path->is_rectilinear;
}
if (path->maybe_fill_region) {
path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
path->fill_is_rectilinear &= path->stroke_is_rectilinear;
path->fill_maybe_region &= path->fill_is_rectilinear;
if (path->fill_maybe_region) {
path->fill_maybe_region = _cairo_fixed_is_integer (x) &&
_cairo_fixed_is_integer (y);
}
if (path->is_empty_fill) {
path->is_empty_fill = path->current_point.x == x &&
if (path->fill_is_empty) {
path->fill_is_empty = path->current_point.x == x &&
path->current_point.y == y;
}
}
 
path->current_point = point;
if (path->has_last_move_point) {
_cairo_path_fixed_extents_add (path, &path->last_move_point);
path->has_last_move_point = FALSE;
 
_cairo_box_add_point (&path->extents, &point);
 
return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
}
_cairo_path_fixed_extents_add (path, &point);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
569,39 → 573,56
cairo_status_t status;
cairo_point_t point[3];
 
/* If this curves does not move, replace it with a line-to.
* This frequently happens with rounded-rectangles and r==0.
*/
if (path->current_point.x == x2 && path->current_point.y == y2) {
if (x1 == x2 && x0 == x2 && y1 == y2 && y0 == y2)
return _cairo_path_fixed_line_to (path, x2, y2);
 
/* We may want to check for the absence of a cusp, in which case
* we can also replace the curve-to with a line-to.
*/
}
 
/* make sure subpaths are started properly */
if (! path->has_current_point) {
status = _cairo_path_fixed_move_to (path, x0, y0);
assert (status == CAIRO_STATUS_SUCCESS);
}
 
status = _cairo_path_fixed_move_to_apply (path);
if (unlikely (status))
return status;
 
/* If the previous op was a degenerate LINE_TO, drop it. */
if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
const cairo_point_t *p;
 
p = _cairo_path_fixed_penultimate_point (path);
if (p->x == path->current_point.x && p->y == path->current_point.y) {
/* previous line element was degenerate, replace */
_cairo_path_fixed_drop_line_to (path);
}
}
 
point[0].x = x0; point[0].y = y0;
point[1].x = x1; point[1].y = y1;
point[2].x = x2; point[2].y = y2;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
if (unlikely (status))
return status;
 
_cairo_box_add_curve_to (&path->extents, &path->current_point,
&point[0], &point[1], &point[2]);
 
path->current_point = point[2];
path->has_current_point = TRUE;
path->is_empty_fill = FALSE;
path->has_curve_to = TRUE;
path->is_rectilinear = FALSE;
path->maybe_fill_region = FALSE;
path->stroke_is_rectilinear = FALSE;
path->fill_is_rectilinear = FALSE;
path->fill_maybe_region = FALSE;
path->fill_is_empty = FALSE;
 
/* coarse bounds */
if (path->has_last_move_point) {
_cairo_path_fixed_extents_add (path, &path->last_move_point);
path->has_last_move_point = FALSE;
return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
}
_cairo_path_fixed_extents_add (path, &point[0]);
_cairo_path_fixed_extents_add (path, &point[1]);
_cairo_path_fixed_extents_add (path, &point[2]);
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
cairo_fixed_t dx0, cairo_fixed_t dy0,
630,35 → 651,28
if (! path->has_current_point)
return CAIRO_STATUS_SUCCESS;
 
/* If the previous op was also a LINE_TO back to the start, discard it */
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
if (path->current_point.x == path->last_move_point.x &&
path->current_point.y == path->last_move_point.y)
{
cairo_path_buf_t *buf;
cairo_point_t *p;
/*
* Add a line_to, to compute flags and solve any degeneracy.
* It will be removed later (if it was actually added).
*/
status = _cairo_path_fixed_line_to (path,
path->last_move_point.x,
path->last_move_point.y);
if (unlikely (status))
return status;
 
buf = cairo_path_tail (path);
if (likely (buf->num_points >= 2)) {
p = &buf->points[buf->num_points-2];
} else {
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
}
/*
* If the command used to close the path is a line_to, drop it.
* We must check that last command is actually a line_to,
* because the path could have been closed with a curve_to (and
* the previous line_to not added as it would be degenerate).
*/
if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO)
_cairo_path_fixed_drop_line_to (path);
 
path->current_point = *p;
buf->num_ops--;
buf->num_points--;
}
}
path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */
 
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
if (unlikely (status))
return status;
 
return _cairo_path_fixed_move_to (path,
path->last_move_point.x,
path->last_move_point.y);
return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
}
 
cairo_bool_t
714,9 → 728,20
}
len += snprintf (buf + len, sizeof (buf), "]");
 
#define STRINGIFYFLAG(x) (path->x ? #x " " : "")
fprintf (stderr,
"_cairo_path_fixed_add (%s, %s)\n",
op_str[(int) op], buf);
"_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n",
op_str[(int) op], buf,
STRINGIFYFLAG(has_current_point),
STRINGIFYFLAG(needs_move_to),
STRINGIFYFLAG(has_extents),
STRINGIFYFLAG(has_curve_to),
STRINGIFYFLAG(stroke_is_rectilinear),
STRINGIFYFLAG(fill_is_rectilinear),
STRINGIFYFLAG(fill_is_empty),
STRINGIFYFLAG(fill_maybe_region)
);
#undef STRINGIFYFLAG
}
 
_cairo_path_buf_add_op (buf, op);
772,6 → 797,9
const cairo_point_t *points,
int num_points)
{
if (num_points == 0)
return;
 
memcpy (buf->points + buf->num_points,
points,
sizeof (points[0]) * num_points);
780,7 → 808,6
 
cairo_status_t
_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
cairo_direction_t dir,
cairo_path_fixed_move_to_func_t *move_to,
cairo_path_fixed_line_to_func_t *line_to,
cairo_path_fixed_curve_to_func_t *curve_to,
787,47 → 814,26
cairo_path_fixed_close_path_func_t *close_path,
void *closure)
{
const uint8_t num_args[] = {
1, /* cairo_path_move_to */
1, /* cairo_path_op_line_to */
3, /* cairo_path_op_curve_to */
0, /* cairo_path_op_close_path */
};
const cairo_path_buf_t *buf;
cairo_status_t status;
const cairo_path_buf_t *buf, *first;
cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
int step = forward ? 1 : -1;
 
buf = first = forward ? cairo_path_head (path) : cairo_path_tail (path);
do {
cairo_point_t *points;
int start, stop, i;
cairo_path_foreach_buf_start (buf, path) {
const cairo_point_t *points = buf->points;
unsigned int i;
 
if (forward) {
start = 0;
stop = buf->num_ops;
points = buf->points;
} else {
start = buf->num_ops - 1;
stop = -1;
points = buf->points + buf->num_points;
}
 
for (i = start; i != stop; i += step) {
cairo_path_op_t op = buf->op[i];
 
if (! forward)
points -= num_args[(int) op];
 
switch (op) {
for (i = 0; i < buf->num_ops; i++) {
switch (buf->op[i]) {
case CAIRO_PATH_OP_MOVE_TO:
status = (*move_to) (closure, &points[0]);
points += 1;
break;
case CAIRO_PATH_OP_LINE_TO:
status = (*line_to) (closure, &points[0]);
points += 1;
break;
case CAIRO_PATH_OP_CURVE_TO:
status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
points += 3;
break;
default:
ASSERT_NOT_REACHED;
835,13 → 841,11
status = (*close_path) (closure);
break;
}
 
if (unlikely (status))
return status;
 
if (forward)
points += num_args[(int) op];
}
} while ((buf = forward ? cairo_path_buf_next (buf) : cairo_path_buf_prev (buf)) != first);
} cairo_path_foreach_buf_end (buf, path);
 
return CAIRO_STATUS_SUCCESS;
}
901,7 → 905,6
cairo_status_t
_cairo_path_fixed_append (cairo_path_fixed_t *path,
const cairo_path_fixed_t *other,
cairo_direction_t dir,
cairo_fixed_t tx,
cairo_fixed_t ty)
{
911,7 → 914,7
closure.offset.x = tx;
closure.offset.y = ty;
 
return _cairo_path_fixed_interpret (other, dir,
return _cairo_path_fixed_interpret (other,
_append_move_to,
_append_line_to,
_append_curve_to,
929,13 → 932,18
cairo_path_buf_t *buf;
unsigned int i;
 
if (path->maybe_fill_region) {
path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
_cairo_fixed_is_integer (offy) &&
_cairo_fixed_is_integer (scalex) &&
_cairo_fixed_is_integer (scaley);
if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) {
_cairo_path_fixed_translate (path, offx, offy);
return;
}
 
path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx;
path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy;
path->current_point.x = _cairo_fixed_mul (scalex, path->current_point.x) + offx;
path->current_point.y = _cairo_fixed_mul (scaley, path->current_point.y) + offy;
 
path->fill_maybe_region = TRUE;
 
cairo_path_foreach_buf_start (buf, path) {
for (i = 0; i < buf->num_points; i++) {
if (scalex != CAIRO_FIXED_ONE)
945,15 → 953,32
if (scaley != CAIRO_FIXED_ONE)
buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
buf->points[i].y += offy;
 
if (path->fill_maybe_region) {
path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
_cairo_fixed_is_integer (buf->points[i].y);
}
}
} cairo_path_foreach_buf_end (buf, path);
 
path->fill_maybe_region &= path->fill_is_rectilinear;
 
path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx;
path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx;
if (scalex < 0) {
cairo_fixed_t t = path->extents.p1.x;
path->extents.p1.x = path->extents.p2.x;
path->extents.p2.x = t;
}
 
path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy;
path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy;
if (scaley < 0) {
cairo_fixed_t t = path->extents.p1.y;
path->extents.p1.y = path->extents.p2.y;
path->extents.p2.y = t;
}
}
 
void
_cairo_path_fixed_translate (cairo_path_fixed_t *path,
966,24 → 991,27
if (offx == 0 && offy == 0)
return;
 
if (path->maybe_fill_region &&
! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
{
path->maybe_fill_region = FALSE;
}
 
path->last_move_point.x += offx;
path->last_move_point.y += offy;
path->current_point.x += offx;
path->current_point.y += offy;
 
path->fill_maybe_region = TRUE;
 
cairo_path_foreach_buf_start (buf, path) {
for (i = 0; i < buf->num_points; i++) {
buf->points[i].x += offx;
buf->points[i].y += offy;
 
if (path->fill_maybe_region) {
path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
_cairo_fixed_is_integer (buf->points[i].y);
}
}
} cairo_path_foreach_buf_end (buf, path);
 
path->fill_maybe_region &= path->fill_is_rectilinear;
 
path->extents.p1.x += offx;
path->extents.p1.y += offy;
path->extents.p2.x += offx;
990,6 → 1018,20
path->extents.p2.y += offy;
}
 
 
static inline void
_cairo_path_fixed_transform_point (cairo_point_t *p,
const cairo_matrix_t *matrix)
{
double dx, dy;
 
dx = _cairo_fixed_to_double (p->x);
dy = _cairo_fixed_to_double (p->y);
cairo_matrix_transform_point (matrix, &dx, &dy);
p->x = _cairo_fixed_from_double (dx);
p->y = _cairo_fixed_from_double (dy);
}
 
/**
* _cairo_path_fixed_transform:
* @path: a #cairo_path_fixed_t to be transformed
1003,80 → 1045,58
_cairo_path_fixed_transform (cairo_path_fixed_t *path,
const cairo_matrix_t *matrix)
{
cairo_box_t extents;
cairo_point_t point;
cairo_path_buf_t *buf;
unsigned int i;
double dx, dy;
 
/* XXX current_point, last_move_to */
 
if (matrix->yx == 0.0 && matrix->xy == 0.0) {
/* Fast path for the common case of scale+transform */
if (matrix->xx == 1. && matrix->yy == 1.) {
_cairo_path_fixed_translate (path,
_cairo_fixed_from_double (matrix->x0),
_cairo_fixed_from_double (matrix->y0));
} else {
_cairo_path_fixed_offset_and_scale (path,
_cairo_fixed_from_double (matrix->x0),
_cairo_fixed_from_double (matrix->y0),
_cairo_fixed_from_double (matrix->xx),
_cairo_fixed_from_double (matrix->yy));
}
return;
}
 
path->extents.p1.x = path->extents.p1.y = INT_MAX;
path->extents.p2.x = path->extents.p2.y = INT_MIN;
path->maybe_fill_region = FALSE;
cairo_path_foreach_buf_start (buf, path) {
for (i = 0; i < buf->num_points; i++) {
dx = _cairo_fixed_to_double (buf->points[i].x);
dy = _cairo_fixed_to_double (buf->points[i].y);
_cairo_path_fixed_transform_point (&path->last_move_point, matrix);
_cairo_path_fixed_transform_point (&path->current_point, matrix);
 
cairo_matrix_transform_point (matrix, &dx, &dy);
buf = cairo_path_head (path);
if (buf->num_points == 0)
return;
 
buf->points[i].x = _cairo_fixed_from_double (dx);
buf->points[i].y = _cairo_fixed_from_double (dy);
extents = path->extents;
point = buf->points[0];
_cairo_path_fixed_transform_point (&point, matrix);
_cairo_box_set (&path->extents, &point, &point);
 
/* XXX need to eliminate surplus move-to's? */
_cairo_path_fixed_extents_add (path, &buf->points[i]);
cairo_path_foreach_buf_start (buf, path) {
for (i = 0; i < buf->num_points; i++) {
_cairo_path_fixed_transform_point (&buf->points[i], matrix);
_cairo_box_add_point (&path->extents, &buf->points[i]);
}
} cairo_path_foreach_buf_end (buf, path);
}
 
cairo_bool_t
_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
const cairo_path_fixed_t *other)
{
const cairo_path_buf_t *path_buf, *other_buf;
if (path->has_curve_to) {
cairo_bool_t is_tight;
 
if (path->current_point.x != other->current_point.x ||
path->current_point.y != other->current_point.y ||
path->has_current_point != other->has_current_point ||
path->has_curve_to != other->has_curve_to ||
path->is_rectilinear != other->is_rectilinear ||
path->maybe_fill_region != other->maybe_fill_region ||
path->last_move_point.x != other->last_move_point.x ||
path->last_move_point.y != other->last_move_point.y)
{
return FALSE;
}
_cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight);
if (!is_tight) {
cairo_bool_t has_extents;
 
other_buf = cairo_path_head (other);
cairo_path_foreach_buf_start (path_buf, path) {
if (path_buf->num_ops != other_buf->num_ops ||
path_buf->num_points != other_buf->num_points ||
memcmp (path_buf->op, other_buf->op,
sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
memcmp (path_buf->points, other_buf->points,
sizeof (cairo_point_t) * path_buf->num_points) != 0)
{
return FALSE;
has_extents = _cairo_path_bounder_extents (path, &extents);
assert (has_extents);
}
other_buf = cairo_path_buf_next (other_buf);
} cairo_path_foreach_buf_end (path_buf, path);
path->extents = extents;
}
 
return TRUE;
/* flags might become more strict than needed */
path->stroke_is_rectilinear = FALSE;
path->fill_is_rectilinear = FALSE;
path->fill_is_empty = FALSE;
path->fill_maybe_region = FALSE;
}
 
/* Closure for path flattening */
1123,7 → 1143,7
cairo_point_t *p0 = &cpf->current_point;
 
if (! _cairo_spline_init (&spline,
cpf->line_to,
(cairo_spline_add_point_func_t)cpf->line_to,
cpf->closure,
p0, p1, p2, p3))
{
1145,7 → 1165,6
 
cairo_status_t
_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
cairo_direction_t dir,
cairo_path_fixed_move_to_func_t *move_to,
cairo_path_fixed_line_to_func_t *line_to,
cairo_path_fixed_close_path_func_t *close_path,
1155,7 → 1174,7
cpf_t flattener;
 
if (! path->has_curve_to) {
return _cairo_path_fixed_interpret (path, dir,
return _cairo_path_fixed_interpret (path,
move_to,
line_to,
NULL,
1168,7 → 1187,7
flattener.line_to = line_to;
flattener.close_path = close_path;
flattener.closure = closure;
return _cairo_path_fixed_interpret (path, dir,
return _cairo_path_fixed_interpret (path,
_cpf_move_to,
_cpf_line_to,
_cpf_curve_to,
1198,18 → 1217,11
}
}
 
/*
* Check whether the given path contains a single rectangle.
*/
cairo_bool_t
_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
cairo_box_t *box)
static inline cairo_bool_t
_path_is_quad (const cairo_path_fixed_t *path)
{
const cairo_path_buf_t *buf = cairo_path_head (path);
 
if (! path->is_rectilinear)
return FALSE;
 
/* Do we have the right number of ops? */
if (buf->num_ops < 4 || buf->num_ops > 6)
return FALSE;
1244,6 → 1256,154
}
}
 
return TRUE;
}
 
static inline cairo_bool_t
_points_form_rect (const cairo_point_t *points)
{
if (points[0].y == points[1].y &&
points[1].x == points[2].x &&
points[2].y == points[3].y &&
points[3].x == points[0].x)
return TRUE;
if (points[0].x == points[1].x &&
points[1].y == points[2].y &&
points[2].x == points[3].x &&
points[3].y == points[0].y)
return TRUE;
return FALSE;
}
 
/*
* Check whether the given path contains a single rectangle.
*/
cairo_bool_t
_cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
cairo_box_t *box)
{
const cairo_path_buf_t *buf;
 
if (! path->fill_is_rectilinear)
return FALSE;
 
if (! _path_is_quad (path))
return FALSE;
 
buf = cairo_path_head (path);
if (_points_form_rect (buf->points)) {
_canonical_box (box, &buf->points[0], &buf->points[2]);
return TRUE;
}
 
return FALSE;
}
 
/* Determine whether two lines A->B and C->D intersect based on the
* algorithm described here: http://paulbourke.net/geometry/pointlineplane/ */
static inline cairo_bool_t
_lines_intersect_or_are_coincident (cairo_point_t a,
cairo_point_t b,
cairo_point_t c,
cairo_point_t d)
{
cairo_int64_t numerator_a, numerator_b, denominator;
cairo_bool_t denominator_negative;
 
denominator = _cairo_int64_sub (_cairo_int32x32_64_mul (d.y - c.y, b.x - a.x),
_cairo_int32x32_64_mul (d.x - c.x, b.y - a.y));
numerator_a = _cairo_int64_sub (_cairo_int32x32_64_mul (d.x - c.x, a.y - c.y),
_cairo_int32x32_64_mul (d.y - c.y, a.x - c.x));
numerator_b = _cairo_int64_sub (_cairo_int32x32_64_mul (b.x - a.x, a.y - c.y),
_cairo_int32x32_64_mul (b.y - a.y, a.x - c.x));
 
if (_cairo_int64_is_zero (denominator)) {
/* If the denominator and numerators are both zero,
* the lines are coincident. */
if (_cairo_int64_is_zero (numerator_a) && _cairo_int64_is_zero (numerator_b))
return TRUE;
 
/* Otherwise, a zero denominator indicates the lines are
* parallel and never intersect. */
return FALSE;
}
 
/* The lines intersect if both quotients are between 0 and 1 (exclusive). */
 
/* We first test whether either quotient is a negative number. */
denominator_negative = _cairo_int64_negative (denominator);
if (_cairo_int64_negative (numerator_a) ^ denominator_negative)
return FALSE;
if (_cairo_int64_negative (numerator_b) ^ denominator_negative)
return FALSE;
 
/* A zero quotient indicates an "intersection" at an endpoint, which
* we aren't considering a true intersection. */
if (_cairo_int64_is_zero (numerator_a) || _cairo_int64_is_zero (numerator_b))
return FALSE;
 
/* If the absolute value of the numerator is larger than or equal to the
* denominator the result of the division would be greater than or equal
* to one. */
if (! denominator_negative) {
if (! _cairo_int64_lt (numerator_a, denominator) ||
! _cairo_int64_lt (numerator_b, denominator))
return FALSE;
} else {
if (! _cairo_int64_lt (denominator, numerator_a) ||
! _cairo_int64_lt (denominator, numerator_b))
return FALSE;
}
 
return TRUE;
}
 
cairo_bool_t
_cairo_path_fixed_is_simple_quad (const cairo_path_fixed_t *path)
{
const cairo_point_t *points;
 
if (! _path_is_quad (path))
return FALSE;
 
points = cairo_path_head (path)->points;
if (_points_form_rect (points))
return TRUE;
 
if (_lines_intersect_or_are_coincident (points[0], points[1],
points[3], points[2]))
return FALSE;
 
if (_lines_intersect_or_are_coincident (points[0], points[3],
points[1], points[2]))
return FALSE;
 
return TRUE;
}
 
cairo_bool_t
_cairo_path_fixed_is_stroke_box (const cairo_path_fixed_t *path,
cairo_box_t *box)
{
const cairo_path_buf_t *buf = cairo_path_head (path);
 
if (! path->fill_is_rectilinear)
return FALSE;
 
/* Do we have the right number of ops? */
if (buf->num_ops != 5)
return FALSE;
 
/* Check whether the ops are those that would be used for a rectangle */
if (buf->op[0] != CAIRO_PATH_OP_MOVE_TO ||
buf->op[1] != CAIRO_PATH_OP_LINE_TO ||
buf->op[2] != CAIRO_PATH_OP_LINE_TO ||
buf->op[3] != CAIRO_PATH_OP_LINE_TO ||
buf->op[4] != CAIRO_PATH_OP_CLOSE_PATH)
{
return FALSE;
}
 
/* Ok, we may have a box, if the points line up */
if (buf->points[0].y == buf->points[1].y &&
buf->points[1].x == buf->points[2].x &&
1286,8 → 1446,11
if (! _cairo_path_fixed_is_box (path, box))
return FALSE;
 
/* This check is valid because the current implementation of
* _cairo_path_fixed_is_box () only accepts rectangles like:
* move,line,line,line[,line|close[,close|move]]. */
buf = cairo_path_head (path);
if (buf->points[0].y == buf->points[1].y)
if (buf->num_ops > 4)
return TRUE;
 
return FALSE;
1331,11 → 1494,8
 
iter = *_iter;
 
if (iter.n_op == iter.buf->num_ops &&
! _cairo_path_fixed_iter_next_op (&iter))
{
if (iter.n_op == iter.buf->num_ops && ! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
}
 
/* Check whether the ops are those that would be used for a rectangle */
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
1350,8 → 1510,20
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
 
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
/* a horizontal/vertical closed line is also a degenerate rectangle */
switch (iter.buf->op[iter.n_op]) {
case CAIRO_PATH_OP_CLOSE_PATH:
_cairo_path_fixed_iter_next_op (&iter);
case CAIRO_PATH_OP_MOVE_TO: /* implicit close */
box->p1 = box->p2 = points[0];
*_iter = iter;
return TRUE;
default:
return FALSE;
case CAIRO_PATH_OP_LINE_TO:
break;
}
 
points[2] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
1359,25 → 1531,24
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
return FALSE;
points[3] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
 
/* Now, there are choices. The rectangle might end with a LINE_TO
* (to the original point), but this isn't required. If it
* doesn't, then it must end with a CLOSE_PATH (which may be implicit). */
if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO)
{
if (! _cairo_path_fixed_iter_next_op (&iter)) {
/* implicit close due to fill */
} else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) {
points[4] = iter.buf->points[iter.n_point++];
if (points[4].x != points[0].x || points[4].y != points[0].y)
return FALSE;
}
else if (! (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH ||
iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO))
{
_cairo_path_fixed_iter_next_op (&iter);
} else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_CLOSE_PATH) {
_cairo_path_fixed_iter_next_op (&iter);
} else if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_MOVE_TO) {
/* implicit close-path due to new-sub-path */
} else {
return FALSE;
}
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
 
/* Ok, we may have a box, if the points line up */
if (points[0].y == points[1].y &&
1411,14 → 1582,5
if (iter->buf == NULL)
return TRUE;
 
if (iter->n_op == iter->buf->num_ops)
return TRUE;
 
if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
iter->buf->num_ops == iter->n_op + 1)
{
return TRUE;
return iter->n_op == iter->buf->num_ops;
}
 
return FALSE;
}
/programs/develop/libraries/cairo/src/cairo-path-in-fill.c
217,7 → 217,7
 
/* XXX Investigate direct inspection of the inflections? */
if (! _cairo_spline_init (&spline,
_cairo_in_fill_line_to,
(cairo_spline_add_point_func_t)_cairo_in_fill_line_to,
in_fill,
&in_fill->current_point, b, c, d))
{
254,13 → 254,12
cairo_status_t status;
cairo_bool_t is_inside;
 
if (path->is_empty_fill)
if (_cairo_path_fixed_fill_is_empty (path))
return FALSE;
 
_cairo_in_fill_init (&in_fill, tolerance, x, y);
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_in_fill_move_to,
_cairo_in_fill_line_to,
_cairo_in_fill_curve_to,
/programs/develop/libraries/cairo/src/cairo-path-private.h
41,11 → 41,11
 
cairo_private cairo_path_t *
_cairo_path_create (cairo_path_fixed_t *path,
cairo_gstate_t *gstate);
cairo_t *cr);
 
cairo_private cairo_path_t *
_cairo_path_create_flat (cairo_path_fixed_t *path,
cairo_gstate_t *gstate);
cairo_t *cr);
 
cairo_private cairo_path_t *
_cairo_path_create_in_error (cairo_status_t status);
/programs/develop/libraries/cairo/src/cairo-path-stroke-boxes.c
0,0 → 1,711
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* 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>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#define _BSD_SOURCE /* for hypot() */
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
#include "cairo-stroke-dash-private.h"
 
typedef struct _segment_t {
cairo_point_t p1, p2;
unsigned flags;
#define HORIZONTAL 0x1
#define FORWARDS 0x2
#define JOIN 0x4
} segment_t;
 
typedef struct _cairo_rectilinear_stroker {
const cairo_stroke_style_t *stroke_style;
const cairo_matrix_t *ctm;
cairo_antialias_t antialias;
 
cairo_fixed_t half_line_x, half_line_y;
cairo_boxes_t *boxes;
cairo_point_t current_point;
cairo_point_t first_point;
cairo_bool_t open_sub_path;
 
cairo_stroker_dash_t dash;
 
cairo_bool_t has_bounds;
cairo_box_t bounds;
 
int num_segments;
int segments_size;
segment_t *segments;
segment_t segments_embedded[8]; /* common case is a single rectangle */
} cairo_rectilinear_stroker_t;
 
static void
_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
const cairo_box_t *boxes,
int num_boxes)
{
stroker->has_bounds = TRUE;
_cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
 
stroker->bounds.p1.x -= stroker->half_line_x;
stroker->bounds.p2.x += stroker->half_line_x;
 
stroker->bounds.p1.y -= stroker->half_line_y;
stroker->bounds.p2.y += stroker->half_line_y;
}
 
static cairo_bool_t
_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_antialias_t antialias,
cairo_boxes_t *boxes)
{
/* This special-case rectilinear stroker only supports
* miter-joined lines (not curves) and a translation-only matrix
* (though it could probably be extended to support a matrix with
* uniform, integer scaling).
*
* It also only supports horizontal and vertical line_to
* elements. But we don't catch that here, but instead return
* UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
* non-rectilinear line_to is encountered.
*/
if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
return FALSE;
 
/* If the miter limit turns right angles into bevels, then we
* can't use this optimization. Remember, the ratio is
* 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
* which we round for safety. */
if (stroke_style->miter_limit < M_SQRT2)
return FALSE;
 
if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
{
return FALSE;
}
 
if (! _cairo_matrix_is_scale (ctm))
return FALSE;
 
stroker->stroke_style = stroke_style;
stroker->ctm = ctm;
stroker->antialias = antialias;
 
stroker->half_line_x =
_cairo_fixed_from_double (fabs(ctm->xx) * stroke_style->line_width / 2.0);
stroker->half_line_y =
_cairo_fixed_from_double (fabs(ctm->yy) * stroke_style->line_width / 2.0);
 
stroker->open_sub_path = FALSE;
stroker->segments = stroker->segments_embedded;
stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
stroker->num_segments = 0;
 
_cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
stroker->has_bounds = FALSE;
 
stroker->boxes = boxes;
 
return TRUE;
}
 
static void
_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
{
if (stroker->segments != stroker->segments_embedded)
free (stroker->segments);
}
 
static cairo_status_t
_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
const cairo_point_t *p1,
const cairo_point_t *p2,
unsigned flags)
{
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (stroker->num_segments == stroker->segments_size) {
int new_size = stroker->segments_size * 2;
segment_t *new_segments;
 
if (stroker->segments == stroker->segments_embedded) {
new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (new_segments, stroker->segments,
stroker->num_segments * sizeof (segment_t));
} else {
new_segments = _cairo_realloc_ab (stroker->segments,
new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
stroker->segments_size = new_size;
stroker->segments = new_segments;
}
 
stroker->segments[stroker->num_segments].p1 = *p1;
stroker->segments[stroker->num_segments].p2 = *p2;
stroker->segments[stroker->num_segments].flags = flags;
stroker->num_segments++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
{
cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
cairo_fixed_t half_line_x = stroker->half_line_x;
cairo_fixed_t half_line_y = stroker->half_line_y;
cairo_status_t status;
int i, j;
 
/* For each segment we generate a single rectangle.
* This rectangle is based on a perpendicular extension (by half the
* line width) of the segment endpoints * after some adjustments of the
* endpoints to account for caps and joins.
*/
for (i = 0; i < stroker->num_segments; i++) {
cairo_bool_t lengthen_initial, lengthen_final;
cairo_point_t *a, *b;
cairo_box_t box;
 
a = &stroker->segments[i].p1;
b = &stroker->segments[i].p2;
 
/* We adjust the initial point of the segment to extend the
* rectangle to include the previous cap or join, (this
* adjustment applies to all segments except for the first
* segment of open, butt-capped paths). However, we must be
* careful not to emit a miter join across a degenerate segment
* which has been elided.
*
* Overlapping segments will be eliminated by the tessellation.
* Ideally, we would not emit these self-intersections at all,
* but that is tricky with segments shorter than half_line_width.
*/
j = i == 0 ? stroker->num_segments - 1 : i-1;
lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
j = i == stroker->num_segments - 1 ? 0 : i+1;
lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL;
if (stroker->open_sub_path) {
if (i == 0)
lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT;
 
if (i == stroker->num_segments - 1)
lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT;
}
 
/* Perform the adjustments of the endpoints. */
if (lengthen_initial | lengthen_final) {
if (a->y == b->y) {
if (a->x < b->x) {
if (lengthen_initial)
a->x -= half_line_x;
if (lengthen_final)
b->x += half_line_x;
} else {
if (lengthen_initial)
a->x += half_line_x;
if (lengthen_final)
b->x -= half_line_x;
}
} else {
if (a->y < b->y) {
if (lengthen_initial)
a->y -= half_line_y;
if (lengthen_final)
b->y += half_line_y;
} else {
if (lengthen_initial)
a->y += half_line_y;
if (lengthen_final)
b->y -= half_line_y;
}
}
}
 
/* Form the rectangle by expanding by half the line width in
* either perpendicular direction. */
if (a->y == b->y) {
a->y -= half_line_y;
b->y += half_line_y;
} else {
a->x -= half_line_x;
b->x += half_line_x;
}
 
if (a->x < b->x) {
box.p1.x = a->x;
box.p2.x = b->x;
} else {
box.p1.x = b->x;
box.p2.x = a->x;
}
if (a->y < b->y) {
box.p1.y = a->y;
box.p2.y = b->y;
} else {
box.p1.y = b->y;
box.p2.y = a->y;
}
 
status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
if (unlikely (status))
return status;
}
 
stroker->num_segments = 0;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
{
cairo_status_t status;
cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
cairo_fixed_t half_line_x = stroker->half_line_x;
cairo_fixed_t half_line_y = stroker->half_line_y;
int i;
 
for (i = 0; i < stroker->num_segments; i++) {
cairo_point_t *a, *b;
cairo_bool_t is_horizontal;
cairo_box_t box;
 
a = &stroker->segments[i].p1;
b = &stroker->segments[i].p2;
 
is_horizontal = stroker->segments[i].flags & HORIZONTAL;
 
/* Handle the joins for a potentially degenerate segment. */
if (line_cap == CAIRO_LINE_CAP_BUTT &&
stroker->segments[i].flags & JOIN &&
(i != stroker->num_segments - 1 ||
(! stroker->open_sub_path && stroker->dash.dash_starts_on)))
{
cairo_slope_t out_slope;
int j = (i + 1) % stroker->num_segments;
cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS);
 
_cairo_slope_init (&out_slope,
&stroker->segments[j].p1,
&stroker->segments[j].p2);
box.p2 = box.p1 = stroker->segments[i].p2;
 
if (is_horizontal) {
if (forwards)
box.p2.x += half_line_x;
else
box.p1.x -= half_line_x;
 
if (out_slope.dy > 0)
box.p1.y -= half_line_y;
else
box.p2.y += half_line_y;
} else {
if (forwards)
box.p2.y += half_line_y;
else
box.p1.y -= half_line_y;
 
if (out_slope.dx > 0)
box.p1.x -= half_line_x;
else
box.p2.x += half_line_x;
}
 
status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
if (unlikely (status))
return status;
}
 
/* Perform the adjustments of the endpoints. */
if (is_horizontal) {
if (line_cap == CAIRO_LINE_CAP_SQUARE) {
if (a->x <= b->x) {
a->x -= half_line_x;
b->x += half_line_x;
} else {
a->x += half_line_x;
b->x -= half_line_x;
}
}
 
a->y += half_line_y;
b->y -= half_line_y;
} else {
if (line_cap == CAIRO_LINE_CAP_SQUARE) {
if (a->y <= b->y) {
a->y -= half_line_y;
b->y += half_line_y;
} else {
a->y += half_line_y;
b->y -= half_line_y;
}
}
 
a->x += half_line_x;
b->x -= half_line_x;
}
 
if (a->x == b->x && a->y == b->y)
continue;
 
if (a->x < b->x) {
box.p1.x = a->x;
box.p2.x = b->x;
} else {
box.p1.x = b->x;
box.p2.x = a->x;
}
if (a->y < b->y) {
box.p1.y = a->y;
box.p2.y = b->y;
} else {
box.p1.y = b->y;
box.p2.y = a->y;
}
 
status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
if (unlikely (status))
return status;
}
 
stroker->num_segments = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_move_to (void *closure,
const cairo_point_t *point)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_status_t status;
 
if (stroker->dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
 
/* reset the dash pattern for new sub paths */
_cairo_stroker_dash_start (&stroker->dash);
 
stroker->current_point = *point;
stroker->first_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_line_to (void *closure,
const cairo_point_t *b)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_point_t *a = &stroker->current_point;
cairo_status_t status;
 
/* We only support horizontal or vertical elements. */
assert (a->x == b->x || a->y == b->y);
 
/* We don't draw anything for degenerate paths. */
if (a->x == b->x && a->y == b->y)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
(a->y == b->y) | JOIN);
 
stroker->current_point = *b;
stroker->open_sub_path = TRUE;
 
return status;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_line_to_dashed (void *closure,
const cairo_point_t *point)
{
cairo_rectilinear_stroker_t *stroker = closure;
const cairo_point_t *a = &stroker->current_point;
const cairo_point_t *b = point;
cairo_bool_t fully_in_bounds;
double sf, sign, remain;
cairo_fixed_t mag;
cairo_status_t status;
cairo_line_t segment;
cairo_bool_t dash_on = FALSE;
unsigned is_horizontal;
 
/* We don't draw anything for degenerate paths. */
if (a->x == b->x && a->y == b->y)
return CAIRO_STATUS_SUCCESS;
 
/* We only support horizontal or vertical elements. */
assert (a->x == b->x || a->y == b->y);
 
fully_in_bounds = TRUE;
if (stroker->has_bounds &&
(! _cairo_box_contains_point (&stroker->bounds, a) ||
! _cairo_box_contains_point (&stroker->bounds, b)))
{
fully_in_bounds = FALSE;
}
 
is_horizontal = a->y == b->y;
if (is_horizontal) {
mag = b->x - a->x;
sf = fabs (stroker->ctm->xx);
} else {
mag = b->y - a->y;
sf = fabs (stroker->ctm->yy);
}
if (mag < 0) {
remain = _cairo_fixed_to_double (-mag);
sign = 1.;
} else {
remain = _cairo_fixed_to_double (mag);
is_horizontal |= FORWARDS;
sign = -1.;
}
 
segment.p2 = segment.p1 = *a;
while (remain > 0.) {
double step_length;
 
step_length = MIN (sf * stroker->dash.dash_remain, remain);
remain -= step_length;
 
mag = _cairo_fixed_from_double (sign*remain);
if (is_horizontal & 0x1)
segment.p2.x = b->x + mag;
else
segment.p2.y = b->y + mag;
 
if (stroker->dash.dash_on &&
(fully_in_bounds ||
_cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
{
status = _cairo_rectilinear_stroker_add_segment (stroker,
&segment.p1,
&segment.p2,
is_horizontal | (remain <= 0.) << 2);
if (unlikely (status))
return status;
 
dash_on = TRUE;
}
else
{
dash_on = FALSE;
}
 
_cairo_stroker_dash_step (&stroker->dash, step_length / sf);
segment.p1 = segment.p2;
}
 
if (stroker->dash.dash_on && ! dash_on &&
(fully_in_bounds ||
_cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
{
 
/* This segment ends on a transition to dash_on, compute a new face
* and add cap for the beginning of the next dash_on step.
*/
 
status = _cairo_rectilinear_stroker_add_segment (stroker,
&segment.p1,
&segment.p1,
is_horizontal | JOIN);
if (unlikely (status))
return status;
}
 
stroker->current_point = *point;
stroker->open_sub_path = TRUE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_close_path (void *closure)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_status_t status;
 
/* We don't draw anything for degenerate paths. */
if (! stroker->open_sub_path)
return CAIRO_STATUS_SUCCESS;
 
if (stroker->dash.dashed) {
status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
&stroker->first_point);
} else {
status = _cairo_rectilinear_stroker_line_to (stroker,
&stroker->first_point);
}
if (unlikely (status))
return status;
 
stroker->open_sub_path = FALSE;
 
if (stroker->dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_antialias_t antialias,
cairo_boxes_t *boxes)
{
cairo_rectilinear_stroker_t rectilinear_stroker;
cairo_int_status_t status;
cairo_box_t box;
 
assert (_cairo_path_fixed_stroke_is_rectilinear (path));
 
if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
stroke_style, ctm, antialias,
boxes))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (! rectilinear_stroker.dash.dashed &&
_cairo_path_fixed_is_stroke_box (path, &box) &&
/* if the segments overlap we need to feed them into the tessellator */
box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x &&
box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y)
{
cairo_box_t b;
 
/* top */
b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
b.p1.y = box.p1.y - rectilinear_stroker.half_line_y;
b.p2.y = box.p1.y + rectilinear_stroker.half_line_y;
status = _cairo_boxes_add (boxes, antialias, &b);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
/* left (excluding top/bottom) */
b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
b.p2.x = box.p1.x + rectilinear_stroker.half_line_x;
b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
status = _cairo_boxes_add (boxes, antialias, &b);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
/* right (excluding top/bottom) */
b.p1.x = box.p2.x - rectilinear_stroker.half_line_x;
b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
b.p1.y = box.p1.y + rectilinear_stroker.half_line_y;
b.p2.y = box.p2.y - rectilinear_stroker.half_line_y;
status = _cairo_boxes_add (boxes, antialias, &b);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
/* bottom */
b.p1.x = box.p1.x - rectilinear_stroker.half_line_x;
b.p2.x = box.p2.x + rectilinear_stroker.half_line_x;
b.p1.y = box.p2.y - rectilinear_stroker.half_line_y;
b.p2.y = box.p2.y + rectilinear_stroker.half_line_y;
status = _cairo_boxes_add (boxes, antialias, &b);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
goto done;
}
 
if (boxes->num_limits) {
_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
boxes->limits,
boxes->num_limits);
}
 
status = _cairo_path_fixed_interpret (path,
_cairo_rectilinear_stroker_move_to,
rectilinear_stroker.dash.dashed ?
_cairo_rectilinear_stroker_line_to_dashed :
_cairo_rectilinear_stroker_line_to,
NULL,
_cairo_rectilinear_stroker_close_path,
&rectilinear_stroker);
if (unlikely (status))
goto BAIL;
 
if (rectilinear_stroker.dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
if (unlikely (status))
goto BAIL;
 
/* As we incrementally tessellate, we do not eliminate self-intersections */
status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
CAIRO_FILL_RULE_WINDING,
boxes);
if (unlikely (status))
goto BAIL;
 
done:
_cairo_rectilinear_stroker_fini (&rectilinear_stroker);
return CAIRO_STATUS_SUCCESS;
 
BAIL:
_cairo_rectilinear_stroker_fini (&rectilinear_stroker);
_cairo_boxes_clear (boxes);
return status;
}
/programs/develop/libraries/cairo/src/cairo-path-stroke-polygon.c
0,0 → 1,1374
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#define _BSD_SOURCE /* for hypot() */
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-contour-inline.h"
#include "cairo-contour-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
 
#define DEBUG 0
 
struct stroker {
cairo_stroke_style_t style;
 
#if DEBUG
cairo_contour_t path;
#endif
 
struct stroke_contour {
/* Note that these are not strictly contours as they may intersect */
cairo_contour_t contour;
} cw, ccw;
cairo_uint64_t contour_tolerance;
cairo_polygon_t *polygon;
 
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double tolerance;
double spline_cusp_tolerance;
double half_line_width;
cairo_bool_t ctm_det_positive;
 
cairo_pen_t pen;
 
cairo_point_t first_point;
 
cairo_bool_t has_initial_sub_path;
 
cairo_bool_t has_current_face;
cairo_stroke_face_t current_face;
 
cairo_bool_t has_first_face;
cairo_stroke_face_t first_face;
 
cairo_bool_t has_bounds;
cairo_box_t bounds;
};
 
static inline double
normalize_slope (double *dx, double *dy);
 
static void
compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
struct stroker *stroker,
cairo_stroke_face_t *face);
 
static cairo_uint64_t
point_distance_sq (const cairo_point_t *p1,
const cairo_point_t *p2)
{
int32_t dx = p1->x - p2->x;
int32_t dy = p1->y - p2->y;
return _cairo_int32x32_64_mul (dx, dx) + _cairo_int32x32_64_mul (dy, dy);
}
 
static cairo_bool_t
within_tolerance (const cairo_point_t *p1,
const cairo_point_t *p2,
cairo_uint64_t tolerance)
{
return FALSE;
return _cairo_int64_lt (point_distance_sq (p1, p2), tolerance);
}
 
static void
contour_add_point (struct stroker *stroker,
struct stroke_contour *c,
const cairo_point_t *point)
{
if (! within_tolerance (point, _cairo_contour_last_point (&c->contour),
stroker->contour_tolerance))
_cairo_contour_add_point (&c->contour, point);
//*_cairo_contour_last_point (&c->contour) = *point;
}
 
static void
translate_point (cairo_point_t *point, const cairo_point_t *offset)
{
point->x += offset->x;
point->y += offset->y;
}
 
static int
slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
{
double c = (dx1 * dy2 - dx2 * dy1);
 
if (c > 0) return 1;
if (c < 0) return -1;
return 0;
}
 
static inline int
range_step (int i, int step, int max)
{
i += step;
if (i < 0)
i = max - 1;
if (i >= max)
i = 0;
return i;
}
 
/*
* Construct a fan around the midpoint using the vertices from pen between
* inpt and outpt.
*/
static void
add_fan (struct stroker *stroker,
const cairo_slope_t *in_vector,
const cairo_slope_t *out_vector,
const cairo_point_t *midpt,
cairo_bool_t clockwise,
struct stroke_contour *c)
{
cairo_pen_t *pen = &stroker->pen;
int start, stop;
 
if (stroker->has_bounds &&
! _cairo_box_contains_point (&stroker->bounds, midpt))
return;
 
assert (stroker->pen.num_vertices);
 
if (clockwise) {
_cairo_pen_find_active_cw_vertices (pen,
in_vector, out_vector,
&start, &stop);
while (start != stop) {
cairo_point_t p = *midpt;
translate_point (&p, &pen->vertices[start].point);
contour_add_point (stroker, c, &p);
 
if (++start == pen->num_vertices)
start = 0;
}
} else {
_cairo_pen_find_active_ccw_vertices (pen,
in_vector, out_vector,
&start, &stop);
while (start != stop) {
cairo_point_t p = *midpt;
translate_point (&p, &pen->vertices[start].point);
contour_add_point (stroker, c, &p);
 
if (start-- == 0)
start += pen->num_vertices;
}
}
}
 
static int
join_is_clockwise (const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out)
{
return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
}
 
static void
inner_join (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out,
int clockwise)
{
#if 0
cairo_point_t last;
const cairo_point_t *p, *outpt;
struct stroke_contour *inner;
cairo_int64_t d_p, d_last;
cairo_int64_t half_line_width;
cairo_bool_t negate;
 
/* XXX line segments shorter than line-width */
 
if (clockwise) {
inner = &stroker->ccw;
outpt = &out->ccw;
negate = 1;
} else {
inner = &stroker->cw;
outpt = &out->cw;
negate = 0;
}
 
half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5;
 
/* On the inside, the previous end-point is always
* closer to the new face by definition.
*/
last = *_cairo_contour_last_point (&inner->contour);
d_last = distance_from_face (out, &last, negate);
_cairo_contour_remove_last_point (&inner->contour);
 
prev:
if (inner->contour.chain.num_points == 0) {
contour_add_point (stroker, inner, outpt);
return;
}
p = _cairo_contour_last_point (&inner->contour);
d_p = distance_from_face (out, p, negate);
if (_cairo_int64_lt (d_p, half_line_width) &&
!_cairo_int64_negative (distance_along_face (out, p)))
{
last = *p;
d_last = d_p;
_cairo_contour_remove_last_point (&inner->contour);
goto prev;
}
 
compute_inner_joint (&last, d_last, p, d_p, half_line_width);
contour_add_point (stroker, inner, &last);
#else
const cairo_point_t *outpt;
struct stroke_contour *inner;
 
if (clockwise) {
inner = &stroker->ccw;
outpt = &out->ccw;
} else {
inner = &stroker->cw;
outpt = &out->cw;
}
contour_add_point (stroker, inner, &in->point);
contour_add_point (stroker, inner, outpt);
#endif
}
 
static void
inner_close (struct stroker *stroker,
const cairo_stroke_face_t *in,
cairo_stroke_face_t *out)
{
#if 0
cairo_point_t last;
const cairo_point_t *p, *outpt, *inpt;
struct stroke_contour *inner;
struct _cairo_contour_chain *chain;
 
/* XXX line segments shorter than line-width */
 
if (join_is_clockwise (in, out)) {
inner = &stroker->ccw;
outpt = &in->ccw;
inpt = &out->ccw;
} else {
inner = &stroker->cw;
outpt = &in->cw;
inpt = &out->cw;
}
 
if (inner->contour.chain.num_points == 0) {
contour_add_point (stroker, inner, &in->point);
contour_add_point (stroker, inner, inpt);
*_cairo_contour_first_point (&inner->contour) =
*_cairo_contour_last_point (&inner->contour);
return;
}
 
line_width = stroker->style.line_width/2;
line_width *= CAIRO_FIXED_ONE;
 
d_last = sign * distance_from_face (out, outpt);
last = *outpt;
 
for (chain = &inner->contour.chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
p = &chain->points[i];
if ((d_p = sign * distance_from_face (in, p)) >= line_width &&
distance_from_edge (stroker, inpt, &last, p) < line_width)
{
goto out;
}
 
if (p->x != last.x || p->y != last.y) {
last = *p;
d_last = d_p;
}
}
}
out:
 
if (d_p != d_last) {
double dot = (line_width - d_last) / (d_p - d_last);
last.x += dot * (p->x - last.x);
last.y += dot * (p->y - last.y);
}
*_cairo_contour_last_point (&inner->contour) = last;
 
for (chain = &inner->contour.chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
cairo_point_t *pp = &chain->points[i];
if (pp == p)
return;
*pp = last;
}
}
#else
const cairo_point_t *inpt;
struct stroke_contour *inner;
 
if (join_is_clockwise (in, out)) {
inner = &stroker->ccw;
inpt = &out->ccw;
} else {
inner = &stroker->cw;
inpt = &out->cw;
}
 
contour_add_point (stroker, inner, &in->point);
contour_add_point (stroker, inner, inpt);
*_cairo_contour_first_point (&inner->contour) =
*_cairo_contour_last_point (&inner->contour);
#endif
}
 
static void
outer_close (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out)
{
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
int clockwise;
 
if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
{
return;
}
 
clockwise = join_is_clockwise (in, out);
if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
outer = &stroker->cw;
} else {
inpt = &in->ccw;
outpt = &out->ccw;
outer = &stroker->ccw;
}
 
if (within_tolerance (inpt, outpt, stroker->contour_tolerance)) {
*_cairo_contour_first_point (&outer->contour) =
*_cairo_contour_last_point (&outer->contour);
return;
}
 
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
if ((in->dev_slope.x * out->dev_slope.x +
in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
{
add_fan (stroker,
&in->dev_vector, &out->dev_vector, &in->point,
clockwise, outer);
break;
}
 
case CAIRO_LINE_JOIN_MITER:
default: {
/* dot product of incoming slope vector with outgoing slope vector */
double in_dot_out = in->dev_slope.x * out->dev_slope.x +
in->dev_slope.y * out->dev_slope.y;
double ml = stroker->style.miter_limit;
 
/* Check the miter limit -- lines meeting at an acute angle
* can generate long miters, the limit converts them to bevel
*
* Consider the miter join formed when two line segments
* meet at an angle psi:
*
* /.\
* /. .\
* /./ \.\
* /./psi\.\
*
* We can zoom in on the right half of that to see:
*
* |\
* | \ psi/2
* | \
* | \
* | \
* | \
* miter \
* length \
* | \
* | .\
* | . \
* |. line \
* \ width \
* \ \
*
*
* The right triangle in that figure, (the line-width side is
* shown faintly with three '.' characters), gives us the
* following expression relating miter length, angle and line
* width:
*
* 1 /sin (psi/2) = miter_length / line_width
*
* The right-hand side of this relationship is the same ratio
* in which the miter limit (ml) is expressed. We want to know
* when the miter length is within the miter limit. That is
* when the following condition holds:
*
* 1/sin(psi/2) <= ml
* 1 <= ml sin(psi/2)
* 1 <= ml² sin²(psi/2)
* 2 <= ml² 2 sin²(psi/2)
* 2·sin²(psi/2) = 1-cos(psi)
* 2 <= ml² (1-cos(psi))
*
* in · out = |in| |out| cos (psi)
*
* in and out are both unit vectors, so:
*
* in · out = cos (psi)
*
* 2 <= ml² (1 - in · out)
*
*/
if (2 <= ml * ml * (1 + in_dot_out)) {
double x1, y1, x2, y2;
double mx, my;
double dx1, dx2, dy1, dy2;
double ix, iy;
double fdx1, fdy1, fdx2, fdy2;
double mdx, mdy;
 
/*
* we've got the points already transformed to device
* space, but need to do some computation with them and
* also need to transform the slope from user space to
* device space
*/
/* outer point of incoming line face */
x1 = _cairo_fixed_to_double (inpt->x);
y1 = _cairo_fixed_to_double (inpt->y);
dx1 = in->dev_slope.x;
dy1 = in->dev_slope.y;
 
/* outer point of outgoing line face */
x2 = _cairo_fixed_to_double (outpt->x);
y2 = _cairo_fixed_to_double (outpt->y);
dx2 = out->dev_slope.x;
dy2 = out->dev_slope.y;
 
/*
* Compute the location of the outer corner of the miter.
* That's pretty easy -- just the intersection of the two
* outer edges. We've got slopes and points on each
* of those edges. Compute my directly, then compute
* mx by using the edge with the larger dy; that avoids
* dividing by values close to zero.
*/
my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
(dx1 * dy2 - dx2 * dy1));
if (fabs (dy1) >= fabs (dy2))
mx = (my - y1) * dx1 / dy1 + x1;
else
mx = (my - y2) * dx2 / dy2 + x2;
 
/*
* When the two outer edges are nearly parallel, slight
* perturbations in the position of the outer points of the lines
* caused by representing them in fixed point form can cause the
* intersection point of the miter to move a large amount. If
* that moves the miter intersection from between the two faces,
* then draw a bevel instead.
*/
 
ix = _cairo_fixed_to_double (in->point.x);
iy = _cairo_fixed_to_double (in->point.y);
 
/* slope of one face */
fdx1 = x1 - ix; fdy1 = y1 - iy;
 
/* slope of the other face */
fdx2 = x2 - ix; fdy2 = y2 - iy;
 
/* slope from the intersection to the miter point */
mdx = mx - ix; mdy = my - iy;
 
/*
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
cairo_point_t p;
 
p.x = _cairo_fixed_from_double (mx);
p.y = _cairo_fixed_from_double (my);
 
*_cairo_contour_last_point (&outer->contour) = p;
*_cairo_contour_first_point (&outer->contour) = p;
return;
}
}
break;
}
 
case CAIRO_LINE_JOIN_BEVEL:
break;
}
contour_add_point (stroker, outer, outpt);
}
 
static void
outer_join (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out,
int clockwise)
{
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
 
if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
{
return;
}
if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
outer = &stroker->cw;
} else {
inpt = &in->ccw;
outpt = &out->ccw;
outer = &stroker->ccw;
}
 
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
add_fan (stroker,
&in->dev_vector, &out->dev_vector, &in->point,
clockwise, outer);
break;
 
case CAIRO_LINE_JOIN_MITER:
default: {
/* dot product of incoming slope vector with outgoing slope vector */
double in_dot_out = in->dev_slope.x * out->dev_slope.x +
in->dev_slope.y * out->dev_slope.y;
double ml = stroker->style.miter_limit;
 
/* Check the miter limit -- lines meeting at an acute angle
* can generate long miters, the limit converts them to bevel
*
* Consider the miter join formed when two line segments
* meet at an angle psi:
*
* /.\
* /. .\
* /./ \.\
* /./psi\.\
*
* We can zoom in on the right half of that to see:
*
* |\
* | \ psi/2
* | \
* | \
* | \
* | \
* miter \
* length \
* | \
* | .\
* | . \
* |. line \
* \ width \
* \ \
*
*
* The right triangle in that figure, (the line-width side is
* shown faintly with three '.' characters), gives us the
* following expression relating miter length, angle and line
* width:
*
* 1 /sin (psi/2) = miter_length / line_width
*
* The right-hand side of this relationship is the same ratio
* in which the miter limit (ml) is expressed. We want to know
* when the miter length is within the miter limit. That is
* when the following condition holds:
*
* 1/sin(psi/2) <= ml
* 1 <= ml sin(psi/2)
* 1 <= ml² sin²(psi/2)
* 2 <= ml² 2 sin²(psi/2)
* 2·sin²(psi/2) = 1-cos(psi)
* 2 <= ml² (1-cos(psi))
*
* in · out = |in| |out| cos (psi)
*
* in and out are both unit vectors, so:
*
* in · out = cos (psi)
*
* 2 <= ml² (1 - in · out)
*
*/
if (2 <= ml * ml * (1 + in_dot_out)) {
double x1, y1, x2, y2;
double mx, my;
double dx1, dx2, dy1, dy2;
double ix, iy;
double fdx1, fdy1, fdx2, fdy2;
double mdx, mdy;
 
/*
* we've got the points already transformed to device
* space, but need to do some computation with them and
* also need to transform the slope from user space to
* device space
*/
/* outer point of incoming line face */
x1 = _cairo_fixed_to_double (inpt->x);
y1 = _cairo_fixed_to_double (inpt->y);
dx1 = in->dev_slope.x;
dy1 = in->dev_slope.y;
 
/* outer point of outgoing line face */
x2 = _cairo_fixed_to_double (outpt->x);
y2 = _cairo_fixed_to_double (outpt->y);
dx2 = out->dev_slope.x;
dy2 = out->dev_slope.y;
 
/*
* Compute the location of the outer corner of the miter.
* That's pretty easy -- just the intersection of the two
* outer edges. We've got slopes and points on each
* of those edges. Compute my directly, then compute
* mx by using the edge with the larger dy; that avoids
* dividing by values close to zero.
*/
my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
(dx1 * dy2 - dx2 * dy1));
if (fabs (dy1) >= fabs (dy2))
mx = (my - y1) * dx1 / dy1 + x1;
else
mx = (my - y2) * dx2 / dy2 + x2;
 
/*
* When the two outer edges are nearly parallel, slight
* perturbations in the position of the outer points of the lines
* caused by representing them in fixed point form can cause the
* intersection point of the miter to move a large amount. If
* that moves the miter intersection from between the two faces,
* then draw a bevel instead.
*/
 
ix = _cairo_fixed_to_double (in->point.x);
iy = _cairo_fixed_to_double (in->point.y);
 
/* slope of one face */
fdx1 = x1 - ix; fdy1 = y1 - iy;
 
/* slope of the other face */
fdx2 = x2 - ix; fdy2 = y2 - iy;
 
/* slope from the intersection to the miter point */
mdx = mx - ix; mdy = my - iy;
 
/*
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
cairo_point_t p;
 
p.x = _cairo_fixed_from_double (mx);
p.y = _cairo_fixed_from_double (my);
 
*_cairo_contour_last_point (&outer->contour) = p;
return;
}
}
break;
}
 
case CAIRO_LINE_JOIN_BEVEL:
break;
}
contour_add_point (stroker,outer, outpt);
}
 
static void
add_cap (struct stroker *stroker,
const cairo_stroke_face_t *f,
struct stroke_contour *c)
{
switch (stroker->style.line_cap) {
case CAIRO_LINE_CAP_ROUND: {
cairo_slope_t slope;
 
slope.dx = -f->dev_vector.dx;
slope.dy = -f->dev_vector.dy;
 
add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c);
break;
}
 
case CAIRO_LINE_CAP_SQUARE: {
cairo_slope_t fvector;
cairo_point_t p;
double dx, dy;
 
dx = f->usr_vector.x;
dy = f->usr_vector.y;
dx *= stroker->half_line_width;
dy *= stroker->half_line_width;
cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
fvector.dx = _cairo_fixed_from_double (dx);
fvector.dy = _cairo_fixed_from_double (dy);
 
p.x = f->ccw.x + fvector.dx;
p.y = f->ccw.y + fvector.dy;
contour_add_point (stroker, c, &p);
 
p.x = f->cw.x + fvector.dx;
p.y = f->cw.y + fvector.dy;
contour_add_point (stroker, c, &p);
}
 
case CAIRO_LINE_CAP_BUTT:
default:
break;
}
contour_add_point (stroker, c, &f->cw);
}
 
static void
add_leading_cap (struct stroker *stroker,
const cairo_stroke_face_t *face,
struct stroke_contour *c)
{
cairo_stroke_face_t reversed;
cairo_point_t t;
 
reversed = *face;
 
/* The initial cap needs an outward facing vector. Reverse everything */
reversed.usr_vector.x = -reversed.usr_vector.x;
reversed.usr_vector.y = -reversed.usr_vector.y;
reversed.dev_vector.dx = -reversed.dev_vector.dx;
reversed.dev_vector.dy = -reversed.dev_vector.dy;
 
t = reversed.cw;
reversed.cw = reversed.ccw;
reversed.ccw = t;
 
add_cap (stroker, &reversed, c);
}
 
static void
add_trailing_cap (struct stroker *stroker,
const cairo_stroke_face_t *face,
struct stroke_contour *c)
{
add_cap (stroker, face, c);
}
 
static inline double
normalize_slope (double *dx, double *dy)
{
double dx0 = *dx, dy0 = *dy;
double mag;
 
assert (dx0 != 0.0 || dy0 != 0.0);
 
if (dx0 == 0.0) {
*dx = 0.0;
if (dy0 > 0.0) {
mag = dy0;
*dy = 1.0;
} else {
mag = -dy0;
*dy = -1.0;
}
} else if (dy0 == 0.0) {
*dy = 0.0;
if (dx0 > 0.0) {
mag = dx0;
*dx = 1.0;
} else {
mag = -dx0;
*dx = -1.0;
}
} else {
mag = hypot (dx0, dy0);
*dx = dx0 / mag;
*dy = dy0 / mag;
}
 
return mag;
}
 
static void
compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
struct stroker *stroker,
cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
double slope_dx, slope_dy;
 
slope_dx = _cairo_fixed_to_double (dev_slope->dx);
slope_dy = _cairo_fixed_to_double (dev_slope->dy);
face->length = normalize_slope (&slope_dx, &slope_dy);
face->dev_slope.x = slope_dx;
face->dev_slope.y = slope_dy;
 
/*
* rotate to get a line_width/2 vector along the face, note that
* the vector must be rotated the right direction in device space,
* but by 90° in user space. So, the rotation depends on
* whether the ctm reflects or not, and that can be determined
* by looking at the determinant of the matrix.
*/
if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
/* Normalize the matrix! */
cairo_matrix_transform_distance (stroker->ctm_inverse,
&slope_dx, &slope_dy);
normalize_slope (&slope_dx, &slope_dy);
 
if (stroker->ctm_det_positive) {
face_dx = - slope_dy * stroker->half_line_width;
face_dy = slope_dx * stroker->half_line_width;
} else {
face_dx = slope_dy * stroker->half_line_width;
face_dy = - slope_dx * stroker->half_line_width;
}
 
/* back to device space */
cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
} else {
face_dx = - slope_dy * stroker->half_line_width;
face_dy = slope_dx * stroker->half_line_width;
}
 
offset_ccw.x = _cairo_fixed_from_double (face_dx);
offset_ccw.y = _cairo_fixed_from_double (face_dy);
offset_cw.x = -offset_ccw.x;
offset_cw.y = -offset_ccw.y;
 
face->ccw = *point;
translate_point (&face->ccw, &offset_ccw);
 
face->point = *point;
 
face->cw = *point;
translate_point (&face->cw, &offset_cw);
 
face->usr_vector.x = slope_dx;
face->usr_vector.y = slope_dy;
 
face->dev_vector = *dev_slope;
}
 
static void
add_caps (struct stroker *stroker)
{
/* check for a degenerative sub_path */
if (stroker->has_initial_sub_path &&
! stroker->has_first_face &&
! stroker->has_current_face &&
stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
{
/* pick an arbitrary slope to use */
cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
cairo_stroke_face_t face;
 
/* arbitrarily choose first_point */
compute_face (&stroker->first_point, &slope, stroker, &face);
 
add_leading_cap (stroker, &face, &stroker->ccw);
add_trailing_cap (stroker, &face, &stroker->ccw);
 
/* ensure the circle is complete */
_cairo_contour_add_point (&stroker->ccw.contour,
_cairo_contour_first_point (&stroker->ccw.contour));
 
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
} else {
if (stroker->has_current_face)
add_trailing_cap (stroker, &stroker->current_face, &stroker->ccw);
 
#if DEBUG
{
FILE *file = fopen ("contours.txt", "a");
_cairo_debug_print_contour (file, &stroker->path);
_cairo_debug_print_contour (file, &stroker->cw.contour);
_cairo_debug_print_contour (file, &stroker->ccw.contour);
fclose (file);
_cairo_contour_reset (&stroker->path);
}
#endif
 
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
 
if (stroker->has_first_face) {
_cairo_contour_add_point (&stroker->ccw.contour,
&stroker->first_face.cw);
add_leading_cap (stroker, &stroker->first_face, &stroker->ccw);
#if DEBUG
{
FILE *file = fopen ("contours.txt", "a");
_cairo_debug_print_contour (file, &stroker->ccw.contour);
fclose (file);
}
#endif
 
_cairo_polygon_add_contour (stroker->polygon,
&stroker->ccw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
}
 
_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
_cairo_contour_reset (&stroker->cw.contour);
}
}
 
static cairo_status_t
close_path (void *closure);
 
static cairo_status_t
move_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
 
/* Cap the start and end of the previous sub path as needed */
add_caps (stroker);
 
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
stroker->has_initial_sub_path = FALSE;
 
stroker->first_point = *point;
 
#if DEBUG
_cairo_contour_add_point (&stroker->path, point);
#endif
 
stroker->current_face.point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
line_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
cairo_stroke_face_t start;
cairo_point_t *p1 = &stroker->current_face.point;
cairo_slope_t dev_slope;
 
stroker->has_initial_sub_path = TRUE;
 
if (p1->x == point->x && p1->y == point->y)
return CAIRO_STATUS_SUCCESS;
 
#if DEBUG
_cairo_contour_add_point (&stroker->path, point);
#endif
 
_cairo_slope_init (&dev_slope, p1, point);
compute_face (p1, &dev_slope, stroker, &start);
 
if (stroker->has_current_face) {
int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector,
&start.dev_vector);
if (clockwise) {
clockwise = clockwise < 0;
/* Join with final face from previous segment */
if (! within_tolerance (&stroker->current_face.ccw, &start.ccw,
stroker->contour_tolerance) ||
! within_tolerance (&stroker->current_face.cw, &start.cw,
stroker->contour_tolerance))
{
outer_join (stroker, &stroker->current_face, &start, clockwise);
inner_join (stroker, &stroker->current_face, &start, clockwise);
}
}
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = start;
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
 
contour_add_point (stroker, &stroker->cw, &start.cw);
contour_add_point (stroker, &stroker->ccw, &start.ccw);
}
 
stroker->current_face = start;
stroker->current_face.point = *point;
stroker->current_face.ccw.x += dev_slope.dx;
stroker->current_face.ccw.y += dev_slope.dy;
stroker->current_face.cw.x += dev_slope.dx;
stroker->current_face.cw.y += dev_slope.dy;
 
contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
spline_to (void *closure,
const cairo_point_t *point,
const cairo_slope_t *tangent)
{
struct stroker *stroker = closure;
cairo_stroke_face_t face;
 
#if DEBUG
_cairo_contour_add_point (&stroker->path, point);
#endif
if ((tangent->dx | tangent->dy) == 0) {
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
cairo_point_t t;
int clockwise;
 
face = stroker->current_face;
 
face.usr_vector.x = -face.usr_vector.x;
face.usr_vector.y = -face.usr_vector.y;
face.dev_vector.dx = -face.dev_vector.dx;
face.dev_vector.dy = -face.dev_vector.dy;
 
t = face.cw;
face.cw = face.ccw;
face.ccw = t;
 
clockwise = join_is_clockwise (&stroker->current_face, &face);
if (clockwise) {
inpt = &stroker->current_face.cw;
outpt = &face.cw;
outer = &stroker->cw;
} else {
inpt = &stroker->current_face.ccw;
outpt = &face.ccw;
outer = &stroker->ccw;
}
 
add_fan (stroker,
&stroker->current_face.dev_vector,
&face.dev_vector,
&stroker->current_face.point,
clockwise, outer);
} else {
compute_face (point, tangent, stroker, &face);
 
if ((face.dev_slope.x * stroker->current_face.dev_slope.x +
face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance)
{
const cairo_point_t *inpt, *outpt;
struct stroke_contour *outer;
int clockwise = join_is_clockwise (&stroker->current_face, &face);
 
stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
 
stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
 
if (clockwise) {
inpt = &stroker->current_face.cw;
outpt = &face.cw;
outer = &stroker->cw;
} else {
inpt = &stroker->current_face.ccw;
outpt = &face.ccw;
outer = &stroker->ccw;
}
add_fan (stroker,
&stroker->current_face.dev_vector,
&face.dev_vector,
&stroker->current_face.point,
clockwise, outer);
}
 
contour_add_point (stroker, &stroker->cw, &face.cw);
contour_add_point (stroker, &stroker->ccw, &face.ccw);
}
 
stroker->current_face = face;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
struct stroker *stroker = closure;
cairo_spline_t spline;
cairo_stroke_face_t face;
 
if (stroker->has_bounds &&
! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
&stroker->bounds))
return line_to (closure, d);
 
if (! _cairo_spline_init (&spline, spline_to, stroker,
&stroker->current_face.point, b, c, d))
return line_to (closure, d);
 
compute_face (&stroker->current_face.point, &spline.initial_slope,
stroker, &face);
 
if (stroker->has_current_face) {
int clockwise = join_is_clockwise (&stroker->current_face, &face);
/* Join with final face from previous segment */
outer_join (stroker, &stroker->current_face, &face, clockwise);
inner_join (stroker, &stroker->current_face, &face, clockwise);
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = face;
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
 
contour_add_point (stroker, &stroker->cw, &face.cw);
contour_add_point (stroker, &stroker->ccw, &face.ccw);
}
stroker->current_face = face;
 
return _cairo_spline_decompose (&spline, stroker->tolerance);
}
 
static cairo_status_t
close_path (void *closure)
{
struct stroker *stroker = closure;
cairo_status_t status;
 
status = line_to (stroker, &stroker->first_point);
if (unlikely (status))
return status;
 
if (stroker->has_first_face && stroker->has_current_face) {
/* Join first and final faces of sub path */
outer_close (stroker, &stroker->current_face, &stroker->first_face);
inner_close (stroker, &stroker->current_face, &stroker->first_face);
#if 0
*_cairo_contour_first_point (&stroker->ccw.contour) =
*_cairo_contour_last_point (&stroker->ccw.contour);
 
*_cairo_contour_first_point (&stroker->cw.contour) =
*_cairo_contour_last_point (&stroker->cw.contour);
#endif
 
_cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour);
_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
 
#if DEBUG
{
FILE *file = fopen ("contours.txt", "a");
_cairo_debug_print_contour (file, &stroker->path);
_cairo_debug_print_contour (file, &stroker->cw.contour);
_cairo_debug_print_contour (file, &stroker->ccw.contour);
fclose (file);
 
_cairo_contour_reset (&stroker->path);
}
#endif
_cairo_contour_reset (&stroker->cw.contour);
_cairo_contour_reset (&stroker->ccw.contour);
} else {
/* Cap the start and end of the sub path as needed */
add_caps (stroker);
}
 
stroker->has_initial_sub_path = FALSE;
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_polygon_t *polygon)
{
struct stroker stroker;
cairo_status_t status;
 
if (style->num_dashes) {
return _cairo_path_fixed_stroke_dashed_to_polygon (path,
style,
ctm,
ctm_inverse,
tolerance,
polygon);
}
 
stroker.has_bounds = polygon->num_limits;
if (stroker.has_bounds) {
/* Extend the bounds in each direction to account for the maximum area
* we might generate trapezoids, to capture line segments that are
* outside of the bounds but which might generate rendering that's
* within bounds.
*/
double dx, dy;
cairo_fixed_t fdx, fdy;
int i;
 
stroker.bounds = polygon->limits[0];
for (i = 1; i < polygon->num_limits; i++)
_cairo_box_add_box (&stroker.bounds, &polygon->limits[i]);
 
_cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
fdx = _cairo_fixed_from_double (dx);
fdy = _cairo_fixed_from_double (dy);
 
stroker.bounds.p1.x -= fdx;
stroker.bounds.p2.x += fdx;
stroker.bounds.p1.y -= fdy;
stroker.bounds.p2.y += fdy;
}
 
stroker.style = *style;
stroker.ctm = ctm;
stroker.ctm_inverse = ctm_inverse;
stroker.tolerance = tolerance;
stroker.half_line_width = style->line_width / 2.;
/* To test whether we need to join two segments of a spline using
* a round-join or a bevel-join, we can inspect the angle between the
* two segments. If the difference between the chord distance
* (half-line-width times the cosine of the bisection angle) and the
* half-line-width itself is greater than tolerance then we need to
* inject a point.
*/
stroker.spline_cusp_tolerance = 1 - tolerance / stroker.half_line_width;
stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance;
stroker.spline_cusp_tolerance *= 2;
stroker.spline_cusp_tolerance -= 1;
stroker.ctm_det_positive =
_cairo_matrix_compute_determinant (ctm) >= 0.0;
 
stroker.pen.num_vertices = 0;
if (path->has_curve_to ||
style->line_join == CAIRO_LINE_JOIN_ROUND ||
style->line_cap == CAIRO_LINE_CAP_ROUND) {
status = _cairo_pen_init (&stroker.pen,
stroker.half_line_width,
tolerance, ctm);
if (unlikely (status))
return status;
 
/* If the line width is so small that the pen is reduced to a
single point, then we have nothing to do. */
if (stroker.pen.num_vertices <= 1)
return CAIRO_STATUS_SUCCESS;
}
 
stroker.has_current_face = FALSE;
stroker.has_first_face = FALSE;
stroker.has_initial_sub_path = FALSE;
 
#if DEBUG
remove ("contours.txt");
remove ("polygons.txt");
_cairo_contour_init (&stroker.path, 0);
#endif
_cairo_contour_init (&stroker.cw.contour, 1);
_cairo_contour_init (&stroker.ccw.contour, -1);
tolerance *= CAIRO_FIXED_ONE;
tolerance *= tolerance;
stroker.contour_tolerance = tolerance;
stroker.polygon = polygon;
 
status = _cairo_path_fixed_interpret (path,
move_to,
line_to,
curve_to,
close_path,
&stroker);
/* Cap the start and end of the final sub path as needed */
if (likely (status == CAIRO_STATUS_SUCCESS))
add_caps (&stroker);
 
_cairo_contour_fini (&stroker.cw.contour);
_cairo_contour_fini (&stroker.ccw.contour);
if (stroker.pen.num_vertices)
_cairo_pen_fini (&stroker.pen);
 
#if DEBUG
{
FILE *file = fopen ("polygons.txt", "a");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
}
#endif
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-path-stroke-traps.c
0,0 → 1,1122
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2013 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
#include "cairo-stroke-dash-private.h"
#include "cairo-traps-private.h"
 
#include <float.h>
 
struct stroker {
const cairo_stroke_style_t *style;
 
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double spline_cusp_tolerance;
double half_line_width;
double tolerance;
double ctm_determinant;
cairo_bool_t ctm_det_positive;
cairo_line_join_t line_join;
 
cairo_traps_t *traps;
 
cairo_pen_t pen;
 
cairo_point_t first_point;
 
cairo_bool_t has_initial_sub_path;
 
cairo_bool_t has_current_face;
cairo_stroke_face_t current_face;
 
cairo_bool_t has_first_face;
cairo_stroke_face_t first_face;
 
cairo_stroker_dash_t dash;
 
cairo_bool_t has_bounds;
cairo_box_t tight_bounds;
cairo_box_t line_bounds;
cairo_box_t join_bounds;
};
 
static cairo_status_t
stroker_init (struct stroker *stroker,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps)
{
cairo_status_t status;
 
stroker->style = style;
stroker->ctm = ctm;
stroker->ctm_inverse = NULL;
if (! _cairo_matrix_is_identity (ctm_inverse))
stroker->ctm_inverse = ctm_inverse;
stroker->line_join = style->line_join;
stroker->half_line_width = style->line_width / 2.0;
stroker->tolerance = tolerance;
stroker->traps = traps;
 
/* To test whether we need to join two segments of a spline using
* a round-join or a bevel-join, we can inspect the angle between the
* two segments. If the difference between the chord distance
* (half-line-width times the cosine of the bisection angle) and the
* half-line-width itself is greater than tolerance then we need to
* inject a point.
*/
stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width;
stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
stroker->spline_cusp_tolerance *= 2;
stroker->spline_cusp_tolerance -= 1;
 
stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
 
status = _cairo_pen_init (&stroker->pen,
stroker->half_line_width,
tolerance, ctm);
if (unlikely (status))
return status;
 
stroker->has_current_face = FALSE;
stroker->has_first_face = FALSE;
stroker->has_initial_sub_path = FALSE;
 
_cairo_stroker_dash_init (&stroker->dash, style);
 
stroker->has_bounds = traps->num_limits;
if (stroker->has_bounds) {
/* Extend the bounds in each direction to account for the maximum area
* we might generate trapezoids, to capture line segments that are outside
* of the bounds but which might generate rendering that's within bounds.
*/
double dx, dy;
cairo_fixed_t fdx, fdy;
 
stroker->tight_bounds = traps->bounds;
 
_cairo_stroke_style_max_distance_from_path (stroker->style, path,
stroker->ctm, &dx, &dy);
 
_cairo_stroke_style_max_line_distance_from_path (stroker->style, path,
stroker->ctm, &dx, &dy);
 
fdx = _cairo_fixed_from_double (dx);
fdy = _cairo_fixed_from_double (dy);
 
stroker->line_bounds = stroker->tight_bounds;
stroker->line_bounds.p1.x -= fdx;
stroker->line_bounds.p2.x += fdx;
stroker->line_bounds.p1.y -= fdy;
stroker->line_bounds.p2.y += fdy;
 
_cairo_stroke_style_max_join_distance_from_path (stroker->style, path,
stroker->ctm, &dx, &dy);
 
fdx = _cairo_fixed_from_double (dx);
fdy = _cairo_fixed_from_double (dy);
 
stroker->join_bounds = stroker->tight_bounds;
stroker->join_bounds.p1.x -= fdx;
stroker->join_bounds.p2.x += fdx;
stroker->join_bounds.p1.y -= fdy;
stroker->join_bounds.p2.y += fdy;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
stroker_fini (struct stroker *stroker)
{
_cairo_pen_fini (&stroker->pen);
}
 
static void
translate_point (cairo_point_t *point, cairo_point_t *offset)
{
point->x += offset->x;
point->y += offset->y;
}
 
static int
join_is_clockwise (const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out)
{
return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
}
 
static int
slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
{
double c = dx1 * dy2 - dx2 * dy1;
if (c > 0) return 1;
if (c < 0) return -1;
return 0;
}
 
static cairo_bool_t
stroker_intersects_join (const struct stroker *stroker,
const cairo_point_t *in,
const cairo_point_t *out)
{
cairo_line_t segment;
 
if (! stroker->has_bounds)
return TRUE;
 
segment.p1 = *in;
segment.p2 = *out;
return _cairo_box_intersects_line_segment (&stroker->join_bounds, &segment);
}
 
static void
join (struct stroker *stroker,
cairo_stroke_face_t *in,
cairo_stroke_face_t *out)
{
int clockwise = join_is_clockwise (out, in);
cairo_point_t *inpt, *outpt;
 
if (in->cw.x == out->cw.x &&
in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x &&
in->ccw.y == out->ccw.y)
{
return;
}
 
if (clockwise) {
inpt = &in->ccw;
outpt = &out->ccw;
} else {
inpt = &in->cw;
outpt = &out->cw;
}
 
if (! stroker_intersects_join (stroker, inpt, outpt))
return;
 
switch (stroker->line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
if ((in->dev_slope.x * out->dev_slope.x +
in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
{
int start, stop;
cairo_point_t tri[3];
cairo_pen_t *pen = &stroker->pen;
 
tri[0] = in->point;
tri[1] = *inpt;
if (clockwise) {
_cairo_pen_find_active_ccw_vertices (pen,
&in->dev_vector, &out->dev_vector,
&start, &stop);
while (start != stop) {
tri[2] = in->point;
translate_point (&tri[2], &pen->vertices[start].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
 
if (start-- == 0)
start += pen->num_vertices;
}
} else {
_cairo_pen_find_active_cw_vertices (pen,
&in->dev_vector, &out->dev_vector,
&start, &stop);
while (start != stop) {
tri[2] = in->point;
translate_point (&tri[2], &pen->vertices[start].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
 
if (++start == pen->num_vertices)
start = 0;
}
}
tri[2] = *outpt;
_cairo_traps_tessellate_triangle (stroker->traps, tri);
break;
}
 
case CAIRO_LINE_JOIN_MITER:
default: {
/* dot product of incoming slope vector with outgoing slope vector */
double in_dot_out = (-in->usr_vector.x * out->usr_vector.x +
-in->usr_vector.y * out->usr_vector.y);
double ml = stroker->style->miter_limit;
 
/* Check the miter limit -- lines meeting at an acute angle
* can generate long miters, the limit converts them to bevel
*
* Consider the miter join formed when two line segments
* meet at an angle psi:
*
* /.\
* /. .\
* /./ \.\
* /./psi\.\
*
* We can zoom in on the right half of that to see:
*
* |\
* | \ psi/2
* | \
* | \
* | \
* | \
* miter \
* length \
* | \
* | .\
* | . \
* |. line \
* \ width \
* \ \
*
*
* The right triangle in that figure, (the line-width side is
* shown faintly with three '.' characters), gives us the
* following expression relating miter length, angle and line
* width:
*
* 1 /sin (psi/2) = miter_length / line_width
*
* The right-hand side of this relationship is the same ratio
* in which the miter limit (ml) is expressed. We want to know
* when the miter length is within the miter limit. That is
* when the following condition holds:
*
* 1/sin(psi/2) <= ml
* 1 <= ml sin(psi/2)
* 1 <= ml² sin²(psi/2)
* 2 <= ml² 2 sin²(psi/2)
* 2·sin²(psi/2) = 1-cos(psi)
* 2 <= ml² (1-cos(psi))
*
* in · out = |in| |out| cos (psi)
*
* in and out are both unit vectors, so:
*
* in · out = cos (psi)
*
* 2 <= ml² (1 - in · out)
*
*/
if (2 <= ml * ml * (1 - in_dot_out)) {
double x1, y1, x2, y2;
double mx, my;
double dx1, dx2, dy1, dy2;
cairo_point_t outer;
cairo_point_t quad[4];
double ix, iy;
double fdx1, fdy1, fdx2, fdy2;
double mdx, mdy;
 
/*
* we've got the points already transformed to device
* space, but need to do some computation with them and
* also need to transform the slope from user space to
* device space
*/
/* outer point of incoming line face */
x1 = _cairo_fixed_to_double (inpt->x);
y1 = _cairo_fixed_to_double (inpt->y);
dx1 = in->usr_vector.x;
dy1 = in->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
 
/* outer point of outgoing line face */
x2 = _cairo_fixed_to_double (outpt->x);
y2 = _cairo_fixed_to_double (outpt->y);
dx2 = out->usr_vector.x;
dy2 = out->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
 
/*
* Compute the location of the outer corner of the miter.
* That's pretty easy -- just the intersection of the two
* outer edges. We've got slopes and points on each
* of those edges. Compute my directly, then compute
* mx by using the edge with the larger dy; that avoids
* dividing by values close to zero.
*/
my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
(dx1 * dy2 - dx2 * dy1));
if (fabs (dy1) >= fabs (dy2))
mx = (my - y1) * dx1 / dy1 + x1;
else
mx = (my - y2) * dx2 / dy2 + x2;
 
/*
* When the two outer edges are nearly parallel, slight
* perturbations in the position of the outer points of the lines
* caused by representing them in fixed point form can cause the
* intersection point of the miter to move a large amount. If
* that moves the miter intersection from between the two faces,
* then draw a bevel instead.
*/
 
ix = _cairo_fixed_to_double (in->point.x);
iy = _cairo_fixed_to_double (in->point.y);
 
/* slope of one face */
fdx1 = x1 - ix; fdy1 = y1 - iy;
 
/* slope of the other face */
fdx2 = x2 - ix; fdy2 = y2 - iy;
 
/* slope from the intersection to the miter point */
mdx = mx - ix; mdy = my - iy;
 
/*
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
/*
* Draw the quadrilateral
*/
outer.x = _cairo_fixed_from_double (mx);
outer.y = _cairo_fixed_from_double (my);
 
quad[0] = in->point;
quad[1] = *inpt;
quad[2] = outer;
quad[3] = *outpt;
 
_cairo_traps_tessellate_convex_quad (stroker->traps, quad);
break;
}
}
/* fall through ... */
}
 
case CAIRO_LINE_JOIN_BEVEL: {
cairo_point_t tri[3];
tri[0] = in->point;
tri[1] = *inpt;
tri[2] = *outpt;
 
_cairo_traps_tessellate_triangle (stroker->traps, tri);
break;
}
}
}
 
static void
add_cap (struct stroker *stroker, cairo_stroke_face_t *f)
{
switch (stroker->style->line_cap) {
case CAIRO_LINE_CAP_ROUND: {
int start, stop;
cairo_slope_t in_slope, out_slope;
cairo_point_t tri[3];
cairo_pen_t *pen = &stroker->pen;
 
in_slope = f->dev_vector;
out_slope.dx = -in_slope.dx;
out_slope.dy = -in_slope.dy;
_cairo_pen_find_active_cw_vertices (pen, &in_slope, &out_slope,
&start, &stop);
tri[0] = f->point;
tri[1] = f->cw;
while (start != stop) {
tri[2] = f->point;
translate_point (&tri[2], &pen->vertices[start].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
 
tri[1] = tri[2];
if (++start == pen->num_vertices)
start = 0;
}
tri[2] = f->ccw;
_cairo_traps_tessellate_triangle (stroker->traps, tri);
break;
}
 
case CAIRO_LINE_CAP_SQUARE: {
double dx, dy;
cairo_slope_t fvector;
cairo_point_t quad[4];
 
dx = f->usr_vector.x;
dy = f->usr_vector.y;
dx *= stroker->half_line_width;
dy *= stroker->half_line_width;
cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
fvector.dx = _cairo_fixed_from_double (dx);
fvector.dy = _cairo_fixed_from_double (dy);
 
quad[0] = f->cw;
quad[1].x = f->cw.x + fvector.dx;
quad[1].y = f->cw.y + fvector.dy;
quad[2].x = f->ccw.x + fvector.dx;
quad[2].y = f->ccw.y + fvector.dy;
quad[3] = f->ccw;
 
_cairo_traps_tessellate_convex_quad (stroker->traps, quad);
break;
}
 
case CAIRO_LINE_CAP_BUTT:
default:
break;
}
}
 
static void
add_leading_cap (struct stroker *stroker,
cairo_stroke_face_t *face)
{
cairo_stroke_face_t reversed;
cairo_point_t t;
 
reversed = *face;
 
/* The initial cap needs an outward facing vector. Reverse everything */
reversed.usr_vector.x = -reversed.usr_vector.x;
reversed.usr_vector.y = -reversed.usr_vector.y;
reversed.dev_vector.dx = -reversed.dev_vector.dx;
reversed.dev_vector.dy = -reversed.dev_vector.dy;
t = reversed.cw;
reversed.cw = reversed.ccw;
reversed.ccw = t;
 
add_cap (stroker, &reversed);
}
 
static void
add_trailing_cap (struct stroker *stroker, cairo_stroke_face_t *face)
{
add_cap (stroker, face);
}
 
static inline double
normalize_slope (double *dx, double *dy)
{
double dx0 = *dx, dy0 = *dy;
 
if (dx0 == 0.0 && dy0 == 0.0)
return 0;
 
if (dx0 == 0.0) {
*dx = 0.0;
if (dy0 > 0.0) {
*dy = 1.0;
return dy0;
} else {
*dy = -1.0;
return -dy0;
}
} else if (dy0 == 0.0) {
*dy = 0.0;
if (dx0 > 0.0) {
*dx = 1.0;
return dx0;
} else {
*dx = -1.0;
return -dx0;
}
} else {
double mag = hypot (dx0, dy0);
*dx = dx0 / mag;
*dy = dy0 / mag;
return mag;
}
}
 
static void
compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
struct stroker *stroker,
cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
double slope_dx, slope_dy;
 
slope_dx = _cairo_fixed_to_double (dev_slope->dx);
slope_dy = _cairo_fixed_to_double (dev_slope->dy);
face->length = normalize_slope (&slope_dx, &slope_dy);
face->dev_slope.x = slope_dx;
face->dev_slope.y = slope_dy;
 
/*
* rotate to get a line_width/2 vector along the face, note that
* the vector must be rotated the right direction in device space,
* but by 90° in user space. So, the rotation depends on
* whether the ctm reflects or not, and that can be determined
* by looking at the determinant of the matrix.
*/
if (stroker->ctm_inverse) {
cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
normalize_slope (&slope_dx, &slope_dy);
 
if (stroker->ctm_det_positive) {
face_dx = - slope_dy * stroker->half_line_width;
face_dy = slope_dx * stroker->half_line_width;
} else {
face_dx = slope_dy * stroker->half_line_width;
face_dy = - slope_dx * stroker->half_line_width;
}
 
/* back to device space */
cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
} else {
face_dx = - slope_dy * stroker->half_line_width;
face_dy = slope_dx * stroker->half_line_width;
}
 
offset_ccw.x = _cairo_fixed_from_double (face_dx);
offset_ccw.y = _cairo_fixed_from_double (face_dy);
offset_cw.x = -offset_ccw.x;
offset_cw.y = -offset_ccw.y;
 
face->ccw = *point;
translate_point (&face->ccw, &offset_ccw);
 
face->point = *point;
 
face->cw = *point;
translate_point (&face->cw, &offset_cw);
 
face->usr_vector.x = slope_dx;
face->usr_vector.y = slope_dy;
 
face->dev_vector = *dev_slope;
}
 
static void
add_caps (struct stroker *stroker)
{
/* check for a degenerative sub_path */
if (stroker->has_initial_sub_path &&
!stroker->has_first_face &&
!stroker->has_current_face &&
stroker->style->line_cap == CAIRO_LINE_CAP_ROUND)
{
/* pick an arbitrary slope to use */
cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
cairo_stroke_face_t face;
 
/* arbitrarily choose first_point
* first_point and current_point should be the same */
compute_face (&stroker->first_point, &slope, stroker, &face);
 
add_leading_cap (stroker, &face);
add_trailing_cap (stroker, &face);
}
 
if (stroker->has_first_face)
add_leading_cap (stroker, &stroker->first_face);
 
if (stroker->has_current_face)
add_trailing_cap (stroker, &stroker->current_face);
}
 
static cairo_bool_t
stroker_intersects_edge (const struct stroker *stroker,
const cairo_stroke_face_t *start,
const cairo_stroke_face_t *end)
{
cairo_box_t box;
 
if (! stroker->has_bounds)
return TRUE;
 
if (_cairo_box_contains_point (&stroker->tight_bounds, &start->cw))
return TRUE;
box.p2 = box.p1 = start->cw;
 
if (_cairo_box_contains_point (&stroker->tight_bounds, &start->ccw))
return TRUE;
_cairo_box_add_point (&box, &start->ccw);
 
if (_cairo_box_contains_point (&stroker->tight_bounds, &end->cw))
return TRUE;
_cairo_box_add_point (&box, &end->cw);
 
if (_cairo_box_contains_point (&stroker->tight_bounds, &end->ccw))
return TRUE;
_cairo_box_add_point (&box, &end->ccw);
 
return (box.p2.x > stroker->tight_bounds.p1.x &&
box.p1.x < stroker->tight_bounds.p2.x &&
box.p2.y > stroker->tight_bounds.p1.y &&
box.p1.y < stroker->tight_bounds.p2.y);
}
 
static void
add_sub_edge (struct stroker *stroker,
const cairo_point_t *p1, const cairo_point_t *p2,
const cairo_slope_t *dev_slope,
cairo_stroke_face_t *start, cairo_stroke_face_t *end)
{
cairo_point_t rectangle[4];
 
compute_face (p1, dev_slope, stroker, start);
 
*end = *start;
end->point = *p2;
rectangle[0].x = p2->x - p1->x;
rectangle[0].y = p2->y - p1->y;
translate_point (&end->ccw, &rectangle[0]);
translate_point (&end->cw, &rectangle[0]);
 
if (p1->x == p2->x && p1->y == p2->y)
return;
 
if (! stroker_intersects_edge (stroker, start, end))
return;
 
rectangle[0] = start->cw;
rectangle[1] = start->ccw;
rectangle[2] = end->ccw;
rectangle[3] = end->cw;
 
_cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
}
 
static cairo_status_t
move_to (void *closure, const cairo_point_t *point)
{
struct stroker *stroker = closure;
 
/* Cap the start and end of the previous sub path as needed */
add_caps (stroker);
 
stroker->first_point = *point;
stroker->current_face.point = *point;
 
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
stroker->has_initial_sub_path = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
move_to_dashed (void *closure, const cairo_point_t *point)
{
/* reset the dash pattern for new sub paths */
struct stroker *stroker = closure;
 
_cairo_stroker_dash_start (&stroker->dash);
return move_to (closure, point);
}
 
static cairo_status_t
line_to (void *closure, const cairo_point_t *point)
{
struct stroker *stroker = closure;
cairo_stroke_face_t start, end;
const cairo_point_t *p1 = &stroker->current_face.point;
const cairo_point_t *p2 = point;
cairo_slope_t dev_slope;
 
stroker->has_initial_sub_path = TRUE;
 
if (p1->x == p2->x && p1->y == p2->y)
return CAIRO_STATUS_SUCCESS;
 
_cairo_slope_init (&dev_slope, p1, p2);
add_sub_edge (stroker, p1, p2, &dev_slope, &start, &end);
 
if (stroker->has_current_face) {
/* Join with final face from previous segment */
join (stroker, &stroker->current_face, &start);
} else if (!stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = start;
stroker->has_first_face = TRUE;
}
stroker->current_face = end;
stroker->has_current_face = TRUE;
 
return CAIRO_STATUS_SUCCESS;
}
 
/*
* Dashed lines. Cap each dash end, join around turns when on
*/
static cairo_status_t
line_to_dashed (void *closure, const cairo_point_t *point)
{
struct stroker *stroker = closure;
double mag, remain, step_length = 0;
double slope_dx, slope_dy;
double dx2, dy2;
cairo_stroke_face_t sub_start, sub_end;
const cairo_point_t *p1 = &stroker->current_face.point;
const cairo_point_t *p2 = point;
cairo_slope_t dev_slope;
cairo_line_t segment;
cairo_bool_t fully_in_bounds;
 
stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
 
if (p1->x == p2->x && p1->y == p2->y)
return CAIRO_STATUS_SUCCESS;
 
fully_in_bounds = TRUE;
if (stroker->has_bounds &&
(! _cairo_box_contains_point (&stroker->join_bounds, p1) ||
! _cairo_box_contains_point (&stroker->join_bounds, p2)))
{
fully_in_bounds = FALSE;
}
 
_cairo_slope_init (&dev_slope, p1, p2);
 
slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
slope_dy = _cairo_fixed_to_double (p2->y - p1->y);
 
if (stroker->ctm_inverse)
cairo_matrix_transform_distance (stroker->ctm_inverse, &slope_dx, &slope_dy);
mag = normalize_slope (&slope_dx, &slope_dy);
if (mag <= DBL_EPSILON)
return CAIRO_STATUS_SUCCESS;
 
remain = mag;
segment.p1 = *p1;
while (remain) {
step_length = MIN (stroker->dash.dash_remain, remain);
remain -= step_length;
dx2 = slope_dx * (mag - remain);
dy2 = slope_dy * (mag - remain);
cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
 
if (stroker->dash.dash_on &&
(fully_in_bounds ||
(! stroker->has_first_face && stroker->dash.dash_starts_on) ||
_cairo_box_intersects_line_segment (&stroker->join_bounds, &segment)))
{
add_sub_edge (stroker,
&segment.p1, &segment.p2,
&dev_slope,
&sub_start, &sub_end);
 
if (stroker->has_current_face) {
/* Join with final face from previous segment */
join (stroker, &stroker->current_face, &sub_start);
 
stroker->has_current_face = FALSE;
} else if (! stroker->has_first_face && stroker->dash.dash_starts_on) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = sub_start;
stroker->has_first_face = TRUE;
} else {
/* Cap dash start if not connecting to a previous segment */
add_leading_cap (stroker, &sub_start);
}
 
if (remain) {
/* Cap dash end if not at end of segment */
add_trailing_cap (stroker, &sub_end);
} else {
stroker->current_face = sub_end;
stroker->has_current_face = TRUE;
}
} else {
if (stroker->has_current_face) {
/* Cap final face from previous segment */
add_trailing_cap (stroker, &stroker->current_face);
 
stroker->has_current_face = FALSE;
}
}
 
_cairo_stroker_dash_step (&stroker->dash, step_length);
segment.p1 = segment.p2;
}
 
if (stroker->dash.dash_on && ! stroker->has_current_face) {
/* This segment ends on a transition to dash_on, compute a new face
* and add cap for the beginning of the next dash_on step.
*
* Note: this will create a degenerate cap if this is not the last line
* in the path. Whether this behaviour is desirable or not is debatable.
* On one side these degenerate caps can not be reproduced with regular
* path stroking.
* On the other hand, Acroread 7 also produces the degenerate caps.
*/
compute_face (point, &dev_slope, stroker, &stroker->current_face);
 
add_leading_cap (stroker, &stroker->current_face);
 
stroker->has_current_face = TRUE;
} else
stroker->current_face.point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
spline_to (void *closure,
const cairo_point_t *point,
const cairo_slope_t *tangent)
{
struct stroker *stroker = closure;
cairo_stroke_face_t face;
 
if ((tangent->dx | tangent->dy) == 0) {
cairo_point_t t;
 
face = stroker->current_face;
 
face.usr_vector.x = -face.usr_vector.x;
face.usr_vector.y = -face.usr_vector.y;
face.dev_slope.x = -face.dev_slope.x;
face.dev_slope.y = -face.dev_slope.y;
face.dev_vector.dx = -face.dev_vector.dx;
face.dev_vector.dy = -face.dev_vector.dy;
 
t = face.cw;
face.cw = face.ccw;
face.ccw = t;
 
join (stroker, &stroker->current_face, &face);
} else {
cairo_point_t rectangle[4];
 
compute_face (&stroker->current_face.point, tangent, stroker, &face);
 
join (stroker, &stroker->current_face, &face);
 
rectangle[0] = face.cw;
rectangle[1] = face.ccw;
 
rectangle[2].x = point->x - face.point.x;
rectangle[2].y = point->y - face.point.y;
face.point = *point;
translate_point (&face.ccw, &rectangle[2]);
translate_point (&face.cw, &rectangle[2]);
 
rectangle[2] = face.ccw;
rectangle[3] = face.cw;
 
_cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
}
 
stroker->current_face = face;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
struct stroker *stroker = closure;
cairo_line_join_t line_join_save;
cairo_spline_t spline;
cairo_stroke_face_t face;
cairo_status_t status;
 
if (stroker->has_bounds &&
! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
&stroker->line_bounds))
return line_to (closure, d);
 
if (! _cairo_spline_init (&spline, spline_to, stroker,
&stroker->current_face.point, b, c, d))
return line_to (closure, d);
 
compute_face (&stroker->current_face.point, &spline.initial_slope,
stroker, &face);
 
if (stroker->has_current_face) {
/* Join with final face from previous segment */
join (stroker, &stroker->current_face, &face);
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = face;
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
}
stroker->current_face = face;
 
/* Temporarily modify the stroker to use round joins to guarantee
* smooth stroked curves. */
line_join_save = stroker->line_join;
stroker->line_join = CAIRO_LINE_JOIN_ROUND;
 
status = _cairo_spline_decompose (&spline, stroker->tolerance);
 
stroker->line_join = line_join_save;
 
return status;
}
 
static cairo_status_t
curve_to_dashed (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
struct stroker *stroker = closure;
cairo_spline_t spline;
cairo_line_join_t line_join_save;
cairo_spline_add_point_func_t func;
cairo_status_t status;
 
func = (cairo_spline_add_point_func_t)line_to_dashed;
 
if (stroker->has_bounds &&
! _cairo_spline_intersects (&stroker->current_face.point, b, c, b,
&stroker->line_bounds))
return func (closure, d, NULL);
 
if (! _cairo_spline_init (&spline, func, stroker,
&stroker->current_face.point, b, c, d))
return func (closure, d, NULL);
 
/* Temporarily modify the stroker to use round joins to guarantee
* smooth stroked curves. */
line_join_save = stroker->line_join;
stroker->line_join = CAIRO_LINE_JOIN_ROUND;
 
status = _cairo_spline_decompose (&spline, stroker->tolerance);
 
stroker->line_join = line_join_save;
 
return status;
}
 
static cairo_status_t
_close_path (struct stroker *stroker)
{
if (stroker->has_first_face && stroker->has_current_face) {
/* Join first and final faces of sub path */
join (stroker, &stroker->current_face, &stroker->first_face);
} else {
/* Cap the start and end of the sub path as needed */
add_caps (stroker);
}
 
stroker->has_initial_sub_path = FALSE;
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
close_path (void *closure)
{
struct stroker *stroker = closure;
cairo_status_t status;
 
status = line_to (stroker, &stroker->first_point);
if (unlikely (status))
return status;
 
return _close_path (stroker);
}
 
static cairo_status_t
close_path_dashed (void *closure)
{
struct stroker *stroker = closure;
cairo_status_t status;
 
status = line_to_dashed (stroker, &stroker->first_point);
if (unlikely (status))
return status;
 
return _close_path (stroker);
}
 
cairo_int_status_t
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps)
{
struct stroker stroker;
cairo_status_t status;
 
status = stroker_init (&stroker, path, style,
ctm, ctm_inverse, tolerance,
traps);
if (unlikely (status))
return status;
 
if (stroker.dash.dashed)
status = _cairo_path_fixed_interpret (path,
move_to_dashed,
line_to_dashed,
curve_to_dashed,
close_path_dashed,
&stroker);
else
status = _cairo_path_fixed_interpret (path,
move_to,
line_to,
curve_to,
close_path,
&stroker);
assert(status == CAIRO_STATUS_SUCCESS);
add_caps (&stroker);
 
stroker_fini (&stroker);
 
return traps->status;
}
/programs/develop/libraries/cairo/src/cairo-path-stroke-tristrip.c
0,0 → 1,1088
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#define _BSD_SOURCE /* for hypot() */
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
#include "cairo-tristrip-private.h"
 
struct stroker {
cairo_stroke_style_t style;
 
cairo_tristrip_t *strip;
 
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double tolerance;
cairo_bool_t ctm_det_positive;
 
cairo_pen_t pen;
 
cairo_bool_t has_sub_path;
 
cairo_point_t first_point;
 
cairo_bool_t has_current_face;
cairo_stroke_face_t current_face;
 
cairo_bool_t has_first_face;
cairo_stroke_face_t first_face;
 
cairo_box_t limit;
cairo_bool_t has_limits;
};
 
static inline double
normalize_slope (double *dx, double *dy);
 
static void
compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
struct stroker *stroker,
cairo_stroke_face_t *face);
 
static void
translate_point (cairo_point_t *point, const cairo_point_t *offset)
{
point->x += offset->x;
point->y += offset->y;
}
 
static int
slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
{
double c = (dx1 * dy2 - dx2 * dy1);
 
if (c > 0) return 1;
if (c < 0) return -1;
return 0;
}
 
static inline int
range_step (int i, int step, int max)
{
i += step;
if (i < 0)
i = max - 1;
if (i >= max)
i = 0;
return i;
}
 
/*
* Construct a fan around the midpoint using the vertices from pen between
* inpt and outpt.
*/
static void
add_fan (struct stroker *stroker,
const cairo_slope_t *in_vector,
const cairo_slope_t *out_vector,
const cairo_point_t *midpt,
const cairo_point_t *inpt,
const cairo_point_t *outpt,
cairo_bool_t clockwise)
{
int start, stop, step, i, npoints;
 
if (clockwise) {
step = 1;
 
start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
in_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
in_vector) < 0)
start = range_step (start, 1, stroker->pen.num_vertices);
 
stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
out_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
out_vector) > 0)
{
stop = range_step (stop, -1, stroker->pen.num_vertices);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
in_vector) < 0)
return;
}
 
npoints = stop - start;
} else {
step = -1;
 
start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
in_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
in_vector) < 0)
start = range_step (start, -1, stroker->pen.num_vertices);
 
stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
out_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
out_vector) > 0)
{
stop = range_step (stop, 1, stroker->pen.num_vertices);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
in_vector) < 0)
return;
}
 
npoints = start - stop;
}
stop = range_step (stop, step, stroker->pen.num_vertices);
if (npoints < 0)
npoints += stroker->pen.num_vertices;
if (npoints <= 1)
return;
 
for (i = start;
i != stop;
i = range_step (i, step, stroker->pen.num_vertices))
{
cairo_point_t p = *midpt;
translate_point (&p, &stroker->pen.vertices[i].point);
//contour_add_point (stroker, c, &p);
}
}
 
static int
join_is_clockwise (const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out)
{
return _cairo_slope_compare (&in->dev_vector, &out->dev_vector) < 0;
}
 
static void
inner_join (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out,
int clockwise)
{
const cairo_point_t *outpt;
 
if (clockwise) {
outpt = &out->ccw;
} else {
outpt = &out->cw;
}
//contour_add_point (stroker, inner, &in->point);
//contour_add_point (stroker, inner, outpt);
}
 
static void
inner_close (struct stroker *stroker,
const cairo_stroke_face_t *in,
cairo_stroke_face_t *out)
{
const cairo_point_t *inpt;
 
if (join_is_clockwise (in, out)) {
inpt = &out->ccw;
} else {
inpt = &out->cw;
}
 
//contour_add_point (stroker, inner, &in->point);
//contour_add_point (stroker, inner, inpt);
//*_cairo_contour_first_point (&inner->contour) =
//*_cairo_contour_last_point (&inner->contour);
}
 
static void
outer_close (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out)
{
const cairo_point_t *inpt, *outpt;
int clockwise;
 
if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
{
return;
}
clockwise = join_is_clockwise (in, out);
if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
} else {
inpt = &in->ccw;
outpt = &out->ccw;
}
 
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
add_fan (stroker,
&in->dev_vector,
&out->dev_vector,
&in->point, inpt, outpt,
clockwise);
break;
 
case CAIRO_LINE_JOIN_MITER:
default: {
/* dot product of incoming slope vector with outgoing slope vector */
double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
-in->usr_vector.y * out->usr_vector.y;
double ml = stroker->style.miter_limit;
 
/* Check the miter limit -- lines meeting at an acute angle
* can generate long miters, the limit converts them to bevel
*
* Consider the miter join formed when two line segments
* meet at an angle psi:
*
* /.\
* /. .\
* /./ \.\
* /./psi\.\
*
* We can zoom in on the right half of that to see:
*
* |\
* | \ psi/2
* | \
* | \
* | \
* | \
* miter \
* length \
* | \
* | .\
* | . \
* |. line \
* \ width \
* \ \
*
*
* The right triangle in that figure, (the line-width side is
* shown faintly with three '.' characters), gives us the
* following expression relating miter length, angle and line
* width:
*
* 1 /sin (psi/2) = miter_length / line_width
*
* The right-hand side of this relationship is the same ratio
* in which the miter limit (ml) is expressed. We want to know
* when the miter length is within the miter limit. That is
* when the following condition holds:
*
* 1/sin(psi/2) <= ml
* 1 <= ml sin(psi/2)
* 1 <= ml² sin²(psi/2)
* 2 <= ml² 2 sin²(psi/2)
* 2·sin²(psi/2) = 1-cos(psi)
* 2 <= ml² (1-cos(psi))
*
* in · out = |in| |out| cos (psi)
*
* in and out are both unit vectors, so:
*
* in · out = cos (psi)
*
* 2 <= ml² (1 - in · out)
*
*/
if (2 <= ml * ml * (1 - in_dot_out)) {
double x1, y1, x2, y2;
double mx, my;
double dx1, dx2, dy1, dy2;
double ix, iy;
double fdx1, fdy1, fdx2, fdy2;
double mdx, mdy;
 
/*
* we've got the points already transformed to device
* space, but need to do some computation with them and
* also need to transform the slope from user space to
* device space
*/
/* outer point of incoming line face */
x1 = _cairo_fixed_to_double (inpt->x);
y1 = _cairo_fixed_to_double (inpt->y);
dx1 = in->usr_vector.x;
dy1 = in->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
 
/* outer point of outgoing line face */
x2 = _cairo_fixed_to_double (outpt->x);
y2 = _cairo_fixed_to_double (outpt->y);
dx2 = out->usr_vector.x;
dy2 = out->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
 
/*
* Compute the location of the outer corner of the miter.
* That's pretty easy -- just the intersection of the two
* outer edges. We've got slopes and points on each
* of those edges. Compute my directly, then compute
* mx by using the edge with the larger dy; that avoids
* dividing by values close to zero.
*/
my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
(dx1 * dy2 - dx2 * dy1));
if (fabs (dy1) >= fabs (dy2))
mx = (my - y1) * dx1 / dy1 + x1;
else
mx = (my - y2) * dx2 / dy2 + x2;
 
/*
* When the two outer edges are nearly parallel, slight
* perturbations in the position of the outer points of the lines
* caused by representing them in fixed point form can cause the
* intersection point of the miter to move a large amount. If
* that moves the miter intersection from between the two faces,
* then draw a bevel instead.
*/
 
ix = _cairo_fixed_to_double (in->point.x);
iy = _cairo_fixed_to_double (in->point.y);
 
/* slope of one face */
fdx1 = x1 - ix; fdy1 = y1 - iy;
 
/* slope of the other face */
fdx2 = x2 - ix; fdy2 = y2 - iy;
 
/* slope from the intersection to the miter point */
mdx = mx - ix; mdy = my - iy;
 
/*
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
cairo_point_t p;
 
p.x = _cairo_fixed_from_double (mx);
p.y = _cairo_fixed_from_double (my);
 
//*_cairo_contour_last_point (&outer->contour) = p;
//*_cairo_contour_first_point (&outer->contour) = p;
return;
}
}
break;
}
 
case CAIRO_LINE_JOIN_BEVEL:
break;
}
//contour_add_point (stroker, outer, outpt);
}
 
static void
outer_join (struct stroker *stroker,
const cairo_stroke_face_t *in,
const cairo_stroke_face_t *out,
int clockwise)
{
const cairo_point_t *inpt, *outpt;
 
if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
{
return;
}
if (clockwise) {
inpt = &in->cw;
outpt = &out->cw;
} else {
inpt = &in->ccw;
outpt = &out->ccw;
}
 
switch (stroker->style.line_join) {
case CAIRO_LINE_JOIN_ROUND:
/* construct a fan around the common midpoint */
add_fan (stroker,
&in->dev_vector,
&out->dev_vector,
&in->point, inpt, outpt,
clockwise);
break;
 
case CAIRO_LINE_JOIN_MITER:
default: {
/* dot product of incoming slope vector with outgoing slope vector */
double in_dot_out = -in->usr_vector.x * out->usr_vector.x +
-in->usr_vector.y * out->usr_vector.y;
double ml = stroker->style.miter_limit;
 
/* Check the miter limit -- lines meeting at an acute angle
* can generate long miters, the limit converts them to bevel
*
* Consider the miter join formed when two line segments
* meet at an angle psi:
*
* /.\
* /. .\
* /./ \.\
* /./psi\.\
*
* We can zoom in on the right half of that to see:
*
* |\
* | \ psi/2
* | \
* | \
* | \
* | \
* miter \
* length \
* | \
* | .\
* | . \
* |. line \
* \ width \
* \ \
*
*
* The right triangle in that figure, (the line-width side is
* shown faintly with three '.' characters), gives us the
* following expression relating miter length, angle and line
* width:
*
* 1 /sin (psi/2) = miter_length / line_width
*
* The right-hand side of this relationship is the same ratio
* in which the miter limit (ml) is expressed. We want to know
* when the miter length is within the miter limit. That is
* when the following condition holds:
*
* 1/sin(psi/2) <= ml
* 1 <= ml sin(psi/2)
* 1 <= ml² sin²(psi/2)
* 2 <= ml² 2 sin²(psi/2)
* 2·sin²(psi/2) = 1-cos(psi)
* 2 <= ml² (1-cos(psi))
*
* in · out = |in| |out| cos (psi)
*
* in and out are both unit vectors, so:
*
* in · out = cos (psi)
*
* 2 <= ml² (1 - in · out)
*
*/
if (2 <= ml * ml * (1 - in_dot_out)) {
double x1, y1, x2, y2;
double mx, my;
double dx1, dx2, dy1, dy2;
double ix, iy;
double fdx1, fdy1, fdx2, fdy2;
double mdx, mdy;
 
/*
* we've got the points already transformed to device
* space, but need to do some computation with them and
* also need to transform the slope from user space to
* device space
*/
/* outer point of incoming line face */
x1 = _cairo_fixed_to_double (inpt->x);
y1 = _cairo_fixed_to_double (inpt->y);
dx1 = in->usr_vector.x;
dy1 = in->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);
 
/* outer point of outgoing line face */
x2 = _cairo_fixed_to_double (outpt->x);
y2 = _cairo_fixed_to_double (outpt->y);
dx2 = out->usr_vector.x;
dy2 = out->usr_vector.y;
cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
 
/*
* Compute the location of the outer corner of the miter.
* That's pretty easy -- just the intersection of the two
* outer edges. We've got slopes and points on each
* of those edges. Compute my directly, then compute
* mx by using the edge with the larger dy; that avoids
* dividing by values close to zero.
*/
my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
(dx1 * dy2 - dx2 * dy1));
if (fabs (dy1) >= fabs (dy2))
mx = (my - y1) * dx1 / dy1 + x1;
else
mx = (my - y2) * dx2 / dy2 + x2;
 
/*
* When the two outer edges are nearly parallel, slight
* perturbations in the position of the outer points of the lines
* caused by representing them in fixed point form can cause the
* intersection point of the miter to move a large amount. If
* that moves the miter intersection from between the two faces,
* then draw a bevel instead.
*/
 
ix = _cairo_fixed_to_double (in->point.x);
iy = _cairo_fixed_to_double (in->point.y);
 
/* slope of one face */
fdx1 = x1 - ix; fdy1 = y1 - iy;
 
/* slope of the other face */
fdx2 = x2 - ix; fdy2 = y2 - iy;
 
/* slope from the intersection to the miter point */
mdx = mx - ix; mdy = my - iy;
 
/*
* Make sure the miter point line lies between the two
* faces by comparing the slopes
*/
if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
slope_compare_sgn (fdx2, fdy2, mdx, mdy))
{
cairo_point_t p;
 
p.x = _cairo_fixed_from_double (mx);
p.y = _cairo_fixed_from_double (my);
 
//*_cairo_contour_last_point (&outer->contour) = p;
return;
}
}
break;
}
 
case CAIRO_LINE_JOIN_BEVEL:
break;
}
//contour_add_point (stroker,outer, outpt);
}
 
static void
add_cap (struct stroker *stroker,
const cairo_stroke_face_t *f)
{
switch (stroker->style.line_cap) {
case CAIRO_LINE_CAP_ROUND: {
cairo_slope_t slope;
 
slope.dx = -f->dev_vector.dx;
slope.dy = -f->dev_vector.dy;
 
add_fan (stroker, &f->dev_vector, &slope,
&f->point, &f->ccw, &f->cw,
FALSE);
break;
}
 
case CAIRO_LINE_CAP_SQUARE: {
double dx, dy;
cairo_slope_t fvector;
cairo_point_t quad[4];
 
dx = f->usr_vector.x;
dy = f->usr_vector.y;
dx *= stroker->style.line_width / 2.0;
dy *= stroker->style.line_width / 2.0;
cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
fvector.dx = _cairo_fixed_from_double (dx);
fvector.dy = _cairo_fixed_from_double (dy);
 
quad[0] = f->ccw;
quad[1].x = f->ccw.x + fvector.dx;
quad[1].y = f->ccw.y + fvector.dy;
quad[2].x = f->cw.x + fvector.dx;
quad[2].y = f->cw.y + fvector.dy;
quad[3] = f->cw;
 
//contour_add_point (stroker, c, &quad[1]);
//contour_add_point (stroker, c, &quad[2]);
}
 
case CAIRO_LINE_CAP_BUTT:
default:
break;
}
//contour_add_point (stroker, c, &f->cw);
}
 
static void
add_leading_cap (struct stroker *stroker,
const cairo_stroke_face_t *face)
{
cairo_stroke_face_t reversed;
cairo_point_t t;
 
reversed = *face;
 
/* The initial cap needs an outward facing vector. Reverse everything */
reversed.usr_vector.x = -reversed.usr_vector.x;
reversed.usr_vector.y = -reversed.usr_vector.y;
reversed.dev_vector.dx = -reversed.dev_vector.dx;
reversed.dev_vector.dy = -reversed.dev_vector.dy;
 
t = reversed.cw;
reversed.cw = reversed.ccw;
reversed.ccw = t;
 
add_cap (stroker, &reversed);
}
 
static void
add_trailing_cap (struct stroker *stroker,
const cairo_stroke_face_t *face)
{
add_cap (stroker, face);
}
 
static inline double
normalize_slope (double *dx, double *dy)
{
double dx0 = *dx, dy0 = *dy;
double mag;
 
assert (dx0 != 0.0 || dy0 != 0.0);
 
if (dx0 == 0.0) {
*dx = 0.0;
if (dy0 > 0.0) {
mag = dy0;
*dy = 1.0;
} else {
mag = -dy0;
*dy = -1.0;
}
} else if (dy0 == 0.0) {
*dy = 0.0;
if (dx0 > 0.0) {
mag = dx0;
*dx = 1.0;
} else {
mag = -dx0;
*dx = -1.0;
}
} else {
mag = hypot (dx0, dy0);
*dx = dx0 / mag;
*dy = dy0 / mag;
}
 
return mag;
}
 
static void
compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
struct stroker *stroker,
cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
double slope_dx, slope_dy;
 
slope_dx = _cairo_fixed_to_double (dev_slope->dx);
slope_dy = _cairo_fixed_to_double (dev_slope->dy);
face->length = normalize_slope (&slope_dx, &slope_dy);
face->dev_slope.x = slope_dx;
face->dev_slope.y = slope_dy;
 
/*
* rotate to get a line_width/2 vector along the face, note that
* the vector must be rotated the right direction in device space,
* but by 90° in user space. So, the rotation depends on
* whether the ctm reflects or not, and that can be determined
* by looking at the determinant of the matrix.
*/
if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
/* Normalize the matrix! */
cairo_matrix_transform_distance (stroker->ctm_inverse,
&slope_dx, &slope_dy);
normalize_slope (&slope_dx, &slope_dy);
 
if (stroker->ctm_det_positive) {
face_dx = - slope_dy * (stroker->style.line_width / 2.0);
face_dy = slope_dx * (stroker->style.line_width / 2.0);
} else {
face_dx = slope_dy * (stroker->style.line_width / 2.0);
face_dy = - slope_dx * (stroker->style.line_width / 2.0);
}
 
/* back to device space */
cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
} else {
face_dx = - slope_dy * (stroker->style.line_width / 2.0);
face_dy = slope_dx * (stroker->style.line_width / 2.0);
}
 
offset_ccw.x = _cairo_fixed_from_double (face_dx);
offset_ccw.y = _cairo_fixed_from_double (face_dy);
offset_cw.x = -offset_ccw.x;
offset_cw.y = -offset_ccw.y;
 
face->ccw = *point;
translate_point (&face->ccw, &offset_ccw);
 
face->point = *point;
 
face->cw = *point;
translate_point (&face->cw, &offset_cw);
 
face->usr_vector.x = slope_dx;
face->usr_vector.y = slope_dy;
 
face->dev_vector = *dev_slope;
}
 
static void
add_caps (struct stroker *stroker)
{
/* check for a degenerative sub_path */
if (stroker->has_sub_path &&
! stroker->has_first_face &&
! stroker->has_current_face &&
stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
{
/* pick an arbitrary slope to use */
cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
cairo_stroke_face_t face;
 
/* arbitrarily choose first_point */
compute_face (&stroker->first_point, &slope, stroker, &face);
 
add_leading_cap (stroker, &face);
add_trailing_cap (stroker, &face);
 
/* ensure the circle is complete */
//_cairo_contour_add_point (&stroker->ccw.contour,
//_cairo_contour_first_point (&stroker->ccw.contour));
} else {
if (stroker->has_current_face)
add_trailing_cap (stroker, &stroker->current_face);
 
//_cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour);
//_cairo_contour_reset (&stroker->ccw.contour);
 
if (stroker->has_first_face) {
//_cairo_contour_add_point (&stroker->ccw.contour,
//&stroker->first_face.cw);
add_leading_cap (stroker, &stroker->first_face);
//_cairo_polygon_add_contour (stroker->polygon,
//&stroker->ccw.contour);
//_cairo_contour_reset (&stroker->ccw.contour);
}
}
}
 
static cairo_status_t
move_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
 
/* Cap the start and end of the previous sub path as needed */
add_caps (stroker);
 
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
stroker->has_sub_path = FALSE;
 
stroker->first_point = *point;
 
stroker->current_face.point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
line_to (void *closure,
const cairo_point_t *point)
{
struct stroker *stroker = closure;
cairo_stroke_face_t start;
cairo_point_t *p1 = &stroker->current_face.point;
cairo_slope_t dev_slope;
 
stroker->has_sub_path = TRUE;
 
if (p1->x == point->x && p1->y == point->y)
return CAIRO_STATUS_SUCCESS;
 
_cairo_slope_init (&dev_slope, p1, point);
compute_face (p1, &dev_slope, stroker, &start);
 
if (stroker->has_current_face) {
int clockwise = join_is_clockwise (&stroker->current_face, &start);
/* Join with final face from previous segment */
outer_join (stroker, &stroker->current_face, &start, clockwise);
inner_join (stroker, &stroker->current_face, &start, clockwise);
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = start;
_cairo_tristrip_move_to (stroker->strip, &start.cw);
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
 
_cairo_tristrip_add_point (stroker->strip, &start.cw);
_cairo_tristrip_add_point (stroker->strip, &start.ccw);
}
 
stroker->current_face = start;
stroker->current_face.point = *point;
stroker->current_face.ccw.x += dev_slope.dx;
stroker->current_face.ccw.y += dev_slope.dy;
stroker->current_face.cw.x += dev_slope.dx;
stroker->current_face.cw.y += dev_slope.dy;
 
_cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw);
_cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
spline_to (void *closure,
const cairo_point_t *point,
const cairo_slope_t *tangent)
{
struct stroker *stroker = closure;
cairo_stroke_face_t face;
 
if (tangent->dx == 0 && tangent->dy == 0) {
const cairo_point_t *inpt, *outpt;
cairo_point_t t;
int clockwise;
 
face = stroker->current_face;
 
face.usr_vector.x = -face.usr_vector.x;
face.usr_vector.y = -face.usr_vector.y;
face.dev_vector.dx = -face.dev_vector.dx;
face.dev_vector.dy = -face.dev_vector.dy;
 
t = face.cw;
face.cw = face.ccw;
face.ccw = t;
 
clockwise = join_is_clockwise (&stroker->current_face, &face);
if (clockwise) {
inpt = &stroker->current_face.cw;
outpt = &face.cw;
} else {
inpt = &stroker->current_face.ccw;
outpt = &face.ccw;
}
 
add_fan (stroker,
&stroker->current_face.dev_vector,
&face.dev_vector,
&stroker->current_face.point, inpt, outpt,
clockwise);
} else {
compute_face (point, tangent, stroker, &face);
 
if (face.dev_slope.x * stroker->current_face.dev_slope.x +
face.dev_slope.y * stroker->current_face.dev_slope.y < 0)
{
const cairo_point_t *inpt, *outpt;
int clockwise = join_is_clockwise (&stroker->current_face, &face);
 
stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x;
stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y;
//contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw);
 
stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x;
stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y;
//contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw);
 
if (clockwise) {
inpt = &stroker->current_face.cw;
outpt = &face.cw;
} else {
inpt = &stroker->current_face.ccw;
outpt = &face.ccw;
}
add_fan (stroker,
&stroker->current_face.dev_vector,
&face.dev_vector,
&stroker->current_face.point, inpt, outpt,
clockwise);
}
 
_cairo_tristrip_add_point (stroker->strip, &face.cw);
_cairo_tristrip_add_point (stroker->strip, &face.ccw);
}
 
stroker->current_face = face;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
curve_to (void *closure,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
struct stroker *stroker = closure;
cairo_spline_t spline;
cairo_stroke_face_t face;
 
if (stroker->has_limits) {
if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d,
&stroker->limit))
return line_to (closure, d);
}
 
if (! _cairo_spline_init (&spline, spline_to, stroker,
&stroker->current_face.point, b, c, d))
return line_to (closure, d);
 
compute_face (&stroker->current_face.point, &spline.initial_slope,
stroker, &face);
 
if (stroker->has_current_face) {
int clockwise = join_is_clockwise (&stroker->current_face, &face);
/* Join with final face from previous segment */
outer_join (stroker, &stroker->current_face, &face, clockwise);
inner_join (stroker, &stroker->current_face, &face, clockwise);
} else {
if (! stroker->has_first_face) {
/* Save sub path's first face in case needed for closing join */
stroker->first_face = face;
_cairo_tristrip_move_to (stroker->strip, &face.cw);
stroker->has_first_face = TRUE;
}
stroker->has_current_face = TRUE;
 
_cairo_tristrip_add_point (stroker->strip, &face.cw);
_cairo_tristrip_add_point (stroker->strip, &face.ccw);
}
stroker->current_face = face;
 
return _cairo_spline_decompose (&spline, stroker->tolerance);
}
 
static cairo_status_t
close_path (void *closure)
{
struct stroker *stroker = closure;
cairo_status_t status;
 
status = line_to (stroker, &stroker->first_point);
if (unlikely (status))
return status;
 
if (stroker->has_first_face && stroker->has_current_face) {
/* Join first and final faces of sub path */
outer_close (stroker, &stroker->current_face, &stroker->first_face);
inner_close (stroker, &stroker->current_face, &stroker->first_face);
} else {
/* Cap the start and end of the sub path as needed */
add_caps (stroker);
}
 
stroker->has_sub_path = FALSE;
stroker->has_first_face = FALSE;
stroker->has_current_face = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
const cairo_stroke_style_t*style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_tristrip_t *strip)
{
struct stroker stroker;
cairo_int_status_t status;
int i;
 
if (style->num_dashes)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
stroker.style = *style;
stroker.ctm = ctm;
stroker.ctm_inverse = ctm_inverse;
stroker.tolerance = tolerance;
 
stroker.ctm_det_positive =
_cairo_matrix_compute_determinant (ctm) >= 0.0;
 
status = _cairo_pen_init (&stroker.pen,
style->line_width / 2.0,
tolerance, ctm);
if (unlikely (status))
return status;
 
if (stroker.pen.num_vertices <= 1)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
stroker.has_current_face = FALSE;
stroker.has_first_face = FALSE;
stroker.has_sub_path = FALSE;
 
stroker.has_limits = strip->num_limits > 0;
stroker.limit = strip->limits[0];
for (i = 1; i < strip->num_limits; i++)
_cairo_box_add_box (&stroker.limit, &strip->limits[i]);
 
stroker.strip = strip;
 
status = _cairo_path_fixed_interpret (path,
move_to,
line_to,
curve_to,
close_path,
&stroker);
/* Cap the start and end of the final sub path as needed */
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
add_caps (&stroker);
 
_cairo_pen_fini (&stroker.pen);
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-path-stroke.c
39,29 → 39,22
#define _BSD_SOURCE /* for hypot() */
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-private.h"
#include "cairo-stroke-dash-private.h"
#include "cairo-traps-private.h"
 
typedef struct _cairo_stroker_dash {
cairo_bool_t dashed;
unsigned int dash_index;
cairo_bool_t dash_on;
cairo_bool_t dash_starts_on;
double dash_remain;
 
double dash_offset;
const double *dashes;
unsigned int num_dashes;
} cairo_stroker_dash_t;
 
typedef struct cairo_stroker {
cairo_stroke_style_t style;
 
const cairo_matrix_t *ctm;
const cairo_matrix_t *ctm_inverse;
double half_line_width;
double tolerance;
double spline_cusp_tolerance;
double ctm_determinant;
cairo_bool_t ctm_det_positive;
 
98,66 → 91,44
} cairo_stroker_t;
 
static void
_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
_cairo_stroker_limit (cairo_stroker_t *stroker,
const cairo_path_fixed_t *path,
const cairo_box_t *boxes,
int num_boxes)
{
double offset;
cairo_bool_t on = TRUE;
unsigned int i = 0;
double dx, dy;
cairo_fixed_t fdx, fdy;
 
if (! dash->dashed)
return;
stroker->has_bounds = TRUE;
_cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
 
offset = dash->dash_offset;
/* Extend the bounds in each direction to account for the maximum area
* we might generate trapezoids, to capture line segments that are outside
* of the bounds but which might generate rendering that's within bounds.
*/
 
/* We stop searching for a starting point as soon as the
offset reaches zero. Otherwise when an initial dash
segment shrinks to zero it will be skipped over. */
while (offset > 0.0 && offset >= dash->dashes[i]) {
offset -= dash->dashes[i];
on = !on;
if (++i == dash->num_dashes)
i = 0;
}
_cairo_stroke_style_max_distance_from_path (&stroker->style, path,
stroker->ctm, &dx, &dy);
 
dash->dash_index = i;
dash->dash_on = dash->dash_starts_on = on;
dash->dash_remain = dash->dashes[i] - offset;
}
fdx = _cairo_fixed_from_double (dx);
fdy = _cairo_fixed_from_double (dy);
 
static void
_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
{
dash->dash_remain -= step;
if (dash->dash_remain <= 0.) {
if (++dash->dash_index == dash->num_dashes)
dash->dash_index = 0;
stroker->bounds.p1.x -= fdx;
stroker->bounds.p2.x += fdx;
 
dash->dash_on = ! dash->dash_on;
dash->dash_remain = dash->dashes[dash->dash_index];
stroker->bounds.p1.y -= fdy;
stroker->bounds.p2.y += fdy;
}
}
 
static void
_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
const cairo_stroke_style_t *style)
{
dash->dashed = style->dash != NULL;
if (! dash->dashed)
return;
 
dash->dashes = style->dash;
dash->num_dashes = style->num_dashes;
dash->dash_offset = style->dash_offset;
 
_cairo_stroker_dash_start (dash);
}
 
static cairo_status_t
_cairo_stroker_init (cairo_stroker_t *stroker,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance)
double tolerance,
const cairo_box_t *limits,
int num_limits)
{
cairo_status_t status;
 
165,18 → 136,28
stroker->ctm = ctm;
stroker->ctm_inverse = ctm_inverse;
stroker->tolerance = tolerance;
stroker->half_line_width = stroke_style->line_width / 2.0;
 
/* To test whether we need to join two segments of a spline using
* a round-join or a bevel-join, we can inspect the angle between the
* two segments. If the difference between the chord distance
* (half-line-width times the cosine of the bisection angle) and the
* half-line-width itself is greater than tolerance then we need to
* inject a point.
*/
stroker->spline_cusp_tolerance = 1 - tolerance / stroker->half_line_width;
stroker->spline_cusp_tolerance *= stroker->spline_cusp_tolerance;
stroker->spline_cusp_tolerance *= 2;
stroker->spline_cusp_tolerance -= 1;
 
stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;
 
status = _cairo_pen_init (&stroker->pen,
stroke_style->line_width / 2.0,
tolerance, ctm);
stroker->half_line_width, tolerance, ctm);
if (unlikely (status))
return status;
 
stroker->has_bounds = FALSE;
 
stroker->has_current_face = FALSE;
stroker->has_first_face = FALSE;
stroker->has_initial_sub_path = FALSE;
185,39 → 166,14
 
stroker->add_external_edge = NULL;
 
stroker->has_bounds = FALSE;
if (num_limits)
_cairo_stroker_limit (stroker, path, limits, num_limits);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_stroker_limit (cairo_stroker_t *stroker,
const cairo_box_t *boxes,
int num_boxes)
{
double dx, dy;
cairo_fixed_t fdx, fdy;
 
stroker->has_bounds = TRUE;
_cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
 
/* Extend the bounds in each direction to account for the maximum area
* we might generate trapezoids, to capture line segments that are outside
* of the bounds but which might generate rendering that's within bounds.
*/
 
_cairo_stroke_style_max_distance_from_path (&stroker->style, stroker->ctm,
&dx, &dy);
 
fdx = _cairo_fixed_from_double (dx);
fdy = _cairo_fixed_from_double (dy);
 
stroker->bounds.p1.x -= fdx;
stroker->bounds.p2.x += fdx;
 
stroker->bounds.p1.y -= fdy;
stroker->bounds.p2.y += fdy;
}
 
static void
_cairo_stroker_fini (cairo_stroker_t *stroker)
{
_cairo_pen_fini (&stroker->pen);
243,11 → 199,11
}
 
/**
* _cairo_slope_compare_sgn
* _cairo_slope_compare_sgn:
*
* Return -1, 0 or 1 depending on the relative slopes of
* two lines.
*/
**/
static int
_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
{
283,100 → 239,117
cairo_bool_t clockwise)
{
cairo_point_t stack_points[64], *points = stack_points;
int start, stop, step, i, npoints;
cairo_pen_t *pen = &stroker->pen;
int start, stop, num_points = 0;
cairo_status_t status;
 
if (stroker->has_bounds &&
! _cairo_box_contains_point (&stroker->bounds, midpt))
goto BEVEL;
 
assert (stroker->pen.num_vertices);
 
if (clockwise) {
step = -1;
_cairo_pen_find_active_ccw_vertices (pen,
in_vector, out_vector,
&start, &stop);
if (stroker->add_external_edge) {
cairo_point_t last;
last = *inpt;
while (start != stop) {
cairo_point_t p = *midpt;
_translate_point (&p, &pen->vertices[start].point);
 
start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
in_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw,
in_vector) < 0)
start = _range_step (start, -1, stroker->pen.num_vertices);
status = stroker->add_external_edge (stroker->closure,
&last, &p);
if (unlikely (status))
return status;
last = p;
 
stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen,
out_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
out_vector) > 0)
{
stop = _range_step (stop, 1, stroker->pen.num_vertices);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
in_vector) < 0)
{
if (start-- == 0)
start += pen->num_vertices;
}
status = stroker->add_external_edge (stroker->closure,
&last, outpt);
} else {
if (start == stop)
goto BEVEL;
 
num_points = stop - start;
if (num_points < 0)
num_points += pen->num_vertices;
num_points += 2;
if (num_points > ARRAY_LENGTH(stack_points)) {
points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
if (unlikely (points == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
npoints = start - stop;
} else {
step = 1;
points[0] = *inpt;
num_points = 1;
while (start != stop) {
points[num_points] = *midpt;
_translate_point (&points[num_points], &pen->vertices[start].point);
num_points++;
 
start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
in_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw,
in_vector) < 0)
start = _range_step (start, 1, stroker->pen.num_vertices);
 
stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen,
out_vector);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw,
out_vector) > 0)
{
stop = _range_step (stop, -1, stroker->pen.num_vertices);
if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw,
in_vector) < 0)
{
goto BEVEL;
if (start-- == 0)
start += pen->num_vertices;
}
points[num_points++] = *outpt;
}
} else {
_cairo_pen_find_active_cw_vertices (pen,
in_vector, out_vector,
&start, &stop);
if (stroker->add_external_edge) {
cairo_point_t last;
last = *inpt;
while (start != stop) {
cairo_point_t p = *midpt;
_translate_point (&p, &pen->vertices[start].point);
 
npoints = stop - start;
status = stroker->add_external_edge (stroker->closure,
&p, &last);
if (unlikely (status))
return status;
last = p;
 
if (++start == pen->num_vertices)
start = 0;
}
stop = _range_step (stop, step, stroker->pen.num_vertices);
 
if (npoints < 0)
npoints += stroker->pen.num_vertices;
npoints += 3;
 
if (npoints <= 1)
status = stroker->add_external_edge (stroker->closure,
outpt, &last);
} else {
if (start == stop)
goto BEVEL;
 
if (npoints > ARRAY_LENGTH (stack_points)) {
points = _cairo_malloc_ab (npoints, sizeof (cairo_point_t));
num_points = stop - start;
if (num_points < 0)
num_points += pen->num_vertices;
num_points += 2;
if (num_points > ARRAY_LENGTH(stack_points)) {
points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
if (unlikely (points == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
points[0] = *inpt;
num_points = 1;
while (start != stop) {
points[num_points] = *midpt;
_translate_point (&points[num_points], &pen->vertices[start].point);
num_points++;
 
/* Construct the fan. */
npoints = 0;
points[npoints++] = *inpt;
for (i = start;
i != stop;
i = _range_step (i, step, stroker->pen.num_vertices))
{
points[npoints] = *midpt;
_translate_point (&points[npoints], &stroker->pen.vertices[i].point);
npoints++;
if (++start == pen->num_vertices)
start = 0;
}
points[npoints++] = *outpt;
 
if (stroker->add_external_edge != NULL) {
for (i = 0; i < npoints - 1; i++) {
if (clockwise) {
status = stroker->add_external_edge (stroker->closure,
&points[i], &points[i+1]);
} else {
status = stroker->add_external_edge (stroker->closure,
&points[i+1], &points[i]);
points[num_points++] = *outpt;
}
if (unlikely (status))
break;
}
} else {
 
if (num_points) {
status = stroker->add_triangle_fan (stroker->closure,
midpt, points, npoints);
midpt, points, num_points);
}
 
if (points != stack_points)
678,8 → 651,8
 
dx = f->usr_vector.x;
dy = f->usr_vector.y;
dx *= stroker->style.line_width / 2.0;
dy *= stroker->style.line_width / 2.0;
dx *= stroker->half_line_width;
dy *= stroker->half_line_width;
cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
fvector.dx = _cairo_fixed_from_double (dx);
fvector.dy = _cairo_fixed_from_double (dy);
801,9 → 774,12
}
 
static void
_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope,
double slope_dx, double slope_dy,
cairo_stroker_t *stroker, cairo_stroke_face_t *face)
_compute_face (const cairo_point_t *point,
const cairo_slope_t *dev_slope,
double slope_dx,
double slope_dy,
cairo_stroker_t *stroker,
cairo_stroke_face_t *face)
{
double face_dx, face_dy;
cairo_point_t offset_ccw, offset_cw;
817,13 → 793,13
*/
if (stroker->ctm_det_positive)
{
face_dx = - slope_dy * (stroker->style.line_width / 2.0);
face_dy = slope_dx * (stroker->style.line_width / 2.0);
face_dx = - slope_dy * stroker->half_line_width;
face_dy = slope_dx * stroker->half_line_width;
}
else
{
face_dx = slope_dy * (stroker->style.line_width / 2.0);
face_dy = - slope_dx * (stroker->style.line_width / 2.0);
face_dx = slope_dy * stroker->half_line_width;
face_dy = - slope_dx * stroker->half_line_width;
}
 
/* back to device space */
857,7 → 833,7
if (stroker->has_initial_sub_path
&& ! stroker->has_first_face
&& ! stroker->has_current_face
&& stroker->style.line_cap == CAIRO_LINE_JOIN_ROUND)
&& stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
{
/* pick an arbitrary slope to use */
double dx = 1.0, dy = 0.0;
1019,6 → 995,91
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_stroker_spline_to (void *closure,
const cairo_point_t *point,
const cairo_slope_t *tangent)
{
cairo_stroker_t *stroker = closure;
cairo_stroke_face_t new_face;
double slope_dx, slope_dy;
cairo_point_t points[3];
cairo_point_t intersect_point;
 
stroker->has_initial_sub_path = TRUE;
 
if (stroker->current_point.x == point->x &&
stroker->current_point.y == point->y)
return CAIRO_STATUS_SUCCESS;
 
slope_dx = _cairo_fixed_to_double (tangent->dx);
slope_dy = _cairo_fixed_to_double (tangent->dy);
 
if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
stroker->ctm_inverse, NULL))
return CAIRO_STATUS_SUCCESS;
 
_compute_face (point, tangent,
slope_dx, slope_dy,
stroker, &new_face);
 
assert (stroker->has_current_face);
 
if ((new_face.dev_slope.x * stroker->current_face.dev_slope.x +
new_face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) {
 
const cairo_point_t *inpt, *outpt;
int clockwise = _cairo_stroker_join_is_clockwise (&new_face,
&stroker->current_face);
 
if (clockwise) {
inpt = &stroker->current_face.cw;
outpt = &new_face.cw;
} else {
inpt = &stroker->current_face.ccw;
outpt = &new_face.ccw;
}
 
_tessellate_fan (stroker,
&stroker->current_face.dev_vector,
&new_face.dev_vector,
&stroker->current_face.point,
inpt, outpt,
clockwise);
}
 
if (_slow_segment_intersection (&stroker->current_face.cw,
&stroker->current_face.ccw,
&new_face.cw,
&new_face.ccw,
&intersect_point)) {
points[0] = stroker->current_face.ccw;
points[1] = new_face.ccw;
points[2] = intersect_point;
stroker->add_triangle (stroker->closure, points);
 
points[0] = stroker->current_face.cw;
points[1] = new_face.cw;
stroker->add_triangle (stroker->closure, points);
} else {
points[0] = stroker->current_face.ccw;
points[1] = stroker->current_face.cw;
points[2] = new_face.cw;
stroker->add_triangle (stroker->closure, points);
 
points[0] = stroker->current_face.ccw;
points[1] = new_face.cw;
points[2] = new_face.ccw;
stroker->add_triangle (stroker->closure, points);
}
 
stroker->current_face = new_face;
stroker->has_current_face = TRUE;
stroker->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
/*
* Dashed lines. Cap each dash end, join around turns when on
*/
1175,18 → 1236,27
cairo_line_join_t line_join_save;
cairo_stroke_face_t face;
double slope_dx, slope_dy;
cairo_path_fixed_line_to_func_t *line_to;
cairo_spline_add_point_func_t line_to;
cairo_spline_add_point_func_t spline_to;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
line_to = stroker->dash.dashed ?
_cairo_stroker_line_to_dashed :
_cairo_stroker_line_to;
(cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
(cairo_spline_add_point_func_t) _cairo_stroker_line_to;
 
/* spline_to is only capable of rendering non-degenerate splines. */
spline_to = stroker->dash.dashed ?
(cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed :
(cairo_spline_add_point_func_t) _cairo_stroker_spline_to;
 
if (! _cairo_spline_init (&spline,
line_to, stroker,
spline_to,
stroker,
&stroker->current_point, b, c, d))
{
return line_to (closure, d);
cairo_slope_t fallback_slope;
_cairo_slope_init (&fallback_slope, &stroker->current_point, d);
return line_to (closure, d, &fallback_slope);
}
 
/* If the line width is so small that the pen is reduced to a
1307,8 → 1377,9
cairo_stroker_t stroker;
cairo_status_t status;
 
status = _cairo_stroker_init (&stroker, stroke_style,
ctm, ctm_inverse, tolerance);
status = _cairo_stroker_init (&stroker, path, stroke_style,
ctm, ctm_inverse, tolerance,
NULL, 0);
if (unlikely (status))
return status;
 
1318,7 → 1389,6
stroker.closure = closure;
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_stroker_move_to,
stroker.dash.dashed ?
_cairo_stroker_line_to_dashed :
1340,7 → 1410,7
}
 
cairo_status_t
_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
1350,8 → 1420,9
cairo_stroker_t stroker;
cairo_status_t status;
 
status = _cairo_stroker_init (&stroker, stroke_style,
ctm, ctm_inverse, tolerance);
status = _cairo_stroker_init (&stroker, path, stroke_style,
ctm, ctm_inverse, tolerance,
polygon->limits, polygon->num_limits);
if (unlikely (status))
return status;
 
1358,11 → 1429,7
stroker.add_external_edge = _cairo_polygon_add_external_edge,
stroker.closure = polygon;
 
if (polygon->num_limits)
_cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits);
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_stroker_move_to,
stroker.dash.dashed ?
_cairo_stroker_line_to_dashed :
1383,8 → 1450,8
return status;
}
 
cairo_status_t
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
cairo_int_status_t
_cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
1391,27 → 1458,10
double tolerance,
cairo_traps_t *traps)
{
cairo_status_t status;
cairo_int_status_t status;
cairo_polygon_t polygon;
 
/* Before we do anything else, we attempt the rectilinear
* stroker. It's careful to generate trapezoids that align to
* device-pixel boundaries when possible. Many backends can render
* those much faster than non-aligned trapezoids, (by using clip
* regions, etc.) */
if (path->is_rectilinear) {
status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
stroke_style,
ctm,
traps);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
_cairo_polygon_init (&polygon);
if (traps->num_limits)
_cairo_polygon_limit (&polygon, traps->limits, traps->num_limits);
 
_cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm,
1433,711 → 1483,3
 
return status;
}
 
typedef struct _segment_t {
cairo_point_t p1, p2;
cairo_bool_t is_horizontal;
cairo_bool_t has_join;
} segment_t;
 
typedef struct _cairo_rectilinear_stroker {
const cairo_stroke_style_t *stroke_style;
const cairo_matrix_t *ctm;
 
cairo_fixed_t half_line_width;
cairo_bool_t do_traps;
void *container;
cairo_point_t current_point;
cairo_point_t first_point;
cairo_bool_t open_sub_path;
 
cairo_stroker_dash_t dash;
 
cairo_bool_t has_bounds;
cairo_box_t bounds;
 
int num_segments;
int segments_size;
segment_t *segments;
segment_t segments_embedded[8]; /* common case is a single rectangle */
} cairo_rectilinear_stroker_t;
 
static void
_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
const cairo_box_t *boxes,
int num_boxes)
{
stroker->has_bounds = TRUE;
_cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
 
stroker->bounds.p1.x -= stroker->half_line_width;
stroker->bounds.p2.x += stroker->half_line_width;
 
stroker->bounds.p1.y -= stroker->half_line_width;
stroker->bounds.p2.y += stroker->half_line_width;
}
 
static cairo_bool_t
_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_bool_t do_traps,
void *container)
{
/* This special-case rectilinear stroker only supports
* miter-joined lines (not curves) and a translation-only matrix
* (though it could probably be extended to support a matrix with
* uniform, integer scaling).
*
* It also only supports horizontal and vertical line_to
* elements. But we don't catch that here, but instead return
* UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
* non-rectilinear line_to is encountered.
*/
if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
return FALSE;
 
/* If the miter limit turns right angles into bevels, then we
* can't use this optimization. Remember, the ratio is
* 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2,
* which we round for safety. */
if (stroke_style->miter_limit < M_SQRT2)
return FALSE;
 
if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
{
return FALSE;
}
 
if (! _cairo_matrix_has_unity_scale (ctm))
return FALSE;
 
stroker->stroke_style = stroke_style;
stroker->ctm = ctm;
 
stroker->half_line_width =
_cairo_fixed_from_double (stroke_style->line_width / 2.0);
stroker->open_sub_path = FALSE;
stroker->segments = stroker->segments_embedded;
stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
stroker->num_segments = 0;
 
_cairo_stroker_dash_init (&stroker->dash, stroke_style);
 
stroker->has_bounds = FALSE;
 
stroker->do_traps = do_traps;
stroker->container = container;
 
return TRUE;
}
 
static void
_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
{
if (stroker->segments != stroker->segments_embedded)
free (stroker->segments);
}
 
static cairo_status_t
_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
const cairo_point_t *p1,
const cairo_point_t *p2,
cairo_bool_t is_horizontal,
cairo_bool_t has_join)
{
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (stroker->num_segments == stroker->segments_size) {
int new_size = stroker->segments_size * 2;
segment_t *new_segments;
 
if (stroker->segments == stroker->segments_embedded) {
new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (new_segments, stroker->segments,
stroker->num_segments * sizeof (segment_t));
} else {
new_segments = _cairo_realloc_ab (stroker->segments,
new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
stroker->segments_size = new_size;
stroker->segments = new_segments;
}
 
stroker->segments[stroker->num_segments].p1 = *p1;
stroker->segments[stroker->num_segments].p2 = *p2;
stroker->segments[stroker->num_segments].has_join = has_join;
stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
stroker->num_segments++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
{
cairo_status_t status;
cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
cairo_fixed_t half_line_width = stroker->half_line_width;
int i;
 
for (i = 0; i < stroker->num_segments; i++) {
cairo_point_t *a, *b;
cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
 
a = &stroker->segments[i].p1;
b = &stroker->segments[i].p2;
 
/* For each segment we generate a single rectangular
* trapezoid. This rectangle is based on a perpendicular
* extension (by half the line width) of the segment endpoints
* after some adjustments of the endpoints to account for caps
* and joins.
*/
 
/* We adjust the initial point of the segment to extend the
* rectangle to include the previous cap or join, (this
* adjustment applies to all segments except for the first
* segment of open, butt-capped paths).
*/
lengthen_initial = TRUE;
if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
lengthen_initial = FALSE;
 
/* The adjustment of the final point is trickier. For all but
* the last segment we shorten the segment at the final
* endpoint to not overlap with the subsequent join. For the
* last segment we do the same shortening if the path is
* closed. If the path is open and butt-capped we do no
* adjustment, while if it's open and square-capped we do a
* lengthening adjustment instead to include the cap.
*/
shorten_final = TRUE;
lengthen_final = FALSE;
if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
shorten_final = FALSE;
if (line_cap == CAIRO_LINE_CAP_SQUARE)
lengthen_final = TRUE;
}
 
/* Perform the adjustments of the endpoints. */
if (a->y == b->y) {
if (a->x < b->x) {
if (lengthen_initial)
a->x -= half_line_width;
if (shorten_final)
b->x -= half_line_width;
else if (lengthen_final)
b->x += half_line_width;
} else {
if (lengthen_initial)
a->x += half_line_width;
if (shorten_final)
b->x += half_line_width;
else if (lengthen_final)
b->x -= half_line_width;
}
 
if (a->x > b->x) {
cairo_point_t *t;
 
t = a;
a = b;
b = t;
}
} else {
if (a->y < b->y) {
if (lengthen_initial)
a->y -= half_line_width;
if (shorten_final)
b->y -= half_line_width;
else if (lengthen_final)
b->y += half_line_width;
} else {
if (lengthen_initial)
a->y += half_line_width;
if (shorten_final)
b->y += half_line_width;
else if (lengthen_final)
b->y -= half_line_width;
}
 
if (a->y > b->y) {
cairo_point_t *t;
 
t = a;
a = b;
b = t;
}
}
 
/* Form the rectangle by expanding by half the line width in
* either perpendicular direction. */
if (a->y == b->y) {
a->y -= half_line_width;
b->y += half_line_width;
} else {
a->x -= half_line_width;
b->x += half_line_width;
}
 
if (stroker->do_traps) {
status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
} else {
cairo_box_t box;
 
box.p1 = *a;
box.p2 = *b;
 
status = _cairo_boxes_add (stroker->container, &box);
}
if (unlikely (status))
return status;
}
 
stroker->num_segments = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
{
cairo_status_t status;
cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
cairo_fixed_t half_line_width = stroker->half_line_width;
int i;
 
for (i = 0; i < stroker->num_segments; i++) {
cairo_point_t *a, *b;
cairo_bool_t is_horizontal;
 
a = &stroker->segments[i].p1;
b = &stroker->segments[i].p2;
 
is_horizontal = stroker->segments[i].is_horizontal;
 
/* Handle the joins for a potentially degenerate segment. */
if (line_cap == CAIRO_LINE_CAP_BUTT &&
stroker->segments[i].has_join &&
(i != stroker->num_segments - 1 ||
(! stroker->open_sub_path && stroker->dash.dash_starts_on)))
{
cairo_point_t p1 = stroker->segments[i].p1;
cairo_point_t p2 = stroker->segments[i].p2;
cairo_slope_t out_slope;
int j = (i + 1) % stroker->num_segments;
 
_cairo_slope_init (&out_slope,
&stroker->segments[j].p1,
&stroker->segments[j].p2);
 
if (is_horizontal) {
if (p1.x <= p2.x) {
p1.x = p2.x;
p2.x += half_line_width;
} else {
p1.x = p2.x - half_line_width;
}
if (out_slope.dy >= 0)
p1.y -= half_line_width;
if (out_slope.dy <= 0)
p2.y += half_line_width;
} else {
if (p1.y <= p2.y) {
p1.y = p2.y;
p2.y += half_line_width;
} else {
p1.y = p2.y - half_line_width;
}
if (out_slope.dx >= 0)
p1.x -= half_line_width;
if (out_slope.dx <= 0)
p2.x += half_line_width;
}
 
if (stroker->do_traps) {
status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
} else {
cairo_box_t box;
 
box.p1 = p1;
box.p2 = p2;
 
status = _cairo_boxes_add (stroker->container, &box);
}
if (unlikely (status))
return status;
}
 
/* Perform the adjustments of the endpoints. */
if (is_horizontal) {
if (line_cap == CAIRO_LINE_CAP_SQUARE) {
if (a->x <= b->x) {
a->x -= half_line_width;
b->x += half_line_width;
} else {
a->x += half_line_width;
b->x -= half_line_width;
}
}
 
if (a->x > b->x) {
cairo_point_t *t;
 
t = a;
a = b;
b = t;
}
 
a->y -= half_line_width;
b->y += half_line_width;
} else {
if (line_cap == CAIRO_LINE_CAP_SQUARE) {
if (a->y <= b->y) {
a->y -= half_line_width;
b->y += half_line_width;
} else {
a->y += half_line_width;
b->y -= half_line_width;
}
}
 
if (a->y > b->y) {
cairo_point_t *t;
 
t = a;
a = b;
b = t;
}
 
a->x -= half_line_width;
b->x += half_line_width;
}
 
if (a->x == b->x && a->y == b->y)
continue;
 
if (stroker->do_traps) {
status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
} else {
cairo_box_t box;
 
box.p1 = *a;
box.p2 = *b;
 
status = _cairo_boxes_add (stroker->container, &box);
}
if (unlikely (status))
return status;
}
 
stroker->num_segments = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_move_to (void *closure,
const cairo_point_t *point)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_status_t status;
 
if (stroker->dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
 
/* reset the dash pattern for new sub paths */
_cairo_stroker_dash_start (&stroker->dash);
 
stroker->current_point = *point;
stroker->first_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_line_to (void *closure,
const cairo_point_t *b)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_point_t *a = &stroker->current_point;
cairo_status_t status;
 
/* We only support horizontal or vertical elements. */
assert (a->x == b->x || a->y == b->y);
 
/* We don't draw anything for degenerate paths. */
if (a->x == b->x && a->y == b->y)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
a->y == b->y,
TRUE);
 
stroker->current_point = *b;
stroker->open_sub_path = TRUE;
 
return status;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_line_to_dashed (void *closure,
const cairo_point_t *point)
{
cairo_rectilinear_stroker_t *stroker = closure;
const cairo_point_t *a = &stroker->current_point;
const cairo_point_t *b = point;
cairo_bool_t fully_in_bounds;
double sign, remain;
cairo_fixed_t mag;
cairo_status_t status;
cairo_line_t segment;
cairo_bool_t dash_on = FALSE;
cairo_bool_t is_horizontal;
 
/* We don't draw anything for degenerate paths. */
if (a->x == b->x && a->y == b->y)
return CAIRO_STATUS_SUCCESS;
 
/* We only support horizontal or vertical elements. */
assert (a->x == b->x || a->y == b->y);
 
fully_in_bounds = TRUE;
if (stroker->has_bounds &&
(! _cairo_box_contains_point (&stroker->bounds, a) ||
! _cairo_box_contains_point (&stroker->bounds, b)))
{
fully_in_bounds = FALSE;
}
 
is_horizontal = a->y == b->y;
if (is_horizontal)
mag = b->x - a->x;
else
mag = b->y - a->y;
if (mag < 0) {
remain = _cairo_fixed_to_double (-mag);
sign = 1.;
} else {
remain = _cairo_fixed_to_double (mag);
sign = -1.;
}
 
segment.p2 = segment.p1 = *a;
while (remain > 0.) {
double step_length;
 
step_length = MIN (stroker->dash.dash_remain, remain);
remain -= step_length;
 
mag = _cairo_fixed_from_double (sign*remain);
if (is_horizontal)
segment.p2.x = b->x + mag;
else
segment.p2.y = b->y + mag;
 
if (stroker->dash.dash_on &&
(fully_in_bounds ||
_cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
{
status = _cairo_rectilinear_stroker_add_segment (stroker,
&segment.p1,
&segment.p2,
is_horizontal,
remain <= 0.);
if (unlikely (status))
return status;
 
dash_on = TRUE;
}
else
{
dash_on = FALSE;
}
 
_cairo_stroker_dash_step (&stroker->dash, step_length);
segment.p1 = segment.p2;
}
 
if (stroker->dash.dash_on && ! dash_on &&
(fully_in_bounds ||
_cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
{
 
/* This segment ends on a transition to dash_on, compute a new face
* and add cap for the beginning of the next dash_on step.
*/
 
status = _cairo_rectilinear_stroker_add_segment (stroker,
&segment.p1,
&segment.p1,
is_horizontal,
TRUE);
if (unlikely (status))
return status;
}
 
stroker->current_point = *point;
stroker->open_sub_path = TRUE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectilinear_stroker_close_path (void *closure)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_status_t status;
 
/* We don't draw anything for degenerate paths. */
if (! stroker->open_sub_path)
return CAIRO_STATUS_SUCCESS;
 
if (stroker->dash.dashed) {
status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
&stroker->first_point);
} else {
status = _cairo_rectilinear_stroker_line_to (stroker,
&stroker->first_point);
}
if (unlikely (status))
return status;
 
stroker->open_sub_path = FALSE;
 
if (stroker->dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_traps_t *traps)
{
cairo_rectilinear_stroker_t rectilinear_stroker;
cairo_int_status_t status;
 
assert (path->is_rectilinear);
 
if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
stroke_style, ctm,
TRUE, traps))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (traps->num_limits) {
_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
traps->limits,
traps->num_limits);
}
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_rectilinear_stroker_move_to,
rectilinear_stroker.dash.dashed ?
_cairo_rectilinear_stroker_line_to_dashed :
_cairo_rectilinear_stroker_line_to,
NULL,
_cairo_rectilinear_stroker_close_path,
&rectilinear_stroker);
if (unlikely (status))
goto BAIL;
 
if (rectilinear_stroker.dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
 
traps->is_rectilinear = 1;
traps->is_rectangular = 1;
/* As we incrementally tessellate, we do not eliminate self-intersections */
traps->has_intersections = traps->num_traps > 1;
BAIL:
_cairo_rectilinear_stroker_fini (&rectilinear_stroker);
 
if (unlikely (status))
_cairo_traps_clear (traps);
 
return status;
}
 
cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_boxes_t *boxes)
{
cairo_rectilinear_stroker_t rectilinear_stroker;
cairo_int_status_t status;
 
assert (path->is_rectilinear);
 
if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
stroke_style, ctm,
FALSE, boxes))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (boxes->num_limits) {
_cairo_rectilinear_stroker_limit (&rectilinear_stroker,
boxes->limits,
boxes->num_limits);
}
 
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_rectilinear_stroker_move_to,
rectilinear_stroker.dash.dashed ?
_cairo_rectilinear_stroker_line_to_dashed :
_cairo_rectilinear_stroker_line_to,
NULL,
_cairo_rectilinear_stroker_close_path,
&rectilinear_stroker);
if (unlikely (status))
goto BAIL;
 
if (rectilinear_stroker.dash.dashed)
status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
else
status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
if (unlikely (status))
goto BAIL;
 
/* As we incrementally tessellate, we do not eliminate self-intersections */
status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
CAIRO_FILL_RULE_WINDING,
boxes);
if (unlikely (status))
goto BAIL;
 
_cairo_rectilinear_stroker_fini (&rectilinear_stroker);
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
_cairo_rectilinear_stroker_fini (&rectilinear_stroker);
_cairo_boxes_clear (boxes);
return status;
}
/programs/develop/libraries/cairo/src/cairo-path.c
37,6 → 37,7
#include "cairoint.h"
 
#include "cairo-private.h"
#include "cairo-backend-private.h"
#include "cairo-error-private.h"
#include "cairo-path-private.h"
#include "cairo-path-fixed-private.h"
48,7 → 49,7
*
* Paths are the most basic drawing tools and are primarily used to implicitly
* generate simple masks.
*/
**/
 
static const cairo_path_t _cairo_path_nil = { CAIRO_STATUS_NO_MEMORY, NULL, 0 };
 
55,7 → 56,6
/* Closure for path interpretation. */
typedef struct cairo_path_count {
int count;
cairo_point_t current_point;
} cpc_t;
 
static cairo_status_t
66,8 → 66,6
 
cpc->count += 2;
 
cpc->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
79,8 → 77,6
 
cpc->count += 2;
 
cpc->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
94,8 → 90,6
 
cpc->count += 4;
 
cpc->current_point = *p3;
 
return CAIRO_STATUS_SUCCESS;
}
 
119,12 → 113,9
cpc_t cpc;
 
cpc.count = 0;
cpc.current_point.x = 0;
cpc.current_point.y = 0;
 
if (flatten) {
status = _cairo_path_fixed_interpret_flat (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpc_move_to,
_cpc_line_to,
_cpc_close_path,
132,7 → 123,6
tolerance);
} else {
status = _cairo_path_fixed_interpret (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpc_move_to,
_cpc_line_to,
_cpc_curve_to,
149,8 → 139,7
/* Closure for path interpretation. */
typedef struct cairo_path_populate {
cairo_path_data_t *data;
cairo_gstate_t *gstate;
cairo_point_t current_point;
cairo_t *cr;
} cpp_t;
 
static cairo_status_t
164,7 → 153,7
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
 
_cairo_gstate_backend_to_user (cpp->gstate, &x, &y);
_cairo_backend_to_user (cpp->cr, &x, &y);
 
data->header.type = CAIRO_PATH_MOVE_TO;
data->header.length = 2;
175,8 → 164,6
 
cpp->data += data->header.length;
 
cpp->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
191,7 → 178,7
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
 
_cairo_gstate_backend_to_user (cpp->gstate, &x, &y);
_cairo_backend_to_user (cpp->cr, &x, &y);
 
data->header.type = CAIRO_PATH_LINE_TO;
data->header.length = 2;
202,8 → 189,6
 
cpp->data += data->header.length;
 
cpp->current_point = *point;
 
return CAIRO_STATUS_SUCCESS;
}
 
221,15 → 206,15
 
x1 = _cairo_fixed_to_double (p1->x);
y1 = _cairo_fixed_to_double (p1->y);
_cairo_gstate_backend_to_user (cpp->gstate, &x1, &y1);
_cairo_backend_to_user (cpp->cr, &x1, &y1);
 
x2 = _cairo_fixed_to_double (p2->x);
y2 = _cairo_fixed_to_double (p2->y);
_cairo_gstate_backend_to_user (cpp->gstate, &x2, &y2);
_cairo_backend_to_user (cpp->cr, &x2, &y2);
 
x3 = _cairo_fixed_to_double (p3->x);
y3 = _cairo_fixed_to_double (p3->y);
_cairo_gstate_backend_to_user (cpp->gstate, &x3, &y3);
_cairo_backend_to_user (cpp->cr, &x3, &y3);
 
data->header.type = CAIRO_PATH_CURVE_TO;
data->header.length = 4;
246,8 → 231,6
 
cpp->data += data->header.length;
 
cpp->current_point = *p3;
 
return CAIRO_STATUS_SUCCESS;
}
 
268,7 → 251,7
static cairo_status_t
_cairo_path_populate (cairo_path_t *path,
cairo_path_fixed_t *path_fixed,
cairo_gstate_t *gstate,
cairo_t *cr,
cairo_bool_t flatten)
{
cairo_status_t status;
275,22 → 258,17
cpp_t cpp;
 
cpp.data = path->data;
cpp.gstate = gstate;
cpp.current_point.x = 0;
cpp.current_point.y = 0;
cpp.cr = cr;
 
if (flatten) {
double tolerance = _cairo_gstate_get_tolerance (gstate);
status = _cairo_path_fixed_interpret_flat (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpp_move_to,
_cpp_line_to,
_cpp_close_path,
&cpp,
tolerance);
cairo_get_tolerance (cr));
} else {
status = _cairo_path_fixed_interpret (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpp_move_to,
_cpp_line_to,
_cpp_curve_to,
331,7 → 309,7
 
static cairo_path_t *
_cairo_path_create_internal (cairo_path_fixed_t *path_fixed,
cairo_gstate_t *gstate,
cairo_t *cr,
cairo_bool_t flatten)
{
cairo_path_t *path;
343,7 → 321,7
}
 
path->num_data = _cairo_path_count (path, path_fixed,
_cairo_gstate_get_tolerance (gstate),
cairo_get_tolerance (cr),
flatten);
if (path->num_data < 0) {
free (path);
359,8 → 337,7
return (cairo_path_t*) &_cairo_path_nil;
}
 
path->status = _cairo_path_populate (path, path_fixed,
gstate, flatten);
path->status = _cairo_path_populate (path, path_fixed, cr, flatten);
} else {
path->data = NULL;
path->status = CAIRO_STATUS_SUCCESS;
382,6 → 359,8
* pointer to a #cairo_path_t returned by a cairo function. Any path
* that is created manually (ie. outside of cairo) should be destroyed
* manually as well.
*
* Since: 1.0
**/
void
cairo_path_destroy (cairo_path_t *path)
389,19 → 368,19
if (path == NULL || path == &_cairo_path_nil)
return;
 
if (path->data)
free (path->data);
 
free (path);
}
slim_hidden_def (cairo_path_destroy);
 
/**
* _cairo_path_create:
* @path: a fixed-point, device-space path to be converted and copied
* @gstate: the current graphics state
* @cr: the current graphics context
*
* Creates a user-space #cairo_path_t copy of the given device-space
* @path. The @gstate parameter provides the inverse CTM for the
* @path. The @cr parameter provides the inverse CTM for the
* conversion.
*
* Return value: the new copy of the path. If there is insufficient
411,18 → 390,18
**/
cairo_path_t *
_cairo_path_create (cairo_path_fixed_t *path,
cairo_gstate_t *gstate)
cairo_t *cr)
{
return _cairo_path_create_internal (path, gstate, FALSE);
return _cairo_path_create_internal (path, cr, FALSE);
}
 
/**
* _cairo_path_create_flat:
* @path: a fixed-point, device-space path to be flattened, converted and copied
* @gstate: the current graphics state
* @cr: the current graphics context
*
* Creates a flattened, user-space #cairo_path_t copy of the given
* device-space @path. The @gstate parameter provide the inverse CTM
* device-space @path. The @cr parameter provide the inverse CTM
* for the conversion, as well as the tolerance value to control the
* accuracy of the flattening.
*
433,9 → 412,9
**/
cairo_path_t *
_cairo_path_create_flat (cairo_path_fixed_t *path,
cairo_gstate_t *gstate)
cairo_t *cr)
{
return _cairo_path_create_internal (path, gstate, TRUE);
return _cairo_path_create_internal (path, cr, TRUE);
}
 
/**
453,18 → 432,7
cairo_t *cr)
{
const cairo_path_data_t *p, *end;
cairo_fixed_t x1_fixed, y1_fixed;
cairo_fixed_t x2_fixed, y2_fixed;
cairo_fixed_t x3_fixed, y3_fixed;
cairo_matrix_t user_to_backend;
cairo_status_t status;
double x, y;
 
user_to_backend = cr->gstate->ctm;
cairo_matrix_multiply (&user_to_backend,
&user_to_backend,
&cr->gstate->target->device_transform);
 
end = &path->data[path->num_data];
for (p = &path->data[0]; p < end; p += p->header.length) {
switch (p->header.type) {
472,12 → 440,7
if (unlikely (p->header.length < 2))
return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
 
x = p[1].point.x, y = p[1].point.y;
cairo_matrix_transform_point (&user_to_backend, &x, &y);
x1_fixed = _cairo_fixed_from_double (x);
y1_fixed = _cairo_fixed_from_double (y);
 
status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed);
cairo_move_to (cr, p[1].point.x, p[1].point.y);
break;
 
case CAIRO_PATH_LINE_TO:
484,12 → 447,7
if (unlikely (p->header.length < 2))
return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
 
x = p[1].point.x, y = p[1].point.y;
cairo_matrix_transform_point (&user_to_backend, &x, &y);
x1_fixed = _cairo_fixed_from_double (x);
y1_fixed = _cairo_fixed_from_double (y);
 
status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed);
cairo_line_to (cr, p[1].point.x, p[1].point.y);
break;
 
case CAIRO_PATH_CURVE_TO:
496,25 → 454,10
if (unlikely (p->header.length < 4))
return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
 
x = p[1].point.x, y = p[1].point.y;
cairo_matrix_transform_point (&user_to_backend, &x, &y);
x1_fixed = _cairo_fixed_from_double (x);
y1_fixed = _cairo_fixed_from_double (y);
 
x = p[2].point.x, y = p[2].point.y;
cairo_matrix_transform_point (&user_to_backend, &x, &y);
x2_fixed = _cairo_fixed_from_double (x);
y2_fixed = _cairo_fixed_from_double (y);
 
x = p[3].point.x, y = p[3].point.y;
cairo_matrix_transform_point (&user_to_backend, &x, &y);
x3_fixed = _cairo_fixed_from_double (x);
y3_fixed = _cairo_fixed_from_double (y);
 
status = _cairo_path_fixed_curve_to (cr->path,
x1_fixed, y1_fixed,
x2_fixed, y2_fixed,
x3_fixed, y3_fixed);
cairo_curve_to (cr,
p[1].point.x, p[1].point.y,
p[2].point.x, p[2].point.y,
p[3].point.x, p[3].point.y);
break;
 
case CAIRO_PATH_CLOSE_PATH:
521,7 → 464,7
if (unlikely (p->header.length < 1))
return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
 
status = _cairo_path_fixed_close_path (cr->path);
cairo_close_path (cr);
break;
 
default:
528,8 → 471,8
return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
}
 
if (unlikely (status))
return status;
if (unlikely (cr->status))
return cr->status;
}
 
return CAIRO_STATUS_SUCCESS;
/programs/develop/libraries/cairo/src/cairo-pattern-inline.h
0,0 → 1,65
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl D. Worth <cworth@redhat.com>
*/
 
#ifndef CAIRO_PATTERN_INLINE_H
#define CAIRO_PATTERN_INLINE_H
 
#include "cairo-pattern-private.h"
 
#include "cairo-list-inline.h"
 
CAIRO_BEGIN_DECLS
 
static inline void
_cairo_pattern_add_observer (cairo_pattern_t *pattern,
cairo_pattern_observer_t *observer,
void (*func) (cairo_pattern_observer_t *,
cairo_pattern_t *,
unsigned int))
{
observer->notify = func;
cairo_list_add (&observer->link, &pattern->observers);
}
 
static inline cairo_surface_t *
_cairo_pattern_get_source (const cairo_surface_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
return _cairo_surface_get_source (pattern->surface, extents);
}
 
CAIRO_END_DECLS
 
#endif /* CAIRO_PATTERN_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-pattern-private.h
0,0 → 1,362
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl D. Worth <cworth@redhat.com>
*/
 
#ifndef CAIRO_PATTERN_PRIVATE_H
#define CAIRO_PATTERN_PRIVATE_H
 
#include "cairo-error-private.h"
#include "cairo-types-private.h"
#include "cairo-list-private.h"
#include "cairo-surface-private.h"
 
#include <stdio.h> /* FILE* */
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_pattern_observer cairo_pattern_observer_t;
 
enum {
CAIRO_PATTERN_NOTIFY_MATRIX = 0x1,
CAIRO_PATTERN_NOTIFY_FILTER = 0x2,
CAIRO_PATTERN_NOTIFY_EXTEND = 0x4,
CAIRO_PATTERN_NOTIFY_OPACITY = 0x9,
};
 
struct _cairo_pattern_observer {
void (*notify) (cairo_pattern_observer_t *,
cairo_pattern_t *pattern,
unsigned int flags);
cairo_list_t link;
};
 
struct _cairo_pattern {
cairo_reference_count_t ref_count;
cairo_status_t status;
cairo_user_data_array_t user_data;
cairo_list_t observers;
 
cairo_pattern_type_t type;
 
cairo_filter_t filter;
cairo_extend_t extend;
cairo_bool_t has_component_alpha;
 
cairo_matrix_t matrix;
double opacity;
};
 
struct _cairo_solid_pattern {
cairo_pattern_t base;
cairo_color_t color;
};
 
typedef struct _cairo_surface_pattern {
cairo_pattern_t base;
 
cairo_surface_t *surface;
} cairo_surface_pattern_t;
 
typedef struct _cairo_gradient_stop {
double offset;
cairo_color_stop_t color;
} cairo_gradient_stop_t;
 
typedef struct _cairo_gradient_pattern {
cairo_pattern_t base;
 
unsigned int n_stops;
unsigned int stops_size;
cairo_gradient_stop_t *stops;
cairo_gradient_stop_t stops_embedded[2];
} cairo_gradient_pattern_t;
 
typedef struct _cairo_linear_pattern {
cairo_gradient_pattern_t base;
 
cairo_point_double_t pd1;
cairo_point_double_t pd2;
} cairo_linear_pattern_t;
 
typedef struct _cairo_radial_pattern {
cairo_gradient_pattern_t base;
 
cairo_circle_double_t cd1;
cairo_circle_double_t cd2;
} cairo_radial_pattern_t;
 
typedef union {
cairo_gradient_pattern_t base;
 
cairo_linear_pattern_t linear;
cairo_radial_pattern_t radial;
} cairo_gradient_pattern_union_t;
 
/*
* A mesh patch is a tensor-product patch (bicubic Bezier surface
* patch). It has 16 control points. Each set of 4 points along the
* sides of the 4x4 grid of control points is a Bezier curve that
* defines one side of the patch. A color is assigned to each
* corner. The inner 4 points provide additional control over the
* shape and the color mapping.
*
* Cairo uses the same convention as the PDF Reference for numbering
* the points and side of the patch.
*
*
* Side 1
*
* p[0][3] p[1][3] p[2][3] p[3][3]
* Side 0 p[0][2] p[1][2] p[2][2] p[3][2] Side 2
* p[0][1] p[1][1] p[2][1] p[3][1]
* p[0][0] p[1][0] p[2][0] p[3][0]
*
* Side 3
*
*
* Point Color
* -------------------------
* points[0][0] colors[0]
* points[0][3] colors[1]
* points[3][3] colors[2]
* points[3][0] colors[3]
*/
 
typedef struct _cairo_mesh_patch {
cairo_point_double_t points[4][4];
cairo_color_t colors[4];
} cairo_mesh_patch_t;
 
typedef struct _cairo_mesh_pattern {
cairo_pattern_t base;
 
cairo_array_t patches;
cairo_mesh_patch_t *current_patch;
int current_side;
cairo_bool_t has_control_point[4];
cairo_bool_t has_color[4];
} cairo_mesh_pattern_t;
 
typedef struct _cairo_raster_source_pattern {
cairo_pattern_t base;
 
cairo_content_t content;
cairo_rectangle_int_t extents;
 
cairo_raster_source_acquire_func_t acquire;
cairo_raster_source_release_func_t release;
cairo_raster_source_snapshot_func_t snapshot;
cairo_raster_source_copy_func_t copy;
cairo_raster_source_finish_func_t finish;
 
/* an explicit pre-allocated member in preference to the general user-data */
void *user_data;
} cairo_raster_source_pattern_t;
 
typedef union {
cairo_pattern_t base;
 
cairo_solid_pattern_t solid;
cairo_surface_pattern_t surface;
cairo_gradient_pattern_union_t gradient;
cairo_mesh_pattern_t mesh;
cairo_raster_source_pattern_t raster_source;
} cairo_pattern_union_t;
 
/* cairo-pattern.c */
 
cairo_private cairo_pattern_t *
_cairo_pattern_create_in_error (cairo_status_t status);
 
cairo_private cairo_status_t
_cairo_pattern_create_copy (cairo_pattern_t **pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_pattern_init (cairo_pattern_t *pattern,
cairo_pattern_type_t type);
 
cairo_private cairo_status_t
_cairo_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private cairo_status_t
_cairo_pattern_init_snapshot (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color);
 
cairo_private void
_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
cairo_surface_t *surface);
 
cairo_private void
_cairo_pattern_fini (cairo_pattern_t *pattern);
 
cairo_private cairo_pattern_t *
_cairo_pattern_create_solid (const cairo_color_t *color);
 
cairo_private void
_cairo_pattern_transform (cairo_pattern_t *pattern,
const cairo_matrix_t *ctm_inverse);
 
cairo_private cairo_bool_t
_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 
cairo_private cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_bool_t
_cairo_pattern_is_clear (const cairo_pattern_t *pattern);
 
cairo_private cairo_bool_t
_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents,
cairo_color_t *color);
 
cairo_private void
_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
double max_value,
cairo_matrix_t *out_matrix,
cairo_circle_double_t out_circle[2]);
 
cairo_private cairo_bool_t
_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial);
 
cairo_private void
_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
double x0, double y0,
double x1, double y1,
double tolerance,
double out_range[2]);
 
cairo_private void
_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
double t,
cairo_circle_double_t *out_circle);
 
cairo_private void
_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
double *out_min,
double *out_max);
 
cairo_private cairo_bool_t
_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
double *out_xmin,
double *out_ymin,
double *out_xmax,
double *out_ymax);
 
cairo_private_no_warn cairo_filter_t
_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *sample);
 
cairo_private void
_cairo_pattern_get_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_int_status_t
_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents);
 
cairo_private unsigned long
_cairo_pattern_hash (const cairo_pattern_t *pattern);
 
cairo_private unsigned long
_cairo_linear_pattern_hash (unsigned long hash,
const cairo_linear_pattern_t *linear);
 
cairo_private unsigned long
_cairo_radial_pattern_hash (unsigned long hash,
const cairo_radial_pattern_t *radial);
 
cairo_private cairo_bool_t
_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
const cairo_linear_pattern_t *b);
 
cairo_private unsigned long
_cairo_pattern_size (const cairo_pattern_t *pattern);
 
cairo_private cairo_bool_t
_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
const cairo_radial_pattern_t *b);
 
cairo_private cairo_bool_t
_cairo_pattern_equal (const cairo_pattern_t *a,
const cairo_pattern_t *b);
 
/* cairo-mesh-pattern-rasterizer.c */
 
cairo_private void
_cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh,
void *data,
int width,
int height,
int stride,
double x_offset,
double y_offset);
 
cairo_private cairo_surface_t *
_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern,
cairo_surface_t *target,
const cairo_rectangle_int_t *extents);
 
cairo_private void
_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern,
cairo_surface_t *surface);
 
cairo_private cairo_status_t
_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern);
 
cairo_private cairo_status_t
_cairo_raster_source_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern);
 
cairo_private void
_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_PATTERN_PRIVATE */
/programs/develop/libraries/cairo/src/cairo-pattern.c
29,9 → 29,21
*/
 
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-snapshot-inline.h"
 
#include <float.h>
 
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 
/**
* SECTION:cairo-pattern
* @Title: cairo_pattern_t
44,68 → 56,108
* brush too.
*
* A cairo pattern is created by using one of the many constructors,
* of the form cairo_pattern_create_<emphasis>type</emphasis>()
* of the form
* <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
* or implicitly through
* cairo_set_source_<emphasis>type</emphasis>() functions.
*/
* <function>cairo_set_source_<emphasis>type</emphasis>()</function>
* functions.
**/
 
#if HAS_FREED_POOL
static freed_pool_t freed_pattern_pool[4];
#endif
static freed_pool_t freed_pattern_pool[5];
 
static const cairo_solid_pattern_t _cairo_pattern_nil = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
{
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NO_MEMORY, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
1.0 /* opacity */
}
};
 
static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
{
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NULL_POINTER, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
1.0 /* opacity */
}
};
 
const cairo_solid_pattern_t _cairo_pattern_black = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
{
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_SUCCESS, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */
};
 
const cairo_solid_pattern_t _cairo_pattern_clear = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
{
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_SUCCESS, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */
};
 
const cairo_solid_pattern_t _cairo_pattern_white = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
{
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_SUCCESS, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */
};
 
static void
_cairo_pattern_notify_observers (cairo_pattern_t *pattern,
unsigned int flags)
{
cairo_pattern_observer_t *pos;
 
cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link)
pos->notify (pos, pattern, flags);
}
 
/**
* _cairo_pattern_set_error:
* @pattern: a pattern
137,7 → 189,7
return _cairo_error (status);
}
 
static void
void
_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
{
#if HAVE_VALGRIND
154,6 → 206,11
case CAIRO_PATTERN_TYPE_RADIAL:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
break;
case CAIRO_PATTERN_TYPE_MESH:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t));
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
break;
}
#endif
 
166,16 → 223,20
 
_cairo_user_data_array_init (&pattern->user_data);
 
if (type == CAIRO_PATTERN_TYPE_SURFACE)
if (type == CAIRO_PATTERN_TYPE_SURFACE ||
type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
else
pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
 
pattern->filter = CAIRO_FILTER_DEFAULT;
pattern->opacity = 1.0;
 
pattern->has_component_alpha = FALSE;
 
cairo_matrix_init_identity (&pattern->matrix);
 
cairo_list_init (&pattern->observers);
}
 
static cairo_status_t
219,10 → 280,24
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern,
const cairo_mesh_pattern_t *other)
{
*pattern = *other;
 
_cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
return _cairo_array_append_multiple (&pattern->patches,
_cairo_array_index_const (&other->patches, 0),
_cairo_array_num_elements (&other->patches));
}
 
cairo_status_t
_cairo_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other)
{
cairo_status_t status;
 
if (other->status)
return _cairo_pattern_set_error (pattern, other->status);
 
248,7 → 323,6
case CAIRO_PATTERN_TYPE_RADIAL: {
cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
cairo_status_t status;
 
if (other->type == CAIRO_PATTERN_TYPE_LINEAR) {
VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)));
261,6 → 335,23
return status;
 
} break;
case CAIRO_PATTERN_TYPE_MESH: {
cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern;
cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other;
 
VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)));
 
status = _cairo_mesh_pattern_init_copy (dst, src);
if (unlikely (status))
return status;
 
} break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
status = _cairo_raster_source_pattern_init_copy (pattern, other);
if (unlikely (status))
return status;
} break;
}
 
/* The reference count and user_data array are unique to the copy. */
293,6 → 384,12
case CAIRO_PATTERN_TYPE_RADIAL:
size = sizeof (cairo_radial_pattern_t);
break;
case CAIRO_PATTERN_TYPE_MESH:
size = sizeof (cairo_mesh_pattern_t);
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
size = sizeof (cairo_raster_source_pattern_t);
break;
}
 
memcpy (pattern, other, size);
324,11 → 421,11
 
cairo_surface_destroy (surface);
 
if (surface_pattern->surface->status)
return surface_pattern->surface->status;
}
status = surface_pattern->surface->status;
} else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
status = _cairo_raster_source_pattern_snapshot (pattern);
 
return CAIRO_STATUS_SUCCESS;
return status;
}
 
void
353,6 → 450,15
if (gradient->stops && gradient->stops != gradient->stops_embedded)
free (gradient->stops);
} break;
case CAIRO_PATTERN_TYPE_MESH: {
cairo_mesh_pattern_t *mesh =
(cairo_mesh_pattern_t *) pattern;
 
_cairo_array_fini (&mesh->patches);
} break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_raster_source_pattern_finish (pattern);
break;
}
 
#if HAVE_VALGRIND
369,6 → 475,11
case CAIRO_PATTERN_TYPE_RADIAL:
VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t));
break;
case CAIRO_PATTERN_TYPE_MESH:
VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t));
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
break;
}
#endif
}
396,6 → 507,12
case CAIRO_PATTERN_TYPE_RADIAL:
pattern = malloc (sizeof (cairo_radial_pattern_t));
break;
case CAIRO_PATTERN_TYPE_MESH:
pattern = malloc (sizeof (cairo_mesh_pattern_t));
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
pattern = malloc (sizeof (cairo_raster_source_pattern_t));
break;
default:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
414,7 → 531,6
return CAIRO_STATUS_SUCCESS;
}
 
 
void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color)
450,19 → 566,19
pattern->stops = NULL;
}
 
void
static void
_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
double x0, double y0, double x1, double y1)
{
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR);
 
pattern->p1.x = _cairo_fixed_from_double (x0);
pattern->p1.y = _cairo_fixed_from_double (y0);
pattern->p2.x = _cairo_fixed_from_double (x1);
pattern->p2.y = _cairo_fixed_from_double (y1);
pattern->pd1.x = x0;
pattern->pd1.y = y0;
pattern->pd2.x = x1;
pattern->pd2.y = y1;
}
 
void
static void
_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
469,12 → 585,12
{
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
 
pattern->c1.x = _cairo_fixed_from_double (cx0);
pattern->c1.y = _cairo_fixed_from_double (cy0);
pattern->r1 = _cairo_fixed_from_double (fabs (radius0));
pattern->c2.x = _cairo_fixed_from_double (cx1);
pattern->c2.y = _cairo_fixed_from_double (cy1);
pattern->r2 = _cairo_fixed_from_double (fabs (radius1));
pattern->cd1.center.x = cx0;
pattern->cd1.center.y = cy0;
pattern->cd1.radius = fabs (radius0);
pattern->cd2.center.x = cx1;
pattern->cd2.center.y = cy1;
pattern->cd2.radius = fabs (radius1);
}
 
cairo_pattern_t *
535,21 → 651,13
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_rgb (double red, double green, double blue)
{
cairo_color_t color;
 
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
 
_cairo_color_init_rgb (&color, red, green, blue);
 
CAIRO_MUTEX_INITIALIZE ();
 
return _cairo_pattern_create_solid (&color);
return cairo_pattern_create_rgba (red, green, blue, 1.0);
}
slim_hidden_def (cairo_pattern_create_rgb);
 
573,6 → 681,8
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue,
607,6 → 717,8
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
665,6 → 777,8
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
716,6 → 830,8
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0,
741,7 → 857,187
return &pattern->base.base;
}
 
/* This order is specified in the diagram in the documentation for
* cairo_pattern_create_mesh() */
static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 };
static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 };
static const int mesh_control_point_i[4] = { 1, 1, 2, 2 };
static const int mesh_control_point_j[4] = { 1, 2, 2, 1 };
 
/**
* cairo_pattern_create_mesh:
*
* Create a new mesh pattern.
*
* Mesh patterns are tensor-product patch meshes (type 7 shadings in
* PDF). Mesh patterns may also be used to create other types of
* shadings that are special cases of tensor-product patch meshes such
* as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded
* triangle meshes (type 4 and 5 shadings in PDF).
*
* Mesh patterns consist of one or more tensor-product patches, which
* should be defined before using the mesh pattern. Using a mesh
* pattern with a partially defined patch as source or mask will put
* the context in an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2,
* 3) and by 4 additional control points (P0, P1, P2, P3) that provide
* further control over the patch and complete the definition of the
* tensor-product patch. The corner C0 is the first point of the
* patch.
*
* Degenerate sides are permitted so straight lines may be used. A
* zero length line on one side may be used to create 3 sided patches.
*
* <informalexample><screen>
* C1 Side 1 C2
* +---------------+
* | |
* | P1 P2 |
* | |
* Side 0 | | Side 2
* | |
* | |
* | P0 P3 |
* | |
* +---------------+
* C0 Side 3 C3
* </screen></informalexample>
*
* Each patch is constructed by first calling
* cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to()
* to specify the first point in the patch (C0). Then the sides are
* specified with calls to cairo_mesh_pattern_curve_to() and
* cairo_mesh_pattern_line_to().
*
* The four additional control points (P0, P1, P2, P3) in a patch can
* be specified with cairo_mesh_pattern_set_control_point().
*
* At each corner of the patch (C0, C1, C2, C3) a color may be
* specified with cairo_mesh_pattern_set_corner_color_rgb() or
* cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color
* is not explicitly specified defaults to transparent black.
*
* A Coons patch is a special case of the tensor-product patch where
* the control points are implicitly defined by the sides of the
* patch. The default value for any control point not specified is the
* implicit value for a Coons patch, i.e. if no control points are
* specified the patch is a Coons patch.
*
* A triangle is a special case of the tensor-product patch where the
* control points are implicitly defined by the sides of the patch,
* all the sides are lines and one of them has length 0, i.e. if the
* patch is specified using just 3 lines, it is a triangle. If the
* corners connected by the 0-length side have the same color, the
* patch is a Gouraud-shaded triangle.
*
* Patches may be oriented differently to the above diagram. For
* example the first point could be at the top left. The diagram only
* shows the relationship between the sides, corners and control
* points. Regardless of where the first point is located, when
* specifying colors, corner 0 will always be the first point, corner
* 1 the point between side 0 and side 1 etc.
*
* Calling cairo_mesh_pattern_end_patch() completes the current
* patch. If less than 4 sides have been defined, the first missing
* side is defined as a line from the current point to the first point
* of the patch (C0) and the other sides are degenerate lines from C0
* to C0. The corners between the added sides will all be coincident
* with C0 of the patch and their color will be set to be the same as
* the color of C0.
*
* Additional patches may be added with additional calls to
* cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch().
*
* <informalexample><programlisting>
* cairo_pattern_t *pattern = cairo_pattern_create_mesh ();
*
* /&ast; Add a Coons patch &ast;/
* cairo_mesh_pattern_begin_patch (pattern);
* cairo_mesh_pattern_move_to (pattern, 0, 0);
* cairo_mesh_pattern_curve_to (pattern, 30, -30, 60, 30, 100, 0);
* cairo_mesh_pattern_curve_to (pattern, 60, 30, 130, 60, 100, 100);
* cairo_mesh_pattern_curve_to (pattern, 60, 70, 30, 130, 0, 100);
* cairo_mesh_pattern_curve_to (pattern, 30, 70, -30, 30, 0, 0);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0);
* cairo_mesh_pattern_end_patch (pattern);
*
* /&ast; Add a Gouraud-shaded triangle &ast;/
* cairo_mesh_pattern_begin_patch (pattern)
* cairo_mesh_pattern_move_to (pattern, 100, 100);
* cairo_mesh_pattern_line_to (pattern, 130, 130);
* cairo_mesh_pattern_line_to (pattern, 130, 70);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0);
* cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1);
* cairo_mesh_pattern_end_patch (pattern)
* </programlisting></informalexample>
*
* When two patches overlap, the last one that has been added is drawn
* over the first one.
*
* When a patch folds over itself, points are sorted depending on
* their parameter coordinates inside the patch. The v coordinate
* ranges from 0 to 1 when moving from side 3 to side 1; the u
* coordinate ranges from 0 to 1 when going from side 0 to side
* 2. Points with higher v coordinate hide points with lower v
* coordinate. When two points have the same v coordinate, the one
* with higher u coordinate is above. This means that points nearer to
* side 1 are above points nearer to side 3; when this is not
* sufficient to decide which point is above (for example when both
* points belong to side 1 or side 3) points nearer to side 2 are
* above points nearer to side 0.
*
* For a complete definition of tensor-product patches, see the PDF
* specification (ISO32000), which describes the parametrization in
* detail.
*
* Note: The coordinates are always in pattern space. For a new
* pattern, pattern space is identical to user space, but the
* relationship between the spaces can be changed with
* cairo_pattern_set_matrix().
*
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the returned
* object and should call cairo_pattern_destroy() when finished with
* it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect the
* status of a pattern use cairo_pattern_status().
*
* Since: 1.12
**/
cairo_pattern_t *
cairo_pattern_create_mesh (void)
{
cairo_mesh_pattern_t *pattern;
 
pattern =
_freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]);
if (unlikely (pattern == NULL)) {
pattern = malloc (sizeof (cairo_mesh_pattern_t));
if (unlikely (pattern == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *) &_cairo_pattern_nil.base;
}
}
 
CAIRO_MUTEX_INITIALIZE ();
 
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH);
_cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
pattern->current_patch = NULL;
CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
 
return &pattern->base;
}
 
/**
* cairo_pattern_reference:
* @pattern: a #cairo_pattern_t
*
753,6 → 1049,8
* cairo_pattern_get_reference_count().
*
* Return value: the referenced #cairo_pattern_t.
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_reference (cairo_pattern_t *pattern)
793,8 → 1091,11
* Checks whether an error has previously occurred for this
* pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY,
* %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
* or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.0
**/
cairo_status_t
cairo_pattern_status (cairo_pattern_t *pattern)
809,6 → 1110,8
* Decreases the reference count on @pattern by one. If the result is
* zero, then @pattern and all associated resources are freed. See
* cairo_pattern_reference().
*
* Since: 1.0
**/
void
cairo_pattern_destroy (cairo_pattern_t *pattern)
828,7 → 1131,10
_cairo_pattern_fini (pattern);
 
/* maintain a small cache of freed patterns */
if (type < ARRAY_LENGTH (freed_pattern_pool))
_freed_pool_put (&freed_pattern_pool[type], pattern);
else
free (pattern);
}
slim_hidden_def (cairo_pattern_destroy);
 
906,6 → 1212,451
key, user_data, destroy);
}
 
/**
* cairo_mesh_pattern_begin_patch:
* @pattern: a #cairo_pattern_t
*
* Begin a patch in a mesh pattern.
*
* After calling this function, the patch shape should be defined with
* cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and
* cairo_mesh_pattern_curve_to().
*
* After defining the patch, cairo_mesh_pattern_end_patch() must be
* called before using @pattern as a source or mask.
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a
* current patch, it will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern)
{
cairo_mesh_pattern_t *mesh;
cairo_status_t status;
cairo_mesh_patch_t *current_patch;
int i;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
status = _cairo_array_allocate (&mesh->patches, 1, (void **) &current_patch);
if (unlikely (status)) {
_cairo_pattern_set_error (pattern, status);
return;
}
 
mesh->current_patch = current_patch;
mesh->current_side = -2; /* no current point */
 
for (i = 0; i < 4; i++)
mesh->has_control_point[i] = FALSE;
 
for (i = 0; i < 4; i++)
mesh->has_color[i] = FALSE;
}
 
 
static void
_calc_control_point (cairo_mesh_patch_t *patch, int control_point)
{
/* The Coons patch is a special case of the Tensor Product patch
* where the four control points are:
*
* P11 = S(1/3, 1/3)
* P12 = S(1/3, 2/3)
* P21 = S(2/3, 1/3)
* P22 = S(2/3, 2/3)
*
* where S is the gradient surface.
*
* When one or more control points has not been specified
* calculated the Coons patch control points are substituted. If
* no control points are specified the gradient will be a Coons
* patch.
*
* The equations below are defined in the ISO32000 standard.
*/
cairo_point_double_t *p[3][3];
int cp_i, cp_j, i, j;
 
cp_i = mesh_control_point_i[control_point];
cp_j = mesh_control_point_j[control_point];
 
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j];
 
p[0][0]->x = (- 4 * p[1][1]->x
+ 6 * (p[1][0]->x + p[0][1]->x)
- 2 * (p[1][2]->x + p[2][1]->x)
+ 3 * (p[2][0]->x + p[0][2]->x)
- 1 * p[2][2]->x) * (1. / 9);
 
p[0][0]->y = (- 4 * p[1][1]->y
+ 6 * (p[1][0]->y + p[0][1]->y)
- 2 * (p[1][2]->y + p[2][1]->y)
+ 3 * (p[2][0]->y + p[0][2]->y)
- 1 * p[2][2]->y) * (1. / 9);
}
 
/**
* cairo_mesh_pattern_end_patch:
* @pattern: a #cairo_pattern_t
*
* Indicates the end of the current patch in a mesh pattern.
*
* If the current patch has less than 4 sides, it is closed with a
* straight line from the current point to the first point of the
* patch as if cairo_mesh_pattern_line_to() was used.
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
* patch or the current patch has no current point, @pattern will be
* put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern)
{
cairo_mesh_pattern_t *mesh;
cairo_mesh_patch_t *current_patch;
int i;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
current_patch = mesh->current_patch;
if (unlikely (!current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (unlikely (mesh->current_side == -2)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
while (mesh->current_side < 3) {
int corner_num;
 
cairo_mesh_pattern_line_to (pattern,
current_patch->points[0][0].x,
current_patch->points[0][0].y);
 
corner_num = mesh->current_side + 1;
if (corner_num < 4 && ! mesh->has_color[corner_num]) {
current_patch->colors[corner_num] = current_patch->colors[0];
mesh->has_color[corner_num] = TRUE;
}
}
 
for (i = 0; i < 4; i++) {
if (! mesh->has_control_point[i])
_calc_control_point (current_patch, i);
}
 
for (i = 0; i < 4; i++) {
if (! mesh->has_color[i])
current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT;
}
 
mesh->current_patch = NULL;
}
 
/**
* cairo_mesh_pattern_curve_to:
* @pattern: a #cairo_pattern_t
* @x1: the X coordinate of the first control point
* @y1: the Y coordinate of the first control point
* @x2: the X coordinate of the second control point
* @y2: the Y coordinate of the second control point
* @x3: the X coordinate of the end of the curve
* @y3: the Y coordinate of the end of the curve
*
* Adds a cubic Bézier spline to the current patch from the current
* point to position (@x3, @y3) in pattern-space coordinates, using
* (@x1, @y1) and (@x2, @y2) as the control points.
*
* If the current patch has no current point before the call to
* cairo_mesh_pattern_curve_to(), this function will behave as if
* preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1,
* @y1).
*
* After this call the current point will be (@x3, @y3).
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
* patch or the current patch already has 4 sides, @pattern will be
* put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
double x1, double y1,
double x2, double y2,
double x3, double y3)
{
cairo_mesh_pattern_t *mesh;
int current_point, i, j;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (!mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (unlikely (mesh->current_side == 3)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (mesh->current_side == -2)
cairo_mesh_pattern_move_to (pattern, x1, y1);
 
assert (mesh->current_side >= -1);
assert (pattern->status == CAIRO_STATUS_SUCCESS);
 
mesh->current_side++;
 
current_point = 3 * mesh->current_side;
 
current_point++;
i = mesh_path_point_i[current_point];
j = mesh_path_point_j[current_point];
mesh->current_patch->points[i][j].x = x1;
mesh->current_patch->points[i][j].y = y1;
 
current_point++;
i = mesh_path_point_i[current_point];
j = mesh_path_point_j[current_point];
mesh->current_patch->points[i][j].x = x2;
mesh->current_patch->points[i][j].y = y2;
 
current_point++;
if (current_point < 12) {
i = mesh_path_point_i[current_point];
j = mesh_path_point_j[current_point];
mesh->current_patch->points[i][j].x = x3;
mesh->current_patch->points[i][j].y = y3;
}
}
slim_hidden_def (cairo_mesh_pattern_curve_to);
 
/**
* cairo_mesh_pattern_line_to:
* @pattern: a #cairo_pattern_t
* @x: the X coordinate of the end of the new line
* @y: the Y coordinate of the end of the new line
*
* Adds a line to the current patch from the current point to position
* (@x, @y) in pattern-space coordinates.
*
* If there is no current point before the call to
* cairo_mesh_pattern_line_to() this function will behave as
* cairo_mesh_pattern_move_to(@pattern, @x, @y).
*
* After this call the current point will be (@x, @y).
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
* patch or the current patch already has 4 sides, @pattern will be
* put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
double x, double y)
{
cairo_mesh_pattern_t *mesh;
cairo_point_double_t last_point;
int last_point_idx, i, j;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (!mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (unlikely (mesh->current_side == 3)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (mesh->current_side == -2) {
cairo_mesh_pattern_move_to (pattern, x, y);
return;
}
 
last_point_idx = 3 * (mesh->current_side + 1);
i = mesh_path_point_i[last_point_idx];
j = mesh_path_point_j[last_point_idx];
 
last_point = mesh->current_patch->points[i][j];
 
cairo_mesh_pattern_curve_to (pattern,
(2 * last_point.x + x) * (1. / 3),
(2 * last_point.y + y) * (1. / 3),
(last_point.x + 2 * x) * (1. / 3),
(last_point.y + 2 * y) * (1. / 3),
x, y);
}
slim_hidden_def (cairo_mesh_pattern_line_to);
 
/**
* cairo_mesh_pattern_move_to:
* @pattern: a #cairo_pattern_t
* @x: the X coordinate of the new position
* @y: the Y coordinate of the new position
*
* Define the first point of the current patch in a mesh pattern.
*
* After this call the current point will be (@x, @y).
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
* patch or the current patch already has at least one side, @pattern
* will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
double x, double y)
{
cairo_mesh_pattern_t *mesh;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (!mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
if (unlikely (mesh->current_side >= 0)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
mesh->current_side = -1;
mesh->current_patch->points[0][0].x = x;
mesh->current_patch->points[0][0].y = y;
}
slim_hidden_def (cairo_mesh_pattern_move_to);
 
/**
* cairo_mesh_pattern_set_control_point:
* @pattern: a #cairo_pattern_t
* @point_num: the control point to set the position for
* @x: the X coordinate of the control point
* @y: the Y coordinate of the control point
*
* Set an internal control point of the current patch.
*
* Valid values for @point_num are from 0 to 3 and identify the
* control points as explained in cairo_pattern_create_mesh().
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
unsigned int point_num,
double x,
double y)
{
cairo_mesh_pattern_t *mesh;
int i, j;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
if (unlikely (point_num > 3)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (!mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
i = mesh_control_point_i[point_num];
j = mesh_control_point_j[point_num];
 
mesh->current_patch->points[i][j].x = x;
mesh->current_patch->points[i][j].y = y;
mesh->has_control_point[point_num] = TRUE;
}
 
/* make room for at least one more color stop */
static cairo_status_t
_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
948,6 → 1699,126
}
 
static void
_cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh,
unsigned int corner_num,
double red, double green, double blue,
double alpha)
{
cairo_color_t *color;
 
assert (mesh->current_patch);
assert (corner_num <= 3);
 
color = &mesh->current_patch->colors[corner_num];
color->red = red;
color->green = green;
color->blue = blue;
color->alpha = alpha;
 
color->red_short = _cairo_color_double_to_short (red);
color->green_short = _cairo_color_double_to_short (green);
color->blue_short = _cairo_color_double_to_short (blue);
color->alpha_short = _cairo_color_double_to_short (alpha);
 
mesh->has_color[corner_num] = TRUE;
}
 
/**
* cairo_mesh_pattern_set_corner_color_rgb:
* @pattern: a #cairo_pattern_t
* @corner_num: the corner to set the color for
* @red: red component of color
* @green: green component of color
* @blue: blue component of color
*
* Sets the color of a corner of the current patch in a mesh pattern.
*
* The color is specified in the same way as in cairo_set_source_rgb().
*
* Valid values for @corner_num are from 0 to 3 and identify the
* corners as explained in cairo_pattern_create_mesh().
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
unsigned int corner_num,
double red, double green, double blue)
{
cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0);
}
 
/**
* cairo_mesh_pattern_set_corner_color_rgba:
* @pattern: a #cairo_pattern_t
* @corner_num: the corner to set the color for
* @red: red component of color
* @green: green component of color
* @blue: blue component of color
* @alpha: alpha component of color
*
* Sets the color of a corner of the current patch in a mesh pattern.
*
* The color is specified in the same way as in cairo_set_source_rgba().
*
* Valid values for @corner_num are from 0 to 3 and identify the
* corners as explained in cairo_pattern_create_mesh().
*
* Note: If @pattern is not a mesh pattern then @pattern will be put
* into an error status with a status of
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch,
* @pattern will be put into an error status with a status of
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.12
**/
void
cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
unsigned int corner_num,
double red, double green, double blue,
double alpha)
{
cairo_mesh_pattern_t *mesh;
 
if (unlikely (pattern->status))
return;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
 
if (unlikely (corner_num > 3)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX);
return;
}
 
mesh = (cairo_mesh_pattern_t *) pattern;
if (unlikely (!mesh->current_patch)) {
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
return;
}
 
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
 
_cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha);
}
slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba);
 
static void
_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
double offset,
double red,
1020,6 → 1891,8
* Note: If the pattern is not a gradient pattern, (eg. a linear or
* radial pattern), then the pattern will be put into an error status
* with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
*
* Since: 1.0
**/
void
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
1028,25 → 1901,9
double green,
double blue)
{
if (pattern->status)
return;
 
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
{
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0);
}
 
offset = _cairo_restrict_value (offset, 0.0, 1.0);
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
 
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset, red, green, blue, 1.0);
}
 
/**
* cairo_pattern_add_color_stop_rgba:
* @pattern: a #cairo_pattern_t
1073,7 → 1930,9
* Note: If the pattern is not a gradient pattern, (eg. a linear or
* radial pattern), then the pattern will be put into an error status
* with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
*/
*
* Since: 1.0
**/
void
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
double offset,
1101,6 → 1960,7
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset, red, green, blue, alpha);
}
slim_hidden_def (cairo_pattern_add_color_stop_rgba);
 
/**
* cairo_pattern_set_matrix:
1133,6 → 1993,8
*
* Also, please note the discussion of the user-space locking
* semantics of cairo_set_source().
*
* Since: 1.0
**/
void
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
1148,6 → 2010,7
return;
 
pattern->matrix = *matrix;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX);
 
inverse = *matrix;
status = cairo_matrix_invert (&inverse);
1162,6 → 2025,8
* @matrix: return value for the matrix
*
* Stores the pattern's transformation matrix into @matrix.
*
* Since: 1.0
**/
void
cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
1188,6 → 2053,8
* cairo_set_source_surface (cr, image, x, y);
* cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
* </programlisting></informalexample>
*
* Since: 1.0
**/
void
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
1196,6 → 2063,7
return;
 
pattern->filter = filter;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER);
}
 
/**
1206,6 → 2074,8
* for details on each filter.
*
* Return value: the current filter used for resizing the pattern.
*
* Since: 1.0
**/
cairo_filter_t
cairo_pattern_get_filter (cairo_pattern_t *pattern)
1225,6 → 2095,8
*
* The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
* and %CAIRO_EXTEND_PAD for gradient patterns.
*
* Since: 1.0
**/
void
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
1233,6 → 2105,7
return;
 
pattern->extend = extend;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND);
}
 
/**
1244,6 → 2117,8
*
* Return value: the current extend strategy used for drawing the
* pattern.
*
* Since: 1.0
**/
cairo_extend_t
cairo_pattern_get_extend (cairo_pattern_t *pattern)
1262,517 → 2137,622
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
}
 
static void
_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
double offset_x,
double offset_y,
int width,
int height,
cairo_bool_t *is_horizontal,
cairo_bool_t *is_vertical)
static cairo_bool_t
_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
{
cairo_point_double_t point0, point1;
double a, b, c, d, tx, ty;
double scale, start, dx, dy;
cairo_fixed_t factors[3];
int i;
return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON &&
fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON;
}
 
/* To classify a pattern as horizontal or vertical, we first
* compute the (fixed point) factors at the corners of the
* pattern. We actually only need 3/4 corners, so we skip the
* fourth.
static cairo_bool_t
_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
{
/* A radial pattern is considered degenerate if it can be
* represented as a solid or clear pattern. This corresponds to
* one of the two cases:
*
* 1) The radii are both very small:
* |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON
*
* 2) The two circles have about the same radius and are very
* close to each other (approximately a cylinder gradient that
* doesn't move with the parameter):
* |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON
*
* These checks are consistent with the assumptions used in
* _cairo_radial_pattern_box_to_parameter ().
*/
point0.x = _cairo_fixed_to_double (pattern->p1.x);
point0.y = _cairo_fixed_to_double (pattern->p1.y);
point1.x = _cairo_fixed_to_double (pattern->p2.x);
point1.y = _cairo_fixed_to_double (pattern->p2.y);
 
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON &&
(MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON ||
MAX (fabs (radial->cd1.center.x - radial->cd2.center.x),
fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON);
}
 
dx = point1.x - point0.x;
dy = point1.y - point0.y;
scale = dx * dx + dy * dy;
scale = (scale) ? 1.0 / scale : 1.0;
static void
_cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear,
double x0, double y0,
double x1, double y1,
double range[2])
{
double t0, tdx, tdy;
double p1x, p1y, pdx, pdy, invsqnorm;
 
start = dx * point0.x + dy * point0.y;
assert (! _linear_pattern_is_degenerate (linear));
 
for (i = 0; i < 3; i++) {
double qx_device = (i % 2) * (width - 1) + offset_x;
double qy_device = (i / 2) * (height - 1) + offset_y;
 
/* transform fragment into pattern space */
double qx = a * qx_device + c * qy_device + tx;
double qy = b * qx_device + d * qy_device + ty;
 
factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
}
 
/* We consider a pattern to be vertical if the fixed point factor
* at the two upper corners is the same. We could accept a small
* change, but determining what change is acceptable would require
* sorting the stops in the pattern and looking at the differences.
/*
* Linear gradients are othrogonal to the line passing through
* their extremes. Because of convexity, the parameter range can
* be computed as the convex hull (one the real line) of the
* parameter values of the 4 corners of the box.
*
* Horizontal works the same way with the two left corners.
* The parameter value t for a point (x,y) can be computed as:
*
* t = (p2 - p1) . (x,y) / |p2 - p1|^2
*
* t0 is the t value for the top left corner
* tdx is the difference between left and right corners
* tdy is the difference between top and bottom corners
*/
 
*is_vertical = factors[1] == factors[0];
*is_horizontal = factors[2] == factors[0];
}
p1x = linear->pd1.x;
p1y = linear->pd1.y;
pdx = linear->pd2.x - p1x;
pdy = linear->pd2.y - p1y;
invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
pdx *= invsqnorm;
pdy *= invsqnorm;
 
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attr)
{
cairo_image_surface_t *image;
pixman_image_t *pixman_image;
pixman_transform_t pixman_transform;
cairo_status_t status;
cairo_bool_t repeat = FALSE;
cairo_bool_t opaque = TRUE;
t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
tdx = (x1 - x0) * pdx;
tdy = (y1 - y0) * pdy;
 
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
unsigned int i;
int clone_offset_x, clone_offset_y;
cairo_matrix_t matrix = pattern->base.matrix;
/*
* Because of the linearity of the t value, tdx can simply be
* added the t0 to move along the top edge. After this, range[0]
* and range[1] represent the parameter range for the top edge, so
* extending it to include the whole box simply requires adding
* tdy to the correct extreme.
*/
 
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
range[0] = range[1] = t0;
if (tdx < 0)
range[0] += tdx;
else
range[1] += tdx;
 
if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
pixman_stops = _cairo_malloc_ab (pattern->n_stops,
sizeof(pixman_gradient_stop_t));
if (unlikely (pixman_stops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (tdy < 0)
range[0] += tdy;
else
range[1] += tdy;
}
 
for (i = 0; i < pattern->n_stops; i++) {
pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset);
pixman_stops[i].color.red = pattern->stops[i].color.red_short;
pixman_stops[i].color.green = pattern->stops[i].color.green_short;
pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha))
opaque = FALSE;
}
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR)
static cairo_bool_t
_extend_range (double range[2], double value, cairo_bool_t valid)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
pixman_point_fixed_t p1, p2;
cairo_fixed_t xdim, ydim;
if (!valid)
range[0] = range[1] = value;
else if (value < range[0])
range[0] = value;
else if (value > range[1])
range[1] = value;
 
xdim = linear->p2.x - linear->p1.x;
ydim = linear->p2.y - linear->p1.y;
return TRUE;
}
 
/*
* 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).
* _cairo_radial_pattern_focus_is_inside:
*
* 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...
* Returns %TRUE if and only if the focus point exists and is
* contained in one of the two extreme circles. This condition is
* equivalent to one of the two extreme circles being completely
* contained in the other one.
*
* Note: if the focus is on the border of one of the two circles (in
* which case the circles are tangent in the focus point), it is not
* considered as contained in the circle, hence this function returns
* %FALSE.
*
*/
#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)
cairo_bool_t
_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
{
double sf;
double cx, cy, cr, dx, dy, dr;
 
if (xdim > ydim)
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
else
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
cx = radial->cd1.center.x;
cy = radial->cd1.center.y;
cr = radial->cd1.radius;
dx = radial->cd2.center.x - cx;
dy = radial->cd2.center.y - cy;
dr = radial->cd2.radius - cr;
 
p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
cairo_matrix_scale (&matrix, sf, sf);
return dx*dx + dy*dy < dr*dr;
}
else
{
p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
}
 
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
}
else
static void
_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
double x0, double y0,
double x1, double y1,
double tolerance,
double range[2])
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
pixman_point_fixed_t c1, c2;
pixman_fixed_t r1, r2;
double cx, cy, cr, dx, dy, dr;
double a, x_focus, y_focus;
double mindr, minx, miny, maxx, maxy;
cairo_bool_t valid;
 
c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
r1 = _cairo_fixed_to_16_16 (radial->r1);
assert (! _radial_pattern_is_degenerate (radial));
assert (x0 < x1);
assert (y0 < y1);
 
c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
r2 = _cairo_fixed_to_16_16 (radial->r2);
tolerance = MAX (tolerance, DBL_EPSILON);
 
pixman_image = pixman_image_create_radial_gradient (&c1, &c2,
r1, r2,
pixman_stops,
pattern->n_stops);
}
range[0] = range[1] = 0;
valid = FALSE;
 
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
x_focus = y_focus = 0; /* silence gcc */
 
if (unlikely (pixman_image == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
cx = radial->cd1.center.x;
cy = radial->cd1.center.y;
cr = radial->cd1.radius;
dx = radial->cd2.center.x - cx;
dy = radial->cd2.center.y - cy;
dr = radial->cd2.radius - cr;
 
if (_cairo_surface_is_image (dst))
{
image = (cairo_image_surface_t *)
_cairo_image_surface_create_for_pixman_image (pixman_image,
PIXMAN_a8r8g8b8);
if (image->base.status)
{
pixman_image_unref (pixman_image);
return image->base.status;
}
/* translate by -(cx, cy) to simplify computations */
x0 -= cx;
y0 -= cy;
x1 -= cx;
y1 -= cy;
 
attr->x_offset = attr->y_offset = 0;
attr->matrix = matrix;
attr->extend = pattern->base.extend;
attr->filter = CAIRO_FILTER_NEAREST;
attr->has_component_alpha = pattern->base.has_component_alpha;
/* enlarge boundaries slightly to avoid rounding problems in the
* parameter range computation */
x0 -= DBL_EPSILON;
y0 -= DBL_EPSILON;
x1 += DBL_EPSILON;
y1 += DBL_EPSILON;
 
*out = &image->base;
/* enlarge boundaries even more to avoid rounding problems when
* testing if a point belongs to the box */
minx = x0 - DBL_EPSILON;
miny = y0 - DBL_EPSILON;
maxx = x1 + DBL_EPSILON;
maxy = y1 + DBL_EPSILON;
 
return CAIRO_STATUS_SUCCESS;
}
/* we dont' allow negative radiuses, so we will be checking that
* t*dr >= mindr to consider t valid */
mindr = -(cr + DBL_EPSILON);
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_bool_t is_horizontal;
cairo_bool_t is_vertical;
/*
* After the previous transformations, the start circle is
* centered in the origin and has radius cr. A 1-unit change in
* the t parameter corresponds to dx,dy,dr changes in the x,y,r of
* the circle (center coordinates, radius).
*
* To compute the minimum range needed to correctly draw the
* pattern, we start with an empty range and extend it to include
* the circles touching the bounding box or within it.
*/
 
_cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
x, y, width, height,
&is_horizontal, &is_vertical);
if (is_horizontal) {
height = 1;
repeat = TRUE;
}
/* width-1 repeating patterns are quite slow with scan-line based
* compositing code, so we use a wider strip and spend some extra
* expense in computing the gradient. It's possible that for narrow
* gradients we'd be better off using a 2 or 4 pixel strip; the
* wider the gradient, the more it's worth spending extra time
* computing a sample.
/*
* Focus, the point where the circle has radius == 0.
*
* r = cr + t * dr = 0
* t = -cr / dr
*
* If the radius is constant (dr == 0) there is no focus (the
* gradient represents a cylinder instead of a cone).
*/
if (is_vertical && width > 8) {
width = 8;
repeat = TRUE;
}
}
if (fabs (dr) >= DBL_EPSILON) {
double t_focus;
 
if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR,
NULL, 0))
t_focus = -cr / dr;
x_focus = t_focus * dx;
y_focus = t_focus * dy;
if (minx <= x_focus && x_focus <= maxx &&
miny <= y_focus && y_focus <= maxy)
{
pixman_image_unref (pixman_image);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
valid = _extend_range (range, t_focus, valid);
}
 
image = (cairo_image_surface_t *)
cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
if (image->base.status) {
pixman_image_unref (pixman_image);
return image->base.status;
}
 
_cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform,
width/2., height/2.);
if (!pixman_image_set_transform (pixman_image, &pixman_transform)) {
cairo_surface_destroy (&image->base);
pixman_image_unref (pixman_image);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
/*
* Circles externally tangent to box edges.
*
* All circles have center in (dx, dy) * t
*
* If the circle is tangent to the line defined by the edge of the
* box, then at least one of the following holds true:
*
* (dx*t) + (cr + dr*t) == x0 (left edge)
* (dx*t) - (cr + dr*t) == x1 (right edge)
* (dy*t) + (cr + dr*t) == y0 (top edge)
* (dy*t) - (cr + dr*t) == y1 (bottom edge)
*
* The solution is only valid if the tangent point is actually on
* the edge, i.e. if its y coordinate is in [y0,y1] for left/right
* edges and if its x coordinate is in [x0,x1] for top/bottom
* edges.
*
* For the first equation:
*
* (dx + dr) * t = x0 - cr
* t = (x0 - cr) / (dx + dr)
* y = dy * t
*
* in the code this becomes:
*
* t_edge = (num) / (den)
* v = (delta) * t_edge
*
* If the denominator in t is 0, the pattern is tangent to a line
* parallel to the edge under examination. The corner-case where
* the boundary line is the same as the edge is handled by the
* focus point case and/or by the a==0 case.
*/
#define T_EDGE(num,den,delta,lower,upper) \
if (fabs (den) >= DBL_EPSILON) { \
double t_edge, v; \
\
t_edge = (num) / (den); \
v = t_edge * (delta); \
if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \
valid = _extend_range (range, t_edge, valid); \
}
 
switch (pattern->base.extend) {
case CAIRO_EXTEND_NONE:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE);
break;
case CAIRO_EXTEND_REPEAT:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL);
break;
case CAIRO_EXTEND_REFLECT:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT);
break;
case CAIRO_EXTEND_PAD:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD);
break;
}
/* circles tangent (externally) to left/right/top/bottom edge */
T_EDGE (x0 - cr, dx + dr, dy, miny, maxy);
T_EDGE (x1 + cr, dx - dr, dy, miny, maxy);
T_EDGE (y0 - cr, dy + dr, dx, minx, maxx);
T_EDGE (y1 + cr, dy - dr, dx, minx, maxx);
 
pixman_image_composite32 (PIXMAN_OP_SRC,
pixman_image,
NULL,
image->pixman_image,
x, y,
0, 0,
0, 0,
width, height);
#undef T_EDGE
 
pixman_image_unref (pixman_image);
/*
* Circles passing through a corner.
*
* A circle passing through the point (x,y) satisfies:
*
* (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
*
* If we set:
* a = dx^2 + dy^2 - dr^2
* b = x*dx + y*dy + cr*dr
* c = x^2 + y^2 - cr^2
* we have:
* a*t^2 - 2*b*t + c == 0
*/
a = dx * dx + dy * dy - dr * dr;
if (fabs (a) < DBL_EPSILON * DBL_EPSILON) {
double b, maxd2;
 
_cairo_debug_check_image_surface_is_defined (&image->base);
/* Ensure that gradients with both a and dr small are
* considered degenerate.
* The floating point version of the degeneracy test implemented
* in _radial_pattern_is_degenerate() is:
*
* 1) The circles are practically the same size:
* |dr| < DBL_EPSILON
* AND
* 2a) The circles are both very small:
* min (r0, r1) < DBL_EPSILON
* OR
* 2b) The circles are very close to each other:
* max (|dx|, |dy|) < 2 * DBL_EPSILON
*
* Assuming that the gradient is not degenerate, we want to
* show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON.
*
* If the gradient is not degenerate yet it has |dr| <
* DBL_EPSILON, (2b) is false, thus:
*
* max (|dx|, |dy|) >= 2*DBL_EPSILON
* which implies:
* 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
*
* From the definition of a, we get:
* a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2
* dx^2 + dy^2 - DBL_EPSILON^2 < dr^2
* 3*DBL_EPSILON^2 < dr^2
*
* which is inconsistent with the hypotheses, thus |dr| <
* DBL_EPSILON is false or the gradient is degenerate.
*/
assert (fabs (dr) >= DBL_EPSILON);
 
status = _cairo_surface_clone_similar (dst, &image->base,
0, 0, width, height,
&clone_offset_x,
&clone_offset_y,
out);
 
cairo_surface_destroy (&image->base);
 
attr->x_offset = -x;
attr->y_offset = -y;
cairo_matrix_init_identity (&attr->matrix);
attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
attr->filter = CAIRO_FILTER_NEAREST;
attr->has_component_alpha = pattern->base.has_component_alpha;
 
return status;
/*
* If a == 0, all the circles are tangent to a line in the
* focus point. If this line is within the box extents, we
* should add the circle with infinite radius, but this would
* make the range unbounded, so we add the smallest circle whose
* distance to the desired (degenerate) circle within the
* bounding box does not exceed tolerance.
*
* The equation of the line is b==0, i.e.:
* x*dx + y*dy + cr*dr == 0
*
* We compute the intersection of the line with the box and
* keep the intersection with maximum square distance (maxd2)
* from the focus point.
*
* In the code the intersection is represented in another
* coordinate system, whose origin is the focus point and
* which has a u,v axes, which are respectively orthogonal and
* parallel to the edge being intersected.
*
* The intersection is valid only if it belongs to the box,
* otherwise it is ignored.
*
* For example:
*
* y = y0
* x*dx + y0*dy + cr*dr == 0
* x = -(y0*dy + cr*dr) / dx
*
* which in (u,v) is:
* u = y0 - y_focus
* v = -(y0*dy + cr*dr) / dx - x_focus
*
* In the code:
* u = (edge) - (u_origin)
* v = -((edge) * (delta) + cr*dr) / (den) - v_focus
*/
#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \
if (fabs (den) >= DBL_EPSILON) { \
double v; \
\
v = -((edge) * (delta) + cr * dr) / (den); \
if ((lower) <= v && v <= (upper)) { \
double u, d2; \
\
u = (edge) - (u_origin); \
v -= (v_origin); \
d2 = u*u + v*v; \
if (maxd2 < d2) \
maxd2 = d2; \
} \
}
 
/* We maintain a small cache here, because we don't want to constantly
* recreate surfaces for simple solid colors. */
#define MAX_SURFACE_CACHE_SIZE 16
static struct {
struct _cairo_pattern_solid_surface_cache{
cairo_color_t color;
cairo_surface_t *surface;
} cache[MAX_SURFACE_CACHE_SIZE];
int size;
} solid_surface_cache;
maxd2 = 0;
 
static cairo_bool_t
_cairo_pattern_solid_surface_matches (
const struct _cairo_pattern_solid_surface_cache *cache,
const cairo_solid_pattern_t *pattern,
cairo_surface_t *dst)
{
if (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color))
return FALSE;
/* degenerate circles (lines) passing through each edge */
T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus);
T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus);
T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus);
T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus);
 
if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1)
return FALSE;
#undef T_EDGE
 
if (! _cairo_surface_is_similar (cache->surface, dst))
return FALSE;
 
return TRUE;
/*
* The limit circle can be transformed rigidly to the y=0 line
* and the circles tangent to it in (0,0) are:
*
* x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0
*
* y is the distance from the line, in our case tolerance;
* x is the distance along the line, i.e. sqrt(maxd2),
* so:
*
* r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance)
* t = (r - cr) / dr =
* (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
*/
if (maxd2 > 0) {
double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
t_limit /= 2 * tolerance * dr;
valid = _extend_range (range, t_limit, valid);
}
 
static cairo_bool_t
_cairo_pattern_solid_surface_matches_color (
const struct _cairo_pattern_solid_surface_cache *cache,
const cairo_solid_pattern_t *pattern,
cairo_surface_t *dst)
{
if (! _cairo_color_equal (&cache->color, &pattern->color))
return FALSE;
 
return _cairo_pattern_solid_surface_matches (cache, pattern, dst);
/*
* Nondegenerate, nonlimit circles passing through the corners.
*
* a == 0 && a*t^2 - 2*b*t + c == 0
*
* t = c / (2*b)
*
* The b == 0 case has just been handled, so we only have to
* compute this if b != 0.
*/
#define T_CORNER(x,y) \
b = (x) * dx + (y) * dy + cr * dr; \
if (fabs (b) >= DBL_EPSILON) { \
double t_corner; \
double x2 = (x) * (x); \
double y2 = (y) * (y); \
double cr2 = (cr) * (cr); \
double c = x2 + y2 - cr2; \
\
t_corner = 0.5 * c / b; \
if (t_corner * dr >= mindr) \
valid = _extend_range (range, t_corner, valid); \
}
 
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_solid (const cairo_solid_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attribs)
{
static int i;
/* circles touching each corner */
T_CORNER (x0, y0);
T_CORNER (x0, y1);
T_CORNER (x1, y0);
T_CORNER (x1, y1);
 
cairo_surface_t *surface, *to_destroy = NULL;
cairo_status_t status;
#undef T_CORNER
} else {
double inva, b, c, d;
 
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
inva = 1 / a;
 
/* Check cache first */
if (i < solid_surface_cache.size &&
_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i],
pattern,
dst))
{
goto DONE;
/*
* Nondegenerate, nonlimit circles passing through the corners.
*
* a != 0 && a*t^2 - 2*b*t + c == 0
*
* t = (b +- sqrt (b*b - a*c)) / a
*
* If the argument of sqrt() is negative, then no circle
* passes through the corner.
*/
#define T_CORNER(x,y) \
b = (x) * dx + (y) * dy + cr * dr; \
c = (x) * (x) + (y) * (y) - cr * cr; \
d = b * b - a * c; \
if (d >= 0) { \
double t_corner; \
\
d = sqrt (d); \
t_corner = (b + d) * inva; \
if (t_corner * dr >= mindr) \
valid = _extend_range (range, t_corner, valid); \
t_corner = (b - d) * inva; \
if (t_corner * dr >= mindr) \
valid = _extend_range (range, t_corner, valid); \
}
 
for (i = 0 ; i < solid_surface_cache.size; i++) {
if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i],
pattern,
dst))
{
goto DONE;
/* circles touching each corner */
T_CORNER (x0, y0);
T_CORNER (x0, y1);
T_CORNER (x1, y0);
T_CORNER (x1, y1);
 
#undef T_CORNER
}
}
 
/* Choose a surface to repaint/evict */
surface = NULL;
if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) {
i = rand () % MAX_SURFACE_CACHE_SIZE;
surface = solid_surface_cache.cache[i].surface;
 
if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
pattern,
dst))
/**
* _cairo_gradient_pattern_box_to_parameter:
*
* Compute a interpolation range sufficient to draw (within the given
* tolerance) the gradient in the given box getting the same result as
* using the (-inf, +inf) range.
*
* Assumes that the pattern is not degenerate. This can be guaranteed
* by simplifying it to a solid clear if _cairo_pattern_is_clear or to
* a solid color if _cairo_gradient_pattern_is_solid.
*
* The range isn't guaranteed to be minimal, but it tries to.
**/
void
_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
double x0, double y0,
double x1, double y1,
double tolerance,
double out_range[2])
{
/* Reuse the surface instead of evicting */
status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern);
if (unlikely (status))
goto EVICT;
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
cairo_surface_reference (surface);
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
_cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient,
x0, y0, x1, y1, out_range);
} else {
_cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient,
x0, y0, x1, y1, tolerance, out_range);
}
else
{
EVICT:
surface = NULL;
}
}
 
if (surface == NULL) {
/* Not cached, need to create new */
surface = _cairo_surface_create_solid_pattern_surface (dst, pattern);
if (surface == NULL) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto UNLOCK;
}
if (unlikely (surface->status)) {
status = surface->status;
goto UNLOCK;
}
 
if (unlikely (! _cairo_surface_is_similar (surface, dst)))
/**
* _cairo_gradient_pattern_interpolate:
*
* Interpolate between the start and end objects of linear or radial
* gradients. The interpolated object is stored in out_circle, with
* the radius being zero in the linear gradient case.
**/
void
_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
double t,
cairo_circle_double_t *out_circle)
{
/* In the rare event of a substitute surface being returned,
* don't cache the fallback.
*/
*out = surface;
goto NOCACHE;
}
}
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
if (i == solid_surface_cache.size)
solid_surface_cache.size++;
#define lerp(a,b) (a)*(1-t) + (b)*t
 
to_destroy = solid_surface_cache.cache[i].surface;
solid_surface_cache.cache[i].surface = surface;
solid_surface_cache.cache[i].color = pattern->color;
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x);
out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y);
out_circle->radius = 0;
} else {
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x);
out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y);
out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius);
}
 
DONE:
*out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
#undef lerp
}
 
NOCACHE:
attribs->x_offset = attribs->y_offset = 0;
cairo_matrix_init_identity (&attribs->matrix);
attribs->extend = CAIRO_EXTEND_REPEAT;
attribs->filter = CAIRO_FILTER_NEAREST;
attribs->has_component_alpha = pattern->base.has_component_alpha;
 
status = CAIRO_STATUS_SUCCESS;
/**
* _cairo_gradient_pattern_fit_to_range:
*
* Scale the extremes of a gradient to guarantee that the coordinates
* and their deltas are within the range (-max_value, max_value). The
* new extremes are stored in out_circle.
*
* The pattern matrix is scaled to guarantee that the aspect of the
* gradient is the same and the result is stored in out_matrix.
*
**/
void
_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
double max_value,
cairo_matrix_t *out_matrix,
cairo_circle_double_t out_circle[2])
{
double dim;
 
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
if (to_destroy)
cairo_surface_destroy (to_destroy);
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
 
return status;
}
out_circle[0].center = linear->pd1;
out_circle[0].radius = 0;
out_circle[1].center = linear->pd2;
out_circle[1].radius = 0;
 
static void
_cairo_pattern_reset_solid_surface_cache (void)
{
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
dim = fabs (linear->pd1.x);
dim = MAX (dim, fabs (linear->pd1.y));
dim = MAX (dim, fabs (linear->pd2.x));
dim = MAX (dim, fabs (linear->pd2.y));
dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x));
dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y));
} else {
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
 
/* remove surfaces starting from the end so that solid_surface_cache.cache
* is always in a consistent state when we release the mutex. */
while (solid_surface_cache.size) {
cairo_surface_t *surface;
out_circle[0] = radial->cd1;
out_circle[1] = radial->cd2;
 
solid_surface_cache.size--;
surface = solid_surface_cache.cache[solid_surface_cache.size].surface;
solid_surface_cache.cache[solid_surface_cache.size].surface = NULL;
 
/* release the lock to avoid the possibility of a recursive
* deadlock when the surface destroy closure gets called */
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
cairo_surface_destroy (surface);
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
dim = fabs (radial->cd1.center.x);
dim = MAX (dim, fabs (radial->cd1.center.y));
dim = MAX (dim, fabs (radial->cd1.radius));
dim = MAX (dim, fabs (radial->cd2.center.x));
dim = MAX (dim, fabs (radial->cd2.center.y));
dim = MAX (dim, fabs (radial->cd2.radius));
dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x));
dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y));
dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius));
}
 
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
}
if (unlikely (dim > max_value)) {
cairo_matrix_t scale;
 
static void
_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
const cairo_rectangle_int_t *extents,
double t[2])
{
double t0, tdx, tdy;
double p1x, p1y, pdx, pdy, invsqnorm;
dim = max_value / dim;
 
p1x = _cairo_fixed_to_double (linear->p1.x);
p1y = _cairo_fixed_to_double (linear->p1.y);
pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
pdx *= invsqnorm;
pdy *= invsqnorm;
out_circle[0].center.x *= dim;
out_circle[0].center.y *= dim;
out_circle[0].radius *= dim;
out_circle[1].center.x *= dim;
out_circle[1].center.y *= dim;
out_circle[1].radius *= dim;
 
t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
tdx = extents->width * pdx;
tdy = extents->height * pdy;
 
t[0] = t[1] = t0;
if (tdx < 0)
t[0] += tdx;
else
t[1] += tdx;
 
if (tdy < 0)
t[0] += tdy;
else
t[1] += tdy;
cairo_matrix_init_scale (&scale, dim, dim);
cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale);
} else {
*out_matrix = gradient->base.matrix;
}
 
static cairo_bool_t
_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
{
return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y;
}
 
static cairo_bool_t
_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
{
return radial->r1 == radial->r2 &&
(radial->r1 == 0 /* && radial->r2 == 0 */ ||
(radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y));
}
 
static cairo_bool_t
_gradient_is_clear (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents)
{
1786,27 → 2766,40
gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
return TRUE;
 
/* Check if the extents intersect the drawn part of the pattern. */
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
if (gradient->base.extend == CAIRO_EXTEND_NONE) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) {
/* degenerate radial gradients are clear */
if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient))
return TRUE;
} else if (gradient->base.extend == CAIRO_EXTEND_NONE) {
/* EXTEND_NONE degenerate linear gradients are clear */
if (_linear_pattern_is_degenerate (linear))
if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
return TRUE;
}
 
if (extents != NULL) {
/* Check if the extents intersect the drawn part of the pattern. */
if (extents != NULL &&
(gradient->base.extend == CAIRO_EXTEND_NONE ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL))
{
double t[2];
_extents_to_linear_parameter (linear, extents, t);
if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0))
 
_cairo_gradient_pattern_box_to_parameter (gradient,
extents->x,
extents->y,
extents->x + extents->width,
extents->y + extents->height,
DBL_EPSILON,
t);
 
if (gradient->base.extend == CAIRO_EXTEND_NONE &&
(t[0] >= gradient->stops[gradient->n_stops - 1].offset ||
t[1] <= gradient->stops[0].offset))
{
return TRUE;
}
}
} else {
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
/* degenerate radial gradients are clear */
if (_radial_pattern_is_degenerate (radial))
 
if (t[0] == t[1])
return TRUE;
/* TODO: check actual intersection */
}
 
for (i = 0; i < gradient->n_stops; i++)
1927,8 → 2920,144
}
 
/**
* _cairo_gradient_pattern_is_solid
* _cairo_pattern_alpha_range:
*
* Convenience function to determine the minimum and maximum alpha in
* the drawn part of a pattern (i.e. ignoring clear parts caused by
* extend modes and/or pattern shape).
*
* If not NULL, out_min and out_max will be set respectively to the
* minimum and maximum alpha value of the pattern.
**/
void
_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
double *out_min,
double *out_max)
{
double alpha_min, alpha_max;
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID: {
const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
alpha_min = alpha_max = solid->color.alpha;
break;
}
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL: {
const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
unsigned int i;
 
assert (gradient->n_stops >= 1);
 
alpha_min = alpha_max = gradient->stops[0].color.alpha;
for (i = 1; i < gradient->n_stops; i++) {
if (alpha_min > gradient->stops[i].color.alpha)
alpha_min = gradient->stops[i].color.alpha;
else if (alpha_max < gradient->stops[i].color.alpha)
alpha_max = gradient->stops[i].color.alpha;
}
 
break;
}
 
case CAIRO_PATTERN_TYPE_MESH: {
const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern;
const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches);
 
assert (n >= 1);
 
alpha_min = alpha_max = patch[0].colors[0].alpha;
for (i = 0; i < n; i++) {
for (j = 0; j < 4; j++) {
if (patch[i].colors[j].alpha < alpha_min)
alpha_min = patch[i].colors[j].alpha;
else if (patch[i].colors[j].alpha > alpha_max)
alpha_max = patch[i].colors[j].alpha;
}
}
 
break;
}
 
default:
ASSERT_NOT_REACHED;
/* fall through */
 
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
alpha_min = 0;
alpha_max = 1;
break;
}
 
if (out_min)
*out_min = alpha_min;
if (out_max)
*out_max = alpha_max;
}
 
/**
* _cairo_mesh_pattern_coord_box:
*
* Convenience function to determine the range of the coordinates of
* the points used to define the patches of the mesh.
*
* This is guaranteed to contain the pattern extents, but might not be
* tight, just like a Bezier curve is always inside the convex hull of
* the control points.
*
* This function cannot be used while the mesh is being constructed.
*
* The function returns TRUE and sets the output parametes to define
* the coodrinate range if the mesh pattern contains at least one
* patch, otherwise it returns FALSE.
**/
cairo_bool_t
_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
double *out_xmin,
double *out_ymin,
double *out_xmax,
double *out_ymax)
{
const cairo_mesh_patch_t *patch;
unsigned int num_patches, i, j, k;
double x0, y0, x1, y1;
 
assert (mesh->current_patch == NULL);
 
num_patches = _cairo_array_num_elements (&mesh->patches);
 
if (num_patches == 0)
return FALSE;
 
patch = _cairo_array_index_const (&mesh->patches, 0);
x0 = x1 = patch->points[0][0].x;
y0 = y1 = patch->points[0][0].y;
 
for (i = 0; i < num_patches; i++) {
for (j = 0; j < 4; j++) {
for (k = 0; k < 4; k++) {
x0 = MIN (x0, patch[i].points[j][k].x);
y0 = MIN (y0, patch[i].points[j][k].y);
x1 = MAX (x1, patch[i].points[j][k].x);
y1 = MAX (y1, patch[i].points[j][k].y);
}
}
}
 
*out_xmin = x0;
*out_ymin = y0;
*out_xmax = x1;
*out_ymax = y1;
 
return TRUE;
}
 
/**
* _cairo_gradient_pattern_is_solid:
*
* Convenience function to determine whether a gradient pattern is
* a solid color within the given extents. In this case the color
* argument is initialized to the color the pattern represents.
1966,7 → 3095,13
if (extents == NULL)
return FALSE;
 
_extents_to_linear_parameter (linear, extents, t);
_cairo_linear_pattern_box_to_parameter (linear,
extents->x,
extents->y,
extents->x + extents->width,
extents->y + extents->height,
t);
 
if (t[0] < 0.0 || t[1] > 1.0)
return FALSE;
}
1987,8 → 3122,24
return TRUE;
}
 
static cairo_bool_t
_mesh_is_clear (const cairo_mesh_pattern_t *mesh)
{
double x1, y1, x2, y2;
cairo_bool_t is_valid;
 
is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
if (!is_valid)
return TRUE;
 
if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON)
return TRUE;
 
return FALSE;
}
 
/**
* _cairo_pattern_is_opaque_solid
* _cairo_pattern_is_opaque_solid:
*
* Convenience function to determine whether a pattern is an opaque
* (alpha==1.0) solid color pattern. This is done by testing whether
2013,8 → 3164,10
 
static cairo_bool_t
_surface_is_opaque (const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *r)
const cairo_rectangle_int_t *sample)
{
cairo_rectangle_int_t extents;
 
if (pattern->surface->content & CAIRO_CONTENT_ALPHA)
return FALSE;
 
2021,22 → 3174,29
if (pattern->base.extend != CAIRO_EXTEND_NONE)
return TRUE;
 
if (r != NULL) {
cairo_rectangle_int_t extents;
 
if (! _cairo_surface_get_extents (pattern->surface, &extents))
return TRUE;
 
if (r->x >= extents.x &&
r->y >= extents.y &&
r->x + r->width <= extents.x + extents.width &&
r->y + r->height <= extents.y + extents.height)
if (sample == NULL)
return FALSE;
 
return _cairo_rectangle_contains_rectangle (&extents, sample);
}
 
static cairo_bool_t
_raster_source_is_opaque (const cairo_raster_source_pattern_t *pattern,
const cairo_rectangle_int_t *sample)
{
if (pattern->content & CAIRO_CONTENT_ALPHA)
return FALSE;
 
if (pattern->base.extend != CAIRO_EXTEND_NONE)
return TRUE;
}
}
 
if (sample == NULL)
return FALSE;
 
return _cairo_rectangle_contains_rectangle (&pattern->extents, sample);
}
 
static cairo_bool_t
2053,8 → 3213,14
}
 
static cairo_bool_t
_raster_source_is_clear (const cairo_raster_source_pattern_t *pattern)
{
return pattern->extents.width == 0 || pattern->extents.height == 0;
}
 
static cairo_bool_t
_gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents)
const cairo_rectangle_int_t *sample)
{
unsigned int i;
 
2075,10 → 3241,16
if (_linear_pattern_is_degenerate (linear))
return FALSE;
 
if (extents == NULL)
if (sample == NULL)
return FALSE;
 
_extents_to_linear_parameter (linear, extents, t);
_cairo_linear_pattern_box_to_parameter (linear,
sample->x,
sample->y,
sample->x + sample->width,
sample->y + sample->height,
t);
 
if (t[0] < 0.0 || t[1] > 1.0)
return FALSE;
}
2093,7 → 3265,7
}
 
/**
* _cairo_pattern_is_opaque
* _cairo_pattern_is_opaque:
*
* Convenience function to determine whether a pattern is an opaque
* pattern (of any type). The same caveats that apply to
2103,7 → 3275,7
**/
cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
const cairo_rectangle_int_t *extents)
const cairo_rectangle_int_t *sample)
{
const cairo_pattern_union_t *pattern;
 
2115,10 → 3287,14
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_pattern_is_opaque_solid (abstract_pattern);
case CAIRO_PATTERN_TYPE_SURFACE:
return _surface_is_opaque (&pattern->surface, extents);
return _surface_is_opaque (&pattern->surface, sample);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _raster_source_is_opaque (&pattern->raster_source, sample);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _gradient_is_opaque (&pattern->gradient.base, extents);
return _gradient_is_opaque (&pattern->gradient.base, sample);
case CAIRO_PATTERN_TYPE_MESH:
return FALSE;
}
 
ASSERT_NOT_REACHED;
2134,14 → 3310,18
return FALSE;
 
pattern = (cairo_pattern_union_t *) abstract_pattern;
switch (pattern->type) {
switch (abstract_pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color);
case CAIRO_PATTERN_TYPE_SURFACE:
return _surface_is_clear (&pattern->surface);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _raster_source_is_clear (&pattern->raster_source);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _gradient_is_clear (&pattern->gradient.base, NULL);
case CAIRO_PATTERN_TYPE_MESH:
return _mesh_is_clear (&pattern->mesh);
}
 
ASSERT_NOT_REACHED;
2161,7 → 3341,7
* XXX: We don't actually have any way of querying the backend for
* the filter radius, so we just guess base on what we know that
* backends do currently (see bug #10508)
*/
**/
cairo_filter_t
_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern,
double *pad_out)
2206,439 → 3386,56
return optimized_filter;
}
 
 
static double
_pixman_nearest_sample (double d)
cairo_filter_t
_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *sample)
{
return ceil (d - .5);
}
 
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
unsigned int flags,
cairo_surface_t **out,
cairo_surface_attributes_t *attr)
{
cairo_surface_t *surface;
cairo_rectangle_int_t extents;
cairo_rectangle_int_t sampled_area;
double x1, y1, x2, y2;
int tx, ty;
cairo_filter_t filter;
double x1, x2, y1, y2;
double pad;
cairo_bool_t is_identity;
cairo_bool_t is_empty;
cairo_bool_t is_bounded;
cairo_int_status_t status;
 
surface = cairo_surface_reference (pattern->surface);
 
is_identity = FALSE;
attr->matrix = pattern->base.matrix;
attr->extend = pattern->base.extend;
attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad);
attr->has_component_alpha = pattern->base.has_component_alpha;
 
attr->x_offset = attr->y_offset = tx = ty = 0;
if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
cairo_matrix_init_identity (&attr->matrix);
attr->x_offset = tx;
attr->y_offset = ty;
is_identity = TRUE;
} else if (attr->filter == CAIRO_FILTER_NEAREST) {
/*
* For NEAREST, we can remove the fractional translation component
* from the transformation - this ensures that the pattern will always
* hit fast-paths in the backends for simple transformations that
* become (almost) identity, without loss of quality.
*/
attr->matrix.x0 = 0;
attr->matrix.y0 = 0;
if (_cairo_matrix_is_pixel_exact (&attr->matrix)) {
/* The rounding here is rather peculiar as it needs to match the
* rounding performed on the sample coordinate used by pixman.
*/
attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0);
attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0);
} else {
attr->matrix.x0 = pattern->base.matrix.x0;
attr->matrix.y0 = pattern->base.matrix.y0;
filter = _cairo_pattern_analyze_filter (pattern, &pad);
if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) {
*sample = *extents;
return filter;
}
 
if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
cairo_matrix_init_identity (&attr->matrix);
attr->x_offset = tx;
attr->y_offset = ty;
is_identity = TRUE;
}
}
x1 = extents->x;
y1 = extents->y;
x2 = extents->x + (int) extents->width;
y2 = extents->y + (int) extents->height;
 
/* XXX: Hack:
*
* The way we currently support CAIRO_EXTEND_REFLECT is to create
* an image twice bigger on each side, and create a pattern of four
* images such that the new image, when repeated, has the same effect
* of reflecting the original pattern.
*/
if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT &&
attr->extend == CAIRO_EXTEND_REFLECT)
{
cairo_t *cr;
cairo_surface_t *src;
int w, h;
 
is_bounded = _cairo_surface_get_extents (surface, &extents);
assert (is_bounded);
 
status = _cairo_surface_clone_similar (dst, surface,
extents.x, extents.y,
extents.width, extents.height,
&extents.x, &extents.y, &src);
if (unlikely (status))
goto BAIL;
 
w = 2 * extents.width;
h = 2 * extents.height;
 
if (is_identity) {
attr->x_offset = -x;
x += tx;
while (x <= -w)
x += w;
while (x >= w)
x -= w;
extents.x += x;
tx = x = 0;
 
attr->y_offset = -y;
y += ty;
while (y <= -h)
y += h;
while (y >= h)
y -= h;
extents.y += y;
ty = y = 0;
}
 
cairo_surface_destroy (surface);
surface = _cairo_surface_create_similar_solid (dst,
dst->content, w, h,
CAIRO_COLOR_TRANSPARENT,
FALSE);
if (surface == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (surface->status)) {
cairo_surface_destroy (src);
return surface->status;
}
 
surface->device_transform = pattern->surface->device_transform;
surface->device_transform_inverse = pattern->surface->device_transform_inverse;
 
cr = cairo_create (surface);
 
cairo_set_source_surface (cr, src, -extents.x, -extents.y);
cairo_paint (cr);
 
cairo_scale (cr, -1, +1);
cairo_set_source_surface (cr, src, extents.x-w, -extents.y);
cairo_paint (cr);
cairo_set_source_surface (cr, src, extents.x, -extents.y);
cairo_paint (cr);
 
cairo_scale (cr, +1, -1);
cairo_set_source_surface (cr, src, extents.x-w, extents.y-h);
cairo_paint (cr);
cairo_set_source_surface (cr, src, extents.x, extents.y-h);
cairo_paint (cr);
cairo_set_source_surface (cr, src, extents.x-w, extents.y);
cairo_paint (cr);
cairo_set_source_surface (cr, src, extents.x, extents.y);
cairo_paint (cr);
 
cairo_scale (cr, -1, +1);
cairo_set_source_surface (cr, src, -extents.x, extents.y-h);
cairo_paint (cr);
cairo_set_source_surface (cr, src, -extents.x, extents.y);
cairo_paint (cr);
 
status = cairo_status (cr);
cairo_destroy (cr);
 
cairo_surface_destroy (src);
 
if (unlikely (status))
goto BAIL;
 
attr->extend = CAIRO_EXTEND_REPEAT;
}
 
/* We first transform the rectangle to the coordinate space of the
* source surface so that we only need to clone that portion of the
* surface that will be read.
*/
x1 = x;
y1 = y;
x2 = x + (int) width;
y2 = y + (int) height;
if (! is_identity) {
_cairo_matrix_transform_bounding_box (&attr->matrix,
_cairo_matrix_transform_bounding_box (&pattern->matrix,
&x1, &y1, &x2, &y2,
NULL);
}
if (x1 > CAIRO_RECT_INT_MIN)
sample->x = floor (x1 - pad);
else
sample->x = CAIRO_RECT_INT_MIN;
 
sampled_area.x = floor (x1 - pad);
sampled_area.y = floor (y1 - pad);
sampled_area.width = ceil (x2 + pad) - sampled_area.x;
sampled_area.height = ceil (y2 + pad) - sampled_area.y;
if (y1 > CAIRO_RECT_INT_MIN)
sample->y = floor (y1 - pad);
else
sample->y = CAIRO_RECT_INT_MIN;
 
sampled_area.x += tx;
sampled_area.y += ty;
if (x2 < CAIRO_RECT_INT_MAX)
sample->width = ceil (x2 + pad);
else
sample->width = CAIRO_RECT_INT_MAX;
 
if ( _cairo_surface_get_extents (surface, &extents)) {
if (attr->extend == CAIRO_EXTEND_NONE) {
/* Never acquire a larger area than the source itself */
is_empty = _cairo_rectangle_intersect (&extents, &sampled_area);
} else {
int trim = 0;
if (y2 < CAIRO_RECT_INT_MAX)
sample->height = ceil (y2 + pad);
else
sample->height = CAIRO_RECT_INT_MAX;
 
if (sampled_area.x >= extents.x &&
sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width)
{
/* source is horizontally contained within extents, trim */
extents.x = sampled_area.x;
extents.width = sampled_area.width;
trim |= 0x1;
}
sample->width -= sample->x;
sample->height -= sample->y;
 
if (sampled_area.y >= extents.y &&
sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height)
{
/* source is vertically contained within extents, trim */
extents.y = sampled_area.y;
extents.height = sampled_area.height;
trim |= 0x2;
return filter;
}
 
if (trim == 0x3) {
/* source is wholly contained within extents, drop the REPEAT */
attr->extend = CAIRO_EXTEND_NONE;
}
 
is_empty = extents.width == 0 || extents.height == 0;
}
}
 
/* XXX can we use is_empty? */
 
status = _cairo_surface_clone_similar (dst, surface,
extents.x, extents.y,
extents.width, extents.height,
&x, &y, out);
if (unlikely (status))
goto BAIL;
 
if (x != 0 || y != 0) {
if (is_identity) {
attr->x_offset -= x;
attr->y_offset -= y;
} else {
cairo_matrix_t m;
 
x -= attr->x_offset;
y -= attr->y_offset;
attr->x_offset = 0;
attr->y_offset = 0;
 
cairo_matrix_init_translate (&m, -x, -y);
cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m);
}
}
 
/* reduce likelihood of range overflow with large downscaling */
if (! is_identity) {
cairo_matrix_t m;
cairo_status_t invert_status;
 
m = attr->matrix;
invert_status = cairo_matrix_invert (&m);
assert (invert_status == CAIRO_STATUS_SUCCESS);
 
if (m.x0 != 0. || m.y0 != 0.) {
/* pixman also limits the [xy]_offset to 16 bits so evenly
* spread the bits between the two.
*/
x = floor (m.x0 / 2);
y = floor (m.y0 / 2);
attr->x_offset -= x;
attr->y_offset -= y;
cairo_matrix_init_translate (&m, x, y);
cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix);
}
}
 
BAIL:
cairo_surface_destroy (surface);
return status;
}
 
/**
* _cairo_pattern_acquire_surface:
* @pattern: a #cairo_pattern_t
* @dst: destination surface
* @x: X coordinate in source corresponding to left side of destination area
* @y: Y coordinate in source corresponding to top side of destination area
* @width: width of destination area
* @height: height of destination area
* @surface_out: location to store a pointer to a surface
* @attributes: surface attributes that destination backend should apply to
* the returned surface
*
* A convenience function to obtain a surface to use as the source for
* drawing on @dst.
*
* Note that this function is only suitable for use when the destination
* surface is pixel based and 1 device unit maps to one pixel.
*
* Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out.
**/
cairo_int_status_t
_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
unsigned int flags,
cairo_surface_t **surface_out,
cairo_surface_attributes_t *attributes)
{
if (unlikely (pattern->status)) {
*surface_out = NULL;
return pattern->status;
}
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern,
dst, x, y, width, height,
surface_out,
attributes);
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern,
dst, x, y, width, height,
surface_out,
attributes);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern,
dst, x, y, width, height,
flags,
surface_out,
attributes);
 
default:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
}
}
 
/**
* _cairo_pattern_release_surface:
* @pattern: a #cairo_pattern_t
* @surface: a surface obtained by _cairo_pattern_acquire_surface
* @attributes: attributes obtained by _cairo_pattern_acquire_surface
*
* Releases resources obtained by _cairo_pattern_acquire_surface.
**/
void
_cairo_pattern_release_surface (const cairo_pattern_t *pattern,
cairo_surface_t *surface,
cairo_surface_attributes_t *attributes)
{
cairo_surface_destroy (surface);
}
 
cairo_int_status_t
_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
unsigned int width,
unsigned int height,
unsigned int flags,
cairo_surface_t **src_out,
cairo_surface_t **mask_out,
cairo_surface_attributes_t *src_attributes,
cairo_surface_attributes_t *mask_attributes)
{
cairo_int_status_t status;
cairo_pattern_union_t src_tmp;
 
if (unlikely (src->status))
return src->status;
if (unlikely (mask != NULL && mask->status))
return mask->status;
 
/* If src and mask are both solid, then the mask alpha can be
* combined into src and mask can be ignored. */
 
if (src->type == CAIRO_PATTERN_TYPE_SOLID &&
mask &&
! mask->has_component_alpha &&
mask->type == CAIRO_PATTERN_TYPE_SOLID)
{
cairo_color_t combined;
cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
 
combined = src_solid->color;
_cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
 
_cairo_pattern_init_solid (&src_tmp.solid, &combined);
 
src = &src_tmp.base;
mask = NULL;
}
 
status = _cairo_pattern_acquire_surface (src, dst,
src_x, src_y,
width, height,
flags,
src_out, src_attributes);
if (unlikely (status))
goto BAIL;
 
if (mask == NULL) {
*mask_out = NULL;
goto BAIL;
}
 
status = _cairo_pattern_acquire_surface (mask, dst,
mask_x, mask_y,
width, height,
flags,
mask_out, mask_attributes);
if (unlikely (status))
_cairo_pattern_release_surface (src, *src_out, src_attributes);
 
BAIL:
if (src == &src_tmp.base)
_cairo_pattern_fini (&src_tmp.base);
 
return status;
}
 
/**
* _cairo_pattern_get_extents:
*
* Return the "target-space" extents of @pattern in @extents.
2689,6 → 3486,29
}
break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
{
const cairo_raster_source_pattern_t *raster =
(const cairo_raster_source_pattern_t *) pattern;
double pad;
 
if (raster->extents.width == 0 || raster->extents.height == 0)
goto EMPTY;
 
if (pattern->extend != CAIRO_EXTEND_NONE)
goto UNBOUNDED;
 
/* The filter can effectively enlarge the extents of the
* pattern, so extend as necessary.
*/
_cairo_pattern_analyze_filter (pattern, &pad);
x1 = raster->extents.x - pad;
y1 = raster->extents.y - pad;
x2 = raster->extents.x + (int) raster->extents.width + pad;
y2 = raster->extents.y + (int) raster->extents.height + pad;
}
break;
 
case CAIRO_PATTERN_TYPE_RADIAL:
{
const cairo_radial_pattern_t *radial =
2695,40 → 3515,32
(const cairo_radial_pattern_t *) pattern;
double cx1, cy1;
double cx2, cy2;
double r, D;
double r1, r2;
 
if (radial->r1 == 0 && radial->r2 == 0)
if (_radial_pattern_is_degenerate (radial)) {
/* cairo-gstate should have optimised degenerate
* patterns to solid clear patterns, so we can ignore
* them here. */
goto EMPTY;
}
 
cx1 = _cairo_fixed_to_double (radial->c1.x);
cy1 = _cairo_fixed_to_double (radial->c1.y);
r = _cairo_fixed_to_double (radial->r1);
x1 = cx1 - r; x2 = cx1 + r;
y1 = cy1 - r; y2 = cy1 + r;
 
cx2 = _cairo_fixed_to_double (radial->c2.x);
cy2 = _cairo_fixed_to_double (radial->c2.y);
r = fabs (_cairo_fixed_to_double (radial->r2));
 
/* TODO: in some cases (focus outside/on the circle) it is
* half-bounded. */
if (pattern->extend != CAIRO_EXTEND_NONE)
goto UNBOUNDED;
 
/* We need to be careful, as if the circles are not
* self-contained, then the solution is actually unbounded.
*/
D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2);
if (D > r*r - 1e-5)
goto UNBOUNDED;
cx1 = radial->cd1.center.x;
cy1 = radial->cd1.center.y;
r1 = radial->cd1.radius;
 
if (cx2 - r < x1)
x1 = cx2 - r;
if (cx2 + r > x2)
x2 = cx2 + r;
cx2 = radial->cd2.center.x;
cy2 = radial->cd2.center.y;
r2 = radial->cd2.radius;
 
if (cy2 - r < y1)
y1 = cy2 - r;
if (cy2 + r > y2)
y2 = cy2 + r;
x1 = MIN (cx1 - r1, cx2 - r2);
y1 = MIN (cy1 - r1, cy2 - r2);
x2 = MAX (cx1 + r1, cx2 + r2);
y2 = MAX (cy1 + r1, cy2 + r2);
}
break;
 
2740,20 → 3552,26
if (pattern->extend != CAIRO_EXTEND_NONE)
goto UNBOUNDED;
 
if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
if (_linear_pattern_is_degenerate (linear)) {
/* cairo-gstate should have optimised degenerate
* patterns to solid ones, so we can again ignore
* them here. */
goto EMPTY;
}
 
/* TODO: to get tight extents, use the matrix to transform
* the pattern instead of transforming the extents later. */
if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
goto UNBOUNDED;
 
if (linear->p1.x == linear->p2.x) {
if (linear->pd1.x == linear->pd2.x) {
x1 = -HUGE_VAL;
x2 = HUGE_VAL;
y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y));
y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y));
} else if (linear->p1.y == linear->p2.y) {
x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x));
x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x));
y1 = MIN (linear->pd1.y, linear->pd2.y);
y2 = MAX (linear->pd1.y, linear->pd2.y);
} else if (linear->pd1.y == linear->pd2.y) {
x1 = MIN (linear->pd1.x, linear->pd2.x);
x2 = MAX (linear->pd1.x, linear->pd2.x);
y1 = -HUGE_VAL;
y2 = HUGE_VAL;
} else {
2762,6 → 3580,29
}
break;
 
case CAIRO_PATTERN_TYPE_MESH:
{
const cairo_mesh_pattern_t *mesh =
(const cairo_mesh_pattern_t *) pattern;
double padx, pady;
cairo_bool_t is_valid;
 
is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2);
if (!is_valid)
goto EMPTY;
 
padx = pady = 1.;
cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady);
padx = fabs (padx);
pady = fabs (pady);
 
x1 -= padx;
y1 -= pady;
x2 += padx;
y2 += pady;
}
break;
 
default:
ASSERT_NOT_REACHED;
}
2811,13 → 3652,51
return;
}
 
/**
* _cairo_pattern_get_ink_extents:
*
* Return the "target-space" inked extents of @pattern in @extents.
**/
cairo_int_status_t
_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
pattern->extend == CAIRO_EXTEND_NONE)
{
const cairo_surface_pattern_t *surface_pattern =
(const cairo_surface_pattern_t *) pattern;
cairo_surface_t *surface = surface_pattern->surface;
 
surface = _cairo_surface_get_source (surface, NULL);
if (_cairo_surface_is_recording (surface)) {
cairo_matrix_t imatrix;
cairo_box_t box;
cairo_status_t status;
 
imatrix = pattern->matrix;
status = cairo_matrix_invert (&imatrix);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface,
&box, &imatrix);
if (unlikely (status))
return status;
 
_cairo_box_round_to_rectangle (&box, extents);
return CAIRO_STATUS_SUCCESS;
}
}
 
_cairo_pattern_get_extents (pattern, extents);
return CAIRO_STATUS_SUCCESS;
}
 
static unsigned long
_cairo_solid_pattern_hash (unsigned long hash,
const cairo_pattern_t *pattern)
const cairo_solid_pattern_t *solid)
{
const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
 
hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
 
return hash;
2839,7 → 3718,7
sizeof (double));
hash = _cairo_hash_bytes (hash,
&gradient->stops[n].color,
sizeof (cairo_color_t));
sizeof (cairo_color_stop_t));
}
 
return hash;
2849,8 → 3728,8
_cairo_linear_pattern_hash (unsigned long hash,
const cairo_linear_pattern_t *linear)
{
hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1));
hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2));
hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1));
hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2));
 
return _cairo_gradient_color_stops_hash (hash, &linear->base);
}
2859,25 → 3738,44
_cairo_radial_pattern_hash (unsigned long hash,
const cairo_radial_pattern_t *radial)
{
hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1));
hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1));
hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2));
hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2));
hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center));
hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius));
hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center));
hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius));
 
return _cairo_gradient_color_stops_hash (hash, &radial->base);
}
 
static unsigned long
_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh)
{
const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
unsigned int i, n = _cairo_array_num_elements (&mesh->patches);
 
for (i = 0; i < n; i++)
hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t));
 
return hash;
}
 
static unsigned long
_cairo_surface_pattern_hash (unsigned long hash,
const cairo_pattern_t *pattern)
const cairo_surface_pattern_t *surface)
{
const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
 
hash ^= surface->surface->unique_id;
 
return hash;
}
 
static unsigned long
_cairo_raster_source_pattern_hash (unsigned long hash,
const cairo_raster_source_pattern_t *raster)
{
hash ^= (uintptr_t)raster->user_data;
 
return hash;
}
 
unsigned long
_cairo_pattern_hash (const cairo_pattern_t *pattern)
{
2901,13 → 3799,17
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_solid_pattern_hash (hash, pattern);
return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_LINEAR:
return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_MESH:
return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_SURFACE:
return _cairo_surface_pattern_hash (hash, pattern);
return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern);
default:
ASSERT_NOT_REACHED;
return FALSE;
2914,49 → 3816,10
}
}
 
static unsigned long
_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
 
return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t));
}
 
unsigned long
_cairo_pattern_size (const cairo_pattern_t *pattern)
{
if (pattern->status)
return 0;
 
/* XXX */
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return sizeof (cairo_solid_pattern_t);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
return sizeof (cairo_surface_pattern_t);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
return sizeof (cairo_linear_pattern_t) +
_cairo_gradient_pattern_color_stops_size (pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
return sizeof (cairo_radial_pattern_t) +
_cairo_gradient_pattern_color_stops_size (pattern);
default:
ASSERT_NOT_REACHED;
return 0;
}
}
 
 
static cairo_bool_t
_cairo_solid_pattern_equal (const cairo_pattern_t *A,
const cairo_pattern_t *B)
_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a,
const cairo_solid_pattern_t *b)
{
const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A;
const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B;
 
return _cairo_color_equal (&a->color, &b->color);
}
 
2983,16 → 3846,16
_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
const cairo_linear_pattern_t *b)
{
if (a->p1.x != b->p1.x)
if (a->pd1.x != b->pd1.x)
return FALSE;
 
if (a->p1.y != b->p1.y)
if (a->pd1.y != b->pd1.y)
return FALSE;
 
if (a->p2.x != b->p2.x)
if (a->pd2.x != b->pd2.x)
return FALSE;
 
if (a->p2.y != b->p2.y)
if (a->pd2.y != b->pd2.y)
return FALSE;
 
return _cairo_gradient_color_stops_equal (&a->base, &b->base);
3002,22 → 3865,22
_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
const cairo_radial_pattern_t *b)
{
if (a->c1.x != b->c1.x)
if (a->cd1.center.x != b->cd1.center.x)
return FALSE;
 
if (a->c1.y != b->c1.y)
if (a->cd1.center.y != b->cd1.center.y)
return FALSE;
 
if (a->r1 != b->r1)
if (a->cd1.radius != b->cd1.radius)
return FALSE;
 
if (a->c2.x != b->c2.x)
if (a->cd2.center.x != b->cd2.center.x)
return FALSE;
 
if (a->c2.y != b->c2.y)
if (a->cd2.center.y != b->cd2.center.y)
return FALSE;
 
if (a->r2 != b->r2)
if (a->cd2.radius != b->cd2.radius)
return FALSE;
 
return _cairo_gradient_color_stops_equal (&a->base, &b->base);
3024,15 → 3887,42
}
 
static cairo_bool_t
_cairo_surface_pattern_equal (const cairo_pattern_t *A,
const cairo_pattern_t *B)
_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a,
const cairo_mesh_pattern_t *b)
{
const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A;
const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B;
const cairo_mesh_patch_t *patch_a, *patch_b;
unsigned int i, num_patches_a, num_patches_b;
 
num_patches_a = _cairo_array_num_elements (&a->patches);
num_patches_b = _cairo_array_num_elements (&b->patches);
 
if (num_patches_a != num_patches_b)
return FALSE;
 
for (i = 0; i < num_patches_a; i++) {
patch_a = _cairo_array_index_const (&a->patches, i);
patch_b = _cairo_array_index_const (&a->patches, i);
if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0)
return FALSE;
}
 
return TRUE;
}
 
static cairo_bool_t
_cairo_surface_pattern_equal (const cairo_surface_pattern_t *a,
const cairo_surface_pattern_t *b)
{
return a->surface->unique_id == b->surface->unique_id;
}
 
static cairo_bool_t
_cairo_raster_source_pattern_equal (const cairo_raster_source_pattern_t *a,
const cairo_raster_source_pattern_t *b)
{
return a->user_data == b->user_data;
}
 
cairo_bool_t
_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
{
3061,7 → 3951,8
 
switch (a->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_solid_pattern_equal (a, b);
return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a,
(cairo_solid_pattern_t *) b);
case CAIRO_PATTERN_TYPE_LINEAR:
return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a,
(cairo_linear_pattern_t *) b);
3068,8 → 3959,15
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
(cairo_radial_pattern_t *) b);
case CAIRO_PATTERN_TYPE_MESH:
return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a,
(cairo_mesh_pattern_t *) b);
case CAIRO_PATTERN_TYPE_SURFACE:
return _cairo_surface_pattern_equal (a, b);
return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
(cairo_surface_pattern_t *) b);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_raster_source_pattern_equal ((cairo_raster_source_pattern_t *) a,
(cairo_raster_source_pattern_t *) b);
default:
ASSERT_NOT_REACHED;
return FALSE;
3077,7 → 3975,7
}
 
/**
* cairo_pattern_get_rgba
* cairo_pattern_get_rgba:
* @pattern: a #cairo_pattern_t
* @red: return value for red component of color, or %NULL
* @green: return value for green component of color, or %NULL
3121,7 → 4019,7
}
 
/**
* cairo_pattern_get_surface
* cairo_pattern_get_surface:
* @pattern: a #cairo_pattern_t
* @surface: return value for surface of pattern, or %NULL
*
3154,7 → 4052,7
}
 
/**
* cairo_pattern_get_color_stop_rgba
* cairo_pattern_get_color_stop_rgba:
* @pattern: a #cairo_pattern_t
* @index: index of the stop to return data for
* @offset: return value for the offset of the stop, or %NULL
3207,7 → 4105,7
}
 
/**
* cairo_pattern_get_color_stop_count
* cairo_pattern_get_color_stop_count:
* @pattern: a #cairo_pattern_t
* @count: return value for the number of color stops, or %NULL
*
3219,7 → 4117,7
* pattern.
*
* Since: 1.4
*/
**/
cairo_status_t
cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
int *count)
3240,7 → 4138,7
}
 
/**
* cairo_pattern_get_linear_points
* cairo_pattern_get_linear_points:
* @pattern: a #cairo_pattern_t
* @x0: return value for the x coordinate of the first point, or %NULL
* @y0: return value for the y coordinate of the first point, or %NULL
3269,19 → 4167,19
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (x0)
*x0 = _cairo_fixed_to_double (linear->p1.x);
*x0 = linear->pd1.x;
if (y0)
*y0 = _cairo_fixed_to_double (linear->p1.y);
*y0 = linear->pd1.y;
if (x1)
*x1 = _cairo_fixed_to_double (linear->p2.x);
*x1 = linear->pd2.x;
if (y1)
*y1 = _cairo_fixed_to_double (linear->p2.y);
*y1 = linear->pd2.y;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_pattern_get_radial_circles
* cairo_pattern_get_radial_circles:
* @pattern: a #cairo_pattern_t
* @x0: return value for the x coordinate of the center of the first circle, or %NULL
* @y0: return value for the y coordinate of the center of the first circle, or %NULL
3313,30 → 4211,381
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (x0)
*x0 = _cairo_fixed_to_double (radial->c1.x);
*x0 = radial->cd1.center.x;
if (y0)
*y0 = _cairo_fixed_to_double (radial->c1.y);
*y0 = radial->cd1.center.y;
if (r0)
*r0 = _cairo_fixed_to_double (radial->r1);
*r0 = radial->cd1.radius;
if (x1)
*x1 = _cairo_fixed_to_double (radial->c2.x);
*x1 = radial->cd2.center.x;
if (y1)
*y1 = _cairo_fixed_to_double (radial->c2.y);
*y1 = radial->cd2.center.y;
if (r1)
*r1 = _cairo_fixed_to_double (radial->r2);
*r1 = radial->cd2.radius;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_mesh_pattern_get_patch_count:
* @pattern: a #cairo_pattern_t
* @count: return value for the number patches, or %NULL
*
* Gets the number of patches specified in the given mesh pattern.
*
* The number only includes patches which have been finished by
* calling cairo_mesh_pattern_end_patch(). For example it will be 0
* during the definition of the first patch.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh
* pattern.
*
* Since: 1.12
**/
cairo_status_t
cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
unsigned int *count)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
 
if (unlikely (pattern->status))
return pattern->status;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (count) {
*count = _cairo_array_num_elements (&mesh->patches);
if (mesh->current_patch)
*count -= 1;
}
 
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def (cairo_mesh_pattern_get_patch_count);
 
/**
* cairo_mesh_pattern_get_path:
* @pattern: a #cairo_pattern_t
* @patch_num: the patch number to return data for
*
* Gets path defining the patch @patch_num for a mesh
* pattern.
*
* @patch_num can range 0 to 1 less than the number returned by
* cairo_mesh_pattern_get_patch_count().
*
* Return value: the path defining the patch, or a path with status
* %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not
* valid for @pattern. If @pattern is not a mesh pattern, a path with
* status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned.
*
* Since: 1.12
**/
cairo_path_t *
cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
unsigned int patch_num)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
const cairo_mesh_patch_t *patch;
cairo_path_t *path;
cairo_path_data_t *data;
unsigned int patch_count;
int l, current_point;
 
if (unlikely (pattern->status))
return _cairo_path_create_in_error (pattern->status);
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH));
 
patch_count = _cairo_array_num_elements (&mesh->patches);
if (mesh->current_patch)
patch_count--;
 
if (unlikely (patch_num >= patch_count))
return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
 
patch = _cairo_array_index_const (&mesh->patches, patch_num);
 
path = malloc (sizeof (cairo_path_t));
if (path == NULL)
return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
path->num_data = 18;
path->data = _cairo_malloc_ab (path->num_data,
sizeof (cairo_path_data_t));
if (path->data == NULL) {
free (path);
return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
data = path->data;
data[0].header.type = CAIRO_PATH_MOVE_TO;
data[0].header.length = 2;
data[1].point.x = patch->points[0][0].x;
data[1].point.y = patch->points[0][0].y;
data += data[0].header.length;
 
current_point = 0;
 
for (l = 0; l < 4; l++) {
int i, j, k;
 
data[0].header.type = CAIRO_PATH_CURVE_TO;
data[0].header.length = 4;
 
for (k = 1; k < 4; k++) {
current_point = (current_point + 1) % 12;
i = mesh_path_point_i[current_point];
j = mesh_path_point_j[current_point];
data[k].point.x = patch->points[i][j].x;
data[k].point.y = patch->points[i][j].y;
}
 
data += data[0].header.length;
}
 
path->status = CAIRO_STATUS_SUCCESS;
 
return path;
}
slim_hidden_def (cairo_mesh_pattern_get_path);
 
/**
* cairo_mesh_pattern_get_corner_color_rgba:
* @pattern: a #cairo_pattern_t
* @patch_num: the patch number to return data for
* @corner_num: the corner number to return data for
* @red: return value for red component of color, or %NULL
* @green: return value for green component of color, or %NULL
* @blue: return value for blue component of color, or %NULL
* @alpha: return value for alpha component of color, or %NULL
*
* Gets the color information in corner @corner_num of patch
* @patch_num for a mesh pattern.
*
* @patch_num can range 0 to 1 less than the number returned by
* cairo_mesh_pattern_get_patch_count().
*
* Valid values for @corner_num are from 0 to 3 and identify the
* corners as explained in cairo_pattern_create_mesh().
*
* Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
* if @patch_num or @corner_num is not valid for @pattern. If
* @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH
* is returned.
*
* Since: 1.12
**/
cairo_status_t
cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
unsigned int patch_num,
unsigned int corner_num,
double *red, double *green,
double *blue, double *alpha)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
unsigned int patch_count;
const cairo_mesh_patch_t *patch;
 
if (unlikely (pattern->status))
return pattern->status;
 
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH))
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (unlikely (corner_num > 3))
return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
 
patch_count = _cairo_array_num_elements (&mesh->patches);
if (mesh->current_patch)
patch_count--;
 
if (unlikely (patch_num >= patch_count))
return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
 
patch = _cairo_array_index_const (&mesh->patches, patch_num);
 
if (red)
*red = patch->colors[corner_num].red;
if (green)
*green = patch->colors[corner_num].green;
if (blue)
*blue = patch->colors[corner_num].blue;
if (alpha)
*alpha = patch->colors[corner_num].alpha;
 
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba);
 
/**
* cairo_mesh_pattern_get_control_point:
* @pattern: a #cairo_pattern_t
* @patch_num: the patch number to return data for
* @point_num: the control point number to return data for
* @x: return value for the x coordinate of the control point, or %NULL
* @y: return value for the y coordinate of the control point, or %NULL
*
* Gets the control point @point_num of patch @patch_num for a mesh
* pattern.
*
* @patch_num can range 0 to 1 less than the number returned by
* cairo_mesh_pattern_get_patch_count().
*
* Valid values for @point_num are from 0 to 3 and identify the
* control points as explained in cairo_pattern_create_mesh().
*
* Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
* if @patch_num or @point_num is not valid for @pattern. If @pattern
* is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
* returned.
*
* Since: 1.12
**/
cairo_status_t
cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
unsigned int patch_num,
unsigned int point_num,
double *x, double *y)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern;
const cairo_mesh_patch_t *patch;
unsigned int patch_count;
int i, j;
 
if (pattern->status)
return pattern->status;
 
if (pattern->type != CAIRO_PATTERN_TYPE_MESH)
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (point_num > 3)
return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
 
patch_count = _cairo_array_num_elements (&mesh->patches);
if (mesh->current_patch)
patch_count--;
 
if (unlikely (patch_num >= patch_count))
return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
 
patch = _cairo_array_index_const (&mesh->patches, patch_num);
 
i = mesh_control_point_i[point_num];
j = mesh_control_point_j[point_num];
 
if (x)
*x = patch->points[i][j].x;
if (y)
*y = patch->points[i][j].y;
 
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def (cairo_mesh_pattern_get_control_point);
 
void
_cairo_pattern_reset_static_data (void)
{
#if HAS_FREED_POOL
int i;
 
for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++)
_freed_pool_reset (&freed_pattern_pool[i]);
#endif
}
 
_cairo_pattern_reset_solid_surface_cache ();
static void
_cairo_debug_print_surface_pattern (FILE *file,
const cairo_surface_pattern_t *pattern)
{
printf (" surface type: %d\n", pattern->surface->type);
}
 
static void
_cairo_debug_print_raster_source_pattern (FILE *file,
const cairo_raster_source_pattern_t *raster)
{
printf (" content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height);
}
 
static void
_cairo_debug_print_linear_pattern (FILE *file,
const cairo_linear_pattern_t *pattern)
{
}
 
static void
_cairo_debug_print_radial_pattern (FILE *file,
const cairo_radial_pattern_t *pattern)
{
}
 
static void
_cairo_debug_print_mesh_pattern (FILE *file,
const cairo_mesh_pattern_t *pattern)
{
}
 
void
_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern)
{
const char *s;
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break;
case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break;
case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break;
case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break;
case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: s = "raster"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
 
fprintf (file, "pattern: %s\n", s);
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
return;
 
switch (pattern->extend) {
case CAIRO_EXTEND_NONE: s = "none"; break;
case CAIRO_EXTEND_REPEAT: s = "repeat"; break;
case CAIRO_EXTEND_REFLECT: s = "reflect"; break;
case CAIRO_EXTEND_PAD: s = "pad"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
fprintf (file, " extend: %s\n", s);
 
switch (pattern->filter) {
case CAIRO_FILTER_FAST: s = "fast"; break;
case CAIRO_FILTER_GOOD: s = "good"; break;
case CAIRO_FILTER_BEST: s = "best"; break;
case CAIRO_FILTER_NEAREST: s = "nearest"; break;
case CAIRO_FILTER_BILINEAR: s = "bilinear"; break;
case CAIRO_FILTER_GAUSSIAN: s = "guassian"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
fprintf (file, " filter: %s\n", s);
fprintf (file, " matrix: [%g %g %g %g %g %g]\n",
pattern->matrix.xx, pattern->matrix.yx,
pattern->matrix.xy, pattern->matrix.yy,
pattern->matrix.x0, pattern->matrix.y0);
switch (pattern->type) {
default:
case CAIRO_PATTERN_TYPE_SOLID:
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_debug_print_raster_source_pattern (file, (cairo_raster_source_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
_cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
_cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
_cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_MESH:
_cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern);
break;
}
}
/programs/develop/libraries/cairo/src/cairo-pdf-operators-private.h
43,6 → 43,7
#define CAIRO_PDF_OPERATORS_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-types-private.h"
 
/* The glyph buffer size is based on the expected maximum glyphs in a
52,7 → 53,8
*/
#define PDF_GLYPH_BUFFER_SIZE 200
 
typedef cairo_status_t (*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id,
typedef cairo_int_status_t
(*cairo_pdf_operators_use_font_subset_t) (unsigned int font_id,
unsigned int subset_id,
void *closure);
 
81,6 → 83,7
double cur_x; /* Current position in PDF text space (Tm in the PDF reference) */
double cur_y;
int hex_width;
cairo_bool_t is_latin;
int num_glyphs;
double glyph_buf_x_pos;
cairo_pdf_glyph_t glyphs[PDF_GLYPH_BUFFER_SIZE];
129,7 → 132,7
 
cairo_private cairo_int_status_t
_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule);
 
cairo_private cairo_int_status_t
139,7 → 142,7
 
cairo_private cairo_int_status_t
_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse);
146,12 → 149,12
 
cairo_private cairo_int_status_t
_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule);
 
cairo_private cairo_int_status_t
_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
/programs/develop/libraries/cairo/src/cairo-pdf-operators.c
163,56 → 163,134
* exceed max_column. In particular, if a single word is larger than
* max_column it will not be broken up.
*/
 
typedef enum _cairo_word_wrap_state {
WRAP_STATE_DELIMITER,
WRAP_STATE_WORD,
WRAP_STATE_STRING,
WRAP_STATE_HEXSTRING
} cairo_word_wrap_state_t;
 
 
typedef struct _word_wrap_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output;
int max_column;
int column;
cairo_bool_t last_write_was_space;
cairo_bool_t in_hexstring;
cairo_bool_t empty_hexstring;
cairo_word_wrap_state_t state;
cairo_bool_t in_escape;
int escape_digits;
} word_wrap_stream_t;
 
 
 
/* Emit word bytes up to the next delimiter character */
static int
_count_word_up_to (const unsigned char *s, int length)
_word_wrap_stream_count_word_up_to (word_wrap_stream_t *stream,
const unsigned char *data, int length)
{
int word = 0;
const unsigned char *s = data;
int count = 0;
 
while (length--) {
if (! (_cairo_isspace (*s) || *s == '<')) {
if (_cairo_isspace (*s) || *s == '<' || *s == '(') {
stream->state = WRAP_STATE_DELIMITER;
break;
}
 
count++;
stream->column++;
s++;
word++;
} else {
return word;
}
}
 
return word;
if (count)
_cairo_output_stream_write (stream->output, data, count);
 
return count;
}
 
 
/* Count up to either the end of the ASCII hexstring or the number
/* Emit hexstring bytes up to either the end of the ASCII hexstring or the number
* of columns remaining.
*/
static int
_count_hexstring_up_to (const unsigned char *s, int length, int columns)
_word_wrap_stream_count_hexstring_up_to (word_wrap_stream_t *stream,
const unsigned char *data, int length)
{
int word = 0;
const unsigned char *s = data;
int count = 0;
cairo_bool_t newline = FALSE;
 
while (length--) {
if (*s++ != '>')
word++;
else
return word;
count++;
stream->column++;
if (*s == '>') {
stream->state = WRAP_STATE_DELIMITER;
break;
}
 
columns--;
if (columns < 0 && word > 1)
return word;
if (stream->column > stream->max_column) {
newline = TRUE;
break;
}
s++;
}
 
return word;
if (count)
_cairo_output_stream_write (stream->output, data, count);
 
if (newline) {
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
 
return count;
}
 
/* Count up to either the end of the string or the number of columns
* remaining.
*/
static int
_word_wrap_stream_count_string_up_to (word_wrap_stream_t *stream,
const unsigned char *data, int length)
{
const unsigned char *s = data;
int count = 0;
cairo_bool_t newline = FALSE;
 
while (length--) {
count++;
stream->column++;
if (!stream->in_escape) {
if (*s == ')') {
stream->state = WRAP_STATE_DELIMITER;
break;
}
if (*s == '\\') {
stream->in_escape = TRUE;
stream->escape_digits = 0;
} else if (stream->column > stream->max_column) {
newline = TRUE;
break;
}
} else {
if (!_cairo_isdigit(*s) || ++stream->escape_digits == 3)
stream->in_escape = FALSE;
}
s++;
}
 
if (count)
_cairo_output_stream_write (stream->output, data, count);
 
if (newline) {
_cairo_output_stream_printf (stream->output, "\\\n");
stream->column = 0;
}
 
return count;
}
 
static cairo_status_t
_word_wrap_stream_write (cairo_output_stream_t *base,
const unsigned char *data,
219,66 → 297,44
unsigned int length)
{
word_wrap_stream_t *stream = (word_wrap_stream_t *) base;
cairo_bool_t newline;
int word;
int count;
 
while (length) {
if (*data == '<') {
stream->in_hexstring = TRUE;
stream->empty_hexstring = TRUE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, "<");
switch (stream->state) {
case WRAP_STATE_WORD:
count = _word_wrap_stream_count_word_up_to (stream, data, length);
break;
case WRAP_STATE_HEXSTRING:
count = _word_wrap_stream_count_hexstring_up_to (stream, data, length);
break;
case WRAP_STATE_STRING:
count = _word_wrap_stream_count_string_up_to (stream, data, length);
break;
case WRAP_STATE_DELIMITER:
count = 1;
stream->column++;
} else if (*data == '>') {
stream->in_hexstring = FALSE;
stream->last_write_was_space = FALSE;
data++;
length--;
_cairo_output_stream_printf (stream->output, ">");
stream->column++;
} else if (_cairo_isspace (*data)) {
newline = (*data == '\n' || *data == '\r');
if (! newline && stream->column >= stream->max_column) {
if (*data == '\n' || stream->column >= stream->max_column) {
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
} else if (*data == '<') {
stream->state = WRAP_STATE_HEXSTRING;
} else if (*data == '(') {
stream->state = WRAP_STATE_STRING;
} else if (!_cairo_isspace (*data)) {
stream->state = WRAP_STATE_WORD;
}
if (*data != '\n')
_cairo_output_stream_write (stream->output, data, 1);
data++;
length--;
if (newline) {
stream->column = 0;
break;
 
default:
ASSERT_NOT_REACHED;
count = length;
break;
}
else
stream->column++;
stream->last_write_was_space = TRUE;
} else {
if (stream->in_hexstring) {
word = _count_hexstring_up_to (data, length,
MAX (stream->max_column - stream->column, 0));
} else {
word = _count_word_up_to (data, length);
data += count;
length -= count;
}
/* Don't wrap if this word is a continuation of a non hex
* string word from a previous call to write. */
if (stream->column + word >= stream->max_column) {
if (stream->last_write_was_space ||
(stream->in_hexstring && !stream->empty_hexstring))
{
_cairo_output_stream_printf (stream->output, "\n");
stream->column = 0;
}
}
_cairo_output_stream_write (stream->output, data, word);
data += word;
length -= word;
stream->column += word;
stream->last_write_was_space = FALSE;
if (stream->in_hexstring)
stream->empty_hexstring = FALSE;
}
}
 
return _cairo_output_stream_get_status (stream->output);
}
312,9 → 368,9
stream->output = output;
stream->max_column = max_column;
stream->column = 0;
stream->last_write_was_space = FALSE;
stream->in_hexstring = FALSE;
stream->empty_hexstring = TRUE;
stream->state = WRAP_STATE_DELIMITER;
stream->in_escape = FALSE;
stream->escape_digits = 0;
 
return &stream->base;
}
437,7 → 493,7
*/
static cairo_status_t
_cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
cairo_matrix_t *path_transform,
cairo_line_cap_t line_cap)
{
458,7 → 514,6
status = _cairo_pdf_path_rectangle (&info, &box);
} else {
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_pdf_path_move_to,
_cairo_pdf_path_line_to,
_cairo_pdf_path_curve_to,
475,7 → 530,7
 
cairo_int_status_t
_cairo_pdf_operators_clip (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule)
{
const char *pdf_operator;
708,13 → 763,13
 
static cairo_int_status_t
_cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
const char *pdf_operator)
{
cairo_status_t status;
cairo_int_status_t status;
cairo_matrix_t m, path_transform;
cairo_bool_t has_ctm = TRUE;
double scale = 1.0;
799,7 → 854,7
 
cairo_int_status_t
_cairo_pdf_operators_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse)
814,7 → 869,7
 
cairo_int_status_t
_cairo_pdf_operators_fill (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule)
{
const char *pdf_operator;
853,7 → 908,7
 
cairo_int_status_t
_cairo_pdf_operators_fill_stroke (cairo_pdf_operators_t *pdf_operators,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
880,6 → 935,26
operator);
}
 
static void
_cairo_pdf_operators_emit_glyph_index (cairo_pdf_operators_t *pdf_operators,
cairo_output_stream_t *stream,
unsigned int glyph)
{
if (pdf_operators->is_latin) {
if (glyph == '(' || glyph == ')' || glyph == '\\')
_cairo_output_stream_printf (stream, "\\%c", glyph);
else if (glyph >= 0x20 && glyph <= 0x7e)
_cairo_output_stream_printf (stream, "%c", glyph);
else
_cairo_output_stream_printf (stream, "\\%03o", glyph);
} else {
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
glyph);
}
}
 
#define GLYPH_POSITION_TOLERANCE 0.001
 
/* Emit the string of glyphs using the 'Tj' operator. This requires
890,15 → 965,14
{
int i;
 
_cairo_output_stream_printf (stream, "<");
_cairo_output_stream_printf (stream, "%s", pdf_operators->is_latin ? "(" : "<");
for (i = 0; i < pdf_operators->num_glyphs; i++) {
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
_cairo_pdf_operators_emit_glyph_index (pdf_operators,
stream,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">Tj\n");
_cairo_output_stream_printf (stream, "%sTj\n", pdf_operators->is_latin ? ")" : ">");
 
return _cairo_output_stream_get_status (stream);
}
918,7 → 992,7
{
int i;
 
_cairo_output_stream_printf (stream, "[<");
_cairo_output_stream_printf (stream, "[%s", pdf_operators->is_latin ? "(" : "<");
for (i = 0; i < pdf_operators->num_glyphs; i++) {
if (pdf_operators->glyphs[i].x_position != pdf_operators->cur_x)
{
934,11 → 1008,19
* calculating subsequent deltas.
*/
rounded_delta = _cairo_lround (delta);
if (abs(rounded_delta) < 3)
rounded_delta = 0;
if (rounded_delta != 0) {
if (pdf_operators->is_latin) {
_cairo_output_stream_printf (stream,
")%d(",
rounded_delta);
} else {
_cairo_output_stream_printf (stream,
">%d<",
rounded_delta);
}
}
 
/* Convert the rounded delta back to text
* space before adding to the current text
947,13 → 1029,12
pdf_operators->cur_x += delta;
}
 
_cairo_output_stream_printf (stream,
"%0*x",
pdf_operators->hex_width,
_cairo_pdf_operators_emit_glyph_index (pdf_operators,
stream,
pdf_operators->glyphs[i].glyph_index);
pdf_operators->cur_x += pdf_operators->glyphs[i].x_advance;
}
_cairo_output_stream_printf (stream, ">]TJ\n");
_cairo_output_stream_printf (stream, "%s]TJ\n", pdf_operators->is_latin ? ")" : ">");
 
return _cairo_output_stream_get_status (stream);
}
1128,6 → 1209,7
}
pdf_operators->font_id = subset_glyph->font_id;
pdf_operators->subset_id = subset_glyph->subset_id;
pdf_operators->is_latin = subset_glyph->is_latin;
 
if (subset_glyph->is_composite)
pdf_operators->hex_width = 4;
/programs/develop/libraries/cairo/src/cairo-pdf-shading-private.h
0,0 → 1,100
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Adrian Johnson
*
* 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 Adrian Johnson.
*
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#ifndef CAIRO_PDF_SHADING_H
#define CAIRO_PDF_SHADING_H
 
#include "cairo-compiler-private.h"
#include "cairo-types-private.h"
#include "cairo-pattern-private.h"
 
 
typedef struct _cairo_pdf_shading {
int shading_type;
int bits_per_coordinate;
int bits_per_component;
int bits_per_flag;
double *decode_array;
int decode_array_length;
unsigned char *data;
unsigned long data_length;
} cairo_pdf_shading_t;
 
 
/**
* _cairo_pdf_shading_init_color:
* @shading: a #cairo_pdf_shading_t to initialize
* @pattern: the #cairo_mesh_pattern_t to initialize from
*
* Generate the PDF shading dictionary data for the a PDF type 7
* shading from RGB part of the specified mesh pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
* include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern);
 
 
/**
* _cairo_pdf_shading_init_alpha:
* @shading: a #cairo_pdf_shading_t to initialize
* @pattern: the #cairo_mesh_pattern_t to initialize from
*
* Generate the PDF shading dictionary data for a PDF type 7
* shading from alpha part of the specified mesh pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful, possible errors
* include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern);
 
/**
* _cairo_pdf_shading_fini:
* @shading: a #cairo_pdf_shading_t
*
* Free all resources associated with @shading. After this call,
* @shading should not be used again without a subsequent call to
* _cairo_pdf_shading_init() again first.
**/
cairo_private void
_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading);
 
 
#endif /* CAIRO_PDF_SHADING_H */
/programs/develop/libraries/cairo/src/cairo-pdf-shading.c
0,0 → 1,279
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Adrian Johnson
*
* 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 Adrian Johnson.
*
* Contributor(s):
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_PDF_OPERATORS
 
#include "cairo-pdf-shading-private.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
#include <float.h>
 
static unsigned char *
encode_coordinate (unsigned char *p, double c)
{
uint32_t f;
 
f = c;
*p++ = f >> 24;
*p++ = (f >> 16) & 0xff;
*p++ = (f >> 8) & 0xff;
*p++ = f & 0xff;
 
return p;
}
 
static unsigned char *
encode_point (unsigned char *p, const cairo_point_double_t *point)
{
p = encode_coordinate (p, point->x);
p = encode_coordinate (p, point->y);
 
return p;
}
 
static unsigned char *
encode_color_component (unsigned char *p, double color)
{
uint16_t c;
 
c = _cairo_color_double_to_short (color);
*p++ = c >> 8;
*p++ = c & 0xff;
 
return p;
}
 
static unsigned char *
encode_color (unsigned char *p, const cairo_color_t *color)
{
p = encode_color_component (p, color->red);
p = encode_color_component (p, color->green);
p = encode_color_component (p, color->blue);
 
return p;
}
 
static unsigned char *
encode_alpha (unsigned char *p, const cairo_color_t *color)
{
p = encode_color_component (p, color->alpha);
 
return p;
}
 
static cairo_status_t
_cairo_pdf_shading_generate_decode_array (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
unsigned int num_color_components, i;
cairo_bool_t is_valid;
 
if (is_alpha)
num_color_components = 1;
else
num_color_components = 3;
 
shading->decode_array_length = 4 + num_color_components * 2;
shading->decode_array = _cairo_malloc_ab (shading->decode_array_length,
sizeof (double));
if (unlikely (shading->decode_array == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
is_valid = _cairo_mesh_pattern_coord_box (mesh,
&shading->decode_array[0],
&shading->decode_array[2],
&shading->decode_array[1],
&shading->decode_array[3]);
 
assert (is_valid);
assert (shading->decode_array[1] - shading->decode_array[0] >= DBL_EPSILON);
assert (shading->decode_array[3] - shading->decode_array[2] >= DBL_EPSILON);
 
for (i = 0; i < num_color_components; i++) {
shading->decode_array[4 + 2*i] = 0;
shading->decode_array[5 + 2*i] = 1;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/* The ISO32000 specification mandates this order for the points which
* define the patch. */
static const int pdf_points_order_i[16] = {
0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2 };
static const int pdf_points_order_j[16] = {
0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1 };
 
static cairo_status_t
_cairo_pdf_shading_generate_data (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
const cairo_mesh_patch_t *patch;
double x_off, y_off, x_scale, y_scale;
unsigned int num_patches;
unsigned int num_color_components;
unsigned char *p;
unsigned int i, j;
 
if (is_alpha)
num_color_components = 1;
else
num_color_components = 3;
 
num_patches = _cairo_array_num_elements (&mesh->patches);
patch = _cairo_array_index_const (&mesh->patches, 0);
 
/* Each patch requires:
*
* 1 flag - 1 byte
* 16 points. Each point is 2 coordinates. Each coordinate is
* stored in 4 bytes.
*
* 4 colors. Each color is stored in 2 bytes * num_color_components.
*/
shading->data_length = num_patches * (1 + 16 * 2 * 4 + 4 * 2 * num_color_components);
shading->data = malloc (shading->data_length);
if (unlikely (shading->data == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
x_off = shading->decode_array[0];
y_off = shading->decode_array[2];
x_scale = UINT32_MAX / (shading->decode_array[1] - x_off);
y_scale = UINT32_MAX / (shading->decode_array[3] - y_off);
 
p = shading->data;
for (i = 0; i < num_patches; i++) {
/* edge flag */
*p++ = 0;
 
/* 16 points */
for (j = 0; j < 16; j++) {
cairo_point_double_t point;
int pi, pj;
 
pi = pdf_points_order_i[j];
pj = pdf_points_order_j[j];
point = patch[i].points[pi][pj];
 
/* Transform the point as specified in the decode array */
point.x -= x_off;
point.y -= y_off;
point.x *= x_scale;
point.y *= y_scale;
 
/* Make sure that rounding errors don't cause
* wraparounds */
point.x = _cairo_restrict_value (point.x, 0, UINT32_MAX);
point.y = _cairo_restrict_value (point.y, 0, UINT32_MAX);
 
p = encode_point (p, &point);
}
 
/* 4 colors */
for (j = 0; j < 4; j++) {
if (is_alpha)
p = encode_alpha (p, &patch[i].colors[j]);
else
p = encode_color (p, &patch[i].colors[j]);
}
}
 
assert (p == shading->data + shading->data_length);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_pdf_shading_init (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *mesh,
cairo_bool_t is_alpha)
{
cairo_status_t status;
 
assert (mesh->base.status == CAIRO_STATUS_SUCCESS);
assert (mesh->current_patch == NULL);
 
shading->shading_type = 7;
 
/*
* Coordinates from the minimum to the maximum value of the mesh
* map to the [0..UINT32_MAX] range and are represented as
* uint32_t values.
*
* Color components are represented as uint16_t (in a 0.16 fixed
* point format, as in the rest of cairo).
*/
shading->bits_per_coordinate = 32;
shading->bits_per_component = 16;
shading->bits_per_flag = 8;
 
shading->decode_array = NULL;
shading->data = NULL;
 
status = _cairo_pdf_shading_generate_decode_array (shading, mesh, is_alpha);
if (unlikely (status))
return status;
 
return _cairo_pdf_shading_generate_data (shading, mesh, is_alpha);
}
 
cairo_status_t
_cairo_pdf_shading_init_color (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern)
{
return _cairo_pdf_shading_init (shading, pattern, FALSE);
}
 
cairo_status_t
_cairo_pdf_shading_init_alpha (cairo_pdf_shading_t *shading,
const cairo_mesh_pattern_t *pattern)
{
return _cairo_pdf_shading_init (shading, pattern, TRUE);
}
 
void
_cairo_pdf_shading_fini (cairo_pdf_shading_t *shading)
{
free (shading->data);
free (shading->decode_array);
}
 
#endif /* CAIRO_HAS_PDF_OPERATORS */
/programs/develop/libraries/cairo/src/cairo-pdf-surface-private.h
60,6 → 60,7
cairo_array_t alphas;
cairo_array_t smasks;
cairo_array_t patterns;
cairo_array_t shadings;
cairo_array_t xobjects;
cairo_array_t fonts;
} cairo_pdf_group_resources_t;
67,14 → 68,20
typedef struct _cairo_pdf_source_surface_entry {
cairo_hash_entry_t base;
unsigned int id;
unsigned char *unique_id;
unsigned long unique_id_length;
cairo_bool_t interpolate;
cairo_bool_t stencil_mask;
cairo_pdf_resource_t surface_res;
int width;
int height;
cairo_rectangle_int_t extents;
} cairo_pdf_source_surface_entry_t;
 
typedef struct _cairo_pdf_source_surface {
cairo_pattern_type_t type;
cairo_surface_t *surface;
cairo_pattern_t *raster_pattern;
cairo_pdf_source_surface_entry_t *hash_entry;
} cairo_pdf_source_surface_t;
 
85,6 → 92,7
cairo_pattern_t *pattern;
cairo_pdf_resource_t pattern_res;
cairo_pdf_resource_t gstate_res;
cairo_bool_t is_shading;
} cairo_pdf_pattern_t;
 
typedef enum _cairo_pdf_operation {
98,6 → 106,7
typedef struct _cairo_pdf_smask_group {
double width;
double height;
cairo_rectangle_int_t extents;
cairo_pdf_resource_t group_res;
cairo_pdf_operation_t operation;
cairo_pattern_t *source;
171,6 → 180,7
cairo_output_stream_t *mem_stream;
cairo_output_stream_t *old_output;
cairo_pdf_resource_t resource;
cairo_box_double_t bbox;
cairo_bool_t is_knockout;
} group_stream;
 
/programs/develop/libraries/cairo/src/cairo-pdf-surface.c
0,0 → 1,7350
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 Red Hat, Inc
* Copyright © 2006 Red Hat, Inc
* Copyright © 2007, 2008 Adrian Johnson
*
* 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):
* Kristian Høgsberg <krh@redhat.com>
* Carl Worth <cworth@cworth.org>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#define _BSD_SOURCE /* for snprintf() */
#include "cairoint.h"
 
#include "cairo-pdf.h"
#include "cairo-pdf-surface-private.h"
#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"
 
#include "cairo-array-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-image-info-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-paginated-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-type3-glyph-surface-private.h"
 
#include <time.h>
#include <zlib.h>
 
/* Issues:
*
* - We embed an image in the stream each time it's composited. We
* could add generation counters to surfaces and remember the stream
* ID for a particular generation for a particular surface.
*
* - Backend specific meta data.
*/
 
/*
* Page Structure of the Generated PDF:
*
* Each page requiring fallbacks images contains a knockout group at
* the top level. The first operation of the knockout group paints a
* group containing all the supported drawing operations. Fallback
* images (if any) are painted in the knockout group. This ensures
* that fallback images do not composite with any content under the
* fallback images.
*
* Streams:
*
* This PDF surface has three types of streams:
* - PDF Stream
* - Content Stream
* - Group Stream
*
* Calling _cairo_output_stream_printf (surface->output, ...) will
* write to the currently open stream.
*
* PDF Stream:
* A PDF Stream may be opened and closed with the following functions:
* _cairo_pdf_surface_open stream ()
* _cairo_pdf_surface_close_stream ()
*
* PDF Streams are written directly to the PDF file. They are used for
* fonts, images and patterns.
*
* Content Stream:
* The Content Stream is opened and closed with the following functions:
* _cairo_pdf_surface_open_content_stream ()
* _cairo_pdf_surface_close_content_stream ()
*
* The Content Stream contains the text and graphics operators.
*
* Group Stream:
* A Group Stream may be opened and closed with the following functions:
* _cairo_pdf_surface_open_group ()
* _cairo_pdf_surface_close_group ()
*
* A Group Stream is a Form XObject. It is used for short sequences
* of operators. As the content is very short the group is stored in
* memory until it is closed. This allows some optimization such as
* including the Resource dictionary and stream length inside the
* XObject instead of using an indirect object.
*/
 
/**
* SECTION:cairo-pdf
* @Title: PDF Surfaces
* @Short_Description: Rendering PDF documents
* @See_Also: #cairo_surface_t
*
* The PDF surface is used to render cairo graphics to Adobe
* PDF files and is a multi-page vector surface backend.
**/
 
static cairo_bool_t
_cairo_pdf_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle);
 
/**
* CAIRO_HAS_PDF_SURFACE:
*
* Defined if the PDF surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.2
**/
 
static const cairo_pdf_version_t _cairo_pdf_versions[] =
{
CAIRO_PDF_VERSION_1_4,
CAIRO_PDF_VERSION_1_5
};
 
#define CAIRO_PDF_VERSION_LAST ARRAY_LENGTH (_cairo_pdf_versions)
 
static const char * _cairo_pdf_version_strings[CAIRO_PDF_VERSION_LAST] =
{
"PDF 1.4",
"PDF 1.5"
};
 
static const char *_cairo_pdf_supported_mime_types[] =
{
CAIRO_MIME_TYPE_JPEG,
CAIRO_MIME_TYPE_JP2,
CAIRO_MIME_TYPE_UNIQUE_ID,
NULL
};
 
typedef struct _cairo_pdf_object {
long offset;
} cairo_pdf_object_t;
 
typedef struct _cairo_pdf_font {
unsigned int font_id;
unsigned int subset_id;
cairo_pdf_resource_t subset_resource;
} cairo_pdf_font_t;
 
typedef struct _cairo_pdf_rgb_linear_function {
cairo_pdf_resource_t resource;
double color1[3];
double color2[3];
} cairo_pdf_rgb_linear_function_t;
 
typedef struct _cairo_pdf_alpha_linear_function {
cairo_pdf_resource_t resource;
double alpha1;
double alpha2;
} cairo_pdf_alpha_linear_function_t;
 
static cairo_pdf_resource_t
_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface);
 
static void
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface);
 
static void
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group);
 
static cairo_int_status_t
_cairo_pdf_surface_add_font (unsigned int font_id,
unsigned int subset_id,
void *closure);
 
static void
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res);
 
static cairo_int_status_t
_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t *resource,
cairo_bool_t compressed,
const char *fmt,
...) CAIRO_PRINTF_FORMAT(4, 5);
static cairo_int_status_t
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface);
 
static cairo_int_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
 
static void
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface);
 
static cairo_pdf_resource_t
_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface);
 
static cairo_pdf_resource_t
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface);
 
static long
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface);
 
static cairo_int_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface);
 
static cairo_int_status_t
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface);
 
static cairo_bool_t
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b);
 
static const cairo_surface_backend_t cairo_pdf_surface_backend;
static const cairo_paginated_surface_backend_t cairo_pdf_surface_paginated_backend;
 
static cairo_pdf_resource_t
_cairo_pdf_surface_new_object (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t resource;
cairo_int_status_t status;
cairo_pdf_object_t object;
 
object.offset = _cairo_output_stream_get_position (surface->output);
 
status = _cairo_array_append (&surface->objects, &object);
if (unlikely (status)) {
resource.id = 0;
return resource;
}
 
resource = surface->next_available_resource;
surface->next_available_resource.id++;
 
return resource;
}
 
static void
_cairo_pdf_surface_update_object (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t resource)
{
cairo_pdf_object_t *object;
 
object = _cairo_array_index (&surface->objects, resource.id - 1);
object->offset = _cairo_output_stream_get_position (surface->output);
}
 
static void
_cairo_pdf_surface_set_size_internal (cairo_pdf_surface_t *surface,
double width,
double height)
{
surface->width = width;
surface->height = height;
cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
&surface->cairo_to_pdf);
}
 
static cairo_bool_t
_path_covers_bbox (cairo_pdf_surface_t *surface,
cairo_path_fixed_t *path)
{
cairo_box_t box;
 
return _cairo_path_fixed_is_box (path, &box) &&
box.p1.x <= 0 &&
box.p1.y <= 0 &&
box.p2.x >= _cairo_fixed_from_double (surface->width) &&
box.p2.y >= _cairo_fixed_from_double (surface->height);
}
 
static cairo_status_t
_cairo_pdf_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_pdf_surface_t *surface = cairo_container_of (clipper,
cairo_pdf_surface_t,
clipper);
cairo_int_status_t status;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
if (path == NULL) {
_cairo_output_stream_printf (surface->output, "Q q\n");
 
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
 
return CAIRO_STATUS_SUCCESS;
}
 
if (_path_covers_bbox (surface, path))
return CAIRO_STATUS_SUCCESS;
 
return _cairo_pdf_operators_clip (&surface->pdf_operators, path, fill_rule);
}
 
static cairo_surface_t *
_cairo_pdf_surface_create_for_stream_internal (cairo_output_stream_t *output,
double width,
double height)
{
cairo_pdf_surface_t *surface;
cairo_status_t status, status_ignored;
 
surface = malloc (sizeof (cairo_pdf_surface_t));
if (unlikely (surface == NULL)) {
/* destroy stream on behalf of caller */
status = _cairo_output_stream_destroy (output);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
_cairo_surface_init (&surface->base,
&cairo_pdf_surface_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
surface->output = output;
surface->width = width;
surface->height = height;
cairo_matrix_init (&surface->cairo_to_pdf, 1, 0, 0, -1, 0, height);
 
_cairo_array_init (&surface->objects, sizeof (cairo_pdf_object_t));
_cairo_array_init (&surface->pages, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&surface->rgb_linear_functions, sizeof (cairo_pdf_rgb_linear_function_t));
_cairo_array_init (&surface->alpha_linear_functions, sizeof (cairo_pdf_alpha_linear_function_t));
_cairo_array_init (&surface->fonts, sizeof (cairo_pdf_font_t));
_cairo_array_init (&surface->smask_groups, sizeof (cairo_pdf_smask_group_t *));
_cairo_array_init (&surface->knockout_group, sizeof (cairo_pdf_resource_t));
 
_cairo_array_init (&surface->page_patterns, sizeof (cairo_pdf_pattern_t));
_cairo_array_init (&surface->page_surfaces, sizeof (cairo_pdf_source_surface_t));
surface->all_surfaces = _cairo_hash_table_create (_cairo_pdf_source_surface_equal);
if (unlikely (surface->all_surfaces == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL0;
}
 
_cairo_pdf_group_resources_init (&surface->resources);
 
surface->font_subsets = _cairo_scaled_font_subsets_create_composite ();
if (! surface->font_subsets) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL1;
}
 
_cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
 
surface->next_available_resource.id = 1;
surface->pages_resource = _cairo_pdf_surface_new_object (surface);
if (surface->pages_resource.id == 0) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL2;
}
 
surface->pdf_version = CAIRO_PDF_VERSION_1_5;
surface->compress_content = TRUE;
surface->pdf_stream.active = FALSE;
surface->pdf_stream.old_output = NULL;
surface->group_stream.active = FALSE;
surface->group_stream.stream = NULL;
surface->group_stream.mem_stream = NULL;
 
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
 
surface->force_fallbacks = FALSE;
surface->select_pattern_gstate_saved = FALSE;
surface->current_pattern_is_solid_color = FALSE;
surface->current_operator = CAIRO_OPERATOR_OVER;
surface->header_emitted = FALSE;
 
_cairo_surface_clipper_init (&surface->clipper,
_cairo_pdf_surface_clipper_intersect_clip_path);
 
_cairo_pdf_operators_init (&surface->pdf_operators,
surface->output,
&surface->cairo_to_pdf,
surface->font_subsets);
_cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators,
_cairo_pdf_surface_add_font,
surface);
_cairo_pdf_operators_enable_actual_text(&surface->pdf_operators, TRUE);
 
surface->paginated_surface = _cairo_paginated_surface_create (
&surface->base,
CAIRO_CONTENT_COLOR_ALPHA,
&cairo_pdf_surface_paginated_backend);
 
status = surface->paginated_surface->status;
if (status == CAIRO_STATUS_SUCCESS) {
/* paginated keeps the only reference to surface now, drop ours */
cairo_surface_destroy (&surface->base);
return surface->paginated_surface;
}
 
BAIL2:
_cairo_scaled_font_subsets_destroy (surface->font_subsets);
BAIL1:
_cairo_hash_table_destroy (surface->all_surfaces);
BAIL0:
_cairo_array_fini (&surface->objects);
free (surface);
 
/* destroy stream on behalf of caller */
status_ignored = _cairo_output_stream_destroy (output);
 
return _cairo_surface_create_in_error (status);
}
 
/**
* cairo_pdf_surface_create_for_stream:
* @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
* to indicate a no-op @write_func. With a no-op @write_func,
* the surface may be queried or used as a source without
* generating any temporary files.
* @closure: the closure argument for @write_func
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PDF surface of the specified size in points to be written
* incrementally to the stream represented by @write_func and @closure.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_pdf_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width_in_points,
double height_in_points)
{
cairo_output_stream_t *output;
 
output = _cairo_output_stream_create (write_func, NULL, closure);
if (_cairo_output_stream_get_status (output))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
 
return _cairo_pdf_surface_create_for_stream_internal (output,
width_in_points,
height_in_points);
}
 
/**
* cairo_pdf_surface_create:
* @filename: a filename for the PDF output (must be writable), %NULL may be
* used to specify no output. This will generate a PDF surface that
* may be queried and used as a source, without generating a
* temporary file.
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PDF surface of the specified size in points to be written
* to @filename.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_pdf_surface_create (const char *filename,
double width_in_points,
double height_in_points)
{
cairo_output_stream_t *output;
 
output = _cairo_output_stream_create_for_filename (filename);
if (_cairo_output_stream_get_status (output))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (output));
 
return _cairo_pdf_surface_create_for_stream_internal (output,
width_in_points,
height_in_points);
}
 
static cairo_bool_t
_cairo_surface_is_pdf (cairo_surface_t *surface)
{
return surface->backend == &cairo_pdf_surface_backend;
}
 
/* If the abstract_surface is a paginated surface, and that paginated
* surface's target is a pdf_surface, then set pdf_surface to that
* target. Otherwise return FALSE.
*/
static cairo_bool_t
_extract_pdf_surface (cairo_surface_t *surface,
cairo_pdf_surface_t **pdf_surface)
{
cairo_surface_t *target;
cairo_status_t status_ignored;
 
if (surface->status)
return FALSE;
if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_paginated (surface)) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
target = _cairo_paginated_surface_get_target (surface);
if (target->status) {
status_ignored = _cairo_surface_set_error (surface,
target->status);
return FALSE;
}
if (target->finished) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_pdf (target)) {
status_ignored = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
*pdf_surface = (cairo_pdf_surface_t *) target;
return TRUE;
}
 
/**
* cairo_pdf_surface_restrict_to_version:
* @surface: a PDF #cairo_surface_t
* @version: PDF version
*
* Restricts the generated PDF file to @version. See cairo_pdf_get_versions()
* for a list of available version values that can be used here.
*
* This function should only be called before any drawing operations
* have been performed on the given surface. The simplest way to do
* this is to call this function immediately after creating the
* surface.
*
* Since: 1.10
**/
void
cairo_pdf_surface_restrict_to_version (cairo_surface_t *abstract_surface,
cairo_pdf_version_t version)
{
cairo_pdf_surface_t *surface = NULL; /* hide compiler warning */
 
if (! _extract_pdf_surface (abstract_surface, &surface))
return;
 
if (version < CAIRO_PDF_VERSION_LAST)
surface->pdf_version = version;
 
_cairo_pdf_operators_enable_actual_text(&surface->pdf_operators,
version >= CAIRO_PDF_VERSION_1_5);
}
 
/**
* cairo_pdf_get_versions:
* @versions: supported version list
* @num_versions: list length
*
* Used to retrieve the list of supported versions. See
* cairo_pdf_surface_restrict_to_version().
*
* Since: 1.10
**/
void
cairo_pdf_get_versions (cairo_pdf_version_t const **versions,
int *num_versions)
{
if (versions != NULL)
*versions = _cairo_pdf_versions;
 
if (num_versions != NULL)
*num_versions = CAIRO_PDF_VERSION_LAST;
}
 
/**
* cairo_pdf_version_to_string:
* @version: a version id
*
* Get the string representation of the given @version id. This function
* will return %NULL if @version isn't valid. See cairo_pdf_get_versions()
* for a way to get the list of valid version ids.
*
* Return value: the string associated to given version.
*
* Since: 1.10
**/
const char *
cairo_pdf_version_to_string (cairo_pdf_version_t version)
{
if (version >= CAIRO_PDF_VERSION_LAST)
return NULL;
 
return _cairo_pdf_version_strings[version];
}
 
/**
* cairo_pdf_surface_set_size:
* @surface: a PDF #cairo_surface_t
* @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
* @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
*
* Changes the size of a PDF surface for the current (and
* subsequent) pages.
*
* This function should only be called before any drawing operations
* have been performed on the current page. The simplest way to do
* this is to call this function immediately after creating the
* surface or immediately after completing a page with either
* cairo_show_page() or cairo_copy_page().
*
* Since: 1.2
**/
void
cairo_pdf_surface_set_size (cairo_surface_t *surface,
double width_in_points,
double height_in_points)
{
cairo_pdf_surface_t *pdf_surface = NULL; /* hide compiler warning */
cairo_status_t status;
 
if (! _extract_pdf_surface (surface, &pdf_surface))
return;
 
_cairo_pdf_surface_set_size_internal (pdf_surface,
width_in_points,
height_in_points);
status = _cairo_paginated_surface_set_size (pdf_surface->paginated_surface,
width_in_points,
height_in_points);
if (status)
status = _cairo_surface_set_error (surface, status);
}
 
static void
_cairo_pdf_surface_clear (cairo_pdf_surface_t *surface)
{
int i, size;
cairo_pdf_pattern_t *pattern;
cairo_pdf_source_surface_t *src_surface;
cairo_pdf_smask_group_t *group;
 
size = _cairo_array_num_elements (&surface->page_patterns);
for (i = 0; i < size; i++) {
pattern = (cairo_pdf_pattern_t *) _cairo_array_index (&surface->page_patterns, i);
cairo_pattern_destroy (pattern->pattern);
}
_cairo_array_truncate (&surface->page_patterns, 0);
 
size = _cairo_array_num_elements (&surface->page_surfaces);
for (i = 0; i < size; i++) {
src_surface = (cairo_pdf_source_surface_t *) _cairo_array_index (&surface->page_surfaces, i);
cairo_surface_destroy (src_surface->surface);
}
_cairo_array_truncate (&surface->page_surfaces, 0);
 
size = _cairo_array_num_elements (&surface->smask_groups);
for (i = 0; i < size; i++) {
_cairo_array_copy_element (&surface->smask_groups, i, &group);
_cairo_pdf_smask_group_destroy (group);
}
_cairo_array_truncate (&surface->smask_groups, 0);
_cairo_array_truncate (&surface->knockout_group, 0);
}
 
static void
_cairo_pdf_group_resources_init (cairo_pdf_group_resources_t *res)
{
int i;
 
for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
res->operators[i] = FALSE;
 
_cairo_array_init (&res->alphas, sizeof (double));
_cairo_array_init (&res->smasks, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&res->patterns, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&res->shadings, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&res->xobjects, sizeof (cairo_pdf_resource_t));
_cairo_array_init (&res->fonts, sizeof (cairo_pdf_font_t));
}
 
static void
_cairo_pdf_group_resources_fini (cairo_pdf_group_resources_t *res)
{
_cairo_array_fini (&res->alphas);
_cairo_array_fini (&res->smasks);
_cairo_array_fini (&res->patterns);
_cairo_array_fini (&res->shadings);
_cairo_array_fini (&res->xobjects);
_cairo_array_fini (&res->fonts);
}
 
static void
_cairo_pdf_group_resources_clear (cairo_pdf_group_resources_t *res)
{
int i;
 
for (i = 0; i < CAIRO_NUM_OPERATORS; i++)
res->operators[i] = FALSE;
 
_cairo_array_truncate (&res->alphas, 0);
_cairo_array_truncate (&res->smasks, 0);
_cairo_array_truncate (&res->patterns, 0);
_cairo_array_truncate (&res->shadings, 0);
_cairo_array_truncate (&res->xobjects, 0);
_cairo_array_truncate (&res->fonts, 0);
}
 
static void
_cairo_pdf_surface_add_operator (cairo_pdf_surface_t *surface,
cairo_operator_t op)
{
cairo_pdf_group_resources_t *res = &surface->resources;
 
res->operators[op] = TRUE;
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_alpha (cairo_pdf_surface_t *surface,
double alpha,
int *index)
{
int num_alphas, i;
double other;
cairo_int_status_t status;
cairo_pdf_group_resources_t *res = &surface->resources;
 
num_alphas = _cairo_array_num_elements (&res->alphas);
for (i = 0; i < num_alphas; i++) {
_cairo_array_copy_element (&res->alphas, i, &other);
if (alpha == other) {
*index = i;
return CAIRO_STATUS_SUCCESS;
}
}
 
status = _cairo_array_append (&res->alphas, &alpha);
if (unlikely (status))
return status;
 
*index = _cairo_array_num_elements (&res->alphas) - 1;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_smask (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t smask)
{
return _cairo_array_append (&(surface->resources.smasks), &smask);
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_pattern (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t pattern)
{
return _cairo_array_append (&(surface->resources.patterns), &pattern);
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_shading (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t shading)
{
return _cairo_array_append (&(surface->resources.shadings), &shading);
}
 
 
static cairo_int_status_t
_cairo_pdf_surface_add_xobject (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t xobject)
{
return _cairo_array_append (&(surface->resources.xobjects), &xobject);
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_font (unsigned int font_id,
unsigned int subset_id,
void *closure)
{
cairo_pdf_surface_t *surface = closure;
cairo_pdf_font_t font;
int num_fonts, i;
cairo_int_status_t status;
cairo_pdf_group_resources_t *res = &surface->resources;
 
num_fonts = _cairo_array_num_elements (&res->fonts);
for (i = 0; i < num_fonts; i++) {
_cairo_array_copy_element (&res->fonts, i, &font);
if (font.font_id == font_id &&
font.subset_id == subset_id)
return CAIRO_STATUS_SUCCESS;
}
 
num_fonts = _cairo_array_num_elements (&surface->fonts);
for (i = 0; i < num_fonts; i++) {
_cairo_array_copy_element (&surface->fonts, i, &font);
if (font.font_id == font_id &&
font.subset_id == subset_id)
return _cairo_array_append (&res->fonts, &font);
}
 
font.font_id = font_id;
font.subset_id = subset_id;
font.subset_resource = _cairo_pdf_surface_new_object (surface);
if (font.subset_resource.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_array_append (&surface->fonts, &font);
if (unlikely (status))
return status;
 
return _cairo_array_append (&res->fonts, &font);
}
 
static cairo_pdf_resource_t
_cairo_pdf_surface_get_font_resource (cairo_pdf_surface_t *surface,
unsigned int font_id,
unsigned int subset_id)
{
cairo_pdf_font_t font;
int num_fonts, i;
 
num_fonts = _cairo_array_num_elements (&surface->fonts);
for (i = 0; i < num_fonts; i++) {
_cairo_array_copy_element (&surface->fonts, i, &font);
if (font.font_id == font_id && font.subset_id == subset_id)
return font.subset_resource;
}
 
font.subset_resource.id = 0;
return font.subset_resource;
}
 
static const char *
_cairo_operator_to_pdf_blend_mode (cairo_operator_t op)
{
switch (op) {
/* The extend blend mode operators */
case CAIRO_OPERATOR_MULTIPLY: return "Multiply";
case CAIRO_OPERATOR_SCREEN: return "Screen";
case CAIRO_OPERATOR_OVERLAY: return "Overlay";
case CAIRO_OPERATOR_DARKEN: return "Darken";
case CAIRO_OPERATOR_LIGHTEN: return "Lighten";
case CAIRO_OPERATOR_COLOR_DODGE: return "ColorDodge";
case CAIRO_OPERATOR_COLOR_BURN: return "ColorBurn";
case CAIRO_OPERATOR_HARD_LIGHT: return "HardLight";
case CAIRO_OPERATOR_SOFT_LIGHT: return "SoftLight";
case CAIRO_OPERATOR_DIFFERENCE: return "Difference";
case CAIRO_OPERATOR_EXCLUSION: return "Exclusion";
case CAIRO_OPERATOR_HSL_HUE: return "Hue";
case CAIRO_OPERATOR_HSL_SATURATION: return "Saturation";
case CAIRO_OPERATOR_HSL_COLOR: return "Color";
case CAIRO_OPERATOR_HSL_LUMINOSITY: return "Luminosity";
 
default:
/* The original Porter-Duff set */
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
return "Normal";
}
}
 
static void
_cairo_pdf_surface_emit_group_resources (cairo_pdf_surface_t *surface,
cairo_pdf_group_resources_t *res)
{
int num_alphas, num_smasks, num_resources, i;
double alpha;
cairo_pdf_resource_t *smask, *pattern, *shading, *xobject;
cairo_pdf_font_t *font;
 
_cairo_output_stream_printf (surface->output, "<<\n");
 
num_alphas = _cairo_array_num_elements (&res->alphas);
num_smasks = _cairo_array_num_elements (&res->smasks);
if (num_alphas > 0 || num_smasks > 0) {
_cairo_output_stream_printf (surface->output,
" /ExtGState <<\n");
 
for (i = 0; i < CAIRO_NUM_OPERATORS; i++) {
if (res->operators[i]) {
_cairo_output_stream_printf (surface->output,
" /b%d << /BM /%s >>\n",
i, _cairo_operator_to_pdf_blend_mode(i));
}
}
 
for (i = 0; i < num_alphas; i++) {
_cairo_array_copy_element (&res->alphas, i, &alpha);
_cairo_output_stream_printf (surface->output,
" /a%d << /CA %f /ca %f >>\n",
i, alpha, alpha);
}
 
for (i = 0; i < num_smasks; i++) {
smask = _cairo_array_index (&res->smasks, i);
_cairo_output_stream_printf (surface->output,
" /s%d %d 0 R\n",
smask->id, smask->id);
}
 
_cairo_output_stream_printf (surface->output,
" >>\n");
}
 
num_resources = _cairo_array_num_elements (&res->patterns);
if (num_resources > 0) {
_cairo_output_stream_printf (surface->output,
" /Pattern <<");
for (i = 0; i < num_resources; i++) {
pattern = _cairo_array_index (&res->patterns, i);
_cairo_output_stream_printf (surface->output,
" /p%d %d 0 R",
pattern->id, pattern->id);
}
 
_cairo_output_stream_printf (surface->output,
" >>\n");
}
 
num_resources = _cairo_array_num_elements (&res->shadings);
if (num_resources > 0) {
_cairo_output_stream_printf (surface->output,
" /Shading <<");
for (i = 0; i < num_resources; i++) {
shading = _cairo_array_index (&res->shadings, i);
_cairo_output_stream_printf (surface->output,
" /sh%d %d 0 R",
shading->id, shading->id);
}
 
_cairo_output_stream_printf (surface->output,
" >>\n");
}
 
num_resources = _cairo_array_num_elements (&res->xobjects);
if (num_resources > 0) {
_cairo_output_stream_printf (surface->output,
" /XObject <<");
 
for (i = 0; i < num_resources; i++) {
xobject = _cairo_array_index (&res->xobjects, i);
_cairo_output_stream_printf (surface->output,
" /x%d %d 0 R",
xobject->id, xobject->id);
}
 
_cairo_output_stream_printf (surface->output,
" >>\n");
}
 
num_resources = _cairo_array_num_elements (&res->fonts);
if (num_resources > 0) {
_cairo_output_stream_printf (surface->output," /Font <<\n");
for (i = 0; i < num_resources; i++) {
font = _cairo_array_index (&res->fonts, i);
_cairo_output_stream_printf (surface->output,
" /f-%d-%d %d 0 R\n",
font->font_id,
font->subset_id,
font->subset_resource.id);
}
_cairo_output_stream_printf (surface->output, " >>\n");
}
 
_cairo_output_stream_printf (surface->output,
">>\n");
}
 
static cairo_pdf_smask_group_t *
_cairo_pdf_surface_create_smask_group (cairo_pdf_surface_t *surface,
const cairo_rectangle_int_t *extents)
{
cairo_pdf_smask_group_t *group;
 
group = calloc (1, sizeof (cairo_pdf_smask_group_t));
if (unlikely (group == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
group->group_res = _cairo_pdf_surface_new_object (surface);
if (group->group_res.id == 0) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
free (group);
return NULL;
}
group->width = surface->width;
group->height = surface->height;
if (extents != NULL) {
group->extents = *extents;
} else {
group->extents.x = 0;
group->extents.y = 0;
group->extents.width = surface->width;
group->extents.height = surface->height;
}
group->extents = *extents;
 
return group;
}
 
static void
_cairo_pdf_smask_group_destroy (cairo_pdf_smask_group_t *group)
{
if (group->operation == PDF_FILL || group->operation == PDF_STROKE)
_cairo_path_fixed_fini (&group->path);
if (group->source)
cairo_pattern_destroy (group->source);
if (group->mask)
cairo_pattern_destroy (group->mask);
free (group->utf8);
free (group->glyphs);
free (group->clusters);
if (group->scaled_font)
cairo_scaled_font_destroy (group->scaled_font);
free (group);
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_smask_group (cairo_pdf_surface_t *surface,
cairo_pdf_smask_group_t *group)
{
return _cairo_array_append (&surface->smask_groups, &group);
}
 
static cairo_bool_t
_cairo_pdf_source_surface_equal (const void *key_a, const void *key_b)
{
const cairo_pdf_source_surface_entry_t *a = key_a;
const cairo_pdf_source_surface_entry_t *b = key_b;
 
if (a->interpolate != b->interpolate)
return FALSE;
 
if (a->unique_id && b->unique_id && a->unique_id_length == b->unique_id_length)
return (memcmp (a->unique_id, b->unique_id, a->unique_id_length) == 0);
 
return (a->id == b->id);
}
 
static void
_cairo_pdf_source_surface_init_key (cairo_pdf_source_surface_entry_t *key)
{
if (key->unique_id && key->unique_id_length > 0) {
key->base.hash = _cairo_hash_bytes (_CAIRO_HASH_INIT_VALUE,
key->unique_id, key->unique_id_length);
} else {
key->base.hash = key->id;
}
}
 
static cairo_int_status_t
_cairo_pdf_surface_acquire_source_image_from_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_image_surface_t **image,
void **image_extra)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
return _cairo_surface_acquire_source_image (surf_pat->surface, image, image_extra);
} break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
cairo_surface_t *surf;
surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
if (!surf)
return CAIRO_INT_STATUS_UNSUPPORTED;
assert (_cairo_surface_is_image (surf));
*image = (cairo_image_surface_t *) surf;
} break;
 
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
default:
ASSERT_NOT_REACHED;
break;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_pdf_surface_release_source_image_from_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_image_surface_t *image,
void *image_extra)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
_cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
} break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_raster_source_pattern_release (pattern, &image->base);
break;
 
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
default:
 
ASSERT_NOT_REACHED;
break;
}
}
 
static cairo_int_status_t
_get_jpx_image_info (cairo_surface_t *source,
cairo_image_info_t *info,
const unsigned char **mime_data,
unsigned long *mime_data_length)
{
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
mime_data, mime_data_length);
if (*mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return _cairo_image_info_get_jpx_info (info, *mime_data, *mime_data_length);
}
 
static cairo_int_status_t
_get_jpeg_image_info (cairo_surface_t *source,
cairo_image_info_t *info,
const unsigned char **mime_data,
unsigned long *mime_data_length)
{
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
mime_data, mime_data_length);
if (*mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return _cairo_image_info_get_jpeg_info (info, *mime_data, *mime_data_length);
}
 
static cairo_int_status_t
_get_source_surface_size (cairo_surface_t *source,
int *width,
int *height,
cairo_rectangle_int_t *extents)
{
cairo_int_status_t status;
cairo_image_info_t info;
const unsigned char *mime_data;
unsigned long mime_data_length;
 
if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
 
*extents = sub->extents;
*width = extents->width;
*height = extents->height;
} else {
cairo_surface_t *free_me = NULL;
cairo_rectangle_int_t surf_extents;
cairo_box_t box;
cairo_bool_t bounded;
 
if (_cairo_surface_is_snapshot (source))
free_me = source = _cairo_surface_snapshot_get_target (source);
 
status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source,
&box, NULL);
if (unlikely (status)) {
cairo_surface_destroy (free_me);
return status;
}
 
bounded = _cairo_surface_get_extents (source, &surf_extents);
cairo_surface_destroy (free_me);
 
*width = surf_extents.width;
*height = surf_extents.height;
 
_cairo_box_round_to_rectangle (&box, extents);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
extents->x = 0;
extents->y = 0;
 
status = _get_jpx_image_info (source, &info, &mime_data, &mime_data_length);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*width = info.width;
*height = info.height;
extents->width = info.width;
extents->height = info.height;
return status;
}
 
status = _get_jpeg_image_info (source, &info, &mime_data, &mime_data_length);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*width = info.width;
*height = info.height;
extents->width = info.width;
extents->height = info.height;
return status;
}
 
if (! _cairo_surface_get_extents (source, extents))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
*width = extents->width;
*height = extents->height;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_pdf_surface_add_source_surface:
* @surface: the pdf surface
* @source_surface: A #cairo_surface_t to use as the source surface
* @source_pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
* @filter: filter type of the source pattern
* @stencil_mask: if true, the surface will be written to the PDF as an /ImageMask
* @extents: extents of the operation that is using this source
* @surface_res: return PDF resource number of the surface
* @width: returns width of surface
* @height: returns height of surface
* @x_offset: x offset of surface
* @t_offset: y offset of surface
* @source_extents: returns extents of source (either ink extents or extents needed to cover @extents)
*
* Add surface or raster_source pattern to list of surfaces to be
* written to the PDF file when the current page is finished. Returns
* a PDF resource to reference the image. A hash table of all images
* in the PDF files (keyed by CAIRO_MIME_TYPE_UNIQUE_ID or surface
* unique_id) to ensure surfaces with the same id are only written
* once to the PDF file.
*
* Only one of @source_pattern or @source_surface is to be
* specified. Set the other to NULL.
**/
static cairo_int_status_t
_cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t *surface,
cairo_surface_t *source_surface,
const cairo_pattern_t *source_pattern,
cairo_filter_t filter,
cairo_bool_t stencil_mask,
const cairo_rectangle_int_t *extents,
cairo_pdf_resource_t *surface_res,
int *width,
int *height,
double *x_offset,
double *y_offset,
cairo_rectangle_int_t *source_extents)
{
cairo_pdf_source_surface_t src_surface;
cairo_pdf_source_surface_entry_t surface_key;
cairo_pdf_source_surface_entry_t *surface_entry;
cairo_int_status_t status;
cairo_bool_t interpolate;
unsigned char *unique_id = NULL;
unsigned long unique_id_length = 0;
cairo_image_surface_t *image;
void *image_extra;
 
switch (filter) {
default:
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
interpolate = TRUE;
break;
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
case CAIRO_FILTER_GAUSSIAN:
interpolate = FALSE;
break;
}
 
*x_offset = 0;
*y_offset = 0;
if (source_pattern) {
if (source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source_pattern,
&image, &image_extra);
if (unlikely (status))
return status;
source_surface = &image->base;
cairo_surface_get_device_offset (source_surface, x_offset, y_offset);
} else {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) source_pattern;
source_surface = surface_pattern->surface;
}
}
 
surface_key.id = source_surface->unique_id;
surface_key.interpolate = interpolate;
cairo_surface_get_mime_data (source_surface, CAIRO_MIME_TYPE_UNIQUE_ID,
(const unsigned char **) &surface_key.unique_id,
&surface_key.unique_id_length);
_cairo_pdf_source_surface_init_key (&surface_key);
surface_entry = _cairo_hash_table_lookup (surface->all_surfaces, &surface_key.base);
if (surface_entry) {
*surface_res = surface_entry->surface_res;
*width = surface_entry->width;
*height = surface_entry->height;
*source_extents = surface_entry->extents;
status = CAIRO_STATUS_SUCCESS;
} else {
status = _get_source_surface_size (source_surface,
width,
height,
source_extents);
if (unlikely(status))
goto release_source;
 
if (surface_key.unique_id && surface_key.unique_id_length > 0) {
unique_id = _cairo_malloc (surface_key.unique_id_length);
if (unique_id == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto release_source;
}
 
unique_id_length = surface_key.unique_id_length;
memcpy (unique_id, surface_key.unique_id, unique_id_length);
} else {
unique_id = NULL;
unique_id_length = 0;
}
}
 
release_source:
if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
_cairo_pdf_surface_release_source_image_from_pattern (surface, source_pattern, image, image_extra);
 
if (status || surface_entry)
return status;
 
surface_entry = malloc (sizeof (cairo_pdf_source_surface_entry_t));
if (surface_entry == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail1;
}
 
surface_entry->id = surface_key.id;
surface_entry->interpolate = interpolate;
surface_entry->stencil_mask = stencil_mask;
surface_entry->unique_id_length = unique_id_length;
surface_entry->unique_id = unique_id;
surface_entry->width = *width;
surface_entry->height = *height;
surface_entry->extents = *source_extents;
_cairo_pdf_source_surface_init_key (surface_entry);
 
src_surface.hash_entry = surface_entry;
if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE) {
src_surface.type = CAIRO_PATTERN_TYPE_RASTER_SOURCE;
src_surface.surface = NULL;
status = _cairo_pattern_create_copy (&src_surface.raster_pattern, source_pattern);
if (unlikely (status))
goto fail2;
 
} else {
src_surface.type = CAIRO_PATTERN_TYPE_SURFACE;
src_surface.surface = cairo_surface_reference (source_surface);
src_surface.raster_pattern = NULL;
}
 
surface_entry->surface_res = _cairo_pdf_surface_new_object (surface);
if (surface_entry->surface_res.id == 0) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
 
status = _cairo_array_append (&surface->page_surfaces, &src_surface);
if (unlikely (status))
goto fail3;
 
status = _cairo_hash_table_insert (surface->all_surfaces,
&surface_entry->base);
if (unlikely(status))
goto fail3;
 
*surface_res = surface_entry->surface_res;
 
return status;
 
fail3:
if (source_pattern && source_pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
cairo_pattern_destroy (src_surface.raster_pattern);
else
cairo_surface_destroy (src_surface.surface);
 
fail2:
free (surface_entry);
 
fail1:
free (unique_id);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_pdf_pattern_or_shading (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_bool_t is_shading,
cairo_pdf_resource_t *pattern_res,
cairo_pdf_resource_t *gstate_res)
{
cairo_pdf_pattern_t pdf_pattern;
cairo_int_status_t status;
 
pdf_pattern.is_shading = is_shading;
 
/* Solid colors are emitted into the content stream */
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
pattern_res->id = 0;
gstate_res->id = 0;
return CAIRO_INT_STATUS_SUCCESS;
}
 
status = _cairo_pattern_create_copy (&pdf_pattern.pattern, pattern);
if (unlikely (status))
return status;
 
pdf_pattern.pattern_res = _cairo_pdf_surface_new_object (surface);
if (pdf_pattern.pattern_res.id == 0) {
cairo_pattern_destroy (pdf_pattern.pattern);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pdf_pattern.gstate_res.id = 0;
 
/* gradient patterns require an smask object to implement transparency */
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
pattern->type == CAIRO_PATTERN_TYPE_RADIAL ||
pattern->type == CAIRO_PATTERN_TYPE_MESH)
{
double min_alpha;
 
_cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
if (pdf_pattern.gstate_res.id == 0) {
cairo_pattern_destroy (pdf_pattern.pattern);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
}
 
pdf_pattern.width = surface->width;
pdf_pattern.height = surface->height;
if (extents != NULL) {
pdf_pattern.extents = *extents;
} else {
pdf_pattern.extents.x = 0;
pdf_pattern.extents.y = 0;
pdf_pattern.extents.width = surface->width;
pdf_pattern.extents.height = surface->height;
}
 
*pattern_res = pdf_pattern.pattern_res;
*gstate_res = pdf_pattern.gstate_res;
 
status = _cairo_array_append (&surface->page_patterns, &pdf_pattern);
if (unlikely (status)) {
cairo_pattern_destroy (pdf_pattern.pattern);
return status;
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
/* Get BBox in PDF coordinates from extents in cairo coordinates */
static void
_get_bbox_from_extents (double surface_height,
const cairo_rectangle_int_t *extents,
cairo_box_double_t *bbox)
{
bbox->p1.x = extents->x;
bbox->p1.y = surface_height - (extents->y + extents->height);
bbox->p2.x = extents->x + extents->width;
bbox->p2.y = surface_height - extents->y;
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_pdf_shading (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_pdf_resource_t *shading_res,
cairo_pdf_resource_t *gstate_res)
{
return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
pattern,
extents,
TRUE,
shading_res,
gstate_res);
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_pdf_resource_t *pattern_res,
cairo_pdf_resource_t *gstate_res)
{
return _cairo_pdf_surface_add_pdf_pattern_or_shading (surface,
pattern,
extents,
FALSE,
pattern_res,
gstate_res);
}
 
static cairo_int_status_t
_cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t *resource,
cairo_bool_t compressed,
const char *fmt,
...)
{
va_list ap;
cairo_pdf_resource_t self, length;
cairo_output_stream_t *output = NULL;
 
if (resource) {
self = *resource;
_cairo_pdf_surface_update_object (surface, self);
} else {
self = _cairo_pdf_surface_new_object (surface);
if (self.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
length = _cairo_pdf_surface_new_object (surface);
if (length.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (compressed) {
output = _cairo_deflate_stream_create (surface->output);
if (_cairo_output_stream_get_status (output))
return _cairo_output_stream_destroy (output);
}
 
surface->pdf_stream.active = TRUE;
surface->pdf_stream.self = self;
surface->pdf_stream.length = length;
surface->pdf_stream.compressed = compressed;
surface->current_pattern_is_solid_color = FALSE;
surface->current_operator = CAIRO_OPERATOR_OVER;
_cairo_pdf_operators_reset (&surface->pdf_operators);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Length %d 0 R\n",
surface->pdf_stream.self.id,
surface->pdf_stream.length.id);
if (compressed)
_cairo_output_stream_printf (surface->output,
" /Filter /FlateDecode\n");
 
if (fmt != NULL) {
va_start (ap, fmt);
_cairo_output_stream_vprintf (surface->output, fmt, ap);
va_end (ap);
}
 
_cairo_output_stream_printf (surface->output,
">>\n"
"stream\n");
 
surface->pdf_stream.start_offset = _cairo_output_stream_get_position (surface->output);
 
if (compressed) {
assert (surface->pdf_stream.old_output == NULL);
surface->pdf_stream.old_output = surface->output;
surface->output = output;
_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
}
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface)
{
cairo_int_status_t status;
long length;
 
if (! surface->pdf_stream.active)
return CAIRO_INT_STATUS_SUCCESS;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
 
if (surface->pdf_stream.compressed) {
cairo_int_status_t status2;
 
status2 = _cairo_output_stream_destroy (surface->output);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = status2;
 
surface->output = surface->pdf_stream.old_output;
_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
surface->pdf_stream.old_output = NULL;
}
 
length = _cairo_output_stream_get_position (surface->output) -
surface->pdf_stream.start_offset;
_cairo_output_stream_printf (surface->output,
"\n"
"endstream\n"
"endobj\n");
 
_cairo_pdf_surface_update_object (surface,
surface->pdf_stream.length);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
" %ld\n"
"endobj\n",
surface->pdf_stream.length.id,
length);
 
surface->pdf_stream.active = FALSE;
 
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = _cairo_output_stream_get_status (surface->output);
 
return status;
}
 
static void
_cairo_pdf_surface_write_memory_stream (cairo_pdf_surface_t *surface,
cairo_output_stream_t *mem_stream,
cairo_pdf_resource_t resource,
cairo_pdf_group_resources_t *resources,
cairo_bool_t is_knockout_group,
const cairo_box_double_t *bbox)
{
_cairo_pdf_surface_update_object (surface, resource);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /XObject\n"
" /Length %d\n",
resource.id,
_cairo_memory_stream_length (mem_stream));
 
if (surface->compress_content) {
_cairo_output_stream_printf (surface->output,
" /Filter /FlateDecode\n");
}
 
_cairo_output_stream_printf (surface->output,
" /Subtype /Form\n"
" /BBox [ %f %f %f %f ]\n"
" /Group <<\n"
" /Type /Group\n"
" /S /Transparency\n"
" /I true\n"
" /CS /DeviceRGB\n",
bbox->p1.x, bbox->p1.y, bbox->p2.x, bbox->p2.y);
 
if (is_knockout_group)
_cairo_output_stream_printf (surface->output,
" /K true\n");
 
_cairo_output_stream_printf (surface->output,
" >>\n"
" /Resources\n");
_cairo_pdf_surface_emit_group_resources (surface, resources);
_cairo_output_stream_printf (surface->output,
">>\n"
"stream\n");
_cairo_memory_stream_copy (mem_stream, surface->output);
_cairo_output_stream_printf (surface->output,
"endstream\n"
"endobj\n");
}
 
static cairo_int_status_t
_cairo_pdf_surface_open_group (cairo_pdf_surface_t *surface,
const cairo_box_double_t *bbox,
cairo_pdf_resource_t *resource)
{
cairo_int_status_t status;
 
assert (surface->pdf_stream.active == FALSE);
assert (surface->group_stream.active == FALSE);
 
surface->group_stream.active = TRUE;
surface->current_pattern_is_solid_color = FALSE;
surface->current_operator = CAIRO_OPERATOR_OVER;
_cairo_pdf_operators_reset (&surface->pdf_operators);
 
surface->group_stream.mem_stream = _cairo_memory_stream_create ();
 
if (surface->compress_content) {
surface->group_stream.stream =
_cairo_deflate_stream_create (surface->group_stream.mem_stream);
} else {
surface->group_stream.stream = surface->group_stream.mem_stream;
}
status = _cairo_output_stream_get_status (surface->group_stream.stream);
 
surface->group_stream.old_output = surface->output;
surface->output = surface->group_stream.stream;
_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
_cairo_pdf_group_resources_clear (&surface->resources);
 
if (resource) {
surface->group_stream.resource = *resource;
} else {
surface->group_stream.resource = _cairo_pdf_surface_new_object (surface);
if (surface->group_stream.resource.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
surface->group_stream.is_knockout = FALSE;
surface->group_stream.bbox = *bbox;
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_open_knockout_group (cairo_pdf_surface_t *surface,
const cairo_box_double_t *bbox)
{
cairo_int_status_t status;
 
status = _cairo_pdf_surface_open_group (surface, bbox, NULL);
if (unlikely (status))
return status;
 
surface->group_stream.is_knockout = TRUE;
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_close_group (cairo_pdf_surface_t *surface,
cairo_pdf_resource_t *group)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS, status2;
 
assert (surface->pdf_stream.active == FALSE);
assert (surface->group_stream.active == TRUE);
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
if (surface->compress_content) {
status = _cairo_output_stream_destroy (surface->group_stream.stream);
surface->group_stream.stream = NULL;
 
_cairo_output_stream_printf (surface->group_stream.mem_stream,
"\n");
}
surface->output = surface->group_stream.old_output;
_cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output);
surface->group_stream.active = FALSE;
_cairo_pdf_surface_write_memory_stream (surface,
surface->group_stream.mem_stream,
surface->group_stream.resource,
&surface->resources,
surface->group_stream.is_knockout,
&surface->group_stream.bbox);
if (group)
*group = surface->group_stream.resource;
 
status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
 
surface->group_stream.mem_stream = NULL;
surface->group_stream.stream = NULL;
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_open_content_stream (cairo_pdf_surface_t *surface,
const cairo_box_double_t *bbox,
cairo_pdf_resource_t *resource,
cairo_bool_t is_form)
{
cairo_int_status_t status;
 
assert (surface->pdf_stream.active == FALSE);
assert (surface->group_stream.active == FALSE);
 
surface->content_resources = _cairo_pdf_surface_new_object (surface);
if (surface->content_resources.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (is_form) {
assert (bbox != NULL);
 
status =
_cairo_pdf_surface_open_stream (surface,
resource,
surface->compress_content,
" /Type /XObject\n"
" /Subtype /Form\n"
" /BBox [ %f %f %f %f ]\n"
" /Group <<\n"
" /Type /Group\n"
" /S /Transparency\n"
" /I true\n"
" /CS /DeviceRGB\n"
" >>\n"
" /Resources %d 0 R\n",
bbox->p1.x,
bbox->p1.y,
bbox->p2.x,
bbox->p2.y,
surface->content_resources.id);
} else {
status =
_cairo_pdf_surface_open_stream (surface,
resource,
surface->compress_content,
NULL);
}
if (unlikely (status))
return status;
 
surface->content = surface->pdf_stream.self;
 
_cairo_output_stream_printf (surface->output, "q\n");
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_close_content_stream (cairo_pdf_surface_t *surface)
{
cairo_int_status_t status;
 
assert (surface->pdf_stream.active == TRUE);
assert (surface->group_stream.active == FALSE);
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "Q\n");
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
return status;
 
_cairo_pdf_surface_update_object (surface, surface->content_resources);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n",
surface->content_resources.id);
_cairo_pdf_surface_emit_group_resources (surface, &surface->resources);
_cairo_output_stream_printf (surface->output,
"endobj\n");
 
return _cairo_output_stream_get_status (surface->output);
}
 
static void
_cairo_pdf_source_surface_entry_pluck (void *entry, void *closure)
{
cairo_pdf_source_surface_entry_t *surface_entry = entry;
cairo_hash_table_t *patterns = closure;
 
_cairo_hash_table_remove (patterns, &surface_entry->base);
free (surface_entry->unique_id);
 
free (surface_entry);
}
 
static cairo_status_t
_cairo_pdf_surface_finish (void *abstract_surface)
{
cairo_pdf_surface_t *surface = abstract_surface;
long offset;
cairo_pdf_resource_t info, catalog;
cairo_status_t status, status2;
 
status = surface->base.status;
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_pdf_surface_emit_font_subsets (surface);
 
_cairo_pdf_surface_write_pages (surface);
 
info = _cairo_pdf_surface_write_info (surface);
if (info.id == 0 && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
catalog = _cairo_pdf_surface_write_catalog (surface);
if (catalog.id == 0 && status == CAIRO_STATUS_SUCCESS)
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
offset = _cairo_pdf_surface_write_xref (surface);
 
_cairo_output_stream_printf (surface->output,
"trailer\n"
"<< /Size %d\n"
" /Root %d 0 R\n"
" /Info %d 0 R\n"
">>\n",
surface->next_available_resource.id,
catalog.id,
info.id);
 
_cairo_output_stream_printf (surface->output,
"startxref\n"
"%ld\n"
"%%%%EOF\n",
offset);
 
/* pdf_operators has already been flushed when the last stream was
* closed so we should never be writing anything here - however,
* the stream may itself be in an error state. */
status2 = _cairo_pdf_operators_fini (&surface->pdf_operators);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
/* close any active streams still open due to fatal errors */
status2 = _cairo_pdf_surface_close_stream (surface);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
if (surface->group_stream.stream != NULL) {
status2 = _cairo_output_stream_destroy (surface->group_stream.stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
}
if (surface->group_stream.mem_stream != NULL) {
status2 = _cairo_output_stream_destroy (surface->group_stream.mem_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
}
if (surface->pdf_stream.active)
surface->output = surface->pdf_stream.old_output;
if (surface->group_stream.active)
surface->output = surface->group_stream.old_output;
 
/* and finish the pdf surface */
status2 = _cairo_output_stream_destroy (surface->output);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
_cairo_pdf_surface_clear (surface);
_cairo_pdf_group_resources_fini (&surface->resources);
 
_cairo_array_fini (&surface->objects);
_cairo_array_fini (&surface->pages);
_cairo_array_fini (&surface->rgb_linear_functions);
_cairo_array_fini (&surface->alpha_linear_functions);
_cairo_array_fini (&surface->page_patterns);
_cairo_array_fini (&surface->page_surfaces);
_cairo_hash_table_foreach (surface->all_surfaces,
_cairo_pdf_source_surface_entry_pluck,
surface->all_surfaces);
_cairo_hash_table_destroy (surface->all_surfaces);
_cairo_array_fini (&surface->smask_groups);
_cairo_array_fini (&surface->fonts);
_cairo_array_fini (&surface->knockout_group);
 
if (surface->font_subsets) {
_cairo_scaled_font_subsets_destroy (surface->font_subsets);
surface->font_subsets = NULL;
}
 
_cairo_surface_clipper_reset (&surface->clipper);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_start_page (void *abstract_surface)
{
cairo_pdf_surface_t *surface = abstract_surface;
 
/* Document header */
if (! surface->header_emitted) {
const char *version;
 
switch (surface->pdf_version) {
case CAIRO_PDF_VERSION_1_4:
version = "1.4";
break;
default:
case CAIRO_PDF_VERSION_1_5:
version = "1.5";
break;
}
 
_cairo_output_stream_printf (surface->output,
"%%PDF-%s\n", version);
_cairo_output_stream_printf (surface->output,
"%%%c%c%c%c\n", 181, 237, 174, 251);
surface->header_emitted = TRUE;
}
 
_cairo_pdf_group_resources_clear (&surface->resources);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_has_fallback_images (void *abstract_surface,
cairo_bool_t has_fallbacks)
{
cairo_int_status_t status;
cairo_pdf_surface_t *surface = abstract_surface;
cairo_box_double_t bbox;
 
surface->has_fallback_images = has_fallbacks;
bbox.p1.x = 0;
bbox.p1.y = 0;
bbox.p2.x = surface->width;
bbox.p2.y = surface->height;
status = _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, has_fallbacks);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
{
return TRUE;
}
 
static cairo_int_status_t
_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t *surface,
const cairo_pattern_t *source,
const cairo_rectangle_int_t *extents,
cairo_pdf_resource_t *surface_res,
int *width,
int *height,
double *x_offset,
double *y_offset)
{
cairo_image_surface_t *image;
cairo_surface_t *pad_image;
void *image_extra;
cairo_int_status_t status;
int w, h;
cairo_rectangle_int_t extents2;
cairo_box_t box;
cairo_rectangle_int_t rect;
cairo_surface_pattern_t pad_pattern;
 
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source,
&image, &image_extra);
if (unlikely (status))
return status;
 
pad_image = &image->base;
 
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (&source->matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &rect);
 
/* Check if image needs padding to fill extents */
w = image->width;
h = image->height;
if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
_cairo_fixed_integer_ceil(box.p1.y) < 0 ||
_cairo_fixed_integer_floor(box.p2.x) > w ||
_cairo_fixed_integer_floor(box.p2.y) > h)
{
pad_image = _cairo_image_surface_create_with_content (image->base.content,
rect.width,
rect.height);
if (pad_image->status) {
status = pad_image->status;
goto BAIL;
}
 
_cairo_pattern_init_for_surface (&pad_pattern, &image->base);
cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y);
pad_pattern.base.extend = CAIRO_EXTEND_PAD;
status = _cairo_surface_paint (pad_image,
CAIRO_OPERATOR_SOURCE, &pad_pattern.base,
NULL);
_cairo_pattern_fini (&pad_pattern.base);
if (unlikely (status))
goto BAIL;
}
 
status = _cairo_pdf_surface_add_source_surface (surface,
pad_image,
NULL,
source->filter,
FALSE,
extents,
surface_res,
width,
height,
x_offset,
y_offset,
&extents2);
if (unlikely (status))
goto BAIL;
 
if (pad_image != &image->base) {
/* If using a padded image, replace _add_source_surface
* x/y_offset with padded image offset. Note:
* _add_source_surface only sets a non zero x/y_offset for
* RASTER_SOURCE patterns. _add_source_surface will always set
* x/y_offset to 0 for surfaces so we can ignore the returned
* offset and replace it with the offset required for the
* padded image */
*x_offset = rect.x;
*y_offset = rect.y;
}
 
BAIL:
if (pad_image != &image->base)
cairo_surface_destroy (pad_image);
 
_cairo_pdf_surface_release_source_image_from_pattern (surface, source, image, image_extra);
 
return status;
}
 
/* Emit alpha channel from the image into the given data, providing
* an id that can be used to reference the resulting SMask object.
*
* In the case that the alpha channel happens to be all opaque, then
* no SMask object will be emitted and *id_ret will be set to 0.
*
* When stencil_mask is TRUE, stream_res is an an input specifying the
* resource to use. When stencil_mask is FALSE, a new resource will be
* created and returned in stream_res.
*/
static cairo_int_status_t
_cairo_pdf_surface_emit_smask (cairo_pdf_surface_t *surface,
cairo_image_surface_t *image,
cairo_bool_t stencil_mask,
const char *interpolate,
cairo_pdf_resource_t *stream_res)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
char *alpha;
unsigned long alpha_size;
uint32_t *pixel32;
uint8_t *pixel8;
int i, x, y, bit, a;
cairo_image_transparency_t transparency;
 
/* This is the only image format we support, which simplifies things. */
assert (image->format == CAIRO_FORMAT_ARGB32 ||
image->format == CAIRO_FORMAT_A8 ||
image->format == CAIRO_FORMAT_A1 );
 
transparency = _cairo_image_analyze_transparency (image);
if (stencil_mask) {
assert (transparency == CAIRO_IMAGE_IS_OPAQUE ||
transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA);
} else {
if (transparency == CAIRO_IMAGE_IS_OPAQUE)
return status;
}
 
if (transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA) {
alpha_size = (image->width + 7) / 8 * image->height;
alpha = _cairo_malloc_ab ((image->width+7) / 8, image->height);
} else {
alpha_size = image->height * image->width;
alpha = _cairo_malloc_ab (image->height, image->width);
}
 
if (unlikely (alpha == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
i = 0;
for (y = 0; y < image->height; y++) {
if (image->format == CAIRO_FORMAT_A1) {
pixel8 = (uint8_t *) (image->data + y * image->stride);
 
for (x = 0; x < (image->width + 7) / 8; x++, pixel8++) {
a = *pixel8;
a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
alpha[i++] = a;
}
} else {
pixel8 = (uint8_t *) (image->data + y * image->stride);
pixel32 = (uint32_t *) (image->data + y * image->stride);
bit = 7;
for (x = 0; x < image->width; x++) {
if (image->format == CAIRO_FORMAT_ARGB32) {
a = (*pixel32 & 0xff000000) >> 24;
pixel32++;
} else {
a = *pixel8;
pixel8++;
}
 
if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
alpha[i++] = a;
} else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
if (bit == 7)
alpha[i] = 0;
if (a != 0)
alpha[i] |= (1 << bit);
bit--;
if (bit < 0) {
bit = 7;
i++;
}
}
}
if (bit != 7)
i++;
}
}
 
if (stencil_mask) {
status = _cairo_pdf_surface_open_stream (surface,
stream_res,
TRUE,
" /Type /XObject\n"
" /Subtype /Image\n"
" /ImageMask true\n"
" /Width %d\n"
" /Height %d\n"
" /Interpolate %s\n"
" /BitsPerComponent 1\n"
" /Decode [1 0]\n",
image->width, image->height, interpolate);
} else {
stream_res->id = 0;
status = _cairo_pdf_surface_open_stream (surface,
NULL,
TRUE,
" /Type /XObject\n"
" /Subtype /Image\n"
" /Width %d\n"
" /Height %d\n"
" /ColorSpace /DeviceGray\n"
" /Interpolate %s\n"
" /BitsPerComponent %d\n",
image->width, image->height, interpolate,
transparency == CAIRO_IMAGE_HAS_ALPHA ? 8 : 1);
}
if (unlikely (status))
goto CLEANUP_ALPHA;
 
if (!stencil_mask)
*stream_res = surface->pdf_stream.self;
 
_cairo_output_stream_write (surface->output, alpha, alpha_size);
status = _cairo_pdf_surface_close_stream (surface);
 
CLEANUP_ALPHA:
free (alpha);
CLEANUP:
return status;
}
 
/* Emit image data into the given surface, providing a resource that
* can be used to reference the data in image_ret. */
static cairo_int_status_t
_cairo_pdf_surface_emit_image (cairo_pdf_surface_t *surface,
cairo_image_surface_t *image_surf,
cairo_pdf_resource_t *image_res,
cairo_filter_t filter,
cairo_bool_t stencil_mask)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
char *data;
unsigned long data_size;
uint32_t *pixel;
int i, x, y, bit;
cairo_pdf_resource_t smask = {0}; /* squelch bogus compiler warning */
cairo_bool_t need_smask;
const char *interpolate = "true";
cairo_image_color_t color;
cairo_image_surface_t *image;
 
image = image_surf;
if (image->format != CAIRO_FORMAT_RGB24 &&
image->format != CAIRO_FORMAT_ARGB32 &&
image->format != CAIRO_FORMAT_A8 &&
image->format != CAIRO_FORMAT_A1)
{
cairo_surface_t *surf;
cairo_surface_pattern_t pattern;
 
surf = _cairo_image_surface_create_with_content (image_surf->base.content,
image_surf->width,
image_surf->height);
image = (cairo_image_surface_t *) surf;
if (surf->status) {
status = surf->status;
goto CLEANUP;
}
 
_cairo_pattern_init_for_surface (&pattern, &image_surf->base);
status = _cairo_surface_paint (surf,
CAIRO_OPERATOR_SOURCE, &pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status))
goto CLEANUP;
}
 
switch (filter) {
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
interpolate = "true";
break;
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
case CAIRO_FILTER_GAUSSIAN:
interpolate = "false";
break;
}
 
if (stencil_mask)
return _cairo_pdf_surface_emit_smask (surface, image, stencil_mask, interpolate, image_res);
 
color = _cairo_image_analyze_color (image);
switch (color) {
default:
case CAIRO_IMAGE_UNKNOWN_COLOR:
ASSERT_NOT_REACHED;
case CAIRO_IMAGE_IS_COLOR:
data_size = image->height * image->width * 3;
data = _cairo_malloc_abc (image->width, image->height, 3);
break;
 
case CAIRO_IMAGE_IS_GRAYSCALE:
data_size = image->height * image->width;
data = _cairo_malloc_ab (image->width, image->height);
break;
case CAIRO_IMAGE_IS_MONOCHROME:
data_size = (image->width + 7) / 8 * image->height;
data = _cairo_malloc_ab ((image->width+7) / 8, image->height);
break;
}
if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
i = 0;
for (y = 0; y < image->height; y++) {
pixel = (uint32_t *) (image->data + y * image->stride);
 
bit = 7;
for (x = 0; x < image->width; x++, pixel++) {
int r, g, b;
 
/* XXX: We're un-premultiplying alpha here. My reading of the PDF
* specification suggests that we should be able to avoid having
* to do this by filling in the SMask's Matte dictionary
* appropriately, but my attempts to do that so far have
* failed. */
if (image->format == CAIRO_FORMAT_ARGB32) {
uint8_t a;
a = (*pixel & 0xff000000) >> 24;
if (a == 0) {
r = g = b = 0;
} else {
r = (((*pixel & 0xff0000) >> 16) * 255 + a / 2) / a;
g = (((*pixel & 0x00ff00) >> 8) * 255 + a / 2) / a;
b = (((*pixel & 0x0000ff) >> 0) * 255 + a / 2) / a;
}
} else if (image->format == CAIRO_FORMAT_RGB24) {
r = (*pixel & 0x00ff0000) >> 16;
g = (*pixel & 0x0000ff00) >> 8;
b = (*pixel & 0x000000ff) >> 0;
} else {
r = g = b = 0;
}
 
switch (color) {
case CAIRO_IMAGE_IS_COLOR:
case CAIRO_IMAGE_UNKNOWN_COLOR:
data[i++] = r;
data[i++] = g;
data[i++] = b;
break;
 
case CAIRO_IMAGE_IS_GRAYSCALE:
data[i++] = r;
break;
 
case CAIRO_IMAGE_IS_MONOCHROME:
if (bit == 7)
data[i] = 0;
if (r != 0)
data[i] |= (1 << bit);
bit--;
if (bit < 0) {
bit = 7;
i++;
}
break;
}
}
if (bit != 7)
i++;
}
 
need_smask = FALSE;
if (image->format == CAIRO_FORMAT_ARGB32 ||
image->format == CAIRO_FORMAT_A8 ||
image->format == CAIRO_FORMAT_A1) {
status = _cairo_pdf_surface_emit_smask (surface, image, FALSE, interpolate, &smask);
if (unlikely (status))
goto CLEANUP_RGB;
 
if (smask.id)
need_smask = TRUE;
}
 
#define IMAGE_DICTIONARY " /Type /XObject\n" \
" /Subtype /Image\n" \
" /Width %d\n" \
" /Height %d\n" \
" /ColorSpace %s\n" \
" /Interpolate %s\n" \
" /BitsPerComponent %d\n"
 
if (need_smask)
status = _cairo_pdf_surface_open_stream (surface,
image_res,
TRUE,
IMAGE_DICTIONARY
" /SMask %d 0 R\n",
image->width, image->height,
color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
interpolate,
color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8,
smask.id);
else
status = _cairo_pdf_surface_open_stream (surface,
image_res,
TRUE,
IMAGE_DICTIONARY,
image->width, image->height,
color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
interpolate,
color == CAIRO_IMAGE_IS_MONOCHROME? 1 : 8);
if (unlikely (status))
goto CLEANUP_RGB;
 
#undef IMAGE_DICTIONARY
 
_cairo_output_stream_write (surface->output, data, data_size);
status = _cairo_pdf_surface_close_stream (surface);
 
CLEANUP_RGB:
free (data);
CLEANUP:
if (image != image_surf)
cairo_surface_destroy (&image->base);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_jpx_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_resource_t res)
{
cairo_int_status_t status;
const unsigned char *mime_data;
unsigned long mime_data_length;
cairo_image_info_t info;
 
if (surface->pdf_version < CAIRO_PDF_VERSION_1_5)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JP2,
&mime_data, &mime_data_length);
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_image_info_get_jpx_info (&info, mime_data, mime_data_length);
if (status)
return status;
 
status = _cairo_pdf_surface_open_stream (surface,
&res,
FALSE,
" /Type /XObject\n"
" /Subtype /Image\n"
" /Width %d\n"
" /Height %d\n"
" /Filter /JPXDecode\n",
info.width,
info.height);
if (status)
return status;
 
_cairo_output_stream_write (surface->output, mime_data, mime_data_length);
status = _cairo_pdf_surface_close_stream (surface);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface,
cairo_surface_t *source,
cairo_pdf_resource_t res)
{
cairo_int_status_t status;
const unsigned char *mime_data;
unsigned long mime_data_length;
cairo_image_info_t info;
const char *colorspace;
 
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
&mime_data, &mime_data_length);
if (unlikely (source->status))
return source->status;
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
if (unlikely (status))
return status;
 
switch (info.num_components) {
case 1:
colorspace = "/DeviceGray";
break;
case 3:
colorspace = "/DeviceRGB";
break;
case 4:
colorspace = "/DeviceCMYK";
break;
default:
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = _cairo_pdf_surface_open_stream (surface,
&res,
FALSE,
" /Type /XObject\n"
" /Subtype /Image\n"
" /Width %d\n"
" /Height %d\n"
" /ColorSpace %s\n"
" /BitsPerComponent %d\n"
" /Filter /DCTDecode\n",
info.width,
info.height,
colorspace,
info.bits_per_component);
if (unlikely (status))
return status;
 
_cairo_output_stream_write (surface->output, mime_data, mime_data_length);
status = _cairo_pdf_surface_close_stream (surface);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *source)
{
cairo_image_surface_t *image;
void *image_extra;
cairo_int_status_t status;
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
} else {
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, source->raster_pattern,
&image, &image_extra);
}
if (unlikely (status))
return status;
 
if (!source->hash_entry->stencil_mask) {
status = _cairo_pdf_surface_emit_jpx_image (surface, &image->base, source->hash_entry->surface_res);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto release_source;
 
status = _cairo_pdf_surface_emit_jpeg_image (surface, &image->base, source->hash_entry->surface_res);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto release_source;
}
 
status = _cairo_pdf_surface_emit_image (surface, image,
&source->hash_entry->surface_res,
source->hash_entry->interpolate,
source->hash_entry->stencil_mask);
 
release_source:
if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
_cairo_surface_release_source_image (source->surface, image, image_extra);
else
_cairo_pdf_surface_release_source_image_from_pattern (surface, source->raster_pattern,
image, image_extra);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *pdf_source)
{
double old_width, old_height;
cairo_paginated_mode_t old_paginated_mode;
cairo_surface_clipper_t old_clipper;
cairo_box_double_t bbox;
cairo_int_status_t status;
int alpha = 0;
cairo_surface_t *free_me = NULL;
cairo_surface_t *source;
const cairo_rectangle_int_t *extents;
int width;
int height;
cairo_bool_t is_subsurface;
 
assert (pdf_source->type == CAIRO_PATTERN_TYPE_SURFACE);
extents = &pdf_source->hash_entry->extents;
width = pdf_source->hash_entry->width;
height = pdf_source->hash_entry->height;
is_subsurface = FALSE;
source = pdf_source->surface;
if (_cairo_surface_is_snapshot (source)) {
free_me = source = _cairo_surface_snapshot_get_target (source);
} else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
 
source = sub->target;
extents = &sub->extents;
width = extents->width;
height = extents->height;
is_subsurface = TRUE;
}
 
old_width = surface->width;
old_height = surface->height;
old_paginated_mode = surface->paginated_mode;
old_clipper = surface->clipper;
_cairo_surface_clipper_init (&surface->clipper,
_cairo_pdf_surface_clipper_intersect_clip_path);
 
_cairo_pdf_surface_set_size_internal (surface, width, height);
 
/* Patterns are emitted after fallback images. The paginated mode
* needs to be set to _RENDER while the recording surface is replayed
* back to this surface.
*/
surface->paginated_mode = CAIRO_PAGINATED_MODE_RENDER;
_cairo_pdf_group_resources_clear (&surface->resources);
_get_bbox_from_extents (height, extents, &bbox);
status = _cairo_pdf_surface_open_content_stream (surface, &bbox, &pdf_source->hash_entry->surface_res, TRUE);
if (unlikely (status))
goto err;
 
if (source->content == CAIRO_CONTENT_COLOR) {
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (unlikely (status))
goto err;
 
_cairo_output_stream_printf (surface->output,
"q /a%d gs 0 0 0 rg 0 0 %f %f re f Q\n",
alpha,
surface->width,
surface->height);
}
 
status = _cairo_recording_surface_replay_region (source,
is_subsurface ? extents : NULL,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
if (unlikely (status))
goto err;
 
status = _cairo_pdf_surface_close_content_stream (surface);
 
_cairo_surface_clipper_reset (&surface->clipper);
surface->clipper = old_clipper;
_cairo_pdf_surface_set_size_internal (surface,
old_width,
old_height);
surface->paginated_mode = old_paginated_mode;
 
err:
cairo_surface_destroy (free_me);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_surface (cairo_pdf_surface_t *surface,
cairo_pdf_source_surface_t *src_surface)
{
if (src_surface->type == CAIRO_PATTERN_TYPE_SURFACE &&
src_surface->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
return _cairo_pdf_surface_emit_recording_surface (surface, src_surface);
 
return _cairo_pdf_surface_emit_image_surface (surface, src_surface);
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_surface_pattern (cairo_pdf_surface_t *surface,
cairo_pdf_pattern_t *pdf_pattern)
{
cairo_pattern_t *pattern = pdf_pattern->pattern;
cairo_int_status_t status;
cairo_pdf_resource_t pattern_resource = {0};
cairo_matrix_t cairo_p2d, pdf_p2d;
cairo_extend_t extend = cairo_pattern_get_extend (pattern);
double xstep, ystep;
cairo_rectangle_int_t pattern_extents;
int pattern_width = 0; /* squelch bogus compiler warning */
int pattern_height = 0; /* squelch bogus compiler warning */
double x_offset;
double y_offset;
char draw_surface[200];
cairo_box_double_t bbox;
 
if (pattern->extend == CAIRO_EXTEND_PAD) {
status = _cairo_pdf_surface_add_padded_image_surface (surface,
pattern,
&pdf_pattern->extents,
&pattern_resource,
&pattern_width,
&pattern_height,
&x_offset,
&y_offset);
pattern_extents.x = 0;
pattern_extents.y = 0;
pattern_extents.width = pattern_width;
pattern_extents.height = pattern_height;
} else {
status = _cairo_pdf_surface_add_source_surface (surface,
NULL,
pattern,
pattern->filter,
FALSE,
&pdf_pattern->extents,
&pattern_resource,
&pattern_width,
&pattern_height,
&x_offset,
&y_offset,
&pattern_extents);
}
if (unlikely (status))
return status;
 
switch (extend) {
case CAIRO_EXTEND_PAD:
case CAIRO_EXTEND_NONE:
{
/* In PS/PDF, (as far as I can tell), all patterns are
* repeating. So we support cairo's EXTEND_NONE semantics
* by setting the repeat step size to a size large enough
* to guarantee that no more than a single occurrence will
* be visible.
*
* First, map the surface extents into pattern space (since
* xstep and ystep are in pattern space). Then use an upper
* bound on the length of the diagonal of the pattern image
* and the surface as repeat size. This guarantees to never
* repeat visibly.
*/
double x1 = 0.0, y1 = 0.0;
double x2 = surface->width, y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->matrix,
&x1, &y1, &x2, &y2,
NULL);
 
/* Rather than computing precise bounds of the union, just
* add the surface extents unconditionally. We only
* required an answer that's large enough, we don't really
* care if it's not as tight as possible.*/
xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
pattern_width + pattern_height);
}
break;
case CAIRO_EXTEND_REPEAT:
xstep = pattern_width;
ystep = pattern_height;
break;
case CAIRO_EXTEND_REFLECT:
pattern_extents.x = 0;
pattern_extents.y = 0;
pattern_extents.width = pattern_width*2;
pattern_extents.height = pattern_height*2;
xstep = pattern_width*2;
ystep = pattern_height*2;
break;
/* All the rest (if any) should have been analyzed away, so this
* case should be unreachable. */
default:
ASSERT_NOT_REACHED;
xstep = 0;
ystep = 0;
}
 
/* At this point, (that is, within the surface backend interface),
* the pattern's matrix maps from cairo's device space to cairo's
* pattern space, (both with their origin at the upper-left, and
* cairo's pattern space of size width,height).
*
* Then, we must emit a PDF pattern object that maps from its own
* pattern space, (which has a size that we establish in the BBox
* dictionary entry), to the PDF page's *initial* space, (which
* does not benefit from the Y-axis flipping matrix that we emit
* on each page). So the PDF patterns matrix maps from a
* (width,height) pattern space to a device space with the origin
* in the lower-left corner.
*
* So to handle all of that, we start with an identity matrix for
* the PDF pattern to device matrix. We translate it up by the
* image height then flip it in the Y direction, (moving us from
* the PDF origin to cairo's origin). We then multiply in the
* inverse of the cairo pattern matrix, (since it maps from device
* to pattern, while we're setting up pattern to device). Finally,
* we translate back down by the image height and flip again to
* end up at the lower-left origin that PDF expects.
*
* Additionally, within the stream that paints the pattern itself,
* we are using a PDF image object that has a size of (1,1) so we
* have to scale it up by the image width and height to fill our
* pattern cell.
*/
cairo_p2d = pattern->matrix;
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &surface->cairo_to_pdf);
cairo_matrix_translate (&pdf_p2d, -x_offset, -y_offset);
cairo_matrix_translate (&pdf_p2d, 0.0, pattern_height);
cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
 
_get_bbox_from_extents (pattern_height, &pattern_extents, &bbox);
_cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
status = _cairo_pdf_surface_open_stream (surface,
&pdf_pattern->pattern_res,
FALSE,
" /PatternType 1\n"
" /BBox [ %f %f %f %f ]\n"
" /XStep %f\n"
" /YStep %f\n"
" /TilingType 1\n"
" /PaintType 1\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Resources << /XObject << /x%d %d 0 R >> >>\n",
bbox.p1.x, bbox.p1.y, bbox.p2.x, bbox.p2.y,
xstep, ystep,
pdf_p2d.xx, pdf_p2d.yx,
pdf_p2d.xy, pdf_p2d.yy,
pdf_p2d.x0, pdf_p2d.y0,
pattern_resource.id,
pattern_resource.id);
if (unlikely (status))
return status;
 
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
((cairo_surface_pattern_t *) pattern)->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
snprintf(draw_surface,
sizeof (draw_surface),
"/x%d Do\n",
pattern_resource.id);
} else {
snprintf(draw_surface,
sizeof (draw_surface),
"q %d 0 0 %d 0 0 cm /x%d Do Q",
pattern_width,
pattern_height,
pattern_resource.id);
}
 
if (extend == CAIRO_EXTEND_REFLECT) {
_cairo_output_stream_printf (surface->output,
"q 0 0 %d %d re W n %s Q\n"
"q -1 0 0 1 %d 0 cm 0 0 %d %d re W n %s Q\n"
"q 1 0 0 -1 0 %d cm 0 0 %d %d re W n %s Q\n"
"q -1 0 0 -1 %d %d cm 0 0 %d %d re W n %s Q\n",
pattern_width, pattern_height,
draw_surface,
pattern_width*2, pattern_width, pattern_height,
draw_surface,
pattern_height*2, pattern_width, pattern_height,
draw_surface,
pattern_width*2, pattern_height*2, pattern_width, pattern_height,
draw_surface);
} else {
_cairo_output_stream_printf (surface->output,
" %s \n",
draw_surface);
}
 
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
return status;
 
return _cairo_output_stream_get_status (surface->output);
}
 
typedef struct _cairo_pdf_color_stop {
double offset;
double color[4];
cairo_pdf_resource_t resource;
} cairo_pdf_color_stop_t;
 
static cairo_int_status_t
cairo_pdf_surface_emit_rgb_linear_function (cairo_pdf_surface_t *surface,
cairo_pdf_color_stop_t *stop1,
cairo_pdf_color_stop_t *stop2,
cairo_pdf_resource_t *function)
{
int num_elems, i;
cairo_pdf_rgb_linear_function_t elem;
cairo_pdf_resource_t res;
cairo_int_status_t status;
 
num_elems = _cairo_array_num_elements (&surface->rgb_linear_functions);
for (i = 0; i < num_elems; i++) {
_cairo_array_copy_element (&surface->rgb_linear_functions, i, &elem);
if (memcmp (&elem.color1[0], &stop1->color[0], sizeof (double)*3) != 0)
continue;
if (memcmp (&elem.color2[0], &stop2->color[0], sizeof (double)*3) != 0)
continue;
*function = elem.resource;
return CAIRO_STATUS_SUCCESS;
}
 
res = _cairo_pdf_surface_new_object (surface);
if (res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /FunctionType 2\n"
" /Domain [ 0 1 ]\n"
" /C0 [ %f %f %f ]\n"
" /C1 [ %f %f %f ]\n"
" /N 1\n"
">>\n"
"endobj\n",
res.id,
stop1->color[0],
stop1->color[1],
stop1->color[2],
stop2->color[0],
stop2->color[1],
stop2->color[2]);
 
elem.resource = res;
memcpy (&elem.color1[0], &stop1->color[0], sizeof (double)*3);
memcpy (&elem.color2[0], &stop2->color[0], sizeof (double)*3);
 
status = _cairo_array_append (&surface->rgb_linear_functions, &elem);
*function = res;
 
return status;
}
 
static cairo_int_status_t
cairo_pdf_surface_emit_alpha_linear_function (cairo_pdf_surface_t *surface,
cairo_pdf_color_stop_t *stop1,
cairo_pdf_color_stop_t *stop2,
cairo_pdf_resource_t *function)
{
int num_elems, i;
cairo_pdf_alpha_linear_function_t elem;
cairo_pdf_resource_t res;
cairo_int_status_t status;
 
num_elems = _cairo_array_num_elements (&surface->alpha_linear_functions);
for (i = 0; i < num_elems; i++) {
_cairo_array_copy_element (&surface->alpha_linear_functions, i, &elem);
if (elem.alpha1 != stop1->color[3])
continue;
if (elem.alpha2 != stop2->color[3])
continue;
*function = elem.resource;
return CAIRO_STATUS_SUCCESS;
}
 
res = _cairo_pdf_surface_new_object (surface);
if (res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /FunctionType 2\n"
" /Domain [ 0 1 ]\n"
" /C0 [ %f ]\n"
" /C1 [ %f ]\n"
" /N 1\n"
">>\n"
"endobj\n",
res.id,
stop1->color[3],
stop2->color[3]);
 
elem.resource = res;
elem.alpha1 = stop1->color[3];
elem.alpha2 = stop2->color[3];
 
status = _cairo_array_append (&surface->alpha_linear_functions, &elem);
*function = res;
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_stitched_colorgradient (cairo_pdf_surface_t *surface,
unsigned int n_stops,
cairo_pdf_color_stop_t *stops,
cairo_bool_t is_alpha,
cairo_pdf_resource_t *function)
{
cairo_pdf_resource_t res;
unsigned int i;
cairo_int_status_t status;
 
/* emit linear gradients between pairs of subsequent stops... */
for (i = 0; i < n_stops-1; i++) {
if (is_alpha) {
status = cairo_pdf_surface_emit_alpha_linear_function (surface,
&stops[i],
&stops[i+1],
&stops[i].resource);
if (unlikely (status))
return status;
} else {
status = cairo_pdf_surface_emit_rgb_linear_function (surface,
&stops[i],
&stops[i+1],
&stops[i].resource);
if (unlikely (status))
return status;
}
}
 
/* ... and stitch them together */
res = _cairo_pdf_surface_new_object (surface);
if (res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /FunctionType 3\n"
" /Domain [ %f %f ]\n",
res.id,
stops[0].offset,
stops[n_stops - 1].offset);
 
_cairo_output_stream_printf (surface->output,
" /Functions [ ");
for (i = 0; i < n_stops-1; i++)
_cairo_output_stream_printf (surface->output,
"%d 0 R ", stops[i].resource.id);
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
" /Bounds [ ");
for (i = 1; i < n_stops-1; i++)
_cairo_output_stream_printf (surface->output,
"%f ", stops[i].offset);
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
" /Encode [ ");
for (i = 1; i < n_stops; i++)
_cairo_output_stream_printf (surface->output,
"0 1 ");
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
 
*function = res;
 
return _cairo_output_stream_get_status (surface->output);
}
 
 
static void
calc_gradient_color (cairo_pdf_color_stop_t *new_stop,
cairo_pdf_color_stop_t *stop1,
cairo_pdf_color_stop_t *stop2)
{
int i;
double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
 
for (i = 0; i < 4; i++)
new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
}
 
#define COLOR_STOP_EPSILON 1e-6
 
static cairo_int_status_t
_cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t *surface,
cairo_gradient_pattern_t *pattern,
cairo_pdf_resource_t *color_function,
cairo_pdf_resource_t *alpha_function)
{
cairo_pdf_color_stop_t *allstops, *stops;
unsigned int n_stops;
unsigned int i;
cairo_bool_t emit_alpha = FALSE;
cairo_int_status_t status;
 
color_function->id = 0;
alpha_function->id = 0;
 
allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_pdf_color_stop_t));
if (unlikely (allstops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
stops = &allstops[1];
n_stops = pattern->n_stops;
 
for (i = 0; i < n_stops; i++) {
stops[i].color[0] = pattern->stops[i].color.red;
stops[i].color[1] = pattern->stops[i].color.green;
stops[i].color[2] = pattern->stops[i].color.blue;
stops[i].color[3] = pattern->stops[i].color.alpha;
if (!CAIRO_ALPHA_IS_OPAQUE (stops[i].color[3]))
emit_alpha = TRUE;
stops[i].offset = pattern->stops[i].offset;
}
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT) {
if (stops[0].offset > COLOR_STOP_EPSILON) {
if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
memcpy (allstops, stops, sizeof (cairo_pdf_color_stop_t));
else
calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
stops = allstops;
n_stops++;
}
stops[0].offset = 0.0;
 
if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
memcpy (&stops[n_stops],
&stops[n_stops - 1],
sizeof (cairo_pdf_color_stop_t));
} else {
calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
}
n_stops++;
}
stops[n_stops-1].offset = 1.0;
}
 
if (stops[0].offset == stops[n_stops - 1].offset) {
/*
* The first and the last stops have the same offset, but we
* don't want a function with an empty domain, because that
* would provoke underdefined behaviour from rasterisers.
* This can only happen with EXTEND_PAD, because EXTEND_NONE
* is optimised into a clear pattern in cairo-gstate, and
* REFLECT/REPEAT are always transformed to have the first
* stop at t=0 and the last stop at t=1. Thus we want a step
* function going from the first color to the last one.
*
* This can be accomplished by stitching three functions:
* - a constant first color function,
* - a step from the first color to the last color (with empty domain)
* - a constant last color function
*/
cairo_pdf_color_stop_t pad_stops[4];
 
assert (pattern->base.extend == CAIRO_EXTEND_PAD);
 
pad_stops[0] = pad_stops[1] = stops[0];
pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
 
pad_stops[0].offset = 0;
pad_stops[3].offset = 1;
 
status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
4,
pad_stops,
FALSE,
color_function);
if (unlikely (status))
goto BAIL;
 
if (emit_alpha) {
status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
4,
pad_stops,
TRUE,
alpha_function);
if (unlikely (status))
goto BAIL;
}
} else if (n_stops == 2) {
/* no need for stitched function */
status = cairo_pdf_surface_emit_rgb_linear_function (surface,
&stops[0],
&stops[n_stops - 1],
color_function);
if (unlikely (status))
goto BAIL;
 
if (emit_alpha) {
status = cairo_pdf_surface_emit_alpha_linear_function (surface,
&stops[0],
&stops[n_stops - 1],
alpha_function);
if (unlikely (status))
goto BAIL;
}
} else {
/* multiple stops: stitch. XXX possible optimization: regularly spaced
* stops do not require stitching. XXX */
status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
n_stops,
stops,
FALSE,
color_function);
if (unlikely (status))
goto BAIL;
 
if (emit_alpha) {
status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
n_stops,
stops,
TRUE,
alpha_function);
if (unlikely (status))
goto BAIL;
}
}
 
BAIL:
free (allstops);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_repeating_function (cairo_pdf_surface_t *surface,
cairo_gradient_pattern_t *pattern,
cairo_pdf_resource_t *function,
int begin,
int end)
{
cairo_pdf_resource_t res;
int i;
 
res = _cairo_pdf_surface_new_object (surface);
if (res.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /FunctionType 3\n"
" /Domain [ %d %d ]\n",
res.id,
begin,
end);
 
_cairo_output_stream_printf (surface->output,
" /Functions [ ");
for (i = begin; i < end; i++)
_cairo_output_stream_printf (surface->output,
"%d 0 R ", function->id);
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
" /Bounds [ ");
for (i = begin + 1; i < end; i++)
_cairo_output_stream_printf (surface->output,
"%d ", i);
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
" /Encode [ ");
for (i = begin; i < end; i++) {
if ((i % 2) && pattern->base.extend == CAIRO_EXTEND_REFLECT) {
_cairo_output_stream_printf (surface->output,
"1 0 ");
} else {
_cairo_output_stream_printf (surface->output,
"0 1 ");
}
}
_cairo_output_stream_printf (surface->output,
"]\n");
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
 
*function = res;
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t *surface,
cairo_pdf_pattern_t *pdf_pattern,
cairo_pdf_resource_t gstate_resource,
cairo_pdf_resource_t gradient_mask)
{
cairo_pdf_resource_t smask_resource;
cairo_int_status_t status;
char buf[100];
double x1, y1, x2, y2;
 
if (pdf_pattern->is_shading) {
snprintf(buf, sizeof(buf),
" /Shading\n"
" << /sh%d %d 0 R >>\n",
gradient_mask.id,
gradient_mask.id);
} else {
snprintf(buf, sizeof(buf),
" /Pattern\n"
" << /p%d %d 0 R >>\n",
gradient_mask.id,
gradient_mask.id);
}
 
if (pdf_pattern->is_shading) {
cairo_box_t box;
 
/* When emitting a shading operator we are in cairo pattern
* coordinates. _cairo_pdf_surface_paint_gradient has set the
* ctm to the pattern matrix (including the convertion from
* pdf to cairo coordinates) */
_cairo_box_from_rectangle (&box, &pdf_pattern->extents);
_cairo_box_to_doubles (&box, &x1, &y1, &x2, &y2);
_cairo_matrix_transform_bounding_box (&pdf_pattern->pattern->matrix, &x1, &y1, &x2, &y2, NULL);
} else {
cairo_box_double_t box;
 
/* When emitting a shading pattern we are in pdf page
* coordinates. The color and alpha shading patterns painted
* in the XObject below contain the cairo pattern to pdf page
* matrix in the /Matrix entry of the pattern. */
_get_bbox_from_extents (pdf_pattern->height, &pdf_pattern->extents, &box);
x1 = box.p1.x;
y1 = box.p1.y;
x2 = box.p2.x;
y2 = box.p2.y;
}
status = _cairo_pdf_surface_open_stream (surface,
NULL,
surface->compress_content,
" /Type /XObject\n"
" /Subtype /Form\n"
" /FormType 1\n"
" /BBox [ %f %f %f %f ]\n"
" /Resources\n"
" << /ExtGState\n"
" << /a0 << /ca 1 /CA 1 >>"
" >>\n"
"%s"
" >>\n"
" /Group\n"
" << /Type /Group\n"
" /S /Transparency\n"
" /I true\n"
" /CS /DeviceGray\n"
" >>\n",
x1,y1,x2,y2,
buf);
if (unlikely (status))
return status;
 
if (pdf_pattern->is_shading) {
_cairo_output_stream_printf (surface->output,
"/a0 gs /sh%d sh\n",
gradient_mask.id);
} else {
_cairo_output_stream_printf (surface->output,
"q\n"
"/a0 gs\n"
"/Pattern cs /p%d scn\n"
"0 0 %f %f re\n"
"f\n"
"Q\n",
gradient_mask.id,
surface->width,
surface->height);
}
 
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
return status;
 
smask_resource = _cairo_pdf_surface_new_object (surface);
if (smask_resource.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Mask\n"
" /S /Luminosity\n"
" /G %d 0 R\n"
">>\n"
"endobj\n",
smask_resource.id,
surface->pdf_stream.self.id);
 
/* Create GState which uses the transparency group as an SMask. */
_cairo_pdf_surface_update_object (surface, gstate_resource);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /ExtGState\n"
" /SMask %d 0 R\n"
" /ca 1\n"
" /CA 1\n"
" /AIS false\n"
">>\n"
"endobj\n",
gstate_resource.id,
smask_resource.id);
 
return _cairo_output_stream_get_status (surface->output);
}
 
static void
_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t *surface,
const cairo_pdf_pattern_t *pdf_pattern,
cairo_pdf_resource_t pattern_resource,
const cairo_matrix_t *pat_to_pdf,
const cairo_circle_double_t*start,
const cairo_circle_double_t*end,
const double *domain,
const char *colorspace,
cairo_pdf_resource_t color_function)
{
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n",
pattern_resource.id);
 
if (!pdf_pattern->is_shading) {
_cairo_output_stream_printf (surface->output,
"<< /Type /Pattern\n"
" /PatternType 2\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Shading\n",
pat_to_pdf->xx, pat_to_pdf->yx,
pat_to_pdf->xy, pat_to_pdf->yy,
pat_to_pdf->x0, pat_to_pdf->y0);
}
 
if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
_cairo_output_stream_printf (surface->output,
" << /ShadingType 2\n"
" /ColorSpace %s\n"
" /Coords [ %f %f %f %f ]\n",
colorspace,
start->center.x, start->center.y,
end->center.x, end->center.y);
} else {
_cairo_output_stream_printf (surface->output,
" << /ShadingType 3\n"
" /ColorSpace %s\n"
" /Coords [ %f %f %f %f %f %f ]\n",
colorspace,
start->center.x, start->center.y,
MAX (start->radius, 0),
end->center.x, end->center.y,
MAX (end->radius, 0));
}
 
_cairo_output_stream_printf (surface->output,
" /Domain [ %f %f ]\n",
domain[0], domain[1]);
 
if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) {
_cairo_output_stream_printf (surface->output,
" /Extend [ true true ]\n");
} else {
_cairo_output_stream_printf (surface->output,
" /Extend [ false false ]\n");
}
 
_cairo_output_stream_printf (surface->output,
" /Function %d 0 R\n"
" >>\n",
color_function.id);
 
if (!pdf_pattern->is_shading) {
_cairo_output_stream_printf (surface->output,
">>\n");
}
 
_cairo_output_stream_printf (surface->output,
"endobj\n");
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t *surface,
cairo_pdf_pattern_t *pdf_pattern)
{
cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern;
cairo_pdf_resource_t color_function, alpha_function;
cairo_matrix_t pat_to_pdf;
cairo_circle_double_t start, end;
double domain[2];
cairo_int_status_t status;
 
assert (pattern->n_stops != 0);
 
status = _cairo_pdf_surface_emit_pattern_stops (surface,
pattern,
&color_function,
&alpha_function);
if (unlikely (status))
return status;
 
pat_to_pdf = pattern->base.matrix;
status = cairo_matrix_invert (&pat_to_pdf);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_INT_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT)
{
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
double x_scale, y_scale, tolerance;
 
/* TODO: use tighter extents */
bounds_x1 = 0;
bounds_y1 = 0;
bounds_x2 = surface->width;
bounds_y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
&bounds_x1, &bounds_y1,
&bounds_x2, &bounds_y2,
NULL);
 
x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
 
tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
tolerance *= MIN (x_scale, y_scale);
 
_cairo_gradient_pattern_box_to_parameter (pattern,
bounds_x1, bounds_y1,
bounds_x2, bounds_y2,
tolerance, domain);
} else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
/*
* If the first and the last stop offset are the same, then
* the color function is a step function.
* _cairo_ps_surface_emit_pattern_stops emits it as a stitched
* function no matter how many stops the pattern has. The
* domain of the stitched function will be [0 1] in this case.
*
* This is done to avoid emitting degenerate gradients for
* EXTEND_PAD patterns having a step color function.
*/
domain[0] = 0.0;
domain[1] = 1.0;
 
assert (pattern->base.extend == CAIRO_EXTEND_PAD);
} else {
domain[0] = pattern->stops[0].offset;
domain[1] = pattern->stops[pattern->n_stops - 1].offset;
}
 
/* PDF requires the first and last stop to be the same as the
* extreme coordinates. For repeating patterns this moves the
* extreme coordinates out to the begin/end of the repeating
* function. For non repeating patterns this may move the extreme
* coordinates in if there are not stops at offset 0 and 1. */
_cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
_cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT)
{
int repeat_begin, repeat_end;
 
repeat_begin = floor (domain[0]);
repeat_end = ceil (domain[1]);
 
status = _cairo_pdf_surface_emit_repeating_function (surface,
pattern,
&color_function,
repeat_begin,
repeat_end);
if (unlikely (status))
return status;
 
if (alpha_function.id != 0) {
status = _cairo_pdf_surface_emit_repeating_function (surface,
pattern,
&alpha_function,
repeat_begin,
repeat_end);
if (unlikely (status))
return status;
}
} else if (pattern->n_stops <= 2) {
/* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
* Type 2 function is used by itself without a stitching
* function. Type 2 functions always have the domain [0 1] */
domain[0] = 0.0;
domain[1] = 1.0;
}
 
_cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
_cairo_pdf_surface_output_gradient (surface, pdf_pattern,
pdf_pattern->pattern_res,
&pat_to_pdf, &start, &end, domain,
"/DeviceRGB", color_function);
 
if (alpha_function.id != 0) {
cairo_pdf_resource_t mask_resource;
 
assert (pdf_pattern->gstate_res.id != 0);
 
/* Create pattern for SMask. */
mask_resource = _cairo_pdf_surface_new_object (surface);
if (mask_resource.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_pdf_surface_output_gradient (surface, pdf_pattern,
mask_resource,
&pat_to_pdf, &start, &end, domain,
"/DeviceGray", alpha_function);
 
status = cairo_pdf_surface_emit_transparency_group (surface,
pdf_pattern,
pdf_pattern->gstate_res,
mask_resource);
if (unlikely (status))
return status;
}
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_mesh_pattern (cairo_pdf_surface_t *surface,
cairo_pdf_pattern_t *pdf_pattern)
{
cairo_matrix_t pat_to_pdf;
cairo_int_status_t status;
cairo_pattern_t *pattern = pdf_pattern->pattern;
cairo_pdf_shading_t shading;
int i;
cairo_pdf_resource_t res;
 
pat_to_pdf = pattern->matrix;
status = cairo_matrix_invert (&pat_to_pdf);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
 
status = _cairo_pdf_shading_init_color (&shading, (cairo_mesh_pattern_t *) pattern);
if (unlikely (status))
return status;
 
res = _cairo_pdf_surface_new_object (surface);
if (unlikely (res.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /ShadingType %d\n"
" /ColorSpace /DeviceRGB\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
res.id,
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
 
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
 
_cairo_output_stream_printf (surface->output,
"]\n"
" /Length %ld\n"
">>\n"
"stream\n",
shading.data_length);
 
_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
 
_cairo_output_stream_printf (surface->output,
"\nendstream\n"
"endobj\n");
 
_cairo_pdf_shading_fini (&shading);
 
_cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Pattern\n"
" /PatternType 2\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Shading %d 0 R\n"
">>\n"
"endobj\n",
pdf_pattern->pattern_res.id,
pat_to_pdf.xx, pat_to_pdf.yx,
pat_to_pdf.xy, pat_to_pdf.yy,
pat_to_pdf.x0, pat_to_pdf.y0,
res.id);
 
if (pdf_pattern->gstate_res.id != 0) {
cairo_pdf_resource_t mask_resource;
 
/* Create pattern for SMask. */
res = _cairo_pdf_surface_new_object (surface);
if (unlikely (res.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_pdf_shading_init_alpha (&shading, (cairo_mesh_pattern_t *) pattern);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /ShadingType %d\n"
" /ColorSpace /DeviceGray\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
res.id,
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
 
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->output, "%f ", shading.decode_array[i]);
 
_cairo_output_stream_printf (surface->output,
"]\n"
" /Length %ld\n"
">>\n"
"stream\n",
shading.data_length);
 
_cairo_output_stream_write (surface->output, shading.data, shading.data_length);
 
_cairo_output_stream_printf (surface->output,
"\nendstream\n"
"endobj\n");
_cairo_pdf_shading_fini (&shading);
 
mask_resource = _cairo_pdf_surface_new_object (surface);
if (unlikely (mask_resource.id == 0))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Pattern\n"
" /PatternType 2\n"
" /Matrix [ %f %f %f %f %f %f ]\n"
" /Shading %d 0 R\n"
">>\n"
"endobj\n",
mask_resource.id,
pat_to_pdf.xx, pat_to_pdf.yx,
pat_to_pdf.xy, pat_to_pdf.yy,
pat_to_pdf.x0, pat_to_pdf.y0,
res.id);
 
status = cairo_pdf_surface_emit_transparency_group (surface,
pdf_pattern,
pdf_pattern->gstate_res,
mask_resource);
if (unlikely (status))
return status;
}
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern_t *pdf_pattern)
{
double old_width, old_height;
cairo_int_status_t status;
 
old_width = surface->width;
old_height = surface->height;
_cairo_pdf_surface_set_size_internal (surface,
pdf_pattern->width,
pdf_pattern->height);
 
switch (pdf_pattern->pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
ASSERT_NOT_REACHED;
status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
break;
 
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
status = _cairo_pdf_surface_emit_surface_pattern (surface, pdf_pattern);
break;
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
break;
 
case CAIRO_PATTERN_TYPE_MESH:
status = _cairo_pdf_surface_emit_mesh_pattern (surface, pdf_pattern);
break;
 
default:
ASSERT_NOT_REACHED;
status = _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
break;
}
 
_cairo_pdf_surface_set_size_internal (surface,
old_width,
old_height);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *source,
const cairo_rectangle_int_t *extents,
cairo_bool_t stencil_mask)
{
cairo_pdf_resource_t surface_res;
int width, height;
cairo_matrix_t cairo_p2d, pdf_p2d;
cairo_int_status_t status;
int alpha;
cairo_rectangle_int_t extents2;
double x_offset;
double y_offset;
 
if (source->extend == CAIRO_EXTEND_PAD &&
!(source->type == CAIRO_PATTERN_TYPE_SURFACE &&
((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING))
{
status = _cairo_pdf_surface_add_padded_image_surface (surface,
source,
extents,
&surface_res,
&width,
&height,
&x_offset,
&y_offset);
} else {
status = _cairo_pdf_surface_add_source_surface (surface,
NULL,
source,
source->filter,
stencil_mask,
extents,
&surface_res,
&width,
&height,
&x_offset,
&y_offset,
&extents2);
}
if (unlikely (status))
return status;
 
cairo_p2d = source->matrix;
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
pdf_p2d = surface->cairo_to_pdf;
cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
cairo_matrix_translate (&pdf_p2d, x_offset, y_offset);
cairo_matrix_translate (&pdf_p2d, 0.0, height);
cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
if (!(source->type == CAIRO_PATTERN_TYPE_SURFACE &&
((cairo_surface_pattern_t *)source)->surface->type == CAIRO_SURFACE_TYPE_RECORDING))
{
cairo_matrix_scale (&pdf_p2d, width, height);
}
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
if (! _cairo_matrix_is_identity (&pdf_p2d)) {
_cairo_output_stream_printf (surface->output,
"%f %f %f %f %f %f cm\n",
pdf_p2d.xx, pdf_p2d.yx,
pdf_p2d.xy, pdf_p2d.yy,
pdf_p2d.x0, pdf_p2d.y0);
}
 
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (unlikely (status))
return status;
 
if (stencil_mask) {
_cairo_output_stream_printf (surface->output,
"/x%d Do\n",
surface_res.id);
} else {
_cairo_output_stream_printf (surface->output,
"/a%d gs /x%d Do\n",
alpha,
surface_res.id);
}
 
return _cairo_pdf_surface_add_xobject (surface, surface_res);
}
 
static cairo_int_status_t
_cairo_pdf_surface_paint_gradient (cairo_pdf_surface_t *surface,
const cairo_pattern_t *source,
const cairo_rectangle_int_t *extents)
{
cairo_pdf_resource_t shading_res, gstate_res;
cairo_matrix_t pat_to_pdf;
cairo_int_status_t status;
int alpha;
 
status = _cairo_pdf_surface_add_pdf_shading (surface, source,
extents,
&shading_res, &gstate_res);
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_INT_STATUS_SUCCESS;
if (unlikely (status))
return status;
 
pat_to_pdf = source->matrix;
status = cairo_matrix_invert (&pat_to_pdf);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_INT_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
if (! _cairo_matrix_is_identity (&pat_to_pdf)) {
_cairo_output_stream_printf (surface->output,
"%f %f %f %f %f %f cm\n",
pat_to_pdf.xx, pat_to_pdf.yx,
pat_to_pdf.xy, pat_to_pdf.yy,
pat_to_pdf.x0, pat_to_pdf.y0);
}
 
status = _cairo_pdf_surface_add_shading (surface, shading_res);
if (unlikely (status))
return status;
 
if (gstate_res.id != 0) {
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/s%d gs /sh%d sh\n",
gstate_res.id,
shading_res.id);
} else {
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/a%d gs /sh%d sh\n",
alpha,
shading_res.id);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_paint_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *source,
const cairo_rectangle_int_t *extents,
cairo_bool_t mask)
{
switch (source->type) {
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_pdf_surface_paint_surface_pattern (surface,
source,
extents,
mask);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return _cairo_pdf_surface_paint_gradient (surface,
source,
extents);
 
case CAIRO_PATTERN_TYPE_SOLID:
default:
ASSERT_NOT_REACHED;
return CAIRO_STATUS_SUCCESS;
}
}
 
static cairo_bool_t
_can_paint_pattern (const cairo_pattern_t *pattern)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return FALSE;
 
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return (pattern->extend == CAIRO_EXTEND_NONE ||
pattern->extend == CAIRO_EXTEND_PAD);
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return TRUE;
 
case CAIRO_PATTERN_TYPE_MESH:
return FALSE;
 
default:
ASSERT_NOT_REACHED;
return FALSE;
}
}
 
static cairo_int_status_t
_cairo_pdf_surface_select_operator (cairo_pdf_surface_t *surface,
cairo_operator_t op)
{
cairo_int_status_t status;
 
if (op == surface->current_operator)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/b%d gs\n", op);
surface->current_operator = op;
_cairo_pdf_surface_add_operator (surface, op);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_select_pattern (cairo_pdf_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_pdf_resource_t pattern_res,
cairo_bool_t is_stroke)
{
cairo_int_status_t status;
int alpha;
const cairo_color_t *solid_color = NULL;
 
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
const cairo_solid_pattern_t *solid = (const cairo_solid_pattern_t *) pattern;
 
solid_color = &solid->color;
}
 
if (solid_color != NULL) {
if (surface->current_pattern_is_solid_color == FALSE ||
surface->current_color_red != solid_color->red ||
surface->current_color_green != solid_color->green ||
surface->current_color_blue != solid_color->blue ||
surface->current_color_is_stroke != is_stroke)
{
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"%f %f %f ",
solid_color->red,
solid_color->green,
solid_color->blue);
 
if (is_stroke)
_cairo_output_stream_printf (surface->output, "RG ");
else
_cairo_output_stream_printf (surface->output, "rg ");
 
surface->current_color_red = solid_color->red;
surface->current_color_green = solid_color->green;
surface->current_color_blue = solid_color->blue;
surface->current_color_is_stroke = is_stroke;
}
 
if (surface->current_pattern_is_solid_color == FALSE ||
surface->current_color_alpha != solid_color->alpha)
{
status = _cairo_pdf_surface_add_alpha (surface, solid_color->alpha, &alpha);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/a%d gs\n",
alpha);
surface->current_color_alpha = solid_color->alpha;
}
 
surface->current_pattern_is_solid_color = TRUE;
} else {
status = _cairo_pdf_surface_add_alpha (surface, 1.0, &alpha);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_add_pattern (surface, pattern_res);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
/* fill-stroke calls select_pattern twice. Don't save if the
* gstate is already saved. */
if (!surface->select_pattern_gstate_saved)
_cairo_output_stream_printf (surface->output, "q ");
 
if (is_stroke) {
_cairo_output_stream_printf (surface->output,
"/Pattern CS /p%d SCN ",
pattern_res.id);
} else {
_cairo_output_stream_printf (surface->output,
"/Pattern cs /p%d scn ",
pattern_res.id);
}
_cairo_output_stream_printf (surface->output,
"/a%d gs\n",
alpha);
surface->select_pattern_gstate_saved = TRUE;
surface->current_pattern_is_solid_color = FALSE;
}
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_unselect_pattern (cairo_pdf_surface_t *surface)
{
cairo_int_status_t status;
 
if (surface->select_pattern_gstate_saved) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "Q\n");
_cairo_pdf_operators_reset (&surface->pdf_operators);
surface->current_pattern_is_solid_color = FALSE;
}
surface->select_pattern_gstate_saved = FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_show_page (void *abstract_surface)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
status = _cairo_pdf_surface_close_content_stream (surface);
if (unlikely (status))
return status;
 
_cairo_surface_clipper_reset (&surface->clipper);
 
status = _cairo_pdf_surface_write_page (surface);
if (unlikely (status))
return status;
 
_cairo_pdf_surface_clear (surface);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_pdf_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_pdf_surface_t *surface = abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
 
/* XXX: The conversion to integers here is pretty bogus, (not to
* mention the arbitrary limitation of width to a short(!). We
* may need to come up with a better interface for get_size.
*/
rectangle->width = ceil (surface->width);
rectangle->height = ceil (surface->height);
 
return TRUE;
}
 
static void
_cairo_pdf_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
_cairo_font_options_init_default (options);
 
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
}
 
static cairo_pdf_resource_t
_cairo_pdf_surface_write_info (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t info;
 
info = _cairo_pdf_surface_new_object (surface);
if (info.id == 0)
return info;
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Creator (cairo %s (http://cairographics.org))\n"
" /Producer (cairo %s (http://cairographics.org))\n"
">>\n"
"endobj\n",
info.id,
cairo_version_string (),
cairo_version_string ());
 
return info;
}
 
static void
_cairo_pdf_surface_write_pages (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t page;
int num_pages, i;
 
_cairo_pdf_surface_update_object (surface, surface->pages_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Pages\n"
" /Kids [ ",
surface->pages_resource.id);
 
num_pages = _cairo_array_num_elements (&surface->pages);
for (i = 0; i < num_pages; i++) {
_cairo_array_copy_element (&surface->pages, i, &page);
_cairo_output_stream_printf (surface->output, "%d 0 R ", page.id);
}
 
_cairo_output_stream_printf (surface->output, "]\n");
_cairo_output_stream_printf (surface->output, " /Count %d\n", num_pages);
 
 
/* TODO: Figure out which other defaults to be inherited by /Page
* objects. */
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
}
 
static cairo_int_status_t
_utf8_to_pdf_string (const char *utf8, char **str_out)
{
int i;
int len;
cairo_bool_t ascii;
char *str;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
ascii = TRUE;
len = strlen (utf8);
for (i = 0; i < len; i++) {
unsigned c = utf8[i];
if (c < 32 || c > 126 || c == '(' || c == ')' || c == '\\') {
ascii = FALSE;
break;
}
}
 
if (ascii) {
str = malloc (len + 3);
if (str == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
str[0] = '(';
for (i = 0; i < len; i++)
str[i+1] = utf8[i];
str[i+1] = ')';
str[i+2] = 0;
} else {
uint16_t *utf16 = NULL;
int utf16_len = 0;
 
status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
if (unlikely (status))
return status;
 
str = malloc (utf16_len*4 + 7);
if (str == NULL) {
free (utf16);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
strcpy (str, "<FEFF");
for (i = 0; i < utf16_len; i++)
snprintf (str + 4*i + 5, 5, "%04X", utf16[i]);
 
strcat (str, ">");
free (utf16);
}
*str_out = str;
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_unicode_for_glyph (cairo_pdf_surface_t *surface,
const char *utf8)
{
uint16_t *utf16 = NULL;
int utf16_len = 0;
cairo_int_status_t status;
int i;
 
if (utf8 && *utf8) {
status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len);
if (unlikely (status))
return status;
}
 
_cairo_output_stream_printf (surface->output, "<");
if (utf16 == NULL || utf16_len == 0) {
/* According to the "ToUnicode Mapping File Tutorial"
* http://www.adobe.com/devnet/acrobat/pdfs/5411.ToUnicode.pdf
*
* Glyphs that do not map to a Unicode code point must be
* mapped to 0xfffd "REPLACEMENT CHARACTER".
*/
_cairo_output_stream_printf (surface->output,
"fffd");
} else {
for (i = 0; i < utf16_len; i++)
_cairo_output_stream_printf (surface->output,
"%04x", (int) (utf16[i]));
}
_cairo_output_stream_printf (surface->output, ">");
 
free (utf16);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Bob Jenkins hash
*
* Public domain code from:
* http://burtleburtle.net/bob/hash/doobs.html
*/
 
#define HASH_MIX(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
 
static uint32_t
_hash_data (const unsigned char *data, int length, uint32_t initval)
{
uint32_t a, b, c, len;
 
len = length;
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
c = initval; /* the previous hash value */
 
while (len >= 12) {
a += (data[0] + ((uint32_t)data[1]<<8) + ((uint32_t)data[2]<<16) + ((uint32_t)data[3]<<24));
b += (data[4] + ((uint32_t)data[5]<<8) + ((uint32_t)data[6]<<16) + ((uint32_t)data[7]<<24));
c += (data[8] + ((uint32_t)data[9]<<8) + ((uint32_t)data[10]<<16)+ ((uint32_t)data[11]<<24));
HASH_MIX (a,b,c);
data += 12;
len -= 12;
}
 
c += length;
switch(len) {
case 11: c+= ((uint32_t) data[10] << 24);
case 10: c+= ((uint32_t) data[9] << 16);
case 9 : c+= ((uint32_t) data[8] << 8);
case 8 : b+= ((uint32_t) data[7] << 24);
case 7 : b+= ((uint32_t) data[6] << 16);
case 6 : b+= ((uint32_t) data[5] << 8);
case 5 : b+= data[4];
case 4 : a+= ((uint32_t) data[3] << 24);
case 3 : a+= ((uint32_t) data[2] << 16);
case 2 : a+= ((uint32_t) data[1] << 8);
case 1 : a+= data[0];
}
HASH_MIX (a,b,c);
 
return c;
}
 
static void
_create_font_subset_tag (cairo_scaled_font_subset_t *font_subset,
const char *font_name,
char *tag)
{
uint32_t hash;
int i;
long numerator;
ldiv_t d;
 
hash = _hash_data ((unsigned char *) font_name, strlen(font_name), 0);
hash = _hash_data ((unsigned char *) (font_subset->glyphs),
font_subset->num_glyphs * sizeof(unsigned long), hash);
 
numerator = abs (hash);
for (i = 0; i < 6; i++) {
d = ldiv (numerator, 26);
numerator = d.quot;
tag[i] = 'A' + d.rem;
}
tag[i] = 0;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_to_unicode_stream (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset,
cairo_pdf_resource_t *stream)
{
unsigned int i, num_bfchar;
cairo_int_status_t status;
 
stream->id = 0;
 
status = _cairo_pdf_surface_open_stream (surface,
NULL,
surface->compress_content,
NULL);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n"
"/CIDSystemInfo\n"
"<< /Registry (Adobe)\n"
" /Ordering (UCS)\n"
" /Supplement 0\n"
">> def\n"
"/CMapName /Adobe-Identity-UCS def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n");
 
if (font_subset->is_composite && !font_subset->is_latin) {
_cairo_output_stream_printf (surface->output,
"<0000> <ffff>\n");
} else {
_cairo_output_stream_printf (surface->output,
"<00> <ff>\n");
}
 
_cairo_output_stream_printf (surface->output,
"endcodespacerange\n");
 
if (font_subset->is_scaled) {
/* Type 3 fonts include glyph 0 in the subset */
num_bfchar = font_subset->num_glyphs;
 
/* The CMap specification has a limit of 100 characters per beginbfchar operator */
_cairo_output_stream_printf (surface->output,
"%d beginbfchar\n",
num_bfchar > 100 ? 100 : num_bfchar);
 
for (i = 0; i < num_bfchar; i++) {
if (i != 0 && i % 100 == 0) {
_cairo_output_stream_printf (surface->output,
"endbfchar\n"
"%d beginbfchar\n",
num_bfchar - i > 100 ? 100 : num_bfchar - i);
}
_cairo_output_stream_printf (surface->output, "<%02x> ", i);
status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
font_subset->utf8[i]);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"\n");
}
} else {
/* Other fonts reserve glyph 0 for .notdef. Omit glyph 0 from the /ToUnicode map */
num_bfchar = font_subset->num_glyphs - 1;
 
/* The CMap specification has a limit of 100 characters per beginbfchar operator */
_cairo_output_stream_printf (surface->output,
"%d beginbfchar\n",
num_bfchar > 100 ? 100 : num_bfchar);
 
for (i = 0; i < num_bfchar; i++) {
if (i != 0 && i % 100 == 0) {
_cairo_output_stream_printf (surface->output,
"endbfchar\n"
"%d beginbfchar\n",
num_bfchar - i > 100 ? 100 : num_bfchar - i);
}
if (font_subset->is_latin)
_cairo_output_stream_printf (surface->output, "<%02x> ", font_subset->to_latin_char[i + 1]);
else if (font_subset->is_composite)
_cairo_output_stream_printf (surface->output, "<%04x> ", i + 1);
else
_cairo_output_stream_printf (surface->output, "<%02x> ", i + 1);
 
status = _cairo_pdf_surface_emit_unicode_for_glyph (surface,
font_subset->utf8[i + 1]);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"\n");
}
}
 
_cairo_output_stream_printf (surface->output,
"endbfchar\n");
 
_cairo_output_stream_printf (surface->output,
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end\n");
 
*stream = surface->pdf_stream.self;
return _cairo_pdf_surface_close_stream (surface);
}
 
#define PDF_UNITS_PER_EM 1000
 
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_font (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset,
cairo_cff_subset_t *subset)
{
cairo_pdf_resource_t stream, descriptor, cidfont_dict;
cairo_pdf_resource_t subset_resource, to_unicode_stream;
cairo_pdf_font_t font;
unsigned int i, last_glyph;
cairo_int_status_t status;
char tag[10];
 
_create_font_subset_tag (font_subset, subset->ps_name, tag);
 
subset_resource = _cairo_pdf_surface_get_font_resource (surface,
font_subset->font_id,
font_subset->subset_id);
if (subset_resource.id == 0)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_pdf_surface_open_stream (surface,
NULL,
TRUE,
font_subset->is_latin ?
" /Subtype /Type1C\n" :
" /Subtype /CIDFontType0C\n");
if (unlikely (status))
return status;
 
stream = surface->pdf_stream.self;
_cairo_output_stream_write (surface->output,
subset->data, subset->data_length);
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
font_subset,
&to_unicode_stream);
if (_cairo_int_status_is_error (status))
return status;
 
descriptor = _cairo_pdf_surface_new_object (surface);
if (descriptor.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /FontDescriptor\n"
" /FontName /%s+%s\n",
descriptor.id,
tag,
subset->ps_name);
 
if (subset->family_name_utf8) {
char *pdf_str;
 
status = _utf8_to_pdf_string (subset->family_name_utf8, &pdf_str);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
" /FontFamily %s\n",
pdf_str);
free (pdf_str);
}
 
_cairo_output_stream_printf (surface->output,
" /Flags 4\n"
" /FontBBox [ %ld %ld %ld %ld ]\n"
" /ItalicAngle 0\n"
" /Ascent %ld\n"
" /Descent %ld\n"
" /CapHeight %ld\n"
" /StemV 80\n"
" /StemH 80\n"
" /FontFile3 %u 0 R\n"
">>\n"
"endobj\n",
(long)(subset->x_min*PDF_UNITS_PER_EM),
(long)(subset->y_min*PDF_UNITS_PER_EM),
(long)(subset->x_max*PDF_UNITS_PER_EM),
(long)(subset->y_max*PDF_UNITS_PER_EM),
(long)(subset->ascent*PDF_UNITS_PER_EM),
(long)(subset->descent*PDF_UNITS_PER_EM),
(long)(subset->y_max*PDF_UNITS_PER_EM),
stream.id);
 
if (font_subset->is_latin) {
/* find last glyph used */
for (i = 255; i >= 32; i--)
if (font_subset->latin_to_subset_glyph_index[i] > 0)
break;
 
last_glyph = i;
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /Type1\n"
" /BaseFont /%s+%s\n"
" /FirstChar 32\n"
" /LastChar %d\n"
" /FontDescriptor %d 0 R\n"
" /Encoding /WinAnsiEncoding\n"
" /Widths [",
subset_resource.id,
tag,
subset->ps_name,
last_glyph,
descriptor.id);
 
for (i = 32; i < last_glyph + 1; i++) {
int glyph = font_subset->latin_to_subset_glyph_index[i];
if (glyph > 0) {
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
} else {
_cairo_output_stream_printf (surface->output, " 0");
}
}
 
_cairo_output_stream_printf (surface->output,
" ]\n");
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
} else {
cidfont_dict = _cairo_pdf_surface_new_object (surface);
if (cidfont_dict.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /CIDFontType0\n"
" /BaseFont /%s+%s\n"
" /CIDSystemInfo\n"
" << /Registry (Adobe)\n"
" /Ordering (Identity)\n"
" /Supplement 0\n"
" >>\n"
" /FontDescriptor %d 0 R\n"
" /W [0 [",
cidfont_dict.id,
tag,
subset->ps_name,
descriptor.id);
 
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset->widths[i]*PDF_UNITS_PER_EM));
 
_cairo_output_stream_printf (surface->output,
" ]]\n"
">>\n"
"endobj\n");
 
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /Type0\n"
" /BaseFont /%s+%s\n"
" /Encoding /Identity-H\n"
" /DescendantFonts [ %d 0 R]\n",
subset_resource.id,
tag,
subset->ps_name,
cidfont_dict.id);
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
}
 
font.font_id = font_subset->font_id;
font.subset_id = font_subset->subset_id;
font.subset_resource = subset_resource;
status = _cairo_array_append (&surface->fonts, &font);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_font_subset (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_int_status_t status;
cairo_cff_subset_t subset;
char name[64];
 
snprintf (name, sizeof name, "CairoFont-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_cff_subset_init (&subset, name, font_subset);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
 
_cairo_cff_subset_fini (&subset);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_cff_fallback_font (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_int_status_t status;
cairo_cff_subset_t subset;
char name[64];
 
/* CFF fallback subsetting does not work with 8-bit glyphs unless
* they are a latin subset */
if (!font_subset->is_composite && !font_subset->is_latin)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
snprintf (name, sizeof name, "CairoFont-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_cff_fallback_init (&subset, name, font_subset);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_cff_font (surface, font_subset, &subset);
 
_cairo_cff_fallback_fini (&subset);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_font (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset,
cairo_type1_subset_t *subset)
{
cairo_pdf_resource_t stream, descriptor, subset_resource, to_unicode_stream;
cairo_pdf_font_t font;
cairo_int_status_t status;
unsigned long length;
unsigned int i, last_glyph;
char tag[10];
 
_create_font_subset_tag (font_subset, subset->base_font, tag);
 
subset_resource = _cairo_pdf_surface_get_font_resource (surface,
font_subset->font_id,
font_subset->subset_id);
if (subset_resource.id == 0)
return CAIRO_STATUS_SUCCESS;
 
length = subset->header_length + subset->data_length + subset->trailer_length;
status = _cairo_pdf_surface_open_stream (surface,
NULL,
TRUE,
" /Length1 %lu\n"
" /Length2 %lu\n"
" /Length3 %lu\n",
subset->header_length,
subset->data_length,
subset->trailer_length);
if (unlikely (status))
return status;
 
stream = surface->pdf_stream.self;
_cairo_output_stream_write (surface->output, subset->data, length);
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
font_subset,
&to_unicode_stream);
if (_cairo_int_status_is_error (status))
return status;
 
last_glyph = font_subset->num_glyphs - 1;
if (font_subset->is_latin) {
/* find last glyph used */
for (i = 255; i >= 32; i--)
if (font_subset->latin_to_subset_glyph_index[i] > 0)
break;
 
last_glyph = i;
}
 
descriptor = _cairo_pdf_surface_new_object (surface);
if (descriptor.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /FontDescriptor\n"
" /FontName /%s+%s\n"
" /Flags 4\n"
" /FontBBox [ %ld %ld %ld %ld ]\n"
" /ItalicAngle 0\n"
" /Ascent %ld\n"
" /Descent %ld\n"
" /CapHeight %ld\n"
" /StemV 80\n"
" /StemH 80\n"
" /FontFile %u 0 R\n"
">>\n"
"endobj\n",
descriptor.id,
tag,
subset->base_font,
(long)(subset->x_min*PDF_UNITS_PER_EM),
(long)(subset->y_min*PDF_UNITS_PER_EM),
(long)(subset->x_max*PDF_UNITS_PER_EM),
(long)(subset->y_max*PDF_UNITS_PER_EM),
(long)(subset->ascent*PDF_UNITS_PER_EM),
(long)(subset->descent*PDF_UNITS_PER_EM),
(long)(subset->y_max*PDF_UNITS_PER_EM),
stream.id);
 
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /Type1\n"
" /BaseFont /%s+%s\n"
" /FirstChar %d\n"
" /LastChar %d\n"
" /FontDescriptor %d 0 R\n",
subset_resource.id,
tag,
subset->base_font,
font_subset->is_latin ? 32 : 0,
last_glyph,
descriptor.id);
 
if (font_subset->is_latin)
_cairo_output_stream_printf (surface->output, " /Encoding /WinAnsiEncoding\n");
 
_cairo_output_stream_printf (surface->output, " /Widths [");
if (font_subset->is_latin) {
for (i = 32; i < last_glyph + 1; i++) {
int glyph = font_subset->latin_to_subset_glyph_index[i];
if (glyph > 0) {
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset->widths[glyph]*PDF_UNITS_PER_EM));
} else {
_cairo_output_stream_printf (surface->output, " 0");
}
}
} else {
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset->widths[i]*PDF_UNITS_PER_EM));
}
 
_cairo_output_stream_printf (surface->output,
" ]\n");
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
 
font.font_id = font_subset->font_id;
font.subset_id = font_subset->subset_id;
font.subset_resource = subset_resource;
return _cairo_array_append (&surface->fonts, &font);
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_font_subset (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_int_status_t status;
cairo_type1_subset_t subset;
char name[64];
 
/* 16-bit glyphs not compatible with Type 1 fonts */
if (font_subset->is_composite && !font_subset->is_latin)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
snprintf (name, sizeof name, "CairoFont-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_type1_subset_init (&subset, name, font_subset, FALSE);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
 
_cairo_type1_subset_fini (&subset);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_type1_fallback_font (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_int_status_t status;
cairo_type1_subset_t subset;
char name[64];
 
/* 16-bit glyphs not compatible with Type 1 fonts */
if (font_subset->is_composite && !font_subset->is_latin)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
snprintf (name, sizeof name, "CairoFont-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_type1_fallback_init_binary (&subset, name, font_subset);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_emit_type1_font (surface, font_subset, &subset);
 
_cairo_type1_fallback_fini (&subset);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_truetype_font_subset (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_pdf_resource_t stream, descriptor, cidfont_dict;
cairo_pdf_resource_t subset_resource, to_unicode_stream;
cairo_int_status_t status;
cairo_pdf_font_t font;
cairo_truetype_subset_t subset;
unsigned int i, last_glyph;
char tag[10];
 
subset_resource = _cairo_pdf_surface_get_font_resource (surface,
font_subset->font_id,
font_subset->subset_id);
if (subset_resource.id == 0)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_truetype_subset_init_pdf (&subset, font_subset);
if (unlikely (status))
return status;
 
_create_font_subset_tag (font_subset, subset.ps_name, tag);
 
status = _cairo_pdf_surface_open_stream (surface,
NULL,
TRUE,
" /Length1 %lu\n",
subset.data_length);
if (unlikely (status)) {
_cairo_truetype_subset_fini (&subset);
return status;
}
 
stream = surface->pdf_stream.self;
_cairo_output_stream_write (surface->output,
subset.data, subset.data_length);
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status)) {
_cairo_truetype_subset_fini (&subset);
return status;
}
 
status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
font_subset,
&to_unicode_stream);
if (_cairo_int_status_is_error (status)) {
_cairo_truetype_subset_fini (&subset);
return status;
}
 
descriptor = _cairo_pdf_surface_new_object (surface);
if (descriptor.id == 0) {
_cairo_truetype_subset_fini (&subset);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /FontDescriptor\n"
" /FontName /%s+%s\n",
descriptor.id,
tag,
subset.ps_name);
 
if (subset.family_name_utf8) {
char *pdf_str;
 
status = _utf8_to_pdf_string (subset.family_name_utf8, &pdf_str);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
" /FontFamily %s\n",
pdf_str);
free (pdf_str);
}
 
_cairo_output_stream_printf (surface->output,
" /Flags %d\n"
" /FontBBox [ %ld %ld %ld %ld ]\n"
" /ItalicAngle 0\n"
" /Ascent %ld\n"
" /Descent %ld\n"
" /CapHeight %ld\n"
" /StemV 80\n"
" /StemH 80\n"
" /FontFile2 %u 0 R\n"
">>\n"
"endobj\n",
font_subset->is_latin ? 32 : 4,
(long)(subset.x_min*PDF_UNITS_PER_EM),
(long)(subset.y_min*PDF_UNITS_PER_EM),
(long)(subset.x_max*PDF_UNITS_PER_EM),
(long)(subset.y_max*PDF_UNITS_PER_EM),
(long)(subset.ascent*PDF_UNITS_PER_EM),
(long)(subset.descent*PDF_UNITS_PER_EM),
(long)(subset.y_max*PDF_UNITS_PER_EM),
stream.id);
 
if (font_subset->is_latin) {
/* find last glyph used */
for (i = 255; i >= 32; i--)
if (font_subset->latin_to_subset_glyph_index[i] > 0)
break;
 
last_glyph = i;
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /TrueType\n"
" /BaseFont /%s+%s\n"
" /FirstChar 32\n"
" /LastChar %d\n"
" /FontDescriptor %d 0 R\n"
" /Encoding /WinAnsiEncoding\n"
" /Widths [",
subset_resource.id,
tag,
subset.ps_name,
last_glyph,
descriptor.id);
 
for (i = 32; i < last_glyph + 1; i++) {
int glyph = font_subset->latin_to_subset_glyph_index[i];
if (glyph > 0) {
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset.widths[glyph]*PDF_UNITS_PER_EM));
} else {
_cairo_output_stream_printf (surface->output, " 0");
}
}
 
_cairo_output_stream_printf (surface->output,
" ]\n");
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
} else {
cidfont_dict = _cairo_pdf_surface_new_object (surface);
if (cidfont_dict.id == 0) {
_cairo_truetype_subset_fini (&subset);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /CIDFontType2\n"
" /BaseFont /%s+%s\n"
" /CIDSystemInfo\n"
" << /Registry (Adobe)\n"
" /Ordering (Identity)\n"
" /Supplement 0\n"
" >>\n"
" /FontDescriptor %d 0 R\n"
" /W [0 [",
cidfont_dict.id,
tag,
subset.ps_name,
descriptor.id);
 
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output,
" %ld",
(long)(subset.widths[i]*PDF_UNITS_PER_EM));
 
_cairo_output_stream_printf (surface->output,
" ]]\n"
">>\n"
"endobj\n");
 
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /Type0\n"
" /BaseFont /%s+%s\n"
" /Encoding /Identity-H\n"
" /DescendantFonts [ %d 0 R]\n",
subset_resource.id,
tag,
subset.ps_name,
cidfont_dict.id);
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
}
 
font.font_id = font_subset->font_id;
font.subset_id = font_subset->subset_id;
font.subset_resource = subset_resource;
status = _cairo_array_append (&surface->fonts, &font);
 
_cairo_truetype_subset_fini (&subset);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_emit_imagemask (cairo_image_surface_t *image,
cairo_output_stream_t *stream)
{
uint8_t *byte, output_byte;
int row, col, num_cols;
 
/* The only image type supported by Type 3 fonts are 1-bit image
* masks */
assert (image->format == CAIRO_FORMAT_A1);
 
_cairo_output_stream_printf (stream,
"BI\n"
"/IM true\n"
"/W %d\n"
"/H %d\n"
"/BPC 1\n"
"/D [1 0]\n",
image->width,
image->height);
 
_cairo_output_stream_printf (stream,
"ID ");
 
num_cols = (image->width + 7) / 8;
for (row = 0; row < image->height; row++) {
byte = image->data + row * image->stride;
for (col = 0; col < num_cols; col++) {
output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
_cairo_output_stream_write (stream, &output_byte, 1);
byte++;
}
}
 
_cairo_output_stream_printf (stream,
"\nEI\n");
 
return _cairo_output_stream_get_status (stream);
}
 
static cairo_int_status_t
_cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_pdf_surface_t *surface = closure;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_int_status_t status2;
unsigned int i;
cairo_surface_t *type3_surface;
cairo_output_stream_t *null_stream;
 
null_stream = _cairo_null_stream_create ();
type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
null_stream,
_cairo_pdf_emit_imagemask,
surface->font_subsets);
if (unlikely (type3_surface->status)) {
status2 = _cairo_output_stream_destroy (null_stream);
return type3_surface->status;
}
 
_cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
_cairo_pdf_surface_add_font,
surface);
 
for (i = 0; i < font_subset->num_glyphs; i++) {
status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface,
font_subset->glyphs[i]);
if (unlikely (status))
break;
}
 
cairo_surface_destroy (type3_surface);
status2 = _cairo_output_stream_destroy (null_stream);
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_pdf_resource_t *glyphs, encoding, char_procs, subset_resource, to_unicode_stream;
cairo_pdf_font_t font;
double *widths;
unsigned int i;
cairo_box_t font_bbox = {{0,0},{0,0}};
cairo_box_t bbox = {{0,0},{0,0}};
cairo_surface_t *type3_surface;
 
if (font_subset->num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
 
subset_resource = _cairo_pdf_surface_get_font_resource (surface,
font_subset->font_id,
font_subset->subset_id);
if (subset_resource.id == 0)
return CAIRO_STATUS_SUCCESS;
 
glyphs = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (cairo_pdf_resource_t));
if (unlikely (glyphs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
widths = _cairo_malloc_ab (font_subset->num_glyphs, sizeof (double));
if (unlikely (widths == NULL)) {
free (glyphs);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_pdf_group_resources_clear (&surface->resources);
type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
NULL,
_cairo_pdf_emit_imagemask,
surface->font_subsets);
if (unlikely (type3_surface->status)) {
free (glyphs);
free (widths);
return type3_surface->status;
}
 
_cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface,
_cairo_pdf_surface_add_font,
surface);
 
for (i = 0; i < font_subset->num_glyphs; i++) {
status = _cairo_pdf_surface_open_stream (surface,
NULL,
surface->compress_content,
NULL);
if (unlikely (status))
break;
 
glyphs[i] = surface->pdf_stream.self;
status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
surface->output,
font_subset->glyphs[i],
&bbox,
&widths[i]);
if (unlikely (status))
break;
 
status = _cairo_pdf_surface_close_stream (surface);
if (unlikely (status))
break;
 
if (i == 0) {
font_bbox.p1.x = bbox.p1.x;
font_bbox.p1.y = bbox.p1.y;
font_bbox.p2.x = bbox.p2.x;
font_bbox.p2.y = bbox.p2.y;
} else {
if (bbox.p1.x < font_bbox.p1.x)
font_bbox.p1.x = bbox.p1.x;
if (bbox.p1.y < font_bbox.p1.y)
font_bbox.p1.y = bbox.p1.y;
if (bbox.p2.x > font_bbox.p2.x)
font_bbox.p2.x = bbox.p2.x;
if (bbox.p2.y > font_bbox.p2.y)
font_bbox.p2.y = bbox.p2.y;
}
}
cairo_surface_destroy (type3_surface);
if (unlikely (status)) {
free (glyphs);
free (widths);
return status;
}
 
encoding = _cairo_pdf_surface_new_object (surface);
if (encoding.id == 0) {
free (glyphs);
free (widths);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Encoding\n"
" /Differences [0", encoding.id);
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output,
" /%d", i);
_cairo_output_stream_printf (surface->output,
"]\n"
">>\n"
"endobj\n");
 
char_procs = _cairo_pdf_surface_new_object (surface);
if (char_procs.id == 0) {
free (glyphs);
free (widths);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<<\n", char_procs.id);
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output,
" /%d %d 0 R\n",
i, glyphs[i].id);
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
 
free (glyphs);
 
status = _cairo_pdf_surface_emit_to_unicode_stream (surface,
font_subset,
&to_unicode_stream);
if (_cairo_int_status_is_error (status)) {
free (widths);
return status;
}
 
_cairo_pdf_surface_update_object (surface, subset_resource);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Font\n"
" /Subtype /Type3\n"
" /FontBBox [%f %f %f %f]\n"
" /FontMatrix [ 1 0 0 1 0 0 ]\n"
" /Encoding %d 0 R\n"
" /CharProcs %d 0 R\n"
" /FirstChar 0\n"
" /LastChar %d\n",
subset_resource.id,
_cairo_fixed_to_double (font_bbox.p1.x),
- _cairo_fixed_to_double (font_bbox.p2.y),
_cairo_fixed_to_double (font_bbox.p2.x),
- _cairo_fixed_to_double (font_bbox.p1.y),
encoding.id,
char_procs.id,
font_subset->num_glyphs - 1);
 
_cairo_output_stream_printf (surface->output,
" /Widths [");
for (i = 0; i < font_subset->num_glyphs; i++)
_cairo_output_stream_printf (surface->output, " %f", widths[i]);
_cairo_output_stream_printf (surface->output,
"]\n");
free (widths);
 
_cairo_output_stream_printf (surface->output,
" /Resources\n");
_cairo_pdf_surface_emit_group_resources (surface, &surface->resources);
 
if (to_unicode_stream.id != 0)
_cairo_output_stream_printf (surface->output,
" /ToUnicode %d 0 R\n",
to_unicode_stream.id);
 
_cairo_output_stream_printf (surface->output,
">>\n"
"endobj\n");
 
font.font_id = font_subset->font_id;
font.subset_id = font_subset->subset_id;
font.subset_resource = subset_resource;
return _cairo_array_append (&surface->fonts, &font);
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_pdf_surface_t *surface = closure;
cairo_int_status_t status;
 
status = _cairo_pdf_surface_emit_cff_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_pdf_surface_emit_truetype_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_pdf_surface_emit_type1_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_pdf_surface_emit_cff_fallback_font (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_pdf_surface_emit_type1_fallback_font (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_pdf_surface_t *surface = closure;
cairo_int_status_t status;
 
status = _cairo_pdf_surface_emit_type3_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_emit_font_subsets (cairo_pdf_surface_t *surface)
{
cairo_int_status_t status;
 
status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
_cairo_pdf_surface_analyze_user_font_subset,
surface);
if (unlikely (status))
goto BAIL;
 
status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
_cairo_pdf_surface_emit_unscaled_font_subset,
surface);
if (unlikely (status))
goto BAIL;
 
status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
_cairo_pdf_surface_emit_scaled_font_subset,
surface);
if (unlikely (status))
goto BAIL;
 
status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
_cairo_pdf_surface_emit_scaled_font_subset,
surface);
 
BAIL:
_cairo_scaled_font_subsets_destroy (surface->font_subsets);
surface->font_subsets = NULL;
 
return status;
}
 
static cairo_pdf_resource_t
_cairo_pdf_surface_write_catalog (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t catalog;
 
catalog = _cairo_pdf_surface_new_object (surface);
if (catalog.id == 0)
return catalog;
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Catalog\n"
" /Pages %d 0 R\n"
">>\n"
"endobj\n",
catalog.id,
surface->pages_resource.id);
 
return catalog;
}
 
static long
_cairo_pdf_surface_write_xref (cairo_pdf_surface_t *surface)
{
cairo_pdf_object_t *object;
int num_objects, i;
long offset;
char buffer[11];
 
num_objects = _cairo_array_num_elements (&surface->objects);
 
offset = _cairo_output_stream_get_position (surface->output);
_cairo_output_stream_printf (surface->output,
"xref\n"
"%d %d\n",
0, num_objects + 1);
 
_cairo_output_stream_printf (surface->output,
"0000000000 65535 f \n");
for (i = 0; i < num_objects; i++) {
object = _cairo_array_index (&surface->objects, i);
snprintf (buffer, sizeof buffer, "%010ld", object->offset);
_cairo_output_stream_printf (surface->output,
"%s 00000 n \n", buffer);
}
 
return offset;
}
 
static cairo_int_status_t
_cairo_pdf_surface_write_mask_group (cairo_pdf_surface_t *surface,
cairo_pdf_smask_group_t *group)
{
cairo_pdf_resource_t mask_group;
cairo_pdf_resource_t smask;
cairo_pdf_smask_group_t *smask_group;
cairo_pdf_resource_t pattern_res, gstate_res;
cairo_int_status_t status;
cairo_box_double_t bbox;
 
/* Create mask group */
_get_bbox_from_extents (group->height, &group->extents, &bbox);
status = _cairo_pdf_surface_open_group (surface, &bbox, NULL);
if (unlikely (status))
return status;
 
if (_can_paint_pattern (group->mask)) {
_cairo_output_stream_printf (surface->output, "q\n");
status = _cairo_pdf_surface_paint_pattern (surface,
group->mask,
&group->extents,
FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "Q\n");
} else {
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, group->mask, NULL,
&pattern_res, &gstate_res);
if (unlikely (status))
return status;
 
if (gstate_res.id != 0) {
smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
if (unlikely (smask_group == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
smask_group->width = group->width;
smask_group->height = group->height;
smask_group->operation = PDF_PAINT;
smask_group->source = cairo_pattern_reference (group->mask);
smask_group->source_res = pattern_res;
status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (smask_group);
return status;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
smask_group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, group->mask, pattern_res, FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"%f %f %f %f re f\n",
bbox.p1.x,
bbox.p1.y,
bbox.p2.x - bbox.p1.x,
bbox.p2.y - bbox.p1.y);
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
return status;
}
}
 
status = _cairo_pdf_surface_close_group (surface, &mask_group);
if (unlikely (status))
return status;
 
/* Create source group */
status = _cairo_pdf_surface_open_group (surface, &bbox, &group->source_res);
if (unlikely (status))
return status;
 
if (_can_paint_pattern (group->source)) {
_cairo_output_stream_printf (surface->output, "q\n");
status = _cairo_pdf_surface_paint_pattern (surface,
group->source,
&group->extents,
FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "Q\n");
} else {
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, group->source, NULL,
&pattern_res, &gstate_res);
if (unlikely (status))
return status;
 
if (gstate_res.id != 0) {
smask_group = _cairo_pdf_surface_create_smask_group (surface, &group->extents);
if (unlikely (smask_group == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
smask_group->operation = PDF_PAINT;
smask_group->source = cairo_pattern_reference (group->source);
smask_group->source_res = pattern_res;
status = _cairo_pdf_surface_add_smask_group (surface, smask_group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (smask_group);
return status;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_add_xobject (surface, smask_group->group_res);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
smask_group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, group->source, pattern_res, FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"%f %f %f %f re f\n",
bbox.p1.x,
bbox.p1.y,
bbox.p2.x - bbox.p1.x,
bbox.p2.y - bbox.p1.y);
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
return status;
}
}
 
status = _cairo_pdf_surface_close_group (surface, NULL);
if (unlikely (status))
return status;
 
/* Create an smask based on the alpha component of mask_group */
smask = _cairo_pdf_surface_new_object (surface);
if (smask.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Mask\n"
" /S /Alpha\n"
" /G %d 0 R\n"
">>\n"
"endobj\n",
smask.id,
mask_group.id);
 
/* Create a GState that uses the smask */
_cairo_pdf_surface_update_object (surface, group->group_res);
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /ExtGState\n"
" /SMask %d 0 R\n"
" /ca 1\n"
" /CA 1\n"
" /AIS false\n"
">>\n"
"endobj\n",
group->group_res.id,
smask.id);
 
return _cairo_output_stream_get_status (surface->output);
}
 
static cairo_int_status_t
_cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t *surface,
cairo_pdf_smask_group_t *group)
{
double old_width, old_height;
cairo_int_status_t status;
cairo_box_double_t bbox;
 
old_width = surface->width;
old_height = surface->height;
_cairo_pdf_surface_set_size_internal (surface,
group->width,
group->height);
/* _mask is a special case that requires two groups - source
* and mask as well as a smask and gstate dictionary */
if (group->operation == PDF_MASK) {
status = _cairo_pdf_surface_write_mask_group (surface, group);
goto RESTORE_SIZE;
}
 
_get_bbox_from_extents (group->height, &group->extents, &bbox);
status = _cairo_pdf_surface_open_group (surface, &bbox, &group->group_res);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_select_pattern (surface,
group->source,
group->source_res,
group->operation == PDF_STROKE);
if (unlikely (status))
return status;
 
switch (group->operation) {
case PDF_PAINT:
_cairo_output_stream_printf (surface->output,
"0 0 %f %f re f\n",
surface->width, surface->height);
break;
case PDF_MASK:
ASSERT_NOT_REACHED;
break;
case PDF_FILL:
status = _cairo_pdf_operators_fill (&surface->pdf_operators,
&group->path,
group->fill_rule);
break;
case PDF_STROKE:
status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
&group->path,
&group->style,
&group->ctm,
&group->ctm_inverse);
break;
case PDF_SHOW_GLYPHS:
status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
group->utf8, group->utf8_len,
group->glyphs, group->num_glyphs,
group->clusters, group->num_clusters,
group->cluster_flags,
group->scaled_font);
break;
}
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_close_group (surface, NULL);
 
RESTORE_SIZE:
_cairo_pdf_surface_set_size_internal (surface,
old_width,
old_height);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_write_patterns_and_smask_groups (cairo_pdf_surface_t *surface)
{
cairo_pdf_pattern_t pattern;
cairo_pdf_smask_group_t *group;
cairo_pdf_source_surface_t src_surface;
unsigned int pattern_index, group_index, surface_index;
cairo_int_status_t status;
 
/* Writing out PDF_MASK groups will cause additional smask groups
* to be appended to surface->smask_groups. Additional patterns
* may also be appended to surface->patterns.
*
* Writing recording surface patterns will cause additional patterns
* and groups to be appended.
*/
pattern_index = 0;
group_index = 0;
surface_index = 0;
while ((pattern_index < _cairo_array_num_elements (&surface->page_patterns)) ||
(group_index < _cairo_array_num_elements (&surface->smask_groups)) ||
(surface_index < _cairo_array_num_elements (&surface->page_surfaces)))
{
for (; group_index < _cairo_array_num_elements (&surface->smask_groups); group_index++) {
_cairo_array_copy_element (&surface->smask_groups, group_index, &group);
status = _cairo_pdf_surface_write_smask_group (surface, group);
if (unlikely (status))
return status;
}
 
for (; pattern_index < _cairo_array_num_elements (&surface->page_patterns); pattern_index++) {
_cairo_array_copy_element (&surface->page_patterns, pattern_index, &pattern);
status = _cairo_pdf_surface_emit_pattern (surface, &pattern);
if (unlikely (status))
return status;
}
 
for (; surface_index < _cairo_array_num_elements (&surface->page_surfaces); surface_index++) {
_cairo_array_copy_element (&surface->page_surfaces, surface_index, &src_surface);
status = _cairo_pdf_surface_emit_surface (surface, &src_surface);
if (unlikely (status))
return status;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_write_page (cairo_pdf_surface_t *surface)
{
cairo_pdf_resource_t page, knockout, res;
cairo_int_status_t status;
unsigned int i, len;
 
_cairo_pdf_group_resources_clear (&surface->resources);
if (surface->has_fallback_images) {
cairo_rectangle_int_t extents;
cairo_box_double_t bbox;
 
extents.x = 0;
extents.y = 0;
extents.width = ceil (surface->width);
extents.height = ceil (surface->height);
_get_bbox_from_extents (surface->height, &extents, &bbox);
status = _cairo_pdf_surface_open_knockout_group (surface, &bbox);
if (unlikely (status))
return status;
 
len = _cairo_array_num_elements (&surface->knockout_group);
for (i = 0; i < len; i++) {
_cairo_array_copy_element (&surface->knockout_group, i, &res);
_cairo_output_stream_printf (surface->output,
"/x%d Do\n",
res.id);
status = _cairo_pdf_surface_add_xobject (surface, res);
if (unlikely (status))
return status;
}
_cairo_output_stream_printf (surface->output,
"/x%d Do\n",
surface->content.id);
status = _cairo_pdf_surface_add_xobject (surface, surface->content);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_close_group (surface, &knockout);
if (unlikely (status))
return status;
 
_cairo_pdf_group_resources_clear (&surface->resources);
status = _cairo_pdf_surface_open_content_stream (surface, NULL, NULL, FALSE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output,
"/x%d Do\n",
knockout.id);
status = _cairo_pdf_surface_add_xobject (surface, knockout);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_close_content_stream (surface);
if (unlikely (status))
return status;
}
 
page = _cairo_pdf_surface_new_object (surface);
if (page.id == 0)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->output,
"%d 0 obj\n"
"<< /Type /Page\n"
" /Parent %d 0 R\n"
" /MediaBox [ 0 0 %f %f ]\n"
" /Contents %d 0 R\n"
" /Group <<\n"
" /Type /Group\n"
" /S /Transparency\n"
" /I true\n"
" /CS /DeviceRGB\n"
" >>\n"
" /Resources %d 0 R\n"
">>\n"
"endobj\n",
page.id,
surface->pages_resource.id,
surface->width,
surface->height,
surface->content.id,
surface->content_resources.id);
 
status = _cairo_array_append (&surface->pages, &page);
if (unlikely (status))
return status;
 
status = _cairo_pdf_surface_write_patterns_and_smask_groups (surface);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_pdf_surface_analyze_surface_pattern_transparency (cairo_pdf_surface_t *surface,
cairo_surface_pattern_t *pattern)
{
cairo_image_surface_t *image;
void *image_extra;
cairo_int_status_t status;
cairo_image_transparency_t transparency;
 
status = _cairo_surface_acquire_source_image (pattern->surface,
&image,
&image_extra);
if (unlikely (status))
return status;
 
if (image->base.status)
return image->base.status;
 
transparency = _cairo_image_analyze_transparency (image);
if (transparency == CAIRO_IMAGE_IS_OPAQUE)
status = CAIRO_STATUS_SUCCESS;
else
status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
 
_cairo_surface_release_source_image (pattern->surface, image, image_extra);
 
return status;
}
 
static cairo_bool_t
_surface_pattern_supported (cairo_surface_pattern_t *pattern)
{
cairo_extend_t extend;
 
if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
return TRUE;
 
if (pattern->surface->backend->acquire_source_image == NULL)
return FALSE;
 
/* Does an ALPHA-only source surface even make sense? Maybe, but I
* don't think it's worth the extra code to support it. */
 
/* XXX: Need to write this function here...
if (pattern->surface->content == CAIRO_CONTENT_ALPHA)
return FALSE;
*/
 
extend = cairo_pattern_get_extend (&pattern->base);
switch (extend) {
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_REPEAT:
case CAIRO_EXTEND_REFLECT:
/* There's no point returning FALSE for EXTEND_PAD, as the image
* surface does not currently implement it either */
case CAIRO_EXTEND_PAD:
return TRUE;
}
 
ASSERT_NOT_REACHED;
return FALSE;
}
 
static cairo_bool_t
_pattern_supported (const cairo_pattern_t *pattern)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return TRUE;
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
default:
ASSERT_NOT_REACHED;
return FALSE;
}
}
 
static cairo_bool_t
_pdf_operator_supported (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return TRUE;
 
default:
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
return FALSE;
}
}
 
static cairo_int_status_t
_cairo_pdf_surface_analyze_operation (cairo_pdf_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
if (surface->force_fallbacks &&
surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (! _pattern_supported (pattern))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (_pdf_operator_supported (op)) {
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
 
if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (pattern->extend == CAIRO_EXTEND_PAD) {
cairo_box_t box;
cairo_rectangle_int_t rect;
cairo_rectangle_int_t rec_extents;
 
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &rect);
 
/* Check if surface needs padding to fill extents */
if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
_cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
_cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
_cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
}
return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
 
/* The SOURCE operator is supported if the pattern is opaque or if
* there is nothing painted underneath. */
if (op == CAIRO_OPERATOR_SOURCE) {
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
 
if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (_cairo_pattern_is_opaque (pattern, extents)) {
return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
} else {
/* FIXME: The analysis surface does not yet have
* the capability to analyze a non opaque recording
* surface and mark it supported if there is
* nothing underneath. For now recording surfaces of
* type CONTENT_COLOR_ALPHA painted with
* OPERATOR_SOURCE will result in a fallback
* image. */
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
} else {
return _cairo_pdf_surface_analyze_surface_pattern_transparency (surface,
surface_pattern);
}
}
 
if (_cairo_pattern_is_opaque (pattern, extents))
return CAIRO_STATUS_SUCCESS;
else
return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_bool_t
_cairo_pdf_surface_operation_supported (cairo_pdf_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
return _cairo_pdf_surface_analyze_operation (surface, op, pattern, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_int_status_t
_cairo_pdf_surface_start_fallback (cairo_pdf_surface_t *surface)
{
cairo_box_double_t bbox;
cairo_int_status_t status;
 
status = _cairo_pdf_surface_close_content_stream (surface);
if (unlikely (status))
return status;
 
status = _cairo_array_append (&surface->knockout_group, &surface->content);
if (unlikely (status))
return status;
 
_cairo_pdf_group_resources_clear (&surface->resources);
bbox.p1.x = 0;
bbox.p1.y = 0;
bbox.p2.x = surface->width;
bbox.p2.y = surface->height;
return _cairo_pdf_surface_open_content_stream (surface, &bbox, NULL, TRUE);
}
 
/* A PDF stencil mask is an A1 mask used with the current color */
static cairo_int_status_t
_cairo_pdf_surface_emit_stencil_mask (cairo_pdf_surface_t *surface,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
cairo_int_status_t status;
cairo_image_surface_t *image;
void *image_extra;
cairo_image_transparency_t transparency;
cairo_pdf_resource_t pattern_res = {0};
 
if (! (source->type == CAIRO_PATTERN_TYPE_SOLID &&
(mask->type == CAIRO_PATTERN_TYPE_SURFACE || mask->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
((cairo_surface_pattern_t *) mask)->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = _cairo_pdf_surface_acquire_source_image_from_pattern (surface, mask,
&image, &image_extra);
if (unlikely (status))
return status;
 
if (image->base.status)
return image->base.status;
 
transparency = _cairo_image_analyze_transparency (image);
if (transparency != CAIRO_IMAGE_IS_OPAQUE &&
transparency != CAIRO_IMAGE_HAS_BILEVEL_ALPHA)
{
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
status = _cairo_pdf_surface_select_pattern (surface, source,
pattern_res, FALSE);
if (unlikely (status))
return status;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "q\n");
status = _cairo_pdf_surface_paint_surface_pattern (surface, mask, extents, TRUE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->output, "Q\n");
 
status = _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_pdf_surface_release_source_image_from_pattern (surface, mask, image, image_extra);
 
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_set_clip (cairo_pdf_surface_t *surface,
cairo_composite_rectangles_t *composite)
{
cairo_clip_t *clip = composite->clip;
 
if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
clip = NULL;
 
if (clip == NULL) {
if (_cairo_composite_rectangles_can_reduce_clip (composite,
surface->clipper.clip))
return CAIRO_STATUS_SUCCESS;
}
 
return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
}
 
static cairo_int_status_t
_cairo_pdf_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_pdf_smask_group_t *group;
cairo_pdf_resource_t pattern_res, gstate_res;
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
status = _cairo_composite_rectangles_init_for_paint (&extents,
&surface->base,
op, source, clip);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
goto cleanup;
} else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
status = _cairo_pdf_surface_start_fallback (surface);
if (unlikely (status))
goto cleanup;
}
 
assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, op);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
if (_can_paint_pattern (source)) {
_cairo_output_stream_printf (surface->output, "q\n");
status = _cairo_pdf_surface_paint_pattern (surface,
source,
&extents.bounded,
FALSE);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output, "Q\n");
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
}
 
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
&extents.bounded,
&pattern_res, &gstate_res);
if (unlikely (status))
goto cleanup;
 
if (gstate_res.id != 0) {
group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
if (unlikely (group == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
group->operation = PDF_PAINT;
status = _cairo_pattern_create_copy (&group->source, source);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
group->source_res = pattern_res;
status = _cairo_pdf_surface_add_smask_group (surface, group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, source,
pattern_res, FALSE);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"0 0 %f %f re f\n",
surface->width, surface->height);
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
goto cleanup;
}
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_pdf_smask_group_t *group;
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
cairo_rectangle_int_t r;
cairo_box_t box;
 
status = _cairo_composite_rectangles_init_for_mask (&extents,
&surface->base,
op, source, mask, clip);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
cairo_int_status_t source_status, mask_status;
 
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
if (_cairo_int_status_is_error (status))
goto cleanup;
source_status = status;
 
if (mask->has_component_alpha) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
} else {
status = _cairo_pdf_surface_analyze_operation (surface, op, mask, &extents.bounded);
if (_cairo_int_status_is_error (status))
goto cleanup;
}
mask_status = status;
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_analysis_surface_merge_status (source_status,
mask_status);
} else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
status = _cairo_pdf_surface_start_fallback (surface);
if (unlikely (status))
goto cleanup;
}
 
assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
assert (_cairo_pdf_surface_operation_supported (surface, op, mask, &extents.bounded));
 
/* get the accurate extents */
status = _cairo_pattern_get_ink_extents (source, &r);
if (unlikely (status))
goto cleanup;
 
/* XXX slight impedance mismatch */
_cairo_box_from_rectangle (&box, &r);
status = _cairo_composite_rectangles_intersect_source_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pattern_get_ink_extents (mask, &r);
if (unlikely (status))
goto cleanup;
 
_cairo_box_from_rectangle (&box, &r);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, op);
if (unlikely (status))
goto cleanup;
 
/* Check if we can use a stencil mask */
status = _cairo_pdf_surface_emit_stencil_mask (surface, source, mask, &extents.bounded);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto cleanup;
 
group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
if (unlikely (group == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
group->operation = PDF_MASK;
status = _cairo_pattern_create_copy (&group->source, source);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
status = _cairo_pattern_create_copy (&group->mask, mask);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
group->source_res = _cairo_pdf_surface_new_object (surface);
if (group->source_res.id == 0) {
_cairo_pdf_smask_group_destroy (group);
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask_group (surface, group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask (surface, group->group_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_add_xobject (surface, group->source_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
group->group_res.id,
group->source_res.id);
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_pdf_smask_group_t *group;
cairo_pdf_resource_t pattern_res, gstate_res;
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
status = _cairo_composite_rectangles_init_for_stroke (&extents,
&surface->base,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
 
/* use the more accurate extents */
if (extents.is_bounded) {
cairo_rectangle_int_t mask;
cairo_box_t box;
 
status = _cairo_path_fixed_stroke_extents (path, style,
ctm, ctm_inverse,
tolerance,
&mask);
if (unlikely (status))
goto cleanup;
 
_cairo_box_from_rectangle (&box, &mask);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
}
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
goto cleanup;
}
 
assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
&extents.bounded,
&pattern_res, &gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, op);
if (unlikely (status))
goto cleanup;
 
if (gstate_res.id != 0) {
group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
if (unlikely (group == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
group->operation = PDF_STROKE;
status = _cairo_pattern_create_copy (&group->source, source);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
group->source_res = pattern_res;
status = _cairo_path_fixed_init_copy (&group->path, path);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
group->style = *style;
group->ctm = *ctm;
group->ctm_inverse = *ctm_inverse;
status = _cairo_pdf_surface_add_smask_group (surface, group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, TRUE);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
path,
style,
ctm,
ctm_inverse);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
goto cleanup;
}
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_int_status_t status;
cairo_pdf_smask_group_t *group;
cairo_pdf_resource_t pattern_res, gstate_res;
cairo_composite_rectangles_t extents;
 
status = _cairo_composite_rectangles_init_for_fill (&extents,
&surface->base,
op, source, path,
clip);
if (unlikely (status))
return status;
 
/* use the more accurate extents */
if (extents.is_bounded) {
cairo_rectangle_int_t mask;
cairo_box_t box;
 
_cairo_path_fixed_fill_extents (path,
fill_rule,
tolerance,
&mask);
 
_cairo_box_from_rectangle (&box, &mask);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
}
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
goto cleanup;
} else if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
status = _cairo_pdf_surface_start_fallback (surface);
if (unlikely (status))
goto cleanup;
}
 
assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, op);
if (unlikely (status))
goto cleanup;
 
if (_can_paint_pattern (source)) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output, "q\n");
status = _cairo_pdf_operators_clip (&surface->pdf_operators,
path,
fill_rule);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_paint_pattern (surface,
source,
&extents.bounded,
FALSE);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output, "Q\n");
status = _cairo_output_stream_get_status (surface->output);
goto cleanup;
}
 
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
&extents.bounded,
&pattern_res, &gstate_res);
if (unlikely (status))
goto cleanup;
 
if (gstate_res.id != 0) {
group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
if (unlikely (group == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
group->operation = PDF_FILL;
status = _cairo_pattern_create_copy (&group->source, source);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
group->source_res = pattern_res;
status = _cairo_path_fixed_init_copy (&group->path, path);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
group->fill_rule = fill_rule;
status = _cairo_pdf_surface_add_smask_group (surface, group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_fill (&surface->pdf_operators,
path,
fill_rule);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
goto cleanup;
}
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_pdf_surface_fill_stroke (void *abstract_surface,
cairo_operator_t fill_op,
const cairo_pattern_t *fill_source,
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
const cairo_path_fixed_t*path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *stroke_ctm,
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_int_status_t status;
cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res;
cairo_composite_rectangles_t extents;
 
/* During analysis we return unsupported and let the _fill and
* _stroke functions that are on the fallback path do the analysis
* for us. During render we may still encounter unsupported
* combinations of fill/stroke patterns. However we can return
* unsupported anytime to let the _fill and _stroke functions take
* over.
*/
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* PDF rendering of fill-stroke is not the same as cairo when
* either the fill or stroke is not opaque.
*/
if ( !_cairo_pattern_is_opaque (fill_source, NULL) ||
!_cairo_pattern_is_opaque (stroke_source, NULL))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (fill_op != stroke_op)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Compute the operation extents using the stroke which will naturally
* be larger than the fill extents.
*/
status = _cairo_composite_rectangles_init_for_stroke (&extents,
&surface->base,
stroke_op, stroke_source,
path, stroke_style, stroke_ctm,
clip);
if (unlikely (status))
return status;
 
/* use the more accurate extents */
if (extents.is_bounded) {
cairo_rectangle_int_t mask;
cairo_box_t box;
 
status = _cairo_path_fixed_stroke_extents (path, stroke_style,
stroke_ctm, stroke_ctm_inverse,
stroke_tolerance,
&mask);
if (unlikely (status))
goto cleanup;
 
_cairo_box_from_rectangle (&box, &mask);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
}
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, fill_op);
if (unlikely (status))
goto cleanup;
 
/* use the more accurate extents */
if (extents.is_bounded) {
cairo_rectangle_int_t mask;
cairo_box_t box;
 
_cairo_path_fixed_fill_extents (path,
fill_rule,
fill_tolerance,
&mask);
 
_cairo_box_from_rectangle (&box, &mask);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
&box);
if (unlikely (status))
goto cleanup;
}
 
fill_pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, fill_source,
&extents.bounded,
&fill_pattern_res,
&gstate_res);
if (unlikely (status))
goto cleanup;
 
assert (gstate_res.id == 0);
 
stroke_pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface,
stroke_source,
&extents.bounded,
&stroke_pattern_res,
&gstate_res);
if (unlikely (status))
goto cleanup;
 
assert (gstate_res.id == 0);
 
/* As PDF has separate graphics state for fill and stroke we can
* select both at the same time */
status = _cairo_pdf_surface_select_pattern (surface, fill_source,
fill_pattern_res, FALSE);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_pattern (surface, stroke_source,
stroke_pattern_res, TRUE);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_fill_stroke (&surface->pdf_operators,
path,
fill_rule,
stroke_style,
stroke_ctm,
stroke_ctm_inverse);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
goto cleanup;
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_bool_t
_cairo_pdf_surface_has_show_text_glyphs (void *abstract_surface)
{
return TRUE;
}
 
static cairo_int_status_t
_cairo_pdf_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_pdf_surface_t *surface = abstract_surface;
cairo_pdf_smask_group_t *group;
cairo_pdf_resource_t pattern_res, gstate_res;
cairo_composite_rectangles_t extents;
cairo_bool_t overlap;
cairo_int_status_t status;
 
status = _cairo_composite_rectangles_init_for_glyphs (&extents,
&surface->base,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
&overlap);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_pdf_surface_analyze_operation (surface, op, source, &extents.bounded);
goto cleanup;
}
 
assert (_cairo_pdf_surface_operation_supported (surface, op, source, &extents.bounded));
 
status = _cairo_pdf_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup;
 
pattern_res.id = 0;
gstate_res.id = 0;
status = _cairo_pdf_surface_add_pdf_pattern (surface, source,
&extents.bounded,
&pattern_res, &gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_select_operator (surface, op);
if (unlikely (status))
goto cleanup;
 
if (gstate_res.id != 0) {
group = _cairo_pdf_surface_create_smask_group (surface, &extents.bounded);
if (unlikely (group == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
 
group->operation = PDF_SHOW_GLYPHS;
status = _cairo_pattern_create_copy (&group->source, source);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
group->source_res = pattern_res;
 
if (utf8_len) {
group->utf8 = malloc (utf8_len);
if (unlikely (group->utf8 == NULL)) {
_cairo_pdf_smask_group_destroy (group);
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
memcpy (group->utf8, utf8, utf8_len);
}
group->utf8_len = utf8_len;
 
if (num_glyphs) {
group->glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (unlikely (group->glyphs == NULL)) {
_cairo_pdf_smask_group_destroy (group);
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
memcpy (group->glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
}
group->num_glyphs = num_glyphs;
 
if (num_clusters) {
group->clusters = _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
if (unlikely (group->clusters == NULL)) {
_cairo_pdf_smask_group_destroy (group);
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto cleanup;
}
memcpy (group->clusters, clusters, sizeof (cairo_text_cluster_t) * num_clusters);
}
group->num_clusters = num_clusters;
 
group->scaled_font = cairo_scaled_font_reference (scaled_font);
status = _cairo_pdf_surface_add_smask_group (surface, group);
if (unlikely (status)) {
_cairo_pdf_smask_group_destroy (group);
goto cleanup;
}
 
status = _cairo_pdf_surface_add_smask (surface, gstate_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_add_xobject (surface, group->group_res);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
 
_cairo_output_stream_printf (surface->output,
"q /s%d gs /x%d Do Q\n",
gstate_res.id,
group->group_res.id);
} else {
status = _cairo_pdf_surface_select_pattern (surface, source, pattern_res, FALSE);
if (unlikely (status))
goto cleanup;
 
/* Each call to show_glyphs() with a transclucent pattern must
* be in a separate text object otherwise overlapping text
* from separate calls to show_glyphs will not composite with
* each other. */
if (! _cairo_pattern_is_opaque (source, &extents.bounded)) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup;
}
 
status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font);
if (unlikely (status))
goto cleanup;
 
status = _cairo_pdf_surface_unselect_pattern (surface);
if (unlikely (status))
goto cleanup;
}
 
_cairo_composite_rectangles_fini (&extents);
return _cairo_output_stream_get_status (surface->output);
 
cleanup:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static const char **
_cairo_pdf_surface_get_supported_mime_types (void *abstract_surface)
{
return _cairo_pdf_supported_mime_types;
}
 
static void
_cairo_pdf_surface_set_paginated_mode (void *abstract_surface,
cairo_paginated_mode_t paginated_mode)
{
cairo_pdf_surface_t *surface = abstract_surface;
 
surface->paginated_mode = paginated_mode;
}
 
static const cairo_surface_backend_t cairo_pdf_surface_backend = {
CAIRO_SURFACE_TYPE_PDF,
_cairo_pdf_surface_finish,
 
_cairo_default_context_create,
 
NULL, /* create similar: handled by wrapper */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* snapshot */
 
NULL, /* _cairo_pdf_surface_copy_page */
_cairo_pdf_surface_show_page,
 
_cairo_pdf_surface_get_extents,
_cairo_pdf_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
/* Here are the drawing functions */
_cairo_pdf_surface_paint,
_cairo_pdf_surface_mask,
_cairo_pdf_surface_stroke,
_cairo_pdf_surface_fill,
_cairo_pdf_surface_fill_stroke,
NULL, /* show_glyphs */
_cairo_pdf_surface_has_show_text_glyphs,
_cairo_pdf_surface_show_text_glyphs,
_cairo_pdf_surface_get_supported_mime_types,
};
 
static const cairo_paginated_surface_backend_t
cairo_pdf_surface_paginated_backend = {
_cairo_pdf_surface_start_page,
_cairo_pdf_surface_set_paginated_mode,
NULL, /* set_bounding_box */
_cairo_pdf_surface_has_fallback_images,
_cairo_pdf_surface_supports_fine_grained_fallbacks,
};
/programs/develop/libraries/cairo/src/cairo-pdf.h
45,14 → 45,14
 
/**
* cairo_pdf_version_t:
* @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification.
* @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification.
* @CAIRO_PDF_VERSION_1_4: The version 1.4 of the PDF specification. (Since 1.10)
* @CAIRO_PDF_VERSION_1_5: The version 1.5 of the PDF specification. (Since 1.10)
*
* #cairo_pdf_version_t is used to describe the version number of the PDF
* specification that a generated PDF file will conform to.
*
* Since 1.10
*/
* Since: 1.10
**/
typedef enum _cairo_pdf_version {
CAIRO_PDF_VERSION_1_4,
CAIRO_PDF_VERSION_1_5
/programs/develop/libraries/cairo/src/cairo-pen.c
41,11 → 41,6
#include "cairo-error-private.h"
#include "cairo-slope-private.h"
 
static int
_cairo_pen_vertices_needed (double tolerance,
double radius,
const cairo_matrix_t *matrix);
 
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen);
 
88,10 → 83,12
* is reflecting
*/
for (i=0; i < pen->num_vertices; i++) {
double theta = 2 * M_PI * i / (double) pen->num_vertices;
double dx = radius * cos (reflect ? -theta : theta);
double dy = radius * sin (reflect ? -theta : theta);
cairo_pen_vertex_t *v = &pen->vertices[i];
double theta = 2 * M_PI * i / (double) pen->num_vertices, dx, dy;
if (reflect)
theta = -theta;
dx = radius * cos (theta);
dy = radius * sin (theta);
cairo_matrix_transform_distance (ctm, &dx, &dy);
v->point.x = _cairo_fixed_from_double (dx);
v->point.y = _cairo_fixed_from_double (dy);
273,7 → 270,7
doesn't matter where on the circle the error is computed.
*/
 
static int
int
_cairo_pen_vertices_needed (double tolerance,
double radius,
const cairo_matrix_t *matrix)
283,21 → 280,16
* compute major axis length for a pen with the specified radius.
* we don't need the minor axis length.
*/
 
double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
radius);
 
/*
* compute number of vertices needed
*/
int num_vertices;
 
/* Where tolerance / M is > 1, we use 4 points */
if (tolerance >= major_axis) {
if (tolerance >= 4*major_axis) { /* XXX relaxed from 2*major for inkscape */
num_vertices = 1;
} else if (tolerance >= major_axis) {
num_vertices = 4;
} else {
double delta = acos (1 - tolerance / major_axis);
num_vertices = ceil (M_PI / delta);
num_vertices = ceil (2*M_PI / acos (1 - tolerance / major_axis));
 
/* number of vertices must be even */
if (num_vertices % 2)
396,3 → 388,88
 
return i;
}
 
void
_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen,
const cairo_slope_t *in,
const cairo_slope_t *out,
int *start, int *stop)
{
 
int lo = 0, hi = pen->num_vertices;
int i;
 
i = (lo + hi) >> 1;
do {
if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
lo = i;
else
hi = i;
i = (lo + hi) >> 1;
} while (hi - lo > 1);
if (_cairo_slope_compare (&pen->vertices[i].slope_cw, in) < 0)
if (++i == pen->num_vertices)
i = 0;
*start = i;
 
if (_cairo_slope_compare (out, &pen->vertices[i].slope_ccw) >= 0) {
lo = i;
hi = i + pen->num_vertices;
i = (lo + hi) >> 1;
do {
int j = i;
if (j >= pen->num_vertices)
j -= pen->num_vertices;
if (_cairo_slope_compare (&pen->vertices[j].slope_cw, out) > 0)
hi = i;
else
lo = i;
i = (lo + hi) >> 1;
} while (hi - lo > 1);
if (i >= pen->num_vertices)
i -= pen->num_vertices;
}
*stop = i;
}
 
void
_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen,
const cairo_slope_t *in,
const cairo_slope_t *out,
int *start, int *stop)
{
int lo = 0, hi = pen->num_vertices;
int i;
 
i = (lo + hi) >> 1;
do {
if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
lo = i;
else
hi = i;
i = (lo + hi) >> 1;
} while (hi - lo > 1);
if (_cairo_slope_compare (in, &pen->vertices[i].slope_ccw) < 0)
if (++i == pen->num_vertices)
i = 0;
*start = i;
 
if (_cairo_slope_compare (&pen->vertices[i].slope_cw, out) <= 0) {
lo = i;
hi = i + pen->num_vertices;
i = (lo + hi) >> 1;
do {
int j = i;
if (j >= pen->num_vertices)
j -= pen->num_vertices;
if (_cairo_slope_compare (out, &pen->vertices[j].slope_ccw) > 0)
hi = i;
else
lo = i;
i = (lo + hi) >> 1;
} while (hi - lo > 1);
if (i >= pen->num_vertices)
i -= pen->num_vertices;
}
*stop = i;
}
/programs/develop/libraries/cairo/src/cairo-pixman-private.h
0,0 → 1,51
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright ©2013 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_PIXMAN_PRIVATE_H
#define CAIRO_PIXMAN_PRIVATE_H
 
#include "cairo-pixman-private.h" /* keep make check happy */
 
#include <pixman.h>
 
#if PIXMAN_VERSION < PIXMAN_VERSION_ENCODE(0,22,0)
#define pixman_image_composite32 pixman_image_composite
#define pixman_image_get_component_alpha(i) 0
#define pixman_image_set_component_alpha(i, x) do { } while (0)
#endif
 
#endif
/programs/develop/libraries/cairo/src/cairo-png.c
39,6 → 39,7
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-output-stream-private.h"
 
#include <stdio.h>
53,7 → 54,15
*
* The PNG functions allow reading PNG images into image surfaces, and writing
* any surface to a PNG file.
*/
*
* It is a toy API. It only offers very simple support for reading and
* writing PNG files, which is sufficient for testing and
* demonstration purposes. Applications which need more control over
* the generated PNG file should access the pixel data directly, using
* cairo_image_surface_get_data() or a backend-specific access
* function, and process it with another library, e.g. gdk-pixbuf or
* libpng.
**/
 
/**
* CAIRO_HAS_PNG_FUNCTIONS:
61,7 → 70,9
* Defined if the PNG functions are available.
* This macro can be used to conditionally compile code using the cairo
* PNG functions.
*/
*
* Since: 1.0
**/
 
struct png_read_closure_t {
cairo_read_func_t read_func;
138,13 → 149,13
png_simple_warning_callback (png_structp png,
png_const_charp error_msg)
{
cairo_status_t *error = png_get_error_ptr (png);
 
/* default to the most likely error */
if (*error == CAIRO_STATUS_SUCCESS)
*error = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* png does not expect to abort and will try to tidy up after a warning */
/* png does not expect to abort and will try to tidy up and continue
* loading the image after a warning. So we also want to return the
* (incorrect?) surface.
*
* We use our own warning callback to squelch any attempts by libpng
* to write to stderr as we may not be in control of that output.
*/
}
 
 
161,7 → 172,7
void *closure)
{
int i;
cairo_status_t status;
cairo_int_status_t status;
cairo_image_surface_t *image;
cairo_image_surface_t * volatile clone;
void *image_extra;
170,7 → 181,7
png_byte **volatile rows = NULL;
png_color_16 white;
int png_color_type;
int depth;
int bpc;
 
status = _cairo_surface_acquire_source_image (surface,
&image,
227,22 → 238,26
 
switch (clone->format) {
case CAIRO_FORMAT_ARGB32:
depth = 8;
bpc = 8;
if (_cairo_image_analyze_transparency (clone) == CAIRO_IMAGE_IS_OPAQUE)
png_color_type = PNG_COLOR_TYPE_RGB;
else
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case CAIRO_FORMAT_RGB30:
bpc = 10;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_RGB24:
depth = 8;
bpc = 8;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_A8:
depth = 8;
bpc = 8;
png_color_type = PNG_COLOR_TYPE_GRAY;
break;
case CAIRO_FORMAT_A1:
depth = 1;
bpc = 1;
png_color_type = PNG_COLOR_TYPE_GRAY;
#ifndef WORDS_BIGENDIAN
png_set_packswap (png);
257,13 → 272,13
 
png_set_IHDR (png, info,
clone->width,
clone->height, depth,
clone->height, bpc,
png_color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
 
white.gray = (1 << depth) - 1;
white.gray = (1 << bpc) - 1;
white.red = white.blue = white.green = white.gray;
png_set_bKGD (png, info, &white);
 
335,6 → 350,8
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
* pixel contents, or %CAIRO_STATUS_WRITE_ERROR if an I/O error occurs
* while attempting to write the file.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_write_to_png (cairo_surface_t *surface,
401,6 → 418,8
* memory could not be allocated for the operation,
* %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface does not have
* pixel contents.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_write_to_png_stream (cairo_surface_t *surface,
697,9 → 716,7
}
 
BAIL:
if (row_pointers != NULL)
free (row_pointers);
if (data != NULL)
free (data);
if (png != NULL)
png_destroy_read_struct (&png, &info, NULL);
731,6 → 748,8
* Alternatively, you can allow errors to propagate through the drawing
* operations and check the status on the context upon completion
* using cairo_status().
*
* Since: 1.0
**/
cairo_surface_t *
cairo_image_surface_create_from_png (const char *filename)
784,6 → 803,8
* Alternatively, you can allow errors to propagate through the drawing
* operations and check the status on the context upon completion
* using cairo_status().
*
* Since: 1.0
**/
cairo_surface_t *
cairo_image_surface_create_from_png_stream (cairo_read_func_t read_func,
/programs/develop/libraries/cairo/src/cairo-polygon-intersect.c
0,0 → 1,1532
/*
* Copyright © 2004 Carl Worth
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2008 Chris Wilson
*
* 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 Carl Worth
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* Provide definitions for standalone compilation */
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-inline.h"
 
typedef cairo_point_t cairo_bo_point32_t;
 
typedef struct _cairo_bo_intersect_ordinate {
int32_t ordinate;
enum { EXACT, INEXACT } exactness;
} cairo_bo_intersect_ordinate_t;
 
typedef struct _cairo_bo_intersect_point {
cairo_bo_intersect_ordinate_t x;
cairo_bo_intersect_ordinate_t y;
} cairo_bo_intersect_point_t;
 
typedef struct _cairo_bo_edge cairo_bo_edge_t;
 
typedef struct _cairo_bo_deferred {
cairo_bo_edge_t *other;
int32_t top;
} cairo_bo_deferred_t;
 
struct _cairo_bo_edge {
int a_or_b;
cairo_edge_t edge;
cairo_bo_edge_t *prev;
cairo_bo_edge_t *next;
cairo_bo_deferred_t deferred;
};
 
/* the parent is always given by index/2 */
#define PQ_PARENT_INDEX(i) ((i) >> 1)
#define PQ_FIRST_ENTRY 1
 
/* left and right children are index * 2 and (index * 2) +1 respectively */
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
 
typedef enum {
CAIRO_BO_EVENT_TYPE_STOP,
CAIRO_BO_EVENT_TYPE_INTERSECTION,
CAIRO_BO_EVENT_TYPE_START
} cairo_bo_event_type_t;
 
typedef struct _cairo_bo_event {
cairo_bo_event_type_t type;
cairo_point_t point;
} cairo_bo_event_t;
 
typedef struct _cairo_bo_start_event {
cairo_bo_event_type_t type;
cairo_point_t point;
cairo_bo_edge_t edge;
} cairo_bo_start_event_t;
 
typedef struct _cairo_bo_queue_event {
cairo_bo_event_type_t type;
cairo_point_t point;
cairo_bo_edge_t *e1;
cairo_bo_edge_t *e2;
} cairo_bo_queue_event_t;
 
typedef struct _pqueue {
int size, max_size;
 
cairo_bo_event_t **elements;
cairo_bo_event_t *elements_embedded[1024];
} pqueue_t;
 
typedef struct _cairo_bo_event_queue {
cairo_freepool_t pool;
pqueue_t pqueue;
cairo_bo_event_t **start_events;
} cairo_bo_event_queue_t;
 
typedef struct _cairo_bo_sweep_line {
cairo_bo_edge_t *head;
int32_t current_y;
cairo_bo_edge_t *current_edge;
} cairo_bo_sweep_line_t;
 
static cairo_fixed_t
_line_compute_intersection_x_for_y (const cairo_line_t *line,
cairo_fixed_t y)
{
cairo_fixed_t x, dy;
 
if (y == line->p1.y)
return line->p1.x;
if (y == line->p2.y)
return line->p2.x;
 
x = line->p1.x;
dy = line->p2.y - line->p1.y;
if (dy != 0) {
x += _cairo_fixed_mul_div_floor (y - line->p1.y,
line->p2.x - line->p1.x,
dy);
}
 
return x;
}
 
static inline int
_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
cairo_bo_point32_t const *b)
{
int cmp;
 
cmp = a->y - b->y;
if (cmp)
return cmp;
 
return a->x - b->x;
}
 
/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
* slope a is respectively greater than, equal to, or less than the
* slope of b.
*
* For each edge, consider the direction vector formed from:
*
* top -> bottom
*
* which is:
*
* (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
*
* We then define the slope of each edge as dx/dy, (which is the
* inverse of the slope typically used in math instruction). We never
* compute a slope directly as the value approaches infinity, but we
* can derive a slope comparison without division as follows, (where
* the ? represents our compare operator).
*
* 1. slope(a) ? slope(b)
* 2. adx/ady ? bdx/bdy
* 3. (adx * bdy) ? (bdx * ady)
*
* Note that from step 2 to step 3 there is no change needed in the
* sign of the result since both ady and bdy are guaranteed to be
* greater than or equal to 0.
*
* When using this slope comparison to sort edges, some care is needed
* when interpreting the results. Since the slope compare operates on
* distance vectors from top to bottom it gives a correct left to
* right sort for edges that have a common top point, (such as two
* edges with start events at the same location). On the other hand,
* the sense of the result will be exactly reversed for two edges that
* have a common stop point.
*/
static inline int
_slope_compare (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b)
{
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm
* begins.
*/
int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
 
/* Since the dy's are all positive by construction we can fast
* path several common cases.
*/
 
/* First check for vertical lines. */
if (adx == 0)
return -bdx;
if (bdx == 0)
return adx;
 
/* Then where the two edges point in different directions wrt x. */
if ((adx ^ bdx) < 0)
return adx;
 
/* Finally we actually need to do the general comparison. */
{
int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
 
return _cairo_int64_cmp (adx_bdy, bdx_ady);
}
}
 
/*
* We need to compare the x-coordinates of a pair of lines for a particular y,
* without loss of precision.
*
* The x-coordinate along an edge for a given y is:
* X = A_x + (Y - A_y) * A_dx / A_dy
*
* So the inequality we wish to test is:
* A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
* where ∘ is our inequality operator.
*
* By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
* all positive, so we can rearrange it thus without causing a sign change:
* A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
* - (Y - A_y) * A_dx * B_dy
*
* Given the assumption that all the deltas fit within 32 bits, we can compute
* this comparison directly using 128 bit arithmetic. For certain, but common,
* input we can reduce this down to a single 32 bit compare by inspecting the
* deltas.
*
* (And put the burden of the work on developing fast 128 bit ops, which are
* required throughout the tessellator.)
*
* See the similar discussion for _slope_compare().
*/
static int
edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b,
int32_t y)
{
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm
* begins.
*/
int32_t dx;
int32_t adx, ady;
int32_t bdx, bdy;
enum {
HAVE_NONE = 0x0,
HAVE_DX = 0x1,
HAVE_ADX = 0x2,
HAVE_DX_ADX = HAVE_DX | HAVE_ADX,
HAVE_BDX = 0x4,
HAVE_DX_BDX = HAVE_DX | HAVE_BDX,
HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX
} have_dx_adx_bdx = HAVE_ALL;
 
/* don't bother solving for abscissa if the edges' bounding boxes
* can be used to order them. */
{
int32_t amin, amax;
int32_t bmin, bmax;
if (a->edge.line.p1.x < a->edge.line.p2.x) {
amin = a->edge.line.p1.x;
amax = a->edge.line.p2.x;
} else {
amin = a->edge.line.p2.x;
amax = a->edge.line.p1.x;
}
if (b->edge.line.p1.x < b->edge.line.p2.x) {
bmin = b->edge.line.p1.x;
bmax = b->edge.line.p2.x;
} else {
bmin = b->edge.line.p2.x;
bmax = b->edge.line.p1.x;
}
if (amax < bmin) return -1;
if (amin > bmax) return +1;
}
 
ady = a->edge.line.p2.y - a->edge.line.p1.y;
adx = a->edge.line.p2.x - a->edge.line.p1.x;
if (adx == 0)
have_dx_adx_bdx &= ~HAVE_ADX;
 
bdy = b->edge.line.p2.y - b->edge.line.p1.y;
bdx = b->edge.line.p2.x - b->edge.line.p1.x;
if (bdx == 0)
have_dx_adx_bdx &= ~HAVE_BDX;
 
dx = a->edge.line.p1.x - b->edge.line.p1.x;
if (dx == 0)
have_dx_adx_bdx &= ~HAVE_DX;
 
#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
switch (have_dx_adx_bdx) {
default:
case HAVE_NONE:
return 0;
case HAVE_DX:
/* A_dy * B_dy * (A_x - B_x) ∘ 0 */
return dx; /* ady * bdy is positive definite */
case HAVE_ADX:
/* 0 ∘ - (Y - A_y) * A_dx * B_dy */
return adx; /* bdy * (y - a->top.y) is positive definite */
case HAVE_BDX:
/* 0 ∘ (Y - B_y) * B_dx * A_dy */
return -bdx; /* ady * (y - b->top.y) is positive definite */
case HAVE_ADX_BDX:
/* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
if ((adx ^ bdx) < 0) {
return adx;
} else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
cairo_int64_t adx_bdy, bdx_ady;
 
/* ∴ A_dx * B_dy ∘ B_dx * A_dy */
 
adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
 
return _cairo_int64_cmp (adx_bdy, bdx_ady);
} else
return _cairo_int128_cmp (A, B);
case HAVE_DX_ADX:
/* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
if ((-adx ^ dx) < 0) {
return dx;
} else {
cairo_int64_t ady_dx, dy_adx;
 
ady_dx = _cairo_int32x32_64_mul (ady, dx);
dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
 
return _cairo_int64_cmp (ady_dx, dy_adx);
}
case HAVE_DX_BDX:
/* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
if ((bdx ^ dx) < 0) {
return dx;
} else {
cairo_int64_t bdy_dx, dy_bdx;
 
bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
 
return _cairo_int64_cmp (bdy_dx, dy_bdx);
}
case HAVE_ALL:
/* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
}
#undef B
#undef A
#undef L
}
 
/*
* We need to compare the x-coordinate of a line for a particular y wrt to a
* given x, without loss of precision.
*
* The x-coordinate along an edge for a given y is:
* X = A_x + (Y - A_y) * A_dx / A_dy
*
* So the inequality we wish to test is:
* A_x + (Y - A_y) * A_dx / A_dy ∘ X
* where ∘ is our inequality operator.
*
* By construction, we know that A_dy (and (Y - A_y)) are
* all positive, so we can rearrange it thus without causing a sign change:
* (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
*
* Given the assumption that all the deltas fit within 32 bits, we can compute
* this comparison directly using 64 bit arithmetic.
*
* See the similar discussion for _slope_compare() and
* edges_compare_x_for_y_general().
*/
static int
edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
int32_t y,
int32_t x)
{
int32_t adx, ady;
int32_t dx, dy;
cairo_int64_t L, R;
 
if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
return 1;
if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
return -1;
 
adx = a->edge.line.p2.x - a->edge.line.p1.x;
dx = x - a->edge.line.p1.x;
 
if (adx == 0)
return -dx;
if (dx == 0 || (adx ^ dx) < 0)
return adx;
 
dy = y - a->edge.line.p1.y;
ady = a->edge.line.p2.y - a->edge.line.p1.y;
 
L = _cairo_int32x32_64_mul (dy, adx);
R = _cairo_int32x32_64_mul (dx, ady);
 
return _cairo_int64_cmp (L, R);
}
 
static int
edges_compare_x_for_y (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b,
int32_t y)
{
/* If the sweep-line is currently on an end-point of a line,
* then we know its precise x value (and considering that we often need to
* compare events at end-points, this happens frequently enough to warrant
* special casing).
*/
enum {
HAVE_NEITHER = 0x0,
HAVE_AX = 0x1,
HAVE_BX = 0x2,
HAVE_BOTH = HAVE_AX | HAVE_BX
} have_ax_bx = HAVE_BOTH;
int32_t ax, bx;
 
if (y == a->edge.line.p1.y)
ax = a->edge.line.p1.x;
else if (y == a->edge.line.p2.y)
ax = a->edge.line.p2.x;
else
have_ax_bx &= ~HAVE_AX;
 
if (y == b->edge.line.p1.y)
bx = b->edge.line.p1.x;
else if (y == b->edge.line.p2.y)
bx = b->edge.line.p2.x;
else
have_ax_bx &= ~HAVE_BX;
 
switch (have_ax_bx) {
default:
case HAVE_NEITHER:
return edges_compare_x_for_y_general (a, b, y);
case HAVE_AX:
return -edge_compare_for_y_against_x (b, y, ax);
case HAVE_BX:
return edge_compare_for_y_against_x (a, y, bx);
case HAVE_BOTH:
return ax - bx;
}
}
 
static inline int
_line_equal (const cairo_line_t *a, const cairo_line_t *b)
{
return a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
a->p2.x == b->p2.x && a->p2.y == b->p2.y;
}
 
static int
_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line,
const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b)
{
int cmp;
 
/* compare the edges if not identical */
if (! _line_equal (&a->edge.line, &b->edge.line)) {
cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
if (cmp)
return cmp;
 
/* The two edges intersect exactly at y, so fall back on slope
* comparison. We know that this compare_edges function will be
* called only when starting a new edge, (not when stopping an
* edge), so we don't have to worry about conditionally inverting
* the sense of _slope_compare. */
cmp = _slope_compare (a, b);
if (cmp)
return cmp;
}
 
/* We've got two collinear edges now. */
return b->edge.bottom - a->edge.bottom;
}
 
static inline cairo_int64_t
det32_64 (int32_t a, int32_t b,
int32_t c, int32_t d)
{
/* det = a * d - b * c */
return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
_cairo_int32x32_64_mul (b, c));
}
 
static inline cairo_int128_t
det64x32_128 (cairo_int64_t a, int32_t b,
cairo_int64_t c, int32_t d)
{
/* det = a * d - b * c */
return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
_cairo_int64x32_128_mul (c, b));
}
 
/* Compute the intersection of two lines as defined by two edges. The
* result is provided as a coordinate pair of 128-bit integers.
*
* Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
* %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
*/
static cairo_bool_t
intersect_lines (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_intersect_point_t *intersection)
{
cairo_int64_t a_det, b_det;
 
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm begins.
* What we're doing to mitigate this is to perform clamping in
* cairo_bo_tessellate_polygon().
*/
int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
 
int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
 
cairo_int64_t den_det;
cairo_int64_t R;
cairo_quorem64_t qr;
 
den_det = det32_64 (dx1, dy1, dx2, dy2);
 
/* Q: Can we determine that the lines do not intersect (within range)
* much more cheaply than computing the intersection point i.e. by
* avoiding the division?
*
* X = ax + t * adx = bx + s * bdx;
* Y = ay + t * ady = by + s * bdy;
* ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
* => t * L = R
*
* Therefore we can reject any intersection (under the criteria for
* valid intersection events) if:
* L^R < 0 => t < 0, or
* L<R => t > 1
*
* (where top/bottom must at least extend to the line endpoints).
*
* A similar substitution can be performed for s, yielding:
* s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
*/
R = det32_64 (dx2, dy2,
b->edge.line.p1.x - a->edge.line.p1.x,
b->edge.line.p1.y - a->edge.line.p1.y);
if (_cairo_int64_negative (den_det)) {
if (_cairo_int64_ge (den_det, R))
return FALSE;
} else {
if (_cairo_int64_le (den_det, R))
return FALSE;
}
 
R = det32_64 (dy1, dx1,
a->edge.line.p1.y - b->edge.line.p1.y,
a->edge.line.p1.x - b->edge.line.p1.x);
if (_cairo_int64_negative (den_det)) {
if (_cairo_int64_ge (den_det, R))
return FALSE;
} else {
if (_cairo_int64_le (den_det, R))
return FALSE;
}
 
/* We now know that the two lines should intersect within range. */
 
a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
a->edge.line.p2.x, a->edge.line.p2.y);
b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
b->edge.line.p2.x, b->edge.line.p2.y);
 
/* x = det (a_det, dx1, b_det, dx2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
b_det, dx2),
den_det);
if (_cairo_int64_eq (qr.rem, den_det))
return FALSE;
#if 0
intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
#else
intersection->x.exactness = EXACT;
if (! _cairo_int64_is_zero (qr.rem)) {
if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
qr.rem = _cairo_int64_negate (qr.rem);
qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
if (_cairo_int64_ge (qr.rem, den_det)) {
qr.quo = _cairo_int64_add (qr.quo,
_cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
} else
intersection->x.exactness = INEXACT;
}
#endif
intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
 
/* y = det (a_det, dy1, b_det, dy2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
b_det, dy2),
den_det);
if (_cairo_int64_eq (qr.rem, den_det))
return FALSE;
#if 0
intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
#else
intersection->y.exactness = EXACT;
if (! _cairo_int64_is_zero (qr.rem)) {
if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
qr.rem = _cairo_int64_negate (qr.rem);
qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
if (_cairo_int64_ge (qr.rem, den_det)) {
qr.quo = _cairo_int64_add (qr.quo,
_cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
} else
intersection->y.exactness = INEXACT;
}
#endif
intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
 
return TRUE;
}
 
static int
_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
int32_t b)
{
/* First compare the quotient */
if (a.ordinate > b)
return +1;
if (a.ordinate < b)
return -1;
/* With quotient identical, if remainder is 0 then compare equal */
/* Otherwise, the non-zero remainder makes a > b */
return INEXACT == a.exactness;
}
 
/* Does the given edge contain the given point. The point must already
* be known to be contained within the line determined by the edge,
* (most likely the point results from an intersection of this edge
* with another).
*
* If we had exact arithmetic, then this function would simply be a
* matter of examining whether the y value of the point lies within
* the range of y values of the edge. But since intersection points
* are not exact due to being rounded to the nearest integer within
* the available precision, we must also examine the x value of the
* point.
*
* The definition of "contains" here is that the given intersection
* point will be seen by the sweep line after the start event for the
* given edge and before the stop event for the edge. See the comments
* in the implementation for more details.
*/
static cairo_bool_t
_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge,
cairo_bo_intersect_point_t *point)
{
int cmp_top, cmp_bottom;
 
/* XXX: When running the actual algorithm, we don't actually need to
* compare against edge->top at all here, since any intersection above
* top is eliminated early via a slope comparison. We're leaving these
* here for now only for the sake of the quadratic-time intersection
* finder which needs them.
*/
 
cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y,
edge->edge.top);
cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y,
edge->edge.bottom);
 
if (cmp_top < 0 || cmp_bottom > 0)
{
return FALSE;
}
 
if (cmp_top > 0 && cmp_bottom < 0)
{
return TRUE;
}
 
/* At this stage, the point lies on the same y value as either
* edge->top or edge->bottom, so we have to examine the x value in
* order to properly determine containment. */
 
/* If the y value of the point is the same as the y value of the
* top of the edge, then the x value of the point must be greater
* to be considered as inside the edge. Similarly, if the y value
* of the point is the same as the y value of the bottom of the
* edge, then the x value of the point must be less to be
* considered as inside. */
 
if (cmp_top == 0) {
cairo_fixed_t top_x;
 
top_x = _line_compute_intersection_x_for_y (&edge->edge.line,
edge->edge.top);
return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0;
} else { /* cmp_bottom == 0 */
cairo_fixed_t bot_x;
 
bot_x = _line_compute_intersection_x_for_y (&edge->edge.line,
edge->edge.bottom);
return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0;
}
}
 
/* Compute the intersection of two edges. The result is provided as a
* coordinate pair of 128-bit integers.
*
* Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection
* that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the
* intersection of the lines defined by the edges occurs outside of
* one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges
* are exactly parallel.
*
* Note that when determining if a candidate intersection is "inside"
* an edge, we consider both the infinitesimal shortening and the
* infinitesimal tilt rules described by John Hobby. Specifically, if
* the intersection is exactly the same as an edge point, it is
* effectively outside (no intersection is returned). Also, if the
* intersection point has the same
*/
static cairo_bool_t
_cairo_bo_edge_intersect (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_point32_t *intersection)
{
cairo_bo_intersect_point_t quorem;
 
if (! intersect_lines (a, b, &quorem))
return FALSE;
 
if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
return FALSE;
 
if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
return FALSE;
 
/* Now that we've correctly compared the intersection point and
* determined that it lies within the edge, then we know that we
* no longer need any more bits of storage for the intersection
* than we do for our edge coordinates. We also no longer need the
* remainder from the division. */
intersection->x = quorem.x.ordinate;
intersection->y = quorem.y.ordinate;
 
return TRUE;
}
 
static inline int
cairo_bo_event_compare (const cairo_bo_event_t *a,
const cairo_bo_event_t *b)
{
int cmp;
 
cmp = _cairo_bo_point32_compare (&a->point, &b->point);
if (cmp)
return cmp;
 
cmp = a->type - b->type;
if (cmp)
return cmp;
 
return a - b;
}
 
static inline void
_pqueue_init (pqueue_t *pq)
{
pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
pq->size = 0;
 
pq->elements = pq->elements_embedded;
}
 
static inline void
_pqueue_fini (pqueue_t *pq)
{
if (pq->elements != pq->elements_embedded)
free (pq->elements);
}
 
static cairo_status_t
_pqueue_grow (pqueue_t *pq)
{
cairo_bo_event_t **new_elements;
pq->max_size *= 2;
 
if (pq->elements == pq->elements_embedded) {
new_elements = _cairo_malloc_ab (pq->max_size,
sizeof (cairo_bo_event_t *));
if (unlikely (new_elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (new_elements, pq->elements_embedded,
sizeof (pq->elements_embedded));
} else {
new_elements = _cairo_realloc_ab (pq->elements,
pq->max_size,
sizeof (cairo_bo_event_t *));
if (unlikely (new_elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pq->elements = new_elements;
return CAIRO_STATUS_SUCCESS;
}
 
static inline cairo_status_t
_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event)
{
cairo_bo_event_t **elements;
int i, parent;
 
if (unlikely (pq->size + 1 == pq->max_size)) {
cairo_status_t status;
 
status = _pqueue_grow (pq);
if (unlikely (status))
return status;
}
 
elements = pq->elements;
 
for (i = ++pq->size;
i != PQ_FIRST_ENTRY &&
cairo_bo_event_compare (event,
elements[parent = PQ_PARENT_INDEX (i)]) < 0;
i = parent)
{
elements[i] = elements[parent];
}
 
elements[i] = event;
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline void
_pqueue_pop (pqueue_t *pq)
{
cairo_bo_event_t **elements = pq->elements;
cairo_bo_event_t *tail;
int child, i;
 
tail = elements[pq->size--];
if (pq->size == 0) {
elements[PQ_FIRST_ENTRY] = NULL;
return;
}
 
for (i = PQ_FIRST_ENTRY;
(child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
i = child)
{
if (child != pq->size &&
cairo_bo_event_compare (elements[child+1],
elements[child]) < 0)
{
child++;
}
 
if (cairo_bo_event_compare (elements[child], tail) >= 0)
break;
 
elements[i] = elements[child];
}
elements[i] = tail;
}
 
static inline cairo_status_t
_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue,
cairo_bo_event_type_t type,
cairo_bo_edge_t *e1,
cairo_bo_edge_t *e2,
const cairo_point_t *point)
{
cairo_bo_queue_event_t *event;
 
event = _cairo_freepool_alloc (&queue->pool);
if (unlikely (event == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
event->type = type;
event->e1 = e1;
event->e2 = e2;
event->point = *point;
 
return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event);
}
 
static void
_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
cairo_bo_event_t *event)
{
_cairo_freepool_free (&queue->pool, event);
}
 
static cairo_bo_event_t *
_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
{
cairo_bo_event_t *event, *cmp;
 
event = event_queue->pqueue.elements[PQ_FIRST_ENTRY];
cmp = *event_queue->start_events;
if (event == NULL ||
(cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
{
event = cmp;
event_queue->start_events++;
}
else
{
_pqueue_pop (&event_queue->pqueue);
}
 
return event;
}
 
CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
cairo_bo_event_t *,
cairo_bo_event_compare)
 
static void
_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue,
cairo_bo_event_t **start_events,
int num_events)
{
_cairo_bo_event_queue_sort (start_events, num_events);
start_events[num_events] = NULL;
 
event_queue->start_events = start_events;
 
_cairo_freepool_init (&event_queue->pool,
sizeof (cairo_bo_queue_event_t));
_pqueue_init (&event_queue->pqueue);
event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
}
 
static cairo_status_t
event_queue_insert_stop (cairo_bo_event_queue_t *event_queue,
cairo_bo_edge_t *edge)
{
cairo_bo_point32_t point;
 
point.y = edge->edge.bottom;
point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
point.y);
return _cairo_bo_event_queue_insert (event_queue,
CAIRO_BO_EVENT_TYPE_STOP,
edge, NULL,
&point);
}
 
static void
_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
{
_pqueue_fini (&event_queue->pqueue);
_cairo_freepool_fini (&event_queue->pool);
}
 
static inline cairo_status_t
event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue,
cairo_bo_edge_t *left,
cairo_bo_edge_t *right)
{
cairo_bo_point32_t intersection;
 
if (_line_equal (&left->edge.line, &right->edge.line))
return CAIRO_STATUS_SUCCESS;
 
/* The names "left" and "right" here are correct descriptions of
* the order of the two edges within the active edge list. So if a
* slope comparison also puts left less than right, then we know
* that the intersection of these two segments has already
* occurred before the current sweep line position. */
if (_slope_compare (left, right) <= 0)
return CAIRO_STATUS_SUCCESS;
 
if (! _cairo_bo_edge_intersect (left, right, &intersection))
return CAIRO_STATUS_SUCCESS;
 
return _cairo_bo_event_queue_insert (event_queue,
CAIRO_BO_EVENT_TYPE_INTERSECTION,
left, right,
&intersection);
}
 
static void
_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
{
sweep_line->head = NULL;
sweep_line->current_y = INT32_MIN;
sweep_line->current_edge = NULL;
}
 
static cairo_status_t
sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *edge)
{
if (sweep_line->current_edge != NULL) {
cairo_bo_edge_t *prev, *next;
int cmp;
 
cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
sweep_line->current_edge,
edge);
if (cmp < 0) {
prev = sweep_line->current_edge;
next = prev->next;
while (next != NULL &&
_cairo_bo_sweep_line_compare_edges (sweep_line,
next, edge) < 0)
{
prev = next, next = prev->next;
}
 
prev->next = edge;
edge->prev = prev;
edge->next = next;
if (next != NULL)
next->prev = edge;
} else if (cmp > 0) {
next = sweep_line->current_edge;
prev = next->prev;
while (prev != NULL &&
_cairo_bo_sweep_line_compare_edges (sweep_line,
prev, edge) > 0)
{
next = prev, prev = next->prev;
}
 
next->prev = edge;
edge->next = next;
edge->prev = prev;
if (prev != NULL)
prev->next = edge;
else
sweep_line->head = edge;
} else {
prev = sweep_line->current_edge;
edge->prev = prev;
edge->next = prev->next;
if (prev->next != NULL)
prev->next->prev = edge;
prev->next = edge;
}
} else {
sweep_line->head = edge;
}
 
sweep_line->current_edge = edge;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *edge)
{
if (edge->prev != NULL)
edge->prev->next = edge->next;
else
sweep_line->head = edge->next;
 
if (edge->next != NULL)
edge->next->prev = edge->prev;
 
if (sweep_line->current_edge == edge)
sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
}
 
static void
_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *left,
cairo_bo_edge_t *right)
{
if (left->prev != NULL)
left->prev->next = right;
else
sweep_line->head = right;
 
if (right->next != NULL)
right->next->prev = left;
 
left->next = right->next;
right->next = left;
 
right->prev = left->prev;
left->prev = right;
}
 
static inline cairo_bool_t
edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
{
if (_line_equal (&a->edge.line, &b->edge.line))
return TRUE;
 
if (_slope_compare (a, b))
return FALSE;
 
/* The choice of y is not truly arbitrary since we must guarantee that it
* is greater than the start of either line.
*/
if (a->edge.line.p1.y == b->edge.line.p1.y) {
return a->edge.line.p1.x == b->edge.line.p1.x;
} else if (a->edge.line.p1.y < b->edge.line.p1.y) {
return edge_compare_for_y_against_x (b,
a->edge.line.p1.y,
a->edge.line.p1.x) == 0;
} else {
return edge_compare_for_y_against_x (a,
b->edge.line.p1.y,
b->edge.line.p1.x) == 0;
}
}
 
static void
edges_end (cairo_bo_edge_t *left,
int32_t bot,
cairo_polygon_t *polygon)
{
cairo_bo_deferred_t *l = &left->deferred;
cairo_bo_edge_t *right = l->other;
 
assert(right->deferred.other == NULL);
if (likely (l->top < bot)) {
_cairo_polygon_add_line (polygon, &left->edge.line, l->top, bot, 1);
_cairo_polygon_add_line (polygon, &right->edge.line, l->top, bot, -1);
}
 
l->other = NULL;
}
 
static inline void
edges_start_or_continue (cairo_bo_edge_t *left,
cairo_bo_edge_t *right,
int top,
cairo_polygon_t *polygon)
{
assert (right->deferred.other == NULL);
 
if (left->deferred.other == right)
return;
 
if (left->deferred.other != NULL) {
if (right != NULL && edges_colinear (left->deferred.other, right)) {
cairo_bo_edge_t *old = left->deferred.other;
 
/* continuation on right, extend right to cover both */
assert (old->deferred.other == NULL);
assert (old->edge.line.p2.y > old->edge.line.p1.y);
 
if (old->edge.line.p1.y < right->edge.line.p1.y)
right->edge.line.p1 = old->edge.line.p1;
if (old->edge.line.p2.y > right->edge.line.p2.y)
right->edge.line.p2 = old->edge.line.p2;
left->deferred.other = right;
return;
}
 
edges_end (left, top, polygon);
}
 
if (right != NULL && ! edges_colinear (left, right)) {
left->deferred.top = top;
left->deferred.other = right;
}
}
 
#define is_zero(w) ((w)[0] == 0 || (w)[1] == 0)
 
static inline void
active_edges (cairo_bo_edge_t *left,
int32_t top,
cairo_polygon_t *polygon)
{
cairo_bo_edge_t *right;
int winding[2] = {0, 0};
 
/* Yes, this is naive. Consider this a placeholder. */
 
while (left != NULL) {
assert (is_zero (winding));
 
do {
winding[left->a_or_b] += left->edge.dir;
if (! is_zero (winding))
break;
 
if unlikely ((left->deferred.other))
edges_end (left, top, polygon);
 
left = left->next;
if (! left)
return;
} while (1);
 
right = left->next;
do {
if unlikely ((right->deferred.other))
edges_end (right, top, polygon);
 
winding[right->a_or_b] += right->edge.dir;
if (is_zero (winding)) {
if (right->next == NULL ||
! edges_colinear (right, right->next))
break;
}
 
right = right->next;
} while (1);
 
edges_start_or_continue (left, right, top, polygon);
 
left = right->next;
}
}
 
static cairo_status_t
intersection_sweep (cairo_bo_event_t **start_events,
int num_events,
cairo_polygon_t *polygon)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
cairo_bo_event_queue_t event_queue;
cairo_bo_sweep_line_t sweep_line;
cairo_bo_event_t *event;
cairo_bo_edge_t *left, *right;
cairo_bo_edge_t *e1, *e2;
 
_cairo_bo_event_queue_init (&event_queue, start_events, num_events);
_cairo_bo_sweep_line_init (&sweep_line);
 
while ((event = _cairo_bo_event_dequeue (&event_queue))) {
if (event->point.y != sweep_line.current_y) {
active_edges (sweep_line.head,
sweep_line.current_y,
polygon);
sweep_line.current_y = event->point.y;
}
 
switch (event->type) {
case CAIRO_BO_EVENT_TYPE_START:
e1 = &((cairo_bo_start_event_t *) event)->edge;
 
status = sweep_line_insert (&sweep_line, e1);
if (unlikely (status))
goto unwind;
 
status = event_queue_insert_stop (&event_queue, e1);
if (unlikely (status))
goto unwind;
 
left = e1->prev;
right = e1->next;
 
if (left != NULL) {
status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
if (unlikely (status))
goto unwind;
}
 
if (right != NULL) {
status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
if (unlikely (status))
goto unwind;
}
 
break;
 
case CAIRO_BO_EVENT_TYPE_STOP:
e1 = ((cairo_bo_queue_event_t *) event)->e1;
_cairo_bo_event_queue_delete (&event_queue, event);
 
if (e1->deferred.other)
edges_end (e1, sweep_line.current_y, polygon);
 
left = e1->prev;
right = e1->next;
 
_cairo_bo_sweep_line_delete (&sweep_line, e1);
 
if (left != NULL && right != NULL) {
status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
if (unlikely (status))
goto unwind;
}
 
break;
 
case CAIRO_BO_EVENT_TYPE_INTERSECTION:
e1 = ((cairo_bo_queue_event_t *) event)->e1;
e2 = ((cairo_bo_queue_event_t *) event)->e2;
_cairo_bo_event_queue_delete (&event_queue, event);
 
/* skip this intersection if its edges are not adjacent */
if (e2 != e1->next)
break;
 
if (e1->deferred.other)
edges_end (e1, sweep_line.current_y, polygon);
if (e2->deferred.other)
edges_end (e2, sweep_line.current_y, polygon);
 
left = e1->prev;
right = e2->next;
 
_cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
 
/* after the swap e2 is left of e1 */
 
if (left != NULL) {
status = event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
if (unlikely (status))
goto unwind;
}
 
if (right != NULL) {
status = event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
if (unlikely (status))
goto unwind;
}
 
break;
}
}
 
unwind:
_cairo_bo_event_queue_fini (&event_queue);
 
return status;
}
 
cairo_status_t
_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
cairo_polygon_t *b, int winding_b)
{
cairo_status_t status;
cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
cairo_bo_start_event_t *events;
cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
cairo_bo_event_t **event_ptrs;
int num_events;
int i, j;
 
/* XXX lazy */
if (winding_a != CAIRO_FILL_RULE_WINDING) {
status = _cairo_polygon_reduce (a, winding_a);
if (unlikely (status))
return status;
}
 
if (winding_b != CAIRO_FILL_RULE_WINDING) {
status = _cairo_polygon_reduce (b, winding_b);
if (unlikely (status))
return status;
}
 
if (unlikely (0 == a->num_edges))
return CAIRO_STATUS_SUCCESS;
 
if (unlikely (0 == b->num_edges)) {
a->num_edges = 0;
return CAIRO_STATUS_SUCCESS;
}
 
events = stack_events;
event_ptrs = stack_event_ptrs;
num_events = a->num_edges + b->num_edges;
if (num_events > ARRAY_LENGTH (stack_events)) {
events = _cairo_malloc_ab_plus_c (num_events,
sizeof (cairo_bo_start_event_t) +
sizeof (cairo_bo_event_t *),
sizeof (cairo_bo_event_t *));
if (unlikely (events == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
event_ptrs = (cairo_bo_event_t **) (events + num_events);
}
 
j = 0;
for (i = 0; i < a->num_edges; i++) {
event_ptrs[j] = (cairo_bo_event_t *) &events[j];
 
events[j].type = CAIRO_BO_EVENT_TYPE_START;
events[j].point.y = a->edges[i].top;
events[j].point.x =
_line_compute_intersection_x_for_y (&a->edges[i].line,
events[j].point.y);
 
events[j].edge.a_or_b = 0;
events[j].edge.edge = a->edges[i];
events[j].edge.deferred.other = NULL;
events[j].edge.prev = NULL;
events[j].edge.next = NULL;
j++;
}
 
for (i = 0; i < b->num_edges; i++) {
event_ptrs[j] = (cairo_bo_event_t *) &events[j];
 
events[j].type = CAIRO_BO_EVENT_TYPE_START;
events[j].point.y = b->edges[i].top;
events[j].point.x =
_line_compute_intersection_x_for_y (&b->edges[i].line,
events[j].point.y);
 
events[j].edge.a_or_b = 1;
events[j].edge.edge = b->edges[i];
events[j].edge.deferred.other = NULL;
events[j].edge.prev = NULL;
events[j].edge.next = NULL;
j++;
}
assert (j == num_events);
 
#if 0
{
FILE *file = fopen ("clip_a.txt", "w");
_cairo_debug_print_polygon (file, a);
fclose (file);
}
{
FILE *file = fopen ("clip_b.txt", "w");
_cairo_debug_print_polygon (file, b);
fclose (file);
}
#endif
 
a->num_edges = 0;
status = intersection_sweep (event_ptrs, num_events, a);
if (events != stack_events)
free (events);
 
#if 0
{
FILE *file = fopen ("clip_result.txt", "w");
_cairo_debug_print_polygon (file, a);
fclose (file);
}
#endif
 
return status;
}
 
cairo_status_t
_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
cairo_fill_rule_t *winding,
cairo_box_t *boxes,
int num_boxes)
{
cairo_polygon_t b;
cairo_status_t status;
int n;
 
if (num_boxes == 0) {
polygon->num_edges = 0;
return CAIRO_STATUS_SUCCESS;
}
 
for (n = 0; n < num_boxes; n++) {
if (polygon->extents.p1.x >= boxes[n].p1.x &&
polygon->extents.p2.x <= boxes[n].p2.x &&
polygon->extents.p1.y >= boxes[n].p1.y &&
polygon->extents.p2.y <= boxes[n].p2.y)
{
return CAIRO_STATUS_SUCCESS;
}
}
 
_cairo_polygon_init (&b, NULL, 0);
for (n = 0; n < num_boxes; n++) {
if (boxes[n].p2.x > polygon->extents.p1.x &&
boxes[n].p1.x < polygon->extents.p2.x &&
boxes[n].p2.y > polygon->extents.p1.y &&
boxes[n].p1.y < polygon->extents.p2.y)
{
cairo_point_t p1, p2;
 
p1.y = boxes[n].p1.y;
p2.y = boxes[n].p2.y;
 
p2.x = p1.x = boxes[n].p1.x;
_cairo_polygon_add_external_edge (&b, &p1, &p2);
 
p2.x = p1.x = boxes[n].p2.x;
_cairo_polygon_add_external_edge (&b, &p2, &p1);
}
}
 
status = _cairo_polygon_intersect (polygon, *winding,
&b, CAIRO_FILL_RULE_WINDING);
_cairo_polygon_fini (&b);
 
*winding = CAIRO_FILL_RULE_WINDING;
return status;
}
/programs/develop/libraries/cairo/src/cairo-polygon-reduce.c
0,0 → 1,1438
/*
* Copyright © 2004 Carl Worth
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2008 Chris Wilson
*
* 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 Carl Worth
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* Provide definitions for standalone compilation */
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-combsort-inline.h"
 
#define DEBUG_POLYGON 0
 
typedef cairo_point_t cairo_bo_point32_t;
 
typedef struct _cairo_bo_intersect_ordinate {
int32_t ordinate;
enum { EXACT, INEXACT } exactness;
} cairo_bo_intersect_ordinate_t;
 
typedef struct _cairo_bo_intersect_point {
cairo_bo_intersect_ordinate_t x;
cairo_bo_intersect_ordinate_t y;
} cairo_bo_intersect_point_t;
 
typedef struct _cairo_bo_edge cairo_bo_edge_t;
 
typedef struct _cairo_bo_deferred {
cairo_bo_edge_t *right;
int32_t top;
} cairo_bo_deferred_t;
 
struct _cairo_bo_edge {
cairo_edge_t edge;
cairo_bo_edge_t *prev;
cairo_bo_edge_t *next;
cairo_bo_deferred_t deferred;
};
 
/* the parent is always given by index/2 */
#define PQ_PARENT_INDEX(i) ((i) >> 1)
#define PQ_FIRST_ENTRY 1
 
/* left and right children are index * 2 and (index * 2) +1 respectively */
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
 
typedef enum {
CAIRO_BO_EVENT_TYPE_STOP,
CAIRO_BO_EVENT_TYPE_INTERSECTION,
CAIRO_BO_EVENT_TYPE_START
} cairo_bo_event_type_t;
 
typedef struct _cairo_bo_event {
cairo_bo_event_type_t type;
cairo_point_t point;
} cairo_bo_event_t;
 
typedef struct _cairo_bo_start_event {
cairo_bo_event_type_t type;
cairo_point_t point;
cairo_bo_edge_t edge;
} cairo_bo_start_event_t;
 
typedef struct _cairo_bo_queue_event {
cairo_bo_event_type_t type;
cairo_point_t point;
cairo_bo_edge_t *e1;
cairo_bo_edge_t *e2;
} cairo_bo_queue_event_t;
 
typedef struct _pqueue {
int size, max_size;
 
cairo_bo_event_t **elements;
cairo_bo_event_t *elements_embedded[1024];
} pqueue_t;
 
typedef struct _cairo_bo_event_queue {
cairo_freepool_t pool;
pqueue_t pqueue;
cairo_bo_event_t **start_events;
} cairo_bo_event_queue_t;
 
typedef struct _cairo_bo_sweep_line {
cairo_bo_edge_t *head;
int32_t current_y;
cairo_bo_edge_t *current_edge;
} cairo_bo_sweep_line_t;
 
static cairo_fixed_t
_line_compute_intersection_x_for_y (const cairo_line_t *line,
cairo_fixed_t y)
{
cairo_fixed_t x, dy;
 
if (y == line->p1.y)
return line->p1.x;
if (y == line->p2.y)
return line->p2.x;
 
x = line->p1.x;
dy = line->p2.y - line->p1.y;
if (dy != 0) {
x += _cairo_fixed_mul_div_floor (y - line->p1.y,
line->p2.x - line->p1.x,
dy);
}
 
return x;
}
 
static inline int
_cairo_bo_point32_compare (cairo_bo_point32_t const *a,
cairo_bo_point32_t const *b)
{
int cmp;
 
cmp = a->y - b->y;
if (cmp)
return cmp;
 
return a->x - b->x;
}
 
/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the
* slope a is respectively greater than, equal to, or less than the
* slope of b.
*
* For each edge, consider the direction vector formed from:
*
* top -> bottom
*
* which is:
*
* (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y)
*
* We then define the slope of each edge as dx/dy, (which is the
* inverse of the slope typically used in math instruction). We never
* compute a slope directly as the value approaches infinity, but we
* can derive a slope comparison without division as follows, (where
* the ? represents our compare operator).
*
* 1. slope(a) ? slope(b)
* 2. adx/ady ? bdx/bdy
* 3. (adx * bdy) ? (bdx * ady)
*
* Note that from step 2 to step 3 there is no change needed in the
* sign of the result since both ady and bdy are guaranteed to be
* greater than or equal to 0.
*
* When using this slope comparison to sort edges, some care is needed
* when interpreting the results. Since the slope compare operates on
* distance vectors from top to bottom it gives a correct left to
* right sort for edges that have a common top point, (such as two
* edges with start events at the same location). On the other hand,
* the sense of the result will be exactly reversed for two edges that
* have a common stop point.
*/
static inline int
_slope_compare (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b)
{
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm
* begins.
*/
int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
 
/* Since the dy's are all positive by construction we can fast
* path several common cases.
*/
 
/* First check for vertical lines. */
if (adx == 0)
return -bdx;
if (bdx == 0)
return adx;
 
/* Then where the two edges point in different directions wrt x. */
if ((adx ^ bdx) < 0)
return adx;
 
/* Finally we actually need to do the general comparison. */
{
int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
 
return _cairo_int64_cmp (adx_bdy, bdx_ady);
}
}
 
/*
* We need to compare the x-coordinates of a pair of lines for a particular y,
* without loss of precision.
*
* The x-coordinate along an edge for a given y is:
* X = A_x + (Y - A_y) * A_dx / A_dy
*
* So the inequality we wish to test is:
* A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy,
* where ∘ is our inequality operator.
*
* By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are
* all positive, so we can rearrange it thus without causing a sign change:
* A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy
* - (Y - A_y) * A_dx * B_dy
*
* Given the assumption that all the deltas fit within 32 bits, we can compute
* this comparison directly using 128 bit arithmetic. For certain, but common,
* input we can reduce this down to a single 32 bit compare by inspecting the
* deltas.
*
* (And put the burden of the work on developing fast 128 bit ops, which are
* required throughout the tessellator.)
*
* See the similar discussion for _slope_compare().
*/
static int
edges_compare_x_for_y_general (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b,
int32_t y)
{
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm
* begins.
*/
int32_t dx;
int32_t adx, ady;
int32_t bdx, bdy;
enum {
HAVE_NONE = 0x0,
HAVE_DX = 0x1,
HAVE_ADX = 0x2,
HAVE_DX_ADX = HAVE_DX | HAVE_ADX,
HAVE_BDX = 0x4,
HAVE_DX_BDX = HAVE_DX | HAVE_BDX,
HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX
} have_dx_adx_bdx = HAVE_ALL;
 
/* don't bother solving for abscissa if the edges' bounding boxes
* can be used to order them. */
{
int32_t amin, amax;
int32_t bmin, bmax;
if (a->edge.line.p1.x < a->edge.line.p2.x) {
amin = a->edge.line.p1.x;
amax = a->edge.line.p2.x;
} else {
amin = a->edge.line.p2.x;
amax = a->edge.line.p1.x;
}
if (b->edge.line.p1.x < b->edge.line.p2.x) {
bmin = b->edge.line.p1.x;
bmax = b->edge.line.p2.x;
} else {
bmin = b->edge.line.p2.x;
bmax = b->edge.line.p1.x;
}
if (amax < bmin) return -1;
if (amin > bmax) return +1;
}
 
ady = a->edge.line.p2.y - a->edge.line.p1.y;
adx = a->edge.line.p2.x - a->edge.line.p1.x;
if (adx == 0)
have_dx_adx_bdx &= ~HAVE_ADX;
 
bdy = b->edge.line.p2.y - b->edge.line.p1.y;
bdx = b->edge.line.p2.x - b->edge.line.p1.x;
if (bdx == 0)
have_dx_adx_bdx &= ~HAVE_BDX;
 
dx = a->edge.line.p1.x - b->edge.line.p1.x;
if (dx == 0)
have_dx_adx_bdx &= ~HAVE_DX;
 
#define L _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (ady, bdy), dx)
#define A _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (adx, bdy), y - a->edge.line.p1.y)
#define B _cairo_int64x32_128_mul (_cairo_int32x32_64_mul (bdx, ady), y - b->edge.line.p1.y)
switch (have_dx_adx_bdx) {
default:
case HAVE_NONE:
return 0;
case HAVE_DX:
/* A_dy * B_dy * (A_x - B_x) ∘ 0 */
return dx; /* ady * bdy is positive definite */
case HAVE_ADX:
/* 0 ∘ - (Y - A_y) * A_dx * B_dy */
return adx; /* bdy * (y - a->top.y) is positive definite */
case HAVE_BDX:
/* 0 ∘ (Y - B_y) * B_dx * A_dy */
return -bdx; /* ady * (y - b->top.y) is positive definite */
case HAVE_ADX_BDX:
/* 0 ∘ (Y - B_y) * B_dx * A_dy - (Y - A_y) * A_dx * B_dy */
if ((adx ^ bdx) < 0) {
return adx;
} else if (a->edge.line.p1.y == b->edge.line.p1.y) { /* common origin */
cairo_int64_t adx_bdy, bdx_ady;
 
/* ∴ A_dx * B_dy ∘ B_dx * A_dy */
 
adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
 
return _cairo_int64_cmp (adx_bdy, bdx_ady);
} else
return _cairo_int128_cmp (A, B);
case HAVE_DX_ADX:
/* A_dy * (A_x - B_x) ∘ - (Y - A_y) * A_dx */
if ((-adx ^ dx) < 0) {
return dx;
} else {
cairo_int64_t ady_dx, dy_adx;
 
ady_dx = _cairo_int32x32_64_mul (ady, dx);
dy_adx = _cairo_int32x32_64_mul (a->edge.line.p1.y - y, adx);
 
return _cairo_int64_cmp (ady_dx, dy_adx);
}
case HAVE_DX_BDX:
/* B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx */
if ((bdx ^ dx) < 0) {
return dx;
} else {
cairo_int64_t bdy_dx, dy_bdx;
 
bdy_dx = _cairo_int32x32_64_mul (bdy, dx);
dy_bdx = _cairo_int32x32_64_mul (y - b->edge.line.p1.y, bdx);
 
return _cairo_int64_cmp (bdy_dx, dy_bdx);
}
case HAVE_ALL:
/* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */
return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
}
#undef B
#undef A
#undef L
}
 
/*
* We need to compare the x-coordinate of a line for a particular y wrt to a
* given x, without loss of precision.
*
* The x-coordinate along an edge for a given y is:
* X = A_x + (Y - A_y) * A_dx / A_dy
*
* So the inequality we wish to test is:
* A_x + (Y - A_y) * A_dx / A_dy ∘ X
* where ∘ is our inequality operator.
*
* By construction, we know that A_dy (and (Y - A_y)) are
* all positive, so we can rearrange it thus without causing a sign change:
* (Y - A_y) * A_dx ∘ (X - A_x) * A_dy
*
* Given the assumption that all the deltas fit within 32 bits, we can compute
* this comparison directly using 64 bit arithmetic.
*
* See the similar discussion for _slope_compare() and
* edges_compare_x_for_y_general().
*/
static int
edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
int32_t y,
int32_t x)
{
int32_t adx, ady;
int32_t dx, dy;
cairo_int64_t L, R;
 
if (x < a->edge.line.p1.x && x < a->edge.line.p2.x)
return 1;
if (x > a->edge.line.p1.x && x > a->edge.line.p2.x)
return -1;
 
adx = a->edge.line.p2.x - a->edge.line.p1.x;
dx = x - a->edge.line.p1.x;
 
if (adx == 0)
return -dx;
if (dx == 0 || (adx ^ dx) < 0)
return adx;
 
dy = y - a->edge.line.p1.y;
ady = a->edge.line.p2.y - a->edge.line.p1.y;
 
L = _cairo_int32x32_64_mul (dy, adx);
R = _cairo_int32x32_64_mul (dx, ady);
 
return _cairo_int64_cmp (L, R);
}
 
static int
edges_compare_x_for_y (const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b,
int32_t y)
{
/* If the sweep-line is currently on an end-point of a line,
* then we know its precise x value (and considering that we often need to
* compare events at end-points, this happens frequently enough to warrant
* special casing).
*/
enum {
HAVE_NEITHER = 0x0,
HAVE_AX = 0x1,
HAVE_BX = 0x2,
HAVE_BOTH = HAVE_AX | HAVE_BX
} have_ax_bx = HAVE_BOTH;
int32_t ax, bx;
 
if (y == a->edge.line.p1.y)
ax = a->edge.line.p1.x;
else if (y == a->edge.line.p2.y)
ax = a->edge.line.p2.x;
else
have_ax_bx &= ~HAVE_AX;
 
if (y == b->edge.line.p1.y)
bx = b->edge.line.p1.x;
else if (y == b->edge.line.p2.y)
bx = b->edge.line.p2.x;
else
have_ax_bx &= ~HAVE_BX;
 
switch (have_ax_bx) {
default:
case HAVE_NEITHER:
return edges_compare_x_for_y_general (a, b, y);
case HAVE_AX:
return -edge_compare_for_y_against_x (b, y, ax);
case HAVE_BX:
return edge_compare_for_y_against_x (a, y, bx);
case HAVE_BOTH:
return ax - bx;
}
}
 
static inline int
_line_equal (const cairo_line_t *a, const cairo_line_t *b)
{
return (a->p1.x == b->p1.x && a->p1.y == b->p1.y &&
a->p2.x == b->p2.x && a->p2.y == b->p2.y);
}
 
static int
_cairo_bo_sweep_line_compare_edges (cairo_bo_sweep_line_t *sweep_line,
const cairo_bo_edge_t *a,
const cairo_bo_edge_t *b)
{
int cmp;
 
/* compare the edges if not identical */
if (! _line_equal (&a->edge.line, &b->edge.line)) {
cmp = edges_compare_x_for_y (a, b, sweep_line->current_y);
if (cmp)
return cmp;
 
/* The two edges intersect exactly at y, so fall back on slope
* comparison. We know that this compare_edges function will be
* called only when starting a new edge, (not when stopping an
* edge), so we don't have to worry about conditionally inverting
* the sense of _slope_compare. */
cmp = _slope_compare (a, b);
if (cmp)
return cmp;
}
 
/* We've got two collinear edges now. */
return b->edge.bottom - a->edge.bottom;
}
 
static inline cairo_int64_t
det32_64 (int32_t a, int32_t b,
int32_t c, int32_t d)
{
/* det = a * d - b * c */
return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
_cairo_int32x32_64_mul (b, c));
}
 
static inline cairo_int128_t
det64x32_128 (cairo_int64_t a, int32_t b,
cairo_int64_t c, int32_t d)
{
/* det = a * d - b * c */
return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
_cairo_int64x32_128_mul (c, b));
}
 
/* Compute the intersection of two lines as defined by two edges. The
* result is provided as a coordinate pair of 128-bit integers.
*
* Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or
* %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
*/
static cairo_bool_t
intersect_lines (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_intersect_point_t *intersection)
{
cairo_int64_t a_det, b_det;
 
/* XXX: We're assuming here that dx and dy will still fit in 32
* bits. That's not true in general as there could be overflow. We
* should prevent that before the tessellation algorithm begins.
* What we're doing to mitigate this is to perform clamping in
* cairo_bo_tessellate_polygon().
*/
int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
 
int32_t dx2 = b->edge.line.p1.x - b->edge.line.p2.x;
int32_t dy2 = b->edge.line.p1.y - b->edge.line.p2.y;
 
cairo_int64_t den_det;
cairo_int64_t R;
cairo_quorem64_t qr;
 
den_det = det32_64 (dx1, dy1, dx2, dy2);
 
/* Q: Can we determine that the lines do not intersect (within range)
* much more cheaply than computing the intersection point i.e. by
* avoiding the division?
*
* X = ax + t * adx = bx + s * bdx;
* Y = ay + t * ady = by + s * bdy;
* ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx)
* => t * L = R
*
* Therefore we can reject any intersection (under the criteria for
* valid intersection events) if:
* L^R < 0 => t < 0, or
* L<R => t > 1
*
* (where top/bottom must at least extend to the line endpoints).
*
* A similar substitution can be performed for s, yielding:
* s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
*/
R = det32_64 (dx2, dy2,
b->edge.line.p1.x - a->edge.line.p1.x,
b->edge.line.p1.y - a->edge.line.p1.y);
if (_cairo_int64_negative (den_det)) {
if (_cairo_int64_ge (den_det, R))
return FALSE;
} else {
if (_cairo_int64_le (den_det, R))
return FALSE;
}
 
R = det32_64 (dy1, dx1,
a->edge.line.p1.y - b->edge.line.p1.y,
a->edge.line.p1.x - b->edge.line.p1.x);
if (_cairo_int64_negative (den_det)) {
if (_cairo_int64_ge (den_det, R))
return FALSE;
} else {
if (_cairo_int64_le (den_det, R))
return FALSE;
}
 
/* We now know that the two lines should intersect within range. */
 
a_det = det32_64 (a->edge.line.p1.x, a->edge.line.p1.y,
a->edge.line.p2.x, a->edge.line.p2.y);
b_det = det32_64 (b->edge.line.p1.x, b->edge.line.p1.y,
b->edge.line.p2.x, b->edge.line.p2.y);
 
/* x = det (a_det, dx1, b_det, dx2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
b_det, dx2),
den_det);
if (_cairo_int64_eq (qr.rem, den_det))
return FALSE;
#if 0
intersection->x.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
#else
intersection->x.exactness = EXACT;
if (! _cairo_int64_is_zero (qr.rem)) {
if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
qr.rem = _cairo_int64_negate (qr.rem);
qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
if (_cairo_int64_ge (qr.rem, den_det)) {
qr.quo = _cairo_int64_add (qr.quo,
_cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
} else
intersection->x.exactness = INEXACT;
}
#endif
intersection->x.ordinate = _cairo_int64_to_int32 (qr.quo);
 
/* y = det (a_det, dy1, b_det, dy2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
b_det, dy2),
den_det);
if (_cairo_int64_eq (qr.rem, den_det))
return FALSE;
#if 0
intersection->y.exactness = _cairo_int64_is_zero (qr.rem) ? EXACT : INEXACT;
#else
intersection->y.exactness = EXACT;
if (! _cairo_int64_is_zero (qr.rem)) {
if (_cairo_int64_negative (den_det) ^ _cairo_int64_negative (qr.rem))
qr.rem = _cairo_int64_negate (qr.rem);
qr.rem = _cairo_int64_mul (qr.rem, _cairo_int32_to_int64 (2));
if (_cairo_int64_ge (qr.rem, den_det)) {
qr.quo = _cairo_int64_add (qr.quo,
_cairo_int32_to_int64 (_cairo_int64_negative (qr.quo) ? -1 : 1));
} else
intersection->y.exactness = INEXACT;
}
#endif
intersection->y.ordinate = _cairo_int64_to_int32 (qr.quo);
 
return TRUE;
}
 
static int
_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
int32_t b)
{
/* First compare the quotient */
if (a.ordinate > b)
return +1;
if (a.ordinate < b)
return -1;
/* With quotient identical, if remainder is 0 then compare equal */
/* Otherwise, the non-zero remainder makes a > b */
return INEXACT == a.exactness;
}
 
/* Does the given edge contain the given point. The point must already
* be known to be contained within the line determined by the edge,
* (most likely the point results from an intersection of this edge
* with another).
*
* If we had exact arithmetic, then this function would simply be a
* matter of examining whether the y value of the point lies within
* the range of y values of the edge. But since intersection points
* are not exact due to being rounded to the nearest integer within
* the available precision, we must also examine the x value of the
* point.
*
* The definition of "contains" here is that the given intersection
* point will be seen by the sweep line after the start event for the
* given edge and before the stop event for the edge. See the comments
* in the implementation for more details.
*/
static cairo_bool_t
_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge,
cairo_bo_intersect_point_t *point)
{
int cmp_top, cmp_bottom;
 
/* XXX: When running the actual algorithm, we don't actually need to
* compare against edge->top at all here, since any intersection above
* top is eliminated early via a slope comparison. We're leaving these
* here for now only for the sake of the quadratic-time intersection
* finder which needs them.
*/
 
cmp_top = _cairo_bo_intersect_ordinate_32_compare (point->y,
edge->edge.top);
cmp_bottom = _cairo_bo_intersect_ordinate_32_compare (point->y,
edge->edge.bottom);
 
if (cmp_top < 0 || cmp_bottom > 0)
{
return FALSE;
}
 
if (cmp_top > 0 && cmp_bottom < 0)
{
return TRUE;
}
 
/* At this stage, the point lies on the same y value as either
* edge->top or edge->bottom, so we have to examine the x value in
* order to properly determine containment. */
 
/* If the y value of the point is the same as the y value of the
* top of the edge, then the x value of the point must be greater
* to be considered as inside the edge. Similarly, if the y value
* of the point is the same as the y value of the bottom of the
* edge, then the x value of the point must be less to be
* considered as inside. */
 
if (cmp_top == 0) {
cairo_fixed_t top_x;
 
top_x = _line_compute_intersection_x_for_y (&edge->edge.line,
edge->edge.top);
return _cairo_bo_intersect_ordinate_32_compare (point->x, top_x) > 0;
} else { /* cmp_bottom == 0 */
cairo_fixed_t bot_x;
 
bot_x = _line_compute_intersection_x_for_y (&edge->edge.line,
edge->edge.bottom);
return _cairo_bo_intersect_ordinate_32_compare (point->x, bot_x) < 0;
}
}
 
/* Compute the intersection of two edges. The result is provided as a
* coordinate pair of 128-bit integers.
*
* Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection
* that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the
* intersection of the lines defined by the edges occurs outside of
* one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges
* are exactly parallel.
*
* Note that when determining if a candidate intersection is "inside"
* an edge, we consider both the infinitesimal shortening and the
* infinitesimal tilt rules described by John Hobby. Specifically, if
* the intersection is exactly the same as an edge point, it is
* effectively outside (no intersection is returned). Also, if the
* intersection point has the same
*/
static cairo_bool_t
_cairo_bo_edge_intersect (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_point32_t *intersection)
{
cairo_bo_intersect_point_t quorem;
 
if (! intersect_lines (a, b, &quorem))
return FALSE;
 
if (! _cairo_bo_edge_contains_intersect_point (a, &quorem))
return FALSE;
 
if (! _cairo_bo_edge_contains_intersect_point (b, &quorem))
return FALSE;
 
/* Now that we've correctly compared the intersection point and
* determined that it lies within the edge, then we know that we
* no longer need any more bits of storage for the intersection
* than we do for our edge coordinates. We also no longer need the
* remainder from the division. */
intersection->x = quorem.x.ordinate;
intersection->y = quorem.y.ordinate;
 
return TRUE;
}
 
static inline int
cairo_bo_event_compare (const cairo_bo_event_t *a,
const cairo_bo_event_t *b)
{
int cmp;
 
cmp = _cairo_bo_point32_compare (&a->point, &b->point);
if (cmp)
return cmp;
 
cmp = a->type - b->type;
if (cmp)
return cmp;
 
return a - b;
}
 
static inline void
_pqueue_init (pqueue_t *pq)
{
pq->max_size = ARRAY_LENGTH (pq->elements_embedded);
pq->size = 0;
 
pq->elements = pq->elements_embedded;
}
 
static inline void
_pqueue_fini (pqueue_t *pq)
{
if (pq->elements != pq->elements_embedded)
free (pq->elements);
}
 
static cairo_status_t
_pqueue_grow (pqueue_t *pq)
{
cairo_bo_event_t **new_elements;
pq->max_size *= 2;
 
if (pq->elements == pq->elements_embedded) {
new_elements = _cairo_malloc_ab (pq->max_size,
sizeof (cairo_bo_event_t *));
if (unlikely (new_elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (new_elements, pq->elements_embedded,
sizeof (pq->elements_embedded));
} else {
new_elements = _cairo_realloc_ab (pq->elements,
pq->max_size,
sizeof (cairo_bo_event_t *));
if (unlikely (new_elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pq->elements = new_elements;
return CAIRO_STATUS_SUCCESS;
}
 
static inline cairo_status_t
_pqueue_push (pqueue_t *pq, cairo_bo_event_t *event)
{
cairo_bo_event_t **elements;
int i, parent;
 
if (unlikely (pq->size + 1 == pq->max_size)) {
cairo_status_t status;
 
status = _pqueue_grow (pq);
if (unlikely (status))
return status;
}
 
elements = pq->elements;
 
for (i = ++pq->size;
i != PQ_FIRST_ENTRY &&
cairo_bo_event_compare (event,
elements[parent = PQ_PARENT_INDEX (i)]) < 0;
i = parent)
{
elements[i] = elements[parent];
}
 
elements[i] = event;
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline void
_pqueue_pop (pqueue_t *pq)
{
cairo_bo_event_t **elements = pq->elements;
cairo_bo_event_t *tail;
int child, i;
 
tail = elements[pq->size--];
if (pq->size == 0) {
elements[PQ_FIRST_ENTRY] = NULL;
return;
}
 
for (i = PQ_FIRST_ENTRY;
(child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
i = child)
{
if (child != pq->size &&
cairo_bo_event_compare (elements[child+1],
elements[child]) < 0)
{
child++;
}
 
if (cairo_bo_event_compare (elements[child], tail) >= 0)
break;
 
elements[i] = elements[child];
}
elements[i] = tail;
}
 
static inline cairo_status_t
_cairo_bo_event_queue_insert (cairo_bo_event_queue_t *queue,
cairo_bo_event_type_t type,
cairo_bo_edge_t *e1,
cairo_bo_edge_t *e2,
const cairo_point_t *point)
{
cairo_bo_queue_event_t *event;
 
event = _cairo_freepool_alloc (&queue->pool);
if (unlikely (event == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
event->type = type;
event->e1 = e1;
event->e2 = e2;
event->point = *point;
 
return _pqueue_push (&queue->pqueue, (cairo_bo_event_t *) event);
}
 
static void
_cairo_bo_event_queue_delete (cairo_bo_event_queue_t *queue,
cairo_bo_event_t *event)
{
_cairo_freepool_free (&queue->pool, event);
}
 
static cairo_bo_event_t *
_cairo_bo_event_dequeue (cairo_bo_event_queue_t *event_queue)
{
cairo_bo_event_t *event, *cmp;
 
event = event_queue->pqueue.elements[PQ_FIRST_ENTRY];
cmp = *event_queue->start_events;
if (event == NULL ||
(cmp != NULL && cairo_bo_event_compare (cmp, event) < 0))
{
event = cmp;
event_queue->start_events++;
}
else
{
_pqueue_pop (&event_queue->pqueue);
}
 
return event;
}
 
CAIRO_COMBSORT_DECLARE (_cairo_bo_event_queue_sort,
cairo_bo_event_t *,
cairo_bo_event_compare)
 
static void
_cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue,
cairo_bo_event_t **start_events,
int num_events)
{
_cairo_bo_event_queue_sort (start_events, num_events);
start_events[num_events] = NULL;
 
event_queue->start_events = start_events;
 
_cairo_freepool_init (&event_queue->pool,
sizeof (cairo_bo_queue_event_t));
_pqueue_init (&event_queue->pqueue);
event_queue->pqueue.elements[PQ_FIRST_ENTRY] = NULL;
}
 
static cairo_status_t
_cairo_bo_event_queue_insert_stop (cairo_bo_event_queue_t *event_queue,
cairo_bo_edge_t *edge)
{
cairo_bo_point32_t point;
 
point.y = edge->edge.bottom;
point.x = _line_compute_intersection_x_for_y (&edge->edge.line,
point.y);
return _cairo_bo_event_queue_insert (event_queue,
CAIRO_BO_EVENT_TYPE_STOP,
edge, NULL,
&point);
}
 
static void
_cairo_bo_event_queue_fini (cairo_bo_event_queue_t *event_queue)
{
_pqueue_fini (&event_queue->pqueue);
_cairo_freepool_fini (&event_queue->pool);
}
 
static inline cairo_status_t
_cairo_bo_event_queue_insert_if_intersect_below_current_y (cairo_bo_event_queue_t *event_queue,
cairo_bo_edge_t *left,
cairo_bo_edge_t *right)
{
cairo_bo_point32_t intersection;
 
if (_line_equal (&left->edge.line, &right->edge.line))
return CAIRO_STATUS_SUCCESS;
 
/* The names "left" and "right" here are correct descriptions of
* the order of the two edges within the active edge list. So if a
* slope comparison also puts left less than right, then we know
* that the intersection of these two segments has already
* occurred before the current sweep line position. */
if (_slope_compare (left, right) <= 0)
return CAIRO_STATUS_SUCCESS;
 
if (! _cairo_bo_edge_intersect (left, right, &intersection))
return CAIRO_STATUS_SUCCESS;
 
return _cairo_bo_event_queue_insert (event_queue,
CAIRO_BO_EVENT_TYPE_INTERSECTION,
left, right,
&intersection);
}
 
static void
_cairo_bo_sweep_line_init (cairo_bo_sweep_line_t *sweep_line)
{
sweep_line->head = NULL;
sweep_line->current_y = INT32_MIN;
sweep_line->current_edge = NULL;
}
 
static cairo_status_t
_cairo_bo_sweep_line_insert (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *edge)
{
if (sweep_line->current_edge != NULL) {
cairo_bo_edge_t *prev, *next;
int cmp;
 
cmp = _cairo_bo_sweep_line_compare_edges (sweep_line,
sweep_line->current_edge,
edge);
if (cmp < 0) {
prev = sweep_line->current_edge;
next = prev->next;
while (next != NULL &&
_cairo_bo_sweep_line_compare_edges (sweep_line,
next, edge) < 0)
{
prev = next, next = prev->next;
}
 
prev->next = edge;
edge->prev = prev;
edge->next = next;
if (next != NULL)
next->prev = edge;
} else if (cmp > 0) {
next = sweep_line->current_edge;
prev = next->prev;
while (prev != NULL &&
_cairo_bo_sweep_line_compare_edges (sweep_line,
prev, edge) > 0)
{
next = prev, prev = next->prev;
}
 
next->prev = edge;
edge->next = next;
edge->prev = prev;
if (prev != NULL)
prev->next = edge;
else
sweep_line->head = edge;
} else {
prev = sweep_line->current_edge;
edge->prev = prev;
edge->next = prev->next;
if (prev->next != NULL)
prev->next->prev = edge;
prev->next = edge;
}
} else {
sweep_line->head = edge;
}
 
sweep_line->current_edge = edge;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_bo_sweep_line_delete (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *edge)
{
if (edge->prev != NULL)
edge->prev->next = edge->next;
else
sweep_line->head = edge->next;
 
if (edge->next != NULL)
edge->next->prev = edge->prev;
 
if (sweep_line->current_edge == edge)
sweep_line->current_edge = edge->prev ? edge->prev : edge->next;
}
 
static void
_cairo_bo_sweep_line_swap (cairo_bo_sweep_line_t *sweep_line,
cairo_bo_edge_t *left,
cairo_bo_edge_t *right)
{
if (left->prev != NULL)
left->prev->next = right;
else
sweep_line->head = right;
 
if (right->next != NULL)
right->next->prev = left;
 
left->next = right->next;
right->next = left;
 
right->prev = left->prev;
left->prev = right;
}
 
static inline cairo_bool_t
edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
{
if (_line_equal (&a->edge.line, &b->edge.line))
return TRUE;
 
if (_slope_compare (a, b))
return FALSE;
 
/* The choice of y is not truly arbitrary since we must guarantee that it
* is greater than the start of either line.
*/
if (a->edge.line.p1.y == b->edge.line.p1.y) {
return a->edge.line.p1.x == b->edge.line.p1.x;
} else if (a->edge.line.p2.y == b->edge.line.p2.y) {
return a->edge.line.p2.x == b->edge.line.p2.x;
} else if (a->edge.line.p1.y < b->edge.line.p1.y) {
return edge_compare_for_y_against_x (b,
a->edge.line.p1.y,
a->edge.line.p1.x) == 0;
} else {
return edge_compare_for_y_against_x (a,
b->edge.line.p1.y,
b->edge.line.p1.x) == 0;
}
}
 
static void
_cairo_bo_edge_end (cairo_bo_edge_t *left,
int32_t bot,
cairo_polygon_t *polygon)
{
cairo_bo_deferred_t *d = &left->deferred;
 
if (likely (d->top < bot)) {
_cairo_polygon_add_line (polygon,
&left->edge.line,
d->top, bot,
1);
_cairo_polygon_add_line (polygon,
&d->right->edge.line,
d->top, bot,
-1);
}
 
d->right = NULL;
}
 
 
static inline void
_cairo_bo_edge_start_or_continue (cairo_bo_edge_t *left,
cairo_bo_edge_t *right,
int top,
cairo_polygon_t *polygon)
{
if (left->deferred.right == right)
return;
 
if (left->deferred.right != NULL) {
if (right != NULL && edges_colinear (left->deferred.right, right))
{
/* continuation on right, so just swap edges */
left->deferred.right = right;
return;
}
 
_cairo_bo_edge_end (left, top, polygon);
}
 
if (right != NULL && ! edges_colinear (left, right)) {
left->deferred.top = top;
left->deferred.right = right;
}
}
 
static inline void
_active_edges_to_polygon (cairo_bo_edge_t *left,
int32_t top,
cairo_fill_rule_t fill_rule,
cairo_polygon_t *polygon)
{
cairo_bo_edge_t *right;
unsigned int mask;
 
if (fill_rule == CAIRO_FILL_RULE_WINDING)
mask = ~0;
else
mask = 1;
 
while (left != NULL) {
int in_out = left->edge.dir;
 
right = left->next;
if (left->deferred.right == NULL) {
while (right != NULL && right->deferred.right == NULL)
right = right->next;
 
if (right != NULL && edges_colinear (left, right)) {
/* continuation on left */
left->deferred = right->deferred;
right->deferred.right = NULL;
}
}
 
right = left->next;
while (right != NULL) {
if (right->deferred.right != NULL)
_cairo_bo_edge_end (right, top, polygon);
 
in_out += right->edge.dir;
if ((in_out & mask) == 0) {
/* skip co-linear edges */
if (right->next == NULL || !edges_colinear (right, right->next))
break;
}
 
right = right->next;
}
 
_cairo_bo_edge_start_or_continue (left, right, top, polygon);
 
left = right;
if (left != NULL)
left = left->next;
}
}
 
 
static cairo_status_t
_cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events,
int num_events,
cairo_fill_rule_t fill_rule,
cairo_polygon_t *polygon)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS; /* silence compiler */
cairo_bo_event_queue_t event_queue;
cairo_bo_sweep_line_t sweep_line;
cairo_bo_event_t *event;
cairo_bo_edge_t *left, *right;
cairo_bo_edge_t *e1, *e2;
 
_cairo_bo_event_queue_init (&event_queue, start_events, num_events);
_cairo_bo_sweep_line_init (&sweep_line);
 
while ((event = _cairo_bo_event_dequeue (&event_queue))) {
if (event->point.y != sweep_line.current_y) {
_active_edges_to_polygon (sweep_line.head,
sweep_line.current_y,
fill_rule, polygon);
 
sweep_line.current_y = event->point.y;
}
 
switch (event->type) {
case CAIRO_BO_EVENT_TYPE_START:
e1 = &((cairo_bo_start_event_t *) event)->edge;
 
status = _cairo_bo_sweep_line_insert (&sweep_line, e1);
if (unlikely (status))
goto unwind;
 
status = _cairo_bo_event_queue_insert_stop (&event_queue, e1);
if (unlikely (status))
goto unwind;
 
left = e1->prev;
right = e1->next;
 
if (left != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1);
if (unlikely (status))
goto unwind;
}
 
if (right != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
if (unlikely (status))
goto unwind;
}
 
break;
 
case CAIRO_BO_EVENT_TYPE_STOP:
e1 = ((cairo_bo_queue_event_t *) event)->e1;
_cairo_bo_event_queue_delete (&event_queue, event);
 
left = e1->prev;
right = e1->next;
 
_cairo_bo_sweep_line_delete (&sweep_line, e1);
 
if (e1->deferred.right != NULL)
_cairo_bo_edge_end (e1, e1->edge.bottom, polygon);
 
if (left != NULL && right != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
if (unlikely (status))
goto unwind;
}
 
break;
 
case CAIRO_BO_EVENT_TYPE_INTERSECTION:
e1 = ((cairo_bo_queue_event_t *) event)->e1;
e2 = ((cairo_bo_queue_event_t *) event)->e2;
_cairo_bo_event_queue_delete (&event_queue, event);
 
/* skip this intersection if its edges are not adjacent */
if (e2 != e1->next)
break;
 
left = e1->prev;
right = e2->next;
 
_cairo_bo_sweep_line_swap (&sweep_line, e1, e2);
 
/* after the swap e2 is left of e1 */
 
if (left != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e2);
if (unlikely (status))
goto unwind;
}
 
if (right != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right);
if (unlikely (status))
goto unwind;
}
 
break;
}
}
 
unwind:
_cairo_bo_event_queue_fini (&event_queue);
 
return status;
}
 
cairo_status_t
_cairo_polygon_reduce (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule)
{
cairo_status_t status;
cairo_bo_start_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_start_event_t)];
cairo_bo_start_event_t *events;
cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1];
cairo_bo_event_t **event_ptrs;
int num_limits;
int num_events;
int i;
 
num_events = polygon->num_edges;
if (unlikely (0 == num_events))
return CAIRO_STATUS_SUCCESS;
 
if (DEBUG_POLYGON) {
FILE *file = fopen ("reduce_in.txt", "w");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
}
 
events = stack_events;
event_ptrs = stack_event_ptrs;
if (num_events > ARRAY_LENGTH (stack_events)) {
events = _cairo_malloc_ab_plus_c (num_events,
sizeof (cairo_bo_start_event_t) +
sizeof (cairo_bo_event_t *),
sizeof (cairo_bo_event_t *));
if (unlikely (events == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
event_ptrs = (cairo_bo_event_t **) (events + num_events);
}
 
for (i = 0; i < num_events; i++) {
event_ptrs[i] = (cairo_bo_event_t *) &events[i];
 
events[i].type = CAIRO_BO_EVENT_TYPE_START;
events[i].point.y = polygon->edges[i].top;
events[i].point.x =
_line_compute_intersection_x_for_y (&polygon->edges[i].line,
events[i].point.y);
 
events[i].edge.edge = polygon->edges[i];
events[i].edge.deferred.right = NULL;
events[i].edge.prev = NULL;
events[i].edge.next = NULL;
}
 
num_limits = polygon->num_limits; polygon->num_limits = 0;
polygon->num_edges = 0;
 
status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs,
num_events,
fill_rule,
polygon);
polygon->num_limits = num_limits;
 
if (events != stack_events)
free (events);
 
if (DEBUG_POLYGON) {
FILE *file = fopen ("reduce_out.txt", "w");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
}
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-polygon.c
1,3 → 1,4
/* -*- 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
36,29 → 37,48
 
#include "cairoint.h"
 
#include "cairo-boxes-private.h"
#include "cairo-contour-private.h"
#include "cairo-error-private.h"
#include "cairo-slope-private.h"
 
void
_cairo_polygon_init (cairo_polygon_t *polygon)
#define DEBUG_POLYGON 0
 
#if DEBUG_POLYGON && !NDEBUG
static void
assert_last_edge_is_valid(cairo_polygon_t *polygon,
const cairo_box_t *limit)
{
VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
cairo_edge_t *edge;
cairo_fixed_t x;
 
polygon->status = CAIRO_STATUS_SUCCESS;
edge = &polygon->edges[polygon->num_edges-1];
 
polygon->num_edges = 0;
assert (edge->bottom > edge->top);
assert (edge->top >= limit->p1.y);
assert (edge->bottom <= limit->p2.y);
 
polygon->edges = polygon->edges_embedded;
polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1,
&edge->line.p2,
edge->top);
assert (x >= limit->p1.x);
assert (x <= limit->p2.x);
 
polygon->has_current_point = FALSE;
polygon->has_current_edge = FALSE;
polygon->num_limits = 0;
 
polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
x = _cairo_edge_compute_intersection_x_for_y (&edge->line.p1,
&edge->line.p2,
edge->bottom);
assert (x >= limit->p1.x);
assert (x <= limit->p2.x);
}
#else
#define assert_last_edge_is_valid(p, l)
#endif
 
static void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2,
int dir);
 
void
_cairo_polygon_limit (cairo_polygon_t *polygon,
const cairo_box_t *limits,
88,6 → 108,141
}
 
void
_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
const cairo_clip_t *clip)
{
if (clip)
_cairo_polygon_limit (polygon, clip->boxes, clip->num_boxes);
else
_cairo_polygon_limit (polygon, 0, 0);
}
 
void
_cairo_polygon_init (cairo_polygon_t *polygon,
const cairo_box_t *limits,
int num_limits)
{
VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
 
polygon->status = CAIRO_STATUS_SUCCESS;
 
polygon->num_edges = 0;
 
polygon->edges = polygon->edges_embedded;
polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
 
polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
 
_cairo_polygon_limit (polygon, limits, num_limits);
}
 
void
_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
const cairo_clip_t *clip)
{
if (clip)
_cairo_polygon_init (polygon, clip->boxes, clip->num_boxes);
else
_cairo_polygon_init (polygon, 0, 0);
}
 
cairo_status_t
_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
const cairo_boxes_t *boxes)
{
const struct _cairo_boxes_chunk *chunk;
int i;
 
VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
 
polygon->status = CAIRO_STATUS_SUCCESS;
 
polygon->num_edges = 0;
 
polygon->edges = polygon->edges_embedded;
polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
if (boxes->num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
polygon->edges_size = 2 * boxes->num_boxes;
polygon->edges = _cairo_malloc_ab (polygon->edges_size,
2*sizeof(cairo_edge_t));
if (unlikely (polygon->edges == NULL))
return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
 
polygon->limits = NULL;
polygon->num_limits = 0;
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_point_t p1, p2;
 
p1 = chunk->base[i].p1;
p2.x = p1.x;
p2.y = chunk->base[i].p2.y;
_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
 
p1 = chunk->base[i].p2;
p2.x = p1.x;
p2.y = chunk->base[i].p1.y;
_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
}
}
 
return polygon->status;
}
 
cairo_status_t
_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
cairo_box_t *boxes,
int num_boxes)
{
int i;
 
VG (VALGRIND_MAKE_MEM_UNDEFINED (polygon, sizeof (cairo_polygon_t)));
 
polygon->status = CAIRO_STATUS_SUCCESS;
 
polygon->num_edges = 0;
 
polygon->edges = polygon->edges_embedded;
polygon->edges_size = ARRAY_LENGTH (polygon->edges_embedded);
if (num_boxes > ARRAY_LENGTH (polygon->edges_embedded)/2) {
polygon->edges_size = 2 * num_boxes;
polygon->edges = _cairo_malloc_ab (polygon->edges_size,
2*sizeof(cairo_edge_t));
if (unlikely (polygon->edges == NULL))
return polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
polygon->extents.p1.x = polygon->extents.p1.y = INT32_MAX;
polygon->extents.p2.x = polygon->extents.p2.y = INT32_MIN;
 
polygon->limits = NULL;
polygon->num_limits = 0;
 
for (i = 0; i < num_boxes; i++) {
cairo_point_t p1, p2;
 
p1 = boxes[i].p1;
p2.x = p1.x;
p2.y = boxes[i].p2.y;
_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
 
p1 = boxes[i].p2;
p2.x = p1.x;
p2.y = boxes[i].p1.y;
_cairo_polygon_add_edge (polygon, &p1, &p2, 1);
}
 
return polygon->status;
}
 
 
void
_cairo_polygon_fini (cairo_polygon_t *polygon)
{
if (polygon->edges != polygon->edges_embedded)
185,12 → 340,13
const int top, const int bottom,
const int dir)
{
cairo_point_t p[2];
int top_y, bot_y;
cairo_point_t bot_left, top_right;
cairo_fixed_t top_y, bot_y;
int n;
 
for (n = 0; n < polygon->num_limits; n++) {
const cairo_box_t *limits = &polygon->limits[n];
cairo_fixed_t pleft, pright;
 
if (top >= limits->p2.y)
continue;
197,168 → 353,161
if (bottom <= limits->p1.y)
continue;
 
if (p1->x >= limits->p1.x && p2->x >= limits->p1.x &&
p1->x <= limits->p2.x && p2->x <= limits->p2.x)
{
top_y = top;
if (top_y < limits->p1.y)
top_y = limits->p1.y;
bot_left.x = limits->p1.x;
bot_left.y = limits->p2.y;
 
bot_y = bottom;
if (bot_y > limits->p2.y)
bot_y = limits->p2.y;
top_right.x = limits->p2.x;
top_right.y = limits->p1.y;
 
/* The useful region */
top_y = MAX (top, limits->p1.y);
bot_y = MIN (bottom, limits->p2.y);
 
/* The projection of the edge on the horizontal axis */
pleft = MIN (p1->x, p2->x);
pright = MAX (p1->x, p2->x);
 
if (limits->p1.x <= pleft && pright <= limits->p2.x) {
/* Projection of the edge completely contained in the box:
* clip vertically by restricting top and bottom */
 
_add_edge (polygon, p1, p2, top_y, bot_y, dir);
}
else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x)
{
p[0].x = limits->p1.x;
p[0].y = limits->p1.y;
top_y = top;
if (top_y < p[0].y)
top_y = p[0].y;
assert_last_edge_is_valid (polygon, limits);
} else if (pright <= limits->p1.x) {
/* Projection of the edge to the left of the box:
* replace with the left side of the box (clipped top/bottom) */
 
p[1].x = limits->p1.x;
p[1].y = limits->p2.y;
bot_y = bottom;
if (bot_y > p[1].y)
bot_y = p[1].y;
_add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir);
assert_last_edge_is_valid (polygon, limits);
} else if (limits->p2.x <= pleft) {
/* Projection of the edge to the right of the box:
* replace with the right side of the box (clipped top/bottom) */
 
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
}
else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x)
{
p[0].x = limits->p2.x;
p[0].y = limits->p1.y;
top_y = top;
if (top_y < p[0].y)
top_y = p[0].y;
_add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir);
assert_last_edge_is_valid (polygon, limits);
} else {
/* The edge and the box intersect in a generic way */
cairo_fixed_t left_y, right_y;
cairo_bool_t top_left_to_bottom_right;
 
p[1].x = limits->p2.x;
p[1].y = limits->p2.y;
bot_y = bottom;
if (bot_y > p[1].y)
bot_y = p[1].y;
/*
* The edge intersects the lines corresponding to the left
* and right sides of the limit box at left_y and right_y,
* but we need to add edges for the range from top_y to
* bot_y.
*
* For both intersections, there are three cases:
*
* 1) It is outside the vertical range of the limit
* box. In this case we can simply further clip the
* edge we will be emitting (i.e. restrict its
* top/bottom limits to those of the limit box).
*
* 2) It is inside the vertical range of the limit
* box. In this case, we need to add the vertical edge
* connecting the correct vertex to the intersection,
* in order to preserve the winding count.
*
* 3) It is exactly on the box. In this case, do nothing.
*
* These operations restrict the active range (stored in
* top_y/bot_y) so that the p1-p2 edge is completely
* inside the box if it is clipped to this vertical range.
*/
 
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
top_left_to_bottom_right = (p1->x <= p2->x) == (p1->y <= p2->y);
if (top_left_to_bottom_right) {
if (pleft >= limits->p1.x) {
left_y = top_y;
} else {
left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p1.x);
if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x)
left_y++;
}
else
{
int left_y, right_y;
int p1_y, p2_y;
 
left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p1.x);
left_y = MIN (left_y, bot_y);
if (top_y < left_y) {
_add_edge (polygon, &limits->p1, &bot_left,
top_y, left_y, dir);
assert_last_edge_is_valid (polygon, limits);
top_y = left_y;
}
 
if (pright <= limits->p2.x) {
right_y = bot_y;
} else {
right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p2.x);
if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x)
right_y--;
}
 
if (left_y == right_y) /* horizontal within bounds */
continue;
 
p1_y = top;
p2_y = bottom;
 
if (left_y < right_y) {
if (p1->x < limits->p1.x && left_y > limits->p1.y) {
p[0].x = limits->p1.x;
p[0].y = limits->p1.y;
top_y = p1_y;
if (top_y < p[0].y)
top_y = p[0].y;
 
p[1].x = limits->p1.x;
p[1].y = limits->p2.y;
bot_y = left_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
 
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p1_y = bot_y;
right_y = MAX (right_y, top_y);
if (bot_y > right_y) {
_add_edge (polygon, &top_right, &limits->p2,
right_y, bot_y, dir);
assert_last_edge_is_valid (polygon, limits);
bot_y = right_y;
}
} else {
if (pright <= limits->p2.x) {
right_y = top_y;
} else {
right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p2.x);
if (_cairo_edge_compute_intersection_x_for_y (p1, p2, right_y) > limits->p2.x)
right_y++;
}
 
if (p2->x > limits->p2.x && right_y < limits->p2.y) {
p[0].x = limits->p2.x;
p[0].y = limits->p1.y;
right_y = MIN (right_y, bot_y);
if (top_y < right_y) {
_add_edge (polygon, &top_right, &limits->p2,
top_y, right_y, dir);
assert_last_edge_is_valid (polygon, limits);
top_y = right_y;
if (top_y < p[0].y)
top_y = p[0].y;
}
 
p[1].x = limits->p2.x;
p[1].y = limits->p2.y;
bot_y = p2_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
 
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p2_y = top_y;
}
if (pleft >= limits->p1.x) {
left_y = bot_y;
} else {
if (p1->x > limits->p2.x && right_y > limits->p1.y) {
p[0].x = limits->p2.x;
p[0].y = limits->p1.y;
top_y = p1_y;
if (top_y < p[0].y)
top_y = p[0].y;
 
p[1].x = limits->p2.x;
p[1].y = limits->p2.y;
bot_y = right_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
 
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p1_y = bot_y;
left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
limits->p1.x);
if (_cairo_edge_compute_intersection_x_for_y (p1, p2, left_y) < limits->p1.x)
left_y--;
}
 
if (p2->x < limits->p1.x && left_y < limits->p2.y) {
p[0].x = limits->p1.x;
p[0].y = limits->p1.y;
top_y = left_y;
if (top_y < p[0].y)
top_y = p[0].y;
 
p[1].x = limits->p1.x;
p[1].y = limits->p2.y;
bot_y = p2_y;
if (bot_y > p[1].y)
bot_y = p[1].y;
 
if (bot_y > top_y)
_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
p2_y = top_y;
left_y = MAX (left_y, top_y);
if (bot_y > left_y) {
_add_edge (polygon, &limits->p1, &bot_left,
left_y, bot_y, dir);
assert_last_edge_is_valid (polygon, limits);
bot_y = left_y;
}
}
 
if (p1_y < limits->p1.y)
p1_y = limits->p1.y;
if (p2_y > limits->p2.y)
p2_y = limits->p2.y;
if (p2_y > p1_y)
_add_edge (polygon, p1, p2, p1_y, p2_y, dir);
if (top_y != bot_y) {
_add_edge (polygon, p1, p2, top_y, bot_y, dir);
assert_last_edge_is_valid (polygon, limits);
}
}
}
}
 
static void
_cairo_polygon_add_edge (cairo_polygon_t *polygon,
const cairo_point_t *p1,
const cairo_point_t *p2)
const cairo_point_t *p2,
int dir)
{
int dir;
 
/* drop horizontal edges */
if (p1->y == p2->y)
return;
 
if (p1->y < p2->y) {
dir = 1;
} else {
if (p1->y > p2->y) {
const cairo_point_t *t;
t = p1, p1 = p2, p2 = t;
dir = -1;
dir = -dir;
}
 
if (polygon->num_limits) {
378,7 → 527,7
const cairo_point_t *p1,
const cairo_point_t *p2)
{
_cairo_polygon_add_edge (polygon, p1, p2);
_cairo_polygon_add_edge (polygon, p1, p2, 1);
return _cairo_polygon_status (polygon);
}
 
409,87 → 558,51
return polygon->status;
}
 
/* flattened path operations */
 
cairo_status_t
_cairo_polygon_move_to (cairo_polygon_t *polygon,
const cairo_point_t *point)
_cairo_polygon_add_contour (cairo_polygon_t *polygon,
const cairo_contour_t *contour)
{
if (polygon->has_current_edge) {
_cairo_polygon_add_edge (polygon,
&polygon->last_point,
&polygon->current_point);
polygon->has_current_edge = FALSE;
}
const struct _cairo_contour_chain *chain;
const cairo_point_t *prev = NULL;
int i;
 
if (! polygon->has_current_point) {
polygon->first_point = *point;
polygon->has_current_point = TRUE;
if (contour->chain.num_points <= 1)
return CAIRO_INT_STATUS_SUCCESS;
 
prev = &contour->chain.points[0];
for (chain = &contour->chain; chain; chain = chain->next) {
for (i = 0; i < chain->num_points; i++) {
_cairo_polygon_add_edge (polygon, prev, &chain->points[i],
contour->direction);
prev = &chain->points[i];
}
}
 
polygon->current_point = *point;
return polygon->status;
}
 
cairo_status_t
_cairo_polygon_line_to (cairo_polygon_t *polygon,
const cairo_point_t *point)
void
_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy)
{
/* squash collinear edges */
if (polygon->has_current_edge) {
if (polygon->current_point.x != point->x ||
polygon->current_point.y != point->y)
{
cairo_slope_t this;
int n;
 
_cairo_slope_init (&this, &polygon->current_point, point);
if (_cairo_slope_equal (&polygon->current_edge, &this)) {
polygon->current_point = *point;
return CAIRO_STATUS_SUCCESS;
}
dx = _cairo_fixed_from_int (dx);
dy = _cairo_fixed_from_int (dy);
 
_cairo_polygon_add_edge (polygon,
&polygon->last_point,
&polygon->current_point);
polygon->extents.p1.x += dx;
polygon->extents.p2.x += dx;
polygon->extents.p1.y += dy;
polygon->extents.p2.y += dy;
 
polygon->last_point = polygon->current_point;
polygon->current_edge = this;
}
} else if (polygon->has_current_point) {
if (polygon->current_point.x != point->x ||
polygon->current_point.y != point->y)
{
polygon->last_point = polygon->current_point;
_cairo_slope_init (&polygon->current_edge,
&polygon->last_point,
point);
polygon->has_current_edge = TRUE;
}
} else {
polygon->first_point = *point;
polygon->has_current_point = TRUE;
}
for (n = 0; n < polygon->num_edges; n++) {
cairo_edge_t *e = &polygon->edges[n];
 
polygon->current_point = *point;
return polygon->status;
}
e->top += dy;
e->bottom += dy;
 
cairo_status_t
_cairo_polygon_close (cairo_polygon_t *polygon)
{
cairo_status_t status;
 
if (polygon->has_current_point) {
status = _cairo_polygon_line_to (polygon, &polygon->first_point);
polygon->has_current_point = FALSE;
e->line.p1.x += dx;
e->line.p2.x += dx;
e->line.p1.y += dy;
e->line.p2.y += dy;
}
 
if (polygon->has_current_edge) {
_cairo_polygon_add_edge (polygon,
&polygon->last_point,
&polygon->current_point);
polygon->has_current_edge = FALSE;
}
 
return polygon->status;
}
/programs/develop/libraries/cairo/src/cairo-private.h
36,22 → 36,29
#ifndef CAIRO_PRIVATE_H
#define CAIRO_PRIVATE_H
 
#include "cairo-types-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
 
CAIRO_BEGIN_DECLS
 
struct _cairo {
cairo_reference_count_t ref_count;
 
cairo_status_t status;
 
cairo_user_data_array_t user_data;
 
cairo_gstate_t *gstate;
cairo_gstate_t gstate_tail[2];
cairo_gstate_t *gstate_freelist;
 
cairo_path_fixed_t path[1];
const cairo_backend_t *backend;
};
 
cairo_private cairo_t *
_cairo_create_in_error (cairo_status_t status);
 
cairo_private void
_cairo_init (cairo_t *cr,
const cairo_backend_t *backend);
 
cairo_private void
_cairo_fini (cairo_t *cr);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-ps-surface-private.h
70,11 → 70,6
int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
cairo_matrix_t cairo_to_ps;
 
/* XXX These 3 are used as temporary storage whilst emitting patterns */
cairo_image_surface_t *image;
cairo_image_surface_t *acquired_image;
void *image_extra;
 
cairo_bool_t use_string_datasource;
 
cairo_bool_t current_pattern_is_solid_color;
/programs/develop/libraries/cairo/src/cairo-ps-surface.c
0,0 → 1,4641
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2003 University of Southern California
* Copyright © 2005 Red Hat, Inc
* Copyright © 2007,2008 Adrian Johnson
*
* 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>
* Kristian Høgsberg <krh@redhat.com>
* Keith Packard <keithp@keithp.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
 
/*
* Design of the PS output:
*
* The PS output is harmonised with the PDF operations using PS procedures
* to emulate the PDF operators.
*
* This has a number of advantages:
* 1. A large chunk of code is shared between the PDF and PS backends.
* See cairo-pdf-operators.
* 2. Using gs to do PS -> PDF and PDF -> PS will always work well.
*/
 
#define _BSD_SOURCE /* for ctime_r(), snprintf(), strdup() */
#include "cairoint.h"
 
#include "cairo-ps.h"
#include "cairo-ps-surface-private.h"
 
#include "cairo-pdf-operators-private.h"
#include "cairo-pdf-shading-private.h"
 
#include "cairo-array-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-list-inline.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-type3-glyph-surface-private.h"
#include "cairo-image-info-private.h"
 
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <zlib.h>
#include <errno.h>
 
#define DEBUG_PS 0
 
#if DEBUG_PS
#define DEBUG_FALLBACK(s) \
fprintf (stderr, "%s::%d -- %s\n", __FUNCTION__, __LINE__, (s))
#else
#define DEBUG_FALLBACK(s)
#endif
 
#ifndef HAVE_CTIME_R
#define ctime_r(T, BUF) ctime (T)
#endif
 
/**
* SECTION:cairo-ps
* @Title: PostScript Surfaces
* @Short_Description: Rendering PostScript documents
* @See_Also: #cairo_surface_t
*
* The PostScript surface is used to render cairo graphics to Adobe
* PostScript files and is a multi-page vector surface backend.
**/
 
/**
* CAIRO_HAS_PS_SURFACE:
*
* Defined if the PostScript surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.2
**/
 
typedef enum {
CAIRO_PS_COMPRESS_NONE,
CAIRO_PS_COMPRESS_LZW,
CAIRO_PS_COMPRESS_DEFLATE
} cairo_ps_compress_t;
 
static const cairo_surface_backend_t cairo_ps_surface_backend;
static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend;
 
static cairo_bool_t
_cairo_ps_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle);
 
static const cairo_ps_level_t _cairo_ps_levels[] =
{
CAIRO_PS_LEVEL_2,
CAIRO_PS_LEVEL_3
};
 
#define CAIRO_PS_LEVEL_LAST ARRAY_LENGTH (_cairo_ps_levels)
 
static const char * _cairo_ps_level_strings[CAIRO_PS_LEVEL_LAST] =
{
"PS Level 2",
"PS Level 3"
};
 
static const char *_cairo_ps_supported_mime_types[] =
{
CAIRO_MIME_TYPE_JPEG,
NULL
};
 
typedef struct _cairo_page_standard_media {
const char *name;
int width;
int height;
} cairo_page_standard_media_t;
 
static const cairo_page_standard_media_t _cairo_page_standard_media[] =
{
{ "A0", 2384, 3371 },
{ "A1", 1685, 2384 },
{ "A2", 1190, 1684 },
{ "A3", 842, 1190 },
{ "A4", 595, 842 },
{ "A5", 420, 595 },
{ "B4", 729, 1032 },
{ "B5", 516, 729 },
{ "Letter", 612, 792 },
{ "Tabloid", 792, 1224 },
{ "Ledger", 1224, 792 },
{ "Legal", 612, 1008 },
{ "Statement", 396, 612 },
{ "Executive", 540, 720 },
{ "Folio", 612, 936 },
{ "Quarto", 610, 780 },
{ "10x14", 720, 1008 },
};
 
typedef struct _cairo_page_media {
char *name;
int width;
int height;
cairo_list_t link;
} cairo_page_media_t;
 
static void
_cairo_ps_surface_emit_header (cairo_ps_surface_t *surface)
{
char ctime_buf[26];
time_t now;
char **comments;
int i, num_comments;
int level;
const char *eps_header = "";
cairo_bool_t has_bbox;
 
if (surface->has_creation_date)
now = surface->creation_date;
else
now = time (NULL);
 
if (surface->ps_level_used == CAIRO_PS_LEVEL_2)
level = 2;
else
level = 3;
 
if (surface->eps)
eps_header = " EPSF-3.0";
 
_cairo_output_stream_printf (surface->final_stream,
"%%!PS-Adobe-3.0%s\n"
"%%%%Creator: cairo %s (http://cairographics.org)\n"
"%%%%CreationDate: %s"
"%%%%Pages: %d\n",
eps_header,
cairo_version_string (),
ctime_r (&now, ctime_buf),
surface->num_pages);
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%DocumentData: Clean7Bit\n"
"%%%%LanguageLevel: %d\n",
level);
 
if (!cairo_list_is_empty (&surface->document_media)) {
cairo_page_media_t *page;
cairo_bool_t first = TRUE;
 
cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
if (first) {
_cairo_output_stream_printf (surface->final_stream,
"%%%%DocumentMedia: ");
first = FALSE;
} else {
_cairo_output_stream_printf (surface->final_stream,
"%%%%+ ");
}
_cairo_output_stream_printf (surface->final_stream,
"%s %d %d 0 () ()\n",
page->name,
page->width,
page->height);
}
}
 
has_bbox = FALSE;
num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
comments = _cairo_array_index (&surface->dsc_header_comments, 0);
for (i = 0; i < num_comments; i++) {
_cairo_output_stream_printf (surface->final_stream,
"%s\n", comments[i]);
if (strncmp (comments[i], "%%BoundingBox:", 14) == 0)
has_bbox = TRUE;
 
free (comments[i]);
comments[i] = NULL;
}
 
if (!has_bbox) {
_cairo_output_stream_printf (surface->final_stream,
"%%%%BoundingBox: %d %d %d %d\n",
surface->bbox_x1,
surface->bbox_y1,
surface->bbox_x2,
surface->bbox_y2);
}
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%EndComments\n");
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%BeginProlog\n");
 
if (surface->eps) {
_cairo_output_stream_printf (surface->final_stream,
"save\n"
"50 dict begin\n");
} else {
_cairo_output_stream_printf (surface->final_stream,
"/languagelevel where\n"
"{ pop languagelevel } { 1 } ifelse\n"
"%d lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto\n"
" (This print job requires a PostScript Language Level %d printer.) show\n"
" showpage quit } if\n",
level,
level);
}
 
_cairo_output_stream_printf (surface->final_stream,
"/q { gsave } bind def\n"
"/Q { grestore } bind def\n"
"/cm { 6 array astore concat } bind def\n"
"/w { setlinewidth } bind def\n"
"/J { setlinecap } bind def\n"
"/j { setlinejoin } bind def\n"
"/M { setmiterlimit } bind def\n"
"/d { setdash } bind def\n"
"/m { moveto } bind def\n"
"/l { lineto } bind def\n"
"/c { curveto } bind def\n"
"/h { closepath } bind def\n"
"/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto\n"
" 0 exch rlineto 0 rlineto closepath } bind def\n"
"/S { stroke } bind def\n"
"/f { fill } bind def\n"
"/f* { eofill } bind def\n"
"/n { newpath } bind def\n"
"/W { clip } bind def\n"
"/W* { eoclip } bind def\n"
"/BT { } bind def\n"
"/ET { } bind def\n"
"/pdfmark where { pop globaldict /?pdfmark /exec load put }\n"
" { globaldict begin /?pdfmark /pop load def /pdfmark\n"
" /cleartomark load def end } ifelse\n"
"/BDC { mark 3 1 roll /BDC pdfmark } bind def\n"
"/EMC { mark /EMC pdfmark } bind def\n"
"/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def\n"
"/Tj { show currentpoint cairo_store_point } bind def\n"
"/TJ {\n"
" {\n"
" dup\n"
" type /stringtype eq\n"
" { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse\n"
" } forall\n"
" currentpoint cairo_store_point\n"
"} bind def\n"
"/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore\n"
" cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def\n"
"/Tf { pop /cairo_font exch def /cairo_font_matrix where\n"
" { pop cairo_selectfont } if } bind def\n"
"/Td { matrix translate cairo_font_matrix matrix concatmatrix dup\n"
" /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point\n"
" /cairo_font where { pop cairo_selectfont } if } bind def\n"
"/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def\n"
" cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def\n"
"/g { setgray } bind def\n"
"/rg { setrgbcolor } bind def\n"
"/d1 { setcachedevice } bind def\n");
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%EndProlog\n");
 
num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
if (num_comments) {
_cairo_output_stream_printf (surface->final_stream,
"%%%%BeginSetup\n");
 
comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
for (i = 0; i < num_comments; i++) {
_cairo_output_stream_printf (surface->final_stream,
"%s\n", comments[i]);
free (comments[i]);
comments[i] = NULL;
}
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%EndSetup\n");
}
}
 
static cairo_status_t
_cairo_ps_surface_emit_type1_font_subset (cairo_ps_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
 
 
{
cairo_type1_subset_t subset;
cairo_status_t status;
int length;
char name[64];
 
snprintf (name, sizeof name, "f-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_type1_subset_init (&subset, name, font_subset, TRUE);
if (unlikely (status))
return status;
 
/* FIXME: Figure out document structure convention for fonts */
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->final_stream,
"%% _cairo_ps_surface_emit_type1_font_subset\n");
#endif
 
length = subset.header_length + subset.data_length + subset.trailer_length;
_cairo_output_stream_write (surface->final_stream, subset.data, length);
 
_cairo_type1_subset_fini (&subset);
 
return CAIRO_STATUS_SUCCESS;
}
 
 
static cairo_status_t
_cairo_ps_surface_emit_type1_font_fallback (cairo_ps_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
{
cairo_type1_subset_t subset;
cairo_status_t status;
int length;
char name[64];
 
snprintf (name, sizeof name, "f-%d-%d",
font_subset->font_id, font_subset->subset_id);
status = _cairo_type1_fallback_init_hex (&subset, name, font_subset);
if (unlikely (status))
return status;
 
/* FIXME: Figure out document structure convention for fonts */
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->final_stream,
"%% _cairo_ps_surface_emit_type1_font_fallback\n");
#endif
 
length = subset.header_length + subset.data_length + subset.trailer_length;
_cairo_output_stream_write (surface->final_stream, subset.data, length);
 
_cairo_type1_fallback_fini (&subset);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_emit_truetype_font_subset (cairo_ps_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
 
 
{
cairo_truetype_subset_t subset;
cairo_status_t status;
unsigned int i, begin, end;
 
status = _cairo_truetype_subset_init_ps (&subset, font_subset);
if (unlikely (status))
return status;
 
/* FIXME: Figure out document structure convention for fonts */
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->final_stream,
"%% _cairo_ps_surface_emit_truetype_font_subset\n");
#endif
 
_cairo_output_stream_printf (surface->final_stream,
"11 dict begin\n"
"/FontType 42 def\n"
"/FontName /%s def\n"
"/PaintType 0 def\n"
"/FontMatrix [ 1 0 0 1 0 0 ] def\n"
"/FontBBox [ 0 0 0 0 ] def\n"
"/Encoding 256 array def\n"
"0 1 255 { Encoding exch /.notdef put } for\n",
subset.ps_name);
 
/* FIXME: Figure out how subset->x_max etc maps to the /FontBBox */
 
if (font_subset->is_latin) {
for (i = 1; i < 256; i++) {
if (font_subset->latin_to_subset_glyph_index[i] > 0) {
if (font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /%s put\n",
i, font_subset->glyph_names[font_subset->latin_to_subset_glyph_index[i]]);
} else {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /g%ld put\n", i, font_subset->latin_to_subset_glyph_index[i]);
}
}
}
} else {
for (i = 1; i < font_subset->num_glyphs; i++) {
if (font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /%s put\n",
i, font_subset->glyph_names[i]);
} else {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /g%d put\n", i, i);
}
}
}
 
_cairo_output_stream_printf (surface->final_stream,
"/CharStrings %d dict dup begin\n"
"/.notdef 0 def\n",
font_subset->num_glyphs);
 
for (i = 1; i < font_subset->num_glyphs; i++) {
if (font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (surface->final_stream,
"/%s %d def\n",
font_subset->glyph_names[i], i);
} else {
_cairo_output_stream_printf (surface->final_stream,
"/g%d %d def\n", i, i);
}
}
 
_cairo_output_stream_printf (surface->final_stream,
"end readonly def\n");
 
_cairo_output_stream_printf (surface->final_stream,
"/sfnts [\n");
begin = 0;
end = 0;
for (i = 0; i < subset.num_string_offsets; i++) {
end = subset.string_offsets[i];
_cairo_output_stream_printf (surface->final_stream,"<");
_cairo_output_stream_write_hex_string (surface->final_stream,
subset.data + begin, end - begin);
_cairo_output_stream_printf (surface->final_stream,"00>\n");
begin = end;
}
if (subset.data_length > end) {
_cairo_output_stream_printf (surface->final_stream,"<");
_cairo_output_stream_write_hex_string (surface->final_stream,
subset.data + end, subset.data_length - end);
_cairo_output_stream_printf (surface->final_stream,"00>\n");
}
 
_cairo_output_stream_printf (surface->final_stream,
"] def\n"
"/f-%d-%d currentdict end definefont pop\n",
font_subset->font_id,
font_subset->subset_id);
 
_cairo_truetype_subset_fini (&subset);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_ps_emit_imagemask (cairo_image_surface_t *image,
cairo_output_stream_t *stream)
{
uint8_t *row, *byte;
int rows, cols;
 
/* The only image type supported by Type 3 fonts are 1-bit image
* masks */
assert (image->format == CAIRO_FORMAT_A1);
 
_cairo_output_stream_printf (stream,
"<<\n"
" /ImageType 1\n"
" /Width %d\n"
" /Height %d\n"
" /ImageMatrix [%d 0 0 %d 0 %d]\n"
" /Decode [1 0]\n"
" /BitsPerComponent 1\n",
image->width,
image->height,
image->width,
-image->height,
image->height);
 
_cairo_output_stream_printf (stream,
" /DataSource {<\n ");
for (row = image->data, rows = image->height; rows; row += image->stride, rows--) {
for (byte = row, cols = (image->width + 7) / 8; cols; byte++, cols--) {
uint8_t output_byte = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (*byte);
_cairo_output_stream_printf (stream, "%02x ", output_byte);
}
_cairo_output_stream_printf (stream, "\n ");
}
_cairo_output_stream_printf (stream, ">}\n>>\n");
 
_cairo_output_stream_printf (stream,
"imagemask\n");
 
return _cairo_output_stream_get_status (stream);
}
 
static cairo_int_status_t
_cairo_ps_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_ps_surface_t *surface = closure;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
unsigned int i;
cairo_surface_t *type3_surface;
 
type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
NULL,
_cairo_ps_emit_imagemask,
surface->font_subsets);
 
for (i = 0; i < font_subset->num_glyphs; i++) {
status = _cairo_type3_glyph_surface_analyze_glyph (type3_surface,
font_subset->glyphs[i]);
if (unlikely (status))
break;
 
}
cairo_surface_finish (type3_surface);
cairo_surface_destroy (type3_surface);
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t *surface,
cairo_scaled_font_subset_t *font_subset)
 
 
{
cairo_status_t status;
unsigned int i;
cairo_box_t font_bbox = {{0,0},{0,0}};
cairo_box_t bbox = {{0,0},{0,0}};
cairo_surface_t *type3_surface;
double width;
 
if (font_subset->num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->final_stream,
"%% _cairo_ps_surface_emit_type3_font_subset\n");
#endif
 
_cairo_output_stream_printf (surface->final_stream,
"8 dict begin\n"
"/FontType 3 def\n"
"/FontMatrix [1 0 0 1 0 0] def\n"
"/Encoding 256 array def\n"
"0 1 255 { Encoding exch /.notdef put } for\n");
 
type3_surface = _cairo_type3_glyph_surface_create (font_subset->scaled_font,
NULL,
_cairo_ps_emit_imagemask,
surface->font_subsets);
status = type3_surface->status;
if (unlikely (status))
return status;
 
for (i = 0; i < font_subset->num_glyphs; i++) {
if (font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /%s put\n",
i, font_subset->glyph_names[i]);
} else {
_cairo_output_stream_printf (surface->final_stream,
"Encoding %d /g%d put\n", i, i);
}
}
 
_cairo_output_stream_printf (surface->final_stream,
"/Glyphs [\n");
 
for (i = 0; i < font_subset->num_glyphs; i++) {
_cairo_output_stream_printf (surface->final_stream,
" { %% %d\n", i);
status = _cairo_type3_glyph_surface_emit_glyph (type3_surface,
surface->final_stream,
font_subset->glyphs[i],
&bbox,
&width);
if (unlikely (status))
break;
 
_cairo_output_stream_printf (surface->final_stream,
" }\n");
if (i == 0) {
font_bbox.p1.x = bbox.p1.x;
font_bbox.p1.y = bbox.p1.y;
font_bbox.p2.x = bbox.p2.x;
font_bbox.p2.y = bbox.p2.y;
} else {
if (bbox.p1.x < font_bbox.p1.x)
font_bbox.p1.x = bbox.p1.x;
if (bbox.p1.y < font_bbox.p1.y)
font_bbox.p1.y = bbox.p1.y;
if (bbox.p2.x > font_bbox.p2.x)
font_bbox.p2.x = bbox.p2.x;
if (bbox.p2.y > font_bbox.p2.y)
font_bbox.p2.y = bbox.p2.y;
}
}
cairo_surface_finish (type3_surface);
cairo_surface_destroy (type3_surface);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->final_stream,
"] def\n"
"/FontBBox [%f %f %f %f] def\n"
"/BuildChar {\n"
" exch /Glyphs get\n"
" exch get\n"
" 10 dict begin exec end\n"
"} bind def\n"
"currentdict\n"
"end\n"
"/f-%d-%d exch definefont pop\n",
_cairo_fixed_to_double (font_bbox.p1.x),
- _cairo_fixed_to_double (font_bbox.p2.y),
_cairo_fixed_to_double (font_bbox.p2.x),
- _cairo_fixed_to_double (font_bbox.p1.y),
font_subset->font_id,
font_subset->subset_id);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_ps_surface_emit_unscaled_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_ps_surface_t *surface = closure;
cairo_int_status_t status;
 
status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
if (_cairo_int_status_is_error (status))
return status;
 
status = _cairo_ps_surface_emit_type1_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_ps_surface_emit_truetype_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_ps_surface_emit_type1_font_fallback (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
ASSERT_NOT_REACHED;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_ps_surface_emit_scaled_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_ps_surface_t *surface = closure;
cairo_int_status_t status;
 
status = _cairo_scaled_font_subset_create_glyph_names (font_subset);
if (_cairo_int_status_is_error (status))
return status;
 
status = _cairo_ps_surface_emit_type3_font_subset (surface, font_subset);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_emit_font_subsets (cairo_ps_surface_t *surface)
{
cairo_status_t status;
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->final_stream,
"%% _cairo_ps_surface_emit_font_subsets\n");
#endif
 
status = _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
_cairo_ps_surface_analyze_user_font_subset,
surface);
if (unlikely (status))
return status;
 
status = _cairo_scaled_font_subsets_foreach_unscaled (surface->font_subsets,
_cairo_ps_surface_emit_unscaled_font_subset,
surface);
if (unlikely (status))
return status;
 
status = _cairo_scaled_font_subsets_foreach_scaled (surface->font_subsets,
_cairo_ps_surface_emit_scaled_font_subset,
surface);
if (unlikely (status))
return status;
 
return _cairo_scaled_font_subsets_foreach_user (surface->font_subsets,
_cairo_ps_surface_emit_scaled_font_subset,
surface);
}
 
static cairo_status_t
_cairo_ps_surface_emit_body (cairo_ps_surface_t *surface)
{
char buf[4096];
int n;
 
if (ferror (surface->tmpfile) != 0)
return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
 
// rewind (surface->tmpfile);
fseek(surface->tmpfile, 0, SEEK_SET);
 
while ((n = fread (buf, 1, sizeof (buf), surface->tmpfile)) > 0)
_cairo_output_stream_write (surface->final_stream, buf, n);
 
if (ferror (surface->tmpfile) != 0)
return _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_ps_surface_emit_footer (cairo_ps_surface_t *surface)
{
_cairo_output_stream_printf (surface->final_stream,
"%%%%Trailer\n");
 
if (surface->eps) {
_cairo_output_stream_printf (surface->final_stream,
"end restore\n");
}
 
_cairo_output_stream_printf (surface->final_stream,
"%%%%EOF\n");
}
 
static cairo_bool_t
_path_covers_bbox (cairo_ps_surface_t *surface,
cairo_path_fixed_t *path)
{
cairo_box_t box;
 
if (_cairo_path_fixed_is_box (path, &box)) {
cairo_rectangle_int_t rect;
 
_cairo_box_round_to_rectangle (&box, &rect);
 
/* skip trivial whole-page clips */
if (_cairo_rectangle_intersect (&rect, &surface->page_bbox)) {
if (rect.x == surface->page_bbox.x &&
rect.width == surface->page_bbox.width &&
rect.y == surface->page_bbox.y &&
rect.height == surface->page_bbox.height)
{
return TRUE;
}
}
}
 
return FALSE;
}
 
static cairo_status_t
_cairo_ps_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_ps_surface_t *surface = cairo_container_of (clipper,
cairo_ps_surface_t,
clipper);
cairo_output_stream_t *stream = surface->stream;
cairo_status_t status;
 
assert (surface->paginated_mode != CAIRO_PAGINATED_MODE_ANALYZE);
 
#if DEBUG_PS
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_intersect_clip_path\n");
#endif
 
if (path == NULL) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (stream, "Q q\n");
 
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
 
return CAIRO_STATUS_SUCCESS;
}
 
if (_path_covers_bbox (surface, path))
return CAIRO_STATUS_SUCCESS;
 
return _cairo_pdf_operators_clip (&surface->pdf_operators,
path,
fill_rule);
}
 
/* PLRM specifies a tolerance of 5 points when matching page sizes */
static cairo_bool_t
_ps_page_dimension_equal (int a, int b)
{
return (abs (a - b) < 5);
}
 
static const char *
_cairo_ps_surface_get_page_media (cairo_ps_surface_t *surface)
{
int width, height, i;
char buf[50];
cairo_page_media_t *page;
const char *page_name;
 
width = _cairo_lround (surface->width);
height = _cairo_lround (surface->height);
 
/* search previously used page sizes */
cairo_list_foreach_entry (page, cairo_page_media_t, &surface->document_media, link) {
if (_ps_page_dimension_equal (width, page->width) &&
_ps_page_dimension_equal (height, page->height))
return page->name;
}
 
/* search list of standard page sizes */
page_name = NULL;
for (i = 0; i < ARRAY_LENGTH (_cairo_page_standard_media); i++) {
if (_ps_page_dimension_equal (width, _cairo_page_standard_media[i].width) &&
_ps_page_dimension_equal (height, _cairo_page_standard_media[i].height))
{
page_name = _cairo_page_standard_media[i].name;
width = _cairo_page_standard_media[i].width;
height = _cairo_page_standard_media[i].height;
break;
}
}
 
page = malloc (sizeof (cairo_page_media_t));
if (unlikely (page == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
if (page_name) {
page->name = strdup (page_name);
} else {
snprintf (buf, sizeof (buf), "%dx%dmm",
(int) _cairo_lround (surface->width * 25.4/72),
(int) _cairo_lround (surface->height * 25.4/72));
page->name = strdup (buf);
}
 
if (unlikely (page->name == NULL)) {
free (page);
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return NULL;
}
 
page->width = width;
page->height = height;
cairo_list_add_tail (&page->link, &surface->document_media);
 
return page->name;
}
 
static cairo_surface_t *
_cairo_ps_surface_create_for_stream_internal (cairo_output_stream_t *stream,
double width,
double height)
{
cairo_status_t status, status_ignored;
cairo_ps_surface_t *surface;
 
surface = malloc (sizeof (cairo_ps_surface_t));
if (unlikely (surface == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP;
}
 
_cairo_surface_init (&surface->base,
&cairo_ps_surface_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
surface->final_stream = stream;
 
surface->tmpfile = tmpfile ();
if (surface->tmpfile == NULL) {
switch (errno) {
case ENOMEM:
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
break;
default:
status = _cairo_error (CAIRO_STATUS_TEMP_FILE_ERROR);
break;
}
goto CLEANUP_SURFACE;
}
 
surface->stream = _cairo_output_stream_create_for_file (surface->tmpfile);
status = _cairo_output_stream_get_status (surface->stream);
if (unlikely (status))
goto CLEANUP_OUTPUT_STREAM;
 
surface->font_subsets = _cairo_scaled_font_subsets_create_simple ();
if (unlikely (surface->font_subsets == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_OUTPUT_STREAM;
}
 
_cairo_scaled_font_subsets_enable_latin_subset (surface->font_subsets, TRUE);
surface->has_creation_date = FALSE;
surface->eps = FALSE;
surface->ps_level = CAIRO_PS_LEVEL_3;
surface->ps_level_used = CAIRO_PS_LEVEL_2;
surface->width = width;
surface->height = height;
cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, height);
surface->paginated_mode = CAIRO_PAGINATED_MODE_ANALYZE;
surface->force_fallbacks = FALSE;
surface->content = CAIRO_CONTENT_COLOR_ALPHA;
surface->use_string_datasource = FALSE;
surface->current_pattern_is_solid_color = FALSE;
 
surface->page_bbox.x = 0;
surface->page_bbox.y = 0;
surface->page_bbox.width = width;
surface->page_bbox.height = height;
 
_cairo_surface_clipper_init (&surface->clipper,
_cairo_ps_surface_clipper_intersect_clip_path);
 
_cairo_pdf_operators_init (&surface->pdf_operators,
surface->stream,
&surface->cairo_to_ps,
surface->font_subsets);
surface->num_pages = 0;
 
cairo_list_init (&surface->document_media);
_cairo_array_init (&surface->dsc_header_comments, sizeof (char *));
_cairo_array_init (&surface->dsc_setup_comments, sizeof (char *));
_cairo_array_init (&surface->dsc_page_setup_comments, sizeof (char *));
 
surface->dsc_comment_target = &surface->dsc_header_comments;
 
surface->paginated_surface = _cairo_paginated_surface_create (
&surface->base,
CAIRO_CONTENT_COLOR_ALPHA,
&cairo_ps_surface_paginated_backend);
status = surface->paginated_surface->status;
if (status == CAIRO_STATUS_SUCCESS) {
/* paginated keeps the only reference to surface now, drop ours */
cairo_surface_destroy (&surface->base);
return surface->paginated_surface;
}
 
_cairo_scaled_font_subsets_destroy (surface->font_subsets);
CLEANUP_OUTPUT_STREAM:
status_ignored = _cairo_output_stream_destroy (surface->stream);
fclose (surface->tmpfile);
CLEANUP_SURFACE:
free (surface);
CLEANUP:
/* destroy stream on behalf of caller */
status_ignored = _cairo_output_stream_destroy (stream);
 
return _cairo_surface_create_in_error (status);
}
 
/**
* cairo_ps_surface_create:
* @filename: a filename for the PS output (must be writable), %NULL may be
* used to specify no output. This will generate a PS surface that
* may be queried and used as a source, without generating a
* temporary file.
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PostScript surface of the specified size in points to be
* written to @filename. See cairo_ps_surface_create_for_stream() for
* a more flexible mechanism for handling the PostScript output than
* simply writing it to a named file.
*
* Note that the size of individual pages of the PostScript output can
* vary. See cairo_ps_surface_set_size().
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_ps_surface_create (const char *filename,
double width_in_points,
double height_in_points)
{
cairo_output_stream_t *stream;
 
stream = _cairo_output_stream_create_for_filename (filename);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
 
return _cairo_ps_surface_create_for_stream_internal (stream,
width_in_points,
height_in_points);
}
 
/**
* cairo_ps_surface_create_for_stream:
* @write_func: a #cairo_write_func_t to accept the output data, may be %NULL
* to indicate a no-op @write_func. With a no-op @write_func,
* the surface may be queried or used as a source without
* generating any temporary files.
* @closure: the closure argument for @write_func
* @width_in_points: width of the surface, in points (1 point == 1/72.0 inch)
* @height_in_points: height of the surface, in points (1 point == 1/72.0 inch)
*
* Creates a PostScript surface of the specified size in points to be
* written incrementally to the stream represented by @write_func and
* @closure. See cairo_ps_surface_create() for a more convenient way
* to simply direct the PostScript output to a named file.
*
* Note that the size of individual pages of the PostScript
* output can vary. See cairo_ps_surface_set_size().
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
**/
cairo_surface_t *
cairo_ps_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
double width_in_points,
double height_in_points)
{
cairo_output_stream_t *stream;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
if (_cairo_output_stream_get_status (stream))
return _cairo_surface_create_in_error (_cairo_output_stream_destroy (stream));
 
return _cairo_ps_surface_create_for_stream_internal (stream,
width_in_points,
height_in_points);
}
 
static cairo_bool_t
_cairo_surface_is_ps (cairo_surface_t *surface)
{
return surface->backend == &cairo_ps_surface_backend;
}
 
/* If the abstract_surface is a paginated surface, and that paginated
* surface's target is a ps_surface, then set ps_surface to that
* target. Otherwise return FALSE.
*/
static cairo_bool_t
_extract_ps_surface (cairo_surface_t *surface,
cairo_bool_t set_error_on_failure,
cairo_ps_surface_t **ps_surface)
{
cairo_surface_t *target;
 
if (surface->status)
return FALSE;
if (surface->finished) {
if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_paginated (surface)) {
if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
target = _cairo_paginated_surface_get_target (surface);
if (target->status) {
if (set_error_on_failure)
_cairo_surface_set_error (surface, target->status);
return FALSE;
}
if (target->finished) {
if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (! _cairo_surface_is_ps (target)) {
if (set_error_on_failure)
_cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return FALSE;
}
 
*ps_surface = (cairo_ps_surface_t *) target;
return TRUE;
}
 
/**
* cairo_ps_surface_restrict_to_level:
* @surface: a PostScript #cairo_surface_t
* @level: PostScript level
*
* Restricts the generated PostSript file to @level. See
* cairo_ps_get_levels() for a list of available level values that
* can be used here.
*
* This function should only be called before any drawing operations
* have been performed on the given surface. The simplest way to do
* this is to call this function immediately after creating the
* surface.
*
* Since: 1.6
**/
void
cairo_ps_surface_restrict_to_level (cairo_surface_t *surface,
cairo_ps_level_t level)
{
cairo_ps_surface_t *ps_surface = NULL;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
if (level < CAIRO_PS_LEVEL_LAST)
ps_surface->ps_level = level;
}
 
/**
* cairo_ps_get_levels:
* @levels: supported level list
* @num_levels: list length
*
* Used to retrieve the list of supported levels. See
* cairo_ps_surface_restrict_to_level().
*
* Since: 1.6
**/
void
cairo_ps_get_levels (cairo_ps_level_t const **levels,
int *num_levels)
{
if (levels != NULL)
*levels = _cairo_ps_levels;
 
if (num_levels != NULL)
*num_levels = CAIRO_PS_LEVEL_LAST;
}
 
/**
* cairo_ps_level_to_string:
* @level: a level id
*
* Get the string representation of the given @level id. This function
* will return %NULL if @level id isn't valid. See cairo_ps_get_levels()
* for a way to get the list of valid level ids.
*
* Return value: the string associated to given level.
*
* Since: 1.6
**/
const char *
cairo_ps_level_to_string (cairo_ps_level_t level)
{
if (level >= CAIRO_PS_LEVEL_LAST)
return NULL;
 
return _cairo_ps_level_strings[level];
}
 
/**
* cairo_ps_surface_set_eps:
* @surface: a PostScript #cairo_surface_t
* @eps: %TRUE to output EPS format PostScript
*
* If @eps is %TRUE, the PostScript surface will output Encapsulated
* PostScript.
*
* This function should only be called before any drawing operations
* have been performed on the current page. The simplest way to do
* this is to call this function immediately after creating the
* surface. An Encapsulated PostScript file should never contain more
* than one page.
*
* Since: 1.6
**/
void
cairo_ps_surface_set_eps (cairo_surface_t *surface,
cairo_bool_t eps)
{
cairo_ps_surface_t *ps_surface = NULL;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
ps_surface->eps = eps;
}
 
/**
* cairo_ps_surface_get_eps:
* @surface: a PostScript #cairo_surface_t
*
* Check whether the PostScript surface will output Encapsulated PostScript.
*
* Return value: %TRUE if the surface will output Encapsulated PostScript.
*
* Since: 1.6
**/
cairo_public cairo_bool_t
cairo_ps_surface_get_eps (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
 
if (! _extract_ps_surface (surface, FALSE, &ps_surface))
return FALSE;
 
return ps_surface->eps;
}
 
/**
* cairo_ps_surface_set_size:
* @surface: a PostScript #cairo_surface_t
* @width_in_points: new surface width, in points (1 point == 1/72.0 inch)
* @height_in_points: new surface height, in points (1 point == 1/72.0 inch)
*
* Changes the size of a PostScript surface for the current (and
* subsequent) pages.
*
* This function should only be called before any drawing operations
* have been performed on the current page. The simplest way to do
* this is to call this function immediately after creating the
* surface or immediately after completing a page with either
* cairo_show_page() or cairo_copy_page().
*
* Since: 1.2
**/
void
cairo_ps_surface_set_size (cairo_surface_t *surface,
double width_in_points,
double height_in_points)
{
cairo_ps_surface_t *ps_surface = NULL;
cairo_status_t status;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
ps_surface->width = width_in_points;
ps_surface->height = height_in_points;
cairo_matrix_init (&ps_surface->cairo_to_ps, 1, 0, 0, -1, 0, height_in_points);
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&ps_surface->pdf_operators,
&ps_surface->cairo_to_ps);
status = _cairo_paginated_surface_set_size (ps_surface->paginated_surface,
width_in_points,
height_in_points);
if (status)
status = _cairo_surface_set_error (surface, status);
}
 
/**
* cairo_ps_surface_dsc_comment:
* @surface: a PostScript #cairo_surface_t
* @comment: a comment string to be emitted into the PostScript output
*
* Emit a comment into the PostScript output for the given surface.
*
* The comment is expected to conform to the PostScript Language
* Document Structuring Conventions (DSC). Please see that manual for
* details on the available comments and their meanings. In
* particular, the \%\%IncludeFeature comment allows a
* device-independent means of controlling printer device features. So
* the PostScript Printer Description Files Specification will also be
* a useful reference.
*
* The comment string must begin with a percent character (\%) and the
* total length of the string (including any initial percent
* characters) must not exceed 255 characters. Violating either of
* these conditions will place @surface into an error state. But
* beyond these two conditions, this function will not enforce
* conformance of the comment with any particular specification.
*
* The comment string should not have a trailing newline.
*
* The DSC specifies different sections in which particular comments
* can appear. This function provides for comments to be emitted
* within three sections: the header, the Setup section, and the
* PageSetup section. Comments appearing in the first two sections
* apply to the entire document while comments in the BeginPageSetup
* section apply only to a single page.
*
* For comments to appear in the header section, this function should
* be called after the surface is created, but before a call to
* cairo_ps_surface_dsc_begin_setup().
*
* For comments to appear in the Setup section, this function should
* be called after a call to cairo_ps_surface_dsc_begin_setup() but
* before a call to cairo_ps_surface_dsc_begin_page_setup().
*
* For comments to appear in the PageSetup section, this function
* should be called after a call to
* cairo_ps_surface_dsc_begin_page_setup().
*
* Note that it is only necessary to call
* cairo_ps_surface_dsc_begin_page_setup() for the first page of any
* surface. After a call to cairo_show_page() or cairo_copy_page()
* comments are unambiguously directed to the PageSetup section of the
* current page. But it doesn't hurt to call this function at the
* beginning of every page as that consistency may make the calling
* code simpler.
*
* As a final note, cairo automatically generates several comments on
* its own. As such, applications must not manually generate any of
* the following comments:
*
* Header section: \%!PS-Adobe-3.0, \%\%Creator, \%\%CreationDate, \%\%Pages,
* \%\%BoundingBox, \%\%DocumentData, \%\%LanguageLevel, \%\%EndComments.
*
* Setup section: \%\%BeginSetup, \%\%EndSetup
*
* PageSetup section: \%\%BeginPageSetup, \%\%PageBoundingBox, \%\%EndPageSetup.
*
* Other sections: \%\%BeginProlog, \%\%EndProlog, \%\%Page, \%\%Trailer, \%\%EOF
*
* Here is an example sequence showing how this function might be used:
*
* <informalexample><programlisting>
* cairo_surface_t *surface = cairo_ps_surface_create (filename, width, height);
* ...
* cairo_ps_surface_dsc_comment (surface, "%%Title: My excellent document");
* cairo_ps_surface_dsc_comment (surface, "%%Copyright: Copyright (C) 2006 Cairo Lover")
* ...
* cairo_ps_surface_dsc_begin_setup (surface);
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor White");
* ...
* cairo_ps_surface_dsc_begin_page_setup (surface);
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A3");
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *InputSlot LargeCapacity");
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaType Glossy");
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *MediaColor Blue");
* ... draw to first page here ..
* cairo_show_page (cr);
* ...
* cairo_ps_surface_dsc_comment (surface, "%%IncludeFeature: *PageSize A5");
* ...
* </programlisting></informalexample>
*
* Since: 1.2
**/
void
cairo_ps_surface_dsc_comment (cairo_surface_t *surface,
const char *comment)
{
cairo_ps_surface_t *ps_surface = NULL;
cairo_status_t status;
char *comment_copy;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
/* A couple of sanity checks on the comment value. */
if (comment == NULL) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_NULL_POINTER);
return;
}
 
if (comment[0] != '%' || strlen (comment) > 255) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_DSC_COMMENT);
return;
}
 
/* Then, copy the comment and store it in the appropriate array. */
comment_copy = strdup (comment);
if (unlikely (comment_copy == NULL)) {
status = _cairo_surface_set_error (surface, CAIRO_STATUS_NO_MEMORY);
return;
}
 
status = _cairo_array_append (ps_surface->dsc_comment_target, &comment_copy);
if (unlikely (status)) {
free (comment_copy);
status = _cairo_surface_set_error (surface, status);
return;
}
}
 
/**
* cairo_ps_surface_dsc_begin_setup:
* @surface: a PostScript #cairo_surface_t
*
* This function indicates that subsequent calls to
* cairo_ps_surface_dsc_comment() should direct comments to the Setup
* section of the PostScript output.
*
* This function should be called at most once per surface, and must
* be called before any call to cairo_ps_surface_dsc_begin_page_setup()
* and before any drawing is performed to the surface.
*
* See cairo_ps_surface_dsc_comment() for more details.
*
* Since: 1.2
**/
void
cairo_ps_surface_dsc_begin_setup (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments)
ps_surface->dsc_comment_target = &ps_surface->dsc_setup_comments;
}
 
/**
* cairo_ps_surface_dsc_begin_page_setup:
* @surface: a PostScript #cairo_surface_t
*
* This function indicates that subsequent calls to
* cairo_ps_surface_dsc_comment() should direct comments to the
* PageSetup section of the PostScript output.
*
* This function call is only needed for the first page of a
* surface. It should be called after any call to
* cairo_ps_surface_dsc_begin_setup() and before any drawing is
* performed to the surface.
*
* See cairo_ps_surface_dsc_comment() for more details.
*
* Since: 1.2
**/
void
cairo_ps_surface_dsc_begin_page_setup (cairo_surface_t *surface)
{
cairo_ps_surface_t *ps_surface = NULL;
 
if (! _extract_ps_surface (surface, TRUE, &ps_surface))
return;
 
if (ps_surface->dsc_comment_target == &ps_surface->dsc_header_comments ||
ps_surface->dsc_comment_target == &ps_surface->dsc_setup_comments)
{
ps_surface->dsc_comment_target = &ps_surface->dsc_page_setup_comments;
}
}
 
static cairo_status_t
_cairo_ps_surface_finish (void *abstract_surface)
{
cairo_status_t status, status2;
cairo_ps_surface_t *surface = abstract_surface;
int i, num_comments;
char **comments;
 
status = surface->base.status;
if (unlikely (status))
goto CLEANUP;
 
_cairo_ps_surface_emit_header (surface);
 
status = _cairo_ps_surface_emit_font_subsets (surface);
if (unlikely (status))
goto CLEANUP;
 
status = _cairo_ps_surface_emit_body (surface);
if (unlikely (status))
goto CLEANUP;
 
_cairo_ps_surface_emit_footer (surface);
 
CLEANUP:
_cairo_scaled_font_subsets_destroy (surface->font_subsets);
 
status2 = _cairo_output_stream_destroy (surface->stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
fclose (surface->tmpfile);
 
status2 = _cairo_output_stream_destroy (surface->final_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
while (! cairo_list_is_empty (&surface->document_media)) {
cairo_page_media_t *page;
 
page = cairo_list_first_entry (&surface->document_media,
cairo_page_media_t,
link);
cairo_list_del (&page->link);
free (page->name);
free (page);
}
 
num_comments = _cairo_array_num_elements (&surface->dsc_header_comments);
comments = _cairo_array_index (&surface->dsc_header_comments, 0);
for (i = 0; i < num_comments; i++)
free (comments[i]);
_cairo_array_fini (&surface->dsc_header_comments);
 
num_comments = _cairo_array_num_elements (&surface->dsc_setup_comments);
comments = _cairo_array_index (&surface->dsc_setup_comments, 0);
for (i = 0; i < num_comments; i++)
free (comments[i]);
_cairo_array_fini (&surface->dsc_setup_comments);
 
num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
for (i = 0; i < num_comments; i++)
free (comments[i]);
_cairo_array_fini (&surface->dsc_page_setup_comments);
 
_cairo_surface_clipper_reset (&surface->clipper);
 
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_start_page (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
 
/* Increment before print so page numbers start at 1. */
surface->num_pages++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_ps_surface_show_page (void *abstract_surface)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
if (surface->clipper.clip != NULL)
_cairo_surface_clipper_reset (&surface->clipper);
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->stream,
"Q Q\n"
"showpage\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
color_is_gray (double red, double green, double blue)
{
const double epsilon = 0.00001;
 
return (fabs (red - green) < epsilon &&
fabs (red - blue) < epsilon);
}
 
/**
* _cairo_ps_surface_acquire_source_surface_from_pattern:
* @surface: the ps surface
* @pattern: A #cairo_pattern_t of type SURFACE or RASTER_SOURCE to use as the source
* @extents: extents of the operation that is using this source
* @width: returns width of surface
* @height: returns height of surface
* @x_offset: returns x offset of surface
* @y_offset: returns y offset of surface
* @surface: returns surface of type image surface or recording surface
* @image_extra: returns image extra for image type surface
*
* Acquire source surface or raster source pattern.
**/
static cairo_status_t
_cairo_ps_surface_acquire_source_surface_from_pattern (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int *width,
int *height,
double *x_offset,
double *y_offset,
cairo_surface_t **source_surface,
void **image_extra)
{
cairo_status_t status;
cairo_image_surface_t *image;
 
*x_offset = *y_offset = 0;
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_t *surf = ((cairo_surface_pattern_t *) pattern)->surface;
 
if (surf->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (surf->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) surf;
 
*width = sub->extents.width;
*height = sub->extents.height;
} else {
cairo_surface_t *free_me = NULL;
cairo_recording_surface_t *recording_surface;
cairo_box_t bbox;
cairo_rectangle_int_t extents;
 
recording_surface = (cairo_recording_surface_t *) surf;
if (_cairo_surface_is_snapshot (&recording_surface->base)) {
free_me = _cairo_surface_snapshot_get_target (&recording_surface->base);
recording_surface = (cairo_recording_surface_t *) free_me;
}
 
status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL);
cairo_surface_destroy (free_me);
if (unlikely (status))
return status;
 
_cairo_box_round_to_rectangle (&bbox, &extents);
*width = extents.width;
*height = extents.height;
}
*source_surface = surf;
 
return CAIRO_STATUS_SUCCESS;
} else {
status = _cairo_surface_acquire_source_image (surf, &image, image_extra);
if (unlikely (status))
return status;
}
} break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
cairo_surface_t *surf;
cairo_box_t box;
cairo_rectangle_int_t rect;
 
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &rect);
surf = _cairo_raster_source_pattern_acquire (pattern, &surface->base, &rect);
if (!surf)
return CAIRO_INT_STATUS_UNSUPPORTED;
assert (_cairo_surface_is_image (surf));
image = (cairo_image_surface_t *) surf;
} break;
 
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
default:
ASSERT_NOT_REACHED;
break;
}
 
*width = image->width;
*height = image->height;
*source_surface = &image->base;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_ps_surface_release_source_surface_from_pattern (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_surface_t *source,
void *image_extra)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *surf_pat = (cairo_surface_pattern_t *) pattern;
if (surf_pat->surface->type != CAIRO_SURFACE_TYPE_RECORDING) {
cairo_image_surface_t *image = (cairo_image_surface_t *) source;
_cairo_surface_release_source_image (surf_pat->surface, image, image_extra);
}
} break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_raster_source_pattern_release (pattern, source);
break;
 
case CAIRO_PATTERN_TYPE_SOLID:
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
default:
 
ASSERT_NOT_REACHED;
break;
}
}
 
/**
* _cairo_ps_surface_create_padded_image_from_image:
* @surface: the ps surface
* @source: The source image
* @extents: extents of the operation that is using this source
* @width: returns width of padded image
* @height: returns height of padded image
* @x_offset: returns x offset of padded image
* @y_offset: returns y offset of padded image
* @image: returns the padded image or NULL if padding not required to fill @extents
*
* Creates a padded image if the source image does not fill the extents.
**/
static cairo_status_t
_cairo_ps_surface_create_padded_image_from_image (cairo_ps_surface_t *surface,
cairo_image_surface_t *source,
const cairo_matrix_t *source_matrix,
const cairo_rectangle_int_t *extents,
int *width,
int *height,
double *x_offset,
double *y_offset,
cairo_image_surface_t **image)
{
cairo_box_t box;
cairo_rectangle_int_t rect;
cairo_surface_t *pad_image;
cairo_surface_pattern_t pad_pattern;
int w, h;
cairo_int_status_t status;
 
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (source_matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &rect);
 
/* Check if image needs padding to fill extents. */
w = source->width;
h = source->height;
if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
_cairo_fixed_integer_ceil(box.p1.y) < 0 ||
_cairo_fixed_integer_floor(box.p2.y) > w ||
_cairo_fixed_integer_floor(box.p2.y) > h)
{
pad_image =
_cairo_image_surface_create_with_pixman_format (NULL,
source->pixman_format,
rect.width, rect.height,
0);
if (pad_image->status)
return pad_image->status;
 
_cairo_pattern_init_for_surface (&pad_pattern, &source->base);
cairo_matrix_init_translate (&pad_pattern.base.matrix, rect.x, rect.y);
pad_pattern.base.extend = CAIRO_EXTEND_PAD;
status = _cairo_surface_paint (pad_image,
CAIRO_OPERATOR_SOURCE,
&pad_pattern.base,
NULL);
_cairo_pattern_fini (&pad_pattern.base);
*image = (cairo_image_surface_t *) pad_image;
*width = rect.width;
*height = rect.height;
*x_offset = rect.x;
*y_offset = rect.y;
} else {
*image = NULL;
status = CAIRO_STATUS_SUCCESS;
}
 
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_analyze_surface_pattern_transparency (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
int width, height;
double x_offset, y_offset;
cairo_surface_t *source;
cairo_image_surface_t *image;
void *image_extra;
cairo_int_status_t status;
cairo_image_transparency_t transparency;
 
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
&width,
&height,
&x_offset,
&y_offset,
&source,
&image_extra);
if (unlikely (status))
return status;
 
image = (cairo_image_surface_t *) source;
if (image->base.status)
return image->base.status;
 
transparency = _cairo_image_analyze_transparency (image);
switch (transparency) {
case CAIRO_IMAGE_IS_OPAQUE:
status = CAIRO_STATUS_SUCCESS;
break;
 
case CAIRO_IMAGE_HAS_BILEVEL_ALPHA:
if (surface->ps_level == CAIRO_PS_LEVEL_2) {
status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
} else {
surface->ps_level_used = CAIRO_PS_LEVEL_3;
status = CAIRO_STATUS_SUCCESS;
}
break;
 
case CAIRO_IMAGE_HAS_ALPHA:
status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
break;
 
case CAIRO_IMAGE_UNKNOWN:
ASSERT_NOT_REACHED;
}
 
_cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra);
 
return status;
}
 
static cairo_bool_t
surface_pattern_supported (const cairo_surface_pattern_t *pattern)
{
if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING)
return TRUE;
 
if (pattern->surface->backend->acquire_source_image == NULL)
return FALSE;
 
/* Does an ALPHA-only source surface even make sense? Maybe, but I
* don't think it's worth the extra code to support it. */
 
/* XXX: Need to write this function here...
content = pattern->surface->content;
if (content == CAIRO_CONTENT_ALPHA)
return FALSE;
*/
 
return TRUE;
}
 
static cairo_bool_t
_gradient_pattern_supported (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern)
{
double min_alpha, max_alpha;
 
if (surface->ps_level == CAIRO_PS_LEVEL_2)
return FALSE;
 
/* Alpha gradients are only supported (by flattening the alpha)
* if there is no variation in the alpha across the gradient. */
_cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha);
if (min_alpha != max_alpha)
return FALSE;
 
surface->ps_level_used = CAIRO_PS_LEVEL_3;
 
return TRUE;
}
 
static cairo_bool_t
pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return TRUE;
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return _gradient_pattern_supported (surface, pattern);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return TRUE;
 
default:
ASSERT_NOT_REACHED;
return FALSE;
}
}
 
static cairo_bool_t
mask_supported (cairo_ps_surface_t *surface,
const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
if (surface->ps_level == CAIRO_PS_LEVEL_2)
return FALSE;
 
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) mask;
if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
/* check if mask if opaque or bilevel alpha */
if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, mask, extents) == CAIRO_INT_STATUS_SUCCESS) {
surface->ps_level_used = CAIRO_PS_LEVEL_3;
return TRUE;
}
}
}
 
return FALSE;
}
 
static cairo_int_status_t
_cairo_ps_surface_analyze_operation (cairo_ps_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
double min_alpha;
 
if (surface->force_fallbacks &&
surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (! pattern_supported (surface, pattern))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Mask is only supported when the mask is an image with opaque or bilevel alpha. */
if (mask && !mask_supported (surface, mask, extents))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
 
if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (pattern->extend == CAIRO_EXTEND_PAD) {
cairo_box_t box;
cairo_rectangle_int_t rect;
cairo_rectangle_int_t rec_extents;
 
/* get the operation extents in pattern space */
_cairo_box_from_rectangle (&box, extents);
_cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
_cairo_box_round_to_rectangle (&box, &rect);
 
/* Check if surface needs padding to fill extents */
if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
_cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
_cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
_cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
}
return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
}
}
 
if (op == CAIRO_OPERATOR_SOURCE) {
if (mask)
return CAIRO_INT_STATUS_UNSUPPORTED;
else
return CAIRO_STATUS_SUCCESS;
}
 
/* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If
* the pattern contains transparency, we return
* CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis
* surface. If the analysis surface determines that there is
* anything drawn under this operation, a fallback image will be
* used. Otherwise the operation will be replayed during the
* render stage and we blend the transparency into the white
* background to convert the pattern to opaque.
*/
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE || pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return _cairo_ps_surface_analyze_surface_pattern_transparency (surface, pattern, extents);
 
/* Patterns whose drawn part is opaque are directly supported;
those whose drawn part is partially transparent can be
supported by flattening the alpha. */
_cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
if (CAIRO_ALPHA_IS_OPAQUE (min_alpha))
return CAIRO_STATUS_SUCCESS;
 
return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
}
 
static cairo_bool_t
_cairo_ps_surface_operation_supported (cairo_ps_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
const cairo_pattern_t *mask,
const cairo_rectangle_int_t *extents)
{
return _cairo_ps_surface_analyze_operation (surface, op, pattern, mask, extents) != CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* The "standard" implementation limit for PostScript string sizes is
* 65535 characters (see PostScript Language Reference, Appendix
* B). We go one short of that because we sometimes need two
* characters in a string to represent a single ASCII85 byte, (for the
* escape sequences "\\", "\(", and "\)") and we must not split these
* across two strings. So we'd be in trouble if we went right to the
* limit and one of these escape sequences just happened to land at
* the end.
*/
#define STRING_ARRAY_MAX_STRING_SIZE (65535-1)
#define STRING_ARRAY_MAX_COLUMN 72
 
typedef struct _string_array_stream {
cairo_output_stream_t base;
cairo_output_stream_t *output;
int column;
int string_size;
cairo_bool_t use_strings;
} string_array_stream_t;
 
static cairo_status_t
_string_array_stream_write (cairo_output_stream_t *base,
const unsigned char *data,
unsigned int length)
{
string_array_stream_t *stream = (string_array_stream_t *) base;
unsigned char c;
const unsigned char backslash = '\\';
 
if (length == 0)
return CAIRO_STATUS_SUCCESS;
 
while (length--) {
if (stream->string_size == 0 && stream->use_strings) {
_cairo_output_stream_printf (stream->output, "(");
stream->column++;
}
 
c = *data++;
if (stream->use_strings) {
switch (c) {
case '\\':
case '(':
case ')':
_cairo_output_stream_write (stream->output, &backslash, 1);
stream->column++;
stream->string_size++;
break;
}
}
/* Have to be careful to never split the final ~> sequence. */
if (c == '~') {
_cairo_output_stream_write (stream->output, &c, 1);
stream->column++;
stream->string_size++;
 
if (length-- == 0)
break;
 
c = *data++;
}
_cairo_output_stream_write (stream->output, &c, 1);
stream->column++;
stream->string_size++;
 
if (stream->use_strings &&
stream->string_size >= STRING_ARRAY_MAX_STRING_SIZE)
{
_cairo_output_stream_printf (stream->output, ")\n");
stream->string_size = 0;
stream->column = 0;
}
if (stream->column >= STRING_ARRAY_MAX_COLUMN) {
_cairo_output_stream_printf (stream->output, "\n ");
stream->string_size += 2;
stream->column = 1;
}
}
 
return _cairo_output_stream_get_status (stream->output);
}
 
static cairo_status_t
_string_array_stream_close (cairo_output_stream_t *base)
{
cairo_status_t status;
string_array_stream_t *stream = (string_array_stream_t *) base;
 
if (stream->use_strings)
_cairo_output_stream_printf (stream->output, ")\n");
 
status = _cairo_output_stream_get_status (stream->output);
 
return status;
}
 
/* A string_array_stream wraps an existing output stream. It takes the
* data provided to it and output one or more consecutive string
* objects, each within the standard PostScript implementation limit
* of 65k characters.
*
* The strings are each separated by a space character for easy
* inclusion within an array object, (but the array delimiters are not
* added by the string_array_stream).
*
* The string array stream is also careful to wrap the output within
* STRING_ARRAY_MAX_COLUMN columns (+/- 1). The stream also adds
* necessary escaping for special characters within a string,
* (specifically '\', '(', and ')').
*/
static cairo_output_stream_t *
_string_array_stream_create (cairo_output_stream_t *output)
{
string_array_stream_t *stream;
 
stream = malloc (sizeof (string_array_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
 
_cairo_output_stream_init (&stream->base,
_string_array_stream_write,
NULL,
_string_array_stream_close);
stream->output = output;
stream->column = 0;
stream->string_size = 0;
stream->use_strings = TRUE;
 
return &stream->base;
}
 
/* A base85_array_stream wraps an existing output stream. It wraps the
* output within STRING_ARRAY_MAX_COLUMN columns (+/- 1). The output
* is not enclosed in strings like string_array_stream.
*/
static cairo_output_stream_t *
_base85_array_stream_create (cairo_output_stream_t *output)
{
string_array_stream_t *stream;
 
stream = malloc (sizeof (string_array_stream_t));
if (unlikely (stream == NULL)) {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_output_stream_t *) &_cairo_output_stream_nil;
}
 
_cairo_output_stream_init (&stream->base,
_string_array_stream_write,
NULL,
_string_array_stream_close);
stream->output = output;
stream->column = 0;
stream->string_size = 0;
stream->use_strings = FALSE;
 
return &stream->base;
}
 
 
/* PS Output - this section handles output of the parts of the recording
* surface we can render natively in PS. */
 
static cairo_status_t
_cairo_ps_surface_flatten_image_transparency (cairo_ps_surface_t *surface,
cairo_image_surface_t *image,
cairo_image_surface_t **opaque_image)
{
cairo_surface_t *opaque;
cairo_surface_pattern_t pattern;
cairo_status_t status;
 
opaque = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
image->width,
image->height);
if (unlikely (opaque->status))
return opaque->status;
 
if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
status = _cairo_surface_paint (opaque,
CAIRO_OPERATOR_SOURCE,
&_cairo_pattern_white.base,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (opaque);
return status;
}
}
 
_cairo_pattern_init_for_surface (&pattern, &image->base);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (opaque, CAIRO_OPERATOR_OVER, &pattern.base, NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status)) {
cairo_surface_destroy (opaque);
return status;
}
 
*opaque_image = (cairo_image_surface_t *) opaque;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_emit_base85_string (cairo_ps_surface_t *surface,
const unsigned char *data,
unsigned long length,
cairo_ps_compress_t compress,
cairo_bool_t use_strings)
{
cairo_output_stream_t *base85_stream, *string_array_stream, *deflate_stream;
unsigned char *data_compressed;
unsigned long data_compressed_size;
cairo_status_t status, status2;
 
if (use_strings)
string_array_stream = _string_array_stream_create (surface->stream);
else
string_array_stream = _base85_array_stream_create (surface->stream);
 
status = _cairo_output_stream_get_status (string_array_stream);
if (unlikely (status))
return _cairo_output_stream_destroy (string_array_stream);
 
base85_stream = _cairo_base85_stream_create (string_array_stream);
status = _cairo_output_stream_get_status (base85_stream);
if (unlikely (status)) {
status2 = _cairo_output_stream_destroy (string_array_stream);
return _cairo_output_stream_destroy (base85_stream);
}
 
switch (compress) {
case CAIRO_PS_COMPRESS_NONE:
_cairo_output_stream_write (base85_stream, data, length);
break;
 
case CAIRO_PS_COMPRESS_LZW:
/* XXX: Should fix cairo-lzw to provide a stream-based interface
* instead. */
data_compressed_size = length;
data_compressed = _cairo_lzw_compress ((unsigned char*)data, &data_compressed_size);
if (unlikely (data_compressed == NULL)) {
status = _cairo_output_stream_destroy (string_array_stream);
status = _cairo_output_stream_destroy (base85_stream);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
_cairo_output_stream_write (base85_stream, data_compressed, data_compressed_size);
free (data_compressed);
break;
 
case CAIRO_PS_COMPRESS_DEFLATE:
deflate_stream = _cairo_deflate_stream_create (base85_stream);
if (_cairo_output_stream_get_status (deflate_stream)) {
return _cairo_output_stream_destroy (deflate_stream);
}
_cairo_output_stream_write (deflate_stream, data, length);
status = _cairo_output_stream_destroy (deflate_stream);
if (unlikely (status)) {
status2 = _cairo_output_stream_destroy (string_array_stream);
status2 = _cairo_output_stream_destroy (base85_stream);
return _cairo_output_stream_destroy (deflate_stream);
}
break;
}
status = _cairo_output_stream_destroy (base85_stream);
 
/* Mark end of base85 data */
_cairo_output_stream_printf (string_array_stream, "~>");
status2 = _cairo_output_stream_destroy (string_array_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_image (cairo_ps_surface_t *surface,
cairo_image_surface_t *image_surf,
cairo_operator_t op,
cairo_filter_t filter,
cairo_bool_t stencil_mask)
{
cairo_status_t status;
unsigned char *data;
unsigned long data_size;
cairo_image_surface_t *ps_image;
int x, y, i, a;
cairo_image_transparency_t transparency;
cairo_bool_t use_mask;
uint32_t *pixel32;
uint8_t *pixel8;
int bit;
cairo_image_color_t color;
const char *interpolate;
cairo_ps_compress_t compress;
const char *compress_filter;
cairo_image_surface_t *image;
 
if (image_surf->base.status)
return image_surf->base.status;
 
image = image_surf;
if (image->format != CAIRO_FORMAT_RGB24 &&
image->format != CAIRO_FORMAT_ARGB32 &&
image->format != CAIRO_FORMAT_A8 &&
image->format != CAIRO_FORMAT_A1)
{
cairo_surface_t *surf;
cairo_surface_pattern_t pattern;
 
surf = _cairo_image_surface_create_with_content (image_surf->base.content,
image_surf->width,
image_surf->height);
image = (cairo_image_surface_t *) surf;
if (surf->status) {
status = surf->status;
goto bail0;
}
 
_cairo_pattern_init_for_surface (&pattern, &image_surf->base);
status = _cairo_surface_paint (surf,
CAIRO_OPERATOR_SOURCE, &pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status))
goto bail0;
}
ps_image = image;
 
switch (filter) {
default:
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
interpolate = "true";
break;
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
case CAIRO_FILTER_GAUSSIAN:
interpolate = "false";
break;
}
 
if (stencil_mask) {
use_mask = FALSE;
color = CAIRO_IMAGE_IS_MONOCHROME;
transparency = CAIRO_IMAGE_HAS_BILEVEL_ALPHA;
} else {
transparency = _cairo_image_analyze_transparency (image);
 
/* PostScript can not represent the alpha channel, so we blend the
current image over a white (or black for CONTENT_COLOR
surfaces) RGB surface to eliminate it. */
 
if (op == CAIRO_OPERATOR_SOURCE ||
transparency == CAIRO_IMAGE_HAS_ALPHA ||
(transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA &&
surface->ps_level == CAIRO_PS_LEVEL_2))
{
status = _cairo_ps_surface_flatten_image_transparency (surface,
image,
&ps_image);
if (unlikely (status))
return status;
 
use_mask = FALSE;
} else if (transparency == CAIRO_IMAGE_IS_OPAQUE) {
use_mask = FALSE;
} else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA */
use_mask = TRUE;
}
 
color = _cairo_image_analyze_color (ps_image);
}
 
/* Type 2 (mask and image interleaved) has the mask and image
* samples interleaved by row. The mask row is first, one bit per
* pixel with (bit 7 first). The row is padded to byte
* boundaries. The image data is 3 bytes per pixel RGB format. */
switch (color) {
default:
case CAIRO_IMAGE_UNKNOWN_COLOR:
ASSERT_NOT_REACHED;
case CAIRO_IMAGE_IS_COLOR:
data_size = ps_image->width * 3;
break;
case CAIRO_IMAGE_IS_GRAYSCALE:
data_size = ps_image->width;
break;
case CAIRO_IMAGE_IS_MONOCHROME:
data_size = (ps_image->width + 7)/8;
break;
}
if (use_mask)
data_size += (ps_image->width + 7)/8;
data_size *= ps_image->height;
data = malloc (data_size);
if (unlikely (data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail1;
}
 
i = 0;
for (y = 0; y < ps_image->height; y++) {
if (stencil_mask || use_mask) {
/* mask row */
if (ps_image->format == CAIRO_FORMAT_A1) {
pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
 
for (x = 0; x < (ps_image->width + 7) / 8; x++, pixel8++) {
a = *pixel8;
a = CAIRO_BITSWAP8_IF_LITTLE_ENDIAN (a);
data[i++] = a;
}
} else {
pixel8 = (uint8_t *) (ps_image->data + y * ps_image->stride);
pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
bit = 7;
for (x = 0; x < ps_image->width; x++) {
if (ps_image->format == CAIRO_FORMAT_ARGB32) {
a = (*pixel32 & 0xff000000) >> 24;
pixel32++;
} else {
a = *pixel8;
pixel8++;
}
 
if (transparency == CAIRO_IMAGE_HAS_ALPHA) {
data[i++] = a;
} else { /* transparency == CAIRO_IMAGE_HAS_BILEVEL_ALPHA or CAIRO_IMAGE_IS_OPAQUE */
if (bit == 7)
data[i] = 0;
if (a != 0)
data[i] |= (1 << bit);
bit--;
if (bit < 0) {
bit = 7;
i++;
}
}
}
if (bit != 7)
i++;
}
}
if (stencil_mask)
continue;
 
/* image row*/
pixel32 = (uint32_t *) (ps_image->data + y * ps_image->stride);
bit = 7;
for (x = 0; x < ps_image->width; x++, pixel32++) {
int r, g, b;
 
if (ps_image->format == CAIRO_FORMAT_ARGB32) {
/* At this point ARGB32 images are either opaque or
* bilevel alpha so we don't need to unpremultiply. */
if (((*pixel32 & 0xff000000) >> 24) == 0) {
r = g = b = 0;
} else {
r = (*pixel32 & 0x00ff0000) >> 16;
g = (*pixel32 & 0x0000ff00) >> 8;
b = (*pixel32 & 0x000000ff) >> 0;
}
} else if (ps_image->format == CAIRO_FORMAT_RGB24) {
r = (*pixel32 & 0x00ff0000) >> 16;
g = (*pixel32 & 0x0000ff00) >> 8;
b = (*pixel32 & 0x000000ff) >> 0;
} else {
r = g = b = 0;
}
 
switch (color) {
case CAIRO_IMAGE_IS_COLOR:
case CAIRO_IMAGE_UNKNOWN_COLOR:
data[i++] = r;
data[i++] = g;
data[i++] = b;
break;
 
case CAIRO_IMAGE_IS_GRAYSCALE:
data[i++] = r;
break;
 
case CAIRO_IMAGE_IS_MONOCHROME:
if (bit == 7)
data[i] = 0;
if (r != 0)
data[i] |= (1 << bit);
bit--;
if (bit < 0) {
bit = 7;
i++;
}
break;
}
}
if (bit != 7)
i++;
}
 
if (surface->ps_level == CAIRO_PS_LEVEL_2) {
compress = CAIRO_PS_COMPRESS_LZW;
compress_filter = "LZWDecode";
} else {
compress = CAIRO_PS_COMPRESS_DEFLATE;
compress_filter = "FlateDecode";
surface->ps_level_used = CAIRO_PS_LEVEL_3;
}
 
if (surface->use_string_datasource) {
/* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator later. */
_cairo_output_stream_printf (surface->stream,
"/CairoImageData [\n");
 
status = _cairo_ps_surface_emit_base85_string (surface,
data,
data_size,
compress,
TRUE);
if (unlikely (status))
goto bail2;
 
_cairo_output_stream_printf (surface->stream,
"] def\n");
_cairo_output_stream_printf (surface->stream,
"/CairoImageDataIndex 0 def\n");
}
 
if (use_mask) {
_cairo_output_stream_printf (surface->stream,
"%s setcolorspace\n"
"5 dict dup begin\n"
" /ImageType 3 def\n"
" /InterleaveType 2 def\n"
" /DataDict 8 dict def\n"
" DataDict begin\n"
" /ImageType 1 def\n"
" /Width %d def\n"
" /Height %d def\n"
" /Interpolate %s def\n"
" /BitsPerComponent %d def\n"
" /Decode [ %s ] def\n",
color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray",
ps_image->width,
ps_image->height,
interpolate,
color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
 
if (surface->use_string_datasource) {
_cairo_output_stream_printf (surface->stream,
" /DataSource {\n"
" CairoImageData CairoImageDataIndex get\n"
" /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
" CairoImageDataIndex CairoImageData length 1 sub gt\n"
" { /CairoImageDataIndex 0 def } if\n"
" } /ASCII85Decode filter /%s filter def\n",
compress_filter);
} else {
_cairo_output_stream_printf (surface->stream,
" /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
compress_filter);
}
 
_cairo_output_stream_printf (surface->stream,
" /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
" end\n"
" /MaskDict 8 dict def\n"
" MaskDict begin\n"
" /ImageType 1 def\n"
" /Width %d def\n"
" /Height %d def\n"
" /Interpolate %s def\n"
" /BitsPerComponent 1 def\n"
" /Decode [ 1 0 ] def\n"
" /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
" end\n"
"end\n"
"image\n",
ps_image->height,
ps_image->width,
ps_image->height,
interpolate,
ps_image->height);
} else {
if (!stencil_mask) {
_cairo_output_stream_printf (surface->stream,
"%s setcolorspace\n",
color == CAIRO_IMAGE_IS_COLOR ? "/DeviceRGB" : "/DeviceGray");
}
_cairo_output_stream_printf (surface->stream,
"8 dict dup begin\n"
" /ImageType 1 def\n"
" /Width %d def\n"
" /Height %d def\n"
" /Interpolate %s def\n"
" /BitsPerComponent %d def\n"
" /Decode [ %s ] def\n",
ps_image->width,
ps_image->height,
interpolate,
color == CAIRO_IMAGE_IS_MONOCHROME ? 1 : 8,
stencil_mask ? "1 0" : color == CAIRO_IMAGE_IS_COLOR ? "0 1 0 1 0 1" : "0 1");
if (surface->use_string_datasource) {
_cairo_output_stream_printf (surface->stream,
" /DataSource {\n"
" CairoImageData CairoImageDataIndex get\n"
" /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
" CairoImageDataIndex CairoImageData length 1 sub gt\n"
" { /CairoImageDataIndex 0 def } if\n"
" } /ASCII85Decode filter /%s filter def\n",
compress_filter);
} else {
_cairo_output_stream_printf (surface->stream,
" /DataSource currentfile /ASCII85Decode filter /%s filter def\n",
compress_filter);
}
 
_cairo_output_stream_printf (surface->stream,
" /Interpolate %s def\n"
" /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
"end\n"
"%s\n",
interpolate,
ps_image->height,
stencil_mask ? "imagemask" : "image");
}
 
if (!surface->use_string_datasource) {
/* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator. */
status = _cairo_ps_surface_emit_base85_string (surface,
data,
data_size,
compress,
FALSE);
_cairo_output_stream_printf (surface->stream, "\n");
} else {
status = CAIRO_STATUS_SUCCESS;
}
 
bail2:
free (data);
 
bail1:
if (!use_mask && ps_image != image)
cairo_surface_destroy (&ps_image->base);
 
bail0:
if (image != image_surf)
cairo_surface_destroy (&image->base);
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface,
cairo_surface_t *source,
int width,
int height)
{
cairo_status_t status;
const unsigned char *mime_data;
unsigned long mime_data_length;
cairo_image_info_t info;
const char *colorspace;
const char *decode;
 
cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG,
&mime_data, &mime_data_length);
if (unlikely (source->status))
return source->status;
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_image_info_get_jpeg_info (&info, mime_data, mime_data_length);
if (unlikely (status))
return status;
 
switch (info.num_components) {
case 1:
colorspace = "/DeviceGray";
decode = "0 1";
break;
case 3:
colorspace = "/DeviceRGB";
decode = "0 1 0 1 0 1";
break;
case 4:
colorspace = "/DeviceCMYK";
decode = "0 1 0 1 0 1 0 1";
break;
default:
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (surface->use_string_datasource) {
/* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator later. */
_cairo_output_stream_printf (surface->stream,
"/CairoImageData [\n");
 
status = _cairo_ps_surface_emit_base85_string (surface,
mime_data,
mime_data_length,
CAIRO_PS_COMPRESS_NONE,
TRUE);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->stream,
"] def\n");
_cairo_output_stream_printf (surface->stream,
"/CairoImageDataIndex 0 def\n");
}
 
_cairo_output_stream_printf (surface->stream,
"%s setcolorspace\n"
"8 dict dup begin\n"
" /ImageType 1 def\n"
" /Width %d def\n"
" /Height %d def\n"
" /BitsPerComponent %d def\n"
" /Decode [ %s ] def\n",
colorspace,
info.width,
info.height,
info.bits_per_component,
decode);
 
if (surface->use_string_datasource) {
_cairo_output_stream_printf (surface->stream,
" /DataSource {\n"
" CairoImageData CairoImageDataIndex get\n"
" /CairoImageDataIndex CairoImageDataIndex 1 add def\n"
" CairoImageDataIndex CairoImageData length 1 sub gt\n"
" { /CairoImageDataIndex 0 def } if\n"
" } /ASCII85Decode filter /DCTDecode filter def\n");
} else {
_cairo_output_stream_printf (surface->stream,
" /DataSource currentfile /ASCII85Decode filter /DCTDecode filter def\n");
}
 
_cairo_output_stream_printf (surface->stream,
" /ImageMatrix [ 1 0 0 -1 0 %d ] def\n"
"end\n"
"image\n",
info.height);
 
if (!surface->use_string_datasource) {
/* Emit the image data as a base85-encoded string which will
* be used as the data source for the image operator. */
status = _cairo_ps_surface_emit_base85_string (surface,
mime_data,
mime_data_length,
CAIRO_PS_COMPRESS_NONE,
FALSE);
}
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_recording_surface (cairo_ps_surface_t *surface,
cairo_surface_t *recording_surface)
{
double old_width, old_height;
cairo_matrix_t old_cairo_to_ps;
cairo_content_t old_content;
cairo_rectangle_int_t old_page_bbox;
cairo_surface_t *free_me = NULL;
cairo_surface_clipper_t old_clipper;
cairo_box_t bbox;
cairo_int_status_t status;
 
old_content = surface->content;
old_width = surface->width;
old_height = surface->height;
old_page_bbox = surface->page_bbox;
old_cairo_to_ps = surface->cairo_to_ps;
old_clipper = surface->clipper;
_cairo_surface_clipper_init (&surface->clipper,
_cairo_ps_surface_clipper_intersect_clip_path);
 
if (_cairo_surface_is_snapshot (recording_surface))
free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
 
status =
_cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
&bbox,
NULL);
if (unlikely (status))
goto err;
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
"%% _cairo_ps_surface_emit_recording_surface (%f, %f), (%f, %f)\n",
_cairo_fixed_to_double (bbox.p1.x),
_cairo_fixed_to_double (bbox.p1.y),
_cairo_fixed_to_double (bbox.p2.x),
_cairo_fixed_to_double (bbox.p2.y));
#endif
 
surface->width = _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x);
surface->height = _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y);
_cairo_box_round_to_rectangle (&bbox, &surface->page_bbox);
 
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
&surface->cairo_to_ps);
_cairo_output_stream_printf (surface->stream, " q\n");
 
if (recording_surface->content == CAIRO_CONTENT_COLOR) {
surface->content = CAIRO_CONTENT_COLOR;
_cairo_output_stream_printf (surface->stream,
" 0 g %d %d %d %d rectfill\n",
surface->page_bbox.x,
surface->page_bbox.y,
surface->page_bbox.width,
surface->page_bbox.height);
}
 
status = _cairo_recording_surface_replay_region (recording_surface,
NULL,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
if (unlikely (status))
goto err;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto err;
 
_cairo_output_stream_printf (surface->stream, " Q\n");
 
_cairo_surface_clipper_reset (&surface->clipper);
surface->clipper = old_clipper;
surface->content = old_content;
surface->width = old_width;
surface->height = old_height;
surface->page_bbox = old_page_bbox;
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
surface->cairo_to_ps = old_cairo_to_ps;
 
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
&surface->cairo_to_ps);
 
err:
cairo_surface_destroy (free_me);
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_emit_recording_subsurface (cairo_ps_surface_t *surface,
cairo_surface_t *recording_surface,
const cairo_rectangle_int_t *extents)
{
double old_width, old_height;
cairo_matrix_t old_cairo_to_ps;
cairo_content_t old_content;
cairo_rectangle_int_t old_page_bbox;
cairo_surface_clipper_t old_clipper;
cairo_surface_t *free_me = NULL;
cairo_int_status_t status;
 
old_content = surface->content;
old_width = surface->width;
old_height = surface->height;
old_page_bbox = surface->page_bbox;
old_cairo_to_ps = surface->cairo_to_ps;
old_clipper = surface->clipper;
_cairo_surface_clipper_init (&surface->clipper,
_cairo_ps_surface_clipper_intersect_clip_path);
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
"%% _cairo_ps_surface_emit_recording_subsurface (%d, %d), (%d, %d)\n",
extents->x, extents->y,
extents->width, extents->height);
#endif
 
surface->page_bbox.x = surface->page_bbox.y = 0;
surface->page_bbox.width = surface->width = extents->width;
surface->page_bbox.height = surface->height = extents->height;
 
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
cairo_matrix_init (&surface->cairo_to_ps, 1, 0, 0, -1, 0, surface->height);
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
&surface->cairo_to_ps);
_cairo_output_stream_printf (surface->stream, " q\n");
 
if (_cairo_surface_is_snapshot (recording_surface))
free_me = recording_surface = _cairo_surface_snapshot_get_target (recording_surface);
 
if (recording_surface->content == CAIRO_CONTENT_COLOR) {
surface->content = CAIRO_CONTENT_COLOR;
_cairo_output_stream_printf (surface->stream,
" 0 g %d %d %d %d rectfill\n",
surface->page_bbox.x,
surface->page_bbox.y,
surface->page_bbox.width,
surface->page_bbox.height);
}
 
status = _cairo_recording_surface_replay_region (recording_surface,
extents,
&surface->base,
CAIRO_RECORDING_REGION_NATIVE);
assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
if (unlikely (status))
goto err;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto err;
 
_cairo_output_stream_printf (surface->stream, " Q\n");
 
_cairo_surface_clipper_reset (&surface->clipper);
surface->clipper = old_clipper;
surface->content = old_content;
surface->width = old_width;
surface->height = old_height;
surface->page_bbox = old_page_bbox;
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
surface->cairo_to_ps = old_cairo_to_ps;
 
_cairo_pdf_operators_set_cairo_to_pdf_matrix (&surface->pdf_operators,
&surface->cairo_to_ps);
 
err:
cairo_surface_destroy (free_me);
return status;
}
 
static void
_cairo_ps_surface_flatten_transparency (cairo_ps_surface_t *surface,
const cairo_color_t *color,
double *red,
double *green,
double *blue)
{
*red = color->red;
*green = color->green;
*blue = color->blue;
 
if (! CAIRO_COLOR_IS_OPAQUE (color)) {
*red *= color->alpha;
*green *= color->alpha;
*blue *= color->alpha;
if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) {
double one_minus_alpha = 1. - color->alpha;
*red += one_minus_alpha;
*green += one_minus_alpha;
*blue += one_minus_alpha;
}
}
}
 
static void
_cairo_ps_surface_emit_solid_pattern (cairo_ps_surface_t *surface,
cairo_solid_pattern_t *pattern)
{
double red, green, blue;
 
_cairo_ps_surface_flatten_transparency (surface, &pattern->color, &red, &green, &blue);
 
if (color_is_gray (red, green, blue))
_cairo_output_stream_printf (surface->stream,
"%f g\n",
red);
else
_cairo_output_stream_printf (surface->stream,
"%f %f %f rg\n",
red, green, blue);
}
 
static cairo_status_t
_cairo_ps_surface_emit_surface (cairo_ps_surface_t *surface,
cairo_pattern_t *source_pattern,
cairo_surface_t *source_surface,
cairo_operator_t op,
int width,
int height,
cairo_bool_t stencil_mask)
{
cairo_int_status_t status;
 
if (source_surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
if (source_surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source_surface;
status = _cairo_ps_surface_emit_recording_subsurface (surface, sub->target, &sub->extents);
} else {
status = _cairo_ps_surface_emit_recording_surface (surface, source_surface);
}
} else {
cairo_image_surface_t *image = (cairo_image_surface_t *) source_surface;
if (source_pattern->extend != CAIRO_EXTEND_PAD) {
status = _cairo_ps_surface_emit_jpeg_image (surface, source_surface,
width, height);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
status = _cairo_ps_surface_emit_image (surface, image,
op, source_pattern->filter, stencil_mask);
}
 
return status;
}
 
 
static void
_path_fixed_init_rectangle (cairo_path_fixed_t *path,
cairo_rectangle_int_t *rect)
{
cairo_status_t status;
 
_cairo_path_fixed_init (path);
 
status = _cairo_path_fixed_move_to (path,
_cairo_fixed_from_int (rect->x),
_cairo_fixed_from_int (rect->y));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (rect->width),
_cairo_fixed_from_int (0));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (0),
_cairo_fixed_from_int (rect->height));
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_path_fixed_rel_line_to (path,
_cairo_fixed_from_int (-rect->width),
_cairo_fixed_from_int (0));
assert (status == CAIRO_STATUS_SUCCESS);
 
status = _cairo_path_fixed_close_path (path);
assert (status == CAIRO_STATUS_SUCCESS);
}
 
static cairo_status_t
_cairo_ps_surface_paint_surface (cairo_ps_surface_t *surface,
cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents,
cairo_operator_t op,
cairo_bool_t stencil_mask)
{
cairo_status_t status;
int width, height;
cairo_matrix_t cairo_p2d, ps_p2d;
cairo_path_fixed_t path;
double x_offset, y_offset;
cairo_surface_t *source;
cairo_image_surface_t *image = NULL;
void *image_extra;
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
&width, &height,
&x_offset, &y_offset,
&source,
&image_extra);
if (unlikely (status))
return status;
 
if (pattern->extend == CAIRO_EXTEND_PAD &&
pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
((cairo_surface_pattern_t *)pattern)->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
cairo_image_surface_t *img;
 
img = (cairo_image_surface_t *) source;
status = _cairo_ps_surface_create_padded_image_from_image (surface,
img,
&pattern->matrix,
extents,
&width, &height,
&x_offset, &y_offset,
&image);
if (unlikely (status))
goto release_source;
}
 
_path_fixed_init_rectangle (&path, extents);
status = _cairo_pdf_operators_clip (&surface->pdf_operators,
&path,
CAIRO_FILL_RULE_WINDING);
_cairo_path_fixed_fini (&path);
if (unlikely (status))
goto release_source;
 
cairo_p2d = pattern->matrix;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_FALLBACK) {
double x_scale = cairo_p2d.xx;
double y_scale = cairo_p2d.yy;
 
_cairo_output_stream_printf (surface->stream,
"%% Fallback Image: x=%f y=%f w=%d h=%d ",
-cairo_p2d.x0/x_scale,
-cairo_p2d.y0/y_scale,
(int)(width/x_scale),
(int)(height/y_scale));
if (x_scale == y_scale) {
_cairo_output_stream_printf (surface->stream,
"res=%fppi ",
x_scale*72);
} else {
_cairo_output_stream_printf (surface->stream,
"res=%fx%fppi ",
x_scale*72,
y_scale*72);
}
_cairo_output_stream_printf (surface->stream,
"size=%ld\n",
(long)width*height*3);
} else {
if (op == CAIRO_OPERATOR_SOURCE) {
_cairo_output_stream_printf (surface->stream,
"%d g 0 0 %f %f rectfill\n",
surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
surface->width,
surface->height);
}
}
 
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
ps_p2d = surface->cairo_to_ps;
cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d);
cairo_matrix_translate (&ps_p2d, x_offset, y_offset);
cairo_matrix_translate (&ps_p2d, 0.0, height);
cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
 
if (! _cairo_matrix_is_identity (&ps_p2d)) {
_cairo_output_stream_printf (surface->stream,
"[ %f %f %f %f %f %f ] concat\n",
ps_p2d.xx, ps_p2d.yx,
ps_p2d.xy, ps_p2d.yy,
ps_p2d.x0, ps_p2d.y0);
}
 
status = _cairo_ps_surface_emit_surface (surface,
pattern,
image ? &image->base : source,
op,
width, height,
stencil_mask);
 
release_source:
if (image)
cairo_surface_destroy (&image->base);
 
_cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra);
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_surface_pattern (cairo_ps_surface_t *surface,
cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents,
cairo_operator_t op)
{
cairo_status_t status;
int pattern_width = 0; /* squelch bogus compiler warning */
int pattern_height = 0; /* squelch bogus compiler warning */
double xstep, ystep;
cairo_matrix_t cairo_p2d, ps_p2d;
cairo_bool_t old_use_string_datasource;
double x_offset, y_offset;
cairo_surface_t *source;
cairo_image_surface_t *image = NULL;
void *image_extra;
 
cairo_p2d = pattern->matrix;
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
status = _cairo_ps_surface_acquire_source_surface_from_pattern (surface,
pattern,
extents,
&pattern_width, &pattern_height,
&x_offset, &y_offset,
&source,
&image_extra);
if (unlikely (status))
return status;
 
if (pattern->extend == CAIRO_EXTEND_PAD) {
cairo_image_surface_t *img;
 
assert (source->type == CAIRO_SURFACE_TYPE_IMAGE);
img = (cairo_image_surface_t *) source;
status = _cairo_ps_surface_create_padded_image_from_image (surface,
img,
&pattern->matrix,
extents,
&pattern_width, &pattern_height,
&x_offset, &y_offset,
&image);
if (unlikely (status))
goto release_source;
}
if (unlikely (status))
goto release_source;
 
switch (pattern->extend) {
case CAIRO_EXTEND_PAD:
case CAIRO_EXTEND_NONE:
{
/* In PS/PDF, (as far as I can tell), all patterns are
* repeating. So we support cairo's EXTEND_NONE semantics
* by setting the repeat step size to a size large enough
* to guarantee that no more than a single occurrence will
* be visible.
*
* First, map the surface extents into pattern space (since
* xstep and ystep are in pattern space). Then use an upper
* bound on the length of the diagonal of the pattern image
* and the surface as repeat size. This guarantees to never
* repeat visibly.
*/
double x1 = 0.0, y1 = 0.0;
double x2 = surface->width, y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->matrix,
&x1, &y1, &x2, &y2,
NULL);
 
/* Rather than computing precise bounds of the union, just
* add the surface extents unconditionally. We only
* required an answer that's large enough, we don't really
* care if it's not as tight as possible.*/
xstep = ystep = ceil ((x2 - x1) + (y2 - y1) +
pattern_width + pattern_height);
break;
}
case CAIRO_EXTEND_REPEAT:
xstep = pattern_width;
ystep = pattern_height;
break;
case CAIRO_EXTEND_REFLECT:
xstep = pattern_width*2;
ystep = pattern_height*2;
break;
/* All the rest (if any) should have been analyzed away, so these
* cases should be unreachable. */
default:
ASSERT_NOT_REACHED;
xstep = 0;
ystep = 0;
}
 
_cairo_output_stream_printf (surface->stream,
"/CairoPattern {\n");
 
old_use_string_datasource = surface->use_string_datasource;
surface->use_string_datasource = TRUE;
if (op == CAIRO_OPERATOR_SOURCE) {
_cairo_output_stream_printf (surface->stream,
"%d g 0 0 %f %f rectfill\n",
surface->content == CAIRO_CONTENT_COLOR ? 0 : 1,
xstep, ystep);
}
status = _cairo_ps_surface_emit_surface (surface,
pattern,
image ? &image->base : source,
op,
pattern_width, pattern_height, FALSE);
if (unlikely (status))
goto release_source;
 
surface->use_string_datasource = old_use_string_datasource;
_cairo_output_stream_printf (surface->stream,
"} bind def\n");
 
_cairo_output_stream_printf (surface->stream,
"<< /PatternType 1\n"
" /PaintType 1\n"
" /TilingType 1\n");
_cairo_output_stream_printf (surface->stream,
" /XStep %f /YStep %f\n",
xstep, ystep);
 
if (pattern->extend == CAIRO_EXTEND_REFLECT) {
_cairo_output_stream_printf (surface->stream,
" /BBox [0 0 %d %d]\n"
" /PaintProc {\n"
" CairoPattern\n"
" [-1 0 0 1 %d 0] concat CairoPattern\n"
" [ 1 0 0 -1 0 %d] concat CairoPattern\n"
" [-1 0 0 1 %d 0] concat CairoPattern\n"
" CairoPattern\n"
" } bind\n",
pattern_width*2, pattern_height*2,
pattern_width*2,
pattern_height*2,
pattern_width*2);
} else {
if (op == CAIRO_OPERATOR_SOURCE) {
_cairo_output_stream_printf (surface->stream,
" /BBox [0 0 %f %f]\n",
xstep, ystep);
} else {
_cairo_output_stream_printf (surface->stream,
" /BBox [0 0 %d %d]\n",
pattern_width, pattern_height);
}
_cairo_output_stream_printf (surface->stream,
" /PaintProc { CairoPattern }\n");
}
 
_cairo_output_stream_printf (surface->stream,
">>\n");
 
cairo_p2d = pattern->matrix;
status = cairo_matrix_invert (&cairo_p2d);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
cairo_matrix_init_identity (&ps_p2d);
cairo_matrix_translate (&ps_p2d, 0.0, surface->height);
cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
cairo_matrix_multiply (&ps_p2d, &cairo_p2d, &ps_p2d);
cairo_matrix_translate (&ps_p2d, 0.0, pattern_height);
cairo_matrix_scale (&ps_p2d, 1.0, -1.0);
 
_cairo_output_stream_printf (surface->stream,
"[ %f %f %f %f %f %f ]\n",
ps_p2d.xx, ps_p2d.yx,
ps_p2d.xy, ps_p2d.yy,
ps_p2d.x0, ps_p2d.y0);
_cairo_output_stream_printf (surface->stream,
"makepattern setpattern\n");
 
release_source:
if (image)
cairo_surface_destroy (&image->base);
 
_cairo_ps_surface_release_source_surface_from_pattern (surface, pattern, source, image_extra);
 
return status;
}
 
typedef struct _cairo_ps_color_stop {
double offset;
double color[4];
} cairo_ps_color_stop_t;
 
static void
_cairo_ps_surface_emit_linear_colorgradient (cairo_ps_surface_t *surface,
cairo_ps_color_stop_t *stop1,
cairo_ps_color_stop_t *stop2)
{
_cairo_output_stream_printf (surface->stream,
" << /FunctionType 2\n"
" /Domain [ 0 1 ]\n"
" /C0 [ %f %f %f ]\n"
" /C1 [ %f %f %f ]\n"
" /N 1\n"
" >>\n",
stop1->color[0],
stop1->color[1],
stop1->color[2],
stop2->color[0],
stop2->color[1],
stop2->color[2]);
}
 
static void
_cairo_ps_surface_emit_stitched_colorgradient (cairo_ps_surface_t *surface,
unsigned int n_stops,
cairo_ps_color_stop_t stops[])
{
unsigned int i;
 
_cairo_output_stream_printf (surface->stream,
"<< /FunctionType 3\n"
" /Domain [ 0 1 ]\n"
" /Functions [\n");
for (i = 0; i < n_stops - 1; i++)
_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[i], &stops[i+1]);
 
_cairo_output_stream_printf (surface->stream, " ]\n");
 
_cairo_output_stream_printf (surface->stream, " /Bounds [ ");
for (i = 1; i < n_stops-1; i++)
_cairo_output_stream_printf (surface->stream, "%f ", stops[i].offset);
_cairo_output_stream_printf (surface->stream, "]\n");
 
_cairo_output_stream_printf (surface->stream, " /Encode [ 1 1 %d { pop 0 1 } for ]\n",
n_stops - 1);
 
_cairo_output_stream_printf (surface->stream, ">>\n");
}
 
static void
calc_gradient_color (cairo_ps_color_stop_t *new_stop,
cairo_ps_color_stop_t *stop1,
cairo_ps_color_stop_t *stop2)
{
int i;
double offset = stop1->offset / (stop1->offset + 1.0 - stop2->offset);
 
for (i = 0; i < 4; i++)
new_stop->color[i] = stop1->color[i] + offset*(stop2->color[i] - stop1->color[i]);
}
 
#define COLOR_STOP_EPSILON 1e-6
 
static cairo_status_t
_cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t *surface,
cairo_gradient_pattern_t *pattern)
{
cairo_ps_color_stop_t *allstops, *stops;
unsigned int i, n_stops;
 
allstops = _cairo_malloc_ab ((pattern->n_stops + 2), sizeof (cairo_ps_color_stop_t));
if (unlikely (allstops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
stops = &allstops[1];
n_stops = pattern->n_stops;
 
for (i = 0; i < n_stops; i++) {
cairo_gradient_stop_t *stop = &pattern->stops[i];
 
stops[i].color[0] = stop->color.red;
stops[i].color[1] = stop->color.green;
stops[i].color[2] = stop->color.blue;
stops[i].color[3] = stop->color.alpha;
stops[i].offset = pattern->stops[i].offset;
}
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT)
{
if (stops[0].offset > COLOR_STOP_EPSILON) {
if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
else
calc_gradient_color (&allstops[0], &stops[0], &stops[n_stops-1]);
stops = allstops;
n_stops++;
}
stops[0].offset = 0.0;
 
if (stops[n_stops-1].offset < 1.0 - COLOR_STOP_EPSILON) {
if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
memcpy (&stops[n_stops],
&stops[n_stops - 1],
sizeof (cairo_ps_color_stop_t));
} else {
calc_gradient_color (&stops[n_stops], &stops[0], &stops[n_stops-1]);
}
n_stops++;
}
stops[n_stops-1].offset = 1.0;
}
 
for (i = 0; i < n_stops; i++) {
double red, green, blue;
cairo_color_t color;
 
_cairo_color_init_rgba (&color,
stops[i].color[0],
stops[i].color[1],
stops[i].color[2],
stops[i].color[3]);
_cairo_ps_surface_flatten_transparency (surface, &color,
&red, &green, &blue);
stops[i].color[0] = red;
stops[i].color[1] = green;
stops[i].color[2] = blue;
}
 
_cairo_output_stream_printf (surface->stream,
"/CairoFunction\n");
if (stops[0].offset == stops[n_stops - 1].offset) {
/*
* The first and the last stops have the same offset, but we
* don't want a function with an empty domain, because that
* would provoke underdefined behaviour from rasterisers.
* This can only happen with EXTEND_PAD, because EXTEND_NONE
* is optimised into a clear pattern in cairo-gstate, and
* REFLECT/REPEAT are always transformed to have the first
* stop at t=0 and the last stop at t=1. Thus we want a step
* function going from the first color to the last one.
*
* This can be accomplished by stitching three functions:
* - a constant first color function,
* - a step from the first color to the last color (with empty domain)
* - a constant last color function
*/
cairo_ps_color_stop_t pad_stops[4];
 
assert (pattern->base.extend == CAIRO_EXTEND_PAD);
 
pad_stops[0] = pad_stops[1] = stops[0];
pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
 
pad_stops[0].offset = 0;
pad_stops[3].offset = 1;
 
_cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops);
} else if (n_stops == 2) {
/* no need for stitched function */
_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
} else {
/* multiple stops: stitch. XXX possible optimization: regulary spaced
* stops do not require stitching. XXX */
_cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops);
}
_cairo_output_stream_printf (surface->stream,
"def\n");
 
free (allstops);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t *surface,
cairo_gradient_pattern_t *pattern,
int begin,
int end)
{
_cairo_output_stream_printf (surface->stream,
"/CairoFunction\n"
"<< /FunctionType 3\n"
" /Domain [ %d %d ]\n"
" /Functions [ %d {CairoFunction} repeat ]\n"
" /Bounds [ %d 1 %d {} for ]\n",
begin,
end,
end - begin,
begin + 1,
end - 1);
 
if (pattern->base.extend == CAIRO_EXTEND_REFLECT) {
_cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { 2 mod 0 eq {0 1} {1 0} ifelse } for ]\n",
begin,
end - 1);
} else {
_cairo_output_stream_printf (surface->stream, " /Encode [ %d 1 %d { pop 0 1 } for ]\n",
begin,
end - 1);
}
 
_cairo_output_stream_printf (surface->stream, ">> def\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_emit_gradient (cairo_ps_surface_t *surface,
cairo_gradient_pattern_t *pattern,
cairo_bool_t is_ps_pattern)
{
cairo_matrix_t pat_to_ps;
cairo_circle_double_t start, end;
double domain[2];
cairo_status_t status;
 
assert (pattern->n_stops != 0);
 
status = _cairo_ps_surface_emit_pattern_stops (surface, pattern);
if (unlikely (status))
return status;
 
pat_to_ps = pattern->base.matrix;
status = cairo_matrix_invert (&pat_to_ps);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT)
{
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
double x_scale, y_scale, tolerance;
 
/* TODO: use tighter extents */
bounds_x1 = 0;
bounds_y1 = 0;
bounds_x2 = surface->width;
bounds_y2 = surface->height;
_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
&bounds_x1, &bounds_y1,
&bounds_x2, &bounds_y2,
NULL);
 
x_scale = surface->base.x_resolution / surface->base.x_fallback_resolution;
y_scale = surface->base.y_resolution / surface->base.y_fallback_resolution;
 
tolerance = fabs (_cairo_matrix_compute_determinant (&pattern->base.matrix));
tolerance /= _cairo_matrix_transformed_circle_major_axis (&pattern->base.matrix, 1);
tolerance *= MIN (x_scale, y_scale);
 
_cairo_gradient_pattern_box_to_parameter (pattern,
bounds_x1, bounds_y1,
bounds_x2, bounds_y2,
tolerance, domain);
} else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
/*
* If the first and the last stop offset are the same, then
* the color function is a step function.
* _cairo_ps_surface_emit_pattern_stops emits it as a stitched
* function no matter how many stops the pattern has. The
* domain of the stitched function will be [0 1] in this case.
*
* This is done to avoid emitting degenerate gradients for
* EXTEND_PAD patterns having a step color function.
*/
domain[0] = 0.0;
domain[1] = 1.0;
 
assert (pattern->base.extend == CAIRO_EXTEND_PAD);
} else {
domain[0] = pattern->stops[0].offset;
domain[1] = pattern->stops[pattern->n_stops - 1].offset;
}
 
/* PS requires the first and last stop to be the same as the
* extreme coordinates. For repeating patterns this moves the
* extreme coordinates out to the begin/end of the repeating
* function. For non repeating patterns this may move the extreme
* coordinates in if there are not stops at offset 0 and 1. */
_cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
_cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
 
if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
pattern->base.extend == CAIRO_EXTEND_REFLECT)
{
int repeat_begin, repeat_end;
 
repeat_begin = floor (domain[0]);
repeat_end = ceil (domain[1]);
 
status = _cairo_ps_surface_emit_repeating_function (surface,
pattern,
repeat_begin,
repeat_end);
if (unlikely (status))
return status;
} else if (pattern->n_stops <= 2) {
/* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
* Type 2 function is used by itself without a stitching
* function. Type 2 functions always have the domain [0 1] */
domain[0] = 0.0;
domain[1] = 1.0;
}
 
if (is_ps_pattern) {
_cairo_output_stream_printf (surface->stream,
"<< /PatternType 2\n"
" /Shading\n");
}
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
_cairo_output_stream_printf (surface->stream,
" << /ShadingType 2\n"
" /ColorSpace /DeviceRGB\n"
" /Coords [ %f %f %f %f ]\n",
start.center.x, start.center.y,
end.center.x, end.center.y);
} else {
_cairo_output_stream_printf (surface->stream,
" << /ShadingType 3\n"
" /ColorSpace /DeviceRGB\n"
" /Coords [ %f %f %f %f %f %f ]\n",
start.center.x, start.center.y,
MAX (start.radius, 0),
end.center.x, end.center.y,
MAX (end.radius, 0));
}
 
if (pattern->base.extend != CAIRO_EXTEND_NONE) {
_cairo_output_stream_printf (surface->stream,
" /Extend [ true true ]\n");
} else {
_cairo_output_stream_printf (surface->stream,
" /Extend [ false false ]\n");
}
 
if (domain[0] == 0.0 && domain[1] == 1.0) {
_cairo_output_stream_printf (surface->stream,
" /Function CairoFunction\n");
} else {
_cairo_output_stream_printf (surface->stream,
" /Function <<\n"
" /FunctionType 3\n"
" /Domain [ 0 1 ]\n"
" /Bounds [ ]\n"
" /Encode [ %f %f ]\n"
" /Functions [ CairoFunction ]\n"
" >>\n",
domain[0], domain[1]);
}
 
_cairo_output_stream_printf (surface->stream,
" >>\n");
 
if (is_ps_pattern) {
_cairo_output_stream_printf (surface->stream,
">>\n"
"[ %f %f %f %f %f %f ]\n"
"makepattern setpattern\n",
pat_to_ps.xx, pat_to_ps.yx,
pat_to_ps.xy, pat_to_ps.yy,
pat_to_ps.x0, pat_to_ps.y0);
} else {
_cairo_output_stream_printf (surface->stream,
"shfill\n");
}
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_mesh_pattern (cairo_ps_surface_t *surface,
cairo_mesh_pattern_t *pattern,
cairo_bool_t is_ps_pattern)
{
cairo_matrix_t pat_to_ps;
cairo_status_t status;
cairo_pdf_shading_t shading;
int i;
 
if (_cairo_array_num_elements (&pattern->patches) == 0)
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
pat_to_ps = pattern->base.matrix;
status = cairo_matrix_invert (&pat_to_ps);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
 
status = _cairo_pdf_shading_init_color (&shading, pattern);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (surface->stream,
"currentfile\n"
"/ASCII85Decode filter /FlateDecode filter /ReusableStreamDecode filter\n");
 
status = _cairo_ps_surface_emit_base85_string (surface,
shading.data,
shading.data_length,
CAIRO_PS_COMPRESS_DEFLATE,
FALSE);
if (status)
return status;
 
_cairo_output_stream_printf (surface->stream,
"\n"
"/CairoData exch def\n");
 
if (is_ps_pattern) {
_cairo_output_stream_printf (surface->stream,
"<< /PatternType 2\n"
" /Shading\n");
}
 
_cairo_output_stream_printf (surface->stream,
" << /ShadingType %d\n"
" /ColorSpace /DeviceRGB\n"
" /DataSource CairoData\n"
" /BitsPerCoordinate %d\n"
" /BitsPerComponent %d\n"
" /BitsPerFlag %d\n"
" /Decode [",
shading.shading_type,
shading.bits_per_coordinate,
shading.bits_per_component,
shading.bits_per_flag);
 
for (i = 0; i < shading.decode_array_length; i++)
_cairo_output_stream_printf (surface->stream, "%f ", shading.decode_array[i]);
 
_cairo_output_stream_printf (surface->stream,
"]\n"
" >>\n");
 
if (is_ps_pattern) {
_cairo_output_stream_printf (surface->stream,
">>\n"
"[ %f %f %f %f %f %f ]\n",
pat_to_ps.xx, pat_to_ps.yx,
pat_to_ps.xy, pat_to_ps.yy,
pat_to_ps.x0, pat_to_ps.y0);
_cairo_output_stream_printf (surface->stream,
"makepattern\n"
"setpattern\n");
} else {
_cairo_output_stream_printf (surface->stream, "shfill\n");
}
 
_cairo_output_stream_printf (surface->stream,
"currentdict /CairoData undef\n");
 
_cairo_pdf_shading_fini (&shading);
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents,
cairo_operator_t op)
{
cairo_status_t status;
 
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
 
if (surface->current_pattern_is_solid_color == FALSE ||
! _cairo_color_equal (&surface->current_color, &solid->color))
{
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
_cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
 
surface->current_pattern_is_solid_color = TRUE;
surface->current_color = solid->color;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
surface->current_pattern_is_solid_color = FALSE;
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
return status;
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
 
_cairo_ps_surface_emit_solid_pattern (surface, (cairo_solid_pattern_t *) pattern);
break;
 
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
status = _cairo_ps_surface_emit_surface_pattern (surface,
(cairo_pattern_t *)pattern,
extents,
op);
if (unlikely (status))
return status;
break;
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
status = _cairo_ps_surface_emit_gradient (surface,
(cairo_gradient_pattern_t *) pattern,
TRUE);
if (unlikely (status))
return status;
break;
 
case CAIRO_PATTERN_TYPE_MESH:
status = _cairo_ps_surface_emit_mesh_pattern (surface,
(cairo_mesh_pattern_t *) pattern,
TRUE);
if (unlikely (status))
return status;
break;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_ps_surface_paint_gradient (cairo_ps_surface_t *surface,
const cairo_pattern_t *source,
const cairo_rectangle_int_t *extents)
{
cairo_matrix_t pat_to_ps;
cairo_status_t status;
 
pat_to_ps = source->matrix;
status = cairo_matrix_invert (&pat_to_ps);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
 
if (! _cairo_matrix_is_identity (&pat_to_ps)) {
_cairo_output_stream_printf (surface->stream,
"[%f %f %f %f %f %f] concat\n",
pat_to_ps.xx, pat_to_ps.yx,
pat_to_ps.xy, pat_to_ps.yy,
pat_to_ps.x0, pat_to_ps.y0);
}
 
if (source->type == CAIRO_PATTERN_TYPE_MESH) {
status = _cairo_ps_surface_emit_mesh_pattern (surface,
(cairo_mesh_pattern_t *)source,
FALSE);
if (unlikely (status))
return status;
} else {
status = _cairo_ps_surface_emit_gradient (surface,
(cairo_gradient_pattern_t *)source,
FALSE);
if (unlikely (status))
return status;
}
 
return status;
}
 
static cairo_status_t
_cairo_ps_surface_paint_pattern (cairo_ps_surface_t *surface,
const cairo_pattern_t *source,
cairo_rectangle_int_t *extents,
cairo_operator_t op,
cairo_bool_t stencil_mask)
{
switch (source->type) {
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_ps_surface_paint_surface (surface,
(cairo_pattern_t *)source,
extents,
op,
stencil_mask);
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return _cairo_ps_surface_paint_gradient (surface,
source,
extents);
 
case CAIRO_PATTERN_TYPE_SOLID:
default:
ASSERT_NOT_REACHED;
return CAIRO_STATUS_SUCCESS;
}
}
 
static cairo_bool_t
_can_paint_pattern (const cairo_pattern_t *pattern)
{
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return FALSE;
 
case CAIRO_PATTERN_TYPE_SURFACE:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return (pattern->extend == CAIRO_EXTEND_NONE ||
pattern->extend == CAIRO_EXTEND_PAD);
 
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
return TRUE;
 
default:
ASSERT_NOT_REACHED;
return FALSE;
}
}
 
static cairo_bool_t
_cairo_ps_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_ps_surface_t *surface = abstract_surface;
 
rectangle->x = 0;
rectangle->y = 0;
 
/* XXX: The conversion to integers here is pretty bogus, (not to
* mention the aribitray limitation of width to a short(!). We
* may need to come up with a better interface for get_extents.
*/
rectangle->width = ceil (surface->width);
rectangle->height = ceil (surface->height);
 
return TRUE;
}
 
static void
_cairo_ps_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
_cairo_font_options_init_default (options);
 
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
}
 
static cairo_int_status_t
_cairo_ps_surface_set_clip (cairo_ps_surface_t *surface,
cairo_composite_rectangles_t *composite)
{
cairo_clip_t *clip = composite->clip;
 
if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
clip = NULL;
 
if (clip == NULL) {
if (_cairo_composite_rectangles_can_reduce_clip (composite,
surface->clipper.clip))
return CAIRO_STATUS_SUCCESS;
}
 
return _cairo_surface_clipper_set_clip (&surface->clipper, clip);
}
 
static cairo_int_status_t
_cairo_ps_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_composite_rectangles_t extents;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_paint (&extents,
&surface->base,
op, source, clip);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
goto cleanup_composite;
}
 
assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
 
#if DEBUG_PS
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_paint\n");
#endif
 
status = _cairo_ps_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup_composite;
 
if (_can_paint_pattern (source)) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (stream, "q\n");
status = _cairo_ps_surface_paint_pattern (surface,
source,
&extents.bounded, op, FALSE);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (stream, "Q\n");
} else {
status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n",
surface->width, surface->height);
}
 
cleanup_composite:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_output_stream_t *stream = surface->stream;
cairo_composite_rectangles_t extents;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_mask (&extents,
&surface->base,
op, source, mask, clip);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded);
goto cleanup_composite;
}
 
assert (_cairo_ps_surface_operation_supported (surface, op, source, mask, &extents.bounded));
 
#if DEBUG_PS
_cairo_output_stream_printf (stream,
"%% _cairo_ps_surface_mask\n");
#endif
 
status = _cairo_ps_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (stream, "q\n");
status = _cairo_ps_surface_paint_pattern (surface,
mask,
&extents.bounded, op, TRUE);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (stream, "Q\n");
 
cleanup_composite:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
status = _cairo_composite_rectangles_init_for_stroke (&extents,
&surface->base,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
 
/* use the more accurate extents */
{
cairo_rectangle_int_t r;
cairo_box_t b;
 
status = _cairo_path_fixed_stroke_extents (path, style,
ctm, ctm_inverse,
tolerance,
&r);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_box_from_rectangle (&b, &r);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
if (unlikely (status))
goto cleanup_composite;
}
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
goto cleanup_composite;
}
 
assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
"%% _cairo_ps_surface_stroke\n");
#endif
 
status = _cairo_ps_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_pdf_operators_stroke (&surface->pdf_operators,
path,
style,
ctm,
ctm_inverse);
 
cleanup_composite:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_int_status_t
_cairo_ps_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_int_status_t status;
 
status = _cairo_composite_rectangles_init_for_fill (&extents,
&surface->base,
op, source, path,
clip);
if (unlikely (status))
return status;
 
/* use the more accurate extents */
{
cairo_rectangle_int_t r;
cairo_box_t b;
 
_cairo_path_fixed_fill_extents (path,
fill_rule,
tolerance,
&r);
 
_cairo_box_from_rectangle (&b, &r);
status = _cairo_composite_rectangles_intersect_mask_extents (&extents, &b);
if (unlikely (status))
goto cleanup_composite;
}
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
goto cleanup_composite;
}
 
assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
"%% _cairo_ps_surface_fill\n");
#endif
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_ps_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup_composite;
 
if (_can_paint_pattern (source)) {
_cairo_output_stream_printf (surface->stream, "q\n");
 
status = _cairo_pdf_operators_clip (&surface->pdf_operators,
path,
fill_rule);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_ps_surface_paint_pattern (surface,
source,
&extents.bounded, op, FALSE);
if (unlikely (status))
goto cleanup_composite;
 
_cairo_output_stream_printf (surface->stream, "Q\n");
_cairo_pdf_operators_reset (&surface->pdf_operators);
} else {
status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_pdf_operators_fill (&surface->pdf_operators,
path,
fill_rule);
}
 
cleanup_composite:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static cairo_bool_t
_cairo_ps_surface_has_show_text_glyphs (void *abstract_surface)
{
return TRUE;
}
 
static cairo_int_status_t
_cairo_ps_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_composite_rectangles_t extents;
cairo_bool_t overlap;
cairo_status_t status;
 
status = _cairo_composite_rectangles_init_for_glyphs (&extents,
&surface->base,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
&overlap);
if (unlikely (status))
return status;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) {
status = _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded);
goto cleanup_composite;
}
 
assert (_cairo_ps_surface_operation_supported (surface, op, source, NULL, &extents.bounded));
 
#if DEBUG_PS
_cairo_output_stream_printf (surface->stream,
"%% _cairo_ps_surface_show_glyphs\n");
#endif
 
status = _cairo_ps_surface_set_clip (surface, &extents);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op);
if (unlikely (status))
goto cleanup_composite;
 
status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font);
 
cleanup_composite:
_cairo_composite_rectangles_fini (&extents);
return status;
}
 
static const char **
_cairo_ps_surface_get_supported_mime_types (void *abstract_surface)
{
return _cairo_ps_supported_mime_types;
}
 
static void
_cairo_ps_surface_set_paginated_mode (void *abstract_surface,
cairo_paginated_mode_t paginated_mode)
{
cairo_ps_surface_t *surface = abstract_surface;
cairo_status_t status;
 
surface->paginated_mode = paginated_mode;
 
if (surface->clipper.clip != NULL) {
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
 
_cairo_output_stream_printf (surface->stream, "Q q\n");
_cairo_surface_clipper_reset (&surface->clipper);
}
}
 
static cairo_int_status_t
_cairo_ps_surface_set_bounding_box (void *abstract_surface,
cairo_box_t *bbox)
{
cairo_ps_surface_t *surface = abstract_surface;
int i, num_comments;
char **comments;
int x1, y1, x2, y2;
cairo_bool_t has_page_media, has_page_bbox;
const char *page_media;
 
x1 = floor (_cairo_fixed_to_double (bbox->p1.x));
y1 = floor (surface->height - _cairo_fixed_to_double (bbox->p2.y));
x2 = ceil (_cairo_fixed_to_double (bbox->p2.x));
y2 = ceil (surface->height - _cairo_fixed_to_double (bbox->p1.y));
 
surface->page_bbox.x = x1;
surface->page_bbox.y = y1;
surface->page_bbox.width = x2 - x1;
surface->page_bbox.height = y2 - y1;
 
_cairo_output_stream_printf (surface->stream,
"%%%%Page: %d %d\n",
surface->num_pages,
surface->num_pages);
 
_cairo_output_stream_printf (surface->stream,
"%%%%BeginPageSetup\n");
 
has_page_media = FALSE;
has_page_bbox = FALSE;
num_comments = _cairo_array_num_elements (&surface->dsc_page_setup_comments);
comments = _cairo_array_index (&surface->dsc_page_setup_comments, 0);
for (i = 0; i < num_comments; i++) {
_cairo_output_stream_printf (surface->stream,
"%s\n", comments[i]);
if (strncmp (comments[i], "%%PageMedia:", 11) == 0)
has_page_media = TRUE;
 
if (strncmp (comments[i], "%%PageBoundingBox:", 18) == 0)
has_page_bbox = TRUE;
 
free (comments[i]);
comments[i] = NULL;
}
_cairo_array_truncate (&surface->dsc_page_setup_comments, 0);
 
if (!has_page_media && !surface->eps) {
page_media = _cairo_ps_surface_get_page_media (surface);
if (unlikely (page_media == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_output_stream_printf (surface->stream,
"%%%%PageMedia: %s\n",
page_media);
}
 
if (!has_page_bbox) {
_cairo_output_stream_printf (surface->stream,
"%%%%PageBoundingBox: %d %d %d %d\n",
x1, y1, x2, y2);
}
 
_cairo_output_stream_printf (surface->stream,
"%%%%EndPageSetup\n"
"q %d %d %d %d rectclip q\n",
surface->page_bbox.x,
surface->page_bbox.y,
surface->page_bbox.width,
surface->page_bbox.height);
 
if (surface->num_pages == 1) {
surface->bbox_x1 = x1;
surface->bbox_y1 = y1;
surface->bbox_x2 = x2;
surface->bbox_y2 = y2;
} else {
if (x1 < surface->bbox_x1)
surface->bbox_x1 = x1;
if (y1 < surface->bbox_y1)
surface->bbox_y1 = y1;
if (x2 > surface->bbox_x2)
surface->bbox_x2 = x2;
if (y2 > surface->bbox_y2)
surface->bbox_y2 = y2;
}
surface->current_pattern_is_solid_color = FALSE;
_cairo_pdf_operators_reset (&surface->pdf_operators);
 
return _cairo_output_stream_get_status (surface->stream);
}
 
static cairo_bool_t
_cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface)
{
return TRUE;
}
 
static const cairo_surface_backend_t cairo_ps_surface_backend = {
CAIRO_SURFACE_TYPE_PS,
_cairo_ps_surface_finish,
 
_cairo_default_context_create,
 
NULL, /* create similar: handled by wrapper */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* snapshot */
 
NULL, /* cairo_ps_surface_copy_page */
_cairo_ps_surface_show_page,
 
_cairo_ps_surface_get_extents,
_cairo_ps_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
/* Here are the drawing functions */
 
_cairo_ps_surface_paint, /* paint */
_cairo_ps_surface_mask,
_cairo_ps_surface_stroke,
_cairo_ps_surface_fill,
NULL, /* fill-stroke */
NULL, /* show_glyphs */
_cairo_ps_surface_has_show_text_glyphs,
_cairo_ps_surface_show_text_glyphs,
_cairo_ps_surface_get_supported_mime_types,
};
 
static const cairo_paginated_surface_backend_t cairo_ps_surface_paginated_backend = {
_cairo_ps_surface_start_page,
_cairo_ps_surface_set_paginated_mode,
_cairo_ps_surface_set_bounding_box,
NULL, /* _cairo_ps_surface_has_fallback_images, */
_cairo_ps_surface_supports_fine_grained_fallbacks,
};
/programs/develop/libraries/cairo/src/cairo-ps.h
49,13 → 49,15
 
/**
* cairo_ps_level_t:
* @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification.
* @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification.
* @CAIRO_PS_LEVEL_2: The language level 2 of the PostScript specification. (Since 1.6)
* @CAIRO_PS_LEVEL_3: The language level 3 of the PostScript specification. (Since 1.6)
*
* #cairo_ps_level_t is used to describe the language level of the
* PostScript Language Reference that a generated PostScript file will
* conform to.
*/
*
* Since: 1.6
**/
typedef enum _cairo_ps_level {
CAIRO_PS_LEVEL_2,
CAIRO_PS_LEVEL_3
/programs/develop/libraries/cairo/src/cairo-qt-surface.cpp
0,0 → 1,1713
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Mozilla Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@mozilla.com>
*/
 
/* Get INT16_MIN etc. as per C99 */
#define __STDC_LIMIT_MACROS
 
#include "cairoint.h"
 
#include "cairo-clip-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-region-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-types-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-fallback-private.h"
 
#include "cairo-ft.h"
#include "cairo-qt.h"
 
#include <memory>
 
#include <QtGui/QPainter>
#include <QtGui/QPaintEngine>
#include <QtGui/QPaintDevice>
#include <QtGui/QImage>
#include <QtGui/QPixmap>
#include <QtGui/QBrush>
#include <QtGui/QPen>
#include <QWidget>
#include <QtCore/QVarLengthArray>
 
#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
extern void qt_draw_glyphs(QPainter *, const quint32 *glyphs, const QPointF *positions, int count);
#endif
 
#include <sys/time.h>
 
/* Enable workaround slow regional Qt paths */
#define ENABLE_FAST_FILL 0
#define ENABLE_FAST_CLIP 0
 
#if 0
#define D(x) x
static const char *
_opstr (cairo_operator_t op)
{
const char *ops[] = {
"CLEAR",
"SOURCE",
"OVER",
"IN",
"OUT",
"ATOP",
"DEST",
"DEST_OVER",
"DEST_IN",
"DEST_OUT",
"DEST_ATOP",
"XOR",
"ADD",
"SATURATE"
};
 
if (op < CAIRO_OPERATOR_CLEAR || op > CAIRO_OPERATOR_SATURATE)
return "(\?\?\?)";
 
return ops[op];
}
#else
#define D(x) do { } while(0)
#endif
 
#ifndef CAIRO_INT_STATUS_SUCCESS
#define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS)
#endif
 
/* Qt::PenStyle optimization based on the assumption that dots are 1*w and dashes are 3*w. */
#define DOT_LENGTH 1.0
#define DASH_LENGTH 3.0
 
struct cairo_qt_surface_t {
cairo_surface_t base;
 
cairo_bool_t supports_porter_duff;
 
QPainter *p;
 
/* The pixmap/image constructors will store their objects here */
QPixmap *pixmap;
QImage *image;
 
QRect window;
 
cairo_surface_clipper_t clipper;
 
cairo_surface_t *image_equiv;
};
 
/* Will be true if we ever try to create a QPixmap and end
* up with one without an alpha channel.
*/
static cairo_bool_t _qpixmaps_have_no_alpha = FALSE;
 
/*
* Helper methods
*/
 
static QPainter::CompositionMode
_qpainter_compositionmode_from_cairo_op (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return QPainter::CompositionMode_Clear;
 
case CAIRO_OPERATOR_SOURCE:
return QPainter::CompositionMode_Source;
case CAIRO_OPERATOR_OVER:
return QPainter::CompositionMode_SourceOver;
case CAIRO_OPERATOR_IN:
return QPainter::CompositionMode_SourceIn;
case CAIRO_OPERATOR_OUT:
return QPainter::CompositionMode_SourceOut;
case CAIRO_OPERATOR_ATOP:
return QPainter::CompositionMode_SourceAtop;
 
case CAIRO_OPERATOR_DEST:
return QPainter::CompositionMode_Destination;
case CAIRO_OPERATOR_DEST_OVER:
return QPainter::CompositionMode_DestinationOver;
case CAIRO_OPERATOR_DEST_IN:
return QPainter::CompositionMode_DestinationIn;
case CAIRO_OPERATOR_DEST_OUT:
return QPainter::CompositionMode_DestinationOut;
case CAIRO_OPERATOR_DEST_ATOP:
return QPainter::CompositionMode_DestinationAtop;
 
case CAIRO_OPERATOR_XOR:
return QPainter::CompositionMode_Xor;
 
default:
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
ASSERT_NOT_REACHED;
}
}
 
static bool
_op_is_supported (cairo_qt_surface_t *qs, cairo_operator_t op)
{
if (qs->p == NULL)
return false;
 
if (qs->supports_porter_duff) {
switch (op) {
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
 
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
 
case CAIRO_OPERATOR_XOR:
return TRUE;
 
default:
ASSERT_NOT_REACHED;
case CAIRO_OPERATOR_ADD:
case CAIRO_OPERATOR_SATURATE:
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return FALSE;
 
}
} else {
return op == CAIRO_OPERATOR_OVER;
}
}
 
static cairo_format_t
_cairo_format_from_qimage_format (QImage::Format fmt)
{
switch (fmt) {
case QImage::Format_ARGB32_Premultiplied:
return CAIRO_FORMAT_ARGB32;
case QImage::Format_RGB32:
return CAIRO_FORMAT_RGB24;
case QImage::Format_Indexed8: // XXX not quite
return CAIRO_FORMAT_A8;
#ifdef WORDS_BIGENDIAN
case QImage::Format_Mono:
#else
case QImage::Format_MonoLSB:
#endif
return CAIRO_FORMAT_A1;
 
case QImage::Format_Invalid:
#ifdef WORDS_BIGENDIAN
case QImage::Format_MonoLSB:
#else
case QImage::Format_Mono:
#endif
case QImage::Format_ARGB32:
case QImage::Format_RGB16:
case QImage::Format_ARGB8565_Premultiplied:
case QImage::Format_RGB666:
case QImage::Format_ARGB6666_Premultiplied:
case QImage::Format_RGB555:
case QImage::Format_ARGB8555_Premultiplied:
case QImage::Format_RGB888:
case QImage::Format_RGB444:
case QImage::Format_ARGB4444_Premultiplied:
case QImage::NImageFormats:
default:
ASSERT_NOT_REACHED;
return (cairo_format_t) -1;
}
}
 
static QImage::Format
_qimage_format_from_cairo_format (cairo_format_t fmt)
{
switch (fmt) {
case CAIRO_FORMAT_INVALID:
ASSERT_NOT_REACHED;
case CAIRO_FORMAT_ARGB32:
return QImage::Format_ARGB32_Premultiplied;
case CAIRO_FORMAT_RGB24:
return QImage::Format_RGB32;
case CAIRO_FORMAT_RGB16_565:
return QImage::Format_RGB16;
case CAIRO_FORMAT_A8:
return QImage::Format_Indexed8; // XXX not quite
case CAIRO_FORMAT_A1:
#ifdef WORDS_BIGENDIAN
return QImage::Format_Mono; // XXX think we need to choose between this and LSB
#else
return QImage::Format_MonoLSB;
#endif
}
 
return QImage::Format_Mono;
}
 
static inline QMatrix
_qmatrix_from_cairo_matrix (const cairo_matrix_t& m)
{
return QMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0);
}
 
/* Path conversion */
typedef struct _qpainter_path_transform {
QPainterPath path;
const cairo_matrix_t *ctm_inverse;
} qpainter_path_data;
 
/* cairo path -> execute in context */
static cairo_status_t
_cairo_path_to_qpainterpath_move_to (void *closure, const cairo_point_t *point)
{
qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (pdata->ctm_inverse)
cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
 
pdata->path.moveTo(x, y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_qpainterpath_line_to (void *closure, const cairo_point_t *point)
{
qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (pdata->ctm_inverse)
cairo_matrix_transform_point (pdata->ctm_inverse, &x, &y);
 
pdata->path.lineTo(x, y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_qpainterpath_curve_to (void *closure, const cairo_point_t *p0, const cairo_point_t *p1, const cairo_point_t *p2)
{
qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
double x0 = _cairo_fixed_to_double (p0->x);
double y0 = _cairo_fixed_to_double (p0->y);
double x1 = _cairo_fixed_to_double (p1->x);
double y1 = _cairo_fixed_to_double (p1->y);
double x2 = _cairo_fixed_to_double (p2->x);
double y2 = _cairo_fixed_to_double (p2->y);
 
if (pdata->ctm_inverse) {
cairo_matrix_transform_point (pdata->ctm_inverse, &x0, &y0);
cairo_matrix_transform_point (pdata->ctm_inverse, &x1, &y1);
cairo_matrix_transform_point (pdata->ctm_inverse, &x2, &y2);
}
 
pdata->path.cubicTo (x0, y0, x1, y1, x2, y2);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_qpainterpath_close_path (void *closure)
{
qpainter_path_data *pdata = static_cast <qpainter_path_data *> (closure);
 
pdata->path.closeSubpath();
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline QPainterPath
path_to_qt (const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm_inverse = NULL)
{
qpainter_path_data data;
cairo_status_t status;
 
if (ctm_inverse && _cairo_matrix_is_identity (ctm_inverse))
ctm_inverse = NULL;
data.ctm_inverse = ctm_inverse;
 
status = _cairo_path_fixed_interpret (path,
_cairo_path_to_qpainterpath_move_to,
_cairo_path_to_qpainterpath_line_to,
_cairo_path_to_qpainterpath_curve_to,
_cairo_path_to_qpainterpath_close_path,
&data);
assert (status == CAIRO_STATUS_SUCCESS);
 
return data.path;
}
 
static inline QPainterPath
path_to_qt (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_matrix_t *ctm_inverse = NULL)
{
QPainterPath qpath = path_to_qt (path, ctm_inverse);
 
qpath.setFillRule (fill_rule == CAIRO_FILL_RULE_WINDING ?
Qt::WindingFill :
Qt::OddEvenFill);
 
return qpath;
}
 
/*
* Surface backend methods
*/
static cairo_surface_t *
_cairo_qt_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
bool use_pixmap;
 
D(fprintf(stderr, "q[%p] create_similar: %d %d [%d] -> ", abstract_surface, width, height, content));
 
use_pixmap = qs->image == NULL;
if (use_pixmap) {
switch (content) {
case CAIRO_CONTENT_ALPHA:
use_pixmap = FALSE;
break;
case CAIRO_CONTENT_COLOR:
break;
case CAIRO_CONTENT_COLOR_ALPHA:
use_pixmap = ! _qpixmaps_have_no_alpha;
break;
}
}
 
if (use_pixmap) {
cairo_surface_t *result =
cairo_qt_surface_create_with_qpixmap (content, width, height);
 
/* XXX result->content is always content. ??? */
if (result->content == content) {
D(fprintf(stderr, "qpixmap content: %d\n", content));
return result;
}
 
_qpixmaps_have_no_alpha = TRUE;
cairo_surface_destroy (result);
}
 
D(fprintf (stderr, "qimage\n"));
return cairo_qt_surface_create_with_qimage
(_cairo_format_from_content (content), width, height);
}
 
static cairo_status_t
_cairo_qt_surface_finish (void *abstract_surface)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] finish\n", abstract_surface));
 
/* Only delete p if we created it */
if (qs->image || qs->pixmap)
delete qs->p;
else
qs->p->restore ();
 
if (qs->image_equiv)
cairo_surface_destroy (qs->image_equiv);
 
_cairo_surface_clipper_reset (&qs->clipper);
 
if (qs->image)
delete qs->image;
 
if (qs->pixmap)
delete qs->pixmap;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_qimg_destroy (void *closure)
{
QImage *qimg = (QImage *) closure;
delete qimg;
}
 
static cairo_status_t
_cairo_qt_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] acquire_source_image\n", abstract_surface));
 
*image_extra = NULL;
 
if (qs->image_equiv) {
*image_out = (cairo_image_surface_t*)
cairo_surface_reference (qs->image_equiv);
 
return CAIRO_STATUS_SUCCESS;
}
 
if (qs->pixmap) {
QImage *qimg = new QImage(qs->pixmap->toImage());
cairo_surface_t *image;
cairo_status_t status;
 
image = cairo_image_surface_create_for_data (qimg->bits(),
_cairo_format_from_qimage_format (qimg->format()),
qimg->width(), qimg->height(),
qimg->bytesPerLine());
 
status = _cairo_user_data_array_set_data (&image->user_data,
(const cairo_user_data_key_t *)&_qimg_destroy,
qimg,
_qimg_destroy);
if (status) {
cairo_surface_destroy (image);
return status;
}
 
*image_out = (cairo_image_surface_t *) image;
return CAIRO_STATUS_SUCCESS;
}
 
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
static void
_cairo_qt_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
//cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] release_source_image\n", abstract_surface));
 
cairo_surface_destroy (&image->base);
}
 
struct _qimage_surface {
cairo_image_surface_t image;
QImage *qimg;
};
 
static cairo_surface_t *
map_qimage_to_image (QImage *qimg, const cairo_rectangle_int_t *extents)
{
struct _qimage_surface *surface;
pixman_image_t *pixman_image;
pixman_format_code_t pixman_format;
uint8_t *data;
 
if (qimg == NULL)
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
switch (qimg->format()) {
case QImage::Format_ARGB32_Premultiplied:
pixman_format = PIXMAN_a8r8g8b8;
break;
case QImage::Format_RGB32:
pixman_format = PIXMAN_x8r8g8b8;
break;
case QImage::Format_Indexed8: // XXX not quite
pixman_format = PIXMAN_a8;
break;
#ifdef WORDS_BIGENDIAN
case QImage::Format_Mono:
#else
case QImage::Format_MonoLSB:
#endif
pixman_format = PIXMAN_a1;
break;
 
case QImage::Format_Invalid:
#ifdef WORDS_BIGENDIAN
case QImage::Format_MonoLSB:
#else
case QImage::Format_Mono:
#endif
case QImage::Format_ARGB32:
case QImage::Format_RGB16:
case QImage::Format_ARGB8565_Premultiplied:
case QImage::Format_RGB666:
case QImage::Format_ARGB6666_Premultiplied:
case QImage::Format_RGB555:
case QImage::Format_ARGB8555_Premultiplied:
case QImage::Format_RGB888:
case QImage::Format_RGB444:
case QImage::Format_ARGB4444_Premultiplied:
case QImage::NImageFormats:
default:
delete qimg;
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
}
 
data = qimg->bits();
data += extents->y * qimg->bytesPerLine();
data += extents->x * PIXMAN_FORMAT_BPP (pixman_format) / 8;
 
pixman_image = pixman_image_create_bits (pixman_format,
extents->width,
extents->height,
(uint32_t *)data,
qimg->bytesPerLine());
if (pixman_image == NULL) {
delete qimg;
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
surface = (struct _qimage_surface *) malloc (sizeof(*surface));
if (unlikely (surface == NULL)) {
pixman_image_unref (pixman_image);
delete qimg;
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_image_surface_init (&surface->image, pixman_image, pixman_format);
surface->qimg = qimg;
 
cairo_surface_set_device_offset (&surface->image.base,
-extents->x, -extents->y);
 
return &surface->image.base;
}
 
static cairo_image_surface_t *
_cairo_qt_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
QImage *qimg = NULL;
 
D(fprintf(stderr, "q[%p] acquire_dest_image\n", abstract_surface));
 
if (qs->image_equiv)
return _cairo_image_surface_map_to_image (qs->image_equiv,
extents);
 
QPoint offset;
 
if (qs->pixmap) {
qimg = new QImage(qs->pixmap->toImage());
} else {
// Try to figure out what kind of QPaintDevice we have, and
// how we can grab an image from it
QPaintDevice *pd = qs->p->device();
if (!pd)
return (cairo_image_surface_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
QPaintDevice *rpd = QPainter::redirected(pd, &offset);
if (rpd)
pd = rpd;
 
if (pd->devType() == QInternal::Image) {
qimg = new QImage(((QImage*) pd)->copy());
} else if (pd->devType() == QInternal::Pixmap) {
qimg = new QImage(((QPixmap*) pd)->toImage());
} else if (pd->devType() == QInternal::Widget) {
qimg = new QImage(QPixmap::grabWindow(((QWidget*)pd)->winId()).toImage());
}
}
 
return (cairo_image_surface_t *) map_qimage_to_image (qimg, extents);
}
 
static cairo_int_status_t
_cairo_qt_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] release_dest_image\n", abstract_surface));
 
if (!qs->image_equiv) {
struct _qimage_surface *qimage = (struct _qimage_surface *)image;
 
// XXX should I be using setBackgroundMode here instead of setCompositionMode?
if (qs->supports_porter_duff)
qs->p->setCompositionMode (QPainter::CompositionMode_Source);
 
qs->p->drawImage ((int)qimage->image.base.device_transform.x0,
(int)qimage->image.base.device_transform.y0,
*qimage->qimg,
(int)qimage->image.base.device_transform.x0,
(int)qimage->image.base.device_transform.y0,
(int)qimage->image.width,
(int)qimage->image.height);
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
 
delete qimage->qimg;
}
 
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_bool_t
_cairo_qt_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
extents->x = qs->window.x();
extents->y = qs->window.y();
extents->width = qs->window.width();
extents->height = qs->window.height();
 
return TRUE;
}
 
static cairo_status_t
_cairo_qt_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_qt_surface_t *qs = cairo_container_of (clipper,
cairo_qt_surface_t,
clipper);
 
if (path == NULL) {
if (qs->pixmap || qs->image) {
// we own p
qs->p->setClipping (false);
} else {
qs->p->restore ();
qs->p->save ();
}
} else {
// XXX Antialiasing is ignored
qs->p->setClipPath (path_to_qt (path, fill_rule), Qt::IntersectClip);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_qt_surface_set_clip_region (cairo_qt_surface_t *qs,
const cairo_region_t *clip_region)
{
_cairo_surface_clipper_reset (&qs->clipper);
 
if (clip_region == NULL) {
// How the clip path is reset depends on whether we own p or not
if (qs->pixmap || qs->image) {
// we own p
qs->p->setClipping (false);
} else {
qs->p->restore ();
qs->p->save ();
}
} else {
QRegion qr;
int num_rects = cairo_region_num_rectangles (clip_region);
for (int i = 0; i < num_rects; ++i) {
cairo_rectangle_int_t rect;
 
cairo_region_get_rectangle (clip_region, i, &rect);
 
QRect r(rect.x, rect.y, rect.width, rect.height);
qr = qr.unite(r);
}
 
qs->p->setClipRegion (qr, Qt::IntersectClip);
}
}
 
static cairo_int_status_t
_cairo_qt_surface_set_clip (cairo_qt_surface_t *qs,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
 
D(fprintf(stderr, "q[%p] intersect_clip_path %s\n", abstract_surface, path ? "(path)" : "(clear)"));
 
if (clip == NULL) {
_cairo_surface_clipper_reset (&qs->clipper);
// How the clip path is reset depends on whether we own p or not
if (qs->pixmap || qs->image) {
// we own p
qs->p->setClipping (false);
} else {
qs->p->restore ();
qs->p->save ();
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
#if ENABLE_FAST_CLIP
// Qt will implicitly enable clipping, and will use ReplaceClip
// instead of IntersectClip if clipping was disabled before
 
// Note: Qt is really bad at dealing with clip paths. It doesn't
// seem to usefully recognize rectangular paths, instead going down
// extremely slow paths whenever a clip path is set. So,
// we do a bunch of work here to try to get rectangles or regions
// down to Qt for clipping.
 
cairo_region_t *clip_region = NULL;
 
status = _cairo_clip_get_region (clip, &clip_region);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
// We weren't able to extract a region from the traps.
// Just hand the path down to QPainter.
status = (cairo_int_status_t)
_cairo_surface_clipper_set_clip (&qs->clipper, clip);
} else if (status == CAIRO_INT_STATUS_SUCCESS) {
_cairo_qt_surface_set_clip_region (qs, clip_region);
status = CAIRO_INT_STATUS_SUCCESS;
}
#else
status = (cairo_int_status_t)
_cairo_surface_clipper_set_clip (&qs->clipper, clip);
#endif
 
return status;
}
 
/*
* Brush conversion
*/
 
struct PatternToBrushConverter {
PatternToBrushConverter (const cairo_pattern_t *pattern) :
mAcquiredImageParent(0),
mAcquiredImage(0),
mAcquiredImageExtra(0)
{
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
QColor color;
color.setRgbF(solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
 
mBrush = QBrush(color);
} else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) pattern;
cairo_surface_t *surface = spattern->surface;
 
if (surface->type == CAIRO_SURFACE_TYPE_QT) {
cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
 
if (qs->image) {
mBrush = QBrush(*qs->image);
} else if (qs->pixmap) {
mBrush = QBrush(*qs->pixmap);
} else {
// do something smart
mBrush = QBrush(0xff0000ff);
}
} else {
cairo_image_surface_t *isurf = NULL;
 
if (surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
isurf = (cairo_image_surface_t*) surface;
} else {
void *image_extra;
 
if (_cairo_surface_acquire_source_image (surface, &isurf, &image_extra) == CAIRO_STATUS_SUCCESS) {
mAcquiredImageParent = surface;
mAcquiredImage = isurf;
mAcquiredImageExtra = image_extra;
} else {
isurf = NULL;
}
}
 
if (isurf) {
mBrush = QBrush (QImage ((const uchar *) isurf->data,
isurf->width,
isurf->height,
isurf->stride,
_qimage_format_from_cairo_format (isurf->format)));
} else {
mBrush = QBrush(0x0000ffff);
}
}
} else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
{
QGradient *grad;
cairo_bool_t reverse_stops = FALSE;
cairo_bool_t emulate_reflect = FALSE;
double offset = 0.0;
 
cairo_extend_t extend = pattern->extend;
 
cairo_gradient_pattern_t *gpat = (cairo_gradient_pattern_t *) pattern;
 
if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *) pattern;
grad = new QLinearGradient (lpat->pd1.x, lpat->pd1.y,
lpat->pd2.x, lpat->pd2.y);
} else if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *) pattern;
 
/* Based on the SVG surface code */
 
cairo_circle_double_t *c0, *c1;
double x0, y0, r0, x1, y1, r1;
 
if (rpat->cd1.radius < rpat->cd2.radius) {
c0 = &rpat->cd1;
c1 = &rpat->cd2;
reverse_stops = FALSE;
} else {
c0 = &rpat->cd2;
c1 = &rpat->cd1;
reverse_stops = TRUE;
}
 
x0 = c0->center.x;
y0 = c0->center.y;
r0 = c0->radius;
x1 = c1->center.x;
y1 = c1->center.y;
r1 = c1->radius;
 
if (r0 == r1) {
grad = new QRadialGradient (x1, y1, r1, x1, y1);
} else {
double fx = (r1 * x0 - r0 * x1) / (r1 - r0);
double fy = (r1 * y0 - r0 * y1) / (r1 - r0);
 
/* QPainter doesn't support the inner circle and use instead a gradient focal.
* That means we need to emulate the cairo behaviour by processing the
* cairo gradient stops.
* The CAIRO_EXTENT_NONE and CAIRO_EXTENT_PAD modes are quite easy to handle,
* it's just a matter of stop position translation and calculation of
* the corresponding SVG radial gradient focal.
* The CAIRO_EXTENT_REFLECT and CAIRO_EXTEND_REPEAT modes require to compute a new
* radial gradient, with an new outer circle, equal to r1 - r0 in the CAIRO_EXTEND_REPEAT
* case, and 2 * (r1 - r0) in the CAIRO_EXTENT_REFLECT case, and a new gradient stop
* list that maps to the original cairo stop list.
*/
if ((extend == CAIRO_EXTEND_REFLECT || extend == CAIRO_EXTEND_REPEAT) && r0 > 0.0) {
double r_org = r1;
double r, x, y;
 
if (extend == CAIRO_EXTEND_REFLECT) {
r1 = 2 * r1 - r0;
emulate_reflect = TRUE;
}
 
offset = fmod (r1, r1 - r0) / (r1 - r0) - 1.0;
r = r1 - r0;
 
/* New position of outer circle. */
x = r * (x1 - fx) / r_org + fx;
y = r * (y1 - fy) / r_org + fy;
 
x1 = x;
y1 = y;
r1 = r;
r0 = 0.0;
} else {
offset = r0 / r1;
}
 
grad = new QRadialGradient (x1, y1, r1, fx, fy);
 
if (extend == CAIRO_EXTEND_NONE && r0 != 0.0)
grad->setColorAt (r0 / r1, Qt::transparent);
}
}
 
switch (extend) {
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_PAD:
grad->setSpread(QGradient::PadSpread);
 
grad->setColorAt (0.0, Qt::transparent);
grad->setColorAt (1.0, Qt::transparent);
break;
 
case CAIRO_EXTEND_REFLECT:
grad->setSpread(QGradient::ReflectSpread);
break;
 
case CAIRO_EXTEND_REPEAT:
grad->setSpread(QGradient::RepeatSpread);
break;
}
 
for (unsigned int i = 0; i < gpat->n_stops; i++) {
int index = i;
if (reverse_stops)
index = gpat->n_stops - i - 1;
 
double offset = gpat->stops[i].offset;
QColor color;
color.setRgbF (gpat->stops[i].color.red,
gpat->stops[i].color.green,
gpat->stops[i].color.blue,
gpat->stops[i].color.alpha);
 
if (emulate_reflect) {
offset = offset / 2.0;
grad->setColorAt (1.0 - offset, color);
}
 
grad->setColorAt (offset, color);
}
 
mBrush = QBrush(*grad);
 
delete grad;
}
 
if (mBrush.style() != Qt::NoBrush &&
pattern->type != CAIRO_PATTERN_TYPE_SOLID &&
! _cairo_matrix_is_identity (&pattern->matrix))
{
cairo_matrix_t pm = pattern->matrix;
cairo_status_t status = cairo_matrix_invert (&pm);
assert (status == CAIRO_STATUS_SUCCESS);
mBrush.setMatrix (_qmatrix_from_cairo_matrix (pm));
}
}
 
~PatternToBrushConverter () {
if (mAcquiredImageParent)
_cairo_surface_release_source_image (mAcquiredImageParent, mAcquiredImage, mAcquiredImageExtra);
}
 
operator QBrush& () {
return mBrush;
}
 
QBrush mBrush;
 
private:
cairo_surface_t *mAcquiredImageParent;
cairo_image_surface_t *mAcquiredImage;
void *mAcquiredImageExtra;
};
 
struct PatternToPenConverter {
PatternToPenConverter (const cairo_pattern_t *source,
const cairo_stroke_style_t *style) :
mBrushConverter(source)
{
Qt::PenJoinStyle join = Qt::MiterJoin;
Qt::PenCapStyle cap = Qt::SquareCap;
 
switch (style->line_cap) {
case CAIRO_LINE_CAP_BUTT:
cap = Qt::FlatCap;
break;
case CAIRO_LINE_CAP_ROUND:
cap = Qt::RoundCap;
break;
case CAIRO_LINE_CAP_SQUARE:
cap = Qt::SquareCap;
break;
}
 
switch (style->line_join) {
case CAIRO_LINE_JOIN_MITER:
join = Qt::MiterJoin;
break;
case CAIRO_LINE_JOIN_ROUND:
join = Qt::RoundJoin;
break;
case CAIRO_LINE_JOIN_BEVEL:
join = Qt::BevelJoin;
break;
}
 
mPen = QPen(mBrushConverter, style->line_width, Qt::SolidLine, cap, join);
mPen.setMiterLimit (style->miter_limit);
 
if (style->dash && style->num_dashes) {
Qt::PenStyle pstyle = Qt::NoPen;
 
if (style->num_dashes == 2) {
if ((style->dash[0] == style->line_width &&
style->dash[1] == style->line_width && style->line_width <= 2.0) ||
(style->dash[0] == 0.0 &&
style->dash[1] == style->line_width * 2 && cap == Qt::RoundCap))
{
pstyle = Qt::DotLine;
} else if (style->dash[0] == style->line_width * DASH_LENGTH &&
style->dash[1] == style->line_width * DASH_LENGTH &&
cap == Qt::FlatCap)
{
pstyle = Qt::DashLine;
}
}
 
if (pstyle != Qt::NoPen) {
mPen.setStyle(pstyle);
return;
}
 
unsigned int odd_dash = style->num_dashes % 2;
 
QVector<qreal> dashes (odd_dash ? style->num_dashes * 2 : style->num_dashes);
for (unsigned int i = 0; i < odd_dash+1; i++) {
for (unsigned int j = 0; j < style->num_dashes; j++) {
// In Qt, the dash lengths are given in units of line width, whereas
// in cairo, they are in user-space units. We'll always apply the CTM,
// so all we have to do here is divide cairo's dash lengths by the line
// width.
dashes.append (style->dash[j] / style->line_width);
}
}
 
mPen.setDashPattern(dashes);
mPen.setDashOffset(style->dash_offset / style->line_width);
}
}
 
~PatternToPenConverter() { }
 
operator QPen& () {
return mPen;
}
 
QPen mPen;
PatternToBrushConverter mBrushConverter;
};
 
/*
* Core drawing operations
*/
 
static bool
_cairo_qt_fast_fill (cairo_qt_surface_t *qs,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path = NULL,
cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING,
double tolerance = 0.0,
cairo_antialias_t antialias = CAIRO_ANTIALIAS_NONE)
{
#if ENABLE_FAST_FILL
QImage *qsSrc_image = NULL;
QPixmap *qsSrc_pixmap = NULL;
std::auto_ptr<QImage> qsSrc_image_d;
 
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *spattern = (cairo_surface_pattern_t*) source;
if (spattern->surface->type == CAIRO_SURFACE_TYPE_QT) {
cairo_qt_surface_t *p = (cairo_qt_surface_t*) spattern->surface;
 
qsSrc_image = p->image;
qsSrc_pixmap = p->pixmap;
} else if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) {
cairo_image_surface_t *p = (cairo_image_surface_t*) spattern->surface;
qsSrc_image = new QImage((const uchar*) p->data,
p->width,
p->height,
p->stride,
_qimage_format_from_cairo_format(p->format));
qsSrc_image_d.reset(qsSrc_image);
}
}
 
if (!qsSrc_image && !qsSrc_pixmap)
return false;
 
// We can only drawTiledPixmap; there's no drawTiledImage
if (! qsSrc_pixmap &&
(source->extend == CAIRO_EXTEND_REPEAT ||
source->extend == CAIRO_EXTEND_REFLECT))
{
return false;
}
 
QMatrix sourceMatrix = _qmatrix_from_cairo_matrix (source->matrix);
 
// We can draw this faster by clipping and calling drawImage/drawPixmap.
// Use our own clipping function so that we can get the
// region handling to end up with the fastest possible clip.
//
// XXX Antialiasing will fail pretty hard here, since we can't clip with AA
// with QPainter.
qs->p->save();
 
if (path) {
cairo_int_status_t status;
 
cairo_clip_t clip, old_clip = qs->clipper.clip;
 
_cairo_clip_init_copy (&clip, &qs->clipper.clip);
status = (cairo_int_status_t) _cairo_clip_clip (&clip,
path,
fill_rule,
tolerance,
antialias);
if (unlikely (status)) {
qs->p->restore();
return false;
}
 
status = _cairo_qt_surface_set_clip (qs, &clip);
if (unlikely (status)) {
qs->p->restore();
return false;
}
 
_cairo_clip_reset (&clip);
qs->clipper.clip = old_clip;
}
 
qs->p->setWorldMatrix (sourceMatrix.inverted(), true);
 
switch (source->extend) {
case CAIRO_EXTEND_REPEAT:
// XXX handle reflect by tiling 4 times first
case CAIRO_EXTEND_REFLECT: {
assert (qsSrc_pixmap);
 
// Render the tiling to cover the entire destination window (because
// it'll be clipped). Transform the window rect by the inverse
// of the current world transform so that the device coordinates
// end up as the right thing.
QRectF dest = qs->p->worldTransform().inverted().mapRect(QRectF(qs->window));
QPointF origin = sourceMatrix.map(QPointF(0.0, 0.0));
 
qs->p->drawTiledPixmap (dest, *qsSrc_pixmap, origin);
}
break;
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_PAD: // XXX not exactly right, but good enough
default:
if (qsSrc_image)
qs->p->drawImage (0, 0, *qsSrc_image);
else if (qsSrc_pixmap)
qs->p->drawPixmap (0, 0, *qsSrc_pixmap);
break;
}
 
qs->p->restore();
 
return true;
#else
return false;
#endif
}
 
static cairo_int_status_t
_cairo_qt_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
cairo_int_status_t status;
 
D(fprintf(stderr, "q[%p] paint op:%s\n", abstract_surface, _opstr(op)));
 
if (! _op_is_supported (qs, op))
return _cairo_surface_fallback_paint (abstract_surface, op, source, clip);
 
status = _cairo_qt_surface_set_clip (qs, clip);
if (unlikely (status))
return status;
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
 
if (! _cairo_qt_fast_fill (qs, source)) {
PatternToBrushConverter brush (source);
qs->p->fillRect (qs->window, brush);
}
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_qt_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] fill op:%s\n", abstract_surface, _opstr(op)));
 
if (! _op_is_supported (qs, op))
return _cairo_surface_fallback_fill (abstract_surface, op,
source, path, fill_rule,
tolerance, antialias, clip);
 
cairo_int_status_t status = _cairo_qt_surface_set_clip (qs, clip);
if (unlikely (status))
return status;
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
 
// XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
// enabled
//qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
 
if (! _cairo_qt_fast_fill (qs, source,
path, fill_rule, tolerance, antialias))
{
PatternToBrushConverter brush(source);
qs->p->fillPath (path_to_qt (path, fill_rule), brush);
}
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_qt_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] stroke op:%s\n", abstract_surface, _opstr(op)));
 
if (! _op_is_supported (qs, op))
return _cairo_surface_fallback_stroke (abstract_surface, op,
source, path, style, ctm,
ctm_inverse, tolerance,
antialias, clip);
 
cairo_int_status_t int_status = _cairo_qt_surface_set_clip (qs, clip);
if (unlikely (int_status))
return int_status;
 
QMatrix savedMatrix = qs->p->worldMatrix();
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (_qpainter_compositionmode_from_cairo_op (op));
 
qs->p->setWorldMatrix (_qmatrix_from_cairo_matrix (*ctm), true);
// XXX Qt4.3, 4.4 misrenders some complex paths if antialiasing is
// enabled
//qs->p->setRenderHint (QPainter::Antialiasing, antialias == CAIRO_ANTIALIAS_NONE ? false : true);
qs->p->setRenderHint (QPainter::SmoothPixmapTransform, source->filter != CAIRO_FILTER_FAST);
 
PatternToPenConverter pen(source, style);
 
qs->p->setPen(pen);
qs->p->drawPath(path_to_qt (path, ctm_inverse));
qs->p->setPen(Qt::black);
 
qs->p->setWorldMatrix (savedMatrix, false);
 
if (qs->supports_porter_duff)
qs->p->setCompositionMode (QPainter::CompositionMode_SourceOver);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_qt_surface_show_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
#if ((QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)) || defined(QT_GLYPHS_API_BACKPORT)) && 0
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
// pick out the colour to use from the cairo source
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) source;
cairo_scaled_glyph_t* glyph;
// documentation says you have to freeze the cache, but I don't believe it
_cairo_scaled_font_freeze_cache(scaled_font);
 
QColor tempColour(solid->color.red * 255, solid->color.green * 255, solid->color.blue * 255);
QVarLengthArray<QPointF> positions(num_glyphs);
QVarLengthArray<unsigned int> glyphss(num_glyphs);
FT_Face face = cairo_ft_scaled_font_lock_face (scaled_font);
const FT_Size_Metrics& ftMetrics = face->size->metrics;
QFont font(face->family_name);
font.setStyleStrategy(QFont::NoFontMerging);
font.setBold(face->style_flags & FT_STYLE_FLAG_BOLD);
font.setItalic(face->style_flags & FT_STYLE_FLAG_ITALIC);
font.setKerning(face->face_flags & FT_FACE_FLAG_KERNING);
font.setPixelSize(ftMetrics.y_ppem);
cairo_ft_scaled_font_unlock_face(scaled_font);
qs->p->setFont(font);
qs->p->setPen(tempColour);
for (int currentGlyph = 0; currentGlyph < num_glyphs; currentGlyph++) {
positions[currentGlyph].setX(glyphs[currentGlyph].x);
positions[currentGlyph].setY(glyphs[currentGlyph].y);
glyphss[currentGlyph] = glyphs[currentGlyph].index;
}
qt_draw_glyphs(qs->p, glyphss.data(), positions.data(), num_glyphs);
_cairo_scaled_font_thaw_cache(scaled_font);
return CAIRO_INT_STATUS_SUCCESS;
#else
return _cairo_surface_fallback_glyphs (abstract_surface, op,
source, glyphs, num_glyphs,
scaled_font, clip);
#endif
}
 
static cairo_int_status_t
_cairo_qt_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
D(fprintf(stderr, "q[%p] mask op:%s\n", abstract_surface, _opstr(op)));
 
if (qs->p && mask->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
cairo_int_status_t result;
 
qs->p->setOpacity (solid_mask->color.alpha);
 
result = _cairo_qt_surface_paint (abstract_surface, op, source, clip);
 
qs->p->setOpacity (1.0);
 
return result;
}
 
// otherwise skip for now
return _cairo_surface_fallback_mask (abstract_surface, op, source, mask, clip);
}
 
static cairo_status_t
_cairo_qt_surface_mark_dirty (void *abstract_surface,
int x, int y,
int width, int height)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t *) abstract_surface;
 
if (qs->p && !(qs->image || qs->pixmap))
qs->p->save ();
 
return CAIRO_STATUS_SUCCESS;
}
 
/*
* Backend struct
*/
 
static const cairo_surface_backend_t cairo_qt_surface_backend = {
CAIRO_SURFACE_TYPE_QT,
_cairo_qt_surface_finish,
 
_cairo_default_context_create, /* XXX */
 
_cairo_qt_surface_create_similar,
NULL, /* similar image */
_cairo_qt_surface_map_to_image,
_cairo_qt_surface_unmap_image,
 
_cairo_surface_default_source,
_cairo_qt_surface_acquire_source_image,
_cairo_qt_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_qt_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
_cairo_qt_surface_mark_dirty,
 
_cairo_qt_surface_paint,
_cairo_qt_surface_mask,
_cairo_qt_surface_stroke,
_cairo_qt_surface_fill,
NULL, /* fill_stroke */
_cairo_qt_surface_show_glyphs
};
 
cairo_surface_t *
cairo_qt_surface_create (QPainter *painter)
{
cairo_qt_surface_t *qs;
 
qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
if (qs == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
memset (qs, 0, sizeof(cairo_qt_surface_t));
 
_cairo_surface_init (&qs->base,
&cairo_qt_surface_backend,
NULL, /* device */
CAIRO_CONTENT_COLOR_ALPHA);
 
_cairo_surface_clipper_init (&qs->clipper,
_cairo_qt_surface_clipper_intersect_clip_path);
 
qs->p = painter;
if (qs->p->paintEngine())
qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
else
qs->supports_porter_duff = FALSE;
 
// Save so that we can always get back to the original state
qs->p->save();
 
qs->window = painter->window();
 
D(fprintf(stderr, "qpainter_surface_create: window: [%d %d %d %d] pd:%d\n",
qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
qs->supports_porter_duff));
 
return &qs->base;
}
 
cairo_surface_t *
cairo_qt_surface_create_with_qimage (cairo_format_t format,
int width,
int height)
{
cairo_qt_surface_t *qs;
 
qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
if (qs == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
memset (qs, 0, sizeof(cairo_qt_surface_t));
 
_cairo_surface_init (&qs->base,
&cairo_qt_surface_backend,
NULL, /* device */
_cairo_content_from_format (format));
 
_cairo_surface_clipper_init (&qs->clipper,
_cairo_qt_surface_clipper_intersect_clip_path);
 
 
QImage *image = new QImage (width, height,
_qimage_format_from_cairo_format (format));
 
qs->image = image;
 
if (!image->isNull()) {
qs->p = new QPainter(image);
qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
}
 
qs->image_equiv = cairo_image_surface_create_for_data (image->bits(),
format,
width, height,
image->bytesPerLine());
 
qs->window = QRect(0, 0, width, height);
 
D(fprintf(stderr, "qpainter_surface_create: qimage: [%d %d %d %d] pd:%d\n",
qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
qs->supports_porter_duff));
 
return &qs->base;
}
 
cairo_surface_t *
cairo_qt_surface_create_with_qpixmap (cairo_content_t content,
int width,
int height)
{
cairo_qt_surface_t *qs;
 
if ((content & CAIRO_CONTENT_COLOR) == 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
 
qs = (cairo_qt_surface_t *) malloc (sizeof(cairo_qt_surface_t));
if (qs == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
memset (qs, 0, sizeof(cairo_qt_surface_t));
 
QPixmap *pixmap = new QPixmap (width, height);
if (pixmap == NULL) {
free (qs);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
// By default, a QPixmap is opaque; however, if it's filled
// with a color with a transparency component, it is converted
// to a format that preserves transparency.
if (content == CAIRO_CONTENT_COLOR_ALPHA)
pixmap->fill(Qt::transparent);
 
_cairo_surface_init (&qs->base,
&cairo_qt_surface_backend,
NULL, /* device */
content);
 
_cairo_surface_clipper_init (&qs->clipper,
_cairo_qt_surface_clipper_intersect_clip_path);
 
qs->pixmap = pixmap;
 
if (!pixmap->isNull()) {
qs->p = new QPainter(pixmap);
qs->supports_porter_duff = qs->p->paintEngine()->hasFeature(QPaintEngine::PorterDuff);
}
 
qs->window = QRect(0, 0, width, height);
 
D(fprintf(stderr, "qpainter_surface_create: qpixmap: [%d %d %d %d] pd:%d\n",
qs->window.x(), qs->window.y(), qs->window.width(), qs->window.height(),
qs->supports_porter_duff));
 
return &qs->base;
}
 
QPainter *
cairo_qt_surface_get_qpainter (cairo_surface_t *surface)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
 
if (surface->type != CAIRO_SURFACE_TYPE_QT)
return NULL;
 
return qs->p;
}
 
QImage *
cairo_qt_surface_get_qimage (cairo_surface_t *surface)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
 
if (surface->type != CAIRO_SURFACE_TYPE_QT)
return NULL;
 
return qs->image;
}
 
cairo_surface_t *
cairo_qt_surface_get_image (cairo_surface_t *surface)
{
cairo_qt_surface_t *qs = (cairo_qt_surface_t*) surface;
 
if (surface->type != CAIRO_SURFACE_TYPE_QT)
return NULL;
 
return qs->image_equiv;
}
 
/*
* TODO:
*
* - Figure out why QBrush isn't working with non-repeated images
*
* - Correct repeat mode; right now, every surface source is EXTEND_REPEAT
* - implement EXTEND_NONE (?? probably need to clip to the extents of the source)
* - implement EXTEND_REFLECT (create temporary and copy 4x, then EXTEND_REPEAT that)
*
* - stroke-image failure
*
* - Implement mask() with non-solid masks (probably will need to use a temporary and use IN)
*
* - Implement gradient sources
*
* - Make create_similar smarter -- create QPixmaps in more circumstances
* (e.g. if the pixmap can have alpha)
*
* - Implement show_glyphs() in terms of Qt
*
*/
/programs/develop/libraries/cairo/src/cairo-quartz-font.c
0,0 → 1,860
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright � 2008 Mozilla Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@mozilla.com>
*/
 
#include "cairoint.h"
 
#include <dlfcn.h>
 
#include "cairo-image-surface-private.h"
#include "cairo-quartz.h"
#include "cairo-quartz-private.h"
 
#include "cairo-error-private.h"
 
/**
* SECTION:cairo-quartz-fonts
* @Title: Quartz (CGFont) Fonts
* @Short_Description: Font support via CGFont on OS X
* @See_Also: #cairo_font_face_t
*
* The Quartz font backend is primarily used to render text on Apple
* MacOS X systems. The CGFont API is used for the internal
* implementation of the font backend methods.
**/
 
/**
* CAIRO_HAS_QUARTZ_FONT:
*
* Defined if the Quartz font backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.6
**/
 
static CFDataRef (*CGFontCopyTableForTagPtr) (CGFontRef font, uint32_t tag) = NULL;
 
/* CreateWithFontName exists in 10.5, but not in 10.4; CreateWithName isn't public in 10.4 */
static CGFontRef (*CGFontCreateWithFontNamePtr) (CFStringRef) = NULL;
static CGFontRef (*CGFontCreateWithNamePtr) (const char *) = NULL;
 
/* These aren't public before 10.5, and some have different names in 10.4 */
static int (*CGFontGetUnitsPerEmPtr) (CGFontRef) = NULL;
static bool (*CGFontGetGlyphAdvancesPtr) (CGFontRef, const CGGlyph[], size_t, int[]) = NULL;
static bool (*CGFontGetGlyphBBoxesPtr) (CGFontRef, const CGGlyph[], size_t, CGRect[]) = NULL;
static CGRect (*CGFontGetFontBBoxPtr) (CGFontRef) = NULL;
 
/* Not public, but present */
static void (*CGFontGetGlyphsForUnicharsPtr) (CGFontRef, const UniChar[], const CGGlyph[], size_t) = NULL;
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
 
/* Not public in the least bit */
static CGPathRef (*CGFontGetGlyphPathPtr) (CGFontRef fontRef, CGAffineTransform *textTransform, int unknown, CGGlyph glyph) = NULL;
 
/* CGFontGetHMetrics isn't public, but the other functions are public/present in 10.5 */
typedef struct {
int ascent;
int descent;
int leading;
} quartz_CGFontMetrics;
static quartz_CGFontMetrics* (*CGFontGetHMetricsPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetAscentPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
 
/* Not public anymore in 64-bits nor in 10.7 */
static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
 
static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
 
static void
quartz_font_ensure_symbols(void)
{
if (_cairo_quartz_font_symbol_lookup_done)
return;
 
CGFontCopyTableForTagPtr = dlsym(RTLD_DEFAULT, "CGFontCopyTableForTag");
 
/* Look for the 10.5 versions first */
CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBBoxes");
if (!CGFontGetGlyphBBoxesPtr)
CGFontGetGlyphBBoxesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphBoundingBoxes");
 
CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnichars");
if (!CGFontGetGlyphsForUnicharsPtr)
CGFontGetGlyphsForUnicharsPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes");
 
CGFontGetFontBBoxPtr = dlsym(RTLD_DEFAULT, "CGFontGetFontBBox");
 
/* We just need one of these two */
CGFontCreateWithFontNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithFontName");
CGFontCreateWithNamePtr = dlsym(RTLD_DEFAULT, "CGFontCreateWithName");
 
/* These have the same name in 10.4 and 10.5 */
CGFontGetUnitsPerEmPtr = dlsym(RTLD_DEFAULT, "CGFontGetUnitsPerEm");
CGFontGetGlyphAdvancesPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphAdvances");
CGFontGetGlyphPathPtr = dlsym(RTLD_DEFAULT, "CGFontGetGlyphPath");
 
CGFontGetHMetricsPtr = dlsym(RTLD_DEFAULT, "CGFontGetHMetrics");
CGFontGetAscentPtr = dlsym(RTLD_DEFAULT, "CGFontGetAscent");
CGFontGetDescentPtr = dlsym(RTLD_DEFAULT, "CGFontGetDescent");
CGFontGetLeadingPtr = dlsym(RTLD_DEFAULT, "CGFontGetLeading");
 
CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
 
FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
 
if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
CGFontGetGlyphBBoxesPtr &&
CGFontGetGlyphsForUnicharsPtr &&
CGFontGetUnitsPerEmPtr &&
CGFontGetGlyphAdvancesPtr &&
CGFontGetGlyphPathPtr &&
(CGFontGetHMetricsPtr || (CGFontGetAscentPtr && CGFontGetDescentPtr && CGFontGetLeadingPtr)))
_cairo_quartz_font_symbols_present = TRUE;
 
_cairo_quartz_font_symbol_lookup_done = TRUE;
}
 
typedef struct _cairo_quartz_font_face cairo_quartz_font_face_t;
typedef struct _cairo_quartz_scaled_font cairo_quartz_scaled_font_t;
 
struct _cairo_quartz_scaled_font {
cairo_scaled_font_t base;
};
 
struct _cairo_quartz_font_face {
cairo_font_face_t base;
 
CGFontRef cgFont;
};
 
/*
* font face backend
*/
 
static cairo_status_t
_cairo_quartz_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
cairo_font_face_t **font_face)
{
const char *family;
char *full_name;
CFStringRef cgFontName = NULL;
CGFontRef cgFont = NULL;
int loop;
 
quartz_font_ensure_symbols();
if (! _cairo_quartz_font_symbols_present)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
family = toy_face->family;
full_name = malloc (strlen (family) + 64); // give us a bit of room to tack on Bold, Oblique, etc.
/* handle CSS-ish faces */
if (!strcmp(family, "serif") || !strcmp(family, "Times Roman"))
family = "Times";
else if (!strcmp(family, "sans-serif") || !strcmp(family, "sans"))
family = "Helvetica";
else if (!strcmp(family, "cursive"))
family = "Apple Chancery";
else if (!strcmp(family, "fantasy"))
family = "Papyrus";
else if (!strcmp(family, "monospace") || !strcmp(family, "mono"))
family = "Courier";
 
/* Try to build up the full name, e.g. "Helvetica Bold Oblique" first,
* then drop the bold, then drop the slant, then drop both.. finally
* just use "Helvetica". And if Helvetica doesn't exist, give up.
*/
for (loop = 0; loop < 5; loop++) {
if (loop == 4)
family = "Helvetica";
 
strcpy (full_name, family);
 
if (loop < 3 && (loop & 1) == 0) {
if (toy_face->weight == CAIRO_FONT_WEIGHT_BOLD)
strcat (full_name, " Bold");
}
 
if (loop < 3 && (loop & 2) == 0) {
if (toy_face->slant == CAIRO_FONT_SLANT_ITALIC)
strcat (full_name, " Italic");
else if (toy_face->slant == CAIRO_FONT_SLANT_OBLIQUE)
strcat (full_name, " Oblique");
}
 
if (CGFontCreateWithFontNamePtr) {
cgFontName = CFStringCreateWithCString (NULL, full_name, kCFStringEncodingASCII);
cgFont = CGFontCreateWithFontNamePtr (cgFontName);
CFRelease (cgFontName);
} else {
cgFont = CGFontCreateWithNamePtr (full_name);
}
 
if (cgFont)
break;
}
 
if (!cgFont) {
/* Give up */
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
*font_face = cairo_quartz_font_face_create_for_cgfont (cgFont);
CGFontRelease (cgFont);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_quartz_font_face_destroy (void *abstract_face)
{
cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
 
CGFontRelease (font_face->cgFont);
}
 
static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend;
 
static cairo_status_t
_cairo_quartz_font_face_scaled_font_create (void *abstract_face,
const cairo_matrix_t *font_matrix,
const cairo_matrix_t *ctm,
const cairo_font_options_t *options,
cairo_scaled_font_t **font_out)
{
cairo_quartz_font_face_t *font_face = abstract_face;
cairo_quartz_scaled_font_t *font = NULL;
cairo_status_t status;
cairo_font_extents_t fs_metrics;
double ems;
CGRect bbox;
 
quartz_font_ensure_symbols();
if (!_cairo_quartz_font_symbols_present)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
font = malloc(sizeof(cairo_quartz_scaled_font_t));
if (font == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memset (font, 0, sizeof(cairo_quartz_scaled_font_t));
 
status = _cairo_scaled_font_init (&font->base,
&font_face->base, font_matrix, ctm, options,
&_cairo_quartz_scaled_font_backend);
if (status)
goto FINISH;
 
ems = CGFontGetUnitsPerEmPtr (font_face->cgFont);
 
/* initialize metrics */
if (CGFontGetFontBBoxPtr && CGFontGetAscentPtr) {
fs_metrics.ascent = (CGFontGetAscentPtr (font_face->cgFont) / ems);
fs_metrics.descent = - (CGFontGetDescentPtr (font_face->cgFont) / ems);
fs_metrics.height = fs_metrics.ascent + fs_metrics.descent +
(CGFontGetLeadingPtr (font_face->cgFont) / ems);
 
bbox = CGFontGetFontBBoxPtr (font_face->cgFont);
fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
fs_metrics.max_y_advance = 0.0;
} else {
CGGlyph wGlyph;
UniChar u;
 
quartz_CGFontMetrics *m;
m = CGFontGetHMetricsPtr (font_face->cgFont);
 
/* On OX 10.4, GetHMetricsPtr sometimes returns NULL for unknown reasons */
if (!m) {
status = _cairo_error(CAIRO_STATUS_NULL_POINTER);
goto FINISH;
}
 
fs_metrics.ascent = (m->ascent / ems);
fs_metrics.descent = - (m->descent / ems);
fs_metrics.height = fs_metrics.ascent + fs_metrics.descent + (m->leading / ems);
 
/* We kind of have to guess here; W's big, right? */
u = (UniChar) 'W';
CGFontGetGlyphsForUnicharsPtr (font_face->cgFont, &u, &wGlyph, 1);
if (wGlyph && CGFontGetGlyphBBoxesPtr (font_face->cgFont, &wGlyph, 1, &bbox)) {
fs_metrics.max_x_advance = CGRectGetMaxX(bbox) / ems;
fs_metrics.max_y_advance = 0.0;
} else {
fs_metrics.max_x_advance = 0.0;
fs_metrics.max_y_advance = 0.0;
}
}
 
status = _cairo_scaled_font_set_metrics (&font->base, &fs_metrics);
 
FINISH:
if (status != CAIRO_STATUS_SUCCESS) {
free (font);
} else {
*font_out = (cairo_scaled_font_t*) font;
}
 
return status;
}
 
const cairo_font_face_backend_t _cairo_quartz_font_face_backend = {
CAIRO_FONT_TYPE_QUARTZ,
_cairo_quartz_font_face_create_for_toy,
_cairo_quartz_font_face_destroy,
_cairo_quartz_font_face_scaled_font_create
};
 
/**
* cairo_quartz_font_face_create_for_cgfont:
* @font: a #CGFontRef obtained through a method external to cairo.
*
* Creates a new font for the Quartz font backend based on a
* #CGFontRef. This font can then be used with
* cairo_set_font_face() or cairo_scaled_font_create().
*
* Return value: a newly created #cairo_font_face_t. Free with
* cairo_font_face_destroy() when you are done using it.
*
* Since: 1.6
**/
cairo_font_face_t *
cairo_quartz_font_face_create_for_cgfont (CGFontRef font)
{
cairo_quartz_font_face_t *font_face;
 
quartz_font_ensure_symbols();
 
font_face = malloc (sizeof (cairo_quartz_font_face_t));
if (!font_face) {
cairo_status_t ignore_status;
ignore_status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *)&_cairo_font_face_nil;
}
 
font_face->cgFont = CGFontRetain (font);
 
_cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
 
return &font_face->base;
}
 
/*
* scaled font backend
*/
 
static cairo_quartz_font_face_t *
_cairo_quartz_scaled_to_face (void *abstract_font)
{
cairo_quartz_scaled_font_t *sfont = (cairo_quartz_scaled_font_t*) abstract_font;
cairo_font_face_t *font_face = sfont->base.font_face;
assert (font_face->backend->type == CAIRO_FONT_TYPE_QUARTZ);
return (cairo_quartz_font_face_t*) font_face;
}
 
static void
_cairo_quartz_scaled_font_fini(void *abstract_font)
{
}
 
#define INVALID_GLYPH 0x00
 
static inline CGGlyph
_cairo_quartz_scaled_glyph_index (cairo_scaled_glyph_t *scaled_glyph) {
unsigned long index = _cairo_scaled_glyph_index (scaled_glyph);
if (index > 0xffff)
return INVALID_GLYPH;
return (CGGlyph) index;
}
 
static cairo_int_status_t
_cairo_quartz_init_glyph_metrics (cairo_quartz_scaled_font_t *font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
cairo_text_extents_t extents = {0, 0, 0, 0, 0, 0};
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
int advance;
CGRect bbox;
double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
double xmin, ymin, xmax, ymax;
 
if (glyph == INVALID_GLYPH)
goto FAIL;
 
if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
goto FAIL;
 
/* broken fonts like Al Bayan return incorrect bounds for some null characters,
see https://bugzilla.mozilla.org/show_bug.cgi?id=534260 */
if (unlikely (bbox.origin.x == -32767 &&
bbox.origin.y == -32767 &&
bbox.size.width == 65534 &&
bbox.size.height == 65534)) {
bbox.origin.x = bbox.origin.y = 0;
bbox.size.width = bbox.size.height = 0;
}
 
bbox = CGRectMake (bbox.origin.x / emscale,
bbox.origin.y / emscale,
bbox.size.width / emscale,
bbox.size.height / emscale);
 
/* Should we want to always integer-align glyph extents, we can do so in this way */
#if 0
{
CGAffineTransform textMatrix;
textMatrix = CGAffineTransformMake (font->base.scale.xx,
-font->base.scale.yx,
-font->base.scale.xy,
font->base.scale.yy,
0.0f, 0.0f);
 
bbox = CGRectApplyAffineTransform (bbox, textMatrix);
bbox = CGRectIntegral (bbox);
bbox = CGRectApplyAffineTransform (bbox, CGAffineTransformInvert (textMatrix));
}
#endif
 
#if 0
fprintf (stderr, "[0x%04x] bbox: %f %f %f %f\n", glyph,
bbox.origin.x / emscale, bbox.origin.y / emscale,
bbox.size.width / emscale, bbox.size.height / emscale);
#endif
 
xmin = CGRectGetMinX(bbox);
ymin = CGRectGetMinY(bbox);
xmax = CGRectGetMaxX(bbox);
ymax = CGRectGetMaxY(bbox);
 
extents.x_bearing = xmin;
extents.y_bearing = - ymax;
extents.width = xmax - xmin;
extents.height = ymax - ymin;
 
extents.x_advance = (double) advance / emscale;
extents.y_advance = 0.0;
 
#if 0
fprintf (stderr, "[0x%04x] extents: bearings: %f %f dim: %f %f adv: %f\n\n", glyph,
extents.x_bearing, extents.y_bearing, extents.width, extents.height, extents.x_advance);
#endif
 
FAIL:
_cairo_scaled_glyph_set_metrics (scaled_glyph,
&font->base,
&extents);
 
return status;
}
 
static void
_cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
{
cairo_path_fixed_t *path = (cairo_path_fixed_t *) info;
cairo_status_t status;
 
switch (el->type) {
case kCGPathElementMoveToPoint:
status = _cairo_path_fixed_move_to (path,
_cairo_fixed_from_double(el->points[0].x),
_cairo_fixed_from_double(el->points[0].y));
assert(!status);
break;
case kCGPathElementAddLineToPoint:
status = _cairo_path_fixed_line_to (path,
_cairo_fixed_from_double(el->points[0].x),
_cairo_fixed_from_double(el->points[0].y));
assert(!status);
break;
case kCGPathElementAddQuadCurveToPoint: {
cairo_fixed_t fx, fy;
double x, y;
if (!_cairo_path_fixed_get_current_point (path, &fx, &fy))
fx = fy = 0;
x = _cairo_fixed_to_double (fx);
y = _cairo_fixed_to_double (fy);
 
status = _cairo_path_fixed_curve_to (path,
_cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0),
_cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0),
_cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0),
_cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0),
_cairo_fixed_from_double(el->points[1].x),
_cairo_fixed_from_double(el->points[1].y));
}
assert(!status);
break;
case kCGPathElementAddCurveToPoint:
status = _cairo_path_fixed_curve_to (path,
_cairo_fixed_from_double(el->points[0].x),
_cairo_fixed_from_double(el->points[0].y),
_cairo_fixed_from_double(el->points[1].x),
_cairo_fixed_from_double(el->points[1].y),
_cairo_fixed_from_double(el->points[2].x),
_cairo_fixed_from_double(el->points[2].y));
assert(!status);
break;
case kCGPathElementCloseSubpath:
status = _cairo_path_fixed_close_path (path);
assert(!status);
break;
}
}
 
static cairo_int_status_t
_cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
CGAffineTransform textMatrix;
CGPathRef glyphPath;
cairo_path_fixed_t *path;
 
if (glyph == INVALID_GLYPH) {
_cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create());
return CAIRO_STATUS_SUCCESS;
}
 
/* scale(1,-1) * font->base.scale */
textMatrix = CGAffineTransformMake (font->base.scale.xx,
font->base.scale.yx,
-font->base.scale.xy,
-font->base.scale.yy,
0, 0);
 
glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph);
if (!glyphPath)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
path = _cairo_path_fixed_create ();
if (!path) {
CGPathRelease (glyphPath);
return _cairo_error(CAIRO_STATUS_NO_MEMORY);
}
 
CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func);
 
CGPathRelease (glyphPath);
 
_cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_quartz_init_glyph_surface (cairo_quartz_scaled_font_t *font,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font);
 
cairo_image_surface_t *surface = NULL;
 
CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph);
 
int advance;
CGRect bbox;
double width, height;
double emscale = CGFontGetUnitsPerEmPtr (font_face->cgFont);
 
CGContextRef cgContext = NULL;
CGAffineTransform textMatrix;
CGRect glyphRect, glyphRectInt;
CGPoint glyphOrigin;
 
//fprintf (stderr, "scaled_glyph: %p surface: %p\n", scaled_glyph, scaled_glyph->surface);
 
/* Create blank 2x2 image if we don't have this character.
* Maybe we should draw a better missing-glyph slug or something,
* but this is ok for now.
*/
if (glyph == INVALID_GLYPH) {
surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, 2, 2);
status = cairo_surface_status ((cairo_surface_t *) surface);
if (status)
return status;
 
_cairo_scaled_glyph_set_surface (scaled_glyph,
&font->base,
surface);
return CAIRO_STATUS_SUCCESS;
}
 
if (!CGFontGetGlyphAdvancesPtr (font_face->cgFont, &glyph, 1, &advance) ||
!CGFontGetGlyphBBoxesPtr (font_face->cgFont, &glyph, 1, &bbox))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* scale(1,-1) * font->base.scale * scale(1,-1) */
textMatrix = CGAffineTransformMake (font->base.scale.xx,
-font->base.scale.yx,
-font->base.scale.xy,
font->base.scale.yy,
0, -0);
glyphRect = CGRectMake (bbox.origin.x / emscale,
bbox.origin.y / emscale,
bbox.size.width / emscale,
bbox.size.height / emscale);
 
glyphRect = CGRectApplyAffineTransform (glyphRect, textMatrix);
 
/* Round the rectangle outwards, so that we don't have to deal
* with non-integer-pixel origins or dimensions.
*/
glyphRectInt = CGRectIntegral (glyphRect);
 
#if 0
fprintf (stderr, "glyphRect[o]: %f %f %f %f\n",
glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
fprintf (stderr, "glyphRectInt: %f %f %f %f\n",
glyphRectInt.origin.x, glyphRectInt.origin.y, glyphRectInt.size.width, glyphRectInt.size.height);
#endif
 
glyphOrigin = glyphRectInt.origin;
 
//textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformInvert (ctm));
 
width = glyphRectInt.size.width;
height = glyphRectInt.size.height;
 
//fprintf (stderr, "glyphRect[n]: %f %f %f %f\n", glyphRect.origin.x, glyphRect.origin.y, glyphRect.size.width, glyphRect.size.height);
 
surface = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
if (surface->base.status)
return surface->base.status;
 
if (surface->width != 0 && surface->height != 0) {
cgContext = CGBitmapContextCreate (surface->data,
surface->width,
surface->height,
8,
surface->stride,
NULL,
kCGImageAlphaOnly);
 
if (cgContext == NULL) {
cairo_surface_destroy (&surface->base);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
CGContextSetFont (cgContext, font_face->cgFont);
CGContextSetFontSize (cgContext, 1.0);
CGContextSetTextMatrix (cgContext, textMatrix);
 
switch (font->base.options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
!CGContextGetAllowsFontSmoothingPtr (cgContext))
CGContextSetAllowsFontSmoothingPtr (cgContext, TRUE);
break;
case CAIRO_ANTIALIAS_NONE:
CGContextSetShouldAntialias (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
CGContextSetShouldAntialias (cgContext, TRUE);
CGContextSetShouldSmoothFonts (cgContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
default:
/* Don't do anything */
break;
}
 
CGContextSetAlpha (cgContext, 1.0);
CGContextShowGlyphsAtPoint (cgContext, - glyphOrigin.x, - glyphOrigin.y, &glyph, 1);
 
CGContextRelease (cgContext);
}
 
cairo_surface_set_device_offset (&surface->base,
- glyphOrigin.x,
height + glyphOrigin.y);
 
_cairo_scaled_glyph_set_surface (scaled_glyph, &font->base, surface);
 
return status;
}
 
static cairo_int_status_t
_cairo_quartz_scaled_glyph_init (void *abstract_font,
cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_info_t info)
{
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t *) abstract_font;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
if (!status && (info & CAIRO_SCALED_GLYPH_INFO_METRICS))
status = _cairo_quartz_init_glyph_metrics (font, scaled_glyph);
 
if (!status && (info & CAIRO_SCALED_GLYPH_INFO_PATH))
status = _cairo_quartz_init_glyph_path (font, scaled_glyph);
 
if (!status && (info & CAIRO_SCALED_GLYPH_INFO_SURFACE))
status = _cairo_quartz_init_glyph_surface (font, scaled_glyph);
 
return status;
}
 
static unsigned long
_cairo_quartz_ucs4_to_index (void *abstract_font,
uint32_t ucs4)
{
cairo_quartz_scaled_font_t *font = (cairo_quartz_scaled_font_t*) abstract_font;
cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(font);
UniChar u = (UniChar) ucs4;
CGGlyph glyph;
 
CGFontGetGlyphsForUnicharsPtr (ffont->cgFont, &u, &glyph, 1);
 
return glyph;
}
 
static cairo_int_status_t
_cairo_quartz_load_truetype_table (void *abstract_font,
unsigned long tag,
long offset,
unsigned char *buffer,
unsigned long *length)
{
cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face (abstract_font);
CFDataRef data = NULL;
 
if (likely (CGFontCopyTableForTagPtr))
data = CGFontCopyTableForTagPtr (font_face->cgFont, tag);
 
if (!data)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (buffer == NULL) {
*length = CFDataGetLength (data);
CFRelease (data);
return CAIRO_STATUS_SUCCESS;
}
 
if (CFDataGetLength (data) < offset + (long) *length) {
CFRelease (data);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
CFDataGetBytes (data, CFRangeMake (offset, *length), buffer);
CFRelease (data);
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_scaled_font_backend_t _cairo_quartz_scaled_font_backend = {
CAIRO_FONT_TYPE_QUARTZ,
_cairo_quartz_scaled_font_fini,
_cairo_quartz_scaled_glyph_init,
NULL, /* text_to_glyphs */
_cairo_quartz_ucs4_to_index,
_cairo_quartz_load_truetype_table,
NULL, /* map_glyphs_to_unicode */
};
 
/*
* private methods that the quartz surface uses
*/
 
CGFontRef
_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *abstract_font)
{
cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
 
return ffont->cgFont;
}
 
/*
* compat with old ATSUI backend
*/
 
/**
* cairo_quartz_font_face_create_for_atsu_font_id:
* @font_id: an ATSUFontID for the font.
*
* Creates a new font for the Quartz font backend based on an
* #ATSUFontID. This font can then be used with
* cairo_set_font_face() or cairo_scaled_font_create().
*
* Return value: a newly created #cairo_font_face_t. Free with
* cairo_font_face_destroy() when you are done using it.
*
* Since: 1.6
**/
cairo_font_face_t *
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
{
quartz_font_ensure_symbols();
 
if (FMGetATSFontRefFromFontPtr != NULL) {
ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
cairo_font_face_t *ff;
 
ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
 
CGFontRelease (cgFont);
 
return ff;
} else {
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_font_face_t *)&_cairo_font_face_nil;
}
}
 
/* This is the old name for the above function, exported for compat purposes */
cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
 
cairo_font_face_t *
cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
{
return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
}
/programs/develop/libraries/cairo/src/cairo-quartz-image-surface.c
0,0 → 1,385
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright � 2008 Mozilla Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@mozilla.com>
*/
 
#include "cairoint.h"
 
#include "cairo-image-surface-inline.h"
#include "cairo-quartz-image.h"
#include "cairo-quartz-private.h"
#include "cairo-surface-backend-private.h"
 
#include "cairo-error-private.h"
#include "cairo-default-context-private.h"
 
#define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)))
#define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH)))
#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)))
#define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
 
static void
DataProviderReleaseCallback (void *info, const void *data, size_t size)
{
cairo_surface_t *surface = (cairo_surface_t *) info;
cairo_surface_destroy (surface);
}
 
static cairo_surface_t *
_cairo_quartz_image_surface_create_similar (void *asurface,
cairo_content_t content,
int width,
int height)
{
cairo_surface_t *isurf =
_cairo_image_surface_create_with_content (content, width, height);
cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
cairo_surface_destroy (isurf);
 
return result;
}
 
static cairo_surface_t *
_cairo_quartz_image_surface_create_similar_image (void *asurface,
cairo_format_t format,
int width,
int height)
{
cairo_surface_t *isurf = cairo_image_surface_create (format, width, height);
cairo_surface_t *result = cairo_quartz_image_surface_create (isurf);
cairo_surface_destroy (isurf);
 
return result;
}
 
static cairo_status_t
_cairo_quartz_image_surface_finish (void *asurface)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
 
/* the imageSurface will be destroyed by the data provider's release callback */
CGImageRelease (surface->image);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_quartz_image_surface_acquire_source_image (void *asurface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
 
*image_out = surface->imageSurface;
*image_extra = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_image_surface_t *
_cairo_quartz_image_surface_map_to_image (void *asurface,
const cairo_rectangle_int_t *extents)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
return _cairo_surface_map_to_image (&surface->imageSurface->base, extents);
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_unmap_image (void *asurface,
cairo_image_surface_t *image)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
return _cairo_surface_unmap_image (&surface->imageSurface->base, image);
}
 
static cairo_bool_t
_cairo_quartz_image_surface_get_extents (void *asurface,
cairo_rectangle_int_t *extents)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
 
extents->x = 0;
extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
return TRUE;
}
 
/* we assume some drawing happened to the image buffer; make sure it's
* represented in the CGImage on flush()
*/
 
static cairo_status_t
_cairo_quartz_image_surface_flush (void *asurface,
unsigned flags)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) asurface;
CGImageRef oldImage = surface->image;
CGImageRef newImage = NULL;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
/* XXX only flush if the image has been modified. */
 
/* To be released by the ReleaseCallback */
cairo_surface_reference ((cairo_surface_t*) surface->imageSurface);
 
newImage = CairoQuartzCreateCGImage (surface->imageSurface->format,
surface->imageSurface->width,
surface->imageSurface->height,
surface->imageSurface->stride,
surface->imageSurface->data,
TRUE,
NULL,
DataProviderReleaseCallback,
surface->imageSurface);
 
surface->image = newImage;
CGImageRelease (oldImage);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_quartz_image_surface_t *surface = abstract_surface;
return _cairo_surface_paint (&surface->imageSurface->base,
op, source, clip);
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_quartz_image_surface_t *surface = abstract_surface;
return _cairo_surface_mask (&surface->imageSurface->base,
op, source, mask, clip);
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_quartz_image_surface_t *surface = abstract_surface;
return _cairo_surface_stroke (&surface->imageSurface->base,
op, source, path,
style, ctm, ctm_inverse,
tolerance, antialias, clip);
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_quartz_image_surface_t *surface = abstract_surface;
return _cairo_surface_fill (&surface->imageSurface->base,
op, source, path,
fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_quartz_image_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_quartz_image_surface_t *surface = abstract_surface;
return _cairo_surface_show_text_glyphs (&surface->imageSurface->base,
op, source,
NULL, 0,
glyphs, num_glyphs,
NULL, 0, 0,
scaled_font, clip);
}
 
 
static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
_cairo_quartz_image_surface_finish,
 
_cairo_default_context_create,
 
_cairo_quartz_image_surface_create_similar,
_cairo_quartz_image_surface_create_similar_image,
_cairo_quartz_image_surface_map_to_image,
_cairo_quartz_image_surface_unmap_image,
 
_cairo_surface_default_source,
_cairo_quartz_image_surface_acquire_source_image,
NULL, /* release_source_image */
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_quartz_image_surface_get_extents,
NULL, /* get_font_options */
 
_cairo_quartz_image_surface_flush,
NULL, /* mark_dirty_rectangle */
 
_cairo_quartz_image_surface_paint,
_cairo_quartz_image_surface_mask,
_cairo_quartz_image_surface_stroke,
_cairo_quartz_image_surface_fill,
NULL, /* fill-stroke */
_cairo_quartz_image_surface_glyphs,
};
 
/**
* cairo_quartz_image_surface_create:
* @image_surface: a cairo image surface to wrap with a quartz image surface
*
* Creates a Quartz surface backed by a CGImageRef that references the
* given image surface. The resulting surface can be rendered quickly
* when used as a source when rendering to a #cairo_quartz_surface. If
* the data in the image surface is ever updated, cairo_surface_flush()
* must be called on the #cairo_quartz_image_surface to ensure that the
* CGImageRef refers to the updated data.
*
* Return value: the newly created surface.
*
* Since: 1.6
**/
cairo_surface_t *
cairo_quartz_image_surface_create (cairo_surface_t *surface)
{
cairo_quartz_image_surface_t *qisurf;
 
CGImageRef image;
 
cairo_image_surface_t *image_surface;
int width, height, stride;
cairo_format_t format;
unsigned char *data;
 
if (surface->status)
return surface;
 
if (! _cairo_surface_is_image (surface))
return SURFACE_ERROR_TYPE_MISMATCH;
 
image_surface = (cairo_image_surface_t*) surface;
width = image_surface->width;
height = image_surface->height;
stride = image_surface->stride;
format = image_surface->format;
data = image_surface->data;
 
if (!_cairo_quartz_verify_surface_size(width, height))
return SURFACE_ERROR_INVALID_SIZE;
 
if (width == 0 || height == 0)
return SURFACE_ERROR_INVALID_SIZE;
 
if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
return SURFACE_ERROR_INVALID_FORMAT;
 
qisurf = malloc(sizeof(cairo_quartz_image_surface_t));
if (qisurf == NULL)
return SURFACE_ERROR_NO_MEMORY;
 
memset (qisurf, 0, sizeof(cairo_quartz_image_surface_t));
 
/* In case the create_cgimage fails, this ref will
* be released via the callback (which will be called in
* case of failure.)
*/
cairo_surface_reference (surface);
 
image = CairoQuartzCreateCGImage (format,
width, height,
stride,
data,
TRUE,
NULL,
DataProviderReleaseCallback,
image_surface);
 
if (!image) {
free (qisurf);
return SURFACE_ERROR_NO_MEMORY;
}
 
_cairo_surface_init (&qisurf->base,
&cairo_quartz_image_surface_backend,
NULL, /* device */
_cairo_content_from_format (format));
 
qisurf->width = width;
qisurf->height = height;
 
qisurf->image = image;
qisurf->imageSurface = image_surface;
 
return &qisurf->base;
}
 
 
cairo_surface_t *
cairo_quartz_image_surface_get_image (cairo_surface_t *asurface)
{
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t*) asurface;
 
if (asurface->type != CAIRO_SURFACE_TYPE_QUARTZ_IMAGE)
return NULL;
 
return (cairo_surface_t*) surface->imageSurface;
}
/programs/develop/libraries/cairo/src/cairo-quartz-private.h
50,6 → 50,13
typedef float cairo_quartz_float_t;
#endif
 
typedef enum {
DO_DIRECT,
DO_SHADING,
DO_IMAGE,
DO_TILED_IMAGE
} cairo_quartz_action_t;
 
typedef struct cairo_quartz_surface {
cairo_surface_t base;
 
61,36 → 68,23
 
cairo_surface_clipper_t clipper;
cairo_rectangle_int_t extents;
 
/* These are stored while drawing operations are in place, set up
* by quartz_setup_source() and quartz_finish_source()
*/
CGAffineTransform sourceTransform;
 
CGImageRef sourceImage;
cairo_surface_t *sourceImageSurface;
CGRect sourceImageRect;
 
CGShadingRef sourceShading;
CGPatternRef sourcePattern;
 
CGInterpolationQuality oldInterpolationQuality;
cairo_rectangle_int_t virtual_extents;
} cairo_quartz_surface_t;
 
typedef struct cairo_quartz_image_surface {
cairo_surface_t base;
 
cairo_rectangle_int_t extents;
int width, height;
 
CGImageRef image;
cairo_image_surface_t *imageSurface;
} cairo_quartz_image_surface_t;
 
cairo_bool_t
cairo_private cairo_bool_t
_cairo_quartz_verify_surface_size(int width, int height);
 
CGImageRef
_cairo_quartz_create_cgimage (cairo_format_t format,
cairo_private CGImageRef
CairoQuartzCreateCGImage (cairo_format_t format,
unsigned int width,
unsigned int height,
unsigned int stride,
100,7 → 94,7
CGDataProviderReleaseDataCallback releaseCallback,
void *releaseInfo);
 
CGFontRef
cairo_private CGFontRef
_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
 
#else
/programs/develop/libraries/cairo/src/cairo-quartz-surface.c
0,0 → 1,2569
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright � 2006, 2007 Mozilla Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@mozilla.com>
*/
 
#define _GNU_SOURCE /* required for RTLD_DEFAULT */
#include "cairoint.h"
 
#include "cairo-quartz-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-recording-surface-private.h"
 
#include <dlfcn.h>
 
#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT ((void *) 0)
#endif
 
#include <limits.h>
 
#undef QUARTZ_DEBUG
 
#ifdef QUARTZ_DEBUG
#define ND(_x) fprintf _x
#else
#define ND(_x) do {} while(0)
#endif
 
#define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
 
/**
* SECTION:cairo-quartz
* @Title: Quartz Surfaces
* @Short_Description: Rendering to Quartz surfaces
* @See_Also: #cairo_surface_t
*
* The Quartz surface is used to render cairo graphics targeting the
* Apple OS X Quartz rendering system.
**/
 
/**
* CAIRO_HAS_QUARTZ_SURFACE:
*
* Defined if the Quartz surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.6
**/
 
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
/* This method is private, but it exists. Its params are are exposed
* as args to the NS* method, but not as CG.
*/
enum PrivateCGCompositeMode {
kPrivateCGCompositeClear = 0,
kPrivateCGCompositeCopy = 1,
kPrivateCGCompositeSourceOver = 2,
kPrivateCGCompositeSourceIn = 3,
kPrivateCGCompositeSourceOut = 4,
kPrivateCGCompositeSourceAtop = 5,
kPrivateCGCompositeDestinationOver = 6,
kPrivateCGCompositeDestinationIn = 7,
kPrivateCGCompositeDestinationOut = 8,
kPrivateCGCompositeDestinationAtop = 9,
kPrivateCGCompositeXOR = 10,
kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
};
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
#endif
 
/* Some of these are present in earlier versions of the OS than where
* they are public; other are not public at all
*/
/* public since 10.5 */
static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
 
/* public since 10.6 */
static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
 
/* not yet public */
static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
 
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
 
/*
* Utility functions
*/
 
#ifdef QUARTZ_DEBUG
static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
static void quartz_image_to_png (CGImageRef, char *dest);
#endif
 
static cairo_quartz_surface_t *
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
cairo_content_t content,
unsigned int width,
unsigned int height);
 
static cairo_bool_t
_cairo_surface_is_quartz (const cairo_surface_t *surface);
 
/* Load all extra symbols */
static void quartz_ensure_symbols (void)
{
if (likely (_cairo_quartz_symbol_lookup_done))
return;
 
CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
 
_cairo_quartz_symbol_lookup_done = TRUE;
}
 
CGImageRef
CairoQuartzCreateCGImage (cairo_format_t format,
unsigned int width,
unsigned int height,
unsigned int stride,
void *data,
cairo_bool_t interpolate,
CGColorSpaceRef colorSpaceOverride,
CGDataProviderReleaseDataCallback releaseCallback,
void *releaseInfo)
{
CGImageRef image = NULL;
CGDataProviderRef dataProvider = NULL;
CGColorSpaceRef colorSpace = colorSpaceOverride;
CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
int bitsPerComponent, bitsPerPixel;
 
switch (format) {
case CAIRO_FORMAT_ARGB32:
if (colorSpace == NULL)
colorSpace = CGColorSpaceCreateDeviceRGB ();
bitinfo |= kCGImageAlphaPremultipliedFirst;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
 
case CAIRO_FORMAT_RGB24:
if (colorSpace == NULL)
colorSpace = CGColorSpaceCreateDeviceRGB ();
bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
bitsPerPixel = 32;
break;
 
case CAIRO_FORMAT_A8:
bitsPerComponent = 8;
bitsPerPixel = 8;
break;
 
case CAIRO_FORMAT_A1:
#ifdef WORDS_BIGENDIAN
bitsPerComponent = 1;
bitsPerPixel = 1;
break;
#endif
 
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_INVALID:
default:
return NULL;
}
 
dataProvider = CGDataProviderCreateWithData (releaseInfo,
data,
height * stride,
releaseCallback);
 
if (unlikely (!dataProvider)) {
// manually release
if (releaseCallback)
releaseCallback (releaseInfo, data, height * stride);
goto FINISH;
}
 
if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
cairo_quartz_float_t decode[] = {1.0, 0.0};
image = CGImageMaskCreate (width, height,
bitsPerComponent,
bitsPerPixel,
stride,
dataProvider,
decode,
interpolate);
} else
image = CGImageCreate (width, height,
bitsPerComponent,
bitsPerPixel,
stride,
colorSpace,
bitinfo,
dataProvider,
NULL,
interpolate,
kCGRenderingIntentDefault);
 
FINISH:
 
CGDataProviderRelease (dataProvider);
 
if (colorSpace != colorSpaceOverride)
CGColorSpaceRelease (colorSpace);
 
return image;
}
 
static inline cairo_bool_t
_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
{
if (unlikely (cgc == NULL))
return FALSE;
 
if (likely (CGContextGetTypePtr)) {
/* 4 is the type value of a bitmap context */
return CGContextGetTypePtr (cgc) == 4;
}
 
/* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
return CGBitmapContextGetBitsPerPixel (cgc) != 0;
}
 
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
 
#define CG_MAX_HEIGHT SHRT_MAX
#define CG_MAX_WIDTH USHRT_MAX
 
/* is the desired size of the surface within bounds? */
cairo_bool_t
_cairo_quartz_verify_surface_size (int width, int height)
{
/* hmmm, allow width, height == 0 ? */
if (width < 0 || height < 0)
return FALSE;
 
if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
return FALSE;
 
return TRUE;
}
 
/*
* Cairo path -> Quartz path conversion helpers
*/
 
/* cairo path -> execute in context */
static cairo_status_t
_cairo_path_to_quartz_context_move_to (void *closure,
const cairo_point_t *point)
{
//ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
CGContextMoveToPoint (closure, x, y);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_quartz_context_line_to (void *closure,
const cairo_point_t *point)
{
//ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
CGContextAddLineToPoint (closure, x, y);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_quartz_context_curve_to (void *closure,
const cairo_point_t *p0,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
//ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
// _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
// _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
// _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
double x0 = _cairo_fixed_to_double (p0->x);
double y0 = _cairo_fixed_to_double (p0->y);
double x1 = _cairo_fixed_to_double (p1->x);
double y1 = _cairo_fixed_to_double (p1->y);
double x2 = _cairo_fixed_to_double (p2->x);
double y2 = _cairo_fixed_to_double (p2->y);
 
CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_path_to_quartz_context_close_path (void *closure)
{
//ND ((stderr, "closepath\n"));
CGContextClosePath (closure);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
CGContextRef closure)
{
cairo_status_t status;
 
CGContextBeginPath (closure);
status = _cairo_path_fixed_interpret (path,
_cairo_path_to_quartz_context_move_to,
_cairo_path_to_quartz_context_line_to,
_cairo_path_to_quartz_context_curve_to,
_cairo_path_to_quartz_context_close_path,
closure);
 
assert (status == CAIRO_STATUS_SUCCESS);
}
 
/*
* Misc helpers/callbacks
*/
 
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
static PrivateCGCompositeMode
_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return kPrivateCGCompositeClear;
case CAIRO_OPERATOR_SOURCE:
return kPrivateCGCompositeCopy;
case CAIRO_OPERATOR_OVER:
return kPrivateCGCompositeSourceOver;
case CAIRO_OPERATOR_IN:
return kPrivateCGCompositeSourceIn;
case CAIRO_OPERATOR_OUT:
return kPrivateCGCompositeSourceOut;
case CAIRO_OPERATOR_ATOP:
return kPrivateCGCompositeSourceAtop;
case CAIRO_OPERATOR_DEST_OVER:
return kPrivateCGCompositeDestinationOver;
case CAIRO_OPERATOR_DEST_IN:
return kPrivateCGCompositeDestinationIn;
case CAIRO_OPERATOR_DEST_OUT:
return kPrivateCGCompositeDestinationOut;
case CAIRO_OPERATOR_DEST_ATOP:
return kPrivateCGCompositeDestinationAtop;
case CAIRO_OPERATOR_XOR:
return kPrivateCGCompositeXOR;
case CAIRO_OPERATOR_ADD:
return kPrivateCGCompositePlusLighter;
 
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_SATURATE:
case CAIRO_OPERATOR_MULTIPLY:
case CAIRO_OPERATOR_SCREEN:
case CAIRO_OPERATOR_OVERLAY:
case CAIRO_OPERATOR_DARKEN:
case CAIRO_OPERATOR_LIGHTEN:
case CAIRO_OPERATOR_COLOR_DODGE:
case CAIRO_OPERATOR_COLOR_BURN:
case CAIRO_OPERATOR_HARD_LIGHT:
case CAIRO_OPERATOR_SOFT_LIGHT:
case CAIRO_OPERATOR_DIFFERENCE:
case CAIRO_OPERATOR_EXCLUSION:
case CAIRO_OPERATOR_HSL_HUE:
case CAIRO_OPERATOR_HSL_SATURATION:
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
default:
ASSERT_NOT_REACHED;
}
}
#endif
 
static CGBlendMode
_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_MULTIPLY:
return kCGBlendModeMultiply;
case CAIRO_OPERATOR_SCREEN:
return kCGBlendModeScreen;
case CAIRO_OPERATOR_OVERLAY:
return kCGBlendModeOverlay;
case CAIRO_OPERATOR_DARKEN:
return kCGBlendModeDarken;
case CAIRO_OPERATOR_LIGHTEN:
return kCGBlendModeLighten;
case CAIRO_OPERATOR_COLOR_DODGE:
return kCGBlendModeColorDodge;
case CAIRO_OPERATOR_COLOR_BURN:
return kCGBlendModeColorBurn;
case CAIRO_OPERATOR_HARD_LIGHT:
return kCGBlendModeHardLight;
case CAIRO_OPERATOR_SOFT_LIGHT:
return kCGBlendModeSoftLight;
case CAIRO_OPERATOR_DIFFERENCE:
return kCGBlendModeDifference;
case CAIRO_OPERATOR_EXCLUSION:
return kCGBlendModeExclusion;
case CAIRO_OPERATOR_HSL_HUE:
return kCGBlendModeHue;
case CAIRO_OPERATOR_HSL_SATURATION:
return kCGBlendModeSaturation;
case CAIRO_OPERATOR_HSL_COLOR:
return kCGBlendModeColor;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return kCGBlendModeLuminosity;
 
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
case CAIRO_OPERATOR_CLEAR:
return kCGBlendModeClear;
case CAIRO_OPERATOR_SOURCE:
return kCGBlendModeCopy;
case CAIRO_OPERATOR_OVER:
return kCGBlendModeNormal;
case CAIRO_OPERATOR_IN:
return kCGBlendModeSourceIn;
case CAIRO_OPERATOR_OUT:
return kCGBlendModeSourceOut;
case CAIRO_OPERATOR_ATOP:
return kCGBlendModeSourceAtop;
case CAIRO_OPERATOR_DEST_OVER:
return kCGBlendModeDestinationOver;
case CAIRO_OPERATOR_DEST_IN:
return kCGBlendModeDestinationIn;
case CAIRO_OPERATOR_DEST_OUT:
return kCGBlendModeDestinationOut;
case CAIRO_OPERATOR_DEST_ATOP:
return kCGBlendModeDestinationAtop;
case CAIRO_OPERATOR_XOR:
return kCGBlendModeXOR;
case CAIRO_OPERATOR_ADD:
return kCGBlendModePlusLighter;
#else
case CAIRO_OPERATOR_CLEAR:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_OUT:
case CAIRO_OPERATOR_ATOP:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_DEST_OUT:
case CAIRO_OPERATOR_DEST_ATOP:
case CAIRO_OPERATOR_XOR:
case CAIRO_OPERATOR_ADD:
#endif
 
case CAIRO_OPERATOR_DEST:
case CAIRO_OPERATOR_SATURATE:
default:
ASSERT_NOT_REACHED;
}
}
 
static cairo_int_status_t
_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
{
CGBlendMode blendmode;
 
assert (op != CAIRO_OPERATOR_DEST);
 
/* Quartz doesn't support SATURATE at all. COLOR_DODGE and
* COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
* uses the definition from the Adobe Supplement.
*/
if (op == CAIRO_OPERATOR_SATURATE ||
op == CAIRO_OPERATOR_COLOR_DODGE ||
op == CAIRO_OPERATOR_COLOR_BURN)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
if (op <= CAIRO_OPERATOR_ADD) {
PrivateCGCompositeMode compmode;
 
compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
CGContextSetCompositeOperation (context, compmode);
return CAIRO_STATUS_SUCCESS;
}
#endif
 
blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
CGContextSetBlendMode (context, blendmode);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
{
ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
 
/* When the destination has no color components, we can avoid some
* fallbacks, but we have to workaround operators which behave
* differently in Quartz. */
if (surface->base.content == CAIRO_CONTENT_ALPHA) {
assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
 
if (op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_IN ||
op == CAIRO_OPERATOR_OUT ||
op == CAIRO_OPERATOR_DEST_IN ||
op == CAIRO_OPERATOR_DEST_ATOP ||
op == CAIRO_OPERATOR_XOR)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (op == CAIRO_OPERATOR_DEST_OVER)
op = CAIRO_OPERATOR_OVER;
else if (op == CAIRO_OPERATOR_SATURATE)
op = CAIRO_OPERATOR_ADD;
else if (op == CAIRO_OPERATOR_COLOR_DODGE)
op = CAIRO_OPERATOR_OVER;
else if (op == CAIRO_OPERATOR_COLOR_BURN)
op = CAIRO_OPERATOR_OVER;
}
 
return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
}
 
static inline CGLineCap
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
{
switch (ccap) {
default:
ASSERT_NOT_REACHED;
 
case CAIRO_LINE_CAP_BUTT:
return kCGLineCapButt;
 
case CAIRO_LINE_CAP_ROUND:
return kCGLineCapRound;
 
case CAIRO_LINE_CAP_SQUARE:
return kCGLineCapSquare;
}
}
 
static inline CGLineJoin
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
{
switch (cjoin) {
default:
ASSERT_NOT_REACHED;
 
case CAIRO_LINE_JOIN_MITER:
return kCGLineJoinMiter;
 
case CAIRO_LINE_JOIN_ROUND:
return kCGLineJoinRound;
 
case CAIRO_LINE_JOIN_BEVEL:
return kCGLineJoinBevel;
}
}
 
static inline CGInterpolationQuality
_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
{
switch (filter) {
case CAIRO_FILTER_NEAREST:
case CAIRO_FILTER_FAST:
return kCGInterpolationNone;
 
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BILINEAR:
case CAIRO_FILTER_GAUSSIAN:
return kCGInterpolationDefault;
 
default:
ASSERT_NOT_REACHED;
return kCGInterpolationDefault;
}
}
 
static inline void
_cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
CGAffineTransform *dst)
{
dst->a = src->xx;
dst->b = src->yx;
dst->c = src->xy;
dst->d = src->yy;
dst->tx = src->x0;
dst->ty = src->y0;
}
 
 
/*
* Source -> Quartz setup and finish functions
*/
 
static void
ComputeGradientValue (void *info,
const cairo_quartz_float_t *in,
cairo_quartz_float_t *out)
{
double fdist = *in;
const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
unsigned int i;
 
/* Put fdist back in the 0.0..1.0 range if we're doing
* REPEAT/REFLECT
*/
if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
fdist = fdist - floor (fdist);
} else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
fdist = fmod (fabs (fdist), 2.0);
if (fdist > 1.0)
fdist = 2.0 - fdist;
}
 
for (i = 0; i < grad->n_stops; i++)
if (grad->stops[i].offset > fdist)
break;
 
if (i == 0 || i == grad->n_stops) {
if (i == grad->n_stops)
--i;
out[0] = grad->stops[i].color.red;
out[1] = grad->stops[i].color.green;
out[2] = grad->stops[i].color.blue;
out[3] = grad->stops[i].color.alpha;
} else {
cairo_quartz_float_t ax = grad->stops[i-1].offset;
cairo_quartz_float_t bx = grad->stops[i].offset - ax;
cairo_quartz_float_t bp = (fdist - ax)/bx;
cairo_quartz_float_t ap = 1.0 - bp;
 
out[0] =
grad->stops[i-1].color.red * ap +
grad->stops[i].color.red * bp;
out[1] =
grad->stops[i-1].color.green * ap +
grad->stops[i].color.green * bp;
out[2] =
grad->stops[i-1].color.blue * ap +
grad->stops[i].color.blue * bp;
out[3] =
grad->stops[i-1].color.alpha * ap +
grad->stops[i].color.alpha * bp;
}
}
 
static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
};
static const CGFunctionCallbacks gradient_callbacks = {
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
};
 
/* Quartz computes a small number of samples of the gradient color
* function. On MacOS X 10.5 it apparently computes only 1024
* samples. */
#define MAX_GRADIENT_RANGE 1024
 
static CGFunctionRef
CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents,
cairo_circle_double_t *start,
cairo_circle_double_t *end)
{
cairo_pattern_t *pat;
cairo_quartz_float_t input_value_range[2];
 
if (gradient->base.extend != CAIRO_EXTEND_NONE) {
double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
double t[2], tolerance;
 
tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
 
bounds_x1 = extents->x;
bounds_y1 = extents->y;
bounds_x2 = extents->x + extents->width;
bounds_y2 = extents->y + extents->height;
_cairo_matrix_transform_bounding_box (&gradient->base.matrix,
&bounds_x1, &bounds_y1,
&bounds_x2, &bounds_y2,
NULL);
 
_cairo_gradient_pattern_box_to_parameter (gradient,
bounds_x1, bounds_y1,
bounds_x2, bounds_y2,
tolerance,
t);
 
if (gradient->base.extend == CAIRO_EXTEND_PAD) {
t[0] = MAX (t[0], -0.5);
t[1] = MIN (t[1], 1.5);
} else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
return NULL;
 
/* set the input range for the function -- the function knows how
to map values outside of 0.0 .. 1.0 to the correct color */
input_value_range[0] = t[0];
input_value_range[1] = t[1];
} else {
input_value_range[0] = 0;
input_value_range[1] = 1;
}
 
_cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
_cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
 
if (_cairo_pattern_create_copy (&pat, &gradient->base))
return NULL;
 
return CGFunctionCreate (pat,
1,
input_value_range,
4,
gradient_output_value_ranges,
&gradient_callbacks);
}
 
/* Obtain a CGImageRef from a #cairo_surface_t * */
 
typedef struct {
cairo_surface_t *surface;
cairo_image_surface_t *image_out;
void *image_extra;
} quartz_source_image_t;
 
static void
DataProviderReleaseCallback (void *info, const void *data, size_t size)
{
quartz_source_image_t *source_img = info;
_cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
free (source_img);
}
 
static cairo_status_t
_cairo_surface_to_cgimage (cairo_surface_t *source,
cairo_rectangle_int_t *extents,
cairo_format_t format,
cairo_matrix_t *matrix,
const cairo_clip_t *clip,
CGImageRef *image_out)
{
cairo_status_t status;
quartz_source_image_t *source_img;
cairo_image_surface_t *image_surface;
 
if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
*image_out = CGImageRetain (surface->image);
return CAIRO_STATUS_SUCCESS;
}
 
if (_cairo_surface_is_quartz (source)) {
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
if (IS_EMPTY (surface)) {
*image_out = NULL;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
*image_out = CGBitmapContextCreateImage (surface->cgContext);
if (*image_out)
return CAIRO_STATUS_SUCCESS;
}
}
 
source_img = malloc (sizeof (quartz_source_image_t));
if (unlikely (source_img == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
source_img->surface = source;
 
if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
image_surface = (cairo_image_surface_t *)
cairo_image_surface_create (format, extents->width, extents->height);
if (unlikely (image_surface->base.status)) {
status = image_surface->base.status;
cairo_surface_destroy (&image_surface->base);
free (source_img);
return status;
}
 
status = _cairo_recording_surface_replay_with_clip (source,
matrix,
&image_surface->base,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (&image_surface->base);
free (source_img);
return status;
}
 
source_img->image_out = image_surface;
source_img->image_extra = NULL;
 
cairo_matrix_init_identity (matrix);
}
else {
status = _cairo_surface_acquire_source_image (source_img->surface,
&source_img->image_out,
&source_img->image_extra);
if (unlikely (status)) {
free (source_img);
return status;
}
}
 
if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
*image_out = NULL;
DataProviderReleaseCallback (source_img,
source_img->image_out->data,
source_img->image_out->height * source_img->image_out->stride);
} else {
*image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
source_img->image_out->width,
source_img->image_out->height,
source_img->image_out->stride,
source_img->image_out->data,
TRUE,
NULL,
DataProviderReleaseCallback,
source_img);
 
/* TODO: differentiate memory error and unsupported surface type */
if (unlikely (*image_out == NULL))
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
 
return status;
}
 
/* Generic #cairo_pattern_t -> CGPattern function */
 
typedef struct {
CGImageRef image;
CGRect imageBounds;
cairo_bool_t do_reflect;
} SurfacePatternDrawInfo;
 
static void
SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
{
SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
 
CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
CGContextScaleCTM (context, 1, -1);
 
CGContextDrawImage (context, info->imageBounds, info->image);
if (info->do_reflect) {
/* draw 3 more copies of the image, flipped.
* DrawImage draws the image according to the current Y-direction into the rectangle given
* (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
* of the base image position, and the Y axis is extending upwards.
*/
 
/* Make the y axis extend downwards, and draw a flipped image below */
CGContextScaleCTM (context, 1, -1);
CGContextDrawImage (context, info->imageBounds, info->image);
 
/* Shift over to the right, and flip vertically (translation is 2x,
* since we'll be flipping and thus rendering the rectangle "backwards"
*/
CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
CGContextScaleCTM (context, -1, 1);
CGContextDrawImage (context, info->imageBounds, info->image);
 
/* Then unflip the Y-axis again, and draw the image above the point. */
CGContextScaleCTM (context, 1, -1);
CGContextDrawImage (context, info->imageBounds, info->image);
}
}
 
static void
SurfacePatternReleaseInfoFunc (void *ainfo)
{
SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
 
CGImageRelease (info->image);
free (info);
}
 
static cairo_int_status_t
_cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
const cairo_pattern_t *apattern,
const cairo_clip_t *clip,
CGPatternRef *cgpat)
{
cairo_surface_pattern_t *spattern;
cairo_surface_t *pat_surf;
cairo_rectangle_int_t extents;
cairo_format_t format = _cairo_format_from_content (dest->base.content);
 
CGImageRef image;
CGRect pbounds;
CGAffineTransform ptransform, stransform;
CGPatternCallbacks cb = { 0,
SurfacePatternDrawFunc,
SurfacePatternReleaseInfoFunc };
SurfacePatternDrawInfo *info;
cairo_quartz_float_t rw, rh;
cairo_status_t status;
cairo_bool_t is_bounded;
 
cairo_matrix_t m;
 
/* SURFACE is the only type we'll handle here */
assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
 
spattern = (cairo_surface_pattern_t *) apattern;
pat_surf = spattern->surface;
 
if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
}
else
_cairo_surface_get_extents (&dest->base, &extents);
 
m = spattern->base.matrix;
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
&m, clip, &image);
if (unlikely (status))
return status;
 
info = malloc (sizeof (SurfacePatternDrawInfo));
if (unlikely (!info))
return CAIRO_STATUS_NO_MEMORY;
 
/* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
* that the data will stick around for this image when the printer gets to it.
* Otherwise, the underlying data store may disappear from under us!
*
* _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
* since the Quartz surfaces have a higher chance of sticking around. If the
* source is a quartz image surface, then it's set up to retain a ref to the
* image surface that it's backed by.
*/
info->image = image;
info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
info->do_reflect = FALSE;
 
pbounds.origin.x = 0;
pbounds.origin.y = 0;
 
if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
pbounds.size.width = 2.0 * extents.width;
pbounds.size.height = 2.0 * extents.height;
info->do_reflect = TRUE;
} else {
pbounds.size.width = extents.width;
pbounds.size.height = extents.height;
}
rw = pbounds.size.width;
rh = pbounds.size.height;
 
cairo_matrix_invert (&m);
_cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
 
/* The pattern matrix is relative to the bottom left, again; the
* incoming cairo pattern matrix is relative to the upper left.
* So we take the pattern matrix and the original context matrix,
* which gives us the correct base translation/y flip.
*/
ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
 
#ifdef QUARTZ_DEBUG
ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
#endif
 
*cgpat = CGPatternCreate (info,
pbounds,
ptransform,
rw, rh,
kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
TRUE,
&cb);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* State used during a drawing operation. */
typedef struct {
/* The destination of the mask */
CGContextRef cgMaskContext;
 
/* The destination of the drawing of the source */
CGContextRef cgDrawContext;
 
/* The filter to be used when drawing the source */
CGInterpolationQuality filter;
 
/* Action type */
cairo_quartz_action_t action;
 
/* Destination rect */
CGRect rect;
 
/* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
CGAffineTransform transform;
 
/* Used with DO_IMAGE and DO_TILED_IMAGE */
CGImageRef image;
 
/* Used with DO_SHADING */
CGShadingRef shading;
 
/* Temporary destination for unbounded operations */
CGLayerRef layer;
CGRect clipRect;
} cairo_quartz_drawing_state_t;
 
/*
Quartz does not support repeating radients. We handle repeating gradients
by manually extending the gradient and repeating color stops. We need to
minimize the number of repetitions since Quartz seems to sample our color
function across the entire range, even if part of that range is not needed
for the visible area of the gradient, and it samples with some fixed resolution,
so if the gradient range is too large it samples with very low resolution and
the gradient is very coarse. _cairo_quartz_create_gradient_function computes
the number of repetitions needed based on the extents.
*/
static cairo_int_status_t
_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents)
{
cairo_matrix_t mat;
cairo_circle_double_t start, end;
CGFunctionRef gradFunc;
CGColorSpaceRef rgb;
bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
 
assert (gradient->n_stops > 0);
 
mat = gradient->base.matrix;
cairo_matrix_invert (&mat);
_cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
 
gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
&start, &end);
 
if (unlikely (gradFunc == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
rgb = CGColorSpaceCreateDeviceRGB ();
 
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
state->shading = CGShadingCreateAxial (rgb,
CGPointMake (start.center.x,
start.center.y),
CGPointMake (end.center.x,
end.center.y),
gradFunc,
extend, extend);
} else {
state->shading = CGShadingCreateRadial (rgb,
CGPointMake (start.center.x,
start.center.y),
MAX (start.radius, 0),
CGPointMake (end.center.x,
end.center.y),
MAX (end.radius, 0),
gradFunc,
extend, extend);
}
 
CGColorSpaceRelease (rgb);
CGFunctionRelease (gradFunc);
 
state->action = DO_SHADING;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
cairo_composite_rectangles_t *composite)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
const cairo_pattern_t *source = &composite->source_pattern.base;
const cairo_clip_t *clip = composite->clip;
cairo_bool_t needs_temp;
cairo_status_t status;
cairo_format_t format = _cairo_format_from_content (composite->surface->content);
 
state->layer = NULL;
state->image = NULL;
state->shading = NULL;
state->cgDrawContext = NULL;
state->cgMaskContext = NULL;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
return status;
 
status = _cairo_quartz_surface_set_cairo_operator (surface, op);
if (unlikely (status))
return status;
 
/* Save before we change the pattern, colorspace, etc. so that
* we can restore and make sure that quartz releases our
* pattern (which may be stack allocated)
*/
 
CGContextSaveGState (surface->cgContext);
state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
state->clipRect = CGRectIntegral (state->clipRect);
state->rect = state->clipRect;
 
state->cgMaskContext = surface->cgContext;
state->cgDrawContext = state->cgMaskContext;
 
state->filter = _cairo_quartz_filter_to_quartz (source->filter);
 
if (op == CAIRO_OPERATOR_CLEAR) {
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
 
state->action = DO_DIRECT;
return CAIRO_STATUS_SUCCESS;
}
 
/*
* To implement mask unbounded operations Quartz needs a temporary
* surface which will be composited entirely (ignoring the mask).
* To implement source unbounded operations Quartz needs a
* temporary surface which allows extending the source to a size
* covering the whole mask, but there are some optimization
* opportunities:
*
* - CLEAR completely ignores the source, thus we can just use a
* solid color fill.
*
* - SOURCE can be implemented by drawing the source and clearing
* outside of the source as long as the two regions have no
* intersection. This happens when the source is a pixel-aligned
* rectangle. If the source is at least as big as the
* intersection between the clip rectangle and the mask
* rectangle, no clear operation is needed.
*/
needs_temp = ! _cairo_operator_bounded_by_mask (op);
 
if (needs_temp) {
state->layer = CGLayerCreateWithContext (surface->cgContext,
state->clipRect.size,
NULL);
state->cgDrawContext = CGLayerGetContext (state->layer);
state->cgMaskContext = state->cgDrawContext;
CGContextTranslateCTM (state->cgDrawContext,
-state->clipRect.origin.x,
-state->clipRect.origin.y);
}
 
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
 
CGContextSetRGBStrokeColor (state->cgDrawContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
CGContextSetRGBFillColor (state->cgDrawContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
 
state->action = DO_DIRECT;
return CAIRO_STATUS_SUCCESS;
}
 
if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
source->type == CAIRO_PATTERN_TYPE_RADIAL)
{
const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
cairo_rectangle_int_t extents;
 
extents = surface->virtual_extents;
extents.x -= surface->base.device_transform.x0;
extents.y -= surface->base.device_transform.y0;
_cairo_rectangle_union (&extents, &surface->extents);
 
return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
}
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
{
const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
cairo_surface_t *pat_surf = spat->surface;
CGImageRef img;
cairo_matrix_t m = spat->base.matrix;
cairo_rectangle_int_t extents;
CGAffineTransform xform;
CGRect srcRect;
cairo_fixed_t fw, fh;
cairo_bool_t is_bounded;
 
_cairo_surface_get_extents (composite->surface, &extents);
status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
&m, clip, &img);
if (unlikely (status))
return status;
 
state->image = img;
 
if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
m.x0 = -ceil (m.x0 - 0.5);
m.y0 = -ceil (m.y0 - 0.5);
} else {
cairo_matrix_invert (&m);
}
 
_cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
 
if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
}
 
srcRect = CGRectMake (0, 0, extents.width, extents.height);
 
if (source->extend == CAIRO_EXTEND_NONE) {
int x, y;
if (op == CAIRO_OPERATOR_SOURCE &&
(pat_surf->content == CAIRO_CONTENT_ALPHA ||
! _cairo_matrix_is_integer_translation (&m, &x, &y)))
{
state->layer = CGLayerCreateWithContext (surface->cgContext,
state->clipRect.size,
NULL);
state->cgDrawContext = CGLayerGetContext (state->layer);
CGContextTranslateCTM (state->cgDrawContext,
-state->clipRect.origin.x,
-state->clipRect.origin.y);
}
 
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
 
state->rect = srcRect;
state->action = DO_IMAGE;
return CAIRO_STATUS_SUCCESS;
}
 
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
 
/* Quartz seems to tile images at pixel-aligned regions only -- this
* leads to seams if the image doesn't end up scaling to fill the
* space exactly. The CGPattern tiling approach doesn't have this
* problem. Check if we're going to fill up the space (within some
* epsilon), and if not, fall back to the CGPattern type.
*/
 
xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
state->transform);
 
srcRect = CGRectApplyAffineTransform (srcRect, xform);
 
fw = _cairo_fixed_from_double (srcRect.size.width);
fh = _cairo_fixed_from_double (srcRect.size.height);
 
if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
(fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
{
/* We're good to use DrawTiledImage, but ensure that
* the math works out */
 
srcRect.size.width = round (srcRect.size.width);
srcRect.size.height = round (srcRect.size.height);
 
xform = CGAffineTransformInvert (xform);
 
srcRect = CGRectApplyAffineTransform (srcRect, xform);
 
state->rect = srcRect;
state->action = DO_TILED_IMAGE;
return CAIRO_STATUS_SUCCESS;
}
 
/* Fall through to generic SURFACE case */
}
 
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_quartz_float_t patternAlpha = 1.0f;
CGColorSpaceRef patternSpace;
CGPatternRef pattern = NULL;
cairo_int_status_t status;
 
status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
if (unlikely (status))
return status;
 
patternSpace = CGColorSpaceCreatePattern (NULL);
CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
CGColorSpaceRelease (patternSpace);
 
/* Quartz likes to munge the pattern phase (as yet unexplained
* why); force it to 0,0 as we've already baked in the correct
* pattern translation into the pattern matrix
*/
CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
 
CGPatternRelease (pattern);
 
state->action = DO_DIRECT;
return CAIRO_STATUS_SUCCESS;
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static void
_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
cairo_composite_rectangles_t *extents)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
 
if (state->layer) {
CGContextDrawLayerInRect (surface->cgContext,
state->clipRect,
state->layer);
CGLayerRelease (state->layer);
}
 
if (state->cgMaskContext)
CGContextRestoreGState (surface->cgContext);
 
if (state->image)
CGImageRelease (state->image);
 
if (state->shading)
CGShadingRelease (state->shading);
}
 
static void
_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
cairo_operator_t op)
{
CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
 
if (state->action == DO_DIRECT) {
CGContextFillRect (state->cgDrawContext, state->rect);
return;
}
 
CGContextConcatCTM (state->cgDrawContext, state->transform);
 
if (state->action == DO_SHADING) {
CGContextDrawShading (state->cgDrawContext, state->shading);
return;
}
 
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
CGContextScaleCTM (state->cgDrawContext, 1, -1);
 
if (state->action == DO_IMAGE) {
CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
if (op == CAIRO_OPERATOR_SOURCE &&
state->cgDrawContext == state->cgMaskContext)
{
CGContextBeginPath (state->cgDrawContext);
CGContextAddRect (state->cgDrawContext, state->rect);
 
CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
CGContextScaleCTM (state->cgDrawContext, 1, -1);
CGContextConcatCTM (state->cgDrawContext,
CGAffineTransformInvert (state->transform));
 
CGContextAddRect (state->cgDrawContext, state->clipRect);
 
CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
CGContextEOFillPath (state->cgDrawContext);
}
} else {
CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
}
}
 
static cairo_image_surface_t *
_cairo_quartz_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
unsigned int stride, bitinfo, bpp, color_comps;
CGColorSpaceRef colorspace;
void *imageData;
cairo_format_t format;
 
if (surface->imageSurfaceEquiv)
return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
 
if (IS_EMPTY (surface))
return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
 
if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
 
// let's hope they don't add YUV under us
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
 
/* XXX TODO: We can handle many more data formats by
* converting to pixman_format_t */
 
if (bpp == 32 && color_comps == 3 &&
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
{
format = CAIRO_FORMAT_ARGB32;
}
else if (bpp == 32 && color_comps == 3 &&
(bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
(bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
{
format = CAIRO_FORMAT_RGB24;
}
else if (bpp == 8 && color_comps == 1)
{
format = CAIRO_FORMAT_A1;
}
else
{
return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
imageData = CGBitmapContextGetData (surface->cgContext);
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
 
return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
format,
extents->width,
extents->height,
stride);
}
 
static cairo_int_status_t
_cairo_quartz_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
if (surface->imageSurfaceEquiv)
return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
 
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
 
/*
* Cairo surface backend implementations
*/
 
static cairo_status_t
_cairo_quartz_surface_finish (void *abstract_surface)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
 
if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
 
/* Restore our saved gstate that we use to reset clipping */
CGContextRestoreGState (surface->cgContext);
_cairo_surface_clipper_reset (&surface->clipper);
 
CGContextRelease (surface->cgContext);
 
surface->cgContext = NULL;
 
if (surface->imageSurfaceEquiv) {
cairo_surface_destroy (surface->imageSurfaceEquiv);
surface->imageSurfaceEquiv = NULL;
}
 
free (surface->imageData);
surface->imageData = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
//ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
 
*image_extra = NULL;
 
*image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
if (unlikely (cairo_surface_status(&(*image_out)->base))) {
cairo_surface_destroy (&(*image_out)->base);
*image_out = NULL;
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_quartz_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
_cairo_quartz_surface_unmap_image (abstract_surface, image);
}
 
static cairo_surface_t *
_cairo_quartz_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_quartz_surface_t *surface, *similar_quartz;
cairo_surface_t *similar;
cairo_format_t format;
 
if (content == CAIRO_CONTENT_COLOR_ALPHA)
format = CAIRO_FORMAT_ARGB32;
else if (content == CAIRO_CONTENT_COLOR)
format = CAIRO_FORMAT_RGB24;
else if (content == CAIRO_CONTENT_ALPHA)
format = CAIRO_FORMAT_A8;
else
return NULL;
 
// verify width and height of surface
if (!_cairo_quartz_verify_surface_size (width, height)) {
return _cairo_surface_create_in_error (_cairo_error
(CAIRO_STATUS_INVALID_SIZE));
}
 
similar = cairo_quartz_surface_create (format, width, height);
if (unlikely (similar->status))
return similar;
 
surface = (cairo_quartz_surface_t *) abstract_surface;
similar_quartz = (cairo_quartz_surface_t *) similar;
similar_quartz->virtual_extents = surface->virtual_extents;
 
return similar;
}
 
static cairo_bool_t
_cairo_quartz_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
 
*extents = surface->extents;
return TRUE;
}
 
static cairo_int_status_t
_cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
cairo_quartz_drawing_state_t state;
cairo_int_status_t rv;
 
ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
extents->surface, extents->op, extents->source_pattern.base.type));
 
rv = _cairo_quartz_setup_state (&state, extents);
if (unlikely (rv))
goto BAIL;
 
_cairo_quartz_draw_source (&state, extents->op);
 
BAIL:
_cairo_quartz_teardown_state (&state, extents);
 
ND ((stderr, "-- paint\n"));
return rv;
}
 
static cairo_int_status_t
_cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
cairo_surface_t *mask_surf,
const cairo_matrix_t *mask_mat,
CGInterpolationQuality filter)
{
CGRect rect;
CGImageRef img;
cairo_status_t status;
CGAffineTransform mask_matrix;
cairo_quartz_drawing_state_t state;
cairo_format_t format = _cairo_format_from_content (extents->surface->content);
cairo_rectangle_int_t dest_extents;
cairo_matrix_t m = *mask_mat;
 
_cairo_surface_get_extents (extents->surface, &dest_extents);
status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
&m, extents->clip, &img);
if (unlikely (status))
return status;
 
status = _cairo_quartz_setup_state (&state, extents);
if (unlikely (status))
goto BAIL;
 
rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
_cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
 
/* ClipToMask is essentially drawing an image, so we need to flip the CTM
* to get the image to appear oriented the right way */
CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
 
state.filter = filter;
 
CGContextSetInterpolationQuality (state.cgMaskContext, filter);
CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
 
CGContextClipToMask (state.cgMaskContext, rect, img);
 
CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
CGContextConcatCTM (state.cgMaskContext, mask_matrix);
 
_cairo_quartz_draw_source (&state, extents->op);
 
BAIL:
_cairo_quartz_teardown_state (&state, extents);
 
CGImageRelease (img);
 
return status;
}
 
static cairo_int_status_t
_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
cairo_composite_rectangles_t *extents)
{
cairo_quartz_drawing_state_t state;
double alpha = extents->mask_pattern.solid.color.alpha;
cairo_status_t status;
 
status = _cairo_quartz_setup_state (&state, extents);
if (unlikely (status))
return status;
 
CGContextSetAlpha (surface->cgContext, alpha);
_cairo_quartz_draw_source (&state, extents->op);
 
_cairo_quartz_teardown_state (&state, extents);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
const cairo_pattern_t *source = &extents->source_pattern.base;
const cairo_pattern_t *mask = &extents->mask_pattern.base;
cairo_surface_t *mask_surf;
cairo_matrix_t matrix;
cairo_status_t status;
cairo_bool_t need_temp;
CGInterpolationQuality filter;
 
ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
extents->surface, extents->op, extents->source_pattern.base.type,
extents->mask_pattern.base.type));
 
if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
return _cairo_quartz_cg_mask_with_solid (surface, extents);
 
need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
mask->extend != CAIRO_EXTEND_NONE);
 
filter = _cairo_quartz_filter_to_quartz (source->filter);
 
if (! need_temp) {
mask_surf = extents->mask_pattern.surface.surface;
 
/* When an opaque surface used as a mask in Quartz, its
* luminosity is used as the alpha value, so we con only use
* surfaces with alpha without creating a temporary mask. */
need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
}
 
if (! need_temp) {
CGInterpolationQuality mask_filter;
cairo_bool_t simple_transform;
 
matrix = mask->matrix;
 
mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
if (mask_filter == kCGInterpolationNone) {
simple_transform = _cairo_matrix_is_translation (&matrix);
if (simple_transform) {
matrix.x0 = ceil (matrix.x0 - 0.5);
matrix.y0 = ceil (matrix.y0 - 0.5);
}
} else {
simple_transform = _cairo_matrix_is_integer_translation (&matrix,
NULL,
NULL);
}
 
/* Quartz only allows one interpolation to be set for mask and
* source, so we can skip the temp surface only if the source
* filtering makes the mask look correct. */
if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
need_temp = ! (simple_transform || filter == mask_filter);
else
filter = mask_filter;
}
 
if (need_temp) {
/* Render the mask to a surface */
mask_surf = _cairo_quartz_surface_create_similar (surface,
CAIRO_CONTENT_ALPHA,
surface->extents.width,
surface->extents.height);
status = mask_surf->status;
if (unlikely (status))
goto BAIL;
 
/* mask_surf is clear, so use OVER instead of SOURCE to avoid a
* temporary layer or fallback to cairo-image. */
status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
if (unlikely (status))
goto BAIL;
 
cairo_matrix_init_identity (&matrix);
}
 
status = _cairo_quartz_cg_mask_with_surface (extents,
mask_surf, &matrix, filter);
 
BAIL:
 
if (need_temp)
cairo_surface_destroy (mask_surf);
 
return status;
}
 
static cairo_int_status_t
_cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
 
ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
extents->surface, extents->op, extents->source_pattern.base.type));
 
rv = _cairo_quartz_setup_state (&state, extents);
if (unlikely (rv))
goto BAIL;
 
CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
 
_cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
 
if (state.action == DO_DIRECT) {
assert (state.cgDrawContext == state.cgMaskContext);
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextFillPath (state.cgMaskContext);
else
CGContextEOFillPath (state.cgMaskContext);
} else {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextClip (state.cgMaskContext);
else
CGContextEOClip (state.cgMaskContext);
 
_cairo_quartz_draw_source (&state, extents->op);
}
 
BAIL:
_cairo_quartz_teardown_state (&state, extents);
 
ND ((stderr, "-- fill\n"));
return rv;
}
 
static cairo_int_status_t
_cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
CGAffineTransform strokeTransform, invStrokeTransform;
 
ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
extents->surface, extents->op, extents->source_pattern.base.type));
 
rv = _cairo_quartz_setup_state (&state, extents);
if (unlikely (rv))
goto BAIL;
 
// Turning antialiasing off used to cause misrendering with
// single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
// That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
CGContextSetLineWidth (state.cgMaskContext, style->line_width);
CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
 
if (style->dash && style->num_dashes) {
cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
cairo_quartz_float_t *fdash = sdash;
unsigned int max_dashes = style->num_dashes;
unsigned int k;
 
if (style->num_dashes%2)
max_dashes *= 2;
if (max_dashes > ARRAY_LENGTH (sdash))
fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
if (unlikely (fdash == NULL)) {
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
 
for (k = 0; k < max_dashes; k++)
fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
 
CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
if (fdash != sdash)
free (fdash);
} else
CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
 
_cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
 
_cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
CGContextConcatCTM (state.cgMaskContext, strokeTransform);
 
if (state.action == DO_DIRECT) {
assert (state.cgDrawContext == state.cgMaskContext);
CGContextStrokePath (state.cgMaskContext);
} else {
CGContextReplacePathWithStrokedPath (state.cgMaskContext);
CGContextClip (state.cgMaskContext);
 
_cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
 
_cairo_quartz_draw_source (&state, extents->op);
}
 
BAIL:
_cairo_quartz_teardown_state (&state, extents);
 
ND ((stderr, "-- stroke\n"));
return rv;
}
 
#if CAIRO_HAS_QUARTZ_FONT
static cairo_int_status_t
_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
CGAffineTransform textTransform, invTextTransform;
CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGGlyph *cg_glyphs = &glyphs_static[0];
CGSize *cg_advances = &cg_advances_static[0];
COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
 
cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
cairo_quartz_float_t xprev, yprev;
int i;
CGFontRef cgfref = NULL;
 
cairo_bool_t didForceFontSmoothing = FALSE;
 
if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
rv = _cairo_quartz_setup_state (&state, extents);
if (unlikely (rv))
goto BAIL;
 
if (state.action == DO_DIRECT) {
assert (state.cgDrawContext == state.cgMaskContext);
CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
} else {
CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
}
 
/* this doesn't addref */
cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
CGContextSetFont (state.cgMaskContext, cgfref);
CGContextSetFontSize (state.cgMaskContext, 1.0);
 
switch (scaled_font->options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
!CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
{
didForceFontSmoothing = TRUE;
CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
}
break;
case CAIRO_ANTIALIAS_NONE:
CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_FAST:
CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
/* Don't do anything */
break;
}
 
if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
if (unlikely (cg_glyphs == NULL)) {
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
 
cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
}
 
/* scale(1,-1) * scaled_font->scale */
textTransform = CGAffineTransformMake (scaled_font->scale.xx,
scaled_font->scale.yx,
-scaled_font->scale.xy,
-scaled_font->scale.yy,
0.0, 0.0);
 
/* scaled_font->scale_inverse * scale(1,-1) */
invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
-scaled_font->scale_inverse.yx,
scaled_font->scale_inverse.xy,
-scaled_font->scale_inverse.yy,
0.0, 0.0);
 
CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
 
/* Convert our glyph positions to glyph advances. We need n-1 advances,
* since the advance at index 0 is applied after glyph 0. */
xprev = glyphs[0].x;
yprev = glyphs[0].y;
 
cg_glyphs[0] = glyphs[0].index;
 
for (i = 1; i < num_glyphs; i++) {
cairo_quartz_float_t xf = glyphs[i].x;
cairo_quartz_float_t yf = glyphs[i].y;
cg_glyphs[i] = glyphs[i].index;
cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
xprev = xf;
yprev = yf;
}
 
/* Translate to the first glyph's position before drawing */
CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
CGContextConcatCTM (state.cgMaskContext, textTransform);
 
CGContextShowGlyphsWithAdvances (state.cgMaskContext,
cg_glyphs,
cg_advances,
num_glyphs);
 
CGContextConcatCTM (state.cgMaskContext, invTextTransform);
CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
 
if (state.action != DO_DIRECT)
_cairo_quartz_draw_source (&state, extents->op);
 
BAIL:
if (didForceFontSmoothing)
CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
 
_cairo_quartz_teardown_state (&state, extents);
 
if (cg_glyphs != glyphs_static)
free (cg_glyphs);
 
return rv;
}
#endif /* CAIRO_HAS_QUARTZ_FONT */
 
static const cairo_compositor_t _cairo_quartz_cg_compositor = {
&_cairo_fallback_compositor,
 
_cairo_quartz_cg_paint,
_cairo_quartz_cg_mask,
_cairo_quartz_cg_stroke,
_cairo_quartz_cg_fill,
#if CAIRO_HAS_QUARTZ_FONT
_cairo_quartz_cg_glyphs,
#else
NULL,
#endif
};
 
static cairo_int_status_t
_cairo_quartz_surface_paint (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
surface, op, source, clip);
}
 
static cairo_int_status_t
_cairo_quartz_surface_mask (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
surface, op, source, mask,
clip);
}
 
static cairo_int_status_t
_cairo_quartz_surface_fill (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
surface, op, source, path,
fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_quartz_surface_stroke (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
surface, op, source, path,
style, ctm,ctm_inverse,
tolerance, antialias, clip);
}
 
static cairo_int_status_t
_cairo_quartz_surface_glyphs (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
surface, op, source,
glyphs, num_glyphs, scaled_font,
clip);
}
 
static cairo_status_t
_cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_quartz_surface_t *surface =
cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
 
ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
 
if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
 
if (path == NULL) {
/* If we're being asked to reset the clip, we can only do it
* by restoring the gstate to our previous saved one, and
* saving it again.
*
* Note that this assumes that ALL quartz surface creation
* functions will do a SaveGState first; we do this in create_internal.
*/
CGContextRestoreGState (surface->cgContext);
CGContextSaveGState (surface->cgContext);
} else {
CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
 
_cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
 
if (fill_rule == CAIRO_FILL_RULE_WINDING)
CGContextClip (surface->cgContext);
else
CGContextEOClip (surface->cgContext);
}
 
ND ((stderr, "-- intersect_clip_path\n"));
 
return CAIRO_STATUS_SUCCESS;
}
 
// XXXtodo implement show_page; need to figure out how to handle begin/end
 
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ,
_cairo_quartz_surface_finish,
 
_cairo_default_context_create,
 
_cairo_quartz_surface_create_similar,
NULL, /* similar image */
_cairo_quartz_surface_map_to_image,
_cairo_quartz_surface_unmap_image,
 
_cairo_surface_default_source,
_cairo_quartz_surface_acquire_source_image,
_cairo_quartz_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_quartz_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
_cairo_quartz_surface_paint,
_cairo_quartz_surface_mask,
_cairo_quartz_surface_stroke,
_cairo_quartz_surface_fill,
NULL, /* fill-stroke */
_cairo_quartz_surface_glyphs,
};
 
cairo_quartz_surface_t *
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
cairo_content_t content,
unsigned int width,
unsigned int height)
{
cairo_quartz_surface_t *surface;
 
quartz_ensure_symbols ();
 
/* Init the base surface */
surface = malloc (sizeof (cairo_quartz_surface_t));
if (unlikely (surface == NULL))
return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
memset (surface, 0, sizeof (cairo_quartz_surface_t));
 
_cairo_surface_init (&surface->base,
&cairo_quartz_surface_backend,
NULL, /* device */
content);
 
_cairo_surface_clipper_init (&surface->clipper,
_cairo_quartz_surface_clipper_intersect_clip_path);
 
/* Save our extents */
surface->extents.x = surface->extents.y = 0;
surface->extents.width = width;
surface->extents.height = height;
surface->virtual_extents = surface->extents;
 
if (IS_EMPTY (surface)) {
surface->cgContext = NULL;
surface->cgContextBaseCTM = CGAffineTransformIdentity;
surface->imageData = NULL;
surface->base.is_clear = TRUE;
return surface;
}
 
/* Save so we can always get back to a known-good CGContext -- this is
* required for proper behaviour of intersect_clip_path(NULL)
*/
CGContextSaveGState (cgContext);
 
surface->cgContext = cgContext;
surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
 
surface->imageData = NULL;
surface->imageSurfaceEquiv = NULL;
 
return surface;
}
 
/**
* cairo_quartz_surface_create_for_cg_context:
* @cgContext: the existing CGContext for which to create the surface
* @width: width of the surface, in pixels
* @height: height of the surface, in pixels
*
* Creates a Quartz surface that wraps the given CGContext. The
* CGContext is assumed to be in the standard Cairo coordinate space
* (that is, with the origin at the upper left and the Y axis
* increasing downward). If the CGContext is in the Quartz coordinate
* space (with the origin at the bottom left), then it should be
* flipped before this function is called. The flip can be accomplished
* using a translate and a scale; for example:
*
* <informalexample><programlisting>
* CGContextTranslateCTM (cgContext, 0.0, height);
* CGContextScaleCTM (cgContext, 1.0, -1.0);
* </programlisting></informalexample>
*
* All Cairo operations are implemented in terms of Quartz operations,
* as long as Quartz-compatible elements are used (such as Quartz fonts).
*
* Return value: the newly created Cairo surface.
*
* Since: 1.6
**/
 
cairo_surface_t *
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
unsigned int width,
unsigned int height)
{
cairo_quartz_surface_t *surf;
 
surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
width, height);
if (likely (!surf->base.status))
CGContextRetain (cgContext);
 
return &surf->base;
}
 
/**
* cairo_quartz_surface_create:
* @format: format of pixels in the surface to create
* @width: width of the surface, in pixels
* @height: height of the surface, in pixels
*
* Creates a Quartz surface backed by a CGBitmap. The surface is
* created using the Device RGB (or Device Gray, for A8) color space.
* All Cairo operations, including those that require software
* rendering, will succeed on this surface.
*
* Return value: the newly created surface.
*
* Since: 1.6
**/
cairo_surface_t *
cairo_quartz_surface_create (cairo_format_t format,
unsigned int width,
unsigned int height)
{
cairo_quartz_surface_t *surf;
CGContextRef cgc;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo;
void *imageData;
int stride;
int bitsPerComponent;
 
if (!_cairo_quartz_verify_surface_size (width, height))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
if (width == 0 || height == 0) {
return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
width, height)->base;
}
 
if (format == CAIRO_FORMAT_ARGB32 ||
format == CAIRO_FORMAT_RGB24)
{
cgColorspace = CGColorSpaceCreateDeviceRGB ();
bitinfo = kCGBitmapByteOrder32Host;
if (format == CAIRO_FORMAT_ARGB32)
bitinfo |= kCGImageAlphaPremultipliedFirst;
else
bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
stride = width * 4;
} else if (format == CAIRO_FORMAT_A8) {
cgColorspace = NULL;
stride = width;
bitinfo = kCGImageAlphaOnly;
bitsPerComponent = 8;
} else if (format == CAIRO_FORMAT_A1) {
/* I don't think we can usefully support this, as defined by
* cairo_format_t -- these are 1-bit pixels stored in 32-bit
* quantities.
*/
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
} else {
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
}
 
/* The Apple docs say that for best performance, the stride and the data
* pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
* so we don't have to anything special on allocation.
*/
stride = (stride + 15) & ~15;
 
imageData = _cairo_malloc_ab (height, stride);
if (unlikely (!imageData)) {
CGColorSpaceRelease (cgColorspace);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
/* zero the memory to match the image surface behaviour */
memset (imageData, 0, height * stride);
 
cgc = CGBitmapContextCreate (imageData,
width,
height,
bitsPerComponent,
stride,
cgColorspace,
bitinfo);
CGColorSpaceRelease (cgColorspace);
 
if (!cgc) {
free (imageData);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
/* flip the Y axis */
CGContextTranslateCTM (cgc, 0.0, height);
CGContextScaleCTM (cgc, 1.0, -1.0);
 
surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
width, height);
if (surf->base.status) {
CGContextRelease (cgc);
free (imageData);
// create_internal will have set an error
return &surf->base;
}
 
surf->base.is_clear = TRUE;
 
surf->imageData = imageData;
surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
 
return &surf->base;
}
 
/**
* cairo_quartz_surface_get_cg_context:
* @surface: the Cairo Quartz surface
*
* Returns the CGContextRef that the given Quartz surface is backed
* by.
*
* A call to cairo_surface_flush() is required before using the
* CGContextRef to ensure that all pending drawing operations are
* finished and to restore any temporary modification cairo has made
* to its state. A call to cairo_surface_mark_dirty() is required
* after the state or the content of the CGContextRef has been
* modified.
*
* Return value: the CGContextRef for the given surface.
*
* Since: 1.6
**/
CGContextRef
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
{
if (surface && _cairo_surface_is_quartz (surface)) {
cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
return quartz->cgContext;
} else
return NULL;
}
 
static cairo_bool_t
_cairo_surface_is_quartz (const cairo_surface_t *surface)
{
return surface->backend == &cairo_quartz_surface_backend;
}
 
/* Debug stuff */
 
#ifdef QUARTZ_DEBUG
 
#include <Movies.h>
 
void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
{
Handle dataRef = NULL;
OSType dataRefType;
CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
 
GraphicsExportComponent grex = 0;
unsigned long sizeWritten;
 
ComponentResult result;
 
// create the data reference
result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
0, &dataRef, &dataRefType);
 
if (NULL != dataRef && noErr == result) {
// get the PNG exporter
result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
&grex);
 
if (grex) {
// tell the exporter where to find its source image
result = GraphicsExportSetInputCGImage (grex, inImageRef);
 
if (noErr == result) {
// tell the exporter where to save the exporter image
result = GraphicsExportSetOutputDataReference (grex, dataRef,
dataRefType);
 
if (noErr == result) {
// write the PNG file
result = GraphicsExportDoExport (grex, &sizeWritten);
}
}
 
// remember to close the component
CloseComponent (grex);
}
 
// remember to dispose of the data reference handle
DisposeHandle (dataRef);
}
}
 
void
quartz_image_to_png (CGImageRef imgref, char *dest)
{
static int sctr = 0;
char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
 
if (dest == NULL) {
fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
sctr++;
dest = sptr;
}
 
ExportCGImageToPNGFile (imgref, dest);
}
 
void
quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
{
static int sctr = 0;
char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
 
if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
return;
}
 
if (dest == NULL) {
fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
sctr++;
dest = sptr;
}
 
CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
if (imgref == NULL) {
fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
return;
}
 
ExportCGImageToPNGFile (imgref, dest);
 
CGImageRelease (imgref);
}
 
#endif /* QUARTZ_DEBUG */
/programs/develop/libraries/cairo/src/cairo-quartz.h
66,10 → 66,8
cairo_public cairo_font_face_t *
cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
 
#ifndef __LP64__
cairo_public cairo_font_face_t *
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
#endif
 
#endif /* CAIRO_HAS_QUARTZ_FONT */
 
/programs/develop/libraries/cairo/src/cairo-raster-source-pattern.c
0,0 → 1,432
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-error-private.h"
#include "cairo-pattern-private.h"
 
/**
* SECTION:cairo-raster-source
* @Title: Raster Sources
* @Short_Description: Supplying arbitrary image data
* @See_Also: #cairo_pattern_t
*
* The raster source provides the ability to supply arbitrary pixel data
* whilst rendering. The pixels are queried at the time of rasterisation
* by means of user callback functions, allowing for the ultimate
* flexibility. For example, in handling compressed image sources, you
* may keep a MRU cache of decompressed images and decompress sources on the
* fly and discard old ones to conserve memory.
*
* For the raster source to be effective, you must at least specify
* the acquire and release callbacks which are used to retrieve the pixel
* data for the region of interest and demark when it can be freed afterwards.
* Other callbacks are provided for when the pattern is copied temporarily
* during rasterisation, or more permanently as a snapshot in order to keep
* the pixel data available for printing.
*
* Since: 1.12
**/
 
cairo_surface_t *
_cairo_raster_source_pattern_acquire (const cairo_pattern_t *abstract_pattern,
cairo_surface_t *target,
const cairo_rectangle_int_t *extents)
{
cairo_raster_source_pattern_t *pattern =
(cairo_raster_source_pattern_t *) abstract_pattern;
 
if (pattern->acquire == NULL)
return NULL;
 
if (extents == NULL)
extents = &pattern->extents;
 
return pattern->acquire (&pattern->base, pattern->user_data,
target, extents);
}
 
void
_cairo_raster_source_pattern_release (const cairo_pattern_t *abstract_pattern,
cairo_surface_t *surface)
{
cairo_raster_source_pattern_t *pattern =
(cairo_raster_source_pattern_t *) abstract_pattern;
 
if (pattern->release == NULL)
return;
 
pattern->release (&pattern->base, pattern->user_data, surface);
}
 
cairo_status_t
_cairo_raster_source_pattern_init_copy (cairo_pattern_t *abstract_pattern,
const cairo_pattern_t *other)
{
cairo_raster_source_pattern_t *pattern =
(cairo_raster_source_pattern_t *) abstract_pattern;
cairo_status_t status;
 
VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_raster_source_pattern_t)));
memcpy(pattern, other, sizeof (cairo_raster_source_pattern_t));
 
status = CAIRO_STATUS_SUCCESS;
if (pattern->copy)
status = pattern->copy (&pattern->base, pattern->user_data, other);
 
return status;
}
 
cairo_status_t
_cairo_raster_source_pattern_snapshot (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern =
(cairo_raster_source_pattern_t *) abstract_pattern;
 
if (pattern->snapshot == NULL)
return CAIRO_STATUS_SUCCESS;
 
return pattern->snapshot (&pattern->base, pattern->user_data);
}
 
void
_cairo_raster_source_pattern_finish (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern =
(cairo_raster_source_pattern_t *) abstract_pattern;
 
if (pattern->finish == NULL)
return;
 
pattern->finish (&pattern->base, pattern->user_data);
}
 
/* Public interface */
 
/**
* cairo_pattern_create_raster_source:
* @user_data: the user data to be passed to all callbacks
* @content: content type for the pixel data that will be returned. Knowing
* the content type ahead of time is used for analysing the operation and
* picking the appropriate rendering path.
* @width: maximum size of the sample area
* @height: maximum size of the sample area
*
* Creates a new user pattern for providing pixel data.
*
* Use the setter functions to associate callbacks with the returned
* pattern. The only mandatory callback is acquire.
*
* Return value: a newly created #cairo_pattern_t. Free with
* cairo_pattern_destroy() when you are done using it.
*
* Since: 1.12
**/
cairo_pattern_t *
cairo_pattern_create_raster_source (void *user_data,
cairo_content_t content,
int width, int height)
{
cairo_raster_source_pattern_t *pattern;
 
CAIRO_MUTEX_INITIALIZE ();
 
if (width < 0 || height < 0)
return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_SIZE);
 
if (! CAIRO_CONTENT_VALID (content))
return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
 
pattern = calloc (1, sizeof (*pattern));
if (unlikely (pattern == NULL))
return _cairo_pattern_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_pattern_init (&pattern->base,
CAIRO_PATTERN_TYPE_RASTER_SOURCE);
CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
 
pattern->content = content;
 
pattern->extents.x = 0;
pattern->extents.y = 0;
pattern->extents.width = width;
pattern->extents.height = height;
 
pattern->user_data = user_data;
 
return &pattern->base;
}
 
/**
* cairo_raster_source_pattern_set_callback_data:
* @pattern: the pattern to update
* @data: the user data to be passed to all callbacks
*
* Updates the user data that is provided to all callbacks.
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *abstract_pattern,
void *data)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
pattern->user_data = data;
}
 
/**
* cairo_raster_source_pattern_get_callback_data:
* @pattern: the pattern to update
*
* Queries the current user data.
*
* Return value: the current user-data passed to each callback
*
* Since: 1.12
**/
void *
cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return NULL;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
return pattern->user_data;
}
 
/**
* cairo_raster_source_pattern_set_acquire:
* @pattern: the pattern to update
* @acquire: acquire callback
* @release: release callback
*
* Specifies the callbacks used to generate the image surface for a rendering
* operation (acquire) and the function used to cleanup that surface afterwards.
*
* The @acquire callback should create a surface (preferably an image
* surface created to match the target using
* cairo_surface_create_similar_image()) that defines at least the region
* of interest specified by extents. The surface is allowed to be the entire
* sample area, but if it does contain a subsection of the sample area,
* the surface extents should be provided by setting the device offset (along
* with its width and height) using cairo_surface_set_device_offset().
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_set_acquire (cairo_pattern_t *abstract_pattern,
cairo_raster_source_acquire_func_t acquire,
cairo_raster_source_release_func_t release)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
pattern->acquire = acquire;
pattern->release = release;
}
 
/**
* cairo_raster_source_pattern_get_acquire:
* @pattern: the pattern to query
* @acquire: return value for the current acquire callback
* @release: return value for the current release callback
*
* Queries the current acquire and release callbacks.
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_get_acquire (cairo_pattern_t *abstract_pattern,
cairo_raster_source_acquire_func_t *acquire,
cairo_raster_source_release_func_t *release)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
if (acquire)
*acquire = pattern->acquire;
if (release)
*release = pattern->release;
}
 
/**
* cairo_raster_source_pattern_set_snapshot:
* @pattern: the pattern to update
* @snapshot: snapshot callback
*
* Sets the callback that will be used whenever a snapshot is taken of the
* pattern, that is whenever the current contents of the pattern should be
* preserved for later use. This is typically invoked whilst printing.
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *abstract_pattern,
cairo_raster_source_snapshot_func_t snapshot)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
pattern->snapshot = snapshot;
}
 
/**
* cairo_raster_source_pattern_get_snapshot:
* @pattern: the pattern to query
*
* Queries the current snapshot callback.
*
* Return value: the current snapshot callback
*
* Since: 1.12
**/
cairo_raster_source_snapshot_func_t
cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return NULL;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
return pattern->snapshot;
}
 
/**
* cairo_raster_source_pattern_set_copy:
* @pattern: the pattern to update
* @copy: the copy callback
*
* Updates the copy callback which is used whenever a temporary copy of the
* pattern is taken.
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_set_copy (cairo_pattern_t *abstract_pattern,
cairo_raster_source_copy_func_t copy)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
pattern->copy = copy;
}
 
/**
* cairo_raster_source_pattern_get_copy:
* @pattern: the pattern to query
*
* Queries the current copy callback.
*
* Return value: the current copy callback
*
* Since: 1.12
**/
cairo_raster_source_copy_func_t
cairo_raster_source_pattern_get_copy (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return NULL;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
return pattern->copy;
}
 
/**
* cairo_raster_source_pattern_set_finish:
* @pattern: the pattern to update
* @finish: the finish callback
*
* Updates the finish callback which is used whenever a pattern (or a copy
* thereof) will no longer be used.
*
* Since: 1.12
**/
void
cairo_raster_source_pattern_set_finish (cairo_pattern_t *abstract_pattern,
cairo_raster_source_finish_func_t finish)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
pattern->finish = finish;
}
 
/**
* cairo_raster_source_pattern_get_finish:
* @pattern: the pattern to query
*
* Queries the current finish callback.
*
* Return value: the current finish callback
*
* Since: 1.12
**/
cairo_raster_source_finish_func_t
cairo_raster_source_pattern_get_finish (cairo_pattern_t *abstract_pattern)
{
cairo_raster_source_pattern_t *pattern;
 
if (abstract_pattern->type != CAIRO_PATTERN_TYPE_RASTER_SOURCE)
return NULL;
 
pattern = (cairo_raster_source_pattern_t *) abstract_pattern;
return pattern->finish;
}
/programs/develop/libraries/cairo/src/cairo-recording-surface-inline.h
0,0 → 1,68
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
* Adrian Johnson <ajohnson@redneon.com>
*/
 
#ifndef CAIRO_RECORDING_SURFACE_INLINE_H
#define CAIRO_RECORDING_SURFACE_INLINE_H
 
#include "cairo-recording-surface-private.h"
 
static inline cairo_bool_t
_cairo_recording_surface_get_bounds (cairo_surface_t *surface,
cairo_rectangle_t *extents)
{
cairo_recording_surface_t *recording = (cairo_recording_surface_t *)surface;
if (recording->unbounded)
return FALSE;
 
*extents = recording->extents_pixels;
return TRUE;
}
 
/**
* _cairo_surface_is_recording:
* @surface: a #cairo_surface_t
*
* Checks if a surface is a #cairo_recording_surface_t
*
* Return value: %TRUE if the surface is a recording surface
**/
static inline cairo_bool_t
_cairo_surface_is_recording (const cairo_surface_t *surface)
{
return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING;
}
 
#endif /* CAIRO_RECORDING_SURFACE_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-recording-surface-private.h
39,7 → 39,8
 
#include "cairoint.h"
#include "cairo-path-fixed-private.h"
#include "cairo-clip-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-backend-private.h"
 
typedef enum {
/* The 5 basic drawing operations. */
60,7 → 61,11
cairo_command_type_t type;
cairo_recording_region_type_t region;
cairo_operator_t op;
cairo_clip_t clip;
cairo_rectangle_int_t extents;
cairo_clip_t *clip;
 
int index;
struct _cairo_command_header *chain;
} cairo_command_header_t;
 
typedef struct _cairo_command_paint {
120,8 → 125,6
typedef struct _cairo_recording_surface {
cairo_surface_t base;
 
cairo_content_t content;
 
/* A recording-surface is logically unbounded, but when used as a
* source we need to render it to an image, so we need a size at
* which to create that image. */
129,11 → 132,16
cairo_rectangle_int_t extents;
cairo_bool_t unbounded;
 
cairo_clip_t clip;
 
cairo_array_t commands;
unsigned int *indices;
unsigned int num_indices;
cairo_bool_t optimize_clears;
 
int replay_start_idx;
struct bbtree {
cairo_box_t extents;
struct bbtree *left, *right;
cairo_command_header_t *chain;
} bbtree;
} cairo_recording_surface_t;
 
slim_hidden_proto (cairo_recording_surface_create);
143,15 → 151,21
cairo_path_fixed_t *path);
 
cairo_private cairo_status_t
_cairo_recording_surface_replay (cairo_surface_t *surface,
_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
long unsigned index,
cairo_surface_t *target);
 
 
cairo_private cairo_status_t
_cairo_recording_surface_replay_analyze_recording_pattern (cairo_surface_t *surface,
_cairo_recording_surface_replay (cairo_surface_t *surface,
cairo_surface_t *target);
 
cairo_private cairo_status_t
_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
const cairo_matrix_t *surface_transform,
cairo_surface_t *target,
const cairo_clip_t *target_clip);
 
cairo_private cairo_status_t
_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
cairo_surface_t *target);
cairo_private cairo_status_t
165,7 → 179,9
cairo_box_t *bbox,
const cairo_matrix_t *transform);
 
cairo_private cairo_bool_t
_cairo_surface_is_recording (const cairo_surface_t *surface);
cairo_private cairo_status_t
_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
cairo_box_t *bbox,
const cairo_matrix_t *transform);
 
#endif /* CAIRO_RECORDING_SURFACE_H */
/programs/develop/libraries/cairo/src/cairo-recording-surface.c
74,14 → 74,21
* improved by improving the implementation of snapshot for the
* various objects. For example, it would be nice to have a
* copy-on-write implementation for _cairo_surface_snapshot.
*/
**/
 
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-clip-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-wrapper-private.h"
#include "cairo-traps-private.h"
 
typedef enum {
CAIRO_RECORDING_REPLAY,
98,7 → 105,7
* This macro was added for completeness in cairo 1.10.
*
* Since: 1.10
*/
**/
 
/* Currently all recording surfaces do have a size which should be passed
* in as the maximum size of any target surface against which the
109,6 → 116,248
* according to the intended replay target).
*/
 
static int bbtree_left_or_right (struct bbtree *bbt,
const cairo_box_t *box)
{
int left, right;
 
if (bbt->left) {
cairo_box_t *e = &bbt->left->extents;
cairo_box_t b;
 
b.p1.x = MIN (e->p1.x, box->p1.x);
b.p1.y = MIN (e->p1.y, box->p1.y);
b.p2.x = MAX (e->p2.x, box->p2.x);
b.p2.y = MAX (e->p2.y, box->p2.y);
 
left = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
left -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
} else
left = 0;
 
if (bbt->right) {
cairo_box_t *e = &bbt->right->extents;
cairo_box_t b;
 
b.p1.x = MIN (e->p1.x, box->p1.x);
b.p1.y = MIN (e->p1.y, box->p1.y);
b.p2.x = MAX (e->p2.x, box->p2.x);
b.p2.y = MAX (e->p2.y, box->p2.y);
 
right = _cairo_fixed_integer_part (b.p2.x - b.p1.x) * _cairo_fixed_integer_part (b.p2.y - b.p1.y);
right -= _cairo_fixed_integer_part (e->p2.x - e->p1.x) * _cairo_fixed_integer_part (e->p2.y - e->p1.y);
} else
right = 0;
 
return left <= right;
}
 
#define INVALID_CHAIN ((cairo_command_header_t *)-1)
 
static struct bbtree *
bbtree_new (const cairo_box_t *box, cairo_command_header_t *chain)
{
struct bbtree *bbt = malloc (sizeof (*bbt));
if (bbt == NULL)
return NULL;
bbt->extents = *box;
bbt->left = bbt->right = NULL;
bbt->chain = chain;
return bbt;
}
 
static void
bbtree_init (struct bbtree *bbt, cairo_command_header_t *header)
{
_cairo_box_from_rectangle (&bbt->extents, &header->extents);
bbt->chain = header;
}
 
static cairo_status_t
bbtree_add (struct bbtree *bbt,
cairo_command_header_t *header,
const cairo_box_t *box)
{
if (box->p1.x < bbt->extents.p1.x || box->p1.y < bbt->extents.p1.y ||
box->p2.x > bbt->extents.p2.x || box->p2.y > bbt->extents.p2.y)
{
if (bbt->chain) {
if (bbtree_left_or_right (bbt, &bbt->extents)) {
if (bbt->left == NULL) {
bbt->left = bbtree_new (&bbt->extents, bbt->chain);
if (unlikely (bbt->left == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
bbtree_add (bbt->left, bbt->chain, &bbt->extents);
} else {
if (bbt->right == NULL) {
bbt->right = bbtree_new (&bbt->extents, bbt->chain);
if (unlikely (bbt->right == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
bbtree_add (bbt->right, bbt->chain, &bbt->extents);
}
 
bbt->chain = NULL;
}
 
bbt->extents.p1.x = MIN (bbt->extents.p1.x, box->p1.x);
bbt->extents.p1.y = MIN (bbt->extents.p1.y, box->p1.y);
bbt->extents.p2.x = MAX (bbt->extents.p2.x, box->p2.x);
bbt->extents.p2.y = MAX (bbt->extents.p2.y, box->p2.y);
}
 
if (box->p1.x == bbt->extents.p1.x && box->p1.y == bbt->extents.p1.y &&
box->p2.x == bbt->extents.p2.x && box->p2.y == bbt->extents.p2.y)
{
cairo_command_header_t *last = header;
while (last->chain) /* expected to be infrequent */
last = last->chain;
last->chain = bbt->chain;
bbt->chain = header;
return CAIRO_STATUS_SUCCESS;
}
 
if (bbtree_left_or_right (bbt, box)) {
if (bbt->left == NULL) {
bbt->left = bbtree_new (box, header);
if (unlikely (bbt->left == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
return bbtree_add (bbt->left, header, box);
} else {
if (bbt->right == NULL) {
bbt->right = bbtree_new (box, header);
if (unlikely (bbt->right == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
return bbtree_add (bbt->right, header, box);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void bbtree_del (struct bbtree *bbt)
{
if (bbt->left)
bbtree_del (bbt->left);
if (bbt->right)
bbtree_del (bbt->right);
 
free (bbt);
}
 
static cairo_bool_t box_outside (const cairo_box_t *a, const cairo_box_t *b)
{
return
a->p1.x >= b->p2.x || a->p1.y >= b->p2.y ||
a->p2.x <= b->p1.x || a->p2.y <= b->p1.y;
}
 
static void
bbtree_foreach_mark_visible (struct bbtree *bbt,
const cairo_box_t *box,
unsigned int **indices)
{
cairo_command_header_t *chain;
 
for (chain = bbt->chain; chain; chain = chain->chain)
*(*indices)++ = chain->index;
 
if (bbt->left && ! box_outside (box, &bbt->left->extents))
bbtree_foreach_mark_visible (bbt->left, box, indices);
if (bbt->right && ! box_outside (box, &bbt->right->extents))
bbtree_foreach_mark_visible (bbt->right, box, indices);
}
 
static inline int intcmp (const unsigned int a, const unsigned int b)
{
return a - b;
}
CAIRO_COMBSORT_DECLARE (sort_indices, unsigned int, intcmp)
 
static inline int sizecmp (unsigned int a, unsigned int b, cairo_command_header_t **elements)
{
const cairo_rectangle_int_t *r;
 
r = &elements[a]->extents;
a = r->width * r->height;
 
r = &elements[b]->extents;
b = r->width * r->height;
 
return b - a;
}
CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_commands, unsigned int, sizecmp)
 
static void
_cairo_recording_surface_destroy_bbtree (cairo_recording_surface_t *surface)
{
cairo_command_t **elements;
int i, num_elements;
 
if (surface->bbtree.chain == INVALID_CHAIN)
return;
 
if (surface->bbtree.left) {
bbtree_del (surface->bbtree.left);
surface->bbtree.left = NULL;
}
if (surface->bbtree.right) {
bbtree_del (surface->bbtree.right);
surface->bbtree.right = NULL;
}
 
elements = _cairo_array_index (&surface->commands, 0);
num_elements = surface->commands.num_elements;
for (i = 0; i < num_elements; i++)
elements[i]->header.chain = NULL;
 
surface->bbtree.chain = INVALID_CHAIN;
}
 
static cairo_status_t
_cairo_recording_surface_create_bbtree (cairo_recording_surface_t *surface)
{
cairo_command_t **elements = _cairo_array_index (&surface->commands, 0);
unsigned int *indices;
cairo_status_t status;
unsigned int i, count;
 
count = surface->commands.num_elements;
if (count > surface->num_indices) {
free (surface->indices);
surface->indices = _cairo_malloc_ab (count, sizeof (int));
if (unlikely (surface->indices == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
surface->num_indices = count;
}
 
indices = surface->indices;
for (i = 0; i < count; i++)
indices[i] = i;
 
sort_commands (indices, count, elements);
 
bbtree_init (&surface->bbtree, &elements[indices[0]]->header);
for (i = 1; i < count; i++) {
cairo_command_header_t *header = &elements[indices[i]]->header;
cairo_box_t box;
 
_cairo_box_from_rectangle (&box, &header->extents);
status = bbtree_add (&surface->bbtree, header, &box);
if (unlikely (status))
goto cleanup;
}
 
return CAIRO_STATUS_SUCCESS;
 
cleanup:
bbtree_del (&surface->bbtree);
return status;
}
 
/**
* cairo_recording_surface_create:
* @content: the content of the recording surface
134,49 → 383,45
cairo_recording_surface_create (cairo_content_t content,
const cairo_rectangle_t *extents)
{
cairo_recording_surface_t *recording_surface;
cairo_status_t status;
cairo_recording_surface_t *surface;
 
recording_surface = malloc (sizeof (cairo_recording_surface_t));
if (unlikely (recording_surface == NULL))
surface = malloc (sizeof (cairo_recording_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&recording_surface->base,
_cairo_surface_init (&surface->base,
&cairo_recording_surface_backend,
NULL, /* device */
content);
 
recording_surface->content = content;
 
recording_surface->unbounded = TRUE;
_cairo_clip_init (&recording_surface->clip);
surface->unbounded = TRUE;
 
/* unbounded -> 'infinite' extents */
if (extents != NULL) {
recording_surface->extents_pixels = *extents;
surface->extents_pixels = *extents;
 
/* XXX check for overflow */
recording_surface->extents.x = floor (extents->x);
recording_surface->extents.y = floor (extents->y);
recording_surface->extents.width = ceil (extents->x + extents->width) - recording_surface->extents.x;
recording_surface->extents.height = ceil (extents->y + extents->height) - recording_surface->extents.y;
surface->extents.x = floor (extents->x);
surface->extents.y = floor (extents->y);
surface->extents.width = ceil (extents->x + extents->width) - surface->extents.x;
surface->extents.height = ceil (extents->y + extents->height) - surface->extents.y;
 
status = _cairo_clip_rectangle (&recording_surface->clip,
&recording_surface->extents);
if (unlikely (status)) {
free (recording_surface);
return _cairo_surface_create_in_error (status);
surface->unbounded = FALSE;
}
 
recording_surface->unbounded = FALSE;
}
_cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
 
_cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *));
surface->base.is_clear = TRUE;
 
recording_surface->replay_start_idx = 0;
recording_surface->base.is_clear = TRUE;
surface->bbtree.left = surface->bbtree.right = NULL;
surface->bbtree.chain = INVALID_CHAIN;
 
return &recording_surface->base;
surface->indices = NULL;
surface->num_indices = 0;
surface->optimize_clears = TRUE;
 
return &surface->base;
}
slim_hidden_def (cairo_recording_surface_create);
 
196,12 → 441,12
static cairo_status_t
_cairo_recording_surface_finish (void *abstract_surface)
{
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_t **elements;
int i, num_elements;
 
num_elements = recording_surface->commands.num_elements;
elements = _cairo_array_index (&recording_surface->commands, 0);
num_elements = surface->commands.num_elements;
elements = _cairo_array_index (&surface->commands, 0);
for (i = 0; i < num_elements; i++) {
cairo_command_t *command = elements[i];
 
238,51 → 483,132
ASSERT_NOT_REACHED;
}
 
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
}
 
_cairo_array_fini (&recording_surface->commands);
_cairo_clip_fini (&recording_surface->clip);
_cairo_array_fini (&surface->commands);
 
if (surface->bbtree.left)
bbtree_del (surface->bbtree.left);
if (surface->bbtree.right)
bbtree_del (surface->bbtree.right);
 
free (surface->indices);
 
return CAIRO_STATUS_SUCCESS;
}
 
struct proxy {
cairo_surface_t base;
cairo_surface_t *image;
};
 
static cairo_status_t
proxy_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
struct proxy *proxy = abstract_surface;
return _cairo_surface_acquire_source_image (proxy->image, image_out, image_extra);
}
 
static void
proxy_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
struct proxy *proxy = abstract_surface;
_cairo_surface_release_source_image (proxy->image, image, image_extra);
}
 
static cairo_status_t
proxy_finish (void *abstract_surface)
{
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t proxy_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
proxy_finish,
NULL,
 
NULL, /* create similar */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
proxy_acquire_source_image,
proxy_release_source_image,
};
 
static cairo_surface_t *
attach_proxy (cairo_surface_t *source,
cairo_surface_t *image)
{
struct proxy *proxy;
 
proxy = malloc (sizeof (*proxy));
if (unlikely (proxy == NULL))
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_surface_init (&proxy->base, &proxy_backend, NULL, image->content);
 
proxy->image = image;
_cairo_surface_attach_snapshot (source, &proxy->base, NULL);
 
return &proxy->base;
}
 
static void
detach_proxy (cairo_surface_t *source,
cairo_surface_t *proxy)
{
cairo_surface_finish (proxy);
cairo_surface_destroy (proxy);
}
 
static cairo_surface_t *
get_proxy (cairo_surface_t *proxy)
{
return ((struct proxy *)proxy)->image;
}
 
static cairo_status_t
_cairo_recording_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_recording_surface_t *surface = abstract_surface;
cairo_surface_t *image, *proxy;
cairo_status_t status;
cairo_recording_surface_t *surface = abstract_surface;
cairo_surface_t *image;
 
image = _cairo_surface_has_snapshot (&surface->base,
&_cairo_image_surface_backend);
if (image != NULL) {
*image_out = (cairo_image_surface_t *) cairo_surface_reference (image);
proxy = _cairo_surface_has_snapshot (abstract_surface, &proxy_backend);
if (proxy != NULL) {
*image_out = (cairo_image_surface_t *)
cairo_surface_reference (get_proxy (proxy));
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
image = _cairo_image_surface_create_with_content (surface->content,
assert (! surface->unbounded);
image = _cairo_image_surface_create_with_content (surface->base.content,
surface->extents.width,
surface->extents.height);
if (unlikely (image->status))
return image->status;
 
cairo_surface_set_device_offset (image,
-surface->extents.x,
-surface->extents.y);
/* Handle recursion by returning future reads from the current image */
proxy = attach_proxy (abstract_surface, image);
status = _cairo_recording_surface_replay (&surface->base, image);
detach_proxy (abstract_surface, proxy);
 
status = _cairo_recording_surface_replay (&surface->base, image);
if (unlikely (status)) {
cairo_surface_destroy (image);
return status;
}
 
_cairo_surface_attach_snapshot (&surface->base, image, NULL);
 
*image_out = (cairo_image_surface_t *) image;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
297,11 → 623,11
}
 
static cairo_status_t
_command_init (cairo_recording_surface_t *recording_surface,
_command_init (cairo_recording_surface_t *surface,
cairo_command_header_t *command,
cairo_command_type_t type,
cairo_operator_t op,
cairo_clip_t *clip)
cairo_composite_rectangles_t *composite)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
308,29 → 634,115
command->type = type;
command->op = op;
command->region = CAIRO_RECORDING_REGION_ALL;
_cairo_clip_init_copy (&command->clip, clip);
if (recording_surface->clip.path != NULL)
status = _cairo_clip_apply_clip (&command->clip, &recording_surface->clip);
 
command->extents = composite->unbounded;
command->chain = NULL;
command->index = surface->commands.num_elements;
 
/* steal the clip */
command->clip = NULL;
if (! _cairo_composite_rectangles_can_reduce_clip (composite,
composite->clip))
{
command->clip = composite->clip;
composite->clip = NULL;
}
 
return status;
}
 
static void
_cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface)
{
cairo_surface_flush (&surface->base);
}
 
static cairo_status_t
_cairo_recording_surface_commit (cairo_recording_surface_t *surface,
cairo_command_header_t *command)
{
_cairo_recording_surface_break_self_copy_loop (surface);
return _cairo_array_append (&surface->commands, &command);
}
 
static void
_cairo_recording_surface_reset (cairo_recording_surface_t *surface)
{
/* Reset the commands and temporaries */
_cairo_recording_surface_finish (surface);
 
surface->bbtree.left = surface->bbtree.right = NULL;
surface->bbtree.chain = INVALID_CHAIN;
 
surface->indices = NULL;
surface->num_indices = 0;
 
_cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
}
 
static cairo_bool_t
is_identity_recording_pattern (const cairo_pattern_t *pattern)
{
cairo_surface_t *surface;
 
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
return FALSE;
 
if (!_cairo_matrix_is_identity(&pattern->matrix))
return FALSE;
 
surface = ((cairo_surface_pattern_t *)pattern)->surface;
return surface->backend->type == CAIRO_SURFACE_TYPE_RECORDING;
}
 
static cairo_int_status_t
_cairo_recording_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_paint_t *command;
cairo_composite_rectangles_t composite;
 
TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
 
if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) {
if (surface->optimize_clears) {
_cairo_recording_surface_reset (surface);
return CAIRO_STATUS_SUCCESS;
}
}
 
if (clip == NULL && surface->optimize_clears &&
(op == CAIRO_OPERATOR_SOURCE ||
(op == CAIRO_OPERATOR_OVER &&
(surface->base.is_clear || _cairo_pattern_is_opaque_solid (source)))))
{
_cairo_recording_surface_reset (surface);
if (is_identity_recording_pattern (source)) {
cairo_surface_t *src = ((cairo_surface_pattern_t *)source)->surface;
return _cairo_recording_surface_replay (src, &surface->base);
}
}
 
status = _cairo_composite_rectangles_init_for_paint (&composite,
&surface->base,
op, source,
clip);
if (unlikely (status))
return status;
 
command = malloc (sizeof (cairo_command_paint_t));
if (unlikely (command == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_COMPOSITE;
}
 
status = _command_init (recording_surface,
&command->header, CAIRO_COMMAND_PAINT, op, clip);
status = _command_init (surface,
&command->header, CAIRO_COMMAND_PAINT, op,
&composite);
if (unlikely (status))
goto CLEANUP_COMMAND;
 
338,23 → 750,22
if (unlikely (status))
goto CLEANUP_COMMAND;
 
status = _cairo_array_append (&recording_surface->commands, &command);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto CLEANUP_SOURCE;
 
/* An optimisation that takes care to not replay what was done
* before surface is cleared. We don't erase recorded commands
* since we may have earlier snapshots of this surface. */
if (op == CAIRO_OPERATOR_CLEAR && clip == NULL)
recording_surface->replay_start_idx = recording_surface->commands.num_elements;
_cairo_recording_surface_destroy_bbtree (surface);
 
_cairo_composite_rectangles_fini (&composite);
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_SOURCE:
_cairo_pattern_fini (&command->source.base);
CLEANUP_COMMAND:
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
CLEANUP_COMPOSITE:
_cairo_composite_rectangles_fini (&composite);
return status;
}
 
363,18 → 774,31
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_mask_t *command;
cairo_composite_rectangles_t composite;
 
TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
 
status = _cairo_composite_rectangles_init_for_mask (&composite,
&surface->base,
op, source, mask,
clip);
if (unlikely (status))
return status;
 
command = malloc (sizeof (cairo_command_mask_t));
if (unlikely (command == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_COMPOSITE;
}
 
status = _command_init (recording_surface,
&command->header, CAIRO_COMMAND_MASK, op, clip);
status = _command_init (surface,
&command->header, CAIRO_COMMAND_MASK, op,
&composite);
if (unlikely (status))
goto CLEANUP_COMMAND;
 
386,10 → 810,13
if (unlikely (status))
goto CLEANUP_SOURCE;
 
status = _cairo_array_append (&recording_surface->commands, &command);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto CLEANUP_MASK;
 
_cairo_recording_surface_destroy_bbtree (surface);
 
_cairo_composite_rectangles_fini (&composite);
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_MASK:
397,8 → 824,10
CLEANUP_SOURCE:
_cairo_pattern_fini (&command->source.base);
CLEANUP_COMMAND:
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
CLEANUP_COMPOSITE:
_cairo_composite_rectangles_fini (&composite);
return status;
}
 
406,24 → 835,38
_cairo_recording_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_stroke_t *command;
cairo_composite_rectangles_t composite;
 
TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
 
status = _cairo_composite_rectangles_init_for_stroke (&composite,
&surface->base,
op, source,
path, style, ctm,
clip);
if (unlikely (status))
return status;
 
command = malloc (sizeof (cairo_command_stroke_t));
if (unlikely (command == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_COMPOSITE;
}
 
status = _command_init (recording_surface,
&command->header, CAIRO_COMMAND_STROKE, op, clip);
status = _command_init (surface,
&command->header, CAIRO_COMMAND_STROKE, op,
&composite);
if (unlikely (status))
goto CLEANUP_COMMAND;
 
444,10 → 887,13
command->tolerance = tolerance;
command->antialias = antialias;
 
status = _cairo_array_append (&recording_surface->commands, &command);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto CLEANUP_STYLE;
 
_cairo_recording_surface_destroy_bbtree (surface);
 
_cairo_composite_rectangles_fini (&composite);
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_STYLE:
457,8 → 903,10
CLEANUP_SOURCE:
_cairo_pattern_fini (&command->source.base);
CLEANUP_COMMAND:
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
CLEANUP_COMPOSITE:
_cairo_composite_rectangles_fini (&composite);
return status;
}
 
466,22 → 914,35
_cairo_recording_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_fill_t *command;
cairo_composite_rectangles_t composite;
 
TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
 
status = _cairo_composite_rectangles_init_for_fill (&composite,
&surface->base,
op, source, path,
clip);
if (unlikely (status))
return status;
 
command = malloc (sizeof (cairo_command_fill_t));
if (unlikely (command == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_COMPOSITE;
}
 
status =_command_init (recording_surface,
&command->header, CAIRO_COMMAND_FILL, op, clip);
status =_command_init (surface,
&command->header, CAIRO_COMMAND_FILL, op,
&composite);
if (unlikely (status))
goto CLEANUP_COMMAND;
 
497,10 → 958,13
command->tolerance = tolerance;
command->antialias = antialias;
 
status = _cairo_array_append (&recording_surface->commands, &command);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto CLEANUP_PATH;
 
_cairo_recording_surface_destroy_bbtree (surface);
 
_cairo_composite_rectangles_fini (&composite);
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_PATH:
508,8 → 972,10
CLEANUP_SOURCE:
_cairo_pattern_fini (&command->source.base);
CLEANUP_COMMAND:
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
CLEANUP_COMPOSITE:
_cairo_composite_rectangles_fini (&composite);
return status;
}
 
531,19 → 997,34
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_recording_surface_t *recording_surface = abstract_surface;
cairo_recording_surface_t *surface = abstract_surface;
cairo_command_show_text_glyphs_t *command;
cairo_composite_rectangles_t composite;
 
TRACE ((stderr, "%s: surface=%d\n", __FUNCTION__, surface->base.unique_id));
 
status = _cairo_composite_rectangles_init_for_glyphs (&composite,
&surface->base,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
NULL);
if (unlikely (status))
return status;
 
command = malloc (sizeof (cairo_command_show_text_glyphs_t));
if (unlikely (command == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_COMPOSITE;
}
 
status = _command_init (recording_surface,
status = _command_init (surface,
&command->header, CAIRO_COMMAND_SHOW_TEXT_GLYPHS,
op, clip);
op, &composite);
if (unlikely (status))
goto CLEANUP_COMMAND;
 
587,10 → 1068,11
 
command->scaled_font = cairo_scaled_font_reference (scaled_font);
 
status = _cairo_array_append (&recording_surface->commands, &command);
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto CLEANUP_SCALED_FONT;
 
_cairo_composite_rectangles_fini (&composite);
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_SCALED_FONT:
602,13 → 1084,325
 
_cairo_pattern_fini (&command->source.base);
CLEANUP_COMMAND:
_cairo_clip_fini (&command->header.clip);
_cairo_clip_destroy (command->header.clip);
free (command);
CLEANUP_COMPOSITE:
_cairo_composite_rectangles_fini (&composite);
return status;
}
 
static void
_command_init_copy (cairo_recording_surface_t *surface,
cairo_command_header_t *dst,
const cairo_command_header_t *src)
{
dst->type = src->type;
dst->op = src->op;
dst->region = CAIRO_RECORDING_REGION_ALL;
 
dst->extents = src->extents;
dst->chain = NULL;
dst->index = surface->commands.num_elements;
 
dst->clip = _cairo_clip_copy (src->clip);
}
 
static cairo_status_t
_cairo_recording_surface_copy__paint (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_paint_t *command;
cairo_status_t status;
 
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
 
_command_init_copy (surface, &command->header, &src->header);
 
status = _cairo_pattern_init_copy (&command->source.base,
&src->paint.source.base);
if (unlikely (status))
goto err_command;
 
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_source;
 
return CAIRO_STATUS_SUCCESS;
 
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
 
static cairo_status_t
_cairo_recording_surface_copy__mask (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_mask_t *command;
cairo_status_t status;
 
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
 
_command_init_copy (surface, &command->header, &src->header);
 
status = _cairo_pattern_init_copy (&command->source.base,
&src->mask.source.base);
if (unlikely (status))
goto err_command;
 
status = _cairo_pattern_init_copy (&command->mask.base,
&src->mask.source.base);
if (unlikely (status))
goto err_source;
 
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_mask;
 
return CAIRO_STATUS_SUCCESS;
 
err_mask:
_cairo_pattern_fini (&command->mask.base);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
 
static cairo_status_t
_cairo_recording_surface_copy__stroke (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_stroke_t *command;
cairo_status_t status;
 
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
 
_command_init_copy (surface, &command->header, &src->header);
 
status = _cairo_pattern_init_copy (&command->source.base,
&src->stroke.source.base);
if (unlikely (status))
goto err_command;
 
status = _cairo_path_fixed_init_copy (&command->path, &src->stroke.path);
if (unlikely (status))
goto err_source;
 
status = _cairo_stroke_style_init_copy (&command->style,
&src->stroke.style);
if (unlikely (status))
goto err_path;
 
command->ctm = src->stroke.ctm;
command->ctm_inverse = src->stroke.ctm_inverse;
command->tolerance = src->stroke.tolerance;
command->antialias = src->stroke.antialias;
 
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_style;
 
return CAIRO_STATUS_SUCCESS;
 
err_style:
_cairo_stroke_style_fini (&command->style);
err_path:
_cairo_path_fixed_fini (&command->path);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
 
static cairo_status_t
_cairo_recording_surface_copy__fill (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_fill_t *command;
cairo_status_t status;
 
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
 
_command_init_copy (surface, &command->header, &src->header);
 
status = _cairo_pattern_init_copy (&command->source.base,
&src->fill.source.base);
if (unlikely (status))
goto err_command;
 
status = _cairo_path_fixed_init_copy (&command->path, &src->fill.path);
if (unlikely (status))
goto err_source;
 
command->fill_rule = src->fill.fill_rule;
command->tolerance = src->fill.tolerance;
command->antialias = src->fill.antialias;
 
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_path;
 
return CAIRO_STATUS_SUCCESS;
 
err_path:
_cairo_path_fixed_fini (&command->path);
err_source:
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
 
static cairo_status_t
_cairo_recording_surface_copy__glyphs (cairo_recording_surface_t *surface,
const cairo_command_t *src)
{
cairo_command_show_text_glyphs_t *command;
cairo_status_t status;
 
command = malloc (sizeof (*command));
if (unlikely (command == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err;
}
 
_command_init_copy (surface, &command->header, &src->header);
 
status = _cairo_pattern_init_copy (&command->source.base,
&src->show_text_glyphs.source.base);
if (unlikely (status))
goto err_command;
 
command->utf8 = NULL;
command->utf8_len = src->show_text_glyphs.utf8_len;
command->glyphs = NULL;
command->num_glyphs = src->show_text_glyphs.num_glyphs;
command->clusters = NULL;
command->num_clusters = src->show_text_glyphs.num_clusters;
 
if (command->utf8_len) {
command->utf8 = malloc (command->utf8_len);
if (unlikely (command->utf8 == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->utf8, src->show_text_glyphs.utf8, command->utf8_len);
}
if (command->num_glyphs) {
command->glyphs = _cairo_malloc_ab (command->num_glyphs,
sizeof (command->glyphs[0]));
if (unlikely (command->glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->glyphs, src->show_text_glyphs.glyphs,
sizeof (command->glyphs[0]) * command->num_glyphs);
}
if (command->num_clusters) {
command->clusters = _cairo_malloc_ab (command->num_clusters,
sizeof (command->clusters[0]));
if (unlikely (command->clusters == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto err_arrays;
}
memcpy (command->clusters, src->show_text_glyphs.clusters,
sizeof (command->clusters[0]) * command->num_clusters);
}
 
command->cluster_flags = src->show_text_glyphs.cluster_flags;
 
command->scaled_font =
cairo_scaled_font_reference (src->show_text_glyphs.scaled_font);
 
status = _cairo_recording_surface_commit (surface, &command->header);
if (unlikely (status))
goto err_arrays;
 
return CAIRO_STATUS_SUCCESS;
 
err_arrays:
free (command->utf8);
free (command->glyphs);
free (command->clusters);
_cairo_pattern_fini (&command->source.base);
err_command:
free(command);
err:
return status;
}
 
static cairo_status_t
_cairo_recording_surface_copy (cairo_recording_surface_t *dst,
cairo_recording_surface_t *src)
{
cairo_command_t **elements;
int i, num_elements;
cairo_status_t status;
 
elements = _cairo_array_index (&src->commands, 0);
num_elements = src->commands.num_elements;
for (i = 0; i < num_elements; i++) {
const cairo_command_t *command = elements[i];
 
switch (command->header.type) {
case CAIRO_COMMAND_PAINT:
status = _cairo_recording_surface_copy__paint (dst, command);
break;
 
case CAIRO_COMMAND_MASK:
status = _cairo_recording_surface_copy__mask (dst, command);
break;
 
case CAIRO_COMMAND_STROKE:
status = _cairo_recording_surface_copy__stroke (dst, command);
break;
 
case CAIRO_COMMAND_FILL:
status = _cairo_recording_surface_copy__fill (dst, command);
break;
 
case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
status = _cairo_recording_surface_copy__glyphs (dst, command);
break;
 
default:
ASSERT_NOT_REACHED;
}
 
if (unlikely (status))
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_recording_surface_snapshot
* _cairo_recording_surface_snapshot:
* @surface: a #cairo_surface_t which must be a recording surface
*
* Make an immutable copy of @surface. It is an error to call a
624,39 → 1418,39
_cairo_recording_surface_snapshot (void *abstract_other)
{
cairo_recording_surface_t *other = abstract_other;
cairo_recording_surface_t *recording_surface;
cairo_recording_surface_t *surface;
cairo_status_t status;
 
recording_surface = malloc (sizeof (cairo_recording_surface_t));
if (unlikely (recording_surface == NULL))
surface = malloc (sizeof (cairo_recording_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&recording_surface->base,
_cairo_surface_init (&surface->base,
&cairo_recording_surface_backend,
NULL, /* device */
other->base.content);
 
recording_surface->extents_pixels = other->extents_pixels;
recording_surface->extents = other->extents;
recording_surface->unbounded = other->unbounded;
recording_surface->content = other->content;
surface->extents_pixels = other->extents_pixels;
surface->extents = other->extents;
surface->unbounded = other->unbounded;
 
_cairo_clip_init_copy (&recording_surface->clip, &other->clip);
surface->base.is_clear = other->base.is_clear;
 
/* XXX We should in theory be able to reuse the original array, but we
* need to handle reference cycles during subsurface and self-copy.
*/
recording_surface->replay_start_idx = 0;
recording_surface->base.is_clear = TRUE;
surface->bbtree.left = surface->bbtree.right = NULL;
surface->bbtree.chain = INVALID_CHAIN;
 
_cairo_array_init (&recording_surface->commands, sizeof (cairo_command_t *));
status = _cairo_recording_surface_replay (&other->base, &recording_surface->base);
surface->indices = NULL;
surface->num_indices = 0;
surface->optimize_clears = TRUE;
 
_cairo_array_init (&surface->commands, sizeof (cairo_command_t *));
status = _cairo_recording_surface_copy (surface, other);
if (unlikely (status)) {
cairo_surface_destroy (&recording_surface->base);
cairo_surface_destroy (&surface->base);
return _cairo_surface_create_in_error (status);
}
 
return &recording_surface->base;
return &surface->base;
}
 
static cairo_bool_t
672,43 → 1466,30
return TRUE;
}
 
/**
* _cairo_surface_is_recording:
* @surface: a #cairo_surface_t
*
* Checks if a surface is a #cairo_recording_surface_t
*
* Return value: %TRUE if the surface is a recording surface
**/
cairo_bool_t
_cairo_surface_is_recording (const cairo_surface_t *surface)
{
return surface->backend == &cairo_recording_surface_backend;
}
 
static const cairo_surface_backend_t cairo_recording_surface_backend = {
CAIRO_SURFACE_TYPE_RECORDING,
_cairo_recording_surface_finish,
 
_cairo_default_context_create,
 
_cairo_recording_surface_create_similar,
_cairo_recording_surface_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
_cairo_recording_surface_acquire_source_image,
_cairo_recording_surface_release_source_image,
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
_cairo_recording_surface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_recording_surface_get_extents,
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
/* Here are the 5 basic drawing operations, (which are in some
* sense the only things that cairo_recording_surface should need to
719,37 → 1500,30
_cairo_recording_surface_mask,
_cairo_recording_surface_stroke,
_cairo_recording_surface_fill,
NULL, /* fill-stroke */
NULL,
 
_cairo_recording_surface_snapshot,
 
NULL, /* is_similar */
NULL, /* fill_stroke */
NULL, /* create_solid_pattern_surface */
NULL, /* can_repaint_solid_pattern_surface */
 
_cairo_recording_surface_has_show_text_glyphs,
_cairo_recording_surface_show_text_glyphs
_cairo_recording_surface_show_text_glyphs,
};
 
cairo_int_status_t
_cairo_recording_surface_get_path (cairo_surface_t *surface,
_cairo_recording_surface_get_path (cairo_surface_t *abstract_surface,
cairo_path_fixed_t *path)
{
cairo_recording_surface_t *recording_surface;
cairo_recording_surface_t *surface;
cairo_command_t **elements;
int i, num_elements;
cairo_int_status_t status;
 
if (surface->status)
return surface->status;
if (unlikely (abstract_surface->status))
return abstract_surface->status;
 
recording_surface = (cairo_recording_surface_t *) surface;
surface = (cairo_recording_surface_t *) abstract_surface;
status = CAIRO_STATUS_SUCCESS;
 
num_elements = recording_surface->commands.num_elements;
elements = _cairo_array_index (&recording_surface->commands, 0);
for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
num_elements = surface->commands.num_elements;
elements = _cairo_array_index (&surface->commands, 0);
for (i = 0; i < num_elements; i++) {
cairo_command_t *command = elements[i];
 
switch (command->header.type) {
765,7 → 1539,7
_cairo_traps_init (&traps);
 
/* XXX call cairo_stroke_to_path() when that is implemented */
status = _cairo_path_fixed_stroke_to_traps (&command->stroke.path,
status = _cairo_path_fixed_stroke_polygon_to_traps (&command->stroke.path,
&command->stroke.style,
&command->stroke.ctm,
&command->stroke.ctm_inverse,
772,7 → 1546,7
command->stroke.tolerance,
&traps);
 
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = _cairo_traps_path (&traps, path);
 
_cairo_traps_fini (&traps);
781,7 → 1555,7
case CAIRO_COMMAND_FILL:
{
status = _cairo_path_fixed_append (path,
&command->fill.path, CAIRO_DIRECTION_FORWARD,
&command->fill.path,
0, 0);
break;
}
802,60 → 1576,105
break;
}
 
return _cairo_surface_set_error (surface, status);
return status;
}
 
#define _clip(c) ((c)->header.clip.path ? &(c)->header.clip : NULL)
static int
_cairo_recording_surface_get_visible_commands (cairo_recording_surface_t *surface,
const cairo_rectangle_int_t *extents)
{
unsigned int num_visible, *indices;
cairo_box_t box;
 
if (surface->commands.num_elements == 0)
return 0;
 
_cairo_box_from_rectangle (&box, extents);
 
if (surface->bbtree.chain == INVALID_CHAIN)
_cairo_recording_surface_create_bbtree (surface);
 
indices = surface->indices;
bbtree_foreach_mark_visible (&surface->bbtree, &box, &indices);
num_visible = indices - surface->indices;
if (num_visible > 1)
sort_indices (surface->indices, num_visible);
 
return num_visible;
}
 
static cairo_status_t
_cairo_recording_surface_replay_internal (cairo_surface_t *surface,
_cairo_recording_surface_replay_internal (cairo_recording_surface_t *surface,
const cairo_rectangle_int_t *surface_extents,
const cairo_matrix_t *surface_transform,
cairo_surface_t *target,
const cairo_clip_t *target_clip,
cairo_recording_replay_type_t type,
cairo_recording_region_type_t region)
{
cairo_recording_surface_t *recording_surface;
cairo_surface_wrapper_t wrapper;
cairo_command_t **elements;
int i, num_elements;
cairo_int_status_t status;
cairo_surface_wrapper_t wrapper;
cairo_bool_t replay_all =
type == CAIRO_RECORDING_REPLAY &&
region == CAIRO_RECORDING_REGION_ALL;
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
cairo_rectangle_int_t extents;
cairo_bool_t use_indices = FALSE;
const cairo_rectangle_int_t *r;
unsigned int i, num_elements;
 
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->base.status))
return surface->base.status;
 
if (unlikely (target->status))
return target->status;
 
if (unlikely (surface->finished))
if (unlikely (surface->base.finished))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
if (surface->is_clear)
if (surface->base.is_clear)
return CAIRO_STATUS_SUCCESS;
 
assert (_cairo_surface_is_recording (surface));
assert (_cairo_surface_is_recording (&surface->base));
 
_cairo_surface_wrapper_init (&wrapper, target);
_cairo_surface_wrapper_set_extents (&wrapper, surface_extents);
if (surface_extents)
_cairo_surface_wrapper_intersect_extents (&wrapper, surface_extents);
r = &_cairo_unbounded_rectangle;
if (! surface->unbounded) {
_cairo_surface_wrapper_intersect_extents (&wrapper, &surface->extents);
r = &surface->extents;
}
_cairo_surface_wrapper_set_inverse_transform (&wrapper, surface_transform);
_cairo_surface_wrapper_set_clip (&wrapper, target_clip);
 
recording_surface = (cairo_recording_surface_t *) surface;
status = CAIRO_STATUS_SUCCESS;
/* Compute the extents of the target clip in recorded device space */
if (! _cairo_surface_wrapper_get_target_extents (&wrapper, &extents))
goto done;
 
num_elements = recording_surface->commands.num_elements;
elements = _cairo_array_index (&recording_surface->commands, 0);
num_elements = surface->commands.num_elements;
elements = _cairo_array_index (&surface->commands, 0);
if (extents.width < r->width || extents.height < r->height) {
num_elements =
_cairo_recording_surface_get_visible_commands (surface, &extents);
use_indices = num_elements != surface->commands.num_elements;
}
 
for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
cairo_command_t *command = elements[i];
for (i = 0; i < num_elements; i++) {
cairo_command_t *command = elements[use_indices ? surface->indices[i] : i];
 
if (type == CAIRO_RECORDING_REPLAY && region != CAIRO_RECORDING_REGION_ALL) {
if (command->header.region != region)
if (! replay_all && command->header.region != region)
continue;
}
 
if (! _cairo_rectangle_intersects (&extents, &command->header.extents))
continue;
 
switch (command->header.type) {
case CAIRO_COMMAND_PAINT:
status = _cairo_surface_wrapper_paint (&wrapper,
command->header.op,
&command->paint.source.base,
_clip (command));
command->header.clip);
break;
 
case CAIRO_COMMAND_MASK:
863,11 → 1682,10
command->header.op,
&command->mask.source.base,
&command->mask.mask.base,
_clip (command));
command->header.clip);
break;
 
case CAIRO_COMMAND_STROKE:
{
status = _cairo_surface_wrapper_stroke (&wrapper,
command->header.op,
&command->stroke.source.base,
877,11 → 1695,12
&command->stroke.ctm_inverse,
command->stroke.tolerance,
command->stroke.antialias,
_clip (command));
command->header.clip);
break;
}
 
case CAIRO_COMMAND_FILL:
{
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_surface_wrapper_has_fill_stroke (&wrapper)) {
cairo_command_t *stroke_command;
 
stroke_command = NULL;
898,8 → 1717,10
 
if (stroke_command != NULL &&
stroke_command->header.type == CAIRO_COMMAND_STROKE &&
_cairo_path_fixed_is_equal (&command->fill.path,
&stroke_command->stroke.path))
_cairo_path_fixed_equal (&command->fill.path,
&stroke_command->stroke.path) &&
_cairo_clip_equal (command->header.clip,
stroke_command->header.clip))
{
status = _cairo_surface_wrapper_fill_stroke (&wrapper,
command->header.op,
915,11 → 1736,11
&stroke_command->stroke.ctm_inverse,
stroke_command->stroke.tolerance,
stroke_command->stroke.antialias,
_clip (command));
command->header.clip);
i++;
}
else
{
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_surface_wrapper_fill (&wrapper,
command->header.op,
&command->fill.source.base,
927,52 → 1748,34
command->fill.fill_rule,
command->fill.tolerance,
command->fill.antialias,
_clip (command));
command->header.clip);
}
break;
}
 
case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
{
cairo_glyph_t *glyphs = command->show_text_glyphs.glyphs;
cairo_glyph_t *glyphs_copy;
int num_glyphs = command->show_text_glyphs.num_glyphs;
 
/* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
* to modify the glyph array that's passed in. We must always
* copy the array before handing it to the backend.
*/
glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (unlikely (glyphs_copy == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
break;
}
 
memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
 
status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
command->header.op,
&command->show_text_glyphs.source.base,
command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
glyphs_copy, num_glyphs,
command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
command->show_text_glyphs.cluster_flags,
command->show_text_glyphs.scaled_font,
_clip (command));
free (glyphs_copy);
command->header.clip);
break;
}
 
default:
ASSERT_NOT_REACHED;
}
 
if (type == CAIRO_RECORDING_CREATE_REGIONS) {
if (status == CAIRO_STATUS_SUCCESS) {
if (status == CAIRO_INT_STATUS_SUCCESS) {
command->header.region = CAIRO_RECORDING_REGION_NATIVE;
} else if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) {
command->header.region = CAIRO_RECORDING_REGION_IMAGE_FALLBACK;
status = CAIRO_STATUS_SUCCESS;
status = CAIRO_INT_STATUS_SUCCESS;
} else {
assert (_cairo_status_is_error (status));
assert (_cairo_int_status_is_error (status));
}
}
 
980,18 → 1783,101
break;
}
 
/* free up any caches */
for (i = recording_surface->replay_start_idx; i < num_elements; i++) {
cairo_command_t *command = elements[i];
done:
_cairo_surface_wrapper_fini (&wrapper);
return _cairo_surface_set_error (&surface->base, status);
}
 
_cairo_clip_drop_cache (&command->header.clip);
cairo_status_t
_cairo_recording_surface_replay_one (cairo_recording_surface_t *surface,
long unsigned index,
cairo_surface_t *target)
{
cairo_surface_wrapper_t wrapper;
cairo_command_t **elements, *command;
cairo_int_status_t status;
 
if (unlikely (surface->base.status))
return surface->base.status;
 
if (unlikely (target->status))
return target->status;
 
if (unlikely (surface->base.finished))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
assert (_cairo_surface_is_recording (&surface->base));
 
/* XXX
* Use a surface wrapper because we may want to do transformed
* replay in the future.
*/
_cairo_surface_wrapper_init (&wrapper, target);
 
if (index > surface->commands.num_elements)
return _cairo_error (CAIRO_STATUS_READ_ERROR);
 
elements = _cairo_array_index (&surface->commands, 0);
command = elements[index];
switch (command->header.type) {
case CAIRO_COMMAND_PAINT:
status = _cairo_surface_wrapper_paint (&wrapper,
command->header.op,
&command->paint.source.base,
command->header.clip);
break;
 
case CAIRO_COMMAND_MASK:
status = _cairo_surface_wrapper_mask (&wrapper,
command->header.op,
&command->mask.source.base,
&command->mask.mask.base,
command->header.clip);
break;
 
case CAIRO_COMMAND_STROKE:
status = _cairo_surface_wrapper_stroke (&wrapper,
command->header.op,
&command->stroke.source.base,
&command->stroke.path,
&command->stroke.style,
&command->stroke.ctm,
&command->stroke.ctm_inverse,
command->stroke.tolerance,
command->stroke.antialias,
command->header.clip);
break;
 
case CAIRO_COMMAND_FILL:
status = _cairo_surface_wrapper_fill (&wrapper,
command->header.op,
&command->fill.source.base,
&command->fill.path,
command->fill.fill_rule,
command->fill.tolerance,
command->fill.antialias,
command->header.clip);
break;
 
case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
status = _cairo_surface_wrapper_show_text_glyphs (&wrapper,
command->header.op,
&command->show_text_glyphs.source.base,
command->show_text_glyphs.utf8, command->show_text_glyphs.utf8_len,
command->show_text_glyphs.glyphs, command->show_text_glyphs.num_glyphs,
command->show_text_glyphs.clusters, command->show_text_glyphs.num_clusters,
command->show_text_glyphs.cluster_flags,
command->show_text_glyphs.scaled_font,
command->header.clip);
break;
 
default:
ASSERT_NOT_REACHED;
}
 
_cairo_surface_wrapper_fini (&wrapper);
 
return _cairo_surface_set_error (surface, status);
return _cairo_surface_set_error (&surface->base, status);
}
 
/**
* _cairo_recording_surface_replay:
* @surface: the #cairo_recording_surface_t
1008,12 → 1894,24
_cairo_recording_surface_replay (cairo_surface_t *surface,
cairo_surface_t *target)
{
return _cairo_recording_surface_replay_internal (surface, NULL,
target,
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL,
target, NULL,
CAIRO_RECORDING_REPLAY,
CAIRO_RECORDING_REGION_ALL);
}
 
cairo_status_t
_cairo_recording_surface_replay_with_clip (cairo_surface_t *surface,
const cairo_matrix_t *surface_transform,
cairo_surface_t *target,
const cairo_clip_t *target_clip)
{
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, surface_transform,
target, target_clip,
CAIRO_RECORDING_REPLAY,
CAIRO_RECORDING_REGION_ALL);
}
 
/* Replay recording to surface. When the return status of each operation is
* one of %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED, or
* %CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY the status of each operation
1024,8 → 1922,8
_cairo_recording_surface_replay_and_create_regions (cairo_surface_t *surface,
cairo_surface_t *target)
{
return _cairo_recording_surface_replay_internal (surface, NULL,
target,
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface, NULL, NULL,
target, NULL,
CAIRO_RECORDING_CREATE_REGIONS,
CAIRO_RECORDING_REGION_ALL);
}
1036,8 → 1934,9
cairo_surface_t *target,
cairo_recording_region_type_t region)
{
return _cairo_recording_surface_replay_internal (surface, surface_extents,
target,
return _cairo_recording_surface_replay_internal ((cairo_recording_surface_t *) surface,
surface_extents, NULL,
target, NULL,
CAIRO_RECORDING_REPLAY,
region);
}
1051,7 → 1950,7
cairo_surface_t *analysis_surface;
cairo_status_t status;
 
null_surface = _cairo_null_surface_create (surface->content);
null_surface = _cairo_null_surface_create (surface->base.content);
analysis_surface = _cairo_analysis_surface_create (null_surface);
cairo_surface_destroy (null_surface);
 
1095,7 → 1994,7
 
memset (&bbox, 0, sizeof (bbox));
 
if (! _cairo_surface_is_recording (surface)) {
if (surface->status || ! _cairo_surface_is_recording (surface)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
goto DONE;
}
1132,3 → 2031,42
 
return _recording_surface_get_ink_bbox (surface, bbox, transform);
}
 
cairo_status_t
_cairo_recording_surface_get_ink_bbox (cairo_recording_surface_t *surface,
cairo_box_t *bbox,
const cairo_matrix_t *transform)
{
return _recording_surface_get_ink_bbox (surface, bbox, transform);
}
 
/**
* cairo_recording_surface_get_extents:
* @surface: a #cairo_recording_surface_t
* @extents: the #cairo_rectangle_t to be assigned the extents
*
* Get the extents of the recording-surface.
*
* Return value: %TRUE if the surface is bounded, of recording type, and
* not in an error state, otherwise %FALSE
*
* Since: 1.12
**/
cairo_bool_t
cairo_recording_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_t *extents)
{
cairo_recording_surface_t *record;
 
if (surface->status || ! _cairo_surface_is_recording (surface)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return FALSE;
}
 
record = (cairo_recording_surface_t *)surface;
if (record->unbounded)
return FALSE;
 
*extents = record->extents_pixels;
return TRUE;
}
/programs/develop/libraries/cairo/src/cairo-rectangle.c
39,6 → 39,15
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
 
const cairo_rectangle_int_t _cairo_empty_rectangle = { 0, 0, 0, 0 };
const cairo_rectangle_int_t _cairo_unbounded_rectangle = {
CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN,
CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN,
};
 
cairo_private void
_cairo_box_from_doubles (cairo_box_t *box,
double *x1, double *y1,
76,23 → 85,11
int num_boxes,
cairo_box_t *extents)
{
int n;
 
assert (num_boxes > 0);
*extents = *boxes;
 
for (n = 1; n < num_boxes; n++) {
if (boxes[n].p1.x < extents->p1.x)
extents->p1.x = boxes[n].p1.x;
if (boxes[n].p2.x > extents->p2.x)
extents->p2.x = boxes[n].p2.x;
 
if (boxes[n].p1.y < extents->p1.y)
extents->p1.y = boxes[n].p1.y;
if (boxes[n].p2.y > extents->p2.y)
extents->p2.y = boxes[n].p2.y;
while (--num_boxes)
_cairo_box_add_box (extents, ++boxes);
}
}
 
/* XXX We currently have a confusing mix of boxes and rectangles as
* exemplified by this function. A #cairo_box_t is a rectangular area
149,6 → 146,29
}
}
 
/* Extends the dst rectangle to also contain src.
* If one of the rectangles is empty, the result is undefined
*/
void
_cairo_rectangle_union (cairo_rectangle_int_t *dst,
const cairo_rectangle_int_t *src)
{
int x1, y1, x2, y2;
 
x1 = MIN (dst->x, src->x);
y1 = MIN (dst->y, src->y);
/* Beware the unsigned promotion, fortunately we have bits to spare
* as (CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN) < UINT_MAX
*/
x2 = MAX (dst->x + (int) dst->width, src->x + (int) src->width);
y2 = MAX (dst->y + (int) dst->height, src->y + (int) src->height);
 
dst->x = x1;
dst->y = y1;
dst->width = x2 - x1;
dst->height = y2 - y1;
}
 
#define P1x (line->p1.x)
#define P1y (line->p1.y)
#define P2x (line->p2.x)
169,7 → 189,7
*/
 
cairo_bool_t
_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
_cairo_box_intersects_line_segment (const cairo_box_t *box, cairo_line_t *line)
{
cairo_fixed_t t1=0, t2=0, t3=0, t4=0;
cairo_int64_t t1y, t2y, t3x, t4x;
238,11 → 258,42
return FALSE;
}
 
cairo_bool_t
_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point)
static cairo_status_t
_cairo_box_add_spline_point (void *closure,
const cairo_point_t *point,
const cairo_slope_t *tangent)
{
if (point->x < box->p1.x || point->x > box->p2.x ||
point->y < box->p1.y || point->y > box->p2.y)
return FALSE;
return TRUE;
_cairo_box_add_point (closure, point);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* assumes a has been previously added */
void
_cairo_box_add_curve_to (cairo_box_t *extents,
const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d)
{
_cairo_box_add_point (extents, d);
if (!_cairo_box_contains_point (extents, b) ||
!_cairo_box_contains_point (extents, c))
{
cairo_status_t status;
 
status = _cairo_spline_bound (_cairo_box_add_spline_point,
extents, a, b, c, d);
assert (status == CAIRO_STATUS_SUCCESS);
}
}
 
void
_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
const cairo_rectangle_t *rectf)
{
recti->x = floor (rectf->x);
recti->y = floor (rectf->y);
recti->width = ceil (rectf->x + rectf->width) - floor (rectf->x);
recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y);
}
/programs/develop/libraries/cairo/src/cairo-rectangular-scan-converter.c
33,7 → 33,7
 
#include "cairoint.h"
 
#include "cairo-combsort-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-error-private.h"
#include "cairo-freelist-private.h"
#include "cairo-list-private.h"
379,9 → 379,10
for (cell = sweep->coverage.head.next; cell != &sweep->coverage.tail; cell = cell->next) {
if (cell->x != prev_x && coverage != prev_coverage) {
int n = sweep->num_spans++;
int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].x = prev_x;
sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
sweep->spans[n].inverse = 0;
sweep->spans[n].coverage = c - (c >> 8);
prev_coverage = coverage;
}
 
388,9 → 389,10
coverage += cell->covered;
if (coverage != prev_coverage) {
int n = sweep->num_spans++;
int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].x = cell->x;
sweep->spans[n].coverage = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].coverage -= sweep->spans[n].coverage >> 8;
sweep->spans[n].inverse = 0;
sweep->spans[n].coverage = c - (c >> 8);
prev_coverage = coverage;
}
coverage += cell->uncovered;
401,13 → 403,16
if (sweep->num_spans) {
if (prev_x <= sweep->xmax) {
int n = sweep->num_spans++;
int c = coverage >> (CAIRO_FIXED_FRAC_BITS * 2 - 8);
sweep->spans[n].x = prev_x;
sweep->spans[n].coverage = coverage;
sweep->spans[n].inverse = 0;
sweep->spans[n].coverage = c - (c >> 8);
}
 
if (coverage && prev_x < sweep->xmax) {
int n = sweep->num_spans++;
sweep->spans[n].x = sweep->xmax;
sweep->spans[n].inverse = 1;
sweep->spans[n].coverage = 0;
}
}
489,13 → 494,13
cairo_status_t status;
 
sweep_line_init (&sweep_line);
sweep_line.xmin = self->xmin;
sweep_line.xmax = self->xmax;
sweep_line.xmin = _cairo_fixed_integer_part (self->extents.p1.x);
sweep_line.xmax = _cairo_fixed_integer_part (self->extents.p2.x);
sweep_line.start = rectangles;
if ((status = setjmp (sweep_line.jmpbuf)))
goto BAIL;
goto out;
 
sweep_line.current_y = self->ymin;
sweep_line.current_y = _cairo_fixed_integer_part (self->extents.p1.y);
start = *sweep_line.start++;
do {
if (start->top_y != sweep_line.current_y) {
554,9 → 559,7
goto out;
}
 
sweep_line.current_y++;
 
do {
while (++sweep_line.current_y < _cairo_fixed_integer_part (self->extents.p2.y)) {
if (stop->bottom_y != sweep_line.current_y) {
render_rows (&sweep_line, renderer,
stop->bottom_y - sweep_line.current_y);
572,22 → 575,83
goto out;
} while (stop->bottom_y == sweep_line.current_y);
 
sweep_line.current_y++;
} while (TRUE);
}
 
out:
status = renderer->render_rows (renderer,
sweep_line.current_y,
self->ymax - sweep_line.current_y,
NULL, 0);
 
BAIL:
sweep_line_fini (&sweep_line);
 
return status;
}
static void generate_row(cairo_span_renderer_t *renderer,
const rectangle_t *r,
int y, int h,
uint16_t coverage)
{
cairo_half_open_span_t spans[4];
unsigned int num_spans = 0;
int x1 = _cairo_fixed_integer_part (r->left);
int x2 = _cairo_fixed_integer_part (r->right);
if (x2 > x1) {
if (! _cairo_fixed_is_integer (r->left)) {
spans[num_spans].x = x1;
spans[num_spans].coverage =
coverage * (256 - _cairo_fixed_fractional_part (r->left)) >> 8;
num_spans++;
x1++;
}
 
if (x2 > x1) {
spans[num_spans].x = x1;
spans[num_spans].coverage = coverage - (coverage >> 8);
num_spans++;
}
 
if (! _cairo_fixed_is_integer (r->right)) {
spans[num_spans].x = x2++;
spans[num_spans].coverage =
coverage * _cairo_fixed_fractional_part (r->right) >> 8;
num_spans++;
}
} else {
spans[num_spans].x = x2++;
spans[num_spans].coverage = coverage * (r->right - r->left) >> 8;
num_spans++;
}
 
spans[num_spans].x = x2;
spans[num_spans].coverage = 0;
num_spans++;
 
renderer->render_rows (renderer, y, h, spans, num_spans);
}
 
static cairo_status_t
generate_box (cairo_rectangular_scan_converter_t *self,
cairo_span_renderer_t *renderer)
{
const rectangle_t *r = self->chunks.base;
int y1 = _cairo_fixed_integer_part (r->top);
int y2 = _cairo_fixed_integer_part (r->bottom);
if (y2 > y1) {
if (! _cairo_fixed_is_integer (r->top)) {
generate_row(renderer, r, y1, 1,
256 - _cairo_fixed_fractional_part (r->top));
y1++;
}
 
if (y2 > y1)
generate_row(renderer, r, y1, y2-y1, 256);
 
if (! _cairo_fixed_is_integer (r->bottom))
generate_row(renderer, r, y2, 1,
_cairo_fixed_fractional_part (r->bottom));
} else
generate_row(renderer, r, y1, 1, r->bottom - r->top);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_rectangular_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
600,10 → 664,14
 
if (unlikely (self->num_rectangles == 0)) {
return renderer->render_rows (renderer,
self->ymin, self->ymax - self->ymin,
_cairo_fixed_integer_part (self->extents.p1.y),
_cairo_fixed_integer_part (self->extents.p2.y - self->extents.p1.y),
NULL, 0);
}
 
if (self->num_rectangles == 1)
return generate_box (self, renderer);
 
rectangles = rectangles_stack;
if (unlikely (self->num_rectangles >= ARRAY_LENGTH (rectangles_stack))) {
rectangles = _cairo_malloc_ab (self->num_rectangles + 1,
672,17 → 740,22
if (unlikely (rectangle == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
rectangle->left = box->p1.x;
rectangle->right = box->p2.x;
rectangle->dir = dir;
rectangle->left = MAX (box->p1.x, self->extents.p1.x);
rectangle->right = MIN (box->p2.x, self->extents.p2.x);
if (unlikely (rectangle->right <= rectangle->left)) {
self->tail->count--;
return CAIRO_STATUS_SUCCESS;
}
 
rectangle->top = box->p1.y;
rectangle->top_y = _cairo_fixed_integer_floor (box->p1.y);
rectangle->bottom = box->p2.y;
rectangle->bottom_y = _cairo_fixed_integer_floor (box->p2.y);
assert (rectangle->bottom_y >= rectangle->top_y);
 
rectangle->top = MAX (box->p1.y, self->extents.p1.y);
rectangle->top_y = _cairo_fixed_integer_floor (rectangle->top);
rectangle->bottom = MIN (box->p2.y, self->extents.p2.y);
rectangle->bottom_y = _cairo_fixed_integer_floor (rectangle->bottom);
if (likely (rectangle->bottom > rectangle->top))
self->num_rectangles++;
else
self->tail->count--;
 
return CAIRO_STATUS_SUCCESS;
}
704,14 → 777,9
const cairo_rectangle_int_t *extents)
{
self->base.destroy = _cairo_rectangular_scan_converter_destroy;
self->base.add_edge = NULL;
self->base.add_polygon = NULL;
self->base.generate = _cairo_rectangular_scan_converter_generate;
 
self->xmin = extents->x;
self->xmax = extents->x + extents->width;
self->ymin = extents->y;
self->ymax = extents->y + extents->height;
_cairo_box_from_rectangle (&self->extents, extents);
 
self->chunks.base = self->buf;
self->chunks.next = NULL;
/programs/develop/libraries/cairo/src/cairo-reference-count-private.h
45,6 → 45,7
} cairo_reference_count_t;
 
#define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count)
#define _cairo_reference_count_dec(RC) _cairo_atomic_int_dec (&(RC)->ref_count)
#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count)
 
#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE))
/programs/develop/libraries/cairo/src/cairo-region-private.h
66,6 → 66,12
cairo_private void
_cairo_region_fini (cairo_region_t *region);
 
cairo_private cairo_region_t *
_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count);
 
cairo_private cairo_box_t *
_cairo_region_get_boxes (const cairo_region_t *region, int *nbox);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_REGION_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-region.c
52,7 → 52,7
* Regions are a simple graphical data type representing an area of
* integer-aligned rectangles. They are often used on raster surfaces
* to track areas of interest, such as change or clip areas.
*/
**/
 
static const cairo_region_t _cairo_region_nil = {
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
104,6 → 104,8
case CAIRO_STATUS_INVALID_SLANT:
case CAIRO_STATUS_INVALID_WEIGHT:
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
case CAIRO_STATUS_DEVICE_FINISHED:
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_region_t *) &_cairo_region_nil;
134,8 → 136,8
_cairo_region_set_error (cairo_region_t *region,
cairo_status_t status)
{
if (! _cairo_status_is_error (status))
return status;
if (status == CAIRO_STATUS_SUCCESS)
return CAIRO_STATUS_SUCCESS;
 
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
234,6 → 236,17
if (unlikely (region == NULL))
return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
region->status = CAIRO_STATUS_SUCCESS;
 
if (count == 1) {
pixman_region32_init_rect (&region->rgn,
rects->x, rects->y,
rects->width, rects->height);
 
return region;
}
 
if (count > ARRAY_LENGTH (stack_pboxes)) {
pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t));
if (unlikely (pboxes == NULL)) {
259,12 → 272,42
return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
return region;
}
slim_hidden_def (cairo_region_create_rectangles);
 
cairo_region_t *
_cairo_region_create_from_boxes (const cairo_box_t *boxes, int count)
{
cairo_region_t *region;
 
region = _cairo_malloc (sizeof (cairo_region_t));
if (unlikely (region == NULL))
return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
CAIRO_REFERENCE_COUNT_INIT (&region->ref_count, 1);
region->status = CAIRO_STATUS_SUCCESS;
 
if (! pixman_region32_init_rects (&region->rgn,
(pixman_box32_t *)boxes, count)) {
free (region);
return _cairo_region_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
return region;
}
slim_hidden_def (cairo_region_create_rectangles);
 
cairo_box_t *
_cairo_region_get_boxes (const cairo_region_t *region, int *nbox)
{
if (region->status) {
nbox = 0;
return NULL;
}
 
return (cairo_box_t *) pixman_region32_rectangles (CONST_CAST &region->rgn, nbox);
}
 
/**
* cairo_region_create_rectangle:
* @rectangle: a #cairo_rectangle_int_t
473,7 → 516,7
* cairo_region_status:
* @region: a #cairo_region_t
*
* Checks whether an error has previous occured for this
* Checks whether an error has previous occurred for this
* region object.
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
799,13 → 842,15
 
/**
* cairo_region_overlap_t:
* @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region
* @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region
* @CAIRO_REGION_OVERLAP_IN: The contents are entirely inside the region. (Since 1.10)
* @CAIRO_REGION_OVERLAP_OUT: The contents are entirely outside the region. (Since 1.10)
* @CAIRO_REGION_OVERLAP_PART: The contents are partially inside and
* partially outside the region.
* partially outside the region. (Since 1.10)
*
* Used as the return value for cairo_region_contains_rectangle().
*/
*
* Since: 1.10
**/
 
/**
* cairo_region_contains_rectangle:
/programs/develop/libraries/cairo/src/cairo-rtree-private.h
38,10 → 38,11
#define CAIRO_RTREE_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-types-private.h"
 
#include "cairo-freelist-private.h"
#include "cairo-list-private.h"
#include "cairo-list-inline.h"
 
enum {
CAIRO_RTREE_NODE_AVAILABLE,
51,7 → 52,6
 
typedef struct _cairo_rtree_node {
struct _cairo_rtree_node *children[4], *parent;
void **owner;
cairo_list_t link;
uint16_t pinned;
uint16_t state;
65,6 → 65,7
cairo_list_t pinned;
cairo_list_t available;
cairo_list_t evictable;
void (*destroy) (cairo_rtree_node_t *);
cairo_freepool_t node_freepool;
} cairo_rtree_t;
 
97,7 → 98,8
int width,
int height,
int min_size,
int node_size);
int node_size,
void (*destroy)(cairo_rtree_node_t *));
 
cairo_private cairo_int_status_t
_cairo_rtree_insert (cairo_rtree_t *rtree,
111,9 → 113,15
int height,
cairo_rtree_node_t **out);
 
cairo_private void
_cairo_rtree_foreach (cairo_rtree_t *rtree,
void (*func)(cairo_rtree_node_t *, void *data),
void *data);
 
static inline void *
_cairo_rtree_pin (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
{
assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
if (! node->pinned) {
cairo_list_move (&node->link, &rtree->pinned);
node->pinned = 1;
/programs/develop/libraries/cairo/src/cairo-rtree.c
57,7 → 57,6
 
node->children[0] = NULL;
node->parent = parent;
node->owner = NULL;
node->state = CAIRO_RTREE_NODE_AVAILABLE;
node->pinned = FALSE;
node->x = x;
78,8 → 77,7
cairo_list_del (&node->link);
 
if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
if (node->owner != NULL)
*node->owner = NULL;
rtree->destroy (node);
} else {
for (i = 0; i < 4 && node->children[i] != NULL; i++)
_cairo_rtree_node_destroy (rtree, node->children[i]);
186,6 → 184,8
assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
assert (node->pinned == FALSE);
 
rtree->destroy (node);
 
node->state = CAIRO_RTREE_NODE_AVAILABLE;
cairo_list_move (&node->link, &rtree->available);
 
225,16 → 225,23
int height,
cairo_rtree_node_t **out)
{
cairo_int_status_t ret = CAIRO_INT_STATUS_UNSUPPORTED;
cairo_rtree_node_t *node, *next;
cairo_list_t tmp_pinned;
int i, cnt;
 
cairo_list_init (&tmp_pinned);
 
/* propagate pinned from children to root */
cairo_list_foreach_entry_safe (node, next, cairo_rtree_node_t,
&rtree->pinned, link)
{
if (node->parent != NULL)
_cairo_rtree_pin (rtree, node->parent);
cairo_list_foreach_entry_safe (node, next,
cairo_rtree_node_t, &rtree->pinned, link) {
node = node->parent;
while (node && ! node->pinned) {
node->pinned = 1;
cairo_list_move (&node->link, &tmp_pinned);
node = node->parent;
}
}
 
cnt = 0;
cairo_list_foreach_entry (node, cairo_rtree_node_t,
245,7 → 252,7
}
 
if (cnt == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
goto out;
 
cnt = hars_petruska_f54_1_random () % cnt;
cairo_list_foreach_entry (node, cairo_rtree_node_t,
253,8 → 260,7
{
if (node->width >= width && node->height >= height && cnt-- == 0) {
if (node->state == CAIRO_RTREE_NODE_OCCUPIED) {
if (node->owner != NULL)
*node->owner = NULL;
rtree->destroy (node);
} else {
for (i = 0; i < 4 && node->children[i] != NULL; i++)
_cairo_rtree_node_destroy (rtree, node->children[i]);
265,69 → 271,39
cairo_list_move (&node->link, &rtree->available);
 
*out = node;
return CAIRO_STATUS_SUCCESS;
ret = CAIRO_STATUS_SUCCESS;
break;
}
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
out:
while (! cairo_list_is_empty (&tmp_pinned)) {
node = cairo_list_first_entry (&tmp_pinned, cairo_rtree_node_t, link);
node->pinned = 0;
cairo_list_move (&node->link, &rtree->evictable);
}
return ret;
}
 
void
_cairo_rtree_unpin (cairo_rtree_t *rtree)
{
cairo_rtree_node_t *node, *next;
cairo_list_t can_collapse;
 
if (cairo_list_is_empty (&rtree->pinned))
return;
 
cairo_list_init (&can_collapse);
 
cairo_list_foreach_entry_safe (node, next,
while (! cairo_list_is_empty (&rtree->pinned)) {
cairo_rtree_node_t *node = cairo_list_first_entry (&rtree->pinned,
cairo_rtree_node_t,
&rtree->pinned,
link)
{
node->pinned = FALSE;
if (node->state == CAIRO_RTREE_NODE_OCCUPIED && node->owner == NULL) {
cairo_bool_t all_available;
int i;
 
node->state = CAIRO_RTREE_NODE_AVAILABLE;
cairo_list_move (&node->link, &rtree->available);
 
all_available = TRUE;
node = node->parent;
for (i = 0; i < 4 && node->children[i] != NULL && all_available; i++)
all_available &= node->children[i]->state == CAIRO_RTREE_NODE_AVAILABLE;
 
if (all_available) {
cairo_list_move (&node->link, &can_collapse);
for (i = 0; i < 4 && node->children[i] != NULL; i++)
cairo_list_del (&node->children[i]->link);
}
}
else
{
link);
node->pinned = 0;
cairo_list_move (&node->link, &rtree->evictable);
}
}
 
cairo_list_foreach_entry_safe (node, next,
cairo_rtree_node_t,
&can_collapse,
link)
{
_cairo_rtree_node_collapse (rtree, node);
}
}
 
void
_cairo_rtree_init (cairo_rtree_t *rtree,
int width,
int height,
int min_size,
int node_size)
int node_size,
void (*destroy) (cairo_rtree_node_t *))
{
assert (node_size >= (int) sizeof (cairo_rtree_node_t));
_cairo_freepool_init (&rtree->node_freepool, node_size);
337,6 → 313,7
cairo_list_init (&rtree->evictable);
 
rtree->min_size = min_size;
rtree->destroy = destroy;
 
memset (&rtree->root, 0, sizeof (rtree->root));
rtree->root.width = width;
351,8 → 328,7
int i;
 
if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
if (rtree->root.owner != NULL)
*rtree->root.owner = NULL;
rtree->destroy (&rtree->root);
} else {
for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
_cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
368,14 → 344,41
cairo_list_add (&rtree->root.link, &rtree->available);
}
 
static void
_cairo_rtree_node_foreach (cairo_rtree_node_t *node,
void (*func)(cairo_rtree_node_t *, void *data),
void *data)
{
int i;
 
for (i = 0; i < 4 && node->children[i] != NULL; i++)
_cairo_rtree_node_foreach(node->children[i], func, data);
 
func(node, data);
}
 
void
_cairo_rtree_foreach (cairo_rtree_t *rtree,
void (*func)(cairo_rtree_node_t *, void *data),
void *data)
{
int i;
 
if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
func(&rtree->root, data);
} else {
for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
_cairo_rtree_node_foreach (rtree->root.children[i], func, data);
}
}
 
void
_cairo_rtree_fini (cairo_rtree_t *rtree)
{
int i;
 
if (rtree->root.state == CAIRO_RTREE_NODE_OCCUPIED) {
if (rtree->root.owner != NULL)
*rtree->root.owner = NULL;
rtree->destroy (&rtree->root);
} else {
for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
_cairo_rtree_node_destroy (rtree, rtree->root.children[i]);
/programs/develop/libraries/cairo/src/cairo-scaled-font-private.h
45,6 → 45,8
#include "cairo-mutex-type-private.h"
#include "cairo-reference-count-private.h"
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t;
 
struct _cairo_scaled_font {
112,13 → 114,7
cairo_bool_t cache_frozen;
cairo_bool_t global_cache_frozen;
 
/*
* One surface backend may store data in each glyph.
* Whichever surface manages to store its pointer here
* first gets to store data in each glyph
*/
const cairo_surface_backend_t *surface_backend;
void *surface_private;
cairo_list_t dev_privates;
 
/* font backend managing this scaled font */
const cairo_scaled_font_backend_t *backend;
125,7 → 121,63
cairo_list_t link;
};
 
struct _cairo_scaled_font_private {
cairo_list_t link;
const void *key;
void (*destroy) (cairo_scaled_font_private_t *,
cairo_scaled_font_t *);
};
 
struct _cairo_scaled_glyph {
cairo_hash_entry_t hash_entry;
 
cairo_text_extents_t metrics; /* user-space metrics */
cairo_text_extents_t fs_metrics; /* font-space metrics */
cairo_box_t bbox; /* device-space bounds */
int16_t x_advance; /* device-space rounded X advance */
int16_t y_advance; /* device-space rounded Y advance */
 
unsigned int has_info;
cairo_image_surface_t *surface; /* device-space image */
cairo_path_fixed_t *path; /* device-space outline */
cairo_surface_t *recording_surface; /* device-space recording-surface */
 
const void *dev_private_key;
void *dev_private;
cairo_list_t dev_privates;
};
 
struct _cairo_scaled_glyph_private {
cairo_list_t link;
const void *key;
void (*destroy) (cairo_scaled_glyph_private_t *,
cairo_scaled_glyph_t *,
cairo_scaled_font_t *);
};
 
cairo_private cairo_scaled_font_private_t *
_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
const void *key);
 
cairo_private void
_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font);
_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
cairo_scaled_font_private_t *priv,
const void *key,
void (*destroy) (cairo_scaled_font_private_t *,
cairo_scaled_font_t *));
 
cairo_private cairo_scaled_glyph_private_t *
_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
const void *key);
 
cairo_private void
_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_private_t *priv,
const void *key,
void (*destroy) (cairo_scaled_glyph_private_t *,
cairo_scaled_glyph_t *,
cairo_scaled_font_t *));
 
CAIRO_END_DECLS
 
#endif /* CAIRO_SCALED_FONT_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-scaled-font-subsets-private.h
47,6 → 47,7
unsigned int subset_glyph_index;
cairo_bool_t is_scaled;
cairo_bool_t is_composite;
cairo_bool_t is_latin;
double x_advance;
double y_advance;
cairo_bool_t utf8_is_mapped;
125,6 → 126,18
_cairo_scaled_font_subsets_destroy (cairo_scaled_font_subsets_t *font_subsets);
 
/**
* _cairo_scaled_font_subsets_enable_latin_subset:
* @font_subsets: a #cairo_scaled_font_subsets_t object to be destroyed
* @use_latin: a #cairo_bool_t indicating if a latin subset is to be used
*
* If enabled, all CP1252 characters will be placed in a separate
* 8-bit latin subset.
**/
cairo_private void
_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
cairo_bool_t use_latin);
 
/**
* _cairo_scaled_font_subsets_map_glyph:
* @font_subsets: a #cairo_scaled_font_subsets_t
* @scaled_font: the font of the glyph to be mapped
207,12 → 220,12
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph_ret);
 
typedef cairo_status_t
typedef cairo_int_status_t
(*cairo_scaled_font_subset_callback_func_t) (cairo_scaled_font_subset_t *font_subset,
void *closure);
 
/**
* _cairo_scaled_font_subsets_foreach:
* _cairo_scaled_font_subsets_foreach_scaled:
* @font_subsets: a #cairo_scaled_font_subsets_t
* @font_subset_callback: a function to be called for each font subset
* @closure: closure data for the callback function
332,7 → 345,7
_cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset);
 
typedef struct _cairo_cff_subset {
char *font_name;
char *family_name_utf8;
char *ps_name;
double *widths;
double x_min, y_min, x_max, y_max;
374,6 → 387,15
_cairo_cff_subset_fini (cairo_cff_subset_t *cff_subset);
 
/**
* _cairo_cff_scaled_font_is_cid_cff:
* @scaled_font: a #cairo_scaled_font_t
*
* Return %TRUE if @scaled_font is a CID CFF font, otherwise return %FALSE.
**/
cairo_private cairo_bool_t
_cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font);
 
/**
* _cairo_cff_fallback_init:
* @cff_subset: a #cairo_cff_subset_t to initialize
* @font_subset: the #cairo_scaled_font_subset_t to initialize from
405,7 → 427,7
_cairo_cff_fallback_fini (cairo_cff_subset_t *cff_subset);
 
typedef struct _cairo_truetype_subset {
char *font_name;
char *family_name_utf8;
char *ps_name;
double *widths;
double x_min, y_min, x_max, y_max;
417,7 → 439,7
} cairo_truetype_subset_t;
 
/**
* _cairo_truetype_subset_init:
* _cairo_truetype_subset_init_ps:
* @truetype_subset: a #cairo_truetype_subset_t to initialize
* @font_subset: the #cairo_scaled_font_subset_t to initialize from
*
425,7 → 447,8
* #cairo_scaled_font_t and the font backend in use) generate a
* truetype file corresponding to @font_subset and initialize
* @truetype_subset with information about the subset and the truetype
* data.
* data. The generated font will be suitable for embedding in
* PostScript.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful,
* %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
433,10 → 456,31
* errors include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset,
_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset);
 
/**
* _cairo_truetype_subset_init_pdf:
* @truetype_subset: a #cairo_truetype_subset_t to initialize
* @font_subset: the #cairo_scaled_font_subset_t to initialize from
*
* If possible (depending on the format of the underlying
* #cairo_scaled_font_t and the font backend in use) generate a
* truetype file corresponding to @font_subset and initialize
* @truetype_subset with information about the subset and the truetype
* data. The generated font will be suitable for embedding in
* PDF.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful,
* %CAIRO_INT_STATUS_UNSUPPORTED if the font can't be subset as a
* truetype file, or an non-zero value indicating an error. Possible
* errors include %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_status_t
_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset);
 
/**
* _cairo_truetype_subset_fini:
* @truetype_subset: a #cairo_truetype_subset_t
*
447,8 → 491,15
cairo_private void
_cairo_truetype_subset_fini (cairo_truetype_subset_t *truetype_subset);
 
cairo_private const char *
_cairo_ps_standard_encoding_to_glyphname (int glyph);
 
cairo_private int
_cairo_unicode_to_winansi (unsigned long unicode);
 
cairo_private const char *
_cairo_winansi_to_glyphname (int glyph);
 
typedef struct _cairo_type1_subset {
char *base_font;
double *widths;
461,8 → 512,6
} cairo_type1_subset_t;
 
 
#if CAIRO_HAS_FT_FONT
 
/**
* _cairo_type1_subset_init:
* @type1_subset: a #cairo_type1_subset_t to initialize
496,9 → 545,6
cairo_private void
_cairo_type1_subset_fini (cairo_type1_subset_t *subset);
 
#endif /* CAIRO_HAS_FT_FONT */
 
 
/**
* _cairo_type1_scaled_font_is_type1:
* @scaled_font: a #cairo_scaled_font_t
530,7 → 576,7
cairo_scaled_font_subset_t *font_subset);
 
/**
* _cairo_type1_fallback_init_hexencode:
* _cairo_type1_fallback_init_hex:
* @type1_subset: a #cairo_type1_subset_t to initialize
* @font_subset: the #cairo_scaled_font_subset_t to initialize from
*
599,23 → 645,6
_cairo_type2_charstrings_fini (cairo_type2_charstrings_t *charstrings);
 
/**
* _cairo_truetype_create_glyph_to_unicode_map:
* @font_subset: the #cairo_scaled_font_subset_t to initialize from
*
* If possible (depending on the format of the underlying
* #cairo_scaled_font_t and the font backend in use) assign
* the unicode character of each glyph in font_subset to
* fontsubset->to_unicode.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful,
* %CAIRO_INT_STATUS_UNSUPPORTED if the unicode encoding of
* the glyphs is not available. Possible errors include
* %CAIRO_STATUS_NO_MEMORY.
**/
cairo_private cairo_int_status_t
_cairo_truetype_create_glyph_to_unicode_map (cairo_scaled_font_subset_t *font_subset);
 
/**
* _cairo_truetype_index_to_ucs4:
* @scaled_font: the #cairo_scaled_font_t
* @index: the glyph index
663,6 → 692,29
char **ps_name,
char **font_name);
 
/**
* _cairo_truetype_get_style:
* @scaled_font: the #cairo_scaled_font_t
* @weight: returns the font weight from the OS/2 table
* @bold: returns true if font is bold
* @italic: returns true if font is italic
*
* If the font is a truetype/opentype font with an OS/2 table, get the
* weight, bold, and italic data from the OS/2 table. The weight
* values have the same meaning as the lfWeight field of the Windows
* LOGFONT structure. Refer to the TrueType Specification for
* definition of the weight values.
*
* Return value: %CAIRO_STATUS_SUCCESS if successful,
* %CAIRO_INT_STATUS_UNSUPPORTED if the font is not TrueType/OpenType
* or the OS/2 table is not present.
**/
cairo_private cairo_int_status_t
_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
int *weight,
cairo_bool_t *bold,
cairo_bool_t *italic);
 
#endif /* CAIRO_HAS_FONT_SUBSET */
 
#endif /* CAIRO_SCALED_FONT_SUBSETS_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-scaled-font-subsets.c
70,6 → 70,7
cairo_bool_t is_scaled;
cairo_bool_t is_composite;
cairo_bool_t is_user;
cairo_bool_t use_latin_subset;
cairo_scaled_font_subsets_t *parent;
cairo_scaled_font_t *scaled_font;
unsigned int font_id;
76,7 → 77,9
 
int current_subset;
int num_glyphs_in_current_subset;
int num_glyphs_in_latin_subset;
int max_glyphs_per_subset;
char latin_char_map[256];
 
cairo_hash_table_t *sub_font_glyphs;
struct _cairo_sub_font *next;
84,6 → 87,7
 
struct _cairo_scaled_font_subsets {
cairo_subsets_type_t type;
cairo_bool_t use_latin_subset;
 
int max_glyphs_per_unscaled_subset_used;
cairo_hash_table_t *unscaled_sub_fonts;
106,6 → 110,8
double x_advance;
double y_advance;
 
cairo_bool_t is_latin;
int latin_character;
cairo_bool_t is_mapped;
uint32_t unicode;
char *utf8;
116,6 → 122,8
unsigned long *glyphs; /* scaled_font_glyph_index */
char **utf8;
unsigned int glyphs_size;
int *to_latin_char;
unsigned long *latin_to_subset_glyph_index;
unsigned int max_glyph;
unsigned int num_glyphs;
 
145,21 → 153,16
sub_font_glyph->base.hash = scaled_font_glyph_index;
}
 
static cairo_bool_t
_cairo_sub_font_glyphs_equal (const void *key_a, const void *key_b)
{
const cairo_sub_font_glyph_t *sub_font_glyph_a = key_a;
const cairo_sub_font_glyph_t *sub_font_glyph_b = key_b;
 
return sub_font_glyph_a->base.hash == sub_font_glyph_b->base.hash;
}
 
static cairo_sub_font_glyph_t *
_cairo_sub_font_glyph_create (unsigned long scaled_font_glyph_index,
unsigned int subset_id,
unsigned int subset_glyph_index,
double x_advance,
double y_advance)
double y_advance,
int latin_character,
uint32_t unicode,
char *utf8,
int utf8_len)
{
cairo_sub_font_glyph_t *sub_font_glyph;
 
174,10 → 177,12
sub_font_glyph->subset_glyph_index = subset_glyph_index;
sub_font_glyph->x_advance = x_advance;
sub_font_glyph->y_advance = y_advance;
sub_font_glyph->is_latin = (latin_character >= 0);
sub_font_glyph->latin_character = latin_character;
sub_font_glyph->is_mapped = FALSE;
sub_font_glyph->unicode = -1;
sub_font_glyph->utf8 = NULL;
sub_font_glyph->utf8_len = 0;
sub_font_glyph->unicode = unicode;
sub_font_glyph->utf8 = utf8;
sub_font_glyph->utf8_len = utf8_len;
 
return sub_font_glyph;
}
185,7 → 190,6
static void
_cairo_sub_font_glyph_destroy (cairo_sub_font_glyph_t *sub_font_glyph)
{
if (sub_font_glyph->utf8 != NULL)
free (sub_font_glyph->utf8);
 
free (sub_font_glyph);
220,6 → 224,10
 
collection->glyphs[subset_glyph_index] = scaled_font_glyph_index;
collection->utf8[subset_glyph_index] = sub_font_glyph->utf8;
collection->to_latin_char[subset_glyph_index] = sub_font_glyph->latin_character;
if (sub_font_glyph->is_latin)
collection->latin_to_subset_glyph_index[sub_font_glyph->latin_character] = subset_glyph_index;
 
if (subset_glyph_index > collection->max_glyph)
collection->max_glyph = subset_glyph_index;
 
266,8 → 274,7
cairo_sub_font_t **sub_font_out)
{
cairo_sub_font_t *sub_font;
cairo_status_t status;
cairo_scaled_font_subsets_glyph_t subset_glyph;
int i;
 
sub_font = malloc (sizeof (cairo_sub_font_t));
if (unlikely (sub_font == NULL))
282,28 → 289,32
sub_font->scaled_font = scaled_font;
sub_font->font_id = font_id;
 
sub_font->use_latin_subset = parent->use_latin_subset;
 
/* latin subsets of Type 3 and CID CFF fonts are not supported */
if (sub_font->is_user || sub_font->is_scaled ||
_cairo_cff_scaled_font_is_cid_cff (scaled_font) )
{
sub_font->use_latin_subset = FALSE;
}
 
if (sub_font->use_latin_subset)
sub_font->current_subset = 1; /* reserve subset 0 for latin glyphs */
else
sub_font->current_subset = 0;
 
sub_font->num_glyphs_in_current_subset = 0;
sub_font->num_glyphs_in_latin_subset = 0;
sub_font->max_glyphs_per_subset = max_glyphs_per_subset;
for (i = 0; i < 256; i++)
sub_font->latin_char_map[i] = FALSE;
 
sub_font->sub_font_glyphs = _cairo_hash_table_create (_cairo_sub_font_glyphs_equal);
sub_font->sub_font_glyphs = _cairo_hash_table_create (NULL);
if (unlikely (sub_font->sub_font_glyphs == NULL)) {
free (sub_font);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
sub_font->next = NULL;
 
/* Reserve first glyph in subset for the .notdef glyph except for
* Type 3 fonts */
if (! is_scaled) {
status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &subset_glyph);
if (unlikely (status)) {
_cairo_hash_table_destroy (sub_font->sub_font_glyphs);
free (sub_font);
return status;
}
}
 
*sub_font_out = sub_font;
return CAIRO_STATUS_SUCCESS;
}
329,10 → 340,39
_cairo_sub_font_destroy (sub_font);
}
 
/* Characters 0x80 to 0x9f in the winansi encoding.
* All other characters in the range 0x00 to 0xff map 1:1 to unicode */
static unsigned int _winansi_0x80_to_0x9f[] = {
0x20ac, 0x0000, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017d, 0x0000,
0x0000, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x0000, 0x017e, 0x0178
};
 
int
_cairo_unicode_to_winansi (unsigned long uni)
{
int i;
 
/* exclude the extra "hyphen" at 0xad to avoid duplicate glyphnames */
if ((uni >= 0x20 && uni <= 0x7e) ||
(uni >= 0xa1 && uni <= 0xff && uni != 0xad) ||
uni == 0)
return uni;
 
for (i = 0; i < 32; i++)
if (_winansi_0x80_to_0x9f[i] == uni)
return i + 0x80;
 
return -1;
}
 
static cairo_status_t
_cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index)
_cairo_sub_font_glyph_lookup_unicode (cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index,
uint32_t *unicode_out,
char **utf8_out,
int *utf8_len_out)
{
uint32_t unicode;
char buf[8];
356,19 → 396,19
return status;
}
 
sub_font_glyph->unicode = unicode;
sub_font_glyph->utf8 = NULL;
sub_font_glyph->utf8_len = 0;
*unicode_out = unicode;
*utf8_out = NULL;
*utf8_len_out = 0;
if (unicode != (uint32_t) -1) {
len = _cairo_ucs4_to_utf8 (unicode, buf);
if (len > 0) {
sub_font_glyph->utf8 = malloc (len + 1);
if (unlikely (sub_font_glyph->utf8 == NULL))
*utf8_out = malloc (len + 1);
if (unlikely (*utf8_out == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (sub_font_glyph->utf8, buf, len);
sub_font_glyph->utf8[len] = 0;
sub_font_glyph->utf8_len = len;
memcpy (*utf8_out, buf, len);
(*utf8_out)[len] = 0;
*utf8_len_out = len;
}
}
 
429,9 → 469,14
if (sub_font_glyph != NULL) {
subset_glyph->font_id = sub_font->font_id;
subset_glyph->subset_id = sub_font_glyph->subset_id;
if (sub_font_glyph->is_latin)
subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
else
subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
 
subset_glyph->is_scaled = sub_font->is_scaled;
subset_glyph->is_composite = sub_font->is_composite;
subset_glyph->is_latin = sub_font_glyph->is_latin;
subset_glyph->x_advance = sub_font_glyph->x_advance;
subset_glyph->y_advance = sub_font_glyph->y_advance;
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
446,37 → 491,22
}
 
static cairo_status_t
_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
_cairo_sub_font_add_glyph (cairo_sub_font_t *sub_font,
unsigned long scaled_font_glyph_index,
const char *utf8,
cairo_bool_t is_latin,
int latin_character,
uint32_t unicode,
char *utf8,
int utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
cairo_sub_font_glyph_t **sub_font_glyph_out)
{
cairo_sub_font_glyph_t key, *sub_font_glyph;
cairo_status_t status;
 
_cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
&key.base);
if (sub_font_glyph == NULL) {
cairo_scaled_glyph_t *scaled_glyph;
cairo_sub_font_glyph_t *sub_font_glyph;
int *num_glyphs_in_subset_ptr;
double x_advance;
double y_advance;
cairo_int_status_t status;
 
if (sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
{
cairo_scaled_font_subsets_glyph_t tmp_subset_glyph;
 
sub_font->current_subset++;
sub_font->num_glyphs_in_current_subset = 0;
 
/* Reserve first glyph in subset for the .notdef glyph
* except for Type 3 fonts */
if (! _cairo_font_face_is_user (sub_font->scaled_font->font_face)) {
status = _cairo_sub_font_map_glyph (sub_font, 0, NULL, -1, &tmp_subset_glyph);
if (unlikely (status))
return status;
}
}
 
_cairo_scaled_font_freeze_cache (sub_font->scaled_font);
status = _cairo_scaled_glyph_lookup (sub_font->scaled_font,
scaled_font_glyph_index,
488,24 → 518,52
return status;
}
 
sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
sub_font->current_subset,
sub_font->num_glyphs_in_current_subset,
scaled_glyph->metrics.x_advance,
scaled_glyph->metrics.y_advance);
x_advance = scaled_glyph->metrics.x_advance;
y_advance = scaled_glyph->metrics.y_advance;
_cairo_scaled_font_thaw_cache (sub_font->scaled_font);
 
if (unlikely (sub_font_glyph == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (!is_latin && sub_font->num_glyphs_in_current_subset == sub_font->max_glyphs_per_subset)
{
sub_font->current_subset++;
sub_font->num_glyphs_in_current_subset = 0;
}
 
status = _cairo_sub_font_glyph_lookup_unicode (sub_font_glyph,
sub_font->scaled_font,
scaled_font_glyph_index);
if (unlikely (status)) {
_cairo_sub_font_glyph_destroy (sub_font_glyph);
if (is_latin)
num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_latin_subset;
else
num_glyphs_in_subset_ptr = &sub_font->num_glyphs_in_current_subset;
 
/* Reserve first glyph in subset for the .notdef glyph except for
* Type 3 fonts */
if (*num_glyphs_in_subset_ptr == 0 &&
scaled_font_glyph_index != 0 &&
! _cairo_font_face_is_user (sub_font->scaled_font->font_face))
{
status = _cairo_sub_font_add_glyph (sub_font,
0,
is_latin,
0,
0,
NULL,
-1,
&sub_font_glyph);
if (unlikely (status))
return status;
}
 
sub_font_glyph = _cairo_sub_font_glyph_create (scaled_font_glyph_index,
is_latin ? 0 : sub_font->current_subset,
*num_glyphs_in_subset_ptr,
x_advance,
y_advance,
is_latin ? latin_character : -1,
unicode,
utf8,
utf8_len);
 
if (unlikely (sub_font_glyph == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_hash_table_insert (sub_font->sub_font_glyphs, &sub_font_glyph->base);
if (unlikely (status)) {
_cairo_sub_font_glyph_destroy (sub_font_glyph);
512,26 → 570,121
return status;
}
 
sub_font->num_glyphs_in_current_subset++;
 
(*num_glyphs_in_subset_ptr)++;
if (sub_font->is_scaled) {
if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_scaled_subset_used)
sub_font->parent->max_glyphs_per_scaled_subset_used = sub_font->num_glyphs_in_current_subset;
if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_scaled_subset_used)
sub_font->parent->max_glyphs_per_scaled_subset_used = *num_glyphs_in_subset_ptr;
} else {
if (sub_font->num_glyphs_in_current_subset > sub_font->parent->max_glyphs_per_unscaled_subset_used)
sub_font->parent->max_glyphs_per_unscaled_subset_used = sub_font->num_glyphs_in_current_subset;
if (*num_glyphs_in_subset_ptr > sub_font->parent->max_glyphs_per_unscaled_subset_used)
sub_font->parent->max_glyphs_per_unscaled_subset_used = *num_glyphs_in_subset_ptr;
}
 
*sub_font_glyph_out = sub_font_glyph;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font,
unsigned long scaled_font_glyph_index,
const char *text_utf8,
int text_utf8_len,
cairo_scaled_font_subsets_glyph_t *subset_glyph)
{
cairo_sub_font_glyph_t key, *sub_font_glyph;
cairo_status_t status;
 
_cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index);
sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs,
&key.base);
if (sub_font_glyph == NULL) {
uint32_t font_unicode;
char *font_utf8;
int font_utf8_len;
cairo_bool_t is_latin;
int latin_character;
 
status = _cairo_sub_font_glyph_lookup_unicode (sub_font->scaled_font,
scaled_font_glyph_index,
&font_unicode,
&font_utf8,
&font_utf8_len);
if (unlikely(status))
return status;
 
/* If the supplied utf8 is a valid single character, use it
* instead of the font lookup */
if (text_utf8 != NULL && text_utf8_len > 0) {
uint32_t *ucs4;
int ucs4_len;
 
status = _cairo_utf8_to_ucs4 (text_utf8, text_utf8_len,
&ucs4, &ucs4_len);
if (status == CAIRO_STATUS_SUCCESS) {
if (ucs4_len == 1) {
font_unicode = ucs4[0];
free (font_utf8);
font_utf8 = malloc (text_utf8_len + 1);
if (font_utf8 == NULL) {
free (ucs4);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
memcpy (font_utf8, text_utf8, text_utf8_len);
font_utf8[text_utf8_len] = 0;
font_utf8_len = text_utf8_len;
}
free (ucs4);
}
}
 
/* If glyph is in the winansi encoding and font is not a user
* font, put glyph in the latin subset. If glyph is .notdef
* the latin subset is preferred but only if the latin subset
* already contains at least one glyph. We don't want to
* create a separate subset just for the .notdef glyph.
*/
is_latin = FALSE;
latin_character = -1;
if (sub_font->use_latin_subset &&
(! _cairo_font_face_is_user (sub_font->scaled_font->font_face)))
{
latin_character = _cairo_unicode_to_winansi (font_unicode);
if (latin_character > 0 ||
(latin_character == 0 && sub_font->num_glyphs_in_latin_subset > 0))
{
if (!sub_font->latin_char_map[latin_character]) {
sub_font->latin_char_map[latin_character] = TRUE;
is_latin = TRUE;
}
}
}
 
status = _cairo_sub_font_add_glyph (sub_font,
scaled_font_glyph_index,
is_latin,
latin_character,
font_unicode,
font_utf8,
font_utf8_len,
&sub_font_glyph);
if (unlikely(status))
return status;
}
 
subset_glyph->font_id = sub_font->font_id;
subset_glyph->subset_id = sub_font_glyph->subset_id;
if (sub_font_glyph->is_latin)
subset_glyph->subset_glyph_index = sub_font_glyph->latin_character;
else
subset_glyph->subset_glyph_index = sub_font_glyph->subset_glyph_index;
 
subset_glyph->is_scaled = sub_font->is_scaled;
subset_glyph->is_composite = sub_font->is_composite;
subset_glyph->is_latin = sub_font_glyph->is_latin;
subset_glyph->x_advance = sub_font_glyph->x_advance;
subset_glyph->y_advance = sub_font_glyph->y_advance;
status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph,
utf8, utf8_len,
text_utf8, text_utf8_len,
&subset_glyph->utf8_is_mapped);
subset_glyph->unicode = sub_font_glyph->unicode;
 
558,6 → 711,7
collection->subset_id = i;
collection->num_glyphs = 0;
collection->max_glyph = 0;
memset (collection->latin_to_subset_glyph_index, 0, 256*sizeof(unsigned long));
 
_cairo_hash_table_foreach (sub_font->sub_font_glyphs,
_cairo_sub_font_glyph_collect, collection);
578,21 → 732,20
subset.utf8 = collection->utf8;
subset.num_glyphs = collection->num_glyphs;
subset.glyph_names = NULL;
/* No need to check for out of memory here. If to_unicode is NULL, the PDF
* surface does not emit an ToUnicode stream */
subset.to_unicode = _cairo_malloc_ab (collection->num_glyphs, sizeof (unsigned long));
if (subset.to_unicode) {
for (j = 0; j < collection->num_glyphs; j++) {
/* default unicode character required when mapping fails */
subset.to_unicode[j] = 0xfffd;
 
subset.is_latin = FALSE;
if (sub_font->use_latin_subset && i == 0) {
subset.is_latin = TRUE;
subset.to_latin_char = collection->to_latin_char;
subset.latin_to_subset_glyph_index = collection->latin_to_subset_glyph_index;
} else {
subset.to_latin_char = NULL;
subset.latin_to_subset_glyph_index = NULL;
}
}
 
collection->status = (collection->font_subset_callback) (&subset,
collection->font_subset_callback_closure);
 
if (subset.to_unicode != NULL)
free (subset.to_unicode);
 
if (subset.glyph_names != NULL) {
for (j = 0; j < collection->num_glyphs; j++)
free (subset.glyph_names[j]);
616,6 → 769,7
}
 
subsets->type = type;
subsets->use_latin_subset = FALSE;
subsets->max_glyphs_per_unscaled_subset_used = 0;
subsets->max_glyphs_per_scaled_subset_used = 0;
subsets->num_sub_fonts = 0;
670,6 → 824,13
free (subsets);
}
 
void
_cairo_scaled_font_subsets_enable_latin_subset (cairo_scaled_font_subsets_t *font_subsets,
cairo_bool_t use_latin)
{
font_subsets->use_latin_subset = use_latin;
}
 
cairo_status_t
_cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets,
cairo_scaled_font_t *scaled_font,
684,7 → 845,7
cairo_matrix_t identity;
cairo_font_options_t font_options;
cairo_scaled_font_t *unscaled_font;
cairo_status_t status;
cairo_int_status_t status;
int max_glyphs;
cairo_bool_t type1_font;
 
737,10 → 898,10
&scaled_glyph);
_cairo_scaled_font_thaw_cache (scaled_font);
}
if (_cairo_status_is_error (status))
if (_cairo_int_status_is_error (status))
return status;
 
if (status == CAIRO_STATUS_SUCCESS &&
if (status == CAIRO_INT_STATUS_SUCCESS &&
subsets->type != CAIRO_SUBSETS_SCALED &&
! _cairo_font_face_is_user (scaled_font->font_face))
{
763,10 → 924,7
return unscaled_font->status;
 
subset_glyph->is_scaled = FALSE;
type1_font = FALSE;
#if CAIRO_HAS_FT_FONT
type1_font = _cairo_type1_scaled_font_is_type1 (unscaled_font);
#endif
if (subsets->type == CAIRO_SUBSETS_COMPOSITE && !type1_font) {
max_glyphs = MAX_GLYPHS_PER_COMPOSITE_FONT;
subset_glyph->is_composite = TRUE;
881,11 → 1039,16
 
collection.glyphs = _cairo_malloc_ab (collection.glyphs_size, sizeof(unsigned long));
collection.utf8 = _cairo_malloc_ab (collection.glyphs_size, sizeof(char *));
if (unlikely (collection.glyphs == NULL || collection.utf8 == NULL)) {
if (collection.glyphs != NULL)
collection.to_latin_char = _cairo_malloc_ab (collection.glyphs_size, sizeof(int));
collection.latin_to_subset_glyph_index = _cairo_malloc_ab (256, sizeof(unsigned long));
if (unlikely (collection.glyphs == NULL ||
collection.utf8 == NULL ||
collection.to_latin_char == NULL ||
collection.latin_to_subset_glyph_index == NULL)) {
free (collection.glyphs);
if (collection.utf8 != NULL)
free (collection.utf8);
free (collection.to_latin_char);
free (collection.latin_to_subset_glyph_index);
 
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
907,6 → 1070,8
}
free (collection.utf8);
free (collection.glyphs);
free (collection.to_latin_char);
free (collection.latin_to_subset_glyph_index);
 
return collection.status;
}
1040,7 → 1205,12
}
 
if (utf16_len == 1) {
int ch = _cairo_unicode_to_winansi (utf16[0]);
if (ch > 0 && _cairo_winansi_to_glyphname (ch))
strncpy (buf, _cairo_winansi_to_glyphname (ch), sizeof (buf));
else
snprintf (buf, sizeof (buf), "uni%04X", (int) utf16[0]);
 
_cairo_string_init_key (&key, buf);
entry = _cairo_hash_table_lookup (names, &key.base);
if (entry != NULL)
1048,7 → 1218,6
} else {
snprintf (buf, sizeof (buf), "g%d", i);
}
if (utf16)
free (utf16);
 
subset->glyph_names[i] = strdup (buf);
1077,7 → 1246,6
 
if (subset->glyph_names != NULL) {
for (i = 0; i < subset->num_glyphs; i++) {
if (subset->glyph_names[i] != NULL)
free (subset->glyph_names[i]);
}
 
/programs/develop/libraries/cairo/src/cairo-scaled-font.c
40,7 → 40,11
 
#include "cairoint.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-backend-private.h"
 
#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
#define ISFINITE(x) isfinite (x)
56,8 → 60,11
*
* #cairo_scaled_font_t represents a realization of a font face at a particular
* size and transformation and a certain set of font options.
*/
**/
 
static uint32_t
_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font);
 
/* Global Glyph Cache
*
* We maintain a global pool of glyphs split between all active fonts. This
197,10 → 204,15
_cairo_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph)
{
const cairo_surface_backend_t *surface_backend = scaled_font->surface_backend;
while (! cairo_list_is_empty (&scaled_glyph->dev_privates)) {
cairo_scaled_glyph_private_t *private =
cairo_list_first_entry (&scaled_glyph->dev_privates,
cairo_scaled_glyph_private_t,
link);
private->destroy (private, scaled_glyph, scaled_font);
}
 
if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL)
surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font);
_cairo_image_scaled_glyph_fini (scaled_font, scaled_glyph);
 
if (scaled_glyph->surface != NULL)
cairo_surface_destroy (&scaled_glyph->surface->base);
241,8 → 253,7
{ NULL, NULL }, /* pages */
FALSE, /* cache_frozen */
FALSE, /* global_cache_frozen */
NULL, /* surface_backend */
NULL, /* surface_private */
{ NULL, NULL }, /* privates */
NULL /* backend */
};
 
309,6 → 320,8
*
* Return value: %CAIRO_STATUS_SUCCESS or another error such as
* %CAIRO_STATUS_NO_MEMORY.
*
* Since: 1.0
**/
cairo_status_t
cairo_scaled_font_status (cairo_scaled_font_t *scaled_font)
436,14 → 449,16
CLEANUP_MUTEX_LOCK:
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
}
 
static void
_cairo_scaled_glyph_page_destroy (void *closure)
_cairo_scaled_glyph_page_destroy (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_page_t *page)
{
cairo_scaled_glyph_page_t *page = closure;
cairo_scaled_font_t *scaled_font;
unsigned int n;
 
scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
assert (!scaled_font->cache_frozen);
assert (!scaled_font->global_cache_frozen);
 
for (n = 0; n < page->num_glyphs; n++) {
_cairo_hash_table_remove (scaled_font->glyphs,
&page->glyphs[n].hash_entry);
451,10 → 466,24
}
 
cairo_list_del (&page->link);
 
free (page);
}
 
static void
_cairo_scaled_glyph_page_pluck (void *closure)
{
cairo_scaled_glyph_page_t *page = closure;
cairo_scaled_font_t *scaled_font;
 
assert (! cairo_list_is_empty (&page->link));
 
scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash;
 
CAIRO_MUTEX_LOCK (scaled_font->mutex);
_cairo_scaled_glyph_page_destroy (scaled_font, page);
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
}
 
/* If a scaled font wants to unlock the font map while still being
* created (needed for user-fonts), we need to take extra care not
* ending up with multiple identical scaled fonts being created.
499,6 → 528,8
 
placeholder_scaled_font->placeholder = TRUE;
 
placeholder_scaled_font->hash_entry.hash
= _cairo_scaled_font_compute_hash (placeholder_scaled_font);
status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
&placeholder_scaled_font->hash_entry);
if (unlikely (status))
524,6 → 555,9
 
CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
 
/* temporary hash value to match the placeholder */
scaled_font->hash_entry.hash
= _cairo_scaled_font_compute_hash (scaled_font);
placeholder_scaled_font =
_cairo_hash_table_lookup (cairo_scaled_font_map->hash_table,
&scaled_font->hash_entry);
595,6 → 629,26
return hash;
}
 
static uint32_t
_cairo_scaled_font_compute_hash (cairo_scaled_font_t *scaled_font)
{
uint32_t hash = FNV1_32_INIT;
 
/* We do a bytewise hash on the font matrices */
hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
hash = _hash_mix_bits (hash);
 
hash ^= (unsigned long) scaled_font->original_font_face;
hash ^= cairo_font_options_hash (&scaled_font->options);
 
/* final mixing of bits */
hash = _hash_mix_bits (hash);
assert (hash != ZOMBIE);
 
return hash;
}
 
static void
_cairo_scaled_font_init_key (cairo_scaled_font_t *scaled_font,
cairo_font_face_t *font_face,
602,11 → 656,10
const cairo_matrix_t *ctm,
const cairo_font_options_t *options)
{
uint32_t hash = FNV1_32_INIT;
 
scaled_font->status = CAIRO_STATUS_SUCCESS;
scaled_font->placeholder = FALSE;
scaled_font->font_face = font_face;
scaled_font->original_font_face = font_face;
scaled_font->font_matrix = *font_matrix;
scaled_font->ctm = *ctm;
/* ignore translation values in the ctm */
614,19 → 667,8
scaled_font->ctm.y0 = 0.;
_cairo_font_options_init_copy (&scaled_font->options, options);
 
/* We do a bytewise hash on the font matrices */
hash = _hash_matrix_fnv (&scaled_font->font_matrix, hash);
hash = _hash_matrix_fnv (&scaled_font->ctm, hash);
hash = _hash_mix_bits (hash);
 
hash ^= (unsigned long) scaled_font->font_face;
hash ^= cairo_font_options_hash (&scaled_font->options);
 
/* final mixing of bits */
hash = _hash_mix_bits (hash);
 
assert (hash != ZOMBIE);
scaled_font->hash_entry.hash = hash;
scaled_font->hash_entry.hash =
_cairo_scaled_font_compute_hash (scaled_font);
}
 
static cairo_bool_t
636,10 → 678,7
const cairo_scaled_font_t *key_a = abstract_key_a;
const cairo_scaled_font_t *key_b = abstract_key_b;
 
if (key_a->hash_entry.hash != key_b->hash_entry.hash)
return FALSE;
 
return key_a->font_face == key_b->font_face &&
return key_a->original_font_face == key_b->original_font_face &&
memcmp ((unsigned char *)(&key_a->font_matrix.xx),
(unsigned char *)(&key_b->font_matrix.xx),
sizeof(cairo_matrix_t)) == 0 &&
666,15 → 705,6
cairo_font_options_equal (&scaled_font->options, options);
}
 
static cairo_bool_t
_cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b)
{
const cairo_scaled_glyph_t *a = abstract_a;
const cairo_scaled_glyph_t *b = abstract_b;
 
return a->hash_entry.hash == b->hash_entry.hash;
}
 
/*
* Basic #cairo_scaled_font_t object management
*/
693,8 → 723,16
if (unlikely (status))
return status;
 
_cairo_scaled_font_init_key (scaled_font, font_face,
font_matrix, ctm, options);
scaled_font->status = CAIRO_STATUS_SUCCESS;
scaled_font->placeholder = FALSE;
scaled_font->font_face = font_face;
scaled_font->original_font_face = font_face;
scaled_font->font_matrix = *font_matrix;
scaled_font->ctm = *ctm;
/* ignore translation values in the ctm */
scaled_font->ctm.x0 = 0.;
scaled_font->ctm.y0 = 0.;
_cairo_font_options_init_copy (&scaled_font->options, options);
 
cairo_matrix_multiply (&scaled_font->scale,
&scaled_font->font_matrix,
723,7 → 761,7
return status;
}
 
scaled_font->glyphs = _cairo_hash_table_create (_cairo_scaled_glyphs_equal);
scaled_font->glyphs = _cairo_hash_table_create (NULL);
if (unlikely (scaled_font->glyphs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
743,8 → 781,7
 
CAIRO_MUTEX_INIT (scaled_font->mutex);
 
scaled_font->surface_backend = NULL;
scaled_font->surface_private = NULL;
cairo_list_init (&scaled_font->dev_privates);
 
scaled_font->backend = backend;
cairo_list_init (&scaled_font->link);
765,16 → 802,16
void
_cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
{
scaled_font->cache_frozen = FALSE;
assert (scaled_font->cache_frozen);
 
if (scaled_font->global_cache_frozen) {
CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
_cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
 
scaled_font->global_cache_frozen = FALSE;
}
 
scaled_font->cache_frozen = FALSE;
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
}
 
781,16 → 818,24
void
_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
{
CAIRO_MUTEX_LOCK (scaled_font->mutex);
assert (! scaled_font->cache_frozen);
 
assert (! scaled_font->global_cache_frozen);
CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
_cairo_cache_remove (&cairo_scaled_glyph_page_cache,
&cairo_list_first_entry (&scaled_font->glyph_pages,
cairo_scaled_glyph_page_t *page =
cairo_list_first_entry (&scaled_font->glyph_pages,
cairo_scaled_glyph_page_t,
link)->cache_entry);
link);
 
cairo_scaled_glyph_page_cache.size -= page->cache_entry.size;
_cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table,
(cairo_hash_entry_t *) &page->cache_entry);
 
_cairo_scaled_glyph_page_destroy (scaled_font, page);
}
CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
}
 
cairo_status_t
825,6 → 870,8
static void
_cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
{
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
scaled_font->finished = TRUE;
 
_cairo_scaled_font_reset_cache (scaled_font);
835,9 → 882,13
 
CAIRO_MUTEX_FINI (scaled_font->mutex);
 
if (scaled_font->surface_backend != NULL &&
scaled_font->surface_backend->scaled_font_fini != NULL)
scaled_font->surface_backend->scaled_font_fini (scaled_font);
while (! cairo_list_is_empty (&scaled_font->dev_privates)) {
cairo_scaled_font_private_t *private =
cairo_list_first_entry (&scaled_font->dev_privates,
cairo_scaled_font_private_t,
link);
private->destroy (private, scaled_font);
}
 
if (scaled_font->backend != NULL && scaled_font->backend->fini != NULL)
scaled_font->backend->fini (scaled_font);
845,32 → 896,79
_cairo_user_data_array_fini (&scaled_font->user_data);
}
 
/* XXX: allow multiple backends to share the font */
void
_cairo_scaled_font_revoke_ownership (cairo_scaled_font_t *scaled_font)
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
{
if (scaled_font->surface_backend == NULL)
return;
/* Release the lock to avoid the possibility of a recursive
* deadlock when the scaled font destroy closure gets called. */
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
_cairo_scaled_font_fini_internal (scaled_font);
CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
}
 
_cairo_scaled_font_reset_cache (scaled_font);
void
_cairo_scaled_font_attach_private (cairo_scaled_font_t *scaled_font,
cairo_scaled_font_private_t *private,
const void *key,
void (*destroy) (cairo_scaled_font_private_t *,
cairo_scaled_font_t *))
{
private->key = key;
private->destroy = destroy;
cairo_list_add (&private->link, &scaled_font->dev_privates);
}
 
if (scaled_font->surface_backend->scaled_font_fini != NULL)
scaled_font->surface_backend->scaled_font_fini (scaled_font);
cairo_scaled_font_private_t *
_cairo_scaled_font_find_private (cairo_scaled_font_t *scaled_font,
const void *key)
{
cairo_scaled_font_private_t *priv;
 
scaled_font->surface_backend = NULL;
scaled_font->surface_private = NULL;
cairo_list_foreach_entry (priv, cairo_scaled_font_private_t,
&scaled_font->dev_privates, link)
{
if (priv->key == key) {
if (priv->link.prev != &scaled_font->dev_privates)
cairo_list_move (&priv->link, &scaled_font->dev_privates);
return priv;
}
}
 
return NULL;
}
 
void
_cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
_cairo_scaled_glyph_attach_private (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_glyph_private_t *private,
const void *key,
void (*destroy) (cairo_scaled_glyph_private_t *,
cairo_scaled_glyph_t *,
cairo_scaled_font_t *))
{
/* Release the lock to avoid the possibility of a recursive
* deadlock when the scaled font destroy closure gets called. */
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
_cairo_scaled_font_fini_internal (scaled_font);
CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
private->key = key;
private->destroy = destroy;
cairo_list_add (&private->link, &scaled_glyph->dev_privates);
}
 
cairo_scaled_glyph_private_t *
_cairo_scaled_glyph_find_private (cairo_scaled_glyph_t *scaled_glyph,
const void *key)
{
cairo_scaled_glyph_private_t *priv;
 
cairo_list_foreach_entry (priv, cairo_scaled_glyph_private_t,
&scaled_glyph->dev_privates, link)
{
if (priv->key == key) {
if (priv->link.prev != &scaled_glyph->dev_privates)
cairo_list_move (&priv->link, &scaled_glyph->dev_privates);
return priv;
}
}
 
return NULL;
}
 
/**
* cairo_scaled_font_create:
* @font_face: a #cairo_font_face_t
890,6 → 988,8
*
* Return value: a newly created #cairo_scaled_font_t. Destroy with
* cairo_scaled_font_destroy()
*
* Since: 1.0
**/
cairo_scaled_font_t *
cairo_scaled_font_create (cairo_font_face_t *font_face,
950,38 → 1050,10
scaled_font->hash_entry.hash = ZOMBIE;
dead = scaled_font;
font_map->mru_scaled_font = NULL;
 
if (font_face->backend->get_implementation != NULL) {
font_face = font_face->backend->get_implementation (font_face,
font_matrix,
ctm,
options);
if (unlikely (font_face->status)) {
_cairo_scaled_font_map_unlock ();
cairo_scaled_font_destroy (scaled_font);
return _cairo_scaled_font_create_in_error (font_face->status);
}
}
 
_cairo_scaled_font_init_key (&key, font_face,
font_matrix, ctm, options);
}
else
{
if (font_face->backend->get_implementation != NULL) {
font_face = font_face->backend->get_implementation (font_face,
font_matrix,
ctm,
options);
if (unlikely (font_face->status)) {
_cairo_scaled_font_map_unlock ();
return _cairo_scaled_font_create_in_error (font_face->status);
}
}
_cairo_scaled_font_init_key (&key, font_face, font_matrix, ctm, options);
 
_cairo_scaled_font_init_key (&key, font_face,
font_matrix, ctm, options);
 
while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
&key.hash_entry)))
{
993,7 → 1065,6
_cairo_scaled_font_placeholder_wait_for_creation_to_finish (scaled_font);
}
 
/* Return existing scaled_font if it exists in the hash table. */
if (scaled_font != NULL) {
/* If the original reference count is 0, then this font must have
* been found in font_map->holdovers, (which means this caching is
1047,9 → 1118,20
&scaled_font->hash_entry);
scaled_font->hash_entry.hash = ZOMBIE;
}
 
 
/* Otherwise create it and insert it into the hash table. */
if (font_face->backend->get_implementation != NULL) {
font_face = font_face->backend->get_implementation (font_face,
font_matrix,
ctm,
options);
if (unlikely (font_face->status)) {
_cairo_scaled_font_map_unlock ();
return _cairo_scaled_font_create_in_error (font_face->status);
}
}
 
/* Otherwise create it and insert it into the hash table. */
status = font_face->backend->scaled_font_create (font_face, font_matrix,
ctm, options, &scaled_font);
/* Did we leave the backend in an error state? */
1081,10 → 1163,14
* ft-font-faces
*/
assert (scaled_font->font_face == font_face);
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
 
scaled_font->original_font_face =
cairo_font_face_reference (original_font_face);
 
scaled_font->hash_entry.hash = _cairo_scaled_font_compute_hash(scaled_font);
 
status = _cairo_hash_table_insert (font_map->hash_table,
&scaled_font->hash_entry);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
1157,11 → 1243,9
status <= CAIRO_STATUS_LAST_STATUS;
status++)
{
if (_cairo_scaled_font_nil_objects[status] != NULL) {
free (_cairo_scaled_font_nil_objects[status]);
_cairo_scaled_font_nil_objects[status] = NULL;
}
}
CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
 
CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
1185,6 → 1269,8
* cairo_scaled_font_get_reference_count().
*
* Returns: the referenced #cairo_scaled_font_t
*
* Since: 1.0
**/
cairo_scaled_font_t *
cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
1208,6 → 1294,8
* Decreases the reference count on @font by one. If the result
* is zero, then @font and all associated resources are freed.
* See cairo_scaled_font_reference().
*
* Since: 1.0
**/
void
cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
1226,6 → 1314,9
if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
return;
 
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
 
font_map = _cairo_scaled_font_map_lock ();
assert (font_map != NULL);
 
1364,6 → 1455,8
* @extents: a #cairo_font_extents_t which to store the retrieved extents.
*
* Gets the metrics for a #cairo_scaled_font_t.
*
* Since: 1.0
**/
void
cairo_scaled_font_extents (cairo_scaled_font_t *scaled_font,
1461,6 → 1554,8
*
* Note that whitespace glyphs do not contribute to the size of the
* rectangle (extents.width and extents.height).
*
* Since: 1.0
**/
void
cairo_scaled_font_glyph_extents (cairo_scaled_font_t *scaled_font,
1856,7 → 1951,7
cairo_text_cluster_flags_t *cluster_flags)
{
int num_chars = 0;
cairo_status_t status;
cairo_int_status_t status;
cairo_glyph_t *orig_glyphs;
cairo_text_cluster_t *orig_clusters;
 
1939,7 → 2034,7
clusters, num_clusters,
cluster_flags);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
if (status == CAIRO_STATUS_SUCCESS) {
if (status == CAIRO_INT_STATUS_SUCCESS) {
/* The checks here are crude; we only should do them in
* user-font backend, but they don't hurt here. This stuff
* can be hard to get right. */
2051,6 → 2146,9
cairo_fixed_t right,
cairo_fixed_t bottom)
{
if (left == right || top == bottom)
return FALSE;
 
return right > extents->p1.x &&
left < extents->p2.x &&
bottom > extents->p1.y &&
2057,6 → 2155,44
top < extents->p2.y;
}
 
static cairo_status_t
_cairo_scaled_font_single_glyph_device_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyph,
cairo_rectangle_int_t *extents)
{
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status;
 
_cairo_scaled_font_freeze_cache (scaled_font);
status = _cairo_scaled_glyph_lookup (scaled_font,
glyph->index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_bool_t round_xy = _cairo_font_options_get_round_glyph_positions (&scaled_font->options) == CAIRO_ROUND_GLYPH_POS_ON;
cairo_box_t box;
cairo_fixed_t v;
 
if (round_xy)
v = _cairo_fixed_from_int (_cairo_lround (glyph->x));
else
v = _cairo_fixed_from_double (glyph->x);
box.p1.x = v + scaled_glyph->bbox.p1.x;
box.p2.x = v + scaled_glyph->bbox.p2.x;
 
if (round_xy)
v = _cairo_fixed_from_int (_cairo_lround (glyph->y));
else
v = _cairo_fixed_from_double (glyph->y);
box.p1.y = v + scaled_glyph->bbox.p1.y;
box.p2.y = v + scaled_glyph->bbox.p2.y;
 
_cairo_box_round_to_rectangle (&box, extents);
}
_cairo_scaled_font_thaw_cache (scaled_font);
return status;
}
 
/*
* Compute a device-space bounding box for the glyphs.
*/
2071,11 → 2207,20
cairo_box_t box = { { INT_MAX, INT_MAX }, { INT_MIN, INT_MIN }};
cairo_scaled_glyph_t *glyph_cache[64];
cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
cairo_round_glyph_positions_t round_glyph_positions = _cairo_font_options_get_round_glyph_positions (&scaled_font->options);
int i;
 
if (unlikely (scaled_font->status))
return scaled_font->status;
 
if (num_glyphs == 1) {
if (overlap_out)
*overlap_out = FALSE;
return _cairo_scaled_font_single_glyph_device_extents (scaled_font,
glyphs,
extents);
}
 
_cairo_scaled_font_freeze_cache (scaled_font);
 
memset (glyph_cache, 0, sizeof (glyph_cache));
2099,10 → 2244,16
glyph_cache[cache_index] = scaled_glyph;
}
 
if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
x = _cairo_fixed_from_int (_cairo_lround (glyphs[i].x));
else
x = _cairo_fixed_from_double (glyphs[i].x);
x1 = x + scaled_glyph->bbox.p1.x;
x2 = x + scaled_glyph->bbox.p2.x;
 
if (round_glyph_positions == CAIRO_ROUND_GLYPH_POS_ON)
y = _cairo_fixed_from_int (_cairo_lround (glyphs[i].y));
else
y = _cairo_fixed_from_double (glyphs[i].y);
y1 = y + scaled_glyph->bbox.p1.y;
y2 = y + scaled_glyph->bbox.p2.y;
2133,17 → 2284,28
return CAIRO_STATUS_SUCCESS;
}
 
void
cairo_bool_t
_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
cairo_rectangle_int_t *extents)
{
double x0 = HUGE_VAL, x1 = -HUGE_VAL;
double y0 = HUGE_VAL, y1 = -HUGE_VAL;
double x0, x1, y0, y1, pad;
int i;
 
for (i = 0; i < num_glyphs; i++) {
/* If any of the factors are suspect (i.e. the font is broken), bail */
if (scaled_font->fs_extents.max_x_advance == 0 ||
scaled_font->fs_extents.height == 0 ||
scaled_font->max_scale == 0)
{
return FALSE;
}
 
assert (num_glyphs);
 
x0 = x1 = glyphs[0].x;
y0 = y1 = glyphs[0].y;
for (i = 1; i < num_glyphs; i++) {
double g;
 
g = glyphs[i].x;
2155,20 → 2317,19
if (g > y1) y1 = g;
}
 
if (x0 <= x1 && y0 <= y1) {
extents->x = floor (x0 - scaled_font->extents.max_x_advance);
extents->width = ceil (x1 + scaled_font->extents.max_x_advance);
extents->width -= extents->x;
pad = MAX(scaled_font->fs_extents.max_x_advance,
scaled_font->fs_extents.height);
pad *= scaled_font->max_scale;
 
extents->y = floor (y0 - scaled_font->extents.ascent);
extents->height = ceil (y1 + scaled_font->extents.descent);
extents->height -= extents->y;
} else {
extents->x = extents->y = 0;
extents->width = extents->height = 0;
extents->x = floor (x0 - pad);
extents->width = ceil (x1 + pad) - extents->x;
extents->y = floor (y0 - pad);
extents->height = ceil (y1 + pad) - extents->y;
return TRUE;
}
}
 
#if 0
/* XXX win32 */
cairo_status_t
_cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t op,
2184,7 → 2345,7
int num_glyphs,
cairo_region_t *clip_region)
{
cairo_status_t status;
cairo_int_status_t status;
cairo_surface_t *mask = NULL;
cairo_format_t mask_format = CAIRO_FORMAT_A1; /* shut gcc up */
cairo_surface_pattern_t mask_pattern;
2215,7 → 2376,7
glyphs += num_glyphs - remaining_glyphs;
num_glyphs = remaining_glyphs;
if (remaining_glyphs == 0)
status = CAIRO_STATUS_SUCCESS;
status = CAIRO_INT_STATUS_SUCCESS;
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_scaled_font_set_error (scaled_font, status);
}
2265,6 → 2426,7
break;
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_RGB24:
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_INVALID:
default:
ASSERT_NOT_REACHED;
2358,6 → 2520,7
cairo_surface_destroy (mask);
return _cairo_scaled_font_set_error (scaled_font, status);
}
#endif
 
/* Add a single-device-unit rectangle to a path. */
static cairo_status_t
2470,7 → 2633,7
int num_glyphs,
cairo_path_fixed_t *path)
{
cairo_status_t status;
cairo_int_status_t status;
int i;
 
status = scaled_font->status;
2485,9 → 2648,9
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_PATH,
&scaled_glyph);
if (status == CAIRO_STATUS_SUCCESS) {
if (status == CAIRO_INT_STATUS_SUCCESS) {
status = _cairo_path_fixed_append (path,
scaled_glyph->path, CAIRO_DIRECTION_FORWARD,
scaled_glyph->path,
_cairo_fixed_from_double (glyphs[i].x),
_cairo_fixed_from_double (glyphs[i].y));
 
2672,6 → 2835,8
cairo_scaled_glyph_page_t *page;
cairo_status_t status;
 
assert (scaled_font->cache_frozen);
 
/* only the first page in the list may contain available slots */
if (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
page = cairo_list_last_entry (&scaled_font->glyph_pages,
2697,7 → 2862,7
status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
NULL,
_cairo_scaled_glyph_page_can_remove,
_cairo_scaled_glyph_page_destroy,
_cairo_scaled_glyph_page_pluck,
MAX_GLYPH_PAGES_CACHED);
if (unlikely (status)) {
CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
2739,8 → 2904,14
_cairo_scaled_glyph_fini (scaled_font, scaled_glyph);
 
if (--page->num_glyphs == 0) {
CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
/* Temporarily disconnect callback to avoid recursive locking */
cairo_scaled_glyph_page_cache.entry_destroy = NULL;
_cairo_cache_remove (&cairo_scaled_glyph_page_cache,
&page->cache_entry);
_cairo_scaled_glyph_page_destroy (scaled_font, page);
cairo_scaled_glyph_page_cache.entry_destroy = _cairo_scaled_glyph_page_pluck;
CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
}
}
 
2777,7 → 2948,7
cairo_scaled_glyph_info_t info,
cairo_scaled_glyph_t **scaled_glyph_ret)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_scaled_glyph_t *scaled_glyph;
cairo_scaled_glyph_info_t need_info;
 
2786,6 → 2957,9
if (unlikely (scaled_font->status))
return scaled_font->status;
 
assert (CAIRO_MUTEX_IS_LOCKED(scaled_font->mutex));
assert (scaled_font->cache_frozen);
 
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
2801,6 → 2975,7
 
memset (scaled_glyph, 0, sizeof (cairo_scaled_glyph_t));
_cairo_scaled_glyph_set_index (scaled_glyph, index);
cairo_list_init (&scaled_glyph->dev_privates);
 
/* ask backend to initialize metrics and shape fields */
status =
2861,11 → 3036,13
* cairo_scaled_font_get_font_face:
* @scaled_font: a #cairo_scaled_font_t
*
* Gets the font face that this scaled font uses. This is the
* font face passed to cairo_scaled_font_create().
* Gets the font face that this scaled font uses. This might be the
* font face passed to cairo_scaled_font_create(), but this does not
* hold true for all possible cases.
*
* Return value: The #cairo_font_face_t with which @scaled_font was
* created.
* created. This object is owned by cairo. To keep a reference to it,
* you must call cairo_scaled_font_reference().
*
* Since: 1.2
**/
/programs/develop/libraries/cairo/src/cairo-script-private.h
0,0 → 1,59
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Chris Wilson
*
* 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 Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SCRIPT_PRIVATE_H
#define CAIRO_SCRIPT_PRIVATE_H
 
#include "cairo.h"
#include "cairo-script.h"
 
#include "cairo-compiler-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-types-private.h"
 
CAIRO_BEGIN_DECLS
 
cairo_private cairo_device_t *
_cairo_script_context_create_internal (cairo_output_stream_t *stream);
 
cairo_private void
_cairo_script_context_attach_snapshots (cairo_device_t *device,
cairo_bool_t enable);
 
slim_hidden_proto (cairo_script_surface_create);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_SCRIPT_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-script-surface.c
0,0 → 1,3999
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Chris Wilson
*
* 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 Chris Wilson.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* The script surface is one that records all operations performed on
* it in the form of a procedural script, similar in fashion to
* PostScript but using Cairo's imaging model. In essence, this is
* equivalent to the recording-surface, but as there is no impedance mismatch
* between Cairo and CairoScript, we can generate output immediately
* without having to copy and hold the data in memory.
*/
 
/**
* SECTION:cairo-script
* @Title: Script Surfaces
* @Short_Description: Rendering to replayable scripts
* @See_Also: #cairo_surface_t
*
* The script surface provides the ability to render to a native
* script that matches the cairo drawing model. The scripts can
* be replayed using tools under the util/cairo-script directoriy,
* or with cairo-perf-trace.
**/
 
/**
* CAIRO_HAS_SCRIPT_SURFACE:
*
* Defined if the script surface backend is available.
* The script surface backend is always built in since 1.12.
*
* Since: 1.12
**/
 
 
#include "cairoint.h"
 
#include "cairo-script.h"
#include "cairo-script-private.h"
 
#include "cairo-analysis-surface-private.h"
#include "cairo-default-context-private.h"
#include "cairo-device-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
#include "cairo-image-surface-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-pattern-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-surface-wrapper-private.h"
 
#if CAIRO_HAS_FT_FONT
#include "cairo-ft-private.h"
#endif
 
#include <ctype.h>
 
#ifdef WORDS_BIGENDIAN
#define to_be32(x) x
#else
#define to_be32(x) bswap_32(x)
#endif
 
#define _cairo_output_stream_puts(S, STR) \
_cairo_output_stream_write ((S), (STR), strlen (STR))
 
#define static cairo_warn static
 
typedef struct _cairo_script_context cairo_script_context_t;
typedef struct _cairo_script_surface cairo_script_surface_t;
typedef struct _cairo_script_implicit_context cairo_script_implicit_context_t;
typedef struct _cairo_script_font cairo_script_font_t;
 
typedef struct _operand {
enum {
SURFACE,
DEFERRED,
} type;
cairo_list_t link;
} operand_t;
 
 
struct deferred_finish {
cairo_list_t link;
operand_t operand;
};
 
struct _cairo_script_context {
cairo_device_t base;
 
int active;
int attach_snapshots;
 
cairo_bool_t owns_stream;
cairo_output_stream_t *stream;
cairo_script_mode_t mode;
 
struct _bitmap {
unsigned long min;
unsigned long count;
unsigned int map[64];
struct _bitmap *next;
} surface_id, font_id;
 
cairo_list_t operands;
cairo_list_t deferred;
 
cairo_list_t fonts;
cairo_list_t defines;
};
 
struct _cairo_script_font {
cairo_scaled_font_private_t base;
 
cairo_bool_t has_sfnt;
unsigned long id;
unsigned long subset_glyph_index;
cairo_list_t link;
cairo_scaled_font_t *parent;
};
 
struct _cairo_script_implicit_context {
cairo_operator_t current_operator;
cairo_fill_rule_t current_fill_rule;
double current_tolerance;
cairo_antialias_t current_antialias;
cairo_stroke_style_t current_style;
cairo_pattern_union_t current_source;
cairo_matrix_t current_ctm;
cairo_matrix_t current_stroke_matrix;
cairo_matrix_t current_font_matrix;
cairo_font_options_t current_font_options;
cairo_scaled_font_t *current_scaled_font;
cairo_path_fixed_t current_path;
cairo_bool_t has_clip;
};
 
struct _cairo_script_surface {
cairo_surface_t base;
 
cairo_surface_wrapper_t wrapper;
 
cairo_surface_clipper_t clipper;
 
operand_t operand;
cairo_bool_t emitted;
cairo_bool_t defined;
cairo_bool_t active;
 
double width, height;
 
/* implicit flattened context */
cairo_script_implicit_context_t cr;
};
 
static const cairo_surface_backend_t _cairo_script_surface_backend;
 
static cairo_script_surface_t *
_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
cairo_content_t content,
cairo_rectangle_t *extents,
cairo_surface_t *passthrough);
 
static void
_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
cairo_scaled_font_t *scaled_font);
 
static void
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr);
 
static void
_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr);
 
static void
_bitmap_release_id (struct _bitmap *b, unsigned long token)
{
struct _bitmap **prev = NULL;
 
do {
if (token < b->min + sizeof (b->map) * CHAR_BIT) {
unsigned int bit, elem;
 
token -= b->min;
elem = token / (sizeof (b->map[0]) * CHAR_BIT);
bit = token % (sizeof (b->map[0]) * CHAR_BIT);
b->map[elem] &= ~(1 << bit);
if (! --b->count && prev) {
*prev = b->next;
free (b);
}
return;
}
prev = &b->next;
b = b->next;
} while (b != NULL);
}
 
static cairo_status_t
_bitmap_next_id (struct _bitmap *b,
unsigned long *id)
{
struct _bitmap *bb, **prev = NULL;
unsigned long min = 0;
 
do {
if (b->min != min)
break;
 
if (b->count < sizeof (b->map) * CHAR_BIT) {
unsigned int n, m, bit;
for (n = 0; n < ARRAY_LENGTH (b->map); n++) {
if (b->map[n] == (unsigned int) -1)
continue;
 
for (m=0, bit=1; m<sizeof (b->map[0])*CHAR_BIT; m++, bit<<=1) {
if ((b->map[n] & bit) == 0) {
b->map[n] |= bit;
b->count++;
*id = n * sizeof (b->map[0])*CHAR_BIT + m + b->min;
return CAIRO_STATUS_SUCCESS;
}
}
}
}
min += sizeof (b->map) * CHAR_BIT;
 
prev = &b->next;
b = b->next;
} while (b != NULL);
 
bb = malloc (sizeof (struct _bitmap));
if (unlikely (bb == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
*prev = bb;
bb->next = b;
bb->min = min;
bb->count = 1;
bb->map[0] = 0x1;
memset (bb->map + 1, 0, sizeof (bb->map) - sizeof (bb->map[0]));
*id = min;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_bitmap_fini (struct _bitmap *b)
{
while (b != NULL) {
struct _bitmap *next = b->next;
free (b);
b = next;
}
}
 
static const char *
_direction_to_string (cairo_bool_t backward)
{
static const char *names[] = {
"FORWARD",
"BACKWARD"
};
assert (backward < ARRAY_LENGTH (names));
return names[backward];
}
 
static const char *
_operator_to_string (cairo_operator_t op)
{
static const char *names[] = {
"CLEAR", /* CAIRO_OPERATOR_CLEAR */
 
"SOURCE", /* CAIRO_OPERATOR_SOURCE */
"OVER", /* CAIRO_OPERATOR_OVER */
"IN", /* CAIRO_OPERATOR_IN */
"OUT", /* CAIRO_OPERATOR_OUT */
"ATOP", /* CAIRO_OPERATOR_ATOP */
 
"DEST", /* CAIRO_OPERATOR_DEST */
"DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
"DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
"DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
"DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
 
"XOR", /* CAIRO_OPERATOR_XOR */
"ADD", /* CAIRO_OPERATOR_ADD */
"SATURATE", /* CAIRO_OPERATOR_SATURATE */
 
"MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
"SCREEN", /* CAIRO_OPERATOR_SCREEN */
"OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
"DARKEN", /* CAIRO_OPERATOR_DARKEN */
"LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
"DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
"BURN", /* CAIRO_OPERATOR_COLOR_BURN */
"HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
"SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
"DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
"EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
"HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
"HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
};
assert (op < ARRAY_LENGTH (names));
return names[op];
}
 
static const char *
_extend_to_string (cairo_extend_t extend)
{
static const char *names[] = {
"EXTEND_NONE", /* CAIRO_EXTEND_NONE */
"EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
"EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
"EXTEND_PAD" /* CAIRO_EXTEND_PAD */
};
assert (extend < ARRAY_LENGTH (names));
return names[extend];
}
 
static const char *
_filter_to_string (cairo_filter_t filter)
{
static const char *names[] = {
"FILTER_FAST", /* CAIRO_FILTER_FAST */
"FILTER_GOOD", /* CAIRO_FILTER_GOOD */
"FILTER_BEST", /* CAIRO_FILTER_BEST */
"FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
"FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
"FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
};
assert (filter < ARRAY_LENGTH (names));
return names[filter];
}
 
static const char *
_fill_rule_to_string (cairo_fill_rule_t rule)
{
static const char *names[] = {
"WINDING", /* CAIRO_FILL_RULE_WINDING */
"EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
};
assert (rule < ARRAY_LENGTH (names));
return names[rule];
}
 
static const char *
_antialias_to_string (cairo_antialias_t antialias)
{
static const char *names[] = {
"ANTIALIAS_DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
"ANTIALIAS_NONE", /* CAIRO_ANTIALIAS_NONE */
"ANTIALIAS_GRAY", /* CAIRO_ANTIALIAS_GRAY */
"ANTIALIAS_SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */
"ANTIALIAS_FAST", /* CAIRO_ANTIALIAS_FAST */
"ANTIALIAS_GOOD", /* CAIRO_ANTIALIAS_GOOD */
"ANTIALIAS_BEST" /* CAIRO_ANTIALIAS_BEST */
};
assert (antialias < ARRAY_LENGTH (names));
return names[antialias];
}
 
static const char *
_line_cap_to_string (cairo_line_cap_t line_cap)
{
static const char *names[] = {
"LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
"LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
"LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
};
assert (line_cap < ARRAY_LENGTH (names));
return names[line_cap];
}
 
static const char *
_line_join_to_string (cairo_line_join_t line_join)
{
static const char *names[] = {
"LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
"LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
"LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
};
assert (line_join < ARRAY_LENGTH (names));
return names[line_join];
}
 
static inline cairo_script_context_t *
to_context (cairo_script_surface_t *surface)
{
return (cairo_script_context_t *) surface->base.device;
}
 
static cairo_bool_t
target_is_active (cairo_script_surface_t *surface)
{
return cairo_list_is_first (&surface->operand.link,
&to_context (surface)->operands);
}
 
static void
target_push (cairo_script_surface_t *surface)
{
cairo_list_move (&surface->operand.link, &to_context (surface)->operands);
}
 
static int
target_depth (cairo_script_surface_t *surface)
{
cairo_list_t *link;
int depth = 0;
 
cairo_list_foreach (link, &to_context (surface)->operands) {
if (link == &surface->operand.link)
break;
depth++;
}
 
return depth;
}
 
static void
_get_target (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
 
if (target_is_active (surface)) {
_cairo_output_stream_puts (ctx->stream, "dup ");
return;
}
 
if (surface->defined) {
_cairo_output_stream_printf (ctx->stream, "s%u ",
surface->base.unique_id);
} else {
int depth = target_depth (surface);
 
assert (! cairo_list_is_empty (&surface->operand.link));
assert (! target_is_active (surface));
 
if (ctx->active) {
_cairo_output_stream_printf (ctx->stream, "%d index ", depth);
_cairo_output_stream_puts (ctx->stream, "/target get exch pop ");
} else {
if (depth == 1) {
_cairo_output_stream_puts (ctx->stream, "exch ");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll ", depth);
}
target_push (surface);
_cairo_output_stream_puts (ctx->stream, "dup ");
}
}
}
 
static const char *
_content_to_string (cairo_content_t content)
{
switch (content) {
case CAIRO_CONTENT_ALPHA: return "ALPHA";
case CAIRO_CONTENT_COLOR: return "COLOR";
default:
case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
}
}
 
static cairo_status_t
_emit_surface (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
 
_cairo_output_stream_printf (ctx->stream,
"<< /content //%s",
_content_to_string (surface->base.content));
if (surface->width != -1 && surface->height != -1) {
_cairo_output_stream_printf (ctx->stream,
" /width %f /height %f",
surface->width,
surface->height);
}
 
if (surface->base.x_fallback_resolution !=
CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT ||
surface->base.y_fallback_resolution !=
CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT)
{
_cairo_output_stream_printf (ctx->stream,
" /fallback-resolution [%f %f]",
surface->base.x_fallback_resolution,
surface->base.y_fallback_resolution);
}
 
if (surface->base.device_transform.x0 != 0. ||
surface->base.device_transform.y0 != 0.)
{
/* XXX device offset is encoded into the pattern matrices etc. */
if (0) {
_cairo_output_stream_printf (ctx->stream,
" /device-offset [%f %f]",
surface->base.device_transform.x0,
surface->base.device_transform.y0);
}
}
 
_cairo_output_stream_puts (ctx->stream, " >> surface context\n");
surface->emitted = TRUE;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_context (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
 
if (target_is_active (surface))
return CAIRO_STATUS_SUCCESS;
 
while (! cairo_list_is_empty (&ctx->operands)) {
operand_t *op;
cairo_script_surface_t *old;
 
op = cairo_list_first_entry (&ctx->operands,
operand_t,
link);
if (op->type == DEFERRED)
break;
 
old = cairo_container_of (op, cairo_script_surface_t, operand);
if (old == surface)
break;
if (old->active)
break;
 
if (! old->defined) {
assert (old->emitted);
_cairo_output_stream_printf (ctx->stream,
"/target get /s%u exch def pop\n",
old->base.unique_id);
old->defined = TRUE;
} else {
_cairo_output_stream_puts (ctx->stream, "pop\n");
}
 
cairo_list_del (&old->operand.link);
}
 
if (target_is_active (surface))
return CAIRO_STATUS_SUCCESS;
 
if (! surface->emitted) {
cairo_status_t status;
 
status = _emit_surface (surface);
if (unlikely (status))
return status;
} else if (cairo_list_is_empty (&surface->operand.link)) {
assert (surface->defined);
_cairo_output_stream_printf (ctx->stream,
"s%u context\n",
surface->base.unique_id);
_cairo_script_implicit_context_reset (&surface->cr);
_cairo_surface_clipper_reset (&surface->clipper);
} else {
int depth = target_depth (surface);
if (depth == 1) {
_cairo_output_stream_puts (ctx->stream, "exch\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll\n",
depth);
}
}
target_push (surface);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_operator (cairo_script_surface_t *surface,
cairo_operator_t op)
{
assert (target_is_active (surface));
 
if (surface->cr.current_operator == op)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_operator = op;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"//%s set-operator\n",
_operator_to_string (op));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_fill_rule (cairo_script_surface_t *surface,
cairo_fill_rule_t fill_rule)
{
assert (target_is_active (surface));
 
if (surface->cr.current_fill_rule == fill_rule)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_fill_rule = fill_rule;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"//%s set-fill-rule\n",
_fill_rule_to_string (fill_rule));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_tolerance (cairo_script_surface_t *surface,
double tolerance,
cairo_bool_t force)
{
assert (target_is_active (surface));
 
if ((! force ||
fabs (tolerance - CAIRO_GSTATE_TOLERANCE_DEFAULT) < 1e-5) &&
surface->cr.current_tolerance == tolerance)
{
return CAIRO_STATUS_SUCCESS;
}
 
surface->cr.current_tolerance = tolerance;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"%f set-tolerance\n",
tolerance);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_antialias (cairo_script_surface_t *surface,
cairo_antialias_t antialias)
{
assert (target_is_active (surface));
 
if (surface->cr.current_antialias == antialias)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_antialias = antialias;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"//%s set-antialias\n",
_antialias_to_string (antialias));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_line_width (cairo_script_surface_t *surface,
double line_width,
cairo_bool_t force)
{
assert (target_is_active (surface));
 
if ((! force ||
fabs (line_width - CAIRO_GSTATE_LINE_WIDTH_DEFAULT) < 1e-5) &&
surface->cr.current_style.line_width == line_width)
{
return CAIRO_STATUS_SUCCESS;
}
 
surface->cr.current_style.line_width = line_width;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"%f set-line-width\n",
line_width);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_line_cap (cairo_script_surface_t *surface,
cairo_line_cap_t line_cap)
{
assert (target_is_active (surface));
 
if (surface->cr.current_style.line_cap == line_cap)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_style.line_cap = line_cap;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"//%s set-line-cap\n",
_line_cap_to_string (line_cap));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_line_join (cairo_script_surface_t *surface,
cairo_line_join_t line_join)
{
assert (target_is_active (surface));
 
if (surface->cr.current_style.line_join == line_join)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_style.line_join = line_join;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"//%s set-line-join\n",
_line_join_to_string (line_join));
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_miter_limit (cairo_script_surface_t *surface,
double miter_limit,
cairo_bool_t force)
{
assert (target_is_active (surface));
 
if ((! force ||
fabs (miter_limit - CAIRO_GSTATE_MITER_LIMIT_DEFAULT) < 1e-5) &&
surface->cr.current_style.miter_limit == miter_limit)
{
return CAIRO_STATUS_SUCCESS;
}
 
surface->cr.current_style.miter_limit = miter_limit;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"%f set-miter-limit\n",
miter_limit);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_dashes_equal (const double *a, const double *b, int num_dashes)
{
while (num_dashes--) {
if (fabs (*a - *b) > 1e-5)
return FALSE;
a++, b++;
}
 
return TRUE;
}
 
static cairo_status_t
_emit_dash (cairo_script_surface_t *surface,
const double *dash,
unsigned int num_dashes,
double offset,
cairo_bool_t force)
{
unsigned int n;
 
assert (target_is_active (surface));
 
if (force &&
num_dashes == 0 &&
surface->cr.current_style.num_dashes == 0)
{
return CAIRO_STATUS_SUCCESS;
}
 
if (! force &&
(surface->cr.current_style.num_dashes == num_dashes &&
(num_dashes == 0 ||
(fabs (surface->cr.current_style.dash_offset - offset) < 1e-5 &&
_dashes_equal (surface->cr.current_style.dash, dash, num_dashes)))))
{
return CAIRO_STATUS_SUCCESS;
}
 
 
if (num_dashes) {
surface->cr.current_style.dash = _cairo_realloc_ab
(surface->cr.current_style.dash, num_dashes, sizeof (double));
if (unlikely (surface->cr.current_style.dash == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
memcpy (surface->cr.current_style.dash, dash,
sizeof (double) * num_dashes);
} else {
free (surface->cr.current_style.dash);
surface->cr.current_style.dash = NULL;
}
 
surface->cr.current_style.num_dashes = num_dashes;
surface->cr.current_style.dash_offset = offset;
 
_cairo_output_stream_puts (to_context (surface)->stream, "[");
for (n = 0; n < num_dashes; n++) {
_cairo_output_stream_printf (to_context (surface)->stream, "%f", dash[n]);
if (n < num_dashes-1)
_cairo_output_stream_puts (to_context (surface)->stream, " ");
}
_cairo_output_stream_printf (to_context (surface)->stream,
"] %f set-dash\n",
offset);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_stroke_style (cairo_script_surface_t *surface,
const cairo_stroke_style_t *style,
cairo_bool_t force)
{
cairo_status_t status;
 
assert (target_is_active (surface));
 
status = _emit_line_width (surface, style->line_width, force);
if (unlikely (status))
return status;
 
status = _emit_line_cap (surface, style->line_cap);
if (unlikely (status))
return status;
 
status = _emit_line_join (surface, style->line_join);
if (unlikely (status))
return status;
 
status = _emit_miter_limit (surface, style->miter_limit, force);
if (unlikely (status))
return status;
 
status = _emit_dash (surface,
style->dash, style->num_dashes, style->dash_offset,
force);
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
static const char *
_format_to_string (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_ARGB32: return "ARGB32";
case CAIRO_FORMAT_RGB30: return "RGB30";
case CAIRO_FORMAT_RGB24: return "RGB24";
case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
case CAIRO_FORMAT_A8: return "A8";
case CAIRO_FORMAT_A1: return "A1";
case CAIRO_FORMAT_INVALID: return "INVALID";
}
ASSERT_NOT_REACHED;
return "INVALID";
}
 
static cairo_status_t
_emit_solid_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
cairo_script_context_t *ctx = to_context (surface);
 
if (! CAIRO_COLOR_IS_OPAQUE (&solid->color))
{
if (! (surface->base.content & CAIRO_CONTENT_COLOR) ||
((solid->color.red_short == 0 || solid->color.red_short == 0xffff) &&
(solid->color.green_short == 0 || solid->color.green_short == 0xffff) &&
(solid->color.blue_short == 0 || solid->color.blue_short == 0xffff) ))
{
_cairo_output_stream_printf (ctx->stream,
"%f a",
solid->color.alpha);
}
else
{
_cairo_output_stream_printf (ctx->stream,
"%f %f %f %f rgba",
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
}
}
else
{
if (solid->color.red_short == solid->color.green_short &&
solid->color.red_short == solid->color.blue_short)
{
_cairo_output_stream_printf (ctx->stream,
"%f g",
solid->color.red);
}
else
{
_cairo_output_stream_printf (ctx->stream,
"%f %f %f rgb",
solid->color.red,
solid->color.green,
solid->color.blue);
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
 
static cairo_status_t
_emit_gradient_color_stops (cairo_gradient_pattern_t *gradient,
cairo_output_stream_t *output)
{
unsigned int n;
 
for (n = 0; n < gradient->n_stops; n++) {
_cairo_output_stream_printf (output,
"\n %f %f %f %f %f add-color-stop",
gradient->stops[n].offset,
gradient->stops[n].color.red,
gradient->stops[n].color.green,
gradient->stops[n].color.blue,
gradient->stops[n].color.alpha);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_linear_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_linear_pattern_t *linear;
 
linear = (cairo_linear_pattern_t *) pattern;
 
_cairo_output_stream_printf (ctx->stream,
"%f %f %f %f linear",
linear->pd1.x, linear->pd1.y,
linear->pd2.x, linear->pd2.y);
return _emit_gradient_color_stops (&linear->base, ctx->stream);
}
 
static cairo_status_t
_emit_radial_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_radial_pattern_t *radial;
 
radial = (cairo_radial_pattern_t *) pattern;
 
_cairo_output_stream_printf (ctx->stream,
"%f %f %f %f %f %f radial",
radial->cd1.center.x,
radial->cd1.center.y,
radial->cd1.radius,
radial->cd2.center.x,
radial->cd2.center.y,
radial->cd2.radius);
return _emit_gradient_color_stops (&radial->base, ctx->stream);
}
 
static cairo_status_t
_emit_mesh_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_pattern_t *mesh;
cairo_status_t status;
unsigned int i, n;
 
mesh = (cairo_pattern_t *) pattern;
status = cairo_mesh_pattern_get_patch_count (mesh, &n);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (ctx->stream, "mesh");
for (i = 0; i < n; i++) {
cairo_path_t *path;
cairo_path_data_t *data;
int j;
 
_cairo_output_stream_printf (ctx->stream, "\n begin-patch");
 
path = cairo_mesh_pattern_get_path (mesh, i);
if (unlikely (path->status))
return path->status;
 
for (j = 0; j < path->num_data; j+=data[0].header.length) {
data = &path->data[j];
switch (data->header.type) {
case CAIRO_PATH_MOVE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f m",
data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_LINE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f l",
data[1].point.x, data[1].point.y);
break;
case CAIRO_PATH_CURVE_TO:
_cairo_output_stream_printf (ctx->stream,
"\n %f %f %f %f %f %f c",
data[1].point.x, data[1].point.y,
data[2].point.x, data[2].point.y,
data[3].point.x, data[3].point.y);
break;
case CAIRO_PATH_CLOSE_PATH:
break;
}
}
cairo_path_destroy (path);
 
for (j = 0; j < 4; j++) {
double x, y;
 
status = cairo_mesh_pattern_get_control_point (mesh, i, j, &x, &y);
if (unlikely (status))
return status;
_cairo_output_stream_printf (ctx->stream,
"\n %d %f %f set-control-point",
j, x, y);
}
 
for (j = 0; j < 4; j++) {
double r, g, b, a;
 
status = cairo_mesh_pattern_get_corner_color_rgba (mesh, i, j, &r, &g, &b, &a);
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (ctx->stream,
"\n %d %f %f %f %f set-corner-color",
j, r, g, b, a);
}
 
_cairo_output_stream_printf (ctx->stream, "\n end-patch");
}
 
return CAIRO_STATUS_SUCCESS;
}
 
struct script_snapshot {
cairo_surface_t base;
};
 
static cairo_status_t
script_snapshot_finish (void *abstract_surface)
{
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t script_snapshot_backend = {
CAIRO_SURFACE_TYPE_SCRIPT,
script_snapshot_finish,
};
 
static void
detach_snapshot (cairo_surface_t *abstract_surface)
{
cairo_script_surface_t *surface = (cairo_script_surface_t *)abstract_surface;
cairo_script_context_t *ctx = to_context (surface);
 
_cairo_output_stream_printf (ctx->stream,
"/s%d undef\n",
surface->base.unique_id);
}
 
static void
attach_snapshot (cairo_script_context_t *ctx,
cairo_surface_t *source)
{
struct script_snapshot *surface;
 
if (! ctx->attach_snapshots)
return;
 
surface = malloc (sizeof (*surface));
if (unlikely (surface == NULL))
return;
 
_cairo_surface_init (&surface->base,
&script_snapshot_backend,
&ctx->base,
source->content);
 
_cairo_output_stream_printf (ctx->stream,
"dup /s%d exch def ",
surface->base.unique_id);
 
_cairo_surface_attach_snapshot (source, &surface->base, detach_snapshot);
cairo_surface_destroy (&surface->base);
}
 
static cairo_status_t
_emit_recording_surface_pattern (cairo_script_surface_t *surface,
cairo_recording_surface_t *source)
{
cairo_script_implicit_context_t old_cr;
cairo_script_context_t *ctx = to_context (surface);
cairo_script_surface_t *similar;
cairo_surface_t *snapshot;
cairo_rectangle_t r, *extents;
cairo_status_t status;
 
snapshot = _cairo_surface_has_snapshot (&source->base, &script_snapshot_backend);
if (snapshot) {
_cairo_output_stream_printf (ctx->stream, "s%d", snapshot->unique_id);
return CAIRO_INT_STATUS_SUCCESS;
}
 
extents = NULL;
if (_cairo_recording_surface_get_bounds (&source->base, &r))
extents = &r;
 
similar = _cairo_script_surface_create_internal (ctx,
source->base.content,
extents,
NULL);
if (unlikely (similar->base.status))
return similar->base.status;
 
similar->base.is_clear = TRUE;
 
_cairo_output_stream_printf (ctx->stream, "//%s ",
_content_to_string (source->base.content));
if (extents) {
_cairo_output_stream_printf (ctx->stream, "[%f %f %f %f]",
extents->x, extents->y,
extents->width, extents->height);
} else
_cairo_output_stream_puts (ctx->stream, "[]");
_cairo_output_stream_puts (ctx->stream, " record\n");
 
attach_snapshot (ctx, &source->base);
 
_cairo_output_stream_puts (ctx->stream, "dup context\n");
 
target_push (similar);
similar->emitted = TRUE;
 
 
old_cr = surface->cr;
_cairo_script_implicit_context_init (&surface->cr);
status = _cairo_recording_surface_replay (&source->base, &similar->base);
surface->cr = old_cr;
 
if (unlikely (status)) {
cairo_surface_destroy (&similar->base);
return status;
}
 
cairo_list_del (&similar->operand.link);
assert (target_is_active (surface));
 
_cairo_output_stream_puts (ctx->stream, "pop ");
cairo_surface_destroy (&similar->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_script_surface_pattern (cairo_script_surface_t *surface,
cairo_script_surface_t *source)
{
_get_target (source);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_write_image_surface (cairo_output_stream_t *output,
const cairo_image_surface_t *image)
{
int stride, row, width;
uint8_t row_stack[CAIRO_STACK_BUFFER_SIZE];
uint8_t *rowdata;
uint8_t *data;
 
stride = image->stride;
width = image->width;
data = image->data;
#if WORDS_BIGENDIAN
switch (image->format) {
case CAIRO_FORMAT_A1:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, (width+7)/8);
data += stride;
}
break;
case CAIRO_FORMAT_A8:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB16_565:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, 2*width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB24:
for (row = image->height; row--; ) {
int col;
rowdata = data;
for (col = width; col--; ) {
_cairo_output_stream_write (output, rowdata, 3);
rowdata+=4;
}
data += stride;
}
break;
case CAIRO_FORMAT_ARGB32:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, 4*width);
data += stride;
}
break;
case CAIRO_FORMAT_INVALID:
default:
ASSERT_NOT_REACHED;
break;
}
#else
if (stride > ARRAY_LENGTH (row_stack)) {
rowdata = malloc (stride);
if (unlikely (rowdata == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
rowdata = row_stack;
 
switch (image->format) {
case CAIRO_FORMAT_A1:
for (row = image->height; row--; ) {
int col;
for (col = 0; col < (width + 7)/8; col++)
rowdata[col] = CAIRO_BITSWAP8 (data[col]);
_cairo_output_stream_write (output, rowdata, (width+7)/8);
data += stride;
}
break;
case CAIRO_FORMAT_A8:
for (row = image->height; row--; ) {
_cairo_output_stream_write (output, data, width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB16_565:
for (row = image->height; row--; ) {
uint16_t *src = (uint16_t *) data;
uint16_t *dst = (uint16_t *) rowdata;
int col;
for (col = 0; col < width; col++)
dst[col] = bswap_16 (src[col]);
_cairo_output_stream_write (output, rowdata, 2*width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB24:
for (row = image->height; row--; ) {
uint8_t *src = data;
int col;
for (col = 0; col < width; col++) {
rowdata[3*col+2] = *src++;
rowdata[3*col+1] = *src++;
rowdata[3*col+0] = *src++;
src++;
}
_cairo_output_stream_write (output, rowdata, 3*width);
data += stride;
}
break;
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_ARGB32:
for (row = image->height; row--; ) {
uint32_t *src = (uint32_t *) data;
uint32_t *dst = (uint32_t *) rowdata;
int col;
for (col = 0; col < width; col++)
dst[col] = bswap_32 (src[col]);
_cairo_output_stream_write (output, rowdata, 4*width);
data += stride;
}
break;
case CAIRO_FORMAT_INVALID:
default:
ASSERT_NOT_REACHED;
break;
}
if (rowdata != row_stack)
free (rowdata);
#endif
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_png_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_output_stream_t *base85_stream;
cairo_status_t status;
const uint8_t *mime_data;
unsigned long mime_data_length;
 
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_PNG,
&mime_data, &mime_data_length);
if (mime_data == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_output_stream_printf (ctx->stream,
"<< "
"/width %d "
"/height %d "
"/format //%s "
"/mime-type (image/png) "
"/source <~",
image->width, image->height,
_format_to_string (image->format));
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (ctx->stream, "~> >> image ");
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_image_surface (cairo_script_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_output_stream_t *base85_stream;
cairo_output_stream_t *zlib_stream;
cairo_int_status_t status, status2;
cairo_surface_t *snapshot;
const uint8_t *mime_data;
unsigned long mime_data_length;
 
snapshot = _cairo_surface_has_snapshot (&image->base,
&script_snapshot_backend);
if (snapshot) {
_cairo_output_stream_printf (ctx->stream, "s%u ", snapshot->unique_id);
return CAIRO_INT_STATUS_SUCCESS;
}
 
status = _emit_png_surface (surface, image);
if (_cairo_int_status_is_error (status)) {
return status;
} else if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_image_surface_t *clone;
uint32_t len;
 
if (image->format == CAIRO_FORMAT_INVALID) {
clone = _cairo_image_surface_coerce (image);
} else {
clone = (cairo_image_surface_t *)
cairo_surface_reference (&image->base);
}
 
_cairo_output_stream_printf (ctx->stream,
"<< "
"/width %d "
"/height %d "
"/format //%s "
"/source ",
clone->width, clone->height,
_format_to_string (clone->format));
 
switch (clone->format) {
case CAIRO_FORMAT_A1:
len = (clone->width + 7)/8;
break;
case CAIRO_FORMAT_A8:
len = clone->width;
break;
case CAIRO_FORMAT_RGB16_565:
len = clone->width * 2;
break;
case CAIRO_FORMAT_RGB24:
len = clone->width * 3;
break;
case CAIRO_FORMAT_RGB30:
case CAIRO_FORMAT_ARGB32:
len = clone->width * 4;
break;
case CAIRO_FORMAT_INVALID:
default:
ASSERT_NOT_REACHED;
len = 0;
break;
}
len *= clone->height;
 
if (len > 24) {
_cairo_output_stream_puts (ctx->stream, "<|");
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
 
len = to_be32 (len);
_cairo_output_stream_write (base85_stream, &len, sizeof (len));
 
zlib_stream = _cairo_deflate_stream_create (base85_stream);
status = _write_image_surface (zlib_stream, clone);
 
status2 = _cairo_output_stream_destroy (zlib_stream);
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
if (unlikely (status))
return status;
} else {
_cairo_output_stream_puts (ctx->stream, "<~");
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
status = _write_image_surface (base85_stream, clone);
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
if (unlikely (status))
return status;
}
_cairo_output_stream_puts (ctx->stream, "~> >> image ");
 
cairo_surface_destroy (&clone->base);
}
 
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JPEG,
&mime_data, &mime_data_length);
if (mime_data != NULL) {
_cairo_output_stream_printf (ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JPEG);
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
}
 
cairo_surface_get_mime_data (&image->base, CAIRO_MIME_TYPE_JP2,
&mime_data, &mime_data_length);
if (mime_data != NULL) {
_cairo_output_stream_printf (ctx->stream,
"\n (%s) <~",
CAIRO_MIME_TYPE_JP2);
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
_cairo_output_stream_write (base85_stream, mime_data, mime_data_length);
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (ctx->stream, "~> set-mime-data\n");
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_image_surface_pattern (cairo_script_surface_t *surface,
cairo_surface_t *source)
{
cairo_image_surface_t *image;
cairo_status_t status;
void *extra;
 
status = _cairo_surface_acquire_source_image (source, &image, &extra);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _emit_image_surface (surface, image);
_cairo_surface_release_source_image (source, image, extra);
}
 
return status;
}
 
static cairo_int_status_t
_emit_subsurface_pattern (cairo_script_surface_t *surface,
cairo_surface_subsurface_t *sub)
{
cairo_surface_t *source = sub->target;
cairo_int_status_t status;
 
switch ((int) source->backend->type) {
case CAIRO_SURFACE_TYPE_RECORDING:
status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SCRIPT:
status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
break;
default:
status = _emit_image_surface_pattern (surface, source);
break;
}
if (unlikely (status))
return status;
 
_cairo_output_stream_printf (to_context (surface)->stream,
"%d %d %d %d subsurface ",
sub->extents.x,
sub->extents.y,
sub->extents.width,
sub->extents.height);
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_surface_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_surface_pattern_t *surface_pattern;
cairo_surface_t *source, *snapshot, *free_me = NULL;
cairo_surface_t *take_snapshot = NULL;
cairo_int_status_t status;
 
surface_pattern = (cairo_surface_pattern_t *) pattern;
source = surface_pattern->surface;
 
if (_cairo_surface_is_snapshot (source)) {
snapshot = _cairo_surface_has_snapshot (source, &script_snapshot_backend);
if (snapshot) {
_cairo_output_stream_printf (ctx->stream,
"s%d pattern ",
snapshot->unique_id);
return CAIRO_INT_STATUS_SUCCESS;
}
 
if (_cairo_surface_snapshot_is_reused (source))
take_snapshot = source;
 
free_me = source = _cairo_surface_snapshot_get_target (source);
}
 
switch ((int) source->backend->type) {
case CAIRO_SURFACE_TYPE_RECORDING:
status = _emit_recording_surface_pattern (surface, (cairo_recording_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SCRIPT:
status = _emit_script_surface_pattern (surface, (cairo_script_surface_t *) source);
break;
case CAIRO_SURFACE_TYPE_SUBSURFACE:
status = _emit_subsurface_pattern (surface, (cairo_surface_subsurface_t *) source);
break;
default:
status = _emit_image_surface_pattern (surface, source);
break;
}
cairo_surface_destroy (free_me);
if (unlikely (status))
return status;
 
if (take_snapshot)
attach_snapshot (ctx, take_snapshot);
 
_cairo_output_stream_puts (ctx->stream, "pattern");
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_raster_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_surface_t *source;
cairo_int_status_t status;
 
source = _cairo_raster_source_pattern_acquire (pattern, &surface->base, NULL);
if (unlikely (source == NULL)) {
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
if (unlikely (source->status))
return source->status;
 
status = _emit_image_surface_pattern (surface, source);
_cairo_raster_source_pattern_release (pattern, source);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (to_context(surface)->stream, "pattern");
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_pattern (cairo_script_surface_t *surface,
const cairo_pattern_t *pattern)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_int_status_t status;
cairo_bool_t is_default_extend;
cairo_bool_t need_newline = TRUE;
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
/* solid colors do not need filter/extend/matrix */
return _emit_solid_pattern (surface, pattern);
 
case CAIRO_PATTERN_TYPE_LINEAR:
status = _emit_linear_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
break;
case CAIRO_PATTERN_TYPE_RADIAL:
status = _emit_radial_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_GRADIENT_DEFAULT;
break;
case CAIRO_PATTERN_TYPE_MESH:
status = _emit_mesh_pattern (surface, pattern);
is_default_extend = TRUE;
break;
case CAIRO_PATTERN_TYPE_SURFACE:
status = _emit_surface_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
status = _emit_raster_pattern (surface, pattern);
is_default_extend = pattern->extend == CAIRO_EXTEND_SURFACE_DEFAULT;
break;
 
default:
ASSERT_NOT_REACHED;
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
if (unlikely (status))
return status;
 
if (! _cairo_matrix_is_identity (&pattern->matrix)) {
if (need_newline) {
_cairo_output_stream_puts (ctx->stream, "\n ");
need_newline = FALSE;
}
 
_cairo_output_stream_printf (ctx->stream,
" [%f %f %f %f %f %f] set-matrix\n ",
pattern->matrix.xx, pattern->matrix.yx,
pattern->matrix.xy, pattern->matrix.yy,
pattern->matrix.x0, pattern->matrix.y0);
}
 
/* XXX need to discriminate the user explicitly setting the default */
if (pattern->filter != CAIRO_FILTER_DEFAULT) {
if (need_newline) {
_cairo_output_stream_puts (ctx->stream, "\n ");
need_newline = FALSE;
}
 
_cairo_output_stream_printf (ctx->stream,
" //%s set-filter\n ",
_filter_to_string (pattern->filter));
}
if (! is_default_extend ){
if (need_newline) {
_cairo_output_stream_puts (ctx->stream, "\n ");
need_newline = FALSE;
}
 
_cairo_output_stream_printf (ctx->stream,
" //%s set-extend\n ",
_extend_to_string (pattern->extend));
}
 
if (need_newline)
_cairo_output_stream_puts (ctx->stream, "\n ");
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_identity (cairo_script_surface_t *surface,
cairo_bool_t *matrix_updated)
{
assert (target_is_active (surface));
 
if (_cairo_matrix_is_identity (&surface->cr.current_ctm))
return CAIRO_INT_STATUS_SUCCESS;
 
_cairo_output_stream_puts (to_context (surface)->stream,
"identity set-matrix\n");
 
*matrix_updated = TRUE;
cairo_matrix_init_identity (&surface->cr.current_ctm);
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_emit_source (cairo_script_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source)
{
cairo_bool_t matrix_updated = FALSE;
cairo_int_status_t status;
 
assert (target_is_active (surface));
 
if (op == CAIRO_OPERATOR_CLEAR) {
/* the source is ignored, so don't change it */
return CAIRO_INT_STATUS_SUCCESS;
}
 
if (_cairo_pattern_equal (&surface->cr.current_source.base, source))
return CAIRO_INT_STATUS_SUCCESS;
 
_cairo_pattern_fini (&surface->cr.current_source.base);
status = _cairo_pattern_init_copy (&surface->cr.current_source.base,
source);
if (unlikely (status))
return status;
 
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
 
status = _emit_pattern (surface, source);
if (unlikely (status))
return status;
 
assert (target_is_active (surface));
_cairo_output_stream_puts (to_context (surface)->stream,
" set-source\n");
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_status_t
_path_move_to (void *closure,
const cairo_point_t *point)
{
_cairo_output_stream_printf (closure,
" %f %f m",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_path_line_to (void *closure,
const cairo_point_t *point)
{
_cairo_output_stream_printf (closure,
" %f %f l",
_cairo_fixed_to_double (point->x),
_cairo_fixed_to_double (point->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_path_curve_to (void *closure,
const cairo_point_t *p1,
const cairo_point_t *p2,
const cairo_point_t *p3)
{
_cairo_output_stream_printf (closure,
" %f %f %f %f %f %f c",
_cairo_fixed_to_double (p1->x),
_cairo_fixed_to_double (p1->y),
_cairo_fixed_to_double (p2->x),
_cairo_fixed_to_double (p2->y),
_cairo_fixed_to_double (p3->x),
_cairo_fixed_to_double (p3->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_path_close (void *closure)
{
_cairo_output_stream_printf (closure,
" h");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_path_boxes (cairo_script_surface_t *surface,
const cairo_path_fixed_t *path)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_path_fixed_iter_t iter;
cairo_status_t status;
struct _cairo_boxes_chunk *chunk;
cairo_boxes_t boxes;
cairo_box_t box;
int i;
 
_cairo_boxes_init (&boxes);
_cairo_path_fixed_iter_init (&iter, path);
while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
if (box.p1.y == box.p2.y || box.p1.x == box.p2.x)
continue;
 
status = _cairo_boxes_add (&boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
if (unlikely (status)) {
_cairo_boxes_fini (&boxes);
return status;
}
}
 
if (! _cairo_path_fixed_iter_at_end (&iter)) {
_cairo_boxes_fini (&boxes);
return FALSE;
}
 
for (chunk = &boxes.chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
const cairo_box_t *b = &chunk->base[i];
double x1 = _cairo_fixed_to_double (b->p1.x);
double y1 = _cairo_fixed_to_double (b->p1.y);
double x2 = _cairo_fixed_to_double (b->p2.x);
double y2 = _cairo_fixed_to_double (b->p2.y);
 
_cairo_output_stream_printf (ctx->stream,
"\n %f %f %f %f rectangle",
x1, y1, x2 - x1, y2 - y1);
}
}
 
_cairo_boxes_fini (&boxes);
return status;
}
 
static cairo_status_t
_emit_path (cairo_script_surface_t *surface,
const cairo_path_fixed_t *path,
cairo_bool_t is_fill)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_box_t box;
cairo_int_status_t status;
 
assert (target_is_active (surface));
assert (_cairo_matrix_is_identity (&surface->cr.current_ctm));
 
if (_cairo_path_fixed_equal (&surface->cr.current_path, path))
return CAIRO_STATUS_SUCCESS;
 
_cairo_path_fixed_fini (&surface->cr.current_path);
 
_cairo_output_stream_puts (ctx->stream, "n");
 
if (path == NULL) {
_cairo_path_fixed_init (&surface->cr.current_path);
_cairo_output_stream_puts (ctx->stream, "\n");
return CAIRO_STATUS_SUCCESS;
}
 
status = _cairo_path_fixed_init_copy (&surface->cr.current_path, path);
if (unlikely (status))
return status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_is_rectangle (path, &box)) {
double x1 = _cairo_fixed_to_double (box.p1.x);
double y1 = _cairo_fixed_to_double (box.p1.y);
double x2 = _cairo_fixed_to_double (box.p2.x);
double y2 = _cairo_fixed_to_double (box.p2.y);
 
assert (x1 > -9999);
 
_cairo_output_stream_printf (ctx->stream,
" %f %f %f %f rectangle",
x1, y1, x2 - x1, y2 - y1);
status = CAIRO_INT_STATUS_SUCCESS;
} else if (is_fill && _cairo_path_fixed_fill_is_rectilinear (path)) {
status = _emit_path_boxes (surface, path);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_path_fixed_interpret (path,
_path_move_to,
_path_line_to,
_path_curve_to,
_path_close,
ctx->stream);
}
 
_cairo_output_stream_puts (ctx->stream, "\n");
 
return status;
}
static cairo_bool_t
_scaling_matrix_equal (const cairo_matrix_t *a,
const cairo_matrix_t *b)
{
return fabs (a->xx - b->xx) < 1e-5 &&
fabs (a->xy - b->xy) < 1e-5 &&
fabs (a->yx - b->yx) < 1e-5 &&
fabs (a->yy - b->yy) < 1e-5;
}
 
static cairo_status_t
_emit_scaling_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *ctm,
cairo_bool_t *matrix_updated)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_bool_t was_identity;
assert (target_is_active (surface));
 
if (_scaling_matrix_equal (&surface->cr.current_ctm, ctm))
return CAIRO_STATUS_SUCCESS;
 
was_identity = _cairo_matrix_is_identity (&surface->cr.current_ctm);
 
*matrix_updated = TRUE;
surface->cr.current_ctm = *ctm;
surface->cr.current_ctm.x0 = 0.;
surface->cr.current_ctm.y0 = 0.;
 
if (_cairo_matrix_is_identity (&surface->cr.current_ctm)) {
_cairo_output_stream_puts (ctx->stream,
"identity set-matrix\n");
} else if (was_identity && fabs (ctm->yx) < 1e-5 && fabs (ctm->xy) < 1e-5) {
_cairo_output_stream_printf (ctx->stream,
"%f %f scale\n",
ctm->xx, ctm->yy);
} else {
_cairo_output_stream_printf (ctx->stream,
"[%f %f %f %f 0 0] set-matrix\n",
ctm->xx, ctm->yx,
ctm->xy, ctm->yy);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_font_matrix (cairo_script_surface_t *surface,
const cairo_matrix_t *font_matrix)
{
cairo_script_context_t *ctx = to_context (surface);
assert (target_is_active (surface));
 
if (memcmp (&surface->cr.current_font_matrix,
font_matrix,
sizeof (cairo_matrix_t)) == 0)
{
return CAIRO_STATUS_SUCCESS;
}
 
surface->cr.current_font_matrix = *font_matrix;
 
if (_cairo_matrix_is_identity (font_matrix)) {
_cairo_output_stream_puts (ctx->stream,
"identity set-font-matrix\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"[%f %f %f %f %f %f] set-font-matrix\n",
font_matrix->xx, font_matrix->yx,
font_matrix->xy, font_matrix->yy,
font_matrix->x0, font_matrix->y0);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_script_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_script_surface_t *surface, *other = abstract_surface;
cairo_surface_t *passthrough = NULL;
cairo_script_context_t *ctx;
cairo_rectangle_t extents;
cairo_status_t status;
 
ctx = to_context (other);
 
status = cairo_device_acquire (&ctx->base);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (! other->emitted) {
status = _emit_surface (other);
if (unlikely (status)) {
cairo_device_release (&ctx->base);
return _cairo_surface_create_in_error (status);
}
 
target_push (other);
}
 
if (_cairo_surface_wrapper_is_active (&other->wrapper)) {
passthrough =
_cairo_surface_wrapper_create_similar (&other->wrapper,
content, width, height);
if (unlikely (passthrough->status)) {
cairo_device_release (&ctx->base);
return passthrough;
}
}
 
extents.x = extents.y = 0;
extents.width = width;
extents.height = height;
surface = _cairo_script_surface_create_internal (ctx, content,
&extents, passthrough);
cairo_surface_destroy (passthrough);
 
if (unlikely (surface->base.status)) {
cairo_device_release (&ctx->base);
return &surface->base;
}
 
_get_target (other);
_cairo_output_stream_printf (ctx->stream,
"%u %u //%s similar dup /s%u exch def context\n",
width, height,
_content_to_string (content),
surface->base.unique_id);
 
surface->emitted = TRUE;
surface->defined = TRUE;
surface->base.is_clear = TRUE;
target_push (surface);
 
cairo_device_release (&ctx->base);
return &surface->base;
}
 
static cairo_status_t
_device_flush (void *abstract_device)
{
cairo_script_context_t *ctx = abstract_device;
 
return _cairo_output_stream_flush (ctx->stream);
}
 
static void
_device_destroy (void *abstract_device)
{
cairo_script_context_t *ctx = abstract_device;
cairo_status_t status;
 
while (! cairo_list_is_empty (&ctx->fonts)) {
cairo_script_font_t *font;
 
font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link);
cairo_list_del (&font->base.link);
cairo_list_del (&font->link);
free (font);
}
 
_bitmap_fini (ctx->surface_id.next);
_bitmap_fini (ctx->font_id.next);
 
if (ctx->owns_stream)
status = _cairo_output_stream_destroy (ctx->stream);
 
free (ctx);
}
 
static cairo_surface_t *
_cairo_script_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_script_surface_t *surface = abstract_surface;
 
if (extents) {
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
}
 
return &surface->base;
}
 
static cairo_status_t
_cairo_script_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_script_surface_t *surface = abstract_surface;
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper,
image_out,
image_extra);
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static void
_cairo_script_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_script_surface_t *surface = abstract_surface;
 
assert (_cairo_surface_wrapper_is_active (&surface->wrapper));
_cairo_surface_wrapper_release_source_image (&surface->wrapper,
image,
image_extra);
}
 
static cairo_status_t
_cairo_script_surface_finish (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_script_context_t *ctx = to_context (surface);
cairo_status_t status = CAIRO_STATUS_SUCCESS, status2;
 
_cairo_surface_wrapper_fini (&surface->wrapper);
 
free (surface->cr.current_style.dash);
surface->cr.current_style.dash = NULL;
 
_cairo_pattern_fini (&surface->cr.current_source.base);
_cairo_path_fixed_fini (&surface->cr.current_path);
_cairo_surface_clipper_reset (&surface->clipper);
 
status = cairo_device_acquire (&ctx->base);
if (unlikely (status))
return status;
 
if (surface->emitted) {
assert (! surface->active);
 
if (! cairo_list_is_empty (&surface->operand.link)) {
if (! ctx->active) {
if (target_is_active (surface)) {
_cairo_output_stream_printf (ctx->stream,
"pop\n");
} else {
int depth = target_depth (surface);
if (depth == 1) {
_cairo_output_stream_printf (ctx->stream,
"exch pop\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll pop\n",
depth);
}
}
cairo_list_del (&surface->operand.link);
} else {
struct deferred_finish *link = malloc (sizeof (*link));
if (link == NULL) {
status2 = _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
cairo_list_del (&surface->operand.link);
} else {
link->operand.type = DEFERRED;
cairo_list_swap (&link->operand.link,
&surface->operand.link);
cairo_list_add (&link->link, &ctx->deferred);
}
}
}
 
if (surface->defined) {
_cairo_output_stream_printf (ctx->stream,
"/s%u undef\n",
surface->base.unique_id);
}
}
 
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_output_stream_flush (to_context (surface)->stream);
 
cairo_device_release (&ctx->base);
 
return status;
}
 
static cairo_int_status_t
_cairo_script_surface_copy_page (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
 
status = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (to_context (surface)->stream, "copy-page\n");
 
BAIL:
cairo_device_release (surface->base.device);
return status;
}
 
static cairo_int_status_t
_cairo_script_surface_show_page (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
 
status = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (to_context (surface)->stream, "show-page\n");
 
BAIL:
cairo_device_release (surface->base.device);
return status;
}
 
static cairo_status_t
_cairo_script_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_script_surface_t *surface = cairo_container_of (clipper,
cairo_script_surface_t,
clipper);
cairo_script_context_t *ctx = to_context (surface);
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
cairo_box_t box;
 
status = _emit_context (surface);
if (unlikely (status))
return status;
 
if (path == NULL) {
if (surface->cr.has_clip) {
_cairo_output_stream_puts (ctx->stream, "reset-clip\n");
surface->cr.has_clip = FALSE;
}
return CAIRO_STATUS_SUCCESS;
}
 
/* skip the trivial clip covering the surface extents */
if (surface->width >= 0 && surface->height >= 0 &&
_cairo_path_fixed_is_box (path, &box))
{
if (box.p1.x <= 0 && box.p1.y <= 0 &&
box.p2.x >= _cairo_fixed_from_double (surface->width) &&
box.p2.y >= _cairo_fixed_from_double (surface->height))
{
return CAIRO_STATUS_SUCCESS;
}
}
 
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
return status;
 
status = _emit_fill_rule (surface, fill_rule);
if (unlikely (status))
return status;
 
if (path->has_curve_to) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
return status;
}
 
if (! _cairo_path_fixed_fill_maybe_region (path)) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
return status;
}
 
status = _emit_path (surface, path, TRUE);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (ctx->stream, "clip+\n");
surface->cr.has_clip = TRUE;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
active (cairo_script_surface_t *surface)
{
cairo_status_t status;
 
status = cairo_device_acquire (surface->base.device);
if (unlikely (status))
return status;
 
if (surface->active++ == 0)
to_context (surface)->active++;
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
inactive (cairo_script_surface_t *surface)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_list_t sorted;
 
assert (surface->active > 0);
if (--surface->active)
goto DONE;
 
assert (ctx->active > 0);
if (--ctx->active)
goto DONE;
 
cairo_list_init (&sorted);
while (! cairo_list_is_empty (&ctx->deferred)) {
struct deferred_finish *df;
cairo_list_t *operand;
int depth;
 
df = cairo_list_first_entry (&ctx->deferred,
struct deferred_finish,
link);
 
depth = 0;
cairo_list_foreach (operand, &ctx->operands) {
if (operand == &df->operand.link)
break;
depth++;
}
 
df->operand.type = depth;
 
if (cairo_list_is_empty (&sorted)) {
cairo_list_move (&df->link, &sorted);
} else {
struct deferred_finish *pos;
 
cairo_list_foreach_entry (pos, struct deferred_finish,
&sorted,
link)
{
if (df->operand.type < pos->operand.type)
break;
}
cairo_list_move_tail (&df->link, &pos->link);
}
}
 
while (! cairo_list_is_empty (&sorted)) {
struct deferred_finish *df;
cairo_list_t *operand;
int depth;
 
df = cairo_list_first_entry (&sorted,
struct deferred_finish,
link);
 
depth = 0;
cairo_list_foreach (operand, &ctx->operands) {
if (operand == &df->operand.link)
break;
depth++;
}
 
if (depth == 0) {
_cairo_output_stream_printf (ctx->stream,
"pop\n");
} else if (depth == 1) {
_cairo_output_stream_printf (ctx->stream,
"exch pop\n");
} else {
_cairo_output_stream_printf (ctx->stream,
"%d -1 roll pop\n",
depth);
}
 
cairo_list_del (&df->operand.link);
cairo_list_del (&df->link);
free (df);
}
 
DONE:
cairo_device_release (surface->base.device);
}
 
static cairo_int_status_t
_cairo_script_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
 
status = active (surface);
if (unlikely (status))
return status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
 
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (to_context (surface)->stream,
"paint\n");
 
inactive (surface);
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_paint (&surface->wrapper,
op, source, clip);
}
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
inactive (surface);
return status;
}
 
static cairo_int_status_t
_cairo_script_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_status_t status;
 
status = active (surface);
if (unlikely (status))
return status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
 
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
 
if (_cairo_pattern_equal (source, mask)) {
_cairo_output_stream_puts (to_context (surface)->stream, "/source get");
} else {
status = _emit_pattern (surface, mask);
if (unlikely (status))
goto BAIL;
}
 
assert (surface->cr.current_operator == op);
 
_cairo_output_stream_puts (to_context (surface)->stream,
" mask\n");
 
inactive (surface);
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_mask (&surface->wrapper,
op, source, mask, clip);
}
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
inactive (surface);
return status;
}
 
static cairo_int_status_t
_cairo_script_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
 
status = active (surface);
if (unlikely (status))
return status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
goto BAIL;
 
status = _emit_path (surface, path, FALSE);
if (unlikely (status))
goto BAIL;
 
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
 
status = _emit_scaling_matrix (surface, ctm, &matrix_updated);
if (unlikely (status))
goto BAIL;
 
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
 
if (_scaling_matrix_equal (&surface->cr.current_ctm,
&surface->cr.current_stroke_matrix))
{
matrix_updated = FALSE;
}
else
{
matrix_updated = TRUE;
surface->cr.current_stroke_matrix = surface->cr.current_ctm;
}
 
status = _emit_stroke_style (surface, style, matrix_updated);
if (unlikely (status))
goto BAIL;
 
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
goto BAIL;
 
status = _emit_antialias (surface, antialias);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (to_context (surface)->stream, "stroke+\n");
 
inactive (surface);
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_stroke (&surface->wrapper,
op, source, path,
style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
inactive (surface);
return status;
}
 
static cairo_int_status_t
_cairo_script_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
cairo_box_t box;
 
status = active (surface);
if (unlikely (status))
return status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
status = _emit_identity (surface, &matrix_updated);
if (unlikely (status))
goto BAIL;
 
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
 
if (! _cairo_path_fixed_is_box (path, &box)) {
status = _emit_fill_rule (surface, fill_rule);
if (unlikely (status))
goto BAIL;
}
 
if (path->has_curve_to) {
status = _emit_tolerance (surface, tolerance, matrix_updated);
if (unlikely (status))
goto BAIL;
}
 
if (! _cairo_path_fixed_fill_maybe_region (path)) {
status = _emit_antialias (surface, antialias);
if (unlikely (status))
goto BAIL;
}
 
status = _emit_path (surface, path, TRUE);
if (unlikely (status))
goto BAIL;
 
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (to_context (surface)->stream, "fill+\n");
 
inactive (surface);
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_fill (&surface->wrapper,
op, source, path,
fill_rule,
tolerance,
antialias,
clip);
}
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
inactive (surface);
return status;
}
 
static cairo_surface_t *
_cairo_script_surface_snapshot (void *abstract_surface)
{
cairo_script_surface_t *surface = abstract_surface;
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper))
return _cairo_surface_wrapper_snapshot (&surface->wrapper);
 
return NULL;
}
 
static cairo_bool_t
_cairo_script_surface_has_show_text_glyphs (void *abstract_surface)
{
return TRUE;
}
 
static const char *
_subpixel_order_to_string (cairo_subpixel_order_t subpixel_order)
{
static const char *names[] = {
"SUBPIXEL_ORDER_DEFAULT", /* CAIRO_SUBPIXEL_ORDER_DEFAULT */
"SUBPIXEL_ORDER_RGB", /* CAIRO_SUBPIXEL_ORDER_RGB */
"SUBPIXEL_ORDER_BGR", /* CAIRO_SUBPIXEL_ORDER_BGR */
"SUBPIXEL_ORDER_VRGB", /* CAIRO_SUBPIXEL_ORDER_VRGB */
"SUBPIXEL_ORDER_VBGR" /* CAIRO_SUBPIXEL_ORDER_VBGR */
};
return names[subpixel_order];
}
static const char *
_hint_style_to_string (cairo_hint_style_t hint_style)
{
static const char *names[] = {
"HINT_STYLE_DEFAULT", /* CAIRO_HINT_STYLE_DEFAULT */
"HINT_STYLE_NONE", /* CAIRO_HINT_STYLE_NONE */
"HINT_STYLE_SLIGHT", /* CAIRO_HINT_STYLE_SLIGHT */
"HINT_STYLE_MEDIUM", /* CAIRO_HINT_STYLE_MEDIUM */
"HINT_STYLE_FULL" /* CAIRO_HINT_STYLE_FULL */
};
return names[hint_style];
}
static const char *
_hint_metrics_to_string (cairo_hint_metrics_t hint_metrics)
{
static const char *names[] = {
"HINT_METRICS_DEFAULT", /* CAIRO_HINT_METRICS_DEFAULT */
"HINT_METRICS_OFF", /* CAIRO_HINT_METRICS_OFF */
"HINT_METRICS_ON" /* CAIRO_HINT_METRICS_ON */
};
return names[hint_metrics];
}
 
static cairo_status_t
_emit_font_options (cairo_script_surface_t *surface,
cairo_font_options_t *font_options)
{
cairo_script_context_t *ctx = to_context (surface);
 
if (cairo_font_options_equal (&surface->cr.current_font_options,
font_options))
{
return CAIRO_STATUS_SUCCESS;
}
 
_cairo_output_stream_printf (ctx->stream, "<<");
 
if (font_options->antialias != surface->cr.current_font_options.antialias) {
_cairo_output_stream_printf (ctx->stream,
" /antialias //%s",
_antialias_to_string (font_options->antialias));
}
 
if (font_options->subpixel_order !=
surface->cr.current_font_options.subpixel_order)
{
_cairo_output_stream_printf (ctx->stream,
" /subpixel-order //%s",
_subpixel_order_to_string (font_options->subpixel_order));
}
 
if (font_options->hint_style !=
surface->cr.current_font_options.hint_style)
{
_cairo_output_stream_printf (ctx->stream,
" /hint-style //%s",
_hint_style_to_string (font_options->hint_style));
}
 
if (font_options->hint_metrics !=
surface->cr.current_font_options.hint_metrics)
{
_cairo_output_stream_printf (ctx->stream,
" /hint-metrics //%s",
_hint_metrics_to_string (font_options->hint_metrics));
}
 
_cairo_output_stream_printf (ctx->stream,
" >> set-font-options\n");
 
surface->cr.current_font_options = *font_options;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private,
cairo_scaled_font_t *scaled_font)
{
cairo_script_font_t *priv = (cairo_script_font_t *)abstract_private;
cairo_script_context_t *ctx = (cairo_script_context_t *)abstract_private->key;
cairo_status_t status;
 
status = cairo_device_acquire (&ctx->base);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
_cairo_output_stream_printf (ctx->stream,
"/f%lu undef /sf%lu undef\n",
priv->id,
priv->id);
 
_bitmap_release_id (&ctx->font_id, priv->id);
cairo_device_release (&ctx->base);
}
 
cairo_list_del (&priv->link);
cairo_list_del (&priv->base.link);
free (priv);
}
 
static cairo_script_font_t *
_cairo_script_font_get (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
{
return (cairo_script_font_t *) _cairo_scaled_font_find_private (font, ctx);
}
 
static long unsigned
_cairo_script_font_id (cairo_script_context_t *ctx, cairo_scaled_font_t *font)
{
return _cairo_script_font_get (ctx, font)->id;
}
 
static cairo_status_t
_emit_type42_font (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_context_t *ctx = to_context (surface);
const cairo_scaled_font_backend_t *backend;
cairo_output_stream_t *base85_stream;
cairo_output_stream_t *zlib_stream;
cairo_status_t status, status2;
unsigned long size;
unsigned int load_flags;
uint32_t len;
uint8_t *buf;
 
backend = scaled_font->backend;
if (backend->load_truetype_table == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = 0;
status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
if (unlikely (status))
return status;
 
buf = malloc (size);
if (unlikely (buf == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
if (unlikely (status)) {
free (buf);
return status;
}
 
#if CAIRO_HAS_FT_FONT
load_flags = _cairo_ft_scaled_font_get_load_flags (scaled_font);
#else
load_flags = 0;
#endif
_cairo_output_stream_printf (ctx->stream,
"<< "
"/type 42 "
"/index 0 "
"/flags %d "
"/source <|",
load_flags);
 
base85_stream = _cairo_base85_stream_create (ctx->stream);
len = to_be32 (size);
_cairo_output_stream_write (base85_stream, &len, sizeof (len));
 
zlib_stream = _cairo_deflate_stream_create (base85_stream);
 
_cairo_output_stream_write (zlib_stream, buf, size);
free (buf);
 
status2 = _cairo_output_stream_destroy (zlib_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
_cairo_output_stream_printf (ctx->stream,
"~> >> font dup /f%lu exch def set-font-face",
_cairo_script_font_id (ctx, scaled_font));
 
return status;
}
 
static cairo_status_t
_emit_scaled_font_init (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_script_font_t **font_out)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_script_font_t *font_private;
cairo_int_status_t status;
 
font_private = malloc (sizeof (cairo_script_font_t));
if (unlikely (font_private == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_scaled_font_attach_private (scaled_font, &font_private->base, ctx,
_cairo_script_scaled_font_fini);
 
font_private->parent = scaled_font;
font_private->subset_glyph_index = 0;
font_private->has_sfnt = TRUE;
 
cairo_list_add (&font_private->link, &ctx->fonts);
 
status = _bitmap_next_id (&ctx->font_id,
&font_private->id);
if (unlikely (status)) {
free (font_private);
return status;
}
 
status = _emit_context (surface);
if (unlikely (status)) {
free (font_private);
return status;
}
 
status = _emit_type42_font (surface, scaled_font);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
*font_out = font_private;
return status;
}
 
font_private->has_sfnt = FALSE;
_cairo_output_stream_printf (ctx->stream,
"dict\n"
" /type 3 set\n"
" /metrics [%f %f %f %f %f] set\n"
" /glyphs array set\n"
" font dup /f%lu exch def set-font-face",
scaled_font->fs_extents.ascent,
scaled_font->fs_extents.descent,
scaled_font->fs_extents.height,
scaled_font->fs_extents.max_x_advance,
scaled_font->fs_extents.max_y_advance,
font_private->id);
 
*font_out = font_private;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_scaled_font (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_matrix_t matrix;
cairo_font_options_t options;
cairo_bool_t matrix_updated = FALSE;
cairo_status_t status;
cairo_script_font_t *font_private;
 
cairo_scaled_font_get_ctm (scaled_font, &matrix);
status = _emit_scaling_matrix (surface, &matrix, &matrix_updated);
if (unlikely (status))
return status;
 
if (! matrix_updated && surface->cr.current_scaled_font == scaled_font)
return CAIRO_STATUS_SUCCESS;
 
surface->cr.current_scaled_font = scaled_font;
 
font_private = _cairo_script_font_get (ctx, scaled_font);
if (font_private == NULL) {
cairo_scaled_font_get_font_matrix (scaled_font, &matrix);
status = _emit_font_matrix (surface, &matrix);
if (unlikely (status))
return status;
 
cairo_scaled_font_get_font_options (scaled_font, &options);
status = _emit_font_options (surface, &options);
if (unlikely (status))
return status;
 
status = _emit_scaled_font_init (surface, scaled_font, &font_private);
if (unlikely (status))
return status;
 
assert (target_is_active (surface));
_cairo_output_stream_printf (ctx->stream,
" /scaled-font get /sf%lu exch def\n",
font_private->id);
} else {
_cairo_output_stream_printf (ctx->stream,
"sf%lu set-scaled-font\n",
font_private->id);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_scaled_glyph_vector (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_script_font_t *font_private,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_script_implicit_context_t old_cr;
cairo_status_t status;
unsigned long index;
 
index = ++font_private->subset_glyph_index;
scaled_glyph->dev_private_key = ctx;
scaled_glyph->dev_private = (void *) index;
 
_cairo_output_stream_printf (ctx->stream,
"%lu <<\n"
" /metrics [%f %f %f %f %f %f]\n"
" /render {\n",
index,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing,
scaled_glyph->fs_metrics.width,
scaled_glyph->fs_metrics.height,
scaled_glyph->fs_metrics.x_advance,
scaled_glyph->fs_metrics.y_advance);
 
if (! _cairo_matrix_is_identity (&scaled_font->scale_inverse)) {
_cairo_output_stream_printf (ctx->stream,
"[%f %f %f %f %f %f] transform\n",
scaled_font->scale_inverse.xx,
scaled_font->scale_inverse.yx,
scaled_font->scale_inverse.xy,
scaled_font->scale_inverse.yy,
scaled_font->scale_inverse.x0,
scaled_font->scale_inverse.y0);
}
 
old_cr = surface->cr;
_cairo_script_implicit_context_init (&surface->cr);
status = _cairo_recording_surface_replay (scaled_glyph->recording_surface,
&surface->base);
surface->cr = old_cr;
 
_cairo_output_stream_puts (ctx->stream, "} >> set\n");
 
return status;
}
 
static cairo_status_t
_emit_scaled_glyph_bitmap (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_script_font_t *font_private,
cairo_scaled_glyph_t *scaled_glyph)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_status_t status;
unsigned long index;
 
index = ++font_private->subset_glyph_index;
scaled_glyph->dev_private_key = ctx;
scaled_glyph->dev_private = (void *) index;
 
_cairo_output_stream_printf (ctx->stream,
"%lu <<\n"
" /metrics [%f %f %f %f %f %f]\n"
" /render {\n"
"%f %f translate\n",
index,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing,
scaled_glyph->fs_metrics.width,
scaled_glyph->fs_metrics.height,
scaled_glyph->fs_metrics.x_advance,
scaled_glyph->fs_metrics.y_advance,
scaled_glyph->fs_metrics.x_bearing,
scaled_glyph->fs_metrics.y_bearing);
 
status = _emit_image_surface (surface, scaled_glyph->surface);
if (unlikely (status))
return status;
 
_cairo_output_stream_puts (ctx->stream, "pattern ");
 
if (! _cairo_matrix_is_identity (&scaled_font->font_matrix)) {
_cairo_output_stream_printf (ctx->stream,
"\n [%f %f %f %f %f %f] set-matrix\n",
scaled_font->font_matrix.xx,
scaled_font->font_matrix.yx,
scaled_font->font_matrix.xy,
scaled_font->font_matrix.yy,
scaled_font->font_matrix.x0,
scaled_font->font_matrix.y0);
}
_cairo_output_stream_puts (ctx->stream,
"mask\n} >> set\n");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_scaled_glyph_prologue (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font)
{
cairo_script_context_t *ctx = to_context (surface);
 
_cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n",
_cairo_script_font_id (ctx, scaled_font));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_emit_scaled_glyphs (cairo_script_surface_t *surface,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
unsigned int num_glyphs)
{
cairo_script_context_t *ctx = to_context (surface);
cairo_script_font_t *font_private;
cairo_status_t status;
unsigned int n;
cairo_bool_t have_glyph_prologue = FALSE;
 
if (num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
 
font_private = _cairo_script_font_get (ctx, scaled_font);
if (font_private->has_sfnt)
return CAIRO_STATUS_SUCCESS;
 
_cairo_scaled_font_freeze_cache (scaled_font);
for (n = 0; n < num_glyphs; n++) {
cairo_scaled_glyph_t *scaled_glyph;
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status))
break;
 
if (scaled_glyph->dev_private_key == ctx)
continue;
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
&scaled_glyph);
if (_cairo_status_is_error (status))
break;
 
if (status == CAIRO_STATUS_SUCCESS) {
if (! have_glyph_prologue) {
status = _emit_scaled_glyph_prologue (surface, scaled_font);
if (unlikely (status))
break;
 
have_glyph_prologue = TRUE;
}
 
status = _emit_scaled_glyph_vector (surface,
scaled_font, font_private,
scaled_glyph);
if (unlikely (status))
break;
 
continue;
}
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
if (_cairo_status_is_error (status))
break;
 
if (status == CAIRO_STATUS_SUCCESS) {
if (! have_glyph_prologue) {
status = _emit_scaled_glyph_prologue (surface, scaled_font);
if (unlikely (status))
break;
 
have_glyph_prologue = TRUE;
}
 
status = _emit_scaled_glyph_bitmap (surface,
scaled_font,
font_private,
scaled_glyph);
if (unlikely (status))
break;
 
continue;
}
}
_cairo_scaled_font_thaw_cache (scaled_font);
 
if (have_glyph_prologue) {
_cairo_output_stream_puts (to_context (surface)->stream, "pop pop\n");
}
 
return status;
}
 
static void
to_octal (int value, char *buf, size_t size)
{
do {
buf[--size] = '0' + (value & 7);
value >>= 3;
} while (size);
}
 
static void
_emit_string_literal (cairo_script_surface_t *surface,
const char *utf8, int len)
{
cairo_script_context_t *ctx = to_context (surface);
char c;
const char *end;
 
_cairo_output_stream_puts (ctx->stream, "(");
 
if (utf8 == NULL) {
end = utf8;
} else {
if (len < 0)
len = strlen (utf8);
end = utf8 + len;
}
 
while (utf8 < end) {
switch ((c = *utf8++)) {
case '\n':
c = 'n';
goto ESCAPED_CHAR;
case '\r':
c = 'r';
goto ESCAPED_CHAR;
case '\t':
c = 't';
goto ESCAPED_CHAR;
case '\b':
c = 'b';
goto ESCAPED_CHAR;
case '\f':
c = 'f';
goto ESCAPED_CHAR;
case '\\':
case '(':
case ')':
ESCAPED_CHAR:
_cairo_output_stream_printf (ctx->stream, "\\%c", c);
break;
default:
if (isprint (c) || isspace (c)) {
_cairo_output_stream_printf (ctx->stream, "%c", c);
} else {
char buf[4] = { '\\' };
 
to_octal (c, buf+1, 3);
_cairo_output_stream_write (ctx->stream, buf, 4);
}
break;
}
}
_cairo_output_stream_puts (ctx->stream, ")");
}
 
static cairo_int_status_t
_cairo_script_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t backward,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_script_surface_t *surface = abstract_surface;
cairo_script_context_t *ctx = to_context (surface);
cairo_script_font_t *font_private;
cairo_scaled_glyph_t *scaled_glyph;
cairo_matrix_t matrix;
cairo_status_t status;
double x, y, ix, iy;
int n;
cairo_output_stream_t *base85_stream = NULL;
 
status = active (surface);
if (unlikely (status))
return status;
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status))
goto BAIL;
 
status = _emit_context (surface);
if (unlikely (status))
goto BAIL;
 
status = _emit_source (surface, op, source);
if (unlikely (status))
goto BAIL;
 
status = _emit_scaled_font (surface, scaled_font);
if (unlikely (status))
goto BAIL;
 
status = _emit_operator (surface, op);
if (unlikely (status))
goto BAIL;
 
status = _emit_scaled_glyphs (surface, scaled_font, glyphs, num_glyphs);
if (unlikely (status))
goto BAIL;
 
/* (utf8) [cx cy [glyphs]] [clusters] backward show_text_glyphs */
/* [cx cy [glyphs]] show_glyphs */
 
if (utf8 != NULL && clusters != NULL) {
_emit_string_literal (surface, utf8, utf8_len);
_cairo_output_stream_puts (ctx->stream, " ");
}
 
matrix = surface->cr.current_ctm;
status = cairo_matrix_invert (&matrix);
assert (status == CAIRO_STATUS_SUCCESS);
 
ix = x = glyphs[0].x;
iy = y = glyphs[0].y;
cairo_matrix_transform_point (&matrix, &ix, &iy);
ix -= scaled_font->font_matrix.x0;
iy -= scaled_font->font_matrix.y0;
 
_cairo_scaled_font_freeze_cache (scaled_font);
font_private = _cairo_script_font_get (ctx, scaled_font);
 
_cairo_output_stream_printf (ctx->stream,
"[%f %f ",
ix, iy);
 
for (n = 0; n < num_glyphs; n++) {
if (font_private->has_sfnt) {
if (glyphs[n].index > 256)
break;
} else {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status)) {
_cairo_scaled_font_thaw_cache (scaled_font);
goto BAIL;
}
 
if ((long unsigned) scaled_glyph->dev_private > 256)
break;
}
}
 
if (n == num_glyphs) {
_cairo_output_stream_puts (ctx->stream, "<~");
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else
_cairo_output_stream_puts (ctx->stream, "[");
 
for (n = 0; n < num_glyphs; n++) {
double dx, dy;
 
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs[n].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (unlikely (status)) {
_cairo_scaled_font_thaw_cache (scaled_font);
goto BAIL;
}
 
if (fabs (glyphs[n].x - x) > 1e-5 || fabs (glyphs[n].y - y) > 1e-5) {
if (fabs (glyphs[n].y - y) < 1e-5) {
if (base85_stream != NULL) {
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status)) {
base85_stream = NULL;
break;
}
 
_cairo_output_stream_printf (ctx->stream,
"~> %f <~", glyphs[n].x - x);
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else {
_cairo_output_stream_printf (ctx->stream,
" ] %f [ ", glyphs[n].x - x);
}
 
x = glyphs[n].x;
} else {
ix = x = glyphs[n].x;
iy = y = glyphs[n].y;
cairo_matrix_transform_point (&matrix, &ix, &iy);
ix -= scaled_font->font_matrix.x0;
iy -= scaled_font->font_matrix.y0;
if (base85_stream != NULL) {
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status)) {
base85_stream = NULL;
break;
}
 
_cairo_output_stream_printf (ctx->stream,
"~> %f %f <~",
ix, iy);
base85_stream = _cairo_base85_stream_create (ctx->stream);
} else {
_cairo_output_stream_printf (ctx->stream,
" ] %f %f [ ",
ix, iy);
}
}
}
if (base85_stream != NULL) {
uint8_t c;
 
if (font_private->has_sfnt)
c = glyphs[n].index;
else
c = (uint8_t) (long unsigned) scaled_glyph->dev_private;
 
_cairo_output_stream_write (base85_stream, &c, 1);
} else {
if (font_private->has_sfnt)
_cairo_output_stream_printf (ctx->stream, " %lu",
glyphs[n].index);
else
_cairo_output_stream_printf (ctx->stream, " %lu",
(long unsigned) scaled_glyph->dev_private);
}
 
dx = scaled_glyph->metrics.x_advance;
dy = scaled_glyph->metrics.y_advance;
cairo_matrix_transform_distance (&scaled_font->ctm, &dx, &dy);
x += dx;
y += dy;
}
_cairo_scaled_font_thaw_cache (scaled_font);
 
if (base85_stream != NULL) {
cairo_status_t status2;
 
status2 = _cairo_output_stream_destroy (base85_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
_cairo_output_stream_printf (ctx->stream, "~>");
} else {
_cairo_output_stream_puts (ctx->stream, " ]");
}
if (unlikely (status))
return status;
 
if (utf8 != NULL && clusters != NULL) {
for (n = 0; n < num_clusters; n++) {
if (clusters[n].num_bytes > UCHAR_MAX ||
clusters[n].num_glyphs > UCHAR_MAX)
{
break;
}
}
 
if (n < num_clusters) {
_cairo_output_stream_puts (ctx->stream, "] [ ");
for (n = 0; n < num_clusters; n++) {
_cairo_output_stream_printf (ctx->stream,
"%d %d ",
clusters[n].num_bytes,
clusters[n].num_glyphs);
}
_cairo_output_stream_puts (ctx->stream, "]");
}
else
{
_cairo_output_stream_puts (ctx->stream, "] <~");
base85_stream = _cairo_base85_stream_create (ctx->stream);
for (n = 0; n < num_clusters; n++) {
uint8_t c[2];
c[0] = clusters[n].num_bytes;
c[1] = clusters[n].num_glyphs;
_cairo_output_stream_write (base85_stream, c, 2);
}
status = _cairo_output_stream_destroy (base85_stream);
if (unlikely (status))
goto BAIL;
 
_cairo_output_stream_puts (ctx->stream, "~>");
}
 
_cairo_output_stream_printf (ctx->stream,
" //%s show-text-glyphs\n",
_direction_to_string (backward));
} else {
_cairo_output_stream_puts (ctx->stream,
"] show-glyphs\n");
}
 
inactive (surface);
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)){
return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper,
op, source,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
backward,
scaled_font,
clip);
}
 
return CAIRO_STATUS_SUCCESS;
 
BAIL:
inactive (surface);
return status;
}
 
static cairo_bool_t
_cairo_script_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_script_surface_t *surface = abstract_surface;
 
if (_cairo_surface_wrapper_is_active (&surface->wrapper)) {
return _cairo_surface_wrapper_get_extents (&surface->wrapper,
rectangle);
}
 
if (surface->width < 0 || surface->height < 0)
return FALSE;
 
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
 
return TRUE;
}
 
static const cairo_surface_backend_t
_cairo_script_surface_backend = {
CAIRO_SURFACE_TYPE_SCRIPT,
_cairo_script_surface_finish,
 
_cairo_default_context_create,
 
_cairo_script_surface_create_similar,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_script_surface_source,
_cairo_script_surface_acquire_source_image,
_cairo_script_surface_release_source_image,
_cairo_script_surface_snapshot,
 
_cairo_script_surface_copy_page,
_cairo_script_surface_show_page,
 
_cairo_script_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
_cairo_script_surface_paint,
_cairo_script_surface_mask,
_cairo_script_surface_stroke,
_cairo_script_surface_fill,
NULL, /* fill/stroke */
NULL, /* glyphs */
_cairo_script_surface_has_show_text_glyphs,
_cairo_script_surface_show_text_glyphs
};
 
static void
_cairo_script_implicit_context_init (cairo_script_implicit_context_t *cr)
{
cr->current_operator = CAIRO_GSTATE_OPERATOR_DEFAULT;
cr->current_fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT;
cr->current_tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT;
cr->current_antialias = CAIRO_ANTIALIAS_DEFAULT;
_cairo_stroke_style_init (&cr->current_style);
_cairo_pattern_init_solid (&cr->current_source.solid,
CAIRO_COLOR_BLACK);
_cairo_path_fixed_init (&cr->current_path);
cairo_matrix_init_identity (&cr->current_ctm);
cairo_matrix_init_identity (&cr->current_stroke_matrix);
cairo_matrix_init_identity (&cr->current_font_matrix);
_cairo_font_options_init_default (&cr->current_font_options);
cr->current_scaled_font = NULL;
cr->has_clip = FALSE;
}
 
static void
_cairo_script_implicit_context_reset (cairo_script_implicit_context_t *cr)
{
free (cr->current_style.dash);
cr->current_style.dash = NULL;
 
_cairo_pattern_fini (&cr->current_source.base);
_cairo_path_fixed_fini (&cr->current_path);
 
_cairo_script_implicit_context_init (cr);
}
 
static cairo_script_surface_t *
_cairo_script_surface_create_internal (cairo_script_context_t *ctx,
cairo_content_t content,
cairo_rectangle_t *extents,
cairo_surface_t *passthrough)
{
cairo_script_surface_t *surface;
 
if (unlikely (ctx == NULL))
return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
 
surface = malloc (sizeof (cairo_script_surface_t));
if (unlikely (surface == NULL))
return (cairo_script_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_script_surface_backend,
&ctx->base,
content);
 
_cairo_surface_wrapper_init (&surface->wrapper, passthrough);
 
_cairo_surface_clipper_init (&surface->clipper,
_cairo_script_surface_clipper_intersect_clip_path);
 
surface->width = surface->height = -1;
if (extents) {
surface->width = extents->width;
surface->height = extents->height;
cairo_surface_set_device_offset (&surface->base,
-extents->x, -extents->y);
}
 
surface->emitted = FALSE;
surface->defined = FALSE;
surface->active = FALSE;
surface->operand.type = SURFACE;
cairo_list_init (&surface->operand.link);
 
_cairo_script_implicit_context_init (&surface->cr);
 
return surface;
}
 
static const cairo_device_backend_t _cairo_script_device_backend = {
CAIRO_DEVICE_TYPE_SCRIPT,
 
NULL, NULL, /* lock, unlock */
 
_device_flush, /* flush */
NULL, /* finish */
_device_destroy
};
 
cairo_device_t *
_cairo_script_context_create_internal (cairo_output_stream_t *stream)
{
cairo_script_context_t *ctx;
 
ctx = malloc (sizeof (cairo_script_context_t));
if (unlikely (ctx == NULL))
return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
memset (ctx, 0, sizeof (cairo_script_context_t));
 
_cairo_device_init (&ctx->base, &_cairo_script_device_backend);
 
cairo_list_init (&ctx->operands);
cairo_list_init (&ctx->deferred);
ctx->stream = stream;
ctx->mode = CAIRO_SCRIPT_MODE_ASCII;
 
cairo_list_init (&ctx->fonts);
cairo_list_init (&ctx->defines);
 
ctx->attach_snapshots = TRUE;
 
return &ctx->base;
}
 
void
_cairo_script_context_attach_snapshots (cairo_device_t *device,
cairo_bool_t enable)
{
cairo_script_context_t *ctx;
 
ctx = (cairo_script_context_t *) device;
ctx->attach_snapshots = enable;
}
 
static cairo_device_t *
_cairo_script_context_create (cairo_output_stream_t *stream)
{
cairo_script_context_t *ctx;
 
ctx = (cairo_script_context_t *)
_cairo_script_context_create_internal (stream);
if (unlikely (ctx->base.status))
return &ctx->base;
 
ctx->owns_stream = TRUE;
_cairo_output_stream_puts (ctx->stream, "%!CairoScript\n");
return &ctx->base;
}
 
/**
* cairo_script_create:
* @filename: the name (path) of the file to write the script to
*
* Creates a output device for emitting the script, used when
* creating the individual surfaces.
*
* Return value: a pointer to the newly created device. The caller
* owns the surface and should call cairo_device_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" device if an error such as out of memory
* occurs. You can use cairo_device_status() to check for this.
*
* Since: 1.12
**/
cairo_device_t *
cairo_script_create (const char *filename)
{
cairo_output_stream_t *stream;
cairo_status_t status;
 
stream = _cairo_output_stream_create_for_filename (filename);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
 
return _cairo_script_context_create (stream);
}
 
/**
* cairo_script_create_for_stream:
* @write_func: callback function passed the bytes written to the script
* @closure: user data to be passed to the callback
*
* Creates a output device for emitting the script, used when
* creating the individual surfaces.
*
* Return value: a pointer to the newly created device. The caller
* owns the surface and should call cairo_device_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" device if an error such as out of memory
* occurs. You can use cairo_device_status() to check for this.
*
* Since: 1.12
**/
cairo_device_t *
cairo_script_create_for_stream (cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_status_t status;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
 
return _cairo_script_context_create (stream);
}
 
/**
* cairo_script_write_comment:
* @script: the script (output device)
* @comment: the string to emit
* @len:the length of the sting to write, or -1 to use strlen()
*
* Emit a string verbatim into the script.
*
* Since: 1.12
**/
void
cairo_script_write_comment (cairo_device_t *script,
const char *comment,
int len)
{
cairo_script_context_t *context = (cairo_script_context_t *) script;
 
if (len < 0)
len = strlen (comment);
 
_cairo_output_stream_puts (context->stream, "% ");
_cairo_output_stream_write (context->stream, comment, len);
_cairo_output_stream_puts (context->stream, "\n");
}
 
/**
* cairo_script_set_mode:
* @script: The script (output device)
* @mode: the new mode
*
* Change the output mode of the script
*
* Since: 1.12
**/
void
cairo_script_set_mode (cairo_device_t *script,
cairo_script_mode_t mode)
{
cairo_script_context_t *context = (cairo_script_context_t *) script;
 
context->mode = mode;
}
 
/**
* cairo_script_get_mode:
* @script: The script (output device) to query
*
* Queries the script for its current output mode.
*
* Return value: the current output mode of the script
*
* Since: 1.12
**/
cairo_script_mode_t
cairo_script_get_mode (cairo_device_t *script)
{
cairo_script_context_t *context = (cairo_script_context_t *) script;
 
return context->mode;
}
 
/**
* cairo_script_surface_create:
* @script: the script (output device)
* @content: the content of the surface
* @width: width in pixels
* @height: height in pixels
*
* Create a new surface that will emit its rendering through @script
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_script_surface_create (cairo_device_t *script,
cairo_content_t content,
double width,
double height)
{
cairo_rectangle_t *extents, r;
 
if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
if (unlikely (script->status))
return _cairo_surface_create_in_error (script->status);
 
extents = NULL;
if (width > 0 && height > 0) {
r.x = r.y = 0;
r.width = width;
r.height = height;
extents = &r;
}
return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
content, extents,
NULL)->base;
}
slim_hidden_def (cairo_script_surface_create);
 
/**
* cairo_script_surface_create_for_target:
* @script: the script (output device)
* @target: a target surface to wrap
*
* Create a pxoy surface that will render to @target and record
* the operations to @device.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_script_surface_create_for_target (cairo_device_t *script,
cairo_surface_t *target)
{
cairo_rectangle_int_t extents;
cairo_rectangle_t rect, *r;
 
if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
if (unlikely (script->status))
return _cairo_surface_create_in_error (script->status);
 
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
 
r = NULL;
if (_cairo_surface_get_extents (target, &extents)) {
rect.x = rect.y = 0;
rect.width = extents.width;
rect.height = extents.height;
r= &rect;
}
return &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
target->content, r,
target)->base;
}
 
/**
* cairo_script_from_recording_surface:
* @script: the script (output device)
* @recording_surface: the recording surface to replay
*
* Converts the record operations in @recording_surface into a script.
*
* Return value: #CAIRO_STATUS_SUCCESS on successful completion or an error code.
*
* Since: 1.12
**/
cairo_status_t
cairo_script_from_recording_surface (cairo_device_t *script,
cairo_surface_t *recording_surface)
{
cairo_rectangle_t r, *extents;
cairo_surface_t *surface;
cairo_status_t status;
 
if (unlikely (script->backend->type != CAIRO_DEVICE_TYPE_SCRIPT))
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
if (unlikely (script->status))
return _cairo_error (script->status);
 
if (unlikely (recording_surface->status))
return recording_surface->status;
 
if (unlikely (! _cairo_surface_is_recording (recording_surface)))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
extents = NULL;
if (_cairo_recording_surface_get_bounds (recording_surface, &r))
extents = &r;
 
surface = &_cairo_script_surface_create_internal ((cairo_script_context_t *) script,
recording_surface->content,
extents,
NULL)->base;
if (unlikely (surface->status))
return surface->status;
 
status = _cairo_recording_surface_replay (recording_surface, surface);
cairo_surface_destroy (surface);
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-script.h
42,9 → 42,18
 
CAIRO_BEGIN_DECLS
 
/**
* cairo_script_mode_t:
* @CAIRO_SCRIPT_MODE_ASCII: the output will be in readable text (default). (Since 1.12)
* @CAIRO_SCRIPT_MODE_BINARY: the output will use byte codes. (Since 1.12)
*
* A set of script output variants.
*
* Since: 1.12
**/
typedef enum {
CAIRO_SCRIPT_MODE_BINARY,
CAIRO_SCRIPT_MODE_ASCII
CAIRO_SCRIPT_MODE_ASCII,
CAIRO_SCRIPT_MODE_BINARY
} cairo_script_mode_t;
 
cairo_public cairo_device_t *
/programs/develop/libraries/cairo/src/cairo-shape-mask-compositor.c
0,0 → 1,337
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-compositor-private.h"
#include "cairo-clip-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-private.h"
#include "cairo-surface-offset-private.h"
 
static cairo_int_status_t
_cairo_shape_mask_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status;
cairo_clip_t *clip;
 
if (! extents->is_bounded)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
mask = _cairo_surface_create_similar_scratch (extents->surface,
CAIRO_CONTENT_ALPHA,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
clip = extents->clip;
if (! _cairo_clip_is_region (clip))
clip = _cairo_clip_copy_region (clip);
 
if (! mask->is_clear) {
status = _cairo_surface_offset_paint (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
clip);
if (unlikely (status))
goto error;
}
 
status = _cairo_surface_offset_stroke (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, style, ctm, ctm_inverse,
tolerance, antialias,
clip);
if (unlikely (status))
goto error;
 
if (clip != extents->clip) {
status = _cairo_clip_combine_with_surface (extents->clip, mask,
extents->bounded.x,
extents->bounded.y);
if (unlikely (status))
goto error;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
if (extents->op == CAIRO_OPERATOR_SOURCE) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_DEST_OUT,
&_cairo_pattern_white.base,
&pattern.base,
clip);
if ((status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_ADD,
&extents->source_pattern.base,
&pattern.base,
clip);
}
} else {
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
clip);
}
_cairo_pattern_fini (&pattern.base);
 
error:
cairo_surface_destroy (mask);
if (clip != extents->clip)
_cairo_clip_destroy (clip);
return status;
}
 
static cairo_int_status_t
_cairo_shape_mask_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status;
cairo_clip_t *clip;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (! extents->is_bounded)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
mask = _cairo_surface_create_similar_scratch (extents->surface,
CAIRO_CONTENT_ALPHA,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
clip = extents->clip;
if (! _cairo_clip_is_region (clip))
clip = _cairo_clip_copy_region (clip);
 
if (! mask->is_clear) {
status = _cairo_surface_offset_paint (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
clip);
if (unlikely (status))
goto error;
}
 
status = _cairo_surface_offset_fill (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, fill_rule, tolerance, antialias,
clip);
if (unlikely (status))
goto error;
 
if (clip != extents->clip) {
status = _cairo_clip_combine_with_surface (extents->clip, mask,
extents->bounded.x,
extents->bounded.y);
if (unlikely (status))
goto error;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
if (extents->op == CAIRO_OPERATOR_SOURCE) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_DEST_OUT,
&_cairo_pattern_white.base,
&pattern.base,
clip);
if ((status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_ADD,
&extents->source_pattern.base,
&pattern.base,
clip);
}
} else {
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
clip);
}
_cairo_pattern_fini (&pattern.base);
 
error:
if (clip != extents->clip)
_cairo_clip_destroy (clip);
cairo_surface_destroy (mask);
return status;
}
 
static cairo_int_status_t
_cairo_shape_mask_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_surface_t *mask;
cairo_surface_pattern_t pattern;
cairo_int_status_t status;
cairo_clip_t *clip;
 
if (! extents->is_bounded)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
mask = _cairo_surface_create_similar_scratch (extents->surface,
CAIRO_CONTENT_ALPHA,
extents->bounded.width,
extents->bounded.height);
if (unlikely (mask->status))
return mask->status;
 
clip = extents->clip;
if (! _cairo_clip_is_region (clip))
clip = _cairo_clip_copy_region (clip);
 
if (! mask->is_clear) {
status = _cairo_surface_offset_paint (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
clip);
if (unlikely (status))
goto error;
}
 
status = _cairo_surface_offset_glyphs (mask,
extents->bounded.x,
extents->bounded.y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
scaled_font, glyphs, num_glyphs,
clip);
if (unlikely (status))
goto error;
 
if (clip != extents->clip) {
status = _cairo_clip_combine_with_surface (extents->clip, mask,
extents->bounded.x,
extents->bounded.y);
if (unlikely (status))
goto error;
}
 
_cairo_pattern_init_for_surface (&pattern, mask);
cairo_matrix_init_translate (&pattern.base.matrix,
-extents->bounded.x,
-extents->bounded.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
pattern.base.extend = CAIRO_EXTEND_NONE;
if (extents->op == CAIRO_OPERATOR_SOURCE) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_DEST_OUT,
&_cairo_pattern_white.base,
&pattern.base,
clip);
if ((status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_surface_mask (extents->surface,
CAIRO_OPERATOR_ADD,
&extents->source_pattern.base,
&pattern.base,
clip);
}
} else {
status = _cairo_surface_mask (extents->surface,
extents->op,
&extents->source_pattern.base,
&pattern.base,
clip);
}
_cairo_pattern_fini (&pattern.base);
 
error:
if (clip != extents->clip)
_cairo_clip_destroy (clip);
cairo_surface_destroy (mask);
return status;
}
 
void
_cairo_shape_mask_compositor_init (cairo_compositor_t *compositor,
const cairo_compositor_t *delegate)
{
compositor->delegate = delegate;
 
compositor->paint = NULL;
compositor->mask = NULL;
compositor->fill = _cairo_shape_mask_compositor_fill;
compositor->stroke = _cairo_shape_mask_compositor_stroke;
compositor->glyphs = _cairo_shape_mask_compositor_glyphs;
}
/programs/develop/libraries/cairo/src/cairo-skia.h
55,24 → 55,6
int height,
int stride);
 
cairo_public unsigned char *
cairo_skia_surface_get_data (cairo_surface_t *surface);
 
cairo_public cairo_format_t
cairo_skia_surface_get_format (cairo_surface_t *surface);
 
cairo_public int
cairo_skia_surface_get_width (cairo_surface_t *surface);
 
cairo_public int
cairo_skia_surface_get_height (cairo_surface_t *surface);
 
cairo_public int
cairo_skia_surface_get_stride (cairo_surface_t *surface);
 
cairo_public cairo_surface_t *
cairo_skia_surface_get_image (cairo_surface_t *surface);
 
CAIRO_END_DECLS
 
#else
/programs/develop/libraries/cairo/src/cairo-slope.c
89,9 → 89,9
*/
if ((a->dx ^ b->dx) < 0 || (a->dy ^ b->dy) < 0) {
if (a->dx > 0 || (a->dx == 0 && a->dy > 0))
return -1;
else
return +1;
else
return -1;
}
 
/* Finally, for identical slopes, we obviously return 0. */
/programs/develop/libraries/cairo/src/cairo-spans-compositor-private.h
0,0 → 1,111
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SPANS_COMPOSITOR_PRIVATE_H
#define CAIRO_SPANS_COMPOSITOR_PRIVATE_H
 
#include "cairo-compositor-private.h"
#include "cairo-types-private.h"
#include "cairo-spans-private.h"
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_abstract_span_renderer {
cairo_span_renderer_t base;
char data[4096];
} cairo_abstract_span_renderer_t;
 
struct cairo_spans_compositor {
cairo_compositor_t base;
 
unsigned int flags;
#define CAIRO_SPANS_COMPOSITOR_HAS_LERP 0x1
 
/* pixel-aligned fast paths */
cairo_int_status_t (*fill_boxes) (void *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes);
 
cairo_int_status_t (*draw_image_boxes) (void *surface,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy);
 
cairo_int_status_t (*copy_boxes) (void *surface,
cairo_surface_t *src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy);
 
cairo_surface_t * (*pattern_to_surface) (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_int_status_t (*composite_boxes) (void *surface,
cairo_operator_t op,
cairo_surface_t *source,
cairo_surface_t *mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents);
 
/* general shape masks using a span renderer */
cairo_int_status_t (*renderer_init) (cairo_abstract_span_renderer_t *renderer,
const cairo_composite_rectangles_t *extents,
cairo_antialias_t antialias,
cairo_bool_t needs_clip);
 
void (*renderer_fini) (cairo_abstract_span_renderer_t *renderer,
cairo_int_status_t status);
};
 
cairo_private void
_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
const cairo_compositor_t *delegate);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_SPANS_COMPOSITOR_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-spans-compositor.c
0,0 → 1,1192
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-compositor-private.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-paginated-private.h"
#include "cairo-pattern-inline.h"
#include "cairo-region-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-spans-compositor-private.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-observer-private.h"
 
typedef struct {
cairo_polygon_t *polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
} composite_spans_info_t;
 
static cairo_int_status_t
composite_polygon (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
 
static cairo_int_status_t
composite_boxes (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes);
 
static cairo_int_status_t
clip_and_composite_polygon (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
static cairo_surface_t *
get_clip_surface (const cairo_spans_compositor_t *compositor,
cairo_surface_t *dst,
const cairo_clip_t *clip,
const cairo_rectangle_int_t *extents)
{
cairo_composite_rectangles_t composite;
cairo_surface_t *surface;
cairo_box_t box;
cairo_polygon_t polygon;
const cairo_clip_path_t *clip_path;
cairo_antialias_t antialias;
cairo_fill_rule_t fill_rule;
cairo_int_status_t status;
 
assert (clip->path);
 
surface = _cairo_surface_create_similar_solid (dst,
CAIRO_CONTENT_ALPHA,
extents->width,
extents->height,
CAIRO_COLOR_TRANSPARENT);
 
_cairo_box_from_rectangle (&box, extents);
_cairo_polygon_init (&polygon, &box, 1);
 
clip_path = clip->path;
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
&polygon);
if (unlikely (status))
goto cleanup_polygon;
 
polygon.num_limits = 0;
 
antialias = clip_path->antialias;
fill_rule = clip_path->fill_rule;
 
if (clip->boxes) {
cairo_polygon_t intersect;
cairo_boxes_t tmp;
 
_cairo_boxes_init_for_array (&tmp, clip->boxes, clip->num_boxes);
status= _cairo_polygon_init_boxes (&intersect, &tmp);
if (unlikely (status))
goto cleanup_polygon;
 
status = _cairo_polygon_intersect (&polygon, fill_rule,
&intersect, CAIRO_FILL_RULE_WINDING);
_cairo_polygon_fini (&intersect);
 
if (unlikely (status))
goto cleanup_polygon;
 
fill_rule = CAIRO_FILL_RULE_WINDING;
}
 
polygon.limits = NULL;
polygon.num_limits = 0;
 
clip_path = clip_path->prev;
while (clip_path) {
if (clip_path->antialias == antialias) {
cairo_polygon_t next;
 
_cairo_polygon_init (&next, NULL, 0);
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
&next);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = _cairo_polygon_intersect (&polygon, fill_rule,
&next, clip_path->fill_rule);
_cairo_polygon_fini (&next);
if (unlikely (status))
goto cleanup_polygon;
 
fill_rule = CAIRO_FILL_RULE_WINDING;
}
 
clip_path = clip_path->prev;
}
 
_cairo_polygon_translate (&polygon, -extents->x, -extents->y);
status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
&polygon,
NULL);
if (unlikely (status))
goto cleanup_polygon;
 
status = composite_polygon (compositor, &composite,
&polygon, fill_rule, antialias);
_cairo_composite_rectangles_fini (&composite);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
goto error;
 
_cairo_polygon_init (&polygon, &box, 1);
 
clip_path = clip->path;
antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT;
clip_path = clip_path->prev;
while (clip_path) {
if (clip_path->antialias == antialias) {
if (polygon.num_edges == 0) {
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
&polygon);
 
fill_rule = clip_path->fill_rule;
polygon.limits = NULL;
polygon.num_limits = 0;
} else {
cairo_polygon_t next;
 
_cairo_polygon_init (&next, NULL, 0);
status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
clip_path->tolerance,
&next);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = _cairo_polygon_intersect (&polygon, fill_rule,
&next, clip_path->fill_rule);
_cairo_polygon_fini (&next);
fill_rule = CAIRO_FILL_RULE_WINDING;
}
if (unlikely (status))
goto error;
}
 
clip_path = clip_path->prev;
}
 
if (polygon.num_edges) {
_cairo_polygon_translate (&polygon, -extents->x, -extents->y);
status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
CAIRO_OPERATOR_IN,
&_cairo_pattern_white.base,
&polygon,
NULL);
if (unlikely (status))
goto cleanup_polygon;
 
status = composite_polygon (compositor, &composite,
&polygon, fill_rule, antialias);
_cairo_composite_rectangles_fini (&composite);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
goto error;
}
 
return surface;
 
cleanup_polygon:
_cairo_polygon_fini (&polygon);
error:
cairo_surface_destroy (surface);
return _cairo_int_surface_create_in_error (status);
}
 
static cairo_int_status_t
fixup_unbounded_mask (const cairo_spans_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_composite_rectangles_t composite;
cairo_surface_t *clip;
cairo_int_status_t status;
 
TRACE((stderr, "%s\n", __FUNCTION__));
 
clip = get_clip_surface (compositor, extents->surface, extents->clip,
&extents->unbounded);
if (unlikely (clip->status)) {
if ((cairo_int_status_t)clip->status == CAIRO_INT_STATUS_NOTHING_TO_DO)
return CAIRO_STATUS_SUCCESS;
 
return clip->status;
}
 
status = _cairo_composite_rectangles_init_for_boxes (&composite,
extents->surface,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
boxes,
NULL);
if (unlikely (status))
goto cleanup_clip;
 
_cairo_pattern_init_for_surface (&composite.mask_pattern.surface, clip);
composite.mask_pattern.base.filter = CAIRO_FILTER_NEAREST;
composite.mask_pattern.base.extend = CAIRO_EXTEND_NONE;
 
status = composite_boxes (compositor, &composite, boxes);
 
_cairo_pattern_fini (&composite.mask_pattern.base);
_cairo_composite_rectangles_fini (&composite);
 
cleanup_clip:
cairo_surface_destroy (clip);
return status;
}
 
static cairo_int_status_t
fixup_unbounded_polygon (const cairo_spans_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_polygon_t polygon, intersect;
cairo_composite_rectangles_t composite;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_int_status_t status;
 
TRACE((stderr, "%s\n", __FUNCTION__));
 
/* Can we treat the clip as a regular clear-polygon and use it to fill? */
status = _cairo_clip_get_polygon (extents->clip, &polygon,
&fill_rule, &antialias);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status= _cairo_polygon_init_boxes (&intersect, boxes);
if (unlikely (status))
goto cleanup_polygon;
 
status = _cairo_polygon_intersect (&polygon, fill_rule,
&intersect, CAIRO_FILL_RULE_WINDING);
_cairo_polygon_fini (&intersect);
 
if (unlikely (status))
goto cleanup_polygon;
 
status = _cairo_composite_rectangles_init_for_polygon (&composite,
extents->surface,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
&polygon,
NULL);
if (unlikely (status))
goto cleanup_polygon;
 
status = composite_polygon (compositor, &composite,
&polygon, fill_rule, antialias);
 
_cairo_composite_rectangles_fini (&composite);
cleanup_polygon:
_cairo_polygon_fini (&polygon);
 
return status;
}
 
static cairo_int_status_t
fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_boxes_t tmp, clear;
cairo_box_t box;
cairo_int_status_t status;
 
assert (boxes->is_pixel_aligned);
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (extents->bounded.width == extents->unbounded.width &&
extents->bounded.height == extents->unbounded.height)
{
return CAIRO_STATUS_SUCCESS;
}
 
/* subtract the drawn boxes from the unbounded area */
_cairo_boxes_init (&clear);
 
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
 
if (boxes->num_boxes) {
_cairo_boxes_init (&tmp);
 
status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
tmp.chunks.next = &boxes->chunks;
tmp.num_boxes += boxes->num_boxes;
 
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
CAIRO_FILL_RULE_WINDING,
&clear);
tmp.chunks.next = NULL;
if (unlikely (status))
goto error;
} else {
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
 
status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
/* If we have a clip polygon, we need to intersect with that as well */
if (extents->clip->path) {
status = fixup_unbounded_polygon (compositor, extents, &clear);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status = fixup_unbounded_mask (compositor, extents, &clear);
} else {
/* Otherwise just intersect with the clip boxes */
if (extents->clip->num_boxes) {
_cairo_boxes_init_for_array (&tmp,
extents->clip->boxes,
extents->clip->num_boxes);
status = _cairo_boxes_intersect (&clear, &tmp, &clear);
if (unlikely (status))
goto error;
}
 
if (clear.is_pixel_aligned) {
status = compositor->fill_boxes (extents->surface,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
} else {
cairo_composite_rectangles_t composite;
 
status = _cairo_composite_rectangles_init_for_boxes (&composite,
extents->surface,
CAIRO_OPERATOR_CLEAR,
&_cairo_pattern_clear.base,
&clear,
NULL);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = composite_boxes (compositor, &composite, &clear);
_cairo_composite_rectangles_fini (&composite);
}
}
}
 
error:
_cairo_boxes_fini (&clear);
return status;
}
 
static cairo_surface_t *
unwrap_source (const cairo_pattern_t *pattern)
{
cairo_rectangle_int_t limit;
 
return _cairo_pattern_get_source ((cairo_surface_pattern_t *)pattern,
&limit);
}
 
static cairo_bool_t
is_recording_pattern (const cairo_pattern_t *pattern)
{
cairo_surface_t *surface;
 
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
return FALSE;
 
surface = ((const cairo_surface_pattern_t *) pattern)->surface;
return _cairo_surface_is_recording (surface);
}
 
static cairo_bool_t
recording_pattern_contains_sample (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *sample)
{
cairo_recording_surface_t *surface;
 
if (! is_recording_pattern (pattern))
return FALSE;
 
if (pattern->extend == CAIRO_EXTEND_NONE)
return TRUE;
 
surface = (cairo_recording_surface_t *) unwrap_source (pattern);
if (surface->unbounded)
return TRUE;
 
return _cairo_rectangle_contains_rectangle (&surface->extents, sample);
}
 
static cairo_bool_t
op_reduces_to_source (const cairo_composite_rectangles_t *extents,
cairo_bool_t no_mask)
{
if (extents->op == CAIRO_OPERATOR_SOURCE)
return TRUE;
 
if (extents->surface->is_clear)
return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
 
if (no_mask && extents->op == CAIRO_OPERATOR_OVER)
return _cairo_pattern_is_opaque (&extents->source_pattern.base,
&extents->source_sample_area);
 
return FALSE;
}
 
static cairo_status_t
upload_boxes (const cairo_spans_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
const cairo_surface_pattern_t *source = &extents->source_pattern.surface;
cairo_surface_t *src;
cairo_rectangle_int_t limit;
cairo_int_status_t status;
int tx, ty;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
src = _cairo_pattern_get_source(source, &limit);
if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_matrix_is_integer_translation (&source->base.matrix, &tx, &ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Check that the data is entirely within the image */
if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
tx += limit.x;
ty += limit.y;
 
if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
status = compositor->draw_image_boxes (dst,
(cairo_image_surface_t *)src,
boxes, tx, ty);
else
status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
tx, ty);
 
return status;
}
 
static cairo_bool_t
_clip_is_region (const cairo_clip_t *clip)
{
int i;
 
if (clip->is_region)
return TRUE;
 
if (clip->path)
return FALSE;
 
for (i = 0; i < clip->num_boxes; i++) {
const cairo_box_t *b = &clip->boxes[i];
if (!_cairo_fixed_is_integer (b->p1.x | b->p1.y | b->p2.x | b->p2.y))
return FALSE;
}
 
return TRUE;
}
 
static cairo_int_status_t
composite_aligned_boxes (const cairo_spans_compositor_t *compositor,
const cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
const cairo_pattern_t *source = &extents->source_pattern.base;
cairo_int_status_t status;
cairo_bool_t need_clip_mask = ! _clip_is_region (extents->clip);
cairo_bool_t op_is_source;
cairo_bool_t no_mask;
cairo_bool_t inplace;
 
TRACE ((stderr, "%s: need_clip_mask=%d, is-bounded=%d\n",
__FUNCTION__, need_clip_mask, extents->is_bounded));
if (need_clip_mask && ! extents->is_bounded) {
TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
CAIRO_COLOR_IS_OPAQUE (&extents->mask_pattern.solid.color);
op_is_source = op_reduces_to_source (extents, no_mask);
inplace = ! need_clip_mask && op_is_source && no_mask;
 
TRACE ((stderr, "%s: op-is-source=%d [op=%d], no-mask=%d, inplace=%d\n",
__FUNCTION__, op_is_source, op, no_mask, inplace));
 
if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) {
/* SOURCE with a mask is actually a LERP in cairo semantics */
if ((compositor->flags & CAIRO_SPANS_COMPOSITOR_HAS_LERP) == 0) {
TRACE ((stderr, "%s: unsupported lerp\n", __FUNCTION__));
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
/* Are we just copying a recording surface? */
if (inplace &&
recording_pattern_contains_sample (&extents->source_pattern.base,
&extents->source_sample_area))
{
cairo_clip_t *recording_clip;
const cairo_pattern_t *source = &extents->source_pattern.base;
 
/* XXX could also do tiling repeat modes... */
 
/* first clear the area about to be overwritten */
if (! dst->is_clear) {
status = compositor->fill_boxes (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
boxes);
if (unlikely (status))
return status;
 
dst->is_clear = TRUE;
}
 
recording_clip = _cairo_clip_from_boxes (boxes);
status = _cairo_recording_surface_replay_with_clip (unwrap_source (source),
&source->matrix,
dst, recording_clip);
_cairo_clip_destroy (recording_clip);
 
return status;
}
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (! need_clip_mask && no_mask && source->type == CAIRO_PATTERN_TYPE_SOLID) {
const cairo_color_t *color;
 
color = &((cairo_solid_pattern_t *) source)->color;
if (op_is_source)
op = CAIRO_OPERATOR_SOURCE;
status = compositor->fill_boxes (dst, op, color, boxes);
} else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) {
status = upload_boxes (compositor, extents, boxes);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_surface_t *src;
cairo_surface_t *mask = NULL;
int src_x, src_y;
int mask_x = 0, mask_y = 0;
 
/* All typical cases will have been resolved before now... */
if (need_clip_mask) {
mask = get_clip_surface (compositor, dst, extents->clip,
&extents->bounded);
if (unlikely (mask->status))
return mask->status;
 
mask_x = -extents->bounded.x;
mask_y = -extents->bounded.y;
}
 
/* XXX but this is still ugly */
if (! no_mask) {
src = compositor->pattern_to_surface (dst,
&extents->mask_pattern.base,
TRUE,
&extents->bounded,
&extents->mask_sample_area,
&src_x, &src_y);
if (unlikely (src->status)) {
cairo_surface_destroy (mask);
return src->status;
}
 
if (mask != NULL) {
status = compositor->composite_boxes (mask, CAIRO_OPERATOR_IN,
src, NULL,
src_x, src_y,
0, 0,
mask_x, mask_y,
boxes, &extents->bounded);
 
cairo_surface_destroy (src);
} else {
mask = src;
mask_x = src_x;
mask_y = src_y;
}
}
 
src = compositor->pattern_to_surface (dst, source, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
status = compositor->composite_boxes (dst, op, src, mask,
src_x, src_y,
mask_x, mask_y,
0, 0,
boxes, &extents->bounded);
cairo_surface_destroy (src);
} else
status = src->status;
 
cairo_surface_destroy (mask);
}
 
if (status == CAIRO_INT_STATUS_SUCCESS && ! extents->is_bounded)
status = fixup_unbounded_boxes (compositor, extents, boxes);
 
return status;
}
 
static cairo_bool_t
composite_needs_clip (const cairo_composite_rectangles_t *composite,
const cairo_box_t *extents)
{
return !_cairo_clip_contains_box (composite->clip, extents);
}
 
static cairo_int_status_t
composite_boxes (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_abstract_span_renderer_t renderer;
cairo_rectangular_scan_converter_t converter;
const struct _cairo_boxes_chunk *chunk;
cairo_int_status_t status;
cairo_box_t box;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
_cairo_box_from_rectangle (&box, &extents->unbounded);
if (composite_needs_clip (extents, &box)) {
TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
_cairo_rectangular_scan_converter_init (&converter, &extents->unbounded);
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
int i;
 
for (i = 0; i < chunk->count; i++) {
status = _cairo_rectangular_scan_converter_add_box (&converter, &box[i], 1);
if (unlikely (status))
goto cleanup_converter;
}
}
 
status = compositor->renderer_init (&renderer, extents,
CAIRO_ANTIALIAS_DEFAULT, FALSE);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = converter.base.generate (&converter.base, &renderer.base);
compositor->renderer_fini (&renderer, status);
 
cleanup_converter:
converter.base.destroy (&converter.base);
return status;
}
 
static cairo_int_status_t
composite_polygon (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_abstract_span_renderer_t renderer;
cairo_scan_converter_t *converter;
cairo_bool_t needs_clip;
cairo_int_status_t status;
 
if (extents->is_bounded)
needs_clip = extents->clip->path != NULL;
else
needs_clip = !_clip_is_region (extents->clip) || extents->clip->num_boxes > 1;
TRACE ((stderr, "%s - needs_clip=%d\n", __FUNCTION__, needs_clip));
if (needs_clip) {
TRACE ((stderr, "%s: unsupported clip\n", __FUNCTION__));
return CAIRO_INT_STATUS_UNSUPPORTED;
converter = _cairo_clip_tor_scan_converter_create (extents->clip,
polygon,
fill_rule, antialias);
} else {
const cairo_rectangle_int_t *r = &extents->unbounded;
 
if (antialias == CAIRO_ANTIALIAS_FAST) {
converter = _cairo_tor22_scan_converter_create (r->x, r->y,
r->x + r->width,
r->y + r->height,
fill_rule, antialias);
status = _cairo_tor22_scan_converter_add_polygon (converter, polygon);
} else if (antialias == CAIRO_ANTIALIAS_NONE) {
converter = _cairo_mono_scan_converter_create (r->x, r->y,
r->x + r->width,
r->y + r->height,
fill_rule);
status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
} else {
converter = _cairo_tor_scan_converter_create (r->x, r->y,
r->x + r->width,
r->y + r->height,
fill_rule, antialias);
status = _cairo_tor_scan_converter_add_polygon (converter, polygon);
}
}
if (unlikely (status))
goto cleanup_converter;
 
status = compositor->renderer_init (&renderer, extents,
antialias, needs_clip);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = converter->generate (converter, &renderer.base);
compositor->renderer_fini (&renderer, status);
 
cleanup_converter:
converter->destroy (converter);
return status;
}
 
static cairo_int_status_t
trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_box_t box;
 
_cairo_boxes_extents (boxes, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_int_status_t
trim_extents_to_polygon (cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon)
{
return _cairo_composite_rectangles_intersect_mask_extents (extents,
&polygon->extents);
}
 
static cairo_int_status_t
clip_and_composite_boxes (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_int_status_t status;
cairo_polygon_t polygon;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
status = trim_extents_to_boxes (extents, boxes);
if (unlikely (status))
return status;
 
if (boxes->num_boxes == 0) {
if (extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
return fixup_unbounded_boxes (compositor, extents, boxes);
}
 
/* Can we reduce drawing through a clip-mask to simply drawing the clip? */
if (extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_clip_t *clip;
 
clip = _cairo_clip_copy (extents->clip);
clip = _cairo_clip_intersect_boxes (clip, boxes);
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &antialias);
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
extents->clip = clip;
 
status = clip_and_composite_polygon (compositor, extents, &polygon,
fill_rule, antialias);
 
clip = extents->clip;
extents->clip = saved_clip;
 
_cairo_polygon_fini (&polygon);
}
_cairo_clip_destroy (clip);
 
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
if (boxes->is_pixel_aligned) {
status = composite_aligned_boxes (compositor, extents, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
status = composite_boxes (compositor, extents, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _cairo_polygon_init_boxes (&polygon, boxes);
if (unlikely (status))
return status;
 
status = composite_polygon (compositor, extents, &polygon,
CAIRO_FILL_RULE_WINDING,
CAIRO_ANTIALIAS_DEFAULT);
_cairo_polygon_fini (&polygon);
 
return status;
}
 
static cairo_int_status_t
clip_and_composite_polygon (const cairo_spans_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* XXX simply uses polygon limits.point extemities, tessellation? */
status = trim_extents_to_polygon (extents, polygon);
if (unlikely (status))
return status;
 
if (_cairo_polygon_is_empty (polygon)) {
cairo_boxes_t boxes;
 
if (extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
_cairo_boxes_init (&boxes);
extents->bounded.width = extents->bounded.height = 0;
return fixup_unbounded_boxes (compositor, extents, &boxes);
}
 
if (extents->is_bounded && extents->clip->path) {
cairo_polygon_t clipper;
cairo_antialias_t clip_antialias;
cairo_fill_rule_t clip_fill_rule;
 
TRACE((stderr, "%s - combining shape with clip polygon\n",
__FUNCTION__));
 
status = _cairo_clip_get_polygon (extents->clip,
&clipper,
&clip_fill_rule,
&clip_antialias);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *old_clip;
 
if (clip_antialias == antialias) {
status = _cairo_polygon_intersect (polygon, fill_rule,
&clipper, clip_fill_rule);
_cairo_polygon_fini (&clipper);
if (unlikely (status))
return status;
 
old_clip = extents->clip;
extents->clip = _cairo_clip_copy_region (extents->clip);
_cairo_clip_destroy (old_clip);
 
status = trim_extents_to_polygon (extents, polygon);
if (unlikely (status))
return status;
 
fill_rule = CAIRO_FILL_RULE_WINDING;
} else {
_cairo_polygon_fini (&clipper);
}
}
}
 
return composite_polygon (compositor, extents,
polygon, fill_rule, antialias);
}
 
/* high-level compositor interface */
 
static cairo_int_status_t
_cairo_spans_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
cairo_boxes_t boxes;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
_cairo_clip_steal_boxes (extents->clip, &boxes);
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_clip_unsteal_boxes (extents->clip, &boxes);
 
return status;
}
 
static cairo_int_status_t
_cairo_spans_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
cairo_int_status_t status;
cairo_boxes_t boxes;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
_cairo_clip_steal_boxes (extents->clip, &boxes);
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_clip_unsteal_boxes (extents->clip, &boxes);
 
return status;
}
 
static cairo_int_status_t
_cairo_spans_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
TRACE_ (_cairo_debug_print_path (stderr, path));
TRACE_ (_cairo_debug_print_clip (stderr, extents->clip));
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
_cairo_boxes_limit (&boxes,
extents->clip->boxes,
extents->clip->num_boxes);
 
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule = CAIRO_FILL_RULE_WINDING;
 
if (! _cairo_rectangle_contains_rectangle (&extents->unbounded,
&extents->mask))
{
if (extents->clip->num_boxes == 1) {
_cairo_polygon_init (&polygon, extents->clip->boxes, 1);
} else {
cairo_box_t limits;
_cairo_box_from_rectangle (&limits, &extents->unbounded);
_cairo_polygon_init (&polygon, &limits, 1);
}
}
else
{
_cairo_polygon_init (&polygon, NULL, 0);
}
status = _cairo_path_fixed_stroke_to_polygon (path,
style,
ctm, ctm_inverse,
tolerance,
&polygon);
TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
polygon.num_limits = 0;
 
if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) {
status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
extents->clip->boxes,
extents->clip->num_boxes);
}
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
 
if (extents->is_bounded) {
extents->clip = _cairo_clip_copy_path (extents->clip);
extents->clip = _cairo_clip_intersect_box(extents->clip,
&polygon.extents);
}
 
status = clip_and_composite_polygon (compositor, extents, &polygon,
fill_rule, antialias);
 
if (extents->is_bounded) {
_cairo_clip_destroy (extents->clip);
extents->clip = saved_clip;
}
}
_cairo_polygon_fini (&polygon);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_spans_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_spans_compositor_t *compositor = (cairo_spans_compositor_t*)_compositor;
cairo_int_status_t status;
 
TRACE((stderr, "%s op=%d, antialias=%d\n", __FUNCTION__, extents->op, antialias));
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
TRACE((stderr, "%s - rectilinear\n", __FUNCTION__));
 
_cairo_boxes_init (&boxes);
if (! _cairo_clip_contains_rectangle (extents->clip, &extents->mask))
_cairo_boxes_limit (&boxes,
extents->clip->boxes,
extents->clip->num_boxes);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
 
TRACE((stderr, "%s - polygon\n", __FUNCTION__));
 
if (! _cairo_rectangle_contains_rectangle (&extents->unbounded,
&extents->mask))
{
TRACE((stderr, "%s - clipping to bounds\n", __FUNCTION__));
if (extents->clip->num_boxes == 1) {
_cairo_polygon_init (&polygon, extents->clip->boxes, 1);
} else {
cairo_box_t limits;
_cairo_box_from_rectangle (&limits, &extents->unbounded);
_cairo_polygon_init (&polygon, &limits, 1);
}
}
else
{
_cairo_polygon_init (&polygon, NULL, 0);
}
 
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
polygon.num_limits = 0;
 
if (status == CAIRO_INT_STATUS_SUCCESS && extents->clip->num_boxes > 1) {
TRACE((stderr, "%s - polygon intersect with %d clip boxes\n",
__FUNCTION__, extents->clip->num_boxes));
status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
extents->clip->boxes,
extents->clip->num_boxes);
}
TRACE_ (_cairo_debug_print_polygon (stderr, &polygon));
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
 
if (extents->is_bounded) {
TRACE((stderr, "%s - polygon discard clip boxes\n",
__FUNCTION__));
extents->clip = _cairo_clip_copy_path (extents->clip);
extents->clip = _cairo_clip_intersect_box(extents->clip,
&polygon.extents);
}
 
status = clip_and_composite_polygon (compositor, extents, &polygon,
fill_rule, antialias);
 
if (extents->is_bounded) {
_cairo_clip_destroy (extents->clip);
extents->clip = saved_clip;
}
}
_cairo_polygon_fini (&polygon);
 
TRACE((stderr, "%s - polygon status=%d\n", __FUNCTION__, status));
}
 
return status;
}
 
void
_cairo_spans_compositor_init (cairo_spans_compositor_t *compositor,
const cairo_compositor_t *delegate)
{
compositor->base.delegate = delegate;
 
compositor->base.paint = _cairo_spans_compositor_paint;
compositor->base.mask = _cairo_spans_compositor_mask;
compositor->base.fill = _cairo_spans_compositor_fill;
compositor->base.stroke = _cairo_spans_compositor_stroke;
compositor->base.glyphs = NULL;
}
/programs/develop/libraries/cairo/src/cairo-spans-private.h
36,11 → 36,9
/* A structure representing an open-ended horizontal span of constant
* pixel coverage. */
typedef struct _cairo_half_open_span {
/* The inclusive x-coordinate of the start of the span. */
int x;
 
/* The pixel coverage for the pixels to the right. */
int coverage;
int32_t x; /* The inclusive x-coordinate of the start of the span. */
uint8_t coverage; /* The pixel coverage for the pixels to the right. */
uint8_t inverse; /* between regular mask and clip */
} cairo_half_open_span_t;
 
/* Span renderer interface. Instances of renderers are provided by
73,17 → 71,6
/* Destroy this scan converter. */
cairo_destroy_func_t destroy;
 
/* Add a single edge to the converter. */
cairo_status_t (*add_edge) (void *abstract_converter,
const cairo_point_t *p1,
const cairo_point_t *p2,
int top, int bottom,
int dir);
 
/* Add a polygon (set of edges) to the converter. */
cairo_status_t (*add_polygon) (void *abstract_converter,
const cairo_polygon_t *polygon);
 
/* Generates coverage spans for rows for the added edges and calls
* the renderer function for each row. After generating spans the
* only valid thing to do with the converter is to destroy it. */
101,13 → 88,43
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
cairo_private cairo_status_t
_cairo_tor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon);
 
cairo_private cairo_scan_converter_t *
_cairo_tor22_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
cairo_private cairo_status_t
_cairo_tor22_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon);
 
cairo_private cairo_scan_converter_t *
_cairo_mono_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule);
cairo_private cairo_status_t
_cairo_mono_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon);
 
cairo_private cairo_scan_converter_t *
_cairo_clip_tor_scan_converter_create (cairo_clip_t *clip,
cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias);
 
typedef struct _cairo_rectangular_scan_converter {
cairo_scan_converter_t base;
 
int xmin, xmax;
int ymin, ymax;
cairo_box_t extents;
 
struct _cairo_rectangular_scan_converter_chunk {
struct _cairo_rectangular_scan_converter_chunk *next;
/programs/develop/libraries/cairo/src/cairo-spans.c
27,63 → 27,11
#include "cairoint.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-clip-private.h"
#include "cairo-error-private.h"
#include "cairo-fixed-private.h"
#include "cairo-types-private.h"
 
static cairo_scan_converter_t *
_create_scan_converter (cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects)
{
if (antialias == CAIRO_ANTIALIAS_NONE) {
ASSERT_NOT_REACHED;
return NULL;
}
 
return _cairo_tor_scan_converter_create (rects->bounded.x,
rects->bounded.y,
rects->bounded.x + rects->bounded.width,
rects->bounded.y + rects->bounded.height,
fill_rule);
}
 
/* XXX Add me to the compositor interface. Ok, first create the compositor
* interface, and then add this with associated fallback!
*/
cairo_status_t
_cairo_surface_composite_polygon (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_polygon_t *polygon,
cairo_region_t *clip_region)
{
cairo_span_renderer_t *renderer;
cairo_scan_converter_t *converter;
cairo_status_t status;
 
converter = _create_scan_converter (fill_rule, antialias, rects);
status = converter->add_polygon (converter, polygon);
if (unlikely (status))
goto CLEANUP_CONVERTER;
 
renderer = _cairo_surface_create_span_renderer (op, pattern, surface,
antialias, rects,
clip_region);
status = converter->generate (converter, renderer);
if (unlikely (status))
goto CLEANUP_RENDERER;
 
status = renderer->finish (renderer);
 
CLEANUP_RENDERER:
renderer->destroy (renderer);
CLEANUP_CONVERTER:
converter->destroy (converter);
return status;
}
 
static void
_cairo_nil_destroy (void *abstract)
{
91,31 → 39,6
}
 
static cairo_status_t
_cairo_nil_scan_converter_add_polygon (void *abstract_converter,
const cairo_polygon_t *polygon)
{
(void) abstract_converter;
(void) polygon;
return _cairo_scan_converter_status (abstract_converter);
}
 
static cairo_status_t
_cairo_nil_scan_converter_add_edge (void *abstract_converter,
const cairo_point_t *p1,
const cairo_point_t *p2,
int top, int bottom,
int dir)
{
(void) abstract_converter;
(void) p1;
(void) p2;
(void) top;
(void) bottom;
(void) dir;
return _cairo_scan_converter_status (abstract_converter);
}
 
static cairo_status_t
_cairo_nil_scan_converter_generate (void *abstract_converter,
cairo_span_renderer_t *renderer)
{
139,8 → 62,6
if (error == CAIRO_STATUS_SUCCESS)
ASSERT_NOT_REACHED;
if (converter->status == CAIRO_STATUS_SUCCESS) {
converter->add_polygon = _cairo_nil_scan_converter_add_polygon;
converter->add_edge = _cairo_nil_scan_converter_add_edge;
converter->generate = _cairo_nil_scan_converter_generate;
converter->status = error;
}
204,6 → 125,8
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
default:
break;
}
314,6 → 237,8
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: RETURN_NIL;
case CAIRO_STATUS_DEVICE_TYPE_MISMATCH: RETURN_NIL;
case CAIRO_STATUS_DEVICE_ERROR: RETURN_NIL;
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: RETURN_NIL;
case CAIRO_STATUS_DEVICE_FINISHED: RETURN_NIL;
default:
break;
}
/programs/develop/libraries/cairo/src/cairo-spline.c
36,9 → 36,51
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-slope-private.h"
 
cairo_bool_t
_cairo_spline_intersects (const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d,
const cairo_box_t *box)
{
cairo_box_t bounds;
 
if (_cairo_box_contains_point (box, a) ||
_cairo_box_contains_point (box, b) ||
_cairo_box_contains_point (box, c) ||
_cairo_box_contains_point (box, d))
{
return TRUE;
}
 
bounds.p2 = bounds.p1 = *a;
_cairo_box_add_point (&bounds, b);
_cairo_box_add_point (&bounds, c);
_cairo_box_add_point (&bounds, d);
 
if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
{
return FALSE;
}
 
#if 0 /* worth refining? */
bounds.p2 = bounds.p1 = *a;
_cairo_box_add_curve_to (&bounds, b, c, d);
if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
{
return FALSE;
}
#endif
 
return TRUE;
}
 
cairo_bool_t
_cairo_spline_init (cairo_spline_t *spline,
cairo_spline_add_point_func_t add_point_func,
void *closure,
45,6 → 87,10
const cairo_point_t *a, const cairo_point_t *b,
const cairo_point_t *c, const cairo_point_t *d)
{
/* If both tangents are zero, this is just a straight line */
if (a->x == b->x && a->y == b->y && c->x == d->x && c->y == d->y)
return FALSE;
 
spline->add_point_func = add_point_func;
spline->closure = closure;
 
67,22 → 113,29
else if (b->x != d->x || b->y != d->y)
_cairo_slope_init (&spline->final_slope, &spline->knots.b, &spline->knots.d);
else
_cairo_slope_init (&spline->final_slope, &spline->knots.a, &spline->knots.d);
return FALSE; /* just treat this as a straight-line from a -> d */
 
/* XXX if the initial, final and vector are all equal, this is just a line */
 
return TRUE;
}
 
static cairo_status_t
_cairo_spline_add_point (cairo_spline_t *spline, cairo_point_t *point)
_cairo_spline_add_point (cairo_spline_t *spline,
const cairo_point_t *point,
const cairo_point_t *knot)
{
cairo_point_t *prev;
cairo_slope_t slope;
 
prev = &spline->last_point;
if (prev->x == point->x && prev->y == point->y)
return CAIRO_STATUS_SUCCESS;
 
_cairo_slope_init (&slope, point, knot);
 
spline->last_point = *point;
return spline->add_point_func (spline->closure, point);
return spline->add_point_func (spline->closure, point, &slope);
}
 
static void
184,13 → 237,15
}
 
static cairo_status_t
_cairo_spline_decompose_into (cairo_spline_knots_t *s1, double tolerance_squared, cairo_spline_t *result)
_cairo_spline_decompose_into (cairo_spline_knots_t *s1,
double tolerance_squared,
cairo_spline_t *result)
{
cairo_spline_knots_t s2;
cairo_status_t status;
 
if (_cairo_spline_error_squared (s1) < tolerance_squared)
return _cairo_spline_add_point (result, &s1->a);
return _cairo_spline_add_point (result, &s1->a, &s1->b);
 
_de_casteljau (s1, &s2);
 
213,7 → 268,8
if (unlikely (status))
return status;
 
return _cairo_spline_add_point (spline, &spline->knots.d);
return spline->add_point_func (spline->closure,
&spline->knots.d, &spline->final_slope);
}
 
/* Note: this function is only good for computing bounds in device space. */
325,7 → 381,7
c = -y0 + y1;
FIND_EXTREMES (a, b, c);
 
status = add_point_func (closure, p0);
status = add_point_func (closure, p0, NULL);
if (unlikely (status))
return status;
 
359,10 → 415,10
 
p.x = _cairo_fixed_from_double (x);
p.y = _cairo_fixed_from_double (y);
status = add_point_func (closure, &p);
status = add_point_func (closure, &p, NULL);
if (unlikely (status))
return status;
}
 
return add_point_func (closure, p3);
return add_point_func (closure, p3, NULL);
}
/programs/develop/libraries/cairo/src/cairo-stroke-dash-private.h
0,0 → 1,70
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* 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>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_STROKE_DASH_PRIVATE_H
#define CAIRO_STROKE_DASH_PRIVATE_H
 
#include "cairoint.h"
 
CAIRO_BEGIN_DECLS
 
typedef struct _cairo_stroker_dash {
cairo_bool_t dashed;
unsigned int dash_index;
cairo_bool_t dash_on;
cairo_bool_t dash_starts_on;
double dash_remain;
 
double dash_offset;
const double *dashes;
unsigned int num_dashes;
} cairo_stroker_dash_t;
 
cairo_private void
_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
const cairo_stroke_style_t *style);
 
cairo_private void
_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
 
cairo_private void
_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-stroke-dash.c
0,0 → 1,96
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
*
* 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>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-stroke-dash-private.h"
 
void
_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
{
double offset;
cairo_bool_t on = TRUE;
unsigned int i = 0;
 
if (! dash->dashed)
return;
 
offset = dash->dash_offset;
 
/* We stop searching for a starting point as soon as the
offset reaches zero. Otherwise when an initial dash
segment shrinks to zero it will be skipped over. */
while (offset > 0.0 && offset >= dash->dashes[i]) {
offset -= dash->dashes[i];
on = !on;
if (++i == dash->num_dashes)
i = 0;
}
 
dash->dash_index = i;
dash->dash_on = dash->dash_starts_on = on;
dash->dash_remain = dash->dashes[i] - offset;
}
 
void
_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
{
dash->dash_remain -= step;
if (dash->dash_remain < CAIRO_FIXED_ERROR_DOUBLE) {
if (++dash->dash_index == dash->num_dashes)
dash->dash_index = 0;
 
dash->dash_on = ! dash->dash_on;
dash->dash_remain += dash->dashes[dash->dash_index];
}
}
 
void
_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
const cairo_stroke_style_t *style)
{
dash->dashed = style->dash != NULL;
if (! dash->dashed)
return;
 
dash->dashes = style->dash;
dash->num_dashes = style->num_dashes;
dash->dash_offset = style->dash_offset;
 
_cairo_stroker_dash_start (dash);
}
/programs/develop/libraries/cairo/src/cairo-stroke-style.c
86,10 → 86,9
void
_cairo_stroke_style_fini (cairo_stroke_style_t *style)
{
if (style->dash) {
free (style->dash);
style->dash = NULL;
}
 
style->num_dashes = 0;
 
VG (VALGRIND_MAKE_MEM_NOACCESS (style, sizeof (cairo_stroke_style_t)));
102,6 → 101,7
*/
void
_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy)
{
111,6 → 111,7
style_expansion = M_SQRT1_2;
 
if (style->line_join == CAIRO_LINE_JOIN_MITER &&
! path->stroke_is_rectilinear &&
style_expansion < M_SQRT2 * style->miter_limit)
{
style_expansion = M_SQRT2 * style->miter_limit;
118,10 → 119,53
 
style_expansion *= style->line_width;
 
if (_cairo_matrix_has_unity_scale (ctm)) {
*dx = *dy = style_expansion;
} else {
*dx = style_expansion * hypot (ctm->xx, ctm->xy);
*dy = style_expansion * hypot (ctm->yy, ctm->yx);
}
}
 
void
_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy)
{
double style_expansion = 0.5 * style->line_width;
if (_cairo_matrix_has_unity_scale (ctm)) {
*dx = *dy = style_expansion;
} else {
*dx = style_expansion * hypot (ctm->xx, ctm->xy);
*dy = style_expansion * hypot (ctm->yy, ctm->yx);
}
}
 
void
_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy)
{
double style_expansion = 0.5;
 
if (style->line_join == CAIRO_LINE_JOIN_MITER &&
! path->stroke_is_rectilinear &&
style_expansion < M_SQRT2 * style->miter_limit)
{
style_expansion = M_SQRT2 * style->miter_limit;
}
 
style_expansion *= style->line_width;
 
if (_cairo_matrix_has_unity_scale (ctm)) {
*dx = *dy = style_expansion;
} else {
*dx = style_expansion * hypot (ctm->xx, ctm->xy);
*dy = style_expansion * hypot (ctm->yy, ctm->yx);
}
}
/*
* Computes the period of a dashed stroke style.
* Returns 0 for non-dashed styles.
191,7 → 235,7
} else {
/* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus
* their coverage is approximated based on the area covered by the caps of adjacent on dases. */
for (i = 0; i < style->num_dashes; i+=2)
for (i = 0; i + 1 < style->num_dashes; i += 2)
stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width);
}
 
/programs/develop/libraries/cairo/src/cairo-surface-backend-private.h
0,0 → 1,221
/* 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>
*/
 
#ifndef CAIRO_SURFACE_BACKEND_PRIVATE_H
#define CAIRO_SURFACE_BACKEND_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
 
CAIRO_BEGIN_DECLS
 
struct _cairo_surface_backend {
cairo_surface_type_t type;
 
cairo_warn cairo_status_t
(*finish) (void *surface);
 
cairo_t *
(*create_context) (void *surface);
 
cairo_surface_t *
(*create_similar) (void *surface,
cairo_content_t content,
int width,
int height);
cairo_surface_t *
(*create_similar_image) (void *surface,
cairo_format_t format,
int width,
int height);
 
cairo_image_surface_t *
(*map_to_image) (void *surface,
const cairo_rectangle_int_t *extents);
cairo_int_status_t
(*unmap_image) (void *surface,
cairo_image_surface_t *image);
 
cairo_surface_t *
(*source) (void *abstract_surface,
cairo_rectangle_int_t *extents);
 
cairo_warn cairo_status_t
(*acquire_source_image) (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra);
 
cairo_warn void
(*release_source_image) (void *abstract_surface,
cairo_image_surface_t *image_out,
void *image_extra);
 
cairo_surface_t *
(*snapshot) (void *surface);
 
cairo_warn cairo_int_status_t
(*copy_page) (void *surface);
 
cairo_warn cairo_int_status_t
(*show_page) (void *surface);
 
/* Get the extents of the current surface. For many surface types
* this will be as simple as { x=0, y=0, width=surface->width,
* height=surface->height}.
*
* If this function is not implemented, or if it returns
* FALSE the surface is considered to be
* boundless and infinite bounds are used for it.
*/
cairo_bool_t
(*get_extents) (void *surface,
cairo_rectangle_int_t *extents);
 
void
(*get_font_options) (void *surface,
cairo_font_options_t *options);
 
cairo_warn cairo_status_t
(*flush) (void *surface,
unsigned flags);
 
cairo_warn cairo_status_t
(*mark_dirty_rectangle) (void *surface,
int x,
int y,
int width,
int height);
 
cairo_warn cairo_int_status_t
(*paint) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*mask) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*stroke) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*fill) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*fill_stroke) (void *surface,
cairo_operator_t fill_op,
const cairo_pattern_t *fill_source,
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
const cairo_path_fixed_t*path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *stroke_ctm,
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
const cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*show_glyphs) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip);
 
cairo_bool_t
(*has_show_text_glyphs) (void *surface);
 
cairo_warn cairo_int_status_t
(*show_text_glyphs) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip);
 
const char **
(*get_supported_mime_types) (void *surface);
};
 
cairo_private cairo_status_t
_cairo_surface_default_acquire_source_image (void *surface,
cairo_image_surface_t **image_out,
void **image_extra);
 
cairo_private void
_cairo_surface_default_release_source_image (void *surface,
cairo_image_surface_t *image,
void *image_extra);
 
cairo_private cairo_surface_t *
_cairo_surface_default_source (void *surface,
cairo_rectangle_int_t *extents);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-clipper-private.h
51,14 → 51,13
double,
cairo_antialias_t);
struct _cairo_surface_clipper {
cairo_clip_t clip;
cairo_bool_t is_clipped;
cairo_clip_t *clip;
cairo_surface_clipper_intersect_clip_path_func_t intersect_clip_path;
};
 
cairo_private cairo_status_t
_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private void
_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
/programs/develop/libraries/cairo/src/cairo-surface-clipper.c
35,20 → 35,83
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-surface-clipper-private.h"
 
/* A collection of routines to facilitate vector surface clipping */
 
/* XXX Eliminate repeated paths and nested clips */
 
static cairo_status_t
_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
const cairo_box_t *box)
{
cairo_status_t status;
 
status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
if (unlikely (status))
return status;
 
status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
if (unlikely (status))
return status;
 
return _cairo_path_fixed_close_path (path);
}
 
static cairo_status_t
_cairo_surface_clipper_intersect_clip_boxes (cairo_surface_clipper_t *clipper,
const cairo_clip_t *clip)
{
cairo_path_fixed_t path;
cairo_status_t status;
int i;
 
if (clip->num_boxes == 0)
return CAIRO_STATUS_SUCCESS;
 
/* Reconstruct the path for the clip boxes.
* XXX maybe a new clipper callback?
*/
 
_cairo_path_fixed_init (&path);
for (i = 0; i < clip->num_boxes; i++) {
status = _cairo_path_fixed_add_box (&path, &clip->boxes[i]);
if (unlikely (status)) {
_cairo_path_fixed_fini (&path);
return status;
}
}
 
status = clipper->intersect_clip_path (clipper, &path,
CAIRO_FILL_RULE_WINDING,
0.,
CAIRO_ANTIALIAS_DEFAULT);
_cairo_path_fixed_fini (&path);
 
return status;
}
 
static cairo_status_t
_cairo_surface_clipper_intersect_clip_path_recursive (cairo_surface_clipper_t *clipper,
cairo_clip_path_t *clip_path)
cairo_clip_path_t *clip_path,
cairo_clip_path_t *end)
{
cairo_status_t status;
 
if (clip_path->prev != NULL) {
if (clip_path->prev != end) {
status =
_cairo_surface_clipper_intersect_clip_path_recursive (clipper,
clip_path->prev);
clip_path->prev,
end);
if (unlikely (status))
return status;
}
62,57 → 125,56
 
cairo_status_t
_cairo_surface_clipper_set_clip (cairo_surface_clipper_t *clipper,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_bool_t clear;
cairo_bool_t incremental = FALSE;
 
/* XXX as we cache a reference to the path, and compare every time,
* we may in future need to install a notification if the clip->path
* is every modified (e.g. cairo_clip_translate).
*/
 
if (clip == NULL && clipper->clip.path == NULL)
if (_cairo_clip_equal (clip, clipper->clip))
return CAIRO_STATUS_SUCCESS;
 
if (clip != NULL && clipper->clip.path != NULL &&
_cairo_clip_equal (clip, &clipper->clip))
/* all clipped out state should never propagate this far */
assert (!_cairo_clip_is_all_clipped (clip));
 
/* XXX Is this an incremental clip? */
if (clipper->clip && clip &&
clip->num_boxes == clipper->clip->num_boxes &&
memcmp (clip->boxes, clipper->clip->boxes,
sizeof (cairo_box_t) * clip->num_boxes) == 0)
{
return CAIRO_STATUS_SUCCESS;
cairo_clip_path_t *clip_path = clip->path;
while (clip_path != NULL && clip_path != clipper->clip->path)
clip_path = clip_path->prev;
 
if (clip_path) {
incremental = TRUE;
status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
clip->path,
clipper->clip->path);
}
}
 
/* all clipped out state should never propagate this far */
assert (clip == NULL || clip->path != NULL);
_cairo_clip_destroy (clipper->clip);
clipper->clip = _cairo_clip_copy (clip);
 
/* Check whether this clip is a continuation of the previous.
* If not, we have to remove the current clip and rebuild.
*/
clear = clip == NULL || clip->path->prev != clipper->clip.path;
if (incremental)
return status;
 
_cairo_clip_reset (&clipper->clip);
_cairo_clip_init_copy (&clipper->clip, clip);
 
if (clear) {
clipper->is_clipped = FALSE;
status = clipper->intersect_clip_path (clipper, NULL, 0, 0, 0);
if (unlikely (status))
return status;
 
if (clip != NULL && clip->path != NULL) {
status =
_cairo_surface_clipper_intersect_clip_path_recursive (clipper,
clip->path);
clipper->is_clipped = TRUE;
}
} else {
cairo_clip_path_t *path = clip->path;
if (clip == NULL)
return CAIRO_STATUS_SUCCESS;
 
clipper->is_clipped = TRUE;
status = clipper->intersect_clip_path (clipper,
&path->path,
path->fill_rule,
path->tolerance,
path->antialias);
status = _cairo_surface_clipper_intersect_clip_boxes (clipper, clip);
if (unlikely (status))
return status;
 
if (clip->path != NULL) {
status = _cairo_surface_clipper_intersect_clip_path_recursive (clipper,
clip->path,
NULL);
}
 
return status;
122,8 → 184,7
_cairo_surface_clipper_init (cairo_surface_clipper_t *clipper,
cairo_surface_clipper_intersect_clip_path_func_t func)
{
_cairo_clip_init (&clipper->clip);
clipper->is_clipped = FALSE;
clipper->clip = NULL;
clipper->intersect_clip_path = func;
}
 
130,6 → 191,6
void
_cairo_surface_clipper_reset (cairo_surface_clipper_t *clipper)
{
_cairo_clip_reset (&clipper->clip);
clipper->is_clipped = FALSE;
_cairo_clip_destroy (clipper->clip);
clipper->clip = NULL;
}
/programs/develop/libraries/cairo/src/cairo-surface-fallback-private.h
3,6 → 3,7
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
34,6 → 35,8
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SURFACE_FALLBACK_PRIVATE_H
41,99 → 44,52
 
#include "cairoint.h"
 
cairo_private cairo_status_t
_cairo_surface_fallback_paint (cairo_surface_t *surface,
CAIRO_BEGIN_DECLS
 
cairo_private cairo_int_status_t
_cairo_surface_fallback_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fallback_mask (cairo_surface_t *surface,
cairo_private cairo_int_status_t
_cairo_surface_fallback_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fallback_stroke (cairo_surface_t *surface,
cairo_private cairo_int_status_t
_cairo_surface_fallback_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t*style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fallback_fill (cairo_surface_t *surface,
cairo_private cairo_int_status_t
_cairo_surface_fallback_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface,
cairo_private cairo_int_status_t
_cairo_surface_fallback_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_surface_t *
_cairo_surface_fallback_snapshot (cairo_surface_t *surface);
CAIRO_END_DECLS
 
cairo_private cairo_status_t
_cairo_surface_fallback_composite (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *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_private cairo_status_t
_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects);
 
cairo_private cairo_status_t
_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *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_private cairo_status_t
_cairo_surface_fallback_clone_similar (cairo_surface_t *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);
 
#endif
#endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-fallback.c
3,6 → 3,7
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
40,1603 → 41,75
 
#include "cairoint.h"
 
#include "cairo-boxes-private.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
#include "cairo-compositor-private.h"
#include "cairo-surface-fallback-private.h"
 
typedef struct {
cairo_surface_t *dst;
cairo_rectangle_int_t extents;
cairo_image_surface_t *image;
cairo_rectangle_int_t image_rect;
void *image_extra;
} fallback_state_t;
 
/**
* _fallback_init:
*
* Acquire destination image surface needed for an image-based
* fallback.
*
* Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the extents are not
* visible, %CAIRO_STATUS_SUCCESS if some portion is visible and all
* went well, or some error status otherwise.
**/
static cairo_int_status_t
_fallback_init (fallback_state_t *state,
cairo_surface_t *dst,
int x,
int y,
int width,
int height)
{
cairo_status_t status;
 
state->extents.x = x;
state->extents.y = y;
state->extents.width = width;
state->extents.height = height;
 
state->dst = dst;
 
status = _cairo_surface_acquire_dest_image (dst, &state->extents,
&state->image, &state->image_rect,
&state->image_extra);
if (unlikely (status))
return status;
 
 
/* XXX: This NULL value tucked away in state->image is a rather
* ugly interface. Cleaner would be to push the
* CAIRO_INT_STATUS_NOTHING_TO_DO value down into
* _cairo_surface_acquire_dest_image and its backend
* counterparts. */
assert (state->image != NULL);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_fallback_fini (fallback_state_t *state)
{
_cairo_surface_release_dest_image (state->dst, &state->extents,
state->image, &state->image_rect,
state->image_extra);
}
 
typedef cairo_status_t
(*cairo_draw_func_t) (void *closure,
cairo_int_status_t
_cairo_surface_fallback_paint (void *surface,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region);
 
static cairo_status_t
_create_composite_mask_pattern (cairo_surface_pattern_t *mask_pattern,
cairo_clip_t *clip,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *mask;
cairo_region_t *clip_region = NULL, *fallback_region = NULL;
cairo_status_t status;
cairo_bool_t clip_surface = FALSE;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (_cairo_status_is_error (status) ||
status == CAIRO_INT_STATUS_NOTHING_TO_DO))
{
return status;
}
 
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* We need to use solid here, because to use CAIRO_OPERATOR_SOURCE with
* a mask (as called via _cairo_surface_mask) triggers assertion failures.
*/
mask = _cairo_surface_create_similar_solid (dst,
CAIRO_CONTENT_ALPHA,
extents->width,
extents->height,
CAIRO_COLOR_TRANSPARENT,
TRUE);
if (unlikely (mask->status))
return mask->status;
 
if (clip_region && (extents->x || extents->y)) {
fallback_region = cairo_region_copy (clip_region);
status = fallback_region->status;
if (unlikely (status))
goto CLEANUP_SURFACE;
 
cairo_region_translate (fallback_region,
-extents->x,
-extents->y);
clip_region = fallback_region;
}
 
status = draw_func (draw_closure, CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base, mask,
extents->x, extents->y,
extents,
clip_region);
if (unlikely (status))
goto CLEANUP_SURFACE;
 
if (clip_surface)
status = _cairo_clip_combine_with_surface (clip, mask, extents->x, extents->y);
 
_cairo_pattern_init_for_surface (mask_pattern, mask);
 
CLEANUP_SURFACE:
if (fallback_region)
cairo_region_destroy (fallback_region);
cairo_surface_destroy (mask);
 
return status;
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
_clip_and_composite_with_mask (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_pattern_t mask_pattern;
cairo_status_t status;
 
status = _create_composite_mask_pattern (&mask_pattern,
clip,
draw_func, draw_closure,
dst, extents);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _cairo_surface_composite (op,
src, &mask_pattern.base, dst,
extents->x, extents->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
 
_cairo_pattern_fini (&mask_pattern.base);
}
 
return status;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
_clip_and_composite_combine (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *intermediate;
cairo_surface_pattern_t pattern;
cairo_surface_pattern_t clip_pattern;
cairo_surface_t *clip_surface;
int clip_x, clip_y;
cairo_status_t status;
 
/* We'd be better off here creating a surface identical in format
* to dst, but we have no way of getting that information. Instead
* we ask the backend to create a similar surface of identical content,
* in the belief that the backend will do something useful - like use
* an identical format. For example, the xlib backend will endeavor to
* use a compatible depth to enable core protocol routines.
*/
intermediate =
_cairo_surface_create_similar_scratch (dst, dst->content,
extents->width,
extents->height);
if (intermediate == NULL) {
intermediate =
_cairo_image_surface_create_with_content (dst->content,
extents->width,
extents->width);
}
if (unlikely (intermediate->status))
return intermediate->status;
 
/* Initialize the intermediate surface from the destination surface */
_cairo_pattern_init_for_surface (&pattern, dst);
status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL, intermediate,
extents->x, extents->y,
0, 0,
0, 0,
extents->width, extents->height,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status))
goto CLEANUP_SURFACE;
 
status = (*draw_func) (draw_closure, op,
src, intermediate,
extents->x, extents->y,
extents,
NULL);
if (unlikely (status))
goto CLEANUP_SURFACE;
 
assert (clip->path != NULL);
clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
if (unlikely (clip_surface->status))
goto CLEANUP_SURFACE;
 
_cairo_pattern_init_for_surface (&clip_pattern, clip_surface);
 
/* Combine that with the clip */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_IN,
&clip_pattern.base, NULL, intermediate,
extents->x - clip_x,
extents->y - clip_y,
0, 0,
0, 0,
extents->width, extents->height,
NULL);
if (unlikely (status))
goto CLEANUP_CLIP;
 
/* Punch the clip out of the destination */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
&clip_pattern.base, NULL, dst,
extents->x - clip_x,
extents->y - clip_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
if (unlikely (status))
goto CLEANUP_CLIP;
 
/* Now add the two results together */
_cairo_pattern_init_for_surface (&pattern, intermediate);
status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
&pattern.base, NULL, dst,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
NULL);
_cairo_pattern_fini (&pattern.base);
 
CLEANUP_CLIP:
_cairo_pattern_fini (&clip_pattern.base);
CLEANUP_SURFACE:
cairo_surface_destroy (intermediate);
 
return status;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
_clip_and_composite_source (cairo_clip_t *clip,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_surface_pattern_t mask_pattern;
cairo_region_t *clip_region = NULL;
cairo_status_t status;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (_cairo_status_is_error (status) ||
status == CAIRO_INT_STATUS_NOTHING_TO_DO))
{
return status;
}
}
 
/* Create a surface that is mask IN clip */
status = _create_composite_mask_pattern (&mask_pattern,
clip,
draw_func, draw_closure,
dst, extents);
if (unlikely (status))
return status;
 
/* Compute dest' = dest OUT (mask IN clip) */
status = _cairo_surface_composite (CAIRO_OPERATOR_DEST_OUT,
&mask_pattern.base, NULL, dst,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
clip_region);
 
if (unlikely (status))
goto CLEANUP_MASK_PATTERN;
 
/* Now compute (src IN (mask IN clip)) ADD dest' */
status = _cairo_surface_composite (CAIRO_OPERATOR_ADD,
src, &mask_pattern.base, dst,
extents->x, extents->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height,
clip_region);
 
CLEANUP_MASK_PATTERN:
_cairo_pattern_fini (&mask_pattern.base);
return status;
}
 
static int
_cairo_rectangle_empty (const cairo_rectangle_int_t *rect)
{
return rect->width == 0 || rect->height == 0;
}
 
/**
* _clip_and_composite:
* @clip: a #cairo_clip_t
* @op: the operator to draw with
* @src: source pattern
* @draw_func: function that can be called to draw with the mask onto a surface.
* @draw_closure: data to pass to @draw_func.
* @dst: destination surface
* @extents: rectangle holding a bounding box for the operation; this
* rectangle will be used as the size for the temporary
* surface.
*
* When there is a surface clip, we typically need to create an intermediate
* surface. This function handles the logic of creating a temporary surface
* drawing to it, then compositing the result onto the target surface.
*
* @draw_func is to called to draw the mask; it will be called no more
* than once.
*
* Return value: %CAIRO_STATUS_SUCCESS if the drawing succeeded.
**/
static cairo_status_t
_clip_and_composite (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_draw_func_t draw_func,
void *draw_closure,
cairo_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_status_t status;
 
if (_cairo_rectangle_empty (extents))
/* Nothing to do */
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR) {
src = &_cairo_pattern_white.base;
op = CAIRO_OPERATOR_DEST_OUT;
}
 
if (op == CAIRO_OPERATOR_SOURCE) {
status = _clip_and_composite_source (clip,
src,
draw_func, draw_closure,
dst, extents);
} else {
cairo_bool_t clip_surface = FALSE;
cairo_region_t *clip_region = NULL;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (_cairo_status_is_error (status) ||
status == CAIRO_INT_STATUS_NOTHING_TO_DO))
{
return status;
}
 
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (clip_surface) {
if (_cairo_operator_bounded_by_mask (op)) {
status = _clip_and_composite_with_mask (clip, op,
src,
draw_func, draw_closure,
dst, extents);
} else {
status = _clip_and_composite_combine (clip, op,
src,
draw_func, draw_closure,
dst, extents);
}
} else {
status = draw_func (draw_closure, op,
src, dst,
0, 0,
extents,
clip_region);
}
}
 
return status;
}
 
/* Composites a region representing a set of trapezoids.
*/
static cairo_status_t
_composite_trap_region (cairo_clip_t *clip,
const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_region_t *trap_region,
const cairo_rectangle_int_t *extents)
{
cairo_status_t status;
cairo_surface_pattern_t mask_pattern;
cairo_pattern_t *mask = NULL;
int mask_x = 0, mask_y =0;
 
if (clip != NULL) {
cairo_surface_t *clip_surface = NULL;
int clip_x, clip_y;
 
clip_surface = _cairo_clip_get_surface (clip, dst, &clip_x, &clip_y);
if (unlikely (clip_surface->status))
return clip_surface->status;
 
if (op == CAIRO_OPERATOR_CLEAR) {
src = &_cairo_pattern_white.base;
op = CAIRO_OPERATOR_DEST_OUT;
}
 
_cairo_pattern_init_for_surface (&mask_pattern, clip_surface);
mask_x = extents->x - clip_x;
mask_y = extents->y - clip_y;
mask = &mask_pattern.base;
}
 
status = _cairo_surface_composite (op, src, mask, dst,
extents->x, extents->y,
mask_x, mask_y,
extents->x, extents->y,
extents->width, extents->height,
trap_region);
 
if (mask != NULL)
_cairo_pattern_fini (mask);
 
return status;
}
 
typedef struct {
cairo_traps_t *traps;
cairo_antialias_t antialias;
} cairo_composite_traps_info_t;
 
static cairo_status_t
_composite_traps_draw_func (void *closure,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_composite_traps_info_t *info = closure;
cairo_status_t status;
cairo_region_t *extents_region = NULL;
 
if (dst_x != 0 || dst_y != 0)
_cairo_traps_translate (info->traps, - dst_x, - dst_y);
 
if (clip_region == NULL &&
!_cairo_operator_bounded_by_source (op)) {
extents_region = cairo_region_create_rectangle (extents);
if (unlikely (extents_region->status))
return extents_region->status;
cairo_region_translate (extents_region, -dst_x, -dst_y);
clip_region = extents_region;
}
 
status = _cairo_surface_composite_trapezoids (op,
src, dst, info->antialias,
extents->x, extents->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
info->traps->traps,
info->traps->num_traps,
clip_region);
 
if (extents_region)
cairo_region_destroy (extents_region);
 
return status;
}
 
enum {
HAS_CLEAR_REGION = 0x1,
};
 
static cairo_status_t
_clip_and_composite_region (const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_region_t *trap_region,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_region_t clear_region;
unsigned int has_region = 0;
cairo_status_t status;
 
if (! _cairo_operator_bounded_by_mask (op) && clip == NULL) {
/* If we optimize drawing with an unbounded operator to
* _cairo_surface_fill_rectangles() or to drawing with a
* clip region, then we have an additional region to clear.
*/
_cairo_region_init_rectangle (&clear_region, extents);
status = cairo_region_subtract (&clear_region, trap_region);
if (unlikely (status))
return status;
 
if (! cairo_region_is_empty (&clear_region))
has_region |= HAS_CLEAR_REGION;
}
 
if ((src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR) &&
clip == NULL)
{
const cairo_color_t *color;
 
if (op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
else
color = &((cairo_solid_pattern_t *)src)->color;
 
/* Solid rectangles special case */
status = _cairo_surface_fill_region (dst, op, color, trap_region);
} else {
/* For a simple rectangle, we can just use composite(), for more
* rectangles, we have to set a clip region. The cost of rasterizing
* trapezoids is pretty high for most backends currently, so it's
* worthwhile even if a region is needed.
*
* If we have a clip surface, we set it as the mask; this only works
* for bounded operators other than SOURCE; for unbounded operators,
* clip and mask cannot be interchanged. For SOURCE, the operator
* as implemented by the backends is different in its handling
* of the mask then what we want.
*
* CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has
* more than rectangle and the destination doesn't support clip
* regions. In that case, we fall through.
*/
status = _composite_trap_region (clip, src, op, dst,
trap_region, extents);
}
 
if (has_region & HAS_CLEAR_REGION) {
if (status == CAIRO_STATUS_SUCCESS) {
status = _cairo_surface_fill_region (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear_region);
}
_cairo_region_fini (&clear_region);
}
 
return status;
}
 
/* avoid using region code to re-validate boxes */
static cairo_status_t
_fill_rectangles (cairo_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_traps_t *traps,
cairo_clip_t *clip)
{
const cairo_color_t *color;
cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
cairo_rectangle_int_t *rects = stack_rects;
cairo_status_t status;
int i;
 
if (! traps->is_rectilinear || ! traps->maybe_region)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* XXX: convert clip region to geometric boxes? */
if (clip != NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* XXX: fallback for the region_subtract() operation */
if (! _cairo_operator_bounded_by_mask (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! (src->type == CAIRO_PATTERN_TYPE_SOLID || op == CAIRO_OPERATOR_CLEAR))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (traps->has_intersections) {
if (traps->is_rectangular) {
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
} else {
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
}
if (unlikely (status))
return status;
}
 
for (i = 0; i < traps->num_traps; i++) {
if (! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
traps->maybe_region = FALSE;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (traps->num_traps,
sizeof (cairo_rectangle_int_t));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (i = 0; i < traps->num_traps; i++) {
int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
 
rects[i].x = x1;
rects[i].y = y1;
rects[i].width = x2 - x1;
rects[i].height = y2 - y1;
}
 
if (op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
else
color = &((cairo_solid_pattern_t *)src)->color;
 
status = _cairo_surface_fill_rectangles (dst, op, color, rects, i);
 
if (rects != stack_rects)
free (rects);
 
return status;
}
 
/* fast-path for very common composite of a single rectangle */
static cairo_status_t
_composite_rectangle (cairo_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_traps_t *traps,
cairo_clip_t *clip)
{
cairo_rectangle_int_t rect;
 
if (clip != NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (traps->num_traps > 1 || ! traps->is_rectilinear || ! traps->maybe_region)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_fixed_is_integer (traps->traps[0].top) ||
! _cairo_fixed_is_integer (traps->traps[0].bottom) ||
! _cairo_fixed_is_integer (traps->traps[0].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[0].right.p1.x))
{
traps->maybe_region = FALSE;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
rect.x = _cairo_fixed_integer_part (traps->traps[0].left.p1.x);
rect.y = _cairo_fixed_integer_part (traps->traps[0].top);
rect.width = _cairo_fixed_integer_part (traps->traps[0].right.p1.x) - rect.x;
rect.height = _cairo_fixed_integer_part (traps->traps[0].bottom) - rect.y;
 
return _cairo_surface_composite (op, src, NULL, dst,
rect.x, rect.y,
0, 0,
rect.x, rect.y,
rect.width, rect.height,
NULL);
}
 
/* Warning: This call modifies the coordinates of traps */
static cairo_status_t
_clip_and_composite_trapezoids (const cairo_pattern_t *src,
cairo_operator_t op,
cairo_surface_t *dst,
cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_composite_traps_info_t traps_info;
cairo_region_t *clip_region = NULL;
cairo_bool_t clip_surface = FALSE;
cairo_status_t status;
 
if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op))
return CAIRO_STATUS_SUCCESS;
 
if (clip != NULL) {
status = _cairo_clip_get_region (clip, &clip_region);
if (unlikely (_cairo_status_is_error (status)))
return status;
if (unlikely (status == CAIRO_INT_STATUS_NOTHING_TO_DO))
return CAIRO_STATUS_SUCCESS;
 
clip_surface = status == CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* Use a fast path if the trapezoids consist of a simple region,
* but we can only do this if we do not have a clip surface, or can
* substitute the mask with the clip.
*/
if (! clip_surface ||
(_cairo_operator_bounded_by_mask (op) && op != CAIRO_OPERATOR_SOURCE))
{
cairo_region_t *trap_region = NULL;
 
if (_cairo_operator_bounded_by_source (op)) {
status = _fill_rectangles (dst, op, src, traps, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
status = _composite_rectangle (dst, op, src, traps, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
status = _cairo_traps_extract_region (traps, &trap_region);
if (unlikely (_cairo_status_is_error (status)))
return status;
 
if (trap_region != NULL) {
status = cairo_region_intersect_rectangle (trap_region, extents);
if (unlikely (status)) {
cairo_region_destroy (trap_region);
return status;
}
 
if (clip_region != NULL) {
status = cairo_region_intersect (trap_region, clip_region);
if (unlikely (status)) {
cairo_region_destroy (trap_region);
return status;
}
}
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t trap_extents;
 
cairo_region_get_extents (trap_region, &trap_extents);
if (! _cairo_rectangle_intersect (extents, &trap_extents)) {
cairo_region_destroy (trap_region);
return CAIRO_STATUS_SUCCESS;
}
}
 
status = _clip_and_composite_region (src, op, dst,
trap_region,
clip_surface ? clip : NULL,
extents);
cairo_region_destroy (trap_region);
 
if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
return status;
}
}
 
/* No fast path, exclude self-intersections and clip trapezoids. */
if (traps->has_intersections) {
if (traps->is_rectangular)
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, CAIRO_FILL_RULE_WINDING);
else if (traps->is_rectilinear)
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (traps, CAIRO_FILL_RULE_WINDING);
else
status = _cairo_bentley_ottmann_tessellate_traps (traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
return status;
}
 
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
traps_info.traps = traps;
traps_info.antialias = antialias;
 
return _clip_and_composite (clip, op, src,
_composite_traps_draw_func,
&traps_info, dst, extents);
}
 
cairo_status_t
_cairo_surface_fallback_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
cairo_clip_path_t *clip_path = clip ? clip->path : NULL;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
cairo_boxes_t boxes;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_status_t status;
cairo_traps_t traps;
 
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
 
status = _cairo_composite_rectangles_init_for_paint (&extents,
rect.width,
rect.height,
op, source,
clip);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status))
return status;
 
/* If the clip cannot be reduced to a set of boxes, we will need to
* use a clipmask. Paint is special as it is the only operation that
* does not implicitly use a mask, so we may be able to reduce this
* operation to a fill...
*/
if (clip != NULL && clip_path->prev == NULL &&
_cairo_operator_bounded_by_mask (op))
{
return _cairo_surface_fill (surface, op, source,
&clip_path->path,
clip_path->fill_rule,
clip_path->tolerance,
clip_path->antialias,
NULL);
return _cairo_compositor_paint (&_cairo_fallback_compositor,
surface, op, source, clip);
}
 
/* meh, surface-fallback is dying anyway... */
_cairo_boxes_init_for_array (&boxes, clip_boxes, num_boxes);
status = _cairo_traps_init_boxes (&traps, &boxes);
if (unlikely (status))
goto CLEANUP_BOXES;
 
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, CAIRO_ANTIALIAS_DEFAULT,
clip,
extents.is_bounded ? &extents.bounded : &extents.unbounded);
_cairo_traps_fini (&traps);
 
CLEANUP_BOXES:
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
return status;
}
 
static cairo_status_t
_cairo_surface_mask_draw_func (void *closure,
cairo_int_status_t
_cairo_surface_fallback_mask (void *surface,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_pattern_t *mask = closure;
cairo_status_t status;
cairo_region_t *extents_region = NULL;
 
if (clip_region == NULL &&
!_cairo_operator_bounded_by_source (op)) {
extents_region = cairo_region_create_rectangle (extents);
if (unlikely (extents_region->status))
return extents_region->status;
cairo_region_translate (extents_region, -dst_x, -dst_y);
clip_region = extents_region;
}
 
if (src) {
status = _cairo_surface_composite (op,
src, mask, dst,
extents->x, extents->y,
extents->x, extents->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
clip_region);
} else {
status = _cairo_surface_composite (op,
mask, NULL, dst,
extents->x, extents->y,
0, 0, /* unused */
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height,
clip_region);
}
 
if (extents_region)
cairo_region_destroy (extents_region);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_mask (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
cairo_status_t status;
 
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
 
status = _cairo_composite_rectangles_init_for_mask (&extents,
rect.width, rect.height,
op, source, mask, clip);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
if (clip != NULL && extents.is_bounded) {
status = _cairo_clip_rectangle (clip, &extents.bounded);
if (unlikely (status))
return status;
return _cairo_compositor_mask (&_cairo_fallback_compositor,
surface, op, source, mask, clip);
}
 
return _clip_and_composite (clip, op, source,
_cairo_surface_mask_draw_func,
(void *) mask,
surface,
extents.is_bounded ? &extents.bounded : &extents.unbounded);
}
 
cairo_status_t
_cairo_surface_fallback_stroke (cairo_surface_t *surface,
cairo_int_status_t
_cairo_surface_fallback_stroke (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_path_fixed_t*path,
const cairo_stroke_style_t*style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
cairo_status_t status;
 
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
 
status = _cairo_composite_rectangles_init_for_stroke (&extents,
rect.width,
rect.height,
op, source,
path, stroke_style, ctm,
clip);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status))
return status;
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
 
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, clip_boxes, num_boxes);
 
if (path->is_rectilinear) {
status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
stroke_style,
ctm,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
 
if (_cairo_status_is_error (status))
goto CLEANUP;
return _cairo_compositor_stroke (&_cairo_fallback_compositor,
surface, op, source, path,
style, ctm,ctm_inverse,
tolerance, antialias, clip);
}
 
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (unlikely (status))
goto CLEANUP;
 
if (polygon.num_edges == 0)
goto DO_TRAPS;
 
if (_cairo_operator_bounded_by_mask (op)) {
_cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
goto CLEANUP;
}
 
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
goto CLEANUP;
 
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip,
extents.is_bounded ? &extents.bounded : &extents.unbounded);
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_fill (cairo_surface_t *surface,
cairo_int_status_t
_cairo_surface_fallback_fill (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_polygon_t polygon;
cairo_traps_t traps;
cairo_box_t boxes_stack[32], *clip_boxes = boxes_stack;
int num_boxes = ARRAY_LENGTH (boxes_stack);
cairo_bool_t is_rectilinear;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
cairo_status_t status;
 
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
 
status = _cairo_composite_rectangles_init_for_fill (&extents,
rect.width,
rect.height,
op, source, path,
return _cairo_compositor_fill (&_cairo_fallback_compositor,
surface, op, source, path,
fill_rule, tolerance, antialias,
clip);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_extents (clip, &extents))
clip = NULL;
 
status = _cairo_clip_to_boxes (&clip, &extents, &clip_boxes, &num_boxes);
if (unlikely (status))
return status;
 
_cairo_traps_init (&traps);
_cairo_traps_limit (&traps, clip_boxes, num_boxes);
 
_cairo_polygon_init (&polygon);
_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
 
if (path->is_empty_fill)
goto DO_TRAPS;
 
is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
if (is_rectilinear) {
status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
fill_rule,
&traps);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
 
if (_cairo_status_is_error (status))
goto CLEANUP;
}
 
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (unlikely (status))
goto CLEANUP;
 
if (polygon.num_edges == 0)
goto DO_TRAPS;
 
if (_cairo_operator_bounded_by_mask (op)) {
_cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
goto CLEANUP;
}
 
if (is_rectilinear) {
status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
&polygon,
fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS))
goto DO_TRAPS;
 
if (unlikely (_cairo_status_is_error (status)))
goto CLEANUP;
}
 
/* Fall back to trapezoid fills. */
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
fill_rule);
if (unlikely (status))
goto CLEANUP;
 
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
clip,
extents.is_bounded ? &extents.bounded : &extents.unbounded);
CLEANUP:
_cairo_traps_fini (&traps);
_cairo_polygon_fini (&polygon);
if (clip_boxes != boxes_stack)
free (clip_boxes);
 
return status;
}
 
typedef struct {
cairo_scaled_font_t *font;
cairo_glyph_t *glyphs;
int num_glyphs;
} cairo_show_glyphs_info_t;
 
static cairo_status_t
_cairo_surface_old_show_glyphs_draw_func (void *closure,
cairo_int_status_t
_cairo_surface_fallback_glyphs (void *surface,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_surface_t *dst,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_region_t *clip_region)
{
cairo_show_glyphs_info_t *glyph_info = closure;
cairo_status_t status;
cairo_region_t *extents_region = NULL;
 
if (clip_region == NULL &&
!_cairo_operator_bounded_by_source (op)) {
extents_region = cairo_region_create_rectangle (extents);
if (unlikely (extents_region->status))
return extents_region->status;
cairo_region_translate (extents_region, -dst_x, -dst_y);
clip_region = extents_region;
}
 
/* Modifying the glyph array is fine because we know that this function
* will be called only once, and we've already made a copy of the
* glyphs in the wrapper.
*/
if (dst_x != 0 || dst_y != 0) {
int i;
 
for (i = 0; i < glyph_info->num_glyphs; ++i) {
((cairo_glyph_t *) glyph_info->glyphs)[i].x -= dst_x;
((cairo_glyph_t *) glyph_info->glyphs)[i].y -= dst_y;
}
}
 
status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
dst,
extents->x, extents->y,
extents->x - dst_x,
extents->y - dst_y,
extents->width,
extents->height,
glyph_info->glyphs,
glyph_info->num_glyphs,
clip_region);
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_scaled_font_show_glyphs (glyph_info->font,
op,
src, dst,
extents->x, extents->y,
extents->x - dst_x,
extents->y - dst_y,
extents->width, extents->height,
glyph_info->glyphs,
glyph_info->num_glyphs,
clip_region);
}
 
if (extents_region)
cairo_region_destroy (extents_region);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_show_glyphs (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_show_glyphs_info_t glyph_info;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
cairo_status_t status;
 
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
 
status = _cairo_composite_rectangles_init_for_glyphs (&extents,
rect.width,
rect.height,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
NULL);
if (unlikely (status))
return status;
 
if (_cairo_clip_contains_rectangle (clip, &extents.mask))
clip = NULL;
 
if (clip != NULL && extents.is_bounded) {
status = _cairo_clip_rectangle (clip, &extents.bounded);
if (unlikely (status))
return status;
return _cairo_compositor_glyphs (&_cairo_fallback_compositor,
surface, op, source,
glyphs, num_glyphs, scaled_font,
clip);
}
 
glyph_info.font = scaled_font;
glyph_info.glyphs = glyphs;
glyph_info.num_glyphs = num_glyphs;
 
return _clip_and_composite (clip, op, source,
_cairo_surface_old_show_glyphs_draw_func,
&glyph_info,
surface,
extents.is_bounded ? &extents.bounded : &extents.unbounded);
}
 
cairo_surface_t *
_cairo_surface_fallback_snapshot (cairo_surface_t *surface)
{
cairo_surface_t *snapshot;
cairo_status_t status;
cairo_format_t format;
cairo_surface_pattern_t pattern;
cairo_image_surface_t *image;
void *image_extra;
 
status = _cairo_surface_acquire_source_image (surface,
&image, &image_extra);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
format = image->format;
if (format == CAIRO_FORMAT_INVALID) {
/* Non-standard images formats can be generated when retrieving
* images from unusual xservers, for example.
*/
format = _cairo_format_from_content (image->base.content);
}
snapshot = cairo_image_surface_create (format,
image->width,
image->height);
if (cairo_surface_status (snapshot)) {
_cairo_surface_release_source_image (surface, image, image_extra);
return snapshot;
}
 
_cairo_pattern_init_for_surface (&pattern, &image->base);
status = _cairo_surface_paint (snapshot,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
_cairo_surface_release_source_image (surface, image, image_extra);
if (unlikely (status)) {
cairo_surface_destroy (snapshot);
return _cairo_surface_create_in_error (status);
}
 
return snapshot;
}
 
cairo_status_t
_cairo_surface_fallback_composite (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *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)
{
fallback_state_t state;
cairo_region_t *fallback_region = NULL;
cairo_status_t status;
 
status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
if (unlikely (status))
return status;
 
/* We know this will never fail with the image backend; but
* instead of calling into it directly, we call
* _cairo_surface_composite so that we get the correct device
* offset handling.
*/
 
if (clip_region != NULL && (state.image_rect.x || state.image_rect.y)) {
fallback_region = cairo_region_copy (clip_region);
status = fallback_region->status;
if (unlikely (status))
goto FAIL;
 
cairo_region_translate (fallback_region,
-state.image_rect.x,
-state.image_rect.y);
clip_region = fallback_region;
}
 
status = _cairo_surface_composite (op, src, mask,
&state.image->base,
src_x, src_y, mask_x, mask_y,
dst_x - state.image_rect.x,
dst_y - state.image_rect.y,
width, height,
clip_region);
FAIL:
if (fallback_region != NULL)
cairo_region_destroy (fallback_region);
_fallback_fini (&state);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
fallback_state_t state;
cairo_rectangle_int_t *offset_rects = NULL;
cairo_status_t status;
int x1, y1, x2, y2;
int i;
 
assert (surface->snapshot_of == NULL);
 
if (num_rects <= 0)
return CAIRO_STATUS_SUCCESS;
 
/* Compute the bounds of the rectangles, so that we know what area of the
* destination surface to fetch
*/
x1 = rects[0].x;
y1 = rects[0].y;
x2 = rects[0].x + rects[0].width;
y2 = rects[0].y + rects[0].height;
 
for (i = 1; i < num_rects; i++) {
if (rects[i].x < x1)
x1 = rects[i].x;
if (rects[i].y < y1)
y1 = rects[i].y;
 
if ((int) (rects[i].x + rects[i].width) > x2)
x2 = rects[i].x + rects[i].width;
if ((int) (rects[i].y + rects[i].height) > y2)
y2 = rects[i].y + rects[i].height;
}
 
status = _fallback_init (&state, surface, x1, y1, x2 - x1, y2 - y1);
if (unlikely (status))
return status;
 
/* If the fetched image isn't at 0,0, we need to offset the rectangles */
 
if (state.image_rect.x != 0 || state.image_rect.y != 0) {
offset_rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t));
if (unlikely (offset_rects == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto DONE;
}
 
for (i = 0; i < num_rects; i++) {
offset_rects[i].x = rects[i].x - state.image_rect.x;
offset_rects[i].y = rects[i].y - state.image_rect.y;
offset_rects[i].width = rects[i].width;
offset_rects[i].height = rects[i].height;
}
 
rects = offset_rects;
}
 
status = _cairo_surface_fill_rectangles (&state.image->base,
op, color,
rects, num_rects);
 
free (offset_rects);
 
DONE:
_fallback_fini (&state);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *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)
{
fallback_state_t state;
cairo_region_t *fallback_region = NULL;
cairo_trapezoid_t *offset_traps = NULL;
cairo_status_t status;
 
status = _fallback_init (&state, dst, dst_x, dst_y, width, height);
if (unlikely (status))
return status;
 
/* If the destination image isn't at 0,0, we need to offset the trapezoids */
 
if (state.image_rect.x != 0 || state.image_rect.y != 0) {
offset_traps = _cairo_malloc_ab (num_traps, sizeof (cairo_trapezoid_t));
if (offset_traps == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
 
_cairo_trapezoid_array_translate_and_scale (offset_traps, traps, num_traps,
- state.image_rect.x, - state.image_rect.y,
1.0, 1.0);
traps = offset_traps;
 
/* similarly we need to adjust the region */
if (clip_region != NULL) {
fallback_region = cairo_region_copy (clip_region);
status = fallback_region->status;
if (unlikely (status))
goto FAIL;
 
cairo_region_translate (fallback_region,
-state.image_rect.x,
-state.image_rect.y);
clip_region = fallback_region;
}
}
 
status = _cairo_surface_composite_trapezoids (op, pattern,
&state.image->base,
antialias,
src_x, src_y,
dst_x - state.image_rect.x,
dst_y - state.image_rect.y,
width, height,
traps, num_traps,
clip_region);
FAIL:
if (offset_traps != NULL)
free (offset_traps);
 
if (fallback_region != NULL)
cairo_region_destroy (fallback_region);
 
_fallback_fini (&state);
 
return status;
}
 
cairo_status_t
_cairo_surface_fallback_clone_similar (cairo_surface_t *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_surface_t *new_surface;
cairo_surface_pattern_t pattern;
cairo_status_t status;
 
new_surface = _cairo_surface_create_similar_scratch (surface,
src->content,
width, height);
if (new_surface == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (new_surface->status))
return new_surface->status;
 
/* We have to copy these here, so that the coordinate spaces are correct */
new_surface->device_transform = src->device_transform;
new_surface->device_transform_inverse = src->device_transform_inverse;
 
_cairo_pattern_init_for_surface (&pattern, src);
cairo_matrix_init_translate (&pattern.base.matrix, src_x, src_y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
 
status = _cairo_surface_paint (new_surface,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
 
if (unlikely (status)) {
cairo_surface_destroy (new_surface);
return status;
}
 
*clone_offset_x = src_x;
*clone_offset_y = src_y;
*clone_out = new_surface;
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-surface-inline.h
0,0 → 1,60
/* 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>
*/
 
#ifndef CAIRO_SURFACE_INLINE_H
#define CAIRO_SURFACE_INLINE_H
 
#include "cairo-surface-private.h"
 
static inline cairo_status_t
__cairo_surface_flush (cairo_surface_t *surface, unsigned flags)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
if (surface->backend->flush)
status = surface->backend->flush (surface, flags);
return status;
}
 
static inline cairo_surface_t *
_cairo_surface_reference (cairo_surface_t *surface)
{
if (!CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
_cairo_reference_count_inc (&surface->ref_count);
return surface;
}
 
#endif /* CAIRO_SURFACE_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-surface-observer-inline.h
0,0 → 1,59
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SURFACE_OBSERVER_INLINE_H
#define CAIRO_SURFACE_OBSERVER_INLINE_H
 
#include "cairo-surface-observer-private.h"
 
static inline cairo_surface_t *
_cairo_surface_observer_get_target (cairo_surface_t *surface)
{
return ((cairo_surface_observer_t *) surface)->target;
}
 
static inline cairo_bool_t
_cairo_surface_is_observer (cairo_surface_t *surface)
{
return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER;
}
 
static inline cairo_bool_t
_cairo_device_is_observer (cairo_device_t *device)
{
return device->backend->type == (cairo_device_type_t)CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER;
}
 
#endif /* CAIRO_SURFACE_OBSERVER_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-surface-observer-private.h
0,0 → 1,208
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H
#define CAIRO_SURFACE_OBSERVER_PRIVATE_H
 
#include "cairoint.h"
 
#include "cairo-device-private.h"
#include "cairo-list-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-time-private.h"
 
struct stat {
double min, max, sum, sum_sq;
unsigned count;
};
 
#define NUM_OPERATORS (CAIRO_OPERATOR_HSL_LUMINOSITY+1)
#define NUM_CAPS (CAIRO_LINE_CAP_SQUARE+1)
#define NUM_JOINS (CAIRO_LINE_JOIN_BEVEL+1)
#define NUM_ANTIALIAS (CAIRO_ANTIALIAS_BEST+1)
#define NUM_FILL_RULE (CAIRO_FILL_RULE_EVEN_ODD+1)
 
struct extents {
struct stat area;
unsigned int bounded, unbounded;
};
 
struct pattern {
unsigned int type[7]; /* native/record/other surface/gradients */
};
 
struct path {
unsigned int type[5]; /* empty/pixel/rectilinear/straight/curved */
};
 
struct clip {
unsigned int type[6]; /* none, region, boxes, single path, polygon, general */
};
 
typedef struct _cairo_observation cairo_observation_t;
typedef struct _cairo_observation_record cairo_observation_record_t;
typedef struct _cairo_device_observer cairo_device_observer_t;
 
struct _cairo_observation_record {
cairo_content_t target_content;
int target_width;
int target_height;
 
int index;
cairo_operator_t op;
int source;
int mask;
int num_glyphs;
int path;
int fill_rule;
double tolerance;
int antialias;
int clip;
cairo_time_t elapsed;
};
 
struct _cairo_observation {
int num_surfaces;
int num_contexts;
int num_sources_acquired;
 
/* XXX put interesting stats here! */
 
struct paint {
cairo_time_t elapsed;
unsigned int count;
struct extents extents;
unsigned int operators[NUM_OPERATORS];
struct pattern source;
struct clip clip;
unsigned int noop;
 
cairo_observation_record_t slowest;
} paint;
 
struct mask {
cairo_time_t elapsed;
unsigned int count;
struct extents extents;
unsigned int operators[NUM_OPERATORS];
struct pattern source;
struct pattern mask;
struct clip clip;
unsigned int noop;
 
cairo_observation_record_t slowest;
} mask;
 
struct fill {
cairo_time_t elapsed;
unsigned int count;
struct extents extents;
unsigned int operators[NUM_OPERATORS];
struct pattern source;
struct path path;
unsigned int antialias[NUM_ANTIALIAS];
unsigned int fill_rule[NUM_FILL_RULE];
struct clip clip;
unsigned int noop;
 
cairo_observation_record_t slowest;
} fill;
 
struct stroke {
cairo_time_t elapsed;
unsigned int count;
struct extents extents;
unsigned int operators[NUM_OPERATORS];
unsigned int caps[NUM_CAPS];
unsigned int joins[NUM_CAPS];
unsigned int antialias[NUM_ANTIALIAS];
struct pattern source;
struct path path;
struct stat line_width;
struct clip clip;
unsigned int noop;
 
cairo_observation_record_t slowest;
} stroke;
 
struct glyphs {
cairo_time_t elapsed;
unsigned int count;
struct extents extents;
unsigned int operators[NUM_OPERATORS];
struct pattern source;
struct clip clip;
unsigned int noop;
 
cairo_observation_record_t slowest;
} glyphs;
 
cairo_array_t timings;
cairo_recording_surface_t *record;
};
 
struct _cairo_device_observer {
cairo_device_t base;
cairo_device_t *target;
 
cairo_observation_t log;
};
 
struct callback_list {
cairo_list_t link;
 
cairo_surface_observer_callback_t func;
void *data;
};
 
struct _cairo_surface_observer {
cairo_surface_t base;
cairo_surface_t *target;
 
cairo_observation_t log;
 
cairo_list_t paint_callbacks;
cairo_list_t mask_callbacks;
cairo_list_t fill_callbacks;
cairo_list_t stroke_callbacks;
cairo_list_t glyphs_callbacks;
 
cairo_list_t flush_callbacks;
cairo_list_t finish_callbacks;
};
 
#endif /* CAIRO_SURFACE_OBSERVER_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-observer.c
0,0 → 1,2101
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-surface-observer-private.h"
#include "cairo-surface-observer-inline.h"
 
#include "cairo-array-private.h"
#include "cairo-combsort-inline.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-subsurface-inline.h"
#include "cairo-reference-count-private.h"
 
#if CAIRO_HAS_SCRIPT_SURFACE
#include "cairo-script-private.h"
#endif
 
static const cairo_surface_backend_t _cairo_surface_observer_backend;
 
/* observation/stats */
 
static void init_stats (struct stat *s)
{
s->min = HUGE_VAL;
s->max = -HUGE_VAL;
}
 
static void init_extents (struct extents *e)
{
init_stats (&e->area);
}
 
static void init_pattern (struct pattern *p)
{
}
 
static void init_path (struct path *p)
{
}
 
static void init_clip (struct clip *c)
{
}
 
static void init_paint (struct paint *p)
{
init_extents (&p->extents);
init_pattern (&p->source);
init_clip (&p->clip);
}
 
static void init_mask (struct mask *m)
{
init_extents (&m->extents);
init_pattern (&m->source);
init_pattern (&m->mask);
init_clip (&m->clip);
}
 
static void init_fill (struct fill *f)
{
init_extents (&f->extents);
init_pattern (&f->source);
init_path (&f->path);
init_clip (&f->clip);
}
 
static void init_stroke (struct stroke *s)
{
init_extents (&s->extents);
init_pattern (&s->source);
init_path (&s->path);
init_clip (&s->clip);
}
 
static void init_glyphs (struct glyphs *g)
{
init_extents (&g->extents);
init_pattern (&g->source);
init_clip (&g->clip);
}
 
static cairo_status_t
log_init (cairo_observation_t *log,
cairo_bool_t record)
{
memset (log, 0, sizeof(*log));
 
init_paint (&log->paint);
init_mask (&log->mask);
init_fill (&log->fill);
init_stroke (&log->stroke);
init_glyphs (&log->glyphs);
 
_cairo_array_init (&log->timings, sizeof (cairo_observation_record_t));
 
if (record) {
log->record = (cairo_recording_surface_t *)
cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
if (unlikely (log->record->base.status))
return log->record->base.status;
 
log->record->optimize_clears = FALSE;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
log_fini (cairo_observation_t *log)
{
_cairo_array_fini (&log->timings);
cairo_surface_destroy (&log->record->base);
}
 
static cairo_surface_t*
get_pattern_surface (const cairo_pattern_t *pattern)
{
return ((cairo_surface_pattern_t *)pattern)->surface;
}
 
static int
classify_pattern (const cairo_pattern_t *pattern,
const cairo_surface_t *target)
{
int classify;
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE:
if (get_pattern_surface (pattern)->type == target->type)
classify = 0;
else if (get_pattern_surface (pattern)->type == CAIRO_SURFACE_TYPE_RECORDING)
classify = 1;
else
classify = 2;
break;
default:
case CAIRO_PATTERN_TYPE_SOLID:
classify = 3;
break;
case CAIRO_PATTERN_TYPE_LINEAR:
classify = 4;
break;
case CAIRO_PATTERN_TYPE_RADIAL:
classify = 5;
break;
case CAIRO_PATTERN_TYPE_MESH:
classify = 6;
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
classify = 7;
break;
}
return classify;
}
 
static void
add_pattern (struct pattern *stats,
const cairo_pattern_t *pattern,
const cairo_surface_t *target)
{
stats->type[classify_pattern(pattern, target)]++;
}
 
static int
classify_path (const cairo_path_fixed_t *path,
cairo_bool_t is_fill)
{
int classify;
 
/* XXX improve for stroke */
classify = -1;
if (is_fill) {
if (path->fill_is_empty)
classify = 0;
else if (_cairo_path_fixed_fill_is_rectilinear (path))
classify = path->fill_maybe_region ? 1 : 2;
} else {
if (_cairo_path_fixed_stroke_is_rectilinear (path))
classify = 2;
}
if (classify == -1)
classify = 3 + (path->has_curve_to != 0);
 
return classify;
}
 
static void
add_path (struct path *stats,
const cairo_path_fixed_t *path,
cairo_bool_t is_fill)
{
stats->type[classify_path(path, is_fill)]++;
}
 
static int
classify_clip (const cairo_clip_t *clip)
{
int classify;
 
if (clip == NULL)
classify = 0;
else if (_cairo_clip_is_region (clip))
classify = 1;
else if (clip->path == NULL)
classify = 2;
else if (clip->path->prev == NULL)
classify = 3;
else if (_cairo_clip_is_polygon (clip))
classify = 4;
else
classify = 5;
 
return classify;
}
 
static void
add_clip (struct clip *stats,
const cairo_clip_t *clip)
{
stats->type[classify_clip (clip)]++;
}
 
static void
stats_add (struct stat *s, double v)
{
if (v < s->min)
s->min = v;
if (v > s->max)
s->max = v;
s->sum += v;
s->sum_sq += v*v;
s->count++;
}
 
static void
add_extents (struct extents *stats,
const cairo_composite_rectangles_t *extents)
{
const cairo_rectangle_int_t *r = extents->is_bounded ? &extents->bounded :&extents->unbounded;
stats_add (&stats->area, r->width * r->height);
stats->bounded += extents->is_bounded != 0;
stats->unbounded += extents->is_bounded == 0;
}
 
/* device interface */
 
static void
_cairo_device_observer_lock (void *_device)
{
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
cairo_status_t ignored;
 
/* cairo_device_acquire() can fail for nil and finished
* devices. We don't care about observing them. */
ignored = cairo_device_acquire (device->target);
}
 
static void
_cairo_device_observer_unlock (void *_device)
{
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
cairo_device_release (device->target);
}
 
static cairo_status_t
_cairo_device_observer_flush (void *_device)
{
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
 
if (device->target == NULL)
return CAIRO_STATUS_SUCCESS;
 
cairo_device_flush (device->target);
return device->target->status;
}
 
static void
_cairo_device_observer_finish (void *_device)
{
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
log_fini (&device->log);
cairo_device_finish (device->target);
}
 
static void
_cairo_device_observer_destroy (void *_device)
{
cairo_device_observer_t *device = (cairo_device_observer_t *) _device;
cairo_device_destroy (device->target);
free (device);
}
 
static const cairo_device_backend_t _cairo_device_observer_backend = {
CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER,
 
_cairo_device_observer_lock,
_cairo_device_observer_unlock,
 
_cairo_device_observer_flush,
_cairo_device_observer_finish,
_cairo_device_observer_destroy,
};
 
static cairo_device_t *
_cairo_device_create_observer_internal (cairo_device_t *target,
cairo_bool_t record)
{
cairo_device_observer_t *device;
cairo_status_t status;
 
device = malloc (sizeof (cairo_device_observer_t));
if (unlikely (device == NULL))
return _cairo_device_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_device_init (&device->base, &_cairo_device_observer_backend);
status = log_init (&device->log, record);
if (unlikely (status)) {
free (device);
return _cairo_device_create_in_error (status);
}
 
device->target = cairo_device_reference (target);
 
return &device->base;
}
 
/* surface interface */
 
static cairo_device_observer_t *
to_device (cairo_surface_observer_t *suface)
{
return (cairo_device_observer_t *)suface->base.device;
}
 
static cairo_surface_t *
_cairo_surface_create_observer_internal (cairo_device_t *device,
cairo_surface_t *target)
{
cairo_surface_observer_t *surface;
cairo_status_t status;
 
surface = malloc (sizeof (cairo_surface_observer_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_surface_observer_backend, device,
target->content);
 
status = log_init (&surface->log,
((cairo_device_observer_t *)device)->log.record != NULL);
if (unlikely (status)) {
free (surface);
return _cairo_surface_create_in_error (status);
}
 
surface->target = cairo_surface_reference (target);
surface->base.type = surface->target->type;
surface->base.is_clear = surface->target->is_clear;
 
cairo_list_init (&surface->paint_callbacks);
cairo_list_init (&surface->mask_callbacks);
cairo_list_init (&surface->fill_callbacks);
cairo_list_init (&surface->stroke_callbacks);
cairo_list_init (&surface->glyphs_callbacks);
 
cairo_list_init (&surface->flush_callbacks);
cairo_list_init (&surface->finish_callbacks);
 
surface->log.num_surfaces++;
to_device (surface)->log.num_surfaces++;
 
return &surface->base;
}
 
static inline void
do_callbacks (cairo_surface_observer_t *surface, cairo_list_t *head)
{
struct callback_list *cb;
 
cairo_list_foreach_entry (cb, struct callback_list, head, link)
cb->func (&surface->base, surface->target, cb->data);
}
 
 
static cairo_status_t
_cairo_surface_observer_finish (void *abstract_surface)
{
cairo_surface_observer_t *surface = abstract_surface;
 
do_callbacks (surface, &surface->finish_callbacks);
 
cairo_surface_destroy (surface->target);
log_fini (&surface->log);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_surface_observer_create_similar (void *abstract_other,
cairo_content_t content,
int width, int height)
{
cairo_surface_observer_t *other = abstract_other;
cairo_surface_t *target, *surface;
 
target = NULL;
if (other->target->backend->create_similar)
target = other->target->backend->create_similar (other->target, content,
width, height);
if (target == NULL)
target = _cairo_image_surface_create_with_content (content,
width, height);
 
surface = _cairo_surface_create_observer_internal (other->base.device,
target);
cairo_surface_destroy (target);
 
return surface;
}
 
static cairo_surface_t *
_cairo_surface_observer_create_similar_image (void *other,
cairo_format_t format,
int width, int height)
{
cairo_surface_observer_t *surface = other;
 
if (surface->target->backend->create_similar_image)
return surface->target->backend->create_similar_image (surface->target,
format,
width, height);
 
return NULL;
}
 
static cairo_image_surface_t *
_cairo_surface_observer_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_surface_observer_t *surface = abstract_surface;
return _cairo_surface_map_to_image (surface->target, extents);
}
 
static cairo_int_status_t
_cairo_surface_observer_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_surface_observer_t *surface = abstract_surface;
return _cairo_surface_unmap_image (surface->target, image);
}
 
static void
record_target (cairo_observation_record_t *r,
cairo_surface_t *target)
{
cairo_rectangle_int_t extents;
 
r->target_content = target->content;
if (_cairo_surface_get_extents (target, &extents)) {
r->target_width = extents.width;
r->target_height = extents.height;
} else {
r->target_width = -1;
r->target_height = -1;
}
}
 
static cairo_observation_record_t *
record_paint (cairo_observation_record_t *r,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
record_target (r, target);
 
r->op = op;
r->source = classify_pattern (source, target);
r->mask = -1;
r->num_glyphs = -1;
r->path = -1;
r->fill_rule = -1;
r->tolerance = -1;
r->antialias = -1;
r->clip = classify_clip (clip);
r->elapsed = elapsed;
 
return r;
}
 
static cairo_observation_record_t *
record_mask (cairo_observation_record_t *r,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
record_target (r, target);
 
r->op = op;
r->source = classify_pattern (source, target);
r->mask = classify_pattern (mask, target);
r->num_glyphs = -1;
r->path = -1;
r->fill_rule = -1;
r->tolerance = -1;
r->antialias = -1;
r->clip = classify_clip (clip);
r->elapsed = elapsed;
 
return r;
}
 
static cairo_observation_record_t *
record_fill (cairo_observation_record_t *r,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
record_target (r, target);
 
r->op = op;
r->source = classify_pattern (source, target);
r->mask = -1;
r->num_glyphs = -1;
r->path = classify_path (path, TRUE);
r->fill_rule = fill_rule;
r->tolerance = tolerance;
r->antialias = antialias;
r->clip = classify_clip (clip);
r->elapsed = elapsed;
 
return r;
}
 
static cairo_observation_record_t *
record_stroke (cairo_observation_record_t *r,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
record_target (r, target);
 
r->op = op;
r->source = classify_pattern (source, target);
r->mask = -1;
r->num_glyphs = -1;
r->path = classify_path (path, FALSE);
r->fill_rule = -1;
r->tolerance = tolerance;
r->antialias = antialias;
r->clip = classify_clip (clip);
r->elapsed = elapsed;
 
return r;
}
 
static cairo_observation_record_t *
record_glyphs (cairo_observation_record_t *r,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
record_target (r, target);
 
r->op = op;
r->source = classify_pattern (source, target);
r->mask = -1;
r->path = -1;
r->num_glyphs = num_glyphs;
r->fill_rule = -1;
r->tolerance = -1;
r->antialias = -1;
r->clip = classify_clip (clip);
r->elapsed = elapsed;
 
return r;
}
 
static void
add_record (cairo_observation_t *log,
cairo_observation_record_t *r)
{
cairo_int_status_t status;
 
r->index = log->record ? log->record->commands.num_elements : 0;
 
status = _cairo_array_append (&log->timings, r);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
static void
sync (cairo_surface_t *target, int x, int y)
{
cairo_rectangle_int_t extents;
 
extents.x = x;
extents.y = y;
extents.width = 1;
extents.height = 1;
 
_cairo_surface_unmap_image (target,
_cairo_surface_map_to_image (target,
&extents));
}
 
static void
midpt (const cairo_composite_rectangles_t *extents, int *x, int *y)
{
*x = extents->bounded.x + extents->bounded.width / 2;
*y = extents->bounded.y + extents->bounded.height / 2;
}
 
static void
add_record_paint (cairo_observation_t *log,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
cairo_observation_record_t record;
cairo_int_status_t status;
 
add_record (log,
record_paint (&record, target, op, source, clip, elapsed));
 
/* We have to bypass the high-level surface layer in case it tries to be
* too smart and discard operations; we need to record exactly what just
* happened on the target.
*/
if (log->record) {
status = log->record->base.backend->paint (&log->record->base,
op, source, clip);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
if (_cairo_time_gt (elapsed, log->paint.slowest.elapsed))
log->paint.slowest = record;
log->paint.elapsed = _cairo_time_add (log->paint.elapsed, elapsed);
}
 
static cairo_int_status_t
_cairo_surface_observer_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_device_observer_t *device = to_device (surface);
cairo_composite_rectangles_t composite;
cairo_int_status_t status;
cairo_time_t t;
int x, y;
 
/* XXX device locking */
 
surface->log.paint.count++;
surface->log.paint.operators[op]++;
add_pattern (&surface->log.paint.source, source, surface->target);
add_clip (&surface->log.paint.clip, clip);
 
device->log.paint.count++;
device->log.paint.operators[op]++;
add_pattern (&device->log.paint.source, source, surface->target);
add_clip (&device->log.paint.clip, clip);
 
status = _cairo_composite_rectangles_init_for_paint (&composite,
surface->target,
op, source,
clip);
if (unlikely (status)) {
surface->log.paint.noop++;
device->log.paint.noop++;
return status;
}
 
midpt (&composite, &x, &y);
 
add_extents (&surface->log.paint.extents, &composite);
add_extents (&device->log.paint.extents, &composite);
_cairo_composite_rectangles_fini (&composite);
 
t = _cairo_time_get ();
status = _cairo_surface_paint (surface->target,
op, source,
clip);
if (unlikely (status))
return status;
 
sync (surface->target, x, y);
t = _cairo_time_get_delta (t);
 
add_record_paint (&surface->log, surface->target, op, source, clip, t);
add_record_paint (&device->log, surface->target, op, source, clip, t);
 
do_callbacks (surface, &surface->paint_callbacks);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
add_record_mask (cairo_observation_t *log,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
cairo_observation_record_t record;
cairo_int_status_t status;
 
add_record (log,
record_mask (&record, target, op, source, mask, clip, elapsed));
 
if (log->record) {
status = log->record->base.backend->mask (&log->record->base,
op, source, mask, clip);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
if (_cairo_time_gt (elapsed, log->mask.slowest.elapsed))
log->mask.slowest = record;
log->mask.elapsed = _cairo_time_add (log->mask.elapsed, elapsed);
}
 
static cairo_int_status_t
_cairo_surface_observer_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_device_observer_t *device = to_device (surface);
cairo_composite_rectangles_t composite;
cairo_int_status_t status;
cairo_time_t t;
int x, y;
 
surface->log.mask.count++;
surface->log.mask.operators[op]++;
add_pattern (&surface->log.mask.source, source, surface->target);
add_pattern (&surface->log.mask.mask, mask, surface->target);
add_clip (&surface->log.mask.clip, clip);
 
device->log.mask.count++;
device->log.mask.operators[op]++;
add_pattern (&device->log.mask.source, source, surface->target);
add_pattern (&device->log.mask.mask, mask, surface->target);
add_clip (&device->log.mask.clip, clip);
 
status = _cairo_composite_rectangles_init_for_mask (&composite,
surface->target,
op, source, mask,
clip);
if (unlikely (status)) {
surface->log.mask.noop++;
device->log.mask.noop++;
return status;
}
 
midpt (&composite, &x, &y);
 
add_extents (&surface->log.mask.extents, &composite);
add_extents (&device->log.mask.extents, &composite);
_cairo_composite_rectangles_fini (&composite);
 
t = _cairo_time_get ();
status = _cairo_surface_mask (surface->target,
op, source, mask,
clip);
if (unlikely (status))
return status;
 
sync (surface->target, x, y);
t = _cairo_time_get_delta (t);
 
add_record_mask (&surface->log,
surface->target, op, source, mask, clip,
t);
add_record_mask (&device->log,
surface->target, op, source, mask, clip,
t);
 
do_callbacks (surface, &surface->mask_callbacks);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
add_record_fill (cairo_observation_t *log,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
cairo_observation_record_t record;
cairo_int_status_t status;
 
add_record (log,
record_fill (&record,
target, op, source,
path, fill_rule, tolerance, antialias,
clip, elapsed));
 
if (log->record) {
status = log->record->base.backend->fill (&log->record->base,
op, source,
path, fill_rule,
tolerance, antialias,
clip);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
if (_cairo_time_gt (elapsed, log->fill.slowest.elapsed))
log->fill.slowest = record;
log->fill.elapsed = _cairo_time_add (log->fill.elapsed, elapsed);
}
 
static cairo_int_status_t
_cairo_surface_observer_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_device_observer_t *device = to_device (surface);
cairo_composite_rectangles_t composite;
cairo_int_status_t status;
cairo_time_t t;
int x, y;
 
surface->log.fill.count++;
surface->log.fill.operators[op]++;
surface->log.fill.fill_rule[fill_rule]++;
surface->log.fill.antialias[antialias]++;
add_pattern (&surface->log.fill.source, source, surface->target);
add_path (&surface->log.fill.path, path, TRUE);
add_clip (&surface->log.fill.clip, clip);
 
device->log.fill.count++;
device->log.fill.operators[op]++;
device->log.fill.fill_rule[fill_rule]++;
device->log.fill.antialias[antialias]++;
add_pattern (&device->log.fill.source, source, surface->target);
add_path (&device->log.fill.path, path, TRUE);
add_clip (&device->log.fill.clip, clip);
 
status = _cairo_composite_rectangles_init_for_fill (&composite,
surface->target,
op, source, path,
clip);
if (unlikely (status)) {
surface->log.fill.noop++;
device->log.fill.noop++;
return status;
}
 
midpt (&composite, &x, &y);
 
add_extents (&surface->log.fill.extents, &composite);
add_extents (&device->log.fill.extents, &composite);
_cairo_composite_rectangles_fini (&composite);
 
t = _cairo_time_get ();
status = _cairo_surface_fill (surface->target,
op, source, path,
fill_rule, tolerance, antialias,
clip);
if (unlikely (status))
return status;
 
sync (surface->target, x, y);
t = _cairo_time_get_delta (t);
 
add_record_fill (&surface->log,
surface->target, op, source, path,
fill_rule, tolerance, antialias,
clip, t);
 
add_record_fill (&device->log,
surface->target, op, source, path,
fill_rule, tolerance, antialias,
clip, t);
 
do_callbacks (surface, &surface->fill_callbacks);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
add_record_stroke (cairo_observation_t *log,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
cairo_observation_record_t record;
cairo_int_status_t status;
 
add_record (log,
record_stroke (&record,
target, op, source,
path, style, ctm,ctm_inverse,
tolerance, antialias,
clip, elapsed));
 
if (log->record) {
status = log->record->base.backend->stroke (&log->record->base,
op, source,
path, style, ctm,ctm_inverse,
tolerance, antialias,
clip);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
if (_cairo_time_gt (elapsed, log->stroke.slowest.elapsed))
log->stroke.slowest = record;
log->stroke.elapsed = _cairo_time_add (log->stroke.elapsed, elapsed);
}
 
static cairo_int_status_t
_cairo_surface_observer_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_device_observer_t *device = to_device (surface);
cairo_composite_rectangles_t composite;
cairo_int_status_t status;
cairo_time_t t;
int x, y;
 
surface->log.stroke.count++;
surface->log.stroke.operators[op]++;
surface->log.stroke.antialias[antialias]++;
surface->log.stroke.caps[style->line_cap]++;
surface->log.stroke.joins[style->line_join]++;
add_pattern (&surface->log.stroke.source, source, surface->target);
add_path (&surface->log.stroke.path, path, FALSE);
add_clip (&surface->log.stroke.clip, clip);
 
device->log.stroke.count++;
device->log.stroke.operators[op]++;
device->log.stroke.antialias[antialias]++;
device->log.stroke.caps[style->line_cap]++;
device->log.stroke.joins[style->line_join]++;
add_pattern (&device->log.stroke.source, source, surface->target);
add_path (&device->log.stroke.path, path, FALSE);
add_clip (&device->log.stroke.clip, clip);
 
status = _cairo_composite_rectangles_init_for_stroke (&composite,
surface->target,
op, source,
path, style, ctm,
clip);
if (unlikely (status)) {
surface->log.stroke.noop++;
device->log.stroke.noop++;
return status;
}
 
midpt (&composite, &x, &y);
 
add_extents (&surface->log.stroke.extents, &composite);
add_extents (&device->log.stroke.extents, &composite);
_cairo_composite_rectangles_fini (&composite);
 
t = _cairo_time_get ();
status = _cairo_surface_stroke (surface->target,
op, source, path,
style, ctm, ctm_inverse,
tolerance, antialias,
clip);
if (unlikely (status))
return status;
 
sync (surface->target, x, y);
t = _cairo_time_get_delta (t);
 
add_record_stroke (&surface->log,
surface->target, op, source, path,
style, ctm,ctm_inverse,
tolerance, antialias,
clip, t);
 
add_record_stroke (&device->log,
surface->target, op, source, path,
style, ctm,ctm_inverse,
tolerance, antialias,
clip, t);
 
do_callbacks (surface, &surface->stroke_callbacks);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
add_record_glyphs (cairo_observation_t *log,
cairo_surface_t *target,
cairo_operator_t op,
const cairo_pattern_t*source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip,
cairo_time_t elapsed)
{
cairo_observation_record_t record;
cairo_int_status_t status;
 
add_record (log,
record_glyphs (&record,
target, op, source,
glyphs, num_glyphs, scaled_font,
clip, elapsed));
 
if (log->record) {
status = log->record->base.backend->show_text_glyphs (&log->record->base,
op, source,
NULL, 0,
glyphs, num_glyphs,
NULL, 0, 0,
scaled_font,
clip);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
if (_cairo_time_gt (elapsed, log->glyphs.slowest.elapsed))
log->glyphs.slowest = record;
log->glyphs.elapsed = _cairo_time_add (log->glyphs.elapsed, elapsed);
}
 
static cairo_int_status_t
_cairo_surface_observer_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_device_observer_t *device = to_device (surface);
cairo_composite_rectangles_t composite;
cairo_int_status_t status;
cairo_glyph_t *dev_glyphs;
cairo_time_t t;
int x, y;
 
surface->log.glyphs.count++;
surface->log.glyphs.operators[op]++;
add_pattern (&surface->log.glyphs.source, source, surface->target);
add_clip (&surface->log.glyphs.clip, clip);
 
device->log.glyphs.count++;
device->log.glyphs.operators[op]++;
add_pattern (&device->log.glyphs.source, source, surface->target);
add_clip (&device->log.glyphs.clip, clip);
 
status = _cairo_composite_rectangles_init_for_glyphs (&composite,
surface->target,
op, source,
scaled_font,
glyphs, num_glyphs,
clip,
NULL);
if (unlikely (status)) {
surface->log.glyphs.noop++;
device->log.glyphs.noop++;
return status;
}
 
midpt (&composite, &x, &y);
 
add_extents (&surface->log.glyphs.extents, &composite);
add_extents (&device->log.glyphs.extents, &composite);
_cairo_composite_rectangles_fini (&composite);
 
/* XXX We have to copy the glyphs, because the backend is allowed to
* modify! */
dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (unlikely (dev_glyphs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t));
 
t = _cairo_time_get ();
status = _cairo_surface_show_text_glyphs (surface->target, op, source,
NULL, 0,
dev_glyphs, num_glyphs,
NULL, 0, 0,
scaled_font,
clip);
free (dev_glyphs);
if (unlikely (status))
return status;
 
sync (surface->target, x, y);
t = _cairo_time_get_delta (t);
 
add_record_glyphs (&surface->log,
surface->target, op, source,
glyphs, num_glyphs, scaled_font,
clip, t);
 
add_record_glyphs (&device->log,
surface->target, op, source,
glyphs, num_glyphs, scaled_font,
clip, t);
 
do_callbacks (surface, &surface->glyphs_callbacks);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_surface_observer_flush (void *abstract_surface, unsigned flags)
{
cairo_surface_observer_t *surface = abstract_surface;
 
do_callbacks (surface, &surface->flush_callbacks);
return _cairo_surface_flush (surface->target, flags);
}
 
static cairo_status_t
_cairo_surface_observer_mark_dirty (void *abstract_surface,
int x, int y,
int width, int height)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_status_t status;
 
printf ("mark-dirty (%d, %d) x (%d, %d)\n", x, y, width, height);
 
status = CAIRO_STATUS_SUCCESS;
if (surface->target->backend->mark_dirty_rectangle)
status = surface->target->backend->mark_dirty_rectangle (surface->target,
x,y, width,height);
 
return status;
}
 
static cairo_int_status_t
_cairo_surface_observer_copy_page (void *abstract_surface)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_status_t status;
 
status = CAIRO_STATUS_SUCCESS;
if (surface->target->backend->copy_page)
status = surface->target->backend->copy_page (surface->target);
 
return status;
}
 
static cairo_int_status_t
_cairo_surface_observer_show_page (void *abstract_surface)
{
cairo_surface_observer_t *surface = abstract_surface;
cairo_status_t status;
 
status = CAIRO_STATUS_SUCCESS;
if (surface->target->backend->show_page)
status = surface->target->backend->show_page (surface->target);
 
return status;
}
 
static cairo_bool_t
_cairo_surface_observer_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_surface_observer_t *surface = abstract_surface;
return _cairo_surface_get_extents (surface->target, extents);
}
 
static void
_cairo_surface_observer_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
cairo_surface_observer_t *surface = abstract_surface;
 
if (surface->target->backend->get_font_options != NULL)
surface->target->backend->get_font_options (surface->target, options);
}
 
static cairo_surface_t *
_cairo_surface_observer_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_surface_observer_t *surface = abstract_surface;
return _cairo_surface_get_source (surface->target, extents);
}
 
static cairo_status_t
_cairo_surface_observer_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_surface_observer_t *surface = abstract_surface;
 
surface->log.num_sources_acquired++;
to_device (surface)->log.num_sources_acquired++;
 
return _cairo_surface_acquire_source_image (surface->target,
image_out, image_extra);
}
 
static void
_cairo_surface_observer_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_observer_t *surface = abstract_surface;
 
_cairo_surface_release_source_image (surface->target, image, image_extra);
}
 
static cairo_surface_t *
_cairo_surface_observer_snapshot (void *abstract_surface)
{
cairo_surface_observer_t *surface = abstract_surface;
 
/* XXX hook onto the snapshot so that we measure number of reads */
 
if (surface->target->backend->snapshot)
return surface->target->backend->snapshot (surface->target);
 
return NULL;
}
 
static cairo_t *
_cairo_surface_observer_create_context(void *target)
{
cairo_surface_observer_t *surface = target;
 
if (_cairo_surface_is_subsurface (&surface->base))
surface = (cairo_surface_observer_t *)
_cairo_surface_subsurface_get_target (&surface->base);
 
surface->log.num_contexts++;
to_device (surface)->log.num_contexts++;
 
return surface->target->backend->create_context (target);
}
 
static const cairo_surface_backend_t _cairo_surface_observer_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
_cairo_surface_observer_finish,
 
_cairo_surface_observer_create_context,
 
_cairo_surface_observer_create_similar,
_cairo_surface_observer_create_similar_image,
_cairo_surface_observer_map_to_image,
_cairo_surface_observer_unmap_image,
 
_cairo_surface_observer_source,
_cairo_surface_observer_acquire_source_image,
_cairo_surface_observer_release_source_image,
_cairo_surface_observer_snapshot,
 
_cairo_surface_observer_copy_page,
_cairo_surface_observer_show_page,
 
_cairo_surface_observer_get_extents,
_cairo_surface_observer_get_font_options,
 
_cairo_surface_observer_flush,
_cairo_surface_observer_mark_dirty,
 
_cairo_surface_observer_paint,
_cairo_surface_observer_mask,
_cairo_surface_observer_stroke,
_cairo_surface_observer_fill,
NULL, /* fill-stroke */
_cairo_surface_observer_glyphs,
};
 
/**
* cairo_surface_create_observer:
* @target: an existing surface for which the observer will watch
*
* Create a new surface that exists solely to watch another is doing. In
* the process it will log operations and times, which are fast, which are
* slow, which are frequent, etc.
*
* Return value: a pointer to the newly allocated surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if @other is already in an error state
* or any other error occurs.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_surface_create_observer (cairo_surface_t *target,
cairo_surface_observer_mode_t mode)
{
cairo_device_t *device;
cairo_surface_t *surface;
cairo_bool_t record;
 
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
if (unlikely (target->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
record = mode & CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS;
device = _cairo_device_create_observer_internal (target->device, record);
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
 
surface = _cairo_surface_create_observer_internal (device, target);
cairo_device_destroy (device);
 
return surface;
}
 
static cairo_status_t
_cairo_surface_observer_add_callback (cairo_list_t *head,
cairo_surface_observer_callback_t func,
void *data)
{
struct callback_list *cb;
 
cb = malloc (sizeof (*cb));
if (unlikely (cb == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
cairo_list_add (&cb->link, head);
cb->func = func;
cb->data = data;
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->paint_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->mask_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->fill_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->stroke_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->glyphs_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->flush_callbacks,
func, data);
}
 
cairo_status_t
cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return abstract_surface->status;
 
if (! _cairo_surface_is_observer (abstract_surface))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *)abstract_surface;
return _cairo_surface_observer_add_callback (&surface->finish_callbacks,
func, data);
}
 
static void
print_extents (cairo_output_stream_t *stream, const struct extents *e)
{
_cairo_output_stream_printf (stream,
" extents: total %g, avg %g [unbounded %d]\n",
e->area.sum,
e->area.sum / e->area.count,
e->unbounded);
}
 
static inline int ordercmp (int a, int b, const unsigned int *array)
{
/* high to low */
return array[b] - array[a];
}
CAIRO_COMBSORT_DECLARE_WITH_DATA (sort_order, int, ordercmp)
 
static void
print_array (cairo_output_stream_t *stream,
const unsigned int *array,
const char **names,
int count)
{
int order[64];
int i, j;
 
assert (count < ARRAY_LENGTH (order));
for (i = j = 0; i < count; i++) {
if (array[i] != 0)
order[j++] = i;
}
 
sort_order (order, j, (void *)array);
for (i = 0; i < j; i++)
_cairo_output_stream_printf (stream, " %d %s%s",
array[order[i]], names[order[i]],
i < j -1 ? "," : "");
}
 
static const char *operator_names[] = {
"CLEAR", /* CAIRO_OPERATOR_CLEAR */
 
"SOURCE", /* CAIRO_OPERATOR_SOURCE */
"OVER", /* CAIRO_OPERATOR_OVER */
"IN", /* CAIRO_OPERATOR_IN */
"OUT", /* CAIRO_OPERATOR_OUT */
"ATOP", /* CAIRO_OPERATOR_ATOP */
 
"DEST", /* CAIRO_OPERATOR_DEST */
"DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
"DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
"DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
"DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
 
"XOR", /* CAIRO_OPERATOR_XOR */
"ADD", /* CAIRO_OPERATOR_ADD */
"SATURATE", /* CAIRO_OPERATOR_SATURATE */
 
"MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
"SCREEN", /* CAIRO_OPERATOR_SCREEN */
"OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
"DARKEN", /* CAIRO_OPERATOR_DARKEN */
"LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
"DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
"BURN", /* CAIRO_OPERATOR_COLOR_BURN */
"HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
"SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
"DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
"EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
"HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
"HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
};
static void
print_operators (cairo_output_stream_t *stream, unsigned int *array)
{
_cairo_output_stream_printf (stream, " op:");
print_array (stream, array, operator_names, NUM_OPERATORS);
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *fill_rule_names[] = {
"non-zero",
"even-odd",
};
static void
print_fill_rule (cairo_output_stream_t *stream, unsigned int *array)
{
_cairo_output_stream_printf (stream, " fill rule:");
print_array (stream, array, fill_rule_names, ARRAY_LENGTH(fill_rule_names));
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *cap_names[] = {
"butt", /* CAIRO_LINE_CAP_BUTT */
"round", /* CAIRO_LINE_CAP_ROUND */
"square" /* CAIRO_LINE_CAP_SQUARE */
};
static void
print_line_caps (cairo_output_stream_t *stream, unsigned int *array)
{
_cairo_output_stream_printf (stream, " caps:");
print_array (stream, array, cap_names, NUM_CAPS);
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *join_names[] = {
"miter", /* CAIRO_LINE_JOIN_MITER */
"round", /* CAIRO_LINE_JOIN_ROUND */
"bevel", /* CAIRO_LINE_JOIN_BEVEL */
};
static void
print_line_joins (cairo_output_stream_t *stream, unsigned int *array)
{
_cairo_output_stream_printf (stream, " joins:");
print_array (stream, array, join_names, NUM_JOINS);
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *antialias_names[] = {
"default",
"none",
"gray",
"subpixel",
"fast",
"good",
"best"
};
static void
print_antialias (cairo_output_stream_t *stream, unsigned int *array)
{
_cairo_output_stream_printf (stream, " antialias:");
print_array (stream, array, antialias_names, NUM_ANTIALIAS);
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *pattern_names[] = {
"native",
"record",
"other surface",
"solid",
"linear",
"radial",
"mesh",
"raster"
};
static void
print_pattern (cairo_output_stream_t *stream,
const char *name,
const struct pattern *p)
{
_cairo_output_stream_printf (stream, " %s:", name);
print_array (stream, p->type, pattern_names, ARRAY_LENGTH (pattern_names));
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *path_names[] = {
"empty",
"pixel-aligned",
"rectliinear",
"straight",
"curved",
};
static void
print_path (cairo_output_stream_t *stream,
const struct path *p)
{
_cairo_output_stream_printf (stream, " path:");
print_array (stream, p->type, path_names, ARRAY_LENGTH (path_names));
_cairo_output_stream_printf (stream, "\n");
}
 
static const char *clip_names[] = {
"none",
"region",
"boxes",
"single path",
"polygon",
"general",
};
static void
print_clip (cairo_output_stream_t *stream, const struct clip *c)
{
_cairo_output_stream_printf (stream, " clip:");
print_array (stream, c->type, clip_names, ARRAY_LENGTH (clip_names));
_cairo_output_stream_printf (stream, "\n");
}
 
static void
print_record (cairo_output_stream_t *stream,
cairo_observation_record_t *r)
{
_cairo_output_stream_printf (stream, " op: %s\n", operator_names[r->op]);
_cairo_output_stream_printf (stream, " source: %s\n",
pattern_names[r->source]);
if (r->mask != -1)
_cairo_output_stream_printf (stream, " mask: %s\n",
pattern_names[r->mask]);
if (r->num_glyphs != -1)
_cairo_output_stream_printf (stream, " num_glyphs: %d\n",
r->num_glyphs);
if (r->path != -1)
_cairo_output_stream_printf (stream, " path: %s\n",
path_names[r->path]);
if (r->fill_rule != -1)
_cairo_output_stream_printf (stream, " fill rule: %s\n",
fill_rule_names[r->fill_rule]);
if (r->antialias != -1)
_cairo_output_stream_printf (stream, " antialias: %s\n",
antialias_names[r->antialias]);
_cairo_output_stream_printf (stream, " clip: %s\n", clip_names[r->clip]);
_cairo_output_stream_printf (stream, " elapsed: %f ns\n",
_cairo_time_to_ns (r->elapsed));
}
 
static double percent (cairo_time_t a, cairo_time_t b)
{
/* Fake %.1f */
return _cairo_round (_cairo_time_to_s (a) * 1000 /
_cairo_time_to_s (b)) / 10;
}
 
static cairo_bool_t
replay_record (cairo_observation_t *log,
cairo_observation_record_t *r,
cairo_device_t *script)
{
#if CAIRO_HAS_SCRIPT_SURFACE
cairo_surface_t *surface;
cairo_int_status_t status;
 
if (log->record == NULL || script == NULL)
return FALSE;
 
surface = cairo_script_surface_create (script,
r->target_content,
r->target_width,
r->target_height);
status =
_cairo_recording_surface_replay_one (log->record, r->index, surface);
cairo_surface_destroy (surface);
 
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
return TRUE;
#else
return FALSE;
#endif
}
 
static cairo_time_t
_cairo_observation_total_elapsed (cairo_observation_t *log)
{
cairo_time_t total;
 
total = log->paint.elapsed;
total = _cairo_time_add (total, log->mask.elapsed);
total = _cairo_time_add (total, log->fill.elapsed);
total = _cairo_time_add (total, log->stroke.elapsed);
total = _cairo_time_add (total, log->glyphs.elapsed);
 
return total;
}
 
static void
_cairo_observation_print (cairo_output_stream_t *stream,
cairo_observation_t *log)
{
cairo_device_t *script;
cairo_time_t total;
 
#if CAIRO_HAS_SCRIPT_SURFACE
script = _cairo_script_context_create_internal (stream);
_cairo_script_context_attach_snapshots (script, FALSE);
#else
script = NULL;
#endif
 
total = _cairo_observation_total_elapsed (log);
 
_cairo_output_stream_printf (stream, "elapsed: %f\n",
_cairo_time_to_ns (total));
_cairo_output_stream_printf (stream, "surfaces: %d\n",
log->num_surfaces);
_cairo_output_stream_printf (stream, "contexts: %d\n",
log->num_contexts);
_cairo_output_stream_printf (stream, "sources acquired: %d\n",
log->num_sources_acquired);
 
 
_cairo_output_stream_printf (stream, "paint: count %d [no-op %d], elapsed %f [%f%%]\n",
log->paint.count, log->paint.noop,
_cairo_time_to_ns (log->paint.elapsed),
percent (log->paint.elapsed, total));
if (log->paint.count) {
print_extents (stream, &log->paint.extents);
print_operators (stream, log->paint.operators);
print_pattern (stream, "source", &log->paint.source);
print_clip (stream, &log->paint.clip);
 
_cairo_output_stream_printf (stream, "slowest paint: %f%%\n",
percent (log->paint.slowest.elapsed,
log->paint.elapsed));
print_record (stream, &log->paint.slowest);
 
_cairo_output_stream_printf (stream, "\n");
if (replay_record (log, &log->paint.slowest, script))
_cairo_output_stream_printf (stream, "\n\n");
}
 
_cairo_output_stream_printf (stream, "mask: count %d [no-op %d], elapsed %f [%f%%]\n",
log->mask.count, log->mask.noop,
_cairo_time_to_ns (log->mask.elapsed),
percent (log->mask.elapsed, total));
if (log->mask.count) {
print_extents (stream, &log->mask.extents);
print_operators (stream, log->mask.operators);
print_pattern (stream, "source", &log->mask.source);
print_pattern (stream, "mask", &log->mask.mask);
print_clip (stream, &log->mask.clip);
 
_cairo_output_stream_printf (stream, "slowest mask: %f%%\n",
percent (log->mask.slowest.elapsed,
log->mask.elapsed));
print_record (stream, &log->mask.slowest);
 
_cairo_output_stream_printf (stream, "\n");
if (replay_record (log, &log->mask.slowest, script))
_cairo_output_stream_printf (stream, "\n\n");
}
 
_cairo_output_stream_printf (stream, "fill: count %d [no-op %d], elaspsed %f [%f%%]\n",
log->fill.count, log->fill.noop,
_cairo_time_to_ns (log->fill.elapsed),
percent (log->fill.elapsed, total));
if (log->fill.count) {
print_extents (stream, &log->fill.extents);
print_operators (stream, log->fill.operators);
print_pattern (stream, "source", &log->fill.source);
print_path (stream, &log->fill.path);
print_fill_rule (stream, log->fill.fill_rule);
print_antialias (stream, log->fill.antialias);
print_clip (stream, &log->fill.clip);
 
_cairo_output_stream_printf (stream, "slowest fill: %f%%\n",
percent (log->fill.slowest.elapsed,
log->fill.elapsed));
print_record (stream, &log->fill.slowest);
 
_cairo_output_stream_printf (stream, "\n");
if (replay_record (log, &log->fill.slowest, script))
_cairo_output_stream_printf (stream, "\n\n");
}
 
_cairo_output_stream_printf (stream, "stroke: count %d [no-op %d], elapsed %f [%f%%]\n",
log->stroke.count, log->stroke.noop,
_cairo_time_to_ns (log->stroke.elapsed),
percent (log->stroke.elapsed, total));
if (log->stroke.count) {
print_extents (stream, &log->stroke.extents);
print_operators (stream, log->stroke.operators);
print_pattern (stream, "source", &log->stroke.source);
print_path (stream, &log->stroke.path);
print_antialias (stream, log->stroke.antialias);
print_line_caps (stream, log->stroke.caps);
print_line_joins (stream, log->stroke.joins);
print_clip (stream, &log->stroke.clip);
 
_cairo_output_stream_printf (stream, "slowest stroke: %f%%\n",
percent (log->stroke.slowest.elapsed,
log->stroke.elapsed));
print_record (stream, &log->stroke.slowest);
 
_cairo_output_stream_printf (stream, "\n");
if (replay_record (log, &log->stroke.slowest, script))
_cairo_output_stream_printf (stream, "\n\n");
}
 
_cairo_output_stream_printf (stream, "glyphs: count %d [no-op %d], elasped %f [%f%%]\n",
log->glyphs.count, log->glyphs.noop,
_cairo_time_to_ns (log->glyphs.elapsed),
percent (log->glyphs.elapsed, total));
if (log->glyphs.count) {
print_extents (stream, &log->glyphs.extents);
print_operators (stream, log->glyphs.operators);
print_pattern (stream, "source", &log->glyphs.source);
print_clip (stream, &log->glyphs.clip);
 
_cairo_output_stream_printf (stream, "slowest glyphs: %f%%\n",
percent (log->glyphs.slowest.elapsed,
log->glyphs.elapsed));
print_record (stream, &log->glyphs.slowest);
 
_cairo_output_stream_printf (stream, "\n");
if (replay_record (log, &log->glyphs.slowest, script))
_cairo_output_stream_printf (stream, "\n\n");
}
 
cairo_device_destroy (script);
}
 
cairo_status_t
cairo_surface_observer_print (cairo_surface_t *abstract_surface,
cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_surface_observer_t *surface;
 
if (unlikely (abstract_surface->status))
return abstract_surface->status;
 
if (unlikely (! _cairo_surface_is_observer (abstract_surface)))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
surface = (cairo_surface_observer_t *) abstract_surface;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
_cairo_observation_print (stream, &surface->log);
return _cairo_output_stream_destroy (stream);
}
 
double
cairo_surface_observer_elapsed (cairo_surface_t *abstract_surface)
{
cairo_surface_observer_t *surface;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_surface->ref_count)))
return -1;
 
if (! _cairo_surface_is_observer (abstract_surface))
return -1;
 
surface = (cairo_surface_observer_t *) abstract_surface;
return _cairo_time_to_ns (_cairo_observation_total_elapsed (&surface->log));
}
 
cairo_status_t
cairo_device_observer_print (cairo_device_t *abstract_device,
cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_device_observer_t *device;
 
if (unlikely (abstract_device->status))
return abstract_device->status;
 
if (unlikely (! _cairo_device_is_observer (abstract_device)))
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
device = (cairo_device_observer_t *) abstract_device;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
_cairo_observation_print (stream, &device->log);
return _cairo_output_stream_destroy (stream);
}
 
double
cairo_device_observer_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (_cairo_observation_total_elapsed (&device->log));
}
 
double
cairo_device_observer_paint_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (device->log.paint.elapsed);
}
 
double
cairo_device_observer_mask_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (device->log.mask.elapsed);
}
 
double
cairo_device_observer_fill_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (device->log.fill.elapsed);
}
 
double
cairo_device_observer_stroke_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (device->log.stroke.elapsed);
}
 
double
cairo_device_observer_glyphs_elapsed (cairo_device_t *abstract_device)
{
cairo_device_observer_t *device;
 
if (unlikely (CAIRO_REFERENCE_COUNT_IS_INVALID (&abstract_device->ref_count)))
return -1;
 
if (! _cairo_device_is_observer (abstract_device))
return -1;
 
device = (cairo_device_observer_t *) abstract_device;
return _cairo_time_to_ns (device->log.glyphs.elapsed);
}
/programs/develop/libraries/cairo/src/cairo-surface-offset-private.h
48,7 → 48,7
int x, int y,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_offset_mask (cairo_surface_t *target,
56,7 → 56,7
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_offset_stroke (cairo_surface_t *surface,
63,13 → 63,13
int x, int y,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_offset_fill (cairo_surface_t *surface,
76,11 → 76,11
int x, int y,
cairo_operator_t op,
const cairo_pattern_t*source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_offset_glyphs (cairo_surface_t *surface,
90,6 → 90,6
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
#endif /* CAIRO_SURFACE_OFFSET_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-offset.c
37,7 → 37,9
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-error-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-offset-private.h"
 
/* A collection of routines to facilitate drawing to an alternate surface. */
58,30 → 60,23
int x, int y,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
cairo_pattern_union_t source_copy;
 
if (unlikely (target->status))
return target->status;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (x | y) {
cairo_matrix_t m;
 
if (clip != NULL) {
cairo_matrix_init_translate (&m, -x, -y);
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
 
dev_clip = &clip_copy;
}
 
cairo_matrix_init_translate (&m, x, y);
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
89,9 → 84,8
 
status = _cairo_surface_paint (target, op, source, dev_clip);
 
FINISH:
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
 
return status;
}
102,10 → 96,10
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
cairo_pattern_union_t source_copy;
cairo_pattern_union_t mask_copy;
 
112,21 → 106,14
if (unlikely (target->status))
return target->status;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (x | y) {
cairo_matrix_t m;
 
if (clip != NULL) {
cairo_matrix_init_translate (&m, -x, -y);
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
 
dev_clip = &clip_copy;
}
 
cairo_matrix_init_translate (&m, x, y);
_copy_transformed_pattern (&source_copy.base, source, &m);
_copy_transformed_pattern (&mask_copy.base, mask, &m);
138,9 → 125,8
source, mask,
dev_clip);
 
FINISH:
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
 
return status;
}
150,16 → 136,16
int x, int y,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t*stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_path_fixed_t path_copy, *dev_path = path;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
cairo_matrix_t dev_ctm = *ctm;
cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
cairo_pattern_union_t source_copy;
168,12 → 154,14
if (unlikely (surface->status))
return surface->status;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (x | y) {
cairo_matrix_t m;
 
dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
 
status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
if (unlikely (status))
goto FINISH;
185,14 → 173,7
 
cairo_matrix_init_translate (&m, -x, -y);
cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
cairo_matrix_init_translate (&m, x, y);
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
209,7 → 190,7
if (dev_path != path)
_cairo_path_fixed_fini (dev_path);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
 
return status;
}
219,26 → 200,28
int x, int y,
cairo_operator_t op,
const cairo_pattern_t*source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = path;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
cairo_pattern_union_t source_copy;
 
if (unlikely (surface->status))
return surface->status;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (x | y) {
cairo_matrix_t m;
 
dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
 
status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
if (unlikely (status))
goto FINISH;
248,15 → 231,6
_cairo_fixed_from_int (-y));
dev_path = &path_copy;
 
if (clip != NULL) {
cairo_matrix_init_translate (&m, -x, -y);
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
cairo_matrix_init_translate (&m, x, y);
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
271,7 → 245,7
if (dev_path != path)
_cairo_path_fixed_fini (dev_path);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
 
return status;
}
284,10 → 258,10
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_clip_t *dev_clip = (cairo_clip_t *) clip;
cairo_pattern_union_t source_copy;
cairo_glyph_t *dev_glyphs;
int i;
295,7 → 269,7
if (unlikely (surface->status))
return surface->status;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
307,15 → 281,8
if (x | y) {
cairo_matrix_t m;
 
if (clip != NULL) {
cairo_matrix_init_translate (&m, -x, -y);
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_clip_copy_with_translation (clip, -x, -y);
 
dev_clip = &clip_copy;
}
 
cairo_matrix_init_translate (&m, x, y);
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
333,9 → 300,8
scaled_font,
dev_clip);
 
FINISH:
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
free (dev_glyphs);
 
return status;
/programs/develop/libraries/cairo/src/cairo-surface-private.h
44,6 → 44,7
#include "cairo-list-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-clip-private.h"
#include "cairo-surface-backend-private.h"
 
typedef void (*cairo_surface_func_t) (cairo_surface_t *);
 
61,7 → 62,10
cairo_reference_count_t ref_count;
cairo_status_t status;
unsigned int unique_id;
unsigned int serial;
cairo_damage_t *damage;
 
unsigned _finishing : 1;
unsigned finished : 1;
unsigned is_clear : 1;
unsigned has_font_options : 1;
101,4 → 105,17
cairo_font_options_t font_options;
};
 
cairo_private cairo_surface_t *
_cairo_surface_create_in_error (cairo_status_t status);
 
cairo_private cairo_surface_t *
_cairo_int_surface_create_in_error (cairo_int_status_t status);
 
cairo_private cairo_surface_t *
_cairo_surface_get_source (cairo_surface_t *surface,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_flush (cairo_surface_t *surface, unsigned flags);
 
#endif /* CAIRO_SURFACE_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-snapshot-inline.h
0,0 → 1,67
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SURFACE_SNAPSHOT_INLINE_H
#define CAIRO_SURFACE_SNAPSHOT_INLINE_H
 
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-inline.h"
 
static inline cairo_bool_t
_cairo_surface_snapshot_is_reused (cairo_surface_t *surface)
{
return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) > 2;
}
 
static inline cairo_surface_t *
_cairo_surface_snapshot_get_target (cairo_surface_t *surface)
{
cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
cairo_surface_t *target;
 
CAIRO_MUTEX_LOCK (snapshot->mutex);
target = _cairo_surface_reference (snapshot->target);
CAIRO_MUTEX_UNLOCK (snapshot->mutex);
 
return target;
}
 
static inline cairo_bool_t
_cairo_surface_is_snapshot (cairo_surface_t *surface)
{
return surface->backend->type == (cairo_surface_type_t)CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT;
}
 
#endif /* CAIRO_SURFACE_SNAPSHOT_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-surface-snapshot-private.h
36,11 → 36,14
#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H
 
#include "cairo-mutex-private.h"
#include "cairo-surface-private.h"
#include "cairo-surface-backend-private.h"
 
struct _cairo_surface_snapshot {
cairo_surface_t base;
 
cairo_mutex_t mutex;
cairo_surface_t *target;
cairo_surface_t *clone;
};
/programs/develop/libraries/cairo/src/cairo-surface-snapshot.c
40,7 → 40,8
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-snapshot-inline.h"
 
static cairo_status_t
_cairo_surface_snapshot_finish (void *abstract_surface)
48,6 → 49,8
cairo_surface_snapshot_t *surface = abstract_surface;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (surface->clone != NULL) {
cairo_surface_finish (surface->clone);
status = surface->clone->status;
55,27 → 58,72
cairo_surface_destroy (surface->clone);
}
 
CAIRO_MUTEX_FINI (surface->mutex);
 
return status;
}
 
static cairo_status_t
_cairo_surface_snapshot_flush (void *abstract_surface, unsigned flags)
{
cairo_surface_snapshot_t *surface = abstract_surface;
cairo_surface_t *target;
cairo_status_t status;
 
target = _cairo_surface_snapshot_get_target (&surface->base);
status = _cairo_surface_flush (target, flags);
cairo_surface_destroy (target);
 
return status;
}
 
static cairo_surface_t *
_cairo_surface_snapshot_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_surface_snapshot_t *surface = abstract_surface;
return _cairo_surface_get_source (surface->target, extents); /* XXX racy */
}
 
struct snapshot_extra {
cairo_surface_t *target;
void *extra;
};
 
static cairo_status_t
_cairo_surface_snapshot_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **extra_out)
{
cairo_surface_snapshot_t *surface = abstract_surface;
struct snapshot_extra *extra;
cairo_status_t status;
 
return _cairo_surface_acquire_source_image (surface->target, image_out, extra_out);
extra = malloc (sizeof (*extra));
if (unlikely (extra == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
extra->target = _cairo_surface_snapshot_get_target (&surface->base);
status = _cairo_surface_acquire_source_image (extra->target, image_out, &extra->extra);
if (unlikely (status)) {
cairo_surface_destroy (extra->target);
free (extra);
}
 
*extra_out = extra;
return status;
}
 
static void
_cairo_surface_snapshot_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *extra)
void *_extra)
{
cairo_surface_snapshot_t *surface = abstract_surface;
struct snapshot_extra *extra = _extra;
 
_cairo_surface_release_source_image (surface->target, image, extra);
_cairo_surface_release_source_image (extra->target, image, extra->extra);
cairo_surface_destroy (extra->target);
free (extra);
}
 
static cairo_bool_t
83,28 → 131,38
cairo_rectangle_int_t *extents)
{
cairo_surface_snapshot_t *surface = abstract_surface;
cairo_surface_t *target;
cairo_bool_t bounded;
 
return _cairo_surface_get_extents (surface->target, extents);
target = _cairo_surface_snapshot_get_target (&surface->base);
bounded = _cairo_surface_get_extents (target, extents);
cairo_surface_destroy (target);
 
return bounded;
}
 
static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT,
_cairo_surface_snapshot_finish,
NULL,
 
NULL, /* create similar */
_cairo_surface_snapshot_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_snapshot_source,
_cairo_surface_snapshot_acquire_source_image,
_cairo_surface_snapshot_release_source_image,
NULL, NULL, /* acquire, release dest */
NULL, /* clone similar */
NULL, /* composite */
NULL, /* fill rectangles */
NULL, /* composite trapezoids */
NULL, /* create span renderer */
NULL, /* check span renderer */
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_surface_snapshot_get_extents,
NULL, /* get-font-options */
 
_cairo_surface_snapshot_flush,
};
 
static void
112,10 → 170,13
{
cairo_surface_snapshot_t *snapshot = (cairo_surface_snapshot_t *) surface;
cairo_image_surface_t *image;
cairo_image_surface_t *clone;
cairo_surface_t *clone;
void *extra;
cairo_status_t status;
 
TRACE ((stderr, "%s: target=%d\n",
__FUNCTION__, snapshot->target->unique_id));
 
/* We need to make an image copy of the original surface since the
* snapshot may exceed the lifetime of the original device, i.e.
* when we later need to use the snapshot the data may have already
122,45 → 183,39
* been lost.
*/
 
CAIRO_MUTEX_LOCK (snapshot->mutex);
 
if (snapshot->target->backend->snapshot != NULL) {
clone = snapshot->target->backend->snapshot (snapshot->target);
if (clone != NULL) {
assert (clone->status || ! _cairo_surface_is_snapshot (clone));
goto done;
}
}
 
/* XXX copy to a similar surface, leave acquisition till later?
* We should probably leave such decisions to the backend in case we
* rely upon devices/connections like Xlib.
*/
status = _cairo_surface_acquire_source_image (snapshot->target, &image, &extra);
if (unlikely (status)) {
snapshot->target = _cairo_surface_create_in_error (status);
status = _cairo_surface_set_error (surface, status);
return;
goto unlock;
}
clone = image->base.backend->snapshot (&image->base);
_cairo_surface_release_source_image (snapshot->target, image, extra);
 
clone = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
image->pixman_format,
image->width,
image->height,
0);
if (likely (clone->base.status == CAIRO_STATUS_SUCCESS)) {
if (clone->stride == image->stride) {
memcpy (clone->data, image->data, image->stride * image->height);
} else {
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, clone->pixman_image,
0, 0,
0, 0,
0, 0,
image->width, image->height);
done:
status = _cairo_surface_set_error (surface, clone->status);
snapshot->target = snapshot->clone = clone;
snapshot->base.type = clone->type;
unlock:
CAIRO_MUTEX_UNLOCK (snapshot->mutex);
}
clone->base.is_clear = FALSE;
 
snapshot->clone = &clone->base;
} else {
snapshot->clone = &clone->base;
status = _cairo_surface_set_error (surface, clone->base.status);
}
 
_cairo_surface_release_source_image (snapshot->target, image, extra);
snapshot->target = snapshot->clone;
snapshot->base.type = snapshot->target->type;
}
 
/**
* _cairo_surface_snapshot
* _cairo_surface_snapshot:
* @surface: a #cairo_surface_t
*
* Make an immutable reference to @surface. It is an error to call a
184,42 → 239,20
cairo_surface_snapshot_t *snapshot;
cairo_status_t status;
 
TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->unique_id));
 
if (unlikely (surface->status))
return _cairo_surface_create_in_error (surface->status);
 
if (surface->finished)
if (unlikely (surface->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (surface->snapshot_of != NULL)
return cairo_surface_reference (surface);
 
if (surface->backend->snapshot != NULL) {
cairo_surface_t *snap;
if (_cairo_surface_is_snapshot (surface))
return cairo_surface_reference (surface);
 
snap = _cairo_surface_has_snapshot (surface, surface->backend);
if (snap != NULL)
return cairo_surface_reference (snap);
 
snap = surface->backend->snapshot (surface);
if (snap != NULL) {
if (unlikely (snap->status))
return snap;
 
status = _cairo_surface_copy_mime_data (snap, surface);
if (unlikely (status)) {
cairo_surface_destroy (snap);
return _cairo_surface_create_in_error (status);
}
 
snap->device_transform = surface->device_transform;
snap->device_transform_inverse = surface->device_transform_inverse;
 
_cairo_surface_attach_snapshot (surface, snap, NULL);
 
return snap;
}
}
 
snapshot = (cairo_surface_snapshot_t *)
_cairo_surface_has_snapshot (surface, &_cairo_surface_snapshot_backend);
if (snapshot != NULL)
235,6 → 268,7
surface->content);
snapshot->base.type = surface->type;
 
CAIRO_MUTEX_INIT (snapshot->mutex);
snapshot->target = surface;
snapshot->clone = NULL;
 
/programs/develop/libraries/cairo/src/cairo-surface-subsurface-inline.h
0,0 → 1,72
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_SURFACE_SUBSURFACE_INLINE_H
#define CAIRO_SURFACE_SUBSURFACE_INLINE_H
 
#include "cairo-surface-subsurface-private.h"
 
static inline cairo_surface_t *
_cairo_surface_subsurface_get_target (cairo_surface_t *surface)
{
return ((cairo_surface_subsurface_t *) surface)->target;
}
 
static inline void
_cairo_surface_subsurface_offset (cairo_surface_t *surface,
int *x, int *y)
{
cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
*x += ss->extents.x;
*y += ss->extents.y;
}
 
static inline cairo_surface_t *
_cairo_surface_subsurface_get_target_with_offset (cairo_surface_t *surface,
int *x, int *y)
{
cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
*x += ss->extents.x;
*y += ss->extents.y;
return ss->target;
}
 
static inline cairo_bool_t
_cairo_surface_is_subsurface (cairo_surface_t *surface)
{
return surface->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE;
}
 
#endif /* CAIRO_SURFACE_SUBSURFACE_INLINE_H */
/programs/develop/libraries/cairo/src/cairo-surface-subsurface-private.h
37,6 → 37,7
#define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H
 
#include "cairo-surface-private.h"
#include "cairo-surface-backend-private.h"
 
struct _cairo_surface_subsurface {
cairo_surface_t base;
44,6 → 45,11
cairo_rectangle_int_t extents;
 
cairo_surface_t *target;
cairo_surface_t *snapshot;
};
 
cairo_private void
_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface,
cairo_surface_t *snapshot);
 
#endif /* CAIRO_SURFACE_SUBSURFACE_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-subsurface.c
35,9 → 35,12
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
 
static const cairo_surface_backend_t _cairo_surface_subsurface_backend;
48,6 → 51,7
cairo_surface_subsurface_t *surface = abstract_surface;
 
cairo_surface_destroy (surface->target);
cairo_surface_destroy (surface->snapshot);
 
return CAIRO_STATUS_SUCCESS;
}
58,30 → 62,67
int width, int height)
{
cairo_surface_subsurface_t *surface = other;
 
if (surface->target->backend->create_similar == NULL)
return NULL;
 
return surface->target->backend->create_similar (surface->target, content, width, height);
}
 
static cairo_surface_t *
_cairo_surface_subsurface_create_similar_image (void *other,
cairo_format_t format,
int width, int height)
{
cairo_surface_subsurface_t *surface = other;
 
if (surface->target->backend->create_similar_image == NULL)
return NULL;
 
return surface->target->backend->create_similar_image (surface->target,
format,
width, height);
}
 
static cairo_image_surface_t *
_cairo_surface_subsurface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t target_extents;
 
target_extents.x = extents->x + surface->extents.x;
target_extents.y = extents->y + surface->extents.y;
target_extents.width = extents->width;
target_extents.height = extents->height;
 
return _cairo_surface_map_to_image (surface->target, &target_extents);
}
 
static cairo_int_status_t
_cairo_surface_subsurface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_surface_subsurface_t *surface = abstract_surface;
return _cairo_surface_unmap_image (surface->target, image);
}
 
static cairo_int_status_t
_cairo_surface_subsurface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
cairo_status_t status;
cairo_clip_t target_clip;
cairo_clip_t *target_clip;
 
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &rect);
if (unlikely (status))
goto CLEANUP;
 
target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
status = _cairo_surface_offset_paint (surface->target,
-surface->extents.x, -surface->extents.y,
op, source, &target_clip);
CLEANUP:
_cairo_clip_fini (&target_clip);
op, source, target_clip);
_cairo_clip_destroy (target_clip);
return status;
}
 
90,23 → 131,18
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
cairo_status_t status;
cairo_clip_t target_clip;
cairo_clip_t *target_clip;
 
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &rect);
if (unlikely (status))
goto CLEANUP;
 
target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
status = _cairo_surface_offset_mask (surface->target,
-surface->extents.x, -surface->extents.y,
op, source, mask, &target_clip);
CLEANUP:
_cairo_clip_fini (&target_clip);
op, source, mask, target_clip);
_cairo_clip_destroy (target_clip);
return status;
}
 
114,28 → 150,23
_cairo_surface_subsurface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
cairo_status_t status;
cairo_clip_t target_clip;
cairo_clip_t *target_clip;
 
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &rect);
if (unlikely (status))
goto CLEANUP;
 
target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
status = _cairo_surface_offset_fill (surface->target,
-surface->extents.x, -surface->extents.y,
op, source, path, fill_rule, tolerance, antialias,
&target_clip);
CLEANUP:
_cairo_clip_fini (&target_clip);
target_clip);
_cairo_clip_destroy (target_clip);
return status;
}
 
143,31 → 174,26
_cairo_surface_subsurface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
cairo_status_t status;
cairo_clip_t target_clip;
cairo_clip_t *target_clip;
 
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &rect);
if (unlikely (status))
goto CLEANUP;
 
target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
status = _cairo_surface_offset_stroke (surface->target,
-surface->extents.x, -surface->extents.y,
op, source, path, stroke_style, ctm, ctm_inverse,
tolerance, antialias,
&target_clip);
CLEANUP:
_cairo_clip_fini (&target_clip);
target_clip);
_cairo_clip_destroy (target_clip);
return status;
}
 
178,41 → 204,28
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
const cairo_clip_t *clip)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t rect = { 0, 0, surface->extents.width, surface->extents.height };
cairo_status_t status;
cairo_clip_t target_clip;
cairo_clip_t *target_clip;
 
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &rect);
if (unlikely (status))
goto CLEANUP;
 
target_clip = _cairo_clip_copy_intersect_rectangle (clip, &rect);
status = _cairo_surface_offset_glyphs (surface->target,
-surface->extents.x, -surface->extents.y,
op, source,
scaled_font, glyphs, num_glyphs,
&target_clip);
*remaining_glyphs = 0;
CLEANUP:
_cairo_clip_fini (&target_clip);
target_clip);
_cairo_clip_destroy (target_clip);
return status;
}
 
static cairo_status_t
_cairo_surface_subsurface_flush (void *abstract_surface)
_cairo_surface_subsurface_flush (void *abstract_surface, unsigned flags)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_status_t status;
 
status = CAIRO_STATUS_SUCCESS;
if (surface->target->backend->flush != NULL)
status = surface->target->backend->flush (surface->target);
 
return status;
return _cairo_surface_flush (surface->target, flags);
}
 
static cairo_status_t
271,27 → 284,18
surface->target->backend->get_font_options (surface->target, options);
}
 
struct extra {
cairo_image_surface_t *image;
void *image_extra;
};
 
static void
cairo_surface_paint_to_target (cairo_surface_t *target,
cairo_surface_subsurface_t *subsurface)
static cairo_surface_t *
_cairo_surface_subsurface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_t *cr;
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_surface_t *source;
cr = cairo_create (target);
source = _cairo_surface_get_source (surface->target, extents);
if (extents)
*extents = surface->extents;
 
cairo_set_source_surface (cr,
subsurface->target,
- subsurface->extents.x,
- subsurface->extents.y);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_destroy (cr);
return source;
}
 
static cairo_status_t
299,117 → 303,41
cairo_image_surface_t **image_out,
void **extra_out)
{
cairo_rectangle_int_t target_extents;
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_image_surface_t *image;
cairo_surface_pattern_t pattern;
cairo_surface_t *image;
cairo_status_t status;
struct extra *extra;
uint8_t *data;
cairo_bool_t ret;
 
if (surface->target->type == CAIRO_SURFACE_TYPE_RECORDING) {
cairo_recording_surface_t *meta = (cairo_recording_surface_t *) surface->target;
cairo_surface_t *snapshot;
 
snapshot = _cairo_surface_has_snapshot (&surface->base,
&_cairo_image_surface_backend);
if (snapshot != NULL) {
*image_out = (cairo_image_surface_t *) cairo_surface_reference (snapshot);
*extra_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
if (! _cairo_surface_has_snapshot (&meta->base,
&_cairo_image_surface_backend))
{
image = (cairo_image_surface_t *)
_cairo_image_surface_create_with_content (meta->content,
image = _cairo_image_surface_create_with_content (surface->base.content,
surface->extents.width,
surface->extents.height);
if (unlikely (image->base.status))
return image->base.status;
if (unlikely (image->status))
return image->status;
 
cairo_surface_paint_to_target (&image->base, surface);
_cairo_pattern_init_for_surface (&pattern, surface->target);
cairo_matrix_init_translate (&pattern.base.matrix,
surface->extents.x,
surface->extents.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status)) {
cairo_surface_destroy (image);
return status;
}
 
_cairo_surface_attach_snapshot (&surface->base, &image->base, NULL);
 
*image_out = image;
*image_out = (cairo_image_surface_t *)image;
*extra_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
}
 
extra = malloc (sizeof (struct extra));
if (unlikely (extra == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = _cairo_surface_acquire_source_image (surface->target, &extra->image, &extra->image_extra);
if (unlikely (status))
goto CLEANUP;
 
ret = _cairo_surface_get_extents (&extra->image->base, &target_extents);
assert (ret);
 
/* only copy if we need to perform sub-byte manipulation */
if (PIXMAN_FORMAT_BPP (extra->image->pixman_format) >= 8 &&
target_extents.x <= surface->extents.x &&
target_extents.y <= surface->extents.y &&
surface->extents.x + surface->extents.width <= target_extents.x + target_extents.width &&
surface->extents.y + surface->extents.height <= target_extents.y + target_extents.height) {
 
assert ((PIXMAN_FORMAT_BPP (extra->image->pixman_format) % 8) == 0);
 
data = extra->image->data + surface->extents.y * extra->image->stride;
data += PIXMAN_FORMAT_BPP (extra->image->pixman_format) / 8 * surface->extents.x;
 
image = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (data,
extra->image->pixman_format,
surface->extents.width,
surface->extents.height,
extra->image->stride);
if (unlikely ((status = image->base.status)))
goto CLEANUP_IMAGE;
 
image->base.is_clear = FALSE;
} else {
image = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
extra->image->pixman_format,
surface->extents.width,
surface->extents.height,
0);
if (unlikely ((status = image->base.status)))
goto CLEANUP_IMAGE;
 
cairo_surface_paint_to_target (&image->base, surface);
}
 
*image_out = image;
*extra_out = extra;
return CAIRO_STATUS_SUCCESS;
 
CLEANUP_IMAGE:
_cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
CLEANUP:
free (extra);
return status;
}
 
static void
_cairo_surface_subsurface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *abstract_extra)
{
cairo_surface_subsurface_t *surface = abstract_surface;
 
if (abstract_extra != NULL) {
struct extra *extra = abstract_extra;
 
_cairo_surface_release_source_image (surface->target, extra->image, extra->image_extra);
free (extra);
}
 
cairo_surface_destroy (&image->base);
}
 
417,62 → 345,74
_cairo_surface_subsurface_snapshot (void *abstract_surface)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_surface_subsurface_t *snapshot;
cairo_surface_pattern_t pattern;
cairo_surface_t *clone;
cairo_status_t status;
 
snapshot = malloc (sizeof (cairo_surface_subsurface_t));
if (unlikely (snapshot == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, surface->target->unique_id));
 
_cairo_surface_init (&snapshot->base,
&_cairo_surface_subsurface_backend,
NULL, /* device */
surface->target->content);
snapshot->target = _cairo_surface_snapshot (surface->target);
if (unlikely (snapshot->target->status)) {
cairo_status_t status;
clone = _cairo_surface_create_similar_scratch (surface->target,
surface->target->content,
surface->extents.width,
surface->extents.height);
if (unlikely (clone->status))
return clone;
 
status = snapshot->target->status;
free (snapshot);
return _cairo_surface_create_in_error (status);
_cairo_pattern_init_for_surface (&pattern, surface->target);
cairo_matrix_init_translate (&pattern.base.matrix,
surface->extents.x, surface->extents.y);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (clone,
CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL);
_cairo_pattern_fini (&pattern.base);
 
if (unlikely (status)) {
cairo_surface_destroy (clone);
clone = _cairo_surface_create_in_error (status);
}
 
snapshot->base.type = snapshot->target->type;
snapshot->extents = surface->extents;
return clone;
}
 
return &snapshot->base;
static cairo_t *
_cairo_surface_subsurface_create_context(void *target)
{
cairo_surface_subsurface_t *surface = target;
return surface->target->backend->create_context (&surface->base);
}
 
static const cairo_surface_backend_t _cairo_surface_subsurface_backend = {
CAIRO_SURFACE_TYPE_SUBSURFACE,
_cairo_surface_subsurface_create_similar,
_cairo_surface_subsurface_finish,
 
_cairo_surface_subsurface_create_context,
 
_cairo_surface_subsurface_create_similar,
_cairo_surface_subsurface_create_similar_image,
_cairo_surface_subsurface_map_to_image,
_cairo_surface_subsurface_unmap_image,
 
_cairo_surface_subsurface_source,
_cairo_surface_subsurface_acquire_source_image,
_cairo_surface_subsurface_release_source_image,
NULL, NULL, /* acquire, release dest */
NULL, /* clone similar */
NULL, /* composite */
NULL, /* fill rectangles */
NULL, /* composite trapezoids */
NULL, /* create span renderer */
NULL, /* check span renderer */
_cairo_surface_subsurface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_surface_subsurface_get_extents,
NULL, /* old_show_glyphs */
_cairo_surface_subsurface_get_font_options,
 
_cairo_surface_subsurface_flush,
_cairo_surface_subsurface_mark_dirty,
NULL, /* font_fini */
NULL, /* glyph_fini */
 
_cairo_surface_subsurface_paint,
_cairo_surface_subsurface_mask,
_cairo_surface_subsurface_stroke,
_cairo_surface_subsurface_fill,
NULL, /* fill/stroke */
_cairo_surface_subsurface_glyphs,
 
_cairo_surface_subsurface_snapshot,
};
 
/**
513,6 → 453,9
{
cairo_surface_subsurface_t *surface;
 
if (unlikely (width < 0 || height < 0))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
if (unlikely (target->finished))
536,6 → 479,8
surface->extents.y = ceil (y);
surface->extents.width = floor (x + width) - surface->extents.x;
surface->extents.height = floor (y + height) - surface->extents.y;
if ((surface->extents.width | surface->extents.height) < 0)
surface->extents.width = surface->extents.height = 0;
 
if (target->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
/* Maintain subsurfaces as 1-depth */
546,7 → 491,81
}
 
surface->target = cairo_surface_reference (target);
surface->base.type = surface->target->type;
 
surface->snapshot = NULL;
 
return &surface->base;
}
 
cairo_surface_t *
_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
const cairo_rectangle_int_t *extents)
{
cairo_surface_subsurface_t *surface;
 
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
if (unlikely (target->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE);
 
surface = malloc (sizeof (cairo_surface_subsurface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
assert (_cairo_matrix_is_translation (&target->device_transform));
 
_cairo_surface_init (&surface->base,
&_cairo_surface_subsurface_backend,
NULL, /* device */
target->content);
 
surface->extents = *extents;
surface->extents.x += target->device_transform.x0;
surface->extents.y += target->device_transform.y0;
 
surface->target = cairo_surface_reference (target);
surface->base.type = surface->target->type;
 
surface->snapshot = NULL;
 
return &surface->base;
}
/* XXX observe mark-dirty */
 
static void
_cairo_surface_subsurface_detach_snapshot (cairo_surface_t *surface)
{
cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
 
TRACE ((stderr, "%s: target=%d\n", __FUNCTION__, ss->target->unique_id));
 
cairo_surface_destroy (ss->snapshot);
ss->snapshot = NULL;
}
 
void
_cairo_surface_subsurface_set_snapshot (cairo_surface_t *surface,
cairo_surface_t *snapshot)
{
cairo_surface_subsurface_t *ss = (cairo_surface_subsurface_t *) surface;
 
TRACE ((stderr, "%s: target=%d, snapshot=%d\n", __FUNCTION__,
ss->target->unique_id, snapshot->unique_id));
 
/* FIXME: attaching the subsurface as a snapshot to its target creates
* a reference cycle. Let's make this call as a no-op until that bug
* is fixed.
*/
return;
 
if (ss->snapshot)
_cairo_surface_detach_snapshot (ss->snapshot);
 
ss->snapshot = cairo_surface_reference (snapshot);
 
_cairo_surface_attach_snapshot (ss->target, &ss->base,
_cairo_surface_subsurface_detach_snapshot);
}
/programs/develop/libraries/cairo/src/cairo-surface-wrapper-private.h
39,7 → 39,9
#ifndef CAIRO_SURFACE_WRAPPER_PRIVATE_H
#define CAIRO_SURFACE_WRAPPER_PRIVATE_H
 
#include "cairoint.h"
#include "cairo-types-private.h"
#include "cairo-surface-backend-private.h"
 
CAIRO_BEGIN_DECLS
 
46,8 → 48,13
struct _cairo_surface_wrapper {
cairo_surface_t *target;
 
cairo_matrix_t transform;
 
cairo_bool_t has_extents;
cairo_rectangle_int_t extents;
const cairo_clip_t *clip;
 
cairo_bool_t needs_transform;
};
 
cairo_private void
55,12 → 62,26
cairo_surface_t *target);
 
cairo_private void
_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper,
_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
const cairo_rectangle_int_t *extents);
 
cairo_private void
_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
const cairo_matrix_t *transform);
 
cairo_private void
_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
const cairo_clip_t *clip);
 
cairo_private void
_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper);
 
static inline cairo_bool_t
_cairo_surface_wrapper_has_fill_stroke (cairo_surface_wrapper_t *wrapper)
{
return wrapper->target->backend->fill_stroke != NULL;
}
 
cairo_private cairo_status_t
_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
cairo_image_surface_t **image_out,
76,7 → 97,7
_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
83,19 → 104,19
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_wrapper_fill_stroke (cairo_surface_wrapper_t *wrapper,
104,7 → 125,7
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
112,17 → 133,17
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper,
130,13 → 151,13
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
const cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_surface_t *
_cairo_surface_wrapper_create_similar (cairo_surface_wrapper_t *wrapper,
163,6 → 184,10
return wrapper->target != (cairo_surface_t *) 0;
}
 
cairo_private cairo_bool_t
_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
cairo_rectangle_int_t *extents);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_SURFACE_WRAPPER_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-surface-wrapper.c
37,7 → 37,9
 
#include "cairoint.h"
 
#include "cairo-clip-inline.h"
#include "cairo-error-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-wrapper-private.h"
 
/* A collection of routines to facilitate surface wrapping */
49,34 → 51,10
{
_cairo_pattern_init_static_copy (pattern, original);
 
/* apply device_transform first so that it is transformed by ctm_inverse */
if (original->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern;
cairo_surface_t *surface;
 
surface_pattern = (cairo_surface_pattern_t *) original;
surface = surface_pattern->surface;
 
if (_cairo_surface_has_device_transform (surface))
_cairo_pattern_transform (pattern, &surface->device_transform);
}
 
if (! _cairo_matrix_is_identity (ctm_inverse))
_cairo_pattern_transform (pattern, ctm_inverse);
}
 
static inline cairo_bool_t
_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
{
return ! _cairo_matrix_is_identity (&wrapper->target->device_transform);
}
 
static cairo_bool_t
_cairo_surface_wrapper_needs_extents_transform (cairo_surface_wrapper_t *wrapper)
{
return wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y);
}
 
cairo_status_t
_cairo_surface_wrapper_acquire_source_image (cairo_surface_wrapper_t *wrapper,
cairo_image_surface_t **image_out,
97,55 → 75,86
_cairo_surface_release_source_image (wrapper->target, image, image_extra);
}
 
static void
_cairo_surface_wrapper_get_transform (cairo_surface_wrapper_t *wrapper,
cairo_matrix_t *m)
{
cairo_matrix_init_identity (m);
 
if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
cairo_matrix_translate (m, -wrapper->extents.x, -wrapper->extents.y);
 
if (! _cairo_matrix_is_identity (&wrapper->transform))
cairo_matrix_multiply (m, &wrapper->transform, m);
 
if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
cairo_matrix_multiply (m, &wrapper->target->device_transform, m);
}
 
static void
_cairo_surface_wrapper_get_inverse_transform (cairo_surface_wrapper_t *wrapper,
cairo_matrix_t *m)
{
cairo_matrix_init_identity (m);
 
if (! _cairo_matrix_is_identity (&wrapper->target->device_transform_inverse))
cairo_matrix_multiply (m, &wrapper->target->device_transform_inverse, m);
 
if (! _cairo_matrix_is_identity (&wrapper->transform)) {
cairo_matrix_t inv;
cairo_status_t status;
 
inv = wrapper->transform;
status = cairo_matrix_invert (&inv);
assert (status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (m, &inv, m);
}
 
if (wrapper->has_extents && (wrapper->extents.x || wrapper->extents.y))
cairo_matrix_translate (m, wrapper->extents.x, wrapper->extents.y);
}
 
static cairo_clip_t *
_cairo_surface_wrapper_get_clip (cairo_surface_wrapper_t *wrapper,
const cairo_clip_t *clip)
{
cairo_clip_t *copy;
 
copy = _cairo_clip_copy (clip);
if (wrapper->has_extents) {
copy = _cairo_clip_intersect_rectangle (copy, &wrapper->extents);
}
copy = _cairo_clip_transform (copy, &wrapper->transform);
if (! _cairo_matrix_is_identity (&wrapper->target->device_transform))
copy = _cairo_clip_transform (copy, &wrapper->target->device_transform);
if (wrapper->clip)
copy = _cairo_clip_intersect_clip (copy, wrapper->clip);
 
return copy;
}
 
cairo_status_t
_cairo_surface_wrapper_paint (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_clip_t *dev_clip;
cairo_pattern_union_t source_copy;
cairo_clip_t target_clip;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
 
155,65 → 164,35
 
status = _cairo_surface_paint (wrapper->target, op, source, dev_clip);
 
FINISH:
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
return status;
}
 
 
cairo_status_t
_cairo_surface_wrapper_mask (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_clip_t *dev_clip;
cairo_pattern_union_t source_copy;
cairo_pattern_union_t mask_copy;
cairo_clip_t target_clip;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
 
226,11 → 205,7
 
status = _cairo_surface_mask (wrapper->target, op, source, mask, dev_clip);
 
FINISH:
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
return status;
}
 
238,52 → 213,33
_cairo_surface_wrapper_stroke (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = path;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
cairo_clip_t *dev_clip;
cairo_matrix_t dev_ctm = *ctm;
cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
cairo_pattern_union_t source_copy;
cairo_clip_t target_clip;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
if (unlikely (status))
goto FINISH;
291,14 → 247,6
_cairo_path_fixed_transform (&path_copy, &m);
dev_path = &path_copy;
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
 
status = cairo_matrix_invert (&m);
309,13 → 257,6
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
}
else
{
if (clip != NULL) {
dev_clip = &clip_copy;
_cairo_clip_init_copy (&clip_copy, clip);
}
}
 
status = _cairo_surface_stroke (wrapper->target, op, source,
dev_path, stroke_style,
326,10 → 267,7
FINISH:
if (dev_path != path)
_cairo_path_fixed_fini (dev_path);
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
return status;
}
 
340,7 → 278,7
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
348,47 → 286,28
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = path;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *)path;
cairo_matrix_t dev_ctm = *stroke_ctm;
cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
cairo_clip_t *dev_clip;
cairo_pattern_union_t stroke_source_copy;
cairo_pattern_union_t fill_source_copy;
cairo_clip_t target_clip;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
if (unlikely (status))
goto FINISH;
396,14 → 315,6
_cairo_path_fixed_transform (&path_copy, &m);
dev_path = &path_copy;
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
cairo_matrix_multiply (&dev_ctm, &dev_ctm, &m);
 
status = cairo_matrix_invert (&m);
417,13 → 328,6
_copy_transformed_pattern (&fill_source_copy.base, fill_source, &m);
fill_source = &fill_source_copy.base;
}
else
{
if (clip != NULL) {
dev_clip = &clip_copy;
_cairo_clip_init_copy (&clip_copy, clip);
}
}
 
status = _cairo_surface_fill_stroke (wrapper->target,
fill_op, fill_source, fill_rule,
438,10 → 342,7
FINISH:
if (dev_path != path)
_cairo_path_fixed_fini (dev_path);
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
return status;
}
 
449,48 → 350,29
_cairo_surface_wrapper_fill (cairo_surface_wrapper_t *wrapper,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_path_fixed_t path_copy, *dev_path = path;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_path_fixed_t path_copy, *dev_path = (cairo_path_fixed_t *) path;
cairo_pattern_union_t source_copy;
cairo_clip_t target_clip;
cairo_clip_t *dev_clip;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
status = _cairo_path_fixed_init_copy (&path_copy, dev_path);
if (unlikely (status))
goto FINISH;
498,14 → 380,6
_cairo_path_fixed_transform (&path_copy, &m);
dev_path = &path_copy;
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
}
 
status = cairo_matrix_invert (&m);
assert (status == CAIRO_STATUS_SUCCESS);
 
512,13 → 386,6
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
}
else
{
if (clip != NULL) {
dev_clip = &clip_copy;
_cairo_clip_init_copy (&clip_copy, clip);
}
}
 
status = _cairo_surface_fill (wrapper->target, op, source,
dev_path, fill_rule,
528,10 → 395,7
FINISH:
if (dev_path != path)
_cairo_path_fixed_fini (dev_path);
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
_cairo_clip_destroy (dev_clip);
return status;
}
 
541,71 → 405,63
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
const cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_clip_t clip_copy, *dev_clip = clip;
cairo_glyph_t *dev_glyphs = glyphs;
cairo_clip_t *dev_clip;
cairo_glyph_t stack_glyphs [CAIRO_STACK_ARRAY_LENGTH(cairo_glyph_t)];
cairo_glyph_t *dev_glyphs = stack_glyphs;
cairo_scaled_font_t *dev_scaled_font = scaled_font;
cairo_pattern_union_t source_copy;
cairo_clip_t target_clip;
cairo_font_options_t options;
 
if (unlikely (wrapper->target->status))
return wrapper->target->status;
 
if (glyphs == NULL || num_glyphs == 0)
return CAIRO_STATUS_SUCCESS;
dev_clip = _cairo_surface_wrapper_get_clip (wrapper, clip);
if (_cairo_clip_is_all_clipped (dev_clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
if (wrapper->has_extents) {
_cairo_clip_init_copy (&target_clip, clip);
status = _cairo_clip_rectangle (&target_clip, &wrapper->extents);
if (unlikely (status))
goto FINISH;
cairo_surface_get_font_options (wrapper->target, &options);
cairo_font_options_merge (&options, &scaled_font->options);
 
dev_clip = clip = &target_clip;
}
 
if (clip && clip->all_clipped) {
status = CAIRO_STATUS_SUCCESS;
goto FINISH;
}
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper) ||
_cairo_surface_wrapper_needs_extents_transform (wrapper))
{
if (wrapper->needs_transform) {
cairo_matrix_t m;
int i;
 
cairo_matrix_init_identity (&m);
_cairo_surface_wrapper_get_transform (wrapper, &m);
 
if (_cairo_surface_wrapper_needs_extents_transform (wrapper))
cairo_matrix_translate (&m, -wrapper->extents.x, -wrapper->extents.y);
if (! _cairo_matrix_is_translation (&wrapper->transform)) {
cairo_matrix_t ctm;
 
if (_cairo_surface_wrapper_needs_device_transform (wrapper))
cairo_matrix_multiply (&m, &wrapper->target->device_transform, &m);
 
if (clip != NULL) {
status = _cairo_clip_init_copy_transformed (&clip_copy, clip, &m);
if (unlikely (status))
goto FINISH;
 
dev_clip = &clip_copy;
/* XXX No device-transform? A bug in the tangle of layers? */
_cairo_matrix_multiply (&ctm,
&wrapper->transform,
&scaled_font->ctm);
dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
&scaled_font->font_matrix,
&ctm, &options);
}
 
if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (dev_glyphs == NULL) {
if (unlikely (dev_glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FINISH;
}
}
 
for (i = 0; i < num_glyphs; i++) {
dev_glyphs[i] = glyphs[i];
cairo_matrix_transform_point (&m, &dev_glyphs[i].x, &dev_glyphs[i].y);
cairo_matrix_transform_point (&m,
&dev_glyphs[i].x,
&dev_glyphs[i].y);
}
 
status = cairo_matrix_invert (&m);
613,30 → 469,42
 
_copy_transformed_pattern (&source_copy.base, source, &m);
source = &source_copy.base;
} else {
if (! cairo_font_options_equal (&options, &scaled_font->options)) {
dev_scaled_font = cairo_scaled_font_create (scaled_font->font_face,
&scaled_font->font_matrix,
&scaled_font->ctm,
&options);
}
else
{
if (clip != NULL) {
dev_clip = &clip_copy;
_cairo_clip_init_copy (&clip_copy, clip);
 
/* show_text_glyphs is special because _cairo_surface_show_text_glyphs is allowed
* to modify the glyph array that's passed in. We must always
* copy the array before handing it to the backend.
*/
if (num_glyphs > ARRAY_LENGTH (stack_glyphs)) {
dev_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (unlikely (dev_glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FINISH;
}
}
 
memcpy (dev_glyphs, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
}
 
status = _cairo_surface_show_text_glyphs (wrapper->target, op, source,
utf8, utf8_len,
dev_glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font,
dev_scaled_font,
dev_clip);
 
FINISH:
if (dev_clip != clip)
_cairo_clip_reset (dev_clip);
if (wrapper->has_extents)
_cairo_clip_reset (&target_clip);
if (dev_glyphs != glyphs)
_cairo_clip_destroy (dev_clip);
if (dev_glyphs != stack_glyphs)
free (dev_glyphs);
if (dev_scaled_font != scaled_font)
cairo_scaled_font_destroy (dev_scaled_font);
return status;
}
 
666,19 → 534,58
}
}
 
static cairo_bool_t
_cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
{
return
(wrapper->has_extents && (wrapper->extents.x | wrapper->extents.y)) ||
! _cairo_matrix_is_identity (&wrapper->transform) ||
! _cairo_matrix_is_identity (&wrapper->target->device_transform);
}
 
void
_cairo_surface_wrapper_set_extents (cairo_surface_wrapper_t *wrapper,
_cairo_surface_wrapper_intersect_extents (cairo_surface_wrapper_t *wrapper,
const cairo_rectangle_int_t *extents)
{
if (extents != NULL) {
if (! wrapper->has_extents) {
wrapper->extents = *extents;
wrapper->has_extents = TRUE;
} else
_cairo_rectangle_intersect (&wrapper->extents, extents);
 
wrapper->needs_transform =
_cairo_surface_wrapper_needs_device_transform (wrapper);
}
 
void
_cairo_surface_wrapper_set_inverse_transform (cairo_surface_wrapper_t *wrapper,
const cairo_matrix_t *transform)
{
cairo_status_t status;
 
if (transform == NULL || _cairo_matrix_is_identity (transform)) {
cairo_matrix_init_identity (&wrapper->transform);
 
wrapper->needs_transform =
_cairo_surface_wrapper_needs_device_transform (wrapper);
} else {
wrapper->has_extents = FALSE;
wrapper->transform = *transform;
status = cairo_matrix_invert (&wrapper->transform);
/* should always be invertible unless given pathological input */
assert (status == CAIRO_STATUS_SUCCESS);
 
wrapper->needs_transform = TRUE;
}
}
 
void
_cairo_surface_wrapper_set_clip (cairo_surface_wrapper_t *wrapper,
const cairo_clip_t *clip)
{
wrapper->clip = clip;
}
 
void
_cairo_surface_wrapper_get_font_options (cairo_surface_wrapper_t *wrapper,
cairo_font_options_t *options)
{
688,7 → 595,10
cairo_surface_t *
_cairo_surface_wrapper_snapshot (cairo_surface_wrapper_t *wrapper)
{
return _cairo_surface_snapshot (wrapper->target);
if (wrapper->target->backend->snapshot)
return wrapper->target->backend->snapshot (wrapper->target);
 
return NULL;
}
 
cairo_bool_t
702,8 → 612,17
cairo_surface_t *target)
{
wrapper->target = cairo_surface_reference (target);
cairo_matrix_init_identity (&wrapper->transform);
wrapper->has_extents = FALSE;
wrapper->extents.x = wrapper->extents.y = 0;
wrapper->clip = NULL;
 
wrapper->needs_transform = FALSE;
if (target) {
wrapper->needs_transform =
! _cairo_matrix_is_identity (&target->device_transform);
}
}
 
void
_cairo_surface_wrapper_fini (cairo_surface_wrapper_t *wrapper)
710,3 → 629,58
{
cairo_surface_destroy (wrapper->target);
}
 
cairo_bool_t
_cairo_surface_wrapper_get_target_extents (cairo_surface_wrapper_t *wrapper,
cairo_rectangle_int_t *extents)
{
cairo_rectangle_int_t clip;
cairo_bool_t has_clip;
 
has_clip = _cairo_surface_get_extents (wrapper->target, &clip);
if (wrapper->clip) {
if (has_clip) {
if (! _cairo_rectangle_intersect (&clip,
_cairo_clip_get_extents (wrapper->clip)))
return FALSE;
} else {
has_clip = TRUE;
clip = *_cairo_clip_get_extents (wrapper->clip);
}
}
 
if (has_clip && wrapper->needs_transform) {
cairo_matrix_t m;
double x1, y1, x2, y2;
 
_cairo_surface_wrapper_get_inverse_transform (wrapper, &m);
 
x1 = clip.x;
y1 = clip.y;
x2 = clip.x + clip.width;
y2 = clip.y + clip.height;
 
_cairo_matrix_transform_bounding_box (&m, &x1, &y1, &x2, &y2, NULL);
 
clip.x = floor (x1);
clip.y = floor (y1);
clip.width = ceil (x2) - clip.x;
clip.height = ceil (y2) - clip.y;
}
 
if (has_clip) {
if (wrapper->has_extents) {
*extents = wrapper->extents;
return _cairo_rectangle_intersect (extents, &clip);
} else {
*extents = clip;
return TRUE;
}
} else if (wrapper->has_extents) {
*extents = wrapper->extents;
return TRUE;
} else {
_cairo_unbounded_rectangle_init (extents);
return TRUE;
}
}
/programs/develop/libraries/cairo/src/cairo-surface.c
38,12 → 38,17
 
#include "cairoint.h"
 
#include "cairo-surface-fallback-private.h"
#include "cairo-array-private.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-damage-private.h"
#include "cairo-device-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
#include "cairo-image-surface-inline.h"
#include "cairo-recording-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-surface-inline.h"
#include "cairo-tee-surface-private.h"
 
/**
58,7 → 63,7
*
* A cairo surface is created by using <firstterm>backend</firstterm>-specific
* constructors, typically of the form
* cairo_<emphasis>backend</emphasis>_surface_create().
* <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>.
*
* Most surface types allow accessing the surface without using Cairo
* functions. If you do this, keep in mind that it is mandatory that you call
91,7 → 96,7
* Note that for other surface types it might be necessary to acquire the
* surface's device first. See cairo_device_acquire() for a discussion of
* devices.
*/
**/
 
#define DEFINE_NIL_SURFACE(status, name) \
const cairo_surface_t name = { \
102,6 → 107,9
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \
status, /* status */ \
0, /* unique id */ \
0, /* serial */ \
NULL, /* damage */ \
FALSE, /* _finishing */ \
FALSE, /* finished */ \
TRUE, /* is_clear */ \
FALSE, /* has_font_options */ \
123,7 → 131,8
CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \
CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \
CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \
CAIRO_HINT_METRICS_DEFAULT /* hint_metrics */ \
CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \
CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \
} /* font_options */ \
}
 
144,6 → 153,12
static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error);
 
static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported);
static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do);
 
static void _cairo_surface_finish_snapshots (cairo_surface_t *surface);
static void _cairo_surface_finish (cairo_surface_t *surface);
 
/**
* _cairo_surface_set_error:
* @surface: a surface
164,19 → 179,23
*
* Return value: the error status.
**/
cairo_status_t
cairo_int_status_t
_cairo_surface_set_error (cairo_surface_t *surface,
cairo_status_t status)
cairo_int_status_t status)
{
/* NOTHING_TO_DO is magic. We use it to break out of the inner-most
* surface function, but anything higher just sees "success".
*/
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_STATUS_SUCCESS;
status = CAIRO_INT_STATUS_SUCCESS;
 
if (status == CAIRO_STATUS_SUCCESS || status >= CAIRO_INT_STATUS_UNSUPPORTED)
if (status == CAIRO_INT_STATUS_SUCCESS ||
status >= (int)CAIRO_INT_STATUS_LAST_STATUS)
return status;
 
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&surface->status, status);
_cairo_status_set_error (&surface->status, (cairo_status_t)status);
 
return _cairo_error (status);
}
201,7 → 220,6
* surface. */
return surface->type;
}
slim_hidden_def (cairo_surface_get_type);
 
/**
* cairo_surface_get_content:
220,7 → 238,6
{
return surface->content;
}
slim_hidden_def(cairo_surface_get_content);
 
/**
* cairo_surface_status:
233,6 → 250,8
* %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR,
* %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or
* %CAIRO_STATUS_INVALID_VISUAL.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_status (cairo_surface_t *surface)
361,7 → 380,6
cairo_list_foreach_entry (snapshot, cairo_surface_t,
&surface->snapshots, snapshot)
{
/* XXX is_similar? */
if (snapshot->backend == backend)
return snapshot;
}
369,24 → 387,13
return NULL;
}
 
static cairo_bool_t
_cairo_surface_is_writable (cairo_surface_t *surface)
{
return ! surface->finished &&
surface->snapshot_of == NULL &&
! _cairo_surface_has_snapshots (surface) &&
! _cairo_surface_has_mime_data (surface);
}
 
static void
cairo_status_t
_cairo_surface_begin_modification (cairo_surface_t *surface)
{
assert (surface->status == CAIRO_STATUS_SUCCESS);
assert (! surface->finished);
assert (surface->snapshot_of == NULL);
 
_cairo_surface_detach_snapshots (surface);
_cairo_surface_detach_mime_data (surface);
return _cairo_surface_flush (surface, 1);
}
 
void
406,7 → 413,10
surface->status = CAIRO_STATUS_SUCCESS;
surface->unique_id = _cairo_surface_allocate_unique_id ();
surface->finished = FALSE;
surface->_finishing = FALSE;
surface->is_clear = FALSE;
surface->serial = 0;
surface->damage = NULL;
surface->owns_device = (device != NULL);
 
_cairo_user_data_array_init (&surface->user_data);
455,12 → 465,15
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
 
if (other->backend->create_similar == NULL)
return NULL;
surface = NULL;
if (other->backend->create_similar)
surface = other->backend->create_similar (other, content, width, height);
if (surface == NULL)
surface = cairo_surface_create_similar_image (other,
_cairo_format_from_content (content),
width, height);
 
surface = other->backend->create_similar (other,
content, width, height);
if (surface == NULL || surface->status)
if (unlikely (surface->status))
return surface;
 
_cairo_surface_copy_similar_properties (surface, other);
485,6 → 498,9
* Initially the surface contents are all 0 (transparent if contents
* have transparency, black otherwise.)
*
* Use cairo_surface_create_similar_image() if you need an image surface
* which can be painted quickly to the target surface.
*
* Return value: a pointer to the newly allocated surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
492,6 → 508,8
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if @other is already in an error state
* or any other error occurs.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_surface_create_similar (cairo_surface_t *other,
499,27 → 517,346
int width,
int height)
{
cairo_surface_t *surface;
 
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
if (unlikely (other->finished))
return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (width < 0 || height < 0))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
 
if (unlikely (! CAIRO_CONTENT_VALID (content)))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
 
return _cairo_surface_create_similar_solid (other,
surface = _cairo_surface_create_similar_solid (other,
content, width, height,
CAIRO_COLOR_TRANSPARENT,
TRUE);
CAIRO_COLOR_TRANSPARENT);
assert (surface->is_clear);
 
return surface;
}
 
/**
* cairo_surface_create_similar_image:
* @other: an existing surface used to select the preference of the new surface
* @format: the format for the new surface
* @width: width of the new surface, (in device-space units)
* @height: height of the new surface (in device-space units)
*
* Create a new image surface that is as compatible as possible for uploading
* to and the use in conjunction with an existing surface. However, this surface
* can still be used like any normal image surface.
*
* Initially the surface contents are all 0 (transparent if contents
* have transparency, black otherwise.)
*
* Use cairo_surface_create_similar() if you don't need an image surface.
*
* Return value: a pointer to the newly allocated image surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if @other is already in an error state
* or any other error occurs.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_surface_create_similar_image (cairo_surface_t *other,
cairo_format_t format,
int width,
int height)
{
cairo_surface_t *image;
 
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
if (unlikely (other->finished))
return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
 
if (unlikely (width < 0 || height < 0))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
if (unlikely (! CAIRO_FORMAT_VALID (format)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT);
 
image = NULL;
if (other->backend->create_similar_image)
image = other->backend->create_similar_image (other,
format, width, height);
if (image == NULL)
image = cairo_image_surface_create (format, width, height);
 
assert (image->is_clear);
 
return image;
}
slim_hidden_def (cairo_surface_create_similar_image);
 
/**
* _cairo_surface_map_to_image:
* @surface: an existing surface used to extract the image from
* @extents: limit the extraction to an rectangular region
*
* Returns an image surface that is the most efficient mechanism for
* modifying the backing store of the target surface. The region
* retrieved is limited to @extents.
*
* Note, the use of the original surface as a target or source whilst
* it is mapped is undefined. The result of mapping the surface
* multiple times is undefined. Calling cairo_surface_destroy() or
* cairo_surface_finish() on the resulting image surface results in
* undefined behavior. Changing the device transform of the image
* surface or of @surface before the image surface is unmapped results
* in undefined behavior.
*
* Assumes that @surface is valid (CAIRO_STATUS_SUCCESS,
* non-finished).
*
* Return value: a pointer to the newly allocated image surface. The
* caller must use _cairo_surface_unmap_image() to destroy this image
* surface.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if @other is already in an error state
* or any other error occurs.
*
* The returned image might have a %CAIRO_FORMAT_INVALID format.
**/
cairo_image_surface_t *
_cairo_surface_map_to_image (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents)
{
cairo_image_surface_t *image = NULL;
 
assert (extents != NULL);
 
/* TODO: require map_to_image != NULL */
if (surface->backend->map_to_image)
image = surface->backend->map_to_image (surface, extents);
 
if (image == NULL)
image = _cairo_image_surface_clone_subimage (surface, extents);
 
return image;
}
 
/**
* _cairo_surface_unmap_image:
* @surface: the surface passed to _cairo_surface_map_to_image().
* @image: the currently mapped image
*
* Unmaps the image surface as returned from
* _cairo_surface_map_to_image().
*
* The content of the image will be uploaded to the target surface.
* Afterwards, the image is destroyed.
*
* Using an image surface which wasn't returned by
* _cairo_surface_map_to_image() results in undefined behavior.
*
* An image surface in error status can be passed to
* _cairo_surface_unmap_image().
*
* Return value: the unmap status.
*
* Even if the unmap status is not successful, @image is destroyed.
**/
cairo_int_status_t
_cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_surface_pattern_t pattern;
cairo_rectangle_int_t extents;
cairo_clip_t *clip;
cairo_int_status_t status;
 
/* map_to_image can return error surfaces */
if (unlikely (image->base.status)) {
status = image->base.status;
goto destroy;
}
 
/* If the image is untouched just skip the update */
if (image->base.serial == 0) {
status = CAIRO_STATUS_SUCCESS;
goto destroy;
}
 
/* TODO: require unmap_image != NULL */
if (surface->backend->unmap_image &&
! _cairo_image_surface_is_clone (image))
{
status = surface->backend->unmap_image (surface, image);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
_cairo_pattern_init_for_surface (&pattern, &image->base);
pattern.base.filter = CAIRO_FILTER_NEAREST;
 
/* We have to apply the translate from map_to_image's extents.x and .y */
cairo_matrix_init_translate (&pattern.base.matrix,
image->base.device_transform.x0,
image->base.device_transform.y0);
 
/* And we also have to clip the operation to the image's extents */
extents.x = image->base.device_transform_inverse.x0;
extents.y = image->base.device_transform_inverse.y0;
extents.width = image->width;
extents.height = image->height;
clip = _cairo_clip_intersect_rectangle (NULL, &extents);
 
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
clip);
 
_cairo_pattern_fini (&pattern.base);
_cairo_clip_destroy (clip);
 
destroy:
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return status;
}
 
/**
* cairo_surface_map_to_image:
* @surface: an existing surface used to extract the image from
* @extents: limit the extraction to an rectangular region
*
* Returns an image surface that is the most efficient mechanism for
* modifying the backing store of the target surface. The region retrieved
* may be limited to the @extents or %NULL for the whole surface
*
* Note, the use of the original surface as a target or source whilst
* it is mapped is undefined. The result of mapping the surface
* multiple times is undefined. Calling cairo_surface_destroy() or
* cairo_surface_finish() on the resulting image surface results in
* undefined behavior. Changing the device transform of the image
* surface or of @surface before the image surface is unmapped results
* in undefined behavior.
*
* Return value: a pointer to the newly allocated image surface. The caller
* must use cairo_surface_unmap_image() to destroy this image surface.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if @other is already in an error state
* or any other error occurs. If the returned pointer does not have an
* error status, it is guaranteed to be an image surface whose format
* is not %CAIRO_FORMAT_INVALID.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_surface_map_to_image (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents)
{
cairo_rectangle_int_t rect;
cairo_image_surface_t *image;
cairo_status_t status;
 
if (unlikely (surface->status))
return _cairo_surface_create_in_error (surface->status);
if (unlikely (surface->finished))
return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
 
if (extents == NULL) {
if (unlikely (! surface->backend->get_extents (surface, &rect)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
 
extents = &rect;
} else {
cairo_rectangle_int_t surface_extents;
 
/* If this surface is bounded, we can't map parts
* that are outside of it. */
if (likely (surface->backend->get_extents (surface, &surface_extents))) {
if (unlikely (! _cairo_rectangle_contains_rectangle (&surface_extents, extents)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
}
}
 
image = _cairo_surface_map_to_image (surface, extents);
 
status = image->base.status;
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return _cairo_surface_create_in_error (status);
}
 
if (image->format == CAIRO_FORMAT_INVALID) {
cairo_surface_destroy (&image->base);
image = _cairo_image_surface_clone_subimage (surface, extents);
}
 
return &image->base;
}
 
/**
* cairo_surface_unmap_image:
* @surface: the surface passed to cairo_surface_map_to_image().
* @image: the currently mapped image
*
* Unmaps the image surface as returned from #cairo_surface_map_to_image().
*
* The content of the image will be uploaded to the target surface.
* Afterwards, the image is destroyed.
*
* Using an image surface which wasn't returned by cairo_surface_map_to_image()
* results in undefined behavior.
*
* Since: 1.12
**/
void
cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_surface_t *image)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
if (unlikely (surface->status)) {
status = surface->status;
goto error;
}
if (unlikely (surface->finished)) {
status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
goto error;
}
if (unlikely (image->status)) {
status = image->status;
goto error;
}
if (unlikely (image->finished)) {
status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
goto error;
}
if (unlikely (! _cairo_surface_is_image (image))) {
status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
goto error;
}
 
status = _cairo_surface_unmap_image (surface,
(cairo_image_surface_t *) image);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
 
return;
 
error:
_cairo_surface_set_error (surface, status);
cairo_surface_finish (image);
cairo_surface_destroy (image);
}
 
cairo_surface_t *
_cairo_surface_create_similar_solid (cairo_surface_t *other,
cairo_content_t content,
int width,
int height,
const cairo_color_t *color,
cairo_bool_t allow_fallback)
const cairo_color_t *color)
{
cairo_status_t status;
cairo_surface_t *surface;
527,10 → 864,7
 
surface = _cairo_surface_create_similar_scratch (other, content,
width, height);
if (surface == NULL && allow_fallback)
surface = _cairo_image_surface_create_with_content (content,
width, height);
if (surface == NULL || surface->status)
if (unlikely (surface->status))
return surface;
 
_cairo_pattern_init_solid (&pattern, color);
546,51 → 880,6
return surface;
}
 
cairo_surface_t *
_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other,
const cairo_solid_pattern_t *solid_pattern)
{
if (other->backend->create_solid_pattern_surface != NULL) {
cairo_surface_t *surface;
 
surface = other->backend->create_solid_pattern_surface (other,
solid_pattern);
if (surface)
return surface;
}
 
return _cairo_surface_create_similar_solid (other,
_cairo_color_get_content (&solid_pattern->color),
1, 1,
&solid_pattern->color,
FALSE);
}
 
cairo_int_status_t
_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other,
cairo_surface_t *solid_surface,
const cairo_solid_pattern_t *solid_pattern)
{
/* Solid pattern surface for these backends are special and not trivial
* to repaint. Skip repainting.
*
* This does not work optimally with things like analysis surface that
* are proxies. But returning UNSUPPORTED is *safe* as it only
* disables some caching.
*/
if (other->backend->create_solid_pattern_surface != NULL &&
! other->backend->can_repaint_solid_pattern_surface (solid_surface,
solid_pattern))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
return _cairo_surface_paint (solid_surface,
CAIRO_OPERATOR_SOURCE,
&solid_pattern->base,
NULL);
}
 
/**
* cairo_surface_reference:
* @surface: a #cairo_surface_t
603,6 → 892,8
* cairo_surface_get_reference_count().
*
* Return value: the referenced #cairo_surface_t.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_surface_reference (cairo_surface_t *surface)
626,6 → 917,8
* Decreases the reference count on @surface by one. If the result is
* zero, then @surface and all associated resources are freed. See
* cairo_surface_reference().
*
* Since: 1.0
**/
void
cairo_surface_destroy (cairo_surface_t *surface)
641,12 → 934,20
 
assert (surface->snapshot_of == NULL);
 
if (! surface->finished)
cairo_surface_finish (surface);
if (! surface->finished) {
_cairo_surface_finish_snapshots (surface);
/* We may have been referenced by a snapshot prior to have
* detaching it with the copy-on-write.
*/
if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count))
return;
 
/* paranoid check that nobody took a reference whilst finishing */
assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
_cairo_surface_finish (surface);
}
 
if (surface->damage)
_cairo_damage_destroy (surface->damage);
 
_cairo_user_data_array_fini (&surface->user_data);
_cairo_user_data_array_fini (&surface->mime_data);
 
653,6 → 954,11
if (surface->owns_device)
cairo_device_destroy (surface->device);
 
assert (surface->snapshot_of == NULL);
assert (! _cairo_surface_has_snapshots (surface));
/* paranoid check that nobody took a reference whilst finishing */
assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
 
free (surface);
}
slim_hidden_def(cairo_surface_destroy);
678,6 → 984,35
return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count);
}
 
static void
_cairo_surface_finish_snapshots (cairo_surface_t *surface)
{
cairo_status_t status;
 
/* update the snapshots *before* we declare the surface as finished */
surface->_finishing = TRUE;
status = _cairo_surface_flush (surface, 0);
(void) status;
}
 
static void
_cairo_surface_finish (cairo_surface_t *surface)
{
cairo_status_t status;
 
surface->finished = TRUE;
 
/* call finish even if in error mode */
if (surface->backend->finish) {
status = surface->backend->finish (surface);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
}
 
assert (surface->snapshot_of == NULL);
assert (!_cairo_surface_has_snapshots (surface));
}
 
/**
* cairo_surface_finish:
* @surface: the #cairo_surface_t to finish
696,12 → 1031,12
* reference count to zero, cairo will call cairo_surface_finish() if
* it hasn't been called already, before freeing the resources
* associated with the surface.
*
* Since: 1.0
**/
void
cairo_surface_finish (cairo_surface_t *surface)
{
cairo_status_t status;
 
if (surface == NULL)
return;
 
711,21 → 1046,15
if (surface->finished)
return;
 
/* update the snapshots *before* we declare the surface as finished */
_cairo_surface_detach_snapshots (surface);
if (surface->snapshot_of != NULL)
_cairo_surface_detach_snapshot (surface);
/* We have to be careful when decoupling potential reference cycles */
cairo_surface_reference (surface);
 
cairo_surface_flush (surface);
surface->finished = TRUE;
_cairo_surface_finish_snapshots (surface);
/* XXX need to block and wait for snapshot references */
_cairo_surface_finish (surface);
 
/* call finish even if in error mode */
if (surface->backend->finish) {
status = surface->backend->finish (surface);
if (unlikely (status))
status = _cairo_surface_set_error (surface, status);
cairo_surface_destroy (surface);
}
}
slim_hidden_def (cairo_surface_finish);
 
/**
760,13 → 1089,18
* function returns %NULL.
*
* Return value: the user data previously attached or %NULL.
*
* Since: 1.0
**/
void *
cairo_surface_get_user_data (cairo_surface_t *surface,
const cairo_user_data_key_t *key)
{
return _cairo_user_data_array_get_data (&surface->user_data,
key);
/* Prevent reads of the array during teardown */
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return NULL;
 
return _cairo_user_data_array_get_data (&surface->user_data, key);
}
 
/**
784,6 → 1118,8
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
* slot could not be allocated for the user data.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_set_user_data (cairo_surface_t *surface,
794,6 → 1130,9
if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return surface->status;
 
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
return _cairo_user_data_array_set_data (&surface->user_data,
key, user_data, destroy);
}
822,7 → 1161,9
 
*data = NULL;
*length = 0;
if (unlikely (surface->status))
 
/* Prevent reads of the array during teardown */
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return;
 
/* The number of mime-types attached to a surface is usually small,
833,7 → 1174,7
num_slots = surface->mime_data.num_elements;
slots = _cairo_array_index (&surface->mime_data, 0);
for (i = 0; i < num_slots; i++) {
if (strcmp ((char *) slots[i].key, mime_type) == 0) {
if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) {
cairo_mime_data_t *mime_data = slots[i].user_data;
 
*data = mime_data->data;
863,8 → 1204,8
*
* The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1).
*
* @Since: 1.10
*/
* Since: 1.10
**/
 
/**
* CAIRO_MIME_TYPE_JPEG:
871,8 → 1212,8
*
* The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1).
*
* @Since: 1.10
*/
* Since: 1.10
**/
 
/**
* CAIRO_MIME_TYPE_PNG:
879,8 → 1220,8
*
* The Portable Network Graphics image file format (ISO/IEC 15948).
*
* @Since: 1.10
*/
* Since: 1.10
**/
 
/**
* CAIRO_MIME_TYPE_URI:
887,10 → 1228,18
*
* URI for an image file (unofficial MIME type).
*
* @Since: 1.10
*/
* Since: 1.10
**/
 
/**
* CAIRO_MIME_TYPE_UNIQUE_ID:
*
* Unique identifier for a surface (cairo specific MIME type).
*
* Since: 1.12
**/
 
/**
* cairo_surface_set_mime_data:
* @surface: a #cairo_surface_t
* @mime_type: the MIME type of the image data
912,7 → 1261,8
* memory and disk space.
*
* The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG,
* %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI.
* %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI,
* %CAIRO_MIME_TYPE_UNIQUE_ID.
*
* See corresponding backend surface docs for details about which MIME
* types it can handle. Caution: the associated MIME data will be
919,10 → 1269,10
* discarded if you draw on the surface afterwards. Use this function
* with care.
*
* Since: 1.10
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
* slot could not be allocated for the user data.
*
* Since: 1.10
**/
cairo_status_t
cairo_surface_set_mime_data (cairo_surface_t *surface,
935,9 → 1285,15
cairo_status_t status;
cairo_mime_data_t *mime_data;
 
if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return surface->status;
 
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
if (unlikely (surface->status))
return surface->status;
if (surface->finished)
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
status = _cairo_intern_string (&mime_type, -1);
963,7 → 1319,6
mime_data,
_cairo_mime_data_destroy);
if (unlikely (status)) {
if (mime_data != NULL)
free (mime_data);
 
return _cairo_surface_set_error (surface, status);
973,6 → 1328,46
}
slim_hidden_def (cairo_surface_set_mime_data);
 
/**
* cairo_surface_supports_mime_type:
* @surface: a #cairo_surface_t
* @mime_type: the mime type
*
* Return whether @surface supports @mime_type.
*
* Return value: %TRUE if @surface supports
* @mime_type, %FALSE otherwise
*
* Since: 1.12
**/
cairo_bool_t
cairo_surface_supports_mime_type (cairo_surface_t *surface,
const char *mime_type)
{
const char **types;
 
if (unlikely (surface->status))
return FALSE;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
 
if (surface->backend->get_supported_mime_types) {
types = surface->backend->get_supported_mime_types (surface);
if (types) {
while (*types) {
if (strcmp (*types, mime_type) == 0)
return TRUE;
types++;
}
}
}
 
return FALSE;
}
slim_hidden_def (cairo_surface_supports_mime_type);
 
static void
_cairo_mime_data_reference (const void *key, void *elt, void *closure)
{
1025,8 → 1420,6
_cairo_surface_set_font_options (cairo_surface_t *surface,
cairo_font_options_t *options)
{
cairo_status_t status;
 
if (surface->status)
return;
 
1033,8 → 1426,7
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status = _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
1057,6 → 1449,8
* for rendering on them, print surfaces to disable hinting of
* metrics and so forth. The result can then be used with
* cairo_scaled_font_create().
*
* Since: 1.0
**/
void
cairo_surface_get_font_options (cairo_surface_t *surface,
1084,6 → 1478,18
}
slim_hidden_def (cairo_surface_get_font_options);
 
cairo_status_t
_cairo_surface_flush (cairo_surface_t *surface, unsigned flags)
{
/* update the current snapshots *before* the user updates the surface */
_cairo_surface_detach_snapshots (surface);
if (surface->snapshot_of != NULL)
_cairo_surface_detach_snapshot (surface);
_cairo_surface_detach_mime_data (surface);
 
return __cairo_surface_flush (surface, flags);
}
 
/**
* cairo_surface_flush:
* @surface: a #cairo_surface_t
1094,6 → 1500,8
* drawing on the surface with cairo to drawing on it directly
* with native APIs. If the surface doesn't support direct access,
* then this function does nothing.
*
* Since: 1.0
**/
void
cairo_surface_flush (cairo_surface_t *surface)
1106,15 → 1514,10
if (surface->finished)
return;
 
/* update the current snapshots *before* the user updates the surface */
_cairo_surface_detach_snapshots (surface);
 
if (surface->backend->flush) {
status = surface->backend->flush (surface);
status = _cairo_surface_flush (surface, 0);
if (unlikely (status))
status = _cairo_surface_set_error (surface, status);
_cairo_surface_set_error (surface, status);
}
}
slim_hidden_def (cairo_surface_flush);
 
/**
1124,12 → 1527,26
* Tells cairo that drawing has been done to surface using means other
* than cairo, and that cairo should reread any cached areas. Note
* that you must call cairo_surface_flush() before doing such drawing.
*/
*
* Since: 1.0
**/
void
cairo_surface_mark_dirty (cairo_surface_t *surface)
{
cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1);
cairo_rectangle_int_t extents;
 
if (unlikely (surface->status))
return;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
_cairo_surface_get_extents (surface, &extents);
cairo_surface_mark_dirty_rectangle (surface,
extents.x, extents.y,
extents.width, extents.height);
}
slim_hidden_def (cairo_surface_mark_dirty);
 
/**
1147,7 → 1564,9
* Any cached clip set on the surface will be reset by this function,
* to make sure that future cairo calls have the clip set that they
* expect.
*/
*
* Since: 1.0
**/
void
cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
int x,
1157,13 → 1576,13
{
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
1174,7 → 1593,19
assert (! _cairo_surface_has_mime_data (surface));
 
surface->is_clear = FALSE;
surface->serial++;
 
if (surface->damage) {
cairo_box_t box;
 
box.p1.x = x;
box.p1.y = y;
box.p2.x = x + width;
box.p2.y = y + height;
 
surface->damage = _cairo_damage_add_box (surface->damage, &box);
}
 
if (surface->backend->mark_dirty_rectangle != NULL) {
/* XXX: FRAGILE: We're ignoring the scaling component of
* device_transform here. I don't know what the right thing to
1187,7 → 1618,7
width, height);
 
if (unlikely (status))
status = _cairo_surface_set_error (surface, status);
_cairo_surface_set_error (surface, status);
}
}
slim_hidden_def (cairo_surface_mark_dirty_rectangle);
1216,17 → 1647,21
{
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
_cairo_surface_begin_modification (surface);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
 
surface->device_transform.xx = sx;
surface->device_transform.yy = sy;
1258,6 → 1693,8
*
* Note that the offset affects drawing to the surface as well as
* using the surface in a source pattern.
*
* Since: 1.0
**/
void
cairo_surface_set_device_offset (cairo_surface_t *surface,
1266,17 → 1703,21
{
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
_cairo_surface_begin_modification (surface);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
 
surface->device_transform.x0 = x_offset;
surface->device_transform.y0 = y_offset;
1353,13 → 1794,13
{
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status = _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
1367,11 → 1808,15
/* XXX Could delay raising the error until we fallback, but throwing
* the error here means that we can catch the real culprit.
*/
status = _cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
_cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
return;
}
 
_cairo_surface_begin_modification (surface);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
 
surface->x_fallback_resolution = x_pixels_per_inch;
surface->y_fallback_resolution = y_pixels_per_inch;
1431,7 → 1876,7
{
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return surface->status;
 
assert (!surface->finished);
1449,6 → 1894,22
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_surface_default_acquire_source_image (void *_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_surface_t *surface = _surface;
cairo_rectangle_int_t extents;
 
if (unlikely (! surface->backend->get_extents (surface, &extents)))
return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
 
*image_out = _cairo_surface_map_to_image (surface, &extents);
*image_extra = NULL;
return (*image_out)->base.status;
}
 
/**
* _cairo_surface_release_source_image:
* @surface: a #cairo_surface_t
1467,507 → 1928,35
surface->backend->release_source_image (surface, image, image_extra);
}
 
/**
* _cairo_surface_acquire_dest_image:
* @surface: a #cairo_surface_t
* @interest_rect: area of @surface for which fallback drawing is being done.
* A value of %NULL indicates that the entire surface is desired.
* XXXX I'd like to get rid of being able to pass %NULL here (nothing seems to)
* @image_out: location to store a pointer to an image surface that includes at least
* the intersection of @interest_rect with the visible area of @surface.
* This surface could be @surface itself, a surface held internal to @surface,
* or it could be a new surface with a copy of the relevant portion of @surface.
* If a new surface is created, it should have the same channels and depth
* as @surface so that copying to and from it is exact.
* @image_rect: location to store area of the original surface occupied
* by the surface stored in @image.
* @image_extra: location to store image specific backend data
*
* Retrieves a local image for a surface for implementing a fallback drawing
* operation. After calling this function, the implementation of the fallback
* drawing operation draws the primitive to the surface stored in @image_out
* then calls _cairo_surface_release_dest_image(),
* which, if a temporary surface was created, copies the bits back to the
* main surface and frees the temporary surface.
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY.
* %CAIRO_INT_STATUS_UNSUPPORTED can be returned but this will mean that
* the backend can't draw with fallbacks. It's possible for the routine
* to store %NULL in @local_out and return %CAIRO_STATUS_SUCCESS;
* that indicates that no part of @interest_rect is visible, so no drawing
* is necessary. _cairo_surface_release_dest_image() should not be called in that
* case.
**/
cairo_status_t
_cairo_surface_acquire_dest_image (cairo_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *image_rect,
void **image_extra)
{
cairo_status_t status;
 
if (surface->status)
return surface->status;
 
assert (_cairo_surface_is_writable (surface));
 
if (surface->backend->acquire_dest_image == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = surface->backend->acquire_dest_image (surface,
interest_rect,
image_out,
image_rect,
image_extra);
if (unlikely (status))
return _cairo_surface_set_error (surface, status);
 
_cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_surface_release_dest_image:
* @surface: a #cairo_surface_t
* @interest_rect: same as passed to the matching _cairo_surface_acquire_dest_image()
* @image: same as returned from the matching _cairo_surface_acquire_dest_image()
* @image_rect: same as returned from the matching _cairo_surface_acquire_dest_image()
* @image_extra: same as return from the matching _cairo_surface_acquire_dest_image()
*
* Finishes the operation started with _cairo_surface_acquire_dest_image(), by, if
* necessary, copying the image from @image back to @surface and freeing any
* resources that were allocated.
**/
void
_cairo_surface_release_dest_image (cairo_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
_cairo_surface_default_release_source_image (void *surface,
cairo_image_surface_t *image,
cairo_rectangle_int_t *image_rect,
void *image_extra)
{
assert (_cairo_surface_is_writable (surface));
cairo_status_t ignored;
 
if (surface->backend->release_dest_image)
surface->backend->release_dest_image (surface, interest_rect,
image, image_rect, image_extra);
ignored = _cairo_surface_unmap_image (surface, image);
(void)ignored;
}
 
static cairo_status_t
_cairo_recording_surface_clone_similar (cairo_surface_t *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_recording_surface_t *recorder = (cairo_recording_surface_t *) src;
cairo_surface_t *similar;
cairo_status_t status;
 
similar = _cairo_surface_has_snapshot (src, surface->backend);
if (similar != NULL) {
*clone_out = cairo_surface_reference (similar);
*clone_offset_x = 0;
*clone_offset_y = 0;
return CAIRO_STATUS_SUCCESS;
}
 
if (recorder->unbounded ||
width*height*8 < recorder->extents.width*recorder->extents.height)
cairo_surface_t *
_cairo_surface_get_source (cairo_surface_t *surface,
cairo_rectangle_int_t *extents)
{
similar = _cairo_surface_create_similar_solid (surface,
src->content,
width, height,
CAIRO_COLOR_TRANSPARENT,
FALSE);
if (similar == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (similar->status))
return similar->status;
 
cairo_surface_set_device_offset (similar, -src_x, -src_y);
 
status = _cairo_recording_surface_replay (src, similar);
if (unlikely (status)) {
cairo_surface_destroy (similar);
return status;
assert (surface->backend->source);
return surface->backend->source (surface, extents);
}
} else {
similar = _cairo_surface_create_similar_scratch (surface,
src->content,
recorder->extents.width,
recorder->extents.height);
if (similar == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (similar->status))
return similar->status;
 
status = _cairo_recording_surface_replay (src, similar);
if (unlikely (status)) {
cairo_surface_destroy (similar);
return status;
}
 
_cairo_surface_attach_snapshot (src, similar, NULL);
 
src_x = src_y = 0;
}
 
*clone_out = similar;
*clone_offset_x = src_x;
*clone_offset_y = src_y;
return CAIRO_STATUS_SUCCESS;
}
 
/**
* _cairo_surface_clone_similar:
* @surface: a #cairo_surface_t
* @src: the source image
* @content: target content mask
* @src_x: extent for the rectangle in src we actually care about
* @src_y: extent for the rectangle in src we actually care about
* @width: extent for the rectangle in src we actually care about
* @height: extent for the rectangle in src we actually care about
* @clone_out: location to store a surface compatible with @surface
* and with contents identical to @src. The caller must call
* cairo_surface_destroy() on the result.
*
* Creates a surface with contents identical to @src but that
* can be used efficiently with @surface. If @surface and @src are
* already compatible then it may return a new reference to @src.
*
* Return value: %CAIRO_STATUS_SUCCESS if a surface was created and stored
* in @clone_out. Otherwise %CAIRO_INT_STATUS_UNSUPPORTED or another
* error like %CAIRO_STATUS_NO_MEMORY.
**/
cairo_status_t
_cairo_surface_clone_similar (cairo_surface_t *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_surface_t *
_cairo_surface_default_source (void *surface,
cairo_rectangle_int_t *extents)
{
cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
cairo_image_surface_t *image;
void *image_extra;
 
if (unlikely (surface->status))
return surface->status;
 
if (unlikely (surface->finished))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 
#if CAIRO_HAS_TEE_SURFACE
 
if (src->type == CAIRO_SURFACE_TYPE_TEE) {
cairo_surface_t *match;
 
match = _cairo_tee_surface_find_match (src,
surface->backend,
src->content);
if (match != NULL)
src = match;
if (extents)
_cairo_surface_get_extents(surface, extents);
return surface;
}
 
#endif
 
if (surface->backend->clone_similar != NULL) {
status = surface->backend->clone_similar (surface, src,
src_x, src_y,
width, height,
clone_offset_x,
clone_offset_y,
clone_out);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
if (_cairo_surface_is_image (src))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* First check to see if we can replay to a similar surface */
if (_cairo_surface_is_recording (src)) {
return _cairo_recording_surface_clone_similar (surface, src,
src_x, src_y,
width, height,
clone_offset_x,
clone_offset_y,
clone_out);
}
 
/* If we failed, try again with an image surface */
status = _cairo_surface_acquire_source_image (src, &image, &image_extra);
if (status == CAIRO_STATUS_SUCCESS) {
status =
surface->backend->clone_similar (surface, &image->base,
src_x, src_y,
width, height,
clone_offset_x,
clone_offset_y,
clone_out);
 
_cairo_surface_release_source_image (src, image, image_extra);
}
}
}
 
/* If we're still unsupported, hit our fallback path to get a clone */
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status =
_cairo_surface_fallback_clone_similar (surface, src,
src_x, src_y,
width, height,
clone_offset_x,
clone_offset_y,
clone_out);
}
 
if (unlikely (status))
return status;
 
/* Update the clone's device_transform (which the underlying surface
* backend knows nothing about) */
if (*clone_out != src) {
(*clone_out)->device_transform = src->device_transform;
(*clone_out)->device_transform_inverse = src->device_transform_inverse;
}
 
return status;
}
 
/**
* _cairo_surface_is_similar
* @surface_a: a #cairo_surface_t
* @surface_b: a #cairo_surface_t
* @content: a #cairo_content_t
*
* Find out whether the given surfaces share the same backend,
* and if so, whether they can be considered similar.
*
* The definition of "similar" depends on the backend. In
* general, it means that the surface is equivalent to one
* that would have been generated by a call to cairo_surface_create_similar().
*
* Return value: %TRUE if the surfaces are similar.
**/
cairo_bool_t
_cairo_surface_is_similar (cairo_surface_t *surface_a,
cairo_surface_t *surface_b)
{
if (surface_a->backend != surface_b->backend)
return FALSE;
 
if (surface_a->backend->is_similar != NULL)
return surface_a->backend->is_similar (surface_a, surface_b);
 
return TRUE;
}
 
cairo_status_t
_cairo_surface_composite (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *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_int_status_t status;
 
if (unlikely (dst->status))
return dst->status;
 
assert (_cairo_surface_is_writable (dst));
 
if (mask) {
/* These operators aren't interpreted the same way by the backends;
* they are implemented in terms of other operators in cairo-gstate.c
*/
assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
}
 
if (dst->backend->composite) {
status = dst->backend->composite (op,
src, mask, dst,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height,
clip_region);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_surface_set_error (dst, status);
}
 
return _cairo_surface_set_error (dst,
_cairo_surface_fallback_composite (op,
src, mask, dst,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height,
clip_region));
}
 
/**
* _cairo_surface_fill_rectangle:
* @surface: a #cairo_surface_t
* @op: the operator to apply to the rectangle
* @color: the source color
* @x: X coordinate of rectangle, in backend coordinates
* @y: Y coordinate of rectangle, in backend coordinates
* @width: width of rectangle, in backend coordinates
* @height: height of rectangle, in backend coordinates
*
* Applies an operator to a rectangle using a solid color as the source.
* See _cairo_surface_fill_rectangles() for full details.
*
* Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
**/
cairo_status_t
_cairo_surface_fill_rectangle (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
int x,
int y,
int width,
int height)
{
cairo_rectangle_int_t rect;
 
if (surface->status)
return surface->status;
 
assert (_cairo_surface_is_writable (surface));
 
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
 
return _cairo_surface_fill_rectangles (surface, op, color, &rect, 1);
}
 
/**
* _cairo_surface_fill_region:
* @surface: a #cairo_surface_t
* @op: the operator to apply to the region
* @color: the source color
* @region: the region to modify, in backend coordinates
*
* Applies an operator to a set of rectangles specified as a
* #cairo_region_t using a solid color as the source.
* See _cairo_surface_fill_rectangles() for full details.
*
* Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
**/
cairo_status_t
_cairo_surface_fill_region (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_region_t *region)
{
int num_rects;
cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
cairo_rectangle_int_t *rects = stack_rects;
cairo_status_t status;
int i;
 
if (surface->status)
return surface->status;
 
assert (_cairo_surface_is_writable (surface));
 
num_rects = cairo_region_num_rectangles (region);
if (num_rects == 0)
return CAIRO_STATUS_SUCCESS;
 
/* catch a common reduction of _cairo_clip_combine_with_surface() */
if (op == CAIRO_OPERATOR_IN &&
_cairo_color_equal (color, CAIRO_COLOR_WHITE))
{
return CAIRO_STATUS_SUCCESS;
}
 
if (num_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (num_rects,
sizeof (cairo_rectangle_int_t));
if (rects == NULL) {
return _cairo_surface_set_error (surface,
_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
}
 
for (i = 0; i < num_rects; i++)
cairo_region_get_rectangle (region, i, &rects[i]);
 
status = _cairo_surface_fill_rectangles (surface,
op, color, rects, num_rects);
 
if (rects != stack_rects)
free (rects);
 
return _cairo_surface_set_error (surface, status);
}
 
/**
* _cairo_surface_fill_rectangles:
* @surface: a #cairo_surface_t
* @op: the operator to apply to the region
* @color: the source color
* @rects: the rectangles to modify, in backend coordinates
* @num_rects: the number of rectangles in @rects
*
* Applies an operator to a set of rectangles using a solid color
* as the source. Note that even if the operator is an unbounded operator
* such as %CAIRO_OPERATOR_IN, only the given set of rectangles
* is affected. This differs from _cairo_surface_composite_trapezoids()
* where the entire destination rectangle is cleared.
*
* Return value: %CAIRO_STATUS_SUCCESS or the error that occurred
**/
cairo_status_t
_cairo_surface_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_int_status_t status;
 
if (surface->status)
return surface->status;
 
assert (_cairo_surface_is_writable (surface));
 
if (num_rects == 0)
return CAIRO_STATUS_SUCCESS;
 
if (surface->backend->fill_rectangles) {
status = surface->backend->fill_rectangles (surface,
op, color,
rects, num_rects);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_surface_set_error (surface, status);
}
 
return _cairo_surface_set_error (surface,
_cairo_surface_fallback_fill_rectangles (surface,
op, color,
rects, num_rects));
}
 
static cairo_status_t
_pattern_has_error (const cairo_pattern_t *pattern)
{
1989,46 → 1978,62
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
nothing_to_do (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source)
{
if (_cairo_pattern_is_clear (source)) {
if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
return TRUE;
 
if (op == CAIRO_OPERATOR_SOURCE)
op = CAIRO_OPERATOR_CLEAR;
}
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return TRUE;
 
if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0)
return TRUE;
 
return FALSE;
}
 
cairo_status_t
_cairo_surface_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
 
if (op == CAIRO_OPERATOR_OVER &&
_cairo_pattern_is_clear (source))
{
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
}
 
status = _pattern_has_error (source);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
 
if (surface->backend->paint != NULL) {
status = surface->backend->paint (surface, op, source, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto FINISH;
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
surface->serial++;
}
 
status = _cairo_surface_fallback_paint (surface, op, source, clip);
 
FINISH:
surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
 
return _cairo_surface_set_error (surface, status);
}
 
2037,19 → 2042,19
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return CAIRO_STATUS_SUCCESS;
 
/* If the mask is blank, this is just an expensive no-op */
if (_cairo_pattern_is_clear (mask) &&
_cairo_operator_bounded_by_mask (op))
2057,12 → 2062,6
return CAIRO_STATUS_SUCCESS;
}
 
if (op == CAIRO_OPERATOR_OVER &&
_cairo_pattern_is_clear (source))
{
return CAIRO_STATUS_SUCCESS;
}
 
status = _pattern_has_error (source);
if (unlikely (status))
return status;
2071,19 → 2070,19
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
 
if (surface->backend->mask != NULL) {
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
status = surface->backend->mask (surface, op, source, mask, clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto FINISH;
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
 
status = _cairo_surface_fallback_mask (surface, op, source, mask, clip);
 
FINISH:
surface->is_clear = FALSE;
 
return _cairo_surface_set_error (surface, status);
}
 
2102,14 → 2101,17
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (surface->is_clear &&
2127,7 → 2129,9
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
if (surface->backend->fill_stroke) {
cairo_matrix_t dev_ctm = *stroke_ctm;
2161,7 → 2165,10
goto FINISH;
 
FINISH:
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
 
return _cairo_surface_set_error (surface, status);
}
2170,57 → 2177,46
_cairo_surface_stroke (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
 
if (op == CAIRO_OPERATOR_OVER &&
_cairo_pattern_is_clear (source))
{
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
}
 
status = _pattern_has_error (source);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
 
if (surface->backend->stroke != NULL) {
status = surface->backend->stroke (surface, op, source,
path, stroke_style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
 
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto FINISH;
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
 
status = _cairo_surface_fallback_stroke (surface, op, source,
path, stroke_style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
 
FINISH:
surface->is_clear = FALSE;
 
return _cairo_surface_set_error (surface, status);
}
 
2228,153 → 2224,46
_cairo_surface_fill (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
 
if (op == CAIRO_OPERATOR_OVER &&
_cairo_pattern_is_clear (source))
{
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
}
 
status = _pattern_has_error (source);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
 
if (surface->backend->fill != NULL) {
status = surface->backend->fill (surface, op, source,
path, fill_rule,
tolerance, antialias,
clip);
 
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto FINISH;
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
 
status = _cairo_surface_fallback_fill (surface, op, source,
path, fill_rule,
tolerance, antialias,
clip);
 
FINISH:
surface->is_clear = FALSE;
 
return _cairo_surface_set_error (surface, status);
}
 
cairo_status_t
_cairo_surface_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *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_int_status_t status;
 
if (dst->status)
return dst->status;
 
assert (_cairo_surface_is_writable (dst));
 
/* These operators aren't interpreted the same way by the backends;
* they are implemented in terms of other operators in cairo-gstate.c
*/
assert (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_CLEAR);
 
if (dst->backend->composite_trapezoids) {
status = dst->backend->composite_trapezoids (op,
pattern, dst,
antialias,
src_x, src_y,
dst_x, dst_y,
width, height,
traps, num_traps,
clip_region);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return _cairo_surface_set_error (dst, status);
}
 
return _cairo_surface_set_error (dst,
_cairo_surface_fallback_composite_trapezoids (op, pattern, dst,
antialias,
src_x, src_y,
dst_x, dst_y,
width, height,
traps, num_traps,
clip_region));
}
 
cairo_span_renderer_t *
_cairo_surface_create_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region)
{
assert (dst->snapshot_of == NULL);
 
if (unlikely (dst->status))
return _cairo_span_renderer_create_in_error (dst->status);
 
if (unlikely (dst->finished))
return _cairo_span_renderer_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
 
if (dst->backend->create_span_renderer) {
return dst->backend->create_span_renderer (op,
pattern, dst,
antialias,
rects,
clip_region);
}
ASSERT_NOT_REACHED;
return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED);
}
 
cairo_bool_t
_cairo_surface_check_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
cairo_antialias_t antialias)
{
assert (dst->snapshot_of == NULL);
assert (dst->status == CAIRO_STATUS_SUCCESS);
assert (! dst->finished);
 
/* XXX: Currently we have no mono span renderer */
if (antialias == CAIRO_ANTIALIAS_NONE)
return FALSE;
 
if (dst->backend->check_span_renderer != NULL)
return dst->backend->check_span_renderer (op, pattern, dst, antialias);
 
return FALSE;
}
 
/**
* cairo_surface_copy_page:
* @surface: a #cairo_surface_t
2388,20 → 2277,17
* namely cairo_copy_page().
*
* Since: 1.6
*/
**/
void
cairo_surface_copy_page (cairo_surface_t *surface)
{
cairo_status_t status_ignored;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
assert (surface->snapshot_of == NULL);
 
if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return;
}
 
2409,8 → 2295,7
if (surface->backend->copy_page == NULL)
return;
 
status_ignored = _cairo_surface_set_error (surface,
surface->backend->copy_page (surface));
_cairo_surface_set_error (surface, surface->backend->copy_page (surface));
}
slim_hidden_def (cairo_surface_copy_page);
 
2429,25 → 2314,27
void
cairo_surface_show_page (cairo_surface_t *surface)
{
cairo_status_t status_ignored;
cairo_status_t status;
 
if (surface->status)
if (unlikely (surface->status))
return;
 
if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return;
}
 
_cairo_surface_begin_modification (surface);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
 
/* It's fine if some backends don't implement show_page */
if (surface->backend->show_page == NULL)
return;
 
status_ignored = _cairo_surface_set_error (surface,
surface->backend->show_page (surface));
_cairo_surface_set_error (surface, surface->backend->show_page (surface));
}
slim_hidden_def (cairo_surface_show_page);
 
2474,7 → 2361,7
*
* This behavior would have to be changed is we ever exported a public
* variant of this function.
*/
**/
cairo_bool_t
_cairo_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_int_t *extents)
2481,6 → 2368,13
{
cairo_bool_t bounded;
 
if (unlikely (surface->status))
goto zero_extents;
if (unlikely (surface->finished)) {
_cairo_surface_set_error(surface, CAIRO_STATUS_SURFACE_FINISHED);
goto zero_extents;
}
 
bounded = FALSE;
if (surface->backend->get_extents != NULL)
bounded = surface->backend->get_extents (surface, extents);
2489,6 → 2383,11
_cairo_unbounded_rectangle_init (extents);
 
return bounded;
 
zero_extents:
extents->x = extents->y = 0;
extents->width = extents->height = 0;
return TRUE;
}
 
/**
2515,14 → 2414,11
cairo_bool_t
cairo_surface_has_show_text_glyphs (cairo_surface_t *surface)
{
cairo_status_t status_ignored;
 
if (surface->status)
if (unlikely (surface->status))
return FALSE;
 
if (surface->finished) {
status_ignored = _cairo_surface_set_error (surface,
CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return FALSE;
}
 
2560,29 → 2456,34
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_int_status_t status;
cairo_scaled_font_t *dev_scaled_font = scaled_font;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (num_glyphs == 0 && utf8_len == 0)
return CAIRO_STATUS_SUCCESS;
 
if (clip && clip->all_clipped)
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
 
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
status = _pattern_has_error (source);
if (unlikely (status))
return status;
 
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
 
status = _pattern_has_error (source);
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
 
_cairo_surface_begin_modification (surface);
 
if (_cairo_surface_has_device_transform (surface) &&
! _cairo_matrix_is_integer_translation (&surface->device_transform, NULL, NULL))
{
2621,32 → 2522,20
if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
surface->backend->show_glyphs)
{
int remaining_glyphs = num_glyphs;
status = surface->backend->show_glyphs (surface, op,
source,
glyphs, num_glyphs,
dev_scaled_font,
clip,
&remaining_glyphs);
glyphs += num_glyphs - remaining_glyphs;
num_glyphs = remaining_glyphs;
if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0)
status = CAIRO_STATUS_SUCCESS;
clip);
}
} else {
/* A mere show_glyphs call. Try show_glyphs backend method first */
if (surface->backend->show_glyphs != NULL) {
int remaining_glyphs = num_glyphs;
status = surface->backend->show_glyphs (surface, op,
source,
glyphs, num_glyphs,
dev_scaled_font,
clip,
&remaining_glyphs);
glyphs += num_glyphs - remaining_glyphs;
num_glyphs = remaining_glyphs;
if (status == CAIRO_INT_STATUS_UNSUPPORTED && remaining_glyphs == 0)
status = CAIRO_STATUS_SUCCESS;
clip);
} else if (surface->backend->show_text_glyphs != NULL) {
/* Intentionally only try show_text_glyphs method for show_glyphs
* calls if backend does not have show_glyphs. If backend has
2666,275 → 2555,19
}
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_surface_fallback_show_glyphs (surface, op,
source,
glyphs, num_glyphs,
dev_scaled_font,
clip);
}
 
if (dev_scaled_font != scaled_font)
cairo_scaled_font_destroy (dev_scaled_font);
 
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
 
return _cairo_surface_set_error (surface, status);
}
 
/* XXX: Previously, we had a function named _cairo_surface_show_glyphs
* with not-so-useful semantics. We've now got a
* _cairo_surface_show_text_glyphs with the proper semantics, and its
* fallback still uses this old function (which still needs to be
* cleaned up in terms of both semantics and naming). */
cairo_status_t
_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_region_t *clip_region)
{
cairo_status_t status;
 
if (dst->status)
return dst->status;
 
assert (_cairo_surface_is_writable (dst));
 
if (dst->backend->old_show_glyphs) {
status = dst->backend->old_show_glyphs (scaled_font,
op, pattern, dst,
source_x, source_y,
dest_x, dest_y,
width, height,
glyphs, num_glyphs,
clip_region);
} else
status = CAIRO_INT_STATUS_UNSUPPORTED;
 
return _cairo_surface_set_error (dst, status);
}
 
static cairo_status_t
_cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst,
cairo_rectangle_int_t *src_rectangle,
cairo_rectangle_int_t *mask_rectangle,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height,
cairo_region_t *clip_region)
{
cairo_rectangle_int_t dst_rectangle;
cairo_region_t clear_region;
cairo_status_t status;
 
/* The area that was drawn is the area in the destination rectangle but
* not within the source or the mask.
*/
dst_rectangle.x = dst_x;
dst_rectangle.y = dst_y;
dst_rectangle.width = width;
dst_rectangle.height = height;
 
_cairo_region_init_rectangle (&clear_region, &dst_rectangle);
 
if (clip_region != NULL) {
status = cairo_region_intersect (&clear_region, clip_region);
if (unlikely (status))
goto CLEANUP_REGIONS;
}
 
if (src_rectangle != NULL) {
if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle))
goto EMPTY;
}
 
if (mask_rectangle != NULL) {
if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle))
goto EMPTY;
}
 
/* Now compute the area that is in dst but not drawn */
status = cairo_region_subtract_rectangle (&clear_region, &dst_rectangle);
if (unlikely (status) || cairo_region_is_empty (&clear_region))
goto CLEANUP_REGIONS;
 
EMPTY:
status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear_region);
 
CLEANUP_REGIONS:
_cairo_region_fini (&clear_region);
 
return _cairo_surface_set_error (dst, status);
}
 
/**
* _cairo_surface_composite_fixup_unbounded:
* @dst: the destination surface
* @src_attr: source surface attributes (from _cairo_pattern_acquire_surface())
* @src_width: width of source surface
* @src_height: height of source surface
* @mask_attr: mask surface attributes or %NULL if no mask
* @mask_width: width of mask surface
* @mask_height: height of mask surface
* @src_x: @src_x from _cairo_surface_composite()
* @src_y: @src_y from _cairo_surface_composite()
* @mask_x: @mask_x from _cairo_surface_composite()
* @mask_y: @mask_y from _cairo_surface_composite()
* @dst_x: @dst_x from _cairo_surface_composite()
* @dst_y: @dst_y from _cairo_surface_composite()
* @width: @width from _cairo_surface_composite()
* @height: @height_x from _cairo_surface_composite()
*
* Eeek! Too many parameters! This is a helper function to take care of fixing
* up for bugs in libpixman and RENDER where, when asked to composite an
* untransformed surface with an unbounded operator (like CLEAR or SOURCE)
* only the region inside both the source and the mask is affected.
* This function clears the region that should have been drawn but was wasn't.
**/
cairo_status_t
_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst,
cairo_surface_attributes_t *src_attr,
int src_width,
int src_height,
cairo_surface_attributes_t *mask_attr,
int mask_width,
int mask_height,
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_rectangle_int_t src_tmp, mask_tmp;
cairo_rectangle_int_t *src_rectangle = NULL;
cairo_rectangle_int_t *mask_rectangle = NULL;
 
if (unlikely (dst->status))
return dst->status;
 
assert (_cairo_surface_is_writable (dst));
 
/* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
* non-repeating sources and masks. Other sources and masks can be ignored.
*/
if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
src_attr->extend == CAIRO_EXTEND_NONE)
{
src_tmp.x = (dst_x - (src_x + src_attr->x_offset));
src_tmp.y = (dst_y - (src_y + src_attr->y_offset));
src_tmp.width = src_width;
src_tmp.height = src_height;
 
src_rectangle = &src_tmp;
}
 
if (mask_attr &&
_cairo_matrix_is_integer_translation (&mask_attr->matrix, NULL, NULL) &&
mask_attr->extend == CAIRO_EXTEND_NONE)
{
mask_tmp.x = (dst_x - (mask_x + mask_attr->x_offset));
mask_tmp.y = (dst_y - (mask_y + mask_attr->y_offset));
mask_tmp.width = mask_width;
mask_tmp.height = mask_height;
 
mask_rectangle = &mask_tmp;
}
 
return _cairo_surface_composite_fixup_unbounded_internal (dst, src_rectangle, mask_rectangle,
dst_x, dst_y, width, height,
clip_region);
}
 
/**
* _cairo_surface_composite_shape_fixup_unbounded:
* @dst: the destination surface
* @src_attr: source surface attributes (from _cairo_pattern_acquire_surface())
* @src_width: width of source surface
* @src_height: height of source surface
* @mask_width: width of mask surface
* @mask_height: height of mask surface
* @src_x: @src_x from _cairo_surface_composite()
* @src_y: @src_y from _cairo_surface_composite()
* @mask_x: @mask_x from _cairo_surface_composite()
* @mask_y: @mask_y from _cairo_surface_composite()
* @dst_x: @dst_x from _cairo_surface_composite()
* @dst_y: @dst_y from _cairo_surface_composite()
* @width: @width from _cairo_surface_composite()
* @height: @height_x from _cairo_surface_composite()
*
* Like _cairo_surface_composite_fixup_unbounded(), but instead of
* handling the case where we have a source pattern and a mask
* pattern, handle the case where we are compositing a source pattern
* using a mask we create ourselves, as in
* _cairo_surface_composite_glyphs() or _cairo_surface_composite_trapezoids()
**/
cairo_status_t
_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst,
cairo_surface_attributes_t *src_attr,
int src_width,
int src_height,
int mask_width,
int mask_height,
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_rectangle_int_t src_tmp, *src= NULL;
cairo_rectangle_int_t mask;
 
if (dst->status)
return dst->status;
 
assert (_cairo_surface_is_writable (dst));
 
/* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
* non-repeating sources and masks. Other sources and masks can be ignored.
*/
if (_cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) &&
src_attr->extend == CAIRO_EXTEND_NONE)
{
src_tmp.x = (dst_x - (src_x + src_attr->x_offset));
src_tmp.y = (dst_y - (src_y + src_attr->y_offset));
src_tmp.width = src_width;
src_tmp.height = src_height;
 
src = &src_tmp;
}
 
mask.x = dst_x - mask_x;
mask.y = dst_y - mask_y;
mask.width = mask_width;
mask.height = mask_height;
 
return _cairo_surface_composite_fixup_unbounded_internal (dst, src, &mask,
dst_x, dst_y, width, height,
clip_region);
}
 
/**
* _cairo_surface_set_resolution
* _cairo_surface_set_resolution:
* @surface: the surface
* @x_res: x resolution, in dpi
* @y_res: y resolution, in dpi
2942,7 → 2575,7
* Set the actual surface resolution of @surface to the given x and y DPI.
* Mainly used for correctly computing the scale factor when fallback
* rendering needs to take place in the paginated surface.
*/
**/
void
_cairo_surface_set_resolution (cairo_surface_t *surface,
double x_res,
2955,171 → 2588,10
surface->y_resolution = y_res;
}
 
/* Generic methods for determining operation extents. */
 
static void
_rectangle_intersect_clip (cairo_rectangle_int_t *extents, cairo_clip_t *clip)
{
const cairo_rectangle_int_t *clip_extents;
cairo_bool_t is_empty;
 
clip_extents = NULL;
if (clip != NULL)
clip_extents = _cairo_clip_get_extents (clip);
 
if (clip_extents != NULL)
is_empty = _cairo_rectangle_intersect (extents, clip_extents);
}
 
static void
_cairo_surface_operation_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_bool_t is_empty;
 
is_empty = _cairo_surface_get_extents (surface, extents);
 
if (_cairo_operator_bounded_by_source (op)) {
cairo_rectangle_int_t source_extents;
 
_cairo_pattern_get_extents (source, &source_extents);
is_empty = _cairo_rectangle_intersect (extents, &source_extents);
}
 
_rectangle_intersect_clip (extents, clip);
}
 
cairo_status_t
_cairo_surface_paint_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
_cairo_surface_operation_extents (surface, op, source, clip, extents);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_surface_mask_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_bool_t is_empty;
 
_cairo_surface_operation_extents (surface, op, source, clip, extents);
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t mask_extents;
 
_cairo_pattern_get_extents (mask, &mask_extents);
is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_surface_stroke_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_status_t status;
cairo_bool_t is_empty;
 
_cairo_surface_operation_extents (surface, op, source, clip, extents);
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t mask_extents;
 
status = _cairo_path_fixed_stroke_extents (path, style,
ctm, ctm_inverse,
tolerance,
&mask_extents);
if (unlikely (status))
return status;
 
is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_surface_fill_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_bool_t is_empty;
 
_cairo_surface_operation_extents (surface, op, source, clip, extents);
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t mask_extents;
 
_cairo_path_fixed_fill_extents (path, fill_rule, tolerance,
&mask_extents);
is_empty = _cairo_rectangle_intersect (extents, &mask_extents);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_status_t
_cairo_surface_glyphs_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents)
{
cairo_status_t status;
cairo_bool_t is_empty;
 
_cairo_surface_operation_extents (surface, op, source, clip, extents);
 
if (_cairo_operator_bounded_by_mask (op)) {
cairo_rectangle_int_t glyph_extents;
 
status = _cairo_scaled_font_glyph_device_extents (scaled_font,
glyphs,
num_glyphs,
&glyph_extents,
NULL);
if (unlikely (status))
return status;
 
is_empty = _cairo_rectangle_intersect (extents, &glyph_extents);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_surface_t *
_cairo_surface_create_in_error (cairo_status_t status)
{
assert (status < CAIRO_STATUS_LAST_STATUS);
switch (status) {
case CAIRO_STATUS_NO_MEMORY:
return (cairo_surface_t *) &_cairo_surface_nil;
3174,6 → 2646,8
case CAIRO_STATUS_INVALID_SLANT:
case CAIRO_STATUS_INVALID_WEIGHT:
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
case CAIRO_STATUS_DEVICE_FINISHED:
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t *) &_cairo_surface_nil;
3180,5 → 2654,22
}
}
 
cairo_surface_t *
_cairo_int_surface_create_in_error (cairo_int_status_t status)
{
if (status < CAIRO_INT_STATUS_LAST_STATUS)
return _cairo_surface_create_in_error (status);
 
switch ((int)status) {
case CAIRO_INT_STATUS_UNSUPPORTED:
return (cairo_surface_t *) &_cairo_surface_nil_unsupported;
case CAIRO_INT_STATUS_NOTHING_TO_DO:
return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do;
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t *) &_cairo_surface_nil;
}
}
 
/* LocalWords: rasterized
*/
/programs/develop/libraries/cairo/src/cairo-svg-surface.c
41,16 → 41,22
 
#define _BSD_SOURCE /* for snprintf() */
#include "cairoint.h"
 
#include "cairo-svg.h"
 
#include "cairo-array-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-info-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-output-stream-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-paginated-private.h"
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-surface-clipper-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-svg-surface-private.h"
 
/**
61,7 → 67,7
*
* The SVG surface is used to render cairo graphics to
* SVG files and is a multi-page vector surface backend.
*/
**/
 
/**
* CAIRO_HAS_SVG_SURFACE:
68,7 → 74,9
*
* Defined if the SVG surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*/
*
* Since: 1.2
**/
 
typedef struct cairo_svg_page cairo_svg_page_t;
 
82,9 → 90,17
 
#define CAIRO_SVG_VERSION_LAST ARRAY_LENGTH (_cairo_svg_versions)
 
static const char *_cairo_svg_supported_mime_types[] =
{
CAIRO_MIME_TYPE_JPEG,
CAIRO_MIME_TYPE_PNG,
CAIRO_MIME_TYPE_URI,
NULL
};
 
static void
_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm_inverse);
 
static cairo_bool_t
192,7 → 208,7
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.2
*/
**/
cairo_surface_t *
cairo_svg_surface_create_for_stream (cairo_write_func_t write_func,
void *closure,
561,10 → 577,10
static cairo_svg_page_t *
_cairo_svg_surface_store_page (cairo_svg_surface_t *surface)
{
unsigned int i;
cairo_svg_page_t page;
cairo_output_stream_t *stream;
cairo_status_t status;
cairo_int_status_t status;
unsigned int i;
 
stream = _cairo_memory_stream_create ();
if (_cairo_output_stream_get_status (stream)) {
714,7 → 730,7
 
static void
_cairo_svg_surface_emit_path (cairo_output_stream_t *output,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm_inverse)
{
cairo_status_t status;
725,7 → 741,6
info.output = output;
info.ctm_inverse = ctm_inverse;
status = _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cairo_svg_path_move_to,
_cairo_svg_path_line_to,
_cairo_svg_path_curve_to,
814,7 → 829,7
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
static cairo_int_status_t
_cairo_svg_document_emit_glyph (cairo_svg_document_t *document,
cairo_scaled_font_t *scaled_font,
unsigned long scaled_font_glyph_index,
821,7 → 836,7
unsigned int font_id,
unsigned int subset_glyph_index)
{
cairo_status_t status;
cairo_int_status_t status;
 
_cairo_output_stream_printf (document->xml_node_glyphs,
"<symbol overflow=\"visible\" id=\"glyph%d-%d\">\n",
840,16 → 855,16
 
_cairo_output_stream_printf (document->xml_node_glyphs, "</symbol>\n");
 
return CAIRO_STATUS_SUCCESS;
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_status_t
static cairo_int_status_t
_cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t *font_subset,
void *closure)
{
cairo_svg_document_t *document = closure;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
unsigned int i;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
_cairo_scaled_font_freeze_cache (font_subset->scaled_font);
for (i = 0; i < font_subset->num_glyphs; i++) {
935,6 → 950,9
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (pattern->type == CAIRO_PATTERN_TYPE_MESH)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* SVG doesn't support extend reflect for image pattern */
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
pattern->extend == CAIRO_EXTEND_REFLECT)
1160,7 → 1178,7
_cairo_surface_base64_encode (cairo_surface_t *surface,
cairo_output_stream_t *output)
{
cairo_status_t status;
cairo_int_status_t status;
base64_write_closure_t info;
 
status = _cairo_surface_base64_encode_jpeg (surface, output);
1384,7 → 1402,7
}
 
paginated_surface = _cairo_svg_surface_create_for_document (document,
source->content,
source->base.content,
source->extents_pixels.width,
source->extents_pixels.height);
if (unlikely (paginated_surface->status))
1423,7 → 1441,7
svg_surface->height);
}
 
if (source->content == CAIRO_CONTENT_ALPHA) {
if (source->base.content == CAIRO_CONTENT_ALPHA) {
_cairo_svg_surface_emit_alpha_filter (document);
_cairo_output_stream_printf (document->xml_node_defs,
"<g id=\"surface%d\" "
1470,6 → 1488,17
document, NULL);
}
 
static cairo_recording_surface_t *
to_recording_surface (const cairo_surface_pattern_t *pattern)
{
cairo_surface_t *surface = pattern->surface;
if (_cairo_surface_is_paginated (surface))
surface = _cairo_paginated_surface_get_recording (surface);
if (_cairo_surface_is_snapshot (surface))
surface = _cairo_surface_snapshot_get_target (surface);
return (cairo_recording_surface_t *) surface;
}
 
static cairo_status_t
_cairo_svg_surface_emit_composite_recording_pattern (cairo_output_stream_t *output,
cairo_svg_surface_t *surface,
1489,7 → 1518,7
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
recording_surface = (cairo_recording_surface_t *) pattern->surface;
recording_surface = to_recording_surface (pattern);
status = _cairo_svg_surface_emit_recording_surface (document, recording_surface);
if (unlikely (status))
return status;
1536,7 → 1565,7
const char *extra_attributes)
{
 
if (_cairo_surface_is_recording (pattern->surface)) {
if (pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
return _cairo_svg_surface_emit_composite_recording_pattern (output, surface,
op, pattern,
pattern_id,
1782,7 → 1811,6
const cairo_matrix_t *parent_matrix)
{
cairo_svg_document_t *document = surface->document;
double x0, y0, x1, y1;
cairo_matrix_t p2u;
cairo_status_t status;
 
1791,17 → 1819,13
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
x0 = _cairo_fixed_to_double (pattern->p1.x);
y0 = _cairo_fixed_to_double (pattern->p1.y);
x1 = _cairo_fixed_to_double (pattern->p2.x);
y1 = _cairo_fixed_to_double (pattern->p2.y);
 
_cairo_output_stream_printf (document->xml_node_defs,
"<linearGradient id=\"linear%d\" "
"gradientUnits=\"userSpaceOnUse\" "
"x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
document->linear_pattern_id,
x0, y0, x1, y1);
pattern->pd1.x, pattern->pd1.y,
pattern->pd2.x, pattern->pd2.y);
 
_cairo_svg_surface_emit_pattern_extend (document->xml_node_defs, &pattern->base.base),
_cairo_svg_surface_emit_transform (document->xml_node_defs, "gradientTransform", &p2u, parent_matrix);
1840,31 → 1864,26
double fx, fy;
cairo_bool_t reverse_stops;
cairo_status_t status;
cairo_point_t *c0, *c1;
cairo_fixed_t radius0, radius1;
cairo_circle_double_t *c0, *c1;
 
extend = pattern->base.base.extend;
 
if (pattern->r1 < pattern->r2) {
c0 = &pattern->c1;
c1 = &pattern->c2;
radius0 = pattern->r1;
radius1 = pattern->r2;
if (pattern->cd1.radius < pattern->cd2.radius) {
c0 = &pattern->cd1;
c1 = &pattern->cd2;
reverse_stops = FALSE;
} else {
c0 = &pattern->c2;
c1 = &pattern->c1;
radius0 = pattern->r2;
radius1 = pattern->r1;
c0 = &pattern->cd2;
c1 = &pattern->cd1;
reverse_stops = TRUE;
}
 
x0 = _cairo_fixed_to_double (c0->x);
y0 = _cairo_fixed_to_double (c0->y);
r0 = _cairo_fixed_to_double (radius0);
x1 = _cairo_fixed_to_double (c1->x);
y1 = _cairo_fixed_to_double (c1->y);
r1 = _cairo_fixed_to_double (radius1);
x0 = c0->center.x;
y0 = c0->center.y;
r0 = c0->radius;
x1 = c1->center.x;
y1 = c1->center.y;
r1 = c1->radius;
 
p2u = pattern->base.base.matrix;
status = cairo_matrix_invert (&p2u);
1871,7 → 1890,7
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
if (pattern->r1 == pattern->r2) {
if (r0 == r1) {
unsigned int n_stops = pattern->base.n_stops;
 
_cairo_output_stream_printf (document->xml_node_defs,
2036,6 → 2055,10
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_svg_surface_emit_radial_pattern (surface, (cairo_radial_pattern_t *) pattern,
output, is_stroke, parent_matrix);
 
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
ASSERT_NOT_REACHED;
}
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
}
2141,7 → 2164,7
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
2149,7 → 2172,7
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_status_t status;
2183,11 → 2206,11
_cairo_svg_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_status_t status;
2278,7 → 2301,7
_cairo_svg_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_svg_surface_t *surface = abstract_surface;
2344,7 → 2367,7
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_status_t status;
cairo_svg_surface_t *surface = abstract_surface;
2382,7 → 2405,7
 
if (mask->type == CAIRO_PATTERN_TYPE_SURFACE) {
const cairo_surface_pattern_t *surface_pattern = (const cairo_surface_pattern_t*) mask;
cairo_content_t content = cairo_surface_get_content (surface_pattern->surface);
cairo_content_t content = surface_pattern->surface->content;
if (content == CAIRO_CONTENT_ALPHA)
discard_filter = TRUE;
}
2434,13 → 2457,13
_cairo_svg_surface_stroke (void *abstract_dst,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t*path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_dst;
cairo_status_t status;
2477,13 → 2500,12
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
const cairo_clip_t *clip)
{
cairo_svg_surface_t *surface = abstract_surface;
cairo_svg_document_t *document = surface->document;
cairo_path_fixed_t path;
cairo_status_t status;
cairo_int_status_t status;
cairo_scaled_font_subsets_glyph_t subset_glyph;
int i;
 
2574,39 → 2596,50
cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
}
 
 
static const char **
_cairo_svg_surface_get_supported_mime_types (void *abstract_surface)
{
return _cairo_svg_supported_mime_types;
}
 
static const cairo_surface_backend_t cairo_svg_surface_backend = {
CAIRO_SURFACE_TYPE_SVG,
_cairo_svg_surface_finish,
 
_cairo_default_context_create,
 
NULL, /* create_similar: handled by wrapper */
_cairo_svg_surface_finish,
NULL, /* create_similar_image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* _cairo_svg_surface_composite, */
NULL, /* _cairo_svg_surface_fill_rectangles, */
NULL, /* _cairo_svg_surface_composite_trapezoids,*/
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* snapshot */
 
_cairo_svg_surface_copy_page,
_cairo_svg_surface_show_page,
 
_cairo_svg_surface_get_extents,
NULL, /* _cairo_svg_surface_old_show_glyphs, */
_cairo_svg_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark dirty rectangle */
NULL, /* scaled font fini */
NULL, /* scaled glyph fini */
 
_cairo_svg_surface_paint,
_cairo_svg_surface_mask,
_cairo_svg_surface_stroke,
_cairo_svg_surface_fill,
_cairo_svg_surface_fill_stroke,
_cairo_svg_surface_show_glyphs,
NULL, /* snapshot */
NULL, /* is_similar */
_cairo_svg_surface_fill_stroke
NULL, /* has_show_text_glyphs */
NULL, /* show_text_glyphs */
_cairo_svg_surface_get_supported_mime_types,
};
 
static cairo_status_t
2835,7 → 2868,7
CAIRO_OPERATOR_SOURCE);
}
 
return status == CAIRO_STATUS_SUCCESS;
return status == CAIRO_INT_STATUS_SUCCESS;
}
 
static const cairo_paginated_surface_backend_t cairo_svg_surface_paginated_backend = {
/programs/develop/libraries/cairo/src/cairo-svg.h
40,12 → 40,14
 
/**
* cairo_svg_version_t:
* @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification.
* @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification.
* @CAIRO_SVG_VERSION_1_1: The version 1.1 of the SVG specification. (Since 1.2)
* @CAIRO_SVG_VERSION_1_2: The version 1.2 of the SVG specification. (Since 1.2)
*
* #cairo_svg_version_t is used to describe the version number of the SVG
* specification that a generated SVG file will conform to.
*/
*
* Since: 1.2
**/
typedef enum _cairo_svg_version {
CAIRO_SVG_VERSION_1_1,
CAIRO_SVG_VERSION_1_2
/programs/develop/libraries/cairo/src/cairo-tee-surface.c
0,0 → 1,602
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Red Hat, Inc
* Copyright © 2009 Chris Wilson
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* This surface supports redirecting all its input to multiple surfaces.
*/
 
#include "cairoint.h"
 
#include "cairo-tee.h"
 
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-tee-surface-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-wrapper-private.h"
#include "cairo-array-private.h"
#include "cairo-image-surface-inline.h"
 
typedef struct _cairo_tee_surface {
cairo_surface_t base;
 
cairo_surface_wrapper_t master;
cairo_array_t slaves;
} cairo_tee_surface_t;
 
slim_hidden_proto (cairo_tee_surface_create);
slim_hidden_proto (cairo_tee_surface_add);
 
static cairo_surface_t *
_cairo_tee_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
 
cairo_tee_surface_t *other = abstract_surface;
cairo_surface_t *similar;
cairo_surface_t *surface;
cairo_surface_wrapper_t *slaves;
int n, num_slaves;
 
similar = _cairo_surface_wrapper_create_similar (&other->master,
content, width, height);
surface = cairo_tee_surface_create (similar);
cairo_surface_destroy (similar);
if (unlikely (surface->status))
return surface;
 
num_slaves = _cairo_array_num_elements (&other->slaves);
slaves = _cairo_array_index (&other->slaves, 0);
for (n = 0; n < num_slaves; n++) {
 
similar = _cairo_surface_wrapper_create_similar (&slaves[n],
content,
width, height);
cairo_tee_surface_add (surface, similar);
cairo_surface_destroy (similar);
}
 
if (unlikely (surface->status)) {
cairo_status_t status = surface->status;
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
 
return surface;
}
 
static cairo_status_t
_cairo_tee_surface_finish (void *abstract_surface)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
int n, num_slaves;
 
_cairo_surface_wrapper_fini (&surface->master);
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++)
_cairo_surface_wrapper_fini (&slaves[n]);
 
_cairo_array_fini (&surface->slaves);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_tee_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_tee_surface_t *surface = abstract_surface;
return _cairo_surface_get_source (surface->master.target, extents);
}
 
static cairo_status_t
_cairo_tee_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
int num_slaves, n;
 
/* we prefer to use a real image surface if available */
if (_cairo_surface_is_image (surface->master.target)) {
return _cairo_surface_wrapper_acquire_source_image (&surface->master,
image_out, image_extra);
}
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
if (_cairo_surface_is_image (slaves[n].target)) {
return _cairo_surface_wrapper_acquire_source_image (&slaves[n],
image_out,
image_extra);
}
}
 
return _cairo_surface_wrapper_acquire_source_image (&surface->master,
image_out, image_extra);
}
 
static void
_cairo_tee_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_tee_surface_t *surface = abstract_surface;
 
_cairo_surface_wrapper_release_source_image (&surface->master,
image, image_extra);
}
 
static cairo_surface_t *
_cairo_tee_surface_snapshot (void *abstract_surface)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
int num_slaves, n;
 
/* we prefer to use a recording surface for our snapshots */
if (_cairo_surface_is_recording (surface->master.target))
return _cairo_surface_wrapper_snapshot (&surface->master);
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
if (_cairo_surface_is_recording (slaves[n].target))
return _cairo_surface_wrapper_snapshot (&slaves[n]);
}
 
return _cairo_surface_wrapper_snapshot (&surface->master);
}
 
static cairo_bool_t
_cairo_tee_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_tee_surface_t *surface = abstract_surface;
 
return _cairo_surface_wrapper_get_extents (&surface->master, rectangle);
}
 
static void
_cairo_tee_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
cairo_tee_surface_t *surface = abstract_surface;
 
_cairo_surface_wrapper_get_font_options (&surface->master, options);
}
 
static cairo_int_status_t
_cairo_tee_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
int n, num_slaves;
cairo_int_status_t status;
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
status = _cairo_surface_wrapper_paint (&slaves[n], op, source, clip);
if (unlikely (status))
return status;
}
 
return _cairo_surface_wrapper_paint (&surface->master, op, source, clip);
}
 
static cairo_int_status_t
_cairo_tee_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
cairo_int_status_t status;
int n, num_slaves;
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
status = _cairo_surface_wrapper_mask (&slaves[n],
op, source, mask, clip);
if (unlikely (status))
return status;
}
 
return _cairo_surface_wrapper_mask (&surface->master,
op, source, mask, clip);
}
 
static cairo_int_status_t
_cairo_tee_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
cairo_int_status_t status;
int n, num_slaves;
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
status = _cairo_surface_wrapper_stroke (&slaves[n],
op, source,
path, style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
if (unlikely (status))
return status;
}
 
return _cairo_surface_wrapper_stroke (&surface->master,
op, source,
path, style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_tee_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
cairo_int_status_t status;
int n, num_slaves;
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
status = _cairo_surface_wrapper_fill (&slaves[n],
op, source,
path, fill_rule,
tolerance, antialias,
clip);
if (unlikely (status))
return status;
}
 
return _cairo_surface_wrapper_fill (&surface->master,
op, source,
path, fill_rule,
tolerance, antialias,
clip);
}
 
static cairo_bool_t
_cairo_tee_surface_has_show_text_glyphs (void *abstract_surface)
{
return TRUE;
}
 
static cairo_int_status_t
_cairo_tee_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
cairo_int_status_t status;
int n, num_slaves;
cairo_glyph_t *glyphs_copy;
 
/* XXX: This copying is ugly. */
glyphs_copy = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
if (unlikely (glyphs_copy == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
status = _cairo_surface_wrapper_show_text_glyphs (&slaves[n], op,
source,
utf8, utf8_len,
glyphs_copy, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font,
clip);
if (unlikely (status))
goto CLEANUP;
}
 
memcpy (glyphs_copy, glyphs, sizeof (cairo_glyph_t) * num_glyphs);
status = _cairo_surface_wrapper_show_text_glyphs (&surface->master, op,
source,
utf8, utf8_len,
glyphs_copy, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font,
clip);
CLEANUP:
free (glyphs_copy);
return status;
}
 
static const cairo_surface_backend_t cairo_tee_surface_backend = {
CAIRO_SURFACE_TYPE_TEE,
_cairo_tee_surface_finish,
 
_cairo_default_context_create, /* XXX */
 
_cairo_tee_surface_create_similar,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_tee_surface_source,
_cairo_tee_surface_acquire_source_image,
_cairo_tee_surface_release_source_image,
_cairo_tee_surface_snapshot,
NULL, /* copy_page */
NULL, /* show_page */
_cairo_tee_surface_get_extents,
_cairo_tee_surface_get_font_options,
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
_cairo_tee_surface_paint,
_cairo_tee_surface_mask,
_cairo_tee_surface_stroke,
_cairo_tee_surface_fill,
NULL, /* fill_stroke */
 
NULL, /* show_glyphs */
 
_cairo_tee_surface_has_show_text_glyphs,
_cairo_tee_surface_show_text_glyphs
};
 
cairo_surface_t *
cairo_tee_surface_create (cairo_surface_t *master)
{
cairo_tee_surface_t *surface;
 
if (unlikely (master->status))
return _cairo_surface_create_in_error (master->status);
 
surface = malloc (sizeof (cairo_tee_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&cairo_tee_surface_backend,
master->device,
master->content);
 
_cairo_surface_wrapper_init (&surface->master, master);
 
_cairo_array_init (&surface->slaves, sizeof (cairo_surface_wrapper_t));
 
return &surface->base;
}
slim_hidden_def (cairo_tee_surface_create);
 
void
cairo_tee_surface_add (cairo_surface_t *abstract_surface,
cairo_surface_t *target)
{
cairo_tee_surface_t *surface;
cairo_surface_wrapper_t slave;
cairo_status_t status;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (abstract_surface->backend != &cairo_tee_surface_backend) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (unlikely (target->status)) {
status = _cairo_surface_set_error (abstract_surface, target->status);
return;
}
 
surface = (cairo_tee_surface_t *) abstract_surface;
 
_cairo_surface_wrapper_init (&slave, target);
status = _cairo_array_append (&surface->slaves, &slave);
if (unlikely (status)) {
_cairo_surface_wrapper_fini (&slave);
status = _cairo_surface_set_error (&surface->base, status);
}
}
slim_hidden_def (cairo_tee_surface_add);
 
void
cairo_tee_surface_remove (cairo_surface_t *abstract_surface,
cairo_surface_t *target)
{
cairo_tee_surface_t *surface;
cairo_surface_wrapper_t *slaves;
int n, num_slaves;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (abstract_surface->backend != &cairo_tee_surface_backend) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
surface = (cairo_tee_surface_t *) abstract_surface;
if (target == surface->master.target) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_INDEX));
return;
}
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
if (slaves[n].target == target)
break;
}
 
if (n == num_slaves) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_INDEX));
return;
}
 
_cairo_surface_wrapper_fini (&slaves[n]);
for (n++; n < num_slaves; n++)
slaves[n-1] = slaves[n];
surface->slaves.num_elements--; /* XXX: cairo_array_remove()? */
}
 
cairo_surface_t *
cairo_tee_surface_index (cairo_surface_t *abstract_surface,
unsigned int index)
{
cairo_tee_surface_t *surface;
 
if (unlikely (abstract_surface->status))
return _cairo_surface_create_in_error (abstract_surface->status);
if (unlikely (abstract_surface->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
 
if (abstract_surface->backend != &cairo_tee_surface_backend)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
surface = (cairo_tee_surface_t *) abstract_surface;
if (index == 0) {
return surface->master.target;
} else {
cairo_surface_wrapper_t *slave;
 
index--;
 
if (index >= _cairo_array_num_elements (&surface->slaves))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX));
 
slave = _cairo_array_index (&surface->slaves, index);
return slave->target;
}
}
 
cairo_surface_t *
_cairo_tee_surface_find_match (void *abstract_surface,
const cairo_surface_backend_t *backend,
cairo_content_t content)
{
cairo_tee_surface_t *surface = abstract_surface;
cairo_surface_wrapper_t *slaves;
int num_slaves, n;
 
/* exact match first */
if (surface->master.target->backend == backend &&
surface->master.target->content == content)
{
return surface->master.target;
}
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
if (slaves[n].target->backend == backend &&
slaves[n].target->content == content)
{
return slaves[n].target;
}
}
 
/* matching backend? */
if (surface->master.target->backend == backend)
return surface->master.target;
 
num_slaves = _cairo_array_num_elements (&surface->slaves);
slaves = _cairo_array_index (&surface->slaves, 0);
for (n = 0; n < num_slaves; n++) {
if (slaves[n].target->backend == backend)
return slaves[n].target;
}
 
return NULL;
}
/programs/develop/libraries/cairo/src/cairo-tee.h
55,7 → 55,7
 
cairo_public cairo_surface_t *
cairo_tee_surface_index (cairo_surface_t *surface,
int index);
unsigned int index);
 
CAIRO_END_DECLS
 
/programs/develop/libraries/cairo/src/cairo-time-private.h
0,0 → 1,94
/* cairo - a vector graphics library with display and print output
*
* Copyright (C) 2011 Andrea Canciani
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of the
* copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*
* Authors: Andrea Canciani <ranma42@gmail.com>
*
*/
 
#ifndef CAIRO_TIME_PRIVATE_H
#define CAIRO_TIME_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-wideint-private.h"
 
/* Make the base type signed for easier arithmetic */
typedef cairo_int64_t cairo_time_t;
 
#define _cairo_time_add _cairo_int64_add
#define _cairo_time_sub _cairo_int64_sub
#define _cairo_time_gt _cairo_int64_gt
#define _cairo_time_lt _cairo_int64_lt
 
#define _cairo_time_to_double _cairo_int64_to_double
#define _cairo_time_from_double _cairo_double_to_int64
 
cairo_private int
_cairo_time_cmp (const void *a,
const void *b);
 
cairo_private double
_cairo_time_to_s (cairo_time_t t);
 
cairo_private cairo_time_t
_cairo_time_from_s (double t);
 
cairo_private cairo_time_t
_cairo_time_get (void);
 
static cairo_always_inline cairo_time_t
_cairo_time_get_delta (cairo_time_t t)
{
cairo_time_t now;
 
now = _cairo_time_get ();
 
return _cairo_time_sub (now, t);
}
 
static cairo_always_inline double
_cairo_time_to_ns (cairo_time_t t)
{
return 1.e9 * _cairo_time_to_s (t);
}
 
static cairo_always_inline cairo_time_t
_cairo_time_max (cairo_time_t a, cairo_time_t b)
{
if (_cairo_int64_gt (a, b))
return a;
else
return b;
}
 
static cairo_always_inline cairo_time_t
_cairo_time_min (cairo_time_t a, cairo_time_t b)
{
if (_cairo_int64_lt (a, b))
return a;
else
return b;
}
 
#endif
/programs/develop/libraries/cairo/src/cairo-time.c
0,0 → 1,225
/* cairo - a vector graphics library with display and print output
*
* Copyright (c) 2007 Netlabs
* Copyright (c) 2006 Mozilla Corporation
* Copyright (c) 2006 Red Hat, Inc.
* Copyright (c) 2011 Andrea Canciani
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* the authors not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. The authors make no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors: Peter Weilbacher <mozilla@weilbacher.org>
* Vladimir Vukicevic <vladimir@pobox.com>
* Carl Worth <cworth@cworth.org>
* Andrea Canciani <ranma42@gmail.com>
*/
 
#include "cairoint.h"
 
#include "cairo-time-private.h"
 
#if HAVE_CLOCK_GETTIME
#if defined(CLOCK_MONOTONIC_RAW)
#define CAIRO_CLOCK CLOCK_MONOTONIC_RAW
#elif defined(CLOCK_MONOTONIC)
#define CAIRO_CLOCK CLOCK_MONOTONIC
#endif
#endif
 
#if defined(__APPLE__)
#include <mach/mach_time.h>
 
static cairo_always_inline double
_cairo_time_1s (void)
{
mach_timebase_info_data_t freq;
 
mach_timebase_info (&freq);
 
return 1000000000. * freq.denom / freq.numer;
}
 
cairo_time_t
_cairo_time_get (void)
{
return mach_absolute_time ();
}
 
#elif defined(__OS2__)
#define INCL_BASE
#include <os2.h>
 
static cairo_always_inline double
_cairo_time_1s (void)
{
ULONG freq;
 
DosTmrQueryFreq (&freq);
 
return freq;
}
 
cairo_time_t
_cairo_time_get (void)
{
QWORD t;
cairo_int64_t r;
 
DosTmrQueryTime (&t);
 
r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32);
r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo));
 
return r;
}
 
#elif _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
 
static cairo_always_inline double
_cairo_time_1s (void)
{
LARGE_INTEGER freq;
 
QueryPerformanceFrequency (&freq);
 
return freq.QuadPart;
}
 
#ifndef HAVE_UINT64_T
static cairo_always_inline cairo_time_t
_cairo_time_from_large_integer (LARGE_INTEGER t)
{
cairo_int64_t r;
 
r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.HighPart), 32);
r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.LowPart));
 
return r;
}
#else
static cairo_always_inline cairo_time_t
_cairo_time_from_large_integer (LARGE_INTEGER t)
{
return t.QuadPart;
}
#endif
 
cairo_time_t
_cairo_time_get (void)
{
LARGE_INTEGER t;
 
QueryPerformanceCounter (&t);
 
return _cairo_time_from_large_integer(t);
}
 
#elif defined(CAIRO_CLOCK)
#include <time.h>
 
static cairo_always_inline double
_cairo_time_1s (void)
{
return 1000000000;
}
 
cairo_time_t
_cairo_time_get (void)
{
struct timespec t;
cairo_time_t r;
 
clock_gettime (CAIRO_CLOCK, &t);
 
r = _cairo_double_to_int64 (_cairo_time_1s ());
r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_nsec));
 
return r;
}
 
#else
#include <sys/time.h>
 
static cairo_always_inline double
_cairo_time_1s (void)
{
return 1000000;
}
 
cairo_time_t
_cairo_time_get (void)
{
struct timeval t;
cairo_time_t r;
 
gettimeofday (&t, NULL);
 
r = _cairo_double_to_int64 (_cairo_time_1s ());
r = _cairo_int64_mul (r, _cairo_int32_to_int64 (t.tv_sec));
r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.tv_usec));
 
return r;
}
 
#endif
 
int
_cairo_time_cmp (const void *a,
const void *b)
{
const cairo_time_t *ta = a, *tb = b;
return _cairo_int64_cmp (*ta, *tb);
}
 
static double
_cairo_time_ticks_per_sec (void)
{
static double ticks = 0;
 
if (unlikely (ticks == 0))
ticks = _cairo_time_1s ();
 
return ticks;
}
 
static double
_cairo_time_s_per_tick (void)
{
static double s = 0;
 
if (unlikely (s == 0))
s = 1. / _cairo_time_ticks_per_sec ();
 
return s;
}
 
double
_cairo_time_to_s (cairo_time_t t)
{
return _cairo_int64_to_double (t) * _cairo_time_s_per_tick ();
}
 
cairo_time_t
_cairo_time_from_s (double t)
{
return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ());
}
/programs/develop/libraries/cairo/src/cairo-tor-scan-converter.c
97,10 → 97,10
#include "cairo-spans-private.h"
#include "cairo-error-private.h"
 
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <setjmp.h>
 
/*-------------------------------------------------------------------------
* cairo specific config
123,39 → 123,6
struct pool;
struct cell_list;
 
static glitter_status_t
blit_with_span_renderer(
struct cell_list *coverages,
cairo_span_renderer_t *span_renderer,
struct pool *span_pool,
int y,
int height,
int xmin,
int xmax);
 
static glitter_status_t
blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
 
#define GLITTER_BLIT_COVERAGES_ARGS \
cairo_span_renderer_t *span_renderer, \
struct pool *span_pool
 
#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do { \
cairo_status_t status = blit_with_span_renderer (cells, \
span_renderer, \
span_pool, \
y, height, \
xmin, xmax); \
if (unlikely (status)) \
return status; \
} while (0)
 
#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do { \
cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \
if (unlikely (status)) \
return status; \
} while (0)
 
/*-------------------------------------------------------------------------
* glitter-paths.h
*/
196,15 → 163,6
int xmin, int ymin,
int xmax, int ymax);
 
/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
* converter. The coordinates represent pixel positions scaled by
* 2**GLITTER_PIXEL_BITS. If this function fails then the scan
* converter should be reset or destroyed. Dir must be +1 or -1,
* with the latter reversing the orientation of the edge. */
I glitter_status_t
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge);
 
/* Render the polygon in the scan converter to the given A8 format
* image raster. Only the pixels accessible as pixels[y*stride+x] for
* x,y inside the clip box are written to, where xmin <= x < xmax,
215,14 → 173,6
* rule is used.
*
* The scan converter must be reset or destroyed after this call. */
#ifndef GLITTER_BLIT_COVERAGES_ARGS
# define GLITTER_BLIT_COVERAGES_ARGS unsigned char *raster_pixels, long raster_stride
#endif
I glitter_status_t
glitter_scan_converter_render(
glitter_scan_converter_t *converter,
int nonzero_fill,
GLITTER_BLIT_COVERAGES_ARGS);
 
/*-------------------------------------------------------------------------
* glitter-paths.c: Implementation internal types
287,7 → 237,6
* vertices are given in grid scaled coordinates. The scale factor
* comes from needing to accurately represent the area 0.5*dx*dy of a
* triangle with base dx and height dy in grid scaled numbers. */
typedef int grid_area_t;
#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
 
/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
339,6 → 288,8
/* Chunk we're allocating from. */
struct _pool_chunk *current;
 
jmp_buf *jmp;
 
/* Free list of previously allocated chunks. All have >= default
* capacity. */
struct _pool_chunk *first_free;
355,8 → 306,17
/* A polygon edge. */
struct edge {
/* Next in y-bucket or active list. */
struct edge *next;
struct edge *next, *prev;
 
/* Number of subsample rows remaining to scan convert of this
* edge. */
grid_scaled_y_t height_left;
 
/* Original sign of the edge: +1 for downwards, -1 for upwards
* edges. */
int dir;
int vertical;
 
/* Current x coordinate while the edge is on the active
* list. Initialised to the x coordinate of the top of the
* edge. The quotient is in grid_scaled_x_t units and the
377,22 → 337,10
 
/* y2-y1 after orienting the edge downwards. */
grid_scaled_y_t dy;
 
/* Number of subsample rows remaining to scan convert of this
* edge. */
grid_scaled_y_t height_left;
 
/* Original sign of the edge: +1 for downwards, -1 for upwards
* edges. */
int dir;
int vertical;
};
 
/* Number of subsample rows per y-bucket. Must be GRID_Y. */
#define EDGE_Y_BUCKET_HEIGHT GRID_Y
#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
 
#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
 
/* A collection of sorted and vertically clipped edges of the polygon.
* Edges are moved from the polygon to an active list while scan
* converting. */
456,8 → 404,8
struct cell {
struct cell *next;
int x;
grid_area_t uncovered_area;
grid_scaled_y_t covered_height;
int16_t uncovered_area;
int16_t covered_height;
};
 
/* A cell list represents the scan line sparsely as cells ordered by
464,15 → 412,11
* ascending x. It is geared towards scanning the cells in order
* using an internal cursor. */
struct cell_list {
/* Points to the left-most cell in the scan line. */
struct cell *head;
/* Sentinel node */
struct cell tail;
/* Sentinel nodes */
struct cell head, tail;
 
/* Cursor state for iterating through the cell list. Points to
* a pointer to the current cell: either &cell_list->head or the next
* field of the previous cell. */
struct cell **cursor;
/* Cursor state for iterating through the cell list. */
struct cell *cursor, *rewind;
 
/* Cells in the cell list are owned by the cell list and are
* allocated from this pool. */
491,7 → 435,7
* the x-coordinate of the intercept of the edge and the scan line. */
struct active_list {
/* Leftmost edge on the current scan line. */
struct edge *head;
struct edge head, tail;
 
/* A lower bound on the height of the active edges is used to
* estimate how soon some active edge ends. We can't advance the
498,6 → 442,7
* scan conversion by a full pixel row if an edge ends somewhere
* within it. */
grid_scaled_y_t min_height;
int is_vertical;
};
 
struct glitter_scan_converter {
505,6 → 450,9
struct active_list active[1];
struct cell_list coverages[1];
 
cairo_half_open_span_t *spans;
cairo_half_open_span_t spans_embedded[64];
 
/* Clip box. */
grid_scaled_x_t xmin, xmax;
grid_scaled_y_t ymin, ymax;
541,7 → 489,7
return qr;
}
 
static void
static struct _pool_chunk *
_pool_chunk_init(
struct _pool_chunk *p,
struct _pool_chunk *prev_chunk,
550,29 → 498,28
p->prev_chunk = prev_chunk;
p->size = 0;
p->capacity = capacity;
return p;
}
 
static struct _pool_chunk *
_pool_chunk_create(
struct _pool_chunk *prev_chunk,
size_t size)
_pool_chunk_create(struct pool *pool, size_t size)
{
struct _pool_chunk *p;
size_t size_with_head = size + sizeof(struct _pool_chunk);
if (size_with_head < size)
return NULL;
p = malloc(size_with_head);
if (p)
_pool_chunk_init(p, prev_chunk, size);
return p;
 
p = malloc(size + sizeof(struct _pool_chunk));
if (unlikely (NULL == p))
longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
 
return _pool_chunk_init(p, pool->current, size);
}
 
static void
pool_init(
struct pool *pool,
pool_init(struct pool *pool,
jmp_buf *jmp,
size_t default_capacity,
size_t embedded_capacity)
{
pool->jmp = jmp;
pool->current = pool->sentinel;
pool->first_free = NULL;
pool->default_capacity = default_capacity;
593,7 → 540,6
p = pool->first_free;
pool->first_free = NULL;
} while (NULL != p);
pool_init(pool, 0, 0);
}
 
/* Satisfy an allocation by first allocating a new large enough chunk
623,11 → 569,8
}
}
 
if (NULL == chunk) {
chunk = _pool_chunk_create (pool->current, capacity);
if (unlikely (NULL == chunk))
return NULL;
}
if (NULL == chunk)
chunk = _pool_chunk_create (pool, capacity);
pool->current = chunk;
 
obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
681,24 → 624,32
cells->cursor = &cells->head;
}
 
/* Rewind the cell list if its cursor has been advanced past x. */
inline static void
cell_list_maybe_rewind (struct cell_list *cells, int x)
{
struct cell *tail = *cells->cursor;
if (tail->x > x)
cell_list_rewind (cells);
if (x < cells->cursor->x) {
cells->cursor = cells->rewind;
if (x < cells->cursor->x)
cells->cursor = &cells->head;
}
}
 
inline static void
cell_list_set_rewind (struct cell_list *cells)
{
cells->rewind = cells->cursor;
}
 
static void
cell_list_init(struct cell_list *cells)
cell_list_init(struct cell_list *cells, jmp_buf *jmp)
{
pool_init(cells->cell_pool.base,
pool_init(cells->cell_pool.base, jmp,
256*sizeof(struct cell),
sizeof(cells->cell_pool.embedded));
cells->tail.next = NULL;
cells->tail.x = INT_MAX;
cells->head = &cells->tail;
cells->head.x = INT_MIN;
cells->head.next = &cells->tail;
cell_list_rewind (cells);
}
 
714,13 → 665,12
cell_list_reset (struct cell_list *cells)
{
cell_list_rewind (cells);
cells->head = &cells->tail;
cells->head.next = &cells->tail;
pool_reset (cells->cell_pool.base);
}
 
static struct cell *
inline static struct cell *
cell_list_alloc (struct cell_list *cells,
struct cell **cursor,
struct cell *tail,
int x)
{
727,14 → 677,11
struct cell *cell;
 
cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
if (unlikely (NULL == cell))
return NULL;
cell->next = tail->next;
tail->next = cell;
cell->x = x;
*(uint32_t *)&cell->uncovered_area = 0;
 
*cursor = cell;
cell->next = tail;
cell->x = x;
cell->uncovered_area = 0;
cell->covered_height = 0;
return cell;
}
 
746,24 → 693,23
inline static struct cell *
cell_list_find (struct cell_list *cells, int x)
{
struct cell **cursor = cells->cursor;
struct cell *tail;
struct cell *tail = cells->cursor;
 
if (tail->x == x)
return tail;
 
while (1) {
UNROLL3({
tail = *cursor;
if (tail->x >= x) {
if (tail->next->x > x)
break;
}
cursor = &tail->next;
tail = tail->next;
});
}
cells->cursor = cursor;
 
if (tail->x == x)
return tail;
if (tail->x != x)
tail = cell_list_alloc (cells, tail, x);
return cells->cursor = tail;
 
return cell_list_alloc (cells, cursor, tail, x);
}
 
/* Find two cells at x1 and x2. This is exactly equivalent
777,94 → 723,36
cell_list_find_pair(struct cell_list *cells, int x1, int x2)
{
struct cell_pair pair;
struct cell **cursor = cells->cursor;
struct cell *cell1;
struct cell *cell2;
struct cell *newcell;
 
/* Find first cell at x1. */
pair.cell1 = cells->cursor;
while (1) {
UNROLL3({
cell1 = *cursor;
if (cell1->x > x1)
if (pair.cell1->next->x > x1)
break;
 
if (cell1->x == x1)
goto found_first;
 
cursor = &cell1->next;
pair.cell1 = pair.cell1->next;
});
}
if (pair.cell1->x != x1)
pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
 
/* New first cell at x1. */
newcell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
if (likely (NULL != newcell)) {
*cursor = newcell;
newcell->next = cell1;
newcell->x = x1;
newcell->uncovered_area = 0;
newcell->covered_height = 0;
}
cell1 = newcell;
found_first:
 
/* Find second cell at x2. */
pair.cell2 = pair.cell1;
while (1) {
UNROLL3({
cell2 = *cursor;
if (cell2->x > x2)
if (pair.cell2->next->x > x2)
break;
if (cell2->x == x2)
goto found_second;
cursor = &cell2->next;
pair.cell2 = pair.cell2->next;
});
}
if (pair.cell2->x != x2)
pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
 
/* New second cell at x2. */
newcell = pool_alloc (cells->cell_pool.base,
sizeof (struct cell));
if (likely (NULL != newcell)) {
*cursor = newcell;
newcell->next = cell2;
newcell->x = x2;
newcell->uncovered_area = 0;
newcell->covered_height = 0;
}
cell2 = newcell;
found_second:
 
cells->cursor = cursor;
pair.cell1 = cell1;
pair.cell2 = cell2;
cells->cursor = pair.cell2;
return pair;
}
 
/* Add an unbounded subpixel span covering subpixels >= x to the
* coverage cells. */
static glitter_status_t
cell_list_add_unbounded_subspan (struct cell_list *cells,
grid_scaled_x_t x)
{
struct cell *cell;
int ix, fx;
 
GRID_X_TO_INT_FRAC(x, ix, fx);
 
cell = cell_list_find (cells, ix);
if (likely (cell != NULL)) {
cell->uncovered_area += 2*fx;
cell->covered_height++;
return GLITTER_STATUS_SUCCESS;
}
 
return GLITTER_STATUS_NO_MEMORY;
}
 
/* Add a subpixel span covering [x1, x2) to the coverage cells. */
inline static glitter_status_t
cell_list_add_subspan(
struct cell_list *cells,
inline static void
cell_list_add_subspan(struct cell_list *cells,
grid_scaled_x_t x1,
grid_scaled_x_t x2)
{
871,6 → 759,9
int ix1, fx1;
int ix2, fx2;
 
if (x1 == x2)
return;
 
GRID_X_TO_INT_FRAC(x1, ix1, fx1);
GRID_X_TO_INT_FRAC(x2, ix2, fx2);
 
877,22 → 768,15
if (ix1 != ix2) {
struct cell_pair p;
p = cell_list_find_pair(cells, ix1, ix2);
if (likely (p.cell1 != NULL && p.cell2 != NULL)) {
p.cell1->uncovered_area += 2*fx1;
++p.cell1->covered_height;
p.cell2->uncovered_area -= 2*fx2;
--p.cell2->covered_height;
return GLITTER_STATUS_SUCCESS;
}
} else {
struct cell *cell = cell_list_find(cells, ix1);
if (likely (cell != NULL)) {
cell->uncovered_area += 2*(fx1-fx2);
return GLITTER_STATUS_SUCCESS;
}
}
return GLITTER_STATUS_NO_MEMORY;
}
 
/* Adds the analytical coverage of an edge crossing the current pixel
* row to the coverage cells and advances the edge's x position to the
911,9 → 795,8
* This function depends on being called with all edges from the
* active list in the order they appear on the list (i.e. with
* non-decreasing x-coordinate.) */
static glitter_status_t
cell_list_render_edge(
struct cell_list *cells,
static void
cell_list_render_edge(struct cell_list *cells,
struct edge *edge,
int sign)
{
944,12 → 827,9
/* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */
struct cell *cell = cell_list_find(cells, ix1);
if (unlikely (NULL == cell))
return GLITTER_STATUS_NO_MEMORY;
 
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y;
return GLITTER_STATUS_SUCCESS;
return;
}
 
/* Orient the edge left-to-right. */
995,9 → 875,6
cell_list_maybe_rewind(cells, ix1);
 
pair = cell_list_find_pair(cells, ix1, ix1+1);
if (unlikely (!pair.cell1 || !pair.cell2))
return GLITTER_STATUS_NO_MEMORY;
 
pair.cell1->uncovered_area += sign*y.quo*(GRID_X + fx1);
pair.cell1->covered_height += sign*y.quo;
y.quo += y1;
1023,8 → 900,6
 
++ix1;
cell = cell_list_find(cells, ix1);
if (unlikely (NULL == cell))
return GLITTER_STATUS_NO_MEMORY;
} while (ix1 != ix2);
 
pair.cell2 = cell;
1032,16 → 907,14
pair.cell2->uncovered_area += sign*(y2 - y.quo)*fx2;
pair.cell2->covered_height += sign*(y2 - y.quo);
}
 
return GLITTER_STATUS_SUCCESS;
}
 
static void
polygon_init (struct polygon *polygon)
polygon_init (struct polygon *polygon, jmp_buf *jmp)
{
polygon->ymin = polygon->ymax = 0;
polygon->y_buckets = polygon->y_buckets_embedded;
pool_init (polygon->edge_pool.base,
pool_init (polygon->edge_pool.base, jmp,
8192 - sizeof (struct _pool_chunk),
sizeof (polygon->edge_pool.embedded));
}
1064,12 → 937,11
grid_scaled_y_t ymax)
{
unsigned h = ymax - ymin;
unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
ymin);
unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
 
pool_reset(polygon->edge_pool.base);
 
if (unlikely (h > 0x7FFFFFFFU - EDGE_Y_BUCKET_HEIGHT))
if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
 
if (polygon->y_buckets != polygon->y_buckets_embedded)
1095,8 → 967,7
}
 
static void
_polygon_insert_edge_into_its_y_bucket(
struct polygon *polygon,
_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
struct edge *e)
{
unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
1105,7 → 976,7
*ptail = e;
}
 
inline static glitter_status_t
inline static void
polygon_add_edge (struct polygon *polygon,
const cairo_edge_t *edge)
{
1116,14 → 987,10
grid_scaled_y_t ymin = polygon->ymin;
grid_scaled_y_t ymax = polygon->ymax;
 
assert (edge->bottom > edge->top);
 
if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
return GLITTER_STATUS_SUCCESS;
return;
 
e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
if (unlikely (NULL == e))
return GLITTER_STATUS_NO_MEMORY;
 
dx = edge->line.p2.x - edge->line.p1.x;
dy = edge->line.p2.y - edge->line.p1.y;
1166,14 → 1033,23
 
e->x.rem -= dy; /* Bias the remainder for faster
* edge advancement. */
return GLITTER_STATUS_SUCCESS;
}
 
static void
active_list_reset (struct active_list *active)
{
active->head = NULL;
active->head.vertical = 1;
active->head.height_left = INT_MAX;
active->head.x.quo = INT_MIN;
active->head.prev = NULL;
active->head.next = &active->tail;
active->tail.prev = &active->head;
active->tail.next = NULL;
active->tail.x.quo = INT_MAX;
active->tail.height_left = INT_MAX;
active->tail.vertical = 1;
active->min_height = 0;
active->is_vertical = 1;
}
 
static void
1203,31 → 1079,46
static struct edge *
merge_sorted_edges (struct edge *head_a, struct edge *head_b)
{
struct edge *head, **next;
struct edge *head, **next, *prev;
int32_t x;
 
prev = head_a->prev;
next = &head;
if (head_a->x.quo <= head_b->x.quo) {
head = head_a;
next = &head;
} else {
head = head_b;
head_b->prev = prev;
goto start_with_b;
}
 
while (1) {
while (head_a != NULL && head_a->x.quo <= head_b->x.quo) {
do {
x = head_b->x.quo;
while (head_a != NULL && head_a->x.quo <= x) {
prev = head_a;
next = &head_a->next;
head_a = head_a->next;
}
 
head_b->prev = prev;
*next = head_b;
if (head_a == NULL)
return head;
 
while (head_b != NULL && head_b->x.quo <= head_a->x.quo) {
start_with_b:
x = head_a->x.quo;
while (head_b != NULL && head_b->x.quo <= x) {
prev = head_b;
next = &head_b->next;
head_b = head_b->next;
}
 
head_a->prev = prev;
*next = head_a;
if (head_b == NULL)
return head;
} while (1);
}
}
 
/*
* Sort (part of) a list.
1256,43 → 1147,42
 
head_other = list->next;
 
/* Single element list -> return */
if (head_other == NULL) {
*head_out = list;
return NULL;
}
 
/* Unroll the first iteration of the following loop (halves the number of calls to merge_sorted_edges):
* - Initialize remaining to be the list containing the elements after the second in the input list.
* - Initialize *head_out to be the sorted list containing the first two element.
*/
remaining = head_other->next;
if (list->x.quo <= head_other->x.quo) {
*head_out = list;
/* list->next = head_other; */ /* The input list is already like this. */
head_other->next = NULL;
} else {
*head_out = head_other;
head_other->prev = list->prev;
head_other->next = list;
list->prev = head_other;
list->next = NULL;
}
 
for (i = 0; i < level && remaining; i++) {
/* Extract a sorted list of the same size as *head_out
* (2^(i+1) elements) from the list of remaining elements. */
remaining = sort_edges (remaining, i, &head_other);
*head_out = merge_sorted_edges (*head_out, head_other);
}
 
/* *head_out now contains (at most) 2^(level+1) elements. */
 
return remaining;
}
 
static struct edge *
merge_unsorted_edges (struct edge *head, struct edge *unsorted)
{
sort_edges (unsorted, UINT_MAX, &unsorted);
return merge_sorted_edges (head, unsorted);
}
 
/* Test if the edges on the active list can be safely advanced by a
* full row without intersections or any edges ending. */
inline static int
active_list_can_step_full_row (struct active_list *active)
can_do_full_row (struct active_list *active)
{
const struct edge *e;
int prev_x = INT_MIN;
1301,14 → 1191,17
* list if we have been dropping edges. */
if (active->min_height <= 0) {
int min_height = INT_MAX;
int is_vertical = 1;
 
e = active->head;
e = active->head.next;
while (NULL != e) {
if (e->height_left < min_height)
min_height = e->height_left;
is_vertical &= e->vertical;
e = e->next;
}
 
active->is_vertical = is_vertical;
active->min_height = min_height;
}
 
1316,8 → 1209,7
return 0;
 
/* Check for intersections as no edges end during the next row. */
e = active->head;
while (NULL != e) {
for (e = active->head.next; e != &active->tail; e = e->next) {
struct quorem x = e->x;
 
if (! e->vertical) {
1327,11 → 1219,10
++x.quo;
}
 
if (x.quo <= prev_x)
if (x.quo < prev_x)
return 0;
 
prev_x = x.quo;
e = e->next;
}
 
return 1;
1340,58 → 1231,55
/* Merges edges on the given subpixel row from the polygon to the
* active_list. */
inline static void
active_list_merge_edges_from_polygon(
struct active_list *active,
grid_scaled_y_t y,
struct polygon *polygon)
active_list_merge_edges_from_bucket(struct active_list *active,
struct edge *edges)
{
/* Split off the edges on the current subrow and merge them into
* the active list. */
unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin);
int min_height = active->min_height;
struct edge *subrow_edges = NULL;
struct edge **ptail = &polygon->y_buckets[ix];
active->head.next = merge_unsorted_edges (active->head.next, edges);
}
 
while (1) {
struct edge *tail = *ptail;
if (NULL == tail) break;
inline static void
polygon_fill_buckets (struct active_list *active,
struct edge *edge,
int y,
struct edge **buckets)
{
grid_scaled_y_t min_height = active->min_height;
int is_vertical = active->is_vertical;
 
if (y == tail->ytop) {
*ptail = tail->next;
tail->next = subrow_edges;
subrow_edges = tail;
if (tail->height_left < min_height)
min_height = tail->height_left;
} else {
ptail = &tail->next;
while (edge) {
struct edge *next = edge->next;
int suby = edge->ytop - y;
if (buckets[suby])
buckets[suby]->prev = edge;
edge->next = buckets[suby];
edge->prev = NULL;
buckets[suby] = edge;
if (edge->height_left < min_height)
min_height = edge->height_left;
is_vertical &= edge->vertical;
edge = next;
}
}
if (subrow_edges) {
sort_edges (subrow_edges, UINT_MAX, &subrow_edges);
active->head = merge_sorted_edges (active->head, subrow_edges);
 
active->is_vertical = is_vertical;
active->min_height = min_height;
}
}
 
/* Advance the edges on the active list by one subsample row by
* updating their x positions. Drop edges from the list that end. */
inline static void
active_list_substep_edges(
struct active_list *active)
sub_row (struct active_list *active,
struct cell_list *coverages,
unsigned int mask)
{
struct edge **cursor = &active->head;
grid_scaled_x_t prev_x = INT_MIN;
struct edge *unsorted = NULL;
struct edge *edge = active->head.next;
int xstart = INT_MIN, prev_x = INT_MIN;
int winding = 0;
 
while (1) {
struct edge *edge;
cell_list_rewind (coverages);
 
UNROLL3({
edge = *cursor;
if (NULL == edge)
break;
while (&active->tail != edge) {
struct edge *next = edge->next;
int xend = edge->x.quo;
 
if (0 != --edge->height_left) {
if (--edge->height_left) {
edge->x.quo += edge->dxdy.quo;
edge->x.rem += edge->dxdy.rem;
if (edge->x.rem >= 0) {
1400,297 → 1288,100
}
 
if (edge->x.quo < prev_x) {
*cursor = edge->next;
edge->next = unsorted;
unsorted = edge;
} else {
struct edge *pos = edge->prev;
pos->next = next;
next->prev = pos;
do {
pos = pos->prev;
} while (edge->x.quo < pos->x.quo);
pos->next->prev = edge;
edge->next = pos->next;
edge->prev = pos;
pos->next = edge;
} else
prev_x = edge->x.quo;
cursor = &edge->next;
}
 
active->min_height = -1;
} else {
*cursor = edge->next;
edge->prev->next = next;
next->prev = edge->prev;
}
});
}
 
if (unsorted) {
sort_edges (unsorted, UINT_MAX, &unsorted);
active->head = merge_sorted_edges (active->head, unsorted);
}
}
 
inline static glitter_status_t
apply_nonzero_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
{
struct edge *edge = active->head;
int winding = 0;
int xstart;
int xend;
int status;
 
cell_list_rewind (coverages);
 
while (NULL != edge) {
xstart = edge->x.quo;
winding = edge->dir;
while (1) {
edge = edge->next;
if (NULL == edge)
return cell_list_add_unbounded_subspan (coverages, xstart);
 
winding += edge->dir;
if (0 == winding) {
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
if ((winding & mask) == 0) {
if (next->x.quo != xend) {
cell_list_add_subspan (coverages, xstart, xend);
xstart = INT_MIN;
}
}
} else if (xstart == INT_MIN)
xstart = xend;
 
xend = edge->x.quo;
status = cell_list_add_subspan (coverages, xstart, xend);
if (unlikely (status))
return status;
 
edge = edge->next;
edge = next;
}
 
return GLITTER_STATUS_SUCCESS;
}
 
static glitter_status_t
apply_evenodd_fill_rule_for_subrow (struct active_list *active,
struct cell_list *coverages)
inline static void dec (struct active_list *a, struct edge *e, int h)
{
struct edge *edge = active->head;
int xstart;
int xend;
int status;
 
cell_list_rewind (coverages);
 
while (NULL != edge) {
xstart = edge->x.quo;
 
while (1) {
edge = edge->next;
if (NULL == edge)
return cell_list_add_unbounded_subspan (coverages, xstart);
 
if (edge->next == NULL || edge->next->x.quo != edge->x.quo)
break;
 
edge = edge->next;
e->height_left -= h;
if (e->height_left == 0) {
e->prev->next = e->next;
e->next->prev = e->prev;
a->min_height = -1;
}
 
xend = edge->x.quo;
status = cell_list_add_subspan (coverages, xstart, xend);
if (unlikely (status))
return status;
 
edge = edge->next;
}
 
return GLITTER_STATUS_SUCCESS;
}
 
static glitter_status_t
apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
inline static void full_step (struct edge *e)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
int status;
 
left_edge = *cursor;
while (NULL != left_edge) {
struct edge *right_edge;
int winding = left_edge->dir;
 
left_edge->height_left -= GRID_Y;
if (left_edge->height_left)
cursor = &left_edge->next;
else
*cursor = left_edge->next;
 
while (1) {
right_edge = *cursor;
if (NULL == right_edge)
return cell_list_render_edge (coverages, left_edge, +1);
 
right_edge->height_left -= GRID_Y;
if (right_edge->height_left)
cursor = &right_edge->next;
else
*cursor = right_edge->next;
 
winding += right_edge->dir;
if (0 == winding) {
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
break;
if (! e->vertical) {
e->x.quo += e->dxdy_full.quo;
e->x.rem += e->dxdy_full.rem;
if (e->x.rem >= 0) {
++e->x.quo;
e->x.rem -= e->dy;
}
}
 
if (! right_edge->vertical) {
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
if (right_edge->x.rem >= 0) {
++right_edge->x.quo;
right_edge->x.rem -= right_edge->dy;
}
}
}
 
status = cell_list_render_edge (coverages, left_edge, +1);
if (unlikely (status))
return status;
 
status = cell_list_render_edge (coverages, right_edge, -1);
if (unlikely (status))
return status;
 
left_edge = *cursor;
}
 
return GLITTER_STATUS_SUCCESS;
}
 
static glitter_status_t
apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
struct cell_list *coverages)
static void
full_row (struct active_list *active,
struct cell_list *coverages,
unsigned int mask)
{
struct edge **cursor = &active->head;
struct edge *left_edge;
int status;
struct edge *left = active->head.next;
 
left_edge = *cursor;
while (NULL != left_edge) {
struct edge *right_edge;
while (&active->tail != left) {
struct edge *right;
int winding;
 
left_edge->height_left -= GRID_Y;
if (left_edge->height_left)
cursor = &left_edge->next;
else
*cursor = left_edge->next;
dec (active, left, GRID_Y);
 
while (1) {
right_edge = *cursor;
if (NULL == right_edge)
return cell_list_render_edge (coverages, left_edge, +1);
winding = left->dir;
right = left->next;
do {
dec (active, right, GRID_Y);
 
right_edge->height_left -= GRID_Y;
if (right_edge->height_left)
cursor = &right_edge->next;
else
*cursor = right_edge->next;
 
if (right_edge->next == NULL ||
right_edge->next->x.quo != right_edge->x.quo)
{
winding += right->dir;
if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
break;
}
 
if (! right_edge->vertical) {
right_edge->x.quo += right_edge->dxdy_full.quo;
right_edge->x.rem += right_edge->dxdy_full.rem;
if (right_edge->x.rem >= 0) {
++right_edge->x.quo;
right_edge->x.rem -= right_edge->dy;
}
}
}
full_step (right);
 
status = cell_list_render_edge (coverages, left_edge, +1);
if (unlikely (status))
return status;
right = right->next;
} while (1);
 
status = cell_list_render_edge (coverages, right_edge, -1);
if (unlikely (status))
return status;
cell_list_set_rewind (coverages);
cell_list_render_edge (coverages, left, +1);
cell_list_render_edge (coverages, right, -1);
 
left_edge = *cursor;
left = right->next;
}
 
return GLITTER_STATUS_SUCCESS;
}
 
/* If the user hasn't configured a coverage blitter, use a default one
* that blits spans directly to an A8 raster. */
#ifndef GLITTER_BLIT_COVERAGES
 
inline static void
blit_span(
unsigned char *row_pixels,
int x, unsigned len,
grid_area_t coverage)
{
int alpha = GRID_AREA_TO_ALPHA(coverage);
if (1 == len) {
row_pixels[x] = alpha;
}
else {
memset(row_pixels + x, alpha, len);
}
}
 
#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \
do { \
int __y = y; \
int __h = height; \
do { \
blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \
} while (--__h); \
} while (0)
 
static void
blit_cells(
struct cell_list *cells,
unsigned char *row_pixels,
int xmin, int xmax)
_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
{
struct cell *cell = cells->head;
int prev_x = xmin;
int coverage = 0;
if (NULL == cell)
return;
 
while (NULL != cell && cell->x < xmin) {
coverage += cell->covered_height;
cell = cell->next;
}
coverage *= GRID_X*2;
 
for (; NULL != cell; cell = cell->next) {
int x = cell->x;
int area;
if (x >= xmax)
break;
if (x > prev_x && 0 != coverage) {
blit_span(row_pixels, prev_x, x - prev_x, coverage);
}
 
coverage += cell->covered_height * GRID_X*2;
area = coverage - cell->uncovered_area;
if (area) {
blit_span(row_pixels, x, 1, area);
}
prev_x = x+1;
}
 
if (0 != coverage && prev_x < xmax) {
blit_span(row_pixels, prev_x, xmax - prev_x, coverage);
}
}
#endif /* GLITTER_BLIT_COVERAGES */
 
static void
_glitter_scan_converter_init(glitter_scan_converter_t *converter)
{
polygon_init(converter->polygon);
polygon_init(converter->polygon, jmp);
active_list_init(converter->active);
cell_list_init(converter->coverages);
cell_list_init(converter->coverages, jmp);
converter->xmin=0;
converter->ymin=0;
converter->xmax=0;
1698,14 → 1389,18
}
 
static void
_glitter_scan_converter_fini(glitter_scan_converter_t *converter)
_glitter_scan_converter_fini(glitter_scan_converter_t *self)
{
polygon_fini(converter->polygon);
cell_list_fini(converter->coverages);
converter->xmin=0;
converter->ymin=0;
converter->xmax=0;
converter->ymax=0;
if (self->spans != self->spans_embedded)
free (self->spans);
 
polygon_fini(self->polygon);
cell_list_fini(self->coverages);
 
self->xmin=0;
self->ymin=0;
self->xmax=0;
self->ymax=0;
}
 
static grid_scaled_t
1733,10 → 1428,21
int xmax, int ymax)
{
glitter_status_t status;
int max_num_spans;
 
converter->xmin = 0; converter->xmax = 0;
converter->ymin = 0; converter->ymax = 0;
 
max_num_spans = xmax - xmin + 1;
 
if (max_num_spans > ARRAY_LENGTH(converter->spans_embedded)) {
converter->spans = _cairo_malloc_ab (max_num_spans,
sizeof (cairo_half_open_span_t));
if (unlikely (converter->spans == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
converter->spans = converter->spans_embedded;
 
xmin = int_to_grid_scaled_x(xmin);
ymin = int_to_grid_scaled_y(ymin);
xmax = int_to_grid_scaled_x(xmax);
1780,7 → 1486,12
(out) = tmp__; \
} while (0)
 
I glitter_status_t
/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
* converter. The coordinates represent pixel positions scaled by
* 2**GLITTER_PIXEL_BITS. If this function fails then the scan
* converter should be reset or destroyed. Dir must be +1 or -1,
* with the latter reversing the orientation of the edge. */
I void
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge)
{
1789,13 → 1500,13
INPUT_TO_GRID_Y (edge->top, e.top);
INPUT_TO_GRID_Y (edge->bottom, e.bottom);
if (e.top >= e.bottom)
return GLITTER_STATUS_SUCCESS;
return;
 
/* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
if (e.line.p1.y == e.line.p2.y)
return GLITTER_STATUS_SUCCESS;
e.line.p2.y++; /* little fudge to prevent a div-by-zero */
 
INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
1802,209 → 1513,127
 
e.dir = edge->dir;
 
return polygon_add_edge (converter->polygon, &e);
polygon_add_edge (converter->polygon, &e);
}
 
#ifndef GLITTER_BLIT_COVERAGES_BEGIN
# define GLITTER_BLIT_COVERAGES_BEGIN
#endif
 
#ifndef GLITTER_BLIT_COVERAGES_END
# define GLITTER_BLIT_COVERAGES_END
#endif
 
#ifndef GLITTER_BLIT_COVERAGES_EMPTY
# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
#endif
 
static cairo_bool_t
active_list_is_vertical (struct active_list *active)
{
struct edge *e;
 
for (e = active->head; e != NULL; e = e->next) {
if (! e->vertical)
return FALSE;
}
 
return TRUE;
}
 
static void
step_edges (struct active_list *active, int count)
{
struct edge **cursor = &active->head;
struct edge *edge;
 
for (edge = *cursor; edge != NULL; edge = *cursor) {
edge->height_left -= GRID_Y * count;
if (edge->height_left)
cursor = &edge->next;
else
*cursor = edge->next;
count *= GRID_Y;
for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
edge->height_left -= count;
if (! edge->height_left) {
edge->prev->next = edge->next;
edge->next->prev = edge->prev;
active->min_height = -1;
}
}
}
 
I glitter_status_t
glitter_scan_converter_render(
glitter_scan_converter_t *converter,
int nonzero_fill,
GLITTER_BLIT_COVERAGES_ARGS)
static glitter_status_t
blit_a8 (struct cell_list *cells,
cairo_span_renderer_t *renderer,
cairo_half_open_span_t *spans,
int y, int height,
int xmin, int xmax)
{
int i, j;
int ymax_i = converter->ymax / GRID_Y;
int ymin_i = converter->ymin / GRID_Y;
int xmin_i, xmax_i;
int h = ymax_i - ymin_i;
struct polygon *polygon = converter->polygon;
struct cell_list *coverages = converter->coverages;
struct active_list *active = converter->active;
struct cell *cell = cells->head.next;
int prev_x = xmin, last_x = -1;
int16_t cover = 0, last_cover = 0;
unsigned num_spans;
 
xmin_i = converter->xmin / GRID_X;
xmax_i = converter->xmax / GRID_X;
if (xmin_i >= xmax_i)
return GLITTER_STATUS_SUCCESS;
if (cell == &cells->tail)
return CAIRO_STATUS_SUCCESS;
 
/* Let the coverage blitter initialise itself. */
GLITTER_BLIT_COVERAGES_BEGIN;
/* Skip cells to the left of the clip region. */
while (cell->x < xmin) {
cover += cell->covered_height;
cell = cell->next;
}
cover *= GRID_X*2;
 
/* Render each pixel row. */
for (i = 0; i < h; i = j) {
int do_full_step = 0;
glitter_status_t status = 0;
/* Form the spans from the coverages and areas. */
num_spans = 0;
for (; cell->x < xmax; cell = cell->next) {
int x = cell->x;
int16_t area;
 
j = i + 1;
 
/* Determine if we can ignore this row or use the full pixel
* stepper. */
if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
if (! active->head) {
for (; j < h && ! polygon->y_buckets[j]; j++)
;
GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
continue;
if (x > prev_x && cover != last_cover) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
++num_spans;
}
 
do_full_step = active_list_can_step_full_row (active);
}
cover += cell->covered_height*GRID_X*2;
area = cover - cell->uncovered_area;
 
if (do_full_step) {
/* Step by a full pixel row's worth. */
if (nonzero_fill) {
status = apply_nonzero_fill_rule_and_step_edges (active,
coverages);
} else {
status = apply_evenodd_fill_rule_and_step_edges (active,
coverages);
if (area != last_cover) {
spans[num_spans].x = x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
last_cover = area;
last_x = x;
++num_spans;
}
 
if (active_list_is_vertical (active)) {
while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
{
active->min_height -= GRID_Y;
j++;
prev_x = x+1;
}
if (j != i + 1)
step_edges (active, j - (i + 1));
}
} else {
grid_scaled_y_t suby;
 
/* Subsample this row. */
for (suby = 0; suby < GRID_Y; suby++) {
grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
 
active_list_merge_edges_from_polygon (active, y, polygon);
 
if (nonzero_fill) {
status |= apply_nonzero_fill_rule_for_subrow (active,
coverages);
} else {
status |= apply_evenodd_fill_rule_for_subrow (active,
coverages);
if (prev_x <= xmax && cover != last_cover) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
++num_spans;
}
 
active_list_substep_edges(active);
if (last_x < xmax && last_cover) {
spans[num_spans].x = xmax;
spans[num_spans].coverage = 0;
++num_spans;
}
}
 
if (unlikely (status))
return status;
 
GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
cell_list_reset (coverages);
 
if (! active->head)
active->min_height = INT_MAX;
else
active->min_height -= GRID_Y;
/* Dump them into the renderer. */
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
 
/* Clean up the coverage blitter. */
GLITTER_BLIT_COVERAGES_END;
 
return GLITTER_STATUS_SUCCESS;
}
 
/*-------------------------------------------------------------------------
* cairo specific implementation: the coverage blitter and
* scan converter subclass. */
 
#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
static glitter_status_t
blit_with_span_renderer (struct cell_list *cells,
blit_a1 (struct cell_list *cells,
cairo_span_renderer_t *renderer,
struct pool *span_pool,
cairo_half_open_span_t *spans,
int y, int height,
int xmin, int xmax)
{
struct cell *cell = cells->head;
int prev_x = xmin;
int cover = 0;
cairo_half_open_span_t *spans;
struct cell *cell = cells->head.next;
int prev_x = xmin, last_x = -1;
int16_t cover = 0;
uint8_t coverage, last_cover = 0;
unsigned num_spans;
 
if (cell == NULL)
return blit_empty_with_span_renderer (renderer, y, height);
if (cell == &cells->tail)
return CAIRO_STATUS_SUCCESS;
 
/* Skip cells to the left of the clip region. */
while (cell != NULL && cell->x < xmin) {
while (cell->x < xmin) {
cover += cell->covered_height;
cell = cell->next;
}
cover *= GRID_X*2;
 
/* Count number of cells remaining. */
{
struct cell *next = cell;
num_spans = 1;
while (next != NULL) {
next = next->next;
++num_spans;
}
num_spans = 2*num_spans;
}
 
/* Allocate enough spans for the row. */
pool_reset (span_pool);
spans = pool_alloc (span_pool, sizeof(spans[0])*num_spans);
if (unlikely (spans == NULL))
return GLITTER_STATUS_NO_MEMORY;
 
/* Form the spans from the coverages and areas. */
num_spans = 0;
 
/* Form the spans from the coverages and areas. */
for (; cell != NULL; cell = cell->next) {
for (; cell->x < xmax; cell = cell->next) {
int x = cell->x;
int area;
int16_t area;
 
if (x >= xmax)
break;
 
if (x > prev_x) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
coverage = GRID_AREA_TO_A1 (cover);
if (x > prev_x && coverage != last_cover) {
last_x = spans[num_spans].x = prev_x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
2011,45 → 1640,131
cover += cell->covered_height*GRID_X*2;
area = cover - cell->uncovered_area;
 
spans[num_spans].x = x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
coverage = GRID_AREA_TO_A1 (area);
if (coverage != last_cover) {
last_x = spans[num_spans].x = x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
prev_x = x+1;
}
 
if (prev_x <= xmax) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
coverage = GRID_AREA_TO_A1 (cover);
if (prev_x <= xmax && coverage != last_cover) {
last_x = spans[num_spans].x = prev_x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
if (prev_x < xmax && cover) {
if (last_x < xmax && last_cover) {
spans[num_spans].x = xmax;
spans[num_spans].coverage = 0;
++num_spans;
}
if (num_spans == 1)
return CAIRO_STATUS_SUCCESS;
 
/* Dump them into the renderer. */
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
 
static glitter_status_t
blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
 
I void
glitter_scan_converter_render(glitter_scan_converter_t *converter,
unsigned int winding_mask,
int antialias,
cairo_span_renderer_t *renderer)
{
return renderer->render_rows (renderer, y, height, NULL, 0);
int i, j;
int ymax_i = converter->ymax / GRID_Y;
int ymin_i = converter->ymin / GRID_Y;
int xmin_i, xmax_i;
int h = ymax_i - ymin_i;
struct polygon *polygon = converter->polygon;
struct cell_list *coverages = converter->coverages;
struct active_list *active = converter->active;
struct edge *buckets[GRID_Y] = { 0 };
 
xmin_i = converter->xmin / GRID_X;
xmax_i = converter->xmax / GRID_X;
if (xmin_i >= xmax_i)
return;
 
/* Render each pixel row. */
for (i = 0; i < h; i = j) {
int do_full_row = 0;
 
j = i + 1;
 
/* Determine if we can ignore this row or use the full pixel
* stepper. */
if (! polygon->y_buckets[i]) {
if (active->head.next == &active->tail) {
active->min_height = INT_MAX;
active->is_vertical = 1;
for (; j < h && ! polygon->y_buckets[j]; j++)
;
continue;
}
 
do_full_row = can_do_full_row (active);
}
 
if (do_full_row) {
/* Step by a full pixel row's worth. */
full_row (active, coverages, winding_mask);
 
if (active->is_vertical) {
while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
{
active->min_height -= GRID_Y;
j++;
}
if (j != i + 1)
step_edges (active, j - (i + 1));
}
} else {
int sub;
 
polygon_fill_buckets (active,
polygon->y_buckets[i],
(i+ymin_i)*GRID_Y,
buckets);
 
/* Subsample this row. */
for (sub = 0; sub < GRID_Y; sub++) {
if (buckets[sub]) {
active_list_merge_edges_from_bucket (active, buckets[sub]);
buckets[sub] = NULL;
}
 
sub_row (active, coverages, winding_mask);
}
}
 
if (antialias)
blit_a8 (coverages, renderer, converter->spans,
i+ymin_i, j-i, xmin_i, xmax_i);
else
blit_a1 (coverages, renderer, converter->spans,
i+ymin_i, j-i, xmin_i, xmax_i);
cell_list_reset (coverages);
 
active->min_height -= GRID_Y;
}
}
 
struct _cairo_tor_scan_converter {
cairo_scan_converter_t base;
 
glitter_scan_converter_t converter[1];
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
 
struct {
struct pool base[1];
cairo_half_open_span_t embedded[32];
} span_pool;
jmp_buf jmp;
};
 
typedef struct _cairo_tor_scan_converter cairo_tor_scan_converter_t;
2062,51 → 1777,25
return;
}
_glitter_scan_converter_fini (self->converter);
pool_fini (self->span_pool.base);
free(self);
}
 
static cairo_status_t
_cairo_tor_scan_converter_add_edge (void *converter,
const cairo_point_t *p1,
const cairo_point_t *p2,
int top, int bottom,
int dir)
{
cairo_tor_scan_converter_t *self = converter;
cairo_status_t status;
cairo_edge_t edge;
 
edge.line.p1 = *p1;
edge.line.p2 = *p2;
edge.top = top;
edge.bottom = bottom;
edge.dir = dir;
 
status = glitter_scan_converter_add_edge (self->converter, &edge);
if (unlikely (status))
return _cairo_scan_converter_set_error (self, _cairo_error (status));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_status_t
_cairo_tor_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
cairo_tor_scan_converter_t *self = converter;
cairo_status_t status;
int i;
 
for (i = 0; i < polygon->num_edges; i++) {
status = glitter_scan_converter_add_edge (self->converter,
&polygon->edges[i]);
if (unlikely (status)) {
return _cairo_scan_converter_set_error (self,
_cairo_error (status));
}
}
#if 0
FILE *file = fopen ("polygon.txt", "w");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
#endif
 
for (i = 0; i < polygon->num_edges; i++)
glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
 
return CAIRO_STATUS_SUCCESS;
}
 
2117,13 → 1806,13
cairo_tor_scan_converter_t *self = converter;
cairo_status_t status;
 
status = glitter_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING,
renderer,
self->span_pool.base);
if (unlikely (status))
if ((status = setjmp (self->jmp)))
return _cairo_scan_converter_set_error (self, _cairo_error (status));
 
glitter_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
self->antialias != CAIRO_ANTIALIAS_NONE,
renderer);
return CAIRO_STATUS_SUCCESS;
}
 
2132,12 → 1821,13
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule)
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_tor_scan_converter_t *self;
cairo_status_t status;
 
self = calloc (1, sizeof(struct _cairo_tor_scan_converter));
self = malloc (sizeof(struct _cairo_tor_scan_converter));
if (unlikely (self == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail_nomem;
2144,15 → 1834,9
}
 
self->base.destroy = _cairo_tor_scan_converter_destroy;
self->base.add_edge = _cairo_tor_scan_converter_add_edge;
self->base.add_polygon = _cairo_tor_scan_converter_add_polygon;
self->base.generate = _cairo_tor_scan_converter_generate;
 
pool_init (self->span_pool.base,
250 * sizeof(self->span_pool.embedded[0]),
sizeof(self->span_pool.embedded));
 
_glitter_scan_converter_init (self->converter);
_glitter_scan_converter_init (self->converter, &self->jmp);
status = glitter_scan_converter_reset (self->converter,
xmin, ymin, xmax, ymax);
if (unlikely (status))
2159,6 → 1843,7
goto bail;
 
self->fill_rule = fill_rule;
self->antialias = antialias;
 
return &self->base;
 
/programs/develop/libraries/cairo/src/cairo-tor22-scan-converter.c
0,0 → 1,1709
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* glitter-paths - polygon scan converter
*
* Copyright (c) 2008 M Joonas Pihlaja
* Copyright (c) 2007 David Turner
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/* This is the Glitter paths scan converter incorporated into cairo.
* The source is from commit 734c53237a867a773640bd5b64816249fa1730f8
* of
*
* http://gitweb.freedesktop.org/?p=users/joonas/glitter-paths
*/
/* Glitter-paths is a stand alone polygon rasteriser derived from
* David Turner's reimplementation of Tor Anderssons's 15x17
* supersampling rasteriser from the Apparition graphics library. The
* main new feature here is cheaply choosing per-scan line between
* doing fully analytical coverage computation for an entire row at a
* time vs. using a supersampling approach.
*
* David Turner's code can be found at
*
* http://david.freetype.org/rasterizer-shootout/raster-comparison-20070813.tar.bz2
*
* In particular this file incorporates large parts of ftgrays_tor10.h
* from raster-comparison-20070813.tar.bz2
*/
/* Overview
*
* A scan converter's basic purpose to take polygon edges and convert
* them into an RLE compressed A8 mask. This one works in two phases:
* gathering edges and generating spans.
*
* 1) As the user feeds the scan converter edges they are vertically
* clipped and bucketted into a _polygon_ data structure. The edges
* are also snapped from the user's coordinates to the subpixel grid
* coordinates used during scan conversion.
*
* user
* |
* | edges
* V
* polygon buckets
*
* 2) Generating spans works by performing a vertical sweep of pixel
* rows from top to bottom and maintaining an _active_list_ of edges
* that intersect the row. From the active list the fill rule
* determines which edges are the left and right edges of the start of
* each span, and their contribution is then accumulated into a pixel
* coverage list (_cell_list_) as coverage deltas. Once the coverage
* deltas of all edges are known we can form spans of constant pixel
* coverage by summing the deltas during a traversal of the cell list.
* At the end of a pixel row the cell list is sent to a coverage
* blitter for rendering to some target surface.
*
* The pixel coverages are computed by either supersampling the row
* and box filtering a mono rasterisation, or by computing the exact
* coverages of edges in the active list. The supersampling method is
* used whenever some edge starts or stops within the row or there are
* edge intersections in the row.
*
* polygon bucket for \
* current pixel row |
* | |
* | activate new edges | Repeat GRID_Y times if we
* V \ are supersampling this row,
* active list / or just once if we're computing
* | | analytical coverage.
* | coverage deltas |
* V |
* pixel coverage list /
* |
* V
* coverage blitter
*/
#include "cairoint.h"
#include "cairo-spans-private.h"
#include "cairo-error-private.h"
 
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <setjmp.h>
 
/*-------------------------------------------------------------------------
* cairo specific config
*/
#define I static
 
/* Prefer cairo's status type. */
#define GLITTER_HAVE_STATUS_T 1
#define GLITTER_STATUS_SUCCESS CAIRO_STATUS_SUCCESS
#define GLITTER_STATUS_NO_MEMORY CAIRO_STATUS_NO_MEMORY
typedef cairo_status_t glitter_status_t;
 
/* The input coordinate scale and the rasterisation grid scales. */
#define GLITTER_INPUT_BITS CAIRO_FIXED_FRAC_BITS
//#define GRID_X_BITS CAIRO_FIXED_FRAC_BITS
//#define GRID_Y 15
#define GRID_X_BITS 2
#define GRID_Y_BITS 2
 
/* Set glitter up to use a cairo span renderer to do the coverage
* blitting. */
struct pool;
struct cell_list;
 
/*-------------------------------------------------------------------------
* glitter-paths.h
*/
 
/* "Input scaled" numbers are fixed precision reals with multiplier
* 2**GLITTER_INPUT_BITS. Input coordinates are given to glitter as
* pixel scaled numbers. These get converted to the internal grid
* scaled numbers as soon as possible. Internal overflow is possible
* if GRID_X/Y inside glitter-paths.c is larger than
* 1<<GLITTER_INPUT_BITS. */
#ifndef GLITTER_INPUT_BITS
# define GLITTER_INPUT_BITS 8
#endif
#define GLITTER_INPUT_SCALE (1<<GLITTER_INPUT_BITS)
typedef int glitter_input_scaled_t;
 
#if !GLITTER_HAVE_STATUS_T
typedef enum {
GLITTER_STATUS_SUCCESS = 0,
GLITTER_STATUS_NO_MEMORY
} glitter_status_t;
#endif
 
#ifndef I
# define I /*static*/
#endif
 
/* Opaque type for scan converting. */
typedef struct glitter_scan_converter glitter_scan_converter_t;
 
/* Reset a scan converter to accept polygon edges and set the clip box
* in pixels. Allocates O(ymax-ymin) bytes of memory. The clip box
* is set to integer pixel coordinates xmin <= x < xmax, ymin <= y <
* ymax. */
I glitter_status_t
glitter_scan_converter_reset(
glitter_scan_converter_t *converter,
int xmin, int ymin,
int xmax, int ymax);
 
/* Render the polygon in the scan converter to the given A8 format
* image raster. Only the pixels accessible as pixels[y*stride+x] for
* x,y inside the clip box are written to, where xmin <= x < xmax,
* ymin <= y < ymax. The image is assumed to be clear on input.
*
* If nonzero_fill is true then the interior of the polygon is
* computed with the non-zero fill rule. Otherwise the even-odd fill
* rule is used.
*
* The scan converter must be reset or destroyed after this call. */
 
/*-------------------------------------------------------------------------
* glitter-paths.c: Implementation internal types
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
 
/* All polygon coordinates are snapped onto a subsample grid. "Grid
* scaled" numbers are fixed precision reals with multiplier GRID_X or
* GRID_Y. */
typedef int grid_scaled_t;
typedef int grid_scaled_x_t;
typedef int grid_scaled_y_t;
 
/* Default x/y scale factors.
* You can either define GRID_X/Y_BITS to get a power-of-two scale
* or define GRID_X/Y separately. */
#if !defined(GRID_X) && !defined(GRID_X_BITS)
# define GRID_X_BITS 8
#endif
#if !defined(GRID_Y) && !defined(GRID_Y_BITS)
# define GRID_Y 15
#endif
 
/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */
#ifdef GRID_X_BITS
# define GRID_X (1 << GRID_X_BITS)
#endif
#ifdef GRID_Y_BITS
# define GRID_Y (1 << GRID_Y_BITS)
#endif
 
/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
* integer and fractional parts. The integer part is floored. */
#if defined(GRID_X_TO_INT_FRAC)
/* do nothing */
#elif defined(GRID_X_BITS)
# define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS)
#else
# define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_general(x, i, f, GRID_X)
#endif
 
#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
(i) = (t) / (m); \
(f) = (t) % (m); \
if ((f) < 0) { \
--(i); \
(f) += (m); \
} \
} while (0)
 
#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
(f) = (t) & ((1 << (b)) - 1); \
(i) = (t) >> (b); \
} while (0)
 
/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want
* to be able to represent exactly areas of subpixel trapezoids whose
* vertices are given in grid scaled coordinates. The scale factor
* comes from needing to accurately represent the area 0.5*dx*dy of a
* triangle with base dx and height dy in grid scaled numbers. */
#define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
 
/* GRID_AREA_TO_ALPHA(area): map [0,GRID_XY] to [0,255]. */
#if GRID_XY == 510
# define GRID_AREA_TO_ALPHA(c) (((c)+1) >> 1)
#elif GRID_XY == 255
# define GRID_AREA_TO_ALPHA(c) (c)
#elif GRID_XY == 64
# define GRID_AREA_TO_ALPHA(c) (((c) << 2) | -(((c) & 0x40) >> 6))
#elif GRID_XY == 32
# define GRID_AREA_TO_ALPHA(c) (((c) << 3) | -(((c) & 0x20) >> 5))
#elif GRID_XY == 128
# define GRID_AREA_TO_ALPHA(c) ((((c) << 1) | -((c) >> 7)) & 255)
#elif GRID_XY == 256
# define GRID_AREA_TO_ALPHA(c) (((c) | -((c) >> 8)) & 255)
#elif GRID_XY == 15
# define GRID_AREA_TO_ALPHA(c) (((c) << 4) + (c))
#elif GRID_XY == 2*256*15
# define GRID_AREA_TO_ALPHA(c) (((c) + ((c)<<4) + 256) >> 9)
#else
# define GRID_AREA_TO_ALPHA(c) (((c)*255 + GRID_XY/2) / GRID_XY)
#endif
 
#define UNROLL3(x) x x x
 
struct quorem {
int32_t quo;
int32_t rem;
};
 
/* Header for a chunk of memory in a memory pool. */
struct _pool_chunk {
/* # bytes used in this chunk. */
size_t size;
 
/* # bytes total in this chunk */
size_t capacity;
 
/* Pointer to the previous chunk or %NULL if this is the sentinel
* chunk in the pool header. */
struct _pool_chunk *prev_chunk;
 
/* Actual data starts here. Well aligned for pointers. */
};
 
/* A memory pool. This is supposed to be embedded on the stack or
* within some other structure. It may optionally be followed by an
* embedded array from which requests are fulfilled until
* malloc needs to be called to allocate a first real chunk. */
struct pool {
/* Chunk we're allocating from. */
struct _pool_chunk *current;
 
jmp_buf *jmp;
 
/* Free list of previously allocated chunks. All have >= default
* capacity. */
struct _pool_chunk *first_free;
 
/* The default capacity of a chunk. */
size_t default_capacity;
 
/* Header for the sentinel chunk. Directly following the pool
* struct should be some space for embedded elements from which
* the sentinel chunk allocates from. */
struct _pool_chunk sentinel[1];
};
 
/* A polygon edge. */
struct edge {
/* Next in y-bucket or active list. */
struct edge *next, *prev;
 
/* Number of subsample rows remaining to scan convert of this
* edge. */
grid_scaled_y_t height_left;
 
/* Original sign of the edge: +1 for downwards, -1 for upwards
* edges. */
int dir;
int vertical;
 
/* Current x coordinate while the edge is on the active
* list. Initialised to the x coordinate of the top of the
* edge. The quotient is in grid_scaled_x_t units and the
* remainder is mod dy in grid_scaled_y_t units.*/
struct quorem x;
 
/* Advance of the current x when moving down a subsample line. */
struct quorem dxdy;
 
/* The clipped y of the top of the edge. */
grid_scaled_y_t ytop;
 
/* y2-y1 after orienting the edge downwards. */
grid_scaled_y_t dy;
};
 
#define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/GRID_Y)
 
/* A collection of sorted and vertically clipped edges of the polygon.
* Edges are moved from the polygon to an active list while scan
* converting. */
struct polygon {
/* The vertical clip extents. */
grid_scaled_y_t ymin, ymax;
 
/* Array of edges all starting in the same bucket. An edge is put
* into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
* it is added to the polygon. */
struct edge **y_buckets;
struct edge *y_buckets_embedded[64];
 
struct {
struct pool base[1];
struct edge embedded[32];
} edge_pool;
};
 
/* A cell records the effect on pixel coverage of polygon edges
* passing through a pixel. It contains two accumulators of pixel
* coverage.
*
* Consider the effects of a polygon edge on the coverage of a pixel
* it intersects and that of the following one. The coverage of the
* following pixel is the height of the edge multiplied by the width
* of the pixel, and the coverage of the pixel itself is the area of
* the trapezoid formed by the edge and the right side of the pixel.
*
* +-----------------------+-----------------------+
* | | |
* | | |
* |_______________________|_______________________|
* | \...................|.......................|\
* | \..................|.......................| |
* | \.................|.......................| |
* | \....covered.....|.......................| |
* | \....area.......|.......................| } covered height
* | \..............|.......................| |
* |uncovered\.............|.......................| |
* | area \............|.......................| |
* |___________\...........|.......................|/
* | | |
* | | |
* | | |
* +-----------------------+-----------------------+
*
* Since the coverage of the following pixel will always be a multiple
* of the width of the pixel, we can store the height of the covered
* area instead. The coverage of the pixel itself is the total
* coverage minus the area of the uncovered area to the left of the
* edge. As it's faster to compute the uncovered area we only store
* that and subtract it from the total coverage later when forming
* spans to blit.
*
* The heights and areas are signed, with left edges of the polygon
* having positive sign and right edges having negative sign. When
* two edges intersect they swap their left/rightness so their
* contribution above and below the intersection point must be
* computed separately. */
struct cell {
struct cell *next;
int x;
int16_t uncovered_area;
int16_t covered_height;
};
 
/* A cell list represents the scan line sparsely as cells ordered by
* ascending x. It is geared towards scanning the cells in order
* using an internal cursor. */
struct cell_list {
/* Sentinel nodes */
struct cell head, tail;
 
/* Cursor state for iterating through the cell list. */
struct cell *cursor, *rewind;
 
/* Cells in the cell list are owned by the cell list and are
* allocated from this pool. */
struct {
struct pool base[1];
struct cell embedded[32];
} cell_pool;
};
 
struct cell_pair {
struct cell *cell1;
struct cell *cell2;
};
 
/* The active list contains edges in the current scan line ordered by
* the x-coordinate of the intercept of the edge and the scan line. */
struct active_list {
/* Leftmost edge on the current scan line. */
struct edge head, tail;
 
/* A lower bound on the height of the active edges is used to
* estimate how soon some active edge ends. We can't advance the
* scan conversion by a full pixel row if an edge ends somewhere
* within it. */
grid_scaled_y_t min_height;
int is_vertical;
};
 
struct glitter_scan_converter {
struct polygon polygon[1];
struct active_list active[1];
struct cell_list coverages[1];
 
cairo_half_open_span_t *spans;
cairo_half_open_span_t spans_embedded[64];
 
/* Clip box. */
grid_scaled_x_t xmin, xmax;
grid_scaled_y_t ymin, ymax;
};
 
/* Compute the floored division a/b. Assumes / and % perform symmetric
* division. */
inline static struct quorem
floored_divrem(int a, int b)
{
struct quorem qr;
qr.quo = a/b;
qr.rem = a%b;
if ((a^b)<0 && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
* division. */
static struct quorem
floored_muldivrem(int x, int a, int b)
{
struct quorem qr;
long long xa = (long long)x*a;
qr.quo = xa/b;
qr.rem = xa%b;
if ((xa>=0) != (b>=0) && qr.rem) {
qr.quo -= 1;
qr.rem += b;
}
return qr;
}
 
static struct _pool_chunk *
_pool_chunk_init(
struct _pool_chunk *p,
struct _pool_chunk *prev_chunk,
size_t capacity)
{
p->prev_chunk = prev_chunk;
p->size = 0;
p->capacity = capacity;
return p;
}
 
static struct _pool_chunk *
_pool_chunk_create(struct pool *pool, size_t size)
{
struct _pool_chunk *p;
 
p = malloc(size + sizeof(struct _pool_chunk));
if (unlikely (NULL == p))
longjmp (*pool->jmp, _cairo_error (CAIRO_STATUS_NO_MEMORY));
 
return _pool_chunk_init(p, pool->current, size);
}
 
static void
pool_init(struct pool *pool,
jmp_buf *jmp,
size_t default_capacity,
size_t embedded_capacity)
{
pool->jmp = jmp;
pool->current = pool->sentinel;
pool->first_free = NULL;
pool->default_capacity = default_capacity;
_pool_chunk_init(pool->sentinel, NULL, embedded_capacity);
}
 
static void
pool_fini(struct pool *pool)
{
struct _pool_chunk *p = pool->current;
do {
while (NULL != p) {
struct _pool_chunk *prev = p->prev_chunk;
if (p != pool->sentinel)
free(p);
p = prev;
}
p = pool->first_free;
pool->first_free = NULL;
} while (NULL != p);
}
 
/* Satisfy an allocation by first allocating a new large enough chunk
* and adding it to the head of the pool's chunk list. This function
* is called as a fallback if pool_alloc() couldn't do a quick
* allocation from the current chunk in the pool. */
static void *
_pool_alloc_from_new_chunk(
struct pool *pool,
size_t size)
{
struct _pool_chunk *chunk;
void *obj;
size_t capacity;
 
/* If the allocation is smaller than the default chunk size then
* try getting a chunk off the free list. Force alloc of a new
* chunk for large requests. */
capacity = size;
chunk = NULL;
if (size < pool->default_capacity) {
capacity = pool->default_capacity;
chunk = pool->first_free;
if (chunk) {
pool->first_free = chunk->prev_chunk;
_pool_chunk_init(chunk, pool->current, chunk->capacity);
}
}
 
if (NULL == chunk)
chunk = _pool_chunk_create (pool, capacity);
pool->current = chunk;
 
obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
chunk->size += size;
return obj;
}
 
/* Allocate size bytes from the pool. The first allocated address
* returned from a pool is aligned to sizeof(void*). Subsequent
* addresses will maintain alignment as long as multiples of void* are
* allocated. Returns the address of a new memory area or %NULL on
* allocation failures. The pool retains ownership of the returned
* memory. */
inline static void *
pool_alloc (struct pool *pool, size_t size)
{
struct _pool_chunk *chunk = pool->current;
 
if (size <= chunk->capacity - chunk->size) {
void *obj = ((unsigned char*)chunk + sizeof(*chunk) + chunk->size);
chunk->size += size;
return obj;
} else {
return _pool_alloc_from_new_chunk(pool, size);
}
}
 
/* Relinquish all pool_alloced memory back to the pool. */
static void
pool_reset (struct pool *pool)
{
/* Transfer all used chunks to the chunk free list. */
struct _pool_chunk *chunk = pool->current;
if (chunk != pool->sentinel) {
while (chunk->prev_chunk != pool->sentinel) {
chunk = chunk->prev_chunk;
}
chunk->prev_chunk = pool->first_free;
pool->first_free = pool->current;
}
/* Reset the sentinel as the current chunk. */
pool->current = pool->sentinel;
pool->sentinel->size = 0;
}
 
/* Rewinds the cell list's cursor to the beginning. After rewinding
* we're good to cell_list_find() the cell any x coordinate. */
inline static void
cell_list_rewind (struct cell_list *cells)
{
cells->cursor = &cells->head;
}
 
inline static void
cell_list_maybe_rewind (struct cell_list *cells, int x)
{
if (x < cells->cursor->x) {
cells->cursor = cells->rewind;
if (x < cells->cursor->x)
cells->cursor = &cells->head;
}
}
 
inline static void
cell_list_set_rewind (struct cell_list *cells)
{
cells->rewind = cells->cursor;
}
 
static void
cell_list_init(struct cell_list *cells, jmp_buf *jmp)
{
pool_init(cells->cell_pool.base, jmp,
256*sizeof(struct cell),
sizeof(cells->cell_pool.embedded));
cells->tail.next = NULL;
cells->tail.x = INT_MAX;
cells->head.x = INT_MIN;
cells->head.next = &cells->tail;
cell_list_rewind (cells);
}
 
static void
cell_list_fini(struct cell_list *cells)
{
pool_fini (cells->cell_pool.base);
}
 
/* Empty the cell list. This is called at the start of every pixel
* row. */
inline static void
cell_list_reset (struct cell_list *cells)
{
cell_list_rewind (cells);
cells->head.next = &cells->tail;
pool_reset (cells->cell_pool.base);
}
 
inline static struct cell *
cell_list_alloc (struct cell_list *cells,
struct cell *tail,
int x)
{
struct cell *cell;
 
cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
cell->next = tail->next;
tail->next = cell;
cell->x = x;
*(uint32_t *)&cell->uncovered_area = 0;
 
return cell;
}
 
/* Find a cell at the given x-coordinate. Returns %NULL if a new cell
* needed to be allocated but couldn't be. Cells must be found with
* non-decreasing x-coordinate until the cell list is rewound using
* cell_list_rewind(). Ownership of the returned cell is retained by
* the cell list. */
inline static struct cell *
cell_list_find (struct cell_list *cells, int x)
{
struct cell *tail = cells->cursor;
 
if (tail->x == x)
return tail;
 
while (1) {
UNROLL3({
if (tail->next->x > x)
break;
tail = tail->next;
});
}
 
if (tail->x != x)
tail = cell_list_alloc (cells, tail, x);
return cells->cursor = tail;
 
}
 
/* Find two cells at x1 and x2. This is exactly equivalent
* to
*
* pair.cell1 = cell_list_find(cells, x1);
* pair.cell2 = cell_list_find(cells, x2);
*
* except with less function call overhead. */
inline static struct cell_pair
cell_list_find_pair(struct cell_list *cells, int x1, int x2)
{
struct cell_pair pair;
 
pair.cell1 = cells->cursor;
while (1) {
UNROLL3({
if (pair.cell1->next->x > x1)
break;
pair.cell1 = pair.cell1->next;
});
}
if (pair.cell1->x != x1)
pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
 
pair.cell2 = pair.cell1;
while (1) {
UNROLL3({
if (pair.cell2->next->x > x2)
break;
pair.cell2 = pair.cell2->next;
});
}
if (pair.cell2->x != x2)
pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
 
cells->cursor = pair.cell2;
return pair;
}
 
/* Add a subpixel span covering [x1, x2) to the coverage cells. */
inline static void
cell_list_add_subspan(struct cell_list *cells,
grid_scaled_x_t x1,
grid_scaled_x_t x2)
{
int ix1, fx1;
int ix2, fx2;
 
if (x1 == x2)
return;
 
GRID_X_TO_INT_FRAC(x1, ix1, fx1);
GRID_X_TO_INT_FRAC(x2, ix2, fx2);
 
if (ix1 != ix2) {
struct cell_pair p;
p = cell_list_find_pair(cells, ix1, ix2);
p.cell1->uncovered_area += 2*fx1;
++p.cell1->covered_height;
p.cell2->uncovered_area -= 2*fx2;
--p.cell2->covered_height;
} else {
struct cell *cell = cell_list_find(cells, ix1);
cell->uncovered_area += 2*(fx1-fx2);
}
}
 
/* Adds the analytical coverage of an edge crossing the current pixel
* row to the coverage cells and advances the edge's x position to the
* following row.
*
* This function is only called when we know that during this pixel row:
*
* 1) The relative order of all edges on the active list doesn't
* change. In particular, no edges intersect within this row to pixel
* precision.
*
* 2) No new edges start in this row.
*
* 3) No existing edges end mid-row.
*
* This function depends on being called with all edges from the
* active list in the order they appear on the list (i.e. with
* non-decreasing x-coordinate.) */
static void
cell_list_render_edge(struct cell_list *cells,
struct edge *edge,
int sign)
{
grid_scaled_x_t fx;
struct cell *cell;
int ix;
 
GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx);
 
/* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */
cell = cell_list_find(cells, ix);
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*2*fx*GRID_Y;
}
 
static void
polygon_init (struct polygon *polygon, jmp_buf *jmp)
{
polygon->ymin = polygon->ymax = 0;
polygon->y_buckets = polygon->y_buckets_embedded;
pool_init (polygon->edge_pool.base, jmp,
8192 - sizeof (struct _pool_chunk),
sizeof (polygon->edge_pool.embedded));
}
 
static void
polygon_fini (struct polygon *polygon)
{
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
 
pool_fini (polygon->edge_pool.base);
}
 
/* Empties the polygon of all edges. The polygon is then prepared to
* receive new edges and clip them to the vertical range
* [ymin,ymax). */
static glitter_status_t
polygon_reset (struct polygon *polygon,
grid_scaled_y_t ymin,
grid_scaled_y_t ymax)
{
unsigned h = ymax - ymin;
unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + GRID_Y-1, ymin);
 
pool_reset(polygon->edge_pool.base);
 
if (unlikely (h > 0x7FFFFFFFU - GRID_Y))
goto bail_no_mem; /* even if you could, you wouldn't want to. */
 
if (polygon->y_buckets != polygon->y_buckets_embedded)
free (polygon->y_buckets);
 
polygon->y_buckets = polygon->y_buckets_embedded;
if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
polygon->y_buckets = _cairo_malloc_ab (num_buckets,
sizeof (struct edge *));
if (unlikely (NULL == polygon->y_buckets))
goto bail_no_mem;
}
memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
 
polygon->ymin = ymin;
polygon->ymax = ymax;
return GLITTER_STATUS_SUCCESS;
 
bail_no_mem:
polygon->ymin = 0;
polygon->ymax = 0;
return GLITTER_STATUS_NO_MEMORY;
}
 
static void
_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
struct edge *e)
{
unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
struct edge **ptail = &polygon->y_buckets[ix];
e->next = *ptail;
*ptail = e;
}
 
inline static void
polygon_add_edge (struct polygon *polygon,
const cairo_edge_t *edge)
{
struct edge *e;
grid_scaled_x_t dx;
grid_scaled_y_t dy;
grid_scaled_y_t ytop, ybot;
grid_scaled_y_t ymin = polygon->ymin;
grid_scaled_y_t ymax = polygon->ymax;
 
if (unlikely (edge->top >= ymax || edge->bottom <= ymin))
return;
 
e = pool_alloc (polygon->edge_pool.base, sizeof (struct edge));
 
dx = edge->line.p2.x - edge->line.p1.x;
dy = edge->line.p2.y - edge->line.p1.y;
e->dy = dy;
e->dir = edge->dir;
 
ytop = edge->top >= ymin ? edge->top : ymin;
ybot = edge->bottom <= ymax ? edge->bottom : ymax;
e->ytop = ytop;
e->height_left = ybot - ytop;
 
if (dx == 0) {
e->vertical = TRUE;
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
e->dxdy.quo = 0;
e->dxdy.rem = 0;
} else {
e->vertical = FALSE;
e->dxdy = floored_divrem (dx, dy);
if (ytop == edge->line.p1.y) {
e->x.quo = edge->line.p1.x;
e->x.rem = 0;
} else {
e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
e->x.quo += edge->line.p1.x;
}
}
 
_polygon_insert_edge_into_its_y_bucket (polygon, e);
 
e->x.rem -= dy; /* Bias the remainder for faster
* edge advancement. */
}
 
static void
active_list_reset (struct active_list *active)
{
active->head.vertical = 1;
active->head.height_left = INT_MAX;
active->head.x.quo = INT_MIN;
active->head.prev = NULL;
active->head.next = &active->tail;
active->tail.prev = &active->head;
active->tail.next = NULL;
active->tail.x.quo = INT_MAX;
active->tail.height_left = INT_MAX;
active->tail.vertical = 1;
active->min_height = 0;
active->is_vertical = 1;
}
 
static void
active_list_init(struct active_list *active)
{
active_list_reset(active);
}
 
/*
* Merge two sorted edge lists.
* Input:
* - head_a: The head of the first list.
* - head_b: The head of the second list; head_b cannot be NULL.
* Output:
* Returns the head of the merged list.
*
* Implementation notes:
* To make it fast (in particular, to reduce to an insertion sort whenever
* one of the two input lists only has a single element) we iterate through
* a list until its head becomes greater than the head of the other list,
* then we switch their roles. As soon as one of the two lists is empty, we
* just attach the other one to the current list and exit.
* Writes to memory are only needed to "switch" lists (as it also requires
* attaching to the output list the list which we will be iterating next) and
* to attach the last non-empty list.
*/
static struct edge *
merge_sorted_edges (struct edge *head_a, struct edge *head_b)
{
struct edge *head, **next, *prev;
int32_t x;
 
prev = head_a->prev;
next = &head;
if (head_a->x.quo <= head_b->x.quo) {
head = head_a;
} else {
head = head_b;
head_b->prev = prev;
goto start_with_b;
}
 
do {
x = head_b->x.quo;
while (head_a != NULL && head_a->x.quo <= x) {
prev = head_a;
next = &head_a->next;
head_a = head_a->next;
}
 
head_b->prev = prev;
*next = head_b;
if (head_a == NULL)
return head;
 
start_with_b:
x = head_a->x.quo;
while (head_b != NULL && head_b->x.quo <= x) {
prev = head_b;
next = &head_b->next;
head_b = head_b->next;
}
 
head_a->prev = prev;
*next = head_a;
if (head_b == NULL)
return head;
} while (1);
}
 
/*
* Sort (part of) a list.
* Input:
* - list: The list to be sorted; list cannot be NULL.
* - limit: Recursion limit.
* Output:
* - head_out: The head of the sorted list containing the first 2^(level+1) elements of the
* input list; if the input list has fewer elements, head_out be a sorted list
* containing all the elements of the input list.
* Returns the head of the list of unprocessed elements (NULL if the sorted list contains
* all the elements of the input list).
*
* Implementation notes:
* Special case single element list, unroll/inline the sorting of the first two elements.
* Some tail recursion is used since we iterate on the bottom-up solution of the problem
* (we start with a small sorted list and keep merging other lists of the same size to it).
*/
static struct edge *
sort_edges (struct edge *list,
unsigned int level,
struct edge **head_out)
{
struct edge *head_other, *remaining;
unsigned int i;
 
head_other = list->next;
 
if (head_other == NULL) {
*head_out = list;
return NULL;
}
 
remaining = head_other->next;
if (list->x.quo <= head_other->x.quo) {
*head_out = list;
head_other->next = NULL;
} else {
*head_out = head_other;
head_other->prev = list->prev;
head_other->next = list;
list->prev = head_other;
list->next = NULL;
}
 
for (i = 0; i < level && remaining; i++) {
remaining = sort_edges (remaining, i, &head_other);
*head_out = merge_sorted_edges (*head_out, head_other);
}
 
return remaining;
}
 
static struct edge *
merge_unsorted_edges (struct edge *head, struct edge *unsorted)
{
sort_edges (unsorted, UINT_MAX, &unsorted);
return merge_sorted_edges (head, unsorted);
}
 
/* Test if the edges on the active list can be safely advanced by a
* full row without intersections or any edges ending. */
inline static int
can_do_full_row (struct active_list *active)
{
const struct edge *e;
 
/* Recomputes the minimum height of all edges on the active
* list if we have been dropping edges. */
if (active->min_height <= 0) {
int min_height = INT_MAX;
int is_vertical = 1;
 
e = active->head.next;
while (NULL != e) {
if (e->height_left < min_height)
min_height = e->height_left;
is_vertical &= e->vertical;
e = e->next;
}
 
active->is_vertical = is_vertical;
active->min_height = min_height;
}
 
if (active->min_height < GRID_Y)
return 0;
 
return active->is_vertical;
}
 
/* Merges edges on the given subpixel row from the polygon to the
* active_list. */
inline static void
active_list_merge_edges_from_bucket(struct active_list *active,
struct edge *edges)
{
active->head.next = merge_unsorted_edges (active->head.next, edges);
}
 
inline static void
polygon_fill_buckets (struct active_list *active,
struct edge *edge,
int y,
struct edge **buckets)
{
grid_scaled_y_t min_height = active->min_height;
int is_vertical = active->is_vertical;
 
while (edge) {
struct edge *next = edge->next;
int suby = edge->ytop - y;
if (buckets[suby])
buckets[suby]->prev = edge;
edge->next = buckets[suby];
edge->prev = NULL;
buckets[suby] = edge;
if (edge->height_left < min_height)
min_height = edge->height_left;
is_vertical &= edge->vertical;
edge = next;
}
 
active->is_vertical = is_vertical;
active->min_height = min_height;
}
 
inline static void
sub_row (struct active_list *active,
struct cell_list *coverages,
unsigned int mask)
{
struct edge *edge = active->head.next;
int xstart = INT_MIN, prev_x = INT_MIN;
int winding = 0;
 
cell_list_rewind (coverages);
 
while (&active->tail != edge) {
struct edge *next = edge->next;
int xend = edge->x.quo;
 
if (--edge->height_left) {
edge->x.quo += edge->dxdy.quo;
edge->x.rem += edge->dxdy.rem;
if (edge->x.rem >= 0) {
++edge->x.quo;
edge->x.rem -= edge->dy;
}
 
if (edge->x.quo < prev_x) {
struct edge *pos = edge->prev;
pos->next = next;
next->prev = pos;
do {
pos = pos->prev;
} while (edge->x.quo < pos->x.quo);
pos->next->prev = edge;
edge->next = pos->next;
edge->prev = pos;
pos->next = edge;
} else
prev_x = edge->x.quo;
} else {
edge->prev->next = next;
next->prev = edge->prev;
}
 
winding += edge->dir;
if ((winding & mask) == 0) {
if (next->x.quo != xend) {
cell_list_add_subspan (coverages, xstart, xend);
xstart = INT_MIN;
}
} else if (xstart == INT_MIN)
xstart = xend;
 
edge = next;
}
}
 
inline static void dec (struct edge *e, int h)
{
e->height_left -= h;
if (e->height_left == 0) {
e->prev->next = e->next;
e->next->prev = e->prev;
}
}
 
static void
full_row (struct active_list *active,
struct cell_list *coverages,
unsigned int mask)
{
struct edge *left = active->head.next;
 
while (&active->tail != left) {
struct edge *right;
int winding;
 
dec (left, GRID_Y);
 
winding = left->dir;
right = left->next;
do {
dec (right, GRID_Y);
 
winding += right->dir;
if ((winding & mask) == 0 && right->next->x.quo != right->x.quo)
break;
 
right = right->next;
} while (1);
 
cell_list_set_rewind (coverages);
cell_list_render_edge (coverages, left, +1);
cell_list_render_edge (coverages, right, -1);
 
left = right->next;
}
}
 
static void
_glitter_scan_converter_init(glitter_scan_converter_t *converter, jmp_buf *jmp)
{
polygon_init(converter->polygon, jmp);
active_list_init(converter->active);
cell_list_init(converter->coverages, jmp);
converter->xmin=0;
converter->ymin=0;
converter->xmax=0;
converter->ymax=0;
}
 
static void
_glitter_scan_converter_fini(glitter_scan_converter_t *self)
{
if (self->spans != self->spans_embedded)
free (self->spans);
 
polygon_fini(self->polygon);
cell_list_fini(self->coverages);
 
self->xmin=0;
self->ymin=0;
self->xmax=0;
self->ymax=0;
}
 
static grid_scaled_t
int_to_grid_scaled(int i, int scale)
{
/* Clamp to max/min representable scaled number. */
if (i >= 0) {
if (i >= INT_MAX/scale)
i = INT_MAX/scale;
}
else {
if (i <= INT_MIN/scale)
i = INT_MIN/scale;
}
return i*scale;
}
 
#define int_to_grid_scaled_x(x) int_to_grid_scaled((x), GRID_X)
#define int_to_grid_scaled_y(x) int_to_grid_scaled((x), GRID_Y)
 
I glitter_status_t
glitter_scan_converter_reset(
glitter_scan_converter_t *converter,
int xmin, int ymin,
int xmax, int ymax)
{
glitter_status_t status;
 
converter->xmin = 0; converter->xmax = 0;
converter->ymin = 0; converter->ymax = 0;
 
if (xmax - xmin > ARRAY_LENGTH(converter->spans_embedded)) {
converter->spans = _cairo_malloc_ab (xmax - xmin,
sizeof (cairo_half_open_span_t));
if (unlikely (converter->spans == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
} else
converter->spans = converter->spans_embedded;
 
xmin = int_to_grid_scaled_x(xmin);
ymin = int_to_grid_scaled_y(ymin);
xmax = int_to_grid_scaled_x(xmax);
ymax = int_to_grid_scaled_y(ymax);
 
active_list_reset(converter->active);
cell_list_reset(converter->coverages);
status = polygon_reset(converter->polygon, ymin, ymax);
if (status)
return status;
 
converter->xmin = xmin;
converter->xmax = xmax;
converter->ymin = ymin;
converter->ymax = ymax;
return GLITTER_STATUS_SUCCESS;
}
 
/* INPUT_TO_GRID_X/Y (in_coord, out_grid_scaled, grid_scale)
* These macros convert an input coordinate in the client's
* device space to the rasterisation grid.
*/
/* Gah.. this bit of ugly defines INPUT_TO_GRID_X/Y so as to use
* shifts if possible, and something saneish if not.
*/
#if !defined(INPUT_TO_GRID_Y) && defined(GRID_Y_BITS) && GRID_Y_BITS <= GLITTER_INPUT_BITS
# define INPUT_TO_GRID_Y(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_Y_BITS)
#else
# define INPUT_TO_GRID_Y(in, out) INPUT_TO_GRID_general(in, out, GRID_Y)
#endif
 
#if !defined(INPUT_TO_GRID_X) && defined(GRID_X_BITS) && GRID_X_BITS <= GLITTER_INPUT_BITS
# define INPUT_TO_GRID_X(in, out) (out) = (in) >> (GLITTER_INPUT_BITS - GRID_X_BITS)
#else
# define INPUT_TO_GRID_X(in, out) INPUT_TO_GRID_general(in, out, GRID_X)
#endif
 
#define INPUT_TO_GRID_general(in, out, grid_scale) do { \
long long tmp__ = (long long)(grid_scale) * (in); \
tmp__ >>= GLITTER_INPUT_BITS; \
(out) = tmp__; \
} while (0)
 
/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan
* converter. The coordinates represent pixel positions scaled by
* 2**GLITTER_PIXEL_BITS. If this function fails then the scan
* converter should be reset or destroyed. Dir must be +1 or -1,
* with the latter reversing the orientation of the edge. */
I void
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
const cairo_edge_t *edge)
{
cairo_edge_t e;
 
INPUT_TO_GRID_Y (edge->top, e.top);
INPUT_TO_GRID_Y (edge->bottom, e.bottom);
if (e.top >= e.bottom)
return;
 
/* XXX: possible overflows if GRID_X/Y > 2**GLITTER_INPUT_BITS */
INPUT_TO_GRID_Y (edge->line.p1.y, e.line.p1.y);
INPUT_TO_GRID_Y (edge->line.p2.y, e.line.p2.y);
if (e.line.p1.y == e.line.p2.y)
e.line.p2.y++; /* Fudge to prevent div-by-zero */
 
INPUT_TO_GRID_X (edge->line.p1.x, e.line.p1.x);
INPUT_TO_GRID_X (edge->line.p2.x, e.line.p2.x);
 
e.dir = edge->dir;
 
polygon_add_edge (converter->polygon, &e);
}
 
static void
step_edges (struct active_list *active, int count)
{
struct edge *edge;
 
count *= GRID_Y;
for (edge = active->head.next; edge != &active->tail; edge = edge->next) {
edge->height_left -= count;
if (! edge->height_left) {
edge->prev->next = edge->next;
edge->next->prev = edge->prev;
}
}
}
 
static glitter_status_t
blit_a8 (struct cell_list *cells,
cairo_span_renderer_t *renderer,
cairo_half_open_span_t *spans,
int y, int height,
int xmin, int xmax)
{
struct cell *cell = cells->head.next;
int prev_x = xmin, last_x = -1;
int16_t cover = 0, last_cover = 0;
unsigned num_spans;
 
if (cell == &cells->tail)
return CAIRO_STATUS_SUCCESS;
 
/* Skip cells to the left of the clip region. */
while (cell->x < xmin) {
cover += cell->covered_height;
cell = cell->next;
}
cover *= GRID_X*2;
 
/* Form the spans from the coverages and areas. */
num_spans = 0;
for (; cell->x < xmax; cell = cell->next) {
int x = cell->x;
int16_t area;
 
if (x > prev_x && cover != last_cover) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
++num_spans;
}
 
cover += cell->covered_height*GRID_X*2;
area = cover - cell->uncovered_area;
 
if (area != last_cover) {
spans[num_spans].x = x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (area);
last_cover = area;
last_x = x;
++num_spans;
}
 
prev_x = x+1;
}
 
if (prev_x <= xmax && cover != last_cover) {
spans[num_spans].x = prev_x;
spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
last_cover = cover;
last_x = prev_x;
++num_spans;
}
 
if (last_x < xmax && last_cover) {
spans[num_spans].x = xmax;
spans[num_spans].coverage = 0;
++num_spans;
}
 
/* Dump them into the renderer. */
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
 
#define GRID_AREA_TO_A1(A) ((GRID_AREA_TO_ALPHA (A) > 127) ? 255 : 0)
static glitter_status_t
blit_a1 (struct cell_list *cells,
cairo_span_renderer_t *renderer,
cairo_half_open_span_t *spans,
int y, int height,
int xmin, int xmax)
{
struct cell *cell = cells->head.next;
int prev_x = xmin, last_x = -1;
int16_t cover = 0;
uint8_t coverage, last_cover = 0;
unsigned num_spans;
 
if (cell == &cells->tail)
return CAIRO_STATUS_SUCCESS;
 
/* Skip cells to the left of the clip region. */
while (cell->x < xmin) {
cover += cell->covered_height;
cell = cell->next;
}
cover *= GRID_X*2;
 
/* Form the spans from the coverages and areas. */
num_spans = 0;
for (; cell->x < xmax; cell = cell->next) {
int x = cell->x;
int16_t area;
 
coverage = GRID_AREA_TO_A1 (cover);
if (x > prev_x && coverage != last_cover) {
last_x = spans[num_spans].x = prev_x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
cover += cell->covered_height*GRID_X*2;
area = cover - cell->uncovered_area;
 
coverage = GRID_AREA_TO_A1 (area);
if (coverage != last_cover) {
last_x = spans[num_spans].x = x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
prev_x = x+1;
}
 
coverage = GRID_AREA_TO_A1 (cover);
if (prev_x <= xmax && coverage != last_cover) {
last_x = spans[num_spans].x = prev_x;
last_cover = spans[num_spans].coverage = coverage;
++num_spans;
}
 
if (last_x < xmax && last_cover) {
spans[num_spans].x = xmax;
spans[num_spans].coverage = 0;
++num_spans;
}
if (num_spans == 1)
return CAIRO_STATUS_SUCCESS;
 
/* Dump them into the renderer. */
return renderer->render_rows (renderer, y, height, spans, num_spans);
}
 
 
I void
glitter_scan_converter_render(glitter_scan_converter_t *converter,
unsigned int winding_mask,
int antialias,
cairo_span_renderer_t *renderer)
{
int i, j;
int ymax_i = converter->ymax / GRID_Y;
int ymin_i = converter->ymin / GRID_Y;
int xmin_i, xmax_i;
int h = ymax_i - ymin_i;
struct polygon *polygon = converter->polygon;
struct cell_list *coverages = converter->coverages;
struct active_list *active = converter->active;
struct edge *buckets[GRID_Y] = { 0 };
 
xmin_i = converter->xmin / GRID_X;
xmax_i = converter->xmax / GRID_X;
if (xmin_i >= xmax_i)
return;
 
/* Render each pixel row. */
for (i = 0; i < h; i = j) {
int do_full_row = 0;
 
j = i + 1;
 
/* Determine if we can ignore this row or use the full pixel
* stepper. */
if (! polygon->y_buckets[i]) {
if (active->head.next == &active->tail) {
active->min_height = INT_MAX;
active->is_vertical = 1;
for (; j < h && ! polygon->y_buckets[j]; j++)
;
continue;
}
 
do_full_row = can_do_full_row (active);
}
 
if (do_full_row) {
/* Step by a full pixel row's worth. */
full_row (active, coverages, winding_mask);
 
if (active->is_vertical) {
while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
{
active->min_height -= GRID_Y;
j++;
}
if (j != i + 1)
step_edges (active, j - (i + 1));
}
} else {
int sub;
 
polygon_fill_buckets (active,
polygon->y_buckets[i],
(i+ymin_i)*GRID_Y,
buckets);
 
/* Subsample this row. */
for (sub = 0; sub < GRID_Y; sub++) {
if (buckets[sub]) {
active_list_merge_edges_from_bucket (active, buckets[sub]);
buckets[sub] = NULL;
}
 
sub_row (active, coverages, winding_mask);
}
}
 
if (antialias)
blit_a8 (coverages, renderer, converter->spans,
i+ymin_i, j-i, xmin_i, xmax_i);
else
blit_a1 (coverages, renderer, converter->spans,
i+ymin_i, j-i, xmin_i, xmax_i);
cell_list_reset (coverages);
 
active->min_height -= GRID_Y;
}
}
 
struct _cairo_tor22_scan_converter {
cairo_scan_converter_t base;
 
glitter_scan_converter_t converter[1];
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
 
jmp_buf jmp;
};
 
typedef struct _cairo_tor22_scan_converter cairo_tor22_scan_converter_t;
 
static void
_cairo_tor22_scan_converter_destroy (void *converter)
{
cairo_tor22_scan_converter_t *self = converter;
if (self == NULL) {
return;
}
_glitter_scan_converter_fini (self->converter);
free(self);
}
 
cairo_status_t
_cairo_tor22_scan_converter_add_polygon (void *converter,
const cairo_polygon_t *polygon)
{
cairo_tor22_scan_converter_t *self = converter;
int i;
 
#if 0
FILE *file = fopen ("polygon.txt", "w");
_cairo_debug_print_polygon (file, polygon);
fclose (file);
#endif
 
for (i = 0; i < polygon->num_edges; i++)
glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_tor22_scan_converter_generate (void *converter,
cairo_span_renderer_t *renderer)
{
cairo_tor22_scan_converter_t *self = converter;
cairo_status_t status;
 
if ((status = setjmp (self->jmp)))
return _cairo_scan_converter_set_error (self, _cairo_error (status));
 
glitter_scan_converter_render (self->converter,
self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
self->antialias != CAIRO_ANTIALIAS_NONE,
renderer);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_scan_converter_t *
_cairo_tor22_scan_converter_create (int xmin,
int ymin,
int xmax,
int ymax,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias)
{
cairo_tor22_scan_converter_t *self;
cairo_status_t status;
 
self = malloc (sizeof(struct _cairo_tor22_scan_converter));
if (unlikely (self == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto bail_nomem;
}
 
self->base.destroy = _cairo_tor22_scan_converter_destroy;
self->base.generate = _cairo_tor22_scan_converter_generate;
 
_glitter_scan_converter_init (self->converter, &self->jmp);
status = glitter_scan_converter_reset (self->converter,
xmin, ymin, xmax, ymax);
if (unlikely (status))
goto bail;
 
self->fill_rule = fill_rule;
self->antialias = antialias;
 
return &self->base;
 
bail:
self->base.destroy(&self->base);
bail_nomem:
return _cairo_scan_converter_create_in_error (status);
}
/programs/develop/libraries/cairo/src/cairo-toy-font-face.c
148,7 → 148,6
hash += ((unsigned long) slant) * 1607;
hash += ((unsigned long) weight) * 1451;
 
assert (hash != 0);
key->base.hash_entry.hash = hash;
}
 
303,9 → 302,7
&key.base.hash_entry);
if (font_face != NULL) {
if (font_face->base.status == CAIRO_STATUS_SUCCESS) {
/* We increment the reference count here manually to avoid
double-locking. */
_cairo_reference_count_inc (&font_face->base.ref_count);
cairo_font_face_reference (&font_face->base);
_cairo_toy_font_face_hash_table_unlock ();
return &font_face->base;
}
312,7 → 309,6
 
/* remove the bad font from the hash table */
_cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
font_face->base.hash_entry.hash = 0;
}
 
/* Otherwise create it and insert into hash table. */
352,10 → 348,6
cairo_toy_font_face_t *font_face = abstract_face;
cairo_hash_table_t *hash_table;
 
if (font_face == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->base.ref_count))
return;
 
hash_table = _cairo_toy_font_face_hash_table_lock ();
/* All created objects must have been mapped in the hash table. */
assert (hash_table != NULL);
366,7 → 358,12
return;
}
 
if (font_face->base.hash_entry.hash != 0)
/* Font faces in SUCCESS status are guaranteed to be in the
* hashtable. Font faces in an error status are removed from the
* hashtable if they are found during a lookup, thus they should
* only be removed if they are in the hashtable. */
if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) ||
_cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face)
_cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
 
_cairo_toy_font_face_hash_table_unlock ();
/programs/develop/libraries/cairo/src/cairo-traps-compositor.c
0,0 → 1,2333
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-pattern-inline.h"
#include "cairo-paginated-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-observer-private.h"
#include "cairo-region-private.h"
#include "cairo-spans-private.h"
#include "cairo-traps-private.h"
#include "cairo-tristrip-private.h"
 
typedef cairo_int_status_t
(*draw_func_t) (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip);
 
static void do_unaligned_row(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b,
int tx, int y, int h,
uint16_t coverage)
{
int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
if (x2 > x1) {
if (! _cairo_fixed_is_integer (b->p1.x)) {
blt(closure, x1, y, 1, h,
coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
x1++;
}
 
if (x2 > x1)
blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
 
if (! _cairo_fixed_is_integer (b->p2.x))
blt(closure, x2, y, 1, h,
coverage * _cairo_fixed_fractional_part (b->p2.x));
} else
blt(closure, x1, y, 1, h,
coverage * (b->p2.x - b->p1.x));
}
 
static void do_unaligned_box(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b, int tx, int ty)
{
int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
if (y2 > y1) {
if (! _cairo_fixed_is_integer (b->p1.y)) {
do_unaligned_row(blt, closure, b, tx, y1, 1,
256 - _cairo_fixed_fractional_part (b->p1.y));
y1++;
}
 
if (y2 > y1)
do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
 
if (! _cairo_fixed_is_integer (b->p2.y))
do_unaligned_row(blt, closure, b, tx, y2, 1,
_cairo_fixed_fractional_part (b->p2.y));
} else
do_unaligned_row(blt, closure, b, tx, y1, 1,
b->p2.y - b->p1.y);
}
 
struct blt_in {
const cairo_traps_compositor_t *compositor;
cairo_surface_t *dst;
cairo_boxes_t boxes;
};
 
static void blt_in(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct blt_in *info = closure;
cairo_color_t color;
 
if (CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage))
return;
 
_cairo_box_from_integers (&info->boxes.chunks.base[0], x, y, w, h);
 
_cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff);
info->compositor->fill_boxes (info->dst,
CAIRO_OPERATOR_IN, &color,
&info->boxes);
}
 
static void
add_rect_with_offset (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2, int dx, int dy)
{
cairo_box_t box;
cairo_int_status_t status;
 
box.p1.x = _cairo_fixed_from_int (x1 - dx);
box.p1.y = _cairo_fixed_from_int (y1 - dy);
box.p2.x = _cairo_fixed_from_int (x2 - dx);
box.p2.y = _cairo_fixed_from_int (y2 - dy);
 
status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
static cairo_int_status_t
combine_clip_as_traps (const cairo_traps_compositor_t *compositor,
cairo_surface_t *mask,
const cairo_clip_t *clip,
const cairo_rectangle_int_t *extents)
{
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_traps_t traps;
cairo_surface_t *src;
cairo_box_t box;
cairo_rectangle_int_t fixup;
int src_x, src_y;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &antialias);
if (status)
return status;
 
_cairo_traps_init (&traps);
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
fill_rule);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
return status;
 
src = compositor->pattern_to_surface (mask, NULL, FALSE,
extents, NULL,
&src_x, &src_y);
if (unlikely (src->status)) {
_cairo_traps_fini (&traps);
return src->status;
}
 
status = compositor->composite_traps (mask, CAIRO_OPERATOR_IN, src,
src_x, src_y,
extents->x, extents->y,
extents,
antialias, &traps);
 
_cairo_traps_extents (&traps, &box);
_cairo_box_round_to_rectangle (&box, &fixup);
_cairo_traps_fini (&traps);
cairo_surface_destroy (src);
 
if (unlikely (status))
return status;
 
if (! _cairo_rectangle_intersect (&fixup, extents))
return CAIRO_STATUS_SUCCESS;
 
if (fixup.width < extents->width || fixup.height < extents->height) {
cairo_boxes_t clear;
 
_cairo_boxes_init (&clear);
 
/* top */
if (fixup.y != extents->y) {
add_rect_with_offset (&clear,
extents->x, extents->y,
extents->x + extents->width,
fixup.y,
extents->x, extents->y);
}
/* left */
if (fixup.x != extents->x) {
add_rect_with_offset (&clear,
extents->x, fixup.y,
fixup.x,
fixup.y + fixup.height,
extents->x, extents->y);
}
/* right */
if (fixup.x + fixup.width != extents->x + extents->width) {
add_rect_with_offset (&clear,
fixup.x + fixup.width,
fixup.y,
extents->x + extents->width,
fixup.y + fixup.height,
extents->x, extents->y);
}
/* bottom */
if (fixup.y + fixup.height != extents->y + extents->height) {
add_rect_with_offset (&clear,
extents->x,
fixup.y + fixup.height,
extents->x + extents->width,
extents->y + extents->height,
extents->x, extents->y);
}
 
status = compositor->fill_boxes (mask,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
 
_cairo_boxes_fini (&clear);
}
 
return status;
}
 
static cairo_status_t
__clip_to_surface (const cairo_traps_compositor_t *compositor,
const cairo_composite_rectangles_t *composite,
const cairo_rectangle_int_t *extents,
cairo_surface_t **surface)
{
cairo_surface_t *mask;
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_traps_t traps;
cairo_boxes_t clear;
cairo_surface_t *src;
int src_x, src_y;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = _cairo_clip_get_polygon (composite->clip, &polygon,
&fill_rule, &antialias);
if (status)
return status;
 
_cairo_traps_init (&traps);
status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
&polygon,
fill_rule);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
return status;
 
mask = _cairo_surface_create_similar_scratch (composite->surface,
CAIRO_CONTENT_ALPHA,
extents->width,
extents->height);
if (unlikely (mask->status)) {
_cairo_traps_fini (&traps);
return status;
}
 
src = compositor->pattern_to_surface (mask, NULL, FALSE,
extents, NULL,
&src_x, &src_y);
if (unlikely (status = src->status))
goto error;
 
status = compositor->acquire (mask);
if (unlikely (status))
goto error;
 
_cairo_boxes_init_from_rectangle (&clear,
0, 0,
extents->width,
extents->height);
status = compositor->fill_boxes (mask,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
if (unlikely (status))
goto error_release;
 
status = compositor->composite_traps (mask, CAIRO_OPERATOR_ADD, src,
src_x, src_y,
extents->x, extents->y,
extents,
antialias, &traps);
if (unlikely (status))
goto error_release;
 
compositor->release (mask);
*surface = mask;
out:
cairo_surface_destroy (src);
_cairo_traps_fini (&traps);
return status;
 
error_release:
compositor->release (mask);
error:
cairo_surface_destroy (mask);
goto out;
}
 
static cairo_surface_t *
traps_get_clip_surface (const cairo_traps_compositor_t *compositor,
const cairo_composite_rectangles_t *composite,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *surface = NULL;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = __clip_to_surface (compositor, composite, extents, &surface);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
surface = _cairo_surface_create_similar_solid (composite->surface,
CAIRO_CONTENT_ALPHA,
extents->width,
extents->height,
CAIRO_COLOR_WHITE);
if (unlikely (surface->status))
return surface;
 
status = _cairo_clip_combine_with_surface (composite->clip, surface,
extents->x, extents->y);
}
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
 
return surface;
}
 
static void blt_unaligned_boxes(const cairo_traps_compositor_t *compositor,
cairo_surface_t *surface,
int dx, int dy,
cairo_box_t *boxes,
int num_boxes)
{
struct blt_in info;
int i;
 
info.compositor = compositor;
info.dst = surface;
_cairo_boxes_init (&info.boxes);
info.boxes.num_boxes = 1;
for (i = 0; i < num_boxes; i++) {
cairo_box_t *b = &boxes[i];
 
if (! _cairo_fixed_is_integer (b->p1.x) ||
! _cairo_fixed_is_integer (b->p1.y) ||
! _cairo_fixed_is_integer (b->p2.x) ||
! _cairo_fixed_is_integer (b->p2.y))
{
do_unaligned_box(blt_in, &info, b, dx, dy);
}
}
}
 
static cairo_surface_t *
create_composite_mask (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *draw_closure,
draw_func_t draw_func,
draw_func_t mask_func,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *surface, *src;
cairo_int_status_t status;
int src_x, src_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
surface = _cairo_surface_create_similar_scratch (dst, CAIRO_CONTENT_ALPHA,
extents->bounded.width,
extents->bounded.height);
if (unlikely (surface->status))
return surface;
 
src = compositor->pattern_to_surface (surface,
&_cairo_pattern_white.base,
FALSE,
&extents->bounded,
&extents->bounded,
&src_x, &src_y);
if (unlikely (src->status)) {
cairo_surface_destroy (surface);
return src;
}
 
status = compositor->acquire (surface);
if (unlikely (status)) {
cairo_surface_destroy (src);
cairo_surface_destroy (surface);
return _cairo_surface_create_in_error (status);
}
 
if (!surface->is_clear) {
cairo_boxes_t clear;
 
_cairo_boxes_init_from_rectangle (&clear,
0, 0,
extents->bounded.width,
extents->bounded.height);
status = compositor->fill_boxes (surface,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
if (unlikely (status))
goto error;
 
surface->is_clear = TRUE;
}
 
if (mask_func) {
status = mask_func (compositor, surface, draw_closure,
CAIRO_OPERATOR_SOURCE, src, src_x, src_y,
extents->bounded.x, extents->bounded.y,
&extents->bounded, extents->clip);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
surface->is_clear = FALSE;
goto out;
}
if (unlikely (status != CAIRO_INT_STATUS_UNSUPPORTED))
goto error;
}
 
/* Is it worth setting the clip region here? */
status = draw_func (compositor, surface, draw_closure,
CAIRO_OPERATOR_ADD, src, src_x, src_y,
extents->bounded.x, extents->bounded.y,
&extents->bounded, NULL);
if (unlikely (status))
goto error;
 
surface->is_clear = FALSE;
if (extents->clip->path != NULL) {
status = combine_clip_as_traps (compositor, surface,
extents->clip, &extents->bounded);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_clip_combine_with_surface (extents->clip, surface,
extents->bounded.x,
extents->bounded.y);
}
if (unlikely (status))
goto error;
} else if (extents->clip->boxes) {
blt_unaligned_boxes(compositor, surface,
extents->bounded.x, extents->bounded.y,
extents->clip->boxes, extents->clip->num_boxes);
 
}
 
out:
compositor->release (surface);
cairo_surface_destroy (src);
return surface;
 
error:
compositor->release (surface);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
cairo_surface_destroy (src);
return surface;
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
clip_and_composite_with_mask (const cairo_traps_compositor_t *compositor,
const cairo_composite_rectangles_t*extents,
draw_func_t draw_func,
draw_func_t mask_func,
void *draw_closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x, int src_y)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *mask;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
mask = create_composite_mask (compositor, dst, draw_closure,
draw_func, mask_func,
extents);
if (unlikely (mask->status))
return mask->status;
 
if (mask->is_clear)
goto skip;
 
if (src != NULL || dst->content != CAIRO_CONTENT_ALPHA) {
compositor->composite (dst, op, src, mask,
extents->bounded.x + src_x,
extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
} else {
compositor->composite (dst, op, mask, NULL,
0, 0,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
 
skip:
cairo_surface_destroy (mask);
return CAIRO_STATUS_SUCCESS;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
clip_and_composite_combine (const cairo_traps_compositor_t *compositor,
const cairo_composite_rectangles_t*extents,
draw_func_t draw_func,
void *draw_closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x, int src_y)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *tmp, *clip;
cairo_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
tmp = _cairo_surface_create_similar_scratch (dst, dst->content,
extents->bounded.width,
extents->bounded.height);
if (unlikely (tmp->status))
return tmp->status;
 
status = compositor->acquire (tmp);
if (unlikely (status)) {
cairo_surface_destroy (tmp);
return status;
}
 
compositor->composite (tmp,
dst->is_clear ? CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE,
dst, NULL,
extents->bounded.x, extents->bounded.y,
0, 0,
0, 0,
extents->bounded.width, extents->bounded.height);
 
status = draw_func (compositor, tmp, draw_closure, op,
src, src_x, src_y,
extents->bounded.x, extents->bounded.y,
&extents->bounded, NULL);
 
if (unlikely (status))
goto cleanup;
 
clip = traps_get_clip_surface (compositor, extents, &extents->bounded);
if (unlikely ((status = clip->status)))
goto cleanup;
 
if (dst->is_clear) {
compositor->composite (dst, CAIRO_OPERATOR_SOURCE, tmp, clip,
0, 0,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
} else {
compositor->lerp (dst, tmp, clip,
0, 0,
0,0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
cairo_surface_destroy (clip);
 
cleanup:
compositor->release (tmp);
cairo_surface_destroy (tmp);
 
return status;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
clip_and_composite_source (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
draw_func_t draw_func,
draw_func_t mask_func,
void *draw_closure,
cairo_surface_t *src,
int src_x,
int src_y,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *mask;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* Create a surface that is mask IN clip */
mask = create_composite_mask (compositor, dst, draw_closure,
draw_func, mask_func,
extents);
if (unlikely (mask->status))
return mask->status;
 
if (mask->is_clear)
goto skip;
 
if (dst->is_clear) {
compositor->composite (dst, CAIRO_OPERATOR_SOURCE, src, mask,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
} else {
compositor->lerp (dst, src, mask,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
}
 
skip:
cairo_surface_destroy (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
can_reduce_alpha_op (cairo_operator_t op)
{
int iop = op;
switch (iop) {
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_ADD:
return TRUE;
default:
return FALSE;
}
}
 
static cairo_bool_t
reduce_alpha_op (cairo_composite_rectangles_t *extents)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
const cairo_pattern_t *pattern = &extents->source_pattern.base;
return dst->is_clear &&
dst->content == CAIRO_CONTENT_ALPHA &&
_cairo_pattern_is_opaque_solid (pattern) &&
can_reduce_alpha_op (op);
}
 
static cairo_status_t
fixup_unbounded_with_mask (const cairo_traps_compositor_t *compositor,
const cairo_composite_rectangles_t *extents)
{
cairo_surface_t *dst = extents->surface;
cairo_surface_t *mask;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
/* XXX can we avoid querying the clip surface again? */
mask = traps_get_clip_surface (compositor, extents, &extents->unbounded);
if (unlikely (mask->status))
return mask->status;
 
/* top */
if (extents->bounded.y != extents->unbounded.y) {
int x = extents->unbounded.x;
int y = extents->unbounded.y;
int width = extents->unbounded.width;
int height = extents->bounded.y - y;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
0, 0,
0, 0,
x, y,
width, height);
}
 
/* left */
if (extents->bounded.x != extents->unbounded.x) {
int x = extents->unbounded.x;
int y = extents->bounded.y;
int width = extents->bounded.x - x;
int height = extents->bounded.height;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
0, y - extents->unbounded.y,
0, 0,
x, y,
width, height);
}
 
/* right */
if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
int x = extents->bounded.x + extents->bounded.width;
int y = extents->bounded.y;
int width = extents->unbounded.x + extents->unbounded.width - x;
int height = extents->bounded.height;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
x - extents->unbounded.x, y - extents->unbounded.y,
0, 0,
x, y,
width, height);
}
 
/* bottom */
if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
int x = extents->unbounded.x;
int y = extents->bounded.y + extents->bounded.height;
int width = extents->unbounded.width;
int height = extents->unbounded.y + extents->unbounded.height - y;
 
compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, mask, NULL,
0, y - extents->unbounded.y,
0, 0,
x, y,
width, height);
}
 
cairo_surface_destroy (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
add_rect (cairo_boxes_t *boxes, int x1, int y1, int x2, int y2)
{
cairo_box_t box;
cairo_int_status_t status;
 
box.p1.x = _cairo_fixed_from_int (x1);
box.p1.y = _cairo_fixed_from_int (y1);
box.p2.x = _cairo_fixed_from_int (x2);
box.p2.y = _cairo_fixed_from_int (y2);
 
status = _cairo_boxes_add (boxes, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
static cairo_status_t
fixup_unbounded (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_boxes_t clear, tmp;
cairo_box_t box;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (extents->bounded.width == extents->unbounded.width &&
extents->bounded.height == extents->unbounded.height)
{
return CAIRO_STATUS_SUCCESS;
}
 
assert (extents->clip->path == NULL);
 
/* subtract the drawn boxes from the unbounded area */
_cairo_boxes_init (&clear);
 
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
 
if (boxes == NULL) {
if (extents->bounded.width == 0 || extents->bounded.height == 0) {
goto empty;
} else {
/* top */
if (extents->bounded.y != extents->unbounded.y) {
add_rect (&clear,
extents->unbounded.x, extents->unbounded.y,
extents->unbounded.x + extents->unbounded.width,
extents->bounded.y);
}
/* left */
if (extents->bounded.x != extents->unbounded.x) {
add_rect (&clear,
extents->unbounded.x, extents->bounded.y,
extents->bounded.x,
extents->bounded.y + extents->bounded.height);
}
/* right */
if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
add_rect (&clear,
extents->bounded.x + extents->bounded.width,
extents->bounded.y,
extents->unbounded.x + extents->unbounded.width,
extents->bounded.y + extents->bounded.height);
}
/* bottom */
if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
add_rect (&clear,
extents->unbounded.x,
extents->bounded.y + extents->bounded.height,
extents->unbounded.x + extents->unbounded.width,
extents->unbounded.y + extents->unbounded.height);
}
}
} else if (boxes->num_boxes) {
_cairo_boxes_init (&tmp);
 
assert (boxes->is_pixel_aligned);
 
status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
 
tmp.chunks.next = &boxes->chunks;
tmp.num_boxes += boxes->num_boxes;
 
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
CAIRO_FILL_RULE_WINDING,
&clear);
tmp.chunks.next = NULL;
if (unlikely (status))
goto error;
} else {
empty:
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
 
status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_INT_STATUS_SUCCESS);
}
 
/* Now intersect with the clip boxes */
if (extents->clip->num_boxes) {
_cairo_boxes_init_for_array (&tmp,
extents->clip->boxes,
extents->clip->num_boxes);
status = _cairo_boxes_intersect (&clear, &tmp, &clear);
if (unlikely (status))
goto error;
}
 
status = compositor->fill_boxes (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
 
error:
_cairo_boxes_fini (&clear);
return status;
}
 
enum {
NEED_CLIP_REGION = 0x1,
NEED_CLIP_SURFACE = 0x2,
FORCE_CLIP_REGION = 0x4,
};
 
static cairo_bool_t
need_bounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = 0;
 
if (extents->clip->num_boxes > 1 ||
extents->mask.width > extents->unbounded.width ||
extents->mask.height > extents->unbounded.height)
{
flags |= NEED_CLIP_REGION;
}
 
if (extents->clip->num_boxes > 1 ||
extents->mask.width > extents->bounded.width ||
extents->mask.height > extents->bounded.height)
{
flags |= FORCE_CLIP_REGION;
}
 
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
 
return flags;
}
 
static cairo_bool_t
need_unbounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = 0;
if (! extents->is_bounded) {
flags |= NEED_CLIP_REGION;
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
}
if (extents->clip->path != NULL)
flags |= NEED_CLIP_SURFACE;
return flags;
}
 
static cairo_status_t
clip_and_composite (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
draw_func_t draw_func,
draw_func_t mask_func,
void *draw_closure,
unsigned int need_clip)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
cairo_pattern_t *source = &extents->source_pattern.base;
cairo_surface_t *src;
int src_x, src_y;
cairo_region_t *clip_region = NULL;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (reduce_alpha_op (extents)) {
op = CAIRO_OPERATOR_ADD;
source = NULL;
}
 
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
source = NULL;
}
 
compositor->acquire (dst);
 
if (need_clip & NEED_CLIP_REGION) {
const cairo_rectangle_int_t *limit;
 
if ((need_clip & FORCE_CLIP_REGION) == 0)
limit = &extents->unbounded;
else
limit = &extents->destination;
 
clip_region = _cairo_clip_get_region (extents->clip);
if (clip_region != NULL &&
cairo_region_contains_rectangle (clip_region,
limit) == CAIRO_REGION_OVERLAP_IN)
clip_region = NULL;
 
if (clip_region != NULL) {
status = compositor->set_clip_region (dst, clip_region);
if (unlikely (status)) {
compositor->release (dst);
return status;
}
}
}
 
if (extents->bounded.width == 0 || extents->bounded.height == 0)
goto skip;
 
src = compositor->pattern_to_surface (dst, source, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (unlikely (status = src->status))
goto error;
 
if (op == CAIRO_OPERATOR_SOURCE) {
status = clip_and_composite_source (compositor, dst,
draw_func, mask_func, draw_closure,
src, src_x, src_y,
extents);
} else {
if (need_clip & NEED_CLIP_SURFACE) {
if (extents->is_bounded) {
status = clip_and_composite_with_mask (compositor, extents,
draw_func, mask_func,
draw_closure,
op, src, src_x, src_y);
} else {
status = clip_and_composite_combine (compositor, extents,
draw_func, draw_closure,
op, src, src_x, src_y);
}
} else {
status = draw_func (compositor,
dst, draw_closure,
op, src, src_x, src_y,
0, 0,
&extents->bounded,
extents->clip);
}
}
cairo_surface_destroy (src);
 
skip:
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
if (need_clip & NEED_CLIP_SURFACE)
status = fixup_unbounded_with_mask (compositor, extents);
else
status = fixup_unbounded (compositor, extents, NULL);
}
 
error:
if (clip_region)
compositor->set_clip_region (dst, NULL);
 
compositor->release (dst);
 
return status;
}
 
/* meta-ops */
 
typedef struct {
cairo_traps_t traps;
cairo_antialias_t antialias;
} composite_traps_info_t;
 
static cairo_int_status_t
composite_traps (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x, int src_y,
int dst_x, int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
composite_traps_info_t *info = closure;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
return compositor->composite_traps (dst, op, src,
src_x - dst_x, src_y - dst_y,
dst_x, dst_y,
extents,
info->antialias, &info->traps);
}
 
typedef struct {
cairo_tristrip_t strip;
cairo_antialias_t antialias;
} composite_tristrip_info_t;
 
static cairo_int_status_t
composite_tristrip (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x, int src_y,
int dst_x, int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
composite_tristrip_info_t *info = closure;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
return compositor->composite_tristrip (dst, op, src,
src_x - dst_x, src_y - dst_y,
dst_x, dst_y,
extents,
info->antialias, &info->strip);
}
 
static cairo_bool_t
is_recording_pattern (const cairo_pattern_t *pattern)
{
cairo_surface_t *surface;
 
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
return FALSE;
 
surface = ((const cairo_surface_pattern_t *) pattern)->surface;
surface = _cairo_surface_get_source (surface, NULL);
return _cairo_surface_is_recording (surface);
}
 
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;
return _cairo_surface_get_source (surface, NULL);
}
 
static cairo_bool_t
recording_pattern_contains_sample (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *sample)
{
cairo_recording_surface_t *surface;
 
if (! is_recording_pattern (pattern))
return FALSE;
 
if (pattern->extend == CAIRO_EXTEND_NONE)
return TRUE;
 
surface = (cairo_recording_surface_t *) recording_pattern_get_surface (pattern);
if (surface->unbounded)
return TRUE;
 
return _cairo_rectangle_contains_rectangle (&surface->extents, sample);
}
 
static cairo_bool_t
op_reduces_to_source (cairo_composite_rectangles_t *extents)
{
if (extents->op == CAIRO_OPERATOR_SOURCE)
return TRUE;
 
if (extents->surface->is_clear)
return extents->op == CAIRO_OPERATOR_OVER || extents->op == CAIRO_OPERATOR_ADD;
 
return FALSE;
}
 
static cairo_status_t
composite_aligned_boxes (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
cairo_operator_t op = extents->op;
cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (extents->clip);
cairo_bool_t op_is_source;
cairo_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (need_clip_mask &&
(! extents->is_bounded || extents->op == CAIRO_OPERATOR_SOURCE))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
op_is_source = op_reduces_to_source (extents);
 
/* Are we just copying a recording surface? */
if (! need_clip_mask && op_is_source &&
recording_pattern_contains_sample (&extents->source_pattern.base,
&extents->source_sample_area))
{
cairo_clip_t *recording_clip;
cairo_pattern_t *source = &extents->source_pattern.base;
 
/* XXX could also do tiling repeat modes... */
 
/* first clear the area about to be overwritten */
if (! dst->is_clear) {
status = compositor->acquire (dst);
if (unlikely (status))
return status;
 
status = compositor->fill_boxes (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
boxes);
compositor->release (dst);
if (unlikely (status))
return status;
}
 
recording_clip = _cairo_clip_from_boxes (boxes);
status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (source),
&source->matrix,
dst, recording_clip);
_cairo_clip_destroy (recording_clip);
 
return status;
}
 
status = compositor->acquire (dst);
if (unlikely (status))
return status;
 
if (! need_clip_mask &&
(op == CAIRO_OPERATOR_CLEAR ||
extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID))
{
const cairo_color_t *color;
 
if (op == CAIRO_OPERATOR_CLEAR) {
color = CAIRO_COLOR_TRANSPARENT;
} else {
color = &((cairo_solid_pattern_t *) &extents->source_pattern)->color;
if (op_is_source)
op = CAIRO_OPERATOR_SOURCE;
}
 
status = compositor->fill_boxes (dst, op, color, boxes);
}
else
{
cairo_surface_t *src, *mask = NULL;
cairo_pattern_t *source = &extents->source_pattern.base;
int src_x, src_y;
int mask_x = 0, mask_y = 0;
 
if (need_clip_mask) {
mask = traps_get_clip_surface (compositor,
extents, &extents->bounded);
if (unlikely (mask->status))
return mask->status;
 
mask_x = -extents->bounded.x;
mask_y = -extents->bounded.y;
 
if (op == CAIRO_OPERATOR_CLEAR) {
source = NULL;
op = CAIRO_OPERATOR_DEST_OUT;
}
} else if (op_is_source)
op = CAIRO_OPERATOR_SOURCE;
 
src = compositor->pattern_to_surface (dst, source, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (likely (src->status == CAIRO_STATUS_SUCCESS)) {
status = compositor->composite_boxes (dst, op, src, mask,
src_x, src_y,
mask_x, mask_y,
0, 0,
boxes, &extents->bounded);
cairo_surface_destroy (src);
} else
status = src->status;
 
cairo_surface_destroy (mask);
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
status = fixup_unbounded (compositor, extents, boxes);
 
compositor->release (dst);
 
return status;
}
 
static cairo_status_t
upload_boxes (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_surface_t *dst = extents->surface;
const cairo_pattern_t *source = &extents->source_pattern.base;
cairo_surface_t *src;
cairo_rectangle_int_t limit;
cairo_int_status_t status;
int tx, ty;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
src = _cairo_pattern_get_source((cairo_surface_pattern_t *)source,
&limit);
if (!(src->type == CAIRO_SURFACE_TYPE_IMAGE || src->type == dst->type))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Check that the data is entirely within the image */
if (extents->bounded.x + tx < limit.x || extents->bounded.y + ty < limit.y)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (extents->bounded.x + extents->bounded.width + tx > limit.x + limit.width ||
extents->bounded.y + extents->bounded.height + ty > limit.y + limit.height)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
tx += limit.x;
ty += limit.y;
 
if (src->type == CAIRO_SURFACE_TYPE_IMAGE)
status = compositor->draw_image_boxes (dst,
(cairo_image_surface_t *)src,
boxes, tx, ty);
else
status = compositor->copy_boxes (dst, src, boxes, &extents->bounded,
tx, ty);
 
return status;
}
 
static cairo_int_status_t
trim_extents_to_traps (cairo_composite_rectangles_t *extents,
cairo_traps_t *traps)
{
cairo_box_t box;
 
_cairo_traps_extents (traps, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_int_status_t
trim_extents_to_tristrip (cairo_composite_rectangles_t *extents,
cairo_tristrip_t *strip)
{
cairo_box_t box;
 
_cairo_tristrip_extents (strip, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_int_status_t
trim_extents_to_boxes (cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_box_t box;
 
_cairo_boxes_extents (boxes, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_int_status_t
boxes_for_traps (cairo_boxes_t *boxes,
cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
/* first check that the traps are rectilinear */
if (antialias == CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
const cairo_trapezoid_t *t = &traps->traps[i];
if (_cairo_fixed_integer_round_down (t->left.p1.x) !=
_cairo_fixed_integer_round_down (t->left.p2.x) ||
_cairo_fixed_integer_round_down (t->right.p1.x) !=
_cairo_fixed_integer_round_down (t->right.p2.x))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
} else {
for (i = 0; i < traps->num_traps; i++) {
const cairo_trapezoid_t *t = &traps->traps[i];
if (t->left.p1.x != t->left.p2.x || t->right.p1.x != t->right.p2.x)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
_cairo_boxes_init (boxes);
 
boxes->num_boxes = traps->num_traps;
boxes->chunks.base = (cairo_box_t *) traps->traps;
boxes->chunks.count = traps->num_traps;
boxes->chunks.size = traps->num_traps;
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
boxes->chunks.base[i].p1.x = x1;
boxes->chunks.base[i].p1.y = y1;
boxes->chunks.base[i].p2.x = x2;
boxes->chunks.base[i].p2.y = y2;
 
if (boxes->is_pixel_aligned) {
boxes->is_pixel_aligned =
_cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
_cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
}
}
} else {
boxes->is_pixel_aligned = TRUE;
 
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
/* round down here to match Pixman's behavior when using traps. */
boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
}
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static cairo_status_t
clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes);
 
static cairo_status_t
clip_and_composite_polygon (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_polygon_t *polygon,
cairo_antialias_t antialias,
cairo_fill_rule_t fill_rule,
cairo_bool_t curvy)
{
composite_traps_info_t traps;
cairo_surface_t *dst = extents->surface;
cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (polygon->num_edges == 0) {
status = CAIRO_INT_STATUS_SUCCESS;
 
if (! extents->is_bounded) {
cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
 
if (clip_region &&
cairo_region_contains_rectangle (clip_region,
&extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
clip_region = NULL;
 
if (clip_region != NULL) {
status = compositor->set_clip_region (dst, clip_region);
if (unlikely (status))
return status;
}
 
if (clip_surface)
status = fixup_unbounded_with_mask (compositor, extents);
else
status = fixup_unbounded (compositor, extents, NULL);
 
if (clip_region != NULL)
compositor->set_clip_region (dst, NULL);
}
 
return status;
}
 
if (extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t clipper;
cairo_fill_rule_t clipper_fill_rule;
cairo_antialias_t clipper_antialias;
 
status = _cairo_clip_get_polygon (extents->clip,
&clipper,
&clipper_fill_rule,
&clipper_antialias);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
if (clipper_antialias == antialias) {
status = _cairo_polygon_intersect (polygon, fill_rule,
&clipper, clipper_fill_rule);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
_cairo_clip_destroy (extents->clip);
extents->clip = clip;
 
fill_rule = CAIRO_FILL_RULE_WINDING;
}
_cairo_polygon_fini (&clipper);
}
}
}
 
if (antialias == CAIRO_ANTIALIAS_NONE && curvy) {
cairo_boxes_t boxes;
 
_cairo_boxes_init (&boxes);
status = _cairo_rasterise_polygon_to_boxes (polygon, fill_rule, &boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
assert (boxes.is_pixel_aligned);
status = clip_and_composite_boxes (compositor, extents, &boxes);
}
_cairo_boxes_fini (&boxes);
if ((status != CAIRO_INT_STATUS_UNSUPPORTED))
return status;
}
 
_cairo_traps_init (&traps.traps);
 
if (antialias == CAIRO_ANTIALIAS_NONE && curvy) {
status = _cairo_rasterise_polygon_to_traps (polygon, fill_rule, antialias, &traps.traps);
} else {
status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
}
if (unlikely (status))
goto CLEANUP_TRAPS;
 
status = trim_extents_to_traps (extents, &traps.traps);
if (unlikely (status))
goto CLEANUP_TRAPS;
 
/* Use a fast path if the trapezoids consist of a set of boxes. */
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (1) {
cairo_boxes_t boxes;
 
status = boxes_for_traps (&boxes, &traps.traps, antialias);
if (status == CAIRO_INT_STATUS_SUCCESS) {
status = clip_and_composite_boxes (compositor, extents, &boxes);
/* XXX need to reconstruct the traps! */
assert (status != CAIRO_INT_STATUS_UNSUPPORTED);
}
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
unsigned int flags = 0;
 
/* For unbounded operations, the X11 server will estimate the
* affected rectangle and apply the operation to that. However,
* there are cases where this is an overestimate (e.g. the
* clip-fill-{eo,nz}-unbounded test).
*
* The clip will trim that overestimate to our expectations.
*/
if (! extents->is_bounded)
flags |= FORCE_CLIP_REGION;
 
traps.antialias = antialias;
status = clip_and_composite (compositor, extents,
composite_traps, NULL, &traps,
need_unbounded_clip (extents) | flags);
}
 
CLEANUP_TRAPS:
_cairo_traps_fini (&traps.traps);
 
return status;
}
 
struct composite_opacity_info {
const cairo_traps_compositor_t *compositor;
uint8_t op;
cairo_surface_t *dst;
cairo_surface_t *src;
int src_x, src_y;
double opacity;
};
 
static void composite_opacity(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_opacity_info *info = closure;
const cairo_traps_compositor_t *compositor = info->compositor;
cairo_surface_t *mask;
int mask_x, mask_y;
cairo_color_t color;
cairo_solid_pattern_t solid;
 
_cairo_color_init_rgba (&color, 0, 0, 0, info->opacity * coverage);
_cairo_pattern_init_solid (&solid, &color);
mask = compositor->pattern_to_surface (info->dst, &solid.base, TRUE,
&_cairo_unbounded_rectangle,
&_cairo_unbounded_rectangle,
&mask_x, &mask_y);
if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
if (info->src) {
compositor->composite (info->dst, info->op, info->src, mask,
x + info->src_x, y + info->src_y,
mask_x, mask_y,
x, y,
w, h);
} else {
compositor->composite (info->dst, info->op, mask, NULL,
mask_x, mask_y,
0, 0,
x, y,
w, h);
}
}
 
cairo_surface_destroy (mask);
}
 
 
static cairo_int_status_t
composite_opacity_boxes (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
const cairo_solid_pattern_t *mask = closure;
struct composite_opacity_info info;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
info.compositor = compositor;
info.op = op;
info.dst = dst;
 
info.src = src;
info.src_x = src_x;
info.src_y = src_y;
 
info.opacity = mask->color.alpha / (double) 0xffff;
 
/* XXX for lots of boxes create a clip region for the fully opaque areas */
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_opacity, &info,
&clip->boxes[i], dst_x, dst_y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_boxes (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
cairo_traps_t traps;
cairo_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = _cairo_traps_init_boxes (&traps, closure);
if (unlikely (status))
return status;
 
status = compositor->composite_traps (dst, op, src,
src_x - dst_x, src_y - dst_y,
dst_x, dst_y,
extents,
CAIRO_ANTIALIAS_DEFAULT, &traps);
_cairo_traps_fini (&traps);
 
return status;
}
 
static cairo_status_t
clip_and_composite_boxes (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
status = trim_extents_to_boxes (extents, boxes);
if (unlikely (status))
return status;
 
if (boxes->is_pixel_aligned && extents->clip->path == NULL &&
extents->source_pattern.base.type == CAIRO_PATTERN_TYPE_SURFACE &&
(op_reduces_to_source (extents) ||
(extents->op == CAIRO_OPERATOR_OVER &&
(extents->source_pattern.surface.surface->content & CAIRO_CONTENT_ALPHA) == 0)))
{
status = upload_boxes (compositor, extents, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
/* Can we reduce drawing through a clip-mask to simply drawing the clip? */
if (extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_clip_t *clip;
 
clip = _cairo_clip_copy (extents->clip);
clip = _cairo_clip_intersect_boxes (clip, boxes);
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
 
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &antialias);
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
extents->clip = clip;
 
status = clip_and_composite_polygon (compositor, extents, &polygon,
antialias, fill_rule, FALSE);
 
clip = extents->clip;
extents->clip = saved_clip;
 
_cairo_polygon_fini (&polygon);
}
_cairo_clip_destroy (clip);
 
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
/* Use a fast path if the boxes are pixel aligned (or nearly aligned!) */
if (boxes->is_pixel_aligned) {
status = composite_aligned_boxes (compositor, extents, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
return clip_and_composite (compositor, extents,
composite_boxes, NULL, boxes,
need_unbounded_clip (extents));
}
 
static cairo_int_status_t
composite_traps_as_boxes (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
composite_traps_info_t *info)
{
cairo_boxes_t boxes;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (! _cairo_traps_to_boxes (&info->traps, info->antialias, &boxes))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return clip_and_composite_boxes (compositor, extents, &boxes);
}
 
static cairo_int_status_t
clip_and_composite_traps (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
composite_traps_info_t *info,
unsigned flags)
{
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = trim_extents_to_traps (extents, &info->traps);
if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
return status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if ((flags & FORCE_CLIP_REGION) == 0)
status = composite_traps_as_boxes (compositor, extents, info);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
/* For unbounded operations, the X11 server will estimate the
* affected rectangle and apply the operation to that. However,
* there are cases where this is an overestimate (e.g. the
* clip-fill-{eo,nz}-unbounded test).
*
* The clip will trim that overestimate to our expectations.
*/
if (! extents->is_bounded)
flags |= FORCE_CLIP_REGION;
 
status = clip_and_composite (compositor, extents,
composite_traps, NULL, info,
need_unbounded_clip (extents) | flags);
}
 
return status;
}
 
static cairo_int_status_t
clip_and_composite_tristrip (const cairo_traps_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
composite_tristrip_info_t *info)
{
cairo_int_status_t status;
unsigned int flags = 0;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = trim_extents_to_tristrip (extents, &info->strip);
if (unlikely (status != CAIRO_INT_STATUS_SUCCESS))
return status;
 
if (! extents->is_bounded)
flags |= FORCE_CLIP_REGION;
 
status = clip_and_composite (compositor, extents,
composite_tristrip, NULL, info,
need_unbounded_clip (extents) | flags);
 
return status;
}
 
struct composite_mask {
cairo_surface_t *mask;
int mask_x, mask_y;
};
 
static cairo_int_status_t
composite_mask (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
struct composite_mask *data = closure;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (src != NULL) {
compositor->composite (dst, op, src, data->mask,
extents->x + src_x, extents->y + src_y,
extents->x + data->mask_x, extents->y + data->mask_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
} else {
compositor->composite (dst, op, data->mask, NULL,
extents->x + data->mask_x, extents->y + data->mask_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
struct composite_box_info {
const cairo_traps_compositor_t *compositor;
cairo_surface_t *dst;
cairo_surface_t *src;
int src_x, src_y;
uint8_t op;
};
 
static void composite_box(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_box_info *info = closure;
const cairo_traps_compositor_t *compositor = info->compositor;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (coverage)) {
cairo_surface_t *mask;
cairo_color_t color;
cairo_solid_pattern_t solid;
int mask_x, mask_y;
 
_cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double)0xffff);
_cairo_pattern_init_solid (&solid, &color);
 
mask = compositor->pattern_to_surface (info->dst, &solid.base, FALSE,
&_cairo_unbounded_rectangle,
&_cairo_unbounded_rectangle,
&mask_x, &mask_y);
 
if (likely (mask->status == CAIRO_STATUS_SUCCESS)) {
compositor->composite (info->dst, info->op, info->src, mask,
x + info->src_x, y + info->src_y,
mask_x, mask_y,
x, y,
w, h);
}
 
cairo_surface_destroy (mask);
} else {
compositor->composite (info->dst, info->op, info->src, NULL,
x + info->src_x, y + info->src_y,
0, 0,
x, y,
w, h);
}
}
 
static cairo_int_status_t
composite_mask_clip_boxes (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
struct composite_mask *data = closure;
struct composite_box_info info;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
info.compositor = compositor;
info.op = CAIRO_OPERATOR_SOURCE;
info.dst = dst;
info.src = data->mask;
info.src_x = data->mask_x;
info.src_y = data->mask_y;
 
info.src_x += dst_x;
info.src_y += dst_y;
 
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_mask_clip (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
struct composite_mask *data = closure;
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
composite_traps_info_t info;
cairo_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &info.antialias);
if (unlikely (status))
return status;
 
_cairo_traps_init (&info.traps);
status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
&polygon,
fill_rule);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
return status;
 
status = composite_traps (compositor, dst, &info,
CAIRO_OPERATOR_SOURCE,
data->mask,
data->mask_x + dst_x, data->mask_y + dst_y,
dst_x, dst_y,
extents, NULL);
_cairo_traps_fini (&info.traps);
 
return status;
}
 
/* high-level compositor interface */
 
static cairo_int_status_t
_cairo_traps_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
cairo_boxes_t boxes;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
_cairo_clip_steal_boxes (extents->clip, &boxes);
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_clip_unsteal_boxes (extents->clip, &boxes);
 
return status;
}
 
static cairo_int_status_t
_cairo_traps_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t*)_compositor;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
if (extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID &&
extents->clip->path == NULL) {
status = clip_and_composite (compositor, extents,
composite_opacity_boxes,
composite_opacity_boxes,
&extents->mask_pattern,
need_unbounded_clip (extents));
} else {
struct composite_mask data;
 
data.mask = compositor->pattern_to_surface (extents->surface,
&extents->mask_pattern.base,
TRUE,
&extents->bounded,
&extents->mask_sample_area,
&data.mask_x,
&data.mask_y);
if (unlikely (data.mask->status))
return data.mask->status;
 
status = clip_and_composite (compositor, extents,
composite_mask,
extents->clip->path ? composite_mask_clip : composite_mask_clip_boxes,
&data, need_bounded_clip (extents));
 
cairo_surface_destroy (data.mask);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_traps_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED && 0 &&
_cairo_clip_is_region (extents->clip)) /* XXX */
{
composite_tristrip_info_t info;
 
info.antialias = antialias;
_cairo_tristrip_init_with_clip (&info.strip, extents->clip);
status = _cairo_path_fixed_stroke_to_tristrip (path, style,
ctm, ctm_inverse,
tolerance,
&info.strip);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_tristrip (compositor, extents, &info);
_cairo_tristrip_fini (&info.strip);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
path->has_curve_to && antialias == CAIRO_ANTIALIAS_NONE) {
cairo_polygon_t polygon;
 
_cairo_polygon_init_with_clip (&polygon, extents->clip);
status = _cairo_path_fixed_stroke_to_polygon (path, style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_polygon (compositor,
extents, &polygon,
CAIRO_ANTIALIAS_NONE,
CAIRO_FILL_RULE_WINDING,
TRUE);
_cairo_polygon_fini (&polygon);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_int_status_t (*func) (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps);
composite_traps_info_t info;
unsigned flags;
 
if (antialias == CAIRO_ANTIALIAS_BEST || antialias == CAIRO_ANTIALIAS_GOOD) {
func = _cairo_path_fixed_stroke_polygon_to_traps;
flags = 0;
} else {
func = _cairo_path_fixed_stroke_to_traps;
flags = need_bounded_clip (extents) & ~NEED_CLIP_SURFACE;
}
 
info.antialias = antialias;
_cairo_traps_init_with_clip (&info.traps, extents->clip);
status = func (path, style, ctm, ctm_inverse, tolerance, &info.traps);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_traps (compositor, extents, &info, flags);
_cairo_traps_fini (&info.traps);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_traps_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite_boxes (compositor, extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_polygon_t polygon;
 
#if 0
if (extents->mask.width > extents->unbounded.width ||
extents->mask.height > extents->unbounded.height)
{
cairo_box_t limits;
_cairo_box_from_rectangle (&limits, &extents->unbounded);
_cairo_polygon_init (&polygon, &limits, 1);
}
else
{
_cairo_polygon_init (&polygon, NULL, 0);
}
 
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _cairo_polygon_intersect_with_boxes (&polygon, &fill_rule,
extents->clip->boxes,
extents->clip->num_boxes);
}
#else
_cairo_polygon_init_with_clip (&polygon, extents->clip);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
#endif
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = clip_and_composite_polygon (compositor, extents, &polygon,
antialias, fill_rule, path->has_curve_to);
}
_cairo_polygon_fini (&polygon);
}
 
return status;
}
 
static cairo_int_status_t
composite_glyphs (const cairo_traps_compositor_t *compositor,
cairo_surface_t *dst,
void *closure,
cairo_operator_t op,
cairo_surface_t *src,
int src_x, int src_y,
int dst_x, int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
cairo_composite_glyphs_info_t *info = closure;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (op == CAIRO_OPERATOR_ADD && (dst->content & CAIRO_CONTENT_COLOR) == 0)
info->use_mask = 0;
 
return compositor->composite_glyphs (dst, op, src,
src_x, src_y,
dst_x, dst_y,
info);
}
 
static cairo_int_status_t
_cairo_traps_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
const cairo_traps_compositor_t *compositor = (cairo_traps_compositor_t *)_compositor;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
status = compositor->check_composite (extents);
if (unlikely (status))
return status;
 
_cairo_scaled_font_freeze_cache (scaled_font);
status = compositor->check_composite_glyphs (extents,
scaled_font, glyphs,
&num_glyphs);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_composite_glyphs_info_t info;
 
info.font = scaled_font;
info.glyphs = glyphs;
info.num_glyphs = num_glyphs;
info.use_mask = overlap || ! extents->is_bounded;
info.extents = extents->bounded;
 
status = clip_and_composite (compositor, extents,
composite_glyphs, NULL, &info,
need_bounded_clip (extents) | FORCE_CLIP_REGION);
}
_cairo_scaled_font_thaw_cache (scaled_font);
 
return status;
}
 
void
_cairo_traps_compositor_init (cairo_traps_compositor_t *compositor,
const cairo_compositor_t *delegate)
{
compositor->base.delegate = delegate;
 
compositor->base.paint = _cairo_traps_compositor_paint;
compositor->base.mask = _cairo_traps_compositor_mask;
compositor->base.fill = _cairo_traps_compositor_fill;
compositor->base.stroke = _cairo_traps_compositor_stroke;
compositor->base.glyphs = _cairo_traps_compositor_glyphs;
}
/programs/develop/libraries/cairo/src/cairo-traps-private.h
0,0 → 1,141
/* 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>
*/
 
#ifndef CAIRO_TRAPS_PRIVATE_H
#define CAIRO_TRAPS_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-types-private.h"
 
CAIRO_BEGIN_DECLS
 
struct _cairo_traps {
cairo_status_t status;
 
cairo_box_t bounds;
const cairo_box_t *limits;
int num_limits;
 
unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
unsigned int has_intersections : 1;
unsigned int is_rectilinear : 1;
unsigned int is_rectangular : 1;
 
int num_traps;
int traps_size;
cairo_trapezoid_t *traps;
cairo_trapezoid_t traps_embedded[16];
};
 
/* cairo-traps.c */
cairo_private void
_cairo_traps_init (cairo_traps_t *traps);
 
cairo_private void
_cairo_traps_init_with_clip (cairo_traps_t *traps,
const cairo_clip_t *clip);
 
cairo_private void
_cairo_traps_limit (cairo_traps_t *traps,
const cairo_box_t *boxes,
int num_boxes);
 
cairo_private cairo_status_t
_cairo_traps_init_boxes (cairo_traps_t *traps,
const cairo_boxes_t *boxes);
 
cairo_private void
_cairo_traps_clear (cairo_traps_t *traps);
 
cairo_private void
_cairo_traps_fini (cairo_traps_t *traps);
 
#define _cairo_traps_status(T) (T)->status
 
cairo_private void
_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
 
cairo_private void
_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
const cairo_point_t t[3]);
 
cairo_private void
_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
const cairo_point_t q[4]);
 
cairo_private cairo_status_t
_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
const cairo_point_t *top_left,
const cairo_point_t *bottom_right);
 
cairo_private void
_cairo_traps_add_trap (cairo_traps_t *traps,
cairo_fixed_t top, cairo_fixed_t bottom,
cairo_line_t *left, cairo_line_t *right);
 
cairo_private int
_cairo_traps_contain (const cairo_traps_t *traps,
double x, double y);
 
cairo_private void
_cairo_traps_extents (const cairo_traps_t *traps,
cairo_box_t *extents);
 
cairo_private cairo_int_status_t
_cairo_traps_extract_region (cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_region_t **region);
 
cairo_private cairo_bool_t
_cairo_traps_to_boxes (cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_boxes_t *boxes);
 
cairo_private cairo_status_t
_cairo_traps_path (const cairo_traps_t *traps,
cairo_path_fixed_t *path);
 
cairo_private cairo_int_status_t
_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_traps_t *traps);
 
CAIRO_END_DECLS
 
#endif /* CAIRO_TRAPS_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-traps.c
39,10 → 39,13
 
#include "cairoint.h"
 
#include "cairo-box-inline.h"
#include "cairo-boxes-private.h"
#include "cairo-error-private.h"
#include "cairo-region-private.h"
#include "cairo-slope-private.h"
#include "cairo-traps-private.h"
#include "cairo-spans-private.h"
 
/* private functions */
 
71,11 → 74,26
const cairo_box_t *limits,
int num_limits)
{
int i;
 
traps->limits = limits;
traps->num_limits = num_limits;
 
traps->bounds = limits[0];
for (i = 1; i < num_limits; i++)
_cairo_box_add_box (&traps->bounds, &limits[i]);
}
 
void
_cairo_traps_init_with_clip (cairo_traps_t *traps,
const cairo_clip_t *clip)
{
_cairo_traps_init (traps);
if (clip)
_cairo_traps_limit (traps, clip->boxes, clip->num_boxes);
}
 
void
_cairo_traps_clear (cairo_traps_t *traps)
{
traps->status = CAIRO_STATUS_SUCCESS;
147,8 → 165,247
trap->right = *right;
}
 
static void
_cairo_traps_add_clipped_trap (cairo_traps_t *traps,
cairo_fixed_t _top, cairo_fixed_t _bottom,
cairo_line_t *_left, cairo_line_t *_right)
{
/* Note: With the goofy trapezoid specification, (where an
* arbitrary two points on the lines can specified for the left
* and right edges), these limit checks would not work in
* general. For example, one can imagine a trapezoid entirely
* within the limits, but with two points used to specify the left
* edge entirely to the right of the limits. Fortunately, for our
* purposes, cairo will never generate such a crazy
* trapezoid. Instead, cairo always uses for its points the
* extreme positions of the edge that are visible on at least some
* trapezoid. With this constraint, it's impossible for both
* points to be outside the limits while the relevant edge is
* entirely inside the limits.
*/
if (traps->num_limits) {
const cairo_box_t *b = &traps->bounds;
cairo_fixed_t top = _top, bottom = _bottom;
cairo_line_t left = *_left, right = *_right;
 
/* Trivially reject if trapezoid is entirely to the right or
* to the left of the limits. */
if (left.p1.x >= b->p2.x && left.p2.x >= b->p2.x)
return;
 
if (right.p1.x <= b->p1.x && right.p2.x <= b->p1.x)
return;
 
/* And reject if the trapezoid is entirely above or below */
if (top >= b->p2.y || bottom <= b->p1.y)
return;
 
/* Otherwise, clip the trapezoid to the limits. We only clip
* where an edge is entirely outside the limits. If we wanted
* to be more clever, we could handle cases where a trapezoid
* edge intersects the edge of the limits, but that would
* require slicing this trapezoid into multiple trapezoids,
* and I'm not sure the effort would be worth it. */
if (top < b->p1.y)
top = b->p1.y;
 
if (bottom > b->p2.y)
bottom = b->p2.y;
 
if (left.p1.x <= b->p1.x && left.p2.x <= b->p1.x)
left.p1.x = left.p2.x = b->p1.x;
 
if (right.p1.x >= b->p2.x && right.p2.x >= b->p2.x)
right.p1.x = right.p2.x = b->p2.x;
 
/* Trivial discards for empty trapezoids that are likely to
* be produced by our tessellators (most notably convex_quad
* when given a simple rectangle).
*/
if (top >= bottom)
return;
 
/* cheap colinearity check */
if (right.p1.x <= left.p1.x && right.p1.y == left.p1.y &&
right.p2.x <= left.p2.x && right.p2.y == left.p2.y)
return;
 
_cairo_traps_add_trap (traps, top, bottom, &left, &right);
} else
_cairo_traps_add_trap (traps, _top, _bottom, _left, _right);
}
 
static int
_compare_point_fixed_by_y (const void *av, const void *bv)
{
const cairo_point_t *a = av, *b = bv;
int ret = a->y - b->y;
if (ret == 0)
ret = a->x - b->x;
return ret;
}
 
void
_cairo_traps_tessellate_convex_quad (cairo_traps_t *traps,
const cairo_point_t q[4])
{
int a, b, c, d;
int i;
cairo_slope_t ab, ad;
cairo_bool_t b_left_of_d;
cairo_line_t left;
cairo_line_t right;
 
/* Choose a as a point with minimal y */
a = 0;
for (i = 1; i < 4; i++)
if (_compare_point_fixed_by_y (&q[i], &q[a]) < 0)
a = i;
 
/* b and d are adjacent to a, while c is opposite */
b = (a + 1) % 4;
c = (a + 2) % 4;
d = (a + 3) % 4;
 
/* Choose between b and d so that b.y is less than d.y */
if (_compare_point_fixed_by_y (&q[d], &q[b]) < 0) {
b = (a + 3) % 4;
d = (a + 1) % 4;
}
 
/* Without freedom left to choose anything else, we have four
* cases to tessellate.
*
* First, we have to determine the Y-axis sort of the four
* vertices, (either abcd or abdc). After that we need to detemine
* which edges will be "left" and which will be "right" in the
* resulting trapezoids. This can be determined by computing a
* slope comparison of ab and ad to determine if b is left of d or
* not.
*
* Note that "left of" here is in the sense of which edges should
* be the left vs. right edges of the trapezoid. In particular, b
* left of d does *not* mean that b.x is less than d.x.
*
* This should hopefully be made clear in the lame ASCII art
* below. Since the same slope comparison is used in all cases, we
* compute it before testing for the Y-value sort. */
 
/* Note: If a == b then the ab slope doesn't give us any
* information. In that case, we can replace it with the ac (or
* equivalenly the bc) slope which gives us exactly the same
* information we need. At worst the names of the identifiers ab
* and b_left_of_d are inaccurate in this case, (would be ac, and
* c_left_of_d). */
if (q[a].x == q[b].x && q[a].y == q[b].y)
_cairo_slope_init (&ab, &q[a], &q[c]);
else
_cairo_slope_init (&ab, &q[a], &q[b]);
 
_cairo_slope_init (&ad, &q[a], &q[d]);
 
b_left_of_d = _cairo_slope_compare (&ab, &ad) > 0;
 
if (q[c].y <= q[d].y) {
if (b_left_of_d) {
/* Y-sort is abcd and b is left of d, (slope(ab) > slope (ad))
*
* top bot left right
* _a a a
* / / /| |\ a.y b.y ab ad
* b / b | b \
* / / | | \ \ b.y c.y bc ad
* c / c | c \
* | / \| \ \ c.y d.y cd ad
* d d d
*/
left.p1 = q[a]; left.p2 = q[b];
right.p1 = q[a]; right.p2 = q[d];
_cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
left.p1 = q[b]; left.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
left.p1 = q[c]; left.p2 = q[d];
_cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right);
} else {
/* Y-sort is abcd and b is right of d, (slope(ab) <= slope (ad))
*
* a a a_
* /| |\ \ \ a.y b.y ad ab
* / b | b \ b
* / / | | \ \ b.y c.y ad bc
* / c | c \ c
* / / |/ \ | c.y d.y ad cd
* d d d
*/
left.p1 = q[a]; left.p2 = q[d];
right.p1 = q[a]; right.p2 = q[b];
_cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
right.p1 = q[b]; right.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[b].y, q[c].y, &left, &right);
right.p1 = q[c]; right.p2 = q[d];
_cairo_traps_add_clipped_trap (traps, q[c].y, q[d].y, &left, &right);
}
} else {
if (b_left_of_d) {
/* Y-sort is abdc and b is left of d, (slope (ab) > slope (ad))
*
* a a a
* // / \ |\ a.y b.y ab ad
* /b/ b \ b \
* / / \ \ \ \ b.y d.y bc ad
* /d/ \ d \ d
* // \ / \| d.y c.y bc dc
* c c c
*/
left.p1 = q[a]; left.p2 = q[b];
right.p1 = q[a]; right.p2 = q[d];
_cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
left.p1 = q[b]; left.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
right.p1 = q[d]; right.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right);
} else {
/* Y-sort is abdc and b is right of d, (slope (ab) <= slope (ad))
*
* a a a
* /| / \ \\ a.y b.y ad ab
* / b / b \b\
* / / / / \ \ b.y d.y ad bc
* d / d / \d\
* |/ \ / \\ d.y c.y dc bc
* c c c
*/
left.p1 = q[a]; left.p2 = q[d];
right.p1 = q[a]; right.p2 = q[b];
_cairo_traps_add_clipped_trap (traps, q[a].y, q[b].y, &left, &right);
right.p1 = q[b]; right.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[b].y, q[d].y, &left, &right);
left.p1 = q[d]; left.p2 = q[c];
_cairo_traps_add_clipped_trap (traps, q[d].y, q[c].y, &left, &right);
}
}
}
 
/* A triangle is simply a degenerate case of a convex
* quadrilateral. We would not benefit from having any distinct
* implementation of triangle vs. quadrilateral tessellation here. */
void
_cairo_traps_tessellate_triangle (cairo_traps_t *traps,
const cairo_point_t t[3])
{
cairo_point_t quad[4];
 
quad[0] = t[0];
quad[1] = t[0];
quad[2] = t[1];
quad[3] = t[2];
 
_cairo_traps_tessellate_convex_quad (traps, quad);
}
 
 
/**
* _cairo_traps_init_box:
* _cairo_traps_init_boxes:
* @traps: a #cairo_traps_t
* @box: an array box that will each be converted to a single trapezoid
* to store in @traps.
229,6 → 486,9
cairo_bool_t reversed;
int n;
 
if (top >= traps->bounds.p2.y || bottom <= traps->bounds.p1.y)
return CAIRO_STATUS_SUCCESS;
 
/* support counter-clockwise winding for rectangular tessellation */
reversed = top_left->x > bottom_right->x;
if (reversed) {
236,6 → 496,9
left.p1.x = left.p2.x = bottom_right->x;
}
 
if (left.p1.x >= traps->bounds.p2.x || right.p1.x <= traps->bounds.p1.x)
return CAIRO_STATUS_SUCCESS;
 
for (n = 0; n < traps->num_limits; n++) {
const cairo_box_t *limits = &traps->limits[n];
cairo_line_t _left, _right;
485,7 → 748,45
}
}
 
static cairo_bool_t
_mono_edge_is_vertical (const cairo_line_t *line)
{
return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
}
 
static cairo_bool_t
_traps_are_pixel_aligned (cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
if (antialias == CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
! _mono_edge_is_vertical (&traps->traps[i].right))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
} else {
for (i = 0; i < traps->num_traps; i++) {
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
}
 
return TRUE;
}
 
/**
* _cairo_traps_extract_region:
* @traps: a #cairo_traps_t
502,6 → 803,7
**/
cairo_int_status_t
_cairo_traps_extract_region (cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_region_t **region)
{
cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
510,21 → 812,13
int i, rect_count;
 
/* we only treat this a hint... */
if (! traps->maybe_region)
if (antialias != CAIRO_ANTIALIAS_NONE && ! traps->maybe_region)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
for (i = 0; i < traps->num_traps; i++) {
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
if (! _traps_are_pixel_aligned (traps, antialias)) {
traps->maybe_region = FALSE;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
if (traps->num_traps > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_rectangle_int_t));
535,19 → 829,30
 
rect_count = 0;
for (i = 0; i < traps->num_traps; i++) {
int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
int y1 = _cairo_fixed_integer_part (traps->traps[i].top);
int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
int y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
int x1, y1, x2, y2;
 
if (antialias == CAIRO_ANTIALIAS_NONE) {
x1 = _cairo_fixed_integer_round_down (traps->traps[i].left.p1.x);
y1 = _cairo_fixed_integer_round_down (traps->traps[i].top);
x2 = _cairo_fixed_integer_round_down (traps->traps[i].right.p1.x);
y2 = _cairo_fixed_integer_round_down (traps->traps[i].bottom);
} else {
x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x);
y1 = _cairo_fixed_integer_part (traps->traps[i].top);
x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x);
y2 = _cairo_fixed_integer_part (traps->traps[i].bottom);
}
 
if (x2 > x1 && y2 > y1) {
rects[rect_count].x = x1;
rects[rect_count].y = y1;
rects[rect_count].width = x2 - x1;
rects[rect_count].height = y2 - y1;
 
rect_count++;
}
}
 
 
*region = cairo_region_create_rectangles (rects, rect_count);
status = (*region)->status;
 
557,6 → 862,66
return status;
}
 
cairo_bool_t
_cairo_traps_to_boxes (cairo_traps_t *traps,
cairo_antialias_t antialias,
cairo_boxes_t *boxes)
{
int i;
 
for (i = 0; i < traps->num_traps; i++) {
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x)
return FALSE;
}
 
_cairo_boxes_init (boxes);
 
boxes->num_boxes = traps->num_traps;
boxes->chunks.base = (cairo_box_t *) traps->traps;
boxes->chunks.count = traps->num_traps;
boxes->chunks.size = traps->num_traps;
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
boxes->chunks.base[i].p1.x = x1;
boxes->chunks.base[i].p1.y = y1;
boxes->chunks.base[i].p2.x = x2;
boxes->chunks.base[i].p2.y = y2;
 
if (boxes->is_pixel_aligned) {
boxes->is_pixel_aligned =
_cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
_cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
}
}
} else {
boxes->is_pixel_aligned = TRUE;
 
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
/* round down here to match Pixman's behavior when using traps. */
boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
}
}
 
return TRUE;
}
 
/* moves trap points such that they become the actual corners of the trapezoid */
static void
_sanitize_trap (cairo_trapezoid_t *t)
603,3 → 968,99
 
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps)
{
cairo_box_t extents;
int n;
 
#if 0
if (traps->has_limits) {
printf ("%s: limits=(%d, %d, %d, %d)\n",
filename,
traps->limits.p1.x, traps->limits.p1.y,
traps->limits.p2.x, traps->limits.p2.y);
}
#endif
 
_cairo_traps_extents (traps, &extents);
fprintf (file, "extents=(%d, %d, %d, %d)\n",
extents.p1.x, extents.p1.y,
extents.p2.x, extents.p2.y);
 
for (n = 0; n < traps->num_traps; n++) {
fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
traps->traps[n].top,
traps->traps[n].bottom,
traps->traps[n].left.p1.x,
traps->traps[n].left.p1.y,
traps->traps[n].left.p2.x,
traps->traps[n].left.p2.y,
traps->traps[n].right.p1.x,
traps->traps[n].right.p1.y,
traps->traps[n].right.p2.x,
traps->traps[n].right.p2.y);
}
}
 
struct cairo_trap_renderer {
cairo_span_renderer_t base;
cairo_traps_t *traps;
};
 
static cairo_status_t
span_to_traps (void *abstract_renderer, int y, int h,
const cairo_half_open_span_t *spans, unsigned num_spans)
{
struct cairo_trap_renderer *r = abstract_renderer;
cairo_fixed_t top, bot;
 
if (num_spans == 0)
return CAIRO_STATUS_SUCCESS;
 
top = _cairo_fixed_from_int (y);
bot = _cairo_fixed_from_int (y + h);
do {
if (spans[0].coverage) {
cairo_fixed_t x0 = _cairo_fixed_from_int(spans[0].x);
cairo_fixed_t x1 = _cairo_fixed_from_int(spans[1].x);
cairo_line_t left = { { x0, top }, { x0, bot } },
right = { { x1, top }, { x1, bot } };
_cairo_traps_add_trap (r->traps, top, bot, &left, &right);
}
spans++;
} while (--num_spans > 1);
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_rasterise_polygon_to_traps (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
struct cairo_trap_renderer renderer;
cairo_scan_converter_t *converter;
cairo_int_status_t status;
cairo_rectangle_int_t r;
 
TRACE ((stderr, "%s: fill_rule=%d, antialias=%d\n",
__FUNCTION__, fill_rule, antialias));
assert(antialias == CAIRO_ANTIALIAS_NONE);
 
renderer.traps = traps;
renderer.base.render_rows = span_to_traps;
 
_cairo_box_round_to_rectangle (&polygon->extents, &r);
converter = _cairo_mono_scan_converter_create (r.x, r.y,
r.x + r.width,
r.y + r.height,
fill_rule);
status = _cairo_mono_scan_converter_add_polygon (converter, polygon);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = converter->generate (converter, &renderer.base);
converter->destroy (converter);
return status;
}
/programs/develop/libraries/cairo/src/cairo-tristrip-private.h
0,0 → 1,94
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef CAIRO_TRISTRIP_PRIVATE_H
#define CAIRO_TRISTRIP_PRIVATE_H
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
#include "cairo-types-private.h"
 
CAIRO_BEGIN_DECLS
 
struct _cairo_tristrip {
cairo_status_t status;
 
/* XXX clipping */
 
const cairo_box_t *limits;
int num_limits;
 
int num_points;
int size_points;
cairo_point_t *points;
cairo_point_t points_embedded[64];
};
 
cairo_private void
_cairo_tristrip_init (cairo_tristrip_t *strip);
 
cairo_private void
_cairo_tristrip_limit (cairo_tristrip_t *strip,
const cairo_box_t *limits,
int num_limits);
 
cairo_private void
_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
const cairo_clip_t *clip);
 
cairo_private void
_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y);
 
cairo_private void
_cairo_tristrip_move_to (cairo_tristrip_t *strip,
const cairo_point_t *point);
 
cairo_private void
_cairo_tristrip_add_point (cairo_tristrip_t *strip,
const cairo_point_t *point);
 
cairo_private void
_cairo_tristrip_extents (const cairo_tristrip_t *strip,
cairo_box_t *extents);
 
cairo_private void
_cairo_tristrip_fini (cairo_tristrip_t *strip);
 
#define _cairo_tristrip_status(T) ((T)->status)
 
CAIRO_END_DECLS
 
#endif /* CAIRO_TRISTRIP_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-tristrip.c
0,0 → 1,185
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Chris Wilson
*
* Contributor(s):
* Chris Wilson <chris@chris-wilsonc.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-error-private.h"
#include "cairo-tristrip-private.h"
 
void
_cairo_tristrip_init (cairo_tristrip_t *strip)
{
VG (VALGRIND_MAKE_MEM_UNDEFINED (strip, sizeof (cairo_tristrip_t)));
 
strip->status = CAIRO_STATUS_SUCCESS;
 
strip->num_limits = 0;
strip->num_points = 0;
 
strip->size_points = ARRAY_LENGTH (strip->points_embedded);
strip->points = strip->points_embedded;
}
 
void
_cairo_tristrip_fini (cairo_tristrip_t *strip)
{
if (strip->points != strip->points_embedded)
free (strip->points);
 
VG (VALGRIND_MAKE_MEM_NOACCESS (strip, sizeof (cairo_tristrip_t)));
}
 
 
void
_cairo_tristrip_limit (cairo_tristrip_t *strip,
const cairo_box_t *limits,
int num_limits)
{
strip->limits = limits;
strip->num_limits = num_limits;
}
 
void
_cairo_tristrip_init_with_clip (cairo_tristrip_t *strip,
const cairo_clip_t *clip)
{
_cairo_tristrip_init (strip);
if (clip)
_cairo_tristrip_limit (strip, clip->boxes, clip->num_boxes);
}
 
/* make room for at least one more trap */
static cairo_bool_t
_cairo_tristrip_grow (cairo_tristrip_t *strip)
{
cairo_point_t *points;
int new_size = 4 * strip->size_points;
 
if (CAIRO_INJECT_FAULT ()) {
strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return FALSE;
}
 
if (strip->points == strip->points_embedded) {
points = _cairo_malloc_ab (new_size, sizeof (cairo_point_t));
if (points != NULL)
memcpy (points, strip->points, sizeof (strip->points_embedded));
} else {
points = _cairo_realloc_ab (strip->points,
new_size, sizeof (cairo_trapezoid_t));
}
 
if (unlikely (points == NULL)) {
strip->status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return FALSE;
}
 
strip->points = points;
strip->size_points = new_size;
return TRUE;
}
 
void
_cairo_tristrip_add_point (cairo_tristrip_t *strip,
const cairo_point_t *p)
{
if (unlikely (strip->num_points == strip->size_points)) {
if (unlikely (! _cairo_tristrip_grow (strip)))
return;
}
 
strip->points[strip->num_points++] = *p;
}
 
/* Insert degenerate triangles to advance to the given point. The
* next point inserted must also be @p. */
void
_cairo_tristrip_move_to (cairo_tristrip_t *strip,
const cairo_point_t *p)
{
if (strip->num_points == 0)
return;
 
_cairo_tristrip_add_point (strip, &strip->points[strip->num_points-1]);
_cairo_tristrip_add_point (strip, p);
#if 0
/* and one more for luck! (to preserve cw/ccw ordering) */
_cairo_tristrip_add_point (strip, p);
#endif
}
 
void
_cairo_tristrip_translate (cairo_tristrip_t *strip, int x, int y)
{
cairo_fixed_t xoff, yoff;
cairo_point_t *p;
int i;
 
xoff = _cairo_fixed_from_int (x);
yoff = _cairo_fixed_from_int (y);
 
for (i = 0, p = strip->points; i < strip->num_points; i++, p++) {
p->x += xoff;
p->y += yoff;
}
}
 
void
_cairo_tristrip_extents (const cairo_tristrip_t *strip,
cairo_box_t *extents)
{
int i;
 
if (strip->num_points == 0) {
extents->p1.x = extents->p1.y = 0;
extents->p2.x = extents->p2.y = 0;
return;
}
 
extents->p2 = extents->p1 = strip->points[0];
for (i = 1; i < strip->num_points; i++) {
const cairo_point_t *p = &strip->points[i];
 
if (p->x < extents->p1.x)
extents->p1.x = p->x;
else if (p->x > extents->p2.x)
extents->p2.x = p->x;
 
if (p->y < extents->p1.y)
extents->p1.y = p->y;
else if (p->y > extents->p2.y)
extents->p2.y = p->y;
}
}
/programs/develop/libraries/cairo/src/cairo-truetype-subset-private.h
64,6 → 64,7
#define TT_TAG_loca MAKE_TT_TAG('l','o','c','a')
#define TT_TAG_maxp MAKE_TT_TAG('m','a','x','p')
#define TT_TAG_name MAKE_TT_TAG('n','a','m','e')
#define TT_TAG_OS2 MAKE_TT_TAG('O','S','/','2')
#define TT_TAG_post MAKE_TT_TAG('p','o','s','t')
#define TT_TAG_prep MAKE_TT_TAG('p','r','e','p')
 
174,7 → 175,19
} tt_name_t;
 
 
/* bitmask for fsSelection field */
#define TT_FS_SELECTION_ITALIC 1
#define TT_FS_SELECTION_BOLD 32
 
/* _unused fields are defined in TT spec but not used by cairo */
typedef struct _tt_os2 {
uint16_t _unused1[2];
uint16_t usWeightClass;
uint16_t _unused2[28];
uint16_t fsSelection;
uint16_t _unused3[11];
} tt_os2_t;
 
/* composite_glyph_t flags */
#define TT_ARG_1_AND_2_ARE_WORDS 0x0001
#define TT_WE_HAVE_A_SCALE 0x0008
/programs/develop/libraries/cairo/src/cairo-truetype-subset.c
42,6 → 42,8
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
92,7 → 94,7
unsigned long last_boundary;
int *parent_to_subset;
cairo_status_t status;
 
cairo_bool_t is_pdf;
};
 
/*
122,7 → 124,8
_cairo_truetype_font_set_error (cairo_truetype_font_t *font,
cairo_status_t status)
{
if (status == CAIRO_STATUS_SUCCESS || status == CAIRO_INT_STATUS_UNSUPPORTED)
if (status == CAIRO_STATUS_SUCCESS ||
status == (int)CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
_cairo_status_set_error (&font->status, status);
132,6 → 135,7
 
static cairo_status_t
_cairo_truetype_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
cairo_bool_t is_pdf,
cairo_truetype_font_t **font_return)
{
cairo_status_t status;
155,6 → 159,10
* return CAIRO_INT_STATUS_UNSUPPORTED;
*/
 
/* We need to use a fallback font generated from the synthesized outlines. */
if (backend->is_synthetic && backend->is_synthetic (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = sizeof (tt_head_t);
status = backend->load_truetype_table (scaled_font_subset->scaled_font,
TT_TAG_head, 0,
206,6 → 214,7
goto fail2;
}
 
font->is_pdf = is_pdf;
font->base.num_glyphs = 0;
font->base.x_min = (int16_t) be16_to_cpu (head.x_min);
font->base.y_min = (int16_t) be16_to_cpu (head.y_min);
262,7 → 271,6
free (font->base.ps_name);
fail3:
free (font->parent_to_subset);
if (font->base.font_name)
free (font->base.font_name);
fail2:
free (font->glyphs);
279,7 → 287,6
_cairo_array_fini (&font->string_offsets);
free (font->base.widths);
free (font->base.ps_name);
if (font->base.font_name)
free (font->base.font_name);
free (font->parent_to_subset);
free (font->glyphs);
393,52 → 400,101
return CAIRO_STATUS_SUCCESS;
}
 
typedef struct _cmap_unicode_range {
unsigned int start;
unsigned int end;
} cmap_unicode_range_t;
 
static cmap_unicode_range_t winansi_unicode_ranges[] = {
{ 0x0020, 0x007f },
{ 0x00a0, 0x00ff },
{ 0x0152, 0x0153 },
{ 0x0160, 0x0161 },
{ 0x0178, 0x0178 },
{ 0x017d, 0x017e },
{ 0x0192, 0x0192 },
{ 0x02c6, 0x02c6 },
{ 0x02dc, 0x02dc },
{ 0x2013, 0x2026 },
{ 0x2030, 0x2030 },
{ 0x2039, 0x203a },
{ 0x20ac, 0x20ac },
{ 0x2122, 0x2122 },
};
 
static cairo_status_t
cairo_truetype_font_write_cmap_table (cairo_truetype_font_t *font,
unsigned long tag)
{
unsigned int i;
int i;
unsigned int j;
int range_offset;
int num_ranges;
int entry_selector;
int length;
 
num_ranges = ARRAY_LENGTH (winansi_unicode_ranges);
 
length = 16 + (num_ranges + 1)*8;
for (i = 0; i < num_ranges; i++)
length += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2;
 
entry_selector = 0;
while ((1 << entry_selector) <= (num_ranges + 1))
entry_selector++;
 
entry_selector--;
 
cairo_truetype_font_write_be16 (font, 0); /* Table version */
cairo_truetype_font_write_be16 (font, 2); /* Num tables */
cairo_truetype_font_write_be16 (font, 1); /* Num tables */
 
cairo_truetype_font_write_be16 (font, 3); /* Platform */
cairo_truetype_font_write_be16 (font, 0); /* Encoding */
cairo_truetype_font_write_be32 (font, 20); /* Offset to start of table */
cairo_truetype_font_write_be16 (font, 1); /* Encoding */
cairo_truetype_font_write_be32 (font, 12); /* Offset to start of table */
 
cairo_truetype_font_write_be16 (font, 1); /* Platform */
cairo_truetype_font_write_be16 (font, 0); /* Encoding */
cairo_truetype_font_write_be32 (font, 52); /* Offset to start of table */
/* Output a format 4 encoding table for the winansi encoding */
 
/* Output a format 4 encoding table. */
 
cairo_truetype_font_write_be16 (font, 4); /* Format */
cairo_truetype_font_write_be16 (font, 32); /* Length */
cairo_truetype_font_write_be16 (font, length); /* Length */
cairo_truetype_font_write_be16 (font, 0); /* Version */
cairo_truetype_font_write_be16 (font, 4); /* 2*segcount */
cairo_truetype_font_write_be16 (font, 4); /* searchrange */
cairo_truetype_font_write_be16 (font, 1); /* entry selector */
cairo_truetype_font_write_be16 (font, 0); /* rangeshift */
cairo_truetype_font_write_be16 (font, 0xf000 + font->base.num_glyphs - 1); /* end count[0] */
cairo_truetype_font_write_be16 (font, 0xffff); /* end count[1] */
cairo_truetype_font_write_be16 (font, num_ranges*2 + 2); /* 2*segcount */
cairo_truetype_font_write_be16 (font, (1 << (entry_selector + 1))); /* searchrange */
cairo_truetype_font_write_be16 (font, entry_selector); /* entry selector */
cairo_truetype_font_write_be16 (font, num_ranges*2 + 2 - (1 << (entry_selector + 1))); /* rangeshift */
for (i = 0; i < num_ranges; i++)
cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].end); /* end count[] */
cairo_truetype_font_write_be16 (font, 0xffff); /* end count[] */
 
cairo_truetype_font_write_be16 (font, 0); /* reserved */
cairo_truetype_font_write_be16 (font, 0xf000); /* startCode[0] */
cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[1] */
cairo_truetype_font_write_be16 (font, 0x1000); /* delta[0] */
cairo_truetype_font_write_be16 (font, 1); /* delta[1] */
cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[0] */
cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[1] */
 
/* Output a format 6 encoding table. */
for (i = 0; i < num_ranges; i++)
cairo_truetype_font_write_be16 (font, winansi_unicode_ranges[i].start); /* startCode[] */
cairo_truetype_font_write_be16 (font, 0xffff); /* startCode[] */
 
cairo_truetype_font_write_be16 (font, 6);
cairo_truetype_font_write_be16 (font, 10 + 2 * font->base.num_glyphs);
cairo_truetype_font_write_be16 (font, 0);
cairo_truetype_font_write_be16 (font, 0); /* First character */
cairo_truetype_font_write_be16 (font, font->base.num_glyphs);
for (i = 0; i < font->base.num_glyphs; i++)
cairo_truetype_font_write_be16 (font, i);
for (i = 0; i < num_ranges; i++)
cairo_truetype_font_write_be16 (font, 0x0000); /* delta[] */
cairo_truetype_font_write_be16 (font, 1); /* delta[] */
 
range_offset = num_ranges*2 + 2;
for (i = 0; i < num_ranges; i++) {
cairo_truetype_font_write_be16 (font, range_offset); /* rangeOffset[] */
range_offset += (winansi_unicode_ranges[i].end - winansi_unicode_ranges[i].start + 1)*2 - 2;
}
cairo_truetype_font_write_be16 (font, 0); /* rangeOffset[] */
 
for (i = 0; i < num_ranges; i++) {
for (j = winansi_unicode_ranges[i].start; j < winansi_unicode_ranges[i].end + 1; j++) {
int ch = _cairo_unicode_to_winansi (j);
int glyph;
 
if (ch > 0)
glyph = font->scaled_font_subset->latin_to_subset_glyph_index[ch];
else
glyph = 0;
cairo_truetype_font_write_be16 (font, glyph);
}
}
 
return font->status;
}
 
985,8 → 1041,9
* The tables in the table directory must be listed in alphabetical
* order. The "cvt", "fpgm", and "prep" are optional tables. They
* will only be embedded in the subset if they exist in the source
* font. The pos parameter of cairo_truetype_font_add_truetype_table()
* specifies the position of the table in the table directory.
* font. "cmap" is only embedded for latin fonts. The pos parameter of
* cairo_truetype_font_add_truetype_table() specifies the position of
* the table in the table directory.
*/
static void
cairo_truetype_font_create_truetype_table_list (cairo_truetype_font_t *font)
1000,23 → 1057,25
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_cvt, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
&size) == CAIRO_INT_STATUS_SUCCESS)
has_cvt = TRUE;
 
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_fpgm, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
&size) == CAIRO_INT_STATUS_SUCCESS)
has_fpgm = TRUE;
 
size = 0;
if (font->backend->load_truetype_table (font->scaled_font_subset->scaled_font,
TT_TAG_prep, 0, NULL,
&size) == CAIRO_STATUS_SUCCESS)
&size) == CAIRO_INT_STATUS_SUCCESS)
has_prep = TRUE;
 
font->num_tables = 0;
pos = 1;
pos = 0;
if (font->is_pdf && font->scaled_font_subset->is_latin)
pos++;
if (has_cvt)
pos++;
if (has_fpgm)
1024,6 → 1083,7
cairo_truetype_font_add_truetype_table (font, TT_TAG_glyf, cairo_truetype_font_write_glyf_table, pos);
 
pos = 0;
if (font->is_pdf && font->scaled_font_subset->is_latin)
cairo_truetype_font_add_truetype_table (font, TT_TAG_cmap, cairo_truetype_font_write_cmap_table, pos++);
if (has_cvt)
cairo_truetype_font_add_truetype_table (font, TT_TAG_cvt, cairo_truetype_font_write_generic_table, pos++);
1039,9 → 1099,10
cairo_truetype_font_add_truetype_table (font, TT_TAG_prep, cairo_truetype_font_write_generic_table, pos);
}
 
cairo_status_t
_cairo_truetype_subset_init (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset)
static cairo_status_t
cairo_truetype_subset_init_internal (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset,
cairo_bool_t is_pdf)
{
cairo_truetype_font_t *font = NULL;
cairo_status_t status;
1052,7 → 1113,7
const unsigned long *string_offsets = NULL;
unsigned long num_strings = 0;
 
status = _cairo_truetype_font_create (font_subset, &font);
status = _cairo_truetype_font_create (font_subset, is_pdf, &font);
if (unlikely (status))
return status;
 
1076,13 → 1137,13
}
 
if (font->base.font_name != NULL) {
truetype_subset->font_name = strdup (font->base.font_name);
if (unlikely (truetype_subset->font_name == NULL)) {
truetype_subset->family_name_utf8 = strdup (font->base.font_name);
if (unlikely (truetype_subset->family_name_utf8 == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
} else {
truetype_subset->font_name = NULL;
truetype_subset->family_name_utf8 = NULL;
}
 
/* The widths array returned must contain only widths for the
1140,8 → 1201,7
fail4:
free (truetype_subset->widths);
fail3:
if (truetype_subset->font_name)
free (truetype_subset->font_name);
free (truetype_subset->family_name_utf8);
fail2:
free (truetype_subset->ps_name);
fail1:
1150,12 → 1210,25
return status;
}
 
cairo_status_t
_cairo_truetype_subset_init_ps (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset)
{
return cairo_truetype_subset_init_internal (truetype_subset, font_subset, FALSE);
}
 
cairo_status_t
_cairo_truetype_subset_init_pdf (cairo_truetype_subset_t *truetype_subset,
cairo_scaled_font_subset_t *font_subset)
{
return cairo_truetype_subset_init_internal (truetype_subset, font_subset, TRUE);
}
 
void
_cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
{
free (subset->ps_name);
if (subset->font_name)
free (subset->font_name);
free (subset->family_name_utf8);
free (subset->widths);
free (subset->data);
free (subset->string_offsets);
1177,7 → 1250,6
uint16_t *end_code;
uint16_t *delta;
uint16_t *range_offset;
uint16_t *glyph_array;
uint16_t c;
 
backend = scaled_font->backend;
1217,7 → 1289,6
start_code = &(end_code[num_segments + 1]);
delta = &(start_code[num_segments]);
range_offset = &(delta[num_segments]);
glyph_array = &(range_offset[num_segments]);
 
/* search for glyph in segments with rangeOffset=0 */
for (i = 0; i < num_segments; i++) {
1270,7 → 1341,7
unsigned long index,
uint32_t *ucs4)
{
cairo_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
const cairo_scaled_font_backend_t *backend;
tt_cmap_t *cmap;
char buf[4];
1322,6 → 1393,107
return status;
}
 
static cairo_status_t
find_name (tt_name_t *name, int name_id, int platform, int encoding, int language, char **str_out)
{
tt_name_record_t *record;
int i, len;
char *str;
char *p;
cairo_bool_t has_tag;
cairo_status_t status;
 
str = NULL;
for (i = 0; i < be16_to_cpu (name->num_records); i++) {
record = &(name->records[i]);
if (be16_to_cpu (record->name) == name_id &&
be16_to_cpu (record->platform) == platform &&
be16_to_cpu (record->encoding) == encoding &&
(language == -1 || be16_to_cpu (record->language) == language)) {
 
str = malloc (be16_to_cpu (record->length) + 1);
if (str == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
len = be16_to_cpu (record->length);
memcpy (str,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
len);
str[be16_to_cpu (record->length)] = 0;
break;
}
}
if (str == NULL) {
*str_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
if (platform == 3) { /* Win platform, unicode encoding */
/* convert to utf8 */
int size = 0;
char *utf8;
uint16_t *u = (uint16_t *) str;
int u_len = len/2;
 
for (i = 0; i < u_len; i++)
size += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), NULL);
 
utf8 = malloc (size + 1);
if (utf8 == NULL) {
status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
p = utf8;
for (i = 0; i < u_len; i++)
p += _cairo_ucs4_to_utf8 (be16_to_cpu(u[i]), p);
*p = 0;
free (str);
str = utf8;
} else if (platform == 1) { /* Mac platform, Mac Roman encoding */
/* Replace characters above 127 with underscores. We could use
* a lookup table to convert to unicode but since most fonts
* include a unicode name this is just a rarely used fallback. */
for (i = 0; i < len; i++) {
if ((unsigned char)str[i] > 127)
str[i] = '_';
}
}
 
/* If font name is prefixed with a PDF subset tag, strip it off. */
p = str;
len = strlen (str);
has_tag = FALSE;
if (len > 7 && p[6] == '+') {
has_tag = TRUE;
for (i = 0; i < 6; i++) {
if (p[i] < 'A' || p[i] > 'Z') {
has_tag = FALSE;
break;
}
}
}
if (has_tag) {
p = malloc (len - 6);
if (unlikely (p == NULL)) {
status =_cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
memcpy (p, str + 7, len - 7);
p[len-7] = 0;
free (str);
str = p;
}
 
*str_out = str;
 
return CAIRO_STATUS_SUCCESS;
 
fail:
free (str);
 
return status;
}
 
cairo_int_status_t
_cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font,
char **ps_name_out,
1330,11 → 1502,9
cairo_status_t status;
const cairo_scaled_font_backend_t *backend;
tt_name_t *name;
tt_name_record_t *record;
unsigned long size;
int i, j;
char *ps_name = NULL;
char *font_name = NULL;
char *family_name = NULL;
 
backend = scaled_font->backend;
if (!backend->load_truetype_table)
1359,71 → 1529,85
if (status)
goto fail;
 
/* Extract the font name and PS name from the name table. At
* present this just looks for the Mac platform/Roman encoded font
* name. It should be extended to use any suitable font name in
* the name table.
*/
for (i = 0; i < be16_to_cpu(name->num_records); i++) {
record = &(name->records[i]);
if ((be16_to_cpu (record->platform) == 1) &&
(be16_to_cpu (record->encoding) == 0)) {
/* Find PS Name (name_id = 6). OT spec says PS name must be one of
* the following two encodings */
status = find_name (name, 6, 3, 1, 0x409, &ps_name); /* win, unicode, english-us */
if (unlikely(status))
goto fail;
 
if (be16_to_cpu (record->name) == 4) {
font_name = malloc (be16_to_cpu(record->length) + 1);
if (font_name == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (!ps_name) {
status = find_name (name, 6, 1, 0, 0, &ps_name); /* mac, roman, english */
if (unlikely(status))
goto fail;
}
strncpy(font_name,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
be16_to_cpu (record->length));
font_name[be16_to_cpu (record->length)] = 0;
 
/* Find Family name (name_id = 1) */
status = find_name (name, 1, 3, 1, 0x409, &family_name); /* win, unicode, english-us */
if (unlikely(status))
goto fail;
 
if (!family_name) {
status = find_name (name, 1, 3, 0, 0x409, &family_name); /* win, symbol, english-us */
if (unlikely(status))
goto fail;
}
 
if (be16_to_cpu (record->name) == 6) {
ps_name = malloc (be16_to_cpu(record->length) + 1);
if (ps_name == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
if (!family_name) {
status = find_name (name, 1, 1, 0, 0, &family_name); /* mac, roman, english */
if (unlikely(status))
goto fail;
}
strncpy(ps_name,
((char*)name) + be16_to_cpu (name->strings_offset) + be16_to_cpu (record->offset),
be16_to_cpu (record->length));
ps_name[be16_to_cpu (record->length)] = 0;
}
 
if (font_name && ps_name)
break;
if (!family_name) {
status = find_name (name, 1, 3, 1, -1, &family_name); /* win, unicode, any language */
if (unlikely(status))
goto fail;
}
}
 
free (name);
 
/* Ensure PS name does not contain any spaces */
/* Ensure PS name is a valid PDF/PS name object. In PDF names are
* treated as UTF8 and non ASCII bytes, ' ', and '#' are encoded
* as '#' followed by 2 hex digits that encode the byte. By also
* encoding the characters in the reserved string we ensure the
* name is also PS compatible. */
if (ps_name) {
for (i = 0, j = 0; ps_name[j]; j++) {
if (ps_name[j] == ' ')
continue;
ps_name[i++] = ps_name[j];
static const char *reserved = "()<>[]{}/%#\\";
char buf[128]; /* max name length is 127 bytes */
char *src = ps_name;
char *dst = buf;
 
while (*src && dst < buf + 127) {
unsigned char c = *src;
if (c < 0x21 || c > 0x7e || strchr (reserved, c)) {
if (dst + 4 > buf + 127)
break;
 
snprintf (dst, 4, "#%02X", c);
src++;
dst += 3;
} else {
*dst++ = *src++;
}
ps_name[i] = '\0';
}
*dst = 0;
free (ps_name);
ps_name = strdup (buf);
if (ps_name == NULL) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
}
 
*ps_name_out = ps_name;
*font_name_out = font_name;
*font_name_out = family_name;
 
return CAIRO_STATUS_SUCCESS;
 
fail:
free (name);
 
if (ps_name != NULL)
free (ps_name);
 
if (font_name != NULL)
free (font_name);
 
free (family_name);
*ps_name_out = NULL;
*font_name_out = NULL;
 
1430,4 → 1614,47
return status;
}
 
cairo_int_status_t
_cairo_truetype_get_style (cairo_scaled_font_t *scaled_font,
int *weight,
cairo_bool_t *bold,
cairo_bool_t *italic)
{
cairo_status_t status;
const cairo_scaled_font_backend_t *backend;
tt_os2_t os2;
unsigned long size;
uint16_t selection;
 
backend = scaled_font->backend;
if (!backend->load_truetype_table)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = 0;
status = backend->load_truetype_table (scaled_font,
TT_TAG_OS2, 0,
NULL,
&size);
if (status)
return status;
 
if (size < sizeof(os2))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = sizeof (os2);
status = backend->load_truetype_table (scaled_font,
TT_TAG_OS2, 0,
(unsigned char *) &os2,
&size);
if (status)
return status;
 
*weight = be16_to_cpu (os2.usWeightClass);
selection = be16_to_cpu (os2.fsSelection);
*bold = (selection & TT_FS_SELECTION_BOLD) ? TRUE : FALSE;
*italic = (selection & TT_FS_SELECTION_ITALIC) ? TRUE : FALSE;
 
return CAIRO_STATUS_SUCCESS;
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type1-fallback.c
35,6 → 35,8
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
146,7 → 148,7
charstring_encode_command (cairo_array_t *data, int command)
{
cairo_status_t status;
int orig_size;
unsigned int orig_size;
unsigned char buf[5];
unsigned char *p = buf;
 
173,7 → 175,7
cairo_charstring_type_t type)
{
cairo_status_t status;
int orig_size;
unsigned int orig_size;
unsigned char buf[10];
unsigned char *p = buf;
 
407,7 → 409,6
path_info.type = type;
if (emit_path) {
status = _cairo_path_fixed_interpret (scaled_glyph->path,
CAIRO_DIRECTION_FORWARD,
_charstring_move_to,
_charstring_line_to,
_charstring_curve_to,
514,6 → 515,20
"} readonly def\n"
"/Encoding 256 array\n"
"0 1 255 {1 index exch /.notdef put} for\n");
if (font->scaled_font_subset->is_latin) {
for (i = 1; i < 256; i++) {
int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
 
if (subset_glyph > 0) {
if (font->scaled_font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (font->output, "dup %d /%s put\n",
i, font->scaled_font_subset->glyph_names[subset_glyph]);
} else {
_cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, subset_glyph);
}
}
}
} else {
for (i = 1; i < font->scaled_font_subset->num_glyphs; i++) {
if (font->scaled_font_subset->glyph_names != NULL) {
_cairo_output_stream_printf (font->output, "dup %d /%s put\n",
522,6 → 537,7
_cairo_output_stream_printf (font->output, "dup %d /g%d put\n", i, i);
}
}
}
_cairo_output_stream_printf (font->output,
"readonly def\n"
"currentdict end\n"
612,7 → 628,7
 
fail:
status2 = _cairo_output_stream_destroy (encrypted_output);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
 
return status;
/programs/develop/libraries/cairo/src/cairo-type1-glyph-names.c
0,0 → 1,410
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 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 Red Hat, Inc.
*
* Contributor(s):
* Kristian Høgsberg <krh@redhat.com>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_FONT_SUBSET
 
#include "cairo-type1-private.h"
#include "cairo-scaled-font-subsets-private.h"
 
#if 0
/*
* The three tables that follow are generated using this perl code:
*/
 
@ps_standard_encoding = (
# 0
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 16
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 32
"space", "exclam", "quotedbl", "numbersign",
"dollar", "percent", "ampersand", "quoteright",
"parenleft", "parenright", "asterisk", "plus",
"comma", "hyphen", "period", "slash",
# 48
"zero", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question",
# 64
"at", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
# 80
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore",
# 96
"quoteleft", "a", "b", "c",
"d", "e", "f", "g",
"h", "i", "j", "k",
"l", "m", "n", "o",
# 112
"p", "q", "r", "s",
"t", "u", "v", "w",
"x", "y", "z", "braceleft",
"bar", "braceright", "asciitilde", NULL,
# 128
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 144
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 160
NULL, "exclamdown", "cent", "sterling",
"fraction", "yen", "florin", "section",
"currency", "quotesingle", "quotedblleft", "guillemotleft",
"guilsinglleft","guilsinglright","fi", "fl",
# 176
NULL, "endash", "dagger", "daggerdbl",
"periodcentered",NULL, "paragraph", "bullet",
"quotesinglbase","quotedblbase","quotedblright","guillemotright",
"ellipsis", "perthousand", NULL, "questiondown",
# 192
NULL, "grave", "acute", "circumflex",
"tilde", "macron", "breve", "dotaccent",
"dieresis", NULL, "ring", "cedilla",
NULL, "hungarumlaut", "ogonek", "caron",
# 208
"emdash", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 224
NULL, "AE", NULL, "ordfeminine",
NULL, NULL, NULL, NULL,
"Lslash", "Oslash", "OE", "ordmasculine",
NULL, NULL, NULL, NULL,
# 240
NULL, "ae", NULL, NULL,
NULL, "dotlessi", NULL, NULL,
"lslash", "oslash", "oe", "germandbls",
NULL, NULL, NULL, NULL
);
 
@winansi_encoding = (
# 0
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 16
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
# 32
"space", "exclam", "quotedbl", "numbersign",
"dollar", "percent", "ampersand", "quotesingle",
"parenleft", "parenright", "asterisk", "plus",
"comma", "hyphen", "period", "slash",
# 48
"zero", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question",
# 64
"at", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
# 80
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore",
# 96
"grave", "a", "b", "c",
"d", "e", "f", "g",
"h", "i", "j", "k",
"l", "m", "n", "o",
# 112
"p", "q", "r", "s",
"t", "u", "v", "w",
"x", "y", "z", "braceleft",
"bar", "braceright", "asciitilde", NULL,
# 128
"Euro", NULL, "quotesinglbase","florin",
"quotedblbase", "ellipsis", "dagger", "daggerdbl",
"circumflex", "perthousand", "Scaron", "guilsinglleft",
"OE", NULL, "Zcaron", NULL,
# 144
NULL, "quoteleft", "quoteright", "quotedblleft",
"quotedblright","bullet", "endash", "emdash",
"tilde", "trademark", "scaron", "guilsinglright",
"oe", NULL, "zcaron", "Ydieresis",
# 160
NULL, "exclamdown", "cent", "sterling",
"currency", "yen", "brokenbar", "section",
"dieresis", "copyright", "ordfeminine", "guillemotleft",
# 173 is also "hyphen" but we leave this NULL to avoid duplicate names
"logicalnot", NULL, "registered", "macron",
# 176
"degree", "plusminus", "twosuperior", "threesuperior",
"acute", "mu", "paragraph", "periodcentered",
"cedilla", "onesuperior", "ordmasculine", "guillemotright",
"onequarter", "onehalf", "threequarters","questiondown",
# 192
"Agrave", "Aacute", "Acircumflex", "Atilde",
"Adieresis", "Aring", "AE", "Ccedilla",
"Egrave", "Eacute", "Ecircumflex", "Edieresis",
"Igrave", "Iacute", "Icircumflex", "Idieresis",
# 208
"Eth", "Ntilde", "Ograve", "Oacute",
"Ocircumflex", "Otilde", "Odieresis", "multiply",
"Oslash", "Ugrave", "Uacute", "Ucircumflex",
"Udieresis", "Yacute", "Thorn", "germandbls",
# 224
"agrave", "aacute", "acircumflex", "atilde",
"adieresis", "aring", "ae", "ccedilla",
"egrave", "eacute", "ecircumflex", "edieresis",
"igrave", "iacute", "icircumflex", "idieresis",
# 240
"eth", "ntilde", "ograve", "oacute",
"ocircumflex", "otilde", "odieresis", "divide",
"oslash", "ugrave", "uacute", "ucircumflex",
"udieresis", "yacute", "thorn", "ydieresis"
);
 
sub print_offsets {
$s = qq();
for $sym (@_) {
if (! ($sym eq NULL)) {
$ss = qq( $hash{$sym}/*$sym*/,);
} else {
$ss = qq( 0,);
}
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
print qq( $s\n);
}
 
@combined = (@ps_standard_encoding, @winansi_encoding);
print "static const char glyph_name_symbol[] = {\n";
%hash = ();
$s = qq( "\\0");
$offset = 1;
for $sym (@combined) {
if (! ($sym eq NULL)) {
if (! exists $hash{$sym}) {
$hash{$sym} = $offset;
$offset += length($sym) + 1;
$ss = qq( "$sym\\0");
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
}
}
print qq( $s\n);
print "};\n\n";
 
print "static const int16_t ps_standard_encoding_offset[256] = {\n";
print_offsets(@ps_standard_encoding);
print "};\n";
 
print "static const int16_t winansi_encoding_offset[256] = {\n";
print_offsets(@winansi_encoding);
print "};\n";
 
exit;
#endif
 
static const char glyph_name_symbol[] = {
"\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
"ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
"plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
"three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
"semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
"C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
"P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
"bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
"quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
"k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
"x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
"exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
"section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
"guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
"daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
"quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
"perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
"macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
"hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
"Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
"oslash\0" "oe\0" "germandbls\0" "Euro\0" "Scaron\0" "Zcaron\0" "trademark\0"
"scaron\0" "zcaron\0" "Ydieresis\0" "brokenbar\0" "copyright\0"
"logicalnot\0" "registered\0" "degree\0" "plusminus\0" "twosuperior\0"
"threesuperior\0" "mu\0" "onesuperior\0" "onequarter\0" "onehalf\0"
"threequarters\0" "Agrave\0" "Aacute\0" "Acircumflex\0" "Atilde\0"
"Adieresis\0" "Aring\0" "Ccedilla\0" "Egrave\0" "Eacute\0" "Ecircumflex\0"
"Edieresis\0" "Igrave\0" "Iacute\0" "Icircumflex\0" "Idieresis\0" "Eth\0"
"Ntilde\0" "Ograve\0" "Oacute\0" "Ocircumflex\0" "Otilde\0" "Odieresis\0"
"multiply\0" "Ugrave\0" "Uacute\0" "Ucircumflex\0" "Udieresis\0" "Yacute\0"
"Thorn\0" "agrave\0" "aacute\0" "acircumflex\0" "atilde\0" "adieresis\0"
"aring\0" "ccedilla\0" "egrave\0" "eacute\0" "ecircumflex\0" "edieresis\0"
"igrave\0" "iacute\0" "icircumflex\0" "idieresis\0" "eth\0" "ntilde\0"
"ograve\0" "oacute\0" "ocircumflex\0" "otilde\0" "odieresis\0" "divide\0"
"ugrave\0" "uacute\0" "ucircumflex\0" "udieresis\0" "yacute\0" "thorn\0"
"ydieresis\0"
};
 
static const int16_t ps_standard_encoding_offset[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
900/*germandbls*/, 0, 0, 0, 0,
};
 
static const int16_t winansi_encoding_offset[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 498/*quotesingle*/,
70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 719/*grave*/,
348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 911/*Euro*/, 0,
628/*quotesinglbase*/, 474/*florin*/, 643/*quotedblbase*/, 685/*ellipsis*/,
579/*dagger*/, 586/*daggerdbl*/, 731/*circumflex*/, 694/*perthousand*/,
916/*Scaron*/, 537/*guilsinglleft*/, 855/*OE*/, 0, 923/*Zcaron*/, 0, 0,
338/*quoteleft*/, 59/*quoteright*/, 510/*quotedblleft*/,
656/*quotedblright*/, 621/*bullet*/, 572/*endash*/, 819/*emdash*/,
742/*tilde*/, 930/*trademark*/, 940/*scaron*/, 551/*guilsinglright*/,
897/*oe*/, 0, 947/*zcaron*/, 954/*Ydieresis*/, 0, 436/*exclamdown*/,
447/*cent*/, 452/*sterling*/, 489/*currency*/, 470/*yen*/, 964/*brokenbar*/,
481/*section*/, 771/*dieresis*/, 974/*copyright*/, 829/*ordfeminine*/,
523/*guillemotleft*/, 984/*logicalnot*/, 0, 995/*registered*/, 748/*macron*/,
1006/*degree*/, 1013/*plusminus*/, 1023/*twosuperior*/,
1035/*threesuperior*/, 725/*acute*/, 1049/*mu*/, 611/*paragraph*/,
596/*periodcentered*/, 785/*cedilla*/, 1052/*onesuperior*/,
858/*ordmasculine*/, 670/*guillemotright*/, 1064/*onequarter*/,
1075/*onehalf*/, 1083/*threequarters*/, 706/*questiondown*/, 1097/*Agrave*/,
1104/*Aacute*/, 1111/*Acircumflex*/, 1123/*Atilde*/, 1130/*Adieresis*/,
1140/*Aring*/, 826/*AE*/, 1146/*Ccedilla*/, 1155/*Egrave*/, 1162/*Eacute*/,
1169/*Ecircumflex*/, 1181/*Edieresis*/, 1191/*Igrave*/, 1198/*Iacute*/,
1205/*Icircumflex*/, 1217/*Idieresis*/, 1227/*Eth*/, 1231/*Ntilde*/,
1238/*Ograve*/, 1245/*Oacute*/, 1252/*Ocircumflex*/, 1264/*Otilde*/,
1271/*Odieresis*/, 1281/*multiply*/, 848/*Oslash*/, 1290/*Ugrave*/,
1297/*Uacute*/, 1304/*Ucircumflex*/, 1316/*Udieresis*/, 1326/*Yacute*/,
1333/*Thorn*/, 900/*germandbls*/, 1339/*agrave*/, 1346/*aacute*/,
1353/*acircumflex*/, 1365/*atilde*/, 1372/*adieresis*/, 1382/*aring*/,
871/*ae*/, 1388/*ccedilla*/, 1397/*egrave*/, 1404/*eacute*/,
1411/*ecircumflex*/, 1423/*edieresis*/, 1433/*igrave*/, 1440/*iacute*/,
1447/*icircumflex*/, 1459/*idieresis*/, 1469/*eth*/, 1473/*ntilde*/,
1480/*ograve*/, 1487/*oacute*/, 1494/*ocircumflex*/, 1506/*otilde*/,
1513/*odieresis*/, 1523/*divide*/, 890/*oslash*/, 1530/*ugrave*/,
1537/*uacute*/, 1544/*ucircumflex*/, 1556/*udieresis*/, 1566/*yacute*/,
1573/*thorn*/, 1579/*ydieresis*/,
};
 
const char *
_cairo_ps_standard_encoding_to_glyphname (int glyph)
{
if (ps_standard_encoding_offset[glyph])
return glyph_name_symbol + ps_standard_encoding_offset[glyph];
else
return NULL;
}
 
const char *
_cairo_winansi_to_glyphname (int glyph)
{
if (winansi_encoding_offset[glyph])
return glyph_name_symbol + winansi_encoding_offset[glyph];
else
return NULL;
}
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type1-subset.c
1,3 → 1,4
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2006 Red Hat, Inc
41,6 → 42,8
 
#define _BSD_SOURCE /* for snprintf(), strdup() */
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_FONT_SUBSET
49,28 → 52,29
#include "cairo-scaled-font-subsets-private.h"
#include "cairo-output-stream-private.h"
 
/* XXX: Eventually, we need to handle other font backends */
#if CAIRO_HAS_FT_FONT
#include <ctype.h>
#include <locale.h>
 
#include "cairo-ft-private.h"
#define TYPE1_STACKSIZE 24 /* Defined in Type 1 Font Format */
 
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_TYPE1_TABLES_H
 
#include <ctype.h>
typedef struct {
int subset_index;
double width;
const char *encrypted_charstring;
int encrypted_charstring_length;
} glyph_data_t;
 
typedef struct _cairo_type1_font_subset {
cairo_scaled_font_subset_t *scaled_font_subset;
 
struct {
cairo_unscaled_font_t *unscaled_font;
unsigned int font_id;
char *base_font;
unsigned int num_glyphs;
double x_min, y_min, x_max, y_max;
double ascent, descent;
double units_per_em;
 
const char *data;
unsigned long header_size;
78,20 → 82,41
unsigned long trailer_size;
} base;
 
FT_Face face;
int num_glyphs;
 
/* The glyphs and glyph_names arrays are indexed by the order of
* the Charstrings in the font. This is not necessarily the same
* order as the glyph index. The index_to_glyph_name() font backend
* function is used to map the glyph index to the glyph order in
* the Charstrings. */
 
glyph_data_t *glyphs;
char **glyph_names;
cairo_array_t glyphs_array;
cairo_array_t glyph_names_array;
 
int num_subrs;
cairo_bool_t subset_subrs;
struct {
int subset_index;
double width;
char *name;
} *glyphs;
const char *subr_string;
int subr_length;
const char *np;
int np_length;
cairo_bool_t used;
} *subrs;
 
/* Indexed by subset_index this maps to the glyph order in the
* glyph_names and glyphs arrays. Has font->num_golyphs
* elements. */
int *subset_index_to_glyphs;
 
cairo_output_stream_t *output;
cairo_array_t contents;
 
const char *rd, *nd;
const char *rd, *nd, *np;
 
int lenIV;
 
char *type1_data;
unsigned int type1_length;
char *type1_end;
110,89 → 135,43
unsigned short eexec_key;
cairo_bool_t hex_encode;
int hex_column;
 
struct {
double stack[TYPE1_STACKSIZE];
int sp;
} build_stack;
 
struct {
int stack[TYPE1_STACKSIZE];
int sp;
} ps_stack;
 
 
} cairo_type1_font_subset_t;
 
 
static cairo_status_t
_cairo_type1_font_subset_init (cairo_type1_font_subset_t *font,
cairo_unscaled_font_t *unscaled_font,
cairo_scaled_font_subset_t *scaled_font_subset,
cairo_bool_t hex_encode)
{
cairo_ft_unscaled_font_t *ft_unscaled_font;
cairo_status_t status;
FT_Face face;
PS_FontInfoRec font_info;
int i, j;
 
ft_unscaled_font = (cairo_ft_unscaled_font_t *) unscaled_font;
 
face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
if (unlikely (face == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (FT_Get_PS_Font_Info(face, &font_info) != 0) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto fail1;
}
 
/* OpenType/CFF fonts also have a PS_FontInfoRec */
#if HAVE_FT_LOAD_SFNT_TABLE
if (FT_IS_SFNT (face)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto fail1;
}
#endif
 
memset (font, 0, sizeof (*font));
font->base.unscaled_font = _cairo_unscaled_font_reference (unscaled_font);
font->base.num_glyphs = face->num_glyphs;
font->base.x_min = face->bbox.xMin / (double)face->units_per_EM;
font->base.y_min = face->bbox.yMin / (double)face->units_per_EM;
font->base.x_max = face->bbox.xMax / (double)face->units_per_EM;
font->base.y_max = face->bbox.yMax / (double)face->units_per_EM;
font->base.ascent = face->ascender / (double)face->units_per_EM;
font->base.descent = face->descender / (double)face->units_per_EM;
font->scaled_font_subset = scaled_font_subset;
 
if (face->family_name) {
font->base.base_font = strdup (face->family_name);
if (unlikely (font->base.base_font == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail2;
}
for (i = 0, j = 0; font->base.base_font[j]; j++) {
if (font->base.base_font[j] == ' ')
continue;
font->base.base_font[i++] = font->base.base_font[j];
}
font->base.base_font[i] = '\0';
}
_cairo_array_init (&font->glyphs_array, sizeof (glyph_data_t));
_cairo_array_init (&font->glyph_names_array, sizeof (char *));
font->subset_index_to_glyphs = NULL;
font->base.num_glyphs = 0;
font->num_subrs = 0;
font->subset_subrs = TRUE;
font->subrs = NULL;
 
font->glyphs = calloc (face->num_glyphs, sizeof font->glyphs[0]);
if (unlikely (font->glyphs == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail3;
}
 
font->hex_encode = hex_encode;
font->num_glyphs = 0;
for (i = 0; i < face->num_glyphs; i++)
font->glyphs[i].subset_index = -1;
 
_cairo_array_init (&font->contents, sizeof (char));
 
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return CAIRO_STATUS_SUCCESS;
 
fail3:
if (font->base.base_font)
free (font->base.base_font);
fail2:
_cairo_unscaled_font_destroy (unscaled_font);
fail1:
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return status;
}
 
static void
201,7 → 180,9
if (font->glyphs[glyph].subset_index >= 0)
return;
 
font->glyphs[glyph].subset_index = font->num_glyphs++;
font->glyphs[glyph].subset_index = font->num_glyphs;
font->subset_index_to_glyphs[font->num_glyphs] = glyph;
font->num_glyphs++;
}
 
static cairo_bool_t
318,6 → 299,164
}
 
static cairo_status_t
cairo_type1_font_subset_get_matrix (cairo_type1_font_subset_t *font,
const char *name,
double *a,
double *b,
double *c,
double *d)
{
const char *start, *end, *segment_end;
int ret, s_max, i, j;
char *s;
struct lconv *locale_data;
const char *decimal_point;
int decimal_point_len;
 
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
 
assert (decimal_point_len != 0);
 
segment_end = font->header_segment + font->header_segment_size;
start = find_token (font->header_segment, segment_end, name);
if (start == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
end = find_token (start, segment_end, "def");
if (end == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
s_max = end - start + 5*decimal_point_len + 1;
s = malloc (s_max);
if (unlikely (s == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
i = 0;
j = 0;
while (i < end - start && j < s_max - decimal_point_len) {
if (start[i] == '.') {
strncpy(s + j, decimal_point, decimal_point_len);
i++;
j += decimal_point_len;
} else {
s[j++] = start[i++];
}
}
s[j] = 0;
 
start = strpbrk (s, "{[");
if (!start) {
free (s);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
start++;
ret = 0;
if (*start)
ret = sscanf(start, "%lf %lf %lf %lf", a, b, c, d);
 
free (s);
 
if (ret != 4)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_get_bbox (cairo_type1_font_subset_t *font)
{
cairo_status_t status;
double x_min, y_min, x_max, y_max;
double xx, yx, xy, yy;
 
status = cairo_type1_font_subset_get_matrix (font, "/FontBBox",
&x_min,
&y_min,
&x_max,
&y_max);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_get_matrix (font, "/FontMatrix",
&xx, &yx, &xy, &yy);
if (unlikely (status))
return status;
 
if (yy == 0.0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Freetype uses 1/yy to get units per EM */
font->base.units_per_em = 1.0/yy;
 
font->base.x_min = x_min / font->base.units_per_em;
font->base.y_min = y_min / font->base.units_per_em;
font->base.x_max = x_max / font->base.units_per_em;
font->base.y_max = y_max / font->base.units_per_em;
font->base.ascent = font->base.y_max;
font->base.descent = font->base.y_min;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_get_fontname (cairo_type1_font_subset_t *font)
{
const char *start, *end, *segment_end;
char *s;
int i;
 
segment_end = font->header_segment + font->header_segment_size;
start = find_token (font->header_segment, segment_end, "/FontName");
if (start == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
start += strlen ("/FontName");
 
end = find_token (start, segment_end, "def");
if (end == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
s = malloc (end - start + 1);
if (unlikely (s == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
strncpy (s, start, end - start);
s[end - start] = 0;
 
start = strchr (s, '/');
if (!start++ || !start) {
free (s);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* If font name is prefixed with a subset tag, strip it off. */
if (strlen(start) > 7 && start[6] == '+') {
for (i = 0; i < 6; i++) {
if (start[i] < 'A' || start[i] > 'Z')
break;
}
if (i == 6)
start += 7;
}
 
font->base.base_font = strdup (start);
free (s);
if (unlikely (font->base.base_font == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
s = font->base.base_font;
while (*s && !is_ps_delimiter(*s))
s++;
 
*s = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_write_header (cairo_type1_font_subset_t *font,
const char *name)
{
391,14 → 530,27
_cairo_output_stream_printf (font->output,
"/Encoding 256 array\n"
"0 1 255 {1 index exch /.notdef put} for\n");
for (i = 1; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].subset_index < 0)
if (font->scaled_font_subset->is_latin) {
for (i = 1; i < 256; i++) {
int subset_glyph = font->scaled_font_subset->latin_to_subset_glyph_index[i];
 
if (subset_glyph > 0) {
_cairo_output_stream_printf (font->output,
"dup %d /%s put\n",
i,
_cairo_winansi_to_glyphname (i));
}
}
} else {
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].subset_index <= 0)
continue;
_cairo_output_stream_printf (font->output,
"dup %d /%s put\n",
font->glyphs[i].subset_index,
font->glyphs[i].name);
font->glyph_names[i]);
}
}
_cairo_output_stream_printf (font->output, "readonly def");
 
end = find_token (start, segment_end, "def");
406,6 → 558,10
return CAIRO_INT_STATUS_UNSUPPORTED;
end += 3;
 
/* There are some buggy fonts that contain more than one /Encoding */
if (find_token (end, segment_end, "/Encoding"))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_output_stream_write (font->output, end, segment_end - end);
 
return font->output->status;
471,7 → 627,7
in = (unsigned char *) font->eexec_segment;
end = (unsigned char *) in + font->eexec_segment_size;
 
font->cleartext = malloc (font->eexec_segment_size);
font->cleartext = malloc (font->eexec_segment_size + 1);
if (unlikely (font->cleartext == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
509,6 → 665,9
for (i = 0; i < 4 && i < font->eexec_segment_size; i++)
font->cleartext[i] = ' ';
 
/* Ensure strtol() can not scan past the end of the cleartext */
font->cleartext[font->eexec_segment_size] = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
527,64 → 686,6
return p;
}
 
static int
cairo_type1_font_subset_lookup_glyph (cairo_type1_font_subset_t *font,
const char *glyph_name, int length)
{
unsigned int i;
 
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].name &&
strncmp (font->glyphs[i].name, glyph_name, length) == 0 &&
font->glyphs[i].name[length] == '\0')
return i;
}
 
return -1;
}
 
static cairo_status_t
cairo_type1_font_subset_get_glyph_names_and_widths (cairo_type1_font_subset_t *font)
{
unsigned int i;
char buffer[256];
FT_Error error;
 
/* Get glyph names and width using the freetype API */
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyphs[i].name != NULL)
continue;
 
error = FT_Load_Glyph (font->face, i,
FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING |
FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM);
if (error != FT_Err_Ok) {
/* propagate fatal errors from FreeType */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
font->glyphs[i].width = font->face->glyph->metrics.horiAdvance / (double)font->face->units_per_EM;
 
error = FT_Get_Glyph_Name(font->face, i, buffer, sizeof buffer);
if (error != FT_Err_Ok) {
/* propagate fatal errors from FreeType */
if (error == FT_Err_Out_Of_Memory)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
font->glyphs[i].name = strdup (buffer);
if (unlikely (font->glyphs[i].name == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
cairo_type1_font_subset_decrypt_charstring (const unsigned char *in, int size, unsigned char *out)
{
618,228 → 719,71
return p;
}
 
#if 0
/*
* The two tables that follow are generated using this perl code:
*/
 
@encoding = (
/* 0 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 16 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 32 */
"space", "exclam", "quotedbl", "numbersign",
"dollar", "percent", "ampersand", "quoteright",
"parenleft", "parenright", "asterisk", "plus",
"comma", "hyphen", "period", "slash",
/* 48 */
"zero", "one", "two", "three",
"four", "five", "six", "seven",
"eight", "nine", "colon", "semicolon",
"less", "equal", "greater", "question",
/* 64 */
"at", "A", "B", "C",
"D", "E", "F", "G",
"H", "I", "J", "K",
"L", "M", "N", "O",
/* 80 */
"P", "Q", "R", "S",
"T", "U", "V", "W",
"X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore",
/* 96 */
"quoteleft", "a", "b", "c",
"d", "e", "f", "g",
"h", "i", "j", "k",
"l", "m", "n", "o",
/* 112 */
"p", "q", "r", "s",
"t", "u", "v", "w",
"x", "y", "z", "braceleft",
"bar", "braceright", "asciitilde", NULL,
/* 128 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 144 */
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 160 */
NULL, "exclamdown", "cent", "sterling",
"fraction", "yen", "florin", "section",
"currency", "quotesingle", "quotedblleft", "guillemotleft",
"guilsinglleft","guilsinglright","fi", "fl",
/* 176 */
NULL, "endash", "dagger", "daggerdbl",
"periodcentered",NULL, "paragraph", "bullet",
"quotesinglbase","quotedblbase","quotedblright","guillemotright",
"ellipsis", "perthousand", NULL, "questiondown",
/* 192 */
NULL, "grave", "acute", "circumflex",
"tilde", "macron", "breve", "dotaccent",
"dieresis", NULL, "ring", "cedilla",
NULL, "hungarumlaut", "ogonek", "caron",
/* 208 */
"emdash", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
/* 224 */
NULL, "AE", NULL, "ordfeminine",
NULL, NULL, NULL, NULL,
"Lslash", "Oslash", "OE", "ordmasculine",
NULL, NULL, NULL, NULL,
/* 240 */
NULL, "ae", NULL, NULL,
NULL, "dotlessi", NULL, NULL,
"lslash", "oslash", "oe", "germandbls",
NULL, NULL, NULL, NULL
);
 
print "static const char ps_standard_encoding_symbol[] = {\n";
$s = qq( "\\0");
for $sym (@encoding) {
if (! ($sym eq NULL)) {
$ss = qq( "$sym\\0");
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
}
print qq( $s\n);
print "};\n\n";
print "static const int16_t ps_standard_encoding_offset[256] = {\n";
$offset = 1;
$s = qq();
for $sym (@encoding) {
if (! ($sym eq NULL)) {
$ss = qq( $offset/*$sym*/,);
$offset += length($sym) + 1;
} else {
$ss = qq( 0,);
}
if (length($s) + length($ss) > 78) {
print qq( $s\n);
$s = "";
}
$s .= $ss;
}
print qq( $s\n);
print "};\n";
exit;
#endif
 
static const char ps_standard_encoding_symbol[] = {
"\0" "space\0" "exclam\0" "quotedbl\0" "numbersign\0" "dollar\0" "percent\0"
"ampersand\0" "quoteright\0" "parenleft\0" "parenright\0" "asterisk\0"
"plus\0" "comma\0" "hyphen\0" "period\0" "slash\0" "zero\0" "one\0" "two\0"
"three\0" "four\0" "five\0" "six\0" "seven\0" "eight\0" "nine\0" "colon\0"
"semicolon\0" "less\0" "equal\0" "greater\0" "question\0" "at\0" "A\0" "B\0"
"C\0" "D\0" "E\0" "F\0" "G\0" "H\0" "I\0" "J\0" "K\0" "L\0" "M\0" "N\0" "O\0"
"P\0" "Q\0" "R\0" "S\0" "T\0" "U\0" "V\0" "W\0" "X\0" "Y\0" "Z\0"
"bracketleft\0" "backslash\0" "bracketright\0" "asciicircum\0" "underscore\0"
"quoteleft\0" "a\0" "b\0" "c\0" "d\0" "e\0" "f\0" "g\0" "h\0" "i\0" "j\0"
"k\0" "l\0" "m\0" "n\0" "o\0" "p\0" "q\0" "r\0" "s\0" "t\0" "u\0" "v\0" "w\0"
"x\0" "y\0" "z\0" "braceleft\0" "bar\0" "braceright\0" "asciitilde\0"
"exclamdown\0" "cent\0" "sterling\0" "fraction\0" "yen\0" "florin\0"
"section\0" "currency\0" "quotesingle\0" "quotedblleft\0" "guillemotleft\0"
"guilsinglleft\0" "guilsinglright\0" "fi\0" "fl\0" "endash\0" "dagger\0"
"daggerdbl\0" "periodcentered\0" "paragraph\0" "bullet\0" "quotesinglbase\0"
"quotedblbase\0" "quotedblright\0" "guillemotright\0" "ellipsis\0"
"perthousand\0" "questiondown\0" "grave\0" "acute\0" "circumflex\0" "tilde\0"
"macron\0" "breve\0" "dotaccent\0" "dieresis\0" "ring\0" "cedilla\0"
"hungarumlaut\0" "ogonek\0" "caron\0" "emdash\0" "AE\0" "ordfeminine\0"
"Lslash\0" "Oslash\0" "OE\0" "ordmasculine\0" "ae\0" "dotlessi\0" "lslash\0"
"oslash\0" "oe\0" "germandbls\0"
};
 
static const int16_t ps_standard_encoding_offset[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1/*space*/, 7/*exclam*/, 14/*quotedbl*/, 23/*numbersign*/,
34/*dollar*/, 41/*percent*/, 49/*ampersand*/, 59/*quoteright*/,
70/*parenleft*/, 80/*parenright*/, 91/*asterisk*/, 100/*plus*/, 105/*comma*/,
111/*hyphen*/, 118/*period*/, 125/*slash*/, 131/*zero*/, 136/*one*/,
140/*two*/, 144/*three*/, 150/*four*/, 155/*five*/, 160/*six*/, 164/*seven*/,
170/*eight*/, 176/*nine*/, 181/*colon*/, 187/*semicolon*/, 197/*less*/,
202/*equal*/, 208/*greater*/, 216/*question*/, 225/*at*/, 228/*A*/, 230/*B*/,
232/*C*/, 234/*D*/, 236/*E*/, 238/*F*/, 240/*G*/, 242/*H*/, 244/*I*/,
246/*J*/, 248/*K*/, 250/*L*/, 252/*M*/, 254/*N*/, 256/*O*/, 258/*P*/,
260/*Q*/, 262/*R*/, 264/*S*/, 266/*T*/, 268/*U*/, 270/*V*/, 272/*W*/,
274/*X*/, 276/*Y*/, 278/*Z*/, 280/*bracketleft*/, 292/*backslash*/,
302/*bracketright*/, 315/*asciicircum*/, 327/*underscore*/, 338/*quoteleft*/,
348/*a*/, 350/*b*/, 352/*c*/, 354/*d*/, 356/*e*/, 358/*f*/, 360/*g*/,
362/*h*/, 364/*i*/, 366/*j*/, 368/*k*/, 370/*l*/, 372/*m*/, 374/*n*/,
376/*o*/, 378/*p*/, 380/*q*/, 382/*r*/, 384/*s*/, 386/*t*/, 388/*u*/,
390/*v*/, 392/*w*/, 394/*x*/, 396/*y*/, 398/*z*/, 400/*braceleft*/,
410/*bar*/, 414/*braceright*/, 425/*asciitilde*/, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
436/*exclamdown*/, 447/*cent*/, 452/*sterling*/, 461/*fraction*/, 470/*yen*/,
474/*florin*/, 481/*section*/, 489/*currency*/, 498/*quotesingle*/,
510/*quotedblleft*/, 523/*guillemotleft*/, 537/*guilsinglleft*/,
551/*guilsinglright*/, 566/*fi*/, 569/*fl*/, 0, 572/*endash*/, 579/*dagger*/,
586/*daggerdbl*/, 596/*periodcentered*/, 0, 611/*paragraph*/, 621/*bullet*/,
628/*quotesinglbase*/, 643/*quotedblbase*/, 656/*quotedblright*/,
670/*guillemotright*/, 685/*ellipsis*/, 694/*perthousand*/, 0,
706/*questiondown*/, 0, 719/*grave*/, 725/*acute*/, 731/*circumflex*/,
742/*tilde*/, 748/*macron*/, 755/*breve*/, 761/*dotaccent*/, 771/*dieresis*/,
0, 780/*ring*/, 785/*cedilla*/, 0, 793/*hungarumlaut*/, 806/*ogonek*/,
813/*caron*/, 819/*emdash*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
826/*AE*/, 0, 829/*ordfeminine*/, 0, 0, 0, 0, 841/*Lslash*/, 848/*Oslash*/,
855/*OE*/, 858/*ordmasculine*/, 0, 0, 0, 0, 0, 871/*ae*/, 0, 0, 0,
874/*dotlessi*/, 0, 0, 883/*lslash*/, 890/*oslash*/, 897/*oe*/,
900/*germandbls*/, 0, 0, 0, 0,
};
 
#define ps_standard_encoding(index) ((index) ? ps_standard_encoding_symbol+ps_standard_encoding_offset[(index)] : NULL)
 
static cairo_status_t
use_standard_encoding_glyph (cairo_type1_font_subset_t *font, int index)
{
const char *glyph_name;
unsigned int i;
 
if (index < 0 || index > 255)
return CAIRO_STATUS_SUCCESS;
 
glyph_name = ps_standard_encoding(index);
glyph_name = _cairo_ps_standard_encoding_to_glyphname (index);
if (glyph_name == NULL)
return CAIRO_STATUS_SUCCESS;
 
index = cairo_type1_font_subset_lookup_glyph (font,
glyph_name,
strlen(glyph_name));
if (index < 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
for (i = 0; i < font->base.num_glyphs; i++) {
if (font->glyph_names[i] && strcmp (font->glyph_names[i], glyph_name) == 0) {
cairo_type1_font_subset_use_glyph (font, i);
 
cairo_type1_font_subset_use_glyph (font, index);
 
return CAIRO_STATUS_SUCCESS;
}
}
 
#define TYPE1_CHARSTRING_COMMAND_ESCAPE (12)
#define TYPE1_CHARSTRING_COMMAND_SEAC (32 + 6)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
 
#define TYPE1_CHARSTRING_COMMAND_HSTEM 0x01
#define TYPE1_CHARSTRING_COMMAND_VSTEM 0x03
#define TYPE1_CHARSTRING_COMMAND_VMOVETO 0x04
#define TYPE1_CHARSTRING_COMMAND_RLINETO 0x05
#define TYPE1_CHARSTRING_COMMAND_HLINETO 0x06
#define TYPE1_CHARSTRING_COMMAND_VLINETO 0x07
#define TYPE1_CHARSTRING_COMMAND_RRCURVETO 0x08
#define TYPE1_CHARSTRING_COMMAND_CLOSEPATH 0x09
#define TYPE1_CHARSTRING_COMMAND_CALLSUBR 0x0a
#define TYPE1_CHARSTRING_COMMAND_RETURN 0x0b
#define TYPE1_CHARSTRING_COMMAND_ESCAPE 0x0c
#define TYPE1_CHARSTRING_COMMAND_HSBW 0x0d
#define TYPE1_CHARSTRING_COMMAND_ENDCHAR 0x0e
#define TYPE1_CHARSTRING_COMMAND_RMOVETO 0x15
#define TYPE1_CHARSTRING_COMMAND_HMOVETO 0x16
#define TYPE1_CHARSTRING_COMMAND_VHCURVETO 0x1e
#define TYPE1_CHARSTRING_COMMAND_HVCURVETO 0x1f
#define TYPE1_CHARSTRING_COMMAND_DOTSECTION 0x0c00
#define TYPE1_CHARSTRING_COMMAND_VSTEM3 0x0c01
#define TYPE1_CHARSTRING_COMMAND_HSTEM3 0x0c02
#define TYPE1_CHARSTRING_COMMAND_SEAC 0x0c06
#define TYPE1_CHARSTRING_COMMAND_SBW 0x0c07
#define TYPE1_CHARSTRING_COMMAND_DIV 0x0c0c
#define TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR 0x0c10
#define TYPE1_CHARSTRING_COMMAND_POP 0x0c11
#define TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT 0x0c21
 
/* Parse the charstring, including recursing into subroutines. Find
* the glyph width, subroutines called, and glyphs required by the
* SEAC operator. */
static cairo_status_t
cairo_type1_font_subset_look_for_seac(cairo_type1_font_subset_t *font,
const char *name, int name_length,
const char *encrypted_charstring, int encrypted_charstring_length)
cairo_type1_font_subset_parse_charstring (cairo_type1_font_subset_t *font,
int glyph,
const char *encrypted_charstring,
int encrypted_charstring_length)
{
cairo_status_t status;
unsigned char *charstring;
const unsigned char *end;
const unsigned char *p;
int stack[5], sp, value;
int command;
 
charstring = malloc (encrypted_charstring_length);
851,18 → 795,72
encrypted_charstring_length,
charstring);
end = charstring + encrypted_charstring_length;
 
p = charstring + 4;
sp = 0;
 
p = charstring + font->lenIV;
status = CAIRO_STATUS_SUCCESS;
while (p < end) {
if (*p < 32) {
command = *p++;
switch (command) {
case TYPE1_CHARSTRING_COMMAND_HSTEM:
case TYPE1_CHARSTRING_COMMAND_VSTEM:
case TYPE1_CHARSTRING_COMMAND_VMOVETO:
case TYPE1_CHARSTRING_COMMAND_RLINETO:
case TYPE1_CHARSTRING_COMMAND_HLINETO:
case TYPE1_CHARSTRING_COMMAND_VLINETO:
case TYPE1_CHARSTRING_COMMAND_RRCURVETO:
case TYPE1_CHARSTRING_COMMAND_CLOSEPATH:
case TYPE1_CHARSTRING_COMMAND_RMOVETO:
case TYPE1_CHARSTRING_COMMAND_HMOVETO:
case TYPE1_CHARSTRING_COMMAND_VHCURVETO:
case TYPE1_CHARSTRING_COMMAND_HVCURVETO:
case TYPE1_CHARSTRING_COMMAND_RETURN:
case TYPE1_CHARSTRING_COMMAND_ENDCHAR:
default:
/* stack clearing operator */
font->build_stack.sp = 0;
break;
 
if (command == TYPE1_CHARSTRING_COMMAND_ESCAPE)
command = 32 + *p++;
case TYPE1_CHARSTRING_COMMAND_CALLSUBR:
if (font->subset_subrs && font->build_stack.sp > 0) {
double int_val;
if (modf(font->build_stack.stack[--font->build_stack.sp], &int_val) == 0.0) {
int subr_num = int_val;
if (subr_num >= 0 && subr_num < font->num_subrs) {
font->subrs[subr_num].used = TRUE;
status = cairo_type1_font_subset_parse_charstring (
font,
glyph,
font->subrs[subr_num].subr_string,
font->subrs[subr_num].subr_length);
break;
}
}
}
font->subset_subrs = FALSE;
break;
 
case TYPE1_CHARSTRING_COMMAND_HSBW:
if (font->build_stack.sp < 2) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
font->glyphs[glyph].width = font->build_stack.stack[1]/font->base.units_per_em;
font->build_stack.sp = 0;
break;
 
case TYPE1_CHARSTRING_COMMAND_ESCAPE:
command = command << 8 | *p++;
switch (command) {
case TYPE1_CHARSTRING_COMMAND_DOTSECTION:
case TYPE1_CHARSTRING_COMMAND_VSTEM3:
case TYPE1_CHARSTRING_COMMAND_HSTEM3:
case TYPE1_CHARSTRING_COMMAND_SETCURRENTPOINT:
default:
/* stack clearing operator */
font->build_stack.sp = 0;
break;
 
case TYPE1_CHARSTRING_COMMAND_SEAC:
/* The seac command takes five integer arguments. The
* last two are glyph indices into the PS standard
870,36 → 868,260
* glyph is composed from. All we need to do is to
* make sure those glyphs are present in the subset
* under their standard names. */
status = use_standard_encoding_glyph (font, stack[3]);
if (font->build_stack.sp < 5) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
status = use_standard_encoding_glyph (font, font->build_stack.stack[3]);
if (unlikely (status))
return status;
goto cleanup;
 
status = use_standard_encoding_glyph (font, stack[4]);
status = use_standard_encoding_glyph (font, font->build_stack.stack[4]);
if (unlikely (status))
return status;
goto cleanup;
 
sp = 0;
font->build_stack.sp = 0;
break;
 
default:
sp = 0;
case TYPE1_CHARSTRING_COMMAND_SBW:
if (font->build_stack.sp < 4) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
font->glyphs[glyph].width = font->build_stack.stack[2]/font->base.units_per_em;
font->build_stack.sp = 0;
break;
 
case TYPE1_CHARSTRING_COMMAND_DIV:
if (font->build_stack.sp < 2) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
} else {
double num1 = font->build_stack.stack[font->build_stack.sp - 2];
double num2 = font->build_stack.stack[font->build_stack.sp - 1];
font->build_stack.sp--;
if (num2 == 0.0) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
font->build_stack.stack[font->build_stack.sp - 1] = num1/num2;
}
break;
 
case TYPE1_CHARSTRING_COMMAND_CALLOTHERSUBR:
if (font->build_stack.sp < 1) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
font->build_stack.sp--;
font->ps_stack.sp = 0;
while (font->build_stack.sp)
font->ps_stack.stack[font->ps_stack.sp++] = font->build_stack.stack[--font->build_stack.sp];
 
break;
 
case TYPE1_CHARSTRING_COMMAND_POP:
if (font->ps_stack.sp < 1) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
 
/* T1 spec states that if the interpreter does not
* support executing the callothersub, the results
* must be taken from the callothersub arguments. */
font->build_stack.stack[font->build_stack.sp++] = font->ps_stack.stack[--font->ps_stack.sp];
break;
}
break;
}
} else {
/* integer argument */
p = cairo_type1_font_subset_decode_integer (p, &value);
if (sp < 5)
stack[sp++] = value;
if (font->build_stack.sp < TYPE1_STACKSIZE) {
int val;
p = cairo_type1_font_subset_decode_integer (p, &val);
font->build_stack.stack[font->build_stack.sp++] = val;
} else {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto cleanup;
}
}
}
 
cleanup:
free (charstring);
 
return status;
}
 
static cairo_status_t
cairo_type1_font_subset_build_subr_list (cairo_type1_font_subset_t *font,
int subr_number,
const char *encrypted_charstring, int encrypted_charstring_length,
const char *np, int np_length)
{
 
font->subrs[subr_number].subr_string = encrypted_charstring;
font->subrs[subr_number].subr_length = encrypted_charstring_length;
font->subrs[subr_number].np = np;
font->subrs[subr_number].np_length = np_length;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
write_used_subrs (cairo_type1_font_subset_t *font,
int subr_number,
const char *subr_string, int subr_string_length,
const char *np, int np_length)
{
cairo_status_t status;
char buffer[256];
int length;
 
if (!font->subrs[subr_number].used)
return CAIRO_STATUS_SUCCESS;
 
length = snprintf (buffer, sizeof buffer,
"dup %d %d %s ",
subr_number, subr_string_length, font->rd);
status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_write_encrypted (font,
subr_string,
subr_string_length);
if (unlikely (status))
return status;
 
if (np) {
status = cairo_type1_font_subset_write_encrypted (font, np, np_length);
} else {
length = snprintf (buffer, sizeof buffer, "%s\n", font->np);
status = cairo_type1_font_subset_write_encrypted (font, buffer, length);
}
if (unlikely (status))
return status;
 
return CAIRO_STATUS_SUCCESS;
}
 
typedef cairo_status_t (*subr_func_t) (cairo_type1_font_subset_t *font,
int subr_number,
const char *subr_string, int subr_string_length,
const char *np, int np_length);
 
static cairo_status_t
cairo_type1_font_for_each_subr (cairo_type1_font_subset_t *font,
const char *array_start,
const char *cleartext_end,
subr_func_t func,
const char **array_end)
{
const char *p, *subr_string;
char *end;
int subr_num, subr_length;
const char *np;
int np_length;
cairo_status_t status;
 
/* We're looking at "dup" at the start of the first subroutine. The subroutines
* definitions are on the form:
*
* dup 5 23 RD <23 binary bytes> NP
*
* or alternatively using -| and |- instead of RD and ND.
* The first number is the subroutine number.
*/
 
p = array_start;
while (p + 3 < cleartext_end && strncmp (p, "dup", 3) == 0) {
p = skip_token (p, cleartext_end);
 
/* get subr number */
subr_num = strtol (p, &end, 10);
if (p == end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (subr_num < 0 || subr_num >= font->num_subrs)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* get subr length */
p = end;
subr_length = strtol (p, &end, 10);
if (p == end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Skip past -| or RD to binary data. There is exactly one space
* between the -| or RD token and the encrypted data, thus '+ 1'. */
subr_string = skip_token (end, cleartext_end) + 1;
 
np = NULL;
np_length = 0;
 
/* Skip binary data and | or NP token. */
p = skip_token (subr_string + subr_length, cleartext_end);
while (p < cleartext_end && _cairo_isspace(*p))
p++;
 
/* Some fonts have "noaccess put" instead of "NP" */
if (p + 3 < cleartext_end && strncmp (p, "put", 3) == 0) {
p = skip_token (p, cleartext_end);
while (p < cleartext_end && _cairo_isspace(*p))
p++;
 
np = subr_string + subr_length;
np_length = p - np;
}
 
status = func (font, subr_num,
subr_string, subr_length, np, np_length);
if (unlikely (status))
return status;
 
}
 
*array_end = (char *) p;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
cairo_type1_font_subset_build_glyph_list (cairo_type1_font_subset_t *font,
int glyph_number,
const char *name, int name_length,
const char *encrypted_charstring, int encrypted_charstring_length)
{
char *s;
glyph_data_t glyph;
cairo_status_t status;
 
s = malloc (name_length + 1);
if (unlikely (s == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
strncpy (s, name, name_length);
s[name_length] = 0;
 
status = _cairo_array_append (&font->glyph_names_array, &s);
if (unlikely (status))
return status;
 
glyph.subset_index = -1;
glyph.width = 0;
glyph.encrypted_charstring = encrypted_charstring;
glyph.encrypted_charstring_length = encrypted_charstring_length;
status = _cairo_array_append (&font->glyphs_array, &glyph);
 
return status;
}
 
static cairo_status_t
write_used_glyphs (cairo_type1_font_subset_t *font,
int glyph_number,
const char *name, int name_length,
const char *charstring, int charstring_length)
{
906,7 → 1128,34
cairo_status_t status;
char buffer[256];
int length;
int subset_id;
int ch;
const char *wa_name;
 
if (font->glyphs[glyph_number].subset_index < 0)
return CAIRO_STATUS_SUCCESS;
 
if (font->scaled_font_subset->is_latin) {
/* When using the WinAnsi encoding in PDF, the /Encoding array
* is ignored and instead glyphs are keyed by glyph names. To
* ensure correct rendering we replace the glyph name in the
* font with the standard name.
**/
subset_id = font->glyphs[glyph_number].subset_index;
if (subset_id > 0) {
ch = font->scaled_font_subset->to_latin_char[subset_id];
wa_name = _cairo_winansi_to_glyphname (ch);
/* If this subset contains any seac glyphs, additional non
* winansi glyphs (wa_name = NULL) may be included in the
* subset. In this case the original name is used.
*/
if (wa_name) {
name = wa_name;
name_length = strlen(name);
}
}
}
 
length = snprintf (buffer, sizeof buffer,
"/%.*s %d %s ",
name_length, name, charstring_length, font->rd);
929,6 → 1178,7
}
 
typedef cairo_status_t (*glyph_func_t) (cairo_type1_font_subset_t *font,
int glyph_number,
const char *name, int name_length,
const char *charstring, int charstring_length);
 
939,9 → 1189,11
glyph_func_t func,
const char **dict_out)
{
int charstring_length, name_length, glyph_index;
int charstring_length, name_length;
const char *p, *charstring, *name;
char *end;
cairo_status_t status;
int glyph_count;
 
/* We're looking at '/' in the name of the first glyph. The glyph
* definitions are on the form:
958,7 → 1210,7
*/
 
p = dict_start;
 
glyph_count = 0;
while (*p == '/') {
name = p + 1;
p = skip_token (p, dict_end);
982,16 → 1234,12
if (p == dict_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
glyph_index = cairo_type1_font_subset_lookup_glyph (font,
name, name_length);
if (font->glyphs[glyph_index].subset_index >= 0) {
cairo_status_t status = func (font,
status = func (font, glyph_count++,
name, name_length,
charstring, charstring_length);
if (unlikely (status))
return status;
}
}
 
*dict_out = p;
 
1004,34 → 1252,110
const char *name)
{
cairo_status_t status;
const char *p, *charstrings, *dict_start;
const char *closefile_token;
char buffer[32], *glyph_count_end;
int num_charstrings, length;
const char *p, *subrs, *charstrings, *array_start, *array_end, *dict_start, *dict_end;
const char *lenIV_start, *lenIV_end, *closefile_token;
char buffer[32], *lenIV_str, *subr_count_end, *glyph_count_end;
int ret, lenIV, length;
const cairo_scaled_font_backend_t *backend;
unsigned int i;
int glyph, j;
 
/* The private dict holds hint information, common subroutines and
* the actual glyph definitions (charstrings).
*
* FIXME: update this comment.
* What we do here is scan directly to the /Subrs token, which
* marks the beginning of the subroutines. We read in all the
* subroutines, then move on to the /CharString token, which marks
* the beginning of the glyph definitions, and read in the charstrings.
*
* What we do here is scan directly the /CharString token, which
* marks the beginning of the glyph definitions. Then we parse
* through the glyph definitions and weed out the glyphs not in
* our subset. Everything else before and after the glyph
* definitions is copied verbatim to the output. It might be
* worthwile to figure out which of the common subroutines are
* used by the glyphs in the subset and get rid of the rest. */
* The charstrings are parsed to extract glyph widths, work out
* which subroutines are called, and to see if any extra glyphs
* need to be included due to the use of the seac glyph combining
* operator.
*
* Finally, the private dict is copied to the subset font minus the
* subroutines and charstrings not required.
*/
 
/* FIXME: The /Subrs array contains binary data and could
* conceivably have "/CharStrings" in it, so we might need to skip
* this more cleverly. */
charstrings = find_token (font->cleartext, font->cleartext_end, "/CharStrings");
/* Determine lenIV, the number of random characters at the start of
each encrypted charstring. The default is 4, but this can be
overridden in the private dict. */
font->lenIV = 4;
if ((lenIV_start = find_token (font->cleartext, font->cleartext_end, "/lenIV")) != NULL) {
lenIV_start += 6;
lenIV_end = find_token (lenIV_start, font->cleartext_end, "def");
if (lenIV_end == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
lenIV_str = malloc (lenIV_end - lenIV_start + 1);
if (unlikely (lenIV_str == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
strncpy (lenIV_str, lenIV_start, lenIV_end - lenIV_start);
lenIV_str[lenIV_end - lenIV_start] = 0;
 
ret = sscanf(lenIV_str, "%d", &lenIV);
free(lenIV_str);
 
if (unlikely (ret <= 0))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Apparently some fonts signal unencrypted charstrings with a negative lenIV,
though this is not part of the Type 1 Font Format specification. See, e.g.
http://lists.gnu.org/archive/html/freetype-devel/2000-06/msg00064.html. */
if (unlikely (lenIV < 0))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font->lenIV = lenIV;
}
 
/* Find start of Subrs */
subrs = find_token (font->cleartext, font->cleartext_end, "/Subrs");
if (subrs == NULL) {
font->subset_subrs = FALSE;
p = font->cleartext;
array_start = NULL;
goto skip_subrs;
}
 
/* Scan past /Subrs and get the array size. */
p = subrs + strlen ("/Subrs");
font->num_subrs = strtol (p, &subr_count_end, 10);
if (subr_count_end == p)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (font->num_subrs <= 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font->subrs = calloc (font->num_subrs, sizeof (font->subrs[0]));
if (unlikely (font->subrs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
/* look for "dup" which marks the beginning of the first subr */
array_start = find_token (subr_count_end, font->cleartext_end, "dup");
if (subrs == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Read in the subroutines */
status = cairo_type1_font_for_each_subr (font,
array_start,
font->cleartext_end,
cairo_type1_font_subset_build_subr_list,
&array_end);
if (unlikely(status))
return status;
 
p = array_end;
skip_subrs:
 
/* Find start of CharStrings */
charstrings = find_token (p, font->cleartext_end, "/CharStrings");
if (charstrings == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Scan past /CharStrings and the integer following it. */
p = charstrings + strlen ("/CharStrings");
num_charstrings = strtol (p, &glyph_count_end, 10);
strtol (p, &glyph_count_end, 10);
if (p == glyph_count_end)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
1044,30 → 1368,71
return CAIRO_INT_STATUS_UNSUPPORTED;
dict_start = p;
 
status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
if (unlikely (status))
return status;
 
/* Now that we have the private dictionary broken down in
* sections, do the first pass through the glyph definitions to
* figure out which subrs and othersubrs are use and which extra
* glyphs may be required by the seac operator. */
* build a list of glyph names and charstrings. */
status = cairo_type1_font_subset_for_each_glyph (font,
dict_start,
font->cleartext_end,
cairo_type1_font_subset_look_for_seac,
&p);
cairo_type1_font_subset_build_glyph_list,
&dict_end);
if (unlikely (status))
return status;
 
closefile_token = find_token (p, font->cleartext_end, "closefile");
if (closefile_token == NULL)
font->glyphs = _cairo_array_index (&font->glyphs_array, 0);
font->glyph_names = _cairo_array_index (&font->glyph_names_array, 0);
font->base.num_glyphs = _cairo_array_num_elements (&font->glyphs_array);
font->subset_index_to_glyphs = calloc (font->base.num_glyphs, sizeof font->subset_index_to_glyphs[0]);
if (unlikely (font->subset_index_to_glyphs == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
backend = font->scaled_font_subset->scaled_font->backend;
if (!backend->index_to_glyph_name)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = cairo_type1_font_subset_get_glyph_names_and_widths (font);
/* Find the glyph number corresponding to each glyph in the subset
* and mark it as in use */
 
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
unsigned long index;
 
status = backend->index_to_glyph_name (font->scaled_font_subset->scaled_font,
font->glyph_names,
font->base.num_glyphs,
font->scaled_font_subset->glyphs[i],
&index);
if (unlikely (status))
return status;
 
cairo_type1_font_subset_use_glyph (font, index);
}
 
/* Go through the charstring of each glyph in use, get the glyph
* width and figure out which extra glyphs may be required by the
* seac operator (which may cause font->num_glyphs to increase
* while this loop is executing). Also subset the Subrs. */
for (j = 0; j < font->num_glyphs; j++) {
glyph = font->subset_index_to_glyphs[j];
font->build_stack.sp = 0;
font->ps_stack.sp = 0;
status = cairo_type1_font_subset_parse_charstring (font,
glyph,
font->glyphs[glyph].encrypted_charstring,
font->glyphs[glyph].encrypted_charstring_length);
if (unlikely (status))
return status;
}
 
/* Always include the first five subroutines in case the Flex/hint mechanism is
* being used. */
for (j = 0; j < MIN (font->num_subrs, 5); j++) {
font->subrs[j].used = TRUE;
}
 
closefile_token = find_token (dict_end, font->cleartext_end, "closefile");
if (closefile_token == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* We're ready to start outputting. First write the header,
* i.e. the public part of the font dict.*/
status = cairo_type1_font_subset_write_header (font, name);
1076,14 → 1441,35
 
font->base.header_size = _cairo_output_stream_get_position (font->output);
 
 
/* Start outputting the private dict. First output everything up
* to the /CharStrings token. */
/* Start outputting the private dict */
if (font->subset_subrs) {
/* First output everything up to the start of the Subrs array. */
status = cairo_type1_font_subset_write_encrypted (font, font->cleartext,
charstrings - font->cleartext);
array_start - font->cleartext);
if (unlikely (status))
return status;
 
/* Write out the subr definitions for each of the glyphs in
* the subset. */
status = cairo_type1_font_for_each_subr (font,
array_start,
font->cleartext_end,
write_used_subrs,
&p);
if (unlikely (status))
return status;
} else {
p = font->cleartext;
}
 
/* If subr subsetting, output everything from end of subrs to
* start of /CharStrings token. If not subr subsetting, output
* everything start of private dict to start of /CharStrings
* token. */
status = cairo_type1_font_subset_write_encrypted (font, p, charstrings - p);
if (unlikely (status))
return status;
 
/* Write out new charstring count */
length = snprintf (buffer, sizeof buffer,
"/CharStrings %d", font->num_glyphs);
1141,12 → 1527,15
 
_cairo_output_stream_write (font->output, cleartomark_token,
font->type1_end - cleartomark_token);
if (*(font->type1_end - 1) != '\n')
_cairo_output_stream_printf (font->output, "\n");
 
} else if (!font->eexec_segment_is_ascii) {
/* Fonts embedded in PDF may omit the fixed-content portion
* that includes the 'cleartomark' operator. Type 1 in PDF is
* always binary. */
 
_cairo_output_stream_printf (font->output, "cleartomark");
_cairo_output_stream_printf (font->output, "cleartomark\n");
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
1183,9 → 1572,11
if (find_token (font->cleartext, font->cleartext_end, "/-|") != NULL) {
font->rd = "-|";
font->nd = "|-";
font->np = "|";
} else if (find_token (font->cleartext, font->cleartext_end, "/RD") != NULL) {
font->rd = "RD";
font->nd = "ND";
font->np = "NP";
} else {
/* Don't know *what* kind of font this is... */
return CAIRO_INT_STATUS_UNSUPPORTED;
1194,6 → 1585,14
font->eexec_key = CAIRO_TYPE1_PRIVATE_DICT_KEY;
font->hex_column = 0;
 
status = cairo_type1_font_subset_get_bbox (font);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_get_fontname (font);
if (unlikely (status))
return status;
 
status = cairo_type1_font_subset_write_private_dict (font, name);
if (unlikely (status))
return status;
1212,6 → 1611,20
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
check_fontdata_is_type1 (const unsigned char *data, long length)
{
/* Test for Type 1 Binary (PFB) */
if (length > 2 && data[0] == 0x80 && data[1] == 0x01)
return TRUE;
 
/* Test for Type 1 1 ASCII (PFA) */
if (length > 2 && data[0] == '%' && data[1] == '!')
return TRUE;
 
return FALSE;
}
 
static cairo_status_t
cairo_type1_font_subset_generate (void *abstract_font,
const char *name)
1218,57 → 1631,46
 
{
cairo_type1_font_subset_t *font = abstract_font;
cairo_ft_unscaled_font_t *ft_unscaled_font;
unsigned long ret;
cairo_scaled_font_t *scaled_font;
cairo_status_t status;
unsigned long data_length;
 
ft_unscaled_font = (cairo_ft_unscaled_font_t *) font->base.unscaled_font;
font->face = _cairo_ft_unscaled_font_lock_face (ft_unscaled_font);
if (unlikely (font->face == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
scaled_font = font->scaled_font_subset->scaled_font;
if (!scaled_font->backend->load_type1_data)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font->type1_length = font->face->stream->size;
status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &data_length);
if (status)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
font->type1_length = data_length;
font->type1_data = malloc (font->type1_length);
if (unlikely (font->type1_data == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto fail;
}
if (unlikely (font->type1_data == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
if (font->face->stream->read != NULL) {
/* Note that read() may be implemented as a macro, thanks POSIX!, so we
* need to wrap the following usage in parentheses in order to
* disambiguate it for the pre-processor - using the verbose function
* pointer dereference for clarity.
*/
ret = (* font->face->stream->read) (font->face->stream, 0,
status = scaled_font->backend->load_type1_data (scaled_font, 0,
(unsigned char *) font->type1_data,
font->type1_length);
if (ret != font->type1_length) {
status = _cairo_error (CAIRO_STATUS_READ_ERROR);
goto fail;
}
} else {
memcpy (font->type1_data,
font->face->stream->base, font->type1_length);
}
&data_length);
if (unlikely (status))
return status;
 
if (!check_fontdata_is_type1 ((unsigned char *)font->type1_data, data_length))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_array_grow_by (&font->contents, 4096);
if (unlikely (status))
goto fail;
return status;
 
font->output = _cairo_output_stream_create (type1_font_write, NULL, font);
if (unlikely ((status = font->output->status)))
goto fail;
return status;
 
status = cairo_type1_font_subset_write (font, name);
if (unlikely (status))
goto fail;
return status;
 
font->base.data = _cairo_array_index (&font->contents, 0);
 
fail:
_cairo_ft_unscaled_font_unlock_face (ft_unscaled_font);
 
return status;
}
 
1284,20 → 1686,26
_cairo_array_fini (&font->contents);
 
free (font->type1_data);
if (font->glyphs != NULL) {
for (i = 0; i < font->base.num_glyphs; i++)
free (font->glyphs[i].name);
for (i = 0; i < _cairo_array_num_elements (&font->glyph_names_array); i++) {
char **s;
 
s = _cairo_array_index (&font->glyph_names_array, i);
free (*s);
}
_cairo_array_fini (&font->glyph_names_array);
_cairo_array_fini (&font->glyphs_array);
 
_cairo_unscaled_font_destroy (font->base.unscaled_font);
free (font->subrs);
 
if (font->output != NULL)
status = _cairo_output_stream_destroy (font->output);
 
if (font->base.base_font)
free (font->base.base_font);
free (font->glyphs);
 
free (font->subset_index_to_glyphs);
 
free (font->cleartext);
 
return status;
}
 
1308,30 → 1716,20
cairo_bool_t hex_encode)
{
cairo_type1_font_subset_t font;
cairo_status_t status, status_ignored;
unsigned long parent_glyph, length;
cairo_status_t status;
unsigned long length;
unsigned int i;
cairo_unscaled_font_t *unscaled_font;
char buf[30];
 
/* XXX: Need to fix this to work with a general cairo_unscaled_font_t. */
if (!_cairo_scaled_font_is_ft (scaled_font_subset->scaled_font))
/* We need to use a fallback font generated from the synthesized outlines. */
if (scaled_font_subset->scaled_font->backend->is_synthetic &&
scaled_font_subset->scaled_font->backend->is_synthetic (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (_cairo_ft_scaled_font_is_vertical (scaled_font_subset->scaled_font))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
unscaled_font = _cairo_ft_scaled_font_get_unscaled_font (scaled_font_subset->scaled_font);
 
status = _cairo_type1_font_subset_init (&font, unscaled_font, hex_encode);
status = _cairo_type1_font_subset_init (&font, scaled_font_subset, hex_encode);
if (unlikely (status))
return status;
 
for (i = 0; i < scaled_font_subset->num_glyphs; i++) {
parent_glyph = scaled_font_subset->glyphs[i];
cairo_type1_font_subset_use_glyph (&font, parent_glyph);
}
 
status = cairo_type1_font_subset_generate (&font, name);
if (unlikely (status))
goto fail1;
1384,7 → 1782,7
fail2:
free (type1_subset->base_font);
fail1:
status_ignored = _cairo_type1_font_subset_fini (&font);
_cairo_type1_font_subset_fini (&font);
 
return status;
}
1400,32 → 1798,26
cairo_bool_t
_cairo_type1_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font)
{
cairo_ft_unscaled_font_t *unscaled;
FT_Face face;
PS_FontInfoRec font_info;
cairo_bool_t is_type1 = FALSE;
cairo_status_t status;
unsigned long length;
unsigned char buf[64];
 
if (!_cairo_scaled_font_is_ft (scaled_font))
if (!scaled_font->backend->load_type1_data)
return FALSE;
unscaled = (cairo_ft_unscaled_font_t *) _cairo_ft_scaled_font_get_unscaled_font (scaled_font);
face = _cairo_ft_unscaled_font_lock_face (unscaled);
if (!face)
 
status = scaled_font->backend->load_type1_data (scaled_font, 0, NULL, &length);
if (status)
return FALSE;
 
if (FT_Get_PS_Font_Info(face, &font_info) == 0)
is_type1 = TRUE;
/* We only need a few bytes to test for Type 1 */
if (length > sizeof (buf))
length = sizeof (buf);
 
/* OpenType/CFF fonts also have a PS_FontInfoRec */
#if HAVE_FT_LOAD_SFNT_TABLE
if (FT_IS_SFNT (face))
is_type1 = FALSE;
#endif
status = scaled_font->backend->load_type1_data (scaled_font, 0, buf, &length);
if (status)
return FALSE;
 
_cairo_ft_unscaled_font_unlock_face (unscaled);
 
return is_type1;
return check_fontdata_is_type1 (buf, length);
}
 
#endif /* CAIRO_HAS_FT_FONT */
 
#endif /* CAIRO_HAS_FONT_SUBSET */
/programs/develop/libraries/cairo/src/cairo-type3-glyph-surface-private.h
45,7 → 45,8
#include "cairo-surface-clipper-private.h"
#include "cairo-pdf-operators-private.h"
 
typedef cairo_status_t (*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image,
typedef cairo_int_status_t
(*cairo_type3_glyph_surface_emit_image_t) (cairo_image_surface_t *image,
cairo_output_stream_t *stream);
 
typedef struct cairo_type3_glyph_surface {
/programs/develop/libraries/cairo/src/cairo-type3-glyph-surface.c
42,7 → 42,9
#include "cairo-output-stream-private.h"
#include "cairo-recording-surface-private.h"
#include "cairo-analysis-surface-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-clipper-private.h"
 
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend;
187,7 → 189,7
_cairo_type3_glyph_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
const cairo_surface_pattern_t *pattern;
223,7 → 225,7
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
return _cairo_type3_glyph_surface_paint (abstract_surface,
op, mask,
234,13 → 236,13
_cairo_type3_glyph_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
260,11 → 262,11
_cairo_type3_glyph_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip)
const cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
285,8 → 287,7
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs)
const cairo_clip_t *clip)
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_int_status_t status;
321,33 → 322,35
 
static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH,
NULL, /* _cairo_type3_glyph_surface_create_similar */
_cairo_type3_glyph_surface_finish,
 
_cairo_default_context_create, /* XXX usable through a context? */
 
NULL, /* create similar */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
NULL, /* source */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
NULL, /* release_dest_image */
NULL, /* clone_similar */
NULL, /* composite */
NULL, /* fill_rectangles */
NULL, /* composite_trapezoids */
NULL, /* create_span_renderer */
NULL, /* check_span_renderer */
NULL, /* cairo_type3_glyph_surface_copy_page */
NULL, /* _cairo_type3_glyph_surface_show_page */
NULL, /* snapshot */
 
NULL, /* copy page */
NULL, /* show page */
 
NULL, /* _cairo_type3_glyph_surface_get_extents */
NULL, /* old_show_glyphs */
NULL, /* _cairo_type3_glyph_surface_get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
 
_cairo_type3_glyph_surface_paint,
_cairo_type3_glyph_surface_mask,
_cairo_type3_glyph_surface_stroke,
_cairo_type3_glyph_surface_fill,
NULL, /* fill-stroke */
_cairo_type3_glyph_surface_show_glyphs,
NULL, /* snapshot */
};
 
static void
415,7 → 418,7
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status, status2;
cairo_int_status_t status, status2;
cairo_output_stream_t *null_stream;
 
if (unlikely (surface->base.status))
433,11 → 436,11
CAIRO_SCALED_GLYPH_INFO_RECORDING_SURFACE,
&scaled_glyph);
 
if (_cairo_status_is_error (status))
if (_cairo_int_status_is_error (status))
goto cleanup;
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = CAIRO_STATUS_SUCCESS;
status = CAIRO_INT_STATUS_SUCCESS;
goto cleanup;
}
 
448,13 → 451,13
 
status = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
status = CAIRO_STATUS_SUCCESS;
status = CAIRO_INT_STATUS_SUCCESS;
 
cleanup:
_cairo_scaled_font_thaw_cache (surface->scaled_font);
 
status2 = _cairo_output_stream_destroy (null_stream);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
 
return status;
469,7 → 472,7
{
cairo_type3_glyph_surface_t *surface = abstract_surface;
cairo_scaled_glyph_t *scaled_glyph;
cairo_status_t status, status2;
cairo_int_status_t status, status2;
double x_advance, y_advance;
cairo_matrix_t font_matrix_inverse;
 
489,10 → 492,10
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&scaled_glyph);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
}
if (_cairo_status_is_error (status)) {
if (_cairo_int_status_is_error (status)) {
_cairo_scaled_font_thaw_cache (surface->scaled_font);
return status;
}
505,7 → 508,7
/* The invertability of font_matrix is tested in
* pdf_operators_show_glyphs before any glyphs are mapped to the
* subset. */
assert (status2 == CAIRO_STATUS_SUCCESS);
assert (status2 == CAIRO_INT_STATUS_SUCCESS);
 
cairo_matrix_transform_distance (&font_matrix_inverse, &x_advance, &y_advance);
*width = x_advance;
522,7 → 525,7
_cairo_fixed_to_double (bbox->p2.x),
- _cairo_fixed_to_double (bbox->p1.y));
 
if (status == CAIRO_STATUS_SUCCESS) {
if (status == CAIRO_INT_STATUS_SUCCESS) {
cairo_output_stream_t *mem_stream;
 
mem_stream = _cairo_memory_stream_create ();
537,17 → 540,17
&surface->base);
 
status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
 
_cairo_output_stream_printf (surface->stream, "Q\n");
 
_cairo_type3_glyph_surface_set_stream (surface, stream);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
_cairo_memory_stream_copy (mem_stream, stream);
 
status2 = _cairo_output_stream_destroy (mem_stream);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = status2;
}
 
/programs/develop/libraries/cairo/src/cairo-types-private.h
44,6 → 44,8
#include "cairo-list-private.h"
#include "cairo-reference-count-private.h"
 
CAIRO_BEGIN_DECLS
 
/**
* SECTION:cairo-types
* @Title: Types
50,7 → 52,7
* @Short_Description: Generic data types
*
* This section lists generic data types used in the cairo API.
*/
**/
 
typedef struct _cairo_array cairo_array_t;
typedef struct _cairo_backend cairo_backend_t;
61,9 → 63,15
typedef struct _cairo_clip_path cairo_clip_path_t;
typedef struct _cairo_color cairo_color_t;
typedef struct _cairo_color_stop cairo_color_stop_t;
typedef struct _cairo_contour cairo_contour_t;
typedef struct _cairo_contour_chain cairo_contour_chain_t;
typedef struct _cairo_contour_iter cairo_contour_iter_t;
typedef struct _cairo_damage cairo_damage_t;
typedef struct _cairo_device_backend cairo_device_backend_t;
typedef struct _cairo_font_face_backend cairo_font_face_backend_t;
typedef struct _cairo_gstate cairo_gstate_t;
typedef struct _cairo_gstate_backend cairo_gstate_backend_t;
typedef struct _cairo_glyph_text_info cairo_glyph_text_info_t;
typedef struct _cairo_hash_entry cairo_hash_entry_t;
typedef struct _cairo_hash_table cairo_hash_table_t;
typedef struct _cairo_image_surface cairo_image_surface_t;
73,18 → 81,32
typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t;
typedef struct _cairo_path_fixed cairo_path_fixed_t;
typedef struct _cairo_rectangle_int16 cairo_glyph_size_t;
typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t;
typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t;
typedef struct _cairo_solid_pattern cairo_solid_pattern_t;
typedef struct _cairo_surface_attributes cairo_surface_attributes_t;
typedef struct _cairo_surface_backend cairo_surface_backend_t;
typedef struct _cairo_surface_observer cairo_surface_observer_t;
typedef struct _cairo_surface_snapshot cairo_surface_snapshot_t;
typedef struct _cairo_surface_subsurface cairo_surface_subsurface_t;
typedef struct _cairo_surface_wrapper cairo_surface_wrapper_t;
typedef struct _cairo_traps cairo_traps_t;
typedef struct _cairo_tristrip cairo_tristrip_t;
typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t;
typedef struct _cairo_xlib_screen_info cairo_xlib_screen_info_t;
 
typedef cairo_array_t cairo_user_data_array_t;
 
typedef struct _cairo_scaled_font_private cairo_scaled_font_private_t;
typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t;
typedef struct _cairo_scaled_glyph cairo_scaled_glyph_t;
typedef struct _cairo_scaled_glyph_private cairo_scaled_glyph_private_t;
 
typedef struct cairo_compositor cairo_compositor_t;
typedef struct cairo_fallback_compositor cairo_fallback_compositor_t;
typedef struct cairo_mask_compositor cairo_mask_compositor_t;
typedef struct cairo_traps_compositor cairo_traps_compositor_t;
typedef struct cairo_spans_compositor cairo_spans_compositor_t;
 
struct _cairo_observer {
cairo_list_t link;
void (*callback) (cairo_observer_t *self, void *arg);
109,7 → 131,7
*
* _cairo_hash_table_insert (hash_table, &my_entry->base);
*
* IMPORTANT: The caller is reponsible for initializing
* IMPORTANT: The caller is responsible for initializing
* my_entry->base.hash with a hash code derived from the key. The
* essential property of the hash code is that keys_equal must never
* return %TRUE for two keys that have different hashes. The best hash
132,9 → 154,7
unsigned int size;
unsigned int num_elements;
unsigned int element_size;
char **elements;
 
cairo_bool_t is_snapshot;
char *elements;
};
 
/**
161,6 → 181,12
CAIRO_LCD_FILTER_FIR5
} cairo_lcd_filter_t;
 
typedef enum _cairo_round_glyph_positions {
CAIRO_ROUND_GLYPH_POS_DEFAULT,
CAIRO_ROUND_GLYPH_POS_ON,
CAIRO_ROUND_GLYPH_POS_OFF
} cairo_round_glyph_positions_t;
 
struct _cairo_font_options {
cairo_antialias_t antialias;
cairo_subpixel_order_t subpixel_order;
167,8 → 193,19
cairo_lcd_filter_t lcd_filter;
cairo_hint_style_t hint_style;
cairo_hint_metrics_t hint_metrics;
cairo_round_glyph_positions_t round_glyph_positions;
};
 
struct _cairo_glyph_text_info {
const char *utf8;
int utf8_len;
 
const cairo_text_cluster_t *clusters;
int num_clusters;
cairo_text_cluster_flags_t cluster_flags;
};
 
 
/* XXX: Right now, the _cairo_color structure puts unpremultiplied
color in the doubles and premultiplied color in the shorts. Yes,
this is crazy insane, (but at least we don't export this
207,25 → 244,11
CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */
} cairo_paginated_mode_t;
 
/* Sure wish C had a real enum type so that this would be distinct
* from #cairo_status_t. Oh well, without that, I'll use this bogus 100
* offset. We want to keep it fit in int8_t as the compiler may choose
* that for #cairo_status_t */
typedef enum _cairo_int_status {
CAIRO_INT_STATUS_UNSUPPORTED = 100,
CAIRO_INT_STATUS_DEGENERATE,
CAIRO_INT_STATUS_NOTHING_TO_DO,
CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY,
CAIRO_INT_STATUS_IMAGE_FALLBACK,
CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN,
 
CAIRO_INT_STATUS_LAST_STATUS
} cairo_int_status_t;
 
typedef enum _cairo_internal_surface_type {
CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000,
CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
CAIRO_INTERNAL_SURFACE_TYPE_OBSERVER,
CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK,
CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING,
233,9 → 256,11
CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH
} cairo_internal_surface_type_t;
 
typedef enum _cairo_internal_device_type {
CAIRO_INTERNAL_DEVICE_TYPE_OBSERVER = 0x1000,
} cairo_device_surface_type_t;
 
#define CAIRO_HAS_TEST_PAGINATED_SURFACE 1
#define CAIRO_HAS_TEST_NULL_SURFACE 1
#define CAIRO_HAS_TEST_WRAPPING_SURFACE 1
 
typedef struct _cairo_slope {
cairo_fixed_t dx;
247,11 → 272,21
double y;
} cairo_point_double_t;
 
typedef struct _cairo_circle_double {
cairo_point_double_t center;
double radius;
} cairo_circle_double_t;
 
typedef struct _cairo_distance_double {
double dx;
double dy;
} cairo_distance_double_t;
 
typedef struct _cairo_box_double {
cairo_point_double_t p1;
cairo_point_double_t p2;
} cairo_box_double_t;
 
typedef struct _cairo_line {
cairo_point_t p1;
cairo_point_t p2;
283,13 → 318,6
typedef struct _cairo_polygon {
cairo_status_t status;
 
cairo_point_t first_point;
cairo_point_t last_point;
cairo_point_t current_point;
cairo_slope_t current_edge;
cairo_bool_t has_current_point;
cairo_bool_t has_current_edge;
 
cairo_box_t extents;
cairo_box_t limit;
const cairo_box_t *limits;
303,7 → 331,8
 
typedef cairo_warn cairo_status_t
(*cairo_spline_add_point_func_t) (void *closure,
const cairo_point_t *point);
const cairo_point_t *point,
const cairo_slope_t *tangent);
 
typedef struct _cairo_spline_knots {
cairo_point_t a, b, c, d;
370,6 → 399,14
CAIRO_IMAGE_UNKNOWN
} cairo_image_transparency_t;
 
typedef enum _cairo_image_color {
CAIRO_IMAGE_IS_COLOR,
CAIRO_IMAGE_IS_GRAYSCALE,
CAIRO_IMAGE_IS_MONOCHROME,
CAIRO_IMAGE_UNKNOWN_COLOR
} cairo_image_color_t;
 
 
struct _cairo_mime_data {
cairo_reference_count_t ref_count;
unsigned char *data;
378,76 → 415,6
void *closure;
};
 
struct _cairo_pattern {
cairo_pattern_type_t type;
cairo_reference_count_t ref_count;
cairo_status_t status;
cairo_user_data_array_t user_data;
 
cairo_matrix_t matrix;
cairo_filter_t filter;
cairo_extend_t extend;
 
cairo_bool_t has_component_alpha;
};
 
struct _cairo_solid_pattern {
cairo_pattern_t base;
cairo_color_t color;
};
 
typedef struct _cairo_surface_pattern {
cairo_pattern_t base;
 
cairo_surface_t *surface;
} cairo_surface_pattern_t;
 
typedef struct _cairo_gradient_stop {
double offset;
cairo_color_stop_t color;
} cairo_gradient_stop_t;
 
typedef struct _cairo_gradient_pattern {
cairo_pattern_t base;
 
unsigned int n_stops;
unsigned int stops_size;
cairo_gradient_stop_t *stops;
cairo_gradient_stop_t stops_embedded[2];
} cairo_gradient_pattern_t;
 
typedef struct _cairo_linear_pattern {
cairo_gradient_pattern_t base;
 
cairo_point_t p1;
cairo_point_t p2;
} cairo_linear_pattern_t;
 
typedef struct _cairo_radial_pattern {
cairo_gradient_pattern_t base;
 
cairo_point_t c1;
cairo_fixed_t r1;
cairo_point_t c2;
cairo_fixed_t r2;
} cairo_radial_pattern_t;
 
typedef union {
cairo_gradient_pattern_t base;
 
cairo_linear_pattern_t linear;
cairo_radial_pattern_t radial;
} cairo_gradient_pattern_union_t;
 
typedef union {
cairo_pattern_type_t type;
cairo_pattern_t base;
 
cairo_solid_pattern_t solid;
cairo_surface_pattern_t surface;
cairo_gradient_pattern_union_t gradient;
} cairo_pattern_union_t;
 
/*
* A #cairo_unscaled_font_t is just an opaque handle we use in the
* glyph cache.
457,21 → 424,6
cairo_reference_count_t ref_count;
const cairo_unscaled_font_backend_t *backend;
} cairo_unscaled_font_t;
CAIRO_END_DECLS
 
typedef struct _cairo_scaled_glyph {
cairo_hash_entry_t hash_entry;
 
cairo_text_extents_t metrics; /* user-space metrics */
cairo_text_extents_t fs_metrics; /* font-space metrics */
cairo_box_t bbox; /* device-space bounds */
int16_t x_advance; /* device-space rounded X advance */
int16_t y_advance; /* device-space rounded Y advance */
 
unsigned int has_info;
cairo_image_surface_t *surface; /* device-space image */
cairo_path_fixed_t *path; /* device-space outline */
cairo_surface_t *recording_surface; /* device-space recording-surface */
 
void *surface_private; /* for the surface backend */
} cairo_scaled_glyph_t;
#endif /* CAIRO_TYPES_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-user-font.c
49,7 → 49,7
* in a font. This is most useful in implementing fonts in non-standard
* formats, like SVG fonts and Flash fonts, but can also be used by games and
* other application to draw "funky" fonts.
*/
**/
 
/**
* CAIRO_HAS_USER_FONT:
59,8 → 59,8
* The user font backend is always built in versions of cairo that support
* this feature (1.8 and later).
*
* @Since: 1.8
*/
* Since: 1.8
**/
 
typedef struct _cairo_user_scaled_font_methods {
cairo_user_scaled_font_init_func_t init;
158,7 → 158,7
status = face->scaled_font_methods.render_glyph ((cairo_scaled_font_t *)scaled_font,
_cairo_scaled_glyph_index(scaled_glyph),
cr, &extents);
if (status == CAIRO_STATUS_SUCCESS)
if (status == CAIRO_INT_STATUS_SUCCESS)
status = cairo_status (cr);
 
cairo_destroy (cr);
229,8 → 229,11
switch (scaled_font->base.options.antialias) {
default:
case CAIRO_ANTIALIAS_DEFAULT:
case CAIRO_ANTIALIAS_FAST:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_GRAY: format = CAIRO_FORMAT_A8; break;
case CAIRO_ANTIALIAS_NONE: format = CAIRO_FORMAT_A1; break;
case CAIRO_ANTIALIAS_BEST:
case CAIRO_ANTIALIAS_SUBPIXEL: format = CAIRO_FORMAT_ARGB32; break;
}
surface = cairo_image_surface_create (format, width, height);
328,11 → 331,12
glyphs, num_glyphs,
clusters, num_clusters, cluster_flags);
 
if (status != CAIRO_STATUS_SUCCESS &&
status != CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED)
if (status != CAIRO_INT_STATUS_SUCCESS &&
status != CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED)
return status;
 
if (status == CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED || *num_glyphs < 0) {
if (status == CAIRO_INT_STATUS_USER_FONT_NOT_IMPLEMENTED ||
*num_glyphs < 0) {
if (orig_glyphs != *glyphs) {
cairo_glyph_free (*glyphs);
*glyphs = orig_glyphs;
/programs/develop/libraries/cairo/src/cairo-version.c
54,31 → 54,31
* vs. in-progress development, (such as from git instead of a tar file,
* or as a "snapshot" tar file as opposed to a "release" tar file).
*
* <informalexample><programlisting>
* <informalexample><screen>
* _____ Major. Always 1, until we invent a new scheme.
* / ___ Minor. Even/Odd = Release/Snapshot (tar files) or Branch/Head (git)
* | / _ Micro. Even/Odd = Tar-file/git
* | | /
* 1.0.0
* </programlisting></informalexample>
* </screen></informalexample>
*
* Here are a few examples of versions that one might see.
* <informalexample><programlisting>
* <informalexample><screen>
* Releases
* --------
* 1.0.0 - A major release
* 1.0.2 - A subsequent maintenance release
* 1.2.0 - Another major release
*
* &nbsp;
* Snapshots
* ---------
* 1.1.2 - A snapshot (working toward the 1.2.0 release)
*
* &nbsp;
* In-progress development (eg. from git)
* --------------------------------------
* 1.0.1 - Development on a maintenance branch (toward 1.0.2 release)
* 1.1.1 - Development on head (toward 1.1.2 snapshot and 1.2.0 release)
* </programlisting></informalexample>
* </screen></informalexample>
* </para>
* <refsect2>
* <title>Compatibility</title>
112,17 → 112,17
* an encoded form suitable for direct comparison. Cairo also provides the
* macro CAIRO_VERSION_ENCODE() to perform the encoding.
*
* <informalexample><programlisting>
* <informalexample><screen>
* Compile-time
* ------------
* CAIRO_VERSION_STRING Human-readable
* CAIRO_VERSION Encoded, suitable for comparison
*
* &nbsp;
* Run-time
* --------
* cairo_version_string() Human-readable
* cairo_version() Encoded, suitable for comparison
* </programlisting></informalexample>
* </screen></informalexample>
*
* For example, checking that the cairo version is greater than or equal
* to 1.0.0 could be achieved at compile-time or run-time as follows:
137,7 → 137,7
* </programlisting></informalexample>
* </para>
* </refsect2>
*/
**/
 
/**
* CAIRO_VERSION:
144,25 → 144,33
*
* The version of cairo available at compile-time, encoded using
* CAIRO_VERSION_ENCODE().
*/
*
* Since: 1.0
**/
 
/**
* CAIRO_VERSION_MAJOR:
*
* The major component of the version of cairo available at compile-time.
*/
*
* Since: 1.0
**/
 
/**
* CAIRO_VERSION_MINOR:
*
* The minor component of the version of cairo available at compile-time.
*/
*
* Since: 1.0
**/
 
/**
* CAIRO_VERSION_MICRO:
*
* The micro component of the version of cairo available at compile-time.
*/
*
* Since: 1.0
**/
 
/**
* CAIRO_VERSION_STRING:
169,7 → 177,9
*
* A human-readable string literal containing the version of cairo available
* at compile-time, in the form of "X.Y.Z".
*/
*
* Since: 1.8
**/
 
/**
* CAIRO_VERSION_ENCODE:
182,8 → 192,10
* Two encoded version numbers can be compared as integers. The encoding ensures
* that later versions compare greater than earlier versions.
*
* @Returns: the encoded version.
*/
* Returns: the encoded version.
*
* Since: 1.0
**/
 
/**
* CAIRO_VERSION_STRINGIZE:
195,10 → 207,10
* returned by %CAIRO_VERSION_STRING and cairo_version_string() are encoded using this macro.
* The parameters to this macro must expand to numerical literals.
*
* @Returns: a string literal containing the version.
* Returns: a string literal containing the version.
*
* @Since: 1.8
*/
* Since: 1.8
**/
 
/**
* cairo_version:
218,6 → 230,8
* equivalents %CAIRO_VERSION and %CAIRO_VERSION_STRING.
*
* Return value: the encoded version.
*
* Since: 1.0
**/
int
cairo_version (void)
235,6 → 249,8
* %CAIRO_VERSION_STRING and %CAIRO_VERSION.
*
* Return value: a string containing the version.
*
* Since: 1.0
**/
const char*
cairo_version_string (void)
/programs/develop/libraries/cairo/src/cairo-vg-surface.c
0,0 → 1,1853
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Opened Hand Ltd.
* Copyright © 2009 Chris Wilson
*
* 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.og/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.
*
* Contributor(s):
* Pierre Tardy <tardyp@gmail.com>
* Øyvind Kolås <pippin@gimp.org>
* Vladimi Vukicevic <vladimir@mozilla.com> (stubbed out base backend)
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-vg.h"
 
#include "cairo-cache-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-clipper-private.h"
 
#include <pixman.h>
#include <VG/openvg.h>
 
//#define OPENVG_DEBUG
 
/*
* Work that needs to be done:
* - Glyph cache / proper font support
*
* - First-class paths
* Paths are expensive for OpenVG, reuse paths whenever possible.
* So add a path cache, and first class paths!
*/
 
typedef struct _cairo_vg_surface cairo_vg_surface_t;
 
/* XXX need GL specific context control. :( */
struct _cairo_vg_context {
cairo_status_t status;
cairo_reference_count_t ref_count;
 
unsigned long target_id;
 
VGPaint paint;
cairo_vg_surface_t *source;
double alpha;
 
cairo_cache_t snapshot_cache;
 
void *display;
void *context;
 
cairo_status_t (*create_target) (cairo_vg_context_t *,
cairo_vg_surface_t *);
cairo_status_t (*set_target) (cairo_vg_context_t *,
cairo_vg_surface_t *);
void (*destroy_target) (cairo_vg_context_t *, cairo_vg_surface_t *);
};
 
struct _cairo_vg_surface {
cairo_surface_t base;
 
cairo_vg_context_t *context;
 
VGImage image;
VGImageFormat format;
int width;
int height;
cairo_bool_t own_image;
 
cairo_cache_entry_t snapshot_cache_entry;
 
cairo_surface_clipper_t clipper;
 
unsigned long target_id;
};
 
static const cairo_surface_backend_t cairo_vg_surface_backend;
 
slim_hidden_proto (cairo_vg_surface_create);
 
static cairo_surface_t *
_vg_surface_create_internal (cairo_vg_context_t *context,
VGImage image,
VGImageFormat format,
int width, int height);
 
static cairo_vg_context_t *
_vg_context_reference (cairo_vg_context_t *context)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
 
_cairo_reference_count_inc (&context->ref_count);
 
return context;
}
 
static cairo_vg_context_t *
_vg_context_lock (cairo_vg_context_t *context)
{
/* XXX if we need to add locking, then it has to be recursive */
return context;
}
 
static cairo_int_status_t
_vg_context_set_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
cairo_status_t status;
 
if (surface->target_id == 0) {
status = context->create_target (context, surface);
if (unlikely (status))
return status;
}
 
if (context->target_id == surface->target_id)
return CAIRO_STATUS_SUCCESS;
 
context->target_id = surface->target_id;
 
return context->set_target (context, surface);
}
 
static void
_vg_context_destroy_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
if (surface->target_id == 0)
return;
 
if (context->target_id == surface->target_id)
context->set_target (context, NULL);
 
context->destroy_target (context, surface);
}
 
static cairo_bool_t
_vg_snapshot_cache_can_remove (const void *entry)
{
return TRUE;
}
 
static void
_vg_snapshot_cache_remove (void *cache_entry)
{
cairo_vg_surface_t *surface = cairo_container_of (cache_entry,
cairo_vg_surface_t,
snapshot_cache_entry);
surface->snapshot_cache_entry.hash = 0;
cairo_surface_destroy (&surface->base);
}
 
static cairo_status_t
_vg_context_init (cairo_vg_context_t *context)
{
cairo_status_t status;
 
context->status = CAIRO_STATUS_SUCCESS;
CAIRO_REFERENCE_COUNT_INIT (&context->ref_count, 1);
 
status = _cairo_cache_init (&context->snapshot_cache,
NULL,
_vg_snapshot_cache_can_remove,
_vg_snapshot_cache_remove,
16*1024*1024);
if (unlikely (status))
return status;
 
context->target_id = 0;
context->source = NULL;
context->alpha = 1.0;
 
context->paint = vgCreatePaint ();
vgLoadIdentity ();
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_vg_context_destroy (cairo_vg_context_t *context)
{
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&context->ref_count));
 
if (! _cairo_reference_count_dec_and_test (&context->ref_count))
return;
 
if (context->paint != VG_INVALID_HANDLE)
vgDestroyPaint (context->paint);
 
_cairo_cache_fini (&context->snapshot_cache);
free (context);
}
 
static void
_vg_context_unlock (cairo_vg_context_t *context)
{
}
 
#ifdef OPENVG_DEBUG
static void check_vg_errors(const char*function,int line)
{
int err = vgGetError();
if (err != VG_NO_ERROR){
printf("%s+%d:vgError detected: 0x%08x.\n",function, line,err);
assert(err == VG_NO_ERROR);
}
 
}
#define CHECK_VG_ERRORS() check_vg_errors(__FILE__,__LINE__)
#else
#define CHECK_VG_ERRORS() do{}while(0)
#endif //OPENVG_DEBUG
 
static pixman_format_code_t
_vg_format_to_pixman (VGImageFormat format,
cairo_bool_t *needs_premult_fixup)
{
*needs_premult_fixup = FALSE;
switch (format) {
/* RGB{A,X} channel ordering */
case VG_sRGBX_8888: return PIXMAN_r8g8b8x8;
case VG_sRGBA_8888: *needs_premult_fixup = TRUE; return PIXMAN_r8g8b8a8;
case VG_sRGBA_8888_PRE: return PIXMAN_r8g8b8a8;
case VG_sRGB_565: return PIXMAN_r5g6b5;
case VG_sRGBA_5551: return 0;
case VG_sRGBA_4444: return 0;
case VG_sL_8: return PIXMAN_g8;
case VG_lRGBX_8888: return 0;
case VG_lRGBA_8888: return 0;
case VG_lRGBA_8888_PRE: return 0;
case VG_lL_8: return 0;
case VG_A_8: return PIXMAN_a8;
case VG_BW_1: return PIXMAN_a1;
case VG_A_1: return PIXMAN_a1;
case VG_A_4: return PIXMAN_a4;
 
/* {A,X}RGB channel ordering */
case VG_sXRGB_8888: return PIXMAN_x8r8g8b8;
case VG_sARGB_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8r8g8b8;
case VG_sARGB_8888_PRE: return PIXMAN_a8r8g8b8;
case VG_sARGB_1555: return 0;
case VG_sARGB_4444: return 0;
case VG_lXRGB_8888: return 0;
case VG_lARGB_8888: return 0;
case VG_lARGB_8888_PRE: return 0;
 
/* BGR{A,X} channel ordering */
case VG_sBGRX_8888: return PIXMAN_b8g8r8x8;
case VG_sBGRA_8888: *needs_premult_fixup = TRUE; return PIXMAN_b8g8r8a8;
case VG_sBGRA_8888_PRE: return PIXMAN_b8g8r8a8;
case VG_sBGR_565: return PIXMAN_b5g6r5;
case VG_sBGRA_5551: return 0;
case VG_sBGRA_4444: return 0;
case VG_lBGRX_8888: return 0;
case VG_lBGRA_8888: return 0;
case VG_lBGRA_8888_PRE: return 0;
 
/* {A,X}BGR channel ordering */
case VG_sXBGR_8888: return PIXMAN_x8b8g8r8;
case VG_sABGR_8888: *needs_premult_fixup = TRUE; return PIXMAN_a8b8g8r8;
case VG_sABGR_8888_PRE: return PIXMAN_a8b8g8r8;
case VG_sABGR_1555: return 0;
case VG_sABGR_4444: return 0;
case VG_lXBGR_8888: return 0;
case VG_lABGR_8888: return 0;
case VG_lABGR_8888_PRE: return 0;
default: return 0;
}
}
 
static pixman_format_code_t
_vg_format_to_content (VGImageFormat format)
{
/* XXX could use more simple bit tests */
switch (format) {
/* RGB{A,X} channel ordering */
case VG_sRGBX_8888: return CAIRO_CONTENT_COLOR;
case VG_sRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sRGB_565: return CAIRO_CONTENT_COLOR;
case VG_sRGBA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sRGBA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sL_8: return CAIRO_CONTENT_ALPHA;
case VG_lRGBX_8888: return CAIRO_CONTENT_COLOR;
case VG_lRGBA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lRGBA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lL_8: return CAIRO_CONTENT_ALPHA;
case VG_A_8: return CAIRO_CONTENT_ALPHA;
case VG_A_4: return CAIRO_CONTENT_ALPHA;
case VG_A_1: return CAIRO_CONTENT_ALPHA;
case VG_BW_1: return CAIRO_CONTENT_ALPHA;
 
/* {A,X}RGB channel ordering */
case VG_sXRGB_8888: return CAIRO_CONTENT_COLOR;
case VG_sARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sARGB_1555: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sARGB_4444: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lXRGB_8888: return CAIRO_CONTENT_COLOR;
case VG_lARGB_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lARGB_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
 
/* BGR{A,X} channel ordering */
case VG_sBGRX_8888: return CAIRO_CONTENT_COLOR;
case VG_sBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sBGR_565: return CAIRO_CONTENT_COLOR;
case VG_sBGRA_5551: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sBGRA_4444: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lBGRX_8888: return CAIRO_CONTENT_COLOR;
case VG_lBGRA_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lBGRA_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
 
/* {A,X}BGR channel ordering */
case VG_sXBGR_8888: return CAIRO_CONTENT_COLOR;
case VG_sABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sABGR_1555: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_sABGR_4444: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lXBGR_8888: return CAIRO_CONTENT_COLOR;
case VG_lABGR_8888: return CAIRO_CONTENT_COLOR_ALPHA;
case VG_lABGR_8888_PRE: return CAIRO_CONTENT_COLOR_ALPHA;
default: return 0;
}
}
 
static VGImageFormat
_vg_format_from_pixman (pixman_format_code_t format)
{
/* XXX _PRE needs fixup */
switch ((int) format) {
case PIXMAN_r5g6b5: return VG_sRGB_565;
case PIXMAN_g8: return VG_sL_8;
case PIXMAN_a8: return VG_A_8;
case PIXMAN_a1: return VG_BW_1;
case PIXMAN_x8r8g8b8: return VG_sXRGB_8888;
case PIXMAN_a8r8g8b8: return VG_sARGB_8888; // _PRE
case PIXMAN_b8g8r8x8: return VG_sBGRX_8888;
case PIXMAN_b8g8r8a8: return VG_sBGRA_8888; // _PRE
case PIXMAN_b5g6r5: return VG_sBGR_565;
case PIXMAN_x8b8g8r8: return VG_sXBGR_8888;
case PIXMAN_a8b8g8r8: return VG_sABGR_8888; // _PRE
default: return 0;
}
}
 
static VGImageFormat
_vg_format_for_content (cairo_content_t content)
{
switch (content) {
case CAIRO_CONTENT_ALPHA: return VG_A_8;
case CAIRO_CONTENT_COLOR: return VG_sXRGB_8888;
default: ASSERT_NOT_REACHED;
case CAIRO_CONTENT_COLOR_ALPHA: return VG_sARGB_8888; // _PRE
}
}
 
static cairo_surface_t *
_vg_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_vg_surface_t *surface = abstract_surface;
 
if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
height > vgGeti (VG_MAX_IMAGE_HEIGHT))
{
return NULL;
}
 
return cairo_vg_surface_create (surface->context, content, width, height);
}
 
static cairo_status_t
_vg_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_vg_surface_t *surface = cairo_container_of (clipper,
cairo_vg_surface_t,
clipper);
cairo_vg_surface_t *mask;
cairo_status_t status;
 
if (path == NULL) {
vgMask (VG_INVALID_HANDLE,
VG_FILL_MASK, 0, 0, surface->width, surface->height);
vgSeti (VG_MASKING, VG_FALSE);
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
mask = (cairo_vg_surface_t *)
_vg_surface_create_similar (surface, CAIRO_CONTENT_ALPHA,
surface->width, surface->height);
if (unlikely (mask == NULL))
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (mask->base.status))
return mask->base.status;
 
status = _cairo_surface_fill (&mask->base,
CAIRO_OPERATOR_SOURCE,
&_cairo_pattern_white.base,
path, fill_rule, tolerance, antialias,
NULL);
if (status) {
cairo_surface_destroy (&mask->base);
return status;
}
 
vgSeti (VG_MASKING, VG_TRUE);
vgMask (mask->image, VG_INTERSECT_MASK, 0, 0, mask->width, mask->height);
 
cairo_surface_destroy (&mask->base);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_vg_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_vg_surface_t *surface = abstract_surface;
 
extents->x = 0;
extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
 
return TRUE;
}
 
#define MAX_SEG 16 /* max number of knots to upload in a batch */
 
typedef struct _vg_path {
VGPath path;
const cairo_matrix_t *ctm_inverse;
 
VGubyte gseg[MAX_SEG];
VGfloat gdata[MAX_SEG*3*2];
int dcount;
int scount;
} vg_path_t;
 
static cairo_status_t
_vg_move_to (void *closure,
const cairo_point_t *point)
{
vg_path_t *path = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (path->ctm_inverse)
cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
 
path->gseg[path->scount++] = VG_MOVE_TO;
path->gdata[path->dcount++] = x;
path->gdata[path->dcount++] = y;
 
if (path->scount >= MAX_SEG-1) {
vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
path->scount = 0;
path->dcount = 0;
}
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_vg_line_to (void *closure,
const cairo_point_t *point)
{
vg_path_t *path = closure;
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
 
if (path->ctm_inverse)
cairo_matrix_transform_point (path->ctm_inverse, &x, &y);
 
path->gseg[path->scount++] = VG_LINE_TO;
path->gdata[path->dcount++] = x;
path->gdata[path->dcount++] = y;
 
if (path->scount >= MAX_SEG-1) {
vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
path->scount = 0;
path->dcount = 0;
}
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_vg_curve_to (void *closure,
const cairo_point_t *p0,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
vg_path_t *path = closure;
double x0 = _cairo_fixed_to_double (p0->x);
double y0 = _cairo_fixed_to_double (p0->y);
double x1 = _cairo_fixed_to_double (p1->x);
double y1 = _cairo_fixed_to_double (p1->y);
double x2 = _cairo_fixed_to_double (p2->x);
double y2 = _cairo_fixed_to_double (p2->y);
 
if (path->ctm_inverse) {
cairo_matrix_transform_point (path->ctm_inverse, &x0, &y0);
cairo_matrix_transform_point (path->ctm_inverse, &x1, &y1);
cairo_matrix_transform_point (path->ctm_inverse, &x2, &y2);
}
 
path->gseg[path->scount++] = VG_CUBIC_TO;
path->gdata[path->dcount++] = x0;
path->gdata[path->dcount++] = y0;
path->gdata[path->dcount++] = x1;
path->gdata[path->dcount++] = y1;
path->gdata[path->dcount++] = x2;
path->gdata[path->dcount++] = y2;
 
if (path->scount >= MAX_SEG-1) {
vgAppendPathData(path->path, path->scount, path->gseg, path->gdata);
path->scount = 0;
path->dcount = 0;
}
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_vg_close_path (void *closure)
{
vg_path_t *path = closure;
 
path->gseg[path->scount++] = VG_CLOSE_PATH;
 
if (path->scount >= MAX_SEG-1) {
vgAppendPathData (path->path, path->scount, path->gseg, path->gdata);
path->scount = 0;
path->dcount = 0;
}
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static void
_vg_path_from_cairo (vg_path_t *vg_path,
const cairo_path_fixed_t *path)
{
cairo_status_t status;
 
vg_path->scount = 0;
vg_path->dcount = 0;
 
status = _cairo_path_fixed_interpret (path,
_vg_move_to,
_vg_line_to,
_vg_curve_to,
_vg_close_path,
vg_path);
assert (status == CAIRO_STATUS_SUCCESS);
 
vgAppendPathData (vg_path->path,
vg_path->scount, vg_path->gseg, vg_path->gdata);
CHECK_VG_ERRORS();
}
 
static cairo_bool_t
_vg_is_supported_operator (cairo_operator_t op)
{
switch ((int) op) {
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_IN:
case CAIRO_OPERATOR_DEST_OVER:
case CAIRO_OPERATOR_DEST_IN:
case CAIRO_OPERATOR_ADD:
return TRUE;
 
default:
return FALSE;
}
}
 
static VGBlendMode
_vg_operator (cairo_operator_t op)
{
switch ((int) op) {
case CAIRO_OPERATOR_SOURCE:
return VG_BLEND_SRC;
case CAIRO_OPERATOR_OVER:
return VG_BLEND_SRC_OVER;
case CAIRO_OPERATOR_IN:
return VG_BLEND_SRC_IN;
case CAIRO_OPERATOR_DEST_OVER:
return VG_BLEND_DST_OVER;
case CAIRO_OPERATOR_DEST_IN:
return VG_BLEND_DST_IN;
case CAIRO_OPERATOR_ADD:
return VG_BLEND_ADDITIVE;
default:
ASSERT_NOT_REACHED;
return VG_BLEND_SRC_OVER;
}
}
 
static VGFillRule
_vg_fill_rule_from_cairo (cairo_fill_rule_t rule)
{
switch (rule) {
case CAIRO_FILL_RULE_EVEN_ODD: return VG_EVEN_ODD;
case CAIRO_FILL_RULE_WINDING: return VG_NON_ZERO;
}
 
ASSERT_NOT_REACHED;
return VG_NON_ZERO;
}
 
static VGRenderingQuality
_vg_rendering_quality_from_cairo (cairo_antialias_t aa)
{
switch (aa) {
case CAIRO_ANTIALIAS_DEFAULT:
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_GOOD:
case CAIRO_ANTIALIAS_BEST:
return VG_RENDERING_QUALITY_BETTER;
 
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_FAST:
return VG_RENDERING_QUALITY_FASTER;
 
case CAIRO_ANTIALIAS_NONE:
return VG_RENDERING_QUALITY_NONANTIALIASED;
}
 
ASSERT_NOT_REACHED;
return VG_RENDERING_QUALITY_BETTER;
}
 
static VGCapStyle
_vg_line_cap_from_cairo (cairo_line_cap_t cap)
{
switch (cap) {
case CAIRO_LINE_CAP_BUTT: return VG_CAP_BUTT;
case CAIRO_LINE_CAP_ROUND: return VG_CAP_ROUND;
case CAIRO_LINE_CAP_SQUARE: return VG_CAP_SQUARE;
}
 
ASSERT_NOT_REACHED;
return VG_CAP_BUTT;
}
 
static VGJoinStyle
_vg_line_join_from_cairo (cairo_line_join_t join)
{
switch (join) {
case CAIRO_LINE_JOIN_MITER: return VG_JOIN_MITER;
case CAIRO_LINE_JOIN_ROUND: return VG_JOIN_ROUND;
case CAIRO_LINE_JOIN_BEVEL: return VG_JOIN_BEVEL;
}
 
ASSERT_NOT_REACHED;
return VG_JOIN_MITER;
}
 
static void
_vg_matrix_from_cairo (VGfloat *dst, const cairo_matrix_t *src)
{
dst[0] = /* sx */ src->xx;
dst[1] = /* shy */ src->yx;
dst[2] = /* w0 */ 0;
dst[3] = /* shx */ src->xy;
dst[4] = /* sy */ src->yy;
dst[5] = /* w1 */ 0;
dst[6] = /* tx */ src->x0;
dst[7] = /* ty */ src->y0;
dst[8] = /* w2 */ 0;
}
 
static cairo_status_t
_vg_setup_gradient_stops (cairo_vg_context_t *context,
const cairo_gradient_pattern_t *pattern)
{
VGint numstops = pattern->n_stops;
VGfloat *stops, stack_stops[CAIRO_STACK_ARRAY_LENGTH (VGfloat)];
int i;
 
if (numstops*5 < ARRAY_LENGTH (stack_stops)) {
stops = stack_stops;
} else {
stops = _cairo_malloc_ab (numstops, 5*sizeof (VGfloat));
if (unlikely (stops == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (i = 0; i < numstops; i++) {
stops[i*5 + 0] = pattern->stops[i].offset;
stops[i*5 + 1] = pattern->stops[i].color.red;
stops[i*5 + 2] = pattern->stops[i].color.green;
stops[i*5 + 3] = pattern->stops[i].color.blue;
stops[i*5 + 4] = pattern->stops[i].color.alpha * context->alpha;
}
 
vgSetParameterfv (context->paint,
VG_PAINT_COLOR_RAMP_STOPS, numstops * 5, stops);
 
if (stops != stack_stops)
free (stops);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static void
_vg_set_source_matrix (const cairo_pattern_t *pat)
{
cairo_matrix_t mat;
cairo_status_t status;
VGfloat vmat[9];
 
mat = pat->matrix;
status = cairo_matrix_invert (&mat);
assert (status == CAIRO_STATUS_SUCCESS);
 
_vg_matrix_from_cairo (vmat, &mat);
 
vgSeti (VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
vgLoadMatrix (vmat);
vgSeti (VG_MATRIX_MODE, VG_MATRIX_STROKE_PAINT_TO_USER);
vgLoadMatrix (vmat);
vgSeti (VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
 
CHECK_VG_ERRORS();
}
 
static cairo_status_t
_vg_setup_linear_source (cairo_vg_context_t *context,
const cairo_linear_pattern_t *lpat)
{
VGfloat linear[4];
 
linear[0] = lpat->pd1.x;
linear[1] = lpat->pd1.y;
linear[2] = lpat->pd2.x;
linear[3] = lpat->pd2.y;
 
vgSetParameteri (context->paint,
VG_PAINT_COLOR_RAMP_SPREAD_MODE,
VG_COLOR_RAMP_SPREAD_PAD);
vgSetParameteri (context->paint,
VG_PAINT_TYPE,
VG_PAINT_TYPE_LINEAR_GRADIENT);
vgSetParameterfv (context->paint,
VG_PAINT_LINEAR_GRADIENT, 4, linear);
 
_vg_set_source_matrix (&lpat->base.base);
 
CHECK_VG_ERRORS();
return _vg_setup_gradient_stops (context, &lpat->base);
 
}
 
static cairo_status_t
_vg_setup_radial_source (cairo_vg_context_t *context,
const cairo_radial_pattern_t *rpat)
{
VGfloat radial[5];
 
radial[0] = rpat->cd1.center.x;
radial[1] = rpat->cd1.center.y;
radial[2] = rpat->cd2.center.x;
radial[3] = rpat->cd2.center.y;
radial[4] = rpat->cd2.radius;
 
vgSetParameteri (context->paint,
VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
vgSetParameteri (context->paint,
VG_PAINT_TYPE, VG_PAINT_TYPE_RADIAL_GRADIENT);
vgSetParameterfv (context->paint,
VG_PAINT_RADIAL_GRADIENT, 5, radial);
 
_vg_set_source_matrix (&rpat->base.base);
 
/* FIXME: copy/adapt fixes from SVG backend to add inner radius */
 
CHECK_VG_ERRORS();
return _vg_setup_gradient_stops (context, &rpat->base);
}
 
static cairo_status_t
_vg_setup_solid_source (cairo_vg_context_t *context,
const cairo_solid_pattern_t *spat)
{
VGfloat color[] = {
spat->color.red,
spat->color.green,
spat->color.blue,
spat->color.alpha * context->alpha
};
 
vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetParameterfv (context->paint, VG_PAINT_COLOR, 4, color);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_vg_surface_t *
_vg_clone_recording_surface (cairo_vg_context_t *context,
cairo_surface_t *surface)
{
VGImage vg_image;
VGImageFormat format;
cairo_status_t status;
cairo_rectangle_int_t extents;
cairo_vg_surface_t *clone;
 
status = _cairo_surface_get_extents (surface, &extents);
if (status)
return NULL;
 
if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
{
return NULL;
}
 
format = _vg_format_for_content (surface->content);
 
/* NONALIASED, FASTER, BETTER */
vg_image = vgCreateImage (format,
extents.width, extents.height,
VG_IMAGE_QUALITY_FASTER);
clone = (cairo_vg_surface_t *)
_vg_surface_create_internal (context, vg_image, format,
extents.width, extents.height);
cairo_surface_set_device_offset (&clone->base, -extents.x, -extents.y);
 
status = _cairo_recording_surface_replay (surface, &clone->base);
if (unlikely (status)) {
cairo_surface_destroy (&clone->base);
return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
}
 
return clone;
}
 
static cairo_vg_surface_t *
_vg_clone_image_surface (cairo_vg_context_t *context,
cairo_surface_t *surface)
{
cairo_image_surface_t *image;
void *image_extra;
cairo_status_t status;
VGImage vg_image;
VGImageFormat format;
cairo_rectangle_int_t extents;
cairo_vg_surface_t *clone;
 
if (surface->backend->acquire_source_image == NULL)
return NULL;
 
status = _cairo_surface_get_extents (surface, &extents);
if (status)
return NULL;
 
if (extents.width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
extents.height > vgGeti (VG_MAX_IMAGE_HEIGHT))
{
return NULL;
}
 
status = _cairo_surface_acquire_source_image (surface,
&image, &image_extra);
if (unlikely (status))
return (cairo_vg_surface_t *) _cairo_surface_create_in_error (status);
 
format = _vg_format_from_pixman (image->pixman_format);
if (format == 0)
format = _vg_format_for_content (image->base.content);
 
/* NONALIASED, FASTER, BETTER */
vg_image = vgCreateImage (format,
image->width, image->height,
VG_IMAGE_QUALITY_FASTER);
clone = (cairo_vg_surface_t *)
_vg_surface_create_internal (context, vg_image, format,
image->width, image->height);
if (unlikely (clone->base.status))
return clone;
 
vgImageSubData (clone->image,
image->data, image->stride,
format, 0, 0, image->width, image->height);
 
_cairo_surface_release_source_image (surface, image, image_extra);
 
return clone;
}
 
static void
_vg_surface_remove_from_cache (cairo_surface_t *abstract_surface)
{
cairo_vg_surface_t *surface = (cairo_vg_surface_t *) abstract_surface;
 
if (surface->snapshot_cache_entry.hash) {
cairo_vg_context_t *context;
 
context = _vg_context_lock (surface->context);
_cairo_cache_remove (&context->snapshot_cache,
&surface->snapshot_cache_entry);
_vg_context_unlock (context);
 
surface->snapshot_cache_entry.hash = 0;
}
}
 
static cairo_status_t
_vg_setup_surface_source (cairo_vg_context_t *context,
const cairo_surface_pattern_t *spat)
{
cairo_surface_t *snapshot;
cairo_vg_surface_t *clone;
cairo_status_t status;
 
snapshot = _cairo_surface_has_snapshot (spat->surface,
&cairo_vg_surface_backend);
if (snapshot != NULL) {
clone = (cairo_vg_surface_t *) cairo_surface_reference (snapshot);
goto DONE;
}
 
if (_cairo_surface_is_recording (spat->surface))
clone = _vg_clone_recording_surface (context, spat->surface);
else
clone = _vg_clone_image_surface (context, spat->surface);
if (clone == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
if (unlikely (clone->base.status))
return clone->base.status;
 
clone->snapshot_cache_entry.hash = clone->base.unique_id;
status = _cairo_cache_insert (&context->snapshot_cache,
&clone->snapshot_cache_entry);
if (unlikely (status)) {
clone->snapshot_cache_entry.hash = 0;
cairo_surface_destroy (&clone->base);
return status;
}
 
_cairo_surface_attach_snapshot (spat->surface, &clone->base,
_vg_surface_remove_from_cache);
 
DONE:
cairo_surface_destroy (&context->source->base);
context->source = clone;
 
vgSetParameteri (context->paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
 
switch (spat->base.extend) {
case CAIRO_EXTEND_PAD:
vgSetParameteri (context->paint,
VG_PAINT_PATTERN_TILING_MODE,
VG_TILE_PAD);
break;
 
case CAIRO_EXTEND_NONE:
vgSetParameteri (context->paint,
VG_PAINT_PATTERN_TILING_MODE,
VG_TILE_FILL);
{
VGfloat color[] = {0,0,0,0};
vgSetfv (VG_TILE_FILL_COLOR, 4, color);
}
break;
 
case CAIRO_EXTEND_REPEAT:
vgSetParameteri (context->paint,
VG_PAINT_PATTERN_TILING_MODE,
VG_TILE_REPEAT);
break;
 
default:
ASSERT_NOT_REACHED;
case CAIRO_EXTEND_REFLECT:
vgSetParameteri (context->paint,
VG_PAINT_PATTERN_TILING_MODE,
VG_TILE_REFLECT);
break;
}
vgPaintPattern (context->paint, context->source->image);
 
_vg_set_source_matrix (&spat->base);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
setup_source (cairo_vg_context_t *context,
const cairo_pattern_t *source)
{
switch (source->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _vg_setup_solid_source (context,
(cairo_solid_pattern_t *) source);
case CAIRO_PATTERN_TYPE_LINEAR:
return _vg_setup_linear_source (context,
(cairo_linear_pattern_t *) source);
case CAIRO_PATTERN_TYPE_RADIAL:
return _vg_setup_radial_source (context,
(cairo_radial_pattern_t *) source);
case CAIRO_PATTERN_TYPE_SURFACE:
return _vg_setup_surface_source (context,
(cairo_surface_pattern_t *) source);
default:
ASSERT_NOT_REACHED;
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
static cairo_int_status_t
_vg_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_vg_surface_t *surface = abstract_surface;
cairo_vg_context_t *context;
cairo_status_t status;
VGfloat state[9];
VGfloat strokeTransform[9];
vg_path_t vg_path;
 
if (! _vg_is_supported_operator (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
context = _vg_context_lock (surface->context);
status = _vg_context_set_target (context, surface);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = setup_source (context, source);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status)) {
_vg_context_unlock (context);
return status;
}
 
vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F,
1, 0, 0, 0,
VG_PATH_CAPABILITY_ALL);
 
vgGetMatrix (state);
_vg_matrix_from_cairo (strokeTransform, ctm);
vgMultMatrix (strokeTransform);
 
vg_path.ctm_inverse = ctm_inverse;
 
_vg_path_from_cairo (&vg_path, path);
 
/* XXX DASH_PATTERN, DASH_PHASE */
vgSetf (VG_STROKE_LINE_WIDTH, style->line_width);
vgSetf (VG_STROKE_MITER_LIMIT, style->miter_limit);
vgSetf (VG_STROKE_JOIN_STYLE, _vg_line_join_from_cairo (style->line_join));
vgSetf (VG_STROKE_CAP_STYLE, _vg_line_cap_from_cairo (style->line_cap));
 
vgSeti (VG_BLEND_MODE, _vg_operator (op));
 
vgSetPaint (context->paint, VG_STROKE_PATH);
 
vgDrawPath (vg_path.path, VG_STROKE_PATH);
 
vgDestroyPath (vg_path.path);
 
vgLoadMatrix (state);
 
CHECK_VG_ERRORS();
_vg_context_unlock (context);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_vg_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_vg_surface_t *surface = abstract_surface;
cairo_vg_context_t *context;
cairo_status_t status;
vg_path_t vg_path;
 
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (! _vg_is_supported_operator (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
context = _vg_context_lock (surface->context);
status = _vg_context_set_target (context, surface);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = setup_source (context, source);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status)) {
_vg_context_unlock (context);
return status;
}
 
vg_path.path = vgCreatePath (VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F,
1, 0,
0, 0,
VG_PATH_CAPABILITY_ALL);
vg_path.ctm_inverse = NULL;
 
_vg_path_from_cairo (&vg_path, path);
 
/* XXX tolerance */
 
vgSeti (VG_BLEND_MODE, _vg_operator (op));
vgSetf (VG_FILL_RULE, _vg_fill_rule_from_cairo (fill_rule));
vgSetf (VG_RENDERING_QUALITY, _vg_rendering_quality_from_cairo (antialias));
 
vgSetPaint (context->paint, VG_FILL_PATH);
 
vgDrawPath (vg_path.path, VG_FILL_PATH);
 
vgDestroyPath (vg_path.path);
 
_vg_context_unlock (context);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_vg_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_vg_surface_t *surface = abstract_surface;
cairo_vg_context_t *context;
cairo_status_t status;
 
if (op == CAIRO_OPERATOR_DEST)
return CAIRO_STATUS_SUCCESS;
 
if (! _vg_is_supported_operator (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
context = _vg_context_lock (surface->context);
status = _vg_context_set_target (context, surface);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = setup_source (context, source);
if (status) {
_vg_context_unlock (context);
return status;
}
 
status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
if (unlikely (status)) {
_vg_context_unlock (context);
return status;
}
 
vgSeti (VG_BLEND_MODE, _vg_operator (op));
vgSetPaint (context->paint, VG_FILL_PATH);
 
{ /* creating a rectangular path that should cover the extent */
VGubyte segs[] = {
VG_MOVE_TO_ABS, VG_LINE_TO_ABS,
VG_LINE_TO_ABS, VG_LINE_TO_ABS,
VG_CLOSE_PATH
};
VGfloat data[] = {
0, 0,
surface->width, 0,
surface->width, surface->height,
0, surface->height
};
VGPath fullext;
 
fullext = vgCreatePath (VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1,0,0,0, VG_PATH_CAPABILITY_ALL);
vgAppendPathData (fullext, sizeof(segs), segs, data);
 
vgDrawPath (fullext, VG_FILL_PATH);
 
vgDestroyPath (fullext);
}
 
_vg_context_unlock (context);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_vg_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_vg_surface_t *surface = abstract_surface;
cairo_status_t status;
 
if (! _vg_is_supported_operator (op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Handle paint-with-alpha to do fades cheaply */
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) mask;
cairo_vg_context_t *context = _vg_context_lock (surface->context);
double alpha = context->alpha;
 
context->alpha = solid->color.alpha;
status = _vg_surface_paint (abstract_surface, op, source, clip);
context->alpha = alpha;
 
_vg_context_unlock (context);
 
return status;
}
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static void
_vg_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
_cairo_font_options_init_default (options);
 
cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_OFF);
}
 
static cairo_int_status_t
_vg_surface_show_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_path_fixed_t path;
 
if (num_glyphs <= 0)
return CAIRO_STATUS_SUCCESS;
 
_cairo_path_fixed_init (&path);
 
/* XXX Glyph cache! OpenVG font support in 1.1? */
 
status = _cairo_scaled_font_glyph_path (scaled_font,
glyphs, num_glyphs,
&path);
if (unlikely (status))
goto BAIL;
 
status = _vg_surface_fill (abstract_surface,
op, source, &path,
CAIRO_FILL_RULE_WINDING,
CAIRO_GSTATE_TOLERANCE_DEFAULT,
CAIRO_ANTIALIAS_DEFAULT,
clip);
BAIL:
_cairo_path_fixed_fini (&path);
return status;
}
 
static inline int
multiply_alpha (int alpha, int color)
{
int temp = alpha * color + 0x80;
return (temp + (temp >> 8)) >> 8;
}
 
static void
premultiply_argb (uint8_t *data,
int width,
int height,
int stride)
{
int i;
 
while (height --) {
uint32_t *row = (uint32_t *) data;
 
for (i = 0; i < width; i++) {
uint32_t p = row[i];
uint8_t alpha;
 
alpha = p >> 24;
if (alpha == 0) {
row[i] = 0;
} else if (alpha != 0xff) {
uint8_t r = multiply_alpha (alpha, (p >> 16) & 0xff);
uint8_t g = multiply_alpha (alpha, (p >> 8) & 0xff);
uint8_t b = multiply_alpha (alpha, (p >> 0) & 0xff);
row[i] = (alpha << 24) | (r << 16) | (g << 8) | (b << 0);
}
}
 
data += stride;
}
}
 
static cairo_int_status_t
_vg_get_image (cairo_vg_surface_t *surface,
int x, int y,
int width, int height,
cairo_image_surface_t **image_out)
{
cairo_image_surface_t *image;
pixman_image_t *pixman_image;
pixman_format_code_t pixman_format;
cairo_bool_t needs_premultiply;
 
pixman_format = _vg_format_to_pixman (surface->format,
&needs_premultiply);
if (pixman_format == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
pixman_image = pixman_image_create_bits (pixman_format,
width, height,
NULL, 0);
if (unlikely (pixman_image == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
vgFinish ();
CHECK_VG_ERRORS();
 
vgGetImageSubData (surface->image,
pixman_image_get_data (pixman_image),
pixman_image_get_stride (pixman_image),
surface->format,
x, y, width, height);
 
image = (cairo_image_surface_t *)
_cairo_image_surface_create_for_pixman_image (pixman_image,
pixman_format);
if (unlikely (image->base.status)) {
pixman_image_unref (pixman_image);
return image->base.status;
}
 
if (needs_premultiply)
premultiply_argb (image->data, width, height, image->stride);
 
*image_out = image;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_vg_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_vg_surface_t *surface = abstract_surface;
 
CHECK_VG_ERRORS();
*image_extra = NULL;
return _vg_get_image (surface,
0, 0, surface->width, surface->height,
image_out);
}
 
static void
_vg_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
}
 
static cairo_status_t
_vg_surface_finish (void *abstract_surface)
{
cairo_vg_surface_t *surface = abstract_surface;
cairo_vg_context_t *context = _vg_context_lock (surface->context);
 
if (surface->snapshot_cache_entry.hash) {
_cairo_cache_remove (&context->snapshot_cache,
&surface->snapshot_cache_entry);
 
surface->snapshot_cache_entry.hash = 0;
}
 
_cairo_surface_clipper_reset (&surface->clipper);
 
if (surface->own_image)
vgDestroyImage (surface->image);
 
_vg_context_destroy_target (context, surface);
 
_vg_context_unlock (context);
_vg_context_destroy (context);
 
CHECK_VG_ERRORS();
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t cairo_vg_surface_backend = {
CAIRO_SURFACE_TYPE_VG,
_vg_surface_finish,
 
_cairo_default_context_create, /* XXX */
 
_vg_surface_create_similar,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
_vg_surface_acquire_source_image,
_vg_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_vg_surface_get_extents,
_vg_surface_get_font_options, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark dirty */
 
_vg_surface_paint,
_vg_surface_mask,
_vg_surface_stroke,
_vg_surface_fill,
NULL, /* fill-stroke */
_vg_surface_show_glyphs,
};
 
static cairo_surface_t *
_vg_surface_create_internal (cairo_vg_context_t *context,
VGImage image,
VGImageFormat format,
int width, int height)
{
cairo_vg_surface_t *surface;
 
surface = malloc (sizeof (cairo_vg_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface->context = _vg_context_reference (context);
 
surface->image = image;
surface->format = format;
 
_cairo_surface_init (&surface->base,
&cairo_vg_surface_backend,
NULL, /* device */
_vg_format_to_content (format));
 
surface->width = width;
surface->height = height;
 
_cairo_surface_clipper_init (&surface->clipper,
_vg_surface_clipper_intersect_clip_path);
 
surface->snapshot_cache_entry.hash = 0;
 
surface->target_id = 0;
 
CHECK_VG_ERRORS();
return &surface->base;
}
 
cairo_surface_t *
cairo_vg_surface_create_for_image (cairo_vg_context_t *context,
VGImage image,
VGImageFormat format,
int width, int height)
{
cairo_bool_t premult;
 
if (context->status)
return _cairo_surface_create_in_error (context->status);
 
if (image == VG_INVALID_HANDLE)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
if (_vg_format_to_pixman (format, &premult) == 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
return _vg_surface_create_internal (context, image, format, width, height);
}
 
cairo_surface_t *
cairo_vg_surface_create (cairo_vg_context_t *context,
cairo_content_t content,
int width,
int height)
{
VGImage image;
VGImageFormat format;
cairo_surface_t *surface;
 
if (context->status)
return _cairo_surface_create_in_error (context->status);
 
if (! CAIRO_CONTENT_VALID (content))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
 
if (width > vgGeti (VG_MAX_IMAGE_WIDTH) ||
height > vgGeti (VG_MAX_IMAGE_HEIGHT))
{
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
}
 
 
format = _vg_format_for_content (content);
image = vgCreateImage (format, width, height, VG_IMAGE_QUALITY_BETTER);
if (image == VG_INVALID_HANDLE)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface = _vg_surface_create_internal (context,
image, format, width, height);
if (unlikely (surface->status))
return surface;
 
((cairo_vg_surface_t *) surface)->own_image = TRUE;
return surface;
}
slim_hidden_def (cairo_vg_surface_create);
 
VGImage
cairo_vg_surface_get_image (cairo_surface_t *abstract_surface)
{
cairo_vg_surface_t *surface;
 
if (abstract_surface->backend != &cairo_vg_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return VG_INVALID_HANDLE;
}
 
surface = (cairo_vg_surface_t *) abstract_surface;
return surface->image;
}
 
int
cairo_vg_surface_get_width (cairo_surface_t *abstract_surface)
{
cairo_vg_surface_t *surface;
 
if (abstract_surface->backend != &cairo_vg_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
surface = (cairo_vg_surface_t *) abstract_surface;
return surface->width;
}
 
int
cairo_vg_surface_get_height (cairo_surface_t *abstract_surface)
{
cairo_vg_surface_t *surface;
 
if (abstract_surface->backend != &cairo_vg_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
surface = (cairo_vg_surface_t *) abstract_surface;
return surface->height;
}
 
VGImageFormat
cairo_vg_surface_get_format (cairo_surface_t *abstract_surface)
{
cairo_vg_surface_t *surface;
 
if (abstract_surface->backend != &cairo_vg_surface_backend) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
surface = (cairo_vg_surface_t *) abstract_surface;
return surface->format;
}
 
/* GL specific context support :-(
*
* OpenVG like cairo defers creation of surface (and the necessary
* paraphernalia to the application.
*/
 
static const cairo_vg_context_t _vg_context_nil = {
CAIRO_STATUS_NO_MEMORY,
CAIRO_REFERENCE_COUNT_INVALID
};
 
static const cairo_vg_context_t _vg_context_nil_invalid_visual = {
CAIRO_STATUS_INVALID_VISUAL,
CAIRO_REFERENCE_COUNT_INVALID
};
 
#if CAIRO_HAS_GLX_FUNCTIONS
#include <GL/glx.h>
 
static cairo_status_t
glx_create_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
/* XXX hmm, magic required for creating an FBO points to VGImage! */
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_status_t
glx_set_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
#if 0
glXMakeContextCurrent (context->display,
(GLXDrawable) surface->target_id,
(GLXDrawable) surface->target_id,
context->context);
#else
return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
}
 
static void
glx_destroy_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
}
 
cairo_vg_context_t *
cairo_vg_context_create_for_glx (Display *dpy, GLXContext ctx)
{
cairo_vg_context_t *context;
cairo_status_t status;
 
context = malloc (sizeof (*context));
if (unlikely (context == NULL))
return (cairo_vg_context_t *) &_vg_context_nil;
 
context->display = dpy;
context->context = ctx;
 
context->create_target = glx_create_target;
context->set_target = glx_set_target;
context->destroy_target = glx_destroy_target;
 
status = _vg_context_init (context);
if (unlikely (status)) {
free (context);
return (cairo_vg_context_t *) &_vg_context_nil;
}
 
return context;
}
#endif
 
#if CAIRO_HAS_EGL_FUNCTIONS
static cairo_status_t
egl_create_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
EGLSurface *egl_surface;
#define RED 1
#define GREEN 3
#define BLUE 5
#define ALPHA 7
int attribs[] = {
EGL_RED_SIZE, 0,
EGL_GREEN_SIZE, 0,
EGL_BLUE_SIZE, 0,
EGL_ALPHA_SIZE, 0,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENVG_BIT,
EGL_NONE
};
pixman_format_code_t pixman_format;
EGLConfig config;
int num_configs = 0;
cairo_bool_t needs_premultiply;
 
pixman_format = _vg_format_to_pixman (surface->format, &needs_premultiply);
if (pixman_format == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* XXX no control over pixel ordering! */
attribs[RED] = PIXMAN_FORMAT_R (pixman_format);
attribs[GREEN] = PIXMAN_FORMAT_G (pixman_format);
attribs[BLUE] = PIXMAN_FORMAT_B (pixman_format);
attribs[ALPHA] = PIXMAN_FORMAT_A (pixman_format);
 
if (! eglChooseConfig (context->display,
attribs,
&config, 1, &num_configs) ||
num_configs != 1)
{
fprintf(stderr, "Error: eglChooseConfig() failed.\n");
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
egl_surface =
eglCreatePbufferFromClientBuffer (context->display,
EGL_OPENVG_IMAGE,
(EGLClientBuffer) surface->image,
config,
NULL);
surface->target_id = (unsigned long) egl_surface;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
egl_set_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
if (! eglMakeCurrent (context->display,
(EGLSurface *) surface->target_id,
(EGLSurface *) surface->target_id,
context->context))
{
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
egl_destroy_target (cairo_vg_context_t *context,
cairo_vg_surface_t *surface)
{
eglDestroySurface (context->display,
(EGLSurface *) surface->target_id);
}
 
cairo_vg_context_t *
cairo_vg_context_create_for_egl (EGLDisplay egl_display,
EGLContext egl_context)
{
cairo_vg_context_t *context;
cairo_status_t status;
 
context = malloc (sizeof (*context));
if (unlikely (context == NULL))
return (cairo_vg_context_t *) &_vg_context_nil;
 
status = _vg_context_init (context);
if (unlikely (status)) {
free (context);
return (cairo_vg_context_t *) &_vg_context_nil;
}
 
context->display = egl_display;
context->context = egl_context;
 
context->create_target = egl_create_target;
context->set_target = egl_set_target;
context->destroy_target = egl_destroy_target;
 
return context;
}
#endif
 
cairo_status_t
cairo_vg_context_status (cairo_vg_context_t *context)
{
return context->status;
}
 
void
cairo_vg_context_destroy (cairo_vg_context_t *context)
{
if (context == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
return;
 
_vg_context_destroy (context);
}
/programs/develop/libraries/cairo/src/cairo-wgl-context.c
0,0 → 1,261
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Eric Anholt
* Copyright © 2009 Chris Wilson
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Zoxc <zoxc32@gmail.com>
*/
 
#include "cairoint.h"
 
#include "cairo-gl-private.h"
 
#include "cairo-error-private.h"
 
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
 
typedef struct _cairo_wgl_context {
cairo_gl_context_t base;
 
HDC dummy_dc;
HWND dummy_wnd;
HGLRC rc;
 
HDC prev_dc;
HGLRC prev_rc;
} cairo_wgl_context_t;
 
typedef struct _cairo_wgl_surface {
cairo_gl_surface_t base;
 
HDC dc;
} cairo_wgl_surface_t;
 
static void
_wgl_acquire (void *abstract_ctx)
{
cairo_wgl_context_t *ctx = abstract_ctx;
 
HDC current_dc;
 
ctx->prev_dc = wglGetCurrentDC ();
ctx->prev_rc = wglGetCurrentContext ();
 
if (ctx->base.current_target == NULL ||
_cairo_gl_surface_is_texture (ctx->base.current_target))
{
current_dc = ctx->dummy_dc;
}
else
{
cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) ctx->base.current_target;
current_dc = surface->dc;
}
 
if (ctx->prev_dc != current_dc ||
(ctx->prev_rc != ctx->rc &&
current_dc != ctx->dummy_dc))
{
wglMakeCurrent (current_dc, ctx->rc);
}
}
 
static void
_wgl_make_current (void *abstract_ctx, cairo_gl_surface_t *abstract_surface)
{
cairo_wgl_context_t *ctx = abstract_ctx;
cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
 
/* Set the window as the target of our context. */
wglMakeCurrent (surface->dc, ctx->rc);
}
 
static void
_wgl_release (void *abstract_ctx)
{
cairo_wgl_context_t *ctx = abstract_ctx;
 
if (ctx->prev_dc != wglGetCurrentDC () ||
ctx->prev_rc != wglGetCurrentContext ())
{
wglMakeCurrent (ctx->prev_dc,
ctx->prev_rc);
}
}
 
static void
_wgl_swap_buffers (void *abstract_ctx,
cairo_gl_surface_t *abstract_surface)
{
cairo_wgl_surface_t *surface = (cairo_wgl_surface_t *) abstract_surface;
 
SwapBuffers (surface->dc);
}
 
static void
_wgl_destroy (void *abstract_ctx)
{
cairo_wgl_context_t *ctx = abstract_ctx;
 
if (ctx->dummy_dc != 0) {
wglMakeCurrent (ctx->dummy_dc, 0);
ReleaseDC (ctx->dummy_wnd, ctx->dummy_dc);
DestroyWindow (ctx->dummy_wnd);
}
}
 
static cairo_status_t
_wgl_dummy_ctx (cairo_wgl_context_t *ctx)
{
WNDCLASSEXA wincl;
PIXELFORMATDESCRIPTOR pfd;
int format;
HDC dc;
 
ZeroMemory (&wincl, sizeof (WNDCLASSEXA));
wincl.cbSize = sizeof (WNDCLASSEXA);
wincl.hInstance = GetModuleHandle (0);
wincl.lpszClassName = "cairo_wgl_context_dummy";
wincl.lpfnWndProc = DefWindowProcA;
wincl.style = CS_OWNDC;
 
RegisterClassExA (&wincl);
 
ctx->dummy_wnd = CreateWindowA ("cairo_wgl_context_dummy", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
ctx->dummy_dc = GetDC (ctx->dummy_wnd);
 
ZeroMemory (&pfd, sizeof (PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
 
format = ChoosePixelFormat (ctx->dummy_dc, &pfd);
SetPixelFormat (ctx->dummy_dc, format, &pfd);
 
wglMakeCurrent(ctx->dummy_dc, ctx->rc);
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_device_t *
cairo_wgl_device_create (HGLRC rc)
{
cairo_wgl_context_t *ctx;
cairo_status_t status;
 
ctx = calloc (1, sizeof (cairo_wgl_context_t));
if (unlikely (ctx == NULL))
return _cairo_gl_context_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
ctx->rc = rc;
ctx->prev_dc = 0;
ctx->prev_rc = 0;
 
status = _wgl_dummy_ctx (ctx);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
ctx->base.acquire = _wgl_acquire;
ctx->base.release = _wgl_release;
ctx->base.make_current = _wgl_make_current;
ctx->base.swap_buffers = _wgl_swap_buffers;
ctx->base.destroy = _wgl_destroy;
 
status = _cairo_gl_dispatch_init (&ctx->base.dispatch,
(cairo_gl_get_proc_addr_func_t) wglGetProcAddress);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
status = _cairo_gl_context_init (&ctx->base);
if (unlikely (status)) {
free (ctx);
return _cairo_gl_context_create_in_error (status);
}
 
ctx->base.release (ctx);
 
return &ctx->base.base;
}
 
HGLRC
cairo_wgl_device_get_context (cairo_device_t *device)
{
cairo_wgl_context_t *ctx;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL) {
_cairo_error_throw (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
return NULL;
}
 
ctx = (cairo_wgl_context_t *) device;
 
return ctx->rc;
}
 
cairo_surface_t *
cairo_gl_surface_create_for_dc (cairo_device_t *device,
HDC dc,
int width,
int height)
{
cairo_wgl_surface_t *surface;
 
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
 
if (device->backend->type != CAIRO_DEVICE_TYPE_GL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
if (width <= 0 || height <= 0)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
surface = calloc (1, sizeof (cairo_wgl_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_gl_surface_init (device, &surface->base,
CAIRO_CONTENT_COLOR_ALPHA, width, height);
surface->dc = dc;
 
return &surface->base.base;
}
/programs/develop/libraries/cairo/src/cairo-wideint-private.h
54,6 → 54,11
cairo_uquorem64_t I
_cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den);
 
cairo_uint64_t I _cairo_double_to_uint64 (double i);
double I _cairo_uint64_to_double (cairo_uint64_t i);
cairo_int64_t I _cairo_double_to_int64 (double i);
double I _cairo_int64_to_double (cairo_uint64_t i);
 
cairo_uint64_t I _cairo_uint32_to_uint64 (uint32_t i);
#define _cairo_uint64_to_uint32(a) ((a).lo)
cairo_uint64_t I _cairo_uint64_add (cairo_uint64_t a, cairo_uint64_t b);
103,6 → 108,19
return qr;
}
 
/*
* These need to be functions or gcc will complain when used on the
* result of a function:
*
* warning: cast from function call of type ‘#cairo_uint64_t’ to
* non-matching type ‘double’
*/
static cairo_always_inline cairo_const cairo_uint64_t _cairo_double_to_uint64 (double i) { return i; }
static cairo_always_inline cairo_const double _cairo_uint64_to_double (cairo_uint64_t i) { return i; }
 
static cairo_always_inline cairo_int64_t I _cairo_double_to_int64 (double i) { return i; }
static cairo_always_inline double I _cairo_int64_to_double (cairo_int64_t i) { return i; }
 
#define _cairo_uint32_to_uint64(i) ((uint64_t) (i))
#define _cairo_uint64_to_uint32(i) ((uint32_t) (i))
#define _cairo_uint64_add(a,b) ((a) + (b))
143,7 → 161,7
#endif
 
/*
* 64-bit comparisions derived from lt or eq
* 64-bit comparisons derived from lt or eq
*/
#define _cairo_uint64_le(a,b) (!_cairo_uint64_gt(a,b))
#define _cairo_uint64_ne(a,b) (!_cairo_uint64_eq(a,b))
/programs/develop/libraries/cairo/src/cairo-wideint-type-private.h
80,6 → 80,9
#ifndef INT32_MAX
# define INT32_MAX (2147483647)
#endif
#ifndef UINT32_MAX
# define UINT32_MAX (4294967295U)
#endif
 
#if HAVE_BYTESWAP_H
# include <byteswap.h>
102,7 → 105,7
 
typedef struct _cairo_uint64 {
uint32_t lo, hi;
} cairo_uint64_t, cairo_int64_t;
/ cairo_uint64_t, cairo_int64_t;
 
#else
 
/programs/develop/libraries/cairo/src/cairo-wideint.c
84,6 → 84,38
static const cairo_uint64_t uint64_carry32 = { 0, 1 };
 
cairo_uint64_t
_cairo_double_to_uint64 (double i)
{
cairo_uint64_t q;
 
q.hi = i * (1. / 4294967296.);
q.lo = i - q.hi * 4294967296.;
return q;
}
 
double
_cairo_uint64_to_double (cairo_uint64_t i)
{
return i.hi * 4294967296. + i.lo;
}
 
cairo_int64_t
_cairo_double_to_int64 (double i)
{
cairo_uint64_t q;
 
q.hi = i * (1. / INT32_MAX);
q.lo = i - q.hi * (double)INT32_MAX;
return q;
}
 
double
_cairo_int64_to_double (cairo_int64_t i)
{
return i.hi * INT32_MAX + i.lo;
}
 
cairo_uint64_t
_cairo_uint32_to_uint64 (uint32_t i)
{
cairo_uint64_t q;
672,7 → 704,8
* bits then the returned remainder is equal to the divisor, and the
* quotient is the largest representable 64 bit integer. It is an
* error to call this function with the high 32 bits of @num being
* non-zero. */
* non-zero.
**/
cairo_uquorem64_t
_cairo_uint_96by64_32x64_divrem (cairo_uint128_t num,
cairo_uint64_t den)
/programs/develop/libraries/cairo/src/cairo-xcb-connection-core.c
0,0 → 1,314
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-xcb-private.h"
 
#include <xcb/xcbext.h>
 
xcb_pixmap_t
_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
uint8_t depth,
xcb_drawable_t drawable,
uint16_t width,
uint16_t height)
{
xcb_pixmap_t pixmap = _cairo_xcb_connection_get_xid (connection);
 
assert (width > 0);
assert (height > 0);
xcb_create_pixmap (connection->xcb_connection,
depth, pixmap, drawable,
width, height);
return pixmap;
}
 
void
_cairo_xcb_connection_free_pixmap (cairo_xcb_connection_t *connection,
xcb_pixmap_t pixmap)
{
xcb_free_pixmap (connection->xcb_connection, pixmap);
_cairo_xcb_connection_put_xid (connection, pixmap);
}
 
xcb_gcontext_t
_cairo_xcb_connection_create_gc (cairo_xcb_connection_t *connection,
xcb_drawable_t drawable,
uint32_t value_mask,
uint32_t *values)
{
xcb_gcontext_t gc = _cairo_xcb_connection_get_xid (connection);
xcb_create_gc (connection->xcb_connection, gc, drawable,
value_mask, values);
return gc;
}
 
void
_cairo_xcb_connection_free_gc (cairo_xcb_connection_t *connection,
xcb_gcontext_t gc)
{
xcb_free_gc (connection->xcb_connection, gc);
_cairo_xcb_connection_put_xid (connection, gc);
}
 
void
_cairo_xcb_connection_change_gc (cairo_xcb_connection_t *connection,
xcb_gcontext_t gc,
uint32_t value_mask,
uint32_t *values)
{
xcb_change_gc (connection->xcb_connection, gc,
value_mask, values);
}
 
void
_cairo_xcb_connection_copy_area (cairo_xcb_connection_t *connection,
xcb_drawable_t src,
xcb_drawable_t dst,
xcb_gcontext_t gc,
int16_t src_x,
int16_t src_y,
int16_t dst_x,
int16_t dst_y,
uint16_t width,
uint16_t height)
{
xcb_copy_area (connection->xcb_connection, src, dst, gc,
src_x, src_y, dst_x, dst_y, width, height);
}
 
void
_cairo_xcb_connection_poly_fill_rectangle (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
uint32_t num_rectangles,
xcb_rectangle_t *rectangles)
{
xcb_poly_fill_rectangle (connection->xcb_connection, dst, gc,
num_rectangles, rectangles);
}
 
void
_cairo_xcb_connection_put_image (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
uint16_t width,
uint16_t height,
int16_t dst_x,
int16_t dst_y,
uint8_t depth,
uint32_t stride,
void *data)
{
const uint32_t req_size = 18;
uint32_t length = height * stride;
uint32_t len = (req_size + length) >> 2;
 
if (len < connection->maximum_request_length) {
xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
dst, gc, width, height, dst_x, dst_y, 0, depth,
length, data);
} else {
int rows = (connection->maximum_request_length - req_size - 4) / stride;
if (rows > 0) {
do {
if (rows > height)
rows = height;
 
length = rows * stride;
 
xcb_put_image (connection->xcb_connection, XCB_IMAGE_FORMAT_Z_PIXMAP,
dst, gc, width, rows, dst_x, dst_y, 0, depth, length, data);
 
height -= rows;
dst_y += rows;
data = (char *) data + length;
} while (height);
} else {
ASSERT_NOT_REACHED;
}
}
}
 
static void
_cairo_xcb_connection_do_put_subimage (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
int16_t src_x,
int16_t src_y,
uint16_t width,
uint16_t height,
uint16_t cpp,
int stride,
int16_t dst_x,
int16_t dst_y,
uint8_t depth,
void *_data)
{
xcb_protocol_request_t xcb_req = {
0 /* count */,
0 /* ext */,
XCB_PUT_IMAGE /* opcode */,
1 /* isvoid (doesn't cause a reply) */
};
xcb_put_image_request_t req;
struct iovec vec_stack[CAIRO_STACK_ARRAY_LENGTH (struct iovec)];
struct iovec *vec = vec_stack;
uint32_t len = 0;
uint8_t *data = _data;
int n = 3;
/* Two extra entries are needed for xcb, two for us */
int entries_needed = height + 2 + 2;
 
req.format = XCB_IMAGE_FORMAT_Z_PIXMAP;
req.drawable = dst;
req.gc = gc;
req.width = width;
req.height = height;
req.dst_x = dst_x;
req.dst_y = dst_y;
req.left_pad = 0;
req.depth = depth;
req.pad0[0] = 0;
req.pad0[1] = 0;
 
if (entries_needed > ARRAY_LENGTH (vec_stack)) {
vec = _cairo_malloc_ab (entries_needed, sizeof (struct iovec));
if (unlikely (vec == NULL)) {
/* XXX loop over ARRAY_LENGTH (vec_stack) */
return;
}
}
 
data += src_y * stride + src_x * cpp;
/* vec[1] will be used in XCB if it has to use BigRequests or insert a sync,
* vec[0] is used if the internal queue needs to be flushed. */
vec[2].iov_base = (char *) &req;
vec[2].iov_len = sizeof (req);
 
/* Now comes the actual data */
while (height--) {
vec[n].iov_base = data;
vec[n].iov_len = cpp * width;
len += cpp * width;
data += stride;
n++;
}
 
/* And again some padding */
vec[n].iov_base = 0;
vec[n].iov_len = -len & 3;
n++;
 
/* For efficiency reasons, this functions writes the request "directly" to
* the xcb connection to avoid having to copy the data around. */
assert (n == entries_needed);
xcb_req.count = n - 2;
xcb_send_request (connection->xcb_connection, 0, &vec[2], &xcb_req);
 
if (vec != vec_stack)
free (vec);
}
 
void
_cairo_xcb_connection_put_subimage (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
int16_t src_x,
int16_t src_y,
uint16_t width,
uint16_t height,
uint16_t cpp,
int stride,
int16_t dst_x,
int16_t dst_y,
uint8_t depth,
void *_data)
{
const uint32_t req_size = sizeof(xcb_put_image_request_t);
uint32_t length = height * cpp * width;
uint32_t len = (req_size + length) >> 2;
 
if (len < connection->maximum_request_length) {
_cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
width, height, cpp, stride, dst_x, dst_y, depth, _data);
} else {
int rows = (connection->maximum_request_length - req_size - 4) / (cpp * width);
if (rows > 0) {
do {
if (rows > height)
rows = height;
 
length = rows * cpp * width;
 
_cairo_xcb_connection_do_put_subimage (connection, dst, gc, src_x, src_y,
width, rows, cpp, stride, dst_x, dst_y, depth, _data);
 
height -= rows;
dst_y += rows;
_data = (char *) _data + stride * rows;
} while (height);
} else {
ASSERT_NOT_REACHED;
}
}
}
 
cairo_status_t
_cairo_xcb_connection_get_image (cairo_xcb_connection_t *connection,
xcb_drawable_t src,
int16_t src_x,
int16_t src_y,
uint16_t width,
uint16_t height,
xcb_get_image_reply_t **reply)
{
xcb_generic_error_t *error;
 
*reply = xcb_get_image_reply (connection->xcb_connection,
xcb_get_image (connection->xcb_connection,
XCB_IMAGE_FORMAT_Z_PIXMAP,
src,
src_x, src_y,
width, height,
(uint32_t) -1),
 
&error);
if (error) {
free (error);
 
free (*reply);
*reply = NULL;
}
 
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-xcb-connection-render.c
0,0 → 1,299
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-xcb-private.h"
 
#include <xcb/xcbext.h>
 
void
_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_drawable_t drawable,
xcb_render_pictformat_t format,
uint32_t value_mask,
uint32_t *value_list)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_create_picture (connection->xcb_connection, picture, drawable,
format, value_mask, value_list);
}
 
void
_cairo_xcb_connection_render_change_picture (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
uint32_t value_mask,
uint32_t *value_list)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_change_picture (connection->xcb_connection, picture,
value_mask, value_list);
}
 
void
_cairo_xcb_connection_render_set_picture_clip_rectangles (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
int16_t clip_x_origin,
int16_t clip_y_origin,
uint32_t rectangles_len,
xcb_rectangle_t *rectangles)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_set_picture_clip_rectangles (connection->xcb_connection, picture,
clip_x_origin, clip_y_origin,
rectangles_len, rectangles);
}
 
void
_cairo_xcb_connection_render_free_picture (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_free_picture (connection->xcb_connection, picture);
_cairo_xcb_connection_put_xid (connection, picture);
}
 
void
_cairo_xcb_connection_render_composite (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t mask,
xcb_render_picture_t dst,
int16_t src_x,
int16_t src_y,
int16_t mask_x,
int16_t mask_y,
int16_t dst_x,
int16_t dst_y,
uint16_t width,
uint16_t height)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
xcb_render_composite (connection->xcb_connection, op, src, mask, dst,
src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
}
 
void
_cairo_xcb_connection_render_trapezoids (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t dst,
xcb_render_pictformat_t mask_format,
int16_t src_x,
int16_t src_y,
uint32_t traps_len,
xcb_render_trapezoid_t *traps)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS);
xcb_render_trapezoids (connection->xcb_connection, op, src, dst,
mask_format, src_x, src_y, traps_len, traps);
}
 
void
_cairo_xcb_connection_render_create_glyph_set (cairo_xcb_connection_t *connection,
xcb_render_glyphset_t id,
xcb_render_pictformat_t format)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_create_glyph_set (connection->xcb_connection, id, format);
}
 
void
_cairo_xcb_connection_render_free_glyph_set (cairo_xcb_connection_t *connection,
xcb_render_glyphset_t glyphset)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_free_glyph_set (connection->xcb_connection, glyphset);
_cairo_xcb_connection_put_xid (connection, glyphset);
}
 
void
_cairo_xcb_connection_render_add_glyphs (cairo_xcb_connection_t *connection,
xcb_render_glyphset_t glyphset,
uint32_t num_glyphs,
uint32_t *glyphs_id,
xcb_render_glyphinfo_t *glyphs,
uint32_t data_len,
uint8_t *data)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_add_glyphs (connection->xcb_connection, glyphset, num_glyphs,
glyphs_id, glyphs, data_len, data);
}
 
void
_cairo_xcb_connection_render_free_glyphs (cairo_xcb_connection_t *connection,
xcb_render_glyphset_t glyphset,
uint32_t num_glyphs,
xcb_render_glyph_t *glyphs)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_free_glyphs (connection->xcb_connection, glyphset, num_glyphs, glyphs);
}
 
void
_cairo_xcb_connection_render_composite_glyphs_8 (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t dst,
xcb_render_pictformat_t mask_format,
xcb_render_glyphset_t glyphset,
int16_t src_x,
int16_t src_y,
uint32_t glyphcmds_len,
uint8_t *glyphcmds)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_composite_glyphs_8 (connection->xcb_connection, op, src, dst, mask_format,
glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
}
 
void
_cairo_xcb_connection_render_composite_glyphs_16 (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t dst,
xcb_render_pictformat_t mask_format,
xcb_render_glyphset_t glyphset,
int16_t src_x,
int16_t src_y,
uint32_t glyphcmds_len,
uint8_t *glyphcmds)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_composite_glyphs_16 (connection->xcb_connection, op, src, dst, mask_format,
glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
}
 
void
_cairo_xcb_connection_render_composite_glyphs_32 (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t dst,
xcb_render_pictformat_t mask_format,
xcb_render_glyphset_t glyphset,
int16_t src_x,
int16_t src_y,
uint32_t glyphcmds_len,
uint8_t *glyphcmds)
{
assert (connection->flags & CAIRO_XCB_HAS_RENDER);
xcb_render_composite_glyphs_32 (connection->xcb_connection, op, src, dst, mask_format,
glyphset, src_x, src_y, glyphcmds_len, glyphcmds);
}
 
void
_cairo_xcb_connection_render_fill_rectangles (cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t dst,
xcb_render_color_t color,
uint32_t num_rects,
xcb_rectangle_t *rects)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES);
xcb_render_fill_rectangles (connection->xcb_connection, op, dst, color,
num_rects, rects);
}
 
void
_cairo_xcb_connection_render_set_picture_transform (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_render_transform_t *transform)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM);
xcb_render_set_picture_transform (connection->xcb_connection, picture, *transform);
}
 
void
_cairo_xcb_connection_render_set_picture_filter (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
uint16_t filter_len,
char *filter)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_FILTERS);
xcb_render_set_picture_filter (connection->xcb_connection, picture,
filter_len, filter, 0, NULL);
}
 
void
_cairo_xcb_connection_render_create_solid_fill (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_render_color_t color)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
xcb_render_create_solid_fill (connection->xcb_connection, picture, color);
}
 
void
_cairo_xcb_connection_render_create_linear_gradient (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_render_pointfix_t p1,
xcb_render_pointfix_t p2,
uint32_t num_stops,
xcb_render_fixed_t *stops,
xcb_render_color_t *colors)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
xcb_render_create_linear_gradient (connection->xcb_connection, picture,
p1, p2, num_stops, stops, colors);
}
 
void
_cairo_xcb_connection_render_create_radial_gradient (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_render_pointfix_t inner,
xcb_render_pointfix_t outer,
xcb_render_fixed_t inner_radius,
xcb_render_fixed_t outer_radius,
uint32_t num_stops,
xcb_render_fixed_t *stops,
xcb_render_color_t *colors)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
xcb_render_create_radial_gradient (connection->xcb_connection, picture,
inner, outer, inner_radius, outer_radius,
num_stops, stops, colors);
}
 
void
_cairo_xcb_connection_render_create_conical_gradient (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_render_pointfix_t center,
xcb_render_fixed_t angle,
uint32_t num_stops,
xcb_render_fixed_t *stops,
xcb_render_color_t *colors)
{
assert (connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS);
xcb_render_create_conical_gradient (connection->xcb_connection, picture,
center, angle, num_stops, stops, colors);
}
/programs/develop/libraries/cairo/src/cairo-xcb-connection-shm.c
0,0 → 1,117
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
 
#include "cairo-xcb-private.h"
 
#include <xcb/xcbext.h>
#include <xcb/shm.h>
 
uint32_t
_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
uint32_t id,
cairo_bool_t readonly)
{
uint32_t segment = _cairo_xcb_connection_get_xid (connection);
assert (connection->flags & CAIRO_XCB_HAS_SHM);
xcb_shm_attach (connection->xcb_connection, segment, id, readonly);
return segment;
}
 
void
_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
uint16_t total_width,
uint16_t total_height,
int16_t src_x,
int16_t src_y,
uint16_t width,
uint16_t height,
int16_t dst_x,
int16_t dst_y,
uint8_t depth,
uint32_t shm,
uint32_t offset)
{
assert (connection->flags & CAIRO_XCB_HAS_SHM);
xcb_shm_put_image (connection->xcb_connection, dst, gc, total_width, total_height,
src_x, src_y, width, height, dst_x, dst_y, depth,
XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shm, offset);
}
 
cairo_status_t
_cairo_xcb_connection_shm_get_image (cairo_xcb_connection_t *connection,
xcb_drawable_t src,
int16_t src_x,
int16_t src_y,
uint16_t width,
uint16_t height,
uint32_t shmseg,
uint32_t offset)
{
xcb_shm_get_image_reply_t *reply;
xcb_generic_error_t *error;
 
assert (connection->flags & CAIRO_XCB_HAS_SHM);
reply = xcb_shm_get_image_reply (connection->xcb_connection,
xcb_shm_get_image (connection->xcb_connection,
src,
src_x, src_y,
width, height,
(uint32_t) -1,
XCB_IMAGE_FORMAT_Z_PIXMAP,
shmseg, offset),
&error);
free (reply);
 
if (error) {
/* an error here should be impossible */
free (error);
return _cairo_error (CAIRO_STATUS_READ_ERROR);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
uint32_t segment)
{
assert (connection->flags & CAIRO_XCB_HAS_SHM);
xcb_shm_detach (connection->xcb_connection, segment);
_cairo_xcb_connection_put_xid (connection, segment);
}
 
#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xcb-connection.c
0,0 → 1,977
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Authors:
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
 
#include "cairoint.h"
 
#include "cairo-xcb-private.h"
#include "cairo-hash-private.h"
#include "cairo-freelist-private.h"
#include "cairo-list-inline.h"
 
#include <xcb/xcbext.h>
#include <xcb/bigreq.h>
#include <errno.h>
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
#include <sys/ipc.h>
#include <sys/shm.h>
#include <xcb/shm.h>
#endif
 
typedef struct _cairo_xcb_xrender_format {
cairo_hash_entry_t key;
xcb_render_pictformat_t xrender_format;
} cairo_xcb_xrender_format_t;
 
typedef struct _cairo_xcb_xid {
cairo_list_t link;
uint32_t xid;
} cairo_xcb_xid_t;
 
#define XCB_RENDER_AT_LEAST(V, major, minor) \
(((V)->major_version > major) || \
(((V)->major_version == major) && ((V)->minor_version >= minor)))
 
#define XCB_RENDER_HAS_CREATE_PICTURE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
#define XCB_RENDER_HAS_COMPOSITE(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
#define XCB_RENDER_HAS_COMPOSITE_TEXT(surface) XCB_RENDER_AT_LEAST((surface), 0, 0)
 
#define XCB_RENDER_HAS_FILL_RECTANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 1)
 
#define XCB_RENDER_HAS_DISJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
#define XCB_RENDER_HAS_CONJOINT(surface) XCB_RENDER_AT_LEAST((surface), 0, 2)
 
#define XCB_RENDER_HAS_TRAPEZOIDS(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
#define XCB_RENDER_HAS_TRIANGLES(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
#define XCB_RENDER_HAS_TRISTRIP(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
#define XCB_RENDER_HAS_TRIFAN(surface) XCB_RENDER_AT_LEAST((surface), 0, 4)
 
#define XCB_RENDER_HAS_PICTURE_TRANSFORM(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
#define XCB_RENDER_HAS_FILTERS(surface) XCB_RENDER_AT_LEAST((surface), 0, 6)
 
#define XCB_RENDER_HAS_EXTENDED_REPEAT(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
#define XCB_RENDER_HAS_GRADIENTS(surface) XCB_RENDER_AT_LEAST((surface), 0, 10)
 
#define XCB_RENDER_HAS_PDF_OPERATORS(surface) XCB_RENDER_AT_LEAST((surface), 0, 11)
 
static cairo_list_t connections;
 
static cairo_status_t
_cairo_xcb_connection_find_visual_formats (cairo_xcb_connection_t *connection,
const xcb_render_query_pict_formats_reply_t *formats)
{
xcb_render_pictscreen_iterator_t screens;
xcb_render_pictdepth_iterator_t depths;
xcb_render_pictvisual_iterator_t visuals;
 
for (screens = xcb_render_query_pict_formats_screens_iterator (formats);
screens.rem;
xcb_render_pictscreen_next (&screens))
{
for (depths = xcb_render_pictscreen_depths_iterator (screens.data);
depths.rem;
xcb_render_pictdepth_next (&depths))
{
for (visuals = xcb_render_pictdepth_visuals_iterator (depths.data);
visuals.rem;
xcb_render_pictvisual_next (&visuals))
{
cairo_xcb_xrender_format_t *f;
cairo_status_t status;
 
f = malloc (sizeof (cairo_xcb_xrender_format_t));
if (unlikely (f == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
f->key.hash = visuals.data->visual;
f->xrender_format = visuals.data->format;
status = _cairo_hash_table_insert (connection->visual_to_xrender_format,
&f->key);
if (unlikely (status))
return status;
}
}
}
 
return CAIRO_STATUS_SUCCESS;
}
 
#if 0
static xcb_format_t *
find_format_for_depth (const xcb_setup_t *setup, uint8_t depth)
{
xcb_format_t *fmt = xcb_setup_pixmap_formats (setup);
xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length (setup);
 
for (; fmt != fmtend; ++fmt)
if (fmt->depth == depth)
return fmt;
 
return 0;
}
#endif
 
static cairo_status_t
_cairo_xcb_connection_parse_xrender_formats (cairo_xcb_connection_t *connection,
const xcb_render_query_pict_formats_reply_t *formats)
{
xcb_render_pictforminfo_iterator_t i;
cairo_status_t status;
 
for (i = xcb_render_query_pict_formats_formats_iterator (formats);
i.rem;
xcb_render_pictforminfo_next (&i))
{
cairo_format_masks_t masks;
pixman_format_code_t pixman_format;
 
if (i.data->type != XCB_RENDER_PICT_TYPE_DIRECT)
continue;
 
masks.alpha_mask =
(unsigned long) i.data->direct.alpha_mask << i.data->direct.alpha_shift;
masks.red_mask =
(unsigned long) i.data->direct.red_mask << i.data->direct.red_shift;
masks.green_mask =
(unsigned long) i.data->direct.green_mask << i.data->direct.green_shift;
masks.blue_mask =
(unsigned long) i.data->direct.blue_mask << i.data->direct.blue_shift;
masks.bpp = i.data->depth;
 
if (_pixman_format_from_masks (&masks, &pixman_format)) {
cairo_hash_entry_t key;
 
key.hash = pixman_format;
if (! _cairo_hash_table_lookup (connection->xrender_formats, &key)) {
cairo_xcb_xrender_format_t *f;
 
f = malloc (sizeof (cairo_xcb_xrender_format_t));
if (unlikely (f == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
f->key.hash = pixman_format;
f->xrender_format = i.data->id;
status = _cairo_hash_table_insert (connection->xrender_formats,
&f->key);
if (unlikely (status))
return status;
 
#if 0
printf ("xrender %x -> (%lx, %lx, %lx, %lx, %d) %x [%d, %d]\n",
i.data->id,
masks.alpha_mask,
masks.red_mask,
masks.green_mask,
masks.blue_mask,
masks.bpp,
pixman_format,
PIXMAN_FORMAT_DEPTH(pixman_format),
PIXMAN_FORMAT_BPP(pixman_format));
#endif
}
}
}
 
status = _cairo_xcb_connection_find_visual_formats (connection, formats);
if (unlikely (status))
return status;
 
connection->standard_formats[CAIRO_FORMAT_A1] =
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a1);
 
connection->standard_formats[CAIRO_FORMAT_A8] =
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8);
 
connection->standard_formats[CAIRO_FORMAT_RGB24] =
_cairo_xcb_connection_get_xrender_format (connection,
PIXMAN_FORMAT (24,
PIXMAN_TYPE_ARGB,
0, 8, 8, 8));
if (connection->standard_formats[CAIRO_FORMAT_RGB24] == XCB_NONE) {
connection->standard_formats[CAIRO_FORMAT_RGB24] =
_cairo_xcb_connection_get_xrender_format (connection,
PIXMAN_FORMAT (24, PIXMAN_TYPE_ABGR,
0, 8, 8, 8));
}
 
connection->standard_formats[CAIRO_FORMAT_ARGB32] =
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8r8g8b8);
if (connection->standard_formats[CAIRO_FORMAT_ARGB32] == XCB_NONE) {
connection->standard_formats[CAIRO_FORMAT_ARGB32] =
_cairo_xcb_connection_get_xrender_format (connection, PIXMAN_a8b8g8r8);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
/*
* We require support for depth 1, 8, 24 and 32 pixmaps
*/
#define DEPTH_MASK(d) (1 << ((d) - 1))
#define REQUIRED_DEPTHS (DEPTH_MASK(1) | \
DEPTH_MASK(8) | \
DEPTH_MASK(24) | \
DEPTH_MASK(32))
static cairo_bool_t
pixmap_depths_usable (cairo_xcb_connection_t *connection,
uint32_t missing,
xcb_drawable_t root)
{
xcb_connection_t *c = connection->xcb_connection;
xcb_void_cookie_t create_cookie[32];
xcb_pixmap_t pixmap;
cairo_bool_t success = TRUE;
int depth, i, j;
 
pixmap = _cairo_xcb_connection_get_xid (connection);
 
for (depth = 1, i = 0; depth <= 32; depth++) {
if (missing & DEPTH_MASK(depth)) {
create_cookie[i] = xcb_create_pixmap_checked (c, depth, pixmap, root, 1, 1);
xcb_free_pixmap (c, pixmap);
if (!create_cookie[i].sequence) {
success = FALSE;
break;
}
i++;
}
}
 
for (j = 0; j < i; j++) {
xcb_generic_error_t *create_error = xcb_request_check (c, create_cookie[j]);
success &= create_error == NULL;
free (create_error);
}
 
_cairo_xcb_connection_put_xid (connection, pixmap);
 
return success;
}
 
static cairo_bool_t
has_required_depths (cairo_xcb_connection_t *connection)
{
xcb_screen_iterator_t screens;
 
for (screens = xcb_setup_roots_iterator (connection->root);
screens.rem;
xcb_screen_next (&screens))
{
xcb_depth_iterator_t depths;
uint32_t missing = REQUIRED_DEPTHS;
 
for (depths = xcb_screen_allowed_depths_iterator (screens.data);
depths.rem;
xcb_depth_next (&depths))
{
missing &= ~DEPTH_MASK (depths.data->depth);
}
if (missing == 0)
continue;
 
/*
* Ok, this is ugly. It should be sufficient at this
* point to just return false, but Xinerama is broken at
* this point and only advertises depths which have an
* associated visual. Of course, the other depths still
* work, but the only way to find out is to try them.
*/
if (! pixmap_depths_usable (connection, missing, screens.data->root))
return FALSE;
}
 
return TRUE;
}
 
static xcb_render_query_version_reply_t *
_render_restrict_env(xcb_render_query_version_reply_t *version)
{
const char *env;
 
if (version == NULL)
return NULL;
 
env = getenv ("CAIRO_DEBUG");
if (env != NULL)
env = strstr (env, "xrender-version=");
if (env != NULL) {
int max_render_major, max_render_minor;
 
env += sizeof ("xrender-version=") - 1;
if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
max_render_major = max_render_minor = -1;
 
if (max_render_major < 0 || max_render_minor < 0) {
free (version);
return NULL;
}
 
if (max_render_major < (int) version->major_version ||
(max_render_major == (int) version->major_version &&
max_render_minor < (int) version->minor_version))
{
version->major_version = max_render_major;
version->minor_version = max_render_minor;
}
}
 
return version;
}
 
static cairo_status_t
_cairo_xcb_connection_query_render (cairo_xcb_connection_t *connection)
{
xcb_connection_t *c = connection->xcb_connection;
xcb_render_query_version_cookie_t version_cookie;
xcb_render_query_pict_formats_cookie_t formats_cookie;
xcb_render_query_version_reply_t *version;
xcb_render_query_pict_formats_reply_t *formats;
cairo_status_t status;
cairo_bool_t present;
 
version_cookie = xcb_render_query_version (c, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
formats_cookie = xcb_render_query_pict_formats (c);
 
present = has_required_depths (connection);
version = xcb_render_query_version_reply (c, version_cookie, 0);
formats = xcb_render_query_pict_formats_reply (c, formats_cookie, 0);
 
version = _render_restrict_env (version);
 
if (! present || version == NULL || formats == NULL) {
free (version);
free (formats);
return CAIRO_STATUS_SUCCESS;
}
 
/* always true if the extension is present (i.e. >= 0.0) */
connection->flags |= CAIRO_XCB_HAS_RENDER;
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE;
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS;
 
if (XCB_RENDER_HAS_FILL_RECTANGLES (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
 
if (XCB_RENDER_HAS_TRAPEZOIDS (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
 
if (XCB_RENDER_HAS_PICTURE_TRANSFORM (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
 
if (XCB_RENDER_HAS_FILTERS (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_FILTERS;
 
if (XCB_RENDER_HAS_PDF_OPERATORS (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
 
if (XCB_RENDER_HAS_EXTENDED_REPEAT (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
 
if (XCB_RENDER_HAS_GRADIENTS (version))
connection->flags |= CAIRO_XCB_RENDER_HAS_GRADIENTS;
 
free (version);
 
status = _cairo_xcb_connection_parse_xrender_formats (connection, formats);
free (formats);
 
return status;
}
 
#if 0
static void
_cairo_xcb_connection_query_cairo (cairo_xcb_connection_t *connection)
{
xcb_connection_t *c = connection->xcb_connection;
xcb_cairo_query_version_reply_t *version;
 
version = xcb_cairo_query_version_reply (c,
xcb_cairo_query_version (c, 0, 0),
0);
 
free (version);
}
#endif
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
static cairo_bool_t
can_use_shm (cairo_xcb_connection_t *connection)
{
cairo_bool_t success = TRUE;
xcb_connection_t *c = connection->xcb_connection;
xcb_void_cookie_t cookie[2];
xcb_generic_error_t *error;
int shmid;
uint32_t shmseg;
void *ptr;
 
shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
if (shmid == -1)
return FALSE;
 
ptr = shmat (shmid, NULL, 0);
if (ptr == (char *) -1) {
shmctl (shmid, IPC_RMID, NULL);
return FALSE;
}
 
shmseg = _cairo_xcb_connection_get_xid (connection);
cookie[0] = xcb_shm_attach_checked (c, shmseg, shmid, FALSE);
cookie[1] = xcb_shm_detach_checked (c, shmseg);
_cairo_xcb_connection_put_xid (connection, shmseg);
 
error = xcb_request_check (c, cookie[0]);
if (error != NULL)
success = FALSE;
 
error = xcb_request_check (c, cookie[1]);
if (error != NULL)
success = FALSE;
 
shmctl (shmid, IPC_RMID, NULL);
shmdt (ptr);
 
return success;
}
 
static void
_cairo_xcb_connection_query_shm (cairo_xcb_connection_t *connection)
{
xcb_connection_t *c = connection->xcb_connection;
xcb_shm_query_version_reply_t *version;
 
version = xcb_shm_query_version_reply (c, xcb_shm_query_version (c), 0);
if (version == NULL)
return;
 
free (version);
 
if (can_use_shm (connection))
connection->flags |= CAIRO_XCB_HAS_SHM;
}
#endif
 
static cairo_status_t
_device_flush (void *device)
{
cairo_xcb_connection_t *connection = device;
cairo_status_t status;
 
status = cairo_device_acquire (&connection->device);
if (unlikely (status))
return status;
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
_cairo_xcb_connection_shm_mem_pools_flush (connection);
#endif
 
xcb_flush (connection->xcb_connection);
 
cairo_device_release (&connection->device);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_pluck_xrender_format (void *entry,
void *closure)
{
_cairo_hash_table_remove (closure, entry);
free (entry);
}
 
static void
_device_finish (void *device)
{
cairo_xcb_connection_t *connection = device;
cairo_bool_t was_cached = FALSE;
 
if (! cairo_list_is_empty (&connection->link)) {
CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
cairo_list_del (&connection->link);
CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
was_cached = TRUE;
}
 
while (! cairo_list_is_empty (&connection->fonts)) {
cairo_xcb_font_t *font;
 
font = cairo_list_first_entry (&connection->fonts,
cairo_xcb_font_t,
link);
_cairo_xcb_font_close (font);
}
 
while (! cairo_list_is_empty (&connection->screens)) {
cairo_xcb_screen_t *screen;
 
screen = cairo_list_first_entry (&connection->screens,
cairo_xcb_screen_t,
link);
_cairo_xcb_screen_finish (screen);
}
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
/* _cairo_xcb_screen_finish finishes surfaces. If any of those surfaces had
* a fallback image, we might have done a SHM PutImage. */
_cairo_xcb_connection_shm_mem_pools_flush (connection);
#endif
 
if (was_cached)
cairo_device_destroy (device);
}
 
static void
_device_destroy (void *device)
{
cairo_xcb_connection_t *connection = device;
 
_cairo_hash_table_foreach (connection->xrender_formats,
_pluck_xrender_format, connection->xrender_formats);
_cairo_hash_table_destroy (connection->xrender_formats);
 
_cairo_hash_table_foreach (connection->visual_to_xrender_format,
_pluck_xrender_format,
connection->visual_to_xrender_format);
_cairo_hash_table_destroy (connection->visual_to_xrender_format);
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
_cairo_xcb_connection_shm_mem_pools_fini (connection);
#endif
_cairo_freepool_fini (&connection->shm_info_freelist);
 
_cairo_freepool_fini (&connection->xid_pool);
 
CAIRO_MUTEX_FINI (connection->shm_mutex);
CAIRO_MUTEX_FINI (connection->screens_mutex);
 
free (connection);
}
 
static const cairo_device_backend_t _cairo_xcb_device_backend = {
CAIRO_DEVICE_TYPE_XCB,
 
NULL, NULL, /* lock, unlock */
 
_device_flush,
_device_finish,
_device_destroy,
};
 
cairo_xcb_connection_t *
_cairo_xcb_connection_get (xcb_connection_t *xcb_connection)
{
cairo_xcb_connection_t *connection;
const xcb_query_extension_reply_t *ext;
cairo_status_t status;
 
CAIRO_MUTEX_INITIALIZE ();
 
CAIRO_MUTEX_LOCK (_cairo_xcb_connections_mutex);
if (connections.next == NULL) {
/* XXX _cairo_init () */
cairo_list_init (&connections);
}
 
cairo_list_foreach_entry (connection,
cairo_xcb_connection_t,
&connections,
link)
{
if (connection->xcb_connection == xcb_connection) {
/* Maintain MRU order. */
if (connections.next != &connection->link)
cairo_list_move (&connection->link, &connections);
 
goto unlock;
}
}
 
connection = malloc (sizeof (cairo_xcb_connection_t));
if (unlikely (connection == NULL))
goto unlock;
 
_cairo_device_init (&connection->device, &_cairo_xcb_device_backend);
 
connection->xcb_connection = xcb_connection;
 
cairo_list_init (&connection->fonts);
cairo_list_init (&connection->screens);
cairo_list_init (&connection->link);
connection->xrender_formats = _cairo_hash_table_create (NULL);
if (connection->xrender_formats == NULL) {
CAIRO_MUTEX_FINI (connection->device.mutex);
free (connection);
connection = NULL;
goto unlock;
}
 
connection->visual_to_xrender_format = _cairo_hash_table_create (NULL);
if (connection->visual_to_xrender_format == NULL) {
_cairo_hash_table_destroy (connection->xrender_formats);
CAIRO_MUTEX_FINI (connection->device.mutex);
free (connection);
connection = NULL;
goto unlock;
}
 
cairo_list_init (&connection->free_xids);
_cairo_freepool_init (&connection->xid_pool,
sizeof (cairo_xcb_xid_t));
 
cairo_list_init (&connection->shm_pools);
cairo_list_init (&connection->shm_pending);
_cairo_freepool_init (&connection->shm_info_freelist,
sizeof (cairo_xcb_shm_info_t));
 
connection->maximum_request_length =
xcb_get_maximum_request_length (xcb_connection);
 
CAIRO_MUTEX_INIT (connection->shm_mutex);
CAIRO_MUTEX_INIT (connection->screens_mutex);
 
CAIRO_MUTEX_LOCK (connection->device.mutex);
 
connection->flags = 0;
connection->force_precision = -1;
 
xcb_prefetch_extension_data (xcb_connection, &xcb_big_requests_id);
xcb_prefetch_extension_data (xcb_connection, &xcb_render_id);
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
xcb_prefetch_extension_data (xcb_connection, &xcb_shm_id);
#endif
#if 0
xcb_prefetch_extension_data (xcb_connection, &xcb_cairo_id);
#endif
 
xcb_prefetch_maximum_request_length (xcb_connection);
 
connection->root = xcb_get_setup (xcb_connection);
connection->render = NULL;
ext = xcb_get_extension_data (xcb_connection, &xcb_render_id);
if (ext != NULL && ext->present) {
status = _cairo_xcb_connection_query_render (connection);
if (unlikely (status)) {
CAIRO_MUTEX_UNLOCK (connection->device.mutex);
_cairo_xcb_connection_destroy (connection);
connection = NULL;
goto unlock;
}
 
connection->render = ext;
}
 
#if 0
ext = xcb_get_extension_data (connection, &xcb_cairo_id);
if (ext != NULL && ext->present)
_cairo_xcb_connection_query_cairo (connection);
#endif
 
connection->shm = NULL;
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
ext = xcb_get_extension_data (xcb_connection, &xcb_shm_id);
if (ext != NULL && ext->present) {
_cairo_xcb_connection_query_shm (connection);
connection->shm = ext;
}
#endif
 
connection->original_flags = connection->flags;
 
CAIRO_MUTEX_UNLOCK (connection->device.mutex);
 
cairo_list_add (&connection->link, &connections);
unlock:
CAIRO_MUTEX_UNLOCK (_cairo_xcb_connections_mutex);
 
return connection;
}
 
xcb_render_pictformat_t
_cairo_xcb_connection_get_xrender_format (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format)
{
cairo_hash_entry_t key;
cairo_xcb_xrender_format_t *format;
 
key.hash = pixman_format;
format = _cairo_hash_table_lookup (connection->xrender_formats, &key);
return format ? format->xrender_format : XCB_NONE;
}
 
xcb_render_pictformat_t
_cairo_xcb_connection_get_xrender_format_for_visual (cairo_xcb_connection_t *connection,
const xcb_visualid_t visual)
{
cairo_hash_entry_t key;
cairo_xcb_xrender_format_t *format;
 
key.hash = visual;
format = _cairo_hash_table_lookup (connection->visual_to_xrender_format, &key);
return format ? format->xrender_format : XCB_NONE;
}
 
void
_cairo_xcb_connection_put_xid (cairo_xcb_connection_t *connection,
uint32_t xid)
{
cairo_xcb_xid_t *cache;
 
assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
cache = _cairo_freepool_alloc (&connection->xid_pool);
if (likely (cache != NULL)) {
cache->xid = xid;
cairo_list_add (&cache->link, &connection->free_xids);
}
}
 
uint32_t
_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection)
{
uint32_t xid;
 
assert (CAIRO_MUTEX_IS_LOCKED (connection->device.mutex));
if (! cairo_list_is_empty (&connection->free_xids)) {
cairo_xcb_xid_t *cache;
 
cache = cairo_list_first_entry (&connection->free_xids,
cairo_xcb_xid_t,
link);
xid = cache->xid;
 
cairo_list_del (&cache->link);
_cairo_freepool_free (&connection->xid_pool, cache);
} else {
xid = xcb_generate_id (connection->xcb_connection);
}
 
return xid;
}
 
/**
* cairo_xcb_device_get_connection:
* @device: a #cairo_device_t for the XCB backend
*
* Get the connection for the XCB device.
*
* Returns: the #xcb_connection_t for the connection
*
* Since: 1.12
**/
xcb_connection_t *
cairo_xcb_device_get_connection (cairo_device_t *device)
{
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB)
return NULL;
 
return ((cairo_xcb_connection_t *)device)->xcb_connection;
}
 
/* public (debug) interface */
 
/**
* cairo_xcb_device_debug_cap_xshm_version:
* @device: a #cairo_device_t for the XCB backend
* @major_version: major version to restrict to
* @minor_version: minor version to restrict to
*
* Restricts all future XCB surfaces for this devices to the specified version
* of the SHM extension. This function exists solely for debugging purpose.
* It let's you find out how cairo would behave with an older version of
* the SHM extension.
*
* Use the special values -1 and -1 for disabling the SHM extension.
*
* Since: 1.12
**/
void
cairo_xcb_device_debug_cap_xshm_version (cairo_device_t *device,
int major_version,
int minor_version)
{
cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return;
}
 
/* First reset all the SHM flags to their original value. This works
* because we only ever clear bits after the connection was created.
*/
connection->flags |= (connection->original_flags & CAIRO_XCB_SHM_MASK);
 
/* clear any flags that are inappropriate for the desired version */
if (major_version < 0 && minor_version < 0) {
connection->flags &= ~(CAIRO_XCB_HAS_SHM);
}
}
 
/**
* cairo_xcb_device_debug_cap_xrender_version:
* @device: a #cairo_device_t for the XCB backend
* @major_version: major version to restrict to
* @minor_version: minor version to restrict to
*
* Restricts all future XCB surfaces for this devices to the specified version
* of the RENDER extension. This function exists solely for debugging purpose.
* It let's you find out how cairo would behave with an older version of
* the RENDER extension.
*
* Use the special values -1 and -1 for disabling the RENDER extension.
*
* Since: 1.12
**/
void
cairo_xcb_device_debug_cap_xrender_version (cairo_device_t *device,
int major_version,
int minor_version)
{
cairo_xcb_connection_t *connection = (cairo_xcb_connection_t *) device;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return;
}
 
/* First reset all the RENDER flags to their original value. This works
* because we only ever clear bits after the connection was created.
*/
connection->flags |= (connection->original_flags & CAIRO_XCB_RENDER_MASK);
 
/* clear any flags that are inappropriate for the desired version */
if (major_version < 0 && minor_version < 0) {
connection->flags &= ~(CAIRO_XCB_HAS_RENDER |
CAIRO_XCB_RENDER_HAS_COMPOSITE |
CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
CAIRO_XCB_RENDER_HAS_FILTERS |
CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
CAIRO_XCB_RENDER_HAS_GRADIENTS);
} else {
xcb_render_query_version_reply_t version;
 
version.major_version = major_version;
version.minor_version = minor_version;
 
if (! XCB_RENDER_HAS_FILL_RECTANGLES (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES;
 
if (! XCB_RENDER_HAS_TRAPEZOIDS (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS;
 
if (! XCB_RENDER_HAS_PICTURE_TRANSFORM (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM;
 
if (! XCB_RENDER_HAS_FILTERS (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_FILTERS;
 
if (! XCB_RENDER_HAS_PDF_OPERATORS (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
 
if (! XCB_RENDER_HAS_EXTENDED_REPEAT (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT;
 
if (! XCB_RENDER_HAS_GRADIENTS (&version))
connection->flags &= ~CAIRO_XCB_RENDER_HAS_GRADIENTS;
}
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_device_debug_cap_xrender_version);
#endif
 
/**
* cairo_xcb_device_debug_set_precision:
* @device: a #cairo_device_t for the XCB backend
* @precision: the precision to use
*
* Render supports two modes of precision when rendering trapezoids. Set
* the precision to the desired mode.
*
* Since: 1.12
**/
void
cairo_xcb_device_debug_set_precision (cairo_device_t *device,
int precision)
{
if (device == NULL || device->status)
return;
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return;
}
 
((cairo_xcb_connection_t *) device)->force_precision = precision;
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_device_debug_set_precision);
#endif
 
/**
* cairo_xcb_device_debug_get_precision:
* @device: a #cairo_device_t for the XCB backend
*
* Get the Xrender precision mode.
*
* Returns: the render precision mode
*
* Since: 1.12
**/
int
cairo_xcb_device_debug_get_precision (cairo_device_t *device)
{
if (device == NULL || device->status)
return -1;
if (device->backend->type != CAIRO_DEVICE_TYPE_XCB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return -1;
}
 
return ((cairo_xcb_connection_t *) device)->force_precision;
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_device_debug_get_precision);
#endif
/programs/develop/libraries/cairo/src/cairo-xcb-private.h
39,6 → 39,8
 
#include "cairo-xcb.h"
 
#include "cairoint.h"
 
#include "cairo-cache-private.h"
#include "cairo-compiler-private.h"
#include "cairo-device-private.h"
46,7 → 48,9
#include "cairo-freelist-private.h"
#include "cairo-list-private.h"
#include "cairo-mutex-private.h"
#include "cairo-pattern-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-spans-private.h"
#include "cairo-surface-private.h"
 
55,10 → 59,20
#include <xcb/xcbext.h>
#include <pixman.h>
 
#define XLIB_COORD_MAX 32767
 
/* maximum number of cached GC's */
#define GC_CACHE_SIZE 4
 
#define CAIRO_XCB_RENDER_AT_LEAST(major, minor) \
((XCB_RENDER_MAJOR_VERSION > major) || \
((XCB_RENDER_MAJOR_VERSION == major) && (XCB_RENDER_MINOR_VERSION >= minor)))
 
typedef struct _cairo_xcb_connection cairo_xcb_connection_t;
typedef struct _cairo_xcb_font cairo_xcb_font_t;
typedef struct _cairo_xcb_screen cairo_xcb_screen_t;
typedef struct _cairo_xcb_surface cairo_xcb_surface_t;
typedef struct _cairo_xcb_picture cairo_xcb_picture_t;
typedef struct _cairo_xcb_shm_mem_pool cairo_xcb_shm_mem_pool_t;
typedef struct _cairo_xcb_shm_info cairo_xcb_shm_info_t;
 
66,39 → 80,60
cairo_xcb_connection_t *connection;
uint32_t shm;
uint32_t offset;
uint64_t seqno;
size_t size;
void *mem;
cairo_xcb_shm_mem_pool_t *pool;
xcb_get_input_focus_cookie_t sync;
cairo_list_t pending;
};
 
struct _cairo_xcb_surface {
cairo_surface_t base;
cairo_surface_t *fallback;
cairo_image_surface_t *fallback;
cairo_boxes_t fallback_damage;
 
cairo_xcb_connection_t *connection;
cairo_xcb_screen_t *screen;
 
cairo_surface_t *drm;
cairo_bool_t marked_dirty;
 
xcb_drawable_t drawable;
cairo_bool_t owns_pixmap;
int use_pixmap;
 
cairo_bool_t deferred_clear;
cairo_color_t deferred_clear_color;
 
int width;
int height;
int depth;
 
unsigned int flags;
xcb_render_picture_t picture;
xcb_render_pictformat_t xrender_format;
pixman_format_code_t pixman_format;
uint32_t precision;
 
cairo_list_t link;
};
 
struct _cairo_xcb_picture {
cairo_surface_t base;
 
cairo_xcb_screen_t *screen;
xcb_render_picture_t picture;
xcb_render_pictformat_t xrender_format;
pixman_format_code_t pixman_format;
 
int width, height;
 
cairo_extend_t extend;
cairo_filter_t filter;
cairo_bool_t has_component_alpha;
xcb_render_transform_t transform;
 
int x0, y0;
int x, y;
 
cairo_list_t link;
};
 
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
typedef struct _cairo_xlib_xcb_surface {
cairo_surface_t base;
135,6 → 170,7
} cairo_xcb_font_glyphset_info_t;
 
struct _cairo_xcb_font {
cairo_scaled_font_private_t base;
cairo_scaled_font_t *scaled_font;
cairo_xcb_connection_t *connection;
cairo_xcb_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
145,10 → 181,9
cairo_xcb_connection_t *connection;
 
xcb_screen_t *xcb_screen;
cairo_device_t *device;
 
xcb_gcontext_t gc[4];
int gc_depths; /* 4 x uint8_t */
xcb_gcontext_t gc[GC_CACHE_SIZE];
uint8_t gc_depths[GC_CACHE_SIZE];
 
cairo_surface_t *stock_colors[CAIRO_STOCK_NUM_COLORS];
struct {
157,7 → 192,6
} solid_cache[16];
int solid_cache_size;
 
cairo_cache_t surface_pattern_cache;
cairo_cache_t linear_pattern_cache;
cairo_cache_t radial_pattern_cache;
cairo_freelist_t pattern_cache_entry_freelist;
164,6 → 198,7
 
cairo_list_t link;
cairo_list_t surfaces;
cairo_list_t pictures;
};
 
struct _cairo_xcb_connection {
170,7 → 205,6
cairo_device_t device;
 
xcb_connection_t *xcb_connection;
cairo_bool_t has_socket;
 
xcb_render_pictformat_t standard_formats[5];
cairo_hash_table_t *xrender_formats;
178,12 → 212,13
 
unsigned int maximum_request_length;
unsigned int flags;
unsigned int original_flags;
 
int force_precision;
 
const xcb_setup_t *root;
const xcb_query_extension_reply_t *render;
const xcb_query_extension_reply_t *shm;
const xcb_query_extension_reply_t *dri2;
uint64_t seqno;
 
cairo_list_t free_xids;
cairo_freepool_t xid_pool;
190,6 → 225,7
 
cairo_mutex_t shm_mutex;
cairo_list_t shm_pools;
cairo_list_t shm_pending;
cairo_freepool_t shm_info_freelist;
 
cairo_mutex_t screens_mutex;
205,18 → 241,26
CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES = 0x0002,
CAIRO_XCB_RENDER_HAS_COMPOSITE = 0x0004,
CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS = 0x0008,
CAIRO_XCB_RENDER_HAS_COMPOSITE_SPANS = 0x0010,
CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0020,
CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0040,
CAIRO_XCB_RENDER_HAS_FILTERS = 0x0080,
CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0100,
CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0200,
CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0400,
CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS = 0x0010,
CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM = 0x0020,
CAIRO_XCB_RENDER_HAS_FILTERS = 0x0040,
CAIRO_XCB_RENDER_HAS_PDF_OPERATORS = 0x0080,
CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT = 0x0100,
CAIRO_XCB_RENDER_HAS_GRADIENTS = 0x0200,
 
CAIRO_XCB_HAS_CAIRO = 0x10000,
CAIRO_XCB_HAS_SHM = 0x80000000,
 
CAIRO_XCB_HAS_DRI2 = 0x40000000,
CAIRO_XCB_HAS_SHM = 0x80000000
CAIRO_XCB_RENDER_MASK = CAIRO_XCB_HAS_RENDER |
CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES |
CAIRO_XCB_RENDER_HAS_COMPOSITE |
CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS |
CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM |
CAIRO_XCB_RENDER_HAS_FILTERS |
CAIRO_XCB_RENDER_HAS_PDF_OPERATORS |
CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT |
CAIRO_XCB_RENDER_HAS_GRADIENTS,
CAIRO_XCB_SHM_MASK = CAIRO_XCB_HAS_SHM
};
 
#define CAIRO_XCB_SHM_SMALL_IMAGE 8192
246,9 → 290,6
return cairo_device_acquire (&connection->device);
}
 
cairo_private cairo_status_t
_cairo_xcb_connection_take_socket (cairo_xcb_connection_t *connection);
 
cairo_private uint32_t
_cairo_xcb_connection_get_xid (cairo_xcb_connection_t *connection);
 
271,6 → 312,7
cairo_private cairo_int_status_t
_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *display,
size_t size,
cairo_bool_t might_reuse,
cairo_xcb_shm_info_t **shm_info_out);
 
cairo_private void
277,10 → 319,13
_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info);
 
cairo_private void
_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection);
 
cairo_private void
_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection);
 
cairo_private void
_cairo_xcb_font_finish (cairo_xcb_font_t *font);
_cairo_xcb_font_close (cairo_xcb_font_t *font);
 
cairo_private cairo_xcb_screen_t *
_cairo_xcb_screen_get (xcb_connection_t *connection,
298,14 → 343,6
_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc);
 
cairo_private cairo_status_t
_cairo_xcb_screen_store_surface_picture (cairo_xcb_screen_t *screen,
cairo_surface_t *picture,
unsigned int size);
cairo_private void
_cairo_xcb_screen_remove_surface_picture (cairo_xcb_screen_t *screen,
cairo_surface_t *picture);
 
cairo_private cairo_status_t
_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
const cairo_linear_pattern_t *linear,
cairo_surface_t *picture);
324,9 → 361,10
const cairo_radial_pattern_t *radial);
 
cairo_private cairo_surface_t *
_cairo_xcb_surface_create_similar_image (cairo_xcb_surface_t *other,
cairo_content_t content,
int width, int height);
_cairo_xcb_surface_create_similar_image (void *abstrct_other,
cairo_format_t format,
int width,
int height);
 
cairo_private cairo_surface_t *
_cairo_xcb_surface_create_similar (void *abstract_other,
343,93 → 381,43
int width,
int height);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_cairo_paint (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
cairo_private_no_warn cairo_bool_t
_cairo_xcb_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_cairo_mask (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_cairo_stroke (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_cairo_fill (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_cairo_glyphs (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
_cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
cairo_antialias_t antialias);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
_cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
cairo_antialias_t antialias);
 
cairo_private cairo_int_status_t
_cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
_cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_clip_t *clip);
cairo_bool_t overlap);
cairo_private void
_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font);
 
451,19 → 439,6
const cairo_color_t *color,
cairo_boxes_t *boxes);
 
static inline void
_cairo_xcb_connection_write (cairo_xcb_connection_t *connection,
struct iovec *vec,
int count)
{
if (unlikely (connection->device.status))
return;
 
connection->seqno++;
if (unlikely (! xcb_writev (connection->xcb_connection, vec, count, 1)))
connection->device.status = _cairo_error (CAIRO_STATUS_WRITE_ERROR);
}
 
cairo_private xcb_pixmap_t
_cairo_xcb_connection_create_pixmap (cairo_xcb_connection_t *connection,
uint8_t depth,
524,7 → 499,7
uint16_t width,
uint16_t height,
uint16_t cpp,
uint16_t stride,
int stride,
int16_t dst_x,
int16_t dst_y,
uint8_t depth,
546,6 → 521,13
uint32_t num_rectangles,
xcb_rectangle_t *rectangles);
 
cairo_private cairo_status_t
_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format,
int width, int height,
cairo_image_surface_t **image_out,
cairo_xcb_shm_info_t **shm_info_out);
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
cairo_private uint32_t
_cairo_xcb_connection_shm_attach (cairo_xcb_connection_t *connection,
552,7 → 534,7
uint32_t id,
cairo_bool_t readonly);
 
cairo_private uint64_t
cairo_private void
_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
582,7 → 564,7
_cairo_xcb_connection_shm_detach (cairo_xcb_connection_t *connection,
uint32_t segment);
#else
static inline uint64_t
static inline void
_cairo_xcb_connection_shm_put_image (cairo_xcb_connection_t *connection,
xcb_drawable_t dst,
xcb_gcontext_t gc,
598,21 → 580,11
uint32_t shm,
uint32_t offset)
{
return 0;
ASSERT_NOT_REACHED;
}
#endif
 
cairo_private void
_cairo_xcb_connection_render_spans (cairo_xcb_connection_t *connection,
xcb_render_picture_t dst,
int op,
xcb_render_picture_t src,
int16_t src_x, int16_t src_y,
int16_t dst_x, int16_t dst_y,
int16_t width, int16_t height,
unsigned int length,
uint16_t *spans);
cairo_private void
_cairo_xcb_connection_render_create_picture (cairo_xcb_connection_t *connection,
xcb_render_picture_t picture,
xcb_drawable_t drawable,
781,6 → 753,10
slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
slim_hidden_proto (cairo_xcb_surface_set_size);
slim_hidden_proto (cairo_xcb_surface_set_drawable);
slim_hidden_proto (cairo_xcb_device_debug_get_precision);
slim_hidden_proto_no_warn (cairo_xcb_device_debug_set_precision);
slim_hidden_proto_no_warn (cairo_xcb_device_debug_cap_xrender_version);
#endif
 
#endif /* CAIRO_XCB_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-xcb-screen.c
0,0 → 1,364
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2008 Chris Wilson
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Authors:
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-xcb-private.h"
#include "cairo-list-inline.h"
 
struct pattern_cache_entry {
cairo_cache_entry_t key;
cairo_xcb_screen_t *screen;
cairo_pattern_union_t pattern;
cairo_surface_t *picture;
};
 
void
_cairo_xcb_screen_finish (cairo_xcb_screen_t *screen)
{
int i;
 
CAIRO_MUTEX_LOCK (screen->connection->screens_mutex);
cairo_list_del (&screen->link);
CAIRO_MUTEX_UNLOCK (screen->connection->screens_mutex);
 
while (! cairo_list_is_empty (&screen->surfaces)) {
cairo_surface_t *surface;
 
surface = &cairo_list_first_entry (&screen->surfaces,
cairo_xcb_surface_t,
link)->base;
 
cairo_surface_finish (surface);
}
 
while (! cairo_list_is_empty (&screen->pictures)) {
cairo_surface_t *surface;
 
surface = &cairo_list_first_entry (&screen->pictures,
cairo_xcb_picture_t,
link)->base;
 
cairo_surface_finish (surface);
}
 
for (i = 0; i < screen->solid_cache_size; i++)
cairo_surface_destroy (screen->solid_cache[i].picture);
 
for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
cairo_surface_destroy (screen->stock_colors[i]);
 
for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
if (screen->gc_depths[i] != 0)
_cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
}
 
_cairo_cache_fini (&screen->linear_pattern_cache);
_cairo_cache_fini (&screen->radial_pattern_cache);
_cairo_freelist_fini (&screen->pattern_cache_entry_freelist);
 
free (screen);
}
 
static cairo_bool_t
_linear_pattern_cache_entry_equal (const void *A, const void *B)
{
const struct pattern_cache_entry *a = A, *b = B;
 
return _cairo_linear_pattern_equal (&a->pattern.gradient.linear,
&b->pattern.gradient.linear);
}
 
static cairo_bool_t
_radial_pattern_cache_entry_equal (const void *A, const void *B)
{
const struct pattern_cache_entry *a = A, *b = B;
 
return _cairo_radial_pattern_equal (&a->pattern.gradient.radial,
&b->pattern.gradient.radial);
}
 
static void
_pattern_cache_entry_destroy (void *closure)
{
struct pattern_cache_entry *entry = closure;
 
_cairo_pattern_fini (&entry->pattern.base);
cairo_surface_destroy (entry->picture);
_cairo_freelist_free (&entry->screen->pattern_cache_entry_freelist, entry);
}
 
cairo_xcb_screen_t *
_cairo_xcb_screen_get (xcb_connection_t *xcb_connection,
xcb_screen_t *xcb_screen)
{
cairo_xcb_connection_t *connection;
cairo_xcb_screen_t *screen;
cairo_status_t status;
int i;
 
connection = _cairo_xcb_connection_get (xcb_connection);
if (unlikely (connection == NULL))
return NULL;
 
CAIRO_MUTEX_LOCK (connection->screens_mutex);
 
cairo_list_foreach_entry (screen,
cairo_xcb_screen_t,
&connection->screens,
link)
{
if (screen->xcb_screen == xcb_screen) {
/* Maintain list in MRU order */
if (&screen->link != connection->screens.next)
cairo_list_move (&screen->link, &connection->screens);
 
goto unlock;
}
}
 
screen = malloc (sizeof (cairo_xcb_screen_t));
if (unlikely (screen == NULL))
goto unlock;
 
screen->connection = connection;
screen->xcb_screen = xcb_screen;
 
_cairo_freelist_init (&screen->pattern_cache_entry_freelist,
sizeof (struct pattern_cache_entry));
cairo_list_init (&screen->link);
cairo_list_init (&screen->surfaces);
cairo_list_init (&screen->pictures);
 
memset (screen->gc_depths, 0, sizeof (screen->gc_depths));
memset (screen->gc, 0, sizeof (screen->gc));
 
screen->solid_cache_size = 0;
for (i = 0; i < ARRAY_LENGTH (screen->stock_colors); i++)
screen->stock_colors[i] = NULL;
 
status = _cairo_cache_init (&screen->linear_pattern_cache,
_linear_pattern_cache_entry_equal,
NULL,
_pattern_cache_entry_destroy,
16);
if (unlikely (status))
goto error_screen;
 
status = _cairo_cache_init (&screen->radial_pattern_cache,
_radial_pattern_cache_entry_equal,
NULL,
_pattern_cache_entry_destroy,
4);
if (unlikely (status))
goto error_linear;
 
cairo_list_add (&screen->link, &connection->screens);
 
unlock:
CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
 
return screen;
 
error_linear:
_cairo_cache_fini (&screen->linear_pattern_cache);
error_screen:
CAIRO_MUTEX_UNLOCK (connection->screens_mutex);
free (screen);
 
return NULL;
}
 
static xcb_gcontext_t
_create_gc (cairo_xcb_screen_t *screen,
xcb_drawable_t drawable)
{
uint32_t values[] = { 0 };
 
return _cairo_xcb_connection_create_gc (screen->connection, drawable,
XCB_GC_GRAPHICS_EXPOSURES,
values);
}
 
xcb_gcontext_t
_cairo_xcb_screen_get_gc (cairo_xcb_screen_t *screen,
xcb_drawable_t drawable,
int depth)
{
int i;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
if (screen->gc_depths[i] == depth) {
screen->gc_depths[i] = 0;
return screen->gc[i];
}
}
 
return _create_gc (screen, drawable);
}
 
void
_cairo_xcb_screen_put_gc (cairo_xcb_screen_t *screen, int depth, xcb_gcontext_t gc)
{
int i;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
for (i = 0; i < ARRAY_LENGTH (screen->gc); i++) {
if (screen->gc_depths[i] == 0)
break;
}
 
if (i == ARRAY_LENGTH (screen->gc)) {
/* perform random substitution to ensure fair caching over depths */
i = rand () % ARRAY_LENGTH (screen->gc);
_cairo_xcb_connection_free_gc (screen->connection, screen->gc[i]);
}
 
screen->gc[i] = gc;
screen->gc_depths[i] = depth;
}
 
cairo_status_t
_cairo_xcb_screen_store_linear_picture (cairo_xcb_screen_t *screen,
const cairo_linear_pattern_t *linear,
cairo_surface_t *picture)
{
struct pattern_cache_entry *entry;
cairo_status_t status;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
if (unlikely (entry == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
entry->key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
entry->key.size = 1;
 
status = _cairo_pattern_init_copy (&entry->pattern.base, &linear->base.base);
if (unlikely (status)) {
_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
return status;
}
 
entry->picture = cairo_surface_reference (picture);
entry->screen = screen;
 
status = _cairo_cache_insert (&screen->linear_pattern_cache,
&entry->key);
if (unlikely (status)) {
cairo_surface_destroy (picture);
_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_surface_t *
_cairo_xcb_screen_lookup_linear_picture (cairo_xcb_screen_t *screen,
const cairo_linear_pattern_t *linear)
{
cairo_surface_t *picture = NULL;
struct pattern_cache_entry tmpl;
struct pattern_cache_entry *entry;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
tmpl.key.hash = _cairo_linear_pattern_hash (_CAIRO_HASH_INIT_VALUE, linear);
_cairo_pattern_init_static_copy (&tmpl.pattern.base, &linear->base.base);
 
entry = _cairo_cache_lookup (&screen->linear_pattern_cache, &tmpl.key);
if (entry != NULL)
picture = cairo_surface_reference (entry->picture);
 
return picture;
}
 
cairo_status_t
_cairo_xcb_screen_store_radial_picture (cairo_xcb_screen_t *screen,
const cairo_radial_pattern_t *radial,
cairo_surface_t *picture)
{
struct pattern_cache_entry *entry;
cairo_status_t status;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
entry = _cairo_freelist_alloc (&screen->pattern_cache_entry_freelist);
if (unlikely (entry == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
entry->key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
entry->key.size = 1;
 
status = _cairo_pattern_init_copy (&entry->pattern.base, &radial->base.base);
if (unlikely (status)) {
_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
return status;
}
 
entry->picture = cairo_surface_reference (picture);
entry->screen = screen;
 
status = _cairo_cache_insert (&screen->radial_pattern_cache, &entry->key);
if (unlikely (status)) {
cairo_surface_destroy (picture);
_cairo_freelist_free (&screen->pattern_cache_entry_freelist, entry);
return status;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
cairo_surface_t *
_cairo_xcb_screen_lookup_radial_picture (cairo_xcb_screen_t *screen,
const cairo_radial_pattern_t *radial)
{
cairo_surface_t *picture = NULL;
struct pattern_cache_entry tmpl;
struct pattern_cache_entry *entry;
 
assert (CAIRO_MUTEX_IS_LOCKED (screen->connection->device.mutex));
 
tmpl.key.hash = _cairo_radial_pattern_hash (_CAIRO_HASH_INIT_VALUE, radial);
_cairo_pattern_init_static_copy (&tmpl.pattern.base, &radial->base.base);
 
entry = _cairo_cache_lookup (&screen->radial_pattern_cache, &tmpl.key);
if (entry != NULL)
picture = cairo_surface_reference (entry->picture);
 
return picture;
}
/programs/develop/libraries/cairo/src/cairo-xcb-shm.c
0,0 → 1,337
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 Chris Wilson
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Red Hat, Inc.
*
* Contributors(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
 
#include "cairo-xcb-private.h"
#include "cairo-list-inline.h"
#include "cairo-mempool-private.h"
 
#include <xcb/shm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
 
#define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
 
/* a simple buddy allocator for memory pools
* XXX fragmentation? use Doug Lea's malloc?
*/
 
typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
 
typedef enum {
PENDING_WAIT,
PENDING_POLL
} shm_wait_type_t;
 
struct _cairo_xcb_shm_mem_pool {
int shmid;
uint32_t shmseg;
void *shm;
 
cairo_mempool_t mem;
 
cairo_list_t link;
};
 
static void
_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
{
cairo_list_del (&pool->link);
 
shmdt (pool->shm);
_cairo_mempool_fini (&pool->mem);
 
free (pool);
}
 
static void
_cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
{
cairo_xcb_connection_t *connection = shm_info->connection;
 
assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
 
_cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
_cairo_freepool_free (&connection->shm_info_freelist, shm_info);
 
/* scan for old, unused pools - hold at least one in reserve */
if (! cairo_list_is_singular (&connection->shm_pools))
{
cairo_xcb_shm_mem_pool_t *pool, *next;
cairo_list_t head;
 
cairo_list_init (&head);
cairo_list_move (connection->shm_pools.next, &head);
 
cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
&connection->shm_pools, link)
{
if (pool->mem.free_bytes == pool->mem.max_bytes) {
_cairo_xcb_connection_shm_detach (connection, pool->shmseg);
_cairo_xcb_shm_mem_pool_destroy (pool);
}
}
 
cairo_list_move (head.next, &connection->shm_pools);
}
}
 
static void
_cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
{
cairo_xcb_shm_info_t *info, *next;
xcb_get_input_focus_reply_t *reply;
 
assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
&connection->shm_pending, pending)
{
switch (wait) {
case PENDING_WAIT:
reply = xcb_wait_for_reply (connection->xcb_connection,
info->sync.sequence, NULL);
break;
case PENDING_POLL:
if (! xcb_poll_for_reply (connection->xcb_connection,
info->sync.sequence,
(void **) &reply, NULL))
/* We cannot be sure the server finished with this image yet, so
* try again later. All other shm info are guaranteed to have a
* larger sequence number and thus don't have to be checked. */
return;
break;
default:
/* silence Clang static analyzer warning */
ASSERT_NOT_REACHED;
reply = NULL;
}
 
free (reply);
cairo_list_del (&info->pending);
_cairo_xcb_shm_info_finalize (info);
}
}
 
cairo_int_status_t
_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
size_t size,
cairo_bool_t might_reuse,
cairo_xcb_shm_info_t **shm_info_out)
{
cairo_xcb_shm_info_t *shm_info;
cairo_xcb_shm_mem_pool_t *pool, *next;
size_t bytes, maxbits = 16, minbits = 8;
size_t shm_allocated = 0;
void *mem = NULL;
cairo_status_t status;
 
assert (connection->flags & CAIRO_XCB_HAS_SHM);
 
CAIRO_MUTEX_LOCK (connection->shm_mutex);
_cairo_xcb_shm_process_pending (connection, PENDING_POLL);
 
if (might_reuse) {
cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
&connection->shm_pending, pending) {
if (shm_info->size >= size) {
cairo_list_del (&shm_info->pending);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
 
xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
shm_info->sync.sequence = XCB_NONE;
 
*shm_info_out = shm_info;
return CAIRO_STATUS_SUCCESS;
}
}
}
 
cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
&connection->shm_pools, link)
{
if (pool->mem.free_bytes > size) {
mem = _cairo_mempool_alloc (&pool->mem, size);
if (mem != NULL) {
/* keep the active pools towards the front */
cairo_list_move (&pool->link, &connection->shm_pools);
goto allocate_shm_info;
}
}
/* scan for old, unused pools */
if (pool->mem.free_bytes == pool->mem.max_bytes) {
_cairo_xcb_connection_shm_detach (connection,
pool->shmseg);
_cairo_xcb_shm_mem_pool_destroy (pool);
} else {
shm_allocated += pool->mem.max_bytes;
}
}
 
if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t));
if (unlikely (pool == NULL)) {
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
bytes = 1 << maxbits;
while (bytes <= size)
bytes <<= 1, maxbits++;
bytes <<= 3;
 
do {
pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
if (pool->shmid != -1)
break;
 
/* If the allocation failed because we asked for too much memory, we try
* again with a smaller request, as long as our allocation still fits. */
bytes >>= 1;
if (errno != EINVAL || bytes < size)
break;
} while (TRUE);
if (pool->shmid == -1) {
int err = errno;
if (! (err == EINVAL || err == ENOMEM))
connection->flags &= ~CAIRO_XCB_HAS_SHM;
free (pool);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
pool->shm = shmat (pool->shmid, NULL, 0);
if (unlikely (pool->shm == (char *) -1)) {
shmctl (pool->shmid, IPC_RMID, NULL);
free (pool);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
status = _cairo_mempool_init (&pool->mem, pool->shm, bytes,
minbits, maxbits - minbits + 1);
if (unlikely (status)) {
shmdt (pool->shm);
free (pool);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return status;
}
 
pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
shmctl (pool->shmid, IPC_RMID, NULL);
 
cairo_list_add (&pool->link, &connection->shm_pools);
mem = _cairo_mempool_alloc (&pool->mem, size);
 
allocate_shm_info:
shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
if (unlikely (shm_info == NULL)) {
_cairo_mempool_free (&pool->mem, mem);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
shm_info->connection = connection;
shm_info->pool = pool;
shm_info->shm = pool->shmseg;
shm_info->size = size;
shm_info->offset = (char *) mem - (char *) pool->shm;
shm_info->mem = mem;
shm_info->sync.sequence = XCB_NONE;
 
/* scan for old, unused pools */
cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
&connection->shm_pools, link)
{
if (pool->mem.free_bytes == pool->mem.max_bytes) {
_cairo_xcb_connection_shm_detach (connection,
pool->shmseg);
_cairo_xcb_shm_mem_pool_destroy (pool);
}
}
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
 
*shm_info_out = shm_info;
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
{
cairo_xcb_connection_t *connection = shm_info->connection;
 
/* We can only return shm_info->mem to the allocator when we can be sure
* that the X server no longer reads from it. Since the X server processes
* requests in order, we send a GetInputFocus here.
* _cairo_xcb_shm_process_pending () will later check if the reply for that
* request was received and then actually mark this memory area as free. */
 
CAIRO_MUTEX_LOCK (connection->shm_mutex);
assert (shm_info->sync.sequence == XCB_NONE);
shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
 
cairo_list_init (&shm_info->pending);
cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
}
 
void
_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
{
CAIRO_MUTEX_LOCK (connection->shm_mutex);
_cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
}
 
void
_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
{
assert (cairo_list_is_empty (&connection->shm_pending));
while (! cairo_list_is_empty (&connection->shm_pools)) {
_cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
cairo_xcb_shm_mem_pool_t,
link));
}
}
 
#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xcb-surface-core.c
0,0 → 1,639
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-boxes-private.h"
#include "cairo-xcb-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-backend-private.h"
 
/* XXX dithering */
 
typedef struct _cairo_xcb_pixmap {
cairo_surface_t base;
 
cairo_xcb_connection_t *connection;
cairo_xcb_screen_t *screen;
 
cairo_surface_t *owner;
xcb_pixmap_t pixmap;
int width;
int height;
int depth;
int x0, y0;
cairo_bool_t repeat;
} cairo_xcb_pixmap_t;
 
static cairo_status_t
_cairo_xcb_pixmap_finish (void *abstract_surface)
{
cairo_xcb_pixmap_t *surface = abstract_surface;
cairo_status_t status;
 
if (surface->owner != NULL) {
cairo_surface_destroy (surface->owner);
} else {
status = _cairo_xcb_connection_acquire (surface->connection);
if (unlikely (status))
return status;
 
_cairo_xcb_connection_free_pixmap (surface->connection,
surface->pixmap);
_cairo_xcb_connection_release (surface->connection);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t _cairo_xcb_pixmap_backend = {
CAIRO_SURFACE_TYPE_XCB,
_cairo_xcb_pixmap_finish,
};
 
static cairo_xcb_pixmap_t *
_cairo_xcb_pixmap_create (cairo_xcb_surface_t *target,
int width, int height)
{
cairo_xcb_pixmap_t *surface;
 
surface = malloc (sizeof (cairo_xcb_pixmap_t));
if (unlikely (surface == NULL))
return (cairo_xcb_pixmap_t *)
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_xcb_pixmap_backend,
NULL,
target->base.content);
 
surface->connection = target->connection;
surface->screen = target->screen;
surface->owner = NULL;
surface->width = width;
surface->height = height;
surface->depth = target->depth;
surface->x0 = surface->y0 = 0;
surface->repeat = FALSE;
 
surface->pixmap =
_cairo_xcb_connection_create_pixmap (surface->connection,
surface->depth,
target->drawable,
width, height);
 
return surface;
}
 
static cairo_xcb_pixmap_t *
_cairo_xcb_pixmap_copy (cairo_xcb_surface_t *target)
{
cairo_xcb_pixmap_t *surface;
 
surface = malloc (sizeof (cairo_xcb_pixmap_t));
if (unlikely (surface == NULL))
return (cairo_xcb_pixmap_t *)
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_xcb_pixmap_backend,
NULL,
target->base.content);
 
surface->connection = target->connection;
surface->screen = target->screen;
surface->pixmap = target->drawable;
surface->owner = cairo_surface_reference (&target->base);
surface->width = target->width;
surface->height = target->height;
surface->depth = target->depth;
surface->x0 = surface->y0 = 0;
surface->repeat = FALSE;
 
return surface;
}
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
static cairo_status_t
_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format,
int width, int height,
cairo_image_surface_t **image_out,
cairo_xcb_shm_info_t **shm_info_out)
{
cairo_surface_t *image = NULL;
cairo_xcb_shm_info_t *shm_info = NULL;
cairo_status_t status;
size_t size, stride;
 
if (! (connection->flags & CAIRO_XCB_HAS_SHM))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP (pixman_format));
size = stride * height;
if (size <= CAIRO_XCB_SHM_SMALL_IMAGE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_xcb_connection_allocate_shm_info (connection, size,
FALSE, &shm_info);
if (unlikely (status))
return status;
 
image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
pixman_format,
width, height,
stride);
status = image->status;
if (unlikely (status)) {
_cairo_xcb_shm_info_destroy (shm_info);
return status;
}
 
status = _cairo_user_data_array_set_data (&image->user_data,
(const cairo_user_data_key_t *) connection,
shm_info,
(cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
 
if (unlikely (status)) {
cairo_surface_destroy (image);
_cairo_xcb_shm_info_destroy (shm_info);
return status;
}
 
*image_out = (cairo_image_surface_t *) image;
*shm_info_out = shm_info;
return CAIRO_STATUS_SUCCESS;
}
#else
static cairo_status_t
_cairo_xcb_shm_image_create_shm (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format,
int width, int height,
cairo_image_surface_t **image_out,
cairo_xcb_shm_info_t **shm_info_out)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
#endif
 
cairo_status_t
_cairo_xcb_shm_image_create (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format,
int width, int height,
cairo_image_surface_t **image_out,
cairo_xcb_shm_info_t **shm_info_out)
{
cairo_surface_t *image = NULL;
cairo_xcb_shm_info_t *shm_info = NULL;
cairo_status_t status;
 
status = _cairo_xcb_shm_image_create_shm (connection,
pixman_format,
width,
height,
image_out,
shm_info_out);
 
if (status != CAIRO_STATUS_SUCCESS) {
image = _cairo_image_surface_create_with_pixman_format (NULL,
pixman_format,
width, height,
0);
status = image->status;
if (unlikely (status))
return status;
 
*image_out = (cairo_image_surface_t *) image;
*shm_info_out = shm_info;
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_xcb_pixmap_t *
_pixmap_from_image (cairo_xcb_surface_t *target,
xcb_render_pictformat_t format,
cairo_image_surface_t *image,
cairo_xcb_shm_info_t *shm_info)
{
xcb_gcontext_t gc;
cairo_xcb_pixmap_t *pixmap;
 
pixmap = _cairo_xcb_pixmap_create (target,
image->width,
image->height);
if (unlikely (pixmap->base.status))
return pixmap;
 
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap->pixmap, image->depth);
 
if (shm_info != NULL) {
_cairo_xcb_connection_shm_put_image (target->connection,
pixmap->pixmap, gc,
image->width, image->height,
0, 0,
image->width, image->height,
0, 0,
image->depth,
shm_info->shm,
shm_info->offset);
} else {
int len;
 
/* Do we need to trim the image? */
len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width,
PIXMAN_FORMAT_BPP (image->pixman_format));
if (len == image->stride) {
_cairo_xcb_connection_put_image (target->connection,
pixmap->pixmap, gc,
image->width, image->height,
0, 0,
image->depth,
image->stride,
image->data);
} else {
_cairo_xcb_connection_put_subimage (target->connection,
pixmap->pixmap, gc,
0, 0,
image->width, image->height,
PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
image->stride,
0, 0,
image->depth,
image->data);
 
}
}
 
_cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
 
return pixmap;
}
 
static cairo_xcb_pixmap_t *
_render_to_pixmap (cairo_xcb_surface_t *target,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
cairo_image_surface_t *image;
cairo_xcb_shm_info_t *shm_info;
cairo_pattern_union_t copy;
cairo_status_t status;
cairo_xcb_pixmap_t *pixmap;
 
status = _cairo_xcb_shm_image_create (target->screen->connection,
target->pixman_format,
extents->width, extents->height,
&image, &shm_info);
if (unlikely (status))
return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
 
_cairo_pattern_init_static_copy (&copy.base, pattern);
cairo_matrix_translate (&copy.base.matrix, -extents->x, -extents->y);
status = _cairo_surface_paint (&image->base,
CAIRO_OPERATOR_SOURCE,
&copy.base,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return (cairo_xcb_pixmap_t *) _cairo_surface_create_in_error (status);
}
 
pixmap = _pixmap_from_image (target, target->xrender_format, image, shm_info);
cairo_surface_destroy (&image->base);
 
if (unlikely (pixmap->base.status))
return pixmap;
 
pixmap->x0 = -extents->x;
pixmap->y0 = -extents->y;
return pixmap;
}
 
static cairo_xcb_pixmap_t *
_copy_to_pixmap (cairo_xcb_surface_t *source)
{
cairo_xcb_pixmap_t *pixmap;
 
/* If the source may be a window, we need to copy it and its children
* via a temporary pixmap so that we can IncludeInferiors on the source
* and use ClipByChildren on the destination.
*/
if (source->owns_pixmap) {
pixmap = _cairo_xcb_pixmap_copy (source);
if (unlikely (pixmap->base.status))
return pixmap;
} else {
uint32_t values[1];
xcb_gcontext_t gc;
 
pixmap = _cairo_xcb_pixmap_create (source,
source->width,
source->height);
if (unlikely (pixmap->base.status))
return pixmap;
 
gc = _cairo_xcb_screen_get_gc (source->screen,
pixmap->pixmap,
pixmap->depth);
 
values[0] = TRUE;
_cairo_xcb_connection_change_gc (pixmap->connection, gc,
XCB_GC_SUBWINDOW_MODE, values);
 
_cairo_xcb_connection_copy_area (pixmap->connection,
source->drawable,
pixmap->pixmap, gc,
0, 0,
0, 0,
source->width,
source->height);
 
values[0] = FALSE;
_cairo_xcb_connection_change_gc (pixmap->connection, gc,
XCB_GC_SUBWINDOW_MODE, values);
 
_cairo_xcb_screen_put_gc (source->screen,
pixmap->depth,
gc);
}
 
return pixmap;
}
static cairo_xcb_pixmap_t *
_cairo_xcb_surface_pixmap (cairo_xcb_surface_t *target,
const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
int tx, int ty)
{
cairo_surface_t *source;
cairo_xcb_pixmap_t *pixmap;
 
source = pattern->surface;
pixmap = (cairo_xcb_pixmap_t *)
_cairo_surface_has_snapshot (source, &_cairo_xcb_pixmap_backend);
if (pixmap != NULL && pixmap->screen == target->screen)
return (cairo_xcb_pixmap_t *) cairo_surface_reference (&pixmap->base);
 
if (source->type == CAIRO_SURFACE_TYPE_XCB &&
((cairo_xcb_surface_t *) source)->screen == target->screen)
{
cairo_xcb_surface_t *xcb_source = (cairo_xcb_surface_t *) source;
 
if (xcb_source->depth == target->depth)
pixmap = _copy_to_pixmap (xcb_source);
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
else if (source->type == CAIRO_SURFACE_TYPE_XLIB &&
((cairo_xlib_xcb_surface_t *) source)->xcb->screen == target->screen)
{
cairo_xcb_surface_t *xcb_source = ((cairo_xlib_xcb_surface_t *) source)->xcb;
 
if (xcb_source->depth == target->depth)
pixmap = _copy_to_pixmap (xcb_source);
}
#endif
 
if (pixmap == NULL) {
cairo_rectangle_int_t rect;
 
if (! _cairo_surface_get_extents (source, &rect)) {
rect.x = rect.y = 0;
rect.width = target->width;
rect.height = target->height;
}
 
pixmap = _render_to_pixmap (target, &pattern->base, &rect);
}
 
if (unlikely (pixmap->base.status))
return pixmap;
 
_cairo_surface_attach_snapshot (source, &pixmap->base, NULL);
 
if (pattern->base.extend != CAIRO_EXTEND_NONE) {
if (extents->x < 0 || extents->y < 0 ||
extents->x + extents->width > pixmap->width ||
extents->y + extents->height > pixmap->height)
{
pixmap->repeat = TRUE;
}
}
 
pixmap->x0 += tx;
pixmap->y0 += ty;
 
return pixmap;
}
 
static cairo_xcb_pixmap_t *
_cairo_xcb_pixmap_for_pattern (cairo_xcb_surface_t *target,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
int tx, ty;
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SURFACE:
/* Core can only perform a native, unscaled blit, but can handle tiles */
if (_cairo_matrix_is_integer_translation (&pattern->matrix, &tx, &ty)) {
switch (pattern->extend) {
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_REPEAT:
return _cairo_xcb_surface_pixmap (target,
(cairo_surface_pattern_t *) pattern,
extents, tx, ty);
 
default:
case CAIRO_EXTEND_PAD:
case CAIRO_EXTEND_REFLECT:
break;
}
}
/* fallthrough */
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _render_to_pixmap (target, pattern, extents);
 
default:
case CAIRO_PATTERN_TYPE_SOLID:
ASSERT_NOT_REACHED;
return NULL;
}
}
 
cairo_status_t
_cairo_xcb_surface_core_copy_boxes (cairo_xcb_surface_t *dst,
const cairo_pattern_t *src_pattern,
const cairo_rectangle_int_t *extents,
const cairo_boxes_t *boxes)
{
cairo_xcb_pixmap_t *src;
const struct _cairo_boxes_chunk *chunk;
xcb_gcontext_t gc;
cairo_status_t status;
 
status = _cairo_xcb_connection_acquire (dst->connection);
if (unlikely (status))
return status;
 
src = _cairo_xcb_pixmap_for_pattern (dst, src_pattern, extents);
status = src->base.status;
if (unlikely (status))
goto CLEANUP_CONNECTION;
 
assert (src->depth == dst->depth);
 
gc = _cairo_xcb_screen_get_gc (dst->screen, src->pixmap, src->depth);
 
if (src->repeat) {
uint32_t mask =
XCB_GC_FILL_STYLE |
XCB_GC_TILE |
XCB_GC_TILE_STIPPLE_ORIGIN_X |
XCB_GC_TILE_STIPPLE_ORIGIN_Y;
uint32_t values[] = {
XCB_FILL_STYLE_TILED,
src->pixmap,
- src->x0, - src->y0,
};
xcb_rectangle_t *xcb_rects;
 
_cairo_xcb_connection_change_gc (dst->connection, gc, mask, values);
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
int i;
 
xcb_rects = (xcb_rectangle_t *) chunk->base;
 
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
 
xcb_rects[i].x = x1;
xcb_rects[i].y = y1;
xcb_rects[i].width = x2 - x1;
xcb_rects[i].height = y2 - y1;
}
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
dst->drawable,
gc, chunk->count, xcb_rects);
}
 
values[0] = 0;
_cairo_xcb_connection_change_gc (dst->connection, gc, XCB_GC_FILL_STYLE, values);
} else {
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
int i;
 
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
 
_cairo_xcb_connection_copy_area (dst->connection,
src->pixmap,
dst->drawable, gc,
src->x0 + x1,
src->y0 + y1,
x1, y1,
x2 - x2, y2 - x2);
}
}
}
 
_cairo_xcb_screen_put_gc (dst->screen, src->depth, gc);
cairo_surface_destroy (&src->base);
 
CLEANUP_CONNECTION:
_cairo_xcb_connection_release (dst->connection);
 
return status;
}
 
cairo_status_t
_cairo_xcb_surface_core_fill_boxes (cairo_xcb_surface_t *dst,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
struct _cairo_boxes_chunk *chunk;
xcb_gcontext_t gc;
cairo_status_t status;
 
status = _cairo_xcb_connection_acquire (dst->connection);
if (unlikely (status))
return status;
 
gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
 
#if 0
xcb_pixmap_t source;
 
source = _dither_source (dst, color);
XSetTSOrigin (surface->dpy, gc, 0, 0);
XSetTile (surface->dpy, gc, source);
#endif
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
xcb_rectangle_t *xcb_rects;
int i;
 
xcb_rects = (xcb_rectangle_t *) chunk->base;
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round (chunk->base[i].p1.x);
int x2 = _cairo_fixed_integer_round (chunk->base[i].p2.x);
int y1 = _cairo_fixed_integer_round (chunk->base[i].p1.y);
int y2 = _cairo_fixed_integer_round (chunk->base[i].p2.y);
 
xcb_rects[i].x = x1;
xcb_rects[i].y = y1;
xcb_rects[i].width = x2 - x1;
xcb_rects[i].height = y2 - y1;
}
 
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
dst->drawable, gc,
chunk->count, xcb_rects);
}
 
_cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
_cairo_xcb_connection_release (dst->connection);
 
return CAIRO_STATUS_SUCCESS;
}
/programs/develop/libraries/cairo/src/cairo-xcb-surface-render.c
0,0 → 1,4865
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "cairo-xcb-private.h"
 
#include "cairo-boxes-private.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-region-private.h"
#include "cairo-surface-offset-private.h"
#include "cairo-surface-snapshot-inline.h"
#include "cairo-surface-subsurface-private.h"
#include "cairo-traps-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-paginated-private.h"
#include "cairo-pattern-inline.h"
 
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
 
static cairo_status_t
_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
cairo_composite_rectangles_t *extents);
 
static inline cairo_xcb_connection_t *
_picture_to_connection (cairo_xcb_picture_t *picture)
{
return (cairo_xcb_connection_t *) picture->base.device;
}
 
static void
_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface);
 
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 cairo_status_t
_cairo_xcb_picture_finish (void *abstract_surface)
{
cairo_xcb_picture_t *surface = abstract_surface;
cairo_xcb_connection_t *connection = _picture_to_connection (surface);
cairo_status_t status;
 
status = _cairo_xcb_connection_acquire (connection);
cairo_list_del (&surface->link);
if (unlikely (status))
return status;
 
_cairo_xcb_connection_render_free_picture (connection, surface->picture);
 
_cairo_xcb_connection_release (connection);
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t _cairo_xcb_picture_backend = {
CAIRO_SURFACE_TYPE_XCB,
_cairo_xcb_picture_finish,
};
 
static const struct xcb_render_transform_t identity_transform = {
1 << 16, 0, 0,
0, 1 << 16, 0,
0, 0, 1 << 16,
};
 
static cairo_xcb_picture_t *
_cairo_xcb_picture_create (cairo_xcb_screen_t *screen,
pixman_format_code_t pixman_format,
xcb_render_pictformat_t xrender_format,
int width, int height)
{
cairo_xcb_picture_t *surface;
 
surface = malloc (sizeof (cairo_xcb_picture_t));
if (unlikely (surface == NULL))
return (cairo_xcb_picture_t *)
_cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_xcb_picture_backend,
&screen->connection->device,
_cairo_content_from_pixman_format (pixman_format));
 
cairo_list_add (&surface->link, &screen->pictures);
 
surface->screen = screen;
surface->picture = _cairo_xcb_connection_get_xid (screen->connection);
surface->pixman_format = pixman_format;
surface->xrender_format = xrender_format;
 
surface->x0 = surface->y0 = 0;
surface->x = surface->y = 0;
surface->width = width;
surface->height = height;
 
surface->transform = identity_transform;
surface->extend = CAIRO_EXTEND_NONE;
surface->filter = CAIRO_FILTER_NEAREST;
surface->has_component_alpha = FALSE;
 
return surface;
}
 
static inline cairo_bool_t
_operator_is_supported (uint32_t flags, cairo_operator_t op)
{
if (op <= CAIRO_OPERATOR_SATURATE)
return TRUE;
 
/* Can we use PDF operators? */
#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
if (op <= CAIRO_OPERATOR_HSL_LUMINOSITY)
return flags & CAIRO_XCB_RENDER_HAS_PDF_OPERATORS;
#endif
 
return FALSE;
}
 
static int
_render_operator (cairo_operator_t op)
{
#define C(x,y) case CAIRO_OPERATOR_##x: return XCB_RENDER_PICT_OP_##y
switch (op) {
C(CLEAR, CLEAR);
C(SOURCE, SRC);
 
C(OVER, OVER);
C(IN, IN);
C(OUT, OUT);
C(ATOP, ATOP);
 
C(DEST, DST);
C(DEST_OVER, OVER_REVERSE);
C(DEST_IN, IN_REVERSE);
C(DEST_OUT, OUT_REVERSE);
C(DEST_ATOP, ATOP_REVERSE);
 
C(XOR, XOR);
C(ADD, ADD);
C(SATURATE, SATURATE);
 
/* PDF operators were added in RENDER 0.11, check if the xcb headers have
* the defines, else fall through to the default case. */
#if CAIRO_XCB_RENDER_AT_LEAST(0, 11)
#define BLEND(x,y) C(x,y)
#else
#define BLEND(x,y) case CAIRO_OPERATOR_##x:
#endif
BLEND(MULTIPLY, MULTIPLY);
BLEND(SCREEN, SCREEN);
BLEND(OVERLAY, OVERLAY);
BLEND(DARKEN, DARKEN);
BLEND(LIGHTEN, LIGHTEN);
BLEND(COLOR_DODGE, COLOR_DODGE);
BLEND(COLOR_BURN, COLOR_BURN);
BLEND(HARD_LIGHT, HARD_LIGHT);
BLEND(SOFT_LIGHT, SOFT_LIGHT);
BLEND(DIFFERENCE, DIFFERENCE);
BLEND(EXCLUSION, EXCLUSION);
BLEND(HSL_HUE, HSL_HUE);
BLEND(HSL_SATURATION, HSL_SATURATION);
BLEND(HSL_COLOR, HSL_COLOR);
BLEND(HSL_LUMINOSITY, HSL_LUMINOSITY);
 
default:
ASSERT_NOT_REACHED;
return XCB_RENDER_PICT_OP_OVER;
}
}
 
static cairo_status_t
_cairo_xcb_surface_set_clip_region (cairo_xcb_surface_t *surface,
cairo_region_t *region)
{
xcb_rectangle_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
xcb_rectangle_t *rects = stack_rects;
int i, num_rects;
 
num_rects = cairo_region_num_rectangles (region);
 
if (num_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (num_rects, sizeof (xcb_rectangle_t));
if (unlikely (rects == NULL)) {
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
}
 
for (i = 0; i < num_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;
}
 
_cairo_xcb_connection_render_set_picture_clip_rectangles (surface->connection,
surface->picture,
0, 0,
num_rects, rects);
 
if (rects != stack_rects)
free (rects);
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_xcb_surface_clear_clip_region (cairo_xcb_surface_t *surface)
{
uint32_t values[] = { XCB_NONE };
_cairo_xcb_connection_render_change_picture (surface->connection,
surface->picture,
XCB_RENDER_CP_CLIP_MASK,
values);
}
 
static void
_cairo_xcb_surface_set_precision (cairo_xcb_surface_t *surface,
cairo_antialias_t antialias)
{
cairo_xcb_connection_t *connection = surface->connection;
uint32_t precision;
 
if (connection->force_precision != -1)
precision = connection->force_precision;
else switch (antialias) {
default:
case CAIRO_ANTIALIAS_DEFAULT:
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_NONE:
case CAIRO_ANTIALIAS_FAST:
case CAIRO_ANTIALIAS_GOOD:
precision = XCB_RENDER_POLY_MODE_IMPRECISE;
break;
case CAIRO_ANTIALIAS_SUBPIXEL:
case CAIRO_ANTIALIAS_BEST:
precision = XCB_RENDER_POLY_MODE_PRECISE;
break;
}
 
if (surface->precision != precision) {
_cairo_xcb_connection_render_change_picture (connection,
surface->picture,
XCB_RENDER_CP_POLY_MODE,
&precision);
surface->precision = precision;
}
}
 
 
static void
_cairo_xcb_surface_ensure_picture (cairo_xcb_surface_t *surface)
{
assert (surface->fallback == NULL);
if (surface->picture == XCB_NONE) {
uint32_t values[1];
uint32_t flags = 0;
 
if (surface->precision != XCB_RENDER_POLY_MODE_PRECISE) {
flags |= XCB_RENDER_CP_POLY_MODE;
values[0] = surface->precision;
}
 
surface->picture = _cairo_xcb_connection_get_xid (surface->connection);
_cairo_xcb_connection_render_create_picture (surface->connection,
surface->picture,
surface->drawable,
surface->xrender_format,
flags, values);
}
}
 
static cairo_xcb_picture_t *
_picture_from_image (cairo_xcb_surface_t *target,
xcb_render_pictformat_t format,
cairo_image_surface_t *image,
cairo_xcb_shm_info_t *shm_info)
{
xcb_pixmap_t pixmap;
xcb_gcontext_t gc;
cairo_xcb_picture_t *picture;
 
pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
image->depth,
target->drawable,
image->width, image->height);
 
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, image->depth);
 
if (shm_info != NULL) {
_cairo_xcb_connection_shm_put_image (target->connection,
pixmap, gc,
image->width, image->height,
0, 0,
image->width, image->height,
0, 0,
image->depth,
shm_info->shm,
shm_info->offset);
} else {
int len;
 
/* Do we need to trim the image? */
len = CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format));
if (len == image->stride) {
_cairo_xcb_connection_put_image (target->connection,
pixmap, gc,
image->width, image->height,
0, 0,
image->depth,
image->stride,
image->data);
} else {
_cairo_xcb_connection_put_subimage (target->connection,
pixmap, gc,
0, 0,
image->width, image->height,
PIXMAN_FORMAT_BPP (image->pixman_format) / 8,
image->stride,
0, 0,
image->depth,
image->data);
 
}
}
 
_cairo_xcb_screen_put_gc (target->screen, image->depth, gc);
 
picture = _cairo_xcb_picture_create (target->screen,
image->pixman_format, format,
image->width, image->height);
if (likely (picture->base.status == CAIRO_STATUS_SUCCESS)) {
_cairo_xcb_connection_render_create_picture (target->connection,
picture->picture, pixmap, format,
0, 0);
}
 
_cairo_xcb_connection_free_pixmap (target->connection, pixmap);
 
return picture;
}
 
static cairo_bool_t
_pattern_is_supported (uint32_t flags,
const cairo_pattern_t *pattern)
 
{
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
return TRUE;
 
if (! _cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL)) {
if ((flags & CAIRO_XCB_RENDER_HAS_PICTURE_TRANSFORM) == 0)
return FALSE;
}
 
switch (pattern->extend) {
default:
ASSERT_NOT_REACHED;
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_REPEAT:
break;
case CAIRO_EXTEND_PAD:
case CAIRO_EXTEND_REFLECT:
if ((flags & CAIRO_XCB_RENDER_HAS_EXTENDED_REPEAT) == 0)
return FALSE;
}
 
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_filter_t filter;
 
filter = pattern->filter;
if (_cairo_matrix_has_unity_scale (&pattern->matrix) &&
_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
{
filter = CAIRO_FILTER_NEAREST;
}
 
if (! (filter == CAIRO_FILTER_NEAREST || filter == CAIRO_FILTER_FAST)) {
if ((flags & CAIRO_XCB_RENDER_HAS_FILTERS) == 0)
return FALSE;
}
} else { /* gradient */
if ((flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) == 0)
return FALSE;
 
/* The RENDER specification says that the inner circle has to be
* completely contained inside the outer one. */
if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL &&
! _cairo_radial_pattern_focus_is_inside ((cairo_radial_pattern_t *) pattern))
{
return FALSE;
}
}
 
return pattern->type != CAIRO_PATTERN_TYPE_MESH;
}
 
static void
_cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture,
const cairo_matrix_t *matrix,
cairo_filter_t filter,
double xc, double yc)
{
xcb_render_transform_t transform;
pixman_transform_t *pixman_transform;
cairo_int_status_t ignored;
 
/* Casting between pixman_transform_t and xcb_render_transform_t is safe
* because they happen to be the exact same type.
*/
pixman_transform = (pixman_transform_t *) &transform;
 
picture->x = picture->x0;
picture->y = picture->y0;
ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc,
pixman_transform,
&picture->x, &picture->y);
(void) ignored;
 
if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) {
_cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture),
picture->picture,
&transform);
 
picture->transform = transform;
}
}
 
static void
_cairo_xcb_picture_set_filter (cairo_xcb_picture_t *picture,
cairo_filter_t filter)
{
const char *render_filter;
int len;
 
if (picture->filter == filter)
return;
 
switch (filter) {
case CAIRO_FILTER_FAST:
render_filter = "fast";
len = strlen ("fast");
break;
 
case CAIRO_FILTER_GOOD:
render_filter = "good";
len = strlen ("good");
break;
 
case CAIRO_FILTER_BEST:
render_filter = "best";
len = strlen ("best");
break;
 
case CAIRO_FILTER_NEAREST:
render_filter = "nearest";
len = strlen ("nearest");
break;
 
case CAIRO_FILTER_BILINEAR:
render_filter = "bilinear";
len = strlen ("bilinear");
break;
 
default:
ASSERT_NOT_REACHED;
case CAIRO_FILTER_GAUSSIAN:
render_filter = "best";
len = strlen ("best");
break;
}
 
_cairo_xcb_connection_render_set_picture_filter (_picture_to_connection (picture),
picture->picture,
len, (char *) render_filter);
picture->filter = filter;
}
 
static void
_cairo_xcb_picture_set_extend (cairo_xcb_picture_t *picture,
cairo_extend_t extend)
{
uint32_t pa[1];
 
if (picture->extend == extend)
return;
 
switch (extend) {
default:
ASSERT_NOT_REACHED;
case CAIRO_EXTEND_NONE:
pa[0] = XCB_RENDER_REPEAT_NONE;
break;
 
case CAIRO_EXTEND_REPEAT:
pa[0] = XCB_RENDER_REPEAT_NORMAL;
break;
 
case CAIRO_EXTEND_REFLECT:
pa[0] = XCB_RENDER_REPEAT_REFLECT;
break;
 
case CAIRO_EXTEND_PAD:
pa[0] = XCB_RENDER_REPEAT_PAD;
break;
}
 
_cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
picture->picture,
XCB_RENDER_CP_REPEAT, pa);
picture->extend = extend;
}
 
static void
_cairo_xcb_picture_set_component_alpha (cairo_xcb_picture_t *picture,
cairo_bool_t ca)
{
uint32_t pa[1];
 
if (picture->has_component_alpha == ca)
return;
 
pa[0] = ca;
 
_cairo_xcb_connection_render_change_picture (_picture_to_connection (picture),
picture->picture,
XCB_RENDER_CP_COMPONENT_ALPHA,
pa);
picture->has_component_alpha = ca;
}
 
static cairo_xcb_picture_t *
_solid_picture (cairo_xcb_surface_t *target,
const cairo_color_t *color)
{
xcb_render_color_t xcb_color;
xcb_render_pictformat_t xrender_format;
cairo_xcb_picture_t *picture;
 
xcb_color.red = color->red_short;
xcb_color.green = color->green_short;
xcb_color.blue = color->blue_short;
xcb_color.alpha = color->alpha_short;
 
xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
picture = _cairo_xcb_picture_create (target->screen,
PIXMAN_a8r8g8b8,
xrender_format,
-1, -1);
if (unlikely (picture->base.status))
return picture;
 
if (target->connection->flags & CAIRO_XCB_RENDER_HAS_GRADIENTS) {
_cairo_xcb_connection_render_create_solid_fill (target->connection,
picture->picture,
xcb_color);
} else {
xcb_pixmap_t pixmap;
uint32_t values[] = { XCB_RENDER_REPEAT_NORMAL };
 
pixmap = _cairo_xcb_connection_create_pixmap (target->connection,
32, target->drawable, 1, 1);
_cairo_xcb_connection_render_create_picture (target->connection,
picture->picture,
pixmap,
xrender_format,
XCB_RENDER_CP_REPEAT,
values);
if (target->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
xcb_rectangle_t rect;
 
rect.x = rect.y = 0;
rect.width = rect.height = 1;
 
_cairo_xcb_connection_render_fill_rectangles (_picture_to_connection (picture),
XCB_RENDER_PICT_OP_SRC,
picture->picture,
xcb_color, 1, &rect);
} else {
xcb_gcontext_t gc;
uint32_t pixel;
 
gc = _cairo_xcb_screen_get_gc (target->screen, pixmap, 32);
 
/* XXX byte ordering? */
pixel = ((color->alpha_short >> 8) << 24) |
((color->red_short >> 8) << 16) |
((color->green_short >> 8) << 8) |
((color->blue_short >> 8) << 0);
 
_cairo_xcb_connection_put_image (target->connection,
pixmap, gc,
1, 1, 0, 0,
32, 4, &pixel);
 
_cairo_xcb_screen_put_gc (target->screen, 32, gc);
}
 
_cairo_xcb_connection_free_pixmap (target->connection, pixmap);
}
 
return picture;
}
 
static cairo_xcb_picture_t *
_cairo_xcb_transparent_picture (cairo_xcb_surface_t *target)
{
cairo_xcb_picture_t *picture;
 
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT];
if (picture == NULL) {
picture = _solid_picture (target, CAIRO_COLOR_TRANSPARENT);
target->screen->stock_colors[CAIRO_STOCK_TRANSPARENT] = &picture->base;
}
 
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
}
 
static cairo_xcb_picture_t *
_cairo_xcb_black_picture (cairo_xcb_surface_t *target)
{
cairo_xcb_picture_t *picture;
 
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_BLACK];
if (picture == NULL) {
picture = _solid_picture (target, CAIRO_COLOR_BLACK);
target->screen->stock_colors[CAIRO_STOCK_BLACK] = &picture->base;
}
 
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
}
 
static cairo_xcb_picture_t *
_cairo_xcb_white_picture (cairo_xcb_surface_t *target)
{
cairo_xcb_picture_t *picture;
 
picture = (cairo_xcb_picture_t *) target->screen->stock_colors[CAIRO_STOCK_WHITE];
if (picture == NULL) {
picture = _solid_picture (target, CAIRO_COLOR_WHITE);
target->screen->stock_colors[CAIRO_STOCK_WHITE] = &picture->base;
}
 
return (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
}
 
static cairo_xcb_picture_t *
_cairo_xcb_solid_picture (cairo_xcb_surface_t *target,
const cairo_solid_pattern_t *pattern)
{
cairo_xcb_picture_t *picture;
cairo_xcb_screen_t *screen;
int i, n_cached;
 
if (pattern->color.alpha_short <= 0x00ff)
return _cairo_xcb_transparent_picture (target);
 
if (pattern->color.alpha_short >= 0xff00) {
if (pattern->color.red_short <= 0x00ff &&
pattern->color.green_short <= 0x00ff &&
pattern->color.blue_short <= 0x00ff)
{
return _cairo_xcb_black_picture (target);
}
 
if (pattern->color.red_short >= 0xff00 &&
pattern->color.green_short >= 0xff00 &&
pattern->color.blue_short >= 0xff00)
{
return _cairo_xcb_white_picture (target);
}
}
 
screen = target->screen;
n_cached = screen->solid_cache_size;
for (i = 0; i < n_cached; i++) {
if (_cairo_color_equal (&screen->solid_cache[i].color, &pattern->color)) {
return (cairo_xcb_picture_t *) cairo_surface_reference (screen->solid_cache[i].picture);
}
}
 
picture = _solid_picture (target, &pattern->color);
if (unlikely (picture->base.status))
return picture;
 
if (screen->solid_cache_size < ARRAY_LENGTH (screen->solid_cache)) {
i = screen->solid_cache_size++;
} else {
i = hars_petruska_f54_1_random () % ARRAY_LENGTH (screen->solid_cache);
cairo_surface_destroy (screen->solid_cache[i].picture);
}
screen->solid_cache[i].picture = cairo_surface_reference (&picture->base);
screen->solid_cache[i].color = pattern->color;
 
return picture;
}
 
static cairo_xcb_picture_t *
_render_to_picture (cairo_xcb_surface_t *target,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
cairo_image_surface_t *image;
cairo_xcb_shm_info_t *shm_info;
cairo_pattern_union_t copy;
cairo_status_t status;
cairo_xcb_picture_t *picture;
pixman_format_code_t pixman_format;
xcb_render_pictformat_t xrender_format;
 
/* XXX handle extend modes via tiling? */
/* XXX alpha-only masks? */
 
pixman_format = PIXMAN_a8r8g8b8;
xrender_format = target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32];
 
status = _cairo_xcb_shm_image_create (target->screen->connection,
pixman_format,
extents->width, extents->height,
&image, &shm_info);
if (unlikely (status))
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
 
_cairo_pattern_init_static_copy (&copy.base, pattern);
cairo_matrix_translate (&copy.base.matrix, extents->x, extents->y);
status = _cairo_surface_paint (&image->base,
CAIRO_OPERATOR_SOURCE,
&copy.base,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
}
 
picture = _picture_from_image (target, xrender_format, image, shm_info);
cairo_surface_destroy (&image->base);
 
if (unlikely (picture->base.status))
return picture;
 
_cairo_xcb_picture_set_component_alpha (picture, pattern->has_component_alpha);
picture->x = -extents->x;
picture->y = -extents->y;
 
return picture;
}
 
static xcb_render_fixed_t *
_gradient_to_xcb (const cairo_gradient_pattern_t *gradient,
unsigned int *n_stops,
char *buf, unsigned int buflen)
{
xcb_render_fixed_t *stops;
xcb_render_color_t *colors;
unsigned int i;
 
assert (gradient->n_stops > 0);
*n_stops = MAX (gradient->n_stops, 2);
 
if (*n_stops * (sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t)) < buflen)
{
stops = (xcb_render_fixed_t *) buf;
}
else
{
stops =
_cairo_malloc_ab (*n_stops,
sizeof (xcb_render_fixed_t) + sizeof (xcb_render_color_t));
if (unlikely (stops == NULL))
return NULL;
}
 
colors = (xcb_render_color_t *) (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;
}
 
return stops;
}
 
static cairo_xcb_picture_t *
_cairo_xcb_linear_picture (cairo_xcb_surface_t *target,
const cairo_linear_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
char buf[CAIRO_STACK_BUFFER_SIZE];
xcb_render_fixed_t *stops;
xcb_render_color_t *colors;
xcb_render_pointfix_t p1, p2;
cairo_matrix_t matrix;
cairo_circle_double_t extremes[2];
cairo_xcb_picture_t *picture;
cairo_status_t status;
unsigned int n_stops;
 
_cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
 
picture = (cairo_xcb_picture_t *)
_cairo_xcb_screen_lookup_linear_picture (target->screen, pattern);
if (picture != NULL)
goto setup_picture;
 
stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
if (unlikely (stops == NULL))
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
picture = _cairo_xcb_picture_create (target->screen,
target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
PIXMAN_a8r8g8b8,
-1, -1);
if (unlikely (picture->base.status)) {
if (stops != (xcb_render_fixed_t *) buf)
free (stops);
return picture;
}
picture->filter = CAIRO_FILTER_DEFAULT;
 
colors = (xcb_render_color_t *) (stops + n_stops);
 
p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
 
_cairo_xcb_connection_render_create_linear_gradient (target->connection,
picture->picture,
p1, p2,
n_stops,
stops, colors);
 
if (stops != (xcb_render_fixed_t *) buf)
free (stops);
 
status = _cairo_xcb_screen_store_linear_picture (target->screen,
pattern,
&picture->base);
if (unlikely (status)) {
cairo_surface_destroy (&picture->base);
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
}
 
setup_picture:
_cairo_xcb_picture_set_matrix (picture, &matrix,
pattern->base.base.filter,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
_cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
_cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
_cairo_xcb_picture_set_component_alpha (picture,
pattern->base.base.has_component_alpha);
 
return picture;
}
 
static cairo_xcb_picture_t *
_cairo_xcb_radial_picture (cairo_xcb_surface_t *target,
const cairo_radial_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
char buf[CAIRO_STACK_BUFFER_SIZE];
xcb_render_fixed_t *stops;
xcb_render_color_t *colors;
xcb_render_pointfix_t p1, p2;
xcb_render_fixed_t r1, r2;
cairo_matrix_t matrix;
cairo_circle_double_t extremes[2];
cairo_xcb_picture_t *picture;
cairo_status_t status;
unsigned int n_stops;
 
_cairo_gradient_pattern_fit_to_range (&pattern->base, PIXMAN_MAX_INT >> 1, &matrix, extremes);
 
picture = (cairo_xcb_picture_t *)
_cairo_xcb_screen_lookup_radial_picture (target->screen, pattern);
if (picture != NULL)
goto setup_picture;
 
stops = _gradient_to_xcb (&pattern->base, &n_stops, buf, sizeof (buf));
if (unlikely (stops == NULL))
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
picture = _cairo_xcb_picture_create (target->screen,
target->screen->connection->standard_formats[CAIRO_FORMAT_ARGB32],
PIXMAN_a8r8g8b8,
-1, -1);
if (unlikely (picture->base.status)) {
if (stops != (xcb_render_fixed_t *) buf)
free (stops);
return picture;
}
picture->filter = CAIRO_FILTER_DEFAULT;
 
colors = (xcb_render_color_t *) (stops + n_stops);
 
p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
 
r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
 
_cairo_xcb_connection_render_create_radial_gradient (target->connection,
picture->picture,
p1, p2, r1, r2,
n_stops,
stops, colors);
 
if (stops != (xcb_render_fixed_t *) buf)
free (stops);
 
status = _cairo_xcb_screen_store_radial_picture (target->screen,
pattern,
&picture->base);
if (unlikely (status)) {
cairo_surface_destroy (&picture->base);
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
}
 
setup_picture:
_cairo_xcb_picture_set_matrix (picture, &matrix,
pattern->base.base.filter,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
_cairo_xcb_picture_set_filter (picture, pattern->base.base.filter);
_cairo_xcb_picture_set_extend (picture, pattern->base.base.extend);
_cairo_xcb_picture_set_component_alpha (picture,
pattern->base.base.has_component_alpha);
 
return picture;
}
 
static cairo_xcb_picture_t *
_copy_to_picture (cairo_xcb_surface_t *source)
{
cairo_xcb_picture_t *picture;
uint32_t values[] = { 0, 1 };
 
if (source->deferred_clear) {
cairo_status_t status = _cairo_xcb_surface_clear (source);
if (unlikely (status))
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
}
 
picture = _cairo_xcb_picture_create (source->screen,
source->xrender_format,
source->pixman_format,
source->width,
source->height);
if (unlikely (picture->base.status))
return picture;
 
_cairo_xcb_connection_render_create_picture (source->connection,
picture->picture,
source->drawable,
source->xrender_format,
XCB_RENDER_CP_GRAPHICS_EXPOSURE |
XCB_RENDER_CP_SUBWINDOW_MODE,
values);
 
return picture;
}
 
static void
_cairo_xcb_surface_setup_surface_picture(cairo_xcb_picture_t *picture,
const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
cairo_filter_t filter;
 
filter = pattern->base.filter;
if (filter != CAIRO_FILTER_NEAREST &&
_cairo_matrix_has_unity_scale (&pattern->base.matrix) &&
_cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.x0)) &&
_cairo_fixed_is_integer (_cairo_fixed_from_double (pattern->base.matrix.y0)))
{
filter = CAIRO_FILTER_NEAREST;
}
_cairo_xcb_picture_set_filter (picture, filter);
 
_cairo_xcb_picture_set_matrix (picture,
&pattern->base.matrix, filter,
extents->x + extents->width/2.,
extents->y + extents->height/2.);
 
 
_cairo_xcb_picture_set_extend (picture, pattern->base.extend);
_cairo_xcb_picture_set_component_alpha (picture, pattern->base.has_component_alpha);
}
 
static cairo_xcb_picture_t *
record_to_picture (cairo_surface_t *target,
const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
cairo_surface_pattern_t tmp_pattern;
cairo_xcb_picture_t *picture;
cairo_status_t status;
cairo_matrix_t matrix;
cairo_surface_t *tmp;
cairo_surface_t *source;
cairo_rectangle_int_t limit;
cairo_extend_t extend;
 
/* XXX: The following was once more or less copied from cairo-xlibs-ource.c,
* record_source() and recording_pattern_get_surface(), can we share a
* single version?
*/
 
/* First get the 'real' recording surface and figure out the size for tmp */
source = _cairo_pattern_get_source (pattern, &limit);
assert (_cairo_surface_is_recording (source));
 
if (! _cairo_matrix_is_identity (&pattern->base.matrix)) {
double x1, y1, x2, y2;
 
matrix = pattern->base.matrix;
status = cairo_matrix_invert (&matrix);
assert (status == CAIRO_STATUS_SUCCESS);
 
x1 = limit.x;
y1 = limit.y;
x2 = limit.x + limit.width;
y2 = limit.y + limit.height;
 
_cairo_matrix_transform_bounding_box (&matrix,
&x1, &y1, &x2, &y2, NULL);
 
limit.x = floor (x1);
limit.y = floor (y1);
limit.width = ceil (x2) - limit.x;
limit.height = ceil (y2) - limit.y;
}
extend = pattern->base.extend;
if (_cairo_rectangle_contains_rectangle (&limit, extents))
extend = CAIRO_EXTEND_NONE;
if (extend == CAIRO_EXTEND_NONE && ! _cairo_rectangle_intersect (&limit, extents))
return _cairo_xcb_transparent_picture ((cairo_xcb_surface_t *) target);
 
/* Now draw the recording surface to an xcb surface */
tmp = _cairo_surface_create_similar_solid (target,
source->content,
limit.width,
limit.height,
CAIRO_COLOR_TRANSPARENT);
if (tmp->status != CAIRO_STATUS_SUCCESS) {
return (cairo_xcb_picture_t *) tmp;
}
 
cairo_matrix_init_translate (&matrix, limit.x, limit.y);
cairo_matrix_multiply (&matrix, &matrix, &pattern->base.matrix);
 
status = _cairo_recording_surface_replay_with_clip (source,
&matrix, tmp,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (tmp);
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
}
 
/* Now that we have drawn this to an xcb surface, try again with that */
_cairo_pattern_init_static_copy (&tmp_pattern.base, &pattern->base);
tmp_pattern.surface = tmp;
cairo_matrix_init_translate (&tmp_pattern.base.matrix, -limit.x, -limit.y);
 
picture = _copy_to_picture ((cairo_xcb_surface_t *) tmp);
if (picture->base.status == CAIRO_STATUS_SUCCESS)
_cairo_xcb_surface_setup_surface_picture (picture, &tmp_pattern, extents);
cairo_surface_destroy (tmp);
return picture;
}
 
static cairo_xcb_picture_t *
_cairo_xcb_surface_picture (cairo_xcb_surface_t *target,
const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
cairo_surface_t *source = pattern->surface;
cairo_xcb_picture_t *picture;
 
picture = (cairo_xcb_picture_t *)
_cairo_surface_has_snapshot (source, &_cairo_xcb_picture_backend);
if (picture != NULL) {
if (picture->screen == target->screen) {
picture = (cairo_xcb_picture_t *) cairo_surface_reference (&picture->base);
_cairo_xcb_surface_setup_surface_picture (picture, pattern, extents);
return picture;
}
picture = NULL;
}
 
if (source->type == CAIRO_SURFACE_TYPE_XCB)
{
if (source->backend->type == CAIRO_SURFACE_TYPE_XCB) {
cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) source;
if (xcb->screen == target->screen && xcb->fallback == NULL) {
picture = _copy_to_picture ((cairo_xcb_surface_t *) source);
if (unlikely (picture->base.status))
return picture;
}
} else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) sub->target;
 
/* XXX repeat interval with source clipping? */
if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) {
xcb_rectangle_t rect;
 
picture = _copy_to_picture (xcb);
if (unlikely (picture->base.status))
return picture;
 
rect.x = sub->extents.x;
rect.y = sub->extents.y;
rect.width = sub->extents.width;
rect.height = sub->extents.height;
 
_cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
picture->picture,
0, 0,
1, &rect);
picture->x0 = rect.x;
picture->y0 = rect.y;
picture->width = rect.width;
picture->height = rect.height;
}
} else if (_cairo_surface_is_snapshot (source)) {
cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
cairo_xcb_surface_t *xcb = (cairo_xcb_surface_t *) snap->target;
 
if (xcb->screen == target->screen && xcb->fallback == NULL) {
picture = _copy_to_picture (xcb);
if (unlikely (picture->base.status))
return picture;
}
}
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
else if (source->type == CAIRO_SURFACE_TYPE_XLIB)
{
if (source->backend->type == CAIRO_SURFACE_TYPE_XLIB) {
cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) source)->xcb;
if (xcb->screen == target->screen && xcb->fallback == NULL) {
picture = _copy_to_picture (xcb);
if (unlikely (picture->base.status))
return picture;
}
} else if (source->backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) {
cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) source;
cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) sub->target)->xcb;
 
if (FALSE && xcb->screen == target->screen && xcb->fallback == NULL) {
xcb_rectangle_t rect;
 
picture = _copy_to_picture (xcb);
if (unlikely (picture->base.status))
return picture;
 
rect.x = sub->extents.x;
rect.y = sub->extents.y;
rect.width = sub->extents.width;
rect.height = sub->extents.height;
 
_cairo_xcb_connection_render_set_picture_clip_rectangles (xcb->connection,
picture->picture,
0, 0,
1, &rect);
picture->x0 = rect.x;
picture->y0 = rect.y;
picture->width = rect.width;
picture->height = rect.height;
}
} else if (_cairo_surface_is_snapshot (source)) {
cairo_surface_snapshot_t *snap = (cairo_surface_snapshot_t *) source;
cairo_xcb_surface_t *xcb = ((cairo_xlib_xcb_surface_t *) snap->target)->xcb;
 
if (xcb->screen == target->screen && xcb->fallback == NULL) {
picture = _copy_to_picture (xcb);
if (unlikely (picture->base.status))
return picture;
}
}
}
#endif
#if CAIRO_HAS_GL_FUNCTIONS
else if (source->type == CAIRO_SURFACE_TYPE_GL)
{
/* pixmap from texture */
}
#endif
else if (source->type == CAIRO_SURFACE_TYPE_RECORDING)
{
/* We have to skip the call to attach_snapshot() because we possibly
* only drew part of the recording surface.
* TODO: When can we safely attach a snapshot?
*/
return record_to_picture(&target->base, pattern, extents);
}
 
if (picture == NULL) {
cairo_image_surface_t *image;
void *image_extra;
cairo_status_t status;
 
status = _cairo_surface_acquire_source_image (source, &image, &image_extra);
if (unlikely (status))
return (cairo_xcb_picture_t *) _cairo_surface_create_in_error (status);
 
if (image->format != CAIRO_FORMAT_INVALID) {
xcb_render_pictformat_t format;
 
format = target->screen->connection->standard_formats[image->format];
 
picture = _picture_from_image (target, format, image, NULL);
_cairo_surface_release_source_image (source, image, image_extra);
} else {
cairo_image_surface_t *conv;
xcb_render_pictformat_t render_format;
 
/* XXX XRenderPutImage! */
 
conv = _cairo_image_surface_coerce (image);
_cairo_surface_release_source_image (source, image, image_extra);
if (unlikely (conv->base.status))
return (cairo_xcb_picture_t *) conv;
 
render_format = target->screen->connection->standard_formats[conv->format];
picture = _picture_from_image (target, render_format, conv, NULL);
cairo_surface_destroy (&conv->base);
}
 
if (unlikely (picture->base.status))
return picture;
}
 
_cairo_surface_attach_snapshot (source,
&picture->base,
NULL);
 
_cairo_xcb_surface_setup_surface_picture (picture, pattern, extents);
return picture;
}
 
static cairo_xcb_picture_t *
_cairo_xcb_picture_for_pattern (cairo_xcb_surface_t *target,
const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents)
{
if (pattern == NULL)
return _cairo_xcb_white_picture (target);
 
if (! _pattern_is_supported (target->connection->flags, pattern))
return _render_to_picture (target, pattern, extents);
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_xcb_solid_picture (target, (cairo_solid_pattern_t *) pattern);
 
case CAIRO_PATTERN_TYPE_LINEAR:
return _cairo_xcb_linear_picture (target,
(cairo_linear_pattern_t *) pattern,
extents);
 
case CAIRO_PATTERN_TYPE_RADIAL:
return _cairo_xcb_radial_picture (target,
(cairo_radial_pattern_t *) pattern,
extents);
 
case CAIRO_PATTERN_TYPE_SURFACE:
return _cairo_xcb_surface_picture (target,
(cairo_surface_pattern_t *) pattern,
extents);
default:
ASSERT_NOT_REACHED;
case CAIRO_PATTERN_TYPE_MESH:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _render_to_picture (target, pattern, extents);
}
}
 
COMPILE_TIME_ASSERT (sizeof (xcb_rectangle_t) <= sizeof (cairo_box_t));
 
static cairo_status_t
_render_fill_boxes (void *abstract_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_xcb_surface_t *dst = abstract_dst;
xcb_rectangle_t stack_xrects[CAIRO_STACK_ARRAY_LENGTH (sizeof (xcb_rectangle_t))];
xcb_rectangle_t *xrects = stack_xrects;
xcb_render_color_t render_color;
int render_op = _render_operator (op);
struct _cairo_boxes_chunk *chunk;
int max_count;
 
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
 
max_count = 0;
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
if (chunk->count > max_count)
max_count = chunk->count;
}
if (max_count > ARRAY_LENGTH (stack_xrects)) {
xrects = _cairo_malloc_ab (max_count, sizeof (xcb_rectangle_t));
if (unlikely (xrects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
int i, j;
 
for (i = j = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_round_down (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_round_down (chunk->base[i].p2.y);
 
if (x2 > x1 && y2 > y1) {
xrects[j].x = x1;
xrects[j].y = y1;
xrects[j].width = x2 - x1;
xrects[j].height = y2 - y1;
j++;
}
}
 
if (j) {
_cairo_xcb_connection_render_fill_rectangles
(dst->connection,
render_op, dst->picture,
render_color, j, xrects);
}
}
 
if (xrects != stack_xrects)
free (xrects);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* pixel aligned, non-overlapping boxes */
static cairo_int_status_t
_render_composite_boxes (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
const cairo_pattern_t *mask_pattern,
const cairo_rectangle_int_t *extents,
const cairo_boxes_t *boxes)
{
cairo_xcb_picture_t *src, *mask;
const struct _cairo_boxes_chunk *chunk;
xcb_rectangle_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (xcb_rectangle_t)];
xcb_rectangle_t *clip_boxes;
cairo_rectangle_int_t stack_extents;
cairo_status_t status;
int num_boxes;
int render_op;
 
render_op = _render_operator (op);
 
if (src_pattern == NULL) {
src_pattern = mask_pattern;
mask_pattern = NULL;
}
 
/* amalgamate into a single Composite call by setting a clip region */
clip_boxes = stack_boxes;
if (boxes->num_boxes > ARRAY_LENGTH (stack_boxes)) {
clip_boxes = _cairo_malloc_ab (boxes->num_boxes, sizeof (xcb_rectangle_t));
if (unlikely (clip_boxes == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
status = src->base.status;
if (unlikely (status))
goto cleanup_boxes;
 
num_boxes = 0;
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
const cairo_box_t *box = chunk->base;
int i;
 
for (i = 0; i < chunk->count; i++) {
int x = _cairo_fixed_integer_round_down (box[i].p1.x);
int y = _cairo_fixed_integer_round_down (box[i].p1.y);
int width = _cairo_fixed_integer_round_down (box[i].p2.x) - x;
int height = _cairo_fixed_integer_round_down (box[i].p2.y) - y;
 
if (width && height) {
clip_boxes[num_boxes].x = x;
clip_boxes[num_boxes].y = y;
clip_boxes[num_boxes].width = width;
clip_boxes[num_boxes].height = height;
num_boxes++;
}
}
}
 
if (num_boxes) {
if (num_boxes > 1) {
_cairo_xcb_connection_render_set_picture_clip_rectangles (dst->connection,
dst->picture,
0, 0,
num_boxes,
clip_boxes);
} else {
stack_extents.x = clip_boxes[0].x;
stack_extents.y = clip_boxes[0].y;
stack_extents.width = clip_boxes[0].width;
stack_extents.height = clip_boxes[0].height;
extents = &stack_extents;
}
 
if (mask_pattern != NULL) {
mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
status = mask->base.status;
if (unlikely (status))
goto cleanup_clip;
 
_cairo_xcb_connection_render_composite (dst->connection,
render_op,
src->picture,
mask->picture,
dst->picture,
src->x + extents->x, src->y + extents->y,
mask->x + extents->x, mask->y + extents->y,
extents->x, extents->y,
extents->width, extents->height);
 
cairo_surface_destroy (&mask->base);
} else {
_cairo_xcb_connection_render_composite (dst->connection,
render_op,
src->picture,
XCB_NONE,
dst->picture,
src->x + extents->x, src->y + extents->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
}
 
cleanup_clip:
 
if (num_boxes > 1)
_cairo_xcb_surface_clear_clip_region (dst);
}
 
cairo_surface_destroy (&src->base);
 
cleanup_boxes:
 
if (clip_boxes != stack_boxes)
free (clip_boxes);
 
return status;
}
 
 
#define CAIRO_FIXED_16_16_MIN _cairo_fixed_from_int (-32768)
#define CAIRO_FIXED_16_16_MAX _cairo_fixed_from_int (32767)
 
static cairo_bool_t
_line_exceeds_16_16 (const cairo_line_t *line)
{
return
line->p1.x <= CAIRO_FIXED_16_16_MIN ||
line->p1.x >= CAIRO_FIXED_16_16_MAX ||
 
line->p2.x <= CAIRO_FIXED_16_16_MIN ||
line->p2.x >= CAIRO_FIXED_16_16_MAX ||
 
line->p1.y <= CAIRO_FIXED_16_16_MIN ||
line->p1.y >= CAIRO_FIXED_16_16_MAX ||
 
line->p2.y <= CAIRO_FIXED_16_16_MIN ||
line->p2.y >= 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,
xcb_render_linefix_t *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));
}
 
typedef struct {
cairo_traps_t traps;
cairo_antialias_t antialias;
} composite_traps_info_t;
 
COMPILE_TIME_ASSERT (sizeof (xcb_render_trapezoid_t) <= sizeof (cairo_trapezoid_t));
 
static cairo_int_status_t
_composite_traps (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x, int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
composite_traps_info_t *info = closure;
const cairo_traps_t *traps = &info->traps;
cairo_xcb_picture_t *src;
cairo_format_t format;
xcb_render_pictformat_t xrender_format;
xcb_render_trapezoid_t *xtraps;
int render_reference_x, render_reference_y;
cairo_status_t status;
int i;
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
if (unlikely (src->base.status))
return src->base.status;
 
if (info->antialias == CAIRO_ANTIALIAS_NONE)
format = CAIRO_FORMAT_A1;
else
format = CAIRO_FORMAT_A8;
xrender_format = dst->screen->connection->standard_formats[format];
 
xtraps = (xcb_render_trapezoid_t *) traps->traps;
for (i = 0; i < traps->num_traps; i++) {
cairo_trapezoid_t t = traps->traps[i];
 
/* top/bottom will be clamped to surface bounds */
xtraps[i].top = _cairo_fixed_to_16_16 (t.top);
xtraps[i].top -= dst_y << 16;
xtraps[i].bottom = _cairo_fixed_to_16_16 (t.bottom);
xtraps[i].bottom -= dst_y << 16;
 
/* However, all the other coordinates will have been left untouched so
* as not to introduce numerical error. Recompute them if they
* exceed the 16.16 limits.
*/
if (unlikely (_line_exceeds_16_16 (&t.left))) {
_project_line_x_onto_16_16 (&t.left,
t.top,
t.bottom,
&xtraps[i].left);
xtraps[i].left.p1.y = xtraps[i].top;
xtraps[i].left.p2.y = xtraps[i].bottom;
} else {
xtraps[i].left.p1.x = _cairo_fixed_to_16_16 (t.left.p1.x);
xtraps[i].left.p1.y = _cairo_fixed_to_16_16 (t.left.p1.y);
xtraps[i].left.p2.x = _cairo_fixed_to_16_16 (t.left.p2.x);
xtraps[i].left.p2.y = _cairo_fixed_to_16_16 (t.left.p2.y);
}
xtraps[i].left.p1.x -= dst_x << 16;
xtraps[i].left.p1.y -= dst_y << 16;
xtraps[i].left.p2.x -= dst_x << 16;
xtraps[i].left.p2.y -= dst_y << 16;
 
if (unlikely (_line_exceeds_16_16 (&t.right))) {
_project_line_x_onto_16_16 (&t.right,
t.top,
t.bottom,
&xtraps[i].right);
xtraps[i].right.p1.y = xtraps[i].top;
xtraps[i].right.p2.y = xtraps[i].bottom;
} else {
xtraps[i].right.p1.x = _cairo_fixed_to_16_16 (t.right.p1.x);
xtraps[i].right.p1.y = _cairo_fixed_to_16_16 (t.right.p1.y);
xtraps[i].right.p2.x = _cairo_fixed_to_16_16 (t.right.p2.x);
xtraps[i].right.p2.y = _cairo_fixed_to_16_16 (t.right.p2.y);
}
xtraps[i].right.p1.x -= dst_x << 16;
xtraps[i].right.p1.y -= dst_y << 16;
xtraps[i].right.p2.x -= dst_x << 16;
xtraps[i].right.p2.y -= dst_y << 16;
}
 
if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
render_reference_x = xtraps[0].left.p1.x >> 16;
render_reference_y = xtraps[0].left.p1.y >> 16;
} else {
render_reference_x = xtraps[0].left.p2.x >> 16;
render_reference_y = xtraps[0].left.p2.y >> 16;
}
render_reference_x += src->x + dst_x;
render_reference_y += src->y + dst_y;
 
_cairo_xcb_surface_set_precision (dst, info->antialias);
_cairo_xcb_connection_render_trapezoids (dst->connection,
_render_operator (op),
src->picture,
dst->picture,
xrender_format,
render_reference_x,
render_reference_y,
traps->num_traps, xtraps);
 
cairo_surface_destroy (&src->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* low-level composite driver */
 
static cairo_xcb_surface_t *
get_clip_surface (const cairo_clip_t *clip,
cairo_xcb_surface_t *target,
int *tx, int *ty)
{
cairo_surface_t *surface;
cairo_status_t status;
 
surface = _cairo_surface_create_similar_solid (&target->base,
CAIRO_CONTENT_ALPHA,
clip->extents.width,
clip->extents.height,
CAIRO_COLOR_WHITE);
if (unlikely (surface->status))
return (cairo_xcb_surface_t *) surface;
 
assert (surface->backend == &_cairo_xcb_surface_backend);
status = _cairo_clip_combine_with_surface (clip, surface,
clip->extents.x, clip->extents.y);
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
 
*tx = clip->extents.x;
*ty = clip->extents.y;
 
return (cairo_xcb_surface_t *) surface;
}
 
typedef cairo_int_status_t
(*xcb_draw_func_t) (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip);
 
static void do_unaligned_row(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b,
int tx, int y, int h,
uint16_t coverage)
{
int x1 = _cairo_fixed_integer_part (b->p1.x) - tx;
int x2 = _cairo_fixed_integer_part (b->p2.x) - tx;
if (x2 > x1) {
if (! _cairo_fixed_is_integer (b->p1.x)) {
blt(closure, x1, y, 1, h,
coverage * (256 - _cairo_fixed_fractional_part (b->p1.x)));
x1++;
}
 
if (x2 > x1)
blt(closure, x1, y, x2-x1, h, (coverage << 8) - (coverage >> 8));
 
if (! _cairo_fixed_is_integer (b->p2.x))
blt(closure, x2, y, 1, h,
coverage * _cairo_fixed_fractional_part (b->p2.x));
} else
blt(closure, x1, y, 1, h,
coverage * (b->p2.x - b->p1.x));
}
 
static void do_unaligned_box(void (*blt)(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage),
void *closure,
const cairo_box_t *b, int tx, int ty)
{
int y1 = _cairo_fixed_integer_part (b->p1.y) - ty;
int y2 = _cairo_fixed_integer_part (b->p2.y) - ty;
if (y2 > y1) {
if (! _cairo_fixed_is_integer (b->p1.y)) {
do_unaligned_row(blt, closure, b, tx, y1, 1,
256 - _cairo_fixed_fractional_part (b->p1.y));
y1++;
}
 
if (y2 > y1)
do_unaligned_row(blt, closure, b, tx, y1, y2-y1, 256);
 
if (! _cairo_fixed_is_integer (b->p2.y))
do_unaligned_row(blt, closure, b, tx, y2, 1,
_cairo_fixed_fractional_part (b->p2.y));
} else
do_unaligned_row(blt, closure, b, tx, y1, 1,
b->p2.y - b->p1.y);
}
 
 
static void blt_in(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
cairo_xcb_surface_t *mask = closure;
xcb_render_color_t color;
xcb_rectangle_t rect;
 
if (coverage == 0xffff)
return;
 
color.red = color.green = color.blue = 0;
color.alpha = coverage;
 
rect.x = x;
rect.y = y;
rect.width = w;
rect.height = h;
 
_cairo_xcb_connection_render_fill_rectangles (mask->connection,
XCB_RENDER_PICT_OP_IN,
mask->picture,
color, 1, &rect);
}
 
static cairo_xcb_surface_t *
_create_composite_mask (cairo_clip_t *clip,
xcb_draw_func_t draw_func,
xcb_draw_func_t mask_func,
void *draw_closure,
cairo_xcb_surface_t *dst,
const cairo_rectangle_int_t*extents)
{
cairo_xcb_surface_t *surface;
cairo_bool_t need_clip_combine;
cairo_int_status_t status;
 
surface = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_similar (dst, CAIRO_CONTENT_ALPHA,
extents->width, extents->height);
if (unlikely (surface->base.status))
return surface;
 
_cairo_xcb_surface_ensure_picture (surface);
 
surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
surface->deferred_clear = TRUE;
surface->base.is_clear = TRUE;
 
if (mask_func) {
status = mask_func (draw_closure, surface,
CAIRO_OPERATOR_ADD, NULL,
extents->x, extents->y,
extents, clip);
if (likely (status != CAIRO_INT_STATUS_UNSUPPORTED))
return surface;
}
 
/* Is it worth setting the clip region here? */
status = draw_func (draw_closure, surface,
CAIRO_OPERATOR_ADD, NULL,
extents->x, extents->y,
extents, NULL);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
}
 
if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
int i;
 
for (i = 0; i < clip->num_boxes; i++) {
cairo_box_t *b = &clip->boxes[i];
 
if (! _cairo_fixed_is_integer (b->p1.x) ||
! _cairo_fixed_is_integer (b->p1.y) ||
! _cairo_fixed_is_integer (b->p2.x) ||
! _cairo_fixed_is_integer (b->p2.y))
{
do_unaligned_box(blt_in, surface, b, extents->x, extents->y);
}
}
 
need_clip_combine = clip->path != NULL;
} else
need_clip_combine = ! _cairo_clip_is_region (clip);
 
if (need_clip_combine) {
status = _cairo_clip_combine_with_surface (clip, &surface->base,
extents->x, extents->y);
if (unlikely (status)) {
cairo_surface_destroy (&surface->base);
return (cairo_xcb_surface_t *) _cairo_surface_create_in_error (status);
}
}
 
return surface;
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
_clip_and_composite_with_mask (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *pattern,
xcb_draw_func_t draw_func,
xcb_draw_func_t mask_func,
void *draw_closure,
cairo_xcb_surface_t *dst,
const cairo_rectangle_int_t*extents)
{
cairo_xcb_surface_t *mask;
cairo_xcb_picture_t *src;
 
mask = _create_composite_mask (clip,
draw_func, mask_func, draw_closure,
dst, extents);
if (unlikely (mask->base.status))
return mask->base.status;
 
if (pattern != NULL || dst->base.content != CAIRO_CONTENT_ALPHA) {
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
if (unlikely (src->base.status)) {
cairo_surface_destroy (&mask->base);
return src->base.status;
}
 
_cairo_xcb_connection_render_composite (dst->connection,
_render_operator (op),
src->picture,
mask->picture,
dst->picture,
extents->x + src->x, extents->y + src->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
 
cairo_surface_destroy (&src->base);
} else {
_cairo_xcb_connection_render_composite (dst->connection,
_render_operator (op),
mask->picture,
XCB_NONE,
dst->picture,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
}
cairo_surface_destroy (&mask->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
_clip_and_composite_combine (cairo_clip_t *clip,
cairo_operator_t op,
const cairo_pattern_t *pattern,
xcb_draw_func_t draw_func,
void *draw_closure,
cairo_xcb_surface_t *dst,
const cairo_rectangle_int_t*extents)
{
cairo_xcb_surface_t *tmp;
cairo_xcb_surface_t *clip_surface;
int clip_x = 0, clip_y = 0;
xcb_render_picture_t clip_picture;
cairo_status_t status;
 
tmp = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_similar (dst, dst->base.content,
extents->width, extents->height);
if (unlikely (tmp->base.status))
return tmp->base.status;
 
/* create_similar() could have done a fallback to an image surface */
assert (tmp->base.backend == &_cairo_xcb_surface_backend);
 
_cairo_xcb_surface_ensure_picture (tmp);
 
if (pattern == NULL) {
status = (*draw_func) (draw_closure, tmp,
CAIRO_OPERATOR_ADD, NULL,
extents->x, extents->y,
extents, NULL);
} else {
/* Initialize the temporary surface from the destination surface */
if (! dst->base.is_clear ||
(dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) == 0)
{
/* XCopyArea may actually be quicker here.
* A good driver should translate if appropriate.
*/
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_SRC,
dst->picture,
XCB_NONE,
tmp->picture,
extents->x, extents->y,
0, 0,
0, 0,
extents->width, extents->height);
}
else
{
xcb_render_color_t clear;
xcb_rectangle_t xrect;
 
clear.red = clear.green = clear.blue = clear.alpha = 0;
 
xrect.x = xrect.y = 0;
xrect.width = extents->width;
xrect.height = extents->height;
 
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
XCB_RENDER_PICT_OP_CLEAR,
dst->picture,
clear, 1, &xrect);
}
 
status = (*draw_func) (draw_closure, tmp, op, pattern,
extents->x, extents->y,
extents, NULL);
}
if (unlikely (status))
goto CLEANUP_SURFACE;
 
clip_surface = get_clip_surface (clip, dst, &clip_x, &clip_y);
status = clip_surface->base.status;
if (unlikely (status))
goto CLEANUP_SURFACE;
 
assert (clip_surface->base.backend == &_cairo_xcb_surface_backend);
clip_picture = clip_surface->picture;
assert (clip_picture != XCB_NONE);
 
if (dst->base.is_clear) {
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_SRC,
tmp->picture, clip_picture, dst->picture,
0, 0,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
} else {
/* Punch the clip out of the destination */
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
clip_picture, XCB_NONE, dst->picture,
extents->x - clip_x,
extents->y - clip_y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
 
/* Now add the two results together */
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_ADD,
tmp->picture, clip_picture, dst->picture,
0, 0,
extents->x - clip_x,
extents->y - clip_y,
extents->x, extents->y,
extents->width, extents->height);
}
cairo_surface_destroy (&clip_surface->base);
 
CLEANUP_SURFACE:
cairo_surface_destroy (&tmp->base);
 
return status;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
_clip_and_composite_source (cairo_clip_t *clip,
const cairo_pattern_t *pattern,
xcb_draw_func_t draw_func,
xcb_draw_func_t mask_func,
void *draw_closure,
cairo_xcb_surface_t *dst,
const cairo_rectangle_int_t *extents)
{
cairo_xcb_surface_t *mask;
cairo_xcb_picture_t *src;
 
/* Create a surface that is mask IN clip */
mask = _create_composite_mask (clip,
draw_func, mask_func, draw_closure,
dst, extents);
if (unlikely (mask->base.status))
return mask->base.status;
 
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
if (unlikely (src->base.status)) {
cairo_surface_destroy (&mask->base);
return src->base.status;
}
 
if (dst->base.is_clear) {
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_SRC,
src->picture,
mask->picture,
dst->picture,
extents->x + src->x, extents->y + src->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
} else {
/* Compute dest' = dest OUT (mask IN clip) */
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
mask->picture,
XCB_NONE,
dst->picture,
0, 0, 0, 0,
extents->x, extents->y,
extents->width, extents->height);
 
/* Now compute (src IN (mask IN clip)) ADD dest' */
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_ADD,
src->picture,
mask->picture,
dst->picture,
extents->x + src->x, extents->y + src->y,
0, 0,
extents->x, extents->y,
extents->width, extents->height);
}
 
cairo_surface_destroy (&src->base);
cairo_surface_destroy (&mask->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
can_reduce_alpha_op (cairo_operator_t op)
{
int iop = op;
switch (iop) {
case CAIRO_OPERATOR_OVER:
case CAIRO_OPERATOR_SOURCE:
case CAIRO_OPERATOR_ADD:
return TRUE;
default:
return FALSE;
}
}
 
static cairo_bool_t
reduce_alpha_op (cairo_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern)
{
return dst->is_clear &&
dst->content == CAIRO_CONTENT_ALPHA &&
_cairo_pattern_is_opaque_solid (pattern) &&
can_reduce_alpha_op (op);
}
 
static cairo_status_t
_cairo_xcb_surface_fixup_unbounded (cairo_xcb_surface_t *dst,
const cairo_composite_rectangles_t *rects)
{
xcb_rectangle_t xrects[4];
int n;
 
if (rects->bounded.width == rects->unbounded.width &&
rects->bounded.height == rects->unbounded.height)
{
return CAIRO_STATUS_SUCCESS;
}
 
n = 0;
if (rects->bounded.width == 0 || rects->bounded.height == 0) {
xrects[n].x = rects->unbounded.x;
xrects[n].width = rects->unbounded.width;
xrects[n].y = rects->unbounded.y;
xrects[n].height = rects->unbounded.height;
n++;
} else {
/* top */
if (rects->bounded.y != rects->unbounded.y) {
xrects[n].x = rects->unbounded.x;
xrects[n].width = rects->unbounded.width;
xrects[n].y = rects->unbounded.y;
xrects[n].height = rects->bounded.y - rects->unbounded.y;
n++;
}
/* left */
if (rects->bounded.x != rects->unbounded.x) {
xrects[n].x = rects->unbounded.x;
xrects[n].width = rects->bounded.x - rects->unbounded.x;
xrects[n].y = rects->bounded.y;
xrects[n].height = rects->bounded.height;
n++;
}
/* right */
if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
xrects[n].x = rects->bounded.x + rects->bounded.width;
xrects[n].width = rects->unbounded.x + rects->unbounded.width - xrects[n].x;
xrects[n].y = rects->bounded.y;
xrects[n].height = rects->bounded.height;
n++;
}
/* bottom */
if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
xrects[n].x = rects->unbounded.x;
xrects[n].width = rects->unbounded.width;
xrects[n].y = rects->bounded.y + rects->bounded.height;
xrects[n].height = rects->unbounded.y + rects->unbounded.height - xrects[n].y;
n++;
}
}
 
if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
xcb_render_color_t color;
 
color.red = 0;
color.green = 0;
color.blue = 0;
color.alpha = 0;
 
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
XCB_RENDER_PICT_OP_CLEAR,
dst->picture,
color, n, xrects);
} else {
int i;
cairo_xcb_picture_t *src;
 
src = _cairo_xcb_transparent_picture (dst);
if (unlikely (src->base.status))
return src->base.status;
 
for (i = 0; i < n; i++) {
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_CLEAR,
src->picture, XCB_NONE, dst->picture,
0, 0,
0, 0,
xrects[i].x, xrects[i].y,
xrects[i].width, xrects[i].height);
}
cairo_surface_destroy (&src->base);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xcb_surface_fixup_unbounded_with_mask (cairo_xcb_surface_t *dst,
const cairo_composite_rectangles_t *rects,
cairo_clip_t *clip)
{
cairo_xcb_surface_t *mask;
int mask_x = 0, mask_y = 0;
 
mask = get_clip_surface (clip, dst, &mask_x, &mask_y);
if (unlikely (mask->base.status))
return mask->base.status;
 
/* top */
if (rects->bounded.y != rects->unbounded.y) {
int x = rects->unbounded.x;
int y = rects->unbounded.y;
int width = rects->unbounded.width;
int height = rects->bounded.y - y;
 
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
mask->picture, XCB_NONE, dst->picture,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* left */
if (rects->bounded.x != rects->unbounded.x) {
int x = rects->unbounded.x;
int y = rects->bounded.y;
int width = rects->bounded.x - x;
int height = rects->bounded.height;
 
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
mask->picture, XCB_NONE, dst->picture,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* right */
if (rects->bounded.x + rects->bounded.width != rects->unbounded.x + rects->unbounded.width) {
int x = rects->bounded.x + rects->bounded.width;
int y = rects->bounded.y;
int width = rects->unbounded.x + rects->unbounded.width - x;
int height = rects->bounded.height;
 
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
mask->picture, XCB_NONE, dst->picture,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* bottom */
if (rects->bounded.y + rects->bounded.height != rects->unbounded.y + rects->unbounded.height) {
int x = rects->unbounded.x;
int y = rects->bounded.y + rects->bounded.height;
int width = rects->unbounded.width;
int height = rects->unbounded.y + rects->unbounded.height - y;
 
_cairo_xcb_connection_render_composite (dst->connection,
XCB_RENDER_PICT_OP_OUT_REVERSE,
mask->picture, XCB_NONE, dst->picture,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
cairo_surface_destroy (&mask->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xcb_surface_fixup_unbounded_boxes (cairo_xcb_surface_t *dst,
const cairo_composite_rectangles_t *extents,
cairo_clip_t *clip,
cairo_boxes_t *boxes)
{
cairo_boxes_t clear;
cairo_box_t box;
cairo_status_t status;
struct _cairo_boxes_chunk *chunk;
int i;
 
if (boxes->num_boxes <= 1 && clip == NULL)
return _cairo_xcb_surface_fixup_unbounded (dst, extents);
 
_cairo_boxes_init (&clear);
 
box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width);
box.p1.y = _cairo_fixed_from_int (extents->unbounded.y);
box.p2.x = _cairo_fixed_from_int (extents->unbounded.x);
box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height);
 
if (clip == NULL) {
cairo_boxes_t tmp;
 
_cairo_boxes_init (&tmp);
 
status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
tmp.chunks.next = &boxes->chunks;
tmp.num_boxes += boxes->num_boxes;
 
status = _cairo_bentley_ottmann_tessellate_boxes (&tmp,
CAIRO_FILL_RULE_WINDING,
&clear);
 
tmp.chunks.next = NULL;
} else {
_cairo_boxes_init_with_clip (&clear, clip);
 
status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box);
assert (status == CAIRO_STATUS_SUCCESS);
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
status = _cairo_boxes_add (&clear,
CAIRO_ANTIALIAS_DEFAULT,
&chunk->base[i]);
if (unlikely (status)) {
_cairo_boxes_fini (&clear);
return status;
}
}
}
 
status = _cairo_bentley_ottmann_tessellate_boxes (&clear,
CAIRO_FILL_RULE_WINDING,
&clear);
}
 
if (likely (status == CAIRO_STATUS_SUCCESS)) {
if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES)
status = _render_fill_boxes (dst,
CAIRO_OPERATOR_CLEAR,
CAIRO_COLOR_TRANSPARENT,
&clear);
else
status = _cairo_xcb_surface_core_fill_boxes (dst,
CAIRO_COLOR_TRANSPARENT,
&clear);
}
 
_cairo_boxes_fini (&clear);
 
return status;
}
 
cairo_status_t
_cairo_xcb_surface_clear (cairo_xcb_surface_t *dst)
{
xcb_gcontext_t gc;
xcb_rectangle_t rect;
cairo_status_t status;
 
status = _cairo_xcb_connection_acquire (dst->connection);
if (unlikely (status))
return status;
 
rect.x = rect.y = 0;
rect.width = dst->width;
rect.height = dst->height;
 
if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES) {
xcb_render_color_t color;
uint8_t op;
 
color.red = dst->deferred_clear_color.red_short;
color.green = dst->deferred_clear_color.green_short;
color.blue = dst->deferred_clear_color.blue_short;
color.alpha = dst->deferred_clear_color.alpha_short;
 
if (color.alpha == 0)
op = XCB_RENDER_PICT_OP_CLEAR;
else
op = XCB_RENDER_PICT_OP_SRC;
 
_cairo_xcb_surface_ensure_picture (dst);
_cairo_xcb_connection_render_fill_rectangles (dst->connection,
op, dst->picture, color,
1, &rect);
} else {
gc = _cairo_xcb_screen_get_gc (dst->screen, dst->drawable, dst->depth);
 
/* XXX color */
_cairo_xcb_connection_poly_fill_rectangle (dst->connection,
dst->drawable, gc,
1, &rect);
 
_cairo_xcb_screen_put_gc (dst->screen, dst->depth, gc);
}
 
_cairo_xcb_connection_release (dst->connection);
 
dst->deferred_clear = FALSE;
return CAIRO_STATUS_SUCCESS;
}
 
enum {
NEED_CLIP_REGION = 0x1,
NEED_CLIP_SURFACE = 0x2,
FORCE_CLIP_REGION = 0x4,
};
 
static cairo_bool_t
need_bounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = NEED_CLIP_REGION;
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
return flags;
}
 
static cairo_bool_t
need_unbounded_clip (cairo_composite_rectangles_t *extents)
{
unsigned int flags = 0;
if (! extents->is_bounded) {
flags |= NEED_CLIP_REGION;
if (! _cairo_clip_is_region (extents->clip))
flags |= NEED_CLIP_SURFACE;
}
if (extents->clip->path != NULL)
flags |= NEED_CLIP_SURFACE;
return flags;
}
 
static cairo_status_t
_clip_and_composite (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
xcb_draw_func_t draw_func,
xcb_draw_func_t mask_func,
void *draw_closure,
cairo_composite_rectangles_t*extents,
unsigned int need_clip)
{
cairo_region_t *clip_region = NULL;
cairo_status_t status;
 
status = _cairo_xcb_connection_acquire (dst->connection);
if (unlikely (status))
return status;
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status)) {
_cairo_xcb_connection_release (dst->connection);
return status;
}
}
 
_cairo_xcb_surface_ensure_picture (dst);
 
if (need_clip & NEED_CLIP_REGION) {
clip_region = _cairo_clip_get_region (extents->clip);
if ((need_clip & FORCE_CLIP_REGION) == 0 && clip_region != NULL &&
cairo_region_contains_rectangle (clip_region,
&extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
clip_region = NULL;
if (clip_region != NULL) {
status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
if (unlikely (status)) {
_cairo_xcb_connection_release (dst->connection);
return status;
}
}
}
 
if (reduce_alpha_op (&dst->base, op, src)) {
op = CAIRO_OPERATOR_ADD;
src = NULL;
}
 
if (extents->bounded.width != 0 && extents->bounded.height != 0) {
if (op == CAIRO_OPERATOR_SOURCE) {
status = _clip_and_composite_source (extents->clip, src,
draw_func, mask_func, draw_closure,
dst, &extents->bounded);
} else {
if (op == CAIRO_OPERATOR_CLEAR) {
op = CAIRO_OPERATOR_DEST_OUT;
src = NULL;
}
 
if (need_clip & NEED_CLIP_SURFACE) {
if (extents->is_bounded) {
status = _clip_and_composite_with_mask (extents->clip, op, src,
draw_func,
mask_func,
draw_closure,
dst, &extents->bounded);
} else {
status = _clip_and_composite_combine (extents->clip, op, src,
draw_func, draw_closure,
dst, &extents->bounded);
}
} else {
status = draw_func (draw_closure,
dst, op, src,
0, 0,
&extents->bounded,
extents->clip);
}
}
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
if (need_clip & NEED_CLIP_SURFACE)
status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst, extents, extents->clip);
else
status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
}
 
if (clip_region)
_cairo_xcb_surface_clear_clip_region (dst);
 
_cairo_xcb_connection_release (dst->connection);
 
return status;
}
 
static cairo_status_t
_core_boxes (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_clip_is_region (extents->clip))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (op == CAIRO_OPERATOR_CLEAR)
return _cairo_xcb_surface_core_fill_boxes (dst, CAIRO_COLOR_TRANSPARENT, boxes);
 
if (op == CAIRO_OPERATOR_OVER) {
if (dst->base.is_clear || _cairo_pattern_is_opaque (src, &extents->bounded))
op = CAIRO_OPERATOR_SOURCE;
}
if (op != CAIRO_OPERATOR_SOURCE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
return _cairo_xcb_surface_core_fill_boxes (dst,
&((cairo_solid_pattern_t *) src)->color,
boxes);
}
 
return _cairo_xcb_surface_core_copy_boxes (dst, src, &extents->bounded, boxes);
}
 
static cairo_status_t
_composite_boxes (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
const cairo_composite_rectangles_t *extents)
{
cairo_clip_t *clip = extents->clip;
cairo_bool_t need_clip_mask = ! _cairo_clip_is_region (clip);
cairo_status_t status;
 
/* If the boxes are not pixel-aligned, we will need to compute a real mask */
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (need_clip_mask &&
(! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE))
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = _cairo_xcb_connection_acquire (dst->connection);
if (unlikely (status))
return status;
 
_cairo_xcb_surface_ensure_picture (dst);
if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_FILL_RECTANGLES && ! need_clip_mask &&
(op == CAIRO_OPERATOR_CLEAR || src->type == CAIRO_PATTERN_TYPE_SOLID))
{
const cairo_color_t *color;
 
if (op == CAIRO_OPERATOR_CLEAR)
color = CAIRO_COLOR_TRANSPARENT;
else
color = &((cairo_solid_pattern_t *) src)->color;
 
status = _render_fill_boxes (dst, op, color, boxes);
}
else
{
cairo_surface_pattern_t mask;
 
if (need_clip_mask) {
cairo_xcb_surface_t *clip_surface;
int clip_x = 0, clip_y = 0;
 
clip_surface = get_clip_surface (extents->clip, dst,
&clip_x, &clip_y);
if (unlikely (clip_surface->base.status))
return clip_surface->base.status;
 
_cairo_pattern_init_for_surface (&mask, &clip_surface->base);
mask.base.filter = CAIRO_FILTER_NEAREST;
cairo_matrix_init_translate (&mask.base.matrix,
-clip_x,
-clip_y);
cairo_surface_destroy (&clip_surface->base);
 
if (op == CAIRO_OPERATOR_CLEAR) {
src = NULL;
op = CAIRO_OPERATOR_DEST_OUT;
}
}
 
status = _render_composite_boxes (dst, op, src,
need_clip_mask ? &mask.base : NULL,
&extents->bounded, boxes);
 
if (need_clip_mask)
_cairo_pattern_fini (&mask.base);
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) {
status =
_cairo_xcb_surface_fixup_unbounded_boxes (dst, extents,
clip, boxes);
}
 
_cairo_xcb_connection_release (dst->connection);
 
return status;
}
 
static cairo_bool_t
cairo_boxes_for_each_box (cairo_boxes_t *boxes,
cairo_bool_t (*func) (cairo_box_t *box,
void *data),
void *data)
{
struct _cairo_boxes_chunk *chunk;
int i;
 
for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++)
if (! func (&chunk->base[i], data))
return FALSE;
}
 
return TRUE;
}
 
struct _image_contains_box {
int width, height;
int tx, ty;
};
 
static cairo_bool_t image_contains_box (cairo_box_t *box, void *closure)
{
struct _image_contains_box *data = closure;
 
/* The box is pixel-aligned so the truncation is safe. */
return
_cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
_cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
_cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
_cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
}
 
struct _image_upload_box {
cairo_xcb_surface_t *surface;
cairo_image_surface_t *image;
xcb_gcontext_t gc;
int tx, ty;
};
 
static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
{
const struct _image_upload_box *iub = closure;
/* The box is pixel-aligned so the truncation is safe. */
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
int bpp = PIXMAN_FORMAT_BPP (iub->image->pixman_format);
int len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, bpp);
if (len == iub->image->stride) {
_cairo_xcb_connection_put_image (iub->surface->connection,
iub->surface->drawable,
iub->gc,
width, height,
x, y,
iub->image->depth,
iub->image->stride,
iub->image->data +
(y + iub->ty) * iub->image->stride +
(x + iub->tx) * bpp/8);
} else {
_cairo_xcb_connection_put_subimage (iub->surface->connection,
iub->surface->drawable,
iub->gc,
x + iub->tx,
y + iub->ty,
width, height,
bpp / 8,
iub->image->stride,
x, y,
iub->image->depth,
iub->image->data);
}
 
return TRUE;
}
 
static cairo_status_t
_upload_image_inplace (cairo_xcb_surface_t *surface,
const cairo_pattern_t *source,
cairo_boxes_t *boxes)
{
const cairo_surface_pattern_t *pattern;
struct _image_contains_box icb;
struct _image_upload_box iub;
cairo_image_surface_t *image;
cairo_status_t status;
int tx, ty;
 
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
pattern = (const cairo_surface_pattern_t *) source;
if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Have we already upload this image to a pixmap? */
{
cairo_xcb_picture_t *snapshot;
 
snapshot = (cairo_xcb_picture_t *)
_cairo_surface_has_snapshot (pattern->surface, &_cairo_xcb_picture_backend);
if (snapshot != NULL) {
if (snapshot->screen == surface->screen)
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
 
image = (cairo_image_surface_t *) pattern->surface;
if (image->format == CAIRO_FORMAT_INVALID)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (image->depth != surface->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_matrix_is_integer_translation (&source->matrix, &tx, &ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Check that the data is entirely within the image */
icb.width = image->width;
icb.height = image->height;
icb.tx = tx;
icb.ty = ty;
if (! cairo_boxes_for_each_box (boxes, image_contains_box, &icb))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (surface->deferred_clear) {
status = _cairo_xcb_surface_clear (surface);
if (unlikely (status))
return status;
}
 
status = _cairo_xcb_connection_acquire (surface->connection);
if (unlikely (status))
return status;
 
iub.surface = surface;
iub.image = image;
iub.gc = _cairo_xcb_screen_get_gc (surface->screen,
surface->drawable,
image->depth);
iub.tx = tx;
iub.ty = ty;
cairo_boxes_for_each_box (boxes, image_upload_box, &iub);
 
_cairo_xcb_screen_put_gc (surface->screen, image->depth, iub.gc);
_cairo_xcb_connection_release (surface->connection);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
trim_extents_to_traps (cairo_composite_rectangles_t *extents,
cairo_traps_t *traps)
{
cairo_box_t box;
 
/* X trims the affected area to the extents of the trapezoids, so
* we need to compensate when fixing up the unbounded area.
*/
_cairo_traps_extents (traps, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_bool_t
_mono_edge_is_vertical (const cairo_line_t *line)
{
return _cairo_fixed_integer_round_down (line->p1.x) == _cairo_fixed_integer_round_down (line->p2.x);
}
 
static cairo_bool_t
_traps_are_pixel_aligned (cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
if (antialias == CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
if (! _mono_edge_is_vertical (&traps->traps[i].left) ||
! _mono_edge_is_vertical (&traps->traps[i].right))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
} else {
for (i = 0; i < traps->num_traps; i++) {
if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x ||
traps->traps[i].right.p1.x != traps->traps[i].right.p2.x ||
! _cairo_fixed_is_integer (traps->traps[i].top) ||
! _cairo_fixed_is_integer (traps->traps[i].bottom) ||
! _cairo_fixed_is_integer (traps->traps[i].left.p1.x) ||
! _cairo_fixed_is_integer (traps->traps[i].right.p1.x))
{
traps->maybe_region = FALSE;
return FALSE;
}
}
}
 
return TRUE;
}
 
static void
_boxes_for_traps (cairo_boxes_t *boxes,
cairo_traps_t *traps,
cairo_antialias_t antialias)
{
int i;
 
_cairo_boxes_init (boxes);
 
boxes->num_boxes = traps->num_traps;
boxes->chunks.base = (cairo_box_t *) traps->traps;
boxes->chunks.count = traps->num_traps;
boxes->chunks.size = traps->num_traps;
 
if (antialias != CAIRO_ANTIALIAS_NONE) {
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
boxes->chunks.base[i].p1.x = x1;
boxes->chunks.base[i].p1.y = y1;
boxes->chunks.base[i].p2.x = x2;
boxes->chunks.base[i].p2.y = y2;
 
if (boxes->is_pixel_aligned) {
boxes->is_pixel_aligned =
_cairo_fixed_is_integer (x1) && _cairo_fixed_is_integer (y1) &&
_cairo_fixed_is_integer (x2) && _cairo_fixed_is_integer (y2);
}
}
} else {
boxes->is_pixel_aligned = TRUE;
 
for (i = 0; i < traps->num_traps; i++) {
/* Note the traps and boxes alias so we need to take the local copies first. */
cairo_fixed_t x1 = traps->traps[i].left.p1.x;
cairo_fixed_t x2 = traps->traps[i].right.p1.x;
cairo_fixed_t y1 = traps->traps[i].top;
cairo_fixed_t y2 = traps->traps[i].bottom;
 
/* round down here to match Pixman's behavior when using traps. */
boxes->chunks.base[i].p1.x = _cairo_fixed_round_down (x1);
boxes->chunks.base[i].p1.y = _cairo_fixed_round_down (y1);
boxes->chunks.base[i].p2.x = _cairo_fixed_round_down (x2);
boxes->chunks.base[i].p2.y = _cairo_fixed_round_down (y2);
}
}
}
 
static cairo_status_t
_composite_polygon (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_polygon_t *polygon,
cairo_antialias_t antialias,
cairo_fill_rule_t fill_rule,
cairo_composite_rectangles_t *extents)
{
composite_traps_info_t traps;
cairo_bool_t clip_surface = ! _cairo_clip_is_region (extents->clip);
cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip);
cairo_status_t status;
 
if (polygon->num_edges == 0) {
status = CAIRO_STATUS_SUCCESS;
 
if (! extents->is_bounded) {
if (cairo_region_contains_rectangle (clip_region, &extents->unbounded) == CAIRO_REGION_OVERLAP_IN)
clip_region = NULL;
 
if (clip_surface == FALSE) {
if (clip_region != NULL) {
status = _cairo_xcb_surface_set_clip_region (dst, clip_region);
if (unlikely (status))
return status;
}
 
status = _cairo_xcb_surface_fixup_unbounded (dst, extents);
 
if (clip_region != NULL)
_cairo_xcb_surface_clear_clip_region (dst);
} else {
status = _cairo_xcb_surface_fixup_unbounded_with_mask (dst,
extents,
extents->clip);
}
}
 
return status;
}
 
if (extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t clipper;
cairo_fill_rule_t clipper_fill_rule;
cairo_antialias_t clipper_antialias;
 
status = _cairo_clip_get_polygon (extents->clip,
&clipper,
&clipper_fill_rule,
&clipper_antialias);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
if (clipper_antialias == antialias) {
status = _cairo_polygon_intersect (polygon, fill_rule,
&clipper, clipper_fill_rule);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_clip_t * clip = _cairo_clip_copy_region (extents->clip);
_cairo_clip_destroy (extents->clip);
extents->clip = clip;
 
fill_rule = CAIRO_FILL_RULE_WINDING;
}
_cairo_polygon_fini (&clipper);
}
}
}
 
_cairo_traps_init (&traps.traps);
 
status = _cairo_bentley_ottmann_tessellate_polygon (&traps.traps, polygon, fill_rule);
if (unlikely (status))
goto CLEANUP_TRAPS;
 
if (traps.traps.has_intersections) {
if (traps.traps.is_rectangular)
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
else if (traps.traps.is_rectilinear)
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
else
status = _cairo_bentley_ottmann_tessellate_traps (&traps.traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status))
goto CLEANUP_TRAPS;
}
 
/* Use a fast path if the trapezoids consist of a simple region,
* but we can only do this if we do not have a clip surface, or can
* substitute the mask with the clip.
*/
if (traps.traps.maybe_region &&
_traps_are_pixel_aligned (&traps.traps, antialias) &&
(! clip_surface ||
(extents->is_bounded && op != CAIRO_OPERATOR_SOURCE)))
{
cairo_boxes_t boxes;
 
_boxes_for_traps (&boxes, &traps.traps, antialias);
status = _clip_and_composite_boxes (dst, op, source, &boxes, extents);
}
else
{
/* Otherwise render the trapezoids to a mask and composite in the usual
* fashion.
*/
traps.antialias = antialias;
status = trim_extents_to_traps (extents, &traps.traps);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
unsigned int flags = 0;
 
/* For unbounded operations, the X11 server will estimate the
* affected rectangle and apply the operation to that. However,
* there are cases where this is an overestimate (e.g. the
* clip-fill-{eo,nz}-unbounded test).
*
* The clip will trim that overestimate to our expectations.
*/
if (! extents->is_bounded)
flags |= FORCE_CLIP_REGION;
 
status = _clip_and_composite (dst, op, source, _composite_traps,
NULL, &traps, extents,
need_unbounded_clip (extents) | flags);
}
}
 
CLEANUP_TRAPS:
_cairo_traps_fini (&traps.traps);
 
return status;
}
 
static cairo_status_t
_clip_and_composite_boxes (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src,
cairo_boxes_t *boxes,
cairo_composite_rectangles_t *extents)
{
composite_traps_info_t info;
cairo_int_status_t status;
 
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
if (boxes->is_pixel_aligned && _cairo_clip_is_region (extents->clip) &&
(op == CAIRO_OPERATOR_SOURCE ||
(dst->base.is_clear && (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))))
{
if (boxes->num_boxes == 1 &&
extents->bounded.width == dst->width &&
extents->bounded.height == dst->height)
{
op = CAIRO_OPERATOR_SOURCE;
dst->deferred_clear = FALSE;
}
 
status = _upload_image_inplace (dst, src, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
/* Can we reduce drawing through a clip-mask to simply drawing the clip? */
if (dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS &&
extents->clip->path != NULL && extents->is_bounded) {
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
cairo_antialias_t antialias;
cairo_clip_t *clip;
 
clip = _cairo_clip_copy (extents->clip);
clip = _cairo_clip_intersect_boxes (clip, boxes);
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &antialias);
_cairo_clip_path_destroy (clip->path);
clip->path = NULL;
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
cairo_clip_t *saved_clip = extents->clip;
extents->clip = clip;
status = _composite_polygon (dst, op, src,
&polygon,
antialias,
fill_rule,
extents);
if (extents->clip != clip)
clip = NULL;
extents->clip = saved_clip;
_cairo_polygon_fini (&polygon);
}
if (clip)
_cairo_clip_destroy (clip);
 
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
if (boxes->is_pixel_aligned &&
_cairo_clip_is_region (extents->clip) &&
op == CAIRO_OPERATOR_SOURCE) {
status = _upload_image_inplace (dst, src, boxes);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
}
 
if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
return _core_boxes (dst, op, src, boxes, extents);
 
/* Use a fast path if the boxes are pixel aligned */
status = _composite_boxes (dst, op, src, boxes, extents);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
 
if ((dst->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* Otherwise render via a mask and composite in the usual fashion. */
status = _cairo_traps_init_boxes (&info.traps, boxes);
if (unlikely (status))
return status;
 
info.antialias = CAIRO_ANTIALIAS_DEFAULT;
status = trim_extents_to_traps (extents, &info.traps);
if (status == CAIRO_INT_STATUS_SUCCESS) {
status = _clip_and_composite (dst, op, src,
_composite_traps, NULL, &info,
extents, need_unbounded_clip (extents));
}
 
_cairo_traps_fini (&info.traps);
return status;
}
 
static cairo_int_status_t
_composite_mask (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
const cairo_pattern_t *mask_pattern = closure;
cairo_xcb_picture_t *src, *mask = NULL;
cairo_status_t status;
 
if (dst->base.is_clear) {
if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
op = CAIRO_OPERATOR_SOURCE;
}
 
if (op == CAIRO_OPERATOR_SOURCE && clip == NULL)
dst->deferred_clear = FALSE;
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
if (src_pattern != NULL) {
src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
if (unlikely (src->base.status))
return src->base.status;
 
mask = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
if (unlikely (mask->base.status)) {
cairo_surface_destroy (&src->base);
return mask->base.status;
}
 
_cairo_xcb_connection_render_composite (dst->connection,
_render_operator (op),
src->picture,
mask->picture,
dst->picture,
extents->x + src->x, extents->y + src->y,
extents->x + mask->x, extents->y + mask->y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
cairo_surface_destroy (&mask->base);
cairo_surface_destroy (&src->base);
} else {
src = _cairo_xcb_picture_for_pattern (dst, mask_pattern, extents);
if (unlikely (src->base.status))
return src->base.status;
 
_cairo_xcb_connection_render_composite (dst->connection,
_render_operator (op),
src->picture,
XCB_NONE,
dst->picture,
extents->x + src->x, extents->y + src->y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
cairo_surface_destroy (&src->base);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
struct composite_box_info {
cairo_xcb_surface_t *dst;
cairo_xcb_picture_t *src;
uint8_t op;
};
 
static void composite_box(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_box_info *info = closure;
 
if (coverage < 0xff00) {
cairo_xcb_picture_t *mask;
cairo_color_t color;
 
color.red_short = color.green_short = color.blue_short = 0;
color.alpha_short = coverage;
 
mask = _solid_picture (info->dst, &color);
if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
_cairo_xcb_connection_render_composite (info->dst->connection,
info->op,
info->src->picture,
mask->picture,
info->dst->picture,
x + info->src->x, y + info->src->y,
0, 0,
x, y,
w, h);
}
cairo_surface_destroy (&mask->base);
} else {
_cairo_xcb_connection_render_composite (info->dst->connection,
info->op,
info->src->picture,
XCB_NONE,
info->dst->picture,
x + info->src->x, y + info->src->y,
0, 0,
x, y,
w, h);
}
}
 
static cairo_int_status_t
_composite_mask_clip_boxes (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
struct composite_box_info info;
cairo_status_t status;
int i;
 
assert (src_pattern == NULL);
assert (op == CAIRO_OPERATOR_ADD);
assert (dst->base.is_clear);
 
if (clip->num_boxes > 1) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
info.op = XCB_RENDER_PICT_OP_SRC;
info.dst = dst;
info.src = _cairo_xcb_picture_for_pattern (dst, closure, extents);
if (unlikely (info.src->base.status))
return info.src->base.status;
 
info.src->x += dst_x;
info.src->y += dst_y;
 
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_box, &info, &clip->boxes[i], dst_x, dst_y);
cairo_surface_destroy (&info.src->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_composite_mask_clip (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
const cairo_pattern_t *mask_pattern = closure;
cairo_polygon_t polygon;
cairo_fill_rule_t fill_rule;
composite_traps_info_t info;
cairo_status_t status;
 
assert (src_pattern == NULL);
assert (op == CAIRO_OPERATOR_ADD);
assert (dst->base.is_clear);
 
status = _cairo_clip_get_polygon (clip, &polygon,
&fill_rule, &info.antialias);
if (unlikely (status))
return status;
 
_cairo_traps_init (&info.traps);
status = _cairo_bentley_ottmann_tessellate_polygon (&info.traps,
&polygon,
fill_rule);
_cairo_polygon_fini (&polygon);
if (unlikely (status))
return status;
 
if (info.traps.has_intersections) {
if (info.traps.is_rectangular)
status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
else if (info.traps.is_rectilinear)
status = _cairo_bentley_ottmann_tessellate_rectilinear_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
else
status = _cairo_bentley_ottmann_tessellate_traps (&info.traps, CAIRO_FILL_RULE_WINDING);
if (unlikely (status)) {
_cairo_traps_fini (&info.traps);
return status;
}
}
 
dst->deferred_clear = FALSE; /* assert(trap extents == extents); */
 
status = _composite_traps (&info,
dst, CAIRO_OPERATOR_SOURCE, mask_pattern,
dst_x, dst_y,
extents, NULL);
_cairo_traps_fini (&info.traps);
 
return status;
}
 
struct composite_opacity_info {
uint8_t op;
cairo_xcb_surface_t *dst;
cairo_xcb_picture_t *src;
double opacity;
};
 
static void composite_opacity(void *closure,
int16_t x, int16_t y,
int16_t w, int16_t h,
uint16_t coverage)
{
struct composite_opacity_info *info = closure;
cairo_xcb_picture_t *mask;
cairo_color_t color;
 
color.red_short = color.green_short = color.blue_short = 0;
color.alpha_short = info->opacity * coverage;
 
mask = _solid_picture (info->dst, &color);
if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) {
if (info->src) {
_cairo_xcb_connection_render_composite (info->dst->connection,
info->op,
info->src->picture,
mask->picture,
info->dst->picture,
x + info->src->x, y + info->src->y,
0, 0,
x, y,
w, h);
} else {
_cairo_xcb_connection_render_composite (info->dst->connection,
info->op,
mask->picture,
XCB_NONE,
info->dst->picture,
0, 0,
0, 0,
x, y,
w, h);
}
}
 
cairo_surface_destroy (&mask->base);
}
 
static cairo_int_status_t
_composite_opacity_boxes (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *src_pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
const cairo_solid_pattern_t *mask_pattern = closure;
struct composite_opacity_info info;
cairo_status_t status;
int i;
 
if (dst->base.is_clear) {
if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
op = CAIRO_OPERATOR_SOURCE;
}
 
if (op == CAIRO_OPERATOR_SOURCE &&
(clip == NULL ||
(clip->extents.width >= extents->width &&
clip->extents.height >= extents->height)))
dst->deferred_clear = FALSE;
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
info.op = _render_operator (op);
info.dst = dst;
 
if (src_pattern != NULL) {
info.src = _cairo_xcb_picture_for_pattern (dst, src_pattern, extents);
if (unlikely (info.src->base.status))
return info.src->base.status;
} else
info.src = NULL;
 
info.opacity = mask_pattern->color.alpha;
 
/* XXX for lots of boxes create a clip region for the fully opaque areas */
if (clip) {
for (i = 0; i < clip->num_boxes; i++)
do_unaligned_box(composite_opacity, &info,
&clip->boxes[i], dst_x, dst_y);
} else {
composite_opacity(&info,
extents->x - dst_x,
extents->y - dst_y,
extents->width,
extents->height,
0xffff);
}
cairo_surface_destroy (&info.src->base);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* high level rasteriser -> compositor */
 
cairo_int_status_t
_cairo_xcb_render_compositor_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
cairo_pattern_t *source = &composite->source_pattern.base;
cairo_boxes_t boxes;
cairo_status_t status;
 
if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
if (composite->clip == NULL &&
source->type == CAIRO_PATTERN_TYPE_SOLID &&
(op == CAIRO_OPERATOR_SOURCE ||
op == CAIRO_OPERATOR_CLEAR ||
(surface->base.is_clear &&
(op == CAIRO_OPERATOR_ADD || op == CAIRO_OPERATOR_OVER))))
{
surface->deferred_clear = TRUE;
surface->deferred_clear_color = composite->source_pattern.solid.color;
return CAIRO_STATUS_SUCCESS;
}
 
_cairo_clip_steal_boxes(composite->clip, &boxes);
status = _clip_and_composite_boxes (surface, op, source, &boxes, composite);
_cairo_clip_unsteal_boxes (composite->clip, &boxes);
 
return status;
}
 
cairo_int_status_t
_cairo_xcb_render_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
cairo_pattern_t *source = &composite->source_pattern.base;
cairo_pattern_t *mask = &composite->mask_pattern.base;
cairo_status_t status;
 
if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
composite->clip->path == NULL &&
! _cairo_clip_is_region (composite->clip)) {
status = _clip_and_composite (surface, op, source,
_composite_opacity_boxes,
_composite_opacity_boxes,
(void *) mask,
composite, need_unbounded_clip (composite));
} else {
xcb_draw_func_t mask_func = NULL;
if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS)
mask_func = composite->clip->path ? _composite_mask_clip : _composite_mask_clip_boxes;
status = _clip_and_composite (surface, op, source,
_composite_mask, mask_func,
(void *) mask,
composite, need_bounded_clip (composite));
}
 
return status;
}
 
static cairo_int_status_t
_cairo_xcb_surface_render_stroke_as_polygon (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
{
cairo_polygon_t polygon;
cairo_status_t status;
 
_cairo_polygon_init_with_clip (&polygon, extents->clip);
status = _cairo_path_fixed_stroke_to_polygon (path,
stroke_style,
ctm, ctm_inverse,
tolerance,
&polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _composite_polygon (dst, op, source,
&polygon, antialias,
CAIRO_FILL_RULE_WINDING,
extents);
}
_cairo_polygon_fini (&polygon);
 
return status;
}
 
static cairo_status_t
_cairo_xcb_surface_render_stroke_via_mask (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
{
cairo_surface_t *image;
cairo_status_t status;
cairo_clip_t *clip;
int x, y;
 
x = extents->bounded.x;
y = extents->bounded.y;
image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8,
extents->bounded.width,
extents->bounded.height);
if (unlikely (image->status))
return image->status;
 
clip = _cairo_clip_copy_region (extents->clip);
status = _cairo_surface_offset_stroke (image, x, y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, stroke_style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
_cairo_clip_destroy (clip);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_surface_pattern_t mask;
 
_cairo_pattern_init_for_surface (&mask, image);
mask.base.filter = CAIRO_FILTER_NEAREST;
 
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
status = _clip_and_composite (dst, op, source,
_composite_mask, NULL, &mask.base,
extents, need_bounded_clip (extents));
_cairo_pattern_fini (&mask.base);
}
 
cairo_surface_finish (image);
cairo_surface_destroy (image);
 
return status;
}
 
cairo_int_status_t
_cairo_xcb_render_compositor_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
cairo_pattern_t *source = &composite->source_pattern.base;
cairo_int_status_t status;
 
if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, composite->clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source,
&boxes, composite);
}
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
status = _cairo_xcb_surface_render_stroke_as_polygon (surface, op, source,
path, style,
ctm, ctm_inverse,
tolerance, antialias,
composite);
} else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
status = _cairo_xcb_surface_render_stroke_via_mask (surface, op, source,
path, style,
ctm, ctm_inverse,
tolerance, antialias,
composite);
} else {
ASSERT_NOT_REACHED;
}
}
 
return status;
}
 
static cairo_status_t
_cairo_xcb_surface_render_fill_as_polygon (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t*source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
{
cairo_polygon_t polygon;
cairo_status_t status;
 
_cairo_polygon_init_with_clip (&polygon, extents->clip);
status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = _composite_polygon (dst, op, source,
&polygon,
antialias,
fill_rule,
extents);
}
_cairo_polygon_fini (&polygon);
 
return status;
}
 
static cairo_status_t
_cairo_xcb_surface_render_fill_via_mask (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_composite_rectangles_t *extents)
{
cairo_surface_t *image;
cairo_status_t status;
cairo_clip_t *clip;
int x, y;
 
x = extents->bounded.x;
y = extents->bounded.y;
image = _cairo_xcb_surface_create_similar_image (dst, CAIRO_FORMAT_A8,
extents->bounded.width,
extents->bounded.height);
if (unlikely (image->status))
return image->status;
 
clip = _cairo_clip_copy_region (extents->clip);
status = _cairo_surface_offset_fill (image, x, y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
path, fill_rule, tolerance, antialias,
clip);
_cairo_clip_destroy (clip);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_surface_pattern_t mask;
 
_cairo_pattern_init_for_surface (&mask, image);
mask.base.filter = CAIRO_FILTER_NEAREST;
 
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
status = _clip_and_composite (dst, op, source,
_composite_mask, NULL, &mask.base,
extents, need_bounded_clip (extents));
 
_cairo_pattern_fini (&mask.base);
}
 
cairo_surface_finish (image);
cairo_surface_destroy (image);
 
return status;
}
 
cairo_int_status_t
_cairo_xcb_render_compositor_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
cairo_pattern_t *source = &composite->source_pattern.base;
cairo_int_status_t status;
 
if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS |
CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, composite->clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
status = _clip_and_composite_boxes (surface, op, source,
&boxes, composite);
}
_cairo_boxes_fini (&boxes);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_TRAPEZOIDS) {
status = _cairo_xcb_surface_render_fill_as_polygon (surface, op, source, path,
fill_rule, tolerance, antialias,
composite);
} else if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) {
status = _cairo_xcb_surface_render_fill_via_mask (surface, op, source, path,
fill_rule, tolerance, antialias,
composite);
} else {
ASSERT_NOT_REACHED;
}
}
 
return status;
}
 
static cairo_status_t
_cairo_xcb_surface_render_glyphs_via_mask (cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_composite_rectangles_t *extents)
{
cairo_surface_t *image;
cairo_content_t content;
cairo_status_t status;
cairo_clip_t *clip;
int x, y;
 
content = CAIRO_CONTENT_ALPHA;
if (scaled_font->options.antialias == CAIRO_ANTIALIAS_SUBPIXEL)
content = CAIRO_CONTENT_COLOR_ALPHA;
 
x = extents->bounded.x;
y = extents->bounded.y;
image = _cairo_xcb_surface_create_similar_image (dst,
_cairo_format_from_content (content),
extents->bounded.width,
extents->bounded.height);
if (unlikely (image->status))
return image->status;
 
clip = _cairo_clip_copy_region (extents->clip);
status = _cairo_surface_offset_glyphs (image, x, y,
CAIRO_OPERATOR_ADD,
&_cairo_pattern_white.base,
scaled_font, glyphs, num_glyphs,
clip);
_cairo_clip_destroy (clip);
if (likely (status == CAIRO_STATUS_SUCCESS)) {
cairo_surface_pattern_t mask;
 
_cairo_pattern_init_for_surface (&mask, image);
mask.base.filter = CAIRO_FILTER_NEAREST;
if (content & CAIRO_CONTENT_COLOR)
mask.base.has_component_alpha = TRUE;
 
cairo_matrix_init_translate (&mask.base.matrix, -x, -y);
status = _clip_and_composite (dst, op, source,
_composite_mask, NULL, &mask.base,
extents, need_bounded_clip (extents));
 
_cairo_pattern_fini (&mask.base);
}
 
cairo_surface_finish (image);
cairo_surface_destroy (image);
 
return status;
}
 
/* 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_xcb_glyph_t;
 
/* compile-time assert that #cairo_xcb_glyph_t is the same size as #cairo_glyph_t */
COMPILE_TIME_ASSERT (sizeof (cairo_xcb_glyph_t) == sizeof (cairo_glyph_t));
 
typedef struct {
cairo_scaled_font_t *font;
cairo_xcb_glyph_t *glyphs;
int num_glyphs;
cairo_bool_t use_mask;
} composite_glyphs_info_t;
 
static cairo_status_t
_can_composite_glyphs (cairo_xcb_surface_t *dst,
cairo_rectangle_int_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
#define GLYPH_CACHE_SIZE 64
cairo_box_t bbox_cache[GLYPH_CACHE_SIZE];
unsigned long glyph_cache[GLYPH_CACHE_SIZE];
#undef GLYPH_CACHE_SIZE
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_glyph_t *glyphs_end, *valid_glyphs;
const int max_glyph_size = dst->connection->maximum_request_length - 64;
 
/* We must initialize the cache with values that cannot match the
* "hash" to guarantee that when compared for the first time they
* will result in a mismatch. The hash function is simply modulus,
* so we cannot use 0 in glyph_cache[0], but we can use it in all
* other array cells.
*/
memset (glyph_cache, 0, sizeof (glyph_cache));
glyph_cache[0] = 1;
 
/* Scan for oversized glyphs or glyphs outside the representable
* range and fallback in that case, discard glyphs outside of the
* image.
*/
valid_glyphs = glyphs;
for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) {
double x1, y1, x2, y2;
cairo_scaled_glyph_t *glyph;
cairo_box_t *bbox;
int width, height, len;
int g;
 
g = glyphs->index % ARRAY_LENGTH (glyph_cache);
if (glyph_cache[g] != glyphs->index) {
status = _cairo_scaled_glyph_lookup (scaled_font,
glyphs->index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&glyph);
if (unlikely (status))
break;
 
glyph_cache[g] = glyphs->index;
bbox_cache[g] = glyph->bbox;
}
bbox = &bbox_cache[g];
 
/* Drop glyphs outside the clipping */
x1 = _cairo_fixed_to_double (bbox->p1.x);
y1 = _cairo_fixed_to_double (bbox->p1.y);
y2 = _cairo_fixed_to_double (bbox->p2.y);
x2 = _cairo_fixed_to_double (bbox->p2.x);
if (unlikely (glyphs->x + x2 <= extents->x ||
glyphs->y + y2 <= extents->y ||
glyphs->x + x1 >= extents->x + extents->width ||
glyphs->y + y1 >= extents->y + extents->height))
{
(*num_glyphs)--;
continue;
}
 
/* XRenderAddGlyph does not handle a glyph surface larger than
* the extended maximum XRequest size.
*/
width = _cairo_fixed_integer_ceil (bbox->p2.x - bbox->p1.x);
height = _cairo_fixed_integer_ceil (bbox->p2.y - bbox->p1.y);
len = CAIRO_STRIDE_FOR_WIDTH_BPP (width, 32) * height;
if (unlikely (len >= max_glyph_size)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
break;
}
 
/* The glyph coordinates must be representable in an int16_t.
* When possible, they will be expressed as an offset from the
* previous glyph, otherwise they will be an offset from the
* operation extents or from the surface origin. If the last
* two options are not valid, fallback.
*/
if (unlikely (glyphs->x > INT16_MAX ||
glyphs->y > INT16_MAX ||
glyphs->x - extents->x < INT16_MIN ||
glyphs->y - extents->y < INT16_MIN))
{
status = CAIRO_INT_STATUS_UNSUPPORTED;
break;
}
 
 
if (unlikely (valid_glyphs != glyphs))
*valid_glyphs = *glyphs;
valid_glyphs++;
}
 
if (unlikely (valid_glyphs != glyphs)) {
for (; glyphs != glyphs_end; glyphs++) {
*valid_glyphs = *glyphs;
valid_glyphs++;
}
}
 
return status;
}
 
/* 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_xcb_surface_emit_glyphs and _emit_glyph_chunks
*/
#define _start_new_glyph_elt(count, glyph) \
(((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
 
/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
* enough room for padding */
typedef struct {
uint8_t len;
uint8_t pad1;
uint16_t pad2;
int16_t deltax;
int16_t deltay;
} x_glyph_elt_t;
#define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4)
 
static void
_cairo_xcb_font_destroy (cairo_xcb_font_t *font)
{
int i;
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xcb_font_glyphset_info_t *info;
 
info = &font->glyphset_info[i];
free (info->pending_free_glyphs);
}
 
cairo_list_del (&font->base.link);
cairo_list_del (&font->link);
 
_cairo_xcb_connection_destroy (font->connection);
 
free (font);
}
 
static void
_cairo_xcb_font_fini (cairo_scaled_font_private_t *abstract_private,
cairo_scaled_font_t *scaled_font)
{
cairo_xcb_font_t *font_private = (cairo_xcb_font_t *)abstract_private;
cairo_xcb_connection_t *connection;
cairo_bool_t have_connection;
cairo_status_t status;
int i;
 
connection = font_private->connection;
 
status = _cairo_xcb_connection_acquire (connection);
have_connection = status == CAIRO_STATUS_SUCCESS;
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xcb_font_glyphset_info_t *info;
 
info = &font_private->glyphset_info[i];
if (info->glyphset && status == CAIRO_STATUS_SUCCESS) {
_cairo_xcb_connection_render_free_glyph_set (connection,
info->glyphset);
}
}
 
if (have_connection)
_cairo_xcb_connection_release (connection);
 
_cairo_xcb_font_destroy (font_private);
}
 
 
static cairo_xcb_font_t *
_cairo_xcb_font_create (cairo_xcb_connection_t *connection,
cairo_scaled_font_t *font)
{
cairo_xcb_font_t *priv;
int i;
 
priv = malloc (sizeof (cairo_xcb_font_t));
if (unlikely (priv == NULL))
return NULL;
 
_cairo_scaled_font_attach_private (font, &priv->base, connection,
_cairo_xcb_font_fini);
 
priv->scaled_font = font;
priv->connection = _cairo_xcb_connection_reference (connection);
cairo_list_add (&priv->link, &connection->fonts);
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xcb_font_glyphset_info_t *info = &priv->glyphset_info[i];
switch (i) {
case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break;
case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break;
default: ASSERT_NOT_REACHED; break;
}
info->xrender_format = 0;
info->glyphset = XCB_NONE;
info->pending_free_glyphs = NULL;
}
 
return priv;
}
 
void
_cairo_xcb_font_close (cairo_xcb_font_t *font)
{
cairo_scaled_font_t *scaled_font;
 
scaled_font = font->scaled_font;
 
//scaled_font->surface_private = NULL;
_cairo_scaled_font_reset_cache (scaled_font);
 
_cairo_xcb_font_destroy (font);
}
 
static void
_cairo_xcb_render_free_glyphs (cairo_xcb_connection_t *connection,
cairo_xcb_font_glyphset_free_glyphs_t *to_free)
{
_cairo_xcb_connection_render_free_glyphs (connection,
to_free->glyphset,
to_free->glyph_count,
to_free->glyph_indices);
}
 
static int
_cairo_xcb_get_glyphset_index_for_format (cairo_format_t format)
{
if (format == CAIRO_FORMAT_A8)
return GLYPHSET_INDEX_A8;
if (format == CAIRO_FORMAT_A1)
return GLYPHSET_INDEX_A1;
 
assert (format == CAIRO_FORMAT_ARGB32);
return GLYPHSET_INDEX_ARGB32;
}
 
 
 
static inline cairo_xcb_font_t *
_cairo_xcb_font_get (const cairo_xcb_connection_t *c,
cairo_scaled_font_t *font)
{
return (cairo_xcb_font_t *)_cairo_scaled_font_find_private (font, c);
}
 
 
static cairo_xcb_font_glyphset_info_t *
_cairo_xcb_scaled_font_get_glyphset_info_for_format (cairo_xcb_connection_t *c,
cairo_scaled_font_t *font,
cairo_format_t format)
{
cairo_xcb_font_t *priv;
cairo_xcb_font_glyphset_info_t *info;
int glyphset_index;
 
glyphset_index = _cairo_xcb_get_glyphset_index_for_format (format);
 
priv = _cairo_xcb_font_get (c, font);
if (priv == NULL) {
priv = _cairo_xcb_font_create (c, font);
if (priv == NULL)
return NULL;
}
 
info = &priv->glyphset_info[glyphset_index];
if (info->glyphset == XCB_NONE) {
info->glyphset = _cairo_xcb_connection_get_xid (c);
info->xrender_format = c->standard_formats[info->format];
 
_cairo_xcb_connection_render_create_glyph_set (c,
info->glyphset,
info->xrender_format);
}
 
return info;
}
 
static cairo_bool_t
_cairo_xcb_glyphset_info_has_pending_free_glyph (
cairo_xcb_font_glyphset_info_t *info,
unsigned long glyph_index)
{
if (info->pending_free_glyphs != NULL) {
cairo_xcb_font_glyphset_free_glyphs_t *to_free;
int i;
 
to_free = 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;
}
 
typedef struct {
cairo_scaled_glyph_private_t base;
 
cairo_xcb_font_glyphset_info_t *glyphset;
} cairo_xcb_glyph_private_t;
 
static cairo_xcb_font_glyphset_info_t *
_cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (cairo_xcb_connection_t *c,
cairo_scaled_font_t *font,
unsigned long glyph_index,
cairo_image_surface_t *surface)
{
cairo_xcb_font_t *priv;
int i;
 
priv = _cairo_xcb_font_get (c, font);
if (priv == NULL)
return NULL;
 
if (surface != NULL) {
i = _cairo_xcb_get_glyphset_index_for_format (surface->format);
 
if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
&priv->glyphset_info[i],
glyph_index))
{
return &priv->glyphset_info[i];
}
} else {
for (i = 0; i < NUM_GLYPHSETS; i++) {
if (_cairo_xcb_glyphset_info_has_pending_free_glyph (
&priv->glyphset_info[i],
glyph_index))
{
return &priv->glyphset_info[i];
}
}
}
 
return NULL;
}
 
static void
_cairo_xcb_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
cairo_scaled_glyph_t *glyph,
cairo_scaled_font_t *font)
{
cairo_xcb_glyph_private_t *priv = (cairo_xcb_glyph_private_t *)glyph_private;
 
if (! font->finished) {
cairo_xcb_font_glyphset_info_t *info = priv->glyphset;
cairo_xcb_font_glyphset_free_glyphs_t *to_free;
cairo_xcb_font_t *font_private;
 
font_private = _cairo_xcb_font_get (glyph_private->key, font);
assert (font_private);
 
to_free = info->pending_free_glyphs;
if (to_free != NULL &&
to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
{
_cairo_xcb_render_free_glyphs (font_private->connection, to_free);
to_free = info->pending_free_glyphs = NULL;
}
 
if (to_free == NULL) {
to_free = malloc (sizeof (cairo_xcb_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 = info->glyphset;
to_free->glyph_count = 0;
info->pending_free_glyphs = to_free;
}
 
to_free->glyph_indices[to_free->glyph_count++] =
_cairo_scaled_glyph_index (glyph);
}
 
cairo_list_del (&glyph_private->link);
free (glyph_private);
}
 
 
static cairo_status_t
_cairo_xcb_glyph_attach (cairo_xcb_connection_t *c,
cairo_scaled_glyph_t *glyph,
cairo_xcb_font_glyphset_info_t *info)
{
cairo_xcb_glyph_private_t *priv;
 
priv = malloc (sizeof (*priv));
if (unlikely (priv == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_scaled_glyph_attach_private (glyph, &priv->base, c,
_cairo_xcb_glyph_fini);
priv->glyphset = info;
 
glyph->dev_private = info;
glyph->dev_private_key = c;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection,
cairo_scaled_font_t *font,
cairo_scaled_glyph_t **scaled_glyph_out)
{
xcb_render_glyphinfo_t glyph_info;
uint32_t glyph_index;
uint8_t *data;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_scaled_glyph_t *scaled_glyph = *scaled_glyph_out;
cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
cairo_bool_t already_had_glyph_surface;
cairo_xcb_font_glyphset_info_t *info;
 
glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
 
/* check to see if we have a pending XRenderFreeGlyph for this glyph */
info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (connection, font, glyph_index, glyph_surface);
if (info != NULL)
return _cairo_xcb_glyph_attach (connection, scaled_glyph, info);
 
if (glyph_surface == NULL) {
status = _cairo_scaled_glyph_lookup (font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_SURFACE,
scaled_glyph_out);
if (unlikely (status))
return status;
 
scaled_glyph = *scaled_glyph_out;
glyph_surface = scaled_glyph->surface;
already_had_glyph_surface = FALSE;
} else {
already_had_glyph_surface = TRUE;
}
 
info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (connection,
font,
glyph_surface->format);
if (unlikely (info == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
 
#if 0
/* If the glyph surface has zero height or width, we create
* a clear 1x1 surface, to avoid various X server bugs.
*/
if (glyph_surface->width == 0 || glyph_surface->height == 0) {
cairo_surface_t *tmp_surface;
 
tmp_surface = cairo_image_surface_create (info->format, 1, 1);
status = tmp_surface->status;
if (unlikely (status))
goto BAIL;
 
tmp_surface->device_transform = glyph_surface->base.device_transform;
tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
 
glyph_surface = (cairo_image_surface_t *) tmp_surface;
}
#endif
 
/* If the glyph format does not match the font format, then we
* create a temporary surface for the glyph image with the font's
* format.
*/
if (glyph_surface->format != info->format) {
glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface,
info->format);
status = glyph_surface->base.status;
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.x_off = scaled_glyph->x_advance;
glyph_info.y_off = scaled_glyph->y_advance;
 
data = glyph_surface->data;
 
/* flip formats around */
switch (_cairo_xcb_get_glyphset_index_for_format (scaled_glyph->surface->format)) {
case GLYPHSET_INDEX_A1:
/* local bitmaps are always stored with bit == byte */
if (_cairo_is_little_endian() != (connection->root->bitmap_format_bit_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
int c = glyph_surface->stride * glyph_surface->height;
const uint8_t *d;
uint8_t *new, *n;
 
new = malloc (c);
if (unlikely (new == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
 
n = new;
d = data;
do {
uint8_t b = *d++;
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
*n++ = b;
} while (--c);
data = new;
}
break;
 
case GLYPHSET_INDEX_A8:
break;
 
case GLYPHSET_INDEX_ARGB32:
if (_cairo_is_little_endian() != (connection->root->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) {
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 */
 
_cairo_xcb_connection_render_add_glyphs (connection,
info->glyphset,
1, &glyph_index, &glyph_info,
glyph_surface->stride * glyph_surface->height,
data);
 
if (data != glyph_surface->data)
free (data);
 
status = _cairo_xcb_glyph_attach (connection, scaled_glyph, 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 (e.g. because image backend requested it), leave it in
* the cache
*/
if (! already_had_glyph_surface)
_cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL);
 
return status;
}
 
typedef void (*cairo_xcb_render_composite_text_func_t)
(cairo_xcb_connection_t *connection,
uint8_t op,
xcb_render_picture_t src,
xcb_render_picture_t dst,
xcb_render_pictformat_t mask_format,
xcb_render_glyphset_t glyphset,
int16_t src_x,
int16_t src_y,
uint32_t len,
uint8_t *cmd);
 
 
static cairo_status_t
_emit_glyphs_chunk (cairo_xcb_surface_t *dst,
cairo_operator_t op,
cairo_xcb_picture_t *src,
/* info for this chunk */
cairo_xcb_glyph_t *glyphs,
int num_glyphs,
int width,
int estimated_req_size,
cairo_xcb_font_glyphset_info_t *info,
xcb_render_pictformat_t mask_format)
{
cairo_xcb_render_composite_text_func_t composite_text_func;
uint8_t stack_buf[CAIRO_STACK_BUFFER_SIZE];
uint8_t *buf = stack_buf;
x_glyph_elt_t *elt = NULL; /* silence compiler */
uint32_t len;
int i;
 
if (estimated_req_size > ARRAY_LENGTH (stack_buf)) {
buf = malloc (estimated_req_size);
if (unlikely (buf == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
len = 0;
for (i = 0; i < num_glyphs; i++) {
if (_start_new_glyph_elt (i, &glyphs[i])) {
if (len & 3)
len += 4 - (len & 3);
 
elt = (x_glyph_elt_t *) (buf + len);
elt->len = 0;
elt->deltax = glyphs[i].i.x;
elt->deltay = glyphs[i].i.y;
len += sizeof (x_glyph_elt_t);
}
 
switch (width) {
case 1: *(uint8_t *) (buf + len) = glyphs[i].index; break;
case 2: *(uint16_t *) (buf + len) = glyphs[i].index; break;
default:
case 4: *(uint32_t *) (buf + len) = glyphs[i].index; break;
}
len += width;
elt->len++;
}
if (len & 3)
len += 4 - (len & 3);
 
switch (width) {
case 1:
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_8;
break;
case 2:
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_16;
break;
default:
case 4:
composite_text_func = _cairo_xcb_connection_render_composite_glyphs_32;
break;
}
composite_text_func (dst->connection,
_render_operator (op),
src->picture,
dst->picture,
mask_format,
info->glyphset,
src->x + glyphs[0].i.x,
src->y + glyphs[0].i.y,
len, buf);
 
if (buf != stack_buf)
free (buf);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_composite_glyphs (void *closure,
cairo_xcb_surface_t *dst,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_clip_t *clip)
{
composite_glyphs_info_t *info = closure;
cairo_scaled_glyph_t *glyph_cache[64];
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_fixed_t x = 0, y = 0;
cairo_xcb_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info;
const unsigned int max_request_size = dst->connection->maximum_request_length - 64;
cairo_xcb_picture_t *src;
 
unsigned long max_index = 0;
int width = 1;
 
unsigned int request_size = 0;
int i;
 
if (dst->deferred_clear) {
status = _cairo_xcb_surface_clear (dst);
if (unlikely (status))
return status;
}
 
src = _cairo_xcb_picture_for_pattern (dst, pattern, extents);
if (unlikely (src->base.status))
return src->base.status;
 
memset (glyph_cache, 0, sizeof (glyph_cache));
 
for (i = 0; i < info->num_glyphs; i++) {
cairo_scaled_glyph_t *glyph;
unsigned long glyph_index = info->glyphs[i].index;
int cache_index = glyph_index % ARRAY_LENGTH (glyph_cache);
int old_width = width;
int this_x, this_y;
 
glyph = glyph_cache[cache_index];
if (glyph == NULL ||
_cairo_scaled_glyph_index (glyph) != glyph_index)
{
status = _cairo_scaled_glyph_lookup (info->font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&glyph);
if (unlikely (status)) {
cairo_surface_destroy (&src->base);
return status;
}
 
/* Send unseen glyphs to the server */
if (glyph->dev_private_key != dst->connection) {
status = _cairo_xcb_surface_add_glyph (dst->connection,
info->font,
&glyph);
if (unlikely (status)) {
cairo_surface_destroy (&src->base);
return status;
}
}
 
glyph_cache[cache_index] = glyph;
}
 
this_x = _cairo_lround (info->glyphs[i].d.x) - dst_x;
this_y = _cairo_lround (info->glyphs[i].d.y) - dst_y;
 
this_glyphset_info = glyph->dev_private;
if (glyphset_info == NULL)
glyphset_info = this_glyphset_info;
 
/* Update max glyph index */
if (glyph_index > max_index) {
max_index = glyph_index;
if (max_index >= 65536)
width = 4;
else if (max_index >= 256)
width = 2;
if (width != old_width)
request_size += (width - old_width) * i;
}
 
/* If we will pass the max request size by adding this glyph,
* flush current glyphs. Note that we account for a
* possible element being added below.
*
* Also flush if changing glyphsets, as Xrender limits one mask
* format per request, so we can either break up, or use a
* wide-enough mask format. We do the former. One reason to
* prefer the latter is the fact that Xserver ADDs all glyphs
* to the mask first, and then composes that to final surface,
* though it's not a big deal.
*
* If the glyph has a coordinate which cannot be represented
* as a 16-bit offset from the previous glyph, flush the
* current chunk. The current glyph will be the first one in
* the next chunk, thus its coordinates will be an offset from
* the destination origin. This offset is guaranteed to be
* representable as 16-bit offset in _can_composite_glyphs().
*/
if (request_size + width > max_request_size - _cairo_sz_x_glyph_elt_t ||
this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
this_glyphset_info != glyphset_info)
{
status = _emit_glyphs_chunk (dst, op, src,
info->glyphs, i,
old_width, request_size,
glyphset_info,
info->use_mask ? glyphset_info->xrender_format : 0);
if (unlikely (status)) {
cairo_surface_destroy (&src->base);
return status;
}
 
info->glyphs += i;
info->num_glyphs -= i;
i = 0;
 
max_index = info->glyphs[0].index;
width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
 
request_size = 0;
 
x = y = 0;
glyphset_info = this_glyphset_info;
}
 
/* Convert absolute glyph position to relative-to-current-point
* position */
info->glyphs[i].i.x = this_x - x;
info->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 (i, &info->glyphs[i]))
request_size += _cairo_sz_x_glyph_elt_t;
 
/* adjust current-position */
x = this_x + glyph->x_advance;
y = this_y + glyph->y_advance;
 
request_size += width;
}
 
if (i) {
status = _emit_glyphs_chunk (dst, op, src,
info->glyphs, i,
width, request_size,
glyphset_info,
info->use_mask ? glyphset_info->xrender_format : 0);
}
 
cairo_surface_destroy (&src->base);
 
return status;
}
 
cairo_int_status_t
_cairo_xcb_render_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *composite,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) composite->surface;
cairo_operator_t op = composite->op;
cairo_pattern_t *source = &composite->source_pattern.base;
cairo_int_status_t status;
 
if (unlikely (! _operator_is_supported (surface->connection->flags, op)))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) {
_cairo_scaled_font_freeze_cache (scaled_font);
 
status = _can_composite_glyphs (surface, &composite->bounded,
scaled_font, glyphs, &num_glyphs);
if (likely (status == CAIRO_INT_STATUS_SUCCESS)) {
composite_glyphs_info_t info;
unsigned flags = 0;
 
info.font = scaled_font;
info.glyphs = (cairo_xcb_glyph_t *) glyphs;
info.num_glyphs = num_glyphs;
info.use_mask =
overlap ||
! composite->is_bounded ||
! _cairo_clip_is_region(composite->clip);
 
if (composite->mask.width > composite->unbounded.width ||
composite->mask.height > composite->unbounded.height)
{
/* Glyphs are tricky since we do not directly control the
* geometry and their inked extents depend on the
* individual glyph-surface size. We must set a clip region
* so that the X server can trim the glyphs appropriately.
*/
flags |= FORCE_CLIP_REGION;
}
status = _clip_and_composite (surface, op, source,
_composite_glyphs, NULL,
&info, composite,
need_bounded_clip (composite) |
flags);
}
 
_cairo_scaled_font_thaw_cache (scaled_font);
}
 
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
assert (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE);
status =
_cairo_xcb_surface_render_glyphs_via_mask (surface, op, source,
scaled_font, glyphs, num_glyphs,
composite);
}
 
return status;
}
/programs/develop/libraries/cairo/src/cairo-xcb-surface.c
0,0 → 1,1537
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Behdad Esfahbod <behdad@behdad.org>
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
 
#include "cairoint.h"
 
#include "cairo-xcb.h"
#include "cairo-xcb-private.h"
 
#include "cairo-composite-rectangles-private.h"
#include "cairo-default-context-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-list-inline.h"
#include "cairo-surface-backend-private.h"
#include "cairo-compositor-private.h"
 
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_proto (cairo_xcb_surface_create);
slim_hidden_proto (cairo_xcb_surface_create_for_bitmap);
slim_hidden_proto (cairo_xcb_surface_create_with_xrender_format);
#endif
 
/**
* SECTION:cairo-xcb
* @Title: XCB Surfaces
* @Short_Description: X Window System rendering using the XCB library
* @See_Also: #cairo_surface_t
*
* The XCB surface is used to render cairo graphics to X Window System
* windows and pixmaps using the XCB library.
*
* Note that the XCB surface automatically takes advantage of the X render
* extension if it is available.
**/
 
/**
* CAIRO_HAS_XCB_SURFACE:
*
* Defined if the xcb surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.12
**/
 
cairo_surface_t *
_cairo_xcb_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
int height)
{
cairo_xcb_surface_t *other = abstract_other;
cairo_xcb_surface_t *surface;
cairo_xcb_connection_t *connection;
xcb_pixmap_t pixmap;
cairo_status_t status;
 
if (unlikely(width > XLIB_COORD_MAX ||
height > XLIB_COORD_MAX ||
width <= 0 ||
height <= 0))
return cairo_image_surface_create (_cairo_format_from_content (content),
width, height);
 
if ((other->connection->flags & CAIRO_XCB_HAS_RENDER) == 0)
return _cairo_xcb_surface_create_similar_image (other,
_cairo_format_from_content (content),
width, height);
 
connection = other->connection;
status = _cairo_xcb_connection_acquire (connection);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (content == other->base.content) {
pixmap = _cairo_xcb_connection_create_pixmap (connection,
other->depth,
other->drawable,
width, height);
 
surface = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_internal (other->screen,
pixmap, TRUE,
other->pixman_format,
other->xrender_format,
width, height);
} else {
cairo_format_t format;
pixman_format_code_t pixman_format;
 
/* XXX find a compatible xrender format */
switch (content) {
case CAIRO_CONTENT_ALPHA:
pixman_format = PIXMAN_a8;
format = CAIRO_FORMAT_A8;
break;
case CAIRO_CONTENT_COLOR:
pixman_format = PIXMAN_x8r8g8b8;
format = CAIRO_FORMAT_RGB24;
break;
default:
ASSERT_NOT_REACHED;
case CAIRO_CONTENT_COLOR_ALPHA:
pixman_format = PIXMAN_a8r8g8b8;
format = CAIRO_FORMAT_ARGB32;
break;
}
 
pixmap = _cairo_xcb_connection_create_pixmap (connection,
PIXMAN_FORMAT_DEPTH (pixman_format),
other->drawable,
width, height);
 
surface = (cairo_xcb_surface_t *)
_cairo_xcb_surface_create_internal (other->screen,
pixmap, TRUE,
pixman_format,
connection->standard_formats[format],
width, height);
}
 
if (unlikely (surface->base.status))
_cairo_xcb_connection_free_pixmap (connection, pixmap);
 
_cairo_xcb_connection_release (connection);
 
return &surface->base;
}
 
cairo_surface_t *
_cairo_xcb_surface_create_similar_image (void *abstract_other,
cairo_format_t format,
int width,
int height)
{
cairo_xcb_surface_t *other = abstract_other;
cairo_xcb_connection_t *connection = other->connection;
 
cairo_xcb_shm_info_t *shm_info;
cairo_image_surface_t *image;
cairo_status_t status;
pixman_format_code_t pixman_format;
 
if (unlikely(width > XLIB_COORD_MAX ||
height > XLIB_COORD_MAX ||
width <= 0 ||
height <= 0))
return NULL;
 
pixman_format = _cairo_format_to_pixman_format_code (format);
 
status = _cairo_xcb_shm_image_create (connection, pixman_format,
width, height, &image,
&shm_info);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (! image->base.is_clear) {
memset (image->data, 0, image->stride * image->height);
image->base.is_clear = TRUE;
}
 
return &image->base;
}
 
static cairo_status_t
_cairo_xcb_surface_finish (void *abstract_surface)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_status_t status;
 
if (surface->fallback != NULL) {
cairo_surface_finish (&surface->fallback->base);
cairo_surface_destroy (&surface->fallback->base);
}
_cairo_boxes_fini (&surface->fallback_damage);
 
cairo_list_del (&surface->link);
 
status = _cairo_xcb_connection_acquire (surface->connection);
if (status == CAIRO_STATUS_SUCCESS) {
if (surface->picture != XCB_NONE) {
_cairo_xcb_connection_render_free_picture (surface->connection,
surface->picture);
}
 
if (surface->owns_pixmap)
_cairo_xcb_connection_free_pixmap (surface->connection, surface->drawable);
_cairo_xcb_connection_release (surface->connection);
}
 
_cairo_xcb_connection_destroy (surface->connection);
 
return status;
}
 
static void
_destroy_image (pixman_image_t *image, void *data)
{
free (data);
}
 
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
static cairo_surface_t *
_cairo_xcb_surface_create_shm_image (cairo_xcb_connection_t *connection,
pixman_format_code_t pixman_format,
int width, int height,
cairo_bool_t might_reuse,
cairo_xcb_shm_info_t **shm_info_out)
{
cairo_surface_t *image;
cairo_xcb_shm_info_t *shm_info;
cairo_int_status_t status;
size_t stride;
 
*shm_info_out = NULL;
 
stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width,
PIXMAN_FORMAT_BPP (pixman_format));
status = _cairo_xcb_connection_allocate_shm_info (connection,
stride * height,
might_reuse,
&shm_info);
if (unlikely (status)) {
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
return NULL;
 
return _cairo_surface_create_in_error (status);
}
 
image = _cairo_image_surface_create_with_pixman_format (shm_info->mem,
pixman_format,
width, height,
stride);
if (unlikely (image->status)) {
_cairo_xcb_shm_info_destroy (shm_info);
return image;
}
 
status = _cairo_user_data_array_set_data (&image->user_data,
(const cairo_user_data_key_t *) connection,
shm_info,
(cairo_destroy_func_t) _cairo_xcb_shm_info_destroy);
if (unlikely (status)) {
cairo_surface_destroy (image);
_cairo_xcb_shm_info_destroy (shm_info);
return _cairo_surface_create_in_error (status);
}
 
*shm_info_out = shm_info;
return image;
}
#endif
 
static cairo_surface_t *
_get_shm_image (cairo_xcb_surface_t *surface,
int x, int y,
int width, int height)
{
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
cairo_xcb_shm_info_t *shm_info;
cairo_surface_t *image;
cairo_status_t status;
 
if ((surface->connection->flags & CAIRO_XCB_HAS_SHM) == 0)
return NULL;
 
image = _cairo_xcb_surface_create_shm_image (surface->connection,
surface->pixman_format,
width, height,
TRUE,
&shm_info);
if (unlikely (image == NULL || image->status))
goto done;
 
status = _cairo_xcb_connection_shm_get_image (surface->connection,
surface->drawable,
x, y,
width, height,
shm_info->shm,
shm_info->offset);
if (unlikely (status)) {
cairo_surface_destroy (image);
image = _cairo_surface_create_in_error (status);
}
 
done:
return image;
#else
return NULL;
#endif
}
 
static cairo_surface_t *
_get_image (cairo_xcb_surface_t *surface,
cairo_bool_t use_shm,
int x, int y,
int width, int height)
{
cairo_surface_t *image;
cairo_xcb_connection_t *connection;
xcb_get_image_reply_t *reply;
cairo_int_status_t status;
 
assert (surface->fallback == NULL);
assert (x >= 0);
assert (y >= 0);
assert (x + width <= surface->width);
assert (y + height <= surface->height);
 
if (surface->deferred_clear) {
image =
_cairo_image_surface_create_with_pixman_format (NULL,
surface->pixman_format,
width, height,
0);
if (surface->deferred_clear_color.alpha_short > 0x00ff) {
cairo_solid_pattern_t solid;
 
_cairo_pattern_init_solid (&solid, &surface->deferred_clear_color);
status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&solid.base,
NULL);
if (unlikely (status)) {
cairo_surface_destroy (image);
image = _cairo_surface_create_in_error (status);
}
}
return image;
}
 
connection = surface->connection;
 
status = _cairo_xcb_connection_acquire (connection);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (use_shm) {
image = _get_shm_image (surface, x, y, width, height);
if (image) {
if (image->status == CAIRO_STATUS_SUCCESS) {
_cairo_xcb_connection_release (connection);
return image;
}
cairo_surface_destroy (image);
}
}
 
status = _cairo_xcb_connection_get_image (connection,
surface->drawable,
x, y,
width, height,
&reply);
if (unlikely (status))
goto FAIL;
 
if (reply == NULL && ! surface->owns_pixmap) {
/* xcb_get_image_t 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
*
* If we hit this fallback too often, we should remember so and
* skip the round-trip from the above GetImage request,
* similar to what cairo-xlib does.
*/
xcb_pixmap_t pixmap;
xcb_gcontext_t gc;
 
gc = _cairo_xcb_screen_get_gc (surface->screen,
surface->drawable,
surface->depth);
pixmap = _cairo_xcb_connection_create_pixmap (connection,
surface->depth,
surface->drawable,
width, height);
 
/* XXX IncludeInferiors? */
_cairo_xcb_connection_copy_area (connection,
surface->drawable,
pixmap, gc,
x, y,
0, 0,
width, height);
 
_cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
 
status = _cairo_xcb_connection_get_image (connection,
pixmap,
0, 0,
width, height,
&reply);
_cairo_xcb_connection_free_pixmap (connection, pixmap);
 
if (unlikely (status))
goto FAIL;
}
 
if (unlikely (reply == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto FAIL;
}
 
/* XXX byte swap */
/* XXX format conversion */
assert (reply->depth == surface->depth);
 
image = _cairo_image_surface_create_with_pixman_format
(xcb_get_image_data (reply),
surface->pixman_format,
width, height,
CAIRO_STRIDE_FOR_WIDTH_BPP (width,
PIXMAN_FORMAT_BPP (surface->pixman_format)));
status = image->status;
if (unlikely (status)) {
free (reply);
goto FAIL;
}
 
/* XXX */
pixman_image_set_destroy_function (((cairo_image_surface_t *)image)->pixman_image, _destroy_image, reply);
 
_cairo_xcb_connection_release (connection);
 
return image;
 
FAIL:
_cairo_xcb_connection_release (connection);
return _cairo_surface_create_in_error (status);
}
 
static cairo_surface_t *
_cairo_xcb_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_xcb_surface_t *surface = abstract_surface;
 
if (extents) {
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
}
 
return &surface->base;
}
 
static cairo_status_t
_cairo_xcb_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_surface_t *image;
 
if (surface->fallback != NULL) {
image = cairo_surface_reference (&surface->fallback->base);
goto DONE;
}
 
image = _cairo_surface_has_snapshot (&surface->base,
&_cairo_image_surface_backend);
if (image != NULL) {
image = cairo_surface_reference (image);
goto DONE;
}
 
image = _get_image (surface, FALSE, 0, 0, surface->width, surface->height);
if (unlikely (image->status))
return image->status;
 
_cairo_surface_attach_snapshot (&surface->base, image, NULL);
 
DONE:
*image_out = (cairo_image_surface_t *) image;
*image_extra = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_xcb_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_surface_destroy (&image->base);
}
 
cairo_bool_t
_cairo_xcb_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_xcb_surface_t *surface = abstract_surface;
 
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
return TRUE;
}
 
static void
_cairo_xcb_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
/* XXX copy from xlib */
_cairo_font_options_init_default (options);
_cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON);
}
 
static cairo_status_t
_put_shm_image (cairo_xcb_surface_t *surface,
xcb_gcontext_t gc,
cairo_image_surface_t *image)
{
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
cairo_xcb_shm_info_t *shm_info;
 
shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
(const cairo_user_data_key_t *) surface->connection);
if (shm_info == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
_cairo_xcb_connection_shm_put_image (surface->connection,
surface->drawable,
gc,
surface->width, surface->height,
0, 0,
image->width, image->height,
image->base.device_transform_inverse.x0,
image->base.device_transform_inverse.y0,
image->depth,
shm_info->shm,
shm_info->offset);
 
return CAIRO_STATUS_SUCCESS;
#else
return CAIRO_INT_STATUS_UNSUPPORTED;
#endif
}
 
static cairo_status_t
_put_image (cairo_xcb_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
 
/* XXX track damaged region? */
 
status = _cairo_xcb_connection_acquire (surface->connection);
if (unlikely (status))
return status;
 
if (image->pixman_format == surface->pixman_format) {
xcb_gcontext_t gc;
 
assert (image->depth == surface->depth);
assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
 
gc = _cairo_xcb_screen_get_gc (surface->screen,
surface->drawable,
surface->depth);
 
status = _put_shm_image (surface, gc, image);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
_cairo_xcb_connection_put_image (surface->connection,
surface->drawable, gc,
image->width, image->height,
image->base.device_transform_inverse.x0,
image->base.device_transform_inverse.y0,
image->depth,
image->stride,
image->data);
status = CAIRO_STATUS_SUCCESS;
}
 
_cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
} else {
ASSERT_NOT_REACHED;
}
 
_cairo_xcb_connection_release (surface->connection);
return status;
}
 
static cairo_int_status_t
_put_shm_image_boxes (cairo_xcb_surface_t *surface,
cairo_image_surface_t *image,
xcb_gcontext_t gc,
cairo_boxes_t *boxes)
{
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
cairo_xcb_shm_info_t *shm_info;
 
shm_info = _cairo_user_data_array_get_data (&image->base.user_data,
(const cairo_user_data_key_t *) surface->connection);
if (shm_info != NULL) {
struct _cairo_boxes_chunk *chunk;
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
int i;
 
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x = _cairo_fixed_integer_part (b->p1.x);
int y = _cairo_fixed_integer_part (b->p1.y);
int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
 
_cairo_xcb_connection_shm_put_image (surface->connection,
surface->drawable,
gc,
surface->width, surface->height,
x, y,
width, height,
x, y,
image->depth,
shm_info->shm,
shm_info->offset);
}
}
}
 
return CAIRO_INT_STATUS_SUCCESS;
#endif
 
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
static cairo_status_t
_put_image_boxes (cairo_xcb_surface_t *surface,
cairo_image_surface_t *image,
cairo_boxes_t *boxes)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
xcb_gcontext_t gc;
 
if (boxes->num_boxes == 0)
return CAIRO_STATUS_SUCCESS;
 
/* XXX track damaged region? */
 
status = _cairo_xcb_connection_acquire (surface->connection);
if (unlikely (status))
return status;
 
assert (image->pixman_format == surface->pixman_format);
assert (image->depth == surface->depth);
assert (image->stride == (int) CAIRO_STRIDE_FOR_WIDTH_BPP (image->width, PIXMAN_FORMAT_BPP (image->pixman_format)));
 
gc = _cairo_xcb_screen_get_gc (surface->screen,
surface->drawable,
surface->depth);
 
status = _put_shm_image_boxes (surface, image, gc, boxes);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
struct _cairo_boxes_chunk *chunk;
 
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
int i;
 
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x = _cairo_fixed_integer_part (b->p1.x);
int y = _cairo_fixed_integer_part (b->p1.y);
int width = _cairo_fixed_integer_part (b->p2.x - b->p1.x);
int height = _cairo_fixed_integer_part (b->p2.y - b->p1.y);
_cairo_xcb_connection_put_image (surface->connection,
surface->drawable, gc,
width, height,
x, y,
image->depth,
image->stride,
image->data +
x * PIXMAN_FORMAT_BPP (image->pixman_format) / 8 +
y * image->stride);
 
}
}
status = CAIRO_STATUS_SUCCESS;
}
 
_cairo_xcb_screen_put_gc (surface->screen, surface->depth, gc);
_cairo_xcb_connection_release (surface->connection);
return status;
}
 
static cairo_status_t
_cairo_xcb_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_status_t status;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
if (likely (surface->fallback == NULL)) {
status = CAIRO_STATUS_SUCCESS;
if (! surface->base.finished && surface->deferred_clear)
status = _cairo_xcb_surface_clear (surface);
 
return status;
}
 
status = surface->base.status;
if (status == CAIRO_STATUS_SUCCESS &&
(! surface->base._finishing || ! surface->owns_pixmap)) {
status = cairo_surface_status (&surface->fallback->base);
 
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_bentley_ottmann_tessellate_boxes (&surface->fallback_damage,
CAIRO_FILL_RULE_WINDING,
&surface->fallback_damage);
 
if (status == CAIRO_STATUS_SUCCESS)
status = _put_image_boxes (surface,
surface->fallback,
&surface->fallback_damage);
 
if (status == CAIRO_STATUS_SUCCESS && ! surface->base._finishing) {
_cairo_surface_attach_snapshot (&surface->base,
&surface->fallback->base,
cairo_surface_finish);
}
}
 
_cairo_boxes_clear (&surface->fallback_damage);
cairo_surface_destroy (&surface->fallback->base);
surface->fallback = NULL;
 
return status;
}
 
static cairo_image_surface_t *
_cairo_xcb_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_surface_t *image;
cairo_status_t status;
 
if (surface->fallback)
return _cairo_surface_map_to_image (&surface->fallback->base, extents);
 
image = _get_image (surface, TRUE,
extents->x, extents->y,
extents->width, extents->height);
status = cairo_surface_status (image);
if (unlikely (status)) {
cairo_surface_destroy(image);
return _cairo_image_surface_create_in_error (status);
}
 
/* Do we have a deferred clear and this image surface does NOT cover the
* whole xcb surface? Have to apply the clear in that case, else
* uploading the image will handle the problem for us.
*/
if (surface->deferred_clear &&
! (extents->width == surface->width &&
extents->height == surface->height)) {
status = _cairo_xcb_surface_clear (surface);
if (unlikely (status)) {
cairo_surface_destroy(image);
return _cairo_image_surface_create_in_error (status);
}
}
surface->deferred_clear = FALSE;
 
cairo_surface_set_device_offset (image, -extents->x, -extents->y);
return (cairo_image_surface_t *) image;
}
 
static cairo_int_status_t
_cairo_xcb_surface_unmap (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_xcb_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
if (surface->fallback)
return _cairo_surface_unmap_image (&surface->fallback->base, image);
 
status = _put_image (abstract_surface, image);
 
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return status;
}
 
static cairo_surface_t *
_cairo_xcb_surface_fallback (cairo_xcb_surface_t *surface,
cairo_composite_rectangles_t *composite)
{
cairo_image_surface_t *image;
cairo_status_t status;
 
status = _cairo_composite_rectangles_add_to_damage (composite,
&surface->fallback_damage);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
if (surface->fallback)
return &surface->fallback->base;
 
image = (cairo_image_surface_t *)
_get_image (surface, TRUE, 0, 0, surface->width, surface->height);
 
/* If there was a deferred clear, _get_image applied it */
if (image->base.status == CAIRO_STATUS_SUCCESS) {
surface->deferred_clear = FALSE;
 
surface->fallback = image;
}
 
return &surface->fallback->base;
}
 
static cairo_int_status_t
_cairo_xcb_fallback_compositor_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
 
return _cairo_surface_paint (fallback, extents->op,
&extents->source_pattern.base,
extents->clip);
}
 
static cairo_int_status_t
_cairo_xcb_fallback_compositor_mask (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
 
return _cairo_surface_mask (fallback, extents->op,
&extents->source_pattern.base,
&extents->mask_pattern.base,
extents->clip);
}
 
static cairo_int_status_t
_cairo_xcb_fallback_compositor_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
 
return _cairo_surface_stroke (fallback, extents->op,
&extents->source_pattern.base,
path, style, ctm, ctm_inverse,
tolerance, antialias,
extents->clip);
}
 
static cairo_int_status_t
_cairo_xcb_fallback_compositor_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
 
return _cairo_surface_fill (fallback, extents->op,
&extents->source_pattern.base,
path, fill_rule, tolerance,
antialias, extents->clip);
}
 
static cairo_int_status_t
_cairo_xcb_fallback_compositor_glyphs (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t *) extents->surface;
cairo_surface_t *fallback = _cairo_xcb_surface_fallback (surface, extents);
 
return _cairo_surface_show_text_glyphs (fallback, extents->op,
&extents->source_pattern.base,
NULL, 0, glyphs, num_glyphs,
NULL, 0, 0,
scaled_font, extents->clip);
}
 
static const cairo_compositor_t _cairo_xcb_fallback_compositor = {
&__cairo_no_compositor,
 
_cairo_xcb_fallback_compositor_paint,
_cairo_xcb_fallback_compositor_mask,
_cairo_xcb_fallback_compositor_stroke,
_cairo_xcb_fallback_compositor_fill,
_cairo_xcb_fallback_compositor_glyphs,
};
 
static const cairo_compositor_t _cairo_xcb_render_compositor = {
&_cairo_xcb_fallback_compositor,
 
_cairo_xcb_render_compositor_paint,
_cairo_xcb_render_compositor_mask,
_cairo_xcb_render_compositor_stroke,
_cairo_xcb_render_compositor_fill,
_cairo_xcb_render_compositor_glyphs,
};
 
static inline const cairo_compositor_t *
get_compositor (cairo_surface_t **s)
{
cairo_xcb_surface_t *surface = (cairo_xcb_surface_t * )*s;
if (surface->fallback) {
*s = &surface->fallback->base;
return ((cairo_image_surface_t *) *s)->compositor;
}
 
return &_cairo_xcb_render_compositor;
}
 
static cairo_int_status_t
_cairo_xcb_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_surface_t *surface = abstract_surface;
const cairo_compositor_t *compositor = get_compositor (&surface);
return _cairo_compositor_paint (compositor, surface, op, source, clip);
}
 
static cairo_int_status_t
_cairo_xcb_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_surface_t *surface = abstract_surface;
const cairo_compositor_t *compositor = get_compositor (&surface);
return _cairo_compositor_mask (compositor, surface, op, source, mask, clip);
}
 
static cairo_int_status_t
_cairo_xcb_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_surface_t *surface = abstract_surface;
const cairo_compositor_t *compositor = get_compositor (&surface);
return _cairo_compositor_stroke (compositor, surface, op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias, clip);
}
 
static cairo_int_status_t
_cairo_xcb_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_surface_t *surface = abstract_surface;
const cairo_compositor_t *compositor = get_compositor (&surface);
return _cairo_compositor_fill (compositor, surface, op,
source, path, fill_rule,
tolerance, antialias, clip);
}
 
static cairo_int_status_t
_cairo_xcb_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_surface_t *surface = abstract_surface;
const cairo_compositor_t *compositor = get_compositor (&surface);
return _cairo_compositor_glyphs (compositor, surface, op,
source, glyphs, num_glyphs,
scaled_font, clip);
}
 
const cairo_surface_backend_t _cairo_xcb_surface_backend = {
CAIRO_SURFACE_TYPE_XCB,
_cairo_xcb_surface_finish,
_cairo_default_context_create,
 
_cairo_xcb_surface_create_similar,
_cairo_xcb_surface_create_similar_image,
_cairo_xcb_surface_map_to_image,
_cairo_xcb_surface_unmap,
 
_cairo_xcb_surface_source,
_cairo_xcb_surface_acquire_source_image,
_cairo_xcb_surface_release_source_image,
NULL, /* snapshot */
 
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_xcb_surface_get_extents,
_cairo_xcb_surface_get_font_options,
 
_cairo_xcb_surface_flush,
NULL,
 
_cairo_xcb_surface_paint,
_cairo_xcb_surface_mask,
_cairo_xcb_surface_stroke,
_cairo_xcb_surface_fill,
NULL, /* fill-stroke */
_cairo_xcb_surface_glyphs,
};
 
cairo_surface_t *
_cairo_xcb_surface_create_internal (cairo_xcb_screen_t *screen,
xcb_drawable_t drawable,
cairo_bool_t owns_pixmap,
pixman_format_code_t pixman_format,
xcb_render_pictformat_t xrender_format,
int width,
int height)
{
cairo_xcb_surface_t *surface;
 
surface = malloc (sizeof (cairo_xcb_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_xcb_surface_backend,
&screen->connection->device,
_cairo_content_from_pixman_format (pixman_format));
 
surface->connection = _cairo_xcb_connection_reference (screen->connection);
surface->screen = screen;
cairo_list_add (&surface->link, &screen->surfaces);
 
surface->drawable = drawable;
surface->owns_pixmap = owns_pixmap;
 
surface->deferred_clear = FALSE;
surface->deferred_clear_color = *CAIRO_COLOR_TRANSPARENT;
 
surface->width = width;
surface->height = height;
surface->depth = PIXMAN_FORMAT_DEPTH (pixman_format);
 
surface->picture = XCB_NONE;
if (screen->connection->force_precision != -1)
surface->precision = screen->connection->force_precision;
else
surface->precision = XCB_RENDER_POLY_MODE_IMPRECISE;
 
surface->pixman_format = pixman_format;
surface->xrender_format = xrender_format;
 
surface->fallback = NULL;
_cairo_boxes_init (&surface->fallback_damage);
 
return &surface->base;
}
 
static xcb_screen_t *
_cairo_xcb_screen_from_visual (xcb_connection_t *connection,
xcb_visualtype_t *visual,
int *depth)
{
xcb_depth_iterator_t d;
xcb_screen_iterator_t s;
 
s = xcb_setup_roots_iterator (xcb_get_setup (connection));
for (; s.rem; xcb_screen_next (&s)) {
if (s.data->root_visual == visual->visual_id) {
*depth = s.data->root_depth;
return s.data;
}
 
d = xcb_screen_allowed_depths_iterator(s.data);
for (; d.rem; xcb_depth_next (&d)) {
xcb_visualtype_iterator_t v = xcb_depth_visuals_iterator (d.data);
 
for (; v.rem; xcb_visualtype_next (&v)) {
if (v.data->visual_id == visual->visual_id) {
*depth = d.data->depth;
return s.data;
}
}
}
}
 
return NULL;
}
 
/**
* cairo_xcb_surface_create:
* @connection: an XCB connection
* @drawable: an XCB drawable
* @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 XCB 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_xcb_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: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_xcb_surface_create (xcb_connection_t *connection,
xcb_drawable_t drawable,
xcb_visualtype_t *visual,
int width,
int height)
{
cairo_xcb_screen_t *screen;
xcb_screen_t *xcb_screen;
cairo_format_masks_t image_masks;
pixman_format_code_t pixman_format;
xcb_render_pictformat_t xrender_format;
int depth;
 
if (xcb_connection_has_error (connection))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
 
if (unlikely (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
if (unlikely (width <= 0 || height <= 0))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
xcb_screen = _cairo_xcb_screen_from_visual (connection, visual, &depth);
if (unlikely (xcb_screen == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
 
image_masks.alpha_mask = 0;
image_masks.red_mask = visual->red_mask;
image_masks.green_mask = visual->green_mask;
image_masks.blue_mask = visual->blue_mask;
if (depth == 32) /* XXX visuals have no alpha! */
image_masks.alpha_mask =
0xffffffff & ~(visual->red_mask | visual->green_mask | visual->blue_mask);
if (depth > 16)
image_masks.bpp = 32;
else if (depth > 8)
image_masks.bpp = 16;
else if (depth > 1)
image_masks.bpp = 8;
else
image_masks.bpp = 1;
 
if (! _pixman_format_from_masks (&image_masks, &pixman_format))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
screen = _cairo_xcb_screen_get (connection, xcb_screen);
if (unlikely (screen == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
xrender_format =
_cairo_xcb_connection_get_xrender_format_for_visual (screen->connection,
visual->visual_id);
 
return _cairo_xcb_surface_create_internal (screen, drawable, FALSE,
pixman_format,
xrender_format,
width, height);
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_surface_create);
#endif
 
/**
* cairo_xcb_surface_create_for_bitmap:
* @connection: an XCB connection
* @screen: the XCB screen associated with @bitmap
* @bitmap: an XCB drawable (a Pixmap with depth 1)
* @width: the current width of @bitmap
* @height: the current height of @bitmap
*
* Creates an XCB surface that draws to the given bitmap.
* This will be drawn to as a %CAIRO_FORMAT_A1 object.
*
* Return value: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_xcb_surface_create_for_bitmap (xcb_connection_t *connection,
xcb_screen_t *screen,
xcb_pixmap_t bitmap,
int width,
int height)
{
cairo_xcb_screen_t *cairo_xcb_screen;
 
if (xcb_connection_has_error (connection))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
 
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
if (unlikely (width <= 0 || height <= 0))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen);
if (unlikely (cairo_xcb_screen == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
return _cairo_xcb_surface_create_internal (cairo_xcb_screen, bitmap, FALSE,
PIXMAN_a1,
cairo_xcb_screen->connection->standard_formats[CAIRO_FORMAT_A1],
width, height);
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_surface_create_for_bitmap);
#endif
 
/**
* cairo_xcb_surface_create_with_xrender_format:
* @connection: an XCB connection
* @drawable: an XCB drawable
* @screen: the XCB screen associated with @drawable
* @format: the picture format to use for drawing to @drawable. The
* depth of @format mush match the depth of the drawable.
* @width: the current width of @drawable
* @height: the current height of @drawable
*
* Creates an XCB 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_xcb_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: a pointer to the newly created surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* This function always returns a valid pointer, but it will return a
* pointer to a "nil" surface if an error such as out of memory
* occurs. You can use cairo_surface_status() to check for this.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_xcb_surface_create_with_xrender_format (xcb_connection_t *connection,
xcb_screen_t *screen,
xcb_drawable_t drawable,
xcb_render_pictforminfo_t *format,
int width,
int height)
{
cairo_xcb_screen_t *cairo_xcb_screen;
cairo_format_masks_t image_masks;
pixman_format_code_t pixman_format;
 
if (xcb_connection_has_error (connection))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
 
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
if (unlikely (width <= 0 || height <= 0))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
image_masks.alpha_mask =
(unsigned long) format->direct.alpha_mask << format->direct.alpha_shift;
image_masks.red_mask =
(unsigned long) format->direct.red_mask << format->direct.red_shift;
image_masks.green_mask =
(unsigned long) format->direct.green_mask << format->direct.green_shift;
image_masks.blue_mask =
(unsigned long) format->direct.blue_mask << format->direct.blue_shift;
#if 0
image_masks.bpp = format->depth;
#else
if (format->depth > 16)
image_masks.bpp = 32;
else if (format->depth > 8)
image_masks.bpp = 16;
else if (format->depth > 1)
image_masks.bpp = 8;
else
image_masks.bpp = 1;
#endif
 
if (! _pixman_format_from_masks (&image_masks, &pixman_format))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
 
cairo_xcb_screen = _cairo_xcb_screen_get (connection, screen);
if (unlikely (cairo_xcb_screen == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
return _cairo_xcb_surface_create_internal (cairo_xcb_screen,
drawable,
FALSE,
pixman_format,
format->id,
width, height);
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_surface_create_with_xrender_format);
#endif
 
/* This does the necessary fixup when a surface's drawable or size changed. */
static void
_drawable_changed (cairo_xcb_surface_t *surface)
{
_cairo_surface_set_error (&surface->base,
_cairo_surface_begin_modification (&surface->base));
_cairo_boxes_clear (&surface->fallback_damage);
cairo_surface_destroy (&surface->fallback->base);
 
surface->deferred_clear = FALSE;
surface->fallback = NULL;
}
 
/**
* cairo_xcb_surface_set_size:
* @surface: a #cairo_surface_t for the XCB backend
* @width: the new width of the surface
* @height: the new height of the surface
*
* Informs cairo of the new size of the XCB 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.
*
* If cairo_surface_flush() wasn't called, some pending operations
* might be discarded.
*
* Since: 1.12
**/
void
cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface,
int width,
int height)
{
cairo_xcb_surface_t *surface;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
 
if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_SIZE));
return;
}
 
surface = (cairo_xcb_surface_t *) abstract_surface;
 
_drawable_changed(surface);
surface->width = width;
surface->height = height;
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_surface_set_size);
#endif
 
/**
* cairo_xcb_surface_set_drawable:
* @surface: a #cairo_surface_t for the XCB backend
* @drawable: the new drawable of the surface
* @width: the new width of the surface
* @height: the new height of the surface
*
* Informs cairo of the new drawable and size of the XCB drawable underlying the
* surface.
*
* If cairo_surface_flush() wasn't called, some pending operations
* might be discarded.
*
* Since: 1.12
**/
void
cairo_xcb_surface_set_drawable (cairo_surface_t *abstract_surface,
xcb_drawable_t drawable,
int width,
int height)
{
cairo_xcb_surface_t *surface;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
 
if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_SIZE));
return;
}
 
surface = (cairo_xcb_surface_t *) abstract_surface;
 
/* XXX: and what about this case? */
if (surface->owns_pixmap)
return;
 
_drawable_changed (surface);
 
if (surface->drawable != drawable) {
cairo_status_t status;
status = _cairo_xcb_connection_acquire (surface->connection);
if (unlikely (status))
return;
 
if (surface->picture != XCB_NONE) {
_cairo_xcb_connection_render_free_picture (surface->connection,
surface->picture);
surface->picture = XCB_NONE;
}
 
_cairo_xcb_connection_release (surface->connection);
 
surface->drawable = drawable;
}
surface->width = width;
surface->height = height;
}
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
slim_hidden_def (cairo_xcb_surface_set_drawable);
#endif
/programs/develop/libraries/cairo/src/cairo-xcb.h
75,6 → 75,15
int width,
int height);
 
cairo_public void
cairo_xcb_surface_set_drawable (cairo_surface_t *surface,
xcb_drawable_t drawable,
int width,
int height);
 
cairo_public xcb_connection_t *
cairo_xcb_device_get_connection (cairo_device_t *device);
 
/* debug interface */
 
cairo_public void
87,6 → 96,17
int major_version,
int minor_version);
 
/*
* @precision: -1 implies automatically choose based on antialiasing mode,
* any other value overrides and sets the corresponding PolyMode.
*/
cairo_public void
cairo_xcb_device_debug_set_precision (cairo_device_t *device,
int precision);
 
cairo_public int
cairo_xcb_device_debug_get_precision (cairo_device_t *device);
 
CAIRO_END_DECLS
 
#else /* CAIRO_HAS_XCB_SURFACE */
/programs/develop/libraries/cairo/src/cairo-xlib-core-compositor.c
0,0 → 1,652
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Behdad Esfahbod <behdad@behdad.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
 
/* The original X drawing API was very restrictive in what it could handle,
* pixel-aligned fill/blits are all that map into Cairo's drawing model.
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
#include "cairo-xlib-surface-private.h"
 
#include "cairo-boxes-private.h"
#include "cairo-clip-inline.h"
#include "cairo-compositor-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-pattern-private.h"
#include "cairo-region-private.h"
#include "cairo-surface-offset-private.h"
 
/* the low-level interface */
 
static cairo_int_status_t
acquire (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
return _cairo_xlib_display_acquire (dst->base.device, &dst->display);
}
 
static cairo_int_status_t
release (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
 
cairo_device_release (&dst->display->base);
dst->display = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
struct _fill_box {
Display *dpy;
Drawable drawable;
GC gc;
//cairo_surface_t *dither = NULL;
};
 
static cairo_bool_t fill_box (cairo_box_t *box, void *closure)
{
struct _fill_box *data = closure;
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
 
XFillRectangle (data->dpy, data->drawable, data->gc, x, y, width, height);
return TRUE;
}
 
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;
}
 
static uint32_t
color_to_pixel (cairo_xlib_surface_t *dst,
const cairo_color_t *color)
{
uint32_t rgba = 0;
int width, shift;
 
_characterize_field (dst->a_mask, &width, &shift);
rgba |= color->alpha_short >> (16 - width) << shift;
 
_characterize_field (dst->r_mask, &width, &shift);
rgba |= color->red_short >> (16 - width) << shift;
 
_characterize_field (dst->g_mask, &width, &shift);
rgba |= color->green_short >> (16 - width) << shift;
 
_characterize_field (dst->b_mask, &width, &shift);
rgba |= color->blue_short >> (16 - width) << shift;
 
return rgba;
}
 
static cairo_int_status_t
_fill_box_init (struct _fill_box *fb,
cairo_xlib_surface_t *dst,
const cairo_color_t *color)
{
cairo_int_status_t status;
 
status = _cairo_xlib_surface_get_gc (dst->display, dst, &fb->gc);
if (unlikely (status))
return status;
 
fb->dpy = dst->display->display;
fb->drawable = dst->drawable;
 
if (dst->visual && dst->visual->class != TrueColor && 0) {
#if 0
cairo_solid_pattern_t solid;
cairo_surface_attributes_t attrs;
 
_cairo_pattern_init_solid (&solid, color);
status = _cairo_pattern_acquire_surface (&solid.base, &dst->base,
0, 0,
ARRAY_LENGTH (dither_pattern[0]),
ARRAY_LENGTH (dither_pattern),
CAIRO_PATTERN_ACQUIRE_NONE,
&dither,
&attrs);
if (unlikely (status)) {
_cairo_xlib_surface_put_gc (dst->display, dst, fb.gc);
return status;
}
 
XSetTSOrigin (fb->dpy, fb->gc,
- (dst->base.device_transform.x0 + attrs.x_offset),
- (dst->base.device_transform.y0 + attrs.y_offset));
XSetTile (fb->dpy, fb->gc, ((cairo_xlib_surface_t *) dither)->drawable);
#endif
} else {
XGCValues gcv;
 
gcv.foreground = color_to_pixel (dst, color);
gcv.fill_style = FillSolid;
 
XChangeGC (fb->dpy, fb->gc, GCFillStyle | GCForeground, &gcv);
}
 
return CAIRO_INT_STATUS_SUCCESS;
}
 
static void
_fill_box_fini (struct _fill_box *fb,
cairo_xlib_surface_t *dst)
{
_cairo_xlib_surface_put_gc (dst->display, dst, fb->gc);
//cairo_surface_destroy (fb->dither);
}
 
cairo_int_status_t
_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_int_status_t status;
struct _fill_box fb;
 
status = _fill_box_init (&fb, dst, color);
if (unlikely (status))
return status;
 
_cairo_boxes_for_each_box (boxes, fill_box, &fb);
 
_fill_box_fini (&fb, dst);
return CAIRO_STATUS_SUCCESS;
}
 
cairo_int_status_t
_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst,
const cairo_color_t *color,
int num_rects,
cairo_rectangle_int_t *rects)
{
cairo_int_status_t status;
struct _fill_box fb;
int i;
 
status = _fill_box_init (&fb, dst, color);
if (unlikely (status))
return status;
 
for (i = 0; i < num_rects; i++)
XFillRectangle (fb.dpy, fb.drawable, fb.gc,
rects[i].x, rects[i].y,
rects[i].width, rects[i].height);
 
_fill_box_fini (&fb, dst);
return CAIRO_STATUS_SUCCESS;
}
 
struct _fallback_box {
cairo_xlib_surface_t *dst;
cairo_format_t format;
const cairo_pattern_t *pattern;
};
 
static cairo_bool_t fallback_box (cairo_box_t *box, void *closure)
{
struct _fallback_box *data = closure;
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
cairo_surface_t *image;
cairo_status_t status;
 
/* XXX for EXTEND_NONE and if the box is wholly outside we can just fill */
 
image = cairo_surface_create_similar_image (&data->dst->base, data->format,
width, height);
status = _cairo_surface_offset_paint (image, x, y,
CAIRO_OPERATOR_SOURCE,
data->pattern, NULL);
if (status == CAIRO_STATUS_SUCCESS) {
status = _cairo_xlib_surface_draw_image (data->dst,
(cairo_image_surface_t *)image,
0, 0,
width, height,
x, y);
}
cairo_surface_destroy (image);
 
return status == CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
fallback_boxes (cairo_xlib_surface_t *dst,
const cairo_pattern_t *pattern,
cairo_boxes_t *boxes)
{
struct _fallback_box fb;
 
/* XXX create_similar_image using pixman_format? */
switch (dst->depth) {
case 8: fb.format = CAIRO_FORMAT_A8; break;
case 16: fb.format = CAIRO_FORMAT_RGB16_565; break;
case 24: fb.format = CAIRO_FORMAT_RGB24; break;
case 30: fb.format = CAIRO_FORMAT_RGB30; break;
case 32: fb.format = CAIRO_FORMAT_ARGB32; break;
default: return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
fb.dst = dst;
fb.pattern = pattern;
 
if (! _cairo_boxes_for_each_box (boxes, fallback_box, &fb))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
render_boxes (cairo_xlib_surface_t *dst,
const cairo_pattern_t *pattern,
cairo_boxes_t *boxes)
{
double pad;
 
if (_cairo_pattern_analyze_filter (pattern, &pad) != CAIRO_FILTER_NEAREST)
return fallback_boxes (dst, pattern, boxes);
 
switch (pattern->extend) {
default:
case CAIRO_EXTEND_NONE:
case CAIRO_EXTEND_REFLECT:
case CAIRO_EXTEND_PAD:
return fallback_boxes (dst, pattern, boxes);
 
case CAIRO_EXTEND_REPEAT: /* XXX Use tiling */
return fallback_boxes (dst, pattern, boxes);
}
}
 
/* the mid-level: converts boxes into drawing operations */
 
struct _box_data {
Display *dpy;
cairo_xlib_surface_t *dst;
cairo_surface_t *src;
GC gc;
int tx, ty;
int width, height;
};
 
static cairo_bool_t source_contains_box (cairo_box_t *box, void *closure)
{
struct _box_data *data = closure;
 
/* The box is pixel-aligned so the truncation is safe. */
return
_cairo_fixed_integer_part (box->p1.x) + data->tx >= 0 &&
_cairo_fixed_integer_part (box->p1.y) + data->ty >= 0 &&
_cairo_fixed_integer_part (box->p2.x) + data->tx <= data->width &&
_cairo_fixed_integer_part (box->p2.y) + data->ty <= data->height;
}
 
static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure)
{
const struct _box_data *iub = closure;
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
 
return _cairo_xlib_surface_draw_image (iub->dst,
(cairo_image_surface_t *)iub->src,
x + iub->tx, y + iub->ty,
width, height,
x, y) == CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
surface_matches_image_format (cairo_xlib_surface_t *surface,
cairo_image_surface_t *image)
{
cairo_format_masks_t format;
 
return (_pixman_format_to_masks (image->pixman_format, &format) &&
(format.alpha_mask == surface->a_mask || surface->a_mask == 0) &&
(format.red_mask == surface->r_mask || surface->r_mask == 0) &&
(format.green_mask == surface->g_mask || surface->g_mask == 0) &&
(format.blue_mask == surface->b_mask || surface->b_mask == 0));
}
 
static cairo_status_t
upload_image_inplace (cairo_xlib_surface_t *dst,
const cairo_pattern_t *source,
cairo_boxes_t *boxes)
{
const cairo_surface_pattern_t *pattern;
struct _box_data iub;
cairo_image_surface_t *image;
 
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
pattern = (const cairo_surface_pattern_t *) source;
if (pattern->surface->type != CAIRO_SURFACE_TYPE_IMAGE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
image = (cairo_image_surface_t *) pattern->surface;
if (image->format == CAIRO_FORMAT_INVALID)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (image->depth != dst->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! surface_matches_image_format (dst, image))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* XXX subsurface */
 
if (! _cairo_matrix_is_integer_translation (&source->matrix,
&iub.tx, &iub.ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
iub.dst = dst;
iub.src = &image->base;
iub.width = image->width;
iub.height = image->height;
 
/* First check that the data is entirely within the image */
if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &iub))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_boxes_for_each_box (boxes, image_upload_box, &iub))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t copy_box (cairo_box_t *box, void *closure)
{
const struct _box_data *cb = closure;
int x = _cairo_fixed_integer_part (box->p1.x);
int y = _cairo_fixed_integer_part (box->p1.y);
int width = _cairo_fixed_integer_part (box->p2.x - box->p1.x);
int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y);
 
XCopyArea (cb->dpy,
((cairo_xlib_surface_t *)cb->src)->drawable,
cb->dst->drawable,
cb->gc,
x + cb->tx, y + cb->ty,
width, height,
x, y);
return TRUE;
}
 
static cairo_status_t
copy_boxes (cairo_xlib_surface_t *dst,
const cairo_pattern_t *source,
cairo_boxes_t *boxes)
{
const cairo_surface_pattern_t *pattern;
struct _box_data cb;
cairo_xlib_surface_t *src;
cairo_status_t status;
 
if (source->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* XXX subsurface */
 
pattern = (const cairo_surface_pattern_t *) source;
if (pattern->surface->backend->type != CAIRO_SURFACE_TYPE_XLIB)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
src = (cairo_xlib_surface_t *) pattern->surface;
if (src->depth != dst->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* We can only have a single control for subwindow_mode on the
* GC. If we have a Window destination, we need to set ClipByChildren,
* but if we have a Window source, we need IncludeInferiors. If we have
* both a Window destination and source, we must fallback. There is
* no convenient way to detect if a drawable is a Pixmap or Window,
* therefore we can only rely on those surfaces that we created
* ourselves to be Pixmaps, and treat everything else as a potential
* Window.
*/
if (! src->owns_pixmap && ! dst->owns_pixmap)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_xlib_surface_same_screen (dst, src))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! _cairo_matrix_is_integer_translation (&source->matrix,
&cb.tx, &cb.ty))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
cb.dpy = dst->display->display;
cb.dst = dst;
cb.src = &src->base;
cb.width = src->width;
cb.height = src->height;
 
/* First check that the data is entirely within the image */
if (! _cairo_boxes_for_each_box (boxes, source_contains_box, &cb))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_xlib_surface_get_gc (dst->display, dst, &cb.gc);
if (unlikely (status))
return status;
 
if (! src->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = IncludeInferiors;
XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv);
}
 
status = CAIRO_STATUS_SUCCESS;
if (! _cairo_boxes_for_each_box (boxes, copy_box, &cb))
status = CAIRO_INT_STATUS_UNSUPPORTED;
 
if (! src->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = ClipByChildren;
XChangeGC (dst->display->display, cb.gc, GCSubwindowMode, &gcv);
}
 
_cairo_xlib_surface_put_gc (dst->display, dst, cb.gc);
 
return status;
}
 
static cairo_status_t
draw_boxes (cairo_composite_rectangles_t *extents,
cairo_boxes_t *boxes)
{
cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
cairo_operator_t op = extents->op;
const cairo_pattern_t *src = &extents->source_pattern.base;
cairo_int_status_t status;
 
if (boxes->num_boxes == 0 && extents->is_bounded)
return CAIRO_STATUS_SUCCESS;
 
if (! boxes->is_pixel_aligned)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (op == CAIRO_OPERATOR_CLEAR)
op = CAIRO_OPERATOR_SOURCE;
 
if (op == CAIRO_OPERATOR_OVER &&
_cairo_pattern_is_opaque (src, &extents->bounded))
op = CAIRO_OPERATOR_SOURCE;
 
if (dst->base.is_clear && op == CAIRO_OPERATOR_OVER)
op = CAIRO_OPERATOR_SOURCE;
 
if (op != CAIRO_OPERATOR_SOURCE)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = acquire (dst);
if (unlikely (status))
return status;
 
if (src->type == CAIRO_PATTERN_TYPE_SOLID) {
status = _cairo_xlib_core_fill_boxes
(dst, &((cairo_solid_pattern_t *) src)->color, boxes);
} else {
status = upload_image_inplace (dst, src, boxes);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status = copy_boxes (dst, src, boxes);
if (status == CAIRO_INT_STATUS_UNSUPPORTED)
status = render_boxes (dst, src, boxes);
}
 
release (dst);
 
return status;
}
 
/* high-level compositor interface */
 
static cairo_int_status_t
_cairo_xlib_core_compositor_paint (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents)
{
cairo_int_status_t status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_clip_is_region (extents->clip)) {
cairo_boxes_t boxes;
 
_cairo_clip_steal_boxes (extents->clip, &boxes);
status = draw_boxes (extents, &boxes);
_cairo_clip_unsteal_boxes (extents->clip, &boxes);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_xlib_core_compositor_stroke (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_int_status_t status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (extents->clip->path == NULL &&
_cairo_path_fixed_stroke_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
style,
ctm,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = draw_boxes (extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
return status;
}
 
static cairo_int_status_t
_cairo_xlib_core_compositor_fill (const cairo_compositor_t *compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_int_status_t status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (extents->clip->path == NULL &&
_cairo_path_fixed_fill_is_rectilinear (path)) {
cairo_boxes_t boxes;
 
_cairo_boxes_init_with_clip (&boxes, extents->clip);
status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
&boxes);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = draw_boxes (extents, &boxes);
_cairo_boxes_fini (&boxes);
}
 
return status;
}
 
const cairo_compositor_t *
_cairo_xlib_core_compositor_get (void)
{
static cairo_compositor_t compositor;
 
if (compositor.delegate == NULL) {
compositor.delegate = _cairo_xlib_fallback_compositor_get ();
 
compositor.paint = _cairo_xlib_core_compositor_paint;
compositor.mask = NULL;
compositor.fill = _cairo_xlib_core_compositor_fill;
compositor.stroke = _cairo_xlib_core_compositor_stroke;
compositor.glyphs = NULL; /* XXX PolyGlyph? */
}
 
return &compositor;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-display.c
0,0 → 1,661
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2007 Chris Wilson
*
* 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 Chris Wilson.
*
* Contributor(s):
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
#include "cairo-xlib-xrender-private.h"
#include "cairo-freelist-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
 
#include <X11/Xlibint.h> /* For XESetCloseDisplay */
 
typedef int (*cairo_xlib_error_func_t) (Display *display,
XErrorEvent *event);
 
static cairo_xlib_display_t *_cairo_xlib_display_list;
 
static int
_noop_error_handler (Display *display,
XErrorEvent *event)
{
return False; /* return value is ignored */
}
 
static void
_cairo_xlib_display_finish (void *abstract_display)
{
cairo_xlib_display_t *display = abstract_display;
Display *dpy = display->display;
 
_cairo_xlib_display_fini_shm (display);
 
if (! cairo_device_acquire (&display->base)) {
cairo_xlib_error_func_t old_handler;
 
/* protect the notifies from triggering XErrors */
XSync (dpy, False);
old_handler = XSetErrorHandler (_noop_error_handler);
 
while (! cairo_list_is_empty (&display->fonts)) {
_cairo_xlib_font_close (cairo_list_first_entry (&display->fonts,
cairo_xlib_font_t,
link));
}
 
while (! cairo_list_is_empty (&display->screens)) {
_cairo_xlib_screen_destroy (display,
cairo_list_first_entry (&display->screens,
cairo_xlib_screen_t,
link));
}
 
XSync (dpy, False);
XSetErrorHandler (old_handler);
 
cairo_device_release (&display->base);
}
}
 
static void
_cairo_xlib_display_destroy (void *abstract_display)
{
cairo_xlib_display_t *display = abstract_display;
 
free (display);
}
 
static int
_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
{
cairo_xlib_display_t *display, **prev, *next;
 
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
for (display = _cairo_xlib_display_list; display; display = display->next)
if (display->display == dpy)
break;
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
if (display == NULL)
return 0;
 
cairo_device_finish (&display->base);
 
/*
* Unhook from the global list
*/
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
prev = &_cairo_xlib_display_list;
for (display = _cairo_xlib_display_list; display; display = next) {
next = display->next;
if (display->display == dpy) {
*prev = next;
break;
} else
prev = &display->next;
}
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
display->display = NULL; /* catch any later invalid access */
cairo_device_destroy (&display->base);
 
/* Return value in accordance with requirements of
* XESetCloseDisplay */
return 0;
}
 
static const cairo_device_backend_t _cairo_xlib_device_backend = {
CAIRO_DEVICE_TYPE_XLIB,
 
NULL,
NULL,
 
NULL, /* flush */
_cairo_xlib_display_finish,
_cairo_xlib_display_destroy,
};
 
static void _cairo_xlib_display_select_compositor (cairo_xlib_display_t *display)
{
#if 1
if (display->render_major > 0 || display->render_minor >= 4)
display->compositor = _cairo_xlib_traps_compositor_get ();
else if (display->render_major > 0 || display->render_minor >= 0)
display->compositor = _cairo_xlib_mask_compositor_get ();
else
display->compositor = _cairo_xlib_core_compositor_get ();
#else
display->compositor = _cairo_xlib_fallback_compositor_get ();
#endif
}
 
/**
* _cairo_xlib_device_create:
* @dpy: the display to create the device for
*
* Gets the device belonging to @dpy, or creates it if it doesn't exist yet.
*
* Returns: the device belonging to @dpy
**/
cairo_device_t *
_cairo_xlib_device_create (Display *dpy)
{
cairo_xlib_display_t *display;
cairo_xlib_display_t **prev;
cairo_device_t *device;
XExtCodes *codes;
const char *env;
 
CAIRO_MUTEX_INITIALIZE ();
 
/* There is an apparent deadlock between this mutex and the
* mutex for the display, but it's actually safe. For the
* app to call XCloseDisplay() while any other thread is
* inside this function would be an error in the logic
* app, and the CloseDisplay hook is the only other place we
* acquire this mutex.
*/
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
 
for (prev = &_cairo_xlib_display_list; (display = *prev); prev = &(*prev)->next)
{
if (display->display == dpy) {
/*
* MRU the list
*/
if (prev != &_cairo_xlib_display_list) {
*prev = display->next;
display->next = _cairo_xlib_display_list;
_cairo_xlib_display_list = display;
}
device = cairo_device_reference (&display->base);
goto UNLOCK;
}
}
 
display = malloc (sizeof (cairo_xlib_display_t));
if (unlikely (display == NULL)) {
device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
goto UNLOCK;
}
 
_cairo_device_init (&display->base, &_cairo_xlib_device_backend);
 
display->display = dpy;
cairo_list_init (&display->screens);
cairo_list_init (&display->fonts);
display->closed = FALSE;
 
/* Xlib calls out to the extension close_display hooks in LIFO
* order. So we have to ensure that all extensions that we depend
* on in our close_display hook are properly initialized before we
* add our hook. For now, that means Render, so we call into its
* QueryVersion function to ensure it gets initialized.
*/
display->render_major = display->render_minor = -1;
XRenderQueryVersion (dpy, &display->render_major, &display->render_minor);
env = getenv ("CAIRO_DEBUG");
if (env != NULL && (env = strstr (env, "xrender-version=")) != NULL) {
int max_render_major, max_render_minor;
 
env += sizeof ("xrender-version=") - 1;
if (sscanf (env, "%d.%d", &max_render_major, &max_render_minor) != 2)
max_render_major = max_render_minor = -1;
 
if (max_render_major < display->render_major ||
(max_render_major == display->render_major &&
max_render_minor < display->render_minor))
{
display->render_major = max_render_major;
display->render_minor = max_render_minor;
}
}
 
_cairo_xlib_display_select_compositor (display);
 
display->white = NULL;
memset (display->alpha, 0, sizeof (display->alpha));
memset (display->solid, 0, sizeof (display->solid));
memset (display->solid_cache, 0, sizeof (display->solid_cache));
memset (display->last_solid_cache, 0, sizeof (display->last_solid_cache));
 
memset (display->cached_xrender_formats, 0,
sizeof (display->cached_xrender_formats));
 
display->force_precision = -1;
 
_cairo_xlib_display_init_shm (display);
 
/* Prior to Render 0.10, there is no protocol support for gradients and
* we call function stubs instead, which would silently consume the drawing.
*/
#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
display->buggy_gradients = TRUE;
#else
display->buggy_gradients = FALSE;
#endif
display->buggy_pad_reflect = FALSE;
display->buggy_repeat = FALSE;
 
/* This buggy_repeat condition is very complicated because there
* are multiple X server code bases (with multiple versioning
* schemes within a code base), and multiple bugs.
*
* The X servers:
*
* 1. The Vendor=="XFree86" code base with release numbers such
* as 4.7.0 (VendorRelease==40700000).
*
* 2. The Vendor=="X.Org" code base (a descendant of the
* XFree86 code base). It originally had things like
* VendorRelease==60700000 for release 6.7.0 but then changed
* its versioning scheme so that, for example,
* VendorRelease==10400000 for the 1.4.0 X server within the
* X.Org 7.3 release.
*
* The bugs:
*
* 1. The original bug that led to the buggy_repeat
* workaround. This was a bug that Owen Taylor investigated,
* understood well, and characterized against carious X
* servers. Confirmed X servers with this bug include:
*
* "XFree86" <= 40500000
* "X.Org" <= 60802000 (only with old numbering >= 60700000)
*
* 2. A separate bug resulting in a crash of the X server when
* using cairo's extend-reflect test case, (which, surprisingly
* enough was not passing RepeatReflect to the X server, but
* instead using RepeatNormal in a workaround). Nobody to date
* has understood the bug well, but it appears to be gone as of
* the X.Org 1.4.0 server. This bug is coincidentally avoided
* by using the same buggy_repeat workaround. Confirmed X
* servers with this bug include:
*
* "X.org" == 60900000 (old versioning scheme)
* "X.org" < 10400000 (new numbering scheme)
*
* For the old-versioning-scheme X servers we don't know
* exactly when second the bug started, but since bug 1 is
* present through 6.8.2 and bug 2 is present in 6.9.0 it seems
* safest to just blacklist all old-versioning-scheme X servers,
* (just using VendorRelease < 70000000), as buggy_repeat=TRUE.
*/
if (_cairo_xlib_vendor_is_xorg (dpy)) {
if (VendorRelease (dpy) >= 60700000) {
if (VendorRelease (dpy) < 70000000)
display->buggy_repeat = TRUE;
 
/* We know that gradients simply do not work in early Xorg servers */
if (VendorRelease (dpy) < 70200000)
display->buggy_gradients = TRUE;
 
/* And the extended repeat modes were not fixed until much later */
display->buggy_pad_reflect = TRUE;
} else {
if (VendorRelease (dpy) < 10400000)
display->buggy_repeat = TRUE;
 
/* Too many bugs in the early drivers */
if (VendorRelease (dpy) < 10699000)
display->buggy_pad_reflect = TRUE;
}
} else if (strstr (ServerVendor (dpy), "XFree86") != NULL) {
if (VendorRelease (dpy) <= 40500000)
display->buggy_repeat = TRUE;
 
display->buggy_gradients = TRUE;
display->buggy_pad_reflect = TRUE;
}
 
codes = XAddExtension (dpy);
if (unlikely (codes == NULL)) {
device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
free (display);
goto UNLOCK;
}
 
XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
cairo_device_reference (&display->base); /* add one for the CloseDisplay */
 
display->next = _cairo_xlib_display_list;
_cairo_xlib_display_list = display;
 
device = &display->base;
 
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
return device;
}
 
cairo_status_t
_cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display)
{
cairo_status_t status;
 
status = cairo_device_acquire (device);
if (status)
return status;
 
*display = (cairo_xlib_display_t *) device;
return CAIRO_STATUS_SUCCESS;
}
 
XRenderPictFormat *
_cairo_xlib_display_get_xrender_format_for_pixman(cairo_xlib_display_t *display,
pixman_format_code_t format)
{
Display *dpy = display->display;
XRenderPictFormat tmpl;
int mask;
 
#define MASK(x) ((1<<(x))-1)
 
tmpl.depth = PIXMAN_FORMAT_DEPTH(format);
mask = PictFormatType | PictFormatDepth;
 
switch (PIXMAN_FORMAT_TYPE(format)) {
case PIXMAN_TYPE_ARGB:
tmpl.type = PictTypeDirect;
 
tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
if (PIXMAN_FORMAT_A(format))
tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) +
PIXMAN_FORMAT_G(format) +
PIXMAN_FORMAT_B(format));
 
tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
tmpl.direct.red = (PIXMAN_FORMAT_G(format) +
PIXMAN_FORMAT_B(format));
 
tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
tmpl.direct.green = PIXMAN_FORMAT_B(format);
 
tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
tmpl.direct.blue = 0;
 
mask |= PictFormatRed | PictFormatRedMask;
mask |= PictFormatGreen | PictFormatGreenMask;
mask |= PictFormatBlue | PictFormatBlueMask;
mask |= PictFormatAlpha | PictFormatAlphaMask;
break;
 
case PIXMAN_TYPE_ABGR:
tmpl.type = PictTypeDirect;
 
tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
if (tmpl.direct.alphaMask)
tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) +
PIXMAN_FORMAT_G(format) +
PIXMAN_FORMAT_R(format));
 
tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
tmpl.direct.blue = (PIXMAN_FORMAT_G(format) +
PIXMAN_FORMAT_R(format));
 
tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
tmpl.direct.green = PIXMAN_FORMAT_R(format);
 
tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
tmpl.direct.red = 0;
 
mask |= PictFormatRed | PictFormatRedMask;
mask |= PictFormatGreen | PictFormatGreenMask;
mask |= PictFormatBlue | PictFormatBlueMask;
mask |= PictFormatAlpha | PictFormatAlphaMask;
break;
 
case PIXMAN_TYPE_BGRA:
tmpl.type = PictTypeDirect;
 
tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format));
tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format));
 
tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format));
tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
PIXMAN_FORMAT_G(format));
 
tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format));
tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) -
PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format));
 
tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
tmpl.direct.alpha = 0;
 
mask |= PictFormatRed | PictFormatRedMask;
mask |= PictFormatGreen | PictFormatGreenMask;
mask |= PictFormatBlue | PictFormatBlueMask;
mask |= PictFormatAlpha | PictFormatAlphaMask;
break;
 
case PIXMAN_TYPE_A:
tmpl.type = PictTypeDirect;
 
tmpl.direct.alpha = 0;
tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format));
 
mask |= PictFormatAlpha | PictFormatAlphaMask;
break;
 
case PIXMAN_TYPE_COLOR:
case PIXMAN_TYPE_GRAY:
/* XXX Find matching visual/colormap */
tmpl.type = PictTypeIndexed;
//tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid;
//mask |= PictFormatColormap;
return NULL;
}
#undef MASK
 
/* XXX caching? */
return XRenderFindFormat(dpy, mask, &tmpl, 0);
}
 
XRenderPictFormat *
_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
cairo_format_t format)
{
XRenderPictFormat *xrender_format;
 
#if ! ATOMIC_OP_NEEDS_MEMORY_BARRIER
xrender_format = display->cached_xrender_formats[format];
if (likely (xrender_format != NULL))
return xrender_format;
#endif
 
xrender_format = display->cached_xrender_formats[format];
if (xrender_format == NULL) {
int pict_format = PictStandardNUM;
 
switch (format) {
case CAIRO_FORMAT_A1:
pict_format = PictStandardA1; break;
case CAIRO_FORMAT_A8:
pict_format = PictStandardA8; break;
case CAIRO_FORMAT_RGB24:
pict_format = PictStandardRGB24; break;
case CAIRO_FORMAT_RGB16_565:
xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
PIXMAN_r5g6b5);
break;
case CAIRO_FORMAT_RGB30:
xrender_format = _cairo_xlib_display_get_xrender_format_for_pixman(display,
PIXMAN_x2r10g10b10);
break;
case CAIRO_FORMAT_INVALID:
default:
ASSERT_NOT_REACHED;
case CAIRO_FORMAT_ARGB32:
pict_format = PictStandardARGB32; break;
}
if (pict_format != PictStandardNUM)
xrender_format =
XRenderFindStandardFormat (display->display, pict_format);
display->cached_xrender_formats[format] = xrender_format;
}
 
return xrender_format;
}
 
cairo_xlib_screen_t *
_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
Screen *screen)
{
cairo_xlib_screen_t *info;
 
cairo_list_foreach_entry (info, cairo_xlib_screen_t, &display->screens, link) {
if (info->screen == screen) {
if (display->screens.next != &info->link)
cairo_list_move (&info->link, &display->screens);
return info;
}
}
 
return NULL;
}
 
cairo_bool_t
_cairo_xlib_display_has_repeat (cairo_device_t *device)
{
return ! ((cairo_xlib_display_t *) device)->buggy_repeat;
}
 
cairo_bool_t
_cairo_xlib_display_has_reflect (cairo_device_t *device)
{
return ! ((cairo_xlib_display_t *) device)->buggy_pad_reflect;
}
 
cairo_bool_t
_cairo_xlib_display_has_gradients (cairo_device_t *device)
{
return ! ((cairo_xlib_display_t *) device)->buggy_gradients;
}
 
/**
* cairo_xlib_device_debug_cap_xrender_version:
* @device: a #cairo_device_t for the Xlib backend
* @major_version: major version to restrict to
* @minor_version: minor version to restrict to
*
* Restricts all future Xlib surfaces for this devices to the specified version
* of the RENDER extension. This function exists solely for debugging purpose.
* It let's you find out how cairo would behave with an older version of
* the RENDER extension.
*
* Use the special values -1 and -1 for disabling the RENDER extension.
*
* Since: 1.12
**/
void
cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
int major_version,
int minor_version)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *) device;
 
if (device == NULL || device->status)
return;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB)
return;
 
if (major_version < display->render_major ||
(major_version == display->render_major &&
minor_version < display->render_minor))
{
display->render_major = major_version;
display->render_minor = minor_version;
}
 
_cairo_xlib_display_select_compositor (display);
}
 
/**
* cairo_xlib_device_debug_set_precision:
* @device: a #cairo_device_t for the Xlib backend
* @precision: the precision to use
*
* Render supports two modes of precision when rendering trapezoids. Set
* the precision to the desired mode.
*
* Since: 1.12
**/
void
cairo_xlib_device_debug_set_precision (cairo_device_t *device,
int precision)
{
if (device == NULL || device->status)
return;
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return;
}
 
((cairo_xlib_display_t *) device)->force_precision = precision;
}
 
/**
* cairo_xlib_device_debug_get_precision:
* @device: a #cairo_device_t for the Xlib backend
*
* Get the Xrender precision mode.
*
* Returns: the render precision mode
*
* Since: 1.12
**/
int
cairo_xlib_device_debug_get_precision (cairo_device_t *device)
{
if (device == NULL || device->status)
return -1;
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return -1;
}
 
return ((cairo_xlib_display_t *) device)->force_precision;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-fallback-compositor.c
0,0 → 1,248
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Behdad Esfahbod <behdad@behdad.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-offset-private.h"
 
static const cairo_compositor_t *
_get_compositor (cairo_surface_t *surface)
{
return ((cairo_image_surface_t *)surface)->compositor;
}
 
static cairo_bool_t
unclipped (cairo_xlib_surface_t *xlib, cairo_clip_t *clip)
{
cairo_rectangle_int_t r;
 
r.x = r.y = 0;
r.width = xlib->width;
r.height = xlib->height;
return _cairo_clip_contains_rectangle (clip, &r);
}
 
static cairo_int_status_t
_cairo_xlib_shm_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
cairo_int_status_t status;
cairo_surface_t *shm;
cairo_bool_t overwrite;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
overwrite =
extents->op <= CAIRO_OPERATOR_SOURCE && unclipped (xlib, extents->clip);
 
shm = _cairo_xlib_surface_get_shm (xlib, overwrite);
if (shm == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_paint (_get_compositor (shm), shm,
extents->op,
&extents->source_pattern.base,
extents->clip);
if (unlikely (status))
return status;
 
xlib->base.is_clear =
extents->op == CAIRO_OPERATOR_CLEAR && unclipped (xlib, extents->clip);
xlib->base.serial++;
xlib->fallback++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_xlib_shm_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
cairo_int_status_t status;
cairo_surface_t *shm;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
if (shm == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_mask (_get_compositor (shm), shm,
extents->op,
&extents->source_pattern.base,
&extents->mask_pattern.base,
extents->clip);
if (unlikely (status))
return status;
 
xlib->base.is_clear = FALSE;
xlib->base.serial++;
xlib->fallback++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_xlib_shm_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
cairo_int_status_t status;
cairo_surface_t *shm;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
if (shm == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_stroke (_get_compositor (shm), shm,
extents->op,
&extents->source_pattern.base,
path, style,
ctm, ctm_inverse,
tolerance,
antialias,
extents->clip);
if (unlikely (status))
return status;
 
xlib->base.is_clear = FALSE;
xlib->base.serial++;
xlib->fallback++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_xlib_shm_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
cairo_int_status_t status;
cairo_surface_t *shm;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
if (shm == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_fill (_get_compositor (shm), shm,
extents->op,
&extents->source_pattern.base,
path,
fill_rule, tolerance, antialias,
extents->clip);
if (unlikely (status))
return status;
 
xlib->base.is_clear = FALSE;
xlib->base.serial++;
xlib->fallback++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static cairo_int_status_t
_cairo_xlib_shm_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_xlib_surface_t *xlib = (cairo_xlib_surface_t *)extents->surface;
cairo_int_status_t status;
cairo_surface_t *shm;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
shm = _cairo_xlib_surface_get_shm (xlib, FALSE);
if (shm == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = _cairo_compositor_glyphs (_get_compositor (shm), shm,
extents->op,
&extents->source_pattern.base,
glyphs, num_glyphs, scaled_font,
extents->clip);
if (unlikely (status))
return status;
 
xlib->base.is_clear = FALSE;
xlib->base.serial++;
xlib->fallback++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
static const cairo_compositor_t _cairo_xlib_shm_compositor = {
&_cairo_fallback_compositor,
 
_cairo_xlib_shm_compositor_paint,
_cairo_xlib_shm_compositor_mask,
_cairo_xlib_shm_compositor_stroke,
_cairo_xlib_shm_compositor_fill,
_cairo_xlib_shm_compositor_glyphs,
};
 
const cairo_compositor_t *
_cairo_xlib_fallback_compositor_get (void)
{
return &_cairo_xlib_shm_compositor;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-private.h
46,24 → 46,25
#include "cairo-list-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-types-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-private.h"
 
#include <pixman.h>
#include <string.h>
 
typedef struct _cairo_xlib_display cairo_xlib_display_t;
typedef struct _cairo_xlib_shm_display cairo_xlib_shm_display_t;
typedef struct _cairo_xlib_screen cairo_xlib_screen_t;
typedef struct _cairo_xlib_source cairo_xlib_source_t;
typedef struct _cairo_xlib_proxy cairo_xlib_proxy_t;
typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
 
typedef struct _cairo_xlib_hook cairo_xlib_hook_t;
typedef struct _cairo_xlib_job cairo_xlib_job_t;
typedef void (*cairo_xlib_notify_func) (Display *, void *);
typedef void (*cairo_xlib_notify_resource_func) (Display *, XID);
 
struct _cairo_xlib_hook {
cairo_xlib_hook_t *prev, *next; /* private */
void (*func) (cairo_xlib_display_t *display, void *data);
};
 
/* size of color cube */
#define CUBE_SIZE 6
/* size of gray ramp */
#define RAMP_SIZE 16
/* maximum number of cached GC's */
#define GC_CACHE_SIZE 4
 
struct _cairo_xlib_display {
cairo_device_t base;
72,15 → 73,42
 
Display *display;
cairo_list_t screens;
cairo_list_t fonts;
 
cairo_xlib_shm_display_t *shm;
 
const cairo_compositor_t *compositor;
 
int render_major;
int render_minor;
XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_RGB16_565 + 1];
 
cairo_xlib_job_t *workqueue;
cairo_freelist_t wq_freelist;
int force_precision;
 
cairo_xlib_hook_t *close_display_hooks;
cairo_surface_t *white;
cairo_surface_t *alpha[256];
cairo_surface_t *solid[32];
uint32_t solid_cache[32]; /* low 16 are opaque, high 16 transparent */
struct {
uint32_t color;
int index;
} last_solid_cache[2];
 
/* TRUE if the server has a bug with repeating pictures
*
* https://bugs.freedesktop.org/show_bug.cgi?id=3566
*
* We can't test for this because it depends on whether the
* picture is in video memory or not.
*
* We also use this variable as a guard against a second
* independent bug with transformed repeating pictures:
*
* http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
*
* Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
* we can reuse the test for now.
*/
unsigned int buggy_gradients :1;
unsigned int buggy_pad_reflect :1;
unsigned int buggy_repeat :1;
103,45 → 131,122
cairo_device_t *device;
Screen *screen;
 
cairo_list_t surfaces;
 
cairo_bool_t has_font_options;
cairo_font_options_t font_options;
 
GC gc[4];
cairo_atomic_int_t gc_depths; /* 4 x uint8_t */
GC gc[GC_CACHE_SIZE];
uint8_t gc_depths[GC_CACHE_SIZE];
 
cairo_list_t visuals;
};
 
enum {
GLYPHSET_INDEX_ARGB32,
GLYPHSET_INDEX_A8,
GLYPHSET_INDEX_A1,
NUM_GLYPHSETS
};
 
typedef struct _cairo_xlib_font_glyphset {
GlyphSet glyphset;
cairo_format_t format;
XRenderPictFormat *xrender_format;
struct _cairo_xlib_font_glyphset_free_glyphs {
int count;
unsigned long indices[128];
} to_free;
} cairo_xlib_font_glyphset_t;
 
typedef struct _cairo_xlib_font {
cairo_scaled_font_private_t base;
cairo_scaled_font_t *font;
cairo_device_t *device;
cairo_list_t link;
cairo_xlib_font_glyphset_t glyphset[NUM_GLYPHSETS];
} cairo_xlib_font_t;
 
struct _cairo_xlib_surface {
cairo_surface_t base;
 
Picture picture;
Drawable drawable;
 
const cairo_compositor_t *compositor;
cairo_surface_t *shm;
int fallback;
 
cairo_xlib_display_t *display;
cairo_xlib_screen_t *screen;
cairo_list_t link;
 
Display *dpy; /* only valid between acquire/release */
cairo_bool_t owns_pixmap;
Visual *visual;
 
int use_pixmap;
 
int width;
int height;
int depth;
 
int precision;
XRenderPictFormat *xrender_format;
/* XXX pixman_format instead of masks? */
uint32_t a_mask;
uint32_t r_mask;
uint32_t g_mask;
uint32_t b_mask;
 
struct _cairo_xlib_source {
cairo_surface_t base;
 
Picture picture;
Pixmap pixmap;
Display *dpy;
 
unsigned int filter:3;
unsigned int extend:3;
unsigned int has_matrix:1;
unsigned int has_component_alpha:1;
} embedded_source;
};
 
struct _cairo_xlib_proxy {
struct _cairo_xlib_source source;
cairo_surface_t *owner;
};
 
inline static cairo_bool_t
_cairo_xlib_vendor_is_xorg (Display *dpy)
{
const char *const vendor = ServerVendor (dpy);
return strstr (vendor, "X.Org") || strstr (vendor, "Xorg");
}
 
cairo_private cairo_status_t
_cairo_xlib_surface_get_gc (cairo_xlib_display_t *display,
cairo_xlib_surface_t *surface,
GC *gc);
 
cairo_private cairo_device_t *
_cairo_xlib_device_create (Display *display);
 
cairo_private void
_cairo_xlib_display_init_shm (cairo_xlib_display_t *display);
 
cairo_private void
_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display);
 
cairo_private cairo_xlib_screen_t *
_cairo_xlib_display_get_screen (cairo_xlib_display_t *display,
Screen *screen);
 
cairo_private void
_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook);
 
cairo_private void
_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, cairo_xlib_hook_t *hook);
 
cairo_private cairo_status_t
_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
cairo_xlib_notify_func notify,
void *data,
void (*destroy)(void *));
cairo_private cairo_status_t
_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
cairo_xlib_notify_resource_func notify,
XID resource);
cairo_private cairo_status_t
_cairo_xlib_display_acquire (cairo_device_t *device,
cairo_xlib_display_t **display);
 
cairo_private void
_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display,
int *major, int *minor);
 
cairo_private cairo_bool_t
_cairo_xlib_display_has_repeat (cairo_device_t *device);
 
151,10 → 256,18
cairo_private cairo_bool_t
_cairo_xlib_display_has_gradients (cairo_device_t *device);
 
cairo_private void
_cairo_xlib_display_set_precision(cairo_device_t *device,
int precision);
 
cairo_private XRenderPictFormat *
_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t *display,
cairo_format_t format);
 
cairo_private XRenderPictFormat *
_cairo_xlib_display_get_xrender_format_for_pixman (cairo_xlib_display_t *display,
pixman_format_code_t format);
 
cairo_private cairo_status_t
_cairo_xlib_screen_get (Display *dpy,
Screen *screen,
161,10 → 274,7
cairo_xlib_screen_t **out);
 
cairo_private void
_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info);
 
cairo_private void
_cairo_xlib_screen_close_display (cairo_xlib_display_t *display,
_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info);
 
cairo_private GC
172,7 → 282,6
cairo_xlib_screen_t *info,
int depth,
Drawable drawable);
 
cairo_private void
_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info,
197,4 → 306,163
cairo_private void
_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info);
 
cairo_private const cairo_compositor_t *
_cairo_xlib_core_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_xlib_fallback_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_xlib_mask_compositor_get (void);
 
cairo_private const cairo_compositor_t *
_cairo_xlib_traps_compositor_get (void);
 
cairo_private void
_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface);
 
cairo_private void
_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface,
cairo_antialias_t antialias);
 
cairo_private cairo_int_status_t
_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display,
cairo_xlib_surface_t *surface,
cairo_surface_attributes_t *attributes,
double xc,
double yc);
 
cairo_private cairo_status_t
_cairo_xlib_surface_draw_image (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_private 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_private void
_cairo_xlib_font_close (cairo_xlib_font_t *font);
 
#define CAIRO_RENDER_AT_LEAST(surface, major, minor) \
(((surface)->render_major > major) || \
(((surface)->render_major == major) && ((surface)->render_minor >= minor)))
 
#define CAIRO_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_RENDER_HAS_COMPOSITE(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
#define CAIRO_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 0)
 
#define CAIRO_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 1)
 
#define CAIRO_RENDER_HAS_DISJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2)
#define CAIRO_RENDER_HAS_CONJOINT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 2)
 
#define CAIRO_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_RENDER_HAS_TRIANGLES(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_RENDER_HAS_TRISTRIP(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
#define CAIRO_RENDER_HAS_TRIFAN(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 4)
 
#define CAIRO_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6)
#define CAIRO_RENDER_HAS_FILTERS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 6)
 
#define CAIRO_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10)
#define CAIRO_RENDER_HAS_GRADIENTS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 10)
 
#define CAIRO_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_RENDER_AT_LEAST((surface), 0, 11)
 
#define CAIRO_RENDER_SUPPORTS_OPERATOR(surface, op) \
((op) <= CAIRO_OPERATOR_SATURATE || \
(CAIRO_RENDER_HAS_PDF_OPERATORS(surface) && \
(op) <= CAIRO_OPERATOR_HSL_LUMINOSITY))
 
/*
* 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;
}
 
cairo_private cairo_int_status_t
_cairo_xlib_core_fill_boxes (cairo_xlib_surface_t *dst,
const cairo_color_t *color,
cairo_boxes_t *boxes);
 
cairo_private cairo_int_status_t
_cairo_xlib_core_fill_rectangles (cairo_xlib_surface_t *dst,
const cairo_color_t *color,
int num_rects,
cairo_rectangle_int_t *rects);
 
static inline 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);
}
 
cairo_private cairo_surface_t *
_cairo_xlib_surface_create_similar_shm (void *surface,
cairo_format_t format,
int width, int height);
 
cairo_private cairo_surface_t *
_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
cairo_bool_t overwrite);
 
cairo_private cairo_int_status_t
_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface);
 
cairo_private cairo_surface_t *
_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
pixman_format_code_t format,
int width, int height);
 
cairo_private cairo_surface_t *
_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
pixman_format_code_t format,
int width, int height);
 
cairo_private void
_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
XImage *ximage);
 
cairo_private void *
_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface);
 
cairo_private void
_cairo_xlib_shm_surface_mark_active (cairo_surface_t *shm);
 
cairo_private cairo_bool_t
_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface);
 
cairo_private cairo_bool_t
_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface);
 
cairo_private Pixmap
_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface);
 
cairo_private XRenderPictFormat *
_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface);
 
cairo_private pixman_format_code_t
_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface);
 
#endif /* CAIRO_XLIB_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-xlib-render-compositor.c
0,0 → 1,1999
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Behdad Esfahbod <behdad@behdad.org>
* Chris Wilson <chris@chris-wilson.co.uk>
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-damage-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-pixman-private.h"
#include "cairo-traps-private.h"
#include "cairo-tristrip-private.h"
 
static cairo_int_status_t
check_composite (const cairo_composite_rectangles_t *extents)
{
cairo_xlib_display_t *display = ((cairo_xlib_surface_t *)extents->surface)->display;
 
if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
acquire (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_int_status_t status;
 
status = _cairo_xlib_display_acquire (dst->base.device, &dst->display);
if (unlikely (status))
return status;
 
dst->dpy = dst->display->display;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
release (void *abstract_dst)
{
cairo_xlib_surface_t *dst = abstract_dst;
 
cairo_device_release (&dst->display->base);
dst->dpy = NULL;
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
set_clip_region (void *_surface,
cairo_region_t *region)
{
cairo_xlib_surface_t *surface = _surface;
 
_cairo_xlib_surface_ensure_picture (surface);
 
if (region != NULL) {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
XRectangle *rects = stack_rects;
int n_rects, i;
 
n_rects = cairo_region_num_rectangles (region);
if (n_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
for (i = 0; i < n_rects; i++) {
cairo_rectangle_int_t rect;
 
cairo_region_get_rectangle (region, i, &rect);
 
rects[i].x = rect.x;
rects[i].y = rect.y;
rects[i].width = rect.width;
rects[i].height = rect.height;
}
XRenderSetPictureClipRectangles (surface->dpy,
surface->picture,
0, 0,
rects, n_rects);
if (rects != stack_rects)
free (rects);
} else {
XRenderPictureAttributes pa;
pa.clip_mask = None;
XRenderChangePicture (surface->dpy,
surface->picture,
CPClipMask, &pa);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
copy_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
cairo_int_status_t status;
Pixmap src;
GC gc;
int i, j;
 
assert (image->depth == dst->depth);
 
status = acquire (dst);
if (unlikely (status))
return status;
 
status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
if (unlikely (status)) {
release (dst);
return status;
}
 
src = _cairo_xlib_shm_surface_get_pixmap (&image->base);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
 
_cairo_xlib_shm_surface_mark_active (&image->base);
XCopyArea (dst->dpy, src, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
} else {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
 
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
if (x2 > x1 && y2 > y1) {
rects[j].x = x1;
rects[j].y = y1;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
}
 
XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
_cairo_xlib_shm_surface_mark_active (&image->base);
XCopyArea (dst->dpy, src, dst->drawable, gc,
0, 0, image->width, image->height, -dx, -dy);
XSetClipMask (dst->dpy, gc, None);
 
if (rects != stack_rects)
free (rects);
}
 
_cairo_xlib_surface_put_gc (dst->display, dst, gc);
release (dst);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
boxes_cover_surface (cairo_boxes_t *boxes,
cairo_xlib_surface_t *surface)
{
cairo_box_t *b;
 
if (boxes->num_boxes != 1)
return FALSE;
 
b = &boxes->chunks.base[0];
 
if (_cairo_fixed_integer_part (b->p1.x) > 0 ||
_cairo_fixed_integer_part (b->p1.y) > 0)
return FALSE;
 
if (_cairo_fixed_integer_part (b->p2.x) < surface->width ||
_cairo_fixed_integer_part (b->p2.y) < surface->height)
return FALSE;
 
return TRUE;
}
 
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
struct _cairo_boxes_chunk *chunk;
cairo_image_surface_t *shm = NULL;
cairo_int_status_t status;
int i;
 
if (image->base.device == dst->base.device) {
if (image->depth != dst->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (_cairo_xlib_shm_surface_get_pixmap (&image->base))
return copy_image_boxes (dst, image, boxes, dx, dy);
 
goto draw_image_boxes;
}
 
if (boxes_cover_surface (boxes, dst))
shm = (cairo_image_surface_t *) _cairo_xlib_surface_get_shm (dst, TRUE);
if (shm) {
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
cairo_rectangle_int_t r;
 
r.x = _cairo_fixed_integer_part (b->p1.x);
r.y = _cairo_fixed_integer_part (b->p1.y);
r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
 
if (shm->pixman_format != image->pixman_format ||
! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
image->stride / sizeof (uint32_t),
shm->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (image->pixman_format),
PIXMAN_FORMAT_BPP (shm->pixman_format),
r.x + dx, r.y + dy,
r.x, r.y,
r.width, r.height))
{
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, shm->pixman_image,
r.x + dx, r.y + dy,
0, 0,
r.x, r.y,
r.width, r.height);
}
 
shm->base.damage =
_cairo_damage_add_rectangle (shm->base.damage, &r);
}
}
dst->base.is_clear = FALSE;
dst->fallback++;
dst->base.serial++;
return CAIRO_INT_STATUS_NOTHING_TO_DO;
}
 
if (image->depth == dst->depth &&
((cairo_xlib_display_t *)dst->display)->shm) {
cairo_box_t extents;
cairo_rectangle_int_t r;
 
_cairo_boxes_extents (boxes, &extents);
_cairo_box_round_to_rectangle (&extents, &r);
 
shm = (cairo_image_surface_t *)
_cairo_xlib_surface_create_shm (dst, image->pixman_format,
r.width, r.height);
if (shm) {
int tx = -r.x, ty = -r.y;
 
assert (shm->pixman_format == image->pixman_format);
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
 
r.x = _cairo_fixed_integer_part (b->p1.x);
r.y = _cairo_fixed_integer_part (b->p1.y);
r.width = _cairo_fixed_integer_part (b->p2.x) - r.x;
r.height = _cairo_fixed_integer_part (b->p2.y) - r.y;
 
if (! pixman_blt ((uint32_t *)image->data, (uint32_t *)shm->data,
image->stride / sizeof (uint32_t),
shm->stride / sizeof (uint32_t),
PIXMAN_FORMAT_BPP (image->pixman_format),
PIXMAN_FORMAT_BPP (shm->pixman_format),
r.x + dx, r.y + dy,
r.x + tx, r.y + ty,
r.width, r.height))
{
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, shm->pixman_image,
r.x + dx, r.y + dy,
0, 0,
r.x + tx, r.y + ty,
r.width, r.height);
}
}
}
 
dx = tx;
dy = ty;
image = shm;
 
if (_cairo_xlib_shm_surface_get_pixmap (&image->base)) {
status = copy_image_boxes (dst, image, boxes, dx, dy);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto out;
}
}
}
 
draw_image_boxes:
status = CAIRO_STATUS_SUCCESS;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
cairo_box_t *b = &chunk->base[i];
int x1 = _cairo_fixed_integer_part (b->p1.x);
int y1 = _cairo_fixed_integer_part (b->p1.y);
int x2 = _cairo_fixed_integer_part (b->p2.x);
int y2 = _cairo_fixed_integer_part (b->p2.y);
if (_cairo_xlib_surface_draw_image (dst, image,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1)) {
status = CAIRO_INT_STATUS_UNSUPPORTED;
goto out;
}
}
}
 
out:
cairo_surface_destroy (&shm->base);
return status;
}
 
static cairo_int_status_t
copy_boxes (void *_dst,
cairo_surface_t *_src,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents,
int dx, int dy)
{
cairo_xlib_surface_t *dst = _dst;
cairo_xlib_surface_t *src = (cairo_xlib_surface_t *)_src;
struct _cairo_boxes_chunk *chunk;
cairo_int_status_t status;
GC gc;
Drawable d;
int i, j;
 
if (! _cairo_xlib_surface_same_screen (dst, src))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
if (dst->depth != src->depth)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
status = acquire (dst);
if (unlikely (status))
return status;
 
status = _cairo_xlib_surface_get_gc (dst->display, dst, &gc);
if (unlikely (status)) {
release (dst);
return status;
}
 
if (src->fallback && src->shm->damage->dirty) {
assert (src != dst);
d = _cairo_xlib_shm_surface_get_pixmap (src->shm);
assert (d != 0);
} else {
if (! src->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = IncludeInferiors;
XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
}
d = src->drawable;
}
 
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
 
XCopyArea (dst->dpy, d, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
} else {
/* We can only have a single control for subwindow_mode on the
* GC. If we have a Window destination, we need to set ClipByChildren,
* but if we have a Window source, we need IncludeInferiors. If we have
* both a Window destination and source, we must fallback. There is
* no convenient way to detect if a drawable is a Pixmap or Window,
* therefore we can only rely on those surfaces that we created
* ourselves to be Pixmaps, and treat everything else as a potential
* Window.
*/
if (src == dst || (!src->owns_pixmap && !dst->owns_pixmap)) {
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
XCopyArea (dst->dpy, d, dst->drawable, gc,
x1 + dx, y1 + dy,
x2 - x1, y2 - y1,
x1, y1);
}
}
} else {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
 
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
rects[j].x = x1;
rects[j].y = y1;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
assert (j == boxes->num_boxes);
 
XSetClipRectangles (dst->dpy, gc, 0, 0, rects, j, Unsorted);
 
XCopyArea (dst->dpy, d, dst->drawable, gc,
extents->x + dx, extents->y + dy,
extents->width, extents->height,
extents->x, extents->y);
 
XSetClipMask (dst->dpy, gc, None);
 
if (rects != stack_rects)
free (rects);
}
}
 
if (src->fallback && src->shm->damage->dirty) {
_cairo_xlib_shm_surface_mark_active (src->shm);
} else if (! src->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = ClipByChildren;
XChangeGC (dst->display->display, gc, GCSubwindowMode, &gcv);
}
 
_cairo_xlib_surface_put_gc (dst->display, dst, gc);
release (dst);
return CAIRO_STATUS_SUCCESS;
}
 
static int
_render_operator (cairo_operator_t op)
{
switch (op) {
case CAIRO_OPERATOR_CLEAR:
return PictOpClear;
 
case CAIRO_OPERATOR_SOURCE:
return PictOpSrc;
case CAIRO_OPERATOR_OVER:
return PictOpOver;
case CAIRO_OPERATOR_IN:
return PictOpIn;
case CAIRO_OPERATOR_OUT:
return PictOpOut;
case CAIRO_OPERATOR_ATOP:
return PictOpAtop;
 
case CAIRO_OPERATOR_DEST:
return PictOpDst;
case CAIRO_OPERATOR_DEST_OVER:
return PictOpOverReverse;
case CAIRO_OPERATOR_DEST_IN:
return PictOpInReverse;
case CAIRO_OPERATOR_DEST_OUT:
return PictOpOutReverse;
case CAIRO_OPERATOR_DEST_ATOP:
return PictOpAtopReverse;
 
case CAIRO_OPERATOR_XOR:
return PictOpXor;
case CAIRO_OPERATOR_ADD:
return PictOpAdd;
case CAIRO_OPERATOR_SATURATE:
return PictOpSaturate;
 
case CAIRO_OPERATOR_MULTIPLY:
return PictOpMultiply;
case CAIRO_OPERATOR_SCREEN:
return PictOpScreen;
case CAIRO_OPERATOR_OVERLAY:
return PictOpOverlay;
case CAIRO_OPERATOR_DARKEN:
return PictOpDarken;
case CAIRO_OPERATOR_LIGHTEN:
return PictOpLighten;
case CAIRO_OPERATOR_COLOR_DODGE:
return PictOpColorDodge;
case CAIRO_OPERATOR_COLOR_BURN:
return PictOpColorBurn;
case CAIRO_OPERATOR_HARD_LIGHT:
return PictOpHardLight;
case CAIRO_OPERATOR_SOFT_LIGHT:
return PictOpSoftLight;
case CAIRO_OPERATOR_DIFFERENCE:
return PictOpDifference;
case CAIRO_OPERATOR_EXCLUSION:
return PictOpExclusion;
case CAIRO_OPERATOR_HSL_HUE:
return PictOpHSLHue;
case CAIRO_OPERATOR_HSL_SATURATION:
return PictOpHSLSaturation;
case CAIRO_OPERATOR_HSL_COLOR:
return PictOpHSLColor;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return PictOpHSLLuminosity;
 
default:
ASSERT_NOT_REACHED;
return PictOpOver;
}
}
 
static cairo_bool_t
fill_reduces_to_source (cairo_operator_t op,
const cairo_color_t *color,
cairo_xlib_surface_t *dst)
{
if (dst->base.is_clear || CAIRO_COLOR_IS_OPAQUE (color)) {
if (op == CAIRO_OPERATOR_OVER)
return TRUE;
if (op == CAIRO_OPERATOR_ADD)
return (dst->base.content & CAIRO_CONTENT_COLOR) == 0;
}
 
return FALSE;
}
 
static cairo_int_status_t
fill_rectangles (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects)
{
cairo_xlib_surface_t *dst = abstract_surface;
XRenderColor render_color;
int i;
 
//X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable));
 
if (fill_reduces_to_source (op, color, dst))
op = CAIRO_OPERATOR_SOURCE;
 
if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
cairo_int_status_t status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_SOURCE)
status = _cairo_xlib_core_fill_rectangles (dst, color, num_rects, rects);
return status;
}
 
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
 
_cairo_xlib_surface_ensure_picture (dst);
if (num_rects == 1) {
/* Take advantage of the protocol compaction that libXrender performs
* to amalgamate sequences of XRenderFillRectangle().
*/
XRenderFillRectangle (dst->dpy,
_render_operator (op),
dst->picture,
&render_color,
rects->x, rects->y,
rects->width, rects->height);
} else {
XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *xrects = stack_xrects;
 
if (num_rects > ARRAY_LENGTH (stack_xrects)) {
xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
if (unlikely (xrects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
for (i = 0; i < num_rects; i++) {
xrects[i].x = rects[i].x;
xrects[i].y = rects[i].y;
xrects[i].width = rects[i].width;
xrects[i].height = rects[i].height;
}
 
XRenderFillRectangles (dst->dpy,
_render_operator (op),
dst->picture,
&render_color, xrects, num_rects);
 
if (xrects != stack_xrects)
free (xrects);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
fill_boxes (void *abstract_surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
cairo_xlib_surface_t *dst = abstract_surface;
XRenderColor render_color;
 
if (fill_reduces_to_source (op, color, dst))
op = CAIRO_OPERATOR_SOURCE;
 
if (!CAIRO_RENDER_HAS_FILL_RECTANGLES(dst->display)) {
cairo_int_status_t status;
 
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (op == CAIRO_OPERATOR_SOURCE)
status = _cairo_xlib_core_fill_boxes (dst, color, boxes);
return status;
}
 
render_color.red = color->red_short;
render_color.green = color->green_short;
render_color.blue = color->blue_short;
render_color.alpha = color->alpha_short;
 
_cairo_xlib_surface_ensure_picture (dst);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
 
/* Take advantage of the protocol compaction that libXrender performs
* to amalgamate sequences of XRenderFillRectangle().
*/
XRenderFillRectangle (dst->dpy,
_render_operator (op),
dst->picture,
&render_color,
x1, y1,
x2 - x1, y2 - y1);
} else {
XRectangle stack_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *xrects = stack_xrects;
struct _cairo_boxes_chunk *chunk;
int i, j;
 
if (boxes->num_boxes > ARRAY_LENGTH (stack_xrects)) {
xrects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (xrects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
xrects[j].x = x1;
xrects[j].y = y1;
xrects[j].width = x2 - x1;
xrects[j].height = y2 - y1;
j++;
}
}
 
XRenderFillRectangles (dst->dpy,
_render_operator (op),
dst->picture,
&render_color, xrects, j);
 
if (xrects != stack_xrects)
free (xrects);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
#if 0
check_composite ()
operation = _categorize_composite_operation (dst, op, src_pattern,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED)
return UNSUPPORTED ("unsupported operation");
 
//X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable));
 
operation = _recategorize_composite_operation (dst, op, src, &src_attr,
mask_pattern != NULL);
if (operation == DO_UNSUPPORTED) {
status = UNSUPPORTED ("unsupported operation");
goto BAIL;
}
#endif
 
static cairo_int_status_t
composite (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
 
op = _render_operator (op);
 
_cairo_xlib_surface_ensure_picture (dst);
if (abstract_mask) {
cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
 
XRenderComposite (dst->dpy, op,
src->picture, mask->picture, dst->picture,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
} else {
XRenderComposite (dst->dpy, op,
src->picture, 0, dst->picture,
src_x, src_y,
0, 0,
dst_x, dst_y,
width, height);
}
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
lerp (void *abstract_dst,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
cairo_xlib_source_t *mask = (cairo_xlib_source_t *)abstract_mask;
 
_cairo_xlib_surface_ensure_picture (dst);
XRenderComposite (dst->dpy, PictOpOutReverse,
mask->picture, None, dst->picture,
mask_x, mask_y,
0, 0,
dst_x, dst_y,
width, height);
XRenderComposite (dst->dpy, PictOpAdd,
src->picture, mask->picture, dst->picture,
src_x, src_y,
mask_x, mask_y,
dst_x, dst_y,
width, height);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_boxes (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
cairo_xlib_surface_t *dst = abstract_dst;
Picture src = ((cairo_xlib_source_t *)abstract_src)->picture;
Picture mask = abstract_mask ? ((cairo_xlib_source_t *)abstract_mask)->picture : 0;
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
XRectangle *rects = stack_rects;
struct _cairo_boxes_chunk *chunk;
int i, j;
 
op = _render_operator (op);
_cairo_xlib_surface_ensure_picture (dst);
if (boxes->num_boxes == 1) {
int x1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.x);
int y1 = _cairo_fixed_integer_part (boxes->chunks.base[0].p1.y);
int x2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.x);
int y2 = _cairo_fixed_integer_part (boxes->chunks.base[0].p2.y);
 
XRenderComposite (dst->dpy, op,
src, mask, dst->picture,
x1 + src_x, y1 + src_y,
x1 + mask_x, y1 + mask_y,
x1 - dst_x, y1 - dst_y,
x2 - x1, y2 - y1);
return CAIRO_STATUS_SUCCESS;
}
 
if (boxes->num_boxes > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (boxes->num_boxes, sizeof (XRectangle));
if (unlikely (rects == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
j = 0;
for (chunk = &boxes->chunks; chunk; chunk = chunk->next) {
for (i = 0; i < chunk->count; i++) {
int x1 = _cairo_fixed_integer_part (chunk->base[i].p1.x);
int y1 = _cairo_fixed_integer_part (chunk->base[i].p1.y);
int x2 = _cairo_fixed_integer_part (chunk->base[i].p2.x);
int y2 = _cairo_fixed_integer_part (chunk->base[i].p2.y);
 
rects[j].x = x1 - dst_x;
rects[j].y = y1 - dst_y;
rects[j].width = x2 - x1;
rects[j].height = y2 - y1;
j++;
}
}
assert (j == boxes->num_boxes);
 
XRenderSetPictureClipRectangles (dst->dpy,
dst->picture,
0, 0,
rects, j);
if (rects != stack_rects)
free (rects);
 
XRenderComposite (dst->dpy, op,
src, mask, dst->picture,
extents->x + src_x, extents->y + src_y,
extents->x + mask_x, extents->y + mask_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
set_clip_region (dst, NULL);
 
return CAIRO_STATUS_SUCCESS;
}
 
/* font rendering */
 
void
_cairo_xlib_font_close (cairo_xlib_font_t *priv)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *)priv->base.key;
int i;
 
/* XXX All I really want is to do is zap my glyphs... */
_cairo_scaled_font_reset_cache (priv->font);
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info;
 
info = &priv->glyphset[i];
if (info->glyphset)
XRenderFreeGlyphSet (display->display, info->glyphset);
}
 
/* XXX locking */
cairo_list_del (&priv->link);
cairo_list_del (&priv->base.link);
free (priv);
}
 
static void
_cairo_xlib_font_fini (cairo_scaled_font_private_t *abstract_private,
cairo_scaled_font_t *font)
{
cairo_xlib_font_t *priv = (cairo_xlib_font_t *) abstract_private;
cairo_status_t status;
cairo_xlib_display_t *display;
int i;
 
cairo_list_del (&priv->base.link);
cairo_list_del (&priv->link);
 
status = _cairo_xlib_display_acquire (priv->device, &display);
if (status)
goto BAIL;
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info;
 
info = &priv->glyphset[i];
if (info->glyphset)
XRenderFreeGlyphSet (display->display, info->glyphset);
}
 
cairo_device_release (&display->base);
BAIL:
cairo_device_destroy (&display->base);
free (priv);
}
 
static cairo_xlib_font_t *
_cairo_xlib_font_create (cairo_xlib_display_t *display,
cairo_scaled_font_t *font)
{
cairo_xlib_font_t *priv;
int i;
 
priv = malloc (sizeof (cairo_xlib_font_t));
if (unlikely (priv == NULL))
return NULL;
 
_cairo_scaled_font_attach_private (font, &priv->base, display,
_cairo_xlib_font_fini);
 
priv->device = cairo_device_reference (&display->base);
priv->font = font;
cairo_list_add (&priv->link, &display->fonts);
 
for (i = 0; i < NUM_GLYPHSETS; i++) {
cairo_xlib_font_glyphset_t *info = &priv->glyphset[i];
switch (i) {
case GLYPHSET_INDEX_ARGB32: info->format = CAIRO_FORMAT_ARGB32; break;
case GLYPHSET_INDEX_A8: info->format = CAIRO_FORMAT_A8; break;
case GLYPHSET_INDEX_A1: info->format = CAIRO_FORMAT_A1; break;
default: ASSERT_NOT_REACHED; break;
}
info->xrender_format = NULL;
info->glyphset = None;
info->to_free.count = 0;
}
 
return priv;
}
 
static int
_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format)
{
if (format == CAIRO_FORMAT_A8)
return GLYPHSET_INDEX_A8;
if (format == CAIRO_FORMAT_A1)
return GLYPHSET_INDEX_A1;
 
assert (format == CAIRO_FORMAT_ARGB32);
return GLYPHSET_INDEX_ARGB32;
}
 
static inline cairo_xlib_font_t *
_cairo_xlib_font_get (const cairo_xlib_display_t *display,
cairo_scaled_font_t *font)
{
return (cairo_xlib_font_t *)_cairo_scaled_font_find_private (font, display);
}
 
typedef struct {
cairo_scaled_glyph_private_t base;
 
 
cairo_xlib_font_glyphset_t *glyphset;
} cairo_xlib_glyph_private_t;
 
static void
_cairo_xlib_glyph_fini (cairo_scaled_glyph_private_t *glyph_private,
cairo_scaled_glyph_t *glyph,
cairo_scaled_font_t *font)
{
cairo_xlib_glyph_private_t *priv = (cairo_xlib_glyph_private_t *)glyph_private;
 
if (! font->finished) {
cairo_xlib_font_t *font_private;
struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
cairo_xlib_font_glyphset_t *info;
 
font_private = _cairo_xlib_font_get (glyph_private->key, font);
assert (font_private);
 
info = priv->glyphset;
to_free = &info->to_free;
if (to_free->count == ARRAY_LENGTH (to_free->indices)) {
cairo_xlib_display_t *display;
 
if (_cairo_xlib_display_acquire (font_private->device,
&display) == CAIRO_STATUS_SUCCESS) {
XRenderFreeGlyphs (display->display,
info->glyphset,
to_free->indices,
to_free->count);
cairo_device_release (&display->base);
}
 
to_free->count = 0;
}
 
to_free->indices[to_free->count++] =
_cairo_scaled_glyph_index (glyph);
}
 
cairo_list_del (&glyph_private->link);
free (glyph_private);
}
 
static cairo_status_t
_cairo_xlib_glyph_attach (cairo_xlib_display_t *display,
cairo_scaled_glyph_t *glyph,
cairo_xlib_font_glyphset_t *info)
{
cairo_xlib_glyph_private_t *priv;
 
priv = malloc (sizeof (*priv));
if (unlikely (priv == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_scaled_glyph_attach_private (glyph, &priv->base, display,
_cairo_xlib_glyph_fini);
priv->glyphset = info;
 
glyph->dev_private = info;
glyph->dev_private_key = display;
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_xlib_font_glyphset_t *
_cairo_xlib_font_get_glyphset_info_for_format (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
cairo_format_t format)
{
cairo_xlib_font_t *priv;
cairo_xlib_font_glyphset_t *info;
int glyphset_index;
 
glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format);
 
priv = _cairo_xlib_font_get (display, font);
if (priv == NULL) {
priv = _cairo_xlib_font_create (display, font);
if (priv == NULL)
return NULL;
}
 
info = &priv->glyphset[glyphset_index];
if (info->glyphset == None) {
info->xrender_format =
_cairo_xlib_display_get_xrender_format (display, info->format);
info->glyphset = XRenderCreateGlyphSet (display->display,
info->xrender_format);
}
 
return info;
}
 
static cairo_bool_t
has_pending_free_glyph (cairo_xlib_font_glyphset_t *info,
unsigned long glyph_index)
{
struct _cairo_xlib_font_glyphset_free_glyphs *to_free;
int i;
 
to_free = &info->to_free;
for (i = 0; i < to_free->count; i++) {
if (to_free->indices[i] == glyph_index) {
to_free->count--;
memmove (&to_free->indices[i],
&to_free->indices[i+1],
(to_free->count - i) * sizeof (to_free->indices[0]));
return TRUE;
}
}
 
return FALSE;
}
 
static cairo_xlib_font_glyphset_t *
find_pending_free_glyph (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
unsigned long glyph_index,
cairo_image_surface_t *surface)
{
cairo_xlib_font_t *priv;
int i;
 
priv = _cairo_xlib_font_get (display, font);
if (priv == NULL)
return NULL;
 
if (surface != NULL) {
i = _cairo_xlib_get_glyphset_index_for_format (surface->format);
if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
return &priv->glyphset[i];
} else {
for (i = 0; i < NUM_GLYPHSETS; i++) {
if (has_pending_free_glyph (&priv->glyphset[i], glyph_index))
return &priv->glyphset[i];
}
}
 
return NULL;
}
 
static cairo_status_t
_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display,
cairo_scaled_font_t *font,
cairo_scaled_glyph_t **pscaled_glyph)
{
XGlyphInfo glyph_info;
unsigned long glyph_index;
unsigned char *data;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_scaled_glyph_t *glyph = *pscaled_glyph;
cairo_image_surface_t *glyph_surface = glyph->surface;
cairo_bool_t already_had_glyph_surface;
cairo_xlib_font_glyphset_t *info;
 
glyph_index = _cairo_scaled_glyph_index (glyph);
 
/* check to see if we have a pending XRenderFreeGlyph for this glyph */
info = find_pending_free_glyph (display, font, glyph_index, glyph_surface);
if (info != NULL)
return _cairo_xlib_glyph_attach (display, glyph, info);
 
if (glyph_surface == NULL) {
status = _cairo_scaled_glyph_lookup (font,
glyph_index,
CAIRO_SCALED_GLYPH_INFO_METRICS |
CAIRO_SCALED_GLYPH_INFO_SURFACE,
pscaled_glyph);
if (unlikely (status))
return status;
 
glyph = *pscaled_glyph;
glyph_surface = glyph->surface;
already_had_glyph_surface = FALSE;
} else {
already_had_glyph_surface = TRUE;
}
 
info = _cairo_xlib_font_get_glyphset_info_for_format (display, font,
glyph_surface->format);
 
#if 0
/* If the glyph surface has zero height or width, we create
* a clear 1x1 surface, to avoid various X server bugs.
*/
if (glyph_surface->width == 0 || glyph_surface->height == 0) {
cairo_surface_t *tmp_surface;
 
tmp_surface = cairo_image_surface_create (info->format, 1, 1);
status = tmp_surface->status;
if (unlikely (status))
goto BAIL;
 
tmp_surface->device_transform = glyph_surface->base.device_transform;
tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
 
glyph_surface = (cairo_image_surface_t *) tmp_surface;
}
#endif
 
/* If the glyph format does not match the font format, then we
* create a temporary surface for the glyph image with the font's
* format.
*/
if (glyph_surface->format != info->format) {
cairo_surface_pattern_t pattern;
cairo_surface_t *tmp_surface;
 
tmp_surface = cairo_image_surface_create (info->format,
glyph_surface->width,
glyph_surface->height);
status = tmp_surface->status;
if (unlikely (status))
goto BAIL;
 
tmp_surface->device_transform = glyph_surface->base.device_transform;
tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse;
 
_cairo_pattern_init_for_surface (&pattern, &glyph_surface->base);
status = _cairo_surface_paint (tmp_surface,
CAIRO_OPERATOR_SOURCE, &pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
 
glyph_surface = (cairo_image_surface_t *) tmp_surface;
 
if (unlikely (status))
goto BAIL;
}
 
/* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */
glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0);
glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0);
glyph_info.width = glyph_surface->width;
glyph_info.height = glyph_surface->height;
glyph_info.xOff = glyph->x_advance;
glyph_info.yOff = glyph->y_advance;
 
data = glyph_surface->data;
 
/* flip formats around */
switch (_cairo_xlib_get_glyphset_index_for_format (glyph->surface->format)) {
case GLYPHSET_INDEX_A1:
/* local bitmaps are always stored with bit == byte */
if (_cairo_is_little_endian() != (BitmapBitOrder (display->display) == LSBFirst)) {
int c = glyph_surface->stride * glyph_surface->height;
unsigned char *d;
unsigned char *new, *n;
 
new = malloc (c);
if (!new) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
n = new;
d = data;
do {
char b = *d++;
b = ((b << 1) & 0xaa) | ((b >> 1) & 0x55);
b = ((b << 2) & 0xcc) | ((b >> 2) & 0x33);
b = ((b << 4) & 0xf0) | ((b >> 4) & 0x0f);
*n++ = b;
} while (--c);
data = new;
}
break;
case GLYPHSET_INDEX_A8:
break;
case GLYPHSET_INDEX_ARGB32:
if (_cairo_is_little_endian() != (ImageByteOrder (display->display) == LSBFirst)) {
unsigned int c = glyph_surface->stride * glyph_surface->height / 4;
const uint32_t *d;
uint32_t *new, *n;
 
new = malloc (4 * c);
if (unlikely (new == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
n = new;
d = (uint32_t *) data;
do {
*n++ = bswap_32 (*d);
d++;
} while (--c);
data = (uint8_t *) new;
}
break;
default:
ASSERT_NOT_REACHED;
break;
}
/* XXX assume X server wants pixman padding. Xft assumes this as well */
 
XRenderAddGlyphs (display->display, info->glyphset,
&glyph_index, &glyph_info, 1,
(char *) data,
glyph_surface->stride * glyph_surface->height);
 
if (data != glyph_surface->data)
free (data);
 
status = _cairo_xlib_glyph_attach (display, glyph, info);
 
BAIL:
if (glyph_surface != glyph->surface)
cairo_surface_destroy (&glyph_surface->base);
 
/* if the scaled glyph didn't already have a surface attached
* to it, release the created surface now that we have it
* uploaded to the X server. If the surface has already been
* there (eg. because image backend requested it), leave it in
* the cache
*/
if (!already_had_glyph_surface)
_cairo_scaled_glyph_set_surface (glyph, font, NULL);
 
return status;
}
 
typedef void (*cairo_xrender_composite_text_func_t)
(Display *dpy,
int op,
Picture src,
Picture dst,
_Xconst XRenderPictFormat *maskFormat,
int xSrc,
int ySrc,
int xDst,
int yDst,
_Xconst XGlyphElt8 *elts,
int nelt);
 
/* Build a struct of the same size of #cairo_glyph_t that can be used both as
* an input glyph with double coordinates, and as "working" glyph with
* integer from-current-point offsets. */
typedef union {
cairo_glyph_t d;
unsigned long index;
struct {
unsigned long index;
int x;
int y;
} i;
} cairo_xlib_glyph_t;
 
/* compile-time assert that #cairo_xlib_glyph_t is the same size as #cairo_glyph_t */
COMPILE_TIME_ASSERT (sizeof (cairo_xlib_glyph_t) == sizeof (cairo_glyph_t));
 
/* Start a new element for the first glyph,
* or for any glyph that has unexpected position,
* or if current element has too many glyphs
* (Xrender limits each element to 252 glyphs, we limit them to 128)
*
* These same conditions need to be mirrored between
* _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks
*/
#define _start_new_glyph_elt(count, glyph) \
(((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y)
 
static cairo_status_t
_emit_glyphs_chunk (cairo_xlib_display_t *display,
cairo_xlib_surface_t *dst,
int dst_x, int dst_y,
cairo_xlib_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *font,
cairo_bool_t use_mask,
cairo_operator_t op,
cairo_xlib_source_t *src,
int src_x, int src_y,
/* info for this chunk */
int num_elts,
int width,
cairo_xlib_font_glyphset_t *info)
{
/* Which XRenderCompositeText function to use */
cairo_xrender_composite_text_func_t composite_text_func;
int size;
 
/* Element buffer stuff */
XGlyphElt8 *elts;
XGlyphElt8 stack_elts[CAIRO_STACK_ARRAY_LENGTH (XGlyphElt8)];
 
/* Reuse the input glyph array for output char generation */
char *char8 = (char *) glyphs;
unsigned short *char16 = (unsigned short *) glyphs;
unsigned int *char32 = (unsigned int *) glyphs;
 
int i;
int nelt; /* Element index */
int n; /* Num output glyphs in current element */
int j; /* Num output glyphs so far */
 
switch (width) {
case 1:
/* don't cast the 8-variant, to catch possible mismatches */
composite_text_func = XRenderCompositeText8;
size = sizeof (char);
break;
case 2:
composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText16;
size = sizeof (unsigned short);
break;
default:
case 4:
composite_text_func = (cairo_xrender_composite_text_func_t) XRenderCompositeText32;
size = sizeof (unsigned int);
}
 
/* Allocate element array */
if (num_elts <= ARRAY_LENGTH (stack_elts)) {
elts = stack_elts;
} else {
elts = _cairo_malloc_ab (num_elts, sizeof (XGlyphElt8));
if (unlikely (elts == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* Fill them in */
nelt = 0;
n = 0;
j = 0;
for (i = 0; i < num_glyphs; i++) {
/* Start a new element for first output glyph,
* or for any glyph that has unexpected position,
* or if current element has too many glyphs.
*
* These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs()
*/
if (_start_new_glyph_elt (j, &glyphs[i])) {
if (j) {
elts[nelt].nchars = n;
nelt++;
n = 0;
}
elts[nelt].chars = char8 + size * j;
elts[nelt].glyphset = info->glyphset;
elts[nelt].xOff = glyphs[i].i.x;
elts[nelt].yOff = glyphs[i].i.y;
}
 
switch (width) {
case 1: char8 [j] = (char) glyphs[i].index; break;
case 2: char16[j] = (unsigned short) glyphs[i].index; break;
default:
case 4: char32[j] = (unsigned int) glyphs[i].index; break;
}
 
n++;
j++;
}
 
if (n) {
elts[nelt].nchars = n;
nelt++;
}
 
/* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the
* expected number of xGlyphElts. */
assert (nelt == num_elts);
 
composite_text_func (display->display, op,
src->picture,
dst->picture,
use_mask ? info->xrender_format : NULL,
src_x + elts[0].xOff + dst_x,
src_y + elts[0].yOff + dst_y,
elts[0].xOff, elts[0].yOff,
(XGlyphElt8 *) elts, nelt);
 
if (elts != stack_elts)
free (elts);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
cairo_xlib_surface_t *dst = (cairo_xlib_surface_t *)extents->surface;
cairo_xlib_display_t *display = dst->display;
int max_request_size, size;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (! CAIRO_RENDER_SUPPORTS_OPERATOR (display, extents->op))
return CAIRO_INT_STATUS_UNSUPPORTED;
 
/* The glyph coordinates must be representable in an int16_t.
* When possible, they will be expressed as an offset from the
* previous glyph, otherwise they will be an offset from the
* surface origin. If we can't guarantee this to be possible,
* fallback.
*/
if (extents->bounded.x + extents->bounded.width > INT16_MAX ||
extents->bounded.y + extents->bounded.height> INT16_MAX ||
extents->bounded.x < INT16_MIN ||
extents->bounded.y < INT16_MIN)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
 
/* Approximate the size of the largest glyph and fallback if we can not
* upload it to the xserver.
*/
size = ceil (font->max_scale);
size = 4 * size * size;
max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display)
: XMaxRequestSize (display->display)) * 4 -
sz_xRenderAddGlyphsReq -
sz_xGlyphInfo -
8;
if (size >= max_request_size)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
return CAIRO_STATUS_SUCCESS;
}
 
/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have
* enough room for padding */
#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4)
 
static cairo_int_status_t
composite_glyphs (void *surface,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
cairo_xlib_surface_t *dst = surface;
cairo_xlib_glyph_t *glyphs = (cairo_xlib_glyph_t *)info->glyphs;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)_src;
cairo_xlib_display_t *display = dst->display;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
cairo_scaled_glyph_t *glyph;
cairo_fixed_t x = dst_x, y = dst_y;
cairo_xlib_font_glyphset_t *glyphset = NULL, *this_glyphset_info;
 
unsigned long max_index = 0;
int width = 1;
int num_elts = 0;
int num_out_glyphs = 0;
int num_glyphs = info->num_glyphs;
 
int max_request_size = XMaxRequestSize (display->display) * 4
- MAX (sz_xRenderCompositeGlyphs8Req,
MAX(sz_xRenderCompositeGlyphs16Req,
sz_xRenderCompositeGlyphs32Req));
int request_size = 0;
int i;
 
op = _render_operator (op),
_cairo_xlib_surface_ensure_picture (dst);
for (i = 0; i < num_glyphs; i++) {
int this_x, this_y;
int old_width;
 
status = _cairo_scaled_glyph_lookup (info->font,
glyphs[i].index,
CAIRO_SCALED_GLYPH_INFO_METRICS,
&glyph);
if (unlikely (status))
return status;
 
this_x = _cairo_lround (glyphs[i].d.x);
this_y = _cairo_lround (glyphs[i].d.y);
 
/* Send unsent glyphs to the server */
if (glyph->dev_private_key != display) {
status = _cairo_xlib_surface_add_glyph (display, info->font, &glyph);
if (unlikely (status))
return status;
}
 
this_glyphset_info = glyph->dev_private;
if (!glyphset)
glyphset = this_glyphset_info;
 
/* The invariant here is that we can always flush the glyphs
* accumulated before this one, using old_width, and they
* would fit in the request.
*/
old_width = width;
 
/* Update max glyph index */
if (glyphs[i].index > max_index) {
max_index = glyphs[i].index;
if (max_index >= 65536)
width = 4;
else if (max_index >= 256)
width = 2;
if (width != old_width)
request_size += (width - old_width) * num_out_glyphs;
}
 
/* If we will pass the max request size by adding this glyph,
* flush current glyphs. Note that we account for a
* possible element being added below.
*
* Also flush if changing glyphsets, as Xrender limits one mask
* format per request, so we can either break up, or use a
* wide-enough mask format. We do the former. One reason to
* prefer the latter is the fact that Xserver ADDs all glyphs
* to the mask first, and then composes that to final surface,
* though it's not a big deal.
*
* If the glyph has a coordinate which cannot be represented
* as a 16-bit offset from the previous glyph, flush the
* current chunk. The current glyph will be the first one in
* the next chunk, thus its coordinates will be an offset from
* the destination origin. This offset is guaranteed to be
* representable as 16-bit offset (otherwise we would have
* fallen back).
*/
if (request_size + width > max_request_size - _cairo_sz_xGlyphElt ||
this_x - x > INT16_MAX || this_x - x < INT16_MIN ||
this_y - y > INT16_MAX || this_y - y < INT16_MIN ||
(this_glyphset_info != glyphset)) {
status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
glyphs, i, info->font, info->use_mask,
op, src, src_x, src_y,
num_elts, old_width, glyphset);
if (unlikely (status))
return status;
 
glyphs += i;
num_glyphs -= i;
i = 0;
max_index = glyphs[i].index;
width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4;
request_size = 0;
num_elts = 0;
num_out_glyphs = 0;
x = y = 0;
glyphset = this_glyphset_info;
}
 
/* Convert absolute glyph position to relative-to-current-point
* position */
glyphs[i].i.x = this_x - x;
glyphs[i].i.y = this_y - y;
 
/* Start a new element for the first glyph,
* or for any glyph that has unexpected position,
* or if current element has too many glyphs.
*
* These same conditions are mirrored in _emit_glyphs_chunk().
*/
if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) {
num_elts++;
request_size += _cairo_sz_xGlyphElt;
}
 
/* adjust current-position */
x = this_x + glyph->x_advance;
y = this_y + glyph->y_advance;
 
num_out_glyphs++;
request_size += width;
}
 
if (num_elts) {
status = _emit_glyphs_chunk (display, dst, dst_x, dst_y,
glyphs, i, info->font, info->use_mask,
op, src, src_x, src_y,
num_elts, width, glyphset);
}
 
return status;
}
 
const cairo_compositor_t *
_cairo_xlib_mask_compositor_get (void)
{
static cairo_mask_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_mask_compositor_init (&compositor,
_cairo_xlib_fallback_compositor_get ());
 
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
compositor.fill_rectangles = fill_rectangles;
compositor.fill_boxes = fill_boxes;
compositor.copy_boxes = copy_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
 
return &compositor.base;
}
 
#define CAIRO_FIXED_16_16_MIN -32768
#define CAIRO_FIXED_16_16_MAX 32767
 
static cairo_bool_t
line_exceeds_16_16 (const cairo_line_t *line)
{
return
line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) ||
line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) ||
line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX);
}
 
static void
project_line_x_onto_16_16 (const cairo_line_t *line,
cairo_fixed_t top,
cairo_fixed_t bottom,
XLineFixed *out)
{
cairo_point_double_t p1, p2;
double m;
 
p1.x = _cairo_fixed_to_double (line->p1.x);
p1.y = _cairo_fixed_to_double (line->p1.y);
 
p2.x = _cairo_fixed_to_double (line->p2.x);
p2.y = _cairo_fixed_to_double (line->p2.y);
 
m = (p2.x - p1.x) / (p2.y - p1.y);
out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y));
out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y));
}
#if 0
static cairo_int_status_T
check_composite_trapezoids ()
{
operation = _categorize_composite_operation (dst, op, pattern, TRUE);
if (operation == DO_UNSUPPORTED)
return UNSUPPORTED ("unsupported operation");
 
operation = _recategorize_composite_operation (dst, op, src,
&attributes, TRUE);
if (operation == DO_UNSUPPORTED) {
status = UNSUPPORTED ("unsupported operation");
goto BAIL;
}
 
}
#endif
 
static cairo_int_status_t
composite_traps (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_display_t *display = dst->display;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
XRenderPictFormat *pict_format;
XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
XTrapezoid *xtraps = xtraps_stack;
int dx, dy;
int i;
 
//X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
 
if (dst->base.is_clear &&
(op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD))
{
op = CAIRO_OPERATOR_SOURCE;
}
 
pict_format =
_cairo_xlib_display_get_xrender_format (display,
antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
 
if (traps->num_traps > ARRAY_LENGTH (xtraps_stack)) {
xtraps = _cairo_malloc_ab (traps->num_traps, sizeof (XTrapezoid));
if (unlikely (xtraps == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
dx = -dst_x << 16;
dy = -dst_y << 16;
for (i = 0; i < traps->num_traps; i++) {
cairo_trapezoid_t *t = &traps->traps[i];
 
/* top/bottom will be clamped to surface bounds */
xtraps[i].top = _cairo_fixed_to_16_16(t->top) + dy;
xtraps[i].bottom = _cairo_fixed_to_16_16(t->bottom) + dy;
 
/* However, all the other coordinates will have been left untouched so
* as not to introduce numerical error. Recompute them if they
* exceed the 16.16 limits.
*/
if (unlikely (line_exceeds_16_16 (&t->left))) {
project_line_x_onto_16_16 (&t->left, t->top, t->bottom,
&xtraps[i].left);
xtraps[i].left.p1.x += dx;
xtraps[i].left.p2.x += dx;
xtraps[i].left.p1.y = xtraps[i].top;
xtraps[i].left.p2.y = xtraps[i].bottom;
} else {
xtraps[i].left.p1.x = _cairo_fixed_to_16_16(t->left.p1.x) + dx;
xtraps[i].left.p1.y = _cairo_fixed_to_16_16(t->left.p1.y) + dy;
xtraps[i].left.p2.x = _cairo_fixed_to_16_16(t->left.p2.x) + dx;
xtraps[i].left.p2.y = _cairo_fixed_to_16_16(t->left.p2.y) + dy;
}
 
if (unlikely (line_exceeds_16_16 (&t->right))) {
project_line_x_onto_16_16 (&t->right, t->top, t->bottom,
&xtraps[i].right);
xtraps[i].right.p1.x += dx;
xtraps[i].right.p2.x += dx;
xtraps[i].right.p1.y = xtraps[i].top;
xtraps[i].right.p2.y = xtraps[i].bottom;
} else {
xtraps[i].right.p1.x = _cairo_fixed_to_16_16(t->right.p1.x) + dx;
xtraps[i].right.p1.y = _cairo_fixed_to_16_16(t->right.p1.y) + dy;
xtraps[i].right.p2.x = _cairo_fixed_to_16_16(t->right.p2.x) + dx;
xtraps[i].right.p2.y = _cairo_fixed_to_16_16(t->right.p2.y) + dy;
}
}
 
if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) {
src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p1.x);
src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p1.y);
} else {
src_x += _cairo_fixed_16_16_floor (xtraps[0].left.p2.x);
src_y += _cairo_fixed_16_16_floor (xtraps[0].left.p2.y);
}
src_x += dst_x;
src_y += dst_y;
 
_cairo_xlib_surface_ensure_picture (dst);
_cairo_xlib_surface_set_precision (dst, antialias);
XRenderCompositeTrapezoids (dst->dpy,
_render_operator (op),
src->picture, dst->picture,
pict_format,
src_x, src_y,
xtraps, traps->num_traps);
 
if (xtraps != xtraps_stack)
free (xtraps);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_tristrip (void *abstract_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_tristrip_t *strip)
{
cairo_xlib_surface_t *dst = abstract_dst;
cairo_xlib_display_t *display = dst->display;
cairo_xlib_source_t *src = (cairo_xlib_source_t *)abstract_src;
XRenderPictFormat *pict_format;
XPointFixed points_stack[CAIRO_STACK_ARRAY_LENGTH (XPointFixed)];
XPointFixed *points = points_stack;
int dx, dy;
int i;
 
//X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable));
 
pict_format =
_cairo_xlib_display_get_xrender_format (display,
antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8);
 
if (strip->num_points > ARRAY_LENGTH (points_stack)) {
points = _cairo_malloc_ab (strip->num_points, sizeof (XPointFixed));
if (unlikely (points == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
dx = -dst_x << 16;
dy = -dst_y << 16;
for (i = 0; i < strip->num_points; i++) {
cairo_point_t *p = &strip->points[i];
 
points[i].x = _cairo_fixed_to_16_16(p->x) + dx;
points[i].y = _cairo_fixed_to_16_16(p->y) + dy;
}
 
src_x += _cairo_fixed_16_16_floor (points[0].x) + dst_x;
src_y += _cairo_fixed_16_16_floor (points[0].y) + dst_y;
 
_cairo_xlib_surface_ensure_picture (dst);
_cairo_xlib_surface_set_precision (dst, antialias);
XRenderCompositeTriStrip (dst->dpy,
_render_operator (op),
src->picture, dst->picture,
pict_format,
src_x, src_y,
points, strip->num_points);
 
if (points != points_stack)
free (points);
 
return CAIRO_STATUS_SUCCESS;
}
 
const cairo_compositor_t *
_cairo_xlib_traps_compositor_get (void)
{
static cairo_traps_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_traps_compositor_init (&compositor,
_cairo_xlib_mask_compositor_get ());
 
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = _cairo_xlib_source_create_for_pattern;
compositor.draw_image_boxes = draw_image_boxes;
compositor.copy_boxes = copy_boxes;
compositor.fill_boxes = fill_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_composite_traps = check_composite_traps;
compositor.composite_traps = composite_traps;
//compositor.check_composite_tristrip = check_composite_tristrip;
compositor.composite_tristrip = composite_tristrip;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
 
return &compositor.base;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-screen.c
0,0 → 1,467
/* Cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Partially on code from xftdpy.c
*
* Copyright © 2000 Keith Packard
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Keith Packard not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Keith Packard makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
#include "cairo-xlib-xrender-private.h"
 
#include "cairo-xlib-surface-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
 
#include "cairo-fontconfig-private.h"
 
static int
parse_boolean (const char *v)
{
char c0, c1;
 
c0 = *v;
if (c0 == 't' || c0 == 'T' || c0 == 'y' || c0 == 'Y' || c0 == '1')
return 1;
if (c0 == 'f' || c0 == 'F' || c0 == 'n' || c0 == 'N' || c0 == '0')
return 0;
if (c0 == 'o')
{
c1 = v[1];
if (c1 == 'n' || c1 == 'N')
return 1;
if (c1 == 'f' || c1 == 'F')
return 0;
}
 
return -1;
}
 
static cairo_bool_t
get_boolean_default (Display *dpy,
const char *option,
cairo_bool_t *value)
{
char *v;
int i;
 
v = XGetDefault (dpy, "Xft", option);
if (v) {
i = parse_boolean (v);
if (i >= 0) {
*value = i;
return TRUE;
}
}
 
return FALSE;
}
 
static cairo_bool_t
get_integer_default (Display *dpy,
const char *option,
int *value)
{
char *v, *e;
 
v = XGetDefault (dpy, "Xft", option);
if (v) {
#if CAIRO_HAS_FC_FONT
if (FcNameConstant ((FcChar8 *) v, value))
return TRUE;
#endif
 
*value = strtol (v, &e, 0);
if (e != v)
return TRUE;
}
 
return FALSE;
}
 
static void
_cairo_xlib_init_screen_font_options (Display *dpy,
cairo_xlib_screen_t *info)
{
cairo_bool_t xft_hinting;
cairo_bool_t xft_antialias;
int xft_hintstyle;
int xft_rgba;
int xft_lcdfilter;
cairo_antialias_t antialias;
cairo_subpixel_order_t subpixel_order;
cairo_lcd_filter_t lcd_filter;
cairo_hint_style_t hint_style;
 
if (!get_boolean_default (dpy, "antialias", &xft_antialias))
xft_antialias = TRUE;
 
if (!get_integer_default (dpy, "lcdfilter", &xft_lcdfilter)) {
/* -1 is an non-existant Fontconfig constant used to differentiate
* the case when no lcdfilter property is available.
*/
xft_lcdfilter = -1;
}
 
if (!get_boolean_default (dpy, "hinting", &xft_hinting))
xft_hinting = TRUE;
 
if (!get_integer_default (dpy, "hintstyle", &xft_hintstyle))
xft_hintstyle = FC_HINT_FULL;
 
if (!get_integer_default (dpy, "rgba", &xft_rgba))
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *) info->device;
 
xft_rgba = FC_RGBA_UNKNOWN;
 
#if RENDER_MAJOR > 0 || RENDER_MINOR >= 6
if (display->render_major > 0 || display->render_minor >= 6) {
int render_order = XRenderQuerySubpixelOrder (dpy,
XScreenNumberOfScreen (info->screen));
 
switch (render_order) {
default:
case SubPixelUnknown:
xft_rgba = FC_RGBA_UNKNOWN;
break;
case SubPixelHorizontalRGB:
xft_rgba = FC_RGBA_RGB;
break;
case SubPixelHorizontalBGR:
xft_rgba = FC_RGBA_BGR;
break;
case SubPixelVerticalRGB:
xft_rgba = FC_RGBA_VRGB;
break;
case SubPixelVerticalBGR:
xft_rgba = FC_RGBA_VBGR;
break;
case SubPixelNone:
xft_rgba = FC_RGBA_NONE;
break;
}
}
#endif
}
 
if (xft_hinting) {
switch (xft_hintstyle) {
case FC_HINT_NONE:
hint_style = CAIRO_HINT_STYLE_NONE;
break;
case FC_HINT_SLIGHT:
hint_style = CAIRO_HINT_STYLE_SLIGHT;
break;
case FC_HINT_MEDIUM:
hint_style = CAIRO_HINT_STYLE_MEDIUM;
break;
case FC_HINT_FULL:
hint_style = CAIRO_HINT_STYLE_FULL;
break;
default:
hint_style = CAIRO_HINT_STYLE_DEFAULT;
}
} else {
hint_style = CAIRO_HINT_STYLE_NONE;
}
 
switch (xft_rgba) {
case FC_RGBA_RGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
break;
case FC_RGBA_BGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
break;
case FC_RGBA_VRGB:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
break;
case FC_RGBA_VBGR:
subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
break;
case FC_RGBA_UNKNOWN:
case FC_RGBA_NONE:
default:
subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
 
switch (xft_lcdfilter) {
case FC_LCD_NONE:
lcd_filter = CAIRO_LCD_FILTER_NONE;
break;
case FC_LCD_DEFAULT:
lcd_filter = CAIRO_LCD_FILTER_FIR5;
break;
case FC_LCD_LIGHT:
lcd_filter = CAIRO_LCD_FILTER_FIR3;
break;
case FC_LCD_LEGACY:
lcd_filter = CAIRO_LCD_FILTER_INTRA_PIXEL;
break;
default:
lcd_filter = CAIRO_LCD_FILTER_DEFAULT;
break;
}
 
if (xft_antialias) {
if (subpixel_order == CAIRO_SUBPIXEL_ORDER_DEFAULT)
antialias = CAIRO_ANTIALIAS_GRAY;
else
antialias = CAIRO_ANTIALIAS_SUBPIXEL;
} else {
antialias = CAIRO_ANTIALIAS_NONE;
}
 
cairo_font_options_set_hint_style (&info->font_options, hint_style);
cairo_font_options_set_antialias (&info->font_options, antialias);
cairo_font_options_set_subpixel_order (&info->font_options, subpixel_order);
_cairo_font_options_set_lcd_filter (&info->font_options, lcd_filter);
cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
}
 
void
_cairo_xlib_screen_destroy (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info)
{
Display *dpy;
int i;
 
dpy = display->display;
 
while (! cairo_list_is_empty (&info->surfaces)) {
cairo_xlib_surface_t *surface;
 
surface = cairo_list_first_entry (&info->surfaces,
cairo_xlib_surface_t,
link);
cairo_surface_finish (&surface->base);
}
 
for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
if (info->gc_depths[i] != 0) {
XFreeGC (dpy, info->gc[i]);
info->gc_depths[i] = 0;
}
}
 
while (! cairo_list_is_empty (&info->visuals)) {
_cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals,
cairo_xlib_visual_info_t,
link));
}
 
cairo_list_del (&info->link);
 
free (info);
}
 
cairo_status_t
_cairo_xlib_screen_get (Display *dpy,
Screen *screen,
cairo_xlib_screen_t **out)
{
cairo_xlib_display_t *display;
cairo_device_t *device;
cairo_xlib_screen_t *info;
cairo_status_t status;
 
device = _cairo_xlib_device_create (dpy);
status = device->status;
if (unlikely (status))
goto CLEANUP_DEVICE;
 
status = _cairo_xlib_display_acquire (device, &display);
if (unlikely (status))
goto CLEANUP_DEVICE;
 
info = _cairo_xlib_display_get_screen (display, screen);
if (info != NULL) {
*out = info;
goto CLEANUP_DISPLAY;
}
 
info = malloc (sizeof (cairo_xlib_screen_t));
if (unlikely (info == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto CLEANUP_DISPLAY;
}
 
info->device = device;
info->screen = screen;
info->has_font_options = FALSE;
memset (info->gc_depths, 0, sizeof (info->gc_depths));
memset (info->gc, 0, sizeof (info->gc));
 
cairo_list_init (&info->surfaces);
cairo_list_init (&info->visuals);
cairo_list_add (&info->link, &display->screens);
 
*out = info;
 
CLEANUP_DISPLAY:
cairo_device_release (&display->base);
 
CLEANUP_DEVICE:
cairo_device_destroy (device);
return status;
}
 
GC
_cairo_xlib_screen_get_gc (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info,
int depth,
Drawable drawable)
{
GC gc = NULL;
int i;
 
for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
if (info->gc_depths[i] == depth) {
info->gc_depths[i] = 0;
gc = info->gc[i];
break;
}
}
 
if (gc == NULL) {
XGCValues gcv;
 
gcv.graphics_exposures = False;
gcv.fill_style = FillTiled;
gc = XCreateGC (display->display,
drawable,
GCGraphicsExposures | GCFillStyle, &gcv);
}
 
return gc;
}
 
void
_cairo_xlib_screen_put_gc (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info,
int depth,
GC gc)
{
int i;
 
for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
if (info->gc_depths[i] == 0)
break;
}
 
if (i == ARRAY_LENGTH (info->gc)) {
/* perform random substitution to ensure fair caching over depths */
i = rand () % ARRAY_LENGTH (info->gc);
XFreeGC(display->display, info->gc[i]);
}
 
info->gc[i] = gc;
info->gc_depths[i] = depth;
}
 
cairo_status_t
_cairo_xlib_screen_get_visual_info (cairo_xlib_display_t *display,
cairo_xlib_screen_t *info,
Visual *v,
cairo_xlib_visual_info_t **out)
{
cairo_xlib_visual_info_t *visual;
cairo_status_t status;
 
cairo_list_foreach_entry (visual,
cairo_xlib_visual_info_t,
&info->visuals,
link)
{
if (visual->visualid == v->visualid) {
*out = visual;
return CAIRO_STATUS_SUCCESS;
}
}
 
status = _cairo_xlib_visual_info_create (display->display,
XScreenNumberOfScreen (info->screen),
v->visualid,
&visual);
if (unlikely (status))
return status;
 
cairo_list_add (&visual->link, &info->visuals);
*out = visual;
return CAIRO_STATUS_SUCCESS;
}
 
cairo_font_options_t *
_cairo_xlib_screen_get_font_options (cairo_xlib_screen_t *info)
{
if (! info->has_font_options) {
_cairo_font_options_init_default (&info->font_options);
_cairo_font_options_set_round_glyph_positions (&info->font_options, CAIRO_ROUND_GLYPH_POS_ON);
 
if (info->screen != NULL) {
cairo_xlib_display_t *display;
 
if (! _cairo_xlib_display_acquire (info->device, &display)) {
_cairo_xlib_init_screen_font_options (display->display,
info);
cairo_device_release (&display->base);
}
}
 
info->has_font_options = TRUE;
}
 
return &info->font_options;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-source.c
0,0 → 1,1153
/* -*- 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_similar_scratch (&dst->base,
is_mask ? CAIRO_CONTENT_ALPHA : CAIRO_CONTENT_COLOR_ALPHA,
extents->width,
extents->height);
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_similar_scratch (&dst->base,
sub->base.content,
sub->extents.width,
sub->extents.height);
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))
surface = _cairo_paginated_surface_get_recording (surface);
if (_cairo_surface_is_snapshot (surface))
surface = _cairo_surface_snapshot_get_target (surface);
return 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_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_similar_scratch (&dst->base,
pattern->surface->content,
upload.width,
upload.height);
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);
status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (&pattern->base),
&matrix, &src->base,
NULL);
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_similar_scratch (&dst->base,
src->content,
upload.width,
upload.height);
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;
}
 
if (! CAIRO_RENDER_HAS_PICTURE_TRANSFORM (display)) {
if (!_cairo_matrix_is_integer_translation (&pattern->matrix, NULL, NULL))
return FALSE;
}
 
if (! CAIRO_RENDER_HAS_FILTERS (display)) {
/* No filters implies no transforms, so we optimise away BILINEAR */
}
 
return TRUE;
}
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 */
/programs/develop/libraries/cairo/src/cairo-xlib-surface-private.h
38,75 → 38,7
#include "cairo-xlib-xrender-private.h"
 
#include "cairo-surface-private.h"
#include "cairo-surface-backend-private.h"
 
typedef struct _cairo_xlib_surface cairo_xlib_surface_t;
 
struct _cairo_xlib_surface {
cairo_surface_t base;
 
cairo_xlib_screen_t *screen;
cairo_xlib_hook_t close_display_hook;
 
Drawable drawable;
cairo_bool_t owns_pixmap;
Visual *visual;
 
int use_pixmap;
 
int render_major;
int render_minor;
 
/* TRUE if the server has a bug with repeating pictures
*
* https://bugs.freedesktop.org/show_bug.cgi?id=3566
*
* We can't test for this because it depends on whether the
* picture is in video memory or not.
*
* We also use this variable as a guard against a second
* independent bug with transformed repeating pictures:
*
* http://lists.freedesktop.org/archives/cairo/2004-September/001839.html
*
* Both are fixed in xorg >= 6.9 and hopefully in > 6.8.2, so
* we can reuse the test for now.
*/
unsigned int buggy_gradients : 1;
unsigned int buggy_pad_reflect : 1;
unsigned int buggy_repeat : 1;
#define CAIRO_XLIB_SURFACE_HAS_BUGGY_GRADIENTS 1
#define CAIRO_XLIB_SURFACE_HAS_BUGGY_PAD_REFLECT 1
#define CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT 1
 
int width;
int height;
int depth;
 
Picture dst_picture, src_picture;
 
unsigned int clip_dirty;
XRectangle embedded_clip_rects[8];
XRectangle *clip_rects;
int num_clip_rects;
cairo_region_t *clip_region;
 
XRenderPictFormat *xrender_format;
cairo_filter_t filter;
cairo_extend_t extend;
cairo_bool_t has_component_alpha;
int precision;
XTransform xtransform;
 
uint32_t a_mask;
uint32_t r_mask;
uint32_t g_mask;
uint32_t b_mask;
};
 
enum {
CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC = 0x01,
CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE = 0x02,
CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL = 0x03
};
 
#endif /* CAIRO_XLIB_SURFACE_PRIVATE_H */
/programs/develop/libraries/cairo/src/cairo-xlib-surface-shm.c
0,0 → 1,1459
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
#include "cairo-xlib-surface-private.h"
 
#if !HAVE_X11_EXTENSIONS_XSHM_H || !(HAVE_X11_EXTENSIONS_SHMPROTO_H || HAVE_X11_EXTENSIONS_SHMSTR_H)
void _cairo_xlib_display_init_shm (cairo_xlib_display_t *display) {}
 
cairo_surface_t *
_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
cairo_bool_t overwrite)
{
return NULL;
}
 
cairo_int_status_t
_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
{
assert (!surface->fallback);
return CAIRO_INT_STATUS_SUCCESS;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
pixman_format_code_t format,
int width, int height)
{
return NULL;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
pixman_format_code_t format,
int width, int height)
{
return NULL;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_similar_shm (void *other,
cairo_format_t format,
int width, int height)
{
return cairo_image_surface_create (format, width, height);
}
 
void
_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
{
ASSERT_NOT_REACHED;
}
 
void
_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
XImage *ximage)
{
ASSERT_NOT_REACHED;
}
 
void *
_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
{
ASSERT_NOT_REACHED;
return NULL;
}
 
Pixmap
_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
{
ASSERT_NOT_REACHED;
return 0;
}
 
XRenderPictFormat *
_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
{
ASSERT_NOT_REACHED;
return NULL;
}
 
cairo_bool_t
_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
{
ASSERT_NOT_REACHED;
return FALSE;
}
 
cairo_bool_t
_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
{
ASSERT_NOT_REACHED;
return TRUE;
}
 
void _cairo_xlib_display_fini_shm (cairo_xlib_display_t *display) {}
 
#else
 
#include "cairo-damage-private.h"
#include "cairo-default-context-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-mempool-private.h"
 
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
#include <X11/extensions/XShm.h>
#if HAVE_X11_EXTENSIONS_SHMPROTO_H
#include <X11/extensions/shmproto.h>
#elif HAVE_X11_EXTENSIONS_SHMSTR_H
#include <X11/extensions/shmstr.h>
#endif
#include <sys/ipc.h>
#include <sys/shm.h>
 
#define MIN_PIXMAP_SIZE 4096
 
#define MIN_BITS 8
#define MIN_SIZE (1<<(MIN_BITS-1))
 
typedef struct _cairo_xlib_shm cairo_xlib_shm_t;
typedef struct _cairo_xlib_shm_info cairo_xlib_shm_info_t;
typedef struct _cairo_xlib_shm_surface cairo_xlib_shm_surface_t;
 
struct _cairo_xlib_shm {
cairo_mempool_t mem;
 
XShmSegmentInfo shm;
unsigned long attached;
cairo_list_t link;
};
 
struct _cairo_xlib_shm_info {
unsigned long last_request;
void *mem;
size_t size;
cairo_xlib_shm_t *pool;
};
 
struct _cairo_xlib_shm_surface {
cairo_image_surface_t image;
 
cairo_list_t link;
cairo_xlib_shm_info_t *info;
Pixmap pixmap;
unsigned long active;
int idle;
};
 
/* the parent is always given by index/2 */
#define PQ_PARENT_INDEX(i) ((i) >> 1)
#define PQ_FIRST_ENTRY 1
 
/* left and right children are index * 2 and (index * 2) +1 respectively */
#define PQ_LEFT_CHILD_INDEX(i) ((i) << 1)
 
#define PQ_TOP(pq) ((pq)->elements[PQ_FIRST_ENTRY])
 
struct pqueue {
int size, max_size;
cairo_xlib_shm_info_t **elements;
};
 
struct _cairo_xlib_shm_display {
int has_pixmaps;
int opcode;
int event;
 
Window window;
unsigned long last_request;
unsigned long last_event;
 
cairo_list_t surfaces;
 
cairo_list_t pool;
struct pqueue info;
};
 
static inline cairo_bool_t
seqno_passed (unsigned long a, unsigned long b)
{
return (long)(b - a) >= 0;
}
 
static inline cairo_bool_t
seqno_before (unsigned long a, unsigned long b)
{
return (long)(b - a) > 0;
}
 
static inline cairo_bool_t
seqno_after (unsigned long a, unsigned long b)
{
return (long)(a - b) > 0;
}
 
static inline cairo_status_t
_pqueue_init (struct pqueue *pq)
{
pq->max_size = 32;
pq->size = 0;
 
pq->elements = _cairo_malloc_ab (pq->max_size,
sizeof (cairo_xlib_shm_info_t *));
if (unlikely (pq->elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
PQ_TOP(pq) = NULL;
return CAIRO_STATUS_SUCCESS;
}
 
static inline void
_pqueue_fini (struct pqueue *pq)
{
free (pq->elements);
}
 
static cairo_status_t
_pqueue_grow (struct pqueue *pq)
{
cairo_xlib_shm_info_t **new_elements;
 
new_elements = _cairo_realloc_ab (pq->elements,
2 * pq->max_size,
sizeof (cairo_xlib_shm_info_t *));
if (unlikely (new_elements == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
pq->elements = new_elements;
pq->max_size *= 2;
return CAIRO_STATUS_SUCCESS;
}
 
static void
_pqueue_shrink (struct pqueue *pq, int min_size)
{
cairo_xlib_shm_info_t **new_elements;
 
if (min_size > pq->max_size)
return;
 
new_elements = _cairo_realloc_ab (pq->elements,
min_size,
sizeof (cairo_xlib_shm_info_t *));
if (unlikely (new_elements == NULL))
return;
 
pq->elements = new_elements;
pq->max_size = min_size;
}
 
static inline cairo_status_t
_pqueue_push (struct pqueue *pq, cairo_xlib_shm_info_t *info)
{
cairo_xlib_shm_info_t **elements;
int i, parent;
 
if (unlikely (pq->size + 1 == pq->max_size)) {
cairo_status_t status;
 
status = _pqueue_grow (pq);
if (unlikely (status))
return status;
}
 
elements = pq->elements;
 
for (i = ++pq->size;
i != PQ_FIRST_ENTRY &&
info->last_request < elements[parent = PQ_PARENT_INDEX (i)]->last_request;
i = parent)
{
elements[i] = elements[parent];
}
 
elements[i] = info;
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline void
_pqueue_pop (struct pqueue *pq)
{
cairo_xlib_shm_info_t **elements = pq->elements;
cairo_xlib_shm_info_t *tail;
int child, i;
 
tail = elements[pq->size--];
if (pq->size == 0) {
elements[PQ_FIRST_ENTRY] = NULL;
_pqueue_shrink (pq, 32);
return;
}
 
for (i = PQ_FIRST_ENTRY;
(child = PQ_LEFT_CHILD_INDEX (i)) <= pq->size;
i = child)
{
if (child != pq->size &&
elements[child+1]->last_request < elements[child]->last_request)
{
child++;
}
 
if (elements[child]->last_request >= tail->last_request)
break;
 
elements[i] = elements[child];
}
elements[i] = tail;
}
 
static cairo_bool_t _x_error_occurred;
 
static int
_check_error_handler (Display *display,
XErrorEvent *event)
{
_x_error_occurred = TRUE;
return False; /* ignored */
}
 
static cairo_bool_t
can_use_shm (Display *dpy, int *has_pixmap)
{
XShmSegmentInfo shm;
int (*old_handler) (Display *display, XErrorEvent *event);
Status success;
int major, minor;
 
if (! XShmQueryExtension (dpy))
return FALSE;
 
XShmQueryVersion (dpy, &major, &minor, has_pixmap);
 
shm.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
if (shm.shmid == -1)
return FALSE;
 
shm.readOnly = FALSE;
shm.shmaddr = shmat (shm.shmid, NULL, 0);
if (shm.shmaddr == (char *) -1) {
shmctl (shm.shmid, IPC_RMID, NULL);
return FALSE;
}
 
assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
_x_error_occurred = FALSE;
 
XLockDisplay (dpy);
XSync (dpy, False);
old_handler = XSetErrorHandler (_check_error_handler);
 
success = XShmAttach (dpy, &shm);
if (success)
XShmDetach (dpy, &shm);
 
XSync (dpy, False);
XSetErrorHandler (old_handler);
XUnlockDisplay (dpy);
 
shmctl (shm.shmid, IPC_RMID, NULL);
shmdt (shm.shmaddr);
 
return success && ! _x_error_occurred;
}
 
static inline Display *
peek_display (cairo_device_t *device)
{
return ((cairo_xlib_display_t *)device)->display;
}
 
static inline unsigned long
peek_processed (cairo_device_t *device)
{
return LastKnownRequestProcessed (peek_display(device));
}
 
static void
_cairo_xlib_display_shm_pool_destroy (cairo_xlib_display_t *display,
cairo_xlib_shm_t *pool)
{
shmdt (pool->shm.shmaddr);
if (display->display) /* may be called after CloseDisplay */
XShmDetach (display->display, &pool->shm);
 
_cairo_mempool_fini (&pool->mem);
 
cairo_list_del (&pool->link);
free (pool);
}
 
static void send_event(cairo_xlib_display_t *display,
cairo_xlib_shm_info_t *info,
unsigned long seqno)
{
XShmCompletionEvent ev;
 
if (! seqno_after (seqno, display->shm->last_event))
return;
 
ev.type = display->shm->event;
ev.send_event = 1; /* XXX or lie? */
ev.serial = NextRequest (display->display);
ev.drawable = display->shm->window;
ev.major_code = display->shm->opcode;
ev.minor_code = X_ShmPutImage;
ev.shmseg = info->pool->shm.shmid;
ev.offset = (char *)info->mem - (char *)info->pool->shm.shmaddr;
 
XSendEvent (display->display, ev.drawable, False, 0, (XEvent *)&ev);
 
display->shm->last_event = ev.serial;
}
 
static void sync (cairo_xlib_display_t *display)
{
cairo_xlib_shm_info_t *info;
struct pqueue *pq = &display->shm->info;
 
XSync (display->display, False);
 
while ((info = PQ_TOP(pq))) {
_cairo_mempool_free (&info->pool->mem, info->mem);
_pqueue_pop (&display->shm->info);
free (info);
}
}
 
static void
_cairo_xlib_shm_info_cleanup (cairo_xlib_display_t *display)
{
cairo_xlib_shm_info_t *info;
Display *dpy = display->display;
struct pqueue *pq = &display->shm->info;
unsigned long processed;
 
if (PQ_TOP(pq) == NULL)
return;
 
XEventsQueued (dpy, QueuedAfterReading);
processed = LastKnownRequestProcessed (dpy);
 
info = PQ_TOP(pq);
do {
if (! seqno_passed (info->last_request, processed)) {
send_event (display, info, display->shm->last_request);
return;
}
 
_cairo_mempool_free (&info->pool->mem, info->mem);
_pqueue_pop (&display->shm->info);
free (info);
} while ((info = PQ_TOP(pq)));
}
 
static cairo_xlib_shm_t *
_cairo_xlib_shm_info_find (cairo_xlib_display_t *display, size_t size,
void **ptr, unsigned long *last_request)
{
cairo_xlib_shm_info_t *info;
struct pqueue *pq = &display->shm->info;
 
if (PQ_TOP(pq) == NULL)
return NULL;
 
info = PQ_TOP(pq);
do {
cairo_xlib_shm_t *pool = info->pool;
 
*last_request = info->last_request;
 
_pqueue_pop (&display->shm->info);
_cairo_mempool_free (&pool->mem, info->mem);
free (info);
 
if (pool->mem.free_bytes >= size) {
void *mem = _cairo_mempool_alloc (&pool->mem, size);
if (mem != NULL) {
*ptr = mem;
return pool;
}
}
} while ((info = PQ_TOP(pq)));
 
return NULL;
}
 
static cairo_xlib_shm_t *
_cairo_xlib_shm_pool_find (cairo_xlib_display_t *display,
size_t size,
void **ptr)
{
cairo_xlib_shm_t *pool;
 
cairo_list_foreach_entry (pool, cairo_xlib_shm_t, &display->shm->pool, link) {
if (pool->mem.free_bytes >= size) {
void *mem = _cairo_mempool_alloc (&pool->mem, size);
if (mem != NULL) {
*ptr = mem;
return pool;
}
}
}
 
return NULL;
}
 
static void
_cairo_xlib_shm_pool_cleanup (cairo_xlib_display_t *display)
{
cairo_xlib_shm_t *pool, *next;
unsigned long processed;
 
processed = LastKnownRequestProcessed (display->display);
 
cairo_list_foreach_entry_safe (pool, next, cairo_xlib_shm_t,
&display->shm->pool, link) {
if (! seqno_passed (pool->attached, processed))
break;
 
if (pool->mem.free_bytes == pool->mem.max_bytes)
_cairo_xlib_display_shm_pool_destroy (display, pool);
}
}
 
static cairo_xlib_shm_t *
_cairo_xlib_shm_pool_create(cairo_xlib_display_t *display,
size_t size, void **ptr)
{
Display *dpy = display->display;
cairo_xlib_shm_t *pool;
size_t bytes, maxbits = 16, minbits = MIN_BITS;
Status success;
 
pool = malloc (sizeof (cairo_xlib_shm_t));
if (pool == NULL)
return NULL;
 
bytes = 1 << maxbits;
while (bytes <= size)
bytes <<= 1, maxbits++;
bytes <<= 3;
 
minbits += (maxbits - 16) / 2;
 
pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
while (pool->shm.shmid == -1 && bytes >= 2*size) {
bytes >>= 1;
pool->shm.shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
}
if (pool->shm.shmid == -1)
goto cleanup;
 
pool->shm.readOnly = FALSE;
pool->shm.shmaddr = shmat (pool->shm.shmid, NULL, 0);
if (pool->shm.shmaddr == (char *) -1) {
shmctl (pool->shm.shmid, IPC_RMID, NULL);
goto cleanup;
}
 
pool->attached = NextRequest (dpy);
success = XShmAttach (dpy, &pool->shm);
#if !IPC_RMID_DEFERRED_RELEASE
XSync (dpy, FALSE);
#endif
shmctl (pool->shm.shmid, IPC_RMID, NULL);
 
if (! success)
goto cleanup_shm;
 
if (_cairo_mempool_init (&pool->mem, pool->shm.shmaddr, bytes,
minbits, maxbits - minbits + 1))
goto cleanup_detach;
 
cairo_list_add (&pool->link, &display->shm->pool);
 
*ptr = _cairo_mempool_alloc (&pool->mem, size);
assert (*ptr != NULL);
return pool;
 
cleanup_detach:
XShmDetach (dpy, &pool->shm);
cleanup_shm:
shmdt (pool->shm.shmaddr);
cleanup:
free (pool);
return NULL;
}
 
static cairo_xlib_shm_info_t *
_cairo_xlib_shm_info_create (cairo_xlib_display_t *display,
size_t size, cairo_bool_t will_sync)
{
cairo_xlib_shm_info_t *info;
cairo_xlib_shm_t *pool;
unsigned long last_request = 0;
void *mem = NULL;
 
_cairo_xlib_shm_info_cleanup (display);
pool = _cairo_xlib_shm_pool_find (display, size, &mem);
_cairo_xlib_shm_pool_cleanup (display);
 
if (pool == NULL && will_sync)
pool = _cairo_xlib_shm_info_find (display, size, &mem, &last_request);
if (pool == NULL)
pool = _cairo_xlib_shm_pool_create (display, size, &mem);
if (pool == NULL)
return NULL;
 
assert (mem != NULL);
 
info = malloc (sizeof (*info));
if (info == NULL) {
_cairo_mempool_free (&pool->mem, mem);
return NULL;
}
 
info->pool = pool;
info->mem = mem;
info->size = size;
info->last_request = last_request;
 
return info;
}
 
static cairo_status_t
_cairo_xlib_shm_surface_flush (void *abstract_surface, unsigned flags)
{
cairo_xlib_shm_surface_t *shm = abstract_surface;
cairo_xlib_display_t *display;
Display *dpy;
cairo_status_t status;
 
if (shm->active == 0)
return CAIRO_STATUS_SUCCESS;
 
if (shm->image.base._finishing)
return CAIRO_STATUS_SUCCESS;
 
if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
shm->active = 0;
return CAIRO_STATUS_SUCCESS;
}
 
status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
if (unlikely (status))
return status;
 
send_event (display, shm->info, shm->active);
 
dpy = display->display;
XEventsQueued (dpy, QueuedAfterReading);
while (! seqno_passed (shm->active, LastKnownRequestProcessed (dpy))) {
LockDisplay(dpy);
_XReadEvents(dpy);
UnlockDisplay(dpy);
}
 
cairo_device_release (&display->base);
shm->active = 0;
 
return CAIRO_STATUS_SUCCESS;
}
 
static inline cairo_bool_t
active (cairo_xlib_shm_surface_t *shm, Display *dpy)
{
return (shm->active &&
! seqno_passed (shm->active, LastKnownRequestProcessed (dpy)));
}
 
static cairo_status_t
_cairo_xlib_shm_surface_finish (void *abstract_surface)
{
cairo_xlib_shm_surface_t *shm = abstract_surface;
cairo_xlib_display_t *display;
cairo_status_t status;
 
if (shm->image.base.damage) {
_cairo_damage_destroy (shm->image.base.damage);
shm->image.base.damage = _cairo_damage_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
}
 
status = _cairo_xlib_display_acquire (shm->image.base.device, &display);
if (unlikely (status))
return status;
 
if (shm->pixmap)
XFreePixmap (display->display, shm->pixmap);
 
if (active (shm, display->display)) {
shm->info->last_request = shm->active;
_pqueue_push (&display->shm->info, shm->info);
if (seqno_before (display->shm->last_request, shm->active))
display->shm->last_request = shm->active;
} else {
_cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
free (shm->info);
 
_cairo_xlib_shm_pool_cleanup (display);
}
 
cairo_list_del (&shm->link);
 
cairo_device_release (&display->base);
return _cairo_image_surface_finish (abstract_surface);
}
 
static const cairo_surface_backend_t cairo_xlib_shm_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_xlib_shm_surface_finish,
 
_cairo_default_context_create,
 
_cairo_image_surface_create_similar,
NULL, /* create similar image */
_cairo_image_surface_map_to_image,
_cairo_image_surface_unmap_image,
 
_cairo_image_surface_source,
_cairo_image_surface_acquire_source_image,
_cairo_image_surface_release_source_image,
_cairo_image_surface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_image_surface_get_extents,
_cairo_image_surface_get_font_options,
 
_cairo_xlib_shm_surface_flush,
NULL,
 
_cairo_image_surface_paint,
_cairo_image_surface_mask,
_cairo_image_surface_stroke,
_cairo_image_surface_fill,
NULL, /* fill-stroke */
_cairo_image_surface_glyphs,
};
 
static cairo_bool_t
has_shm (cairo_xlib_surface_t *surface)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
return display->shm != NULL;
}
 
static int
has_shm_pixmaps (cairo_xlib_surface_t *surface)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *)surface->base.device;
if (!display->shm)
return 0;
 
return display->shm->has_pixmaps;
}
 
static cairo_xlib_shm_surface_t *
_cairo_xlib_shm_surface_create (cairo_xlib_surface_t *other,
pixman_format_code_t format,
int width, int height,
cairo_bool_t will_sync,
int create_pixmap)
{
cairo_xlib_shm_surface_t *shm;
cairo_xlib_display_t *display;
pixman_image_t *image;
int stride, size;
 
stride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, PIXMAN_FORMAT_BPP(format));
size = stride * height;
if (size < MIN_SIZE)
return NULL;
 
shm = malloc (sizeof (*shm));
if (unlikely (shm == NULL))
return (cairo_xlib_shm_surface_t *)_cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_surface_init (&shm->image.base,
&cairo_xlib_shm_surface_backend,
other->base.device,
_cairo_content_from_pixman_format (format));
 
if (_cairo_xlib_display_acquire (other->base.device, &display))
goto cleanup_shm;
 
shm->info = _cairo_xlib_shm_info_create (display, size, will_sync);
if (shm->info == NULL)
goto cleanup_display;
 
image = pixman_image_create_bits (format, width, height,
(uint32_t *) shm->info->mem, stride);
if (image == NULL)
goto cleanup_info;
 
_cairo_image_surface_init (&shm->image, image, format);
 
shm->pixmap = 0;
if (create_pixmap && size >= create_pixmap) {
shm->pixmap = XShmCreatePixmap (display->display,
other->drawable,
shm->info->mem,
&shm->info->pool->shm,
shm->image.width,
shm->image.height,
shm->image.depth);
}
shm->active = shm->info->last_request;
shm->idle = -5;
 
assert (shm->active == 0 || will_sync);
 
cairo_list_add (&shm->link, &display->shm->surfaces);
 
cairo_device_release (&display->base);
 
return shm;
 
cleanup_info:
_cairo_mempool_free (&shm->info->pool->mem, shm->info->mem);
free(shm->info);
cleanup_display:
cairo_device_release (&display->base);
cleanup_shm:
free (shm);
return NULL;
}
 
static void
_cairo_xlib_surface_update_shm (cairo_xlib_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
cairo_xlib_display_t *display;
cairo_damage_t *damage;
GC gc;
 
damage = _cairo_damage_reduce (surface->base.damage);
surface->base.damage = _cairo_damage_create();
 
if (_cairo_xlib_display_acquire (surface->base.device, &display))
goto cleanup_damage;
 
if (_cairo_xlib_surface_get_gc (display, surface, &gc))
goto cleanup_display;
 
if (! surface->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = IncludeInferiors;
XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
}
 
if (damage->region) {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
XRectangle *rects = stack_rects;
cairo_rectangle_int_t r;
int n_rects, i;
 
n_rects = cairo_region_num_rectangles (damage->region);
if (n_rects == 0) {
} else if (n_rects == 1) {
cairo_region_get_rectangle (damage->region, 0, &r);
XCopyArea (display->display,
surface->drawable, shm->pixmap, gc,
r.x, r.y,
r.width, r.height,
r.x, r.y);
} else {
if (n_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
if (unlikely (rects == NULL)) {
rects = stack_rects;
n_rects = ARRAY_LENGTH (stack_rects);
}
}
for (i = 0; i < n_rects; i++) {
cairo_region_get_rectangle (damage->region, i, &r);
 
rects[i].x = r.x;
rects[i].y = r.y;
rects[i].width = r.width;
rects[i].height = r.height;
}
XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
 
XCopyArea (display->display,
surface->drawable, shm->pixmap, gc,
0, 0,
shm->image.width, shm->image.height,
0, 0);
 
if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
XSetClipMask (display->display, gc, None);
}
} else {
XCopyArea (display->display,
surface->drawable, shm->pixmap, gc,
0, 0,
shm->image.width, shm->image.height,
0, 0);
}
 
if (! surface->owns_pixmap) {
XGCValues gcv;
 
gcv.subwindow_mode = ClipByChildren;
XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
}
 
sync (display);
shm->active = 0;
shm->idle--;
 
_cairo_xlib_surface_put_gc (display, surface, gc);
cleanup_display:
cairo_device_release (&display->base);
cleanup_damage:
_cairo_damage_destroy (damage);
}
 
static void
_cairo_xlib_surface_clear_shm (cairo_xlib_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface->shm;
 
assert (shm->active == 0);
 
_cairo_damage_destroy (surface->base.damage);
surface->base.damage = _cairo_damage_create();
 
memset (shm->image.data, 0, shm->image.stride * shm->image.height);
shm->image.base.is_clear = TRUE;
}
 
static void inc_idle (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
shm->idle++;
}
 
static void dec_idle (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *)surface;
shm->idle--;
}
 
cairo_surface_t *
_cairo_xlib_surface_get_shm (cairo_xlib_surface_t *surface,
cairo_bool_t overwrite)
{
if (surface->fallback) {
assert (surface->base.damage);
assert (surface->shm);
assert (surface->shm->damage);
goto done;
}
 
if (surface->shm == NULL) {
pixman_format_code_t pixman_format;
cairo_bool_t will_sync;
 
if (! has_shm_pixmaps (surface))
return NULL;
 
if ((surface->width | surface->height) < 32)
return NULL;
 
pixman_format = _pixman_format_for_xlib_surface (surface);
if (pixman_format == 0)
return NULL;
 
will_sync = !surface->base.is_clear && !overwrite;
 
surface->shm =
&_cairo_xlib_shm_surface_create (surface, pixman_format,
surface->width, surface->height,
will_sync, 1)->image.base;
if (surface->shm == NULL)
return NULL;
 
assert (surface->base.damage == NULL);
if (surface->base.serial || !surface->owns_pixmap) {
cairo_rectangle_int_t rect;
 
rect.x = rect.y = 0;
rect.width = surface->width;
rect.height = surface->height;
 
surface->base.damage =
_cairo_damage_add_rectangle (NULL, &rect);
} else
surface->base.damage = _cairo_damage_create ();
 
surface->shm->damage = _cairo_damage_create ();
}
 
if (overwrite) {
_cairo_damage_destroy (surface->base.damage);
surface->base.damage = _cairo_damage_create ();
}
 
if (!surface->base.is_clear && surface->base.damage->dirty)
_cairo_xlib_surface_update_shm (surface);
 
_cairo_xlib_shm_surface_flush (surface->shm, 1);
 
if (surface->base.is_clear && surface->base.damage->dirty)
_cairo_xlib_surface_clear_shm (surface);
 
done:
dec_idle(surface->shm);
return surface->shm;
}
 
cairo_int_status_t
_cairo_xlib_surface_put_shm (cairo_xlib_surface_t *surface)
{
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
 
if (!surface->fallback) {
if (surface->shm)
inc_idle (surface->shm);
return CAIRO_INT_STATUS_SUCCESS;
}
 
if (surface->shm->damage->dirty) {
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface->shm;
cairo_xlib_display_t *display;
cairo_damage_t *damage;
GC gc;
 
status = _cairo_xlib_display_acquire (surface->base.device, &display);
if (unlikely (status))
return status;
 
damage = _cairo_damage_reduce (shm->image.base.damage);
shm->image.base.damage = _cairo_damage_create ();
 
TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__,
damage->region ? cairo_region_num_rectangles (damage->region) : 0));
if (damage->status == CAIRO_STATUS_SUCCESS && damage->region) {
XRectangle stack_rects[CAIRO_STACK_ARRAY_LENGTH (sizeof (XRectangle))];
XRectangle *rects = stack_rects;
cairo_rectangle_int_t r;
int n_rects, i;
 
n_rects = cairo_region_num_rectangles (damage->region);
if (n_rects == 0)
goto out;
 
status = _cairo_xlib_surface_get_gc (display, surface, &gc);
if (unlikely (status))
goto out;
 
if (n_rects == 1) {
cairo_region_get_rectangle (damage->region, 0, &r);
_cairo_xlib_shm_surface_mark_active (surface->shm);
XCopyArea (display->display,
shm->pixmap, surface->drawable, gc,
r.x, r.y,
r.width, r.height,
r.x, r.y);
} else {
if (n_rects > ARRAY_LENGTH (stack_rects)) {
rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle));
if (unlikely (rects == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
_cairo_xlib_surface_put_gc (display, surface, gc);
goto out;
}
}
for (i = 0; i < n_rects; i++) {
cairo_region_get_rectangle (damage->region, i, &r);
 
rects[i].x = r.x;
rects[i].y = r.y;
rects[i].width = r.width;
rects[i].height = r.height;
}
XSetClipRectangles (display->display, gc, 0, 0, rects, i, YXBanded);
 
_cairo_xlib_shm_surface_mark_active (surface->shm);
XCopyArea (display->display,
shm->pixmap, surface->drawable, gc,
0, 0,
shm->image.width, shm->image.height,
0, 0);
 
if (damage->status == CAIRO_STATUS_SUCCESS && damage->region)
XSetClipMask (display->display, gc, None);
}
 
_cairo_xlib_surface_put_gc (display, surface, gc);
}
 
out:
_cairo_damage_destroy (damage);
cairo_device_release (&display->base);
}
 
return status;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_shm (cairo_xlib_surface_t *other,
pixman_format_code_t format,
int width, int height)
{
cairo_surface_t *surface;
 
surface = NULL;
if (has_shm (other))
surface = &_cairo_xlib_shm_surface_create (other, format, width, height,
FALSE, has_shm_pixmaps (other))->image.base;
 
return surface;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_shm__image (cairo_xlib_surface_t *surface,
pixman_format_code_t format,
int width, int height)
{
if (! has_shm(surface))
return NULL;
 
return &_cairo_xlib_shm_surface_create (surface, format, width, height,
FALSE, 0)->image.base;
}
 
cairo_surface_t *
_cairo_xlib_surface_create_similar_shm (void *other,
cairo_format_t format,
int width, int height)
{
cairo_surface_t *surface;
 
surface = _cairo_xlib_surface_create_shm (other,
_cairo_format_to_pixman_format_code (format),
width, height);
if (surface) {
if (! surface->is_clear) {
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
assert (shm->active == 0);
memset (shm->image.data, 0, shm->image.stride * shm->image.height);
shm->image.base.is_clear = TRUE;
}
} else
surface = cairo_image_surface_create (format, width, height);
 
return surface;
}
 
void
_cairo_xlib_shm_surface_mark_active (cairo_surface_t *_shm)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) _shm;
cairo_xlib_display_t *display = (cairo_xlib_display_t *) _shm->device;
 
shm->active = NextRequest (display->display);
}
 
void
_cairo_xlib_shm_surface_get_ximage (cairo_surface_t *surface,
XImage *ximage)
{
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
int native_byte_order = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
cairo_format_masks_t image_masks;
int ret;
 
ret = _pixman_format_to_masks (shm->image.pixman_format, &image_masks);
assert (ret);
 
ximage->width = shm->image.width;
ximage->height = shm->image.height;
ximage->format = ZPixmap;
ximage->data = (char *) shm->image.data;
ximage->obdata = (char *)&shm->info->pool->shm;
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 = shm->image.depth;
ximage->bytes_per_line = shm->image.stride;
ximage->bits_per_pixel = image_masks.bpp;
ximage->red_mask = image_masks.red_mask;
ximage->green_mask = image_masks.green_mask;
ximage->blue_mask = image_masks.blue_mask;
ximage->xoffset = 0;
 
ret = XInitImage (ximage);
assert (ret != 0);
}
 
void *
_cairo_xlib_shm_surface_get_obdata (cairo_surface_t *surface)
{
cairo_xlib_display_t *display = (cairo_xlib_display_t *) surface->device;
cairo_xlib_shm_surface_t *shm = (cairo_xlib_shm_surface_t *) surface;
 
display->shm->last_event = shm->active = NextRequest (display->display);
return &shm->info->pool->shm;
}
 
Pixmap
_cairo_xlib_shm_surface_get_pixmap (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm;
 
shm = (cairo_xlib_shm_surface_t *) surface;
return shm->pixmap;
}
 
XRenderPictFormat *
_cairo_xlib_shm_surface_get_xrender_format (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm;
 
shm = (cairo_xlib_shm_surface_t *) surface;
if (shm->image.format != CAIRO_FORMAT_INVALID)
return _cairo_xlib_display_get_xrender_format ((cairo_xlib_display_t *)surface->device,
shm->image.format);
 
return _cairo_xlib_display_get_xrender_format_for_pixman((cairo_xlib_display_t *)surface->device,
shm->image.pixman_format);
}
 
cairo_bool_t
_cairo_xlib_shm_surface_is_active (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm;
 
shm = (cairo_xlib_shm_surface_t *) surface;
if (shm->active == 0)
return FALSE;
 
if (seqno_passed (shm->active, peek_processed (shm->image.base.device))) {
shm->active = 0;
return FALSE;
}
 
return TRUE;
}
 
cairo_bool_t
_cairo_xlib_shm_surface_is_idle (cairo_surface_t *surface)
{
cairo_xlib_shm_surface_t *shm;
 
shm = (cairo_xlib_shm_surface_t *) surface;
return shm->idle > 0;
}
 
#define XORG_VERSION_ENCODE(major,minor,patch,snap) \
(((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap)
 
static cairo_bool_t
has_broken_send_shm_event (cairo_xlib_display_t *display,
cairo_xlib_shm_display_t *shm)
{
Display *dpy = display->display;
int (*old_handler) (Display *display, XErrorEvent *event);
XShmCompletionEvent ev;
XShmSegmentInfo info;
 
info.shmid = shmget (IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
if (info.shmid == -1)
return TRUE;
 
info.readOnly = FALSE;
info.shmaddr = shmat (info.shmid, NULL, 0);
if (info.shmaddr == (char *) -1) {
shmctl (info.shmid, IPC_RMID, NULL);
return TRUE;
}
 
ev.type = shm->event;
ev.send_event = 1;
ev.serial = 1;
ev.drawable = shm->window;
ev.major_code = shm->opcode;
ev.minor_code = X_ShmPutImage;
 
ev.shmseg = info.shmid;
ev.offset = 0;
 
assert (CAIRO_MUTEX_IS_LOCKED (_cairo_xlib_display_mutex));
_x_error_occurred = FALSE;
 
XLockDisplay (dpy);
XSync (dpy, False);
old_handler = XSetErrorHandler (_check_error_handler);
 
XShmAttach (dpy, &info);
XSendEvent (dpy, ev.drawable, False, 0, (XEvent *)&ev);
XShmDetach (dpy, &info);
 
XSync (dpy, False);
XSetErrorHandler (old_handler);
XUnlockDisplay (dpy);
 
shmctl (info.shmid, IPC_RMID, NULL);
shmdt (info.shmaddr);
 
return _x_error_occurred;
}
 
static cairo_bool_t
xorg_has_buggy_send_shm_completion_event(cairo_xlib_display_t *display,
cairo_xlib_shm_display_t *shm)
{
Display *dpy = display->display;
 
/* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent,
* the Xserver may crash if it does not take care when processing
* the event type. For instance versions of Xorg prior to 1.11.1
* exhibited this bug, and was fixed by:
*
* commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39
* Author: Sam Spilsbury <sam.spilsbury@canonical.com>
* Date: Wed Sep 14 09:58:34 2011 +0800
*
* Remove the SendEvent bit (0x80) before doing range checks on event type.
*/
if (_cairo_xlib_vendor_is_xorg (dpy) &&
VendorRelease (dpy) < XORG_VERSION_ENCODE(1,11,0,1))
return TRUE;
 
/* For everyone else check that no error is generated */
return has_broken_send_shm_event (display, shm);
}
 
void
_cairo_xlib_display_init_shm (cairo_xlib_display_t *display)
{
cairo_xlib_shm_display_t *shm;
XSetWindowAttributes attr;
XExtCodes *codes;
int has_pixmap, scr;
 
display->shm = NULL;
 
if (!can_use_shm (display->display, &has_pixmap))
return;
 
shm = malloc (sizeof (*shm));
if (unlikely (shm == NULL))
return;
 
codes = XInitExtension (display->display, SHMNAME);
if (codes == NULL) {
free (shm);
return;
}
 
shm->opcode = codes ->major_opcode;
shm->event = codes->first_event;
 
if (unlikely (_pqueue_init (&shm->info))) {
free (shm);
return;
}
 
scr = DefaultScreen (display->display);
attr.override_redirect = 1;
shm->window = XCreateWindow (display->display,
DefaultRootWindow (display->display), -1, -1,
1, 1, 0,
DefaultDepth (display->display, scr),
InputOutput,
DefaultVisual (display->display, scr),
CWOverrideRedirect, &attr);
shm->last_event = 0;
shm->last_request = 0;
 
if (xorg_has_buggy_send_shm_completion_event(display, shm))
has_pixmap = 0;
 
shm->has_pixmaps = has_pixmap ? MIN_PIXMAP_SIZE : 0;
cairo_list_init (&shm->pool);
 
cairo_list_init (&shm->surfaces);
 
display->shm = shm;
}
 
void
_cairo_xlib_display_fini_shm (cairo_xlib_display_t *display)
{
cairo_xlib_shm_display_t *shm = display->shm;
 
if (shm == NULL)
return;
 
while (!cairo_list_is_empty (&shm->surfaces))
cairo_surface_finish (&cairo_list_first_entry (&shm->surfaces,
cairo_xlib_shm_surface_t,
link)->image.base);
 
_pqueue_fini (&shm->info);
 
while (!cairo_list_is_empty (&shm->pool)) {
cairo_xlib_shm_t *pool;
 
pool = cairo_list_first_entry (&shm->pool, cairo_xlib_shm_t, link);
_cairo_xlib_display_shm_pool_destroy (display, pool);
}
 
if (display->display)
XDestroyWindow (display->display, shm->window);
 
free (shm);
display->shm = NULL;
}
#endif
#endif
/programs/develop/libraries/cairo/src/cairo-xlib-surface.c
0,0 → 1,2383
/* -*- 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"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
#include "cairo-xlib-surface-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-clip-private.h"
#include "cairo-damage-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-pattern-private.h"
#include "cairo-pixman-private.h"
#include "cairo-region-private.h"
#include "cairo-scaled-font-private.h"
#include "cairo-surface-snapshot-private.h"
#include "cairo-surface-subsurface-private.h"
 
#include <X11/Xutil.h> /* for XDestroyImage */
 
#include <X11/extensions/XShm.h>
#include <sys/ipc.h>
#include <sys/shm.h>
 
#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
 
/**
* SECTION:cairo-xlib
* @Title: XLib Surfaces
* @Short_Description: X Window System rendering using XLib
* @See_Also: #cairo_surface_t
*
* The XLib surface is used to render cairo graphics to X Window System
* windows and pixmaps using the XLib library.
*
* Note that the XLib surface automatically takes advantage of X render extension
* if it is available.
**/
 
/**
* CAIRO_HAS_XLIB_SURFACE:
*
* Defined if the Xlib surface backend is available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.0
**/
 
/**
* SECTION:cairo-xlib-xrender
* @Title: XLib-XRender Backend
* @Short_Description: X Window System rendering using XLib and the X Render extension
* @See_Also: #cairo_surface_t
*
* The XLib surface is used to render cairo graphics to X Window System
* windows and pixmaps using the XLib and Xrender libraries.
*
* Note that the XLib surface automatically takes advantage of X Render extension
* if it is available.
**/
 
/**
* CAIRO_HAS_XLIB_XRENDER_SURFACE:
*
* Defined if the XLib/XRender surface functions are available.
* This macro can be used to conditionally compile backend-specific code.
*
* Since: 1.6
**/
 
/* 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);
 
/*
* 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 },
} };
 
static Visual *
_visual_for_xrender_format(Screen *screen,
XRenderPictFormat *xrender_format)
{
int d, v;
 
/* XXX Consider searching through the list of known cairo_visual_t for
* the reverse mapping.
*/
 
for (d = 0; d < screen->ndepths; d++) {
Depth *d_info = &screen->depths[d];
 
if (d_info->depth != xrender_format->depth)
continue;
 
for (v = 0; v < d_info->nvisuals; v++) {
Visual *visual = &d_info->visuals[v];
 
switch (visual->class) {
case TrueColor:
if (xrender_format->type != PictTypeDirect)
continue;
break;
 
case DirectColor:
/* Prefer TrueColor to DirectColor.
* (XRenderFindVisualFormat considers both TrueColor and DirectColor
* Visuals to match the same PictFormat.)
*/
continue;
 
case StaticGray:
case GrayScale:
case StaticColor:
case PseudoColor:
if (xrender_format->type != PictTypeIndexed)
continue;
break;
}
 
if (xrender_format ==
XRenderFindVisualFormat (DisplayOfScreen(screen), visual))
return visual;
}
}
 
return NULL;
}
 
static cairo_content_t
_xrender_format_to_content (XRenderPictFormat *xrender_format)
{
cairo_content_t content;
 
/* 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;
 
content = 0;
if (xrender_format->direct.alphaMask)
content |= CAIRO_CONTENT_ALPHA;
if (xrender_format->direct.redMask |
xrender_format->direct.greenMask |
xrender_format->direct.blueMask)
content |= CAIRO_CONTENT_COLOR;
 
return content;
}
 
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 (width == 0 || height == 0)
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 = NULL;
if (src->xrender_format &&
_xrender_format_to_content (src->xrender_format) == content)
{
xrender_format = src->xrender_format;
}
if (xrender_format == NULL) {
xrender_format =
_cairo_xlib_display_get_xrender_format (display,
_cairo_format_from_content (content));
}
if (xrender_format) {
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, height, xrender_format->depth);
 
if (xrender_format == src->xrender_format)
visual = src->visual;
else
visual = _visual_for_xrender_format(src->screen->screen,
xrender_format);
 
surface = (cairo_xlib_surface_t *)
_cairo_xlib_surface_create_internal (src->screen, pix, visual,
xrender_format,
width, height,
xrender_format->depth);
}
else
{
Screen *screen = src->screen->screen;
int depth;
 
/* No compatible 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);
}
 
if (likely (surface->base.status == CAIRO_STATUS_SUCCESS))
surface->owns_pixmap = TRUE;
else
XFreePixmap (display->display, pix);
 
cairo_device_release (&display->base);
 
return &surface->base;
}
 
static void
_cairo_xlib_surface_discard_shm (cairo_xlib_surface_t *surface)
{
if (surface->shm == NULL)
return;
 
/* Force the flush for an external surface */
if (!surface->owns_pixmap)
cairo_surface_flush (surface->shm);
 
cairo_surface_finish (surface->shm);
cairo_surface_destroy (surface->shm);
surface->shm = NULL;
 
_cairo_damage_destroy (surface->base.damage);
surface->base.damage = NULL;
 
surface->fallback = 0;
}
 
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;
 
cairo_list_del (&surface->link);
 
status = _cairo_xlib_display_acquire (surface->base.device, &display);
if (unlikely (status))
return status;
 
X_DEBUG ((display->display, "finish (drawable=%x)", (unsigned int) surface->drawable));
 
if (surface->embedded_source.picture)
XRenderFreePicture (display->display, surface->embedded_source.picture);
if (surface->picture)
XRenderFreePicture (display->display, surface->picture);
 
_cairo_xlib_surface_discard_shm (surface);
 
if (surface->owns_pixmap)
XFreePixmap (display->display, surface->drawable);
 
cairo_device_release (&display->base);
 
return status;
}
 
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 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 = _cairo_is_little_endian () ? 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 int bits_per_pixel(cairo_xlib_surface_t *surface)
{
if (surface->depth > 16)
return 32;
else if (surface->depth > 8)
return 16;
else if (surface->depth > 1)
return 8;
else
return 1;
}
 
pixman_format_code_t
_pixman_format_for_xlib_surface (cairo_xlib_surface_t *surface)
{
cairo_format_masks_t masks;
pixman_format_code_t format;
 
masks.bpp = bits_per_pixel (surface);
masks.alpha_mask = surface->a_mask;
masks.red_mask = surface->r_mask;
masks.green_mask = surface->g_mask;
masks.blue_mask = surface->b_mask;
if (! _pixman_format_from_masks (&masks, &format))
return 0;
 
return format;
}
 
static cairo_surface_t *
_get_image_surface (cairo_xlib_surface_t *surface,
const cairo_rectangle_int_t *extents,
int try_shm)
{
cairo_int_status_t status;
cairo_image_surface_t *image = NULL;
XImage *ximage;
pixman_format_code_t pixman_format;
cairo_xlib_display_t *display;
 
assert (extents->x >= 0);
assert (extents->y >= 0);
assert (extents->x + extents->width <= surface->width);
assert (extents->y + extents->height <= surface->height);
 
if (surface->base.is_clear ||
(surface->base.serial == 0 && surface->owns_pixmap))
{
pixman_format = _pixman_format_for_xlib_surface (surface);
if (pixman_format)
{
return _cairo_image_surface_create_with_pixman_format (NULL,
pixman_format,
extents->width,
extents->height,
0);
}
}
 
if (surface->shm) {
cairo_image_surface_t *src = (cairo_image_surface_t *) surface->shm;
cairo_surface_t *dst;
cairo_surface_pattern_t pattern;
 
dst = cairo_image_surface_create (src->format,
extents->width, extents->height);
if (unlikely (dst->status))
return dst;
 
_cairo_pattern_init_for_surface (&pattern, &src->base);
cairo_matrix_init_translate (&pattern.base.matrix,
extents->x, extents->y);
status = _cairo_surface_paint (dst, CAIRO_OPERATOR_SOURCE, &pattern.base, NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status)) {
cairo_surface_destroy (dst);
dst = _cairo_surface_create_in_error (status);
}
 
return dst;
}
 
status = _cairo_xlib_display_acquire (surface->base.device, &display);
if (status)
return _cairo_surface_create_in_error (status);
 
pixman_format = _pixman_format_for_xlib_surface (surface);
if (try_shm && pixman_format) {
image = (cairo_image_surface_t *)
_cairo_xlib_surface_create_shm__image (surface, pixman_format,
extents->width, extents->height);
if (image && image->base.status == CAIRO_STATUS_SUCCESS) {
cairo_xlib_error_func_t old_handler;
XImage shm_image;
Bool success;
 
_cairo_xlib_shm_surface_get_ximage (&image->base, &shm_image);
 
old_handler = XSetErrorHandler (_noop_error_handler);
success = XShmGetImage (display->display,
surface->drawable,
&shm_image,
extents->x, extents->y,
AllPlanes);
XSetErrorHandler (old_handler);
 
if (success) {
cairo_device_release (&display->base);
return &image->base;
}
 
cairo_surface_destroy (&image->base);
}
}
 
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) {
XGCValues gcv;
 
gcv.subwindow_mode = IncludeInferiors;
XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
 
XCopyArea (display->display, surface->drawable, pixmap, gc,
extents->x, extents->y,
extents->width, extents->height,
0, 0);
 
gcv.subwindow_mode = ClipByChildren;
XChangeGC (display->display, gc, GCSubwindowMode, &gcv);
 
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);
 
/* 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 (pixman_format &&
ximage->bitmap_unit == 32 && ximage->bitmap_pad == 32 &&
(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)) {
cairo_surface_destroy (&image->base);
return _cairo_surface_create_in_error (status);
}
 
return &image->base;
}
 
void
_cairo_xlib_surface_set_precision (cairo_xlib_surface_t *surface,
cairo_antialias_t antialias)
{
cairo_xlib_display_t *display = surface->display;
int precision;
 
if (display->force_precision != -1)
precision = display->force_precision;
else switch (antialias) {
default:
case CAIRO_ANTIALIAS_DEFAULT:
case CAIRO_ANTIALIAS_GRAY:
case CAIRO_ANTIALIAS_NONE:
case CAIRO_ANTIALIAS_FAST:
case CAIRO_ANTIALIAS_GOOD:
precision = PolyModeImprecise;
break;
case CAIRO_ANTIALIAS_BEST:
case CAIRO_ANTIALIAS_SUBPIXEL:
precision = PolyModePrecise;
break;
}
 
if (surface->precision != precision) {
XRenderPictureAttributes pa;
 
pa.poly_mode = precision;
XRenderChangePicture (display->display, surface->picture,
CPPolyMode, &pa);
 
surface->precision = precision;
}
}
 
void
_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface)
{
cairo_xlib_display_t *display = surface->display;
XRenderPictureAttributes pa;
int mask = 0;
 
if (surface->picture)
return;
 
if (display->force_precision != -1)
pa.poly_mode = display->force_precision;
else
pa.poly_mode = PolyModeImprecise;
if (pa.poly_mode)
mask |= CPPolyMode;
 
surface->precision = pa.poly_mode;
surface->picture = XRenderCreatePicture (display->display,
surface->drawable,
surface->xrender_format,
mask, &pa);
}
 
cairo_status_t
_cairo_xlib_surface_draw_image (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 = _cairo_is_little_endian () ? LSBFirst : MSBFirst;
cairo_surface_t *shm_image = NULL;
pixman_image_t *pixman_image = NULL;
cairo_status_t status;
cairo_bool_t own_data = FALSE;
cairo_bool_t is_rgb_image;
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;
ximage.obdata = NULL;
 
status = _cairo_xlib_display_acquire (surface->base.device, &display);
if (unlikely (status))
return status;
 
is_rgb_image = _pixman_format_to_masks (image->pixman_format, &image_masks);
 
if (is_rgb_image &&
(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;
if (image->base.device != surface->base.device) {
/* If PutImage will break the image up into chunks, prefer to
* send it all in one pass with ShmPutImage. For larger images,
* it is further advantageous to reduce the number of copies,
* albeit at the expense of more SHM bookkeeping.
*/
int max_request_size = XExtendedMaxRequestSize (display->display);
if (max_request_size == 0)
max_request_size = XMaxRequestSize (display->display);
if (max_request_size > 8192)
max_request_size = 8192;
if (width * height * 4 > max_request_size) {
shm_image = _cairo_xlib_surface_create_shm__image (surface,
image->pixman_format,
width, height);
if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image, NULL, clone->pixman_image,
src_x, src_y,
0, 0,
0, 0,
width, height);
ximage.obdata = _cairo_xlib_shm_surface_get_obdata (shm_image);
ximage.data = (char *)clone->data;
ximage.bytes_per_line = clone->stride;
ximage.width = width;
ximage.height = height;
src_x = src_y = 0;
}
}
} else
ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&image->base);
 
ret = XInitImage (&ximage);
assert (ret != 0);
}
else if (surface->visual == NULL || surface->visual->class == TrueColor)
{
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 = bits_per_pixel (surface);
ret = _pixman_format_from_masks (&image_masks, &intermediate_format);
assert (ret);
 
shm_image = _cairo_xlib_surface_create_shm__image (surface,
intermediate_format,
width, height);
if (shm_image && shm_image->status == CAIRO_STATUS_SUCCESS) {
cairo_image_surface_t *clone = (cairo_image_surface_t *) shm_image;
 
pixman_image_composite32 (PIXMAN_OP_SRC,
image->pixman_image,
NULL,
clone->pixman_image,
src_x, src_y,
0, 0,
0, 0,
width, height);
 
ximage.data = (char *) clone->data;
ximage.obdata = _cairo_xlib_shm_surface_get_obdata (&clone->base);
ximage.bytes_per_line = clone->stride;
} else {
pixman_image = pixman_image_create_bits (intermediate_format,
width, 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,
src_x, src_y,
0, 0,
0, 0,
width, height);
 
ximage.data = (char *) pixman_image_get_data (pixman_image);
ximage.bytes_per_line = pixman_image_get_stride (pixman_image);
}
 
ximage.width = width;
ximage.height = height;
ximage.bits_per_pixel = image_masks.bpp;
 
ret = XInitImage (&ximage);
assert (ret != 0);
 
src_x = src_y = 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;
 
ximage.bits_per_pixel = bits_per_pixel(surface);
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)) {
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;
 
if (ximage.obdata)
XShmPutImage (display->display, surface->drawable, gc, &ximage,
src_x, src_y, dst_x, dst_y, width, height, True);
else
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 (shm_image)
cairo_surface_destroy (shm_image);
if (pixman_image)
pixman_image_unref (pixman_image);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
_cairo_xlib_surface_source(void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_xlib_surface_t *surface = abstract_surface;
 
if (extents) {
extents->x = extents->y = 0;
extents->width = surface->width;
extents->height = surface->height;
}
 
return &surface->base;
}
 
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_rectangle_int_t extents;
 
*image_extra = NULL;
*image_out = (cairo_image_surface_t *)
_cairo_xlib_surface_get_shm (abstract_surface, FALSE);
if (*image_out)
return (*image_out)->base.status;
 
extents.x = extents.y = 0;
extents.width = surface->width;
extents.height = surface->height;
 
*image_out = (cairo_image_surface_t*)
_get_image_surface (surface, &extents, TRUE);
return (*image_out)->base.status;
}
 
static cairo_surface_t *
_cairo_xlib_surface_snapshot (void *abstract_surface)
{
cairo_xlib_surface_t *surface = abstract_surface;
cairo_rectangle_int_t extents;
 
extents.x = extents.y = 0;
extents.width = surface->width;
extents.height = surface->height;
 
return _get_image_surface (surface, &extents, FALSE);
}
 
static void
_cairo_xlib_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_xlib_surface_t *surface = abstract_surface;
 
if (&image->base == surface->shm)
return;
 
cairo_surface_destroy (&image->base);
}
 
static cairo_image_surface_t *
_cairo_xlib_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_xlib_surface_t *surface = abstract_surface;
cairo_surface_t *image;
 
image = _cairo_xlib_surface_get_shm (abstract_surface, FALSE);
if (image) {
assert (surface->base.damage);
surface->fallback++;
return _cairo_image_surface_map_to_image (image, extents);
}
 
image = _get_image_surface (abstract_surface, extents, TRUE);
cairo_surface_set_device_offset (image, -extents->x, -extents->y);
 
return (cairo_image_surface_t *) image;
}
 
static cairo_int_status_t
_cairo_xlib_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_xlib_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
if (surface->shm) {
cairo_rectangle_int_t r;
 
assert (surface->fallback);
assert (surface->base.damage);
 
r.x = image->base.device_transform_inverse.x0;
r.y = image->base.device_transform_inverse.y0;
r.width = image->width;
r.height = image->height;
 
TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n",
__FUNCTION__, r.x, r.y, r.width, r.height));
surface->shm->damage =
_cairo_damage_add_rectangle (surface->shm->damage, &r);
 
return _cairo_image_surface_unmap_image (surface->shm, image);
}
 
status = _cairo_xlib_surface_draw_image (abstract_surface, image,
0, 0,
image->width, image->height,
image->base.device_transform_inverse.x0,
image->base.device_transform_inverse.y0);
 
cairo_surface_finish (&image->base);
cairo_surface_destroy (&image->base);
 
return status;
}
 
static cairo_status_t
_cairo_xlib_surface_flush (void *abstract_surface,
unsigned flags)
{
cairo_xlib_surface_t *surface = abstract_surface;
cairo_int_status_t status;
 
if (flags)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_xlib_surface_put_shm (surface);
if (unlikely (status))
return status;
 
surface->fallback >>= 1;
if (surface->shm && _cairo_xlib_shm_surface_is_idle (surface->shm))
_cairo_xlib_surface_discard_shm (surface);
 
return CAIRO_STATUS_SUCCESS;
}
 
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 inline cairo_int_status_t
get_compositor (cairo_xlib_surface_t **surface,
const cairo_compositor_t **compositor)
{
cairo_xlib_surface_t *s = *surface;
cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;;
 
if (s->fallback) {
assert (s->base.damage != NULL);
assert (s->shm != NULL);
assert (s->shm->damage != NULL);
if (! _cairo_xlib_shm_surface_is_active (s->shm)) {
*surface = (cairo_xlib_surface_t *) s->shm;
*compositor = ((cairo_image_surface_t *) s->shm)->compositor;
s->fallback++;
} else {
status = _cairo_xlib_surface_put_shm (s);
s->fallback = 0;
*compositor = s->compositor;
}
} else
*compositor = s->compositor;
 
return status;
}
 
static cairo_int_status_t
_cairo_xlib_surface_paint (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_xlib_surface_t *surface = _surface;
const cairo_compositor_t *compositor;
cairo_int_status_t status;
 
status = get_compositor (&surface, &compositor);
if (unlikely (status))
return status;
 
return _cairo_compositor_paint (compositor, &surface->base,
op, source,
clip);
}
 
static cairo_int_status_t
_cairo_xlib_surface_mask (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_xlib_surface_t *surface = _surface;
const cairo_compositor_t *compositor;
cairo_int_status_t status;
 
status = get_compositor (&surface, &compositor);
if (unlikely (status))
return status;
 
return _cairo_compositor_mask (compositor, &surface->base,
op, source, mask,
clip);
}
 
static cairo_int_status_t
_cairo_xlib_surface_stroke (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xlib_surface_t *surface = _surface;
const cairo_compositor_t *compositor;
cairo_int_status_t status;
 
status = get_compositor (&surface, &compositor);
if (unlikely (status))
return status;
 
return _cairo_compositor_stroke (compositor, &surface->base,
op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_xlib_surface_fill (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xlib_surface_t *surface = _surface;
const cairo_compositor_t *compositor;
cairo_int_status_t status;
 
status = get_compositor (&surface, &compositor);
if (unlikely (status))
return status;
 
return _cairo_compositor_fill (compositor, &surface->base,
op, source,
path, fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_cairo_xlib_surface_glyphs (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_xlib_surface_t *surface = _surface;
const cairo_compositor_t *compositor;
cairo_int_status_t status;
 
status = get_compositor (&surface, &compositor);
if (unlikely (status))
return status;
 
return _cairo_compositor_glyphs (compositor, &surface->base,
op, source,
glyphs, num_glyphs, scaled_font,
clip);
}
 
static const cairo_surface_backend_t cairo_xlib_surface_backend = {
CAIRO_SURFACE_TYPE_XLIB,
_cairo_xlib_surface_finish,
 
_cairo_default_context_create,
 
_cairo_xlib_surface_create_similar,
_cairo_xlib_surface_create_similar_shm,
_cairo_xlib_surface_map_to_image,
_cairo_xlib_surface_unmap_image,
 
_cairo_xlib_surface_source,
_cairo_xlib_surface_acquire_source_image,
_cairo_xlib_surface_release_source_image,
_cairo_xlib_surface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_xlib_surface_get_extents,
_cairo_xlib_surface_get_font_options,
 
_cairo_xlib_surface_flush,
NULL, /* mark_dirty_rectangle */
 
_cairo_xlib_surface_paint,
_cairo_xlib_surface_mask,
_cairo_xlib_surface_stroke,
_cairo_xlib_surface_fill,
NULL, /* fill-stroke */
_cairo_xlib_surface_glyphs,
};
 
/**
* _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;
}
 
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));
}
 
surface->display = display;
if (CAIRO_RENDER_HAS_CREATE_PICTURE (display)) {
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);
}
}
}
 
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->compositor = display->compositor;
surface->shm = NULL;
surface->fallback = 0;
 
surface->drawable = drawable;
surface->owns_pixmap = FALSE;
surface->use_pixmap = 0;
surface->width = width;
surface->height = height;
 
surface->picture = None;
surface->precision = PolyModePrecise;
 
surface->embedded_source.picture = None;
 
surface->visual = visual;
surface->xrender_format = xrender_format;
surface->depth = depth;
 
/*
* 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;
}
 
cairo_list_add (&surface->link, &screen->surfaces);
 
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;
}
 
static cairo_bool_t valid_size (int width, int height)
{
/* Note: the minimum surface size allowed in the X protocol is 1x1.
* However, as we historically did not check the minimum size we
* allowed applications to lie and set the correct size later (one hopes).
* To preserve compatability we must allow applications to use
* 0x0 surfaces.
*/
return (width >= 0 && width <= XLIB_COORD_MAX &&
height >= 0 && height <= XLIB_COORD_MAX);
}
 
/**
* 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
*
* Since: 1.0
**/
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 (! valid_size (width, height)) {
/* you're lying, and you know it! */
return _cairo_surface_create_in_error (_cairo_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);
}
 
/**
* 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
*
* Since: 1.0
**/
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 (! valid_size (width, height))
return _cairo_surface_create_in_error (_cairo_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
*
* Since: 1.0
**/
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 (! valid_size (width, height))
return _cairo_surface_create_in_error (_cairo_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,
_visual_for_xrender_format (scr, format),
format, width, height, 0);
}
 
/**
* 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.
*
* Since: 1.0
**/
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 (unlikely (abstract_surface->finished)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (! _cairo_surface_is_xlib (abstract_surface)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (surface->width == width && surface->height == height)
return;
 
if (! valid_size (width, height)) {
_cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_SIZE));
return;
}
 
status = _cairo_surface_flush (abstract_surface, 0);
if (unlikely (status)) {
_cairo_surface_set_error (abstract_surface, status);
return;
}
 
_cairo_xlib_surface_discard_shm (surface);
 
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.
*
* Since: 1.0
**/
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 (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (! _cairo_surface_is_xlib (abstract_surface)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
return;
}
 
if (! valid_size (width, height)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_INVALID_SIZE));
return;
}
 
/* XXX: and what about this case? */
if (surface->owns_pixmap)
return;
 
status = _cairo_surface_flush (abstract_surface, 0);
if (unlikely (status)) {
_cairo_surface_set_error (abstract_surface, status);
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->picture != None) {
XRenderFreePicture (display->display, surface->picture);
if (unlikely (status)) {
status = _cairo_surface_set_error (&surface->base, status);
return;
}
 
surface->picture = None;
}
 
cairo_device_release (&display->base);
 
surface->drawable = drawable;
}
 
if (surface->width != width || surface->height != height) {
_cairo_xlib_surface_discard_shm (surface);
 
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
*
* Gets the X Visual associated with @surface, suitable for use with the
* underlying X Drawable. If @surface was created by
* cairo_xlib_surface_create(), the return value is the Visual passed to that
* constructor.
*
* Return value: the Visual or %NULL if there is no appropriate Visual for
* @surface.
*
* Since: 1.2
**/
Visual *
cairo_xlib_surface_get_visual (cairo_surface_t *surface)
{
cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface;
 
if (! _cairo_surface_is_xlib (surface)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
return xlib_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;
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-visual.c
0,0 → 1,194
/* Cairo - a vector graphics library with display and print output
*
* Copyright © 2008 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 Red Hat, Inc.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
*/
 
#include "cairoint.h"
 
#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib-private.h"
 
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
 
/* A perceptual distance metric between two colors. No sqrt needed
* since the square of the distance is still a valid metric. */
 
/* XXX: This is currently using linear distance in RGB space which is
* decidedly not perceptually linear. If someone cared a lot about the
* quality, they might choose something else here. Then again, they
* might also choose not to use a PseudoColor visual... */
static inline int
_color_distance (unsigned short r1, unsigned short g1, unsigned short b1,
unsigned short r2, unsigned short g2, unsigned short b2)
{
r1 >>= 8; g1 >>= 8; b1 >>= 8;
r2 >>= 8; g2 >>= 8; b2 >>= 8;
 
return ((r2 - r1) * (r2 - r1) +
(g2 - g1) * (g2 - g1) +
(b2 - b1) * (b2 - b1));
}
 
cairo_status_t
_cairo_xlib_visual_info_create (Display *dpy,
int screen,
VisualID visualid,
cairo_xlib_visual_info_t **out)
{
cairo_xlib_visual_info_t *info;
Colormap colormap = DefaultColormap (dpy, screen);
XColor color;
int gray, red, green, blue;
int i, j, distance, min_distance = 0;
XColor colors[256];
unsigned short cube_index_to_short[CUBE_SIZE];
unsigned short ramp_index_to_short[RAMP_SIZE];
unsigned char gray_to_pseudocolor[RAMP_SIZE];
 
for (i = 0; i < CUBE_SIZE; i++)
cube_index_to_short[i] = (0xffff * i + ((CUBE_SIZE-1)>>1)) / (CUBE_SIZE-1);
for (i = 0; i < RAMP_SIZE; i++)
ramp_index_to_short[i] = (0xffff * i + ((RAMP_SIZE-1)>>1)) / (RAMP_SIZE-1);
 
info = malloc (sizeof (cairo_xlib_visual_info_t));
if (unlikely (info == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
cairo_list_init (&info->link);
info->visualid = visualid;
 
/* Allocate a gray ramp and a color cube.
* Give up as soon as failures start. */
 
for (gray = 0; gray < RAMP_SIZE; gray++) {
color.red = color.green = color.blue = ramp_index_to_short[gray];
if (! XAllocColor (dpy, colormap, &color))
goto DONE_ALLOCATE;
}
 
/* XXX: Could do this in a more clever order to have the best
* possible results from early failure. Could also choose a cube
* uniformly distributed in a better space than RGB. */
for (red = 0; red < CUBE_SIZE; red++) {
for (green = 0; green < CUBE_SIZE; green++) {
for (blue = 0; blue < CUBE_SIZE; blue++) {
color.red = cube_index_to_short[red];
color.green = cube_index_to_short[green];
color.blue = cube_index_to_short[blue];
color.pixel = 0;
color.flags = 0;
color.pad = 0;
if (! XAllocColor (dpy, colormap, &color))
goto DONE_ALLOCATE;
}
}
}
DONE_ALLOCATE:
 
for (i = 0; i < ARRAY_LENGTH (colors); i++)
colors[i].pixel = i;
XQueryColors (dpy, colormap, colors, ARRAY_LENGTH (colors));
 
/* Search for nearest colors within allocated colormap. */
for (gray = 0; gray < RAMP_SIZE; gray++) {
for (i = 0; i < 256; i++) {
distance = _color_distance (ramp_index_to_short[gray],
ramp_index_to_short[gray],
ramp_index_to_short[gray],
colors[i].red,
colors[i].green,
colors[i].blue);
if (i == 0 || distance < min_distance) {
gray_to_pseudocolor[gray] = colors[i].pixel;
min_distance = distance;
if (!min_distance)
break;
}
}
}
for (red = 0; red < CUBE_SIZE; red++) {
for (green = 0; green < CUBE_SIZE; green++) {
for (blue = 0; blue < CUBE_SIZE; blue++) {
for (i = 0; i < 256; i++) {
distance = _color_distance (cube_index_to_short[red],
cube_index_to_short[green],
cube_index_to_short[blue],
colors[i].red,
colors[i].green,
colors[i].blue);
if (i == 0 || distance < min_distance) {
info->cube_to_pseudocolor[red][green][blue] = colors[i].pixel;
min_distance = distance;
if (!min_distance)
break;
}
}
}
}
}
 
for (i = 0, j = 0; i < 256; i++) {
if (j < CUBE_SIZE - 1 && (((i<<8)+i) - (int)cube_index_to_short[j]) > ((int)cube_index_to_short[j+1] - ((i<<8)+i)))
j++;
info->field8_to_cube[i] = j;
 
info->dither8_to_cube[i] = ((int)i - 128) / (CUBE_SIZE - 1);
}
for (i = 0, j = 0; i < 256; i++) {
if (j < RAMP_SIZE - 1 && (((i<<8)+i) - (int)ramp_index_to_short[j]) > ((int)ramp_index_to_short[j+1] - ((i<<8)+i)))
j++;
info->gray8_to_pseudocolor[i] = gray_to_pseudocolor[j];
}
 
for (i = 0; i < 256; i++) {
info->colors[i].a = 0xff;
info->colors[i].r = colors[i].red >> 8;
info->colors[i].g = colors[i].green >> 8;
info->colors[i].b = colors[i].blue >> 8;
}
 
*out = info;
return CAIRO_STATUS_SUCCESS;
}
 
void
_cairo_xlib_visual_info_destroy (cairo_xlib_visual_info_t *info)
{
/* No need for XFreeColors() whilst using DefaultColormap */
_cairo_list_del (&info->link);
free (info);
}
 
#endif /* !CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-xcb-surface.c
0,0 → 1,844
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2009 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#if CAIRO_HAS_XLIB_XCB_FUNCTIONS
 
#include "cairo-xlib.h"
#include "cairo-xcb.h"
 
#include "cairo-xcb-private.h"
#include "cairo-xlib-xrender-private.h"
 
#include "cairo-default-context-private.h"
#include "cairo-list-inline.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-backend-private.h"
 
#include <X11/Xlib-xcb.h>
#include <X11/Xlibint.h> /* For XESetCloseDisplay */
 
struct cairo_xlib_xcb_display_t {
cairo_device_t base;
 
Display *dpy;
cairo_device_t *xcb_device;
XExtCodes *codes;
 
cairo_list_t link;
};
typedef struct cairo_xlib_xcb_display_t cairo_xlib_xcb_display_t;
 
/* List of all #cairo_xlib_xcb_display_t alive,
* protected by _cairo_xlib_display_mutex */
static cairo_list_t displays;
 
static cairo_surface_t *
_cairo_xlib_xcb_surface_create (void *dpy,
void *scr,
void *visual,
void *format,
cairo_surface_t *xcb);
 
static cairo_surface_t *
_cairo_xlib_xcb_surface_create_similar (void *abstract_other,
cairo_content_t content,
int width,
int height)
{
cairo_xlib_xcb_surface_t *other = abstract_other;
cairo_surface_t *xcb;
 
xcb = other->xcb->base.backend->create_similar (other->xcb, content, width, height);
if (unlikely (xcb == NULL || xcb->status))
return xcb;
 
return _cairo_xlib_xcb_surface_create (other->display, other->screen, NULL, NULL, xcb);
}
 
static cairo_status_t
_cairo_xlib_xcb_surface_finish (void *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
cairo_status_t status;
 
cairo_surface_finish (&surface->xcb->base);
status = surface->xcb->base.status;
cairo_surface_destroy (&surface->xcb->base);
surface->xcb = NULL;
 
return status;
}
 
static cairo_surface_t *
_cairo_xlib_xcb_surface_create_similar_image (void *abstract_other,
cairo_format_t format,
int width,
int height)
{
cairo_xlib_xcb_surface_t *surface = abstract_other;
return cairo_surface_create_similar_image (&surface->xcb->base, format, width, height);
}
 
static cairo_image_surface_t *
_cairo_xlib_xcb_surface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_map_to_image (&surface->xcb->base, extents);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_unmap (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_unmap_image (&surface->xcb->base, image);
}
 
static cairo_surface_t *
_cairo_xlib_xcb_surface_source (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_get_source (&surface->xcb->base, extents);
}
 
static cairo_status_t
_cairo_xlib_xcb_surface_acquire_source_image (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_acquire_source_image (&surface->xcb->base,
image_out, image_extra);
}
 
static void
_cairo_xlib_xcb_surface_release_source_image (void *abstract_surface,
cairo_image_surface_t *image_out,
void *image_extra)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
_cairo_surface_release_source_image (&surface->xcb->base, image_out, image_extra);
}
 
static cairo_bool_t
_cairo_xlib_xcb_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *extents)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_get_extents (&surface->xcb->base, extents);
}
 
static void
_cairo_xlib_xcb_surface_get_font_options (void *abstract_surface,
cairo_font_options_t *options)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
cairo_surface_get_font_options (&surface->xcb->base, options);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_paint (&surface->xcb->base, op, source, clip);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_mask (&surface->xcb->base, op, source, mask, clip);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_stroke (&surface->xcb->base,
op, source, path, style, ctm, ctm_inverse,
tolerance, antialias, clip);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_fill (&surface->xcb->base,
op, source, path,
fill_rule, tolerance,
antialias, clip);
}
 
static cairo_int_status_t
_cairo_xlib_xcb_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
return _cairo_surface_show_text_glyphs (&surface->xcb->base, op, source,
NULL, 0,
glyphs, num_glyphs,
NULL, 0, 0,
scaled_font, clip);
}
 
static cairo_status_t
_cairo_xlib_xcb_surface_flush (void *abstract_surface, unsigned flags)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
/* We have to call cairo_surface_flush() to make sure snapshots are detached */
return _cairo_surface_flush (&surface->xcb->base, flags);
}
 
static cairo_status_t
_cairo_xlib_xcb_surface_mark_dirty (void *abstract_surface,
int x, int y,
int width, int height)
{
cairo_xlib_xcb_surface_t *surface = abstract_surface;
cairo_surface_mark_dirty_rectangle (&surface->xcb->base, x, y, width, height);
return cairo_surface_status (&surface->xcb->base);
}
 
static const cairo_surface_backend_t _cairo_xlib_xcb_surface_backend = {
CAIRO_SURFACE_TYPE_XLIB,
_cairo_xlib_xcb_surface_finish,
 
_cairo_default_context_create, /* XXX */
 
_cairo_xlib_xcb_surface_create_similar,
_cairo_xlib_xcb_surface_create_similar_image,
_cairo_xlib_xcb_surface_map_to_image,
_cairo_xlib_xcb_surface_unmap,
 
_cairo_xlib_xcb_surface_source,
_cairo_xlib_xcb_surface_acquire_source_image,
_cairo_xlib_xcb_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_xlib_xcb_surface_get_extents,
_cairo_xlib_xcb_surface_get_font_options,
 
_cairo_xlib_xcb_surface_flush,
_cairo_xlib_xcb_surface_mark_dirty,
 
_cairo_xlib_xcb_surface_paint,
_cairo_xlib_xcb_surface_mask,
_cairo_xlib_xcb_surface_stroke,
_cairo_xlib_xcb_surface_fill,
NULL, /* fill_stroke */
_cairo_xlib_xcb_surface_glyphs,
};
 
static void
_cairo_xlib_xcb_display_finish (void *abstract_display)
{
cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) abstract_display;
 
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
cairo_list_del (&display->link);
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
cairo_device_destroy (display->xcb_device);
display->xcb_device = NULL;
 
XESetCloseDisplay (display->dpy, display->codes->extension, NULL);
/* Drop the reference from _cairo_xlib_xcb_device_create */
cairo_device_destroy (&display->base);
}
 
static int
_cairo_xlib_xcb_close_display(Display *dpy, XExtCodes *codes)
{
cairo_xlib_xcb_display_t *display;
 
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
cairo_list_foreach_entry (display,
cairo_xlib_xcb_display_t,
&displays,
link)
{
if (display->dpy == dpy)
{
/* _cairo_xlib_xcb_display_finish will lock the mutex again
* -> deadlock (This mutex isn't recursive) */
cairo_device_reference (&display->base);
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
/* Make sure the xcb and xlib-xcb devices are finished */
cairo_device_finish (display->xcb_device);
cairo_device_finish (&display->base);
 
cairo_device_destroy (&display->base);
return 0;
}
}
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
return 0;
}
 
static const cairo_device_backend_t _cairo_xlib_xcb_device_backend = {
CAIRO_DEVICE_TYPE_XLIB,
 
NULL,
NULL,
 
NULL, /* flush */
_cairo_xlib_xcb_display_finish,
free, /* destroy */
};
 
static cairo_device_t *
_cairo_xlib_xcb_device_create (Display *dpy, cairo_device_t *xcb_device)
{
cairo_xlib_xcb_display_t *display = NULL;
cairo_device_t *device;
 
if (xcb_device == NULL)
return NULL;
 
CAIRO_MUTEX_INITIALIZE ();
 
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
if (displays.next == NULL) {
cairo_list_init (&displays);
}
 
cairo_list_foreach_entry (display,
cairo_xlib_xcb_display_t,
&displays,
link)
{
if (display->dpy == dpy) {
/* Maintain MRU order. */
if (displays.next != &display->link)
cairo_list_move (&display->link, &displays);
 
/* Grab a reference for our caller */
device = cairo_device_reference (&display->base);
assert (display->xcb_device == xcb_device);
goto unlock;
}
}
 
display = malloc (sizeof (cairo_xlib_xcb_display_t));
if (unlikely (display == NULL)) {
device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
goto unlock;
}
 
display->codes = XAddExtension (dpy);
if (unlikely (display->codes == NULL)) {
device = _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
free (display);
goto unlock;
}
 
_cairo_device_init (&display->base, &_cairo_xlib_xcb_device_backend);
 
XESetCloseDisplay (dpy, display->codes->extension, _cairo_xlib_xcb_close_display);
/* Add a reference for _cairo_xlib_xcb_display_finish. This basically means
* that the device's reference count never drops to zero
* as long as our Display* is alive. We need this because there is no
* "XDelExtension" to undo XAddExtension and having lots of registered
* extensions slows down libX11. */
cairo_device_reference (&display->base);
 
display->dpy = dpy;
display->xcb_device = cairo_device_reference(xcb_device);
 
cairo_list_add (&display->link, &displays);
device = &display->base;
 
unlock:
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
 
return device;
}
 
static cairo_surface_t *
_cairo_xlib_xcb_surface_create (void *dpy,
void *scr,
void *visual,
void *format,
cairo_surface_t *xcb)
{
cairo_xlib_xcb_surface_t *surface;
 
if (unlikely (xcb->status))
return xcb;
 
surface = malloc (sizeof (*surface));
if (unlikely (surface == NULL)) {
cairo_surface_destroy (xcb);
return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
}
 
_cairo_surface_init (&surface->base,
&_cairo_xlib_xcb_surface_backend,
_cairo_xlib_xcb_device_create (dpy, xcb->device),
xcb->content);
 
/* _cairo_surface_init() got another reference to the device, drop ours */
cairo_device_destroy (surface->base.device);
 
surface->display = dpy;
surface->screen = scr;
surface->visual = visual;
surface->format = format;
surface->xcb = (cairo_xcb_surface_t *) xcb;
 
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_surface_t *
cairo_xlib_surface_create (Display *dpy,
Drawable drawable,
Visual *visual,
int width,
int height)
{
Screen *scr;
xcb_visualtype_t xcb_visual;
 
scr = _cairo_xlib_screen_from_visual (dpy, visual);
if (scr == NULL)
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL));
 
xcb_visual.visual_id = visual->visualid;
#if defined(__cplusplus) || defined(c_plusplus)
xcb_visual._class = visual->c_class;
#else
xcb_visual._class = visual->class;
#endif
xcb_visual.bits_per_rgb_value = visual->bits_per_rgb;
xcb_visual.colormap_entries = visual->map_entries;
xcb_visual.red_mask = visual->red_mask;
xcb_visual.green_mask = visual->green_mask;
xcb_visual.blue_mask = visual->blue_mask;
 
return _cairo_xlib_xcb_surface_create (dpy, scr, visual, NULL,
cairo_xcb_surface_create (XGetXCBConnection (dpy),
drawable,
&xcb_visual,
width, height));
}
 
cairo_surface_t *
cairo_xlib_surface_create_for_bitmap (Display *dpy,
Pixmap bitmap,
Screen *scr,
int width,
int height)
{
return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, NULL,
cairo_xcb_surface_create_for_bitmap (XGetXCBConnection (dpy),
(xcb_screen_t *) scr,
bitmap,
width, height));
}
 
#if CAIRO_HAS_XLIB_XRENDER_SURFACE
static xcb_screen_t *
_cairo_xcb_screen_from_root (xcb_connection_t *connection,
xcb_window_t id)
{
xcb_screen_iterator_t s;
 
s = xcb_setup_roots_iterator (xcb_get_setup (connection));
for (; s.rem; xcb_screen_next (&s)) {
if (s.data->root == id)
return s.data;
}
 
return NULL;
}
cairo_surface_t *
cairo_xlib_surface_create_with_xrender_format (Display *dpy,
Drawable drawable,
Screen *scr,
XRenderPictFormat *format,
int width,
int height)
{
xcb_render_pictforminfo_t xcb_format;
xcb_connection_t *connection;
xcb_screen_t *screen;
 
xcb_format.id = format->id;
xcb_format.type = format->type;
xcb_format.depth = format->depth;
xcb_format.direct.red_shift = format->direct.red;
xcb_format.direct.red_mask = format->direct.redMask;
xcb_format.direct.green_shift = format->direct.green;
xcb_format.direct.green_mask = format->direct.greenMask;
xcb_format.direct.blue_shift = format->direct.blue;
xcb_format.direct.blue_mask = format->direct.blueMask;
xcb_format.direct.alpha_shift = format->direct.alpha;
xcb_format.direct.alpha_mask = format->direct.alphaMask;
xcb_format.colormap = format->colormap;
 
connection = XGetXCBConnection (dpy);
screen = _cairo_xcb_screen_from_root (connection, (xcb_window_t) scr->root);
 
return _cairo_xlib_xcb_surface_create (dpy, scr, NULL, format,
cairo_xcb_surface_create_with_xrender_format (connection, screen,
drawable,
&xcb_format,
width, height));
}
 
XRenderPictFormat *
cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface)
{
cairo_xlib_xcb_surface_t *xlib_surface = (cairo_xlib_xcb_surface_t *) surface;
 
/* Throw an error for a non-xlib surface */
if (surface->type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
return xlib_surface->format;
}
#endif
 
void
cairo_xlib_surface_set_size (cairo_surface_t *abstract_surface,
int width,
int height)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
cairo_status_t status;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
status = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
 
cairo_xcb_surface_set_size (&surface->xcb->base, width, height);
if (unlikely (surface->xcb->base.status)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (surface->xcb->base.status));
}
}
 
void
cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface,
Drawable drawable,
int width,
int height)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *)abstract_surface;
cairo_status_t status;
 
if (unlikely (abstract_surface->status))
return;
if (unlikely (abstract_surface->finished)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
 
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
status = _cairo_surface_set_error (abstract_surface,
CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return;
}
 
cairo_xcb_surface_set_drawable (&surface->xcb->base, drawable, width, height);
if (unlikely (surface->xcb->base.status)) {
status = _cairo_surface_set_error (abstract_surface,
_cairo_error (surface->xcb->base.status));
}
}
 
Display *
cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
return surface->display;
}
 
Drawable
cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->finished)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
return 0;
}
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
/* This can happen when e.g. create_similar falls back to an image surface
* because we don't have the RENDER extension. */
if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
return surface->xcb->drawable;
}
 
Screen *
cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
return surface->screen;
}
 
Visual *
cairo_xlib_surface_get_visual (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return NULL;
}
 
return surface->visual;
}
 
int
cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->finished)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
return 0;
}
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
/* This can happen when e.g. create_similar falls back to an image surface
* because we don't have the RENDER extension. */
if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
return surface->xcb->depth;
}
 
int
cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->finished)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
return 0;
}
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
/* This can happen when e.g. create_similar falls back to an image surface
* because we don't have the RENDER extension. */
if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
return surface->xcb->width;
}
 
int
cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface)
{
cairo_xlib_xcb_surface_t *surface = (cairo_xlib_xcb_surface_t *) abstract_surface;
 
if (unlikely (abstract_surface->finished)) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_FINISHED);
return 0;
}
if (surface->base.type != CAIRO_SURFACE_TYPE_XLIB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
/* This can happen when e.g. create_similar falls back to an image surface
* because we don't have the RENDER extension. */
if (surface->xcb->base.type != CAIRO_SURFACE_TYPE_XCB) {
_cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
return 0;
}
 
return surface->xcb->height;
}
 
void
cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
int major, int minor)
{
cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
 
if (device == NULL || device->status)
return;
 
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB)
return;
 
cairo_xcb_device_debug_cap_xrender_version (display->xcb_device,
major, minor);
}
 
void
cairo_xlib_device_debug_set_precision (cairo_device_t *device,
int precision)
{
cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
 
if (device == NULL || device->status)
return;
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return;
}
 
cairo_xcb_device_debug_set_precision (display->xcb_device, precision);
}
 
int
cairo_xlib_device_debug_get_precision (cairo_device_t *device)
{
cairo_xlib_xcb_display_t *display = (cairo_xlib_xcb_display_t *) device;
 
if (device == NULL || device->status)
return -1;
if (device->backend->type != CAIRO_DEVICE_TYPE_XLIB) {
cairo_status_t status;
 
status = _cairo_device_set_error (device, CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
(void) status;
return -1;
}
 
return cairo_xcb_device_debug_get_precision (display->xcb_device);
}
 
#endif /* CAIRO_HAS_XLIB_XCB_FUNCTIONS */
/programs/develop/libraries/cairo/src/cairo-xlib-xrender-private.h
96,27 → 96,32
#define PictOpBlendMaximum 0x3e
#endif
 
/* There doesn't appear to be a simple #define that we can conditionalize
* on. Instead, use the version; gradients were introdiced in 0.10. */
#if RENDER_MAJOR == 0 && RENDER_MINOR < 10
#if !HAVE_XRENDERCREATELINEARGRADIENT
#define XRenderCreateLinearGradient _int_consume
 
typedef struct _XLinearGradient {
XPointFixed p1;
XPointFixed p2;
} XLinearGradient;
#endif
 
#if !HAVE_XRENDERCREATERADIALGRADIENT
#define XRenderCreateRadialGradient _int_consume
#define XRenderCreateConicalGradient _int_consume
 
typedef struct _XCircle {
XFixed x;
XFixed y;
XFixed radius;
} XCircle;
typedef struct _XLinearGradient {
XPointFixed p1;
XPointFixed p2;
} XLinearGradient;
 
typedef struct _XRadialGradient {
XCircle inner;
XCircle outer;
} XRadialGradient;
#endif
 
#if !HAVE_XRENDERCREATECONICALGRADIENT
#define XRenderCreateConicalGradient _int_consume
 
typedef struct _XConicalGradient {
XPointFixed center;
XFixed angle; /* in degrees */
/programs/develop/libraries/cairo/src/cairo-xlib.h
91,6 → 91,24
cairo_public int
cairo_xlib_surface_get_height (cairo_surface_t *surface);
 
/* debug interface */
 
cairo_public void
cairo_xlib_device_debug_cap_xrender_version (cairo_device_t *device,
int major_version,
int minor_version);
 
/*
* @precision: -1 implies automatically choose based on antialiasing mode,
* any other value overrides and sets the corresponding PolyMode.
*/
cairo_public void
cairo_xlib_device_debug_set_precision (cairo_device_t *device,
int precision);
 
cairo_public int
cairo_xlib_device_debug_get_precision (cairo_device_t *device);
 
CAIRO_END_DECLS
 
#else /* CAIRO_HAS_XLIB_SURFACE */
/programs/develop/libraries/cairo/src/cairo-xml-surface.c
0,0 → 1,1210
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2009 Chris Wilson
*
* 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 Chris Wilson.
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
/* This surface is intended to produce a verbose, hierarchical, DAG XML file
* representing a single surface. It is intended to be used by debuggers,
* such as cairo-sphinx, or by application test-suites that what a log of
* operations.
*/
 
#include "cairoint.h"
 
#include "cairo-xml.h"
 
#include "cairo-clip-private.h"
#include "cairo-device-private.h"
#include "cairo-default-context-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-error-private.h"
#include "cairo-output-stream-private.h"
#include "cairo-recording-surface-inline.h"
 
#define static cairo_warn static
 
typedef struct _cairo_xml_surface cairo_xml_surface_t;
 
typedef struct _cairo_xml {
cairo_device_t base;
 
cairo_output_stream_t *stream;
int indent;
} cairo_xml_t;
 
struct _cairo_xml_surface {
cairo_surface_t base;
 
double width, height;
};
 
slim_hidden_proto (cairo_xml_for_recording_surface);
 
static const cairo_surface_backend_t _cairo_xml_surface_backend;
 
static const char *
_operator_to_string (cairo_operator_t op)
{
static const char *names[] = {
"CLEAR", /* CAIRO_OPERATOR_CLEAR */
 
"SOURCE", /* CAIRO_OPERATOR_SOURCE */
"OVER", /* CAIRO_OPERATOR_OVER */
"IN", /* CAIRO_OPERATOR_IN */
"OUT", /* CAIRO_OPERATOR_OUT */
"ATOP", /* CAIRO_OPERATOR_ATOP */
 
"DEST", /* CAIRO_OPERATOR_DEST */
"DEST_OVER", /* CAIRO_OPERATOR_DEST_OVER */
"DEST_IN", /* CAIRO_OPERATOR_DEST_IN */
"DEST_OUT", /* CAIRO_OPERATOR_DEST_OUT */
"DEST_ATOP", /* CAIRO_OPERATOR_DEST_ATOP */
 
"XOR", /* CAIRO_OPERATOR_XOR */
"ADD", /* CAIRO_OPERATOR_ADD */
"SATURATE", /* CAIRO_OPERATOR_SATURATE */
 
"MULTIPLY", /* CAIRO_OPERATOR_MULTIPLY */
"SCREEN", /* CAIRO_OPERATOR_SCREEN */
"OVERLAY", /* CAIRO_OPERATOR_OVERLAY */
"DARKEN", /* CAIRO_OPERATOR_DARKEN */
"LIGHTEN", /* CAIRO_OPERATOR_LIGHTEN */
"DODGE", /* CAIRO_OPERATOR_COLOR_DODGE */
"BURN", /* CAIRO_OPERATOR_COLOR_BURN */
"HARD_LIGHT", /* CAIRO_OPERATOR_HARD_LIGHT */
"SOFT_LIGHT", /* CAIRO_OPERATOR_SOFT_LIGHT */
"DIFFERENCE", /* CAIRO_OPERATOR_DIFFERENCE */
"EXCLUSION", /* CAIRO_OPERATOR_EXCLUSION */
"HSL_HUE", /* CAIRO_OPERATOR_HSL_HUE */
"HSL_SATURATION", /* CAIRO_OPERATOR_HSL_SATURATION */
"HSL_COLOR", /* CAIRO_OPERATOR_HSL_COLOR */
"HSL_LUMINOSITY" /* CAIRO_OPERATOR_HSL_LUMINOSITY */
};
assert (op < ARRAY_LENGTH (names));
return names[op];
}
 
static const char *
_extend_to_string (cairo_extend_t extend)
{
static const char *names[] = {
"EXTEND_NONE", /* CAIRO_EXTEND_NONE */
"EXTEND_REPEAT", /* CAIRO_EXTEND_REPEAT */
"EXTEND_REFLECT", /* CAIRO_EXTEND_REFLECT */
"EXTEND_PAD" /* CAIRO_EXTEND_PAD */
};
assert (extend < ARRAY_LENGTH (names));
return names[extend];
}
 
static const char *
_filter_to_string (cairo_filter_t filter)
{
static const char *names[] = {
"FILTER_FAST", /* CAIRO_FILTER_FAST */
"FILTER_GOOD", /* CAIRO_FILTER_GOOD */
"FILTER_BEST", /* CAIRO_FILTER_BEST */
"FILTER_NEAREST", /* CAIRO_FILTER_NEAREST */
"FILTER_BILINEAR", /* CAIRO_FILTER_BILINEAR */
"FILTER_GAUSSIAN", /* CAIRO_FILTER_GAUSSIAN */
};
assert (filter < ARRAY_LENGTH (names));
return names[filter];
}
 
static const char *
_fill_rule_to_string (cairo_fill_rule_t rule)
{
static const char *names[] = {
"WINDING", /* CAIRO_FILL_RULE_WINDING */
"EVEN_ODD" /* CAIRO_FILL_RILE_EVEN_ODD */
};
assert (rule < ARRAY_LENGTH (names));
return names[rule];
}
 
static const char *
_antialias_to_string (cairo_antialias_t antialias)
{
static const char *names[] = {
"DEFAULT", /* CAIRO_ANTIALIAS_DEFAULT */
"NONE", /* CAIRO_ANTIALIAS_NONE */
"GRAY", /* CAIRO_ANTIALIAS_GRAY */
"SUBPIXEL", /* CAIRO_ANTIALIAS_SUBPIXEL */
"FAST", /* CAIRO_ANTIALIAS_FAST */
"GOOD", /* CAIRO_ANTIALIAS_GOOD */
"BEST", /* CAIRO_ANTIALIAS_BEST */
};
assert (antialias < ARRAY_LENGTH (names));
return names[antialias];
}
 
static const char *
_line_cap_to_string (cairo_line_cap_t line_cap)
{
static const char *names[] = {
"LINE_CAP_BUTT", /* CAIRO_LINE_CAP_BUTT */
"LINE_CAP_ROUND", /* CAIRO_LINE_CAP_ROUND */
"LINE_CAP_SQUARE" /* CAIRO_LINE_CAP_SQUARE */
};
assert (line_cap < ARRAY_LENGTH (names));
return names[line_cap];
}
 
static const char *
_line_join_to_string (cairo_line_join_t line_join)
{
static const char *names[] = {
"LINE_JOIN_MITER", /* CAIRO_LINE_JOIN_MITER */
"LINE_JOIN_ROUND", /* CAIRO_LINE_JOIN_ROUND */
"LINE_JOIN_BEVEL", /* CAIRO_LINE_JOIN_BEVEL */
};
assert (line_join < ARRAY_LENGTH (names));
return names[line_join];
}
 
static const char *
_content_to_string (cairo_content_t content)
{
switch (content) {
case CAIRO_CONTENT_ALPHA: return "ALPHA";
case CAIRO_CONTENT_COLOR: return "COLOR";
default:
case CAIRO_CONTENT_COLOR_ALPHA: return "COLOR_ALPHA";
}
}
 
static const char *
_format_to_string (cairo_format_t format)
{
switch (format) {
case CAIRO_FORMAT_ARGB32: return "ARGB32";
case CAIRO_FORMAT_RGB30: return "RGB30";
case CAIRO_FORMAT_RGB24: return "RGB24";
case CAIRO_FORMAT_RGB16_565: return "RGB16_565";
case CAIRO_FORMAT_A8: return "A8";
case CAIRO_FORMAT_A1: return "A1";
case CAIRO_FORMAT_INVALID: return "INVALID";
}
ASSERT_NOT_REACHED;
return "INVALID";
}
 
static cairo_status_t
_device_flush (void *abstract_device)
{
cairo_xml_t *xml = abstract_device;
cairo_status_t status;
 
status = _cairo_output_stream_flush (xml->stream);
 
return status;
}
 
static void
_device_destroy (void *abstract_device)
{
cairo_xml_t *xml = abstract_device;
cairo_status_t status;
 
status = _cairo_output_stream_destroy (xml->stream);
 
free (xml);
}
 
static const cairo_device_backend_t _cairo_xml_device_backend = {
CAIRO_DEVICE_TYPE_XML,
 
NULL, NULL, /* lock, unlock */
 
_device_flush,
NULL, /* finish */
_device_destroy
};
 
static cairo_device_t *
_cairo_xml_create_internal (cairo_output_stream_t *stream)
{
cairo_xml_t *xml;
 
xml = malloc (sizeof (cairo_xml_t));
if (unlikely (xml == NULL))
return _cairo_device_create_in_error (CAIRO_STATUS_NO_MEMORY);
 
memset (xml, 0, sizeof (cairo_xml_t));
 
_cairo_device_init (&xml->base, &_cairo_xml_device_backend);
 
xml->indent = 0;
xml->stream = stream;
 
return &xml->base;
}
 
static void
_cairo_xml_indent (cairo_xml_t *xml, int indent)
{
xml->indent += indent;
assert (xml->indent >= 0);
}
 
static void CAIRO_PRINTF_FORMAT (2, 3)
_cairo_xml_printf (cairo_xml_t *xml, const char *fmt, ...)
{
va_list ap;
char indent[80];
int len;
 
len = MIN (xml->indent, ARRAY_LENGTH (indent));
memset (indent, ' ', len);
_cairo_output_stream_write (xml->stream, indent, len);
 
va_start (ap, fmt);
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
va_end (ap);
 
_cairo_output_stream_write (xml->stream, "\n", 1);
}
 
static void CAIRO_PRINTF_FORMAT (2, 3)
_cairo_xml_printf_start (cairo_xml_t *xml, const char *fmt, ...)
{
char indent[80];
int len;
 
len = MIN (xml->indent, ARRAY_LENGTH (indent));
memset (indent, ' ', len);
_cairo_output_stream_write (xml->stream, indent, len);
 
if (fmt != NULL) {
va_list ap;
 
va_start (ap, fmt);
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
va_end (ap);
}
}
 
static void CAIRO_PRINTF_FORMAT (2, 3)
_cairo_xml_printf_continue (cairo_xml_t *xml, const char *fmt, ...)
{
va_list ap;
 
va_start (ap, fmt);
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
va_end (ap);
}
 
static void CAIRO_PRINTF_FORMAT (2, 3)
_cairo_xml_printf_end (cairo_xml_t *xml, const char *fmt, ...)
{
if (fmt != NULL) {
va_list ap;
 
va_start (ap, fmt);
_cairo_output_stream_vprintf (xml->stream, fmt, ap);
va_end (ap);
}
 
_cairo_output_stream_write (xml->stream, "\n", 1);
}
 
static cairo_surface_t *
_cairo_xml_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
cairo_rectangle_t extents;
 
extents.x = extents.y = 0;
extents.width = width;
extents.height = height;
 
return cairo_recording_surface_create (content, &extents);
}
 
static cairo_bool_t
_cairo_xml_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
cairo_xml_surface_t *surface = abstract_surface;
 
if (surface->width < 0 || surface->height < 0)
return FALSE;
 
rectangle->x = 0;
rectangle->y = 0;
rectangle->width = surface->width;
rectangle->height = surface->height;
 
return TRUE;
}
 
static cairo_status_t
_cairo_xml_move_to (void *closure,
const cairo_point_t *p1)
{
_cairo_xml_printf_continue (closure, " %f %f m",
_cairo_fixed_to_double (p1->x),
_cairo_fixed_to_double (p1->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_line_to (void *closure,
const cairo_point_t *p1)
{
_cairo_xml_printf_continue (closure, " %f %f l",
_cairo_fixed_to_double (p1->x),
_cairo_fixed_to_double (p1->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_curve_to (void *closure,
const cairo_point_t *p1,
const cairo_point_t *p2,
const cairo_point_t *p3)
{
_cairo_xml_printf_continue (closure, " %f %f %f %f %f %f c",
_cairo_fixed_to_double (p1->x),
_cairo_fixed_to_double (p1->y),
_cairo_fixed_to_double (p2->x),
_cairo_fixed_to_double (p2->y),
_cairo_fixed_to_double (p3->x),
_cairo_fixed_to_double (p3->y));
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_close_path (void *closure)
{
_cairo_xml_printf_continue (closure, " h");
 
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_xml_emit_path (cairo_xml_t *xml,
const cairo_path_fixed_t *path)
{
cairo_status_t status;
 
_cairo_xml_printf_start (xml, "<path>");
status = _cairo_path_fixed_interpret (path,
_cairo_xml_move_to,
_cairo_xml_line_to,
_cairo_xml_curve_to,
_cairo_xml_close_path,
xml);
assert (status == CAIRO_STATUS_SUCCESS);
_cairo_xml_printf_end (xml, "</path>");
}
 
static void
_cairo_xml_emit_string (cairo_xml_t *xml,
const char *node,
const char *data)
{
_cairo_xml_printf (xml, "<%s>%s</%s>", node, data, node);
}
 
static void
_cairo_xml_emit_double (cairo_xml_t *xml,
const char *node,
double data)
{
_cairo_xml_printf (xml, "<%s>%f</%s>", node, data, node);
}
 
static cairo_xml_t *
to_xml (cairo_xml_surface_t *surface)
{
return (cairo_xml_t *) surface->base.device;
}
 
static cairo_status_t
_cairo_xml_surface_emit_clip_boxes (cairo_xml_surface_t *surface,
cairo_clip_t *clip)
{
cairo_box_t *box;
cairo_status_t status;
cairo_xml_t *xml;
int n;
 
if (clip->num_boxes == 0)
return CAIRO_STATUS_SUCCESS;
 
/* skip the trivial clip covering the surface extents */
if (surface->width >= 0 && surface->height >= 0 && clip->num_boxes == 1) {
box = &clip->boxes[0];
if (box->p1.x <= 0 && box->p1.y <= 0 &&
box->p2.x - box->p1.x >= _cairo_fixed_from_double (surface->width) &&
box->p2.y - box->p1.y >= _cairo_fixed_from_double (surface->height))
{
return CAIRO_STATUS_SUCCESS;
}
}
 
xml = to_xml (surface);
 
_cairo_xml_printf (xml, "<clip>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_printf (xml, "<path>");
_cairo_xml_indent (xml, 2);
for (n = 0; n < clip->num_boxes; n++) {
box = &clip->boxes[n];
 
_cairo_xml_printf_start (xml, "%f %f m",
_cairo_fixed_to_double (box->p1.x),
_cairo_fixed_to_double (box->p1.y));
_cairo_xml_printf_continue (xml, " %f %f l",
_cairo_fixed_to_double (box->p2.x),
_cairo_fixed_to_double (box->p1.y));
_cairo_xml_printf_continue (xml, " %f %f l",
_cairo_fixed_to_double (box->p2.x),
_cairo_fixed_to_double (box->p2.y));
_cairo_xml_printf_continue (xml, " %f %f l",
_cairo_fixed_to_double (box->p1.x),
_cairo_fixed_to_double (box->p2.y));
_cairo_xml_printf_end (xml, " h");
}
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</path>");
_cairo_xml_emit_double (xml, "tolerance", 1.0);
_cairo_xml_emit_string (xml, "antialias",
_antialias_to_string (CAIRO_ANTIALIAS_NONE));
_cairo_xml_emit_string (xml, "fill-rule",
_fill_rule_to_string (CAIRO_FILL_RULE_WINDING));
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</clip>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_surface_emit_clip_path (cairo_xml_surface_t *surface,
cairo_clip_path_t *clip_path)
{
cairo_box_t box;
cairo_status_t status;
cairo_xml_t *xml;
 
if (clip_path == NULL)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_xml_surface_emit_clip_path (surface, clip_path->prev);
if (unlikely (status))
return status;
 
/* skip the trivial clip covering the surface extents */
if (surface->width >= 0 && surface->height >= 0 &&
_cairo_path_fixed_is_box (&clip_path->path, &box))
{
if (box.p1.x <= 0 && box.p1.y <= 0 &&
box.p2.x - box.p1.x >= _cairo_fixed_from_double (surface->width) &&
box.p2.y - box.p1.y >= _cairo_fixed_from_double (surface->height))
{
return CAIRO_STATUS_SUCCESS;
}
}
 
xml = to_xml (surface);
 
_cairo_xml_printf_start (xml, "<clip>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_path (xml, &clip_path->path);
_cairo_xml_emit_double (xml, "tolerance", clip_path->tolerance);
_cairo_xml_emit_string (xml, "antialias",
_antialias_to_string (clip_path->antialias));
_cairo_xml_emit_string (xml, "fill-rule",
_fill_rule_to_string (clip_path->fill_rule));
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf_end (xml, "</clip>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_surface_emit_clip (cairo_xml_surface_t *surface,
const cairo_clip_t *clip)
{
cairo_status_t status;
 
if (clip == NULL)
return CAIRO_STATUS_SUCCESS;
 
status = _cairo_xml_surface_emit_clip_boxes (surface, clip);
if (unlikely (status))
return status;
 
return _cairo_xml_surface_emit_clip_path (surface, clip->path);
}
 
static cairo_status_t
_cairo_xml_emit_solid (cairo_xml_t *xml,
const cairo_solid_pattern_t *solid)
{
_cairo_xml_printf (xml, "<solid>%f %f %f %f</solid>",
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
return CAIRO_STATUS_SUCCESS;
}
 
static void
_cairo_xml_emit_matrix (cairo_xml_t *xml,
const cairo_matrix_t *matrix)
{
if (! _cairo_matrix_is_identity (matrix)) {
_cairo_xml_printf (xml, "<matrix>%f %f %f %f %f %f</matrix>",
matrix->xx, matrix->yx,
matrix->xy, matrix->yy,
matrix->x0, matrix->y0);
}
}
 
static void
_cairo_xml_emit_gradient (cairo_xml_t *xml,
const cairo_gradient_pattern_t *gradient)
{
unsigned int i;
 
for (i = 0; i < gradient->n_stops; i++) {
_cairo_xml_printf (xml,
"<color-stop>%f %f %f %f %f</color-stop>",
gradient->stops[i].offset,
gradient->stops[i].color.red,
gradient->stops[i].color.green,
gradient->stops[i].color.blue,
gradient->stops[i].color.alpha);
}
}
 
static cairo_status_t
_cairo_xml_emit_linear (cairo_xml_t *xml,
const cairo_linear_pattern_t *linear)
{
_cairo_xml_printf (xml,
"<linear x1='%f' y1='%f' x2='%f' y2='%f'>",
linear->pd1.x, linear->pd1.y,
linear->pd2.x, linear->pd2.y);
_cairo_xml_indent (xml, 2);
_cairo_xml_emit_gradient (xml, &linear->base);
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</linear>");
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_emit_radial (cairo_xml_t *xml,
const cairo_radial_pattern_t *radial)
{
_cairo_xml_printf (xml,
"<radial x1='%f' y1='%f' r1='%f' x2='%f' y2='%f' r2='%f'>",
radial->cd1.center.x, radial->cd1.center.y, radial->cd1.radius,
radial->cd2.center.x, radial->cd2.center.y, radial->cd2.radius);
_cairo_xml_indent (xml, 2);
_cairo_xml_emit_gradient (xml, &radial->base);
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</radial>");
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_write_func (void *closure, const unsigned char *data, unsigned len)
{
_cairo_output_stream_write (closure, data, len);
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_emit_image (cairo_xml_t *xml,
cairo_image_surface_t *image)
{
cairo_output_stream_t *stream;
cairo_status_t status;
 
_cairo_xml_printf_start (xml,
"<image width='%d' height='%d' format='%s'>",
image->width, image->height,
_format_to_string (image->format));
 
stream = _cairo_base64_stream_create (xml->stream);
status = cairo_surface_write_to_png_stream (&image->base,
_write_func, stream);
assert (status == CAIRO_STATUS_SUCCESS);
status = _cairo_output_stream_destroy (stream);
if (unlikely (status))
return status;
 
_cairo_xml_printf_end (xml, "</image>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_emit_surface (cairo_xml_t *xml,
const cairo_surface_pattern_t *pattern)
{
cairo_surface_t *source = pattern->surface;
cairo_status_t status;
 
if (_cairo_surface_is_recording (source)) {
status = cairo_xml_for_recording_surface (&xml->base, source);
} else {
cairo_image_surface_t *image;
void *image_extra;
 
status = _cairo_surface_acquire_source_image (source,
&image, &image_extra);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_image (xml, image);
 
_cairo_surface_release_source_image (source, image, image_extra);
}
 
return status;
}
 
static cairo_status_t
_cairo_xml_emit_pattern (cairo_xml_t *xml,
const char *source_or_mask,
const cairo_pattern_t *pattern)
{
cairo_status_t status;
 
_cairo_xml_printf (xml, "<%s-pattern>", source_or_mask);
_cairo_xml_indent (xml, 2);
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
status = _cairo_xml_emit_solid (xml, (cairo_solid_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
status = _cairo_xml_emit_linear (xml, (cairo_linear_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
status = _cairo_xml_emit_radial (xml, (cairo_radial_pattern_t *) pattern);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
status = _cairo_xml_emit_surface (xml, (cairo_surface_pattern_t *) pattern);
break;
default:
ASSERT_NOT_REACHED;
status = CAIRO_INT_STATUS_UNSUPPORTED;
break;
}
 
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) {
_cairo_xml_emit_matrix (xml, &pattern->matrix);
_cairo_xml_printf (xml,
"<extend>%s</extend>",
_extend_to_string (pattern->extend));
_cairo_xml_printf (xml,
"<filter>%s</filter>",
_filter_to_string (pattern->filter));
}
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</%s-pattern>", source_or_mask);
 
return status;
}
 
static cairo_int_status_t
_cairo_xml_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_xml_surface_t *surface = abstract_surface;
cairo_xml_t *xml = to_xml (surface);
cairo_status_t status;
 
_cairo_xml_printf (xml, "<paint>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
 
status = _cairo_xml_surface_emit_clip (surface, clip);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "source", source);
if (unlikely (status))
return status;
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</paint>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_xml_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_xml_surface_t *surface = abstract_surface;
cairo_xml_t *xml = to_xml (surface);
cairo_status_t status;
 
_cairo_xml_printf (xml, "<mask>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
 
status = _cairo_xml_surface_emit_clip (surface, clip);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "source", source);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "mask", mask);
if (unlikely (status))
return status;
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</mask>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_xml_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xml_surface_t *surface = abstract_surface;
cairo_xml_t *xml = to_xml (surface);
cairo_status_t status;
 
_cairo_xml_printf (xml, "<stroke>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
_cairo_xml_emit_double (xml, "line-width", style->line_width);
_cairo_xml_emit_double (xml, "miter-limit", style->miter_limit);
_cairo_xml_emit_string (xml, "line-cap", _line_cap_to_string (style->line_cap));
_cairo_xml_emit_string (xml, "line-join", _line_join_to_string (style->line_join));
 
status = _cairo_xml_surface_emit_clip (surface, clip);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "source", source);
if (unlikely (status))
return status;
 
if (style->num_dashes) {
unsigned int i;
 
_cairo_xml_printf_start (xml, "<dash offset='%f'>",
style->dash_offset);
for (i = 0; i < style->num_dashes; i++)
_cairo_xml_printf_continue (xml, "%f ", style->dash[i]);
 
_cairo_xml_printf_end (xml, "</dash>");
}
 
_cairo_xml_emit_path (xml, path);
_cairo_xml_emit_double (xml, "tolerance", tolerance);
_cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
 
_cairo_xml_emit_matrix (xml, ctm);
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</stroke>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
_cairo_xml_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t*path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_xml_surface_t *surface = abstract_surface;
cairo_xml_t *xml = to_xml (surface);
cairo_status_t status;
 
_cairo_xml_printf (xml, "<fill>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
 
status = _cairo_xml_surface_emit_clip (surface, clip);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "source", source);
if (unlikely (status))
return status;
 
_cairo_xml_emit_path (xml, path);
_cairo_xml_emit_double (xml, "tolerance", tolerance);
_cairo_xml_emit_string (xml, "antialias", _antialias_to_string (antialias));
_cairo_xml_emit_string (xml, "fill-rule", _fill_rule_to_string (fill_rule));
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</fill>");
 
return CAIRO_STATUS_SUCCESS;
}
 
#if CAIRO_HAS_FT_FONT
#include "cairo-ft-private.h"
static cairo_status_t
_cairo_xml_emit_type42_font (cairo_xml_t *xml,
cairo_scaled_font_t *scaled_font)
{
const cairo_scaled_font_backend_t *backend;
cairo_output_stream_t *base64_stream;
cairo_output_stream_t *zlib_stream;
cairo_status_t status, status2;
unsigned long size;
uint32_t len;
uint8_t *buf;
 
backend = scaled_font->backend;
if (backend->load_truetype_table == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
 
size = 0;
status = backend->load_truetype_table (scaled_font, 0, 0, NULL, &size);
if (unlikely (status))
return status;
 
buf = malloc (size);
if (unlikely (buf == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = backend->load_truetype_table (scaled_font, 0, 0, buf, &size);
if (unlikely (status)) {
free (buf);
return status;
}
 
_cairo_xml_printf_start (xml, "<font type='42' flags='%d' index='0'>",
_cairo_ft_scaled_font_get_load_flags (scaled_font));
 
 
base64_stream = _cairo_base64_stream_create (xml->stream);
len = size;
_cairo_output_stream_write (base64_stream, &len, sizeof (len));
 
zlib_stream = _cairo_deflate_stream_create (base64_stream);
 
_cairo_output_stream_write (zlib_stream, buf, size);
free (buf);
 
status2 = _cairo_output_stream_destroy (zlib_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
status2 = _cairo_output_stream_destroy (base64_stream);
if (status == CAIRO_STATUS_SUCCESS)
status = status2;
 
_cairo_xml_printf_end (xml, "</font>");
 
return status;
}
#else
static cairo_status_t
_cairo_xml_emit_type42_font (cairo_xml_t *xml,
cairo_scaled_font_t *scaled_font)
{
return CAIRO_INT_STATUS_UNSUPPORTED;
}
#endif
 
static cairo_status_t
_cairo_xml_emit_type3_font (cairo_xml_t *xml,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs)
{
_cairo_xml_printf_start (xml, "<font type='3'>");
_cairo_xml_printf_end (xml, "</font>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
_cairo_xml_emit_scaled_font (cairo_xml_t *xml,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs)
{
cairo_int_status_t status;
 
_cairo_xml_printf (xml, "<scaled-font>");
_cairo_xml_indent (xml, 2);
 
status = _cairo_xml_emit_type42_font (xml, scaled_font);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
status = _cairo_xml_emit_type3_font (xml, scaled_font,
glyphs, num_glyphs);
}
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "<scaled-font>");
 
return status;
}
 
static cairo_int_status_t
_cairo_xml_surface_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_xml_surface_t *surface = abstract_surface;
cairo_xml_t *xml = to_xml (surface);
cairo_status_t status;
int i;
 
_cairo_xml_printf (xml, "<glyphs>");
_cairo_xml_indent (xml, 2);
 
_cairo_xml_emit_string (xml, "operator", _operator_to_string (op));
 
status = _cairo_xml_surface_emit_clip (surface, clip);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_pattern (xml, "source", source);
if (unlikely (status))
return status;
 
status = _cairo_xml_emit_scaled_font (xml, scaled_font, glyphs, num_glyphs);
if (unlikely (status))
return status;
 
for (i = 0; i < num_glyphs; i++) {
_cairo_xml_printf (xml, "<glyph index='%lu'>%f %f</glyph>",
glyphs[i].index,
glyphs[i].x,
glyphs[i].y);
}
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</glyphs>");
 
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_surface_backend_t
_cairo_xml_surface_backend = {
CAIRO_SURFACE_TYPE_XML,
NULL,
 
_cairo_default_context_create,
 
_cairo_xml_surface_create_similar,
NULL, /* create_similar_image */
NULL, /* map_to_image */
NULL, /* unmap_image */
 
_cairo_surface_default_source,
NULL, /* acquire source image */
NULL, /* release source image */
NULL, /* snapshot */
 
NULL, /* copy page */
NULL, /* show page */
 
_cairo_xml_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
_cairo_xml_surface_paint,
_cairo_xml_surface_mask,
_cairo_xml_surface_stroke,
_cairo_xml_surface_fill,
NULL, /* fill_stroke */
_cairo_xml_surface_glyphs,
};
 
static cairo_surface_t *
_cairo_xml_surface_create_internal (cairo_device_t *device,
cairo_content_t content,
double width,
double height)
{
cairo_xml_surface_t *surface;
 
surface = malloc (sizeof (cairo_xml_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&_cairo_xml_surface_backend,
device,
content);
 
surface->width = width;
surface->height = height;
 
return &surface->base;
}
 
cairo_device_t *
cairo_xml_create (const char *filename)
{
cairo_output_stream_t *stream;
cairo_status_t status;
 
stream = _cairo_output_stream_create_for_filename (filename);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
 
return _cairo_xml_create_internal (stream);
}
 
cairo_device_t *
cairo_xml_create_for_stream (cairo_write_func_t write_func,
void *closure)
{
cairo_output_stream_t *stream;
cairo_status_t status;
 
stream = _cairo_output_stream_create (write_func, NULL, closure);
if ((status = _cairo_output_stream_get_status (stream)))
return _cairo_device_create_in_error (status);
 
return _cairo_xml_create_internal (stream);
}
 
cairo_surface_t *
cairo_xml_surface_create (cairo_device_t *device,
cairo_content_t content,
double width, double height)
{
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
if (unlikely (device->status))
return _cairo_surface_create_in_error (device->status);
 
return _cairo_xml_surface_create_internal (device, content, width, height);
}
 
cairo_status_t
cairo_xml_for_recording_surface (cairo_device_t *device,
cairo_surface_t *recording_surface)
{
cairo_box_t bbox;
cairo_rectangle_int_t extents;
cairo_surface_t *surface;
cairo_xml_t *xml;
cairo_status_t status;
 
if (unlikely (device->status))
return device->status;
 
if (unlikely (recording_surface->status))
return recording_surface->status;
 
if (unlikely (device->backend->type != CAIRO_DEVICE_TYPE_XML))
return _cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH);
 
if (unlikely (! _cairo_surface_is_recording (recording_surface)))
return _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
 
status = _cairo_recording_surface_get_bbox ((cairo_recording_surface_t *) recording_surface,
&bbox, NULL);
if (unlikely (status))
return status;
 
_cairo_box_round_to_rectangle (&bbox, &extents);
surface = _cairo_xml_surface_create_internal (device,
recording_surface->content,
extents.width,
extents.height);
if (unlikely (surface->status))
return surface->status;
 
xml = (cairo_xml_t *) device;
 
_cairo_xml_printf (xml,
"<surface content='%s' width='%d' height='%d'>",
_content_to_string (recording_surface->content),
extents.width, extents.height);
_cairo_xml_indent (xml, 2);
 
cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
status = _cairo_recording_surface_replay (recording_surface, surface);
cairo_surface_destroy (surface);
 
_cairo_xml_indent (xml, -2);
_cairo_xml_printf (xml, "</surface>");
 
return status;
}
slim_hidden_def (cairo_xml_for_recording_surface);
/programs/develop/libraries/cairo/src/cairo.c
3,6 → 3,7
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
34,15 → 35,21
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
#include "cairo-private.h"
 
#include "cairo-arc-private.h"
#include "cairo-backend-private.h"
#include "cairo-error-private.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
#include "cairo-surface-private.h"
#include "cairo-surface-backend-private.h"
 
#include <assert.h>
 
/**
* SECTION:cairo
* @Title: cairo_t
56,9 → 63,9
* draw shapes with cairo_stroke() or cairo_fill().
*
* #cairo_t<!-- -->'s can be pushed to a stack via cairo_save().
* They may then safely be changed, without loosing the current state.
* They may then safely be changed, without losing the current state.
* Use cairo_restore() to restore to the saved state.
*/
**/
 
/**
* SECTION:cairo-text
85,7 → 92,7
* the pangocairo that is part of the Pango text layout and rendering library.
* Pango is available from <ulink
* url="http://www.pango.org/">http://www.pango.org/</ulink>.
*/
**/
 
/**
* SECTION:cairo-transforms
98,84 → 105,58
* drawing instruments from the <firstterm>user space</firstterm> into the
* surface's canonical coordinate system, also known as the <firstterm>device
* space</firstterm>.
*/
**/
 
#define CAIRO_TOLERANCE_MINIMUM _cairo_fixed_to_double(1)
#define DEFINE_NIL_CONTEXT(status) \
{ \
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \
status, /* status */ \
{ 0, 0, 0, NULL }, /* user_data */ \
NULL \
}
 
#if !defined(INFINITY)
#define INFINITY HUGE_VAL
#endif
 
static const cairo_t _cairo_nil = {
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NO_MEMORY, /* status */
{ 0, 0, 0, NULL }, /* user_data */
NULL, /* gstate */
{{ 0 }, { 0 }}, /* gstate_tail */
NULL, /* gstate_freelist */
{{ /* path */
{ 0, 0 }, /* last_move_point */
{ 0, 0 }, /* current point */
FALSE, /* has_current_point */
FALSE, /* has_last_move_point */
FALSE, /* has_curve_to */
FALSE, /* is_box */
FALSE, /* maybe_fill_region */
TRUE, /* is_empty_fill */
{ {0, 0}, {0, 0}}, /* extents */
{{{NULL,NULL}}} /* link */
}}
static const cairo_t _cairo_nil[] = {
DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_MEMORY),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_RESTORE),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_POP_GROUP),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_NO_CURRENT_POINT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MATRIX),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STATUS),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_NULL_POINTER),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRING),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_PATH_DATA),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_READ_ERROR),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_WRITE_ERROR),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_FINISHED),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_SURFACE_TYPE_MISMATCH),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_PATTERN_TYPE_MISMATCH),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CONTENT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_FORMAT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_VISUAL),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_FILE_NOT_FOUND),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DASH),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_DSC_COMMENT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_INDEX),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_TEMP_FILE_ERROR),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_STRIDE),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_FONT_TYPE_MISMATCH),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_IMMUTABLE),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_ERROR),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_NEGATIVE_COUNT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_CLUSTERS),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SLANT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_WEIGHT),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_SIZE),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_TYPE_MISMATCH),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_ERROR),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_INVALID_MESH_CONSTRUCTION),
DEFINE_NIL_CONTEXT (CAIRO_STATUS_DEVICE_FINISHED)
};
COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1);
 
static const cairo_t _cairo_nil__null_pointer = {
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NULL_POINTER, /* status */
{ 0, 0, 0, NULL }, /* user_data */
NULL, /* gstate */
{{ 0 }, { 0 }}, /* gstate_tail */
NULL, /* gstate_freelist */
{{ /* path */
{ 0, 0 }, /* last_move_point */
{ 0, 0 }, /* current point */
FALSE, /* has_current_point */
FALSE, /* has_last_move_point */
FALSE, /* has_curve_to */
FALSE, /* is_box */
FALSE, /* maybe_fill_region */
TRUE, /* is_empty_fill */
{ {0, 0}, {0, 0}}, /* extents */
{{{NULL,NULL}}} /* link */
}}
};
#include <assert.h>
 
/**
* _cairo_error:
* @status: a status value indicating an error, (eg. not
* %CAIRO_STATUS_SUCCESS)
*
* Checks that status is an error status, but does nothing else.
*
* All assignments of an error status to any user-visible object
* within the cairo application should result in a call to
* _cairo_error().
*
* The purpose of this function is to allow the user to set a
* breakpoint in _cairo_error() to generate a stack trace for when the
* user causes cairo to detect an error.
*
* Return value: the error status.
**/
cairo_status_t
_cairo_error (cairo_status_t status)
{
CAIRO_ENSURE_UNIQUE;
assert (_cairo_status_is_error (status));
 
return status;
}
 
/**
* _cairo_set_error:
* @cr: a cairo context
* @status: a status value indicating an error
199,89 → 180,7
_cairo_status_set_error (&cr->status, _cairo_error (status));
}
 
/* We keep a small stash of contexts to reduce malloc pressure */
#define CAIRO_STASH_SIZE 4
#if CAIRO_NO_MUTEX
static struct {
cairo_t pool[CAIRO_STASH_SIZE];
int occupied;
} _context_stash;
 
static cairo_t *
_context_get (void)
{
int avail;
 
avail = ffs (~_context_stash.occupied) - 1;
if (avail >= CAIRO_STASH_SIZE)
return malloc (sizeof (cairo_t));
 
_context_stash.occupied |= 1 << avail;
return &_context_stash.pool[avail];
}
 
static void
_context_put (cairo_t *cr)
{
if (cr < &_context_stash.pool[0] ||
cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
{
free (cr);
return;
}
 
_context_stash.occupied &= ~(1 << (cr - &_context_stash.pool[0]));
}
#elif HAS_ATOMIC_OPS
static struct {
cairo_t pool[CAIRO_STASH_SIZE];
cairo_atomic_int_t occupied;
} _context_stash;
 
static cairo_t *
_context_get (void)
{
cairo_atomic_int_t avail, old, new;
 
do {
old = _cairo_atomic_int_get (&_context_stash.occupied);
avail = ffs (~old) - 1;
if (avail >= CAIRO_STASH_SIZE)
return malloc (sizeof (cairo_t));
 
new = old | (1 << avail);
} while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new));
 
return &_context_stash.pool[avail];
}
 
static void
_context_put (cairo_t *cr)
{
cairo_atomic_int_t old, new, avail;
 
if (cr < &_context_stash.pool[0] ||
cr >= &_context_stash.pool[CAIRO_STASH_SIZE])
{
free (cr);
return;
}
 
avail = ~(1 << (cr - &_context_stash.pool[0]));
do {
old = _cairo_atomic_int_get (&_context_stash.occupied);
new = old & avail;
} while (! _cairo_atomic_int_cmpxchg (&_context_stash.occupied, old, new));
}
#else
#define _context_get() malloc (sizeof (cairo_t))
#define _context_put(cr) free (cr)
#endif
 
/* XXX This should disappear in favour of a common pool of error objects. */
static cairo_t *_cairo_nil__objects[CAIRO_STATUS_LAST_STATUS + 1];
 
static cairo_t *
cairo_t *
_cairo_create_in_error (cairo_status_t status)
{
cairo_t *cr;
288,51 → 187,12
 
assert (status != CAIRO_STATUS_SUCCESS);
 
/* special case OOM in order to avoid another allocation */
switch ((int) status) {
case CAIRO_STATUS_NO_MEMORY:
return (cairo_t *) &_cairo_nil;
case CAIRO_STATUS_NULL_POINTER:
return (cairo_t *) &_cairo_nil__null_pointer;
}
cr = (cairo_t *) &_cairo_nil[status - CAIRO_STATUS_NO_MEMORY];
assert (status == cr->status);
 
CAIRO_MUTEX_LOCK (_cairo_error_mutex);
cr = _cairo_nil__objects[status];
if (cr == NULL) {
cr = malloc (sizeof (cairo_t));
if (unlikely (cr == NULL)) {
CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_t *) &_cairo_nil;
}
 
*cr = _cairo_nil;
cr->status = status;
_cairo_nil__objects[status] = cr;
}
CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
 
return cr;
}
 
void
_cairo_reset_static_data (void)
{
int status;
 
CAIRO_MUTEX_LOCK (_cairo_error_mutex);
for (status = CAIRO_STATUS_SUCCESS;
status <= CAIRO_STATUS_LAST_STATUS;
status++)
{
if (_cairo_nil__objects[status] != NULL) {
free (_cairo_nil__objects[status]);
_cairo_nil__objects[status] = NULL;
}
}
CAIRO_MUTEX_UNLOCK (_cairo_error_mutex);
}
 
/**
* cairo_create:
* @target: target surface for the context
341,7 → 201,8
* default values and with @target as a target surface. The target
* surface should be constructed with a backend-specific function such
* as cairo_image_surface_create() (or any other
* cairo_<emphasis>backend</emphasis>_surface_create() variant).
* <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>
* variant).
*
* This function references @target, so you can immediately
* call cairo_surface_destroy() on it if you don't need to
352,46 → 213,41
* with cairo_destroy() when you are done using the #cairo_t.
* This function never returns %NULL. If memory cannot be
* allocated, a special #cairo_t object will be returned on
* which cairo_status() returns %CAIRO_STATUS_NO_MEMORY.
* You can use this object normally, but no drawing will
* be done.
* which cairo_status() returns %CAIRO_STATUS_NO_MEMORY. If
* you attempt to target a surface which does not support
* writing (such as #cairo_mime_surface_t) then a
* %CAIRO_STATUS_WRITE_ERROR will be raised. You can use this
* object normally, but no drawing will be done.
*
* Since: 1.0
**/
cairo_t *
cairo_create (cairo_surface_t *target)
{
cairo_t *cr;
cairo_status_t status;
 
if (unlikely (target == NULL))
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NULL_POINTER));
if (unlikely (target->status))
return _cairo_create_in_error (target->status);
 
cr = _context_get ();
if (unlikely (cr == NULL))
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
if (target->backend->create_context == NULL)
return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_WRITE_ERROR));
 
return target->backend->create_context (target);
 
}
slim_hidden_def (cairo_create);
 
void
_cairo_init (cairo_t *cr,
const cairo_backend_t *backend)
{
CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1);
 
cr->status = CAIRO_STATUS_SUCCESS;
 
_cairo_user_data_array_init (&cr->user_data);
_cairo_path_fixed_init (cr->path);
 
cr->gstate = &cr->gstate_tail[0];
cr->gstate_freelist = &cr->gstate_tail[1];
cr->gstate_tail[1].next = NULL;
 
status = _cairo_gstate_init (cr->gstate, target);
if (unlikely (status)) {
_context_put (cr);
cr = _cairo_create_in_error (status);
cr->backend = backend;
}
 
return cr;
}
slim_hidden_def (cairo_create);
 
/**
* cairo_reference:
* @cr: a #cairo_t
404,6 → 260,8
* cairo_get_reference_count().
*
* Return value: the referenced #cairo_t.
*
* Since: 1.0
**/
cairo_t *
cairo_reference (cairo_t *cr)
418,6 → 276,12
return cr;
}
 
void
_cairo_fini (cairo_t *cr)
{
_cairo_user_data_array_fini (&cr->user_data);
}
 
/**
* cairo_destroy:
* @cr: a #cairo_t
425,6 → 289,8
* Decreases the reference count on @cr by one. If the result
* is zero, then @cr and all associated resources are freed.
* See cairo_reference().
*
* Since: 1.0
**/
void
cairo_destroy (cairo_t *cr)
437,28 → 303,8
if (! _cairo_reference_count_dec_and_test (&cr->ref_count))
return;
 
while (cr->gstate != &cr->gstate_tail[0]) {
if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
break;
cr->backend->destroy (cr);
}
 
_cairo_gstate_fini (cr->gstate);
cr->gstate_freelist = cr->gstate_freelist->next; /* skip over tail[1] */
while (cr->gstate_freelist != NULL) {
cairo_gstate_t *gstate = cr->gstate_freelist;
cr->gstate_freelist = gstate->next;
free (gstate);
}
 
_cairo_path_fixed_fini (cr->path);
 
_cairo_user_data_array_fini (&cr->user_data);
 
/* mark the context as invalid to protect against misuse */
cr->status = CAIRO_STATUS_NULL_POINTER;
 
_context_put (cr);
}
slim_hidden_def (cairo_destroy);
 
/**
479,8 → 325,7
cairo_get_user_data (cairo_t *cr,
const cairo_user_data_key_t *key)
{
return _cairo_user_data_array_get_data (&cr->user_data,
key);
return _cairo_user_data_array_get_data (&cr->user_data, key);
}
 
/**
549,6 → 394,8
* a #cairo_t is freed. If the reference count of a #cairo_t
* drops to zero in response to a call to cairo_destroy(),
* any saved states will be freed along with the #cairo_t.
*
* Since: 1.0
**/
void
cairo_save (cairo_t *cr)
558,7 → 405,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_save (&cr->gstate, &cr->gstate_freelist);
status = cr->backend->save (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
571,6 → 418,8
* Restores @cr to the state saved by a preceding call to
* cairo_save() and removes that state from the stack of
* saved states.
*
* Since: 1.0
**/
void
cairo_restore (cairo_t *cr)
580,7 → 429,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist);
status = cr->backend->restore (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
629,7 → 478,7
* </programlisting></informalexample>
*
* Since: 1.2
*/
**/
void
cairo_push_group (cairo_t *cr)
{
654,73 → 503,17
* detailed description of group rendering.
*
* Since: 1.2
*/
**/
void
cairo_push_group_with_content (cairo_t *cr, cairo_content_t content)
{
cairo_surface_t *group_surface;
cairo_clip_t *clip;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
clip = _cairo_gstate_get_clip (cr->gstate);
if (clip->all_clipped) {
group_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
status = group_surface->status;
status = cr->backend->push_group (cr, content);
if (unlikely (status))
goto bail;
} else {
cairo_surface_t *parent_surface;
const cairo_rectangle_int_t *clip_extents;
cairo_rectangle_int_t extents;
cairo_matrix_t matrix;
cairo_bool_t is_empty;
 
parent_surface = _cairo_gstate_get_target (cr->gstate);
 
/* Get the extents that we'll use in creating our new group surface */
is_empty = _cairo_surface_get_extents (parent_surface, &extents);
clip_extents = _cairo_clip_get_extents (_cairo_gstate_get_clip (cr->gstate));
if (clip_extents != NULL)
is_empty = _cairo_rectangle_intersect (&extents, clip_extents);
 
group_surface = _cairo_surface_create_similar_solid (parent_surface,
content,
extents.width,
extents.height,
CAIRO_COLOR_TRANSPARENT,
TRUE);
status = group_surface->status;
if (unlikely (status))
goto bail;
 
/* Set device offsets on the new surface so that logically it appears at
* the same location on the parent surface -- when we pop_group this,
* the source pattern will get fixed up for the appropriate target surface
* device offsets, so we want to set our own surface offsets from /that/,
* and not from the device origin. */
cairo_surface_set_device_offset (group_surface,
parent_surface->device_transform.x0 - extents.x,
parent_surface->device_transform.y0 - extents.y);
 
/* If we have a current path, we need to adjust it to compensate for
* the device offset just applied. */
cairo_matrix_init_translate (&matrix, -extents.x, -extents.y);
_cairo_path_fixed_transform (cr->path, &matrix);
}
 
/* create a new gstate for the redirect */
cairo_save (cr);
if (unlikely (cr->status))
goto bail;
 
status = _cairo_gstate_redirect_target (cr->gstate, group_surface);
 
bail:
cairo_surface_destroy (group_surface);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def(cairo_push_group_with_content);
749,65 → 542,15
cairo_pattern_t *
cairo_pop_group (cairo_t *cr)
{
cairo_surface_t *group_surface, *parent_target;
cairo_pattern_t *group_pattern;
cairo_matrix_t group_matrix, device_transform_matrix;
cairo_status_t status;
 
if (unlikely (cr->status))
return _cairo_pattern_create_in_error (cr->status);
 
/* Grab the active surfaces */
group_surface = _cairo_gstate_get_target (cr->gstate);
parent_target = _cairo_gstate_get_parent_target (cr->gstate);
group_pattern = cr->backend->pop_group (cr);
if (unlikely (group_pattern->status))
_cairo_set_error (cr, group_pattern->status);
 
/* Verify that we are at the right nesting level */
if (parent_target == NULL) {
_cairo_set_error (cr, CAIRO_STATUS_INVALID_POP_GROUP);
return _cairo_pattern_create_in_error (CAIRO_STATUS_INVALID_POP_GROUP);
}
 
/* We need to save group_surface before we restore; we don't need
* to reference parent_target and original_target, since the
* gstate will still hold refs to them once we restore. */
group_surface = cairo_surface_reference (group_surface);
 
cairo_restore (cr);
 
if (unlikely (cr->status)) {
group_pattern = _cairo_pattern_create_in_error (cr->status);
goto done;
}
 
group_pattern = cairo_pattern_create_for_surface (group_surface);
status = group_pattern->status;
if (unlikely (status)) {
_cairo_set_error (cr, status);
goto done;
}
 
_cairo_gstate_get_matrix (cr->gstate, &group_matrix);
/* Transform by group_matrix centered around device_transform so that when
* we call _cairo_gstate_copy_transformed_pattern the result is a pattern
* with a matrix equivalent to the device_transform of group_surface. */
if (_cairo_surface_has_device_transform (group_surface)) {
cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
_cairo_pattern_transform (group_pattern, &group_matrix);
_cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
} else {
cairo_pattern_set_matrix (group_pattern, &group_matrix);
}
 
/* If we have a current path, we need to adjust it to compensate for
* the device offset just removed. */
cairo_matrix_multiply (&device_transform_matrix,
&_cairo_gstate_get_target (cr->gstate)->device_transform,
&group_surface->device_transform_inverse);
_cairo_path_fixed_transform (cr->path, &device_transform_matrix);
 
done:
cairo_surface_destroy (group_surface);
 
return group_pattern;
}
slim_hidden_def(cairo_pop_group);
824,7 → 567,7
* operations:
*
* <informalexample><programlisting>
* #cairo_pattern_t *group = cairo_pop_group (cr);
* cairo_pattern_t *group = cairo_pop_group (cr);
* cairo_set_source (cr, group);
* cairo_pattern_destroy (group);
* </programlisting></informalexample>
859,6 → 602,8
* each available compositing operator.
*
* The default operator is %CAIRO_OPERATOR_OVER.
*
* Since: 1.0
**/
void
cairo_set_operator (cairo_t *cr, cairo_operator_t op)
868,7 → 613,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_operator (cr->gstate, op);
status = cr->backend->set_operator (cr, op);
if (unlikely (status))
_cairo_set_error (cr, status);
}
875,31 → 620,36
slim_hidden_def (cairo_set_operator);
 
 
static cairo_bool_t
_current_source_matches_solid (cairo_t *cr,
double red,
double green,
double blue,
double alpha)
#if 0
/**
* cairo_set_opacity:
* @cr: a #cairo_t
* @opacity: the level of opacity to use when compositing
*
* Sets the compositing opacity to be used for all drawing
* operations. The effect is to fade out the operations
* using the alpha value.
*
* The default opacity is 1.
*
* Since: TBD
**/
void
cairo_set_opacity (cairo_t *cr, double opacity)
{
const cairo_pattern_t *current;
cairo_color_t color;
cairo_status_t status;
 
current = cr->gstate->source;
if (current->type != CAIRO_PATTERN_TYPE_SOLID)
return FALSE;
if (unlikely (cr->status))
return;
 
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
status = cr->backend->set_opacity (cr, opacity);
if (unlikely (status))
_cairo_set_error (cr, status);
}
#endif
 
_cairo_color_init_rgba (&color, red, green, blue, alpha);
return _cairo_color_equal (&color,
&((cairo_solid_pattern_t *) current)->color);
}
/**
* cairo_set_source_rgb
* cairo_set_source_rgb:
* @cr: a cairo context
* @red: red component of color
* @green: green component of color
915,24 → 665,20
*
* The default source pattern is opaque black, (that is, it is
* equivalent to cairo_set_source_rgb(cr, 0.0, 0.0, 0.0)).
*
* Since: 1.0
**/
void
cairo_set_source_rgb (cairo_t *cr, double red, double green, double blue)
{
cairo_pattern_t *pattern;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
if (_current_source_matches_solid (cr, red, green, blue, 1.))
return;
 
/* push the current pattern to the freed lists */
cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
 
pattern = cairo_pattern_create_rgb (red, green, blue);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
status = cr->backend->set_source_rgba (cr, red, green, blue, 1.);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def (cairo_set_source_rgb);
 
954,6 → 700,8
*
* The default source pattern is opaque black, (that is, it is
* equivalent to cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0)).
*
* Since: 1.0
**/
void
cairo_set_source_rgba (cairo_t *cr,
960,20 → 708,14
double red, double green, double blue,
double alpha)
{
cairo_pattern_t *pattern;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
if (_current_source_matches_solid (cr, red, green, blue, alpha))
return;
 
/* push the current pattern to the freed lists */
cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
 
pattern = cairo_pattern_create_rgba (red, green, blue, alpha);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
status = cr->backend->set_source_rgba (cr, red, green, blue, alpha);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
998,6 → 740,8
* The resulting pattern can be queried with cairo_get_source() so
* that these attributes can be modified if desired, (eg. to create a
* repeating pattern with cairo_pattern_set_extend()).
*
* Since: 1.0
**/
void
cairo_set_source_surface (cairo_t *cr,
1005,27 → 749,24
double x,
double y)
{
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
/* push the current pattern to the freed lists */
cairo_set_source (cr, (cairo_pattern_t *) &_cairo_pattern_black);
if (unlikely (surface == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
pattern = cairo_pattern_create_for_surface (surface);
 
cairo_matrix_init_translate (&matrix, -x, -y);
cairo_pattern_set_matrix (pattern, &matrix);
 
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
status = cr->backend->set_source_surface (cr, surface, x, y);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def (cairo_set_source_surface);
 
/**
* cairo_set_source
* cairo_set_source:
* @cr: a cairo context
* @source: a #cairo_pattern_t to be used as the source for
* subsequent drawing operations.
1042,6 → 783,8
* The default source pattern is a solid pattern that is opaque black,
* (that is, it is equivalent to cairo_set_source_rgb(cr, 0.0, 0.0,
* 0.0)).
*
* Since: 1.0
**/
void
cairo_set_source (cairo_t *cr, cairo_pattern_t *source)
1051,17 → 794,17
if (unlikely (cr->status))
return;
 
if (source == NULL) {
if (unlikely (source == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
if (source->status) {
if (unlikely (source->status)) {
_cairo_set_error (cr, source->status);
return;
}
 
status = _cairo_gstate_set_source (cr->gstate, source);
status = cr->backend->set_source (cr, source);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1076,6 → 819,8
* Return value: the current source pattern. This object is owned by
* cairo. To keep a reference to it, you must call
* cairo_pattern_reference().
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_get_source (cairo_t *cr)
1083,7 → 828,7
if (unlikely (cr->status))
return _cairo_pattern_create_in_error (cr->status);
 
return _cairo_gstate_get_source (cr->gstate);
return cr->backend->get_source (cr);
}
 
/**
1101,6 → 846,8
* within Cairo is limited by the precision of its internal arithmetic, and
* the prescribed @tolerance is restricted to the smallest
* representable internal value.
*
* Since: 1.0
**/
void
cairo_set_tolerance (cairo_t *cr, double tolerance)
1110,10 → 857,7
if (unlikely (cr->status))
return;
 
if (tolerance < CAIRO_TOLERANCE_MINIMUM)
tolerance = CAIRO_TOLERANCE_MINIMUM;
 
status = _cairo_gstate_set_tolerance (cr->gstate, tolerance);
status = cr->backend->set_tolerance (cr, tolerance);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1131,6 → 875,8
*
* Note that this option does not affect text rendering, instead see
* cairo_font_options_set_antialias().
*
* Since: 1.0
**/
void
cairo_set_antialias (cairo_t *cr, cairo_antialias_t antialias)
1140,7 → 886,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_antialias (cr->gstate, antialias);
status = cr->backend->set_antialias (cr, antialias);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1157,6 → 903,8
* on the semantics of each available fill rule.
*
* The default fill rule is %CAIRO_FILL_RULE_WINDING.
*
* Since: 1.0
**/
void
cairo_set_fill_rule (cairo_t *cr, cairo_fill_rule_t fill_rule)
1166,7 → 914,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_fill_rule (cr->gstate, fill_rule);
status = cr->backend->set_fill_rule (cr, fill_rule);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1196,6 → 944,8
* construction.
*
* The default line width value is 2.0.
*
* Since: 1.0
**/
void
cairo_set_line_width (cairo_t *cr, double width)
1208,7 → 958,7
if (width < 0.)
width = 0.;
 
status = _cairo_gstate_set_line_width (cr->gstate, width);
status = cr->backend->set_line_width (cr, width);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1229,6 → 979,8
* construction.
*
* The default line cap style is %CAIRO_LINE_CAP_BUTT.
*
* Since: 1.0
**/
void
cairo_set_line_cap (cairo_t *cr, cairo_line_cap_t line_cap)
1238,7 → 990,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_line_cap (cr->gstate, line_cap);
status = cr->backend->set_line_cap (cr, line_cap);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1259,6 → 1011,8
* construction.
*
* The default line join style is %CAIRO_LINE_JOIN_MITER.
*
* Since: 1.0
**/
void
cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
1268,7 → 1022,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_line_join (cr->gstate, line_join);
status = cr->backend->set_line_join (cr, line_join);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1305,6 → 1059,8
* If any value in @dashes is negative, or if all values are 0, then
* @cr will be put into an error state with a status of
* %CAIRO_STATUS_INVALID_DASH.
*
* Since: 1.0
**/
void
cairo_set_dash (cairo_t *cr,
1317,8 → 1073,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_dash (cr->gstate,
dashes, num_dashes, offset);
status = cr->backend->set_dash (cr, dashes, num_dashes, offset);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1335,7 → 1090,7
* Return value: the length of the dash array, or 0 if no dash array set.
*
* Since: 1.4
*/
**/
int
cairo_get_dash_count (cairo_t *cr)
{
1344,7 → 1099,7
if (unlikely (cr->status))
return 0;
 
_cairo_gstate_get_dash (cr->gstate, NULL, &num_dashes, NULL);
cr->backend->get_dash (cr, NULL, &num_dashes, NULL);
 
return num_dashes;
}
1369,7 → 1124,7
if (unlikely (cr->status))
return;
 
_cairo_gstate_get_dash (cr->gstate, dashes, NULL, offset);
cr->backend->get_dash (cr, dashes, NULL, offset);
}
 
/**
1399,6 → 1154,8
*
* A miter limit for a desired angle can be computed as: miter limit =
* 1/sin(angle/2)
*
* Since: 1.0
**/
void
cairo_set_miter_limit (cairo_t *cr, double limit)
1408,7 → 1165,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_miter_limit (cr->gstate, limit);
status = cr->backend->set_miter_limit (cr, limit);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1424,6 → 1181,8
* user-space coordinate according to the CTM in place before the new
* call to cairo_translate(). In other words, the translation of the
* user-space origin takes place after any existing transformation.
*
* Since: 1.0
**/
void
cairo_translate (cairo_t *cr, double tx, double ty)
1433,7 → 1192,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_translate (cr->gstate, tx, ty);
status = cr->backend->translate (cr, tx, ty);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1449,6 → 1208,8
* and Y user-space axes by @sx and @sy respectively. The scaling of
* the axes takes place after any existing transformation of user
* space.
*
* Since: 1.0
**/
void
cairo_scale (cairo_t *cr, double sx, double sy)
1458,7 → 1219,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_scale (cr->gstate, sx, sy);
status = cr->backend->scale (cr, sx, sy);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1475,6 → 1236,8
* places after any existing transformation of user space. The
* rotation direction for positive angles is from the positive X axis
* toward the positive Y axis.
*
* Since: 1.0
**/
void
cairo_rotate (cairo_t *cr, double angle)
1484,7 → 1247,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_rotate (cr->gstate, angle);
status = cr->backend->rotate (cr, angle);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1497,6 → 1260,8
* Modifies the current transformation matrix (CTM) by applying
* @matrix as an additional transformation. The new transformation of
* user space takes place after any existing transformation.
*
* Since: 1.0
**/
void
cairo_transform (cairo_t *cr,
1507,7 → 1272,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_transform (cr->gstate, matrix);
status = cr->backend->transform (cr, matrix);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1520,6 → 1285,8
*
* Modifies the current transformation matrix (CTM) by setting it
* equal to @matrix.
*
* Since: 1.0
**/
void
cairo_set_matrix (cairo_t *cr,
1530,7 → 1297,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_matrix (cr->gstate, matrix);
status = cr->backend->set_matrix (cr, matrix);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1544,14 → 1311,20
* to the identity matrix. That is, the user-space and device-space
* axes will be aligned and one user-space unit will transform to one
* device-space unit.
*
* Since: 1.0
**/
void
cairo_identity_matrix (cairo_t *cr)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_identity_matrix (cr->gstate);
status = cr->backend->set_identity_matrix (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
1563,6 → 1336,8
* Transform a coordinate from user space to device space by
* multiplying the given point by the current transformation matrix
* (CTM).
*
* Since: 1.0
**/
void
cairo_user_to_device (cairo_t *cr, double *x, double *y)
1570,7 → 1345,7
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_device (cr->gstate, x, y);
cr->backend->user_to_device (cr, x, y);
}
slim_hidden_def (cairo_user_to_device);
 
1584,6 → 1359,8
* function is similar to cairo_user_to_device() except that the
* translation components of the CTM will be ignored when transforming
* (@dx,@dy).
*
* Since: 1.0
**/
void
cairo_user_to_device_distance (cairo_t *cr, double *dx, double *dy)
1591,7 → 1368,7
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_device_distance (cr->gstate, dx, dy);
cr->backend->user_to_device_distance (cr, dx, dy);
}
slim_hidden_def (cairo_user_to_device_distance);
 
1604,6 → 1381,8
* Transform a coordinate from device space to user space by
* multiplying the given point by the inverse of the current
* transformation matrix (CTM).
*
* Since: 1.0
**/
void
cairo_device_to_user (cairo_t *cr, double *x, double *y)
1611,8 → 1390,9
if (unlikely (cr->status))
return;
 
_cairo_gstate_device_to_user (cr->gstate, x, y);
cr->backend->device_to_user (cr, x, y);
}
slim_hidden_def (cairo_device_to_user);
 
/**
* cairo_device_to_user_distance:
1624,6 → 1404,8
* function is similar to cairo_device_to_user() except that the
* translation components of the inverse CTM will be ignored when
* transforming (@dx,@dy).
*
* Since: 1.0
**/
void
cairo_device_to_user_distance (cairo_t *cr, double *dx, double *dy)
1631,7 → 1413,7
if (unlikely (cr->status))
return;
 
_cairo_gstate_device_to_user_distance (cr->gstate, dx, dy);
cr->backend->device_to_user_distance (cr, dx, dy);
}
 
/**
1640,45 → 1422,22
*
* Clears the current path. After this call there will be no path and
* no current point.
*
* Since: 1.0
**/
void
cairo_new_path (cairo_t *cr)
{
if (unlikely (cr->status))
return;
 
_cairo_path_fixed_fini (cr->path);
_cairo_path_fixed_init (cr->path);
}
slim_hidden_def(cairo_new_path);
 
/**
* cairo_move_to:
* @cr: a cairo context
* @x: the X coordinate of the new position
* @y: the Y coordinate of the new position
*
* Begin a new sub-path. After this call the current point will be (@x,
* @y).
**/
void
cairo_move_to (cairo_t *cr, double x, double y)
{
cairo_status_t status;
cairo_fixed_t x_fixed, y_fixed;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
status = _cairo_path_fixed_move_to (cr->path, x_fixed, y_fixed);
status = cr->backend->new_path (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def(cairo_move_to);
slim_hidden_def(cairo_new_path);
 
/**
* cairo_new_sub_path:
1701,13 → 1460,43
void
cairo_new_sub_path (cairo_t *cr)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
_cairo_path_fixed_new_sub_path (cr->path);
status = cr->backend->new_sub_path (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
* cairo_move_to:
* @cr: a cairo context
* @x: the X coordinate of the new position
* @y: the Y coordinate of the new position
*
* Begin a new sub-path. After this call the current point will be (@x,
* @y).
*
* Since: 1.0
**/
void
cairo_move_to (cairo_t *cr, double x, double y)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
status = cr->backend->move_to (cr, x, y);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def(cairo_move_to);
 
 
/**
* cairo_line_to:
* @cr: a cairo context
* @x: the X coordinate of the end of the new line
1719,21 → 1508,18
*
* If there is no current point before the call to cairo_line_to()
* this function will behave as cairo_move_to(@cr, @x, @y).
*
* Since: 1.0
**/
void
cairo_line_to (cairo_t *cr, double x, double y)
{
cairo_status_t status;
cairo_fixed_t x_fixed, y_fixed;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
 
status = _cairo_path_fixed_line_to (cr->path, x_fixed, y_fixed);
status = cr->backend->line_to (cr, x, y);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1757,6 → 1543,8
* If there is no current point before the call to cairo_curve_to()
* this function will behave as if preceded by a call to
* cairo_move_to(@cr, @x1, @y1).
*
* Since: 1.0
**/
void
cairo_curve_to (cairo_t *cr,
1765,30 → 1553,14
double x3, double y3)
{
cairo_status_t status;
cairo_fixed_t x1_fixed, y1_fixed;
cairo_fixed_t x2_fixed, y2_fixed;
cairo_fixed_t x3_fixed, y3_fixed;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
_cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
_cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
 
x1_fixed = _cairo_fixed_from_double (x1);
y1_fixed = _cairo_fixed_from_double (y1);
 
x2_fixed = _cairo_fixed_from_double (x2);
y2_fixed = _cairo_fixed_from_double (y2);
 
x3_fixed = _cairo_fixed_from_double (x3);
y3_fixed = _cairo_fixed_from_double (y3);
 
status = _cairo_path_fixed_curve_to (cr->path,
x1_fixed, y1_fixed,
x2_fixed, y2_fixed,
x3_fixed, y3_fixed);
status = cr->backend->curve_to (cr,
x1, y1,
x2, y2,
x3, y3);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1806,8 → 1578,8
* Adds a circular arc of the given @radius to the current path. The
* arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
* the direction of increasing angles to end at @angle2. If @angle2 is
* less than @angle1 it will be progressively increased by 2*M_PI
* until it is greater than @angle1.
* less than @angle1 it will be progressively increased by
* <literal>2*M_PI</literal> until it is greater than @angle1.
*
* If there is a current point, an initial line segment will be added
* to the path to connect the current point to the beginning of the
1815,11 → 1587,12
* calling cairo_new_sub_path() before calling cairo_arc().
*
* Angles are measured in radians. An angle of 0.0 is in the direction
* of the positive X axis (in user space). An angle of %M_PI/2.0 radians
* (90 degrees) is in the direction of the positive Y axis (in
* user space). Angles increase in the direction from the positive X
* axis toward the positive Y axis. So with the default transformation
* matrix, angles increase in a clockwise direction.
* of the positive X axis (in user space). An angle of
* <literal>M_PI/2.0</literal> radians (90 degrees) is in the
* direction of the positive Y axis (in user space). Angles increase
* in the direction from the positive X axis toward the positive Y
* axis. So with the default transformation matrix, angles increase in
* a clockwise direction.
*
* (To convert from degrees to radians, use <literal>degrees * (M_PI /
* 180.)</literal>.)
1840,6 → 1613,8
* cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI);
* cairo_restore (cr);
* </programlisting></informalexample>
*
* Since: 1.0
**/
void
cairo_arc (cairo_t *cr,
1847,25 → 1622,23
double radius,
double angle1, double angle2)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0) {
cairo_line_to (cr, xc, yc); /* might become a move_to */
cairo_line_to (cr, xc, yc);
return;
if (angle2 < angle1) {
/* increase angle2 by multiples of full circle until it
* satisfies angle2 >= angle1 */
angle2 = fmod (angle2 - angle1, 2 * M_PI);
if (angle2 < 0)
angle2 += 2 * M_PI;
angle2 += angle1;
}
 
while (angle2 < angle1)
angle2 += 2 * M_PI;
 
cairo_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
 
_cairo_arc_path (cr, xc, yc, radius,
angle1, angle2);
status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, TRUE);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
1880,11 → 1653,13
* Adds a circular arc of the given @radius to the current path. The
* arc is centered at (@xc, @yc), begins at @angle1 and proceeds in
* the direction of decreasing angles to end at @angle2. If @angle2 is
* greater than @angle1 it will be progressively decreased by 2*M_PI
* until it is less than @angle1.
* greater than @angle1 it will be progressively decreased by
* <literal>2*M_PI</literal> until it is less than @angle1.
*
* See cairo_arc() for more details. This function differs only in the
* direction of the arc between the two angles.
*
* Since: 1.0
**/
void
cairo_arc_negative (cairo_t *cr,
1892,22 → 1667,23
double radius,
double angle1, double angle2)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0)
return;
 
while (angle2 > angle1)
if (angle2 > angle1) {
/* decrease angle2 by multiples of full circle until it
* satisfies angle2 <= angle1 */
angle2 = fmod (angle2 - angle1, 2 * M_PI);
if (angle2 > 0)
angle2 -= 2 * M_PI;
angle2 += angle1;
}
 
cairo_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
 
_cairo_arc_path_negative (cr, xc, yc, radius,
angle1, angle2);
status = cr->backend->arc (cr, xc, yc, radius, angle1, angle2, FALSE);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/* XXX: NYI
1922,13 → 1698,26
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_arc_to (cr->gstate,
x1, y1,
x2, y2,
radius);
status = cr->backend->arc_to (cr, x1, y1, x2, y2, radius);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
void
cairo_rel_arc_to (cairo_t *cr,
double dx1, double dy1,
double dx2, double dy2,
double radius)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
status = cr->backend->rel_arc_to (cr, dx1, dy1, dx2, dy2, radius);
if (unlikely (status))
_cairo_set_error (cr, status);
}
*/
 
/**
1946,22 → 1735,18
* It is an error to call this function with no current point. Doing
* so will cause @cr to shutdown with a status of
* %CAIRO_STATUS_NO_CURRENT_POINT.
*
* Since: 1.0
**/
void
cairo_rel_move_to (cairo_t *cr, double dx, double dy)
{
cairo_fixed_t dx_fixed, dy_fixed;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
status = _cairo_path_fixed_rel_move_to (cr->path, dx_fixed, dy_fixed);
status = cr->backend->rel_move_to (cr, dx, dy);
if (unlikely (status))
_cairo_set_error (cr, status);
}
1983,22 → 1768,18
* It is an error to call this function with no current point. Doing
* so will cause @cr to shutdown with a status of
* %CAIRO_STATUS_NO_CURRENT_POINT.
*
* Since: 1.0
**/
void
cairo_rel_line_to (cairo_t *cr, double dx, double dy)
{
cairo_fixed_t dx_fixed, dy_fixed;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
 
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
 
status = _cairo_path_fixed_rel_line_to (cr->path, dx_fixed, dy_fixed);
status = cr->backend->rel_line_to (cr, dx, dy);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2028,6 → 1809,8
* It is an error to call this function with no current point. Doing
* so will cause @cr to shutdown with a status of
* %CAIRO_STATUS_NO_CURRENT_POINT.
*
* Since: 1.0
**/
void
cairo_rel_curve_to (cairo_t *cr,
2035,31 → 1818,15
double dx2, double dy2,
double dx3, double dy3)
{
cairo_fixed_t dx1_fixed, dy1_fixed;
cairo_fixed_t dx2_fixed, dy2_fixed;
cairo_fixed_t dx3_fixed, dy3_fixed;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
_cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3);
 
dx1_fixed = _cairo_fixed_from_double (dx1);
dy1_fixed = _cairo_fixed_from_double (dy1);
 
dx2_fixed = _cairo_fixed_from_double (dx2);
dy2_fixed = _cairo_fixed_from_double (dy2);
 
dx3_fixed = _cairo_fixed_from_double (dx3);
dy3_fixed = _cairo_fixed_from_double (dy3);
 
status = _cairo_path_fixed_rel_curve_to (cr->path,
dx1_fixed, dy1_fixed,
dx2_fixed, dy2_fixed,
dx3_fixed, dy3_fixed);
status = cr->backend->rel_curve_to (cr,
dx1, dy1,
dx2, dy2,
dx3, dy3);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2083,6 → 1850,8
* cairo_rel_line_to (cr, -width, 0);
* cairo_close_path (cr);
* </programlisting></informalexample>
*
* Since: 1.0
**/
void
cairo_rectangle (cairo_t *cr,
2089,14 → 1858,14
double x, double y,
double width, double height)
{
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
cairo_move_to (cr, x, y);
cairo_rel_line_to (cr, width, 0);
cairo_rel_line_to (cr, 0, height);
cairo_rel_line_to (cr, -width, 0);
cairo_close_path (cr);
status = cr->backend->rectangle (cr, x, y, width, height);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
#if 0
2142,6 → 1911,8
* not be necessary to save the "last move_to point" during processing
* as the MOVE_TO immediately after the CLOSE_PATH will provide that
* point.
*
* Since: 1.0
**/
void
cairo_close_path (cairo_t *cr)
2151,7 → 1922,7
if (unlikely (cr->status))
return;
 
status = _cairo_path_fixed_close_path (cr->path);
status = cr->backend->close_path (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2205,9 → 1976,7
return;
}
 
_cairo_gstate_path_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
cr->backend->path_extents (cr, x1, y1, x2, y2);
}
 
/**
2216,6 → 1985,8
*
* A drawing operator that paints the current source everywhere within
* the current clip region.
*
* Since: 1.0
**/
void
cairo_paint (cairo_t *cr)
2225,7 → 1996,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_paint (cr->gstate);
status = cr->backend->paint (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2240,6 → 2011,8
* the current clip region using a mask of constant alpha value
* @alpha. The effect is similar to cairo_paint(), but the drawing
* is faded out using the alpha value.
*
* Since: 1.0
**/
void
cairo_paint_with_alpha (cairo_t *cr,
2246,30 → 2019,13
double alpha)
{
cairo_status_t status;
cairo_color_t color;
cairo_solid_pattern_t pattern;
 
if (unlikely (cr->status))
return;
 
if (CAIRO_ALPHA_IS_OPAQUE (alpha)) {
cairo_paint (cr);
return;
}
 
if (CAIRO_ALPHA_IS_ZERO (alpha) &&
_cairo_operator_bounded_by_mask (cr->gstate->op)) {
return;
}
 
_cairo_color_init_rgba (&color, 0., 0., 0., alpha);
_cairo_pattern_init_solid (&pattern, &color);
 
status = _cairo_gstate_mask (cr->gstate, &pattern.base);
status = cr->backend->paint_with_alpha (cr, alpha);
if (unlikely (status))
_cairo_set_error (cr, status);
 
_cairo_pattern_fini (&pattern.base);
}
 
/**
2281,7 → 2037,9
* using the alpha channel of @pattern as a mask. (Opaque
* areas of @pattern are painted with the source, transparent
* areas are not painted.)
*/
*
* Since: 1.0
**/
void
cairo_mask (cairo_t *cr,
cairo_pattern_t *pattern)
2291,17 → 2049,17
if (unlikely (cr->status))
return;
 
if (pattern == NULL) {
if (unlikely (pattern == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
if (pattern->status) {
if (unlikely (pattern->status)) {
_cairo_set_error (cr, pattern->status);
return;
}
 
status = _cairo_gstate_mask (cr->gstate, pattern);
status = cr->backend->mask (cr, pattern);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2318,7 → 2076,9
* using the alpha channel of @surface as a mask. (Opaque
* areas of @surface are painted with the source, transparent
* areas are not painted.)
*/
*
* Since: 1.0
**/
void
cairo_mask_surface (cairo_t *cr,
cairo_surface_t *surface,
2372,13 → 2132,20
*
* In no case will a cap style of %CAIRO_LINE_CAP_BUTT cause anything
* to be drawn in the case of either degenerate segments or sub-paths.
*
* Since: 1.0
**/
void
cairo_stroke (cairo_t *cr)
{
cairo_stroke_preserve (cr);
cairo_status_t status;
 
cairo_new_path (cr);
if (unlikely (cr->status))
return;
 
status = cr->backend->stroke (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def(cairo_stroke);
 
2394,6 → 2161,8
* See cairo_set_line_width(), cairo_set_line_join(),
* cairo_set_line_cap(), cairo_set_dash(), and
* cairo_stroke_preserve().
*
* Since: 1.0
**/
void
cairo_stroke_preserve (cairo_t *cr)
2403,7 → 2172,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_stroke (cr->gstate, cr->path);
status = cr->backend->stroke_preserve (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2418,13 → 2187,20
* filled). After cairo_fill(), the current path will be cleared from
* the cairo context. See cairo_set_fill_rule() and
* cairo_fill_preserve().
*
* Since: 1.0
**/
void
cairo_fill (cairo_t *cr)
{
cairo_fill_preserve (cr);
cairo_status_t status;
 
cairo_new_path (cr);
if (unlikely (cr->status))
return;
 
status = cr->backend->fill (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
2437,6 → 2213,8
* path within the cairo context.
*
* See cairo_set_fill_rule() and cairo_fill().
*
* Since: 1.0
**/
void
cairo_fill_preserve (cairo_t *cr)
2446,7 → 2224,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_fill (cr->gstate, cr->path);
status = cr->backend->fill_preserve (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2463,6 → 2241,8
*
* This is a convenience function that simply calls
* cairo_surface_copy_page() on @cr's target.
*
* Since: 1.0
**/
void
cairo_copy_page (cairo_t *cr)
2472,7 → 2252,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_copy_page (cr->gstate);
status = cr->backend->copy_page (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2486,6 → 2266,8
*
* This is a convenience function that simply calls
* cairo_surface_show_page() on @cr's target.
*
* Since: 1.0
**/
void
cairo_show_page (cairo_t *cr)
2495,7 → 2277,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_show_page (cr->gstate);
status = cr->backend->show_page (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2517,6 → 2299,8
*
* Return value: A non-zero value if the point is inside, or zero if
* outside.
*
* Since: 1.0
**/
cairo_bool_t
cairo_in_stroke (cairo_t *cr, double x, double y)
2527,9 → 2311,7
if (unlikely (cr->status))
return FALSE;
 
status = _cairo_gstate_in_stroke (cr->gstate,
cr->path,
x, y, &inside);
status = cr->backend->in_stroke (cr, x, y, &inside);
if (unlikely (status))
_cairo_set_error (cr, status);
 
2551,14 → 2333,23
*
* Return value: A non-zero value if the point is inside, or zero if
* outside.
*
* Since: 1.0
**/
cairo_bool_t
cairo_in_fill (cairo_t *cr, double x, double y)
{
cairo_status_t status;
cairo_bool_t inside = FALSE;
 
if (unlikely (cr->status))
return FALSE;
 
return _cairo_gstate_in_fill (cr->gstate, cr->path, x, y);
status = cr->backend->in_fill (cr, x, y, &inside);
if (unlikely (status))
_cairo_set_error (cr, status);
 
return inside;
}
 
/**
2588,6 → 2379,8
* See cairo_stroke(), cairo_set_line_width(), cairo_set_line_join(),
* cairo_set_line_cap(), cairo_set_dash(), and
* cairo_stroke_preserve().
*
* Since: 1.0
**/
void
cairo_stroke_extents (cairo_t *cr,
2608,9 → 2401,7
return;
}
 
status = _cairo_gstate_stroke_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
status = cr->backend->stroke_extents (cr, x1, y1, x2, y2);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2639,6 → 2430,8
* if the non-inked path extents are desired.
*
* See cairo_fill(), cairo_set_fill_rule() and cairo_fill_preserve().
*
* Since: 1.0
**/
void
cairo_fill_extents (cairo_t *cr,
2659,9 → 2452,7
return;
}
 
status = _cairo_gstate_fill_extents (cr->gstate,
cr->path,
x1, y1, x2, y2);
status = cr->backend->fill_extents (cr, x1, y1, x2, y2);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2687,13 → 2478,20
* calling cairo_clip() within a cairo_save()/cairo_restore()
* pair. The only other means of increasing the size of the clip
* region is cairo_reset_clip().
*
* Since: 1.0
**/
void
cairo_clip (cairo_t *cr)
{
cairo_clip_preserve (cr);
cairo_status_t status;
 
cairo_new_path (cr);
if (unlikely (cr->status))
return;
 
status = cr->backend->clip (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
 
/**
2717,6 → 2515,8
* calling cairo_clip_preserve() within a cairo_save()/cairo_restore()
* pair. The only other means of increasing the size of the clip
* region is cairo_reset_clip().
*
* Since: 1.0
**/
void
cairo_clip_preserve (cairo_t *cr)
2726,7 → 2526,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_clip (cr->gstate, cr->path);
status = cr->backend->clip_preserve (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2747,6 → 2547,8
* higher-level code which calls cairo_clip(). Consider using
* cairo_save() and cairo_restore() around cairo_clip() as a more
* robust means of temporarily restricting the clip region.
*
* Since: 1.0
**/
void
cairo_reset_clip (cairo_t *cr)
2756,7 → 2558,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_reset_clip (cr->gstate);
status = cr->backend->reset_clip (cr);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2779,7 → 2581,8
double *x1, double *y1,
double *x2, double *y2)
{
if (unlikely (cr->status)) {
cairo_status_t status;
 
if (x1)
*x1 = 0.0;
if (y1)
2789,16 → 2592,13
if (y2)
*y2 = 0.0;
 
if (unlikely (cr->status))
return;
}
 
if (! _cairo_gstate_clip_extents (cr->gstate, x1, y1, x2, y2)) {
*x1 = -INFINITY;
*y1 = -INFINITY;
*x2 = +INFINITY;
*y2 = +INFINITY;
status = cr->backend->clip_extents (cr, x1, y1, x2, y2);
if (unlikely (status))
_cairo_set_error (cr, status);
}
}
 
/**
* cairo_in_clip:
2820,32 → 2620,19
cairo_bool_t
cairo_in_clip (cairo_t *cr, double x, double y)
{
cairo_status_t status;
cairo_bool_t inside = FALSE;
 
if (unlikely (cr->status))
return FALSE;
 
return _cairo_gstate_in_clip (cr->gstate, x, y);
}
status = cr->backend->in_clip (cr, x, y, &inside);
if (unlikely (status))
_cairo_set_error (cr, status);
 
static cairo_rectangle_list_t *
_cairo_rectangle_list_create_in_error (cairo_status_t status)
{
cairo_rectangle_list_t *list;
 
if (status == CAIRO_STATUS_NO_MEMORY)
return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
 
list = malloc (sizeof (cairo_rectangle_list_t));
if (unlikely (list == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_rectangle_list_t*) &_cairo_rectangles_nil;
return inside;
}
 
list->status = status;
list->rectangles = NULL;
list->num_rectangles = 0;
return list;
}
 
/**
* cairo_copy_clip_rectangle_list:
* @cr: a cairo context
2869,7 → 2656,7
if (unlikely (cr->status))
return _cairo_rectangle_list_create_in_error (cr->status);
 
return _cairo_gstate_copy_clip_rectangle_list (cr->gstate);
return cr->backend->clip_copy_rectangle_list (cr);
}
 
/**
2922,6 → 2709,8
*
* This function is equivalent to a call to cairo_toy_font_face_create()
* followed by cairo_set_font_face().
*
* Since: 1.0
**/
void
cairo_select_font_face (cairo_t *cr,
2929,12 → 2718,21
cairo_font_slant_t slant,
cairo_font_weight_t weight)
{
cairo_font_face_t *font_face;
cairo_status_t status;
 
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_select_font_face (cr->gstate, family, slant, weight);
font_face = cairo_toy_font_face_create (family, slant, weight);
if (unlikely (font_face->status)) {
_cairo_set_error (cr, font_face->status);
return;
}
 
status = cr->backend->set_font_face (cr, font_face);
cairo_font_face_destroy (font_face);
 
if (unlikely (status))
_cairo_set_error (cr, status);
}
2946,6 → 2744,8
* will be stored.
*
* Gets the font extents for the currently selected font.
*
* Since: 1.0
**/
void
cairo_font_extents (cairo_t *cr,
2962,7 → 2762,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_get_font_extents (cr->gstate, extents);
status = cr->backend->font_extents (cr, extents);
if (unlikely (status))
_cairo_set_error (cr, status);
}
2975,6 → 2775,8
* Replaces the current #cairo_font_face_t object in the #cairo_t with
* @font_face. The replaced font face in the #cairo_t will be
* destroyed if there are no other references to it.
*
* Since: 1.0
**/
void
cairo_set_font_face (cairo_t *cr,
2985,7 → 2787,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_font_face (cr->gstate, font_face);
status = cr->backend->set_font_face (cr, font_face);
if (unlikely (status))
_cairo_set_error (cr, status);
}
3007,25 → 2809,18
* objects it is passed to, (for example, calling
* cairo_set_font_face() with a nil font will trigger an error that
* will shutdown the #cairo_t object).
*
* Since: 1.0
**/
cairo_font_face_t *
cairo_get_font_face (cairo_t *cr)
{
cairo_status_t status;
cairo_font_face_t *font_face;
 
if (unlikely (cr->status))
return (cairo_font_face_t*) &_cairo_font_face_nil;
 
status = _cairo_gstate_get_font_face (cr->gstate, &font_face);
if (unlikely (status)) {
_cairo_set_error (cr, status);
return (cairo_font_face_t*) &_cairo_font_face_nil;
return cr->backend->get_font_face (cr);
}
 
return font_face;
}
 
/**
* cairo_set_font_size:
* @cr: a #cairo_t
3040,6 → 2835,8
* If text is drawn without a call to cairo_set_font_size(), (nor
* cairo_set_font_matrix() nor cairo_set_scaled_font()), the default
* font size is 10.0.
*
* Since: 1.0
**/
void
cairo_set_font_size (cairo_t *cr, double size)
3049,7 → 2846,7
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_font_size (cr->gstate, size);
status = cr->backend->set_font_size (cr, size);
if (unlikely (status))
_cairo_set_error (cr, status);
}
3056,7 → 2853,7
slim_hidden_def (cairo_set_font_size);
 
/**
* cairo_set_font_matrix
* cairo_set_font_matrix:
* @cr: a #cairo_t
* @matrix: a #cairo_matrix_t describing a transform to be applied to
* the current font.
3067,6 → 2864,8
* simple scale is used (see cairo_set_font_size()), but a more
* complex font matrix can be used to shear the font
* or stretch it unequally along the two axes
*
* Since: 1.0
**/
void
cairo_set_font_matrix (cairo_t *cr,
3077,18 → 2876,21
if (unlikely (cr->status))
return;
 
status = _cairo_gstate_set_font_matrix (cr->gstate, matrix);
status = cr->backend->set_font_matrix (cr, matrix);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def (cairo_set_font_matrix);
 
/**
* cairo_get_font_matrix
* cairo_get_font_matrix:
* @cr: a #cairo_t
* @matrix: return value for the matrix
*
* Stores the current font matrix into @matrix. See
* cairo_set_font_matrix().
*
* Since: 1.0
**/
void
cairo_get_font_matrix (cairo_t *cr, cairo_matrix_t *matrix)
3098,7 → 2900,7
return;
}
 
_cairo_gstate_get_font_matrix (cr->gstate, matrix);
cr->backend->get_font_matrix (cr, matrix);
}
 
/**
3111,6 → 2913,8
* options derived from underlying surface; if the value in @options
* has a default value (like %CAIRO_ANTIALIAS_DEFAULT), then the value
* from the surface is used.
*
* Since: 1.0
**/
void
cairo_set_font_options (cairo_t *cr,
3127,7 → 2931,9
return;
}
 
_cairo_gstate_set_font_options (cr->gstate, options);
status = cr->backend->set_font_options (cr, options);
if (unlikely (status))
_cairo_set_error (cr, status);
}
slim_hidden_def (cairo_set_font_options);
 
3141,6 → 2947,8
* Note that the returned options do not include any options derived
* from the underlying surface; they are literally the options
* passed to cairo_set_font_options().
*
* Since: 1.0
**/
void
cairo_get_font_options (cairo_t *cr,
3155,7 → 2963,7
return;
}
 
_cairo_gstate_get_font_options (cr->gstate, options);
cr->backend->get_font_options (cr, options);
}
 
/**
3176,41 → 2984,23
const cairo_scaled_font_t *scaled_font)
{
cairo_status_t status;
cairo_bool_t was_previous;
 
if (unlikely (cr->status))
return;
 
if (scaled_font == NULL) {
status = _cairo_error (CAIRO_STATUS_NULL_POINTER);
goto BAIL;
if ((scaled_font == NULL)) {
_cairo_set_error (cr, _cairo_error (CAIRO_STATUS_NULL_POINTER));
return;
}
 
status = scaled_font->status;
if (unlikely (status))
goto BAIL;
 
if (scaled_font == cr->gstate->scaled_font)
if (unlikely (status)) {
_cairo_set_error (cr, status);
return;
}
 
was_previous = scaled_font == cr->gstate->previous_scaled_font;
 
status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
status = cr->backend->set_scaled_font (cr, (cairo_scaled_font_t *) scaled_font);
if (unlikely (status))
goto BAIL;
 
status = _cairo_gstate_set_font_matrix (cr->gstate, &scaled_font->font_matrix);
if (unlikely (status))
goto BAIL;
 
_cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
 
if (was_previous)
cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font);
 
return;
 
BAIL:
_cairo_set_error (cr, status);
}
 
3237,21 → 3027,13
cairo_scaled_font_t *
cairo_get_scaled_font (cairo_t *cr)
{
cairo_status_t status;
cairo_scaled_font_t *scaled_font;
 
if (unlikely (cr->status))
return _cairo_scaled_font_create_in_error (cr->status);
 
status = _cairo_gstate_get_scaled_font (cr->gstate, &scaled_font);
if (unlikely (status)) {
_cairo_set_error (cr, status);
return _cairo_scaled_font_create_in_error (status);
return cr->backend->get_scaled_font (cr);
}
slim_hidden_def (cairo_get_scaled_font);
 
return scaled_font;
}
 
/**
* cairo_text_extents:
* @cr: a #cairo_t
3271,6 → 3053,8
* characters. In particular, trailing whitespace characters are
* likely to not affect the size of the rectangle, though they will
* affect the x_advance and y_advance values.
*
* Since: 1.0
**/
void
cairo_text_extents (cairo_t *cr,
3278,8 → 3062,9
cairo_text_extents_t *extents)
{
cairo_status_t status;
cairo_scaled_font_t *scaled_font;
cairo_glyph_t *glyphs = NULL;
int num_glyphs;
int num_glyphs = 0;
double x, y;
 
extents->x_bearing = 0.0;
3295,19 → 3080,24
if (utf8 == NULL)
return;
 
scaled_font = cairo_get_scaled_font (cr);
if (unlikely (scaled_font->status)) {
_cairo_set_error (cr, scaled_font->status);
return;
}
 
cairo_get_current_point (cr, &x, &y);
 
status = _cairo_gstate_text_to_glyphs (cr->gstate,
status = cairo_scaled_font_text_to_glyphs (scaled_font,
x, y,
utf8, strlen (utf8),
utf8, -1,
&glyphs, &num_glyphs,
NULL, NULL,
NULL);
NULL, NULL, NULL);
 
if (status == CAIRO_STATUS_SUCCESS)
status = _cairo_gstate_glyph_extents (cr->gstate,
if (likely (status == CAIRO_STATUS_SUCCESS)) {
status = cr->backend->glyph_extents (cr,
glyphs, num_glyphs,
extents);
}
cairo_glyph_free (glyphs);
 
if (unlikely (status))
3331,6 → 3121,8
*
* Note that whitespace glyphs do not contribute to the size of the
* rectangle (extents.width and extents.height).
*
* Since: 1.0
**/
void
cairo_glyph_extents (cairo_t *cr,
3353,18 → 3145,17
if (num_glyphs == 0)
return;
 
if (num_glyphs < 0) {
if (unlikely (num_glyphs < 0)) {
_cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
return;
}
 
if (glyphs == NULL) {
if (unlikely (glyphs == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
status = _cairo_gstate_glyph_extents (cr->gstate, glyphs, num_glyphs,
extents);
status = cr->backend->glyph_extents (cr, glyphs, num_glyphs, extents);
if (unlikely (status))
_cairo_set_error (cr, status);
}
3395,6 → 3186,8
* and simple programs, but it is not expected to be adequate for
* serious text-using applications. See cairo_show_glyphs() for the
* "real" text display API in cairo.
*
* Since: 1.0
**/
void
cairo_show_text (cairo_t *cr, const char *utf8)
3409,6 → 3202,8
cairo_bool_t has_show_text_glyphs;
cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
cairo_text_cluster_t stack_clusters[CAIRO_STACK_ARRAY_LENGTH (cairo_text_cluster_t)];
cairo_scaled_font_t *scaled_font;
cairo_glyph_text_info_t info, *i;
 
if (unlikely (cr->status))
return;
3416,7 → 3211,11
if (utf8 == NULL)
return;
 
cairo_get_current_point (cr, &x, &y);
scaled_font = cairo_get_scaled_font (cr);
if (unlikely (scaled_font->status)) {
_cairo_set_error (cr, scaled_font->status);
return;
}
 
utf8_len = strlen (utf8);
 
3434,7 → 3233,8
num_clusters = 0;
}
 
status = _cairo_gstate_text_to_glyphs (cr->gstate,
cairo_get_current_point (cr, &x, &y);
status = cairo_scaled_font_text_to_glyphs (scaled_font,
x, y,
utf8, utf8_len,
&glyphs, &num_glyphs,
3446,24 → 3246,28
if (num_glyphs == 0)
return;
 
status = _cairo_gstate_show_text_glyphs (cr->gstate,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags);
i = NULL;
if (has_show_text_glyphs) {
info.utf8 = utf8;
info.utf8_len = utf8_len;
info.clusters = clusters;
info.num_clusters = num_clusters;
info.cluster_flags = cluster_flags;
i = &info;
}
 
status = cr->backend->glyphs (cr, glyphs, num_glyphs, i);
if (unlikely (status))
goto BAIL;
 
last_glyph = &glyphs[num_glyphs - 1];
status = _cairo_gstate_glyph_extents (cr->gstate,
last_glyph, 1,
&extents);
status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
if (unlikely (status))
goto BAIL;
 
x = last_glyph->x + extents.x_advance;
y = last_glyph->y + extents.y_advance;
cairo_move_to (cr, x, y);
cr->backend->move_to (cr, x, y);
 
BAIL:
if (glyphs != stack_glyphs)
3484,6 → 3288,8
* A drawing operator that generates the shape from an array of glyphs,
* rendered according to the current font face, font size
* (font matrix), and font options.
*
* Since: 1.0
**/
void
cairo_show_glyphs (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
3506,11 → 3312,7
return;
}
 
status = _cairo_gstate_show_text_glyphs (cr->gstate,
NULL, 0,
glyphs, num_glyphs,
NULL, 0,
FALSE);
status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
if (unlikely (status))
_cairo_set_error (cr, status);
}
3588,6 → 3390,10
return;
}
 
if (num_glyphs == 0 && utf8_len == 0)
return;
 
if (utf8) {
/* Make sure clusters cover the entire glyphs and utf8 arrays,
* and that cluster boundaries are UTF-8 boundaries. */
status = _cairo_validate_text_clusters (utf8, utf8_len,
3602,18 → 3408,20
status2 = _cairo_utf8_to_ucs4 (utf8, utf8_len, NULL, NULL);
if (status2)
status = status2;
} else {
cairo_glyph_text_info_t info;
 
_cairo_set_error (cr, status);
return;
info.utf8 = utf8;
info.utf8_len = utf8_len;
info.clusters = clusters;
info.num_clusters = num_clusters;
info.cluster_flags = cluster_flags;
 
status = cr->backend->glyphs (cr, glyphs, num_glyphs, &info);
}
 
if (num_glyphs == 0 && utf8_len == 0)
return;
 
status = _cairo_gstate_show_text_glyphs (cr->gstate,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters, cluster_flags);
} else {
status = cr->backend->glyphs (cr, glyphs, num_glyphs, NULL);
}
if (unlikely (status))
_cairo_set_error (cr, status);
}
3641,6 → 3449,8
* and simple programs, but it is not expected to be adequate for
* serious text-using applications. See cairo_glyph_path() for the
* "real" text path API in cairo.
*
* Since: 1.0
**/
void
cairo_text_path (cairo_t *cr, const char *utf8)
3649,6 → 3459,7
cairo_text_extents_t extents;
cairo_glyph_t stack_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)];
cairo_glyph_t *glyphs, *last_glyph;
cairo_scaled_font_t *scaled_font;
int num_glyphs;
double x, y;
 
3658,35 → 3469,33
if (utf8 == NULL)
return;
 
cairo_get_current_point (cr, &x, &y);
 
glyphs = stack_glyphs;
num_glyphs = ARRAY_LENGTH (stack_glyphs);
 
status = _cairo_gstate_text_to_glyphs (cr->gstate,
scaled_font = cairo_get_scaled_font (cr);
if (unlikely (scaled_font->status)) {
_cairo_set_error (cr, scaled_font->status);
return;
}
 
cairo_get_current_point (cr, &x, &y);
status = cairo_scaled_font_text_to_glyphs (scaled_font,
x, y,
utf8, strlen (utf8),
utf8, -1,
&glyphs, &num_glyphs,
NULL, NULL,
NULL);
NULL, NULL, NULL);
 
if (unlikely (status))
goto BAIL;
 
if (num_glyphs == 0)
return;
 
status = _cairo_gstate_glyph_path (cr->gstate,
glyphs, num_glyphs,
cr->path);
status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
 
if (unlikely (status))
goto BAIL;
 
last_glyph = &glyphs[num_glyphs - 1];
status = _cairo_gstate_glyph_extents (cr->gstate,
last_glyph, 1,
&extents);
status = cr->backend->glyph_extents (cr, last_glyph, 1, &extents);
 
if (unlikely (status))
goto BAIL;
3693,7 → 3502,7
 
x = last_glyph->x + extents.x_advance;
y = last_glyph->y + extents.y_advance;
cairo_move_to (cr, x, y);
cr->backend->move_to (cr, x, y);
 
BAIL:
if (glyphs != stack_glyphs)
3712,6 → 3521,8
* Adds closed paths for the glyphs to the current path. The generated
* path if filled, achieves an effect similar to that of
* cairo_show_glyphs().
*
* Since: 1.0
**/
void
cairo_glyph_path (cairo_t *cr, const cairo_glyph_t *glyphs, int num_glyphs)
3724,19 → 3535,17
if (num_glyphs == 0)
return;
 
if (num_glyphs < 0) {
if (unlikely (num_glyphs < 0)) {
_cairo_set_error (cr, CAIRO_STATUS_NEGATIVE_COUNT);
return;
}
 
if (glyphs == NULL) {
if (unlikely (glyphs == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
status = _cairo_gstate_glyph_path (cr->gstate,
glyphs, num_glyphs,
cr->path);
status = cr->backend->glyph_path (cr, glyphs, num_glyphs);
if (unlikely (status))
_cairo_set_error (cr, status);
}
3748,6 → 3557,8
* Gets the current compositing operator for a cairo context.
*
* Return value: the current compositing operator.
*
* Since: 1.0
**/
cairo_operator_t
cairo_get_operator (cairo_t *cr)
3755,10 → 3566,31
if (unlikely (cr->status))
return CAIRO_GSTATE_OPERATOR_DEFAULT;
 
return _cairo_gstate_get_operator (cr->gstate);
return cr->backend->get_operator (cr);
}
 
#if 0
/**
* cairo_get_opacity:
* @cr: a cairo context
*
* Gets the current compositing opacity for a cairo context.
*
* Return value: the current compositing opacity.
*
* Since: TBD
**/
double
cairo_get_opacity (cairo_t *cr)
{
if (unlikely (cr->status))
return 1.;
 
return cr->backend->get_opacity (cr);
}
#endif
 
/**
* cairo_get_tolerance:
* @cr: a cairo context
*
3765,6 → 3597,8
* Gets the current tolerance value, as set by cairo_set_tolerance().
*
* Return value: the current tolerance value.
*
* Since: 1.0
**/
double
cairo_get_tolerance (cairo_t *cr)
3772,7 → 3606,7
if (unlikely (cr->status))
return CAIRO_GSTATE_TOLERANCE_DEFAULT;
 
return _cairo_gstate_get_tolerance (cr->gstate);
return cr->backend->get_tolerance (cr);
}
slim_hidden_def (cairo_get_tolerance);
 
3780,9 → 3614,12
* cairo_get_antialias:
* @cr: a cairo context
*
* Gets the current shape antialiasing mode, as set by cairo_set_shape_antialias().
* Gets the current shape antialiasing mode, as set by
* cairo_set_antialias().
*
* Return value: the current shape antialiasing mode.
*
* Since: 1.0
**/
cairo_antialias_t
cairo_get_antialias (cairo_t *cr)
3790,7 → 3627,7
if (unlikely (cr->status))
return CAIRO_ANTIALIAS_DEFAULT;
 
return _cairo_gstate_get_antialias (cr->gstate);
return cr->backend->get_antialias (cr);
}
 
/**
3810,7 → 3647,7
if (unlikely (cr->status))
return FALSE;
 
return cr->path->has_current_point;
return cr->backend->has_current_point (cr);
}
 
/**
3842,25 → 3679,20
*
* Some functions unset the current path and as a result, current point:
* cairo_fill(), cairo_stroke().
*
* Since: 1.0
**/
void
cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret)
{
cairo_fixed_t x_fixed, y_fixed;
double x, y;
 
x = y = 0;
if (cr->status == CAIRO_STATUS_SUCCESS &&
_cairo_path_fixed_get_current_point (cr->path, &x_fixed, &y_fixed))
cr->backend->has_current_point (cr))
{
x = _cairo_fixed_to_double (x_fixed);
y = _cairo_fixed_to_double (y_fixed);
_cairo_gstate_backend_to_user (cr->gstate, &x, &y);
cr->backend->get_current_point (cr, &x, &y);
}
else
{
x = 0.0;
y = 0.0;
}
 
if (x_ret)
*x_ret = x;
3876,6 → 3708,8
* Gets the current fill rule, as set by cairo_set_fill_rule().
*
* Return value: the current fill rule.
*
* Since: 1.0
**/
cairo_fill_rule_t
cairo_get_fill_rule (cairo_t *cr)
3883,7 → 3717,7
if (unlikely (cr->status))
return CAIRO_GSTATE_FILL_RULE_DEFAULT;
 
return _cairo_gstate_get_fill_rule (cr->gstate);
return cr->backend->get_fill_rule (cr);
}
 
/**
3896,6 → 3730,8
* cairo_get_line_width().
*
* Return value: the current line width.
*
* Since: 1.0
**/
double
cairo_get_line_width (cairo_t *cr)
3903,7 → 3739,7
if (unlikely (cr->status))
return CAIRO_GSTATE_LINE_WIDTH_DEFAULT;
 
return _cairo_gstate_get_line_width (cr->gstate);
return cr->backend->get_line_width (cr);
}
slim_hidden_def (cairo_get_line_width);
 
3914,6 → 3750,8
* Gets the current line cap style, as set by cairo_set_line_cap().
*
* Return value: the current line cap style.
*
* Since: 1.0
**/
cairo_line_cap_t
cairo_get_line_cap (cairo_t *cr)
3921,7 → 3759,7
if (unlikely (cr->status))
return CAIRO_GSTATE_LINE_CAP_DEFAULT;
 
return _cairo_gstate_get_line_cap (cr->gstate);
return cr->backend->get_line_cap (cr);
}
 
/**
3931,6 → 3769,8
* Gets the current line join style, as set by cairo_set_line_join().
*
* Return value: the current line join style.
*
* Since: 1.0
**/
cairo_line_join_t
cairo_get_line_join (cairo_t *cr)
3938,7 → 3778,7
if (unlikely (cr->status))
return CAIRO_GSTATE_LINE_JOIN_DEFAULT;
 
return _cairo_gstate_get_line_join (cr->gstate);
return cr->backend->get_line_join (cr);
}
 
/**
3948,6 → 3788,8
* Gets the current miter limit, as set by cairo_set_miter_limit().
*
* Return value: the current miter limit.
*
* Since: 1.0
**/
double
cairo_get_miter_limit (cairo_t *cr)
3955,7 → 3797,7
if (unlikely (cr->status))
return CAIRO_GSTATE_MITER_LIMIT_DEFAULT;
 
return _cairo_gstate_get_miter_limit (cr->gstate);
return cr->backend->get_miter_limit (cr);
}
 
/**
3964,6 → 3806,8
* @matrix: return value for the matrix
*
* Stores the current transformation matrix (CTM) into @matrix.
*
* Since: 1.0
**/
void
cairo_get_matrix (cairo_t *cr, cairo_matrix_t *matrix)
3973,7 → 3817,7
return;
}
 
_cairo_gstate_get_matrix (cr->gstate, matrix);
cr->backend->get_matrix (cr, matrix);
}
slim_hidden_def (cairo_get_matrix);
 
3992,6 → 3836,8
*
* Return value: the target surface. This object is owned by cairo. To
* keep a reference to it, you must call cairo_surface_reference().
*
* Since: 1.0
**/
cairo_surface_t *
cairo_get_target (cairo_t *cr)
3999,7 → 3845,7
if (unlikely (cr->status))
return _cairo_surface_create_in_error (cr->status);
 
return _cairo_gstate_get_original_target (cr->gstate);
return cr->backend->get_original_target (cr);
}
slim_hidden_def (cairo_get_target);
 
4029,7 → 3875,7
if (unlikely (cr->status))
return _cairo_surface_create_in_error (cr->status);
 
return _cairo_gstate_get_target (cr->gstate);
return cr->backend->get_current_target (cr);
}
 
/**
4057,6 → 3903,8
* Return value: the copy of the current path. The caller owns the
* returned object and should call cairo_path_destroy() when finished
* with it.
*
* Since: 1.0
**/
cairo_path_t *
cairo_copy_path (cairo_t *cr)
4064,7 → 3912,7
if (unlikely (cr->status))
return _cairo_path_create_in_error (cr->status);
 
return _cairo_path_create (cr->path, cr->gstate);
return cr->backend->copy_path (cr);
}
 
/**
4099,6 → 3947,8
* Return value: the copy of the current path. The caller owns the
* returned object and should call cairo_path_destroy() when finished
* with it.
*
* Since: 1.0
**/
cairo_path_t *
cairo_copy_path_flat (cairo_t *cr)
4106,7 → 3956,7
if (unlikely (cr->status))
return _cairo_path_create_in_error (cr->status);
 
return _cairo_path_create_flat (cr->path, cr->gstate);
return cr->backend->copy_path_flat (cr);
}
 
/**
4120,6 → 3970,8
* #cairo_path_t for details on how the path data structure should be
* initialized, and note that <literal>path->status</literal> must be
* initialized to %CAIRO_STATUS_SUCCESS.
*
* Since: 1.0
**/
void
cairo_append_path (cairo_t *cr,
4130,12 → 3982,12
if (unlikely (cr->status))
return;
 
if (path == NULL) {
if (unlikely (path == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
if (path->status) {
if (unlikely (path->status)) {
if (path->status > CAIRO_STATUS_SUCCESS &&
path->status <= CAIRO_STATUS_LAST_STATUS)
_cairo_set_error (cr, path->status);
4147,12 → 3999,12
if (path->num_data == 0)
return;
 
if (path->data == NULL) {
if (unlikely (path->data == NULL)) {
_cairo_set_error (cr, CAIRO_STATUS_NULL_POINTER);
return;
}
 
status = _cairo_path_append_to_context (path, cr);
status = cr->backend->append_path (cr, path);
if (unlikely (status))
_cairo_set_error (cr, status);
}
4164,6 → 4016,8
* Checks whether an error has previously occurred for this context.
*
* Returns: the current status of this context, see #cairo_status_t
*
* Since: 1.0
**/
cairo_status_t
cairo_status (cairo_t *cr)
/programs/develop/libraries/cairo/src/cairo.h
101,6 → 101,8
* /<!-- -->* do something *<!-- -->/
* }
* </programlisting></informalexample>
*
* Since: 1.0
**/
typedef int cairo_bool_t;
 
116,6 → 118,8
*
* Memory management of #cairo_t is done with
* cairo_reference() and cairo_destroy().
*
* Since: 1.0
**/
typedef struct _cairo cairo_t;
 
143,6 → 147,8
*
* Memory management of #cairo_surface_t is done with
* cairo_surface_reference() and cairo_surface_destroy().
*
* Since: 1.0
**/
typedef struct _cairo_surface cairo_surface_t;
 
152,8 → 158,8
* A #cairo_device_t represents the driver interface for drawing
* operations to a #cairo_surface_t. There are different subtypes of
* #cairo_device_t for different drawing backends; for example,
* cairo_xcb_device_create() creates a device that wraps the connection
* to an X Windows System using the XCB library.
* cairo_egl_device_create() creates a device that wraps an EGL display and
* context.
*
* The type of a device can be queried with cairo_device_get_type().
*
180,6 → 186,8
* x_new = xx * x + xy * y + x0;
* y_new = yx * x + yy * y + y0;
* </programlisting>
*
* Since: 1.0
**/
typedef struct _cairo_matrix {
double xx; double yx;
196,9 → 204,10
* cairo_pattern_create_rgb() creates a pattern for a solid
* opaque color.
*
* Other than various cairo_pattern_create_<emphasis>type</emphasis>()
* functions, some of the pattern types can be implicitly created
* using various cairo_set_source_<emphasis>type</emphasis>() functions;
* Other than various
* <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
* functions, some of the pattern types can be implicitly created using various
* <function>cairo_set_source_<emphasis>type</emphasis>()</function> functions;
* for example cairo_set_source_rgb().
*
* The type of a pattern can be queried with cairo_pattern_get_type().
205,6 → 214,8
*
* Memory management of #cairo_pattern_t is done with
* cairo_pattern_reference() and cairo_pattern_destroy().
*
* Since: 1.0
**/
typedef struct _cairo_pattern cairo_pattern_t;
 
215,6 → 226,8
* #cairo_destroy_func_t the type of function which is called when a
* data element is destroyed. It is passed the pointer to the data
* element and should free any memory and resources allocated for it.
*
* Since: 1.0
**/
typedef void (*cairo_destroy_func_t) (void *data);
 
227,6 → 240,8
* and there is no need to initialize the object; only the unique
* address of a #cairo_data_key_t object is used. Typically, you
* would just use the address of a static #cairo_data_key_t object.
*
* Since: 1.0
**/
typedef struct _cairo_user_data_key {
int unused;
234,26 → 249,26
 
/**
* cairo_status_t:
* @CAIRO_STATUS_SUCCESS: no error has occurred
* @CAIRO_STATUS_NO_MEMORY: out of memory
* @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save()
* @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group()
* @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined
* @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible)
* @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t
* @CAIRO_STATUS_NULL_POINTER: %NULL pointer
* @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8
* @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid
* @CAIRO_STATUS_READ_ERROR: error while reading from input stream
* @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream
* @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished
* @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation
* @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation
* @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t
* @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t
* @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual*
* @CAIRO_STATUS_FILE_NOT_FOUND: file not found
* @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting
* @CAIRO_STATUS_SUCCESS: no error has occurred (Since 1.0)
* @CAIRO_STATUS_NO_MEMORY: out of memory (Since 1.0)
* @CAIRO_STATUS_INVALID_RESTORE: cairo_restore() called without matching cairo_save() (Since 1.0)
* @CAIRO_STATUS_INVALID_POP_GROUP: no saved group to pop, i.e. cairo_pop_group() without matching cairo_push_group() (Since 1.0)
* @CAIRO_STATUS_NO_CURRENT_POINT: no current point defined (Since 1.0)
* @CAIRO_STATUS_INVALID_MATRIX: invalid matrix (not invertible) (Since 1.0)
* @CAIRO_STATUS_INVALID_STATUS: invalid value for an input #cairo_status_t (Since 1.0)
* @CAIRO_STATUS_NULL_POINTER: %NULL pointer (Since 1.0)
* @CAIRO_STATUS_INVALID_STRING: input string not valid UTF-8 (Since 1.0)
* @CAIRO_STATUS_INVALID_PATH_DATA: input path data not valid (Since 1.0)
* @CAIRO_STATUS_READ_ERROR: error while reading from input stream (Since 1.0)
* @CAIRO_STATUS_WRITE_ERROR: error while writing to output stream (Since 1.0)
* @CAIRO_STATUS_SURFACE_FINISHED: target surface has been finished (Since 1.0)
* @CAIRO_STATUS_SURFACE_TYPE_MISMATCH: the surface type is not appropriate for the operation (Since 1.0)
* @CAIRO_STATUS_PATTERN_TYPE_MISMATCH: the pattern type is not appropriate for the operation (Since 1.0)
* @CAIRO_STATUS_INVALID_CONTENT: invalid value for an input #cairo_content_t (Since 1.0)
* @CAIRO_STATUS_INVALID_FORMAT: invalid value for an input #cairo_format_t (Since 1.0)
* @CAIRO_STATUS_INVALID_VISUAL: invalid value for an input Visual* (Since 1.0)
* @CAIRO_STATUS_FILE_NOT_FOUND: file not found (Since 1.0)
* @CAIRO_STATUS_INVALID_DASH: invalid value for a dash setting (Since 1.0)
* @CAIRO_STATUS_INVALID_DSC_COMMENT: invalid value for a DSC comment (Since 1.2)
* @CAIRO_STATUS_INVALID_INDEX: invalid index passed to getter (Since 1.4)
* @CAIRO_STATUS_CLIP_NOT_REPRESENTABLE: clip region not representable in desired format (Since 1.4)
270,6 → 285,11
* @CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED: user-font method not implemented (Since 1.10)
* @CAIRO_STATUS_DEVICE_TYPE_MISMATCH: the device type is not appropriate for the operation (Since 1.10)
* @CAIRO_STATUS_DEVICE_ERROR: an operation to the device caused an unspecified error (Since 1.10)
* @CAIRO_STATUS_INVALID_MESH_CONSTRUCTION: a mesh pattern
* construction operation was used outside of a
* cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch()
* pair (Since 1.12)
* @CAIRO_STATUS_DEVICE_FINISHED: target device has been finished (Since 1.12)
* @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of
* status values defined in this enumeration. When using this value, note
* that the version of cairo at run-time may have additional status values
282,6 → 302,8
*
* New entries may be added in future versions. Use cairo_status_to_string()
* to get a human-readable representation of an error message.
*
* Since: 1.0
**/
typedef enum _cairo_status {
CAIRO_STATUS_SUCCESS = 0,
321,6 → 343,8
CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED,
CAIRO_STATUS_DEVICE_TYPE_MISMATCH,
CAIRO_STATUS_DEVICE_ERROR,
CAIRO_STATUS_INVALID_MESH_CONSTRUCTION,
CAIRO_STATUS_DEVICE_FINISHED,
 
CAIRO_STATUS_LAST_STATUS
} cairo_status_t;
327,9 → 351,9
 
/**
* cairo_content_t:
* @CAIRO_CONTENT_COLOR: The surface will hold color content only.
* @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only.
* @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content.
* @CAIRO_CONTENT_COLOR: The surface will hold color content only. (Since 1.0)
* @CAIRO_CONTENT_ALPHA: The surface will hold alpha content only. (Since 1.0)
* @CAIRO_CONTENT_COLOR_ALPHA: The surface will hold color and alpha content. (Since 1.0)
*
* #cairo_content_t is used to describe the content that a surface will
* contain, whether color information, alpha information (translucence
338,6 → 362,8
* Note: The large values here are designed to keep #cairo_content_t
* values distinct from #cairo_format_t values so that the
* implementation can detect the error if users confuse the two types.
*
* Since: 1.0
**/
typedef enum _cairo_content {
CAIRO_CONTENT_COLOR = 0x1000,
346,6 → 372,48
} cairo_content_t;
 
/**
* cairo_format_t:
* @CAIRO_FORMAT_INVALID: no such format exists or is supported.
* @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
* alpha in the upper 8 bits, then red, then green, then blue.
* The 32-bit quantities are stored native-endian. Pre-multiplied
* alpha is used. (That is, 50% transparent red is 0x80800000,
* not 0x80ff0000.) (Since 1.0)
* @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
* the upper 8 bits unused. Red, Green, and Blue are stored
* in the remaining 24 bits in that order. (Since 1.0)
* @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
* an alpha value. (Since 1.0)
* @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
* an alpha value. Pixels are packed together into 32-bit
* quantities. The ordering of the bits matches the
* endianess of the platform. On a big-endian machine, the
* first pixel is in the uppermost bit, on a little-endian
* machine the first pixel is in the least-significant bit. (Since 1.0)
* @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
* with red in the upper 5 bits, then green in the middle
* 6 bits, and blue in the lower 5 bits. (Since 1.2)
* @CAIRO_FORMAT_RGB30: like RGB24 but with 10bpc. (Since 1.12)
*
* #cairo_format_t is used to identify the memory format of
* image data.
*
* New entries may be added in future versions.
*
* Since: 1.0
**/
typedef enum _cairo_format {
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4,
CAIRO_FORMAT_RGB30 = 5
} cairo_format_t;
 
 
/**
* cairo_write_func_t:
* @closure: the output closure
* @data: the buffer containing the data to write
360,6 → 428,8
* %CAIRO_STATUS_WRITE_ERROR otherwise.
*
* Returns: the status code of the write operation
*
* Since: 1.0
**/
typedef cairo_status_t (*cairo_write_func_t) (void *closure,
const unsigned char *data,
380,11 → 450,31
* %CAIRO_STATUS_READ_ERROR otherwise.
*
* Returns: the status code of the read operation
*
* Since: 1.0
**/
typedef cairo_status_t (*cairo_read_func_t) (void *closure,
unsigned char *data,
unsigned int length);
 
/**
* cairo_rectangle_int_t:
* @x: X coordinate of the left side of the rectangle
* @y: Y coordinate of the the top side of the rectangle
* @width: width of the rectangle
* @height: height of the rectangle
*
* A data structure for holding a rectangle with integer coordinates.
*
* Since: 1.10
**/
 
typedef struct _cairo_rectangle_int {
int x, y;
int width, height;
} cairo_rectangle_int_t;
 
 
/* Functions for manipulating state objects */
cairo_public cairo_t *
cairo_create (cairo_surface_t *target);
430,64 → 520,64
 
/**
* cairo_operator_t:
* @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded)
* @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded)
* @CAIRO_OPERATOR_CLEAR: clear destination layer (bounded) (Since 1.0)
* @CAIRO_OPERATOR_SOURCE: replace destination layer (bounded) (Since 1.0)
* @CAIRO_OPERATOR_OVER: draw source layer on top of destination layer
* (bounded)
* (bounded) (Since 1.0)
* @CAIRO_OPERATOR_IN: draw source where there was destination content
* (unbounded)
* (unbounded) (Since 1.0)
* @CAIRO_OPERATOR_OUT: draw source where there was no destination
* content (unbounded)
* content (unbounded) (Since 1.0)
* @CAIRO_OPERATOR_ATOP: draw source on top of destination content and
* only there
* @CAIRO_OPERATOR_DEST: ignore the source
* @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source
* only there (Since 1.0)
* @CAIRO_OPERATOR_DEST: ignore the source (Since 1.0)
* @CAIRO_OPERATOR_DEST_OVER: draw destination on top of source (Since 1.0)
* @CAIRO_OPERATOR_DEST_IN: leave destination only where there was
* source content (unbounded)
* source content (unbounded) (Since 1.0)
* @CAIRO_OPERATOR_DEST_OUT: leave destination only where there was no
* source content
* source content (Since 1.0)
* @CAIRO_OPERATOR_DEST_ATOP: leave destination on top of source content
* and only there (unbounded)
* and only there (unbounded) (Since 1.0)
* @CAIRO_OPERATOR_XOR: source and destination are shown where there is only
* one of them
* @CAIRO_OPERATOR_ADD: source and destination layers are accumulated
* one of them (Since 1.0)
* @CAIRO_OPERATOR_ADD: source and destination layers are accumulated (Since 1.0)
* @CAIRO_OPERATOR_SATURATE: like over, but assuming source and dest are
* disjoint geometries
* disjoint geometries (Since 1.0)
* @CAIRO_OPERATOR_MULTIPLY: source and destination layers are multiplied.
* This causes the result to be at least as dark as the darker inputs.
* This causes the result to be at least as dark as the darker inputs. (Since 1.10)
* @CAIRO_OPERATOR_SCREEN: source and destination are complemented and
* multiplied. This causes the result to be at least as light as the lighter
* inputs.
* inputs. (Since 1.10)
* @CAIRO_OPERATOR_OVERLAY: multiplies or screens, depending on the
* lightness of the destination color.
* lightness of the destination color. (Since 1.10)
* @CAIRO_OPERATOR_DARKEN: replaces the destination with the source if it
* is darker, otherwise keeps the source.
* is darker, otherwise keeps the source. (Since 1.10)
* @CAIRO_OPERATOR_LIGHTEN: replaces the destination with the source if it
* is lighter, otherwise keeps the source.
* is lighter, otherwise keeps the source. (Since 1.10)
* @CAIRO_OPERATOR_COLOR_DODGE: brightens the destination color to reflect
* the source color.
* the source color. (Since 1.10)
* @CAIRO_OPERATOR_COLOR_BURN: darkens the destination color to reflect
* the source color.
* @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependant on source
* color.
* @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependant on source
* color.
* the source color. (Since 1.10)
* @CAIRO_OPERATOR_HARD_LIGHT: Multiplies or screens, dependent on source
* color. (Since 1.10)
* @CAIRO_OPERATOR_SOFT_LIGHT: Darkens or lightens, dependent on source
* color. (Since 1.10)
* @CAIRO_OPERATOR_DIFFERENCE: Takes the difference of the source and
* destination color.
* destination color. (Since 1.10)
* @CAIRO_OPERATOR_EXCLUSION: Produces an effect similar to difference, but
* with lower contrast.
* with lower contrast. (Since 1.10)
* @CAIRO_OPERATOR_HSL_HUE: Creates a color with the hue of the source
* and the saturation and luminosity of the target.
* and the saturation and luminosity of the target. (Since 1.10)
* @CAIRO_OPERATOR_HSL_SATURATION: Creates a color with the saturation
* of the source and the hue and luminosity of the target. Painting with
* this mode onto a gray area prduces no change.
* this mode onto a gray area produces no change. (Since 1.10)
* @CAIRO_OPERATOR_HSL_COLOR: Creates a color with the hue and saturation
* of the source and the luminosity of the target. This preserves the gray
* levels of the target and is useful for coloring monochrome images or
* tinting color images.
* tinting color images. (Since 1.10)
* @CAIRO_OPERATOR_HSL_LUMINOSITY: Creates a color with the luminosity of
* the source and the hue and saturation of the target. This produces an
* inverse effect to @CAIRO_OPERATOR_HSL_COLOR.
* inverse effect to @CAIRO_OPERATOR_HSL_COLOR. (Since 1.10)
*
* #cairo_operator_t is used to set the compositing operator for all cairo
* drawing operations.
506,6 → 596,8
* For a more detailed explanation of the effects of each operator, including
* the mathematical definitions, see
* <ulink url="http://cairographics.org/operators/">http://cairographics.org/operators/</ulink>.
*
* Since: 1.0
**/
typedef enum _cairo_operator {
CAIRO_OPERATOR_CLEAR,
569,21 → 661,52
/**
* cairo_antialias_t:
* @CAIRO_ANTIALIAS_DEFAULT: Use the default antialiasing for
* the subsystem and target device
* @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask
* the subsystem and target device, since 1.0
* @CAIRO_ANTIALIAS_NONE: Use a bilevel alpha mask, since 1.0
* @CAIRO_ANTIALIAS_GRAY: Perform single-color antialiasing (using
* shades of gray for black text on a white background, for example).
* shades of gray for black text on a white background, for example), since 1.0
* @CAIRO_ANTIALIAS_SUBPIXEL: Perform antialiasing by taking
* advantage of the order of subpixel elements on devices
* such as LCD panels
* such as LCD panels, since 1.0
* @CAIRO_ANTIALIAS_FAST: Hint that the backend should perform some
* antialiasing but prefer speed over quality, since 1.12
* @CAIRO_ANTIALIAS_GOOD: The backend should balance quality against
* performance, since 1.12
* @CAIRO_ANTIALIAS_BEST: Hint that the backend should render at the highest
* quality, sacrificing speed if necessary, since 1.12
*
* Specifies the type of antialiasing to do when rendering text or shapes.
*
* As it is not necessarily clear from the above what advantages a particular
* antialias method provides, since 1.12, there is also a set of hints:
* @CAIRO_ANTIALIAS_FAST: Allow the backend to degrade raster quality for speed
* @CAIRO_ANTIALIAS_GOOD: A balance between speed and quality
* @CAIRO_ANTIALIAS_BEST: A high-fidelity, but potentially slow, raster mode
*
* These make no guarantee on how the backend will perform its rasterisation
* (if it even rasterises!), nor that they have any differing effect other
* than to enable some form of antialiasing. In the case of glyph rendering,
* @CAIRO_ANTIALIAS_FAST and @CAIRO_ANTIALIAS_GOOD will be mapped to
* @CAIRO_ANTIALIAS_GRAY, with @CAIRO_ANTALIAS_BEST being equivalent to
* @CAIRO_ANTIALIAS_SUBPIXEL.
*
* The interpretation of @CAIRO_ANTIALIAS_DEFAULT is left entirely up to
* the backend, typically this will be similar to @CAIRO_ANTIALIAS_GOOD.
*
* Since: 1.0
**/
typedef enum _cairo_antialias {
CAIRO_ANTIALIAS_DEFAULT,
 
/* method */
CAIRO_ANTIALIAS_NONE,
CAIRO_ANTIALIAS_GRAY,
CAIRO_ANTIALIAS_SUBPIXEL
CAIRO_ANTIALIAS_SUBPIXEL,
 
/* hints */
CAIRO_ANTIALIAS_FAST,
CAIRO_ANTIALIAS_GOOD,
CAIRO_ANTIALIAS_BEST
} cairo_antialias_t;
 
cairo_public void
595,11 → 718,11
* left-to-right, counts +1. If the path crosses the ray
* from right to left, counts -1. (Left and right are determined
* from the perspective of looking along the ray from the starting
* point.) If the total count is non-zero, the point will be filled.
* point.) If the total count is non-zero, the point will be filled. (Since 1.0)
* @CAIRO_FILL_RULE_EVEN_ODD: Counts the total number of
* intersections, without regard to the orientation of the contour. If
* the total number of intersections is odd, the point will be
* filled.
* filled. (Since 1.0)
*
* #cairo_fill_rule_t is used to select how paths are filled. For both
* fill rules, whether or not a point is included in the fill is
613,6 → 736,8
* The default fill rule is %CAIRO_FILL_RULE_WINDING.
*
* New entries may be added in future versions.
*
* Since: 1.0
**/
typedef enum _cairo_fill_rule {
CAIRO_FILL_RULE_WINDING,
627,13 → 752,15
 
/**
* cairo_line_cap_t:
* @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point
* @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point
* @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point
* @CAIRO_LINE_CAP_BUTT: start(stop) the line exactly at the start(end) point (Since 1.0)
* @CAIRO_LINE_CAP_ROUND: use a round ending, the center of the circle is the end point (Since 1.0)
* @CAIRO_LINE_CAP_SQUARE: use squared ending, the center of the square is the end point (Since 1.0)
*
* Specifies how to render the endpoints of the path when stroking.
*
* The default line cap style is %CAIRO_LINE_CAP_BUTT.
*
* Since: 1.0
**/
typedef enum _cairo_line_cap {
CAIRO_LINE_CAP_BUTT,
647,15 → 774,17
/**
* cairo_line_join_t:
* @CAIRO_LINE_JOIN_MITER: use a sharp (angled) corner, see
* cairo_set_miter_limit()
* cairo_set_miter_limit() (Since 1.0)
* @CAIRO_LINE_JOIN_ROUND: use a rounded join, the center of the circle is the
* joint point
* joint point (Since 1.0)
* @CAIRO_LINE_JOIN_BEVEL: use a cut-off join, the join is cut off at half
* the line width from the joint point
* the line width from the joint point (Since 1.0)
*
* Specifies how to render the junction of two lines when stroking.
*
* The default line join style is %CAIRO_LINE_JOIN_MITER.
*
* Since: 1.0
**/
typedef enum _cairo_line_join {
CAIRO_LINE_JOIN_MITER,
902,6 → 1031,8
*
* Memory management of #cairo_scaled_font_t is done with
* cairo_scaled_font_reference() and cairo_scaled_font_destroy().
*
* Since: 1.0
**/
typedef struct _cairo_scaled_font cairo_scaled_font_t;
 
921,6 → 1052,8
*
* Memory management of #cairo_font_face_t is done with
* cairo_font_face_reference() and cairo_font_face_destroy().
*
* Since: 1.0
**/
typedef struct _cairo_font_face cairo_font_face_t;
 
947,6 → 1080,8
* Note that the offsets given by @x and @y are not cumulative. When
* drawing or measuring text, each glyph is individually positioned
* with respect to the overall origin
*
* Since: 1.0
**/
typedef struct {
unsigned long index;
994,7 → 1129,7
/**
* cairo_text_cluster_flags_t:
* @CAIRO_TEXT_CLUSTER_FLAG_BACKWARD: The clusters in the cluster array
* map to glyphs in the glyph array from end to start.
* map to glyphs in the glyph array from end to start. (Since 1.8)
*
* Specifies properties of a text cluster mapping.
*
1030,6 → 1165,8
* doubled. They will change slightly due to hinting (so you can't
* assume that metrics are independent of the transformation matrix),
* but otherwise will remain unchanged.
*
* Since: 1.0
**/
typedef struct {
double x_bearing;
1052,7 → 1189,7
* portions below the baseline. Note that this is not always
* exactly equal to the maximum of the extents of all the
* glyphs in the font, but rather is picked to express the
* font designer's intent as to how the the font should
* font designer's intent as to how the font should
* align with elements below it.
* @height: the recommended vertical distance between baselines when
* setting consecutive lines of text with the font. This
1062,10 → 1199,10
* is at a premium, most fonts can be set with only
* a distance of @ascent+@descent between lines.
* @max_x_advance: the maximum distance in the X direction that
* the the origin is advanced for any glyph in the font.
* the origin is advanced for any glyph in the font.
* @max_y_advance: the maximum distance in the Y direction that
* the the origin is advanced for any glyph in the font.
* this will be zero for normal fonts used for horizontal
* the origin is advanced for any glyph in the font.
* This will be zero for normal fonts used for horizontal
* writing. (The scripts of East Asia are sometimes written
* vertically.)
*
1080,6 → 1217,8
* not be doubled. They will change slightly due to hinting (so you
* can't assume that metrics are independent of the transformation
* matrix), but otherwise will remain unchanged.
*
* Since: 1.0
**/
typedef struct {
double ascent;
1091,11 → 1230,13
 
/**
* cairo_font_slant_t:
* @CAIRO_FONT_SLANT_NORMAL: Upright font style
* @CAIRO_FONT_SLANT_ITALIC: Italic font style
* @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style
* @CAIRO_FONT_SLANT_NORMAL: Upright font style, since 1.0
* @CAIRO_FONT_SLANT_ITALIC: Italic font style, since 1.0
* @CAIRO_FONT_SLANT_OBLIQUE: Oblique font style, since 1.0
*
* Specifies variants of a font face based on their slant.
*
* Since: 1.0
**/
typedef enum _cairo_font_slant {
CAIRO_FONT_SLANT_NORMAL,
1105,10 → 1246,12
 
/**
* cairo_font_weight_t:
* @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight
* @CAIRO_FONT_WEIGHT_BOLD: Bold font weight
* @CAIRO_FONT_WEIGHT_NORMAL: Normal font weight, since 1.0
* @CAIRO_FONT_WEIGHT_BOLD: Bold font weight, since 1.0
*
* Specifies variants of a font face based on their weight.
*
* Since: 1.0
**/
typedef enum _cairo_font_weight {
CAIRO_FONT_WEIGHT_NORMAL,
1118,19 → 1261,21
/**
* cairo_subpixel_order_t:
* @CAIRO_SUBPIXEL_ORDER_DEFAULT: Use the default subpixel order for
* for the target device
* for the target device, since 1.0
* @CAIRO_SUBPIXEL_ORDER_RGB: Subpixel elements are arranged horizontally
* with red at the left
* with red at the left, since 1.0
* @CAIRO_SUBPIXEL_ORDER_BGR: Subpixel elements are arranged horizontally
* with blue at the left
* with blue at the left, since 1.0
* @CAIRO_SUBPIXEL_ORDER_VRGB: Subpixel elements are arranged vertically
* with red at the top
* with red at the top, since 1.0
* @CAIRO_SUBPIXEL_ORDER_VBGR: Subpixel elements are arranged vertically
* with blue at the top
* with blue at the top, since 1.0
*
* The subpixel order specifies the order of color elements within
* each pixel on the display device when rendering with an
* antialiasing mode of %CAIRO_ANTIALIAS_SUBPIXEL.
*
* Since: 1.0
**/
typedef enum _cairo_subpixel_order {
CAIRO_SUBPIXEL_ORDER_DEFAULT,
1143,15 → 1288,15
/**
* cairo_hint_style_t:
* @CAIRO_HINT_STYLE_DEFAULT: Use the default hint style for
* font backend and target device
* @CAIRO_HINT_STYLE_NONE: Do not hint outlines
* font backend and target device, since 1.0
* @CAIRO_HINT_STYLE_NONE: Do not hint outlines, since 1.0
* @CAIRO_HINT_STYLE_SLIGHT: Hint outlines slightly to improve
* contrast while retaining good fidelity to the original
* shapes.
* shapes, since 1.0
* @CAIRO_HINT_STYLE_MEDIUM: Hint outlines with medium strength
* giving a compromise between fidelity to the original shapes
* and contrast
* @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast
* and contrast, since 1.0
* @CAIRO_HINT_STYLE_FULL: Hint outlines to maximize contrast, since 1.0
*
* Specifies the type of hinting to do on font outlines. Hinting
* is the process of fitting outlines to the pixel grid in order
1161,6 → 1306,8
* styles are supported by all font backends.
*
* New entries may be added in future versions.
*
* Since: 1.0
**/
typedef enum _cairo_hint_style {
CAIRO_HINT_STYLE_DEFAULT,
1173,9 → 1320,9
/**
* cairo_hint_metrics_t:
* @CAIRO_HINT_METRICS_DEFAULT: Hint metrics in the default
* manner for the font backend and target device
* @CAIRO_HINT_METRICS_OFF: Do not hint font metrics
* @CAIRO_HINT_METRICS_ON: Hint font metrics
* manner for the font backend and target device, since 1.0
* @CAIRO_HINT_METRICS_OFF: Do not hint font metrics, since 1.0
* @CAIRO_HINT_METRICS_ON: Hint font metrics, since 1.0
*
* Specifies whether to hint font metrics; hinting font metrics
* means quantizing them so that they are integer values in
1182,6 → 1329,8
* device space. Doing this improves the consistency of
* letter and line spacing, however it also means that text
* will be laid out differently at different zoom factors.
*
* Since: 1.0
**/
typedef enum _cairo_hint_metrics {
CAIRO_HINT_METRICS_DEFAULT,
1197,8 → 1346,8
*
* Individual features of a #cairo_font_options_t can be set or
* accessed using functions named
* cairo_font_options_set_<emphasis>feature_name</emphasis> and
* cairo_font_options_get_<emphasis>feature_name</emphasis>, like
* <function>cairo_font_options_set_<emphasis>feature_name</emphasis>()</function> and
* <function>cairo_font_options_get_<emphasis>feature_name</emphasis>()</function>, like
* cairo_font_options_set_antialias() and
* cairo_font_options_get_antialias().
*
1208,6 → 1357,8
* cairo_font_options_hash() should be used to copy, check
* for equality, merge, or compute a hash value of
* #cairo_font_options_t objects.
*
* Since: 1.0
**/
typedef struct _cairo_font_options cairo_font_options_t;
 
1352,10 → 1503,11
 
/**
* cairo_font_type_t:
* @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api
* @CAIRO_FONT_TYPE_FT: The font is of type FreeType
* @CAIRO_FONT_TYPE_WIN32: The font is of type Win32
* @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6)
* @CAIRO_FONT_TYPE_TOY: The font was created using cairo's toy font api (Since: 1.2)
* @CAIRO_FONT_TYPE_FT: The font is of type FreeType (Since: 1.2)
* @CAIRO_FONT_TYPE_WIN32: The font is of type Win32 (Since: 1.2)
* @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6, in 1.2 and
* 1.4 it was named CAIRO_FONT_TYPE_ATSUI)
* @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8)
*
* #cairo_font_type_t is used to describe the type of a given font
1364,8 → 1516,8
*
* The type of a font face is determined by the function used to
* create it, which will generally be of the form
* cairo_<emphasis>type</emphasis>_font_face_create(). The font face type can be queried
* with cairo_font_face_get_type()
* <function>cairo_<emphasis>type</emphasis>_font_face_create(<!-- -->)</function>.
* The font face type can be queried with cairo_font_face_get_type()
*
* The various #cairo_font_face_t functions can be used with a font face
* of any type.
1378,7 → 1530,8
* fonts of any type, but some font backends also provide
* type-specific functions that must only be called with a scaled font
* of the appropriate type. These functions have names that begin with
* cairo_<emphasis>type</emphasis>_scaled_font() such as cairo_ft_scaled_font_lock_face().
* <function>cairo_<emphasis>type</emphasis>_scaled_font(<!-- -->)</function>
* such as cairo_ft_scaled_font_lock_face().
*
* The behavior of calling a type-specific function with a scaled font
* of the wrong type is undefined.
1624,7 → 1777,8
* using cairo_glyph_allocate() and placed in @glyphs. Upon return,
* @num_glyphs should contain the number of generated glyphs. If the value
* @glyphs points at has changed after the call, the caller will free the
* allocated glyph array using cairo_glyph_free().
* allocated glyph array using cairo_glyph_free(). The caller will also free
* the original value of @glyphs, so the callback shouldn't do so.
* The callback should populate the glyph indices and positions (in font space)
* assuming that the text is to be shown at the origin.
*
1635,8 → 1789,9
* as a cluster buffer, and @num_clusters points to the number of cluster
* entries available there. If the provided cluster array is too short for
* the conversion (or for convenience), a new cluster array may be allocated
* using cairo_text_cluster_allocate() and placed in @clusters. Upon return,
* @num_clusters should contain the number of generated clusters.
* using cairo_text_cluster_allocate() and placed in @clusters. In this case,
* the original value of @clusters will still be freed by the caller. Upon
* return, @num_clusters should contain the number of generated clusters.
* If the value @clusters points at has changed after the call, the caller
* will free the allocated cluster array using cairo_text_cluster_free().
*
1795,14 → 1950,16
 
/**
* cairo_path_data_type_t:
* @CAIRO_PATH_MOVE_TO: A move-to operation
* @CAIRO_PATH_LINE_TO: A line-to operation
* @CAIRO_PATH_CURVE_TO: A curve-to operation
* @CAIRO_PATH_CLOSE_PATH: A close-path operation
* @CAIRO_PATH_MOVE_TO: A move-to operation, since 1.0
* @CAIRO_PATH_LINE_TO: A line-to operation, since 1.0
* @CAIRO_PATH_CURVE_TO: A curve-to operation, since 1.0
* @CAIRO_PATH_CLOSE_PATH: A close-path operation, since 1.0
*
* #cairo_path_data_t is used to describe the type of one portion
* of a path when represented as a #cairo_path_t.
* See #cairo_path_data_t for details.
*
* Since: 1.0
**/
typedef enum _cairo_path_data_type {
CAIRO_PATH_MOVE_TO,
1876,6 → 2033,8
* always use <literal>data->header.length</literal> to
* iterate over the path data, instead of hardcoding the number of
* elements for each element type.
*
* Since: 1.0
**/
typedef union _cairo_path_data_t cairo_path_data_t;
union _cairo_path_data_t {
1906,6 → 2065,8
* array. This number is larger than the number of independent path
* portions (defined in #cairo_path_data_type_t), since the data
* includes both headers and coordinates for each portion.
*
* Since: 1.0
**/
typedef struct cairo_path {
cairo_status_t status;
1941,13 → 2102,15
 
/**
* cairo_device_type_t:
* @CAIRO_DEVICE_TYPE_DRM: The surface is of type Direct Render Manager
* @CAIRO_DEVICE_TYPE_GL: The surface is of type OpenGL
* @CAIRO_DEVICE_TYPE_SCRIPT: The surface is of type script
* @CAIRO_DEVICE_TYPE_XCB: The surface is of type xcb
* @CAIRO_DEVICE_TYPE_XLIB: The surface is of type xlib
* @CAIRO_DEVICE_TYPE_XML: The surface is of type XML
* cairo_surface_create_for_rectangle()
* @CAIRO_DEVICE_TYPE_DRM: The device is of type Direct Render Manager, since 1.10
* @CAIRO_DEVICE_TYPE_GL: The device is of type OpenGL, since 1.10
* @CAIRO_DEVICE_TYPE_SCRIPT: The device is of type script, since 1.10
* @CAIRO_DEVICE_TYPE_XCB: The device is of type xcb, since 1.10
* @CAIRO_DEVICE_TYPE_XLIB: The device is of type xlib, since 1.10
* @CAIRO_DEVICE_TYPE_XML: The device is of type XML, since 1.10
* @CAIRO_DEVICE_TYPE_COGL: The device is of type cogl, since 1.12
* @CAIRO_DEVICE_TYPE_WIN32: The device is of type win32, since 1.12
* @CAIRO_DEVICE_TYPE_INVALID: The device is invalid, since 1.10
*
* #cairo_device_type_t is used to describe the type of a given
* device. The devices types are also known as "backends" within cairo.
1954,13 → 2117,14
*
* The device type can be queried with cairo_device_get_type()
*
* The various #cairo_device_t functions can be used with surfaces of
* The various #cairo_device_t functions can be used with devices of
* any type, but some backends also provide type-specific functions
* that must only be called with a device of the appropriate
* type. These functions have names that begin with
* cairo_<emphasis>type</emphasis>_device<!-- --> such as cairo_xcb_device_debug_set_render_version().
* <literal>cairo_<emphasis>type</emphasis>_device</literal> such as
* cairo_xcb_device_debug_cap_xrender_version().
*
* The behavior of calling a type-specific function with a surface of
* The behavior of calling a type-specific function with a device of
* the wrong type is undefined.
*
* New entries may be added in future versions.
1973,7 → 2137,11
CAIRO_DEVICE_TYPE_SCRIPT,
CAIRO_DEVICE_TYPE_XCB,
CAIRO_DEVICE_TYPE_XLIB,
CAIRO_DEVICE_TYPE_XML
CAIRO_DEVICE_TYPE_XML,
CAIRO_DEVICE_TYPE_COGL,
CAIRO_DEVICE_TYPE_WIN32,
 
CAIRO_DEVICE_TYPE_INVALID = -1
} cairo_device_type_t;
 
cairo_public cairo_device_type_t
2020,6 → 2188,20
int height);
 
cairo_public cairo_surface_t *
cairo_surface_create_similar_image (cairo_surface_t *other,
cairo_format_t format,
int width,
int height);
 
cairo_public cairo_surface_t *
cairo_surface_map_to_image (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents);
 
cairo_public void
cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_surface_t *image);
 
cairo_public cairo_surface_t *
cairo_surface_create_for_rectangle (cairo_surface_t *target,
double x,
double y,
2026,7 → 2208,85
double width,
double height);
 
typedef enum {
CAIRO_SURFACE_OBSERVER_NORMAL = 0,
CAIRO_SURFACE_OBSERVER_RECORD_OPERATIONS = 0x1
} cairo_surface_observer_mode_t;
 
cairo_public cairo_surface_t *
cairo_surface_create_observer (cairo_surface_t *target,
cairo_surface_observer_mode_t mode);
 
typedef void (*cairo_surface_observer_callback_t) (cairo_surface_t *observer,
cairo_surface_t *target,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_paint_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_mask_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_fill_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_stroke_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_glyphs_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_flush_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_add_finish_callback (cairo_surface_t *abstract_surface,
cairo_surface_observer_callback_t func,
void *data);
 
cairo_public cairo_status_t
cairo_surface_observer_print (cairo_surface_t *surface,
cairo_write_func_t write_func,
void *closure);
cairo_public double
cairo_surface_observer_elapsed (cairo_surface_t *surface);
 
cairo_public cairo_status_t
cairo_device_observer_print (cairo_device_t *device,
cairo_write_func_t write_func,
void *closure);
 
cairo_public double
cairo_device_observer_elapsed (cairo_device_t *device);
 
cairo_public double
cairo_device_observer_paint_elapsed (cairo_device_t *device);
 
cairo_public double
cairo_device_observer_mask_elapsed (cairo_device_t *device);
 
cairo_public double
cairo_device_observer_fill_elapsed (cairo_device_t *device);
 
cairo_public double
cairo_device_observer_stroke_elapsed (cairo_device_t *device);
 
cairo_public double
cairo_device_observer_glyphs_elapsed (cairo_device_t *device);
 
cairo_public cairo_surface_t *
cairo_surface_reference (cairo_surface_t *surface);
 
cairo_public void
2046,20 → 2306,20
 
/**
* cairo_surface_type_t:
* @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image
* @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf
* @CAIRO_SURFACE_TYPE_PS: The surface is of type ps
* @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib
* @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb
* @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz
* @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz
* @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32
* @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos
* @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb
* @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg
* @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2
* @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface
* @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image
* @CAIRO_SURFACE_TYPE_IMAGE: The surface is of type image, since 1.2
* @CAIRO_SURFACE_TYPE_PDF: The surface is of type pdf, since 1.2
* @CAIRO_SURFACE_TYPE_PS: The surface is of type ps, since 1.2
* @CAIRO_SURFACE_TYPE_XLIB: The surface is of type xlib, since 1.2
* @CAIRO_SURFACE_TYPE_XCB: The surface is of type xcb, since 1.2
* @CAIRO_SURFACE_TYPE_GLITZ: The surface is of type glitz, since 1.2
* @CAIRO_SURFACE_TYPE_QUARTZ: The surface is of type quartz, since 1.2
* @CAIRO_SURFACE_TYPE_WIN32: The surface is of type win32, since 1.2
* @CAIRO_SURFACE_TYPE_BEOS: The surface is of type beos, since 1.2
* @CAIRO_SURFACE_TYPE_DIRECTFB: The surface is of type directfb, since 1.2
* @CAIRO_SURFACE_TYPE_SVG: The surface is of type svg, since 1.2
* @CAIRO_SURFACE_TYPE_OS2: The surface is of type os2, since 1.4
* @CAIRO_SURFACE_TYPE_WIN32_PRINTING: The surface is a win32 printing surface, since 1.6
* @CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: The surface is of type quartz_image, since 1.6
* @CAIRO_SURFACE_TYPE_SCRIPT: The surface is of type script, since 1.10
* @CAIRO_SURFACE_TYPE_QT: The surface is of type Qt, since 1.10
* @CAIRO_SURFACE_TYPE_RECORDING: The surface is of type recording, since 1.10
2071,6 → 2331,7
* @CAIRO_SURFACE_TYPE_SKIA: The surface is of type Skia, since 1.10
* @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
* cairo_surface_create_for_rectangle(), since 1.10
* @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl, since 1.12
*
* #cairo_surface_type_t is used to describe the type of a given
* surface. The surface types are also known as "backends" or "surface
2077,7 → 2338,8
* backends" within cairo.
*
* The type of a surface is determined by the function used to create
* it, which will generally be of the form cairo_<emphasis>type</emphasis>_surface_create(),
* it, which will generally be of the form
* <function>cairo_<emphasis>type</emphasis>_surface_create(<!-- -->)</function>,
* (though see cairo_surface_create_similar() as well).
*
* The surface type can be queried with cairo_surface_get_type()
2086,7 → 2348,7
* any type, but some backends also provide type-specific functions
* that must only be called with a surface of the appropriate
* type. These functions have names that begin with
* cairo_<emphasis>type</emphasis>_surface<!-- --> such as cairo_image_surface_get_width().
* <literal>cairo_<emphasis>type</emphasis>_surface</literal> such as cairo_image_surface_get_width().
*
* The behavior of calling a type-specific function with a surface of
* the wrong type is undefined.
2119,7 → 2381,8
CAIRO_SURFACE_TYPE_TEE,
CAIRO_SURFACE_TYPE_XML,
CAIRO_SURFACE_TYPE_SKIA,
CAIRO_SURFACE_TYPE_SUBSURFACE
CAIRO_SURFACE_TYPE_SUBSURFACE,
CAIRO_SURFACE_TYPE_COGL
} cairo_surface_type_t;
 
cairo_public cairo_surface_type_t
2155,6 → 2418,7
#define CAIRO_MIME_TYPE_PNG "image/png"
#define CAIRO_MIME_TYPE_JP2 "image/jp2"
#define CAIRO_MIME_TYPE_URI "text/x-uri"
#define CAIRO_MIME_TYPE_UNIQUE_ID "application/x-cairo.uuid"
 
cairo_public void
cairo_surface_get_mime_data (cairo_surface_t *surface,
2170,6 → 2434,10
cairo_destroy_func_t destroy,
void *closure);
 
cairo_public cairo_bool_t
cairo_surface_supports_mime_type (cairo_surface_t *surface,
const char *mime_type);
 
cairo_public void
cairo_surface_get_font_options (cairo_surface_t *surface,
cairo_font_options_t *options);
2218,43 → 2486,6
 
/* Image-surface functions */
 
/**
* cairo_format_t:
* @CAIRO_FORMAT_INVALID: no such format exists or is supported.
* @CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with
* alpha in the upper 8 bits, then red, then green, then blue.
* The 32-bit quantities are stored native-endian. Pre-multiplied
* alpha is used. (That is, 50% transparent red is 0x80800000,
* not 0x80ff0000.)
* @CAIRO_FORMAT_RGB24: each pixel is a 32-bit quantity, with
* the upper 8 bits unused. Red, Green, and Blue are stored
* in the remaining 24 bits in that order.
* @CAIRO_FORMAT_A8: each pixel is a 8-bit quantity holding
* an alpha value.
* @CAIRO_FORMAT_A1: each pixel is a 1-bit quantity holding
* an alpha value. Pixels are packed together into 32-bit
* quantities. The ordering of the bits matches the
* endianess of the platform. On a big-endian machine, the
* first pixel is in the uppermost bit, on a little-endian
* machine the first pixel is in the least-significant bit.
* @CAIRO_FORMAT_RGB16_565: each pixel is a 16-bit quantity
* with red in the upper 5 bits, then green in the middle
* 6 bits, and blue in the lower 5 bits.
*
* #cairo_format_t is used to identify the memory format of
* image data.
*
* New entries may be added in future versions.
**/
typedef enum _cairo_format {
CAIRO_FORMAT_INVALID = -1,
CAIRO_FORMAT_ARGB32 = 0,
CAIRO_FORMAT_RGB24 = 1,
CAIRO_FORMAT_A8 = 2,
CAIRO_FORMAT_A1 = 3,
CAIRO_FORMAT_RGB16_565 = 4
} cairo_format_t;
 
cairo_public cairo_surface_t *
cairo_image_surface_create (cairo_format_t format,
int width,
2310,6 → 2541,155
double *width,
double *height);
 
cairo_public cairo_bool_t
cairo_recording_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_t *extents);
 
/* raster-source pattern (callback) functions */
 
/**
* cairo_raster_source_acquire_func_t:
* @pattern: the pattern being rendered from
* @callback_data: the user data supplied during creation
* @target: the rendering target surface
* @extents: rectangular region of interest in pixels in sample space
*
* #cairo_raster_source_acquire_func_t is the type of function which is
* called when a pattern is being rendered from. It should create a surface
* that provides the pixel data for the region of interest as defined by
* extents, though the surface itself does not have to be limited to that
* area. For convenience the surface should probably be of image type,
* created with cairo_surface_create_similar_image() for the target (which
* enables the number of copies to be reduced during transfer to the
* device). Another option, might be to return a similar surface to the
* target for explicit handling by the application of a set of cached sources
* on the device. The region of sample data provided should be defined using
* cairo_surface_set_device_offset() to specify the top-left corner of the
* sample data (along with width and height of the surface).
*
* Returns: a #cairo_surface_t
*
* Since: 1.12
**/
typedef cairo_surface_t *
(*cairo_raster_source_acquire_func_t) (cairo_pattern_t *pattern,
void *callback_data,
cairo_surface_t *target,
const cairo_rectangle_int_t *extents);
 
/**
* cairo_raster_source_release_func_t:
* @pattern: the pattern being rendered from
* @callback_data: the user data supplied during creation
* @surface: the surface created during acquire
*
* #cairo_raster_source_release_func_t is the type of function which is
* called when the pixel data is no longer being access by the pattern
* for the rendering operation. Typically this function will simply
* destroy the surface created during acquire.
*
* Since: 1.12
**/
typedef void
(*cairo_raster_source_release_func_t) (cairo_pattern_t *pattern,
void *callback_data,
cairo_surface_t *surface);
 
/**
* cairo_raster_source_snapshot_func_t:
* @pattern: the pattern being rendered from
* @callback_data: the user data supplied during creation
*
* #cairo_raster_source_snapshot_func_t is the type of function which is
* called when the pixel data needs to be preserved for later use
* during printing. This pattern will be accessed again later, and it
* is expected to provide the pixel data that was current at the time
* of snapshotting.
*
* Return value: CAIRO_STATUS_SUCCESS on success, or one of the
* #cairo_status_t error codes for failure.
*
* Since: 1.12
**/
typedef cairo_status_t
(*cairo_raster_source_snapshot_func_t) (cairo_pattern_t *pattern,
void *callback_data);
 
/**
* cairo_raster_source_copy_func_t:
* @pattern: the #cairo_pattern_t that was copied to
* @callback_data: the user data supplied during creation
* @other: the #cairo_pattern_t being used as the source for the copy
*
* #cairo_raster_source_copy_func_t is the type of function which is
* called when the pattern gets copied as a normal part of rendering.
*
* Return value: CAIRO_STATUS_SUCCESS on success, or one of the
* #cairo_status_t error codes for failure.
*
* Since: 1.12
**/
typedef cairo_status_t
(*cairo_raster_source_copy_func_t) (cairo_pattern_t *pattern,
void *callback_data,
const cairo_pattern_t *other);
 
/**
* cairo_raster_source_finish_func_t:
* @pattern: the pattern being rendered from
* @callback_data: the user data supplied during creation
*
* #cairo_raster_source_finish_func_t is the type of function which is
* called when the pattern (or a copy thereof) is no longer required.
*
* Since: 1.12
**/
typedef void
(*cairo_raster_source_finish_func_t) (cairo_pattern_t *pattern,
void *callback_data);
 
cairo_public cairo_pattern_t *
cairo_pattern_create_raster_source (void *user_data,
cairo_content_t content,
int width, int height);
 
cairo_public void
cairo_raster_source_pattern_set_callback_data (cairo_pattern_t *pattern,
void *data);
 
cairo_public void *
cairo_raster_source_pattern_get_callback_data (cairo_pattern_t *pattern);
 
cairo_public void
cairo_raster_source_pattern_set_acquire (cairo_pattern_t *pattern,
cairo_raster_source_acquire_func_t acquire,
cairo_raster_source_release_func_t release);
 
cairo_public void
cairo_raster_source_pattern_get_acquire (cairo_pattern_t *pattern,
cairo_raster_source_acquire_func_t *acquire,
cairo_raster_source_release_func_t *release);
cairo_public void
cairo_raster_source_pattern_set_snapshot (cairo_pattern_t *pattern,
cairo_raster_source_snapshot_func_t snapshot);
 
cairo_public cairo_raster_source_snapshot_func_t
cairo_raster_source_pattern_get_snapshot (cairo_pattern_t *pattern);
 
cairo_public void
cairo_raster_source_pattern_set_copy (cairo_pattern_t *pattern,
cairo_raster_source_copy_func_t copy);
 
cairo_public cairo_raster_source_copy_func_t
cairo_raster_source_pattern_get_copy (cairo_pattern_t *pattern);
 
cairo_public void
cairo_raster_source_pattern_set_finish (cairo_pattern_t *pattern,
cairo_raster_source_finish_func_t finish);
 
cairo_public cairo_raster_source_finish_func_t
cairo_raster_source_pattern_get_finish (cairo_pattern_t *pattern);
 
/* Pattern creation functions */
 
cairo_public cairo_pattern_t *
2331,6 → 2711,9
double cx1, double cy1, double radius1);
 
cairo_public cairo_pattern_t *
cairo_pattern_create_mesh (void);
 
cairo_public cairo_pattern_t *
cairo_pattern_reference (cairo_pattern_t *pattern);
 
cairo_public void
2355,10 → 2738,12
/**
* cairo_pattern_type_t:
* @CAIRO_PATTERN_TYPE_SOLID: The pattern is a solid (uniform)
* color. It may be opaque or translucent.
* @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image).
* @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient.
* @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient.
* color. It may be opaque or translucent, since 1.2.
* @CAIRO_PATTERN_TYPE_SURFACE: The pattern is a based on a surface (an image), since 1.2.
* @CAIRO_PATTERN_TYPE_LINEAR: The pattern is a linear gradient, since 1.2.
* @CAIRO_PATTERN_TYPE_RADIAL: The pattern is a radial gradient, since 1.2.
* @CAIRO_PATTERN_TYPE_MESH: The pattern is a mesh, since 1.12.
* @CAIRO_PATTERN_TYPE_RASTER_SOURCE: The pattern is a user pattern providing raster data, since 1.12.
*
* #cairo_pattern_type_t is used to describe the type of a given pattern.
*
2386,7 → 2771,9
CAIRO_PATTERN_TYPE_SOLID,
CAIRO_PATTERN_TYPE_SURFACE,
CAIRO_PATTERN_TYPE_LINEAR,
CAIRO_PATTERN_TYPE_RADIAL
CAIRO_PATTERN_TYPE_RADIAL,
CAIRO_PATTERN_TYPE_MESH,
CAIRO_PATTERN_TYPE_RASTER_SOURCE
} cairo_pattern_type_t;
 
cairo_public cairo_pattern_type_t
2404,6 → 2791,42
double alpha);
 
cairo_public void
cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern);
 
cairo_public void
cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern);
 
cairo_public void
cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern,
double x1, double y1,
double x2, double y2,
double x3, double y3);
 
cairo_public void
cairo_mesh_pattern_line_to (cairo_pattern_t *pattern,
double x, double y);
 
cairo_public void
cairo_mesh_pattern_move_to (cairo_pattern_t *pattern,
double x, double y);
 
cairo_public void
cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern,
unsigned int point_num,
double x, double y);
 
cairo_public void
cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern,
unsigned int corner_num,
double red, double green, double blue);
 
cairo_public void
cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern,
unsigned int corner_num,
double red, double green, double blue,
double alpha);
 
cairo_public void
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
const cairo_matrix_t *matrix);
 
2414,10 → 2837,10
/**
* cairo_extend_t:
* @CAIRO_EXTEND_NONE: pixels outside of the source pattern
* are fully transparent
* @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating
* are fully transparent (Since 1.0)
* @CAIRO_EXTEND_REPEAT: the pattern is tiled by repeating (Since 1.0)
* @CAIRO_EXTEND_REFLECT: the pattern is tiled by reflecting
* at the edges (Implemented for surface patterns since 1.6)
* at the edges (Since 1.0; but only implemented for surface patterns since 1.6)
* @CAIRO_EXTEND_PAD: pixels outside of the pattern copy
* the closest pixel from the source (Since 1.2; but only
* implemented for surface patterns since 1.6)
2427,10 → 2850,14
* example, outside the surface bounds or outside the gradient
* geometry).
*
* Mesh patterns are not affected by the extend mode.
*
* The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
* and %CAIRO_EXTEND_PAD for gradient patterns.
*
* New entries may be added in future versions.
*
* Since: 1.0
**/
typedef enum _cairo_extend {
CAIRO_EXTEND_NONE,
2448,21 → 2875,23
/**
* cairo_filter_t:
* @CAIRO_FILTER_FAST: A high-performance filter, with quality similar
* to %CAIRO_FILTER_NEAREST
* to %CAIRO_FILTER_NEAREST (Since 1.0)
* @CAIRO_FILTER_GOOD: A reasonable-performance filter, with quality
* similar to %CAIRO_FILTER_BILINEAR
* similar to %CAIRO_FILTER_BILINEAR (Since 1.0)
* @CAIRO_FILTER_BEST: The highest-quality available, performance may
* not be suitable for interactive use.
* @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering
* @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions
* not be suitable for interactive use. (Since 1.0)
* @CAIRO_FILTER_NEAREST: Nearest-neighbor filtering (Since 1.0)
* @CAIRO_FILTER_BILINEAR: Linear interpolation in two dimensions (Since 1.0)
* @CAIRO_FILTER_GAUSSIAN: This filter value is currently
* unimplemented, and should not be used in current code.
* unimplemented, and should not be used in current code. (Since 1.0)
*
* #cairo_filter_t is used to indicate what filtering should be
* applied when reading pixel values from patterns. See
* cairo_pattern_set_source() for indicating the desired filter to be
* cairo_pattern_set_filter() for indicating the desired filter to be
* used with a particular pattern.
*/
*
* Since: 1.0
**/
typedef enum _cairo_filter {
CAIRO_FILTER_FAST,
CAIRO_FILTER_GOOD,
2508,6 → 2937,27
double *x0, double *y0, double *r0,
double *x1, double *y1, double *r1);
 
cairo_public cairo_status_t
cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern,
unsigned int *count);
 
cairo_public cairo_path_t *
cairo_mesh_pattern_get_path (cairo_pattern_t *pattern,
unsigned int patch_num);
 
cairo_public cairo_status_t
cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern,
unsigned int patch_num,
unsigned int corner_num,
double *red, double *green,
double *blue, double *alpha);
 
cairo_public cairo_status_t
cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern,
unsigned int patch_num,
unsigned int point_num,
double *x, double *y);
 
/* Matrix functions */
 
cairo_public void
2573,23 → 3023,6
**/
typedef struct _cairo_region cairo_region_t;
 
/**
* cairo_rectangle_int_t:
* @x: X coordinate of the left side of the rectangle
* @y: Y coordinate of the the top side of the rectangle
* @width: width of the rectangle
* @height: height of the rectangle
*
* A data structure for holding a rectangle with integer coordinates.
*
* Since: 1.10
**/
 
typedef struct _cairo_rectangle_int {
int x, y;
int width, height;
} cairo_rectangle_int_t;
 
typedef enum _cairo_region_overlap {
CAIRO_REGION_OVERLAP_IN, /* completely inside region */
CAIRO_REGION_OVERLAP_OUT, /* completely outside region */
/programs/develop/libraries/cairo/src/cairoint.h
71,7 → 71,15
#include <pixman.h>
 
#include "cairo-compiler-private.h"
#include "cairo-error-private.h"
 
#if CAIRO_HAS_PDF_SURFACE || \
CAIRO_HAS_PS_SURFACE || \
CAIRO_HAS_SCRIPT_SURFACE || \
CAIRO_HAS_XML_SURFACE
#define CAIRO_HAS_DEFLATE_STREAM 1
#endif
 
#if CAIRO_HAS_PS_SURFACE || \
CAIRO_HAS_PDF_SURFACE || \
CAIRO_HAS_SVG_SURFACE || \
79,7 → 87,9
#define CAIRO_HAS_FONT_SUBSET 1
#endif
 
#if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_PDF_SURFACE || CAIRO_HAS_FONT_SUBSET
#if CAIRO_HAS_PS_SURFACE || \
CAIRO_HAS_PDF_SURFACE || \
CAIRO_HAS_FONT_SUBSET
#define CAIRO_HAS_PDF_OPERATORS 1
#endif
 
182,6 → 192,13
#endif
}
 
static cairo_always_inline cairo_bool_t
_cairo_is_little_endian (void)
{
static const int i = 1;
return *((char *) &i) == 0x01;
}
 
#ifdef WORDS_BIGENDIAN
#define CAIRO_BITSWAP8_IF_LITTLE_ENDIAN(c) (c)
#else
243,6 → 260,7
#include "cairo-cache-private.h"
#include "cairo-reference-count-private.h"
#include "cairo-spans-private.h"
#include "cairo-surface-private.h"
 
cairo_private void
_cairo_box_from_doubles (cairo_box_t *box,
263,74 → 281,72
cairo_rectangle_int_t *rectangle);
 
cairo_private void
_cairo_box_add_curve_to (cairo_box_t *extents,
const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d);
 
cairo_private void
_cairo_boxes_get_extents (const cairo_box_t *boxes,
int num_boxes,
cairo_box_t *extents);
 
cairo_private extern const cairo_rectangle_int_t _cairo_empty_rectangle;
cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle;
 
static inline void
_cairo_unbounded_rectangle_init (cairo_rectangle_int_t *rect)
{
rect->x = CAIRO_RECT_INT_MIN;
rect->y = CAIRO_RECT_INT_MIN;
rect->width = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN;
rect->height = CAIRO_RECT_INT_MAX - CAIRO_RECT_INT_MIN;
*rect = _cairo_unbounded_rectangle;
}
 
cairo_private cairo_bool_t
cairo_private_no_warn cairo_bool_t
_cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
const cairo_rectangle_int_t *src);
 
cairo_private cairo_bool_t
_cairo_box_intersects_line_segment (cairo_box_t *box,
cairo_line_t *line) cairo_pure;
static inline cairo_bool_t
_cairo_rectangle_intersects (const cairo_rectangle_int_t *dst,
const cairo_rectangle_int_t *src)
{
return !(src->x >= dst->x + (int) dst->width ||
src->x + (int) src->width <= dst->x ||
src->y >= dst->y + (int) dst->height ||
src->y + (int) src->height <= dst->y);
}
 
cairo_private cairo_bool_t
_cairo_box_contains_point (cairo_box_t *box,
const cairo_point_t *point) cairo_pure;
static inline cairo_bool_t
_cairo_rectangle_contains_rectangle (const cairo_rectangle_int_t *a,
const cairo_rectangle_int_t *b)
{
return (a->x <= b->x &&
a->x + (int) a->width >= b->x + (int) b->width &&
a->y <= b->y &&
a->y + (int) a->height >= b->y + (int) b->height);
}
 
/* cairo-array.c structures and functions */
 
cairo_private void
_cairo_array_init (cairo_array_t *array, int element_size);
_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
const cairo_rectangle_t *rectf);
 
/* Extends the dst rectangle to also contain src.
* If one of the rectangles is empty, the result is undefined
*/
cairo_private void
_cairo_array_init_snapshot (cairo_array_t *array,
const cairo_array_t *other);
_cairo_rectangle_union (cairo_rectangle_int_t *dst,
const cairo_rectangle_int_t *src);
 
cairo_private void
_cairo_array_fini (cairo_array_t *array);
cairo_private cairo_bool_t
_cairo_box_intersects_line_segment (const cairo_box_t *box,
cairo_line_t *line) cairo_pure;
 
cairo_private cairo_status_t
_cairo_array_grow_by (cairo_array_t *array, unsigned int additional);
cairo_private cairo_bool_t
_cairo_spline_intersects (const cairo_point_t *a,
const cairo_point_t *b,
const cairo_point_t *c,
const cairo_point_t *d,
const cairo_box_t *box) cairo_pure;
 
cairo_private void
_cairo_array_truncate (cairo_array_t *array, unsigned int num_elements);
 
cairo_private cairo_status_t
_cairo_array_append (cairo_array_t *array, const void *element);
 
cairo_private cairo_status_t
_cairo_array_append_multiple (cairo_array_t *array,
const void *elements,
int num_elements);
 
cairo_private cairo_status_t
_cairo_array_allocate (cairo_array_t *array,
unsigned int num_elements,
void **elements);
 
cairo_private void *
_cairo_array_index (cairo_array_t *array, unsigned int index);
 
cairo_private void
_cairo_array_copy_element (cairo_array_t *array, int index, void *dst);
 
cairo_private int
_cairo_array_num_elements (cairo_array_t *array);
 
cairo_private int
_cairo_array_size (cairo_array_t *array);
 
typedef struct {
const cairo_user_data_key_t *key;
void *user_data;
355,7 → 371,7
 
cairo_private cairo_status_t
_cairo_user_data_array_copy (cairo_user_data_array_t *dst,
cairo_user_data_array_t *src);
const cairo_user_data_array_t *src);
 
cairo_private void
_cairo_user_data_array_foreach (cairo_user_data_array_t *array,
389,7 → 405,7
};
 
cairo_private void
_cairo_reset_static_data (void);
_cairo_default_context_reset_static_data (void);
 
cairo_private void
_cairo_toy_font_face_reset_static_data (void);
397,6 → 413,14
cairo_private void
_cairo_ft_font_reset_static_data (void);
 
cairo_private void
_cairo_win32_font_reset_static_data (void);
 
#if CAIRO_HAS_COGL_SURFACE
void
_cairo_cogl_context_reset_static_data (void);
#endif
 
/* the font backend interface */
 
struct _cairo_unscaled_font_backend {
433,12 → 457,14
* Value of glyphs array is scaled_font_glyph_index.
*/
unsigned long *glyphs;
unsigned long *to_unicode;
char **utf8;
char **glyph_names;
int *to_latin_char;
unsigned long *latin_to_subset_glyph_index;
unsigned int num_glyphs;
cairo_bool_t is_composite;
cairo_bool_t is_scaled;
cairo_bool_t is_latin;
} cairo_scaled_font_subset_t;
 
struct _cairo_scaled_font_backend {
471,22 → 497,25
unsigned long
(*ucs4_to_index) (void *scaled_font,
uint32_t ucs4);
cairo_warn cairo_int_status_t
(*show_glyphs) (void *scaled_font,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_region_t *clip_region,
int *remaining_glyphs);
 
/* Read data from a sfnt font table.
* @scaled_font: font
* @tag: 4 byte table name specifying the table to read.
* @offset: offset into the table
* @buffer: buffer to write data into. Caller must ensure there is sufficient space.
* If NULL, return the size of the table in @length.
* @length: If @buffer is NULL, the size of the table will be returned in @length.
* If @buffer is not null, @length specifies the number of bytes to read.
*
* If less than @length bytes are available to read this function
* returns CAIRO_INT_STATUS_UNSUPPORTED. Note that requesting more
* bytes than are available in the table may continue reading data
* from the following table and return success. If this is
* undesirable the caller should first query the table size. If an
* error occurs the output value of @length is undefined.
*
* Returns CAIRO_INT_STATUS_UNSUPPORTED if not a sfnt style font or table not found.
*/
cairo_warn cairo_int_status_t
(*load_truetype_table)(void *scaled_font,
unsigned long tag,
500,6 → 529,48
(*index_to_ucs4)(void *scaled_font,
unsigned long index,
uint32_t *ucs4);
 
cairo_warn cairo_bool_t
(*is_synthetic)(void *scaled_font);
 
/* For type 1 fonts, return the glyph name for a given glyph index.
* A glyph index and list of glyph names in the Type 1 fonts is provided.
* The function returns the index of the glyph in the list of glyph names.
* @scaled_font: font
* @glyph_names: the names of each glyph in the Type 1 font in the
* order they appear in the CharStrings array
* @num_glyph_names: the number of names in the glyph_names array
* @glyph_index: the given glyph index
* @glyph_array_index: (index into glyph_names) the glyph name corresponding
* to the glyph_index
*/
 
cairo_warn cairo_int_status_t
(*index_to_glyph_name)(void *scaled_font,
char **glyph_names,
int num_glyph_names,
unsigned long glyph_index,
unsigned long *glyph_array_index);
 
/* Read data from a PostScript font.
* @scaled_font: font
* @offset: offset into the table
* @buffer: buffer to write data into. Caller must ensure there is sufficient space.
* If NULL, return the size of the table in @length.
* @length: If @buffer is NULL, the size of the table will be returned in @length.
* If @buffer is not null, @length specifies the number of bytes to read.
*
* If less than @length bytes are available to read this function
* returns CAIRO_INT_STATUS_UNSUPPORTED. If an error occurs the
* output value of @length is undefined.
*
* Returns CAIRO_INT_STATUS_UNSUPPORTED if not a Type 1 font.
*/
cairo_warn cairo_int_status_t
(*load_type1_data) (void *scaled_font,
long offset,
unsigned char *buffer,
unsigned long *length);
};
 
struct _cairo_font_face_backend {
550,301 → 621,6
 
#endif
 
struct _cairo_surface_backend {
cairo_surface_type_t type;
 
cairo_surface_t *
(*create_similar) (void *surface,
cairo_content_t content,
int width,
int height);
 
cairo_warn cairo_status_t
(*finish) (void *surface);
 
cairo_warn cairo_status_t
(*acquire_source_image) (void *abstract_surface,
cairo_image_surface_t **image_out,
void **image_extra);
 
void
(*release_source_image) (void *abstract_surface,
cairo_image_surface_t *image,
void *image_extra);
 
cairo_warn cairo_status_t
(*acquire_dest_image) (void *abstract_surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *image_rect,
void **image_extra);
 
void
(*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);
 
/* Create a new surface (@clone_out) with the following
* characteristics:
*
* 1. It is as compatible as possible with @surface (in terms of
* efficiency)
*
* 2. It has the same contents as @src within the given rectangle.
*
* 3. The offset of the similar surface with respect to the original
* surface is returned in the clone_offset vector.
* - if you clone the entire surface, this vector is zero.
* - if you clone (src_x, src_y)x(w, h) the vector is (src_x, src_y);
*/
cairo_warn cairo_status_t
(*clone_similar) (void *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);
 
/* XXX remove to a separate cairo_surface_compositor_t */
/* XXX: dst should be the first argument for consistency */
cairo_warn cairo_int_status_t
(*composite) (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
void *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_warn cairo_int_status_t
(*fill_rectangles) (void *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects);
 
/* XXX: dst should be the first argument for consistency */
cairo_warn cairo_int_status_t
(*composite_trapezoids) (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *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 *region);
 
cairo_warn cairo_span_renderer_t *
(*create_span_renderer) (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region);
 
cairo_warn cairo_bool_t
(*check_span_renderer) (cairo_operator_t op,
const cairo_pattern_t *pattern,
void *dst,
cairo_antialias_t antialias);
 
cairo_warn cairo_int_status_t
(*copy_page) (void *surface);
 
cairo_warn cairo_int_status_t
(*show_page) (void *surface);
 
/* Get the extents of the current surface. For many surface types
* this will be as simple as { x=0, y=0, width=surface->width,
* height=surface->height}.
*
* If this function is not implemented, or if it returns
* FALSE the surface is considered to be
* boundless and infinite bounds are used for it.
*/
cairo_warn cairo_bool_t
(*get_extents) (void *surface,
cairo_rectangle_int_t *extents);
 
/*
* This is an optional entry to let the surface manage its own glyph
* resources. If null, render against this surface, using image
* surfaces as glyphs.
*/
cairo_warn cairo_int_status_t
(*old_show_glyphs) (cairo_scaled_font_t *font,
cairo_operator_t op,
const cairo_pattern_t *pattern,
void *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_region_t *clip_region);
 
void
(*get_font_options) (void *surface,
cairo_font_options_t *options);
 
cairo_warn cairo_status_t
(*flush) (void *surface);
 
cairo_warn cairo_status_t
(*mark_dirty_rectangle) (void *surface,
int x,
int y,
int width,
int height);
 
void
(*scaled_font_fini) (cairo_scaled_font_t *scaled_font);
 
void
(*scaled_glyph_fini) (cairo_scaled_glyph_t *scaled_glyph,
cairo_scaled_font_t *scaled_font);
 
/* OK, I'm starting over somewhat by defining the 5 top-level
* drawing operators for the surface backend here with consistent
* naming and argument-order conventions. */
cairo_warn cairo_int_status_t
(*paint) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*mask) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*stroke) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*fill) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
 
cairo_warn cairo_int_status_t
(*show_glyphs) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
int *remaining_glyphs);
 
cairo_surface_t *
(*snapshot) (void *surface);
 
cairo_bool_t
(*is_similar) (void *surface_a,
void *surface_b);
 
cairo_warn cairo_int_status_t
(*fill_stroke) (void *surface,
cairo_operator_t fill_op,
const cairo_pattern_t *fill_source,
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *stroke_ctm,
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip);
 
cairo_surface_t *
(*create_solid_pattern_surface)
(void *surface,
const cairo_solid_pattern_t *solid_pattern);
 
cairo_bool_t
(*can_repaint_solid_pattern_surface)
(void *surface,
const cairo_solid_pattern_t *solid_pattern);
 
cairo_bool_t
(*has_show_text_glyphs) (void *surface);
 
cairo_warn cairo_int_status_t
(*show_text_glyphs) (void *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip);
};
 
#include "cairo-surface-private.h"
 
struct _cairo_image_surface {
cairo_surface_t base;
 
pixman_format_code_t pixman_format;
cairo_format_t format;
unsigned char *data;
 
int width;
int height;
int stride;
int depth;
 
pixman_image_t *pixman_image;
 
unsigned owns_data : 1;
unsigned transparency : 2;
};
 
extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend;
 
#define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE
#define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD
#define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD
853,7 → 629,7
extern const cairo_private cairo_solid_pattern_t _cairo_pattern_black;
extern const cairo_private cairo_solid_pattern_t _cairo_pattern_white;
 
typedef struct _cairo_surface_attributes {
struct _cairo_surface_attributes {
cairo_matrix_t matrix;
cairo_extend_t extend;
cairo_filter_t filter;
861,25 → 637,8
int x_offset;
int y_offset;
void *extra;
} cairo_surface_attributes_t;
};
 
typedef struct _cairo_traps {
cairo_status_t status;
 
const cairo_box_t *limits;
int num_limits;
 
unsigned int maybe_region : 1; /* hint: 0 implies that it cannot be */
unsigned int has_intersections : 1;
unsigned int is_rectilinear : 1;
unsigned int is_rectangular : 1;
 
int num_traps;
int traps_size;
cairo_trapezoid_t *traps;
cairo_trapezoid_t traps_embedded[16];
} cairo_traps_t;
 
#define CAIRO_FONT_SLANT_DEFAULT CAIRO_FONT_SLANT_NORMAL
#define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL
 
927,7 → 686,9
cairo_point_t point;
cairo_point_t cw;
cairo_slope_t dev_vector;
cairo_point_double_t dev_slope;
cairo_point_double_t usr_vector;
double length;
} cairo_stroke_face_t;
 
/* cairo.c */
944,7 → 705,7
}
 
/* C99 round() rounds to the nearest integral value with halfway cases rounded
* away from 0. _cairo_round rounds halfway cases toward negative infinity.
* away from 0. _cairo_round rounds halfway cases toward positive infinity.
* This matches the rounding behaviour of _cairo_lround. */
static inline double cairo_const
_cairo_round (double r)
956,7 → 717,11
cairo_private int
_cairo_lround (double d) cairo_const;
#else
#define _cairo_lround lround
static inline int cairo_const
_cairo_lround (double r)
{
return _cairo_round (r);
}
#endif
 
cairo_private uint16_t
987,13 → 752,6
_cairo_color_double_to_short (double d) cairo_const;
 
cairo_private void
_cairo_color_init (cairo_color_t *color);
 
cairo_private void
_cairo_color_init_rgb (cairo_color_t *color,
double red, double green, double blue);
 
cairo_private void
_cairo_color_init_rgba (cairo_color_t *color,
double red, double green, double blue,
double alpha);
1079,6 → 837,13
cairo_private cairo_lcd_filter_t
_cairo_font_options_get_lcd_filter (const cairo_font_options_t *options);
 
cairo_private void
_cairo_font_options_set_round_glyph_positions (cairo_font_options_t *options,
cairo_round_glyph_positions_t round);
 
cairo_private cairo_round_glyph_positions_t
_cairo_font_options_get_round_glyph_positions (const cairo_font_options_t *options);
 
/* cairo-hull.c */
cairo_private cairo_status_t
_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices);
1114,10 → 879,6
_cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
const cairo_path_fixed_t *other);
 
cairo_private cairo_bool_t
_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
const cairo_path_fixed_t *other);
 
cairo_private void
_cairo_path_fixed_fini (cairo_path_fixed_t *path);
 
1186,7 → 947,6
 
cairo_private cairo_status_t
_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
cairo_direction_t dir,
cairo_path_fixed_move_to_func_t *move_to,
cairo_path_fixed_line_to_func_t *line_to,
cairo_path_fixed_curve_to_func_t *curve_to,
1195,7 → 955,6
 
cairo_private cairo_status_t
_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
cairo_direction_t dir,
cairo_path_fixed_move_to_func_t *move_to,
cairo_path_fixed_line_to_func_t *line_to,
cairo_path_fixed_close_path_func_t *close_path,
1202,7 → 961,12
void *closure,
double tolerance);
 
 
cairo_private cairo_bool_t
_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
cairo_box_t *box);
 
cairo_private cairo_bool_t
_cairo_path_fixed_extents (const cairo_path_fixed_t *path,
cairo_box_t *box);
 
1260,14 → 1024,15
double tolerance,
cairo_polygon_t *polygon);
 
cairo_private cairo_int_status_t
_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps);
cairo_private cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
cairo_antialias_t antialias,
cairo_polygon_t *polygon);
 
cairo_private cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_antialias_t antialias,
cairo_boxes_t *boxes);
 
cairo_private cairo_region_t *
1291,18 → 1056,29
cairo_polygon_t *polygon);
 
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
_cairo_path_fixed_stroke_to_tristrip (const cairo_path_fixed_t *path,
const cairo_stroke_style_t*style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_tristrip_t *strip);
 
cairo_private cairo_status_t
_cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_traps_t *traps);
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_polygon_t *polygon);
 
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
cairo_antialias_t antialias,
cairo_boxes_t *boxes);
 
cairo_private cairo_status_t
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
1310,6 → 1086,14
double tolerance,
cairo_traps_t *traps);
 
cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_polygon_to_traps (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps);
 
cairo_private cairo_status_t
_cairo_path_fixed_stroke_to_shaper (cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
1380,7 → 1164,7
cairo_rectangle_int_t *extents,
cairo_bool_t *overlap);
 
cairo_private void
cairo_private cairo_bool_t
_cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font,
const cairo_glyph_t *glyphs,
int num_glyphs,
1453,9 → 1237,21
 
cairo_private void
_cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy);
cairo_private void
_cairo_stroke_style_max_line_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy);
 
cairo_private void
_cairo_stroke_style_max_join_distance_from_path (const cairo_stroke_style_t *style,
const cairo_path_fixed_t *path,
const cairo_matrix_t *ctm,
double *dx, double *dy);
 
cairo_private double
_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style);
 
1478,16 → 1274,13
 
/* cairo-surface.c */
 
cairo_private cairo_surface_t *
_cairo_surface_create_in_error (cairo_status_t status);
 
cairo_private cairo_status_t
_cairo_surface_copy_mime_data (cairo_surface_t *dst,
cairo_surface_t *src);
 
cairo_private cairo_status_t
cairo_private_no_warn cairo_int_status_t
_cairo_surface_set_error (cairo_surface_t *surface,
cairo_status_t status);
cairo_int_status_t status);
 
cairo_private void
_cairo_surface_set_resolution (cairo_surface_t *surface,
1501,22 → 1294,16
int height);
 
cairo_private cairo_surface_t *
_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_surface_t *
_cairo_surface_create_similar_solid (cairo_surface_t *other,
cairo_content_t content,
int width,
int height,
const cairo_color_t *color,
cairo_bool_t allow_fallback);
const cairo_color_t *color);
 
cairo_private cairo_surface_t *
_cairo_surface_create_solid_pattern_surface (cairo_surface_t *other,
const cairo_solid_pattern_t *solid_pattern);
 
cairo_private cairo_int_status_t
_cairo_surface_repaint_solid_pattern_surface (cairo_surface_t *other,
cairo_surface_t *solid_surface,
const cairo_solid_pattern_t *solid_pattern);
 
cairo_private void
_cairo_surface_init (cairo_surface_t *surface,
const cairo_surface_backend_t *backend,
1528,54 → 1315,25
cairo_font_options_t *options);
 
cairo_private cairo_status_t
_cairo_surface_composite (cairo_operator_t op,
const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *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_private cairo_status_t
_cairo_surface_fill_rectangle (cairo_surface_t *surface,
_cairo_surface_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
int x,
int y,
int width,
int height);
const cairo_pattern_t *source,
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fill_region (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_region_t *region);
cairo_private cairo_image_surface_t *
_cairo_surface_map_to_image (cairo_surface_t *surface,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_fill_rectangles (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_color_t *color,
cairo_rectangle_int_t *rects,
int num_rects);
cairo_private cairo_int_status_t
_cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_image_surface_t *image);
 
cairo_private cairo_status_t
_cairo_surface_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_mask (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fill_stroke (cairo_surface_t *surface,
1592,29 → 1350,29
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_stroke (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_fill (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_show_text_glyphs (cairo_surface_t *surface,
1628,87 → 1386,9
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip);
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_surface_paint_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_mask_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_stroke_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_fill_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_glyphs_extents (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
cairo_clip_t *clip,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_composite_trapezoids (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *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 ntraps,
cairo_region_t *clip_region);
 
cairo_private cairo_span_renderer_t *
_cairo_surface_create_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
cairo_antialias_t antialias,
const cairo_composite_rectangles_t *rects,
cairo_region_t *clip_region);
 
cairo_private cairo_bool_t
_cairo_surface_check_span_renderer (cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *dst,
cairo_antialias_t antialias);
 
cairo_private cairo_status_t
_cairo_surface_acquire_source_image (cairo_surface_t *surface,
cairo_image_surface_t **image_out,
void **image_extra);
1718,31 → 1398,6
cairo_image_surface_t *image,
void *image_extra);
 
cairo_private cairo_status_t
_cairo_surface_acquire_dest_image (cairo_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t **image_out,
cairo_rectangle_int_t *image_rect,
void **image_extra);
 
cairo_private void
_cairo_surface_release_dest_image (cairo_surface_t *surface,
cairo_rectangle_int_t *interest_rect,
cairo_image_surface_t *image,
cairo_rectangle_int_t *image_rect,
void *image_extra);
 
cairo_private cairo_status_t
_cairo_surface_clone_similar (cairo_surface_t *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_private cairo_surface_t *
_cairo_surface_snapshot (cairo_surface_t *surface);
 
1758,67 → 1413,13
cairo_private void
_cairo_surface_detach_snapshot (cairo_surface_t *snapshot);
 
cairo_private cairo_bool_t
_cairo_surface_is_similar (cairo_surface_t *surface_a,
cairo_surface_t *surface_b);
cairo_private cairo_status_t
_cairo_surface_begin_modification (cairo_surface_t *surface);
 
cairo_private cairo_bool_t
cairo_private_no_warn cairo_bool_t
_cairo_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_int_t *extents);
 
cairo_private cairo_status_t
_cairo_surface_old_show_glyphs (cairo_scaled_font_t *scaled_font,
cairo_operator_t op,
const cairo_pattern_t *pattern,
cairo_surface_t *surface,
int source_x,
int source_y,
int dest_x,
int dest_y,
unsigned int width,
unsigned int height,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_region_t *clip_region);
 
cairo_private cairo_status_t
_cairo_surface_composite_fixup_unbounded (cairo_surface_t *dst,
cairo_surface_attributes_t *src_attr,
int src_width,
int src_height,
cairo_surface_attributes_t *mask_attr,
int mask_width,
int mask_height,
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_private cairo_status_t
_cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t *dst,
cairo_surface_attributes_t *src_attr,
int src_width,
int src_height,
int mask_width,
int mask_height,
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_private cairo_bool_t
_cairo_surface_is_opaque (const cairo_surface_t *surface);
 
cairo_private void
_cairo_surface_set_device_scale (cairo_surface_t *surface,
double sx,
1863,7 → 1464,7
* in cairo-xlib-surface.c--again see -Wswitch-enum).
*/
#define CAIRO_FORMAT_VALID(format) ((format) >= CAIRO_FORMAT_ARGB32 && \
(format) <= CAIRO_FORMAT_RGB16_565)
(format) <= CAIRO_FORMAT_RGB30)
 
/* pixman-required stride alignment in bytes. */
#define CAIRO_STRIDE_ALIGNMENT (sizeof (uint32_t))
1907,6 → 1508,10
cairo_format_masks_t *masks);
 
cairo_private void
_cairo_image_scaled_glyph_fini (cairo_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph);
 
cairo_private void
_cairo_image_reset_static_data (void);
 
cairo_private cairo_surface_t *
1931,23 → 1536,18
_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface,
cairo_format_t format);
 
cairo_private void
_cairo_image_surface_span_render_row (int y,
const cairo_half_open_span_t *spans,
unsigned num_spans,
uint8_t *data,
uint32_t stride);
 
cairo_private cairo_image_transparency_t
_cairo_image_analyze_transparency (cairo_image_surface_t *image);
 
cairo_private cairo_bool_t
_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure;
cairo_private cairo_image_color_t
_cairo_image_analyze_color (cairo_image_surface_t *image);
 
cairo_private cairo_bool_t
_cairo_surface_is_recording (const cairo_surface_t *surface) cairo_pure;
/* cairo-pen.c */
cairo_private int
_cairo_pen_vertices_needed (double tolerance,
double radius,
const cairo_matrix_t *matrix);
 
/* cairo-pen.c */
cairo_private cairo_status_t
_cairo_pen_init (cairo_pen_t *pen,
double radius,
1966,13 → 1566,6
cairo_private cairo_status_t
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points);
 
cairo_private cairo_status_t
_cairo_pen_add_points_for_slopes (cairo_pen_t *pen,
cairo_point_t *a,
cairo_point_t *b,
cairo_point_t *c,
cairo_point_t *d);
 
cairo_private int
_cairo_pen_find_active_cw_vertex_index (const cairo_pen_t *pen,
const cairo_slope_t *slope);
1981,16 → 1574,47
_cairo_pen_find_active_ccw_vertex_index (const cairo_pen_t *pen,
const cairo_slope_t *slope);
 
/* cairo-polygon.c */
cairo_private void
_cairo_polygon_init (cairo_polygon_t *polygon);
_cairo_pen_find_active_cw_vertices (const cairo_pen_t *pen,
const cairo_slope_t *in,
const cairo_slope_t *out,
int *start, int *stop);
 
cairo_private void
_cairo_polygon_limit (cairo_polygon_t *polygon,
_cairo_pen_find_active_ccw_vertices (const cairo_pen_t *pen,
const cairo_slope_t *in,
const cairo_slope_t *out,
int *start, int *stop);
 
/* cairo-polygon.c */
cairo_private void
_cairo_polygon_init (cairo_polygon_t *polygon,
const cairo_box_t *boxes,
int num_boxes);
 
cairo_private void
_cairo_polygon_init_with_clip (cairo_polygon_t *polygon,
const cairo_clip_t *clip);
 
cairo_private cairo_status_t
_cairo_polygon_init_boxes (cairo_polygon_t *polygon,
const cairo_boxes_t *boxes);
 
cairo_private cairo_status_t
_cairo_polygon_init_box_array (cairo_polygon_t *polygon,
cairo_box_t *boxes,
int num_boxes);
 
cairo_private void
_cairo_polygon_limit (cairo_polygon_t *polygon,
const cairo_box_t *limits,
int num_limits);
 
cairo_private void
_cairo_polygon_limit_to_clip (cairo_polygon_t *polygon,
const cairo_clip_t *clip);
 
cairo_private void
_cairo_polygon_fini (cairo_polygon_t *polygon);
 
cairo_private cairo_status_t
2005,16 → 1629,34
const cairo_point_t *p2);
 
cairo_private cairo_status_t
_cairo_polygon_move_to (cairo_polygon_t *polygon,
const cairo_point_t *point);
_cairo_polygon_add_contour (cairo_polygon_t *polygon,
const cairo_contour_t *contour);
 
cairo_private void
_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy);
 
cairo_private cairo_status_t
_cairo_polygon_line_to (cairo_polygon_t *polygon,
const cairo_point_t *point);
_cairo_polygon_reduce (cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
 
cairo_private cairo_status_t
_cairo_polygon_close (cairo_polygon_t *polygon);
_cairo_polygon_intersect (cairo_polygon_t *a, int winding_a,
cairo_polygon_t *b, int winding_b);
 
cairo_private cairo_status_t
_cairo_polygon_intersect_with_boxes (cairo_polygon_t *polygon,
cairo_fill_rule_t *winding,
cairo_box_t *boxes,
int num_boxes);
 
static inline cairo_bool_t
_cairo_polygon_is_empty (const cairo_polygon_t *polygon)
{
return
polygon->num_edges == 0 ||
polygon->extents.p2.x <= polygon->extents.p1.x;
}
 
#define _cairo_polygon_status(P) ((cairo_polygon_t *) (P))->status
 
/* cairo-spline.c */
2065,12 → 1707,27
_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
double *sx, double *sy, int x_major);
 
cairo_private cairo_bool_t
_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure;
static inline cairo_bool_t
_cairo_matrix_is_identity (const cairo_matrix_t *matrix)
{
return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
matrix->xy == 0.0 && matrix->yy == 1.0 &&
matrix->x0 == 0.0 && matrix->y0 == 0.0);
}
 
cairo_private cairo_bool_t
_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure;
static inline cairo_bool_t
_cairo_matrix_is_translation (const cairo_matrix_t *matrix)
{
return (matrix->xx == 1.0 && matrix->yx == 0.0 &&
matrix->xy == 0.0 && matrix->yy == 1.0);
}
 
static inline cairo_bool_t
_cairo_matrix_is_scale (const cairo_matrix_t *matrix)
{
return matrix->yx == 0.0 && matrix->xy == 0.0;
}
 
cairo_private cairo_bool_t
_cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
int *itx, int *ity);
2085,47 → 1742,22
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
double radius) cairo_pure;
 
cairo_private void
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
pixman_transform_t *pixman_transform,
double xc,
double yc);
cairo_private cairo_bool_t
_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix,
cairo_filter_t filter,
int *out_x_offset,
int *out_y_offset);
 
/* cairo-traps.c */
cairo_private void
_cairo_traps_init (cairo_traps_t *traps);
 
cairo_private void
_cairo_traps_limit (cairo_traps_t *traps,
const cairo_box_t *boxes,
int num_boxes);
 
cairo_private cairo_status_t
_cairo_traps_init_boxes (cairo_traps_t *traps,
const cairo_boxes_t *boxes);
_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix,
cairo_filter_t filter,
double xc,
double yc,
pixman_transform_t *out_transform,
int *out_x_offset,
int *out_y_offset);
 
cairo_private void
_cairo_traps_clear (cairo_traps_t *traps);
 
cairo_private void
_cairo_traps_fini (cairo_traps_t *traps);
 
#define _cairo_traps_status(T) (T)->status
 
cairo_private void
_cairo_traps_translate (cairo_traps_t *traps, int x, int y);
 
cairo_private cairo_status_t
_cairo_traps_tessellate_rectangle (cairo_traps_t *traps,
const cairo_point_t *top_left,
const cairo_point_t *bottom_right);
 
cairo_private void
_cairo_traps_add_trap (cairo_traps_t *traps,
cairo_fixed_t top, cairo_fixed_t bottom,
cairo_line_t *left, cairo_line_t *right);
 
cairo_private cairo_status_t
_cairo_bentley_ottmann_tessellate_rectilinear_polygon (cairo_traps_t *traps,
const cairo_polygon_t *polygon,
cairo_fill_rule_t fill_rule);
2157,23 → 1789,7
cairo_fill_rule_t fill_rule,
cairo_boxes_t *boxes);
 
cairo_private int
_cairo_traps_contain (const cairo_traps_t *traps,
double x, double y);
 
cairo_private void
_cairo_traps_extents (const cairo_traps_t *traps,
cairo_box_t *extents);
 
cairo_private cairo_int_status_t
_cairo_traps_extract_region (cairo_traps_t *traps,
cairo_region_t **region);
 
cairo_private cairo_status_t
_cairo_traps_path (const cairo_traps_t *traps,
cairo_path_fixed_t *path);
 
cairo_private void
_cairo_trapezoid_array_translate_and_scale (cairo_trapezoid_t *offset_traps,
cairo_trapezoid_t *src_traps,
int num_traps,
2180,142 → 1796,6
double tx, double ty,
double sx, double sy);
 
/* cairo-pattern.c */
 
cairo_private cairo_pattern_t *
_cairo_pattern_create_in_error (cairo_status_t status);
 
cairo_private cairo_status_t
_cairo_pattern_create_copy (cairo_pattern_t **pattern,
const cairo_pattern_t *other);
 
cairo_private cairo_status_t
_cairo_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private cairo_status_t
_cairo_pattern_init_snapshot (cairo_pattern_t *pattern,
const cairo_pattern_t *other);
 
cairo_private void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color);
 
cairo_private void
_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
cairo_surface_t *surface);
 
cairo_private void
_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
double x0, double y0, double x1, double y1);
 
cairo_private void
_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1);
 
cairo_private void
_cairo_pattern_fini (cairo_pattern_t *pattern);
 
cairo_private cairo_pattern_t *
_cairo_pattern_create_solid (const cairo_color_t *color);
 
cairo_private void
_cairo_pattern_transform (cairo_pattern_t *pattern,
const cairo_matrix_t *ctm_inverse);
 
cairo_private cairo_bool_t
_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents,
cairo_color_t *color);
 
cairo_private cairo_bool_t
_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 
cairo_private cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents);
 
cairo_private cairo_bool_t
_cairo_pattern_is_clear (const cairo_pattern_t *pattern);
 
cairo_private_no_warn cairo_filter_t
_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern,
double *pad_out);
 
enum {
CAIRO_PATTERN_ACQUIRE_NONE = 0x0,
CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1
};
cairo_private cairo_int_status_t
_cairo_pattern_acquire_surface (const cairo_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
unsigned int flags,
cairo_surface_t **surface_out,
cairo_surface_attributes_t *attributes);
 
cairo_private void
_cairo_pattern_release_surface (const cairo_pattern_t *pattern,
cairo_surface_t *surface,
cairo_surface_attributes_t *attributes);
 
cairo_private cairo_int_status_t
_cairo_pattern_acquire_surfaces (const cairo_pattern_t *src,
const cairo_pattern_t *mask,
cairo_surface_t *dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
unsigned int width,
unsigned int height,
unsigned int flags,
cairo_surface_t **src_out,
cairo_surface_t **mask_out,
cairo_surface_attributes_t *src_attributes,
cairo_surface_attributes_t *mask_attributes);
 
cairo_private void
_cairo_pattern_get_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents);
 
cairo_private unsigned long
_cairo_pattern_hash (const cairo_pattern_t *pattern);
 
cairo_private unsigned long
_cairo_linear_pattern_hash (unsigned long hash,
const cairo_linear_pattern_t *linear);
 
cairo_private unsigned long
_cairo_radial_pattern_hash (unsigned long hash,
const cairo_radial_pattern_t *radial);
 
cairo_private cairo_bool_t
_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
const cairo_linear_pattern_t *b);
 
cairo_private unsigned long
_cairo_pattern_size (const cairo_pattern_t *pattern);
 
cairo_private cairo_bool_t
_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
const cairo_radial_pattern_t *b);
 
cairo_private cairo_bool_t
_cairo_pattern_equal (const cairo_pattern_t *a,
const cairo_pattern_t *b);
 
cairo_private void
_cairo_pattern_reset_static_data (void);
 
#if CAIRO_HAS_DRM_SURFACE
 
cairo_private void
2326,6 → 1806,9
cairo_private void
_cairo_clip_reset_static_data (void);
 
cairo_private void
_cairo_pattern_reset_static_data (void);
 
/* cairo-unicode.c */
 
cairo_private int
2353,6 → 1836,11
int *items_written);
#endif
 
cairo_private void
_cairo_matrix_multiply (cairo_matrix_t *r,
const cairo_matrix_t *a,
const cairo_matrix_t *b);
 
/* cairo-observer.c */
 
cairo_private void
2381,6 → 1869,7
slim_hidden_proto (cairo_get_current_point);
slim_hidden_proto (cairo_get_line_width);
slim_hidden_proto (cairo_get_matrix);
slim_hidden_proto (cairo_get_scaled_font);
slim_hidden_proto (cairo_get_target);
slim_hidden_proto (cairo_get_tolerance);
slim_hidden_proto (cairo_glyph_allocate);
2408,15 → 1897,26
slim_hidden_proto (cairo_move_to);
slim_hidden_proto (cairo_new_path);
slim_hidden_proto (cairo_paint);
slim_hidden_proto (cairo_pattern_add_color_stop_rgba);
slim_hidden_proto (cairo_pattern_create_for_surface);
slim_hidden_proto (cairo_pattern_create_rgb);
slim_hidden_proto (cairo_pattern_create_rgba);
slim_hidden_proto (cairo_pattern_destroy);
slim_hidden_proto (cairo_pattern_get_extend);
slim_hidden_proto (cairo_mesh_pattern_curve_to);
slim_hidden_proto (cairo_mesh_pattern_get_control_point);
slim_hidden_proto (cairo_mesh_pattern_get_corner_color_rgba);
slim_hidden_proto (cairo_mesh_pattern_get_patch_count);
slim_hidden_proto (cairo_mesh_pattern_get_path);
slim_hidden_proto (cairo_mesh_pattern_line_to);
slim_hidden_proto (cairo_mesh_pattern_move_to);
slim_hidden_proto (cairo_mesh_pattern_set_corner_color_rgba);
slim_hidden_proto_no_warn (cairo_pattern_reference);
slim_hidden_proto (cairo_pattern_set_matrix);
slim_hidden_proto (cairo_pop_group);
slim_hidden_proto (cairo_push_group_with_content);
slim_hidden_proto_no_warn (cairo_path_destroy);
slim_hidden_proto (cairo_recording_surface_create);
slim_hidden_proto (cairo_rel_line_to);
slim_hidden_proto (cairo_restore);
slim_hidden_proto (cairo_save);
2434,6 → 1934,7
slim_hidden_proto (cairo_scaled_font_get_user_data);
slim_hidden_proto (cairo_scaled_font_set_user_data);
slim_hidden_proto (cairo_scaled_font_text_to_glyphs);
slim_hidden_proto (cairo_set_font_matrix);
slim_hidden_proto (cairo_set_font_options);
slim_hidden_proto (cairo_set_font_size);
slim_hidden_proto (cairo_set_line_cap);
2449,14 → 1950,13
slim_hidden_proto (cairo_stroke);
slim_hidden_proto (cairo_stroke_preserve);
slim_hidden_proto (cairo_surface_copy_page);
slim_hidden_proto (cairo_surface_create_similar_image);
slim_hidden_proto (cairo_surface_destroy);
slim_hidden_proto (cairo_surface_finish);
slim_hidden_proto (cairo_surface_flush);
slim_hidden_proto (cairo_surface_get_content);
slim_hidden_proto (cairo_surface_get_device_offset);
slim_hidden_proto (cairo_surface_get_font_options);
slim_hidden_proto (cairo_surface_get_mime_data);
slim_hidden_proto (cairo_surface_get_type);
slim_hidden_proto (cairo_surface_has_show_text_glyphs);
slim_hidden_proto (cairo_surface_mark_dirty);
slim_hidden_proto (cairo_surface_mark_dirty_rectangle);
2466,6 → 1966,7
slim_hidden_proto (cairo_surface_set_mime_data);
slim_hidden_proto (cairo_surface_show_page);
slim_hidden_proto (cairo_surface_status);
slim_hidden_proto (cairo_surface_supports_mime_type);
slim_hidden_proto (cairo_text_cluster_allocate);
slim_hidden_proto (cairo_text_cluster_free);
slim_hidden_proto (cairo_toy_font_face_create);
2477,6 → 1978,7
slim_hidden_proto (cairo_user_font_face_set_init_func);
slim_hidden_proto (cairo_user_font_face_set_render_glyph_func);
slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func);
slim_hidden_proto (cairo_device_to_user);
slim_hidden_proto (cairo_user_to_device);
slim_hidden_proto (cairo_user_to_device_distance);
slim_hidden_proto (cairo_version_string);
2541,6 → 2043,20
_cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path);
 
cairo_private void
_cairo_debug_print_clip (FILE *stream, cairo_clip_t *clip);
_cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon);
 
cairo_private void
_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps);
 
cairo_private void
_cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip);
 
#if 0
#define TRACE(x) fprintf (stderr, "%s: ", __FILE__), fprintf x
#define TRACE_(x) x
#else
#define TRACE(x)
#define TRACE_(x)
#endif
 
#endif
/programs/develop/libraries/cairo/src/check-has-hidden-symbols.c
0,0 → 1,3
#include "cairoint.h"
 
CAIRO_HAS_HIDDEN_SYMBOLS
/programs/develop/libraries/cairo/src/check-link.c
0,0 → 1,24
#define CAIRO_VERSION_H 1
 
#include <cairo.h>
 
/* get the "real" version info instead of dummy cairo-version.h */
#undef CAIRO_VERSION_H
#include "../cairo-version.h"
 
#include <stdio.h>
 
int
main (void)
{
printf ("Check linking to the just built cairo library\n");
if (cairo_version () == CAIRO_VERSION) {
return 0;
} else {
fprintf (stderr,
"Error: linked to cairo version %s instead of %s\n",
cairo_version_string (),
CAIRO_VERSION_STRING);
return 1;
}
}
/programs/develop/libraries/cairo/src/test-base-compositor-surface.c
0,0 → 1,824
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
* Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "test-compositor-surface-private.h"
 
#include "cairo-clip-private.h"
#include "cairo-composite-rectangles-private.h"
#include "cairo-compositor-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-traps-private.h"
 
/* The intention is that this is a surface that just works, and most
* important of all does not try to be clever!
*/
 
typedef cairo_int_status_t
(*draw_func_t) (cairo_image_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents);
 
static pixman_op_t
_pixman_operator (cairo_operator_t op)
{
switch ((int) op) {
case CAIRO_OPERATOR_CLEAR:
return PIXMAN_OP_CLEAR;
 
case CAIRO_OPERATOR_SOURCE:
return PIXMAN_OP_SRC;
case CAIRO_OPERATOR_OVER:
return PIXMAN_OP_OVER;
case CAIRO_OPERATOR_IN:
return PIXMAN_OP_IN;
case CAIRO_OPERATOR_OUT:
return PIXMAN_OP_OUT;
case CAIRO_OPERATOR_ATOP:
return PIXMAN_OP_ATOP;
 
case CAIRO_OPERATOR_DEST:
return PIXMAN_OP_DST;
case CAIRO_OPERATOR_DEST_OVER:
return PIXMAN_OP_OVER_REVERSE;
case CAIRO_OPERATOR_DEST_IN:
return PIXMAN_OP_IN_REVERSE;
case CAIRO_OPERATOR_DEST_OUT:
return PIXMAN_OP_OUT_REVERSE;
case CAIRO_OPERATOR_DEST_ATOP:
return PIXMAN_OP_ATOP_REVERSE;
 
case CAIRO_OPERATOR_XOR:
return PIXMAN_OP_XOR;
case CAIRO_OPERATOR_ADD:
return PIXMAN_OP_ADD;
case CAIRO_OPERATOR_SATURATE:
return PIXMAN_OP_SATURATE;
 
case CAIRO_OPERATOR_MULTIPLY:
return PIXMAN_OP_MULTIPLY;
case CAIRO_OPERATOR_SCREEN:
return PIXMAN_OP_SCREEN;
case CAIRO_OPERATOR_OVERLAY:
return PIXMAN_OP_OVERLAY;
case CAIRO_OPERATOR_DARKEN:
return PIXMAN_OP_DARKEN;
case CAIRO_OPERATOR_LIGHTEN:
return PIXMAN_OP_LIGHTEN;
case CAIRO_OPERATOR_COLOR_DODGE:
return PIXMAN_OP_COLOR_DODGE;
case CAIRO_OPERATOR_COLOR_BURN:
return PIXMAN_OP_COLOR_BURN;
case CAIRO_OPERATOR_HARD_LIGHT:
return PIXMAN_OP_HARD_LIGHT;
case CAIRO_OPERATOR_SOFT_LIGHT:
return PIXMAN_OP_SOFT_LIGHT;
case CAIRO_OPERATOR_DIFFERENCE:
return PIXMAN_OP_DIFFERENCE;
case CAIRO_OPERATOR_EXCLUSION:
return PIXMAN_OP_EXCLUSION;
case CAIRO_OPERATOR_HSL_HUE:
return PIXMAN_OP_HSL_HUE;
case CAIRO_OPERATOR_HSL_SATURATION:
return PIXMAN_OP_HSL_SATURATION;
case CAIRO_OPERATOR_HSL_COLOR:
return PIXMAN_OP_HSL_COLOR;
case CAIRO_OPERATOR_HSL_LUMINOSITY:
return PIXMAN_OP_HSL_LUMINOSITY;
 
default:
ASSERT_NOT_REACHED;
return PIXMAN_OP_OVER;
}
}
 
static cairo_image_surface_t *
create_composite_mask (cairo_image_surface_t *dst,
void *draw_closure,
draw_func_t draw_func,
const cairo_composite_rectangles_t *extents)
{
cairo_image_surface_t *surface;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
surface = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL, PIXMAN_a8,
extents->bounded.width,
extents->bounded.height,
0);
if (unlikely (surface->base.status))
return surface;
 
status = draw_func (surface, draw_closure,
CAIRO_OPERATOR_ADD, &_cairo_pattern_white.base,
extents->bounded.x, extents->bounded.y,
&extents->bounded);
if (unlikely (status))
goto error;
 
status = _cairo_clip_combine_with_surface (extents->clip,
&surface->base,
extents->bounded.x,
extents->bounded.y);
if (unlikely (status))
goto error;
 
return surface;
 
error:
cairo_surface_destroy (&surface->base);
return (cairo_image_surface_t *)_cairo_surface_create_in_error (status);
}
 
/* Handles compositing with a clip surface when the operator allows
* us to combine the clip with the mask
*/
static cairo_status_t
clip_and_composite_with_mask (const cairo_composite_rectangles_t*extents,
draw_func_t draw_func,
void *draw_closure)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
cairo_image_surface_t *mask;
pixman_image_t *src;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
int src_x, src_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
mask = create_composite_mask (dst, draw_closure, draw_func, extents);
if (unlikely (mask->base.status))
return mask->base.status;
 
src = _pixman_image_for_pattern (dst,
&extents->source_pattern.base, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (unlikely (src == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto error;
}
 
pixman_image_composite32 (_pixman_operator (extents->op),
src, mask->pixman_image, dst->pixman_image,
extents->bounded.x + src_x,
extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
pixman_image_unref (src);
error:
cairo_surface_destroy (&mask->base);
return status;
}
 
/* Handles compositing with a clip surface when we have to do the operation
* in two pieces and combine them together.
*/
static cairo_status_t
clip_and_composite_combine (const cairo_composite_rectangles_t*extents,
draw_func_t draw_func,
void *draw_closure)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
cairo_image_surface_t *tmp, *clip;
int clip_x, clip_y;
cairo_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
tmp = (cairo_image_surface_t *)
_cairo_image_surface_create_with_pixman_format (NULL,
dst->pixman_format,
extents->bounded.width,
extents->bounded.height,
0);
if (unlikely (tmp->base.status))
return tmp->base.status;
 
pixman_image_composite32 (PIXMAN_OP_SRC,
dst->pixman_image, NULL, tmp->pixman_image,
extents->bounded.x, extents->bounded.y,
0, 0,
0, 0,
extents->bounded.width, extents->bounded.height);
 
status = draw_func (tmp, draw_closure,
extents->op, &extents->source_pattern.base,
extents->bounded.x, extents->bounded.y,
&extents->bounded);
if (unlikely (status))
goto error;
 
clip = (cairo_image_surface_t *)
_cairo_clip_get_surface (extents->clip, &dst->base, &clip_x, &clip_y);
if (unlikely (clip->base.status))
goto error;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
clip->pixman_image, NULL, dst->pixman_image,
extents->bounded.x - clip_x, extents->bounded.y - clip_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
pixman_image_composite32 (PIXMAN_OP_ADD,
tmp->pixman_image, clip->pixman_image, dst->pixman_image,
0, 0,
extents->bounded.x - clip_x, extents->bounded.y - clip_y,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
cairo_surface_destroy (&clip->base);
 
error:
cairo_surface_destroy (&tmp->base);
 
return status;
}
 
/* Handles compositing for %CAIRO_OPERATOR_SOURCE, which is special; it's
* defined as (src IN mask IN clip) ADD (dst OUT (mask IN clip))
*/
static cairo_status_t
clip_and_composite_source (const cairo_composite_rectangles_t *extents,
draw_func_t draw_func,
void *draw_closure)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
cairo_image_surface_t *mask;
pixman_image_t *src;
int src_x, src_y;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
mask = create_composite_mask (dst, draw_closure, draw_func, extents);
if (unlikely (mask->base.status))
return mask->base.status;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask->pixman_image, NULL, dst->pixman_image,
0, 0,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
src = _pixman_image_for_pattern (dst,
&extents->source_pattern.base, FALSE,
&extents->bounded,
&extents->source_sample_area,
&src_x, &src_y);
if (unlikely (src == NULL)) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto error;
}
 
pixman_image_composite32 (PIXMAN_OP_ADD,
src, mask->pixman_image, dst->pixman_image,
extents->bounded.x + src_x, extents->bounded.y + src_y,
0, 0,
extents->bounded.x, extents->bounded.y,
extents->bounded.width, extents->bounded.height);
 
pixman_image_unref (src);
 
error:
cairo_surface_destroy (&mask->base);
return status;
}
 
static cairo_status_t
fixup_unbounded (const cairo_composite_rectangles_t *extents)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *)extents->surface;
pixman_image_t *mask;
int mask_x, mask_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
if (! _cairo_clip_is_region (extents->clip)) {
cairo_image_surface_t *clip;
 
clip = (cairo_image_surface_t *)
_cairo_clip_get_surface (extents->clip, &dst->base,
&mask_x, &mask_y);
if (unlikely (clip->base.status))
return clip->base.status;
 
mask = pixman_image_ref (clip->pixman_image);
cairo_surface_destroy (&clip->base);
} else {
mask_x = mask_y = 0;
mask = _pixman_image_for_color (CAIRO_COLOR_WHITE);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
/* top */
if (extents->bounded.y != extents->unbounded.y) {
int x = extents->unbounded.x;
int y = extents->unbounded.y;
int width = extents->unbounded.width;
int height = extents->bounded.y - y;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* left */
if (extents->bounded.x != extents->unbounded.x) {
int x = extents->unbounded.x;
int y = extents->bounded.y;
int width = extents->bounded.x - x;
int height = extents->bounded.height;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* right */
if (extents->bounded.x + extents->bounded.width != extents->unbounded.x + extents->unbounded.width) {
int x = extents->bounded.x + extents->bounded.width;
int y = extents->bounded.y;
int width = extents->unbounded.x + extents->unbounded.width - x;
int height = extents->bounded.height;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
/* bottom */
if (extents->bounded.y + extents->bounded.height != extents->unbounded.y + extents->unbounded.height) {
int x = extents->unbounded.x;
int y = extents->bounded.y + extents->bounded.height;
int width = extents->unbounded.width;
int height = extents->unbounded.y + extents->unbounded.height - y;
 
pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE,
mask, NULL, dst->pixman_image,
x - mask_x, y - mask_y,
0, 0,
x, y,
width, height);
}
 
pixman_image_unref (mask);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
set_clip_region (cairo_composite_rectangles_t *extents)
{
cairo_image_surface_t *dst = (cairo_image_surface_t *) extents->surface;
cairo_region_t *region = _cairo_clip_get_region (extents->clip);
pixman_region32_t *rgn = region ? &region->rgn : NULL;
if (! pixman_image_set_clip_region32 (dst->pixman_image, rgn))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
clip_and_composite (cairo_composite_rectangles_t *extents,
draw_func_t draw_func,
void *draw_closure)
{
cairo_status_t status;
 
status = set_clip_region (extents);
if (unlikely (status))
return status;
 
if (extents->op == CAIRO_OPERATOR_SOURCE) {
status = clip_and_composite_source (extents, draw_func, draw_closure);
} else {
if (extents->op == CAIRO_OPERATOR_CLEAR) {
extents->source_pattern.solid = _cairo_pattern_white;
extents->op = CAIRO_OPERATOR_DEST_OUT;
}
if (! _cairo_clip_is_region (extents->clip)) {
if (extents->is_bounded)
status = clip_and_composite_with_mask (extents, draw_func, draw_closure);
else
status = clip_and_composite_combine (extents, draw_func, draw_closure);
} else {
status = draw_func ((cairo_image_surface_t *) extents->surface,
draw_closure,
extents->op,
&extents->source_pattern.base,
0, 0,
&extents->bounded);
}
}
 
if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded)
status = fixup_unbounded (extents);
 
return status;
}
 
/* high-level compositor interface */
 
static cairo_int_status_t
composite_paint (cairo_image_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents)
{
cairo_rectangle_int_t sample;
pixman_image_t *src;
int src_x, src_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
_cairo_pattern_sampled_area (pattern, extents, &sample);
src = _pixman_image_for_pattern (dst,
pattern, FALSE,
extents, &sample,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
TRACE ((stderr, "%s: src=(%d, %d), dst=(%d, %d) size=%dx%d\n", __FUNCTION__,
extents->x + src_x, extents->y + src_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height));
 
pixman_image_composite32 (_pixman_operator (op),
src, NULL, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (src);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
base_compositor_paint (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
TRACE ((stderr, "%s\n", __FUNCTION__));
return clip_and_composite (extents, composite_paint, NULL);
}
 
static cairo_int_status_t
composite_mask (cairo_image_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents)
{
cairo_rectangle_int_t sample;
pixman_image_t *src, *mask;
int src_x, src_y;
int mask_x, mask_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
_cairo_pattern_sampled_area (pattern, extents, &sample);
src = _pixman_image_for_pattern (dst, pattern, FALSE,
extents, &sample,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
_cairo_pattern_sampled_area (closure, extents, &sample);
mask = _pixman_image_for_pattern (dst, closure, TRUE,
extents, &sample,
&mask_x, &mask_y);
if (unlikely (mask == NULL)) {
pixman_image_unref (src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
extents->x + src_x, extents->y + src_y,
extents->x + mask_x, extents->y + mask_y,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (mask);
pixman_image_unref (src);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
base_compositor_mask (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents)
{
TRACE ((stderr, "%s\n", __FUNCTION__));
return clip_and_composite (extents, composite_mask, &extents->mask_pattern.base);
}
 
typedef struct {
cairo_traps_t traps;
cairo_antialias_t antialias;
} composite_traps_info_t;
 
static cairo_int_status_t
composite_traps (cairo_image_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents)
{
composite_traps_info_t *info = closure;
cairo_rectangle_int_t sample;
pixman_image_t *src, *mask;
int src_x, src_y;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
_cairo_pattern_sampled_area (pattern, extents, &sample);
src = _pixman_image_for_pattern (dst, pattern, FALSE,
extents, &sample,
&src_x, &src_y);
if (unlikely (src == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
mask = pixman_image_create_bits (info->antialias == CAIRO_ANTIALIAS_NONE ? PIXMAN_a1 : PIXMAN_a8,
extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL)) {
pixman_image_unref (src);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
 
_pixman_image_add_traps (mask, extents->x, extents->y, &info->traps);
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
extents->x + src_x - dst_x, extents->y + src_y - dst_y,
0, 0,
extents->x - dst_x, extents->y - dst_y,
extents->width, extents->height);
 
pixman_image_unref (mask);
pixman_image_unref (src);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
trim_extents_to_traps (cairo_composite_rectangles_t *extents,
cairo_traps_t *traps)
{
cairo_box_t box;
 
/* X trims the affected area to the extents of the trapezoids, so
* we need to compensate when fixing up the unbounded area.
*/
_cairo_traps_extents (traps, &box);
return _cairo_composite_rectangles_intersect_mask_extents (extents, &box);
}
 
static cairo_int_status_t
base_compositor_stroke (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias)
{
composite_traps_info_t info;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
info.antialias = antialias;
_cairo_traps_init_with_clip (&info.traps, extents->clip);
status = _cairo_path_fixed_stroke_polygon_to_traps (path, style,
ctm, ctm_inverse,
tolerance,
&info.traps);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = trim_extents_to_traps (extents, &info.traps);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite (extents, composite_traps, &info);
_cairo_traps_fini (&info.traps);
 
return status;
}
 
static cairo_int_status_t
base_compositor_fill (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias)
{
composite_traps_info_t info;
cairo_int_status_t status;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
info.antialias = antialias;
_cairo_traps_init_with_clip (&info.traps, extents->clip);
status = _cairo_path_fixed_fill_to_traps (path,
fill_rule, tolerance,
&info.traps);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = trim_extents_to_traps (extents, &info.traps);
if (likely (status == CAIRO_INT_STATUS_SUCCESS))
status = clip_and_composite (extents, composite_traps, &info);
_cairo_traps_fini (&info.traps);
 
return status;
}
 
static cairo_int_status_t
composite_glyphs (cairo_image_surface_t *dst,
void *closure,
cairo_operator_t op,
const cairo_pattern_t *pattern,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents)
{
cairo_composite_glyphs_info_t *info = closure;
pixman_image_t *mask;
cairo_status_t status;
int i;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
 
mask = pixman_image_create_bits (PIXMAN_a8,
extents->width, extents->height,
NULL, 0);
if (unlikely (mask == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
status = CAIRO_STATUS_SUCCESS;
_cairo_scaled_font_freeze_cache (info->font);
for (i = 0; i < info->num_glyphs; i++) {
cairo_image_surface_t *glyph_surface;
cairo_scaled_glyph_t *scaled_glyph;
unsigned long glyph_index = info->glyphs[i].index;
int x, y;
 
status = _cairo_scaled_glyph_lookup (info->font, glyph_index,
CAIRO_SCALED_GLYPH_INFO_SURFACE,
&scaled_glyph);
 
if (unlikely (status))
break;
 
glyph_surface = scaled_glyph->surface;
if (glyph_surface->width && glyph_surface->height) {
/* round glyph locations to the nearest pixel */
/* XXX: FRAGILE: We're ignoring device_transform scaling here. A bug? */
x = _cairo_lround (info->glyphs[i].x -
glyph_surface->base.device_transform.x0);
y = _cairo_lround (info->glyphs[i].y -
glyph_surface->base.device_transform.y0);
 
pixman_image_composite32 (PIXMAN_OP_ADD,
glyph_surface->pixman_image, NULL, mask,
0, 0,
0, 0,
x - extents->x, y - extents->y,
glyph_surface->width,
glyph_surface->height);
}
}
_cairo_scaled_font_thaw_cache (info->font);
 
if (status == CAIRO_STATUS_SUCCESS) {
cairo_rectangle_int_t sample;
pixman_image_t *src;
int src_x, src_y;
 
_cairo_pattern_sampled_area (pattern, extents, &sample);
src = _pixman_image_for_pattern (dst, pattern, FALSE,
extents, &sample,
&src_x, &src_y);
if (src != NULL) {
dst_x = extents->x - dst_x;
dst_y = extents->y - dst_y;
pixman_image_composite32 (_pixman_operator (op),
src, mask, dst->pixman_image,
src_x + dst_x, src_y + dst_y,
0, 0,
dst_x, dst_y,
extents->width, extents->height);
pixman_image_unref (src);
} else
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
pixman_image_unref (mask);
 
return status;
}
 
static cairo_int_status_t
base_compositor_glyphs (const cairo_compositor_t *_compositor,
cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_bool_t overlap)
{
cairo_composite_glyphs_info_t info;
 
info.font = scaled_font;
info.glyphs = glyphs;
info.num_glyphs = num_glyphs;
 
TRACE ((stderr, "%s\n", __FUNCTION__));
return clip_and_composite (extents, composite_glyphs, &info);
}
 
static const cairo_compositor_t base_compositor = {
&__cairo_no_compositor,
 
base_compositor_paint,
base_compositor_mask,
base_compositor_stroke,
base_compositor_fill,
base_compositor_glyphs,
};
 
cairo_surface_t *
_cairo_test_base_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (&base_compositor,
content, width, height);
}
/programs/develop/libraries/cairo/src/test-compositor-surface-private.h
0,0 → 1,56
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef TEST_COMPOSITOR_SURFACE_PRIVATE_H
#define TEST_COMPOSITOR_SURFACE_PRIVATE_H
 
#include "cairo.h"
 
#include "test-compositor-surface.h"
 
#include "cairo-compiler-private.h"
#include "cairo-compositor-private.h"
 
CAIRO_BEGIN_DECLS
 
cairo_private cairo_surface_t *
test_compositor_surface_create (const cairo_compositor_t *compositor,
cairo_content_t content,
int width,
int height);
 
CAIRO_END_DECLS
 
#endif /* TEST_COMPOSITOR_SURFACE_PRIVATE H */
/programs/develop/libraries/cairo/src/test-compositor-surface.c
0,0 → 1,264
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "cairoint.h"
 
#include "test-compositor-surface-private.h"
 
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-backend-private.h"
 
typedef struct _test_compositor_surface {
cairo_image_surface_t base;
} test_compositor_surface_t;
 
static const cairo_surface_backend_t test_compositor_surface_backend;
 
cairo_surface_t *
test_compositor_surface_create (const cairo_compositor_t *compositor,
cairo_content_t content,
int width,
int height)
{
test_compositor_surface_t *surface;
pixman_image_t *pixman_image;
pixman_format_code_t pixman_format;
 
switch (content) {
case CAIRO_CONTENT_ALPHA:
pixman_format = PIXMAN_a8;
break;
case CAIRO_CONTENT_COLOR:
pixman_format = PIXMAN_x8r8g8b8;
break;
case CAIRO_CONTENT_COLOR_ALPHA:
pixman_format = PIXMAN_a8r8g8b8;
break;
default:
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
}
 
pixman_image = pixman_image_create_bits (pixman_format, width, height,
NULL, 0);
if (unlikely (pixman_image == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface = malloc (sizeof (test_compositor_surface_t));
if (unlikely (surface == NULL)) {
pixman_image_unref (pixman_image);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
_cairo_surface_init (&surface->base.base,
&test_compositor_surface_backend,
NULL, /* device */
content);
_cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
 
surface->base.compositor = compositor;
 
return &surface->base.base;
}
 
static cairo_surface_t *
test_compositor_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
test_compositor_surface_t *surface = abstract_surface;
 
return test_compositor_surface_create (surface->base.compositor,
content, width, height);
}
 
static cairo_int_status_t
test_compositor_surface_paint (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_paint (surface->base.compositor,
_surface, op, source,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_mask (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_mask (surface->base.compositor,
_surface, op, source, mask,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_stroke (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
if (antialias == CAIRO_ANTIALIAS_DEFAULT)
antialias = CAIRO_ANTIALIAS_BEST;
return _cairo_compositor_stroke (surface->base.compositor,
_surface, op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_fill (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
if (antialias == CAIRO_ANTIALIAS_DEFAULT)
antialias = CAIRO_ANTIALIAS_BEST;
return _cairo_compositor_fill (surface->base.compositor,
_surface, op, source,
path, fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_glyphs (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_glyphs (surface->base.compositor,
_surface, op, source,
glyphs, num_glyphs, scaled_font,
clip);
}
 
static const cairo_surface_backend_t test_compositor_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_surface_finish,
_cairo_default_context_create,
 
test_compositor_surface_create_similar,
NULL, /* create similar image */
_cairo_image_surface_map_to_image,
_cairo_image_surface_unmap_image,
 
_cairo_image_surface_source,
_cairo_image_surface_acquire_source_image,
_cairo_image_surface_release_source_image,
_cairo_image_surface_snapshot,
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_image_surface_get_extents,
_cairo_image_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
test_compositor_surface_paint,
test_compositor_surface_mask,
test_compositor_surface_stroke,
test_compositor_surface_fill,
NULL, /* fill/stroke */
test_compositor_surface_glyphs,
};
 
static const cairo_compositor_t *
get_fallback_compositor (void)
{
return &_cairo_fallback_compositor;
}
 
cairo_surface_t *
_cairo_test_fallback_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (get_fallback_compositor(),
content, width, height);
}
 
cairo_surface_t *
_cairo_test_mask_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (_cairo_image_mask_compositor_get(),
content, width, height);
}
 
cairo_surface_t *
_cairo_test_traps_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (_cairo_image_traps_compositor_get(),
content, width, height);
}
 
cairo_surface_t *
_cairo_test_spans_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (_cairo_image_spans_compositor_get(),
content, width, height);
}
/programs/develop/libraries/cairo/src/test-compositor-surface.h
0,0 → 1,71
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef TEST_COMPOSITOR_SURFACE_H
#define TEST_COMPOSITOR_SURFACE_H
 
#include "cairo.h"
 
CAIRO_BEGIN_DECLS
 
cairo_surface_t *
_cairo_test_fallback_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
 
cairo_surface_t *
_cairo_test_mask_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
cairo_surface_t *
_cairo_test_traps_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
cairo_surface_t *
_cairo_test_spans_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
cairo_surface_t *
_cairo_test_base_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
CAIRO_END_DECLS
 
#endif /* TEST_COMPOSITOR_SURFACE_H */
/programs/develop/libraries/cairo/src/test-null-compositor-surface.c
0,0 → 1,480
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
 
#include "cairoint.h"
 
#include "test-null-compositor-surface.h"
 
#include "cairo-compositor-private.h"
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-surface-backend-private.h"
#include "cairo-spans-compositor-private.h"
#include "cairo-spans-private.h"
 
typedef struct _test_compositor_surface {
cairo_image_surface_t base;
} test_compositor_surface_t;
 
static const cairo_surface_backend_t test_compositor_surface_backend;
 
static cairo_surface_t *
test_compositor_surface_create (const cairo_compositor_t *compositor,
cairo_content_t content,
int width,
int height)
{
test_compositor_surface_t *surface;
pixman_image_t *pixman_image;
pixman_format_code_t pixman_format;
 
switch (content) {
case CAIRO_CONTENT_ALPHA:
pixman_format = PIXMAN_a8;
break;
case CAIRO_CONTENT_COLOR:
pixman_format = PIXMAN_x8r8g8b8;
break;
case CAIRO_CONTENT_COLOR_ALPHA:
pixman_format = PIXMAN_a8r8g8b8;
break;
default:
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_CONTENT));
}
 
pixman_image = pixman_image_create_bits (pixman_format, width, height,
NULL, 0);
if (unlikely (pixman_image == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
surface = malloc (sizeof (test_compositor_surface_t));
if (unlikely (surface == NULL)) {
pixman_image_unref (pixman_image);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
 
_cairo_surface_init (&surface->base.base,
&test_compositor_surface_backend,
NULL, /* device */
content);
_cairo_image_surface_init (&surface->base, pixman_image, pixman_format);
 
surface->base.compositor = compositor;
 
return &surface->base.base;
}
 
static cairo_surface_t *
test_compositor_surface_create_similar (void *abstract_surface,
cairo_content_t content,
int width,
int height)
{
test_compositor_surface_t *surface = abstract_surface;
 
return test_compositor_surface_create (surface->base.compositor,
content, width, height);
}
 
static cairo_int_status_t
test_compositor_surface_paint (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_paint (surface->base.compositor,
_surface, op, source,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_mask (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_mask (surface->base.compositor,
_surface, op, source, mask,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_stroke (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_stroke (surface->base.compositor,
_surface, op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_fill (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_fill (surface->base.compositor,
_surface, op, source,
path, fill_rule, tolerance, antialias,
clip);
}
 
static cairo_int_status_t
test_compositor_surface_glyphs (void *_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
test_compositor_surface_t *surface = _surface;
return _cairo_compositor_glyphs (surface->base.compositor,
_surface, op, source,
glyphs, num_glyphs, scaled_font,
clip);
}
 
static const cairo_surface_backend_t test_compositor_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_surface_finish,
_cairo_default_context_create,
 
test_compositor_surface_create_similar,
NULL, /* create similar image */
_cairo_image_surface_map_to_image,
_cairo_image_surface_unmap_image,
 
_cairo_image_surface_source,
_cairo_image_surface_acquire_source_image,
_cairo_image_surface_release_source_image,
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_cairo_image_surface_get_extents,
_cairo_image_surface_get_font_options,
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
test_compositor_surface_paint,
test_compositor_surface_mask,
test_compositor_surface_stroke,
test_compositor_surface_fill,
NULL, /* fill/stroke */
test_compositor_surface_glyphs,
};
 
static cairo_int_status_t
acquire (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
release (void *abstract_dst)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
set_clip_region (void *_surface,
cairo_region_t *region)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_surface_t *
pattern_to_surface (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)
{
return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
}
 
static cairo_int_status_t
fill_boxes (void *_dst,
cairo_operator_t op,
const cairo_color_t *color,
cairo_boxes_t *boxes)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
draw_image_boxes (void *_dst,
cairo_image_surface_t *image,
cairo_boxes_t *boxes,
int dx, int dy)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
lerp (void *_dst,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
unsigned int width,
unsigned int height)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_boxes (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
cairo_surface_t *abstract_mask,
int src_x,
int src_y,
int mask_x,
int mask_y,
int dst_x,
int dst_y,
cairo_boxes_t *boxes,
const cairo_rectangle_int_t *extents)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_traps (void *_dst,
cairo_operator_t op,
cairo_surface_t *abstract_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
const cairo_rectangle_int_t *extents,
cairo_antialias_t antialias,
cairo_traps_t *traps)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
check_composite_glyphs (const cairo_composite_rectangles_t *extents,
cairo_scaled_font_t *scaled_font,
cairo_glyph_t *glyphs,
int *num_glyphs)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
composite_glyphs (void *_dst,
cairo_operator_t op,
cairo_surface_t *_src,
int src_x,
int src_y,
int dst_x,
int dst_y,
cairo_composite_glyphs_info_t *info)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
spans (void *abstract_renderer,
int y, int height,
const cairo_half_open_span_t *spans,
unsigned num_spans)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_status_t
finish_spans (void *abstract_renderer)
{
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_int_status_t
span_renderer_init (cairo_abstract_span_renderer_t *_r,
const cairo_composite_rectangles_t *composite,
cairo_antialias_t antialias,
cairo_bool_t needs_clip)
{
cairo_span_renderer_t *r = (cairo_span_renderer_t *)_r;
r->render_rows = spans;
r->finish = finish_spans;
return CAIRO_STATUS_SUCCESS;
}
 
static void
span_renderer_fini (cairo_abstract_span_renderer_t *_r,
cairo_int_status_t status)
{
}
 
static const cairo_compositor_t *
no_fallback_compositor_get (void)
{
return &__cairo_no_compositor;
}
 
static cairo_int_status_t
check_composite (const cairo_composite_rectangles_t *extents)
{
return CAIRO_STATUS_SUCCESS;
}
 
static const cairo_compositor_t *
no_traps_compositor_get (void)
{
static cairo_traps_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_traps_compositor_init (&compositor,
no_fallback_compositor_get ());
 
compositor.acquire = acquire;
compositor.release = release;
compositor.set_clip_region = set_clip_region;
compositor.pattern_to_surface = pattern_to_surface;
compositor.draw_image_boxes = draw_image_boxes;
//compositor.copy_boxes = copy_boxes;
compositor.fill_boxes = fill_boxes;
compositor.check_composite = check_composite;
compositor.composite = composite;
compositor.lerp = lerp;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_composite_traps = check_composite_traps;
compositor.composite_traps = composite_traps;
compositor.check_composite_glyphs = check_composite_glyphs;
compositor.composite_glyphs = composite_glyphs;
}
 
return &compositor.base;
}
 
static const cairo_compositor_t *
no_spans_compositor_get (void)
{
static cairo_spans_compositor_t compositor;
 
if (compositor.base.delegate == NULL) {
_cairo_spans_compositor_init (&compositor,
no_traps_compositor_get());
 
//compositor.acquire = acquire;
//compositor.release = release;
compositor.fill_boxes = fill_boxes;
//compositor.check_composite_boxes = check_composite_boxes;
compositor.composite_boxes = composite_boxes;
//compositor.check_span_renderer = check_span_renderer;
compositor.renderer_init = span_renderer_init;
compositor.renderer_fini = span_renderer_fini;
}
 
return &compositor.base;
}
 
cairo_surface_t *
_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (no_fallback_compositor_get(),
content, width, height);
}
 
cairo_surface_t *
_cairo_test_no_traps_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (no_traps_compositor_get(),
content, width, height);
}
 
cairo_surface_t *
_cairo_test_no_spans_compositor_surface_create (cairo_content_t content,
int width,
int height)
{
return test_compositor_surface_create (no_spans_compositor_get(),
content, width, height);
}
/programs/develop/libraries/cairo/src/test-null-compositor-surface.h
0,0 → 1,60
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is Intel Corporation
*
* Contributor(s):
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#ifndef TEST_NULL_COMPOSITOR_SURFACE_H
#define TEST_NULL_COMPOSITOR_SURFACE_H
 
#include "cairo.h"
 
CAIRO_BEGIN_DECLS
 
cairo_surface_t *
_cairo_test_no_fallback_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
cairo_surface_t *
_cairo_test_no_traps_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
cairo_surface_t *
_cairo_test_no_spans_compositor_surface_create (cairo_content_t content,
int width,
int height);
 
CAIRO_END_DECLS
 
#endif /* TEST_NULL_COMPOSITOR_SURFACE_H */
/programs/develop/libraries/cairo/src/test-paginated-surface.c
0,0 → 1,286
/* cairo - a vector graphics library with display and print output
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl Worth <cworth@cworth.org>
*/
 
/* This isn't a "real" surface, but just something to be used by the
* test suite to help exercise the paginated-surface paths in cairo.
*
* The defining feature of this backend is that it uses a paginated
* surface to record all operations, and then replays everything to an
* image surface.
*
* It's possible that this code might serve as a good starting point
* for someone working on bringing up a new paginated-surface-based
* backend.
*/
 
#include "cairoint.h"
 
#include "test-paginated-surface.h"
 
#include "cairo-default-context-private.h"
#include "cairo-error-private.h"
#include "cairo-paginated-private.h"
#include "cairo-surface-backend-private.h"
 
typedef struct _test_paginated_surface {
cairo_surface_t base;
cairo_surface_t *target;
cairo_paginated_mode_t paginated_mode;
} test_paginated_surface_t;
 
static const cairo_surface_backend_t test_paginated_surface_backend;
static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend;
 
cairo_surface_t *
_cairo_test_paginated_surface_create (cairo_surface_t *target)
{
cairo_status_t status;
cairo_surface_t *paginated;
test_paginated_surface_t *surface;
 
status = cairo_surface_status (target);
if (unlikely (status))
return _cairo_surface_create_in_error (status);
 
surface = malloc (sizeof (test_paginated_surface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
_cairo_surface_init (&surface->base,
&test_paginated_surface_backend,
NULL, /* device */
target->content);
 
surface->target = cairo_surface_reference (target);
 
paginated = _cairo_paginated_surface_create (&surface->base,
target->content,
&test_paginated_surface_paginated_backend);
status = paginated->status;
if (status == CAIRO_STATUS_SUCCESS) {
/* paginated keeps the only reference to surface now, drop ours */
cairo_surface_destroy (&surface->base);
return paginated;
}
 
cairo_surface_destroy (target);
free (surface);
return _cairo_surface_create_in_error (status);
}
 
static cairo_status_t
_test_paginated_surface_finish (void *abstract_surface)
{
test_paginated_surface_t *surface = abstract_surface;
 
cairo_surface_destroy (surface->target);
 
return CAIRO_STATUS_SUCCESS;
}
 
static cairo_bool_t
_test_paginated_surface_get_extents (void *abstract_surface,
cairo_rectangle_int_t *rectangle)
{
test_paginated_surface_t *surface = abstract_surface;
 
return _cairo_surface_get_extents (surface->target, rectangle);
}
 
static cairo_int_status_t
_test_paginated_surface_paint (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
test_paginated_surface_t *surface = abstract_surface;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_surface_paint (surface->target, op, source, clip);
}
 
static cairo_int_status_t
_test_paginated_surface_mask (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
test_paginated_surface_t *surface = abstract_surface;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_surface_mask (surface->target,
op, source, mask, clip);
}
 
static cairo_int_status_t
_test_paginated_surface_stroke (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_paginated_surface_t *surface = abstract_surface;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_surface_stroke (surface->target, op, source,
path, style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
}
 
static cairo_int_status_t
_test_paginated_surface_fill (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
test_paginated_surface_t *surface = abstract_surface;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_surface_fill (surface->target, op, source,
path, fill_rule,
tolerance, antialias,
clip);
}
 
static cairo_bool_t
_test_paginated_surface_has_show_text_glyphs (void *abstract_surface)
{
test_paginated_surface_t *surface = abstract_surface;
 
return cairo_surface_has_show_text_glyphs (surface->target);
}
 
static cairo_int_status_t
_test_paginated_surface_show_text_glyphs (void *abstract_surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
test_paginated_surface_t *surface = abstract_surface;
 
if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
return CAIRO_STATUS_SUCCESS;
 
return _cairo_surface_show_text_glyphs (surface->target, op, source,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters,
cluster_flags,
scaled_font,
clip);
}
 
 
static void
_test_paginated_surface_set_paginated_mode (void *abstract_surface,
cairo_paginated_mode_t mode)
{
test_paginated_surface_t *surface = abstract_surface;
 
surface->paginated_mode = mode;
}
 
static const cairo_surface_backend_t test_paginated_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_TEST_PAGINATED,
_test_paginated_surface_finish,
_cairo_default_context_create,
 
/* Since we are a paginated user, we get to regard most of the
* surface backend interface as historical cruft and ignore it. */
 
NULL, /* create_similar */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
 
_cairo_surface_default_source,
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* snapshot */
 
NULL, /* copy_page */
NULL, /* show_page */
 
_test_paginated_surface_get_extents,
NULL, /* get_font_options */
 
NULL, /* flush */
NULL, /* mark_dirty_rectangle */
 
/* Here is the more "modern" section of the surface backend
* interface which is mostly just drawing functions */
 
_test_paginated_surface_paint,
_test_paginated_surface_mask,
_test_paginated_surface_stroke,
_test_paginated_surface_fill,
NULL, /* fill-stroke */
NULL, /* replaced by show_text_glyphs */
_test_paginated_surface_has_show_text_glyphs,
_test_paginated_surface_show_text_glyphs
};
 
static const cairo_paginated_surface_backend_t test_paginated_surface_paginated_backend = {
NULL, /* start_page */
_test_paginated_surface_set_paginated_mode
};
/programs/develop/libraries/newlib/include/stdio.h
266,6 → 266,10
_ATTRIBUTE ((__format__ (__scanf__, 2, 3))));
int _EXFUN(snprintf, (char *, size_t, const char *, ...)
_ATTRIBUTE ((__format__ (__printf__, 3, 4))));
int _EXFUN(snprintf, (char *, size_t, const char *, ...)
_ATTRIBUTE ((__format__ (__printf__, 3, 4))));
int _EXFUN(snprintf, (char *, size_t, const char *, ...)
_ATTRIBUTE ((__format__ (__printf__, 3, 4))));
int _EXFUN(sniprintf, (char *, size_t, const char *, ...)
_ATTRIBUTE ((__format__ (__printf__, 3, 4))));
char * _EXFUN(tempnam, (const char *, const char *));
/programs/develop/libraries/newlib/sdk/fasm/include/cairo2.inc
0,0 → 1,1112
import cairo2,\
__cairo_clip_all,'__cairo_clip_all',\
__cairo_contour_add_point,'__cairo_contour_add_point',\
__cairo_contour_remove_last_chain,'__cairo_contour_remove_last_chain',\
__cairo_no_compositor,'__cairo_no_compositor',\
_cairo_analysis_surface_create,'_cairo_analysis_surface_create',\
_cairo_analysis_surface_get_bounding_box,'_cairo_analysis_surface_get_bounding_box',\
_cairo_analysis_surface_get_ctm,'_cairo_analysis_surface_get_ctm',\
_cairo_analysis_surface_get_supported,'_cairo_analysis_surface_get_supported',\
_cairo_analysis_surface_get_unsupported,'_cairo_analysis_surface_get_unsupported',\
_cairo_analysis_surface_has_supported,'_cairo_analysis_surface_has_supported',\
_cairo_analysis_surface_has_unsupported,'_cairo_analysis_surface_has_unsupported',\
_cairo_analysis_surface_merge_status,'_cairo_analysis_surface_merge_status',\
_cairo_analysis_surface_set_ctm,'_cairo_analysis_surface_set_ctm',\
_cairo_arc_path,'_cairo_arc_path',\
_cairo_arc_path_negative,'_cairo_arc_path_negative',\
_cairo_array_allocate,'_cairo_array_allocate',\
_cairo_array_append,'_cairo_array_append',\
_cairo_array_append_multiple,'_cairo_array_append_multiple',\
_cairo_array_copy_element,'_cairo_array_copy_element',\
_cairo_array_fini,'_cairo_array_fini',\
_cairo_array_grow_by,'_cairo_array_grow_by',\
_cairo_array_index,'_cairo_array_index',\
_cairo_array_index_const,'_cairo_array_index_const',\
_cairo_array_init,'_cairo_array_init',\
_cairo_array_num_elements,'_cairo_array_num_elements',\
_cairo_array_size,'_cairo_array_size',\
_cairo_array_truncate,'_cairo_array_truncate',\
_cairo_atomic_int_cmpxchg_return_old_impl,'_cairo_atomic_int_cmpxchg_return_old_impl',\
_cairo_atomic_int_dec_and_test,'_cairo_atomic_int_dec_and_test',\
_cairo_atomic_int_inc,'_cairo_atomic_int_inc',\
_cairo_atomic_mutex,'_cairo_atomic_mutex',\
_cairo_atomic_ptr_cmpxchg_return_old_impl,'_cairo_atomic_ptr_cmpxchg_return_old_impl',\
_cairo_base64_stream_create,'_cairo_base64_stream_create',\
_cairo_base85_stream_create,'_cairo_base85_stream_create',\
_cairo_bentley_ottmann_tessellate_boxes,'_cairo_bentley_ottmann_tessellate_boxes',\
_cairo_bentley_ottmann_tessellate_polygon,'_cairo_bentley_ottmann_tessellate_polygon',\
_cairo_bentley_ottmann_tessellate_rectangular_traps,'_cairo_bentley_ottmann_tessellate_rectangular_traps',\
_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes,'_cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes',\
_cairo_bentley_ottmann_tessellate_rectilinear_traps,'_cairo_bentley_ottmann_tessellate_rectilinear_traps',\
_cairo_bentley_ottmann_tessellate_traps,'_cairo_bentley_ottmann_tessellate_traps',\
_cairo_botor_scan_converter_init,'_cairo_botor_scan_converter_init',\
_cairo_box_add_curve_to,'_cairo_box_add_curve_to',\
_cairo_box_from_doubles,'_cairo_box_from_doubles',\
_cairo_box_from_rectangle,'_cairo_box_from_rectangle',\
_cairo_box_intersects_line_segment,'_cairo_box_intersects_line_segment',\
_cairo_box_round_to_rectangle,'_cairo_box_round_to_rectangle',\
_cairo_box_to_doubles,'_cairo_box_to_doubles',\
_cairo_boxes_add,'_cairo_boxes_add',\
_cairo_boxes_clear,'_cairo_boxes_clear',\
_cairo_boxes_extents,'_cairo_boxes_extents',\
_cairo_boxes_fini,'_cairo_boxes_fini',\
_cairo_boxes_for_each_box,'_cairo_boxes_for_each_box',\
_cairo_boxes_get_extents,'_cairo_boxes_get_extents',\
_cairo_boxes_init,'_cairo_boxes_init',\
_cairo_boxes_init_for_array,'_cairo_boxes_init_for_array',\
_cairo_boxes_init_from_rectangle,'_cairo_boxes_init_from_rectangle',\
_cairo_boxes_init_with_clip,'_cairo_boxes_init_with_clip',\
_cairo_boxes_intersect,'_cairo_boxes_intersect',\
_cairo_boxes_limit,'_cairo_boxes_limit',\
_cairo_boxes_to_array,'_cairo_boxes_to_array',\
_cairo_cache_fini,'_cairo_cache_fini',\
_cairo_cache_foreach,'_cairo_cache_foreach',\
_cairo_cache_freeze,'_cairo_cache_freeze',\
_cairo_cache_init,'_cairo_cache_init',\
_cairo_cache_insert,'_cairo_cache_insert',\
_cairo_cache_lookup,'_cairo_cache_lookup',\
_cairo_cache_remove,'_cairo_cache_remove',\
_cairo_cache_thaw,'_cairo_cache_thaw',\
_cairo_cff_fallback_fini,'_cairo_cff_fallback_fini',\
_cairo_cff_fallback_init,'_cairo_cff_fallback_init',\
_cairo_cff_scaled_font_is_cid_cff,'_cairo_cff_scaled_font_is_cid_cff',\
_cairo_cff_subset_fini,'_cairo_cff_subset_fini',\
_cairo_cff_subset_init,'_cairo_cff_subset_init',\
_cairo_clip_combine_with_surface,'_cairo_clip_combine_with_surface',\
_cairo_clip_contains_box,'_cairo_clip_contains_box',\
_cairo_clip_contains_extents,'_cairo_clip_contains_extents',\
_cairo_clip_contains_rectangle,'_cairo_clip_contains_rectangle',\
_cairo_clip_copy,'_cairo_clip_copy',\
_cairo_clip_copy_path,'_cairo_clip_copy_path',\
_cairo_clip_copy_rectangle_list,'_cairo_clip_copy_rectangle_list',\
_cairo_clip_copy_region,'_cairo_clip_copy_region',\
_cairo_clip_copy_with_translation,'_cairo_clip_copy_with_translation',\
_cairo_clip_create,'_cairo_clip_create',\
_cairo_clip_destroy,'_cairo_clip_destroy',\
_cairo_clip_equal,'_cairo_clip_equal',\
_cairo_clip_from_boxes,'_cairo_clip_from_boxes',\
_cairo_clip_get_extents,'_cairo_clip_get_extents',\
_cairo_clip_get_image,'_cairo_clip_get_image',\
_cairo_clip_get_polygon,'_cairo_clip_get_polygon',\
_cairo_clip_get_region,'_cairo_clip_get_region',\
_cairo_clip_get_surface,'_cairo_clip_get_surface',\
_cairo_clip_intersect_box,'_cairo_clip_intersect_box',\
_cairo_clip_intersect_boxes,'_cairo_clip_intersect_boxes',\
_cairo_clip_intersect_clip,'_cairo_clip_intersect_clip',\
_cairo_clip_intersect_path,'_cairo_clip_intersect_path',\
_cairo_clip_intersect_rectangle,'_cairo_clip_intersect_rectangle',\
_cairo_clip_intersect_rectilinear_path,'_cairo_clip_intersect_rectilinear_path',\
_cairo_clip_is_polygon,'_cairo_clip_is_polygon',\
_cairo_clip_is_region,'_cairo_clip_is_region',\
_cairo_clip_path_destroy,'_cairo_clip_path_destroy',\
_cairo_clip_path_reference,'_cairo_clip_path_reference',\
_cairo_clip_reduce_for_composite,'_cairo_clip_reduce_for_composite',\
_cairo_clip_reduce_to_rectangle,'_cairo_clip_reduce_to_rectangle',\
_cairo_clip_reset_static_data,'_cairo_clip_reset_static_data',\
_cairo_clip_tor_scan_converter_create,'_cairo_clip_tor_scan_converter_create',\
_cairo_clip_transform,'_cairo_clip_transform',\
_cairo_clip_translate,'_cairo_clip_translate',\
_cairo_color_double_to_short,'_cairo_color_double_to_short',\
_cairo_color_equal,'_cairo_color_equal',\
_cairo_color_get_content,'_cairo_color_get_content',\
_cairo_color_get_rgba,'_cairo_color_get_rgba',\
_cairo_color_get_rgba_premultiplied,'_cairo_color_get_rgba_premultiplied',\
_cairo_color_init_rgba,'_cairo_color_init_rgba',\
_cairo_color_multiply_alpha,'_cairo_color_multiply_alpha',\
_cairo_color_stop_equal,'_cairo_color_stop_equal',\
_cairo_composite_rectangles_add_to_damage,'_cairo_composite_rectangles_add_to_damage',\
_cairo_composite_rectangles_can_reduce_clip,'_cairo_composite_rectangles_can_reduce_clip',\
_cairo_composite_rectangles_fini,'_cairo_composite_rectangles_fini',\
_cairo_composite_rectangles_init_for_boxes,'_cairo_composite_rectangles_init_for_boxes',\
_cairo_composite_rectangles_init_for_fill,'_cairo_composite_rectangles_init_for_fill',\
_cairo_composite_rectangles_init_for_glyphs,'_cairo_composite_rectangles_init_for_glyphs',\
_cairo_composite_rectangles_init_for_mask,'_cairo_composite_rectangles_init_for_mask',\
_cairo_composite_rectangles_init_for_paint,'_cairo_composite_rectangles_init_for_paint',\
_cairo_composite_rectangles_init_for_polygon,'_cairo_composite_rectangles_init_for_polygon',\
_cairo_composite_rectangles_init_for_stroke,'_cairo_composite_rectangles_init_for_stroke',\
_cairo_composite_rectangles_intersect_mask_extents,'_cairo_composite_rectangles_intersect_mask_extents',\
_cairo_composite_rectangles_intersect_source_extents,'_cairo_composite_rectangles_intersect_source_extents',\
_cairo_compositor_fill,'_cairo_compositor_fill',\
_cairo_compositor_glyphs,'_cairo_compositor_glyphs',\
_cairo_compositor_mask,'_cairo_compositor_mask',\
_cairo_compositor_paint,'_cairo_compositor_paint',\
_cairo_compositor_stroke,'_cairo_compositor_stroke',\
_cairo_content_from_format,'_cairo_content_from_format',\
_cairo_content_from_pixman_format,'_cairo_content_from_pixman_format',\
_cairo_contour_add,'_cairo_contour_add',\
_cairo_contour_add_reversed,'_cairo_contour_add_reversed',\
_cairo_contour_fini,'_cairo_contour_fini',\
_cairo_contour_init,'_cairo_contour_init',\
_cairo_contour_reset,'_cairo_contour_reset',\
_cairo_contour_reverse,'_cairo_contour_reverse',\
_cairo_contour_simplify,'_cairo_contour_simplify',\
_cairo_create_in_error,'_cairo_create_in_error',\
_cairo_damage_add_box,'_cairo_damage_add_box',\
_cairo_damage_add_rectangle,'_cairo_damage_add_rectangle',\
_cairo_damage_add_region,'_cairo_damage_add_region',\
_cairo_damage_create,'_cairo_damage_create',\
_cairo_damage_create_in_error,'_cairo_damage_create_in_error',\
_cairo_damage_destroy,'_cairo_damage_destroy',\
_cairo_damage_reduce,'_cairo_damage_reduce',\
_cairo_debug_print_boxes,'_cairo_debug_print_boxes',\
_cairo_debug_print_clip,'_cairo_debug_print_clip',\
_cairo_debug_print_contour,'_cairo_debug_print_contour',\
_cairo_debug_print_path,'_cairo_debug_print_path',\
_cairo_debug_print_pattern,'_cairo_debug_print_pattern',\
_cairo_debug_print_polygon,'_cairo_debug_print_polygon',\
_cairo_debug_print_traps,'_cairo_debug_print_traps',\
_cairo_default_context_create,'_cairo_default_context_create',\
_cairo_default_context_fini,'_cairo_default_context_fini',\
_cairo_default_context_init,'_cairo_default_context_init',\
_cairo_default_context_reset_static_data,'_cairo_default_context_reset_static_data',\
_cairo_deflate_stream_create,'_cairo_deflate_stream_create',\
_cairo_device_create_in_error,'_cairo_device_create_in_error',\
_cairo_device_init,'_cairo_device_init',\
_cairo_device_set_error,'_cairo_device_set_error',\
_cairo_empty_rectangle,'_cairo_empty_rectangle',\
_cairo_error,'_cairo_error',\
_cairo_fallback_compositor,'_cairo_fallback_compositor',\
_cairo_fini,'_cairo_fini',\
_cairo_font_face_init,'_cairo_font_face_init',\
_cairo_font_face_is_user,'_cairo_font_face_is_user',\
_cairo_font_face_nil,'_cairo_font_face_nil',\
_cairo_font_face_set_error,'_cairo_font_face_set_error',\
_cairo_font_face_twin_create_fallback,'_cairo_font_face_twin_create_fallback',\
_cairo_font_face_twin_create_for_toy,'_cairo_font_face_twin_create_for_toy',\
_cairo_font_options_get_lcd_filter,'_cairo_font_options_get_lcd_filter',\
_cairo_font_options_get_round_glyph_positions,'_cairo_font_options_get_round_glyph_positions',\
_cairo_font_options_init_copy,'_cairo_font_options_init_copy',\
_cairo_font_options_init_default,'_cairo_font_options_init_default',\
_cairo_font_options_set_lcd_filter,'_cairo_font_options_set_lcd_filter',\
_cairo_font_options_set_round_glyph_positions,'_cairo_font_options_set_round_glyph_positions',\
_cairo_format_bits_per_pixel,'_cairo_format_bits_per_pixel',\
_cairo_format_from_content,'_cairo_format_from_content',\
_cairo_format_from_pixman_format,'_cairo_format_from_pixman_format',\
_cairo_format_to_pixman_format_code,'_cairo_format_to_pixman_format_code',\
_cairo_freelist_alloc,'_cairo_freelist_alloc',\
_cairo_freelist_calloc,'_cairo_freelist_calloc',\
_cairo_freelist_fini,'_cairo_freelist_fini',\
_cairo_freelist_free,'_cairo_freelist_free',\
_cairo_freelist_init,'_cairo_freelist_init',\
_cairo_freepool_alloc_array,'_cairo_freepool_alloc_array',\
_cairo_freepool_alloc_from_new_pool,'_cairo_freepool_alloc_from_new_pool',\
_cairo_freepool_fini,'_cairo_freepool_fini',\
_cairo_freepool_init,'_cairo_freepool_init',\
_cairo_ft_font_face_backend,'_cairo_ft_font_face_backend',\
_cairo_ft_font_reset_static_data,'_cairo_ft_font_reset_static_data',\
_cairo_ft_scaled_font_get_load_flags,'_cairo_ft_scaled_font_get_load_flags',\
_cairo_ft_unscaled_font_map_mutex,'_cairo_ft_unscaled_font_map_mutex',\
_cairo_glyph_cache_mutex,'_cairo_glyph_cache_mutex',\
_cairo_gradient_pattern_box_to_parameter,'_cairo_gradient_pattern_box_to_parameter',\
_cairo_gradient_pattern_fit_to_range,'_cairo_gradient_pattern_fit_to_range',\
_cairo_gradient_pattern_interpolate,'_cairo_gradient_pattern_interpolate',\
_cairo_gradient_pattern_is_solid,'_cairo_gradient_pattern_is_solid',\
_cairo_gstate_backend_to_user_rectangle,'_cairo_gstate_backend_to_user_rectangle',\
_cairo_gstate_clip,'_cairo_gstate_clip',\
_cairo_gstate_clip_extents,'_cairo_gstate_clip_extents',\
_cairo_gstate_copy_clip_rectangle_list,'_cairo_gstate_copy_clip_rectangle_list',\
_cairo_gstate_copy_page,'_cairo_gstate_copy_page',\
_cairo_gstate_device_to_user,'_cairo_gstate_device_to_user',\
_cairo_gstate_device_to_user_distance,'_cairo_gstate_device_to_user_distance',\
_cairo_gstate_fill,'_cairo_gstate_fill',\
_cairo_gstate_fill_extents,'_cairo_gstate_fill_extents',\
_cairo_gstate_fini,'_cairo_gstate_fini',\
_cairo_gstate_get_antialias,'_cairo_gstate_get_antialias',\
_cairo_gstate_get_clip,'_cairo_gstate_get_clip',\
_cairo_gstate_get_dash,'_cairo_gstate_get_dash',\
_cairo_gstate_get_fill_rule,'_cairo_gstate_get_fill_rule',\
_cairo_gstate_get_font_extents,'_cairo_gstate_get_font_extents',\
_cairo_gstate_get_font_face,'_cairo_gstate_get_font_face',\
_cairo_gstate_get_font_matrix,'_cairo_gstate_get_font_matrix',\
_cairo_gstate_get_font_options,'_cairo_gstate_get_font_options',\
_cairo_gstate_get_line_cap,'_cairo_gstate_get_line_cap',\
_cairo_gstate_get_line_join,'_cairo_gstate_get_line_join',\
_cairo_gstate_get_line_width,'_cairo_gstate_get_line_width',\
_cairo_gstate_get_matrix,'_cairo_gstate_get_matrix',\
_cairo_gstate_get_miter_limit,'_cairo_gstate_get_miter_limit',\
_cairo_gstate_get_opacity,'_cairo_gstate_get_opacity',\
_cairo_gstate_get_operator,'_cairo_gstate_get_operator',\
_cairo_gstate_get_original_target,'_cairo_gstate_get_original_target',\
_cairo_gstate_get_scaled_font,'_cairo_gstate_get_scaled_font',\
_cairo_gstate_get_source,'_cairo_gstate_get_source',\
_cairo_gstate_get_target,'_cairo_gstate_get_target',\
_cairo_gstate_get_tolerance,'_cairo_gstate_get_tolerance',\
_cairo_gstate_glyph_extents,'_cairo_gstate_glyph_extents',\
_cairo_gstate_glyph_path,'_cairo_gstate_glyph_path',\
_cairo_gstate_identity_matrix,'_cairo_gstate_identity_matrix',\
_cairo_gstate_in_clip,'_cairo_gstate_in_clip',\
_cairo_gstate_in_fill,'_cairo_gstate_in_fill',\
_cairo_gstate_in_stroke,'_cairo_gstate_in_stroke',\
_cairo_gstate_init,'_cairo_gstate_init',\
_cairo_gstate_is_group,'_cairo_gstate_is_group',\
_cairo_gstate_mask,'_cairo_gstate_mask',\
_cairo_gstate_paint,'_cairo_gstate_paint',\
_cairo_gstate_path_extents,'_cairo_gstate_path_extents',\
_cairo_gstate_redirect_target,'_cairo_gstate_redirect_target',\
_cairo_gstate_reset_clip,'_cairo_gstate_reset_clip',\
_cairo_gstate_restore,'_cairo_gstate_restore',\
_cairo_gstate_rotate,'_cairo_gstate_rotate',\
_cairo_gstate_save,'_cairo_gstate_save',\
_cairo_gstate_scale,'_cairo_gstate_scale',\
_cairo_gstate_set_antialias,'_cairo_gstate_set_antialias',\
_cairo_gstate_set_dash,'_cairo_gstate_set_dash',\
_cairo_gstate_set_fill_rule,'_cairo_gstate_set_fill_rule',\
_cairo_gstate_set_font_face,'_cairo_gstate_set_font_face',\
_cairo_gstate_set_font_matrix,'_cairo_gstate_set_font_matrix',\
_cairo_gstate_set_font_options,'_cairo_gstate_set_font_options',\
_cairo_gstate_set_font_size,'_cairo_gstate_set_font_size',\
_cairo_gstate_set_line_cap,'_cairo_gstate_set_line_cap',\
_cairo_gstate_set_line_join,'_cairo_gstate_set_line_join',\
_cairo_gstate_set_line_width,'_cairo_gstate_set_line_width',\
_cairo_gstate_set_matrix,'_cairo_gstate_set_matrix',\
_cairo_gstate_set_miter_limit,'_cairo_gstate_set_miter_limit',\
_cairo_gstate_set_opacity,'_cairo_gstate_set_opacity',\
_cairo_gstate_set_operator,'_cairo_gstate_set_operator',\
_cairo_gstate_set_source,'_cairo_gstate_set_source',\
_cairo_gstate_set_tolerance,'_cairo_gstate_set_tolerance',\
_cairo_gstate_show_page,'_cairo_gstate_show_page',\
_cairo_gstate_show_text_glyphs,'_cairo_gstate_show_text_glyphs',\
_cairo_gstate_stroke,'_cairo_gstate_stroke',\
_cairo_gstate_stroke_extents,'_cairo_gstate_stroke_extents',\
_cairo_gstate_transform,'_cairo_gstate_transform',\
_cairo_gstate_translate,'_cairo_gstate_translate',\
_cairo_gstate_user_to_device,'_cairo_gstate_user_to_device',\
_cairo_gstate_user_to_device_distance,'_cairo_gstate_user_to_device_distance',\
_cairo_half_from_float,'_cairo_half_from_float',\
_cairo_hash_bytes,'_cairo_hash_bytes',\
_cairo_hash_string,'_cairo_hash_string',\
_cairo_hash_table_create,'_cairo_hash_table_create',\
_cairo_hash_table_destroy,'_cairo_hash_table_destroy',\
_cairo_hash_table_foreach,'_cairo_hash_table_foreach',\
_cairo_hash_table_insert,'_cairo_hash_table_insert',\
_cairo_hash_table_lookup,'_cairo_hash_table_lookup',\
_cairo_hash_table_random_entry,'_cairo_hash_table_random_entry',\
_cairo_hash_table_remove,'_cairo_hash_table_remove',\
_cairo_hull_compute,'_cairo_hull_compute',\
_cairo_image_analyze_color,'_cairo_image_analyze_color',\
_cairo_image_analyze_transparency,'_cairo_image_analyze_transparency',\
_cairo_image_info_get_jpeg_info,'_cairo_image_info_get_jpeg_info',\
_cairo_image_info_get_jpx_info,'_cairo_image_info_get_jpx_info',\
_cairo_image_info_get_png_info,'_cairo_image_info_get_png_info',\
_cairo_image_mask_compositor_get,'_cairo_image_mask_compositor_get',\
_cairo_image_reset_static_data,'_cairo_image_reset_static_data',\
_cairo_image_scaled_glyph_fini,'_cairo_image_scaled_glyph_fini',\
_cairo_image_solid_cache_mutex,'_cairo_image_solid_cache_mutex',\
_cairo_image_source_backend,'_cairo_image_source_backend',\
_cairo_image_source_create_for_pattern,'_cairo_image_source_create_for_pattern',\
_cairo_image_spans_compositor_get,'_cairo_image_spans_compositor_get',\
_cairo_image_surface_acquire_source_image,'_cairo_image_surface_acquire_source_image',\
_cairo_image_surface_assume_ownership_of_data,'_cairo_image_surface_assume_ownership_of_data',\
_cairo_image_surface_backend,'_cairo_image_surface_backend',\
_cairo_image_surface_clone_subimage,'_cairo_image_surface_clone_subimage',\
_cairo_image_surface_coerce,'_cairo_image_surface_coerce',\
_cairo_image_surface_coerce_to_format,'_cairo_image_surface_coerce_to_format',\
_cairo_image_surface_create_for_pixman_image,'_cairo_image_surface_create_for_pixman_image',\
_cairo_image_surface_create_from_image,'_cairo_image_surface_create_from_image',\
_cairo_image_surface_create_similar,'_cairo_image_surface_create_similar',\
_cairo_image_surface_create_with_content,'_cairo_image_surface_create_with_content',\
_cairo_image_surface_create_with_pixman_format,'_cairo_image_surface_create_with_pixman_format',\
_cairo_image_surface_fill,'_cairo_image_surface_fill',\
_cairo_image_surface_finish,'_cairo_image_surface_finish',\
_cairo_image_surface_get_extents,'_cairo_image_surface_get_extents',\
_cairo_image_surface_get_font_options,'_cairo_image_surface_get_font_options',\
_cairo_image_surface_glyphs,'_cairo_image_surface_glyphs',\
_cairo_image_surface_init,'_cairo_image_surface_init',\
_cairo_image_surface_map_to_image,'_cairo_image_surface_map_to_image',\
_cairo_image_surface_mask,'_cairo_image_surface_mask',\
_cairo_image_surface_paint,'_cairo_image_surface_paint',\
_cairo_image_surface_release_source_image,'_cairo_image_surface_release_source_image',\
_cairo_image_surface_snapshot,'_cairo_image_surface_snapshot',\
_cairo_image_surface_source,'_cairo_image_surface_source',\
_cairo_image_surface_stroke,'_cairo_image_surface_stroke',\
_cairo_image_surface_unmap_image,'_cairo_image_surface_unmap_image',\
_cairo_image_traps_compositor_get,'_cairo_image_traps_compositor_get',\
_cairo_init,'_cairo_init',\
_cairo_int128_cmp,'_cairo_int128_cmp',\
_cairo_int128_divrem,'_cairo_int128_divrem',\
_cairo_int128_lt,'_cairo_int128_lt',\
_cairo_int32_to_int128,'_cairo_int32_to_int128',\
_cairo_int64_to_int128,'_cairo_int64_to_int128',\
_cairo_int64x64_128_mul,'_cairo_int64x64_128_mul',\
_cairo_int_96by64_32x64_divrem,'_cairo_int_96by64_32x64_divrem',\
_cairo_int_surface_create_in_error,'_cairo_int_surface_create_in_error',\
_cairo_intern_string,'_cairo_intern_string',\
_cairo_intern_string_mutex,'_cairo_intern_string_mutex',\
_cairo_intern_string_reset_static_data,'_cairo_intern_string_reset_static_data',\
_cairo_linear_pattern_equal,'_cairo_linear_pattern_equal',\
_cairo_linear_pattern_hash,'_cairo_linear_pattern_hash',\
_cairo_lround,'_cairo_lround',\
_cairo_lzw_compress,'_cairo_lzw_compress',\
_cairo_mask_compositor_init,'_cairo_mask_compositor_init',\
_cairo_matrix_compute_basis_scale_factors,'_cairo_matrix_compute_basis_scale_factors',\
_cairo_matrix_compute_determinant,'_cairo_matrix_compute_determinant',\
_cairo_matrix_get_affine,'_cairo_matrix_get_affine',\
_cairo_matrix_has_unity_scale,'_cairo_matrix_has_unity_scale',\
_cairo_matrix_is_integer_translation,'_cairo_matrix_is_integer_translation',\
_cairo_matrix_is_invertible,'_cairo_matrix_is_invertible',\
_cairo_matrix_is_pixel_exact,'_cairo_matrix_is_pixel_exact',\
_cairo_matrix_is_pixman_translation,'_cairo_matrix_is_pixman_translation',\
_cairo_matrix_is_scale_0,'_cairo_matrix_is_scale_0',\
_cairo_matrix_multiply,'_cairo_matrix_multiply',\
_cairo_matrix_to_pixman_matrix_offset,'_cairo_matrix_to_pixman_matrix_offset',\
_cairo_matrix_transform_bounding_box,'_cairo_matrix_transform_bounding_box',\
_cairo_matrix_transform_bounding_box_fixed,'_cairo_matrix_transform_bounding_box_fixed',\
_cairo_matrix_transformed_circle_major_axis,'_cairo_matrix_transformed_circle_major_axis',\
_cairo_memory_stream_copy,'_cairo_memory_stream_copy',\
_cairo_memory_stream_create,'_cairo_memory_stream_create',\
_cairo_memory_stream_destroy,'_cairo_memory_stream_destroy',\
_cairo_memory_stream_length,'_cairo_memory_stream_length',\
_cairo_mempool_alloc,'_cairo_mempool_alloc',\
_cairo_mempool_fini,'_cairo_mempool_fini',\
_cairo_mempool_free,'_cairo_mempool_free',\
_cairo_mempool_init,'_cairo_mempool_init',\
_cairo_mesh_pattern_coord_box,'_cairo_mesh_pattern_coord_box',\
_cairo_mesh_pattern_rasterize,'_cairo_mesh_pattern_rasterize',\
_cairo_mono_scan_converter_add_polygon,'_cairo_mono_scan_converter_add_polygon',\
_cairo_mono_scan_converter_create,'_cairo_mono_scan_converter_create',\
_cairo_null_stream_create,'_cairo_null_stream_create',\
_cairo_null_surface_create,'_cairo_null_surface_create',\
_cairo_observers_notify,'_cairo_observers_notify',\
_cairo_operator_bounded_by_either,'_cairo_operator_bounded_by_either',\
_cairo_operator_bounded_by_mask,'_cairo_operator_bounded_by_mask',\
_cairo_operator_bounded_by_source,'_cairo_operator_bounded_by_source',\
_cairo_output_stream_close,'_cairo_output_stream_close',\
_cairo_output_stream_create,'_cairo_output_stream_create',\
_cairo_output_stream_create_for_file,'_cairo_output_stream_create_for_file',\
_cairo_output_stream_create_for_filename,'_cairo_output_stream_create_for_filename',\
_cairo_output_stream_create_in_error,'_cairo_output_stream_create_in_error',\
_cairo_output_stream_destroy,'_cairo_output_stream_destroy',\
_cairo_output_stream_fini,'_cairo_output_stream_fini',\
_cairo_output_stream_flush,'_cairo_output_stream_flush',\
_cairo_output_stream_get_position,'_cairo_output_stream_get_position',\
_cairo_output_stream_get_status,'_cairo_output_stream_get_status',\
_cairo_output_stream_init,'_cairo_output_stream_init',\
_cairo_output_stream_nil,'_cairo_output_stream_nil',\
_cairo_output_stream_printf,'_cairo_output_stream_printf',\
_cairo_output_stream_vprintf,'_cairo_output_stream_vprintf',\
_cairo_output_stream_write,'_cairo_output_stream_write',\
_cairo_output_stream_write_hex_string,'_cairo_output_stream_write_hex_string',\
_cairo_paginated_surface_create,'_cairo_paginated_surface_create',\
_cairo_paginated_surface_get_recording,'_cairo_paginated_surface_get_recording',\
_cairo_paginated_surface_get_target,'_cairo_paginated_surface_get_target',\
_cairo_paginated_surface_set_size,'_cairo_paginated_surface_set_size',\
_cairo_path_append_to_context,'_cairo_path_append_to_context',\
_cairo_path_bounder_extents,'_cairo_path_bounder_extents',\
_cairo_path_create,'_cairo_path_create',\
_cairo_path_create_flat,'_cairo_path_create_flat',\
_cairo_path_create_in_error,'_cairo_path_create_in_error',\
_cairo_path_fixed_append,'_cairo_path_fixed_append',\
_cairo_path_fixed_approximate_clip_extents,'_cairo_path_fixed_approximate_clip_extents',\
_cairo_path_fixed_approximate_fill_extents,'_cairo_path_fixed_approximate_fill_extents',\
_cairo_path_fixed_approximate_stroke_extents,'_cairo_path_fixed_approximate_stroke_extents',\
_cairo_path_fixed_close_path,'_cairo_path_fixed_close_path',\
_cairo_path_fixed_create,'_cairo_path_fixed_create',\
_cairo_path_fixed_curve_to,'_cairo_path_fixed_curve_to',\
_cairo_path_fixed_destroy,'_cairo_path_fixed_destroy',\
_cairo_path_fixed_equal,'_cairo_path_fixed_equal',\
_cairo_path_fixed_extents,'_cairo_path_fixed_extents',\
_cairo_path_fixed_fill_extents,'_cairo_path_fixed_fill_extents',\
_cairo_path_fixed_fill_rectilinear_to_boxes,'_cairo_path_fixed_fill_rectilinear_to_boxes',\
_cairo_path_fixed_fill_rectilinear_to_polygon,'_cairo_path_fixed_fill_rectilinear_to_polygon',\
_cairo_path_fixed_fill_to_polygon,'_cairo_path_fixed_fill_to_polygon',\
_cairo_path_fixed_fill_to_traps,'_cairo_path_fixed_fill_to_traps',\
_cairo_path_fixed_fini,'_cairo_path_fixed_fini',\
_cairo_path_fixed_get_current_point,'_cairo_path_fixed_get_current_point',\
_cairo_path_fixed_hash,'_cairo_path_fixed_hash',\
_cairo_path_fixed_in_fill,'_cairo_path_fixed_in_fill',\
_cairo_path_fixed_init,'_cairo_path_fixed_init',\
_cairo_path_fixed_init_copy,'_cairo_path_fixed_init_copy',\
_cairo_path_fixed_interpret,'_cairo_path_fixed_interpret',\
_cairo_path_fixed_interpret_flat,'_cairo_path_fixed_interpret_flat',\
_cairo_path_fixed_is_box,'_cairo_path_fixed_is_box',\
_cairo_path_fixed_is_rectangle,'_cairo_path_fixed_is_rectangle',\
_cairo_path_fixed_is_simple_quad,'_cairo_path_fixed_is_simple_quad',\
_cairo_path_fixed_is_stroke_box,'_cairo_path_fixed_is_stroke_box',\
_cairo_path_fixed_iter_at_end,'_cairo_path_fixed_iter_at_end',\
_cairo_path_fixed_iter_init,'_cairo_path_fixed_iter_init',\
_cairo_path_fixed_iter_is_fill_box,'_cairo_path_fixed_iter_is_fill_box',\
_cairo_path_fixed_line_to,'_cairo_path_fixed_line_to',\
_cairo_path_fixed_move_to,'_cairo_path_fixed_move_to',\
_cairo_path_fixed_new_sub_path,'_cairo_path_fixed_new_sub_path',\
_cairo_path_fixed_rel_curve_to,'_cairo_path_fixed_rel_curve_to',\
_cairo_path_fixed_rel_line_to,'_cairo_path_fixed_rel_line_to',\
_cairo_path_fixed_rel_move_to,'_cairo_path_fixed_rel_move_to',\
_cairo_path_fixed_size,'_cairo_path_fixed_size',\
_cairo_path_fixed_stroke_dashed_to_polygon,'_cairo_path_fixed_stroke_dashed_to_polygon',\
_cairo_path_fixed_stroke_extents,'_cairo_path_fixed_stroke_extents',\
_cairo_path_fixed_stroke_polygon_to_traps,'_cairo_path_fixed_stroke_polygon_to_traps',\
_cairo_path_fixed_stroke_rectilinear_to_boxes,'_cairo_path_fixed_stroke_rectilinear_to_boxes',\
_cairo_path_fixed_stroke_to_polygon,'_cairo_path_fixed_stroke_to_polygon',\
_cairo_path_fixed_stroke_to_shaper,'_cairo_path_fixed_stroke_to_shaper',\
_cairo_path_fixed_stroke_to_traps,'_cairo_path_fixed_stroke_to_traps',\
_cairo_path_fixed_stroke_to_tristrip,'_cairo_path_fixed_stroke_to_tristrip',\
_cairo_path_fixed_transform,'_cairo_path_fixed_transform',\
_cairo_path_fixed_translate,'_cairo_path_fixed_translate',\
_cairo_pattern_alpha_range,'_cairo_pattern_alpha_range',\
_cairo_pattern_analyze_filter,'_cairo_pattern_analyze_filter',\
_cairo_pattern_black,'_cairo_pattern_black',\
_cairo_pattern_clear,'_cairo_pattern_clear',\
_cairo_pattern_create_copy,'_cairo_pattern_create_copy',\
_cairo_pattern_create_in_error,'_cairo_pattern_create_in_error',\
_cairo_pattern_create_solid,'_cairo_pattern_create_solid',\
_cairo_pattern_equal,'_cairo_pattern_equal',\
_cairo_pattern_fini,'_cairo_pattern_fini',\
_cairo_pattern_get_extents,'_cairo_pattern_get_extents',\
_cairo_pattern_get_ink_extents,'_cairo_pattern_get_ink_extents',\
_cairo_pattern_hash,'_cairo_pattern_hash',\
_cairo_pattern_init,'_cairo_pattern_init',\
_cairo_pattern_init_copy,'_cairo_pattern_init_copy',\
_cairo_pattern_init_for_surface,'_cairo_pattern_init_for_surface',\
_cairo_pattern_init_snapshot,'_cairo_pattern_init_snapshot',\
_cairo_pattern_init_solid,'_cairo_pattern_init_solid',\
_cairo_pattern_init_static_copy,'_cairo_pattern_init_static_copy',\
_cairo_pattern_is_clear,'_cairo_pattern_is_clear',\
_cairo_pattern_is_opaque,'_cairo_pattern_is_opaque',\
_cairo_pattern_is_opaque_solid,'_cairo_pattern_is_opaque_solid',\
_cairo_pattern_reset_static_data,'_cairo_pattern_reset_static_data',\
_cairo_pattern_sampled_area,'_cairo_pattern_sampled_area',\
_cairo_pattern_solid_surface_cache_lock,'_cairo_pattern_solid_surface_cache_lock',\
_cairo_pattern_transform,'_cairo_pattern_transform',\
_cairo_pattern_white,'_cairo_pattern_white',\
_cairo_pdf_operators_clip,'_cairo_pdf_operators_clip',\
_cairo_pdf_operators_emit_stroke_style,'_cairo_pdf_operators_emit_stroke_style',\
_cairo_pdf_operators_enable_actual_text,'_cairo_pdf_operators_enable_actual_text',\
_cairo_pdf_operators_fill,'_cairo_pdf_operators_fill',\
_cairo_pdf_operators_fill_stroke,'_cairo_pdf_operators_fill_stroke',\
_cairo_pdf_operators_fini,'_cairo_pdf_operators_fini',\
_cairo_pdf_operators_flush,'_cairo_pdf_operators_flush',\
_cairo_pdf_operators_init,'_cairo_pdf_operators_init',\
_cairo_pdf_operators_reset,'_cairo_pdf_operators_reset',\
_cairo_pdf_operators_set_cairo_to_pdf_matrix,'_cairo_pdf_operators_set_cairo_to_pdf_matrix',\
_cairo_pdf_operators_set_font_subsets_callback,'_cairo_pdf_operators_set_font_subsets_callback',\
_cairo_pdf_operators_set_stream,'_cairo_pdf_operators_set_stream',\
_cairo_pdf_operators_show_text_glyphs,'_cairo_pdf_operators_show_text_glyphs',\
_cairo_pdf_operators_stroke,'_cairo_pdf_operators_stroke',\
_cairo_pdf_shading_fini,'_cairo_pdf_shading_fini',\
_cairo_pdf_shading_init_alpha,'_cairo_pdf_shading_init_alpha',\
_cairo_pdf_shading_init_color,'_cairo_pdf_shading_init_color',\
_cairo_pen_add_points,'_cairo_pen_add_points',\
_cairo_pen_find_active_ccw_vertex_index,'_cairo_pen_find_active_ccw_vertex_index',\
_cairo_pen_find_active_ccw_vertices,'_cairo_pen_find_active_ccw_vertices',\
_cairo_pen_find_active_cw_vertex_index,'_cairo_pen_find_active_cw_vertex_index',\
_cairo_pen_find_active_cw_vertices,'_cairo_pen_find_active_cw_vertices',\
_cairo_pen_fini,'_cairo_pen_fini',\
_cairo_pen_init,'_cairo_pen_init',\
_cairo_pen_init_copy,'_cairo_pen_init_copy',\
_cairo_pen_vertices_needed,'_cairo_pen_vertices_needed',\
_cairo_polygon_add_contour,'_cairo_polygon_add_contour',\
_cairo_polygon_add_external_edge,'_cairo_polygon_add_external_edge',\
_cairo_polygon_add_line,'_cairo_polygon_add_line',\
_cairo_polygon_fini,'_cairo_polygon_fini',\
_cairo_polygon_init,'_cairo_polygon_init',\
_cairo_polygon_init_box_array,'_cairo_polygon_init_box_array',\
_cairo_polygon_init_boxes,'_cairo_polygon_init_boxes',\
_cairo_polygon_init_with_clip,'_cairo_polygon_init_with_clip',\
_cairo_polygon_intersect,'_cairo_polygon_intersect',\
_cairo_polygon_intersect_with_boxes,'_cairo_polygon_intersect_with_boxes',\
_cairo_polygon_limit,'_cairo_polygon_limit',\
_cairo_polygon_limit_to_clip,'_cairo_polygon_limit_to_clip',\
_cairo_polygon_reduce,'_cairo_polygon_reduce',\
_cairo_polygon_translate,'_cairo_polygon_translate',\
_cairo_ps_standard_encoding_to_glyphname,'_cairo_ps_standard_encoding_to_glyphname',\
_cairo_radial_pattern_equal,'_cairo_radial_pattern_equal',\
_cairo_radial_pattern_focus_is_inside,'_cairo_radial_pattern_focus_is_inside',\
_cairo_radial_pattern_hash,'_cairo_radial_pattern_hash',\
_cairo_raster_source_pattern_acquire,'_cairo_raster_source_pattern_acquire',\
_cairo_raster_source_pattern_finish,'_cairo_raster_source_pattern_finish',\
_cairo_raster_source_pattern_init_copy,'_cairo_raster_source_pattern_init_copy',\
_cairo_raster_source_pattern_release,'_cairo_raster_source_pattern_release',\
_cairo_raster_source_pattern_snapshot,'_cairo_raster_source_pattern_snapshot',\
_cairo_rasterise_polygon_to_boxes,'_cairo_rasterise_polygon_to_boxes',\
_cairo_rasterise_polygon_to_traps,'_cairo_rasterise_polygon_to_traps',\
_cairo_recording_surface_get_bbox,'_cairo_recording_surface_get_bbox',\
_cairo_recording_surface_get_ink_bbox,'_cairo_recording_surface_get_ink_bbox',\
_cairo_recording_surface_get_path,'_cairo_recording_surface_get_path',\
_cairo_recording_surface_replay,'_cairo_recording_surface_replay',\
_cairo_recording_surface_replay_and_create_regions,'_cairo_recording_surface_replay_and_create_regions',\
_cairo_recording_surface_replay_one,'_cairo_recording_surface_replay_one',\
_cairo_recording_surface_replay_region,'_cairo_recording_surface_replay_region',\
_cairo_recording_surface_replay_with_clip,'_cairo_recording_surface_replay_with_clip',\
_cairo_rectangle_int_from_double,'_cairo_rectangle_int_from_double',\
_cairo_rectangle_intersect,'_cairo_rectangle_intersect',\
_cairo_rectangle_list_create_in_error,'_cairo_rectangle_list_create_in_error',\
_cairo_rectangle_union,'_cairo_rectangle_union',\
_cairo_rectangles_nil,'_cairo_rectangles_nil',\
_cairo_rectangular_scan_converter_add_box,'_cairo_rectangular_scan_converter_add_box',\
_cairo_rectangular_scan_converter_init,'_cairo_rectangular_scan_converter_init',\
_cairo_region_create_from_boxes,'_cairo_region_create_from_boxes',\
_cairo_region_create_in_error,'_cairo_region_create_in_error',\
_cairo_region_fini,'_cairo_region_fini',\
_cairo_region_get_boxes,'_cairo_region_get_boxes',\
_cairo_region_init,'_cairo_region_init',\
_cairo_region_init_rectangle,'_cairo_region_init_rectangle',\
_cairo_rtree_evict_random,'_cairo_rtree_evict_random',\
_cairo_rtree_fini,'_cairo_rtree_fini',\
_cairo_rtree_foreach,'_cairo_rtree_foreach',\
_cairo_rtree_init,'_cairo_rtree_init',\
_cairo_rtree_insert,'_cairo_rtree_insert',\
_cairo_rtree_node_collapse,'_cairo_rtree_node_collapse',\
_cairo_rtree_node_create,'_cairo_rtree_node_create',\
_cairo_rtree_node_destroy,'_cairo_rtree_node_destroy',\
_cairo_rtree_node_insert,'_cairo_rtree_node_insert',\
_cairo_rtree_node_remove,'_cairo_rtree_node_remove',\
_cairo_rtree_reset,'_cairo_rtree_reset',\
_cairo_rtree_unpin,'_cairo_rtree_unpin',\
_cairo_scaled_font_attach_private,'_cairo_scaled_font_attach_private',\
_cairo_scaled_font_create_in_error,'_cairo_scaled_font_create_in_error',\
_cairo_scaled_font_error_mutex,'_cairo_scaled_font_error_mutex',\
_cairo_scaled_font_find_private,'_cairo_scaled_font_find_private',\
_cairo_scaled_font_fini,'_cairo_scaled_font_fini',\
_cairo_scaled_font_freeze_cache,'_cairo_scaled_font_freeze_cache',\
_cairo_scaled_font_get_max_scale,'_cairo_scaled_font_get_max_scale',\
_cairo_scaled_font_glyph_approximate_extents,'_cairo_scaled_font_glyph_approximate_extents',\
_cairo_scaled_font_glyph_device_extents,'_cairo_scaled_font_glyph_device_extents',\
_cairo_scaled_font_glyph_path,'_cairo_scaled_font_glyph_path',\
_cairo_scaled_font_init,'_cairo_scaled_font_init',\
_cairo_scaled_font_is_ft,'_cairo_scaled_font_is_ft',\
_cairo_scaled_font_map_destroy,'_cairo_scaled_font_map_destroy',\
_cairo_scaled_font_map_mutex,'_cairo_scaled_font_map_mutex',\
_cairo_scaled_font_register_placeholder_and_unlock_font_map,'_cairo_scaled_font_register_placeholder_and_unlock_font_map',\
_cairo_scaled_font_reset_cache,'_cairo_scaled_font_reset_cache',\
_cairo_scaled_font_reset_static_data,'_cairo_scaled_font_reset_static_data',\
_cairo_scaled_font_set_error,'_cairo_scaled_font_set_error',\
_cairo_scaled_font_set_metrics,'_cairo_scaled_font_set_metrics',\
_cairo_scaled_font_subset_create_glyph_names,'_cairo_scaled_font_subset_create_glyph_names',\
_cairo_scaled_font_subsets_create_composite,'_cairo_scaled_font_subsets_create_composite',\
_cairo_scaled_font_subsets_create_scaled,'_cairo_scaled_font_subsets_create_scaled',\
_cairo_scaled_font_subsets_create_simple,'_cairo_scaled_font_subsets_create_simple',\
_cairo_scaled_font_subsets_destroy,'_cairo_scaled_font_subsets_destroy',\
_cairo_scaled_font_subsets_enable_latin_subset,'_cairo_scaled_font_subsets_enable_latin_subset',\
_cairo_scaled_font_subsets_foreach_scaled,'_cairo_scaled_font_subsets_foreach_scaled',\
_cairo_scaled_font_subsets_foreach_unscaled,'_cairo_scaled_font_subsets_foreach_unscaled',\
_cairo_scaled_font_subsets_foreach_user,'_cairo_scaled_font_subsets_foreach_user',\
_cairo_scaled_font_subsets_map_glyph,'_cairo_scaled_font_subsets_map_glyph',\
_cairo_scaled_font_thaw_cache,'_cairo_scaled_font_thaw_cache',\
_cairo_scaled_font_unregister_placeholder_and_lock_font_map,'_cairo_scaled_font_unregister_placeholder_and_lock_font_map',\
_cairo_scaled_glyph_attach_private,'_cairo_scaled_glyph_attach_private',\
_cairo_scaled_glyph_find_private,'_cairo_scaled_glyph_find_private',\
_cairo_scaled_glyph_lookup,'_cairo_scaled_glyph_lookup',\
_cairo_scaled_glyph_page_cache_mutex,'_cairo_scaled_glyph_page_cache_mutex',\
_cairo_scaled_glyph_set_metrics,'_cairo_scaled_glyph_set_metrics',\
_cairo_scaled_glyph_set_path,'_cairo_scaled_glyph_set_path',\
_cairo_scaled_glyph_set_recording_surface,'_cairo_scaled_glyph_set_recording_surface',\
_cairo_scaled_glyph_set_surface,'_cairo_scaled_glyph_set_surface',\
_cairo_scan_converter_create_in_error,'_cairo_scan_converter_create_in_error',\
_cairo_scan_converter_set_error,'_cairo_scan_converter_set_error',\
_cairo_scan_converter_status,'_cairo_scan_converter_status',\
_cairo_script_context_attach_snapshots,'_cairo_script_context_attach_snapshots',\
_cairo_script_context_create_internal,'_cairo_script_context_create_internal',\
_cairo_shape_mask_compositor_init,'_cairo_shape_mask_compositor_init',\
_cairo_slope_compare,'_cairo_slope_compare',\
_cairo_span_renderer_create_in_error,'_cairo_span_renderer_create_in_error',\
_cairo_span_renderer_set_error,'_cairo_span_renderer_set_error',\
_cairo_span_renderer_status,'_cairo_span_renderer_status',\
_cairo_spans_compositor_init,'_cairo_spans_compositor_init',\
_cairo_spline_bound,'_cairo_spline_bound',\
_cairo_spline_decompose,'_cairo_spline_decompose',\
_cairo_spline_init,'_cairo_spline_init',\
_cairo_spline_intersects,'_cairo_spline_intersects',\
_cairo_stock_color,'_cairo_stock_color',\
_cairo_stroke_style_dash_approximate,'_cairo_stroke_style_dash_approximate',\
_cairo_stroke_style_dash_can_approximate,'_cairo_stroke_style_dash_can_approximate',\
_cairo_stroke_style_dash_period,'_cairo_stroke_style_dash_period',\
_cairo_stroke_style_dash_stroked,'_cairo_stroke_style_dash_stroked',\
_cairo_stroke_style_fini,'_cairo_stroke_style_fini',\
_cairo_stroke_style_init,'_cairo_stroke_style_init',\
_cairo_stroke_style_init_copy,'_cairo_stroke_style_init_copy',\
_cairo_stroke_style_max_distance_from_path,'_cairo_stroke_style_max_distance_from_path',\
_cairo_stroke_style_max_join_distance_from_path,'_cairo_stroke_style_max_join_distance_from_path',\
_cairo_stroke_style_max_line_distance_from_path,'_cairo_stroke_style_max_line_distance_from_path',\
_cairo_stroker_dash_init,'_cairo_stroker_dash_init',\
_cairo_stroker_dash_start,'_cairo_stroker_dash_start',\
_cairo_stroker_dash_step,'_cairo_stroker_dash_step',\
_cairo_surface_acquire_source_image,'_cairo_surface_acquire_source_image',\
_cairo_surface_attach_snapshot,'_cairo_surface_attach_snapshot',\
_cairo_surface_begin_modification,'_cairo_surface_begin_modification',\
_cairo_surface_clipper_init,'_cairo_surface_clipper_init',\
_cairo_surface_clipper_reset,'_cairo_surface_clipper_reset',\
_cairo_surface_clipper_set_clip,'_cairo_surface_clipper_set_clip',\
_cairo_surface_copy_mime_data,'_cairo_surface_copy_mime_data',\
_cairo_surface_create_for_rectangle_int,'_cairo_surface_create_for_rectangle_int',\
_cairo_surface_create_in_error,'_cairo_surface_create_in_error',\
_cairo_surface_create_similar_scratch,'_cairo_surface_create_similar_scratch',\
_cairo_surface_create_similar_solid,'_cairo_surface_create_similar_solid',\
_cairo_surface_default_acquire_source_image,'_cairo_surface_default_acquire_source_image',\
_cairo_surface_default_release_source_image,'_cairo_surface_default_release_source_image',\
_cairo_surface_default_source,'_cairo_surface_default_source',\
_cairo_surface_detach_snapshot,'_cairo_surface_detach_snapshot',\
_cairo_surface_fallback_fill,'_cairo_surface_fallback_fill',\
_cairo_surface_fallback_glyphs,'_cairo_surface_fallback_glyphs',\
_cairo_surface_fallback_mask,'_cairo_surface_fallback_mask',\
_cairo_surface_fallback_paint,'_cairo_surface_fallback_paint',\
_cairo_surface_fallback_stroke,'_cairo_surface_fallback_stroke',\
_cairo_surface_fill,'_cairo_surface_fill',\
_cairo_surface_fill_stroke,'_cairo_surface_fill_stroke',\
_cairo_surface_flush,'_cairo_surface_flush',\
_cairo_surface_get_extents,'_cairo_surface_get_extents',\
_cairo_surface_get_source,'_cairo_surface_get_source',\
_cairo_surface_has_device_transform,'_cairo_surface_has_device_transform',\
_cairo_surface_has_snapshot,'_cairo_surface_has_snapshot',\
_cairo_surface_init,'_cairo_surface_init',\
_cairo_surface_is_paginated,'_cairo_surface_is_paginated',\
_cairo_surface_map_to_image,'_cairo_surface_map_to_image',\
_cairo_surface_mask,'_cairo_surface_mask',\
_cairo_surface_offset_fill,'_cairo_surface_offset_fill',\
_cairo_surface_offset_glyphs,'_cairo_surface_offset_glyphs',\
_cairo_surface_offset_mask,'_cairo_surface_offset_mask',\
_cairo_surface_offset_paint,'_cairo_surface_offset_paint',\
_cairo_surface_offset_stroke,'_cairo_surface_offset_stroke',\
_cairo_surface_paint,'_cairo_surface_paint',\
_cairo_surface_release_device_reference,'_cairo_surface_release_device_reference',\
_cairo_surface_release_source_image,'_cairo_surface_release_source_image',\
_cairo_surface_set_device_scale,'_cairo_surface_set_device_scale',\
_cairo_surface_set_error,'_cairo_surface_set_error',\
_cairo_surface_set_font_options,'_cairo_surface_set_font_options',\
_cairo_surface_set_resolution,'_cairo_surface_set_resolution',\
_cairo_surface_show_text_glyphs,'_cairo_surface_show_text_glyphs',\
_cairo_surface_snapshot,'_cairo_surface_snapshot',\
_cairo_surface_stroke,'_cairo_surface_stroke',\
_cairo_surface_subsurface_set_snapshot,'_cairo_surface_subsurface_set_snapshot',\
_cairo_surface_unmap_image,'_cairo_surface_unmap_image',\
_cairo_surface_wrapper_acquire_source_image,'_cairo_surface_wrapper_acquire_source_image',\
_cairo_surface_wrapper_create_similar,'_cairo_surface_wrapper_create_similar',\
_cairo_surface_wrapper_fill,'_cairo_surface_wrapper_fill',\
_cairo_surface_wrapper_fill_stroke,'_cairo_surface_wrapper_fill_stroke',\
_cairo_surface_wrapper_fini,'_cairo_surface_wrapper_fini',\
_cairo_surface_wrapper_get_extents,'_cairo_surface_wrapper_get_extents',\
_cairo_surface_wrapper_get_font_options,'_cairo_surface_wrapper_get_font_options',\
_cairo_surface_wrapper_get_target_extents,'_cairo_surface_wrapper_get_target_extents',\
_cairo_surface_wrapper_has_show_text_glyphs,'_cairo_surface_wrapper_has_show_text_glyphs',\
_cairo_surface_wrapper_init,'_cairo_surface_wrapper_init',\
_cairo_surface_wrapper_intersect_extents,'_cairo_surface_wrapper_intersect_extents',\
_cairo_surface_wrapper_mask,'_cairo_surface_wrapper_mask',\
_cairo_surface_wrapper_paint,'_cairo_surface_wrapper_paint',\
_cairo_surface_wrapper_release_source_image,'_cairo_surface_wrapper_release_source_image',\
_cairo_surface_wrapper_set_clip,'_cairo_surface_wrapper_set_clip',\
_cairo_surface_wrapper_set_inverse_transform,'_cairo_surface_wrapper_set_inverse_transform',\
_cairo_surface_wrapper_show_text_glyphs,'_cairo_surface_wrapper_show_text_glyphs',\
_cairo_surface_wrapper_snapshot,'_cairo_surface_wrapper_snapshot',\
_cairo_surface_wrapper_stroke,'_cairo_surface_wrapper_stroke',\
_cairo_tor22_scan_converter_add_polygon,'_cairo_tor22_scan_converter_add_polygon',\
_cairo_tor22_scan_converter_create,'_cairo_tor22_scan_converter_create',\
_cairo_tor_scan_converter_add_polygon,'_cairo_tor_scan_converter_add_polygon',\
_cairo_tor_scan_converter_create,'_cairo_tor_scan_converter_create',\
_cairo_toy_font_face_mutex,'_cairo_toy_font_face_mutex',\
_cairo_toy_font_face_reset_static_data,'_cairo_toy_font_face_reset_static_data',\
_cairo_trapezoid_array_translate_and_scale,'_cairo_trapezoid_array_translate_and_scale',\
_cairo_traps_add_trap,'_cairo_traps_add_trap',\
_cairo_traps_clear,'_cairo_traps_clear',\
_cairo_traps_compositor_init,'_cairo_traps_compositor_init',\
_cairo_traps_contain,'_cairo_traps_contain',\
_cairo_traps_extents,'_cairo_traps_extents',\
_cairo_traps_extract_region,'_cairo_traps_extract_region',\
_cairo_traps_fini,'_cairo_traps_fini',\
_cairo_traps_init,'_cairo_traps_init',\
_cairo_traps_init_boxes,'_cairo_traps_init_boxes',\
_cairo_traps_init_with_clip,'_cairo_traps_init_with_clip',\
_cairo_traps_limit,'_cairo_traps_limit',\
_cairo_traps_path,'_cairo_traps_path',\
_cairo_traps_tessellate_convex_quad,'_cairo_traps_tessellate_convex_quad',\
_cairo_traps_tessellate_rectangle,'_cairo_traps_tessellate_rectangle',\
_cairo_traps_tessellate_triangle,'_cairo_traps_tessellate_triangle',\
_cairo_traps_to_boxes,'_cairo_traps_to_boxes',\
_cairo_traps_translate,'_cairo_traps_translate',\
_cairo_tristrip_add_point,'_cairo_tristrip_add_point',\
_cairo_tristrip_extents,'_cairo_tristrip_extents',\
_cairo_tristrip_fini,'_cairo_tristrip_fini',\
_cairo_tristrip_init,'_cairo_tristrip_init',\
_cairo_tristrip_init_with_clip,'_cairo_tristrip_init_with_clip',\
_cairo_tristrip_limit,'_cairo_tristrip_limit',\
_cairo_tristrip_move_to,'_cairo_tristrip_move_to',\
_cairo_tristrip_translate,'_cairo_tristrip_translate',\
_cairo_truetype_get_style,'_cairo_truetype_get_style',\
_cairo_truetype_index_to_ucs4,'_cairo_truetype_index_to_ucs4',\
_cairo_truetype_read_font_name,'_cairo_truetype_read_font_name',\
_cairo_truetype_subset_fini,'_cairo_truetype_subset_fini',\
_cairo_truetype_subset_init_pdf,'_cairo_truetype_subset_init_pdf',\
_cairo_truetype_subset_init_ps,'_cairo_truetype_subset_init_ps',\
_cairo_twin_charmap,'_cairo_twin_charmap',\
_cairo_twin_outlines,'_cairo_twin_outlines',\
_cairo_type1_fallback_fini,'_cairo_type1_fallback_fini',\
_cairo_type1_fallback_init_binary,'_cairo_type1_fallback_init_binary',\
_cairo_type1_fallback_init_hex,'_cairo_type1_fallback_init_hex',\
_cairo_type1_scaled_font_is_type1,'_cairo_type1_scaled_font_is_type1',\
_cairo_type1_subset_fini,'_cairo_type1_subset_fini',\
_cairo_type1_subset_init,'_cairo_type1_subset_init',\
_cairo_type2_charstrings_fini,'_cairo_type2_charstrings_fini',\
_cairo_type2_charstrings_init,'_cairo_type2_charstrings_init',\
_cairo_type3_glyph_surface_analyze_glyph,'_cairo_type3_glyph_surface_analyze_glyph',\
_cairo_type3_glyph_surface_create,'_cairo_type3_glyph_surface_create',\
_cairo_type3_glyph_surface_emit_glyph,'_cairo_type3_glyph_surface_emit_glyph',\
_cairo_type3_glyph_surface_set_font_subsets_callback,'_cairo_type3_glyph_surface_set_font_subsets_callback',\
_cairo_ucs4_to_utf8,'_cairo_ucs4_to_utf8',\
_cairo_uint128_add,'_cairo_uint128_add',\
_cairo_uint128_cmp,'_cairo_uint128_cmp',\
_cairo_uint128_divrem,'_cairo_uint128_divrem',\
_cairo_uint128_eq,'_cairo_uint128_eq',\
_cairo_uint128_lsl,'_cairo_uint128_lsl',\
_cairo_uint128_lt,'_cairo_uint128_lt',\
_cairo_uint128_mul,'_cairo_uint128_mul',\
_cairo_uint128_negate,'_cairo_uint128_negate',\
_cairo_uint128_not,'_cairo_uint128_not',\
_cairo_uint128_rsa,'_cairo_uint128_rsa',\
_cairo_uint128_rsl,'_cairo_uint128_rsl',\
_cairo_uint128_sub,'_cairo_uint128_sub',\
_cairo_uint32_to_uint128,'_cairo_uint32_to_uint128',\
_cairo_uint64_to_uint128,'_cairo_uint64_to_uint128',\
_cairo_uint64x64_128_mul,'_cairo_uint64x64_128_mul',\
_cairo_uint_96by64_32x64_divrem,'_cairo_uint_96by64_32x64_divrem',\
_cairo_unbounded_rectangle,'_cairo_unbounded_rectangle',\
_cairo_unicode_to_winansi,'_cairo_unicode_to_winansi',\
_cairo_unscaled_font_destroy,'_cairo_unscaled_font_destroy',\
_cairo_unscaled_font_init,'_cairo_unscaled_font_init',\
_cairo_unscaled_font_reference,'_cairo_unscaled_font_reference',\
_cairo_user_data_array_copy,'_cairo_user_data_array_copy',\
_cairo_user_data_array_fini,'_cairo_user_data_array_fini',\
_cairo_user_data_array_foreach,'_cairo_user_data_array_foreach',\
_cairo_user_data_array_get_data,'_cairo_user_data_array_get_data',\
_cairo_user_data_array_init,'_cairo_user_data_array_init',\
_cairo_user_data_array_set_data,'_cairo_user_data_array_set_data',\
_cairo_user_font_face_backend,'_cairo_user_font_face_backend',\
_cairo_utf8_get_char_validated,'_cairo_utf8_get_char_validated',\
_cairo_utf8_to_ucs4,'_cairo_utf8_to_ucs4',\
_cairo_utf8_to_utf16,'_cairo_utf8_to_utf16',\
_cairo_validate_text_clusters,'_cairo_validate_text_clusters',\
_cairo_winansi_to_glyphname,'_cairo_winansi_to_glyphname',\
_do_cairo_gstate_backend_to_user,'_do_cairo_gstate_backend_to_user',\
_do_cairo_gstate_backend_to_user_distance,'_do_cairo_gstate_backend_to_user_distance',\
_do_cairo_gstate_user_to_backend,'_do_cairo_gstate_user_to_backend',\
_do_cairo_gstate_user_to_backend_distance,'_do_cairo_gstate_user_to_backend_distance',\
_pixman_format_from_masks,'_pixman_format_from_masks',\
_pixman_format_to_masks,'_pixman_format_to_masks',\
_pixman_image_add_traps,'_pixman_image_add_traps',\
_pixman_image_add_tristrip,'_pixman_image_add_tristrip',\
_pixman_image_for_color,'_pixman_image_for_color',\
_pixman_image_for_pattern,'_pixman_image_for_pattern',\
cairo_append_path,'cairo_append_path',\
cairo_arc,'cairo_arc',\
cairo_arc_negative,'cairo_arc_negative',\
cairo_clip,'cairo_clip',\
cairo_clip_extents,'cairo_clip_extents',\
cairo_clip_preserve,'cairo_clip_preserve',\
cairo_close_path,'cairo_close_path',\
cairo_copy_clip_rectangle_list,'cairo_copy_clip_rectangle_list',\
cairo_copy_page,'cairo_copy_page',\
cairo_copy_path,'cairo_copy_path',\
cairo_copy_path_flat,'cairo_copy_path_flat',\
cairo_create,'cairo_create',\
cairo_curve_to,'cairo_curve_to',\
cairo_debug_reset_static_data,'cairo_debug_reset_static_data',\
cairo_destroy,'cairo_destroy',\
cairo_device_acquire,'cairo_device_acquire',\
cairo_device_destroy,'cairo_device_destroy',\
cairo_device_finish,'cairo_device_finish',\
cairo_device_flush,'cairo_device_flush',\
cairo_device_get_reference_count,'cairo_device_get_reference_count',\
cairo_device_get_type,'cairo_device_get_type',\
cairo_device_get_user_data,'cairo_device_get_user_data',\
cairo_device_reference,'cairo_device_reference',\
cairo_device_release,'cairo_device_release',\
cairo_device_set_user_data,'cairo_device_set_user_data',\
cairo_device_status,'cairo_device_status',\
cairo_device_to_user,'cairo_device_to_user',\
cairo_device_to_user_distance,'cairo_device_to_user_distance',\
cairo_fill,'cairo_fill',\
cairo_fill_extents,'cairo_fill_extents',\
cairo_fill_preserve,'cairo_fill_preserve',\
cairo_font_extents,'cairo_font_extents',\
cairo_font_face_destroy,'cairo_font_face_destroy',\
cairo_font_face_get_reference_count,'cairo_font_face_get_reference_count',\
cairo_font_face_get_type,'cairo_font_face_get_type',\
cairo_font_face_get_user_data,'cairo_font_face_get_user_data',\
cairo_font_face_reference,'cairo_font_face_reference',\
cairo_font_face_set_user_data,'cairo_font_face_set_user_data',\
cairo_font_face_status,'cairo_font_face_status',\
cairo_font_options_copy,'cairo_font_options_copy',\
cairo_font_options_create,'cairo_font_options_create',\
cairo_font_options_destroy,'cairo_font_options_destroy',\
cairo_font_options_equal,'cairo_font_options_equal',\
cairo_font_options_get_antialias,'cairo_font_options_get_antialias',\
cairo_font_options_get_hint_metrics,'cairo_font_options_get_hint_metrics',\
cairo_font_options_get_hint_style,'cairo_font_options_get_hint_style',\
cairo_font_options_get_subpixel_order,'cairo_font_options_get_subpixel_order',\
cairo_font_options_hash,'cairo_font_options_hash',\
cairo_font_options_merge,'cairo_font_options_merge',\
cairo_font_options_set_antialias,'cairo_font_options_set_antialias',\
cairo_font_options_set_hint_metrics,'cairo_font_options_set_hint_metrics',\
cairo_font_options_set_hint_style,'cairo_font_options_set_hint_style',\
cairo_font_options_set_subpixel_order,'cairo_font_options_set_subpixel_order',\
cairo_font_options_status,'cairo_font_options_status',\
cairo_format_stride_for_width,'cairo_format_stride_for_width',\
cairo_ft_font_face_create_for_ft_face,'cairo_ft_font_face_create_for_ft_face',\
cairo_ft_font_face_get_synthesize,'cairo_ft_font_face_get_synthesize',\
cairo_ft_font_face_set_synthesize,'cairo_ft_font_face_set_synthesize',\
cairo_ft_font_face_unset_synthesize,'cairo_ft_font_face_unset_synthesize',\
cairo_ft_scaled_font_lock_face,'cairo_ft_scaled_font_lock_face',\
cairo_ft_scaled_font_unlock_face,'cairo_ft_scaled_font_unlock_face',\
cairo_get_antialias,'cairo_get_antialias',\
cairo_get_current_point,'cairo_get_current_point',\
cairo_get_dash,'cairo_get_dash',\
cairo_get_dash_count,'cairo_get_dash_count',\
cairo_get_fill_rule,'cairo_get_fill_rule',\
cairo_get_font_face,'cairo_get_font_face',\
cairo_get_font_matrix,'cairo_get_font_matrix',\
cairo_get_font_options,'cairo_get_font_options',\
cairo_get_group_target,'cairo_get_group_target',\
cairo_get_line_cap,'cairo_get_line_cap',\
cairo_get_line_join,'cairo_get_line_join',\
cairo_get_line_width,'cairo_get_line_width',\
cairo_get_matrix,'cairo_get_matrix',\
cairo_get_miter_limit,'cairo_get_miter_limit',\
cairo_get_operator,'cairo_get_operator',\
cairo_get_reference_count,'cairo_get_reference_count',\
cairo_get_scaled_font,'cairo_get_scaled_font',\
cairo_get_source,'cairo_get_source',\
cairo_get_target,'cairo_get_target',\
cairo_get_tolerance,'cairo_get_tolerance',\
cairo_get_user_data,'cairo_get_user_data',\
cairo_glyph_allocate,'cairo_glyph_allocate',\
cairo_glyph_extents,'cairo_glyph_extents',\
cairo_glyph_free,'cairo_glyph_free',\
cairo_glyph_path,'cairo_glyph_path',\
cairo_has_current_point,'cairo_has_current_point',\
cairo_identity_matrix,'cairo_identity_matrix',\
cairo_image_surface_create,'cairo_image_surface_create',\
cairo_image_surface_create_for_data,'cairo_image_surface_create_for_data',\
cairo_image_surface_create_from_png,'cairo_image_surface_create_from_png',\
cairo_image_surface_create_from_png_stream,'cairo_image_surface_create_from_png_stream',\
cairo_image_surface_get_data,'cairo_image_surface_get_data',\
cairo_image_surface_get_format,'cairo_image_surface_get_format',\
cairo_image_surface_get_height,'cairo_image_surface_get_height',\
cairo_image_surface_get_stride,'cairo_image_surface_get_stride',\
cairo_image_surface_get_width,'cairo_image_surface_get_width',\
cairo_in_clip,'cairo_in_clip',\
cairo_in_fill,'cairo_in_fill',\
cairo_in_stroke,'cairo_in_stroke',\
cairo_line_to,'cairo_line_to',\
cairo_mask,'cairo_mask',\
cairo_mask_surface,'cairo_mask_surface',\
cairo_matrix_init,'cairo_matrix_init',\
cairo_matrix_init_identity,'cairo_matrix_init_identity',\
cairo_matrix_init_rotate,'cairo_matrix_init_rotate',\
cairo_matrix_init_scale,'cairo_matrix_init_scale',\
cairo_matrix_init_translate,'cairo_matrix_init_translate',\
cairo_matrix_invert,'cairo_matrix_invert',\
cairo_matrix_multiply,'cairo_matrix_multiply',\
cairo_matrix_rotate,'cairo_matrix_rotate',\
cairo_matrix_scale,'cairo_matrix_scale',\
cairo_matrix_transform_distance,'cairo_matrix_transform_distance',\
cairo_matrix_transform_point,'cairo_matrix_transform_point',\
cairo_matrix_translate,'cairo_matrix_translate',\
cairo_mesh_pattern_begin_patch,'cairo_mesh_pattern_begin_patch',\
cairo_mesh_pattern_curve_to,'cairo_mesh_pattern_curve_to',\
cairo_mesh_pattern_end_patch,'cairo_mesh_pattern_end_patch',\
cairo_mesh_pattern_get_control_point,'cairo_mesh_pattern_get_control_point',\
cairo_mesh_pattern_get_corner_color_rgba,'cairo_mesh_pattern_get_corner_color_rgba',\
cairo_mesh_pattern_get_patch_count,'cairo_mesh_pattern_get_patch_count',\
cairo_mesh_pattern_get_path,'cairo_mesh_pattern_get_path',\
cairo_mesh_pattern_line_to,'cairo_mesh_pattern_line_to',\
cairo_mesh_pattern_move_to,'cairo_mesh_pattern_move_to',\
cairo_mesh_pattern_set_control_point,'cairo_mesh_pattern_set_control_point',\
cairo_mesh_pattern_set_corner_color_rgb,'cairo_mesh_pattern_set_corner_color_rgb',\
cairo_mesh_pattern_set_corner_color_rgba,'cairo_mesh_pattern_set_corner_color_rgba',\
cairo_move_to,'cairo_move_to',\
cairo_new_path,'cairo_new_path',\
cairo_new_sub_path,'cairo_new_sub_path',\
cairo_paint,'cairo_paint',\
cairo_paint_with_alpha,'cairo_paint_with_alpha',\
cairo_path_destroy,'cairo_path_destroy',\
cairo_path_extents,'cairo_path_extents',\
cairo_pattern_add_color_stop_rgb,'cairo_pattern_add_color_stop_rgb',\
cairo_pattern_add_color_stop_rgba,'cairo_pattern_add_color_stop_rgba',\
cairo_pattern_create_for_surface,'cairo_pattern_create_for_surface',\
cairo_pattern_create_linear,'cairo_pattern_create_linear',\
cairo_pattern_create_mesh,'cairo_pattern_create_mesh',\
cairo_pattern_create_radial,'cairo_pattern_create_radial',\
cairo_pattern_create_raster_source,'cairo_pattern_create_raster_source',\
cairo_pattern_create_rgb,'cairo_pattern_create_rgb',\
cairo_pattern_create_rgba,'cairo_pattern_create_rgba',\
cairo_pattern_destroy,'cairo_pattern_destroy',\
cairo_pattern_get_color_stop_count,'cairo_pattern_get_color_stop_count',\
cairo_pattern_get_color_stop_rgba,'cairo_pattern_get_color_stop_rgba',\
cairo_pattern_get_extend,'cairo_pattern_get_extend',\
cairo_pattern_get_filter,'cairo_pattern_get_filter',\
cairo_pattern_get_linear_points,'cairo_pattern_get_linear_points',\
cairo_pattern_get_matrix,'cairo_pattern_get_matrix',\
cairo_pattern_get_radial_circles,'cairo_pattern_get_radial_circles',\
cairo_pattern_get_reference_count,'cairo_pattern_get_reference_count',\
cairo_pattern_get_rgba,'cairo_pattern_get_rgba',\
cairo_pattern_get_surface,'cairo_pattern_get_surface',\
cairo_pattern_get_type,'cairo_pattern_get_type',\
cairo_pattern_get_user_data,'cairo_pattern_get_user_data',\
cairo_pattern_reference,'cairo_pattern_reference',\
cairo_pattern_set_extend,'cairo_pattern_set_extend',\
cairo_pattern_set_filter,'cairo_pattern_set_filter',\
cairo_pattern_set_matrix,'cairo_pattern_set_matrix',\
cairo_pattern_set_user_data,'cairo_pattern_set_user_data',\
cairo_pattern_status,'cairo_pattern_status',\
cairo_pop_group,'cairo_pop_group',\
cairo_pop_group_to_source,'cairo_pop_group_to_source',\
cairo_push_group,'cairo_push_group',\
cairo_push_group_with_content,'cairo_push_group_with_content',\
cairo_raster_source_pattern_get_acquire,'cairo_raster_source_pattern_get_acquire',\
cairo_raster_source_pattern_get_callback_data,'cairo_raster_source_pattern_get_callback_data',\
cairo_raster_source_pattern_get_copy,'cairo_raster_source_pattern_get_copy',\
cairo_raster_source_pattern_get_finish,'cairo_raster_source_pattern_get_finish',\
cairo_raster_source_pattern_get_snapshot,'cairo_raster_source_pattern_get_snapshot',\
cairo_raster_source_pattern_set_acquire,'cairo_raster_source_pattern_set_acquire',\
cairo_raster_source_pattern_set_callback_data,'cairo_raster_source_pattern_set_callback_data',\
cairo_raster_source_pattern_set_copy,'cairo_raster_source_pattern_set_copy',\
cairo_raster_source_pattern_set_finish,'cairo_raster_source_pattern_set_finish',\
cairo_raster_source_pattern_set_snapshot,'cairo_raster_source_pattern_set_snapshot',\
cairo_recording_surface_create,'cairo_recording_surface_create',\
cairo_recording_surface_get_extents,'cairo_recording_surface_get_extents',\
cairo_recording_surface_ink_extents,'cairo_recording_surface_ink_extents',\
cairo_rectangle,'cairo_rectangle',\
cairo_rectangle_list_destroy,'cairo_rectangle_list_destroy',\
cairo_reference,'cairo_reference',\
cairo_region_contains_point,'cairo_region_contains_point',\
cairo_region_contains_rectangle,'cairo_region_contains_rectangle',\
cairo_region_copy,'cairo_region_copy',\
cairo_region_create,'cairo_region_create',\
cairo_region_create_rectangle,'cairo_region_create_rectangle',\
cairo_region_create_rectangles,'cairo_region_create_rectangles',\
cairo_region_destroy,'cairo_region_destroy',\
cairo_region_equal,'cairo_region_equal',\
cairo_region_get_extents,'cairo_region_get_extents',\
cairo_region_get_rectangle,'cairo_region_get_rectangle',\
cairo_region_intersect,'cairo_region_intersect',\
cairo_region_intersect_rectangle,'cairo_region_intersect_rectangle',\
cairo_region_is_empty,'cairo_region_is_empty',\
cairo_region_num_rectangles,'cairo_region_num_rectangles',\
cairo_region_reference,'cairo_region_reference',\
cairo_region_status,'cairo_region_status',\
cairo_region_subtract,'cairo_region_subtract',\
cairo_region_subtract_rectangle,'cairo_region_subtract_rectangle',\
cairo_region_translate,'cairo_region_translate',\
cairo_region_union,'cairo_region_union',\
cairo_region_union_rectangle,'cairo_region_union_rectangle',\
cairo_region_xor,'cairo_region_xor',\
cairo_region_xor_rectangle,'cairo_region_xor_rectangle',\
cairo_rel_curve_to,'cairo_rel_curve_to',\
cairo_rel_line_to,'cairo_rel_line_to',\
cairo_rel_move_to,'cairo_rel_move_to',\
cairo_reset_clip,'cairo_reset_clip',\
cairo_restore,'cairo_restore',\
cairo_rotate,'cairo_rotate',\
cairo_save,'cairo_save',\
cairo_scale,'cairo_scale',\
cairo_scaled_font_create,'cairo_scaled_font_create',\
cairo_scaled_font_destroy,'cairo_scaled_font_destroy',\
cairo_scaled_font_extents,'cairo_scaled_font_extents',\
cairo_scaled_font_get_ctm,'cairo_scaled_font_get_ctm',\
cairo_scaled_font_get_font_face,'cairo_scaled_font_get_font_face',\
cairo_scaled_font_get_font_matrix,'cairo_scaled_font_get_font_matrix',\
cairo_scaled_font_get_font_options,'cairo_scaled_font_get_font_options',\
cairo_scaled_font_get_reference_count,'cairo_scaled_font_get_reference_count',\
cairo_scaled_font_get_scale_matrix,'cairo_scaled_font_get_scale_matrix',\
cairo_scaled_font_get_type,'cairo_scaled_font_get_type',\
cairo_scaled_font_get_user_data,'cairo_scaled_font_get_user_data',\
cairo_scaled_font_glyph_extents,'cairo_scaled_font_glyph_extents',\
cairo_scaled_font_reference,'cairo_scaled_font_reference',\
cairo_scaled_font_set_user_data,'cairo_scaled_font_set_user_data',\
cairo_scaled_font_status,'cairo_scaled_font_status',\
cairo_scaled_font_text_extents,'cairo_scaled_font_text_extents',\
cairo_scaled_font_text_to_glyphs,'cairo_scaled_font_text_to_glyphs',\
cairo_script_create,'cairo_script_create',\
cairo_script_create_for_stream,'cairo_script_create_for_stream',\
cairo_script_from_recording_surface,'cairo_script_from_recording_surface',\
cairo_script_get_mode,'cairo_script_get_mode',\
cairo_script_set_mode,'cairo_script_set_mode',\
cairo_script_surface_create,'cairo_script_surface_create',\
cairo_script_surface_create_for_target,'cairo_script_surface_create_for_target',\
cairo_script_write_comment,'cairo_script_write_comment',\
cairo_select_font_face,'cairo_select_font_face',\
cairo_set_antialias,'cairo_set_antialias',\
cairo_set_dash,'cairo_set_dash',\
cairo_set_fill_rule,'cairo_set_fill_rule',\
cairo_set_font_face,'cairo_set_font_face',\
cairo_set_font_matrix,'cairo_set_font_matrix',\
cairo_set_font_options,'cairo_set_font_options',\
cairo_set_font_size,'cairo_set_font_size',\
cairo_set_line_cap,'cairo_set_line_cap',\
cairo_set_line_join,'cairo_set_line_join',\
cairo_set_line_width,'cairo_set_line_width',\
cairo_set_matrix,'cairo_set_matrix',\
cairo_set_miter_limit,'cairo_set_miter_limit',\
cairo_set_operator,'cairo_set_operator',\
cairo_set_scaled_font,'cairo_set_scaled_font',\
cairo_set_source,'cairo_set_source',\
cairo_set_source_rgb,'cairo_set_source_rgb',\
cairo_set_source_rgba,'cairo_set_source_rgba',\
cairo_set_source_surface,'cairo_set_source_surface',\
cairo_set_tolerance,'cairo_set_tolerance',\
cairo_set_user_data,'cairo_set_user_data',\
cairo_show_glyphs,'cairo_show_glyphs',\
cairo_show_page,'cairo_show_page',\
cairo_show_text,'cairo_show_text',\
cairo_show_text_glyphs,'cairo_show_text_glyphs',\
cairo_status,'cairo_status',\
cairo_status_to_string,'cairo_status_to_string',\
cairo_stroke,'cairo_stroke',\
cairo_stroke_extents,'cairo_stroke_extents',\
cairo_stroke_preserve,'cairo_stroke_preserve',\
cairo_surface_copy_page,'cairo_surface_copy_page',\
cairo_surface_create_for_rectangle,'cairo_surface_create_for_rectangle',\
cairo_surface_create_similar,'cairo_surface_create_similar',\
cairo_surface_create_similar_image,'cairo_surface_create_similar_image',\
cairo_surface_destroy,'cairo_surface_destroy',\
cairo_surface_finish,'cairo_surface_finish',\
cairo_surface_flush,'cairo_surface_flush',\
cairo_surface_get_content,'cairo_surface_get_content',\
cairo_surface_get_device,'cairo_surface_get_device',\
cairo_surface_get_device_offset,'cairo_surface_get_device_offset',\
cairo_surface_get_fallback_resolution,'cairo_surface_get_fallback_resolution',\
cairo_surface_get_font_options,'cairo_surface_get_font_options',\
cairo_surface_get_mime_data,'cairo_surface_get_mime_data',\
cairo_surface_get_reference_count,'cairo_surface_get_reference_count',\
cairo_surface_get_type,'cairo_surface_get_type',\
cairo_surface_get_user_data,'cairo_surface_get_user_data',\
cairo_surface_has_show_text_glyphs,'cairo_surface_has_show_text_glyphs',\
cairo_surface_map_to_image,'cairo_surface_map_to_image',\
cairo_surface_mark_dirty,'cairo_surface_mark_dirty',\
cairo_surface_mark_dirty_rectangle,'cairo_surface_mark_dirty_rectangle',\
cairo_surface_reference,'cairo_surface_reference',\
cairo_surface_set_device_offset,'cairo_surface_set_device_offset',\
cairo_surface_set_fallback_resolution,'cairo_surface_set_fallback_resolution',\
cairo_surface_set_mime_data,'cairo_surface_set_mime_data',\
cairo_surface_set_user_data,'cairo_surface_set_user_data',\
cairo_surface_show_page,'cairo_surface_show_page',\
cairo_surface_status,'cairo_surface_status',\
cairo_surface_supports_mime_type,'cairo_surface_supports_mime_type',\
cairo_surface_unmap_image,'cairo_surface_unmap_image',\
cairo_surface_write_to_png,'cairo_surface_write_to_png',\
cairo_surface_write_to_png_stream,'cairo_surface_write_to_png_stream',\
cairo_svg_get_versions,'cairo_svg_get_versions',\
cairo_svg_surface_create,'cairo_svg_surface_create',\
cairo_svg_surface_create_for_stream,'cairo_svg_surface_create_for_stream',\
cairo_svg_surface_restrict_to_version,'cairo_svg_surface_restrict_to_version',\
cairo_svg_version_to_string,'cairo_svg_version_to_string',\
cairo_text_cluster_allocate,'cairo_text_cluster_allocate',\
cairo_text_cluster_free,'cairo_text_cluster_free',\
cairo_text_extents,'cairo_text_extents',\
cairo_text_path,'cairo_text_path',\
cairo_toy_font_face_create,'cairo_toy_font_face_create',\
cairo_toy_font_face_get_family,'cairo_toy_font_face_get_family',\
cairo_toy_font_face_get_slant,'cairo_toy_font_face_get_slant',\
cairo_toy_font_face_get_weight,'cairo_toy_font_face_get_weight',\
cairo_transform,'cairo_transform',\
cairo_translate,'cairo_translate',\
cairo_user_font_face_create,'cairo_user_font_face_create',\
cairo_user_font_face_get_init_func,'cairo_user_font_face_get_init_func',\
cairo_user_font_face_get_render_glyph_func,'cairo_user_font_face_get_render_glyph_func',\
cairo_user_font_face_get_text_to_glyphs_func,'cairo_user_font_face_get_text_to_glyphs_func',\
cairo_user_font_face_get_unicode_to_glyph_func,'cairo_user_font_face_get_unicode_to_glyph_func',\
cairo_user_font_face_set_init_func,'cairo_user_font_face_set_init_func',\
cairo_user_font_face_set_render_glyph_func,'cairo_user_font_face_set_render_glyph_func',\
cairo_user_font_face_set_text_to_glyphs_func,'cairo_user_font_face_set_text_to_glyphs_func',\
cairo_user_font_face_set_unicode_to_glyph_func,'cairo_user_font_face_set_unicode_to_glyph_func',\
cairo_user_to_device,'cairo_user_to_device',\
cairo_user_to_device_distance,'cairo_user_to_device_distance',\
cairo_version,'cairo_version',\
cairo_version_string,'cairo_version_string'
/programs/develop/libraries/pixman/Makefile
5,7 → 5,7
CFLAGS = -U_Win32 -U_WIN32 -U__MINGW32__ -c -O2 -Wall -Winline -fomit-frame-pointer
 
LD = ld
LDFLAGS = -shared -s -nostdlib -T ../newlib/dll.lds --entry _DllStartup --image-base=0 --out-implib $(LIBRARY).dll.a
LDFLAGS = -shared -s -nostdlib -T ../newlib/dll.lds --entry _DllStartup --image-base=0 --out-implib lib$(LIBRARY).dll.a
 
STRIP = $(PREFIX)strip
 
15,6 → 15,7
 
LIBS:= -ldll -lc.dll -lgcc
 
#DEFINES = -DHAVE_CONFIG_H -DPIXMAN_NO_TLS
DEFINES = -DHAVE_CONFIG_H
 
 
/programs/develop/libraries/pixman/config.h
35,7 → 35,7
/* #undef HAVE_LIBPIXMAN_1 */
 
/* Whether we have libpng */
/* #undef HAVE_LIBPNG */
#define HAVE_LIBPNG 1
 
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
117,7 → 117,7
#define TLS __thread
 
/* Whether the tool chain supports __attribute__((constructor)) */
#define TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR /**/
//#define TOOLCHAIN_SUPPORTS_ATTRIBUTE_CONSTRUCTOR /**/
 
/* use ARM IWMMXT compiler intrinsics */
/* #undef USE_ARM_IWMMXT */