/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, ¤t_ptr, data_end); |
cff_index_fini (&index); |
if (status) |
goto fail1; |
/* read top dict */ |
cff_index_init (&index); |
status = cff_index_read (&index, ¤t_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 = ©->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 = ©->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 = ©->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, ®ion); |
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, ®);\ |
} 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, ®); \ |
}\ |
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, ¤t_vertex_index); |
if (unlikely (status)) |
return status; |
current_vertex_index++; |
status =_cairo_array_append (indices, ¤t_vertex_index); |
if (unlikely (status)) |
return status; |
} |
for (i = 0; i < number_of_new_indices; i++) { |
status = _cairo_array_append (indices, ¤t_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 ? ®ion->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, ®ion->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, ®ion); |
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, ®ion); |
} |
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 (); |
* |
* /* Add a Coons patch */ |
* 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); |
* |
* /* Add a Gouraud-shaded triangle */ |
* 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 **) ¤t_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 (®ion->ref_count, 1); |
region->status = CAIRO_STATUS_SUCCESS; |
if (count == 1) { |
pixman_region32_init_rect (®ion->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 (®ion->ref_count, 1); |
region->status = CAIRO_STATUS_SUCCESS; |
if (! pixman_region32_init_rects (®ion->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 ®ion->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= ▭ |
} |
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 = ▭ |
} 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 |
* |
* |
* Snapshots |
* --------- |
* 1.1.2 - A snapshot (working toward the 1.2.0 release) |
* |
* |
* 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 |
* |
* |
* 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 (©.base, pattern); |
cairo_matrix_translate (©.base.matrix, -extents->x, -extents->y); |
status = _cairo_surface_paint (&image->base, |
CAIRO_OPERATOR_SOURCE, |
©.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 (©.base, pattern); |
cairo_matrix_translate (©.base.matrix, extents->x, extents->y); |
status = _cairo_surface_paint (&image->base, |
CAIRO_OPERATOR_SOURCE, |
©.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 ? ®ion->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 |
}; |