diff --git a/boilerplate/cairo-boilerplate-test-surfaces.c b/boilerplate/cairo-boilerplate-test-surfaces.c index 909475b38..f7a89a728 100644 --- a/boilerplate/cairo-boilerplate-test-surfaces.c +++ b/boilerplate/cairo-boilerplate-test-surfaces.c @@ -30,66 +30,133 @@ #include -#include -#include +#include +#include #if CAIRO_HAS_TEST_PAGINATED_SURFACE #include #endif -#if CAIRO_HAS_TEST_NULL_SURFACE -#include -#endif -#if CAIRO_HAS_TEST_WRAPPING_SURFACE -#include -#endif static cairo_surface_t * -_cairo_boilerplate_test_fallback_create_surface (const char *name, - cairo_content_t content, - double width, - double height, - double max_width, - double max_height, - cairo_boilerplate_mode_t mode, - int id, - void **closure) +_cairo_boilerplate_test_base_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) { *closure = NULL; - return _cairo_test_fallback_surface_create (content, - ceil (width), ceil (height)); + return _cairo_test_base_compositor_surface_create (content, ceil (width), ceil (height)); } + static cairo_surface_t * -_cairo_boilerplate_test_fallback16_create_surface (const char *name, - cairo_content_t content, - double width, - double height, - double max_width, - double max_height, - cairo_boilerplate_mode_t mode, - int id, - void **closure) +_cairo_boilerplate_test_fallback_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) { *closure = NULL; - return _cairo_test_fallback16_surface_create (content, - ceil (width), ceil (height)); + return _cairo_test_fallback_compositor_surface_create (content, ceil (width), ceil (height)); } -#if CAIRO_HAS_TEST_NULL_SURFACE static cairo_surface_t * -_cairo_boilerplate_test_null_create_surface (const char *name, - cairo_content_t content, - double width, - double height, - double max_width, - double max_height, - cairo_boilerplate_mode_t mode, - int id, - void **closure) +_cairo_boilerplate_test_mask_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) { *closure = NULL; - return _cairo_test_null_surface_create (content); + return _cairo_test_mask_compositor_surface_create (content, ceil (width), ceil (height)); +} + + +static cairo_surface_t * +_cairo_boilerplate_test_traps_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + *closure = NULL; + return _cairo_test_traps_compositor_surface_create (content, ceil (width), ceil (height)); +} + +static cairo_surface_t * +_cairo_boilerplate_test_spans_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + *closure = NULL; + return _cairo_test_spans_compositor_surface_create (content, ceil (width), ceil (height)); +} + +static cairo_surface_t * +_cairo_boilerplate_test_no_fallback_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + *closure = NULL; + return _cairo_test_no_fallback_compositor_surface_create (content, ceil (width), ceil (height)); +} + +static cairo_surface_t * +_cairo_boilerplate_test_no_traps_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + *closure = NULL; + return _cairo_test_no_traps_compositor_surface_create (content, ceil (width), ceil (height)); +} + +static cairo_surface_t * +_cairo_boilerplate_test_no_spans_compositor_create_surface (const char *name, + cairo_content_t content, + double width, + double height, + double max_width, + double max_height, + cairo_boilerplate_mode_t mode, + int id, + void **closure) +{ + *closure = NULL; + return _cairo_test_no_spans_compositor_surface_create (content, ceil (width), ceil (height)); } -#endif #if CAIRO_HAS_TEST_PAGINATED_SURFACE static const cairo_user_data_key_t test_paginated_closure_key; @@ -201,40 +268,38 @@ _cairo_boilerplate_test_paginated_cleanup (void *closure) } #endif -#if CAIRO_HAS_TEST_WRAPPING_SURFACE -static cairo_surface_t * -_cairo_boilerplate_test_wrapping_create_surface (const char *name, - cairo_content_t content, - double width, - double height, - double max_width, - double max_height, - cairo_boilerplate_mode_t mode, - int id, - void **closure) -{ - cairo_surface_t *target; - cairo_surface_t *surface; - cairo_format_t format; - - *closure = NULL; - - format = cairo_boilerplate_format_from_content (content); - target = cairo_image_surface_create (format, ceil (width), ceil (height)); - surface = _cairo_test_wrapping_surface_create (target); - cairo_surface_destroy (target); - - return surface; -} -#endif - static const cairo_boilerplate_target_t targets[] = { + { + "test-base", "image", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_base_compositor_surface_create", + _cairo_boilerplate_test_base_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, + { + "test-base", "image", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR, 0, + "_cairo_test_base_compositor_surface_create", + _cairo_boilerplate_test_base_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, FALSE, FALSE, FALSE + }, + { "test-fallback", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0, - "_cairo_test_fallback_surface_create", - _cairo_boilerplate_test_fallback_create_surface, + "_cairo_test_fallback_compositor_surface_create", + _cairo_boilerplate_test_fallback_compositor_create_surface, cairo_surface_create_similar, NULL, NULL, _cairo_boilerplate_get_image_surface, @@ -243,40 +308,128 @@ static const cairo_boilerplate_target_t targets[] = { }, { "test-fallback", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0, - "_cairo_test_fallback_surface_create", - _cairo_boilerplate_test_fallback_create_surface, + "_cairo_test_fallback_compositor_surface_create", + _cairo_boilerplate_test_fallback_compositor_create_surface, cairo_surface_create_similar, NULL, NULL, _cairo_boilerplate_get_image_surface, cairo_surface_write_to_png, NULL, NULL, NULL, FALSE, FALSE, FALSE }, + { - "test-fallback16", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + "test-mask", "mask", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR_ALPHA, 0, - "_cairo_test_fallback16_surface_create", - _cairo_boilerplate_test_fallback16_create_surface, + "_cairo_test_traps_compositor_surface_create", + _cairo_boilerplate_test_mask_compositor_create_surface, cairo_surface_create_similar, NULL, NULL, - NULL, /* _cairo_boilerplate_get_image_surface, */ + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, + { + "test-mask", "mask", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR, 0, + "_cairo_test_mask_compositor_surface_create", + _cairo_boilerplate_test_mask_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, cairo_surface_write_to_png, NULL, NULL, NULL, FALSE, FALSE, FALSE }, + { - "test-fallback16", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, + "test-traps", "traps", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_traps_compositor_surface_create", + _cairo_boilerplate_test_traps_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, + { + "test-traps", "traps", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, CAIRO_CONTENT_COLOR, 0, - "_cairo_test_fallback16_surface_create", - _cairo_boilerplate_test_fallback16_create_surface, + "_cairo_test_traps_compositor_surface_create", + _cairo_boilerplate_test_traps_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, FALSE, FALSE, FALSE + }, + + { + "test-spans", "spans", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_spans_compositor_surface_create", + _cairo_boilerplate_test_spans_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, + { + "test-spans", "spans", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR, 0, + "_cairo_test_spans_compositor_surface_create", + _cairo_boilerplate_test_spans_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, FALSE, FALSE, FALSE + }, + + { + "no-fallback", "image", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_no_fallback_compositor_surface_create", + _cairo_boilerplate_test_no_fallback_compositor_create_surface, cairo_surface_create_similar, NULL, NULL, - NULL, /* _cairo_boilerplate_get_image_surface, */ + _cairo_boilerplate_get_image_surface, cairo_surface_write_to_png, NULL, NULL, NULL, FALSE, FALSE, FALSE }, + { + "no-traps", "traps", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_no_traps_compositor_surface_create", + _cairo_boilerplate_test_no_traps_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, + { + "no-spans", "spans", NULL, NULL, + CAIRO_SURFACE_TYPE_IMAGE, + CAIRO_CONTENT_COLOR_ALPHA, 0, + "_cairo_test_no_spans_compositor_surface_create", + _cairo_boilerplate_test_no_spans_compositor_create_surface, + cairo_surface_create_similar, + NULL, NULL, + _cairo_boilerplate_get_image_surface, + cairo_surface_write_to_png, + NULL, NULL, NULL, TRUE, FALSE, FALSE + }, #if CAIRO_HAS_TEST_PAGINATED_SURFACE { "test-paginated", "image", NULL, NULL, @@ -305,33 +458,5 @@ static const cairo_boilerplate_target_t targets[] = { NULL, NULL, FALSE, TRUE, FALSE }, #endif -#if CAIRO_HAS_TEST_WRAPPING_SURFACE - { - "test-wrapping", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, - CAIRO_CONTENT_COLOR_ALPHA, 0, - "_cairo_test_wrapping_surface_create", - _cairo_boilerplate_test_wrapping_create_surface, - cairo_surface_create_similar, - NULL, NULL, - _cairo_boilerplate_get_image_surface, - cairo_surface_write_to_png, - NULL, NULL, NULL, FALSE, FALSE, FALSE - }, -#endif -#if CAIRO_HAS_TEST_NULL_SURFACE - { - "null", "image", NULL, NULL, - CAIRO_INTERNAL_SURFACE_TYPE_NULL, - CAIRO_CONTENT_COLOR_ALPHA, 0, - "_cairo_test_null_surface_create", - _cairo_boilerplate_test_null_create_surface, - cairo_surface_create_similar, - NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, - TRUE, TRUE, FALSE - }, -#endif }; CAIRO_BOILERPLATE (test, targets) diff --git a/boilerplate/cairo-boilerplate-xlib.c b/boilerplate/cairo-boilerplate-xlib.c index 6818cafd9..cad288477 100644 --- a/boilerplate/cairo-boilerplate-xlib.c +++ b/boilerplate/cairo-boilerplate-xlib.c @@ -31,7 +31,6 @@ #if CAIRO_HAS_XLIB_XRENDER_SURFACE #include #endif -#include #include /* for XDestroyImage */ @@ -412,6 +411,7 @@ _cairo_boilerplate_xlib_window_create_surface (const char *name, cairo_status_t cairo_boilerplate_xlib_surface_disable_render (cairo_surface_t *abstract_surface) { +#if 0 /* The following stunt doesn't work with xlib-xcb because it doesn't use * cairo_xlib_surface_t for its surfaces. Sadly, there is no sane * alternative, so we can't disable render with xlib-xcb. @@ -439,6 +439,7 @@ cairo_boilerplate_xlib_surface_disable_render (cairo_surface_t *abstract_surface #if CAIRO_XLIB_SURFACE_HAS_BUGGY_REPEAT surface->buggy_repeat = TRUE; #endif +#endif #endif return CAIRO_STATUS_SUCCESS; diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c index 229c4c77c..8e252f52b 100644 --- a/boilerplate/cairo-boilerplate.c +++ b/boilerplate/cairo-boilerplate.c @@ -683,16 +683,12 @@ cairo_boilerplate_get_image_target (cairo_content_t content) if (cairo_boilerplate_targets == NULL) _cairo_boilerplate_register_all (); - for (list = cairo_boilerplate_targets; list != NULL; list = list->next) { - const cairo_boilerplate_target_t *target = list->target; - if (target->expected_type == CAIRO_SURFACE_TYPE_IMAGE && - target->content == content) - { - return target; - } + switch (content) { + default: + case CAIRO_CONTENT_ALPHA: return NULL; + case CAIRO_CONTENT_COLOR: return &builtin_targets[1]; + case CAIRO_CONTENT_COLOR_ALPHA: return &builtin_targets[0]; } - - return NULL; } const cairo_boilerplate_target_t * diff --git a/configure.ac b/configure.ac index 4b85c8a73..5879a4443 100644 --- a/configure.ac +++ b/configure.ac @@ -202,11 +202,11 @@ CAIRO_ENABLE_SURFACE_BACKEND(skia, Skia, no, [ [skia_DIR="`pwd`/../skia"]) AC_ARG_WITH([skia-bulid], [AS_HELP_STRING([--with-skia-build=(Release|Debug)] - [build of skia to link with, default is Relese])], + [build of skia to link with, default is Release])], [skia_BUILD="$withval"], [skia_BUILD="Release"]) skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects" - if test "x$(skia_BUILD)" = x"Relese"; then + if test "x$skia_BUILD" = x"Release"; then skia_NONPKGCONFIG_CFLAGS="-DSK_RELEASE -DSK_CAN_USE_FLOAT $skia_NONPKGCONFIG_CFLAGS" fi skia_NONPKGCONFIG_LIBS="--start-group $skia_DIR/out/$skia_BUILD/obj.target/gyp/libeffects.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libimages.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libutils.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libopts.a $skia_DIR/out/$skia_BUILD/obj.target/gyp/libcore.a -end-group" diff --git a/perf/Makefile.sources b/perf/Makefile.sources index b99234b27..1fcf14809 100644 --- a/perf/Makefile.sources +++ b/perf/Makefile.sources @@ -1,5 +1,6 @@ libcairoperf_sources = \ - cairo-perf.c \ + cairo-perf.c \ + cairo-perf-report.c \ cairo-stats.c \ $(NULL) diff --git a/perf/cairo-perf-compare-backends.c b/perf/cairo-perf-compare-backends.c index 163063774..2cbb24c5d 100644 --- a/perf/cairo-perf-compare-backends.c +++ b/perf/cairo-perf-compare-backends.c @@ -73,7 +73,7 @@ print_change_bar (double change, double max_change, int use_utf) { - int units_per_cell = (int) ceil (max_change / CHANGE_BAR_WIDTH); + int units_per_cell = ceil (max_change / CHANGE_BAR_WIDTH); static char const *ascii_boxes[8] = { "****","***" ,"***", "**", "**", "*", "*", "" @@ -369,7 +369,7 @@ main (int argc, if (args.num_filenames) { reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t)); for (i = 0; i < args.num_filenames; i++) { - cairo_perf_report_load (&reports[i], args.filenames[i], + cairo_perf_report_load (&reports[i], args.filenames[i], i, test_report_cmp_name); printf ("loaded: %s, %d tests\n", args.filenames[i], reports[i].tests_count); @@ -377,7 +377,7 @@ main (int argc, } else { args.num_filenames = 1; reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t)); - cairo_perf_report_load (&reports[0], NULL, test_report_cmp_name); + cairo_perf_report_load (&reports[0], NULL, 0, test_report_cmp_name); } cairo_perf_reports_compare (reports, args.num_filenames, &args.options); diff --git a/perf/cairo-perf-diff-files.c b/perf/cairo-perf-diff-files.c index 34311b044..08509271f 100644 --- a/perf/cairo-perf-diff-files.c +++ b/perf/cairo-perf-diff-files.c @@ -161,16 +161,14 @@ test_diff_print_binary (test_diff_t *diff, else printf ("%5s %26s", diff->tests[0]->backend, diff->tests[0]->name); - if (diff->tests[0]->size) { - printf (" %6.2f (%.2f %4.2f%%) -> %6.2f (%.2f %4.2f%%): %5.2fx ", - diff->tests[0]->stats.min_ticks / diff->tests[0]->stats.ticks_per_ms, - diff->tests[0]->stats.median_ticks / diff->tests[0]->stats.ticks_per_ms, - diff->tests[0]->stats.std_dev * 100, - diff->tests[1]->stats.min_ticks / diff->tests[1]->stats.ticks_per_ms, - diff->tests[1]->stats.median_ticks / diff->tests[1]->stats.ticks_per_ms, - diff->tests[1]->stats.std_dev * 100, - fabs (diff->change)); - } + printf (" %6.2f (%.2f %4.2f%%) -> %6.2f (%.2f %4.2f%%): %5.2fx ", + diff->tests[0]->stats.min_ticks / diff->tests[0]->stats.ticks_per_ms, + diff->tests[0]->stats.median_ticks / diff->tests[0]->stats.ticks_per_ms, + diff->tests[0]->stats.std_dev * 100, + diff->tests[1]->stats.min_ticks / diff->tests[1]->stats.ticks_per_ms, + diff->tests[1]->stats.median_ticks / diff->tests[1]->stats.ticks_per_ms, + diff->tests[1]->stats.std_dev * 100, + fabs (diff->change)); if (diff->change > 1.0) printf ("speedup\n"); @@ -191,24 +189,32 @@ test_diff_print_multi (test_diff_t *diff, double test_time; double change; - printf ("%s (backend: %s-%s, size: %d)\n", - diff->tests[0]->name, - diff->tests[0]->backend, - diff->tests[0]->content, - diff->tests[0]->size); + if (diff->tests[0]->size) { + printf ("%s (backend: %s-%s, size: %d)\n", + diff->tests[0]->name, + diff->tests[0]->backend, + diff->tests[0]->content, + diff->tests[0]->size); + } else { + printf ("%s (backend: %s)\n", + diff->tests[0]->name, + diff->tests[0]->backend); + } for (i = 0; i < diff->num_tests; i++) { test_time = diff->tests[i]->stats.min_ticks; if (! options->use_ticks) test_time /= diff->tests[i]->stats.ticks_per_ms; change = diff->max / test_time; - printf ("%8s %6.2f: %5.2fx ", - diff->tests[i]->configuration, + printf ("[%d] %6.2f: %5.2fx ", + diff->tests[i]->fileno, diff->tests[i]->stats.min_ticks / diff->tests[i]->stats.ticks_per_ms, change); if (options->print_change_bars) print_change_bar (change, max_change, options->use_utf); + else + printf("\n"); } printf("\n"); @@ -476,8 +482,11 @@ main (int argc, reports = xmalloc (args.num_filenames * sizeof (cairo_perf_report_t)); - for (i = 0; i < args.num_filenames; i++ ) - cairo_perf_report_load (&reports[i], args.filenames[i], NULL); + for (i = 0; i < args.num_filenames; i++ ) { + cairo_perf_report_load (&reports[i], args.filenames[i], i, NULL); + printf ("[%d] %s\n", i, args.filenames[i]); + } + printf ("\n"); cairo_perf_reports_compare (reports, args.num_filenames, &args.options); diff --git a/perf/cairo-perf-micro.c b/perf/cairo-perf-micro.c index 096986bcd..f3c6f8d9d 100644 --- a/perf/cairo-perf-micro.c +++ b/perf/cairo-perf-micro.c @@ -50,12 +50,13 @@ #define CAIRO_PERF_ITERATIONS_DEFAULT 100 #define CAIRO_PERF_LOW_STD_DEV 0.03 -#define CAIRO_PERF_STABLE_STD_DEV_COUNT 5 -#define CAIRO_PERF_ITERATION_MS_DEFAULT 2000 +#define CAIRO_PERF_STABLE_STD_DEV_COUNT 5 +#define CAIRO_PERF_ITERATION_MS_DEFAULT 2000 #define CAIRO_PERF_ITERATION_MS_FAST 5 typedef struct _cairo_perf_case { - CAIRO_PERF_DECL (*run); + CAIRO_PERF_RUN_DECL (*run); + cairo_bool_t (*enabled) (cairo_perf_t *perf); unsigned int min_size; unsigned int max_size; } cairo_perf_case_t; @@ -251,7 +252,7 @@ cairo_perf_run (cairo_perf_t *perf, cairo_boilerplate_content (perf->target->content)); else cairo_save (perf->cr); - times[i] = perf_func (perf->cr, perf->size, perf->size, loops) / loops; + times[i] = perf_func (perf->cr, perf->size, perf->size, loops) ; if (similar) cairo_pattern_destroy (cairo_pop_group (perf->cr)); else @@ -263,7 +264,7 @@ cairo_perf_run (cairo_perf_t *perf, _content_to_string (perf->target->content, similar), name, perf->size, _cairo_time_to_double (_cairo_time_from_s (1.)) / 1000.); - printf (" %lld", (long long) times[i]); + printf (" %lld", (long long) (times[i] / (double) loops)); } else if (! perf->exact_iterations) { if (i > 0) { _cairo_stats_compute (&stats, times, i+1); @@ -287,18 +288,18 @@ cairo_perf_run (cairo_perf_t *perf, if (count_func != NULL) { double count = count_func (perf->cr, perf->size, perf->size); fprintf (perf->summary, - "%10lld %#8.3f %#8.3f %#5.2f%% %3d: %.2f\n", - (long long) stats.min_ticks, - _cairo_time_to_s (stats.min_ticks) * 1000.0, - _cairo_time_to_s (stats.median_ticks) * 1000.0, + "%10lld/%d %#8.3f %#8.3f %#5.2f%% %3d: %.2f\n", + (long long) stats.min_ticks, loops, + _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops, + _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops, stats.std_dev * 100.0, stats.iterations, count / _cairo_time_to_s (stats.min_ticks)); } else { fprintf (perf->summary, - "%10lld %#8.3f %#8.3f %#5.2f%% %3d\n", - (long long) stats.min_ticks, - _cairo_time_to_s (stats.min_ticks) * 1000.0, - _cairo_time_to_s (stats.median_ticks) * 1000.0, + "%10lld/%d %#8.3f %#8.3f %#5.2f%% %3d\n", + (long long) stats.min_ticks, loops, + _cairo_time_to_s (stats.min_ticks) * 1000.0 / loops, + _cairo_time_to_s (stats.median_ticks) * 1000.0 / loops, stats.std_dev * 100.0, stats.iterations); } fflush (perf->summary); @@ -491,6 +492,9 @@ main (int argc, for (j = 0; perf_cases[j].run; j++) { const cairo_perf_case_t *perf_case = &perf_cases[j]; + if (! perf_case->enabled (&perf)) + continue; + for (perf.size = perf_case->min_size; perf.size <= perf_case->max_size; perf.size *= 2) @@ -536,42 +540,48 @@ main (int argc, return 0; } +#define FUNC(f) f, f##_enabled const cairo_perf_case_t perf_cases[] = { - { paint, 64, 512}, - { paint_with_alpha, 64, 512}, - { fill, 64, 512}, - { stroke, 64, 512}, - { text, 64, 512}, - { glyphs, 64, 512}, - { mask, 64, 512}, - { line, 32, 512}, - { curve, 32, 512}, - { disjoint, 64, 512}, - { hatching, 64, 512}, - { tessellate, 100, 100}, - { subimage_copy, 16, 512}, - { hash_table, 16, 16}, - { pattern_create_radial, 16, 16}, - { zrusin, 415, 415}, - { world_map, 800, 800}, - { box_outline, 100, 100}, - { mosaic, 800, 800 }, - { long_lines, 100, 100}, - { unaligned_clip, 100, 100}, - { rectangles, 512, 512}, - { rounded_rectangles, 512, 512}, - { long_dashed_lines, 512, 512}, - { composite_checker, 16, 512}, - { twin, 800, 800}, - { dragon, 1024, 1024 }, - { pythagoras_tree, 768, 768 }, - { intersections, 512, 512 }, - { many_strokes, 32, 512 }, - { wide_strokes, 32, 512 }, - { many_fills, 32, 512 }, - { wide_fills, 32, 512 }, - { many_curves, 32, 512 }, - { spiral, 512, 512 }, - { wave, 500, 500 }, + { FUNC(pixel), 1, 1 }, + { FUNC(paint), 64, 512}, + { FUNC(paint_with_alpha), 64, 512}, + { FUNC(fill), 64, 512}, + { FUNC(stroke), 64, 512}, + { FUNC(text), 64, 512}, + { FUNC(glyphs), 64, 512}, + { FUNC(mask), 64, 512}, + { FUNC(line), 32, 512}, + { FUNC(a1_line), 32, 512}, + { FUNC(curve), 32, 512}, + { FUNC(a1_curve), 32, 512}, + { FUNC(disjoint), 64, 512}, + { FUNC(hatching), 64, 512}, + { FUNC(tessellate), 100, 100}, + { FUNC(subimage_copy), 16, 512}, + { FUNC(hash_table), 16, 16}, + { FUNC(pattern_create_radial), 16, 16}, + { FUNC(zrusin), 415, 415}, + { FUNC(world_map), 800, 800}, + { FUNC(box_outline), 100, 100}, + { FUNC(mosaic), 800, 800 }, + { FUNC(long_lines), 100, 100}, + { FUNC(unaligned_clip), 100, 100}, + { FUNC(rectangles), 512, 512}, + { FUNC(rounded_rectangles), 512, 512}, + { FUNC(long_dashed_lines), 512, 512}, + { FUNC(composite_checker), 16, 512}, + { FUNC(twin), 800, 800}, + { FUNC(dragon), 1024, 1024 }, + { FUNC(sierpinski), 32, 1024 }, + { FUNC(pythagoras_tree), 768, 768 }, + { FUNC(intersections), 512, 512 }, + { FUNC(many_strokes), 32, 512 }, + { FUNC(wide_strokes), 32, 512 }, + { FUNC(many_fills), 32, 512 }, + { FUNC(wide_fills), 32, 512 }, + { FUNC(many_curves), 32, 512 }, + { FUNC(spiral), 512, 512 }, + { FUNC(wave), 500, 500 }, + { FUNC(fill_clip), 16, 512 }, { NULL } }; diff --git a/perf/cairo-perf-report.c b/perf/cairo-perf-report.c index fcce0e035..8df78c647 100644 --- a/perf/cairo-perf-report.c +++ b/perf/cairo-perf-report.c @@ -110,6 +110,7 @@ do { \ static test_report_status_t test_report_parse (test_report_t *report, + int fileno, char *line, char *configuration) { @@ -137,6 +138,7 @@ test_report_parse (test_report_t *report, skip_space (); + report->fileno = fileno; report->configuration = configuration; parse_string (report->backend); end = strrchr (report->backend, '.'); @@ -369,7 +371,7 @@ cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report, void cairo_perf_report_load (cairo_perf_report_t *report, - const char *filename, + const char *filename, int id, int (*cmp) (const void *, const void *)) { FILE *file; @@ -401,6 +403,7 @@ cairo_perf_report_load (cairo_perf_report_t *report, report->tests_size = 16; report->tests = xmalloc (report->tests_size * sizeof (test_report_t)); report->tests_count = 0; + report->fileno = id; if (filename == NULL) { file = stdin; @@ -425,7 +428,7 @@ cairo_perf_report_load (cairo_perf_report_t *report, break; status = test_report_parse (&report->tests[report->tests_count], - line, report->configuration); + id, line, report->configuration); if (status == TEST_REPORT_STATUS_ERROR) fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s", line_number, filename, line); diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c index 2cef2b14c..8e637a7d2 100644 --- a/perf/cairo-perf-trace.c +++ b/perf/cairo-perf-trace.c @@ -79,7 +79,7 @@ basename_no_ext (char *path) name = basename (path); - dot = strchr (name, '.'); + dot = strrchr (name, '.'); if (dot) *dot = '\0'; @@ -108,6 +108,7 @@ struct trace { void *closure; cairo_surface_t *surface; cairo_bool_t observe; + int tile_size; }; cairo_bool_t @@ -132,7 +133,7 @@ cairo_perf_can_run (cairo_perf_t *perf, return TRUE; copy = xstrdup (name); - dot = strchr (copy, '.'); + dot = strrchr (copy, '.'); if (dot != NULL) *dot = '\0'; @@ -435,6 +436,7 @@ parse_options (cairo_perf_t *perf, perf->raw = FALSE; perf->observe = FALSE; perf->list_only = FALSE; + perf->tile_size = 0; perf->names = NULL; perf->num_names = 0; perf->summary = stdout; @@ -443,7 +445,7 @@ parse_options (cairo_perf_t *perf, perf->num_exclude_names = 0; while (1) { - c = _cairo_getopt (argc, argv, "i:x:lsrvc"); + c = _cairo_getopt (argc, argv, "t:i:x:lsrvc"); if (c == -1) break; @@ -457,6 +459,14 @@ parse_options (cairo_perf_t *perf, exit (1); } break; + case 't': + perf->tile_size = strtoul (optarg, &end, 10); + if (*end != '\0') { + fprintf (stderr, "Invalid argument for -t (not an integer): %s\n", + optarg); + exit (1); + } + break; case 'l': perf->list_only = TRUE; break; @@ -489,6 +499,11 @@ parse_options (cairo_perf_t *perf, } } + if (perf->observe && perf->tile_size) { + fprintf (stderr, "Can't mix observer and tiling. Sorry.\n"); + exit (1); + } + if (verbose && perf->summary == NULL) perf->summary = stderr; #if HAVE_UNISTD_H @@ -535,6 +550,79 @@ have_trace_filenames (cairo_perf_t *perf) return FALSE; } +static void +_tiling_surface_finish (cairo_surface_t *observer, + cairo_surface_t *target, + void *closure) +{ + struct trace *args = closure; + cairo_surface_t *surface; + cairo_content_t content; + cairo_rectangle_t r; + int width, height; + int x, y, w, h; + + cairo_recording_surface_get_extents (target, &r); + w = r.width; + h = r.height; + + content = cairo_surface_get_content (target); + + for (y = 0; y < h; y += args->tile_size) { + height = args->tile_size; + if (y + height > h) + height = h - y; + + for (x = 0; x < w; x += args->tile_size) { + cairo_t *cr; + + width = args->tile_size; + if (x + width > w) + width = w - x; + + /* XXX to correctly observe the playback we would need + * to replay the target onto the observer directly. + */ + surface = args->target->create_similar (args->surface, + content, width, height); + + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, target, -x, -y); + cairo_paint (cr); + cairo_destroy (cr); + + cairo_surface_destroy (surface); + } + } +} + +static cairo_surface_t * +_tiling_surface_create (void *closure, + cairo_content_t content, + double width, + double height, + long uid) +{ + cairo_rectangle_t r; + cairo_surface_t *surface, *observer; + + r.x = r.y = 0; + r.width = width; + r.height = height; + + surface = cairo_recording_surface_create (content, &r); + observer = cairo_surface_create_observer (surface, + CAIRO_SURFACE_OBSERVER_NORMAL); + cairo_surface_destroy (surface); + + cairo_surface_observer_add_finish_callback (observer, + _tiling_surface_finish, + closure); + + return observer; +} + static void cairo_perf_trace (cairo_perf_t *perf, const cairo_boilerplate_target_t *target, @@ -549,7 +637,7 @@ cairo_perf_trace (cairo_perf_t *perf, char *trace_cpy, *name; const cairo_script_interpreter_hooks_t hooks = { &args, - _similar_surface_create, + perf->tile_size ? _tiling_surface_create : _similar_surface_create, NULL, /* surface_destroy */ _context_create, NULL, /* context_destroy */ @@ -557,6 +645,7 @@ cairo_perf_trace (cairo_perf_t *perf, NULL /* copy_page */ }; + args.tile_size = perf->tile_size; args.observe = perf->observe; trace_cpy = xstrdup (trace); @@ -648,26 +737,30 @@ cairo_perf_trace (cairo_perf_t *perf, } cairo_script_interpreter_run (csi, trace); + line_no = cairo_script_interpreter_get_line_number (csi); + + /* Finish before querying timings in case we are using an intermediate + * target and so need to destroy all surfaces before rendering + * commences. + */ + cairo_script_interpreter_finish (csi); if (perf->observe) { cairo_device_t *observer = cairo_surface_get_device (args.surface); - times[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_elapsed (observer)); - paint[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_paint_elapsed (observer)); - mask[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_mask_elapsed (observer)); - stroke[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_stroke_elapsed (observer)); - fill[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_fill_elapsed (observer)); - glyphs[i] = _cairo_time_from_s (1.e9 * cairo_device_observer_glyphs_elapsed (observer)); + times[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_elapsed (observer)); + paint[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_paint_elapsed (observer)); + mask[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_mask_elapsed (observer)); + stroke[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_stroke_elapsed (observer)); + fill[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_fill_elapsed (observer)); + glyphs[i] = _cairo_time_from_s (1.e-9 * cairo_device_observer_glyphs_elapsed (observer)); } else { clear_surface (args.surface); /* queue a write to the sync'ed surface */ cairo_perf_timer_stop (); times[i] = cairo_perf_timer_elapsed (); } - cairo_script_interpreter_finish (csi); scache_clear (); - line_no = cairo_script_interpreter_get_line_number (csi); - cairo_surface_destroy (args.surface); if (target->cleanup) @@ -766,28 +859,28 @@ cairo_perf_trace (cairo_perf_t *perf, fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); - _cairo_stats_compute (&stats, paint, i+1); + _cairo_stats_compute (&stats, paint, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); - _cairo_stats_compute (&stats, mask, i+1); + _cairo_stats_compute (&stats, mask, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); - _cairo_stats_compute (&stats, fill, i+1); + _cairo_stats_compute (&stats, fill, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); - _cairo_stats_compute (&stats, stroke, i+1); + _cairo_stats_compute (&stats, stroke, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); - _cairo_stats_compute (&stats, glyphs, i+1); + _cairo_stats_compute (&stats, glyphs, i); fprintf (perf->summary, " %#9.3f", _cairo_time_to_s (stats.median_ticks)); fprintf (perf->summary, - " %5d\n", i+1); + " %5d\n", i); } else { fprintf (perf->summary, "%#8.3f %#8.3f %#6.2f%% %4d/%d\n", @@ -807,11 +900,6 @@ cairo_perf_trace (cairo_perf_t *perf, perf->test_number++; free (trace_cpy); - - cairo_debug_reset_static_data (); -#if HAVE_FCFINI - FcFini (); -#endif } static void diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h index 11c03f750..d6a71ccb5 100644 --- a/perf/cairo-perf.h +++ b/perf/cairo-perf.h @@ -83,6 +83,8 @@ typedef struct _cairo_perf { double ms_per_iteration; cairo_bool_t fast_and_sloppy; + unsigned int tile_size; + /* Stuff used internally */ cairo_time_t *times; const cairo_boilerplate_target_t **targets; @@ -121,6 +123,7 @@ cairo_perf_cover_sources_and_operators (cairo_perf_t *perf, typedef struct _test_report { int id; + int fileno; const char *configuration; char *backend; char *content; @@ -149,6 +152,7 @@ typedef struct _test_diff { typedef struct _cairo_perf_report { char *configuration; const char *name; + int fileno; test_report_t *tests; int tests_size; int tests_count; @@ -162,7 +166,7 @@ typedef enum { void cairo_perf_report_load (cairo_perf_report_t *report, - const char *filename, + const char *filename, int id, int (*cmp) (const void *, const void *)); void @@ -177,7 +181,10 @@ int test_report_cmp_name (const void *a, const void *b); -#define CAIRO_PERF_DECL(func) void (func) (cairo_perf_t *perf, cairo_t *cr, int width, int height) +#define CAIRO_PERF_ENABLED_DECL(func) cairo_bool_t (func ## _enabled) (cairo_perf_t *perf) +#define CAIRO_PERF_RUN_DECL(func) void (func) (cairo_perf_t *perf, cairo_t *cr, int width, int height) + +#define CAIRO_PERF_DECL(func) CAIRO_PERF_RUN_DECL(func); CAIRO_PERF_ENABLED_DECL(func) CAIRO_PERF_DECL (fill); CAIRO_PERF_DECL (paint); @@ -214,6 +221,11 @@ CAIRO_PERF_DECL (many_fills); CAIRO_PERF_DECL (wide_fills); CAIRO_PERF_DECL (many_curves); CAIRO_PERF_DECL (curve); +CAIRO_PERF_DECL (a1_curve); CAIRO_PERF_DECL (line); +CAIRO_PERF_DECL (a1_line); +CAIRO_PERF_DECL (pixel); +CAIRO_PERF_DECL (sierpinski); +CAIRO_PERF_DECL (fill_clip); #endif diff --git a/perf/cairo-stats.c b/perf/cairo-stats.c index e088e198f..c422d6cc6 100644 --- a/perf/cairo-stats.c +++ b/perf/cairo-stats.c @@ -25,16 +25,25 @@ #include "cairo-stats.h" +#include + void _cairo_stats_compute (cairo_stats_t *stats, cairo_time_t *values, int num_values) { - int i; - cairo_time_t sumtime; - double sum, mean, delta, q1, q3, iqr; - double outlier_min, outlier_max; - int min_valid, num_valid; + cairo_time_t sum, mean, delta, q1, q3, iqr; + cairo_time_t outlier_min, outlier_max; + int i, min_valid, num_valid; + + assert (num_values > 0); + + if (num_values == 1) { + stats->min_ticks = stats->median_ticks = values[0]; + stats->std_dev = 0; + stats->iterations = 1; + return; + } /* First, identify any outliers, using the definition of "mild * outliers" from: @@ -48,44 +57,35 @@ _cairo_stats_compute (cairo_stats_t *stats, */ qsort (values, num_values, sizeof (cairo_time_t), _cairo_time_cmp); - q1 = _cairo_time_to_s (values[(1*num_values)/4]); - q3 = _cairo_time_to_s (values[(3*num_values)/4]); + q1 = values[1*num_values/4]; + q3 = values[3*num_values/4]; + /* XXX assumes we have native uint64_t */ iqr = q3 - q1; + outlier_min = q1 - 3 * iqr / 2; + outlier_max = q3 + 3 * iqr / 2; - outlier_min = _cairo_time_from_s (q1 - 1.5 * iqr); - outlier_max = _cairo_time_from_s (q3 + 1.5 * iqr); - - min_valid = 0; - while (min_valid < num_values && - _cairo_time_to_s (values[min_valid]) < outlier_min) - { - min_valid++; - } + for (i = 0; i < num_values && values[i] < outlier_min; i++) + ; + min_valid = i; - i = min_valid; - num_valid = 0; - while (i + num_valid < num_values && - _cairo_time_to_s (values[i+num_valid]) <= outlier_max) - { - num_valid++; - } + for (i = 0; i < num_values && values[i] <= outlier_max; i++) + ; + num_valid = i - min_valid; + assert(num_valid); stats->iterations = num_valid; stats->min_ticks = values[min_valid]; - - sumtime = _cairo_time_from_s (0); - for (i = min_valid; i < min_valid + num_valid; i++) { - sumtime = _cairo_time_add (sumtime, values[i]); - stats->min_ticks = _cairo_time_min (stats->min_ticks, values[i]); - } - - mean = _cairo_time_to_s (sumtime) / num_valid; stats->median_ticks = values[min_valid + num_valid / 2]; - sum = 0.0; + sum = 0; + for (i = min_valid; i < min_valid + num_valid; i++) + sum = _cairo_time_add (sum, values[i]); + mean = sum / num_valid; + + sum = 0; for (i = min_valid; i < min_valid + num_valid; i++) { - delta = _cairo_time_to_s (values[i]) - mean; + delta = values[i] - mean; sum += delta * delta; } diff --git a/perf/micro/Makefile.sources b/perf/micro/Makefile.sources index 0c2bca08a..e72d44ad9 100644 --- a/perf/micro/Makefile.sources +++ b/perf/micro/Makefile.sources @@ -7,6 +7,7 @@ libcairo_perf_micro_sources = \ hatching.c \ hash-table.c \ line.c \ + a1-line.c \ long-lines.c \ mosaic.c \ paint.c \ @@ -35,7 +36,11 @@ libcairo_perf_micro_sources = \ wide-fills.c \ many-curves.c \ curve.c \ + a1-curve.c \ spiral.c \ + pixel.c \ + sierpinski.c \ + fill-clip.c \ $(NULL) libcairo_perf_micro_headers = \ diff --git a/perf/micro/a1-curve.c b/perf/micro/a1-curve.c new file mode 100644 index 000000000..594c46d5c --- /dev/null +++ b/perf/micro/a1-curve.c @@ -0,0 +1,112 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +#include "cairo-perf.h" + +static uint32_t state; + +static double +uniform_random (double minval, double maxval) +{ + static uint32_t const poly = 0x9a795537U; + uint32_t n = 32; + while (n-->0) + state = 2*state < state ? (2*state ^ poly) : 2*state; + return minval + state * (maxval - minval) / 4294967296.0; +} + +static cairo_time_t +do_curve_stroke (cairo_t *cr, int width, int height, int loops) +{ + state = 0xc0ffee; + cairo_set_line_width (cr, 2.); + cairo_perf_timer_start (); + + while (loops--) { + double x1 = uniform_random (0, width); + double x2 = uniform_random (0, width); + double x3 = uniform_random (0, width); + double y1 = uniform_random (0, height); + double y2 = uniform_random (0, height); + double y3 = uniform_random (0, height); + cairo_move_to (cr, uniform_random (0, width), uniform_random (0, height)); + cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); + cairo_stroke(cr); + } + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +do_curve_fill (cairo_t *cr, int width, int height, int loops) +{ + state = 0xc0ffee; + cairo_perf_timer_start (); + + while (loops--) { + double x0 = uniform_random (0, width); + double x1 = uniform_random (0, width); + double x2 = uniform_random (0, width); + double x3 = uniform_random (0, width); + double xm = uniform_random (0, width); + double xn = uniform_random (0, width); + double y0 = uniform_random (0, height); + double y1 = uniform_random (0, height); + double y2 = uniform_random (0, height); + double y3 = uniform_random (0, height); + double ym = uniform_random (0, height); + double yn = uniform_random (0, height); + + cairo_move_to (cr, xm, ym); + cairo_curve_to (cr, x1, y1, x2, y2, xn, yn); + cairo_curve_to (cr, x3, y3, x0, y0, xm, ym); + cairo_close_path (cr); + + cairo_fill(cr); + } + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +cairo_bool_t +a1_curve_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "a1-curve", NULL); +} + +void +a1_curve (cairo_perf_t *perf, cairo_t *cr, int width, int height) +{ + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + + cairo_perf_run (perf, "a1-curve-stroked", do_curve_stroke, NULL); + cairo_perf_run (perf, "a1-curve-filled", do_curve_fill, NULL); +} diff --git a/perf/micro/a1-line.c b/perf/micro/a1-line.c new file mode 100644 index 000000000..ae8660212 --- /dev/null +++ b/perf/micro/a1-line.c @@ -0,0 +1,223 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +#include "cairo-perf.h" + +static cairo_time_t +horizontal (cairo_t *cr, int width, int height, int loops) +{ + double h = height/2 + .5; + + cairo_move_to (cr, 0, h); + cairo_line_to (cr, width, h); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +horizontal_hair (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + return horizontal (cr, width, height, loops); +} + +static cairo_time_t +horizontal_wide (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 5.); + return horizontal (cr, width, height, loops); +} + +static cairo_time_t +nearly_horizontal (cairo_t *cr, int width, int height, int loops) +{ + double h = height/2; + + cairo_move_to (cr, 0, h); + cairo_line_to (cr, width, h+1); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +nearly_horizontal_hair (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + return nearly_horizontal (cr, width, height, loops); +} + +static cairo_time_t +nearly_horizontal_wide (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 5.); + return nearly_horizontal (cr, width, height, loops); +} + + +static cairo_time_t +vertical (cairo_t *cr, int width, int height, int loops) +{ + double w = width/2 + .5; + + cairo_move_to (cr, w, 0); + cairo_line_to (cr, w, height); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +vertical_hair (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + return vertical (cr, width, height, loops); +} + +static cairo_time_t +vertical_wide (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 5.); + return vertical (cr, width, height, loops); +} + +static cairo_time_t +nearly_vertical (cairo_t *cr, int width, int height, int loops) +{ + double w = width/2; + + cairo_move_to (cr, w, 0); + cairo_line_to (cr, w+1, height); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +nearly_vertical_hair (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + return nearly_vertical (cr, width, height, loops); +} + +static cairo_time_t +nearly_vertical_wide (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 5.); + return nearly_vertical (cr, width, height, loops); +} + + +static cairo_time_t +diagonal (cairo_t *cr, int width, int height, int loops) +{ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, width, height); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +diagonal_hair (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + return diagonal (cr, width, height, loops); +} + +static cairo_time_t +diagonal_wide (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 5.); + return diagonal (cr, width, height, loops); +} + +cairo_bool_t +a1_line_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "a1-line", NULL); +} + +void +a1_line (cairo_perf_t *perf, cairo_t *cr, int width, int height) +{ + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + + cairo_perf_run (perf, "a1-line-hh", horizontal_hair, NULL); + cairo_perf_run (perf, "a1-line-hw", horizontal_wide, NULL); + cairo_perf_run (perf, "a1-line-nhh", nearly_horizontal_hair, NULL); + cairo_perf_run (perf, "a1-line-nhw", nearly_horizontal_wide, NULL); + + cairo_perf_run (perf, "a1-line-vh", vertical_hair, NULL); + cairo_perf_run (perf, "a1-line-vw", vertical_wide, NULL); + cairo_perf_run (perf, "a1-line-nvh", nearly_vertical_hair, NULL); + cairo_perf_run (perf, "a1-line-nvw", nearly_vertical_wide, NULL); + + cairo_perf_run (perf, "a1-line-dh", diagonal_hair, NULL); + cairo_perf_run (perf, "a1-line-dw", diagonal_wide, NULL); +} diff --git a/perf/micro/box-outline.c b/perf/micro/box-outline.c index a0c47f779..1e654eb95 100644 --- a/perf/micro/box-outline.c +++ b/perf/micro/box-outline.c @@ -64,6 +64,55 @@ box_outline_stroke (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +static cairo_time_t +box_outline_alpha_stroke (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_source_rgb (cr, 0, 0, 1); /* blue */ + cairo_paint (cr); + + cairo_rectangle (cr, + 1.5, 1.5, + width - 3, height - 3); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgba (cr, 1, 0, 0, .5); /* red */ + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +box_outline_aa_stroke (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_source_rgb (cr, 0, 0, 1); /* blue */ + cairo_paint (cr); + + cairo_translate (cr, .5, .5); + cairo_rectangle (cr, + 1.5, 1.5, + width - 3, height - 3); + cairo_set_line_width (cr, 1.0); + cairo_set_source_rgb (cr, 1, 0, 0); /* red */ + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + static cairo_time_t box_outline_fill (cairo_t *cr, int width, int height, int loops) { @@ -91,12 +140,76 @@ box_outline_fill (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +static cairo_time_t +box_outline_alpha_fill (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_source_rgb (cr, 0, 0, 1); /* blue */ + cairo_paint (cr); + + cairo_rectangle (cr, + 1.0, 1.0, + width - 2, height - 2); + cairo_rectangle (cr, + 2.0, 2.0, + width - 4, height - 4); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_source_rgba (cr, 0, 1, 0, .5); /* green */ + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +box_outline_aa_fill (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_source_rgb (cr, 0, 0, 1); /* blue */ + cairo_paint (cr); + + cairo_translate (cr, .5, .5); + cairo_rectangle (cr, + 1.0, 1.0, + width - 2, height - 2); + cairo_rectangle (cr, + 2.0, 2.0, + width - 4, height - 4); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_source_rgb (cr, 0, 1, 0); /* green */ + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + + return cairo_perf_timer_elapsed (); +} + +cairo_bool_t +box_outline_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "box-outline", NULL); +} + void box_outline (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "box-outline", NULL)) - return; - cairo_perf_run (perf, "box-outline-stroke", box_outline_stroke, NULL); cairo_perf_run (perf, "box-outline-fill", box_outline_fill, NULL); + + cairo_perf_run (perf, "box-outline-alpha-stroke", box_outline_alpha_stroke, NULL); + cairo_perf_run (perf, "box-outline-alpha-fill", box_outline_alpha_fill, NULL); + + cairo_perf_run (perf, "box-outline-aa-stroke", box_outline_aa_stroke, NULL); + cairo_perf_run (perf, "box-outline-aa-fill", box_outline_aa_fill, NULL); } diff --git a/perf/micro/composite-checker.c b/perf/micro/composite-checker.c index 0d7af0c87..d6d17ab63 100644 --- a/perf/micro/composite-checker.c +++ b/perf/micro/composite-checker.c @@ -75,6 +75,12 @@ do_composite_checker (cairo_t *cr, return cairo_perf_timer_elapsed (); } +cairo_bool_t +composite_checker_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "composite-checker", NULL); +} + void composite_checker (cairo_perf_t *perf, cairo_t *cr, @@ -83,9 +89,6 @@ composite_checker (cairo_perf_t *perf, { cairo_surface_t *image; - if (! cairo_perf_can_run (perf, "composite-checker", NULL)) - return; - /* Create the checker pattern. We don't actually need to draw * anything on it since that wouldn't affect performance. */ diff --git a/perf/micro/curve.c b/perf/micro/curve.c index 7def326e6..3b5a16342 100644 --- a/perf/micro/curve.c +++ b/perf/micro/curve.c @@ -95,12 +95,15 @@ do_curve_fill (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +curve_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "curve", NULL); +} + void curve (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "curve", NULL)) - return; - cairo_set_source_rgb (cr, 1., 1., 1.); cairo_perf_run (perf, "curve-stroked", do_curve_stroke, NULL); diff --git a/perf/micro/disjoint.c b/perf/micro/disjoint.c index d7f502608..623eb6f48 100644 --- a/perf/micro/disjoint.c +++ b/perf/micro/disjoint.c @@ -85,6 +85,12 @@ draw (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +disjoint_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "disjoint", NULL); +} + void disjoint (cairo_perf_t *perf, cairo_t *cr, int width, int height) { diff --git a/perf/micro/dragon.c b/perf/micro/dragon.c index 5bc8d26f8..e215eaccc 100644 --- a/perf/micro/dragon.c +++ b/perf/micro/dragon.c @@ -265,12 +265,15 @@ do_dragon_solid_circle_clip (cairo_t *cr, int width, int height, int loops) return do_dragon_solid (cr, width, height, loops); } +cairo_bool_t +dragon_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "dragon", NULL); +} + void dragon (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "dragon", NULL)) - return; - cairo_perf_run (perf, "dragon-solid", do_dragon_solid, NULL); cairo_perf_run (perf, "dragon-unaligned-solid", do_dragon_solid_unaligned, NULL); cairo_perf_run (perf, "dragon-solid-aligned-clip", do_dragon_solid_aligned_clip, NULL); diff --git a/perf/micro/fill-clip.c b/perf/micro/fill-clip.c new file mode 100644 index 000000000..2d014aca8 --- /dev/null +++ b/perf/micro/fill-clip.c @@ -0,0 +1,126 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +/* Compares the overhead for WebKit's drawRect() */ + +#include "cairo-perf.h" + +#include + +static cairo_time_t +clip_paint (cairo_t *cr, int width, int height, int loops) +{ + int x = width/4, w = width/2; + int y = height/4, h = height/2; + + cairo_perf_timer_start (); + + while (loops--) { + cairo_reset_clip (cr); + cairo_rectangle (cr, x, y, w, h); + cairo_clip (cr); + cairo_paint (cr); + } + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +rect_fill (cairo_t *cr, int width, int height, int loops) +{ + int x = width/4, w = width/2; + int y = height/4, h = height/2; + + cairo_perf_timer_start (); + + while (loops--) { + cairo_rectangle (cr, x, y, w, h); + cairo_fill (cr); + } + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +direct (cairo_t *cr, int width, int height, int loops) +{ + int x = width/4, w = width/2; + int y = height/4, h = height/2; + cairo_surface_t *surface, *image; + uint8_t *data; + int stride, bpp; + + + surface = cairo_get_target (cr); + image = cairo_surface_map_to_image (surface, NULL); + data = cairo_image_surface_get_data (image); + stride = cairo_image_surface_get_stride (image); + + switch (cairo_image_surface_get_format (image)) { + default: + case CAIRO_FORMAT_INVALID: + case CAIRO_FORMAT_A1: bpp = 0; break; + case CAIRO_FORMAT_A8: bpp = 8; break; + case CAIRO_FORMAT_RGB16_565: bpp = 16; break; + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_ARGB32: bpp = 32; break; + } + + cairo_perf_timer_start (); + + while (loops--) { + pixman_fill ((uint32_t *)data, stride / sizeof(uint32_t), bpp, + x, y, w, h, + -1); + } + + cairo_perf_timer_stop (); + + cairo_surface_unmap_image (surface, image); + + return cairo_perf_timer_elapsed (); +} + +cairo_bool_t +fill_clip_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "fillclip", NULL); +} + +void +fill_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height) +{ + cairo_set_source_rgb (cr, 1., 1., 1.); + + cairo_perf_run (perf, "fillclip-clip", clip_paint, NULL); + cairo_perf_run (perf, "fillclip-fill", rect_fill, NULL); + cairo_perf_run (perf, "fillclip-direct", direct, NULL); +} diff --git a/perf/micro/fill.c b/perf/micro/fill.c index 079522718..d356c26d5 100644 --- a/perf/micro/fill.c +++ b/perf/micro/fill.c @@ -107,12 +107,15 @@ do_fill_eo_noaa (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +fill_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "fill", NULL); +} + void fill (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "fill", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "fill", do_fill, NULL); cairo_perf_cover_sources_and_operators (perf, "fill-annuli", do_fill_annuli, NULL); cairo_perf_cover_sources_and_operators (perf, "fill-eo-noaa", do_fill_eo_noaa, NULL); diff --git a/perf/micro/glyphs.c b/perf/micro/glyphs.c index 74c67eeeb..5f088b29e 100644 --- a/perf/micro/glyphs.c +++ b/perf/micro/glyphs.c @@ -170,12 +170,15 @@ DECL(48ca, 48, CAIRO_ANTIALIAS_SUBPIXEL) DECL(8mono, 8, CAIRO_ANTIALIAS_NONE) DECL(48mono, 48, CAIRO_ANTIALIAS_NONE) +cairo_bool_t +glyphs_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "glyphs", NULL); +} + void glyphs (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "glyphs", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "glyphs8mono", do_glyphs8mono, count_glyphs8mono); cairo_perf_cover_sources_and_operators (perf, "glyphs8", do_glyphs8, count_glyphs8); cairo_perf_cover_sources_and_operators (perf, "glyphs8ca", do_glyphs8ca, count_glyphs8ca); diff --git a/perf/micro/hash-table.c b/perf/micro/hash-table.c index a02663813..d16291770 100644 --- a/perf/micro/hash-table.c +++ b/perf/micro/hash-table.c @@ -101,12 +101,15 @@ do_hash_table (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +hash_table_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "hash-table", NULL); +} + void hash_table (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "hash-table", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "hash-table", do_hash_table, NULL); } diff --git a/perf/micro/hatching.c b/perf/micro/hatching.c index e31d30101..b51acec5d 100644 --- a/perf/micro/hatching.c +++ b/perf/micro/hatching.c @@ -170,12 +170,15 @@ F(clip_alpha_aligned_mono, clip_alpha, aligned, mono) F(clip_alpha_misaligned_mono, clip_alpha, misaligned, mono) F(clip_alpha_rotated_mono, clip_alpha, rotated, mono) +cairo_bool_t +hatching_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "hatching", NULL); +} + void hatching (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "hatching", NULL)) - return; - cairo_perf_run (perf, "hatching-aligned-aa", draw_aligned_aa, NULL); cairo_perf_run (perf, "hatching-misaligned-aa", draw_misaligned_aa, NULL); cairo_perf_run (perf, "hatching-rotated-aa", draw_rotated_aa, NULL); diff --git a/perf/micro/intersections.c b/perf/micro/intersections.c index 9a81eee85..57931faf8 100644 --- a/perf/micro/intersections.c +++ b/perf/micro/intersections.c @@ -143,12 +143,15 @@ random_curve_nz (cairo_t *cr, int width, int height, int loops) return draw_random_curve (cr, CAIRO_FILL_RULE_WINDING, width, height, loops); } +cairo_bool_t +intersections_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "intersections", NULL); +} + void intersections (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "intersections", NULL)) - return; - cairo_perf_run (perf, "intersections-nz-fill", random_nz, NULL); cairo_perf_run (perf, "intersections-eo-fill", random_eo, NULL); diff --git a/perf/micro/line.c b/perf/micro/line.c index 7ba9f9048..3ed5f8dac 100644 --- a/perf/micro/line.c +++ b/perf/micro/line.c @@ -196,12 +196,15 @@ diagonal_wide (cairo_t *cr, int width, int height, int loops) return diagonal (cr, width, height, loops); } +cairo_bool_t +line_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "line", NULL); +} + void line (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "line", NULL)) - return; - cairo_set_source_rgb (cr, 1., 1., 1.); cairo_perf_run (perf, "line-hh", horizontal_hair, NULL); diff --git a/perf/micro/long-dashed-lines.c b/perf/micro/long-dashed-lines.c index fa99b2008..ba66a4af4 100644 --- a/perf/micro/long-dashed-lines.c +++ b/perf/micro/long-dashed-lines.c @@ -61,11 +61,14 @@ do_long_dashed_lines (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +long_dashed_lines_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "long-dashed-lines", NULL); +} + void long_dashed_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "long-dashed-lines", NULL)) - return; - cairo_perf_run (perf, "long-dashed-lines", do_long_dashed_lines, NULL); } diff --git a/perf/micro/long-lines.c b/perf/micro/long-lines.c index b3a945855..a0d134c2d 100644 --- a/perf/micro/long-lines.c +++ b/perf/micro/long-lines.c @@ -132,12 +132,15 @@ long_lines_cropped_once (cairo_t *cr, int width, int height, int loops) return do_long_lines (cr, width, height, loops, LONG_LINES_CROPPED | LONG_LINES_ONCE); } +cairo_bool_t +long_lines_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "long-lines", NULL); +} + void long_lines (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "long-lines", NULL)) - return; - cairo_perf_run (perf, "long-lines-uncropped", long_lines_uncropped, NULL); cairo_perf_run (perf, "long-lines-uncropped-once", long_lines_uncropped_once, NULL); cairo_perf_run (perf, "long-lines-cropped", long_lines_cropped, NULL); diff --git a/perf/micro/many-curves.c b/perf/micro/many-curves.c index dc7cdf9fe..f985d349a 100644 --- a/perf/micro/many-curves.c +++ b/perf/micro/many-curves.c @@ -118,12 +118,15 @@ do_many_curves_filled (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +many_curves_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "many-curves", NULL); +} + void many_curves (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "many-curves", NULL)) - return; - cairo_set_source_rgb (cr, 1., 1., 1.); cairo_perf_run (perf, "many-curves-hair-stroked", do_many_curves_hair_stroked, NULL); diff --git a/perf/micro/many-fills.c b/perf/micro/many-fills.c index eb56e8b92..9d3fd6435 100644 --- a/perf/micro/many-fills.c +++ b/perf/micro/many-fills.c @@ -170,12 +170,15 @@ do_many_fills (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +many_fills_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "many-fills", NULL); +} + void many_fills (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "many-fills", NULL)) - return; - cairo_perf_run (perf, "many-fills-halign", do_many_fills_ha, NULL); cairo_perf_run (perf, "many-fills-valign", do_many_fills_va, NULL); cairo_perf_run (perf, "many-fills-horizontal", do_many_fills_h, NULL); diff --git a/perf/micro/many-strokes.c b/perf/micro/many-strokes.c index f0339457b..9aeb393de 100644 --- a/perf/micro/many-strokes.c +++ b/perf/micro/many-strokes.c @@ -45,7 +45,7 @@ do_many_strokes_ha (cairo_t *cr, int width, int height, int loops) state = 0xc0ffee; for (count = 0; count < 1000; count++) { - double h = floor (uniform_random (0, height)); + double h = floor (uniform_random (0, height)) + .5; cairo_move_to (cr, floor (uniform_random (0, width)), h); cairo_line_to (cr, ceil (uniform_random (0, width)), h); } @@ -97,7 +97,7 @@ do_many_strokes_va (cairo_t *cr, int width, int height, int loops) state = 0xc0ffee; for (count = 0; count < 1000; count++) { - double v = floor (uniform_random (0, width)); + double v = floor (uniform_random (0, width)) + .5; cairo_move_to (cr, v, floor (uniform_random (0, height))); cairo_line_to (cr, v, ceil (uniform_random (0, height))); } @@ -169,12 +169,15 @@ do_many_strokes (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +many_strokes_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "many-strokes", NULL); +} + void many_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "many-strokes", NULL)) - return; - cairo_perf_run (perf, "many-strokes-halign", do_many_strokes_ha, NULL); cairo_perf_run (perf, "many-strokes-valign", do_many_strokes_va, NULL); cairo_perf_run (perf, "many-strokes-horizontal", do_many_strokes_h, NULL); diff --git a/perf/micro/mask.c b/perf/micro/mask.c index 79092e855..11a3ba730 100644 --- a/perf/micro/mask.c +++ b/perf/micro/mask.c @@ -272,6 +272,12 @@ do_mask_radial (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +mask_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "mask", NULL); +} + void mask (cairo_perf_t *perf, cairo_t *cr, int width, int height) { diff --git a/perf/micro/mosaic.c b/perf/micro/mosaic.c index d76b84002..ed30ae555 100644 --- a/perf/micro/mosaic.c +++ b/perf/micro/mosaic.c @@ -160,12 +160,15 @@ mosaic_tessellate_curves (cairo_t *cr, int width, int height, int loops) return mosaic_perform (cr, MOSAIC_TESSELLATE | MOSAIC_CURVE_TO, width, height, loops); } +cairo_bool_t +mosaic_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "mosaic", NULL); +} + void mosaic (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "mosaic", NULL)) - return; - cairo_perf_run (perf, "mosaic-fill-curves", mosaic_fill_curves, NULL); cairo_perf_run (perf, "mosaic-fill-lines", mosaic_fill_lines, NULL); cairo_perf_run (perf, "mosaic-tessellate-curves", mosaic_tessellate_curves, NULL); diff --git a/perf/micro/paint-with-alpha.c b/perf/micro/paint-with-alpha.c index 3c1f69ee6..047e35c43 100644 --- a/perf/micro/paint-with-alpha.c +++ b/perf/micro/paint-with-alpha.c @@ -44,12 +44,15 @@ count_paint_with_alpha (cairo_t *cr, int width, int height) return width * height / 1e6; /* Mpix/s */ } +cairo_bool_t +paint_with_alpha_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "paint-with-alpha", NULL); +} + void paint_with_alpha (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "paint-with-alpha", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "paint-with-alpha", do_paint_with_alpha, count_paint_with_alpha); diff --git a/perf/micro/paint.c b/perf/micro/paint.c index dc7e0a0fd..2a59a45e0 100644 --- a/perf/micro/paint.c +++ b/perf/micro/paint.c @@ -44,11 +44,14 @@ count_paint (cairo_t *cr, int width, int height) return width * height / 1e6; /* Mpix/s */ } +cairo_bool_t +paint_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "paint", NULL); +} + void paint (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "paint", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "paint", do_paint, count_paint); } diff --git a/perf/micro/pattern_create_radial.c b/perf/micro/pattern_create_radial.c index 13260bb57..f236ef548 100644 --- a/perf/micro/pattern_create_radial.c +++ b/perf/micro/pattern_create_radial.c @@ -79,14 +79,17 @@ do_pattern_create_radial (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +pattern_create_radial_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "pattern-create-radial", NULL); +} + void pattern_create_radial (cairo_perf_t *perf, cairo_t *cr, int width, int height) { int i; - if (! cairo_perf_can_run (perf, "pattern-create-radial", NULL)) - return; - srand (time (0)); for (i = 0; i < RADIALS_COUNT; i++) { diff --git a/perf/micro/pixel.c b/perf/micro/pixel.c new file mode 100644 index 000000000..12b8f47b2 --- /dev/null +++ b/perf/micro/pixel.c @@ -0,0 +1,177 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +/* Measure the overhead in setting a single pixel */ + +#include "cairo-perf.h" + +static cairo_time_t +pixel_paint (cairo_t *cr, int width, int height, int loops) +{ + cairo_perf_timer_start (); + + while (loops--) + cairo_paint (cr); + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_mask (cairo_t *cr, int width, int height, int loops) +{ + cairo_surface_t *mask; + cairo_t *cr2; + + mask = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_ALPHA, + 1, 1); + cr2 = cairo_create (mask); + cairo_set_source_rgb (cr2, 1,1,1); + cairo_paint (cr2); + cairo_destroy (cr2); + + cairo_perf_timer_start (); + + while (loops--) + cairo_mask_surface (cr, mask, 0, 0); + + cairo_perf_timer_stop (); + + cairo_surface_destroy (mask); + + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_rectangle (cairo_t *cr, int width, int height, int loops) +{ + cairo_new_path (cr); + cairo_rectangle (cr, 0, 0, 1, 1); + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_subrectangle (cairo_t *cr, int width, int height, int loops) +{ + cairo_new_path (cr); + cairo_rectangle (cr, 0.1, 0.1, .8, .8); + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_triangle (cairo_t *cr, int width, int height, int loops) +{ + cairo_new_path (cr); + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, 1, 1); + cairo_line_to (cr, 0, 1); + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_circle (cairo_t *cr, int width, int height, int loops) +{ + cairo_new_path (cr); + cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI); + + cairo_perf_timer_start (); + + while (loops--) + cairo_fill_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + return cairo_perf_timer_elapsed (); +} + +static cairo_time_t +pixel_stroke (cairo_t *cr, int width, int height, int loops) +{ + cairo_set_line_width (cr, 1.); + cairo_new_path (cr); + cairo_move_to (cr, 0, 0.5); + cairo_line_to (cr, 1, 0.5); + + cairo_perf_timer_start (); + + while (loops--) + cairo_stroke_preserve (cr); + + cairo_perf_timer_stop (); + + cairo_new_path (cr); + return cairo_perf_timer_elapsed (); +} + +cairo_bool_t +pixel_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "pixel", NULL); +} + +void +pixel (cairo_perf_t *perf, cairo_t *cr, int width, int height) +{ + cairo_set_source_rgb (cr, 1., 1., 1.); + + cairo_perf_run (perf, "pixel-paint", pixel_paint, NULL); + cairo_perf_run (perf, "pixel-mask", pixel_mask, NULL); + cairo_perf_run (perf, "pixel-rectangle", pixel_rectangle, NULL); + cairo_perf_run (perf, "pixel-subrectangle", pixel_subrectangle, NULL); + cairo_perf_run (perf, "pixel-triangle", pixel_triangle, NULL); + cairo_perf_run (perf, "pixel-circle", pixel_circle, NULL); + cairo_perf_run (perf, "pixel-stroke", pixel_stroke, NULL); +} diff --git a/perf/micro/pythagoras-tree.c b/perf/micro/pythagoras-tree.c index 6e823a8f6..9d3ca1155 100644 --- a/perf/micro/pythagoras-tree.c +++ b/perf/micro/pythagoras-tree.c @@ -82,11 +82,14 @@ do_pythagoras_tree (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +pythagoras_tree_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "pythagoras-tree", NULL); +} + void pythagoras_tree (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "pythagoras-tree", NULL)) - return; - cairo_perf_run (perf, "pythagoras-tree", do_pythagoras_tree, NULL); } diff --git a/perf/micro/rectangles.c b/perf/micro/rectangles.c index 891572b60..9228a4efa 100644 --- a/perf/micro/rectangles.c +++ b/perf/micro/rectangles.c @@ -95,14 +95,17 @@ do_rectangle (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +rectangles_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "rectangles", NULL); +} + void rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height) { int i; - if (! cairo_perf_can_run (perf, "rectangles", NULL)) - return; - srand (8478232); for (i = 0; i < RECTANGLE_COUNT; i++) { diff --git a/perf/micro/rounded-rectangles.c b/perf/micro/rounded-rectangles.c index 598524925..1e432dd1f 100644 --- a/perf/micro/rounded-rectangles.c +++ b/perf/micro/rounded-rectangles.c @@ -119,14 +119,17 @@ do_rectangles_once (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +rounded_rectangles_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "rounded-rectangles", NULL); +} + void rounded_rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height) { int i; - if (! cairo_perf_can_run (perf, "rounded-rectangles", NULL)) - return; - srand (8478232); for (i = 0; i < RECTANGLE_COUNT; i++) { rects[i].x = rand () % width; diff --git a/perf/micro/sierpinski.c b/perf/micro/sierpinski.c new file mode 100644 index 000000000..c6f5fadc2 --- /dev/null +++ b/perf/micro/sierpinski.c @@ -0,0 +1,94 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +#include "cairo-perf.h" + +static const double m_1_sqrt_3 = 0.577359269; + +static void +T (cairo_t *cr, int size) +{ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, size, 0); + cairo_line_to (cr, size/2, size*m_1_sqrt_3); + + size /= 2; + if (size >= 4) { + T (cr, size); + cairo_save (cr); { + cairo_translate (cr, size, 0); + T (cr, size); + } cairo_restore (cr); + cairo_save (cr); { + cairo_translate (cr, size/2, size*m_1_sqrt_3); + T (cr, size); + } cairo_restore (cr); + } +} + +static cairo_time_t +draw (cairo_t *cr, int width, int height, int loops) +{ + int t_height = height/2; + int t_width = t_height / m_1_sqrt_3; + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_line_width (cr, 1.); + + cairo_perf_timer_start (); + + while (loops--) { + cairo_save (cr); + T (cr, t_width); + + cairo_translate (cr, 0, height); + cairo_scale (cr, 1, -1); + + T (cr, t_width); + + cairo_stroke (cr); + cairo_restore (cr); + } + + cairo_perf_timer_stop (); + + return cairo_perf_timer_elapsed (); +} + +cairo_bool_t +sierpinski_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "sierpinski", NULL); +} + +void +sierpinski (cairo_perf_t *perf, cairo_t *cr, int width, int height) +{ + cairo_perf_run (perf, "sierpinski", draw, NULL); +} diff --git a/perf/micro/spiral.c b/perf/micro/spiral.c index 85d058018..87dbcb529 100644 --- a/perf/micro/spiral.c +++ b/perf/micro/spiral.c @@ -326,12 +326,15 @@ draw_spiral_stroke_na (cairo_t *cr, int width, int height, int loops) width, height, loops); } +cairo_bool_t +spiral_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "spiral", NULL); +} + void spiral (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "spiral", NULL)) - return; - cairo_perf_run (perf, "spiral-box-nonalign-evenodd-fill", draw_spiral_eo_na_box, NULL); cairo_perf_run (perf, "spiral-box-nonalign-nonzero-fill", draw_spiral_nz_na_box, NULL); cairo_perf_run (perf, "spiral-box-pixalign-evenodd-fill", draw_spiral_eo_pa_box, NULL); diff --git a/perf/micro/stroke.c b/perf/micro/stroke.c index 8d7dc5274..4b2954770 100644 --- a/perf/micro/stroke.c +++ b/perf/micro/stroke.c @@ -86,12 +86,15 @@ do_strokes (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +stroke_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "stroke", NULL); +} + void stroke (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "stroke", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "stroke", do_stroke, NULL); cairo_perf_cover_sources_and_operators (perf, "strokes", do_strokes, NULL); } diff --git a/perf/micro/subimage_copy.c b/perf/micro/subimage_copy.c index eb04154a1..e749c062f 100644 --- a/perf/micro/subimage_copy.c +++ b/perf/micro/subimage_copy.c @@ -52,15 +52,18 @@ do_subimage_copy (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +subimage_copy_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "subimage-copy", NULL); +} + void subimage_copy (cairo_perf_t *perf, cairo_t *cr, int width, int height) { cairo_surface_t *image; cairo_t *cr2; - if (! cairo_perf_can_run (perf, "subimage-copy", NULL)) - return; - cairo_set_source_rgb (cr, 0, 0, 1); /* blue */ cairo_paint (cr); diff --git a/perf/micro/tessellate.c b/perf/micro/tessellate.c index 38c8b86fd..b6277fe63 100644 --- a/perf/micro/tessellate.c +++ b/perf/micro/tessellate.c @@ -141,12 +141,15 @@ tessellate_256 (cairo_t *cr, int width, int height, int loops) return do_tessellate (cr, 256, loops); } +cairo_bool_t +tessellate_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "tessellate", NULL); +} + void tessellate (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "tessellate", NULL)) - return; - cairo_perf_run (perf, "tessellate-16", tessellate_16, NULL); cairo_perf_run (perf, "tessellate-64", tessellate_64, NULL); cairo_perf_run (perf, "tessellate-256", tessellate_256, NULL); diff --git a/perf/micro/text.c b/perf/micro/text.c index bd2ca7b4c..cdb319936 100644 --- a/perf/micro/text.c +++ b/perf/micro/text.c @@ -56,11 +56,14 @@ do_text (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +text_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "text", NULL); +} + void text (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "text", NULL)) - return; - cairo_perf_cover_sources_and_operators (perf, "text", do_text, NULL); } diff --git a/perf/micro/twin.c b/perf/micro/twin.c index cc6f02044..99433bd65 100644 --- a/perf/micro/twin.c +++ b/perf/micro/twin.c @@ -43,14 +43,17 @@ do_twin (cairo_t *cr, return cairo_perf_timer_elapsed (); } +cairo_bool_t +twin_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "twin", NULL); +} + void twin (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "twin", NULL)) - return; - cairo_perf_run (perf, "twin", do_twin, NULL); } diff --git a/perf/micro/unaligned-clip.c b/perf/micro/unaligned-clip.c index d71549cc2..41e327f1e 100644 --- a/perf/micro/unaligned-clip.c +++ b/perf/micro/unaligned-clip.c @@ -60,11 +60,14 @@ do_unaligned_clip (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +unaligned_clip_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "unaligned-clip", NULL); +} + void unaligned_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "unaligned-clip", NULL)) - return; - cairo_perf_run (perf, "unaligned-clip", do_unaligned_clip, NULL); } diff --git a/perf/micro/wave.c b/perf/micro/wave.c index 88029f8cb..f6e6f74a3 100644 --- a/perf/micro/wave.c +++ b/perf/micro/wave.c @@ -102,11 +102,14 @@ do_wave (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +wave_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "wave", NULL); +} + void wave (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "wave", NULL)) - return; - cairo_perf_run (perf, "wave", do_wave, NULL); } diff --git a/perf/micro/wide-fills.c b/perf/micro/wide-fills.c index a9beeeb1f..0747e6e67 100644 --- a/perf/micro/wide-fills.c +++ b/perf/micro/wide-fills.c @@ -170,12 +170,15 @@ do_wide_fills (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +wide_fills_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "wide-fills", NULL); +} + void wide_fills (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "wide-fills", NULL)) - return; - cairo_perf_run (perf, "wide-fills-halign", do_wide_fills_ha, NULL); cairo_perf_run (perf, "wide-fills-valign", do_wide_fills_va, NULL); cairo_perf_run (perf, "wide-fills-horizontal", do_wide_fills_h, NULL); diff --git a/perf/micro/wide-strokes.c b/perf/micro/wide-strokes.c index e6c27dabb..141309148 100644 --- a/perf/micro/wide-strokes.c +++ b/perf/micro/wide-strokes.c @@ -169,12 +169,15 @@ do_wide_strokes (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +wide_strokes_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "wide-strokes", NULL); +} + void wide_strokes (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "wide-strokes", NULL)) - return; - cairo_set_source_rgb (cr, 1., 1., 1.); cairo_perf_run (perf, "wide-strokes-halign", do_wide_strokes_ha, NULL); diff --git a/perf/micro/world-map.c b/perf/micro/world-map.c index cb0aeee6b..ff22eebf3 100644 --- a/perf/micro/world-map.c +++ b/perf/micro/world-map.c @@ -134,12 +134,15 @@ do_world_map_both (cairo_t *cr, int width, int height, int loops) return do_world_map (cr, width, height, loops, FILL | STROKE); } +cairo_bool_t +world_map_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "world-map", NULL); +} + void world_map (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "world-map", NULL)) - return; - cairo_perf_run (perf, "world-map-stroke", do_world_map_stroke, NULL); cairo_perf_run (perf, "world-map-fill", do_world_map_fill, NULL); cairo_perf_run (perf, "world-map", do_world_map_both, NULL); diff --git a/perf/micro/zrusin.c b/perf/micro/zrusin.c index c4dccbfa1..7d8b004bf 100644 --- a/perf/micro/zrusin.c +++ b/perf/micro/zrusin.c @@ -84,11 +84,15 @@ zrusin_another_fill (cairo_t *cr, int width, int height, int loops) return cairo_perf_timer_elapsed (); } +cairo_bool_t +zrusin_enabled (cairo_perf_t *perf) +{ + return cairo_perf_can_run (perf, "zrusin", NULL); +} + void zrusin (cairo_perf_t *perf, cairo_t *cr, int width, int height) { - if (! cairo_perf_can_run (perf, "zrusin", NULL)) - return; cairo_perf_run (perf, "zrusin-another-tessellate", zrusin_another_tessellate, NULL); cairo_perf_run (perf, "zrusin-another-fill", zrusin_another_fill, NULL); diff --git a/src/Makefile.sources b/src/Makefile.sources index 0b820f8fc..d23d504f8 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -54,6 +54,7 @@ cairo_private = \ cairoint.h \ cairo-analysis-surface-private.h \ cairo-arc-private.h \ + cairo-array-private.h \ cairo-atomic-private.h \ cairo-backend-private.h \ cairo-box-private.h \ @@ -62,6 +63,7 @@ cairo_private = \ cairo-clip-private.h \ cairo-combsort-private.h \ cairo-compiler-private.h \ + cairo-compositor-private.h \ cairo-contour-private.h \ cairo-composite-rectangles-private.h \ cairo-default-context-private.h \ @@ -97,10 +99,11 @@ cairo_private = \ cairo-scaled-font-private.h \ cairo-slope-private.h \ cairo-spans-private.h \ + cairo-spans-compositor-private.h \ cairo-stroke-dash-private.h \ - cairo-surface-fallback-private.h \ cairo-surface-private.h \ cairo-surface-clipper-private.h \ + cairo-surface-fallback-private.h \ cairo-surface-observer-private.h \ cairo-surface-offset-private.h \ cairo-surface-subsurface-private.h \ @@ -108,6 +111,8 @@ cairo_private = \ cairo-surface-wrapper-private.h \ cairo-time-private.h \ cairo-types-private.h \ + cairo-traps-private.h \ + cairo-tristrip-private.h \ cairo-user-font-private.h \ cairo-wideint-private.h \ cairo-wideint-type-private.h \ @@ -134,11 +139,13 @@ cairo_sources = \ cairo-clip-surface.c \ cairo-color.c \ cairo-composite-rectangles.c \ + cairo-compositor.c \ cairo-contour.c \ cairo-debug.c \ cairo-default-context.c \ cairo-device.c \ cairo-error.c \ + cairo-fallback-compositor.c \ cairo-fixed.c \ cairo-font-face.c \ cairo-font-face-twin.c \ @@ -149,14 +156,19 @@ cairo_sources = \ 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-matrix.c \ + cairo-mask-compositor.c \ cairo-mesh-pattern-rasterizer.c \ cairo-mime-surface.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 \ @@ -168,6 +180,7 @@ cairo_sources = \ cairo-path-stroke.c \ cairo-path-stroke-boxes.c \ cairo-path-stroke-polygon.c \ + cairo-path-stroke-tristrip.c \ cairo-pattern.c \ cairo-pen.c \ cairo-polygon.c \ @@ -181,12 +194,13 @@ cairo_sources = \ cairo-scaled-font.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-fallback.c \ cairo-surface-clipper.c \ + cairo-surface-fallback.c \ cairo-surface-observer.c \ cairo-surface-offset.c \ cairo-surface-snapshot.c \ @@ -195,8 +209,12 @@ cairo_sources = \ cairo-system.c \ cairo-time.c \ cairo-tor-scan-converter.c \ + cairo-tor22-scan-converter.c \ + cairo-clip-tor-scan-converter.c \ cairo-toy-font-face.c \ cairo-traps.c \ + cairo-tristrip.c \ + cairo-traps-compositor.c \ cairo-unicode.c \ cairo-user-font.c \ cairo-version.c \ @@ -253,18 +271,15 @@ cairo_ft_sources = cairo-ft-font.c # These are private, even though they look like public headers cairo_test_surfaces_private = \ - test-fallback-surface.h \ - test-fallback16-surface.h \ - test-null-surface.h \ + test-compositor-surface.h \ + test-null-compositor-surface.h \ test-paginated-surface.h \ - test-wrapping-surface.h \ $(NULL) cairo_test_surfaces_sources = \ - test-fallback-surface.c \ - test-fallback16-surface.c \ - test-null-surface.c \ + test-compositor-surface.c \ + test-null-compositor-surface.c \ + test-base-compositor-surface.c \ test-paginated-surface.c \ - test-wrapping-surface.c \ $(NULL) cairo_xlib_headers = cairo-xlib.h @@ -275,7 +290,11 @@ cairo_xlib_private = \ $(NULL) cairo_xlib_sources = \ cairo-xlib-display.c \ + cairo-xlib-core-compositor.c \ + cairo-xlib-fallback-compositor.c \ + cairo-xlib-render-compositor.c \ cairo-xlib-screen.c \ + cairo-xlib-source.c \ cairo-xlib-surface.c \ cairo-xlib-visual.c \ cairo-xlib-xcb-surface.c \ @@ -343,7 +362,10 @@ cairo_gl_sources = cairo-gl-composite.c \ cairo-gl-glyphs.c \ cairo-gl-gradient.c \ cairo-gl-info.c \ + cairo-gl-operand.c \ cairo-gl-shaders.c \ + cairo-gl-spans-compositor.c \ + cairo-gl-traps-compositor.c \ cairo-gl-surface.c cairo_glesv2_headers = $(cairo_gl_headers) diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c index beb9a07ba..e1c28b658 100644 --- a/src/cairo-analysis-surface.c +++ b/src/cairo-analysis-surface.c @@ -144,7 +144,7 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, cairo_analysis_surface_t *tmp; cairo_surface_t *source, *proxy; cairo_matrix_t p2d; - cairo_status_t status; + cairo_status_t status, analysis_status; assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE); surface_pattern = (const cairo_surface_pattern_t *) pattern; @@ -175,11 +175,16 @@ _analyze_recording_surface_pattern (cairo_analysis_surface_t *surface, if (_cairo_surface_is_subsurface (source)) source = _cairo_surface_subsurface_get_target (source); - status = _cairo_recording_surface_replay_and_create_regions (source, &tmp->base); + 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); - return status; + if (unlikely (status)) + return status; + + return analysis_status; } static cairo_int_status_t @@ -548,8 +553,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *remaining_glyphs) + const cairo_clip_t *clip) { cairo_analysis_surface_t *surface = abstract_surface; cairo_int_status_t status, backend_status; @@ -562,8 +566,7 @@ _cairo_analysis_surface_show_glyphs (void *abstract_surface, source, glyphs, num_glyphs, scaled_font, - clip, - remaining_glyphs); + clip); if (_cairo_int_status_is_error (backend_status)) return backend_status; } @@ -652,21 +655,14 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface, 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); + 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) @@ -701,35 +697,26 @@ static const cairo_surface_backend_t cairo_analysis_surface_backend = { NULL, /* create_similar_image */ NULL, /* map_to_image */ NULL, /* unmap */ + 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, - _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_show_glyphs, _cairo_analysis_surface_has_show_text_glyphs, _cairo_analysis_surface_show_text_glyphs }; @@ -891,48 +878,38 @@ typedef cairo_int_status_t cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const 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 */ - _cairo_default_context_create, /* XXX */ + NULL, /* only accessed through the surface functions */ NULL, /* create_similar */ NULL, /* create similar image */ NULL, /* map to image */ NULL, /* unmap image*/ + 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 */ - (_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 */ + (_show_glyphs_func) _return_success, /* show_glyphs */ NULL, /* has_show_text_glyphs */ NULL /* show_text_glyphs */ }; diff --git a/src/cairo-array-private.h b/src/cairo-array-private.h new file mode 100644 index 000000000..35b29e5fc --- /dev/null +++ b/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 + */ + +#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 */ diff --git a/src/cairo-array.c b/src/cairo-array.c index 52b283ff8..4f3c082ff 100644 --- a/src/cairo-array.c +++ b/src/cairo-array.c @@ -37,6 +37,7 @@ */ #include "cairoint.h" +#include "cairo-array-private.h" #include "cairo-error-private.h" /** @@ -385,11 +386,11 @@ _cairo_user_data_array_fini (cairo_user_data_array_t *array) 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); diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index 9f5888caa..327fed1d9 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -79,6 +79,7 @@ _cairo_atomic_ptr_get (void **x) #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 @@ typedef AO_t cairo_atomic_int_t; # 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 @@ typedef int32_t cairo_atomic_int_t; # 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 @@ typedef cairo_atomic_intptr_t cairo_atomic_int_t; 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); diff --git a/src/cairo-bentley-ottmann-rectangular.c b/src/cairo-bentley-ottmann-rectangular.c index 519117869..aec7ae417 100644 --- a/src/cairo-bentley-ottmann-rectangular.c +++ b/src/cairo-bentley-ottmann-rectangular.c @@ -42,6 +42,7 @@ #include "cairo-error-private.h" #include "cairo-combsort-private.h" #include "cairo-list-private.h" +#include "cairo-traps-private.h" #include @@ -740,7 +741,7 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, 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; + rectangle_t **rectangles_chain = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i, j, y_min, y_max; @@ -789,13 +790,15 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, y_max = _cairo_fixed_integer_floor (y_max) + 1; y_max -= y_min; - 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); + 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*)); } - memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; @@ -839,28 +842,38 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; - 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]; + 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++; } } - 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) + 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); - 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+2, j-2, + status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, fill_rule, FALSE, out); if (rectangles != stack_rectangles) diff --git a/src/cairo-bentley-ottmann-rectilinear.c b/src/cairo-bentley-ottmann-rectilinear.c index a3eb4901a..1edeeb525 100644 --- a/src/cairo-bentley-ottmann-rectilinear.c +++ b/src/cairo-bentley-ottmann-rectilinear.c @@ -41,6 +41,7 @@ #include "cairo-boxes-private.h" #include "cairo-combsort-private.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; @@ -436,74 +437,6 @@ _cairo_bentley_ottmann_tessellate_rectilinear (cairo_bo_event_t **start_events return CAIRO_STATUS_SUCCESS; } -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, diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c index 70d4482f0..634da6f1a 100644 --- a/src/cairo-bentley-ottmann.c +++ b/src/cairo-bentley-ottmann.c @@ -41,6 +41,7 @@ #include "cairo-error-private.h" #include "cairo-freelist-private.h" #include "cairo-combsort-private.h" +#include "cairo-traps-private.h" #define DEBUG_PRINT_STATE 0 #define DEBUG_EVENTS 0 @@ -1301,7 +1302,14 @@ event_log (const char *fmt, ...) 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)) + unsigned 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))) return TRUE; if (_slope_compare (a, b)) @@ -1310,8 +1318,9 @@ edges_colinear (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b) /* 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 */ + return ((p >> 1) & p) != 0; } 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, @@ -1377,8 +1386,9 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, if (left->deferred_trap.right == right) return CAIRO_STATUS_SUCCESS; + 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; @@ -1390,7 +1400,7 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, return status; } - if (right != NULL && ! edges_colinear (left, right)) { + if (! edges_colinear (left, right)) { left->deferred_trap.top = top; left->deferred_trap.right = right; @@ -1406,112 +1416,50 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left, } static inline cairo_status_t -_active_edges_to_traps (cairo_bo_edge_t *left, - int32_t top, - cairo_fill_rule_t fill_rule, - cairo_traps_t *traps) +_active_edges_to_traps (cairo_bo_edge_t *pos, + int32_t top, + unsigned mask, + cairo_traps_t *traps) { - cairo_bo_edge_t *right; + cairo_bo_edge_t *left; cairo_status_t status; + 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 = 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)) { - /* continuation on left */ - left->deferred_trap = right->deferred_trap; - right->deferred_trap.right = NULL; - } - } - - /* 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; - - /* skip co-linear edges */ - next = right->next; - if (next != NULL) - skip = edges_colinear (right, next); - - if (! skip) - break; - } - - right = right->next; + in_out = 0; + left = pos; + while (pos != NULL) { + if (pos != left && pos->deferred_trap.right) { + if (edges_colinear (left, pos)) { + /* continuation on left */ + assert (left->deferred_trap.right == NULL); + left->deferred_trap = pos->deferred_trap; + pos->deferred_trap.right = NULL; + } else { + status = _cairo_bo_edge_end_trap (pos, top, traps); + if (unlikely (status)) + return status; } - - 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; } - } 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; - } + in_out += pos->edge.dir; + if ((in_out & mask) == 0) { + /* skip co-linear edges */ + if (pos->next == NULL || ! edges_colinear (pos, pos->next)) { + status = _cairo_bo_edge_start_or_continue_trap (left, pos, + top, traps); + if (unlikely (status)) + return status; - right = right->next; + left = pos->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; } return CAIRO_STATUS_SUCCESS; @@ -1524,7 +1472,7 @@ _active_edges_to_traps (cairo_bo_edge_t *left, 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) { @@ -1536,6 +1484,12 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_event_t **start_events, 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; @@ -1781,7 +1735,7 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, 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) + 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); diff --git a/src/cairo-botor-scan-converter.c b/src/cairo-botor-scan-converter.c index 0778a5dcd..cbb752f0f 100644 --- a/src/cairo-botor-scan-converter.c +++ b/src/cairo-botor-scan-converter.c @@ -1397,6 +1397,7 @@ render_rows (cairo_botor_scan_converter_t *self, 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,12 +1414,14 @@ render_rows (cairo_botor_scan_converter_t *self, 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; } if (cover && prev_x < self->xmax) { spans[num_spans].x = self->xmax; + spans[num_spans].inverse = 1; spans[num_spans].coverage = 0; ++num_spans; } @@ -2179,8 +2182,6 @@ _cairo_botor_scan_converter_init (cairo_botor_scan_converter_t *self, 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; diff --git a/src/cairo-box-private.h b/src/cairo-box-private.h index 3bced9966..d6b994127 100644 --- a/src/cairo-box-private.h +++ b/src/cairo-box-private.h @@ -37,6 +37,7 @@ #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, @@ -47,6 +48,15 @@ _cairo_box_set (cairo_box_t *box, 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, @@ -63,13 +73,49 @@ _cairo_box_add_point (cairo_box_t *box, 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 (cairo_box_t *box, +_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 */ diff --git a/src/cairo-boxes-intersect.c b/src/cairo-boxes-intersect.c index 230c6f018..dd4c2410f 100644 --- a/src/cairo-boxes-intersect.c +++ b/src/cairo-boxes-intersect.c @@ -535,19 +535,44 @@ _cairo_boxes_intersect_with_box (const cairo_boxes_t *boxes, const cairo_box_t *box, cairo_boxes_t *out) { - const struct _cairo_boxes_chunk *chunk; cairo_status_t status; - int i; + 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; + _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; + } } } diff --git a/src/cairo-boxes-private.h b/src/cairo-boxes-private.h index 890d1b2d2..57d1228b2 100644 --- a/src/cairo-boxes-private.h +++ b/src/cairo-boxes-private.h @@ -42,11 +42,14 @@ 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; @@ -69,6 +72,10 @@ _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, @@ -81,7 +88,7 @@ _cairo_boxes_add (cairo_boxes_t *boxes, 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, @@ -104,6 +111,11 @@ _cairo_boxes_intersect (const cairo_boxes_t *a, 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 void _cairo_boxes_fini (cairo_boxes_t *boxes); diff --git a/src/cairo-boxes.c b/src/cairo-boxes.c index 130a44c76..182601a73 100644 --- a/src/cairo-boxes.c +++ b/src/cairo-boxes.c @@ -33,6 +33,7 @@ #include "cairoint.h" +#include "cairo-box-private.h" #include "cairo-boxes-private.h" #include "cairo-error-private.h" @@ -52,6 +53,16 @@ _cairo_boxes_init (cairo_boxes_t *boxes) boxes->is_pixel_aligned = TRUE; } +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) @@ -154,13 +165,8 @@ _cairo_boxes_add_internal (cairo_boxes_t *boxes, 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 @@ -261,38 +267,34 @@ _cairo_boxes_add (cairo_boxes_t *boxes, 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; if (boxes->num_boxes == 0) { - extents->x = extents->y = extents->width = extents->height = 0; + box->p1.x = box->p1.y = box->p2.x = box->p2.y = 0; return; } - box.p1.y = box.p1.x = INT_MAX; - box.p2.y = box.p2.x = INT_MIN; - + 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 @@ -354,17 +356,38 @@ _cairo_boxes_fini (cairo_boxes_t *boxes) } } +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; +} + + void _cairo_debug_print_boxes (FILE *stream, const cairo_boxes_t *boxes) { - cairo_rectangle_int_t extents; const struct _cairo_boxes_chunk *chunk; + cairo_box_t extents; int i; _cairo_boxes_extents (boxes, &extents); - fprintf (stream, "boxes x %d: (%d, %d) x (%d, %d)\n", + fprintf (stream, "boxes x %d: (%f, %f) x (%f, %f)\n", boxes->num_boxes, - extents.x, extents.y, extents.width, extents.height); + _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++) { diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c index 6c7179585..3ee3ef39a 100644 --- a/src/cairo-cff-subset.c +++ b/src/cairo-cff-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 @@ -2897,7 +2899,7 @@ cairo_bool_t _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) { const cairo_scaled_font_backend_t *backend; - cairo_status_t status; + cairo_int_status_t status; unsigned char *data; unsigned long data_length; unsigned char *current_ptr; @@ -2916,7 +2918,7 @@ _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) /* 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_STATUS_SUCCESS) + 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) { data = malloc (data_length); if (unlikely (data == NULL)) { @@ -2933,7 +2935,7 @@ _cairo_cff_scaled_font_is_cid_cff (cairo_scaled_font_t *scaled_font) if (status == CAIRO_INT_STATUS_UNSUPPORTED && backend->load_type1_data && (status = backend->load_type1_data (scaled_font, - 0, NULL, &data_length)) == CAIRO_STATUS_SUCCESS) + 0, NULL, &data_length)) == CAIRO_INT_STATUS_SUCCESS) { data = malloc (data_length); if (unlikely (data == NULL)) { diff --git a/src/cairo-clip-boxes.c b/src/cairo-clip-boxes.c index e18c2f8ed..3b8265b9c 100644 --- a/src/cairo-clip-boxes.c +++ b/src/cairo-clip-boxes.c @@ -40,6 +40,8 @@ */ #include "cairoint.h" + +#include "cairo-box-private.h" #include "cairo-clip-private.h" #include "cairo-error-private.h" #include "cairo-freed-pool-private.h" @@ -62,7 +64,6 @@ pot (int v) return v; } - static cairo_bool_t _cairo_clip_contains_rectangle_box (const cairo_clip_t *clip, const cairo_rectangle_int_t *rect, @@ -90,7 +91,6 @@ _cairo_clip_contains_rectangle_box (const cairo_clip_t *clip, } /* Check for a clip-box that wholly contains the rectangle */ - assert (clip->num_boxes); 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 && @@ -176,6 +176,8 @@ _cairo_clip_intersect_rectangle_box (cairo_clip_t *clip, 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; } @@ -269,6 +271,7 @@ _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)) @@ -301,10 +304,11 @@ _cairo_clip_intersect_boxes (cairo_clip_t *clip, } else { clip->boxes = _cairo_boxes_to_array (boxes, &clip->num_boxes, TRUE); } - _cairo_boxes_extents (boxes, &extents); + _cairo_boxes_extents (boxes, &limits); if (boxes == &clip_boxes) _cairo_boxes_fini (&clip_boxes); + _cairo_box_round_to_rectangle (&limits, &extents); if (clip->path == NULL) clip->extents = extents; else if (! _cairo_rectangle_intersect (&clip->extents, &extents)) @@ -557,37 +561,10 @@ _cairo_clip_reduce_for_composite (const cairo_clip_t *clip, return _cairo_clip_reduce_to_rectangle (clip, r); } -cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t *clip, - cairo_boxes_t *boxes) -{ - _cairo_boxes_init_for_array (boxes, clip->boxes, clip->num_boxes); - - if (clip->path == NULL) { - cairo_box_t *src = clip->boxes; - int i; - - clip->boxes = _cairo_malloc_ab (clip->num_boxes, sizeof (cairo_box_t)); - if (clip->boxes == NULL) { - clip->boxes = src; - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < clip->num_boxes; i++) { - clip->boxes[i].p1.x = _cairo_fixed_floor (src[i].p1.x); - clip->boxes[i].p1.y = _cairo_fixed_floor (src[i].p1.y); - clip->boxes[i].p2.x = _cairo_fixed_ceil (src[i].p2.x); - clip->boxes[i].p2.y = _cairo_fixed_ceil (src[i].p2.y); - } - } - - return CAIRO_STATUS_SUCCESS; - -} - 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); @@ -603,7 +580,8 @@ _cairo_clip_from_boxes (const cairo_boxes_t *boxes) return _cairo_clip_set_all_clipped (clip); } - _cairo_boxes_extents (boxes, &clip->extents); + _cairo_boxes_extents (boxes, &extents); + _cairo_box_round_to_rectangle (&extents, &clip->extents); return clip; } diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h index 3ebcb0e2d..7cbef24a7 100644 --- a/src/cairo-clip-private.h +++ b/src/cairo-clip-private.h @@ -40,6 +40,7 @@ #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" @@ -160,14 +161,30 @@ _cairo_clip_get_extents (const cairo_clip_t *clip); cairo_private cairo_surface_t * _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 (const cairo_clip_t *clip, cairo_surface_t *dst, int dst_x, int dst_y); -cairo_private cairo_status_t -_cairo_clip_to_boxes (cairo_clip_t *clip, - cairo_boxes_t *boxes); +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; +} cairo_private cairo_clip_t * _cairo_clip_from_boxes (const cairo_boxes_t *boxes); diff --git a/src/cairo-clip-region.c b/src/cairo-clip-region.c index 28a0d4bbd..e3f4891e3 100644 --- a/src/cairo-clip-region.c +++ b/src/cairo-clip-region.c @@ -105,10 +105,16 @@ _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; + return FALSE; + + if (clip->num_boxes == 0) + return TRUE; if (clip->region == NULL) _cairo_clip_extract_region ((cairo_clip_t *) clip); diff --git a/src/cairo-clip-surface.c b/src/cairo-clip-surface.c index 79e6a6274..e1e931294 100644 --- a/src/cairo-clip-surface.c +++ b/src/cairo-clip-surface.c @@ -103,8 +103,7 @@ _cairo_clip_get_surface (const cairo_clip_t *clip, CAIRO_CONTENT_ALPHA, clip->extents.width, clip->extents.height, - CAIRO_COLOR_TRANSPARENT, - TRUE); + CAIRO_COLOR_WHITE); if (unlikely (surface->status)) return surface; @@ -114,12 +113,7 @@ _cairo_clip_get_surface (const cairo_clip_t *clip, copy_path = copy->path; copy->path = NULL; - assert (copy->num_boxes); - status = _cairo_surface_paint (surface, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - copy); - + status = CAIRO_STATUS_SUCCESS; clip_path = copy_path; while (status == CAIRO_STATUS_SUCCESS && clip_path) { status = _cairo_surface_fill (surface, @@ -140,3 +134,32 @@ _cairo_clip_get_surface (const cairo_clip_t *clip, *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; +} diff --git a/src/cairo-clip-tor-scan-converter.c b/src/cairo-clip-tor-scan-converter.c new file mode 100644 index 000000000..e32a5a9d9 --- /dev/null +++ b/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 +#include +#include +#include +#include + +/* 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< +#include +#include + +/* 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); +} + diff --git a/src/cairo-clip.c b/src/cairo-clip.c index 6355a977b..44f4e7b2d 100644 --- a/src/cairo-clip.c +++ b/src/cairo-clip.c @@ -246,16 +246,9 @@ _cairo_clip_intersect_path (cairo_clip_t *clip, if (extents.width == 0 || extents.height == 0) return _cairo_clip_set_all_clipped (clip); - if (clip && ! _cairo_rectangle_intersect (&clip->extents, &extents)) - return _cairo_clip_set_all_clipped (clip); - - if (clip == NULL) { - clip = _cairo_clip_create (); - if (unlikely (clip == NULL)) - return _cairo_clip_set_all_clipped (clip); - - clip->extents = extents; - } + clip = _cairo_clip_intersect_rectangle (clip, &extents); + if (_cairo_clip_is_all_clipped (clip)) + return clip; clip_path = _cairo_clip_path_create (clip); if (unlikely (clip_path == NULL)) @@ -263,7 +256,7 @@ _cairo_clip_intersect_path (cairo_clip_t *clip, status = _cairo_path_fixed_init_copy (&clip_path->path, path); if (unlikely (status)) - return _cairo_clip_set_all_clipped (clip); + return _cairo_clip_set_all_clipped (clip); clip_path->fill_rule = fill_rule; clip_path->tolerance = tolerance; @@ -532,9 +525,10 @@ _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip) } fprintf (stream, "clip:\n"); - fprintf (stream, " extents: (%d, %d) x (%d, %d)", + fprintf (stream, " extents: (%d, %d) x (%d, %d), is-region? %d", clip->extents.x, clip->extents.y, - clip->extents.width, clip->extents.height); + 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++) { diff --git a/src/cairo-composite-rectangles-private.h b/src/cairo-composite-rectangles-private.h index f0553ab0b..8a06bf9f7 100644 --- a/src/cairo-composite-rectangles-private.h +++ b/src/cairo-composite-rectangles-private.h @@ -39,6 +39,7 @@ #include "cairo-types-private.h" #include "cairo-error-private.h" +#include "cairo-pattern-private.h" CAIRO_BEGIN_DECLS @@ -52,6 +53,9 @@ CAIRO_BEGIN_DECLS * */ 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 destination; @@ -60,19 +64,27 @@ struct _cairo_composite_rectangles { 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, - const cairo_rectangle_int_t *unbounded, + 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_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, @@ -80,7 +92,7 @@ _cairo_composite_rectangles_init_for_mask (cairo_composite_rectangles_t *extents cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, @@ -90,15 +102,31 @@ _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *exten cairo_private cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, 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, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, diff --git a/src/cairo-composite-rectangles.c b/src/cairo-composite-rectangles.c index a25e6d34f..a50a3474c 100644 --- a/src/cairo-composite-rectangles.c +++ b/src/cairo-composite-rectangles.c @@ -46,57 +46,93 @@ 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, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { - extents->clip = NULL; - extents->destination = *unbounded; - if (_cairo_clip_is_all_clipped (clip)) return FALSE; + extents->surface = surface; + extents->op = op; + + _cairo_surface_get_extents (surface, &extents->destination); + extents->clip = NULL; + extents->unbounded = extents->destination; - if (clip != NULL) { - if (! _cairo_rectangle_intersect (&extents->unbounded, - _cairo_clip_get_extents (clip))) - return FALSE; - } + 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? */ + return TRUE; } cairo_int_status_t _cairo_composite_rectangles_init_for_paint (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { - if (! _cairo_composite_rectangles_init (extents, unbounded, - op, source, clip)) + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } - extents->mask = *unbounded; + 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 (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; } @@ -117,6 +153,21 @@ _cairo_composite_rectangles_intersect (cairo_composite_rectangles_t *extents, if (_cairo_clip_is_all_clipped (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) { + _cairo_composite_rectangles_fini (extents); + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } + } + return CAIRO_STATUS_SUCCESS; } @@ -125,7 +176,6 @@ _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t const cairo_box_t *box) { cairo_rectangle_int_t mask; - cairo_int_status_t status; cairo_clip_t *clip; _cairo_box_round_to_rectangle (box, &mask); @@ -139,39 +189,70 @@ _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t _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; + extents->mask = mask; clip = extents->clip; - status = _cairo_composite_rectangles_intersect (extents, clip); + extents->clip = _cairo_clip_reduce_for_composite (clip, extents); if (clip != extents->clip) _cairo_clip_destroy (clip); - return status; + if (_cairo_clip_is_all_clipped (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, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t*surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { - if (! _cairo_composite_rectangles_init (extents, unbounded, - op, source, clip)) + if (! _cairo_composite_rectangles_init (extents, + 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, clip); } cairo_int_status_t _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, @@ -179,8 +260,8 @@ _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *exten const cairo_matrix_t *ctm, const cairo_clip_t *clip) { - if (! _cairo_composite_rectangles_init (extents, unbounded, - op, source, clip)) + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } @@ -192,14 +273,14 @@ _cairo_composite_rectangles_init_for_stroke (cairo_composite_rectangles_t *exten cairo_int_status_t _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *extents, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_clip_t *clip) { - if (! _cairo_composite_rectangles_init (extents, unbounded, - op, source, clip)) + if (! _cairo_composite_rectangles_init (extents, + surface, op, source, clip)) { return CAIRO_INT_STATUS_NOTHING_TO_DO; } @@ -209,9 +290,48 @@ _cairo_composite_rectangles_init_for_fill (cairo_composite_rectangles_t *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, - const cairo_rectangle_int_t *unbounded, + cairo_surface_t *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_scaled_font_t *scaled_font, @@ -222,8 +342,7 @@ _cairo_composite_rectangles_init_for_glyphs (cairo_composite_rectangles_t *exten { cairo_status_t status; - if (! _cairo_composite_rectangles_init (extents, unbounded, - 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. diff --git a/src/cairo-compositor-private.h b/src/cairo-compositor-private.h new file mode 100644 index 000000000..72507b218 --- /dev/null +++ b/src/cairo-compositor-private.h @@ -0,0 +1,355 @@ +/* -*- 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 + */ + +#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 + (*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 + (*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_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 */ diff --git a/src/cairo-compositor.c b/src/cairo-compositor.c new file mode 100644 index 000000000..cf943e734 --- /dev/null +++ b/src/cairo-compositor.c @@ -0,0 +1,213 @@ +/* -*- 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 + */ + +#include "cairoint.h" + +#include "cairo-compositor-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; + + 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); + + _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; + + 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); + + _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; + + 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); + + _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; + + 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); + + _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; + + 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); + + _cairo_composite_rectangles_fini (&extents); + + return status; +} diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c index 017eb7ba6..98cc6684d 100644 --- a/src/cairo-default-context.c +++ b/src/cairo-default-context.c @@ -158,8 +158,7 @@ _cairo_default_context_push_group (void *abstract_cr, cairo_content_t content) content, extents.width, extents.height, - CAIRO_COLOR_TRANSPARENT, - TRUE); + CAIRO_COLOR_TRANSPARENT); status = group_surface->status; if (unlikely (status)) goto bail; diff --git a/src/cairo-fallback-compositor.c b/src/cairo-fallback-compositor.c new file mode 100644 index 000000000..105859d62 --- /dev/null +++ b/src/cairo-fallback-compositor.c @@ -0,0 +1,174 @@ +/* -*- 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 + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-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_surface_t *image; + cairo_int_status_t status; + + image = cairo_surface_map_to_image (extents->surface, &extents->unbounded); + status = _cairo_surface_offset_paint (image, + -extents->unbounded.x, + -extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + extents->clip); + cairo_surface_unmap_image (extents->surface, image); + + return status; +} + +static cairo_int_status_t +_cairo_fallback_compositor_mask (const cairo_compositor_t *_compositor, + cairo_composite_rectangles_t *extents) +{ + cairo_surface_t *image; + cairo_int_status_t status; + + image = cairo_surface_map_to_image (extents->surface, &extents->unbounded); + status = _cairo_surface_offset_mask (image, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + &extents->mask_pattern.base, + extents->clip); + cairo_surface_unmap_image (extents->surface, image); + + return status; +} + +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_surface_t *image; + cairo_int_status_t status; + + image = cairo_surface_map_to_image (extents->surface, &extents->unbounded); + status = _cairo_surface_offset_stroke (image, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + path, style, + ctm, ctm_inverse, + tolerance, + antialias, + extents->clip); + cairo_surface_unmap_image (extents->surface, image); + + return status; +} + +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_surface_t *image; + cairo_int_status_t status; + + image = cairo_surface_map_to_image (extents->surface, &extents->unbounded); + status = _cairo_surface_offset_fill (image, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + path, + fill_rule, tolerance, antialias, + extents->clip); + cairo_surface_unmap_image (extents->surface, image); + + return status; +} + +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_surface_t *image; + cairo_int_status_t status; + + image = cairo_surface_map_to_image (extents->surface, &extents->unbounded); + status = _cairo_surface_offset_glyphs (image, + extents->unbounded.x, + extents->unbounded.y, + extents->op, + &extents->source_pattern.base, + scaled_font, glyphs, num_glyphs, + extents->clip); + cairo_surface_unmap_image (extents->surface, image); + + return status; +} + +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, +}; diff --git a/src/cairo-freed-pool-private.h b/src/cairo-freed-pool-private.h index 259c57dbd..0ec6de3d1 100644 --- a/src/cairo-freed-pool-private.h +++ b/src/cairo-freed-pool-private.h @@ -42,7 +42,9 @@ CAIRO_BEGIN_DECLS -#if HAS_ATOMIC_OPS +#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. */ @@ -128,7 +130,7 @@ typedef int freed_pool_t; #define _freed_pool_get(pool) NULL #define _freed_pool_put(pool, ptr) free(ptr) -#define _freed_pool_reset(ptr) assert((ptr) != NULL) +#define _freed_pool_reset(ptr) #endif diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 9024463e6..e0df535c3 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -2586,7 +2586,6 @@ static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = { _cairo_ft_scaled_glyph_init, NULL, /* text_to_glyphs */ _cairo_ft_ucs4_to_index, - NULL, /* show_glyphs */ _cairo_ft_load_truetype_table, _cairo_ft_index_to_ucs4, _cairo_ft_is_synthetic, diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 01fe72597..c59501ecc 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -49,326 +49,6 @@ #include "cairo-error-private.h" #include "cairo-image-surface-private.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); -} - -/* - * Like cairo_pattern_acquire_surface(), but returns a matrix that transforms - * from dest to src coords. - */ -static cairo_status_t -_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand, - const cairo_pattern_t *src, - cairo_gl_surface_t *dst, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height) -{ - cairo_status_t status; - cairo_matrix_t m; - cairo_gl_surface_t *surface; - cairo_surface_attributes_t *attributes; - attributes = &operand->texture.attributes; - - status = _cairo_pattern_acquire_surface (src, &dst->base, - src_x, src_y, - width, height, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) - &surface, - attributes); - if (unlikely (status)) - return status; - - if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device) && - (attributes->extend == CAIRO_EXTEND_REPEAT || - attributes->extend == CAIRO_EXTEND_REFLECT)) - { - _cairo_pattern_release_surface (src, - &surface->base, - attributes); - return UNSUPPORTED ("EXT_texture_rectangle with repeat/reflect"); - } - - assert (surface->base.backend == &_cairo_gl_surface_backend); - assert (_cairo_gl_surface_is_texture (surface)); - - operand->type = CAIRO_GL_OPERAND_TEXTURE; - operand->texture.surface = surface; - operand->texture.tex = surface->tex; - /* Translate the matrix from - * (unnormalized src -> unnormalized src) to - * (unnormalized dst -> unnormalized src) - */ - cairo_matrix_init_translate (&m, - src_x - dst_x + attributes->x_offset, - src_y - dst_y + attributes->y_offset); - cairo_matrix_multiply (&attributes->matrix, - &m, - &attributes->matrix); - - - /* Translate the matrix from - * (unnormalized dst -> unnormalized src) to - * (unnormalized dst -> normalized src) - */ - if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) { - cairo_matrix_init_scale (&m, - 1.0, - 1.0); - } else { - cairo_matrix_init_scale (&m, - 1.0 / surface->width, - 1.0 / surface->height); - } - cairo_matrix_multiply (&attributes->matrix, - &attributes->matrix, - &m); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_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; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *pattern, - cairo_gl_surface_t *dst, - int src_x, int src_y, - int dst_x, int dst_y) -{ - 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); - } - - cairo_matrix_translate (&operand->gradient.m, src_x - dst_x, src_y - dst_y); - - operand->gradient.extend = pattern->extend; - - return CAIRO_STATUS_SUCCESS; -} - -static 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_pattern_release_surface (NULL, /* XXX */ - &operand->texture.surface->base, - &operand->texture.attributes); - break; - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_SPANS: - break; - } - - operand->type = CAIRO_GL_OPERAND_NONE; -} - -static cairo_int_status_t -_cairo_gl_operand_init (cairo_gl_operand_t *operand, - const cairo_pattern_t *pattern, - cairo_gl_surface_t *dst, - int src_x, int src_y, - int dst_x, int dst_y, - int width, int height) -{ - cairo_int_status_t status; - - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_SOLID: - return _cairo_gl_solid_operand_init (operand, - &((cairo_solid_pattern_t *) pattern)->color); - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - status = _cairo_gl_gradient_operand_init (operand, - pattern, dst, - src_x, src_y, - dst_x, dst_y); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* fall through */ - - default: - case CAIRO_PATTERN_TYPE_MESH: - case CAIRO_PATTERN_TYPE_SURFACE: - return _cairo_gl_pattern_texture_setup (operand, - pattern, dst, - src_x, src_y, - dst_x, dst_y, - width, height); - } -} - -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; -} - - cairo_int_status_t _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, const cairo_pattern_t *pattern, @@ -384,6 +64,22 @@ _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, width, height); } +void +_cairo_gl_composite_set_source_operand (cairo_gl_composite_t *setup, + const cairo_gl_operand_t *source) +{ + _cairo_gl_operand_destroy (&setup->src); + 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, @@ -403,91 +99,25 @@ _cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, } 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_gl_operand_destroy (&setup->mask); - setup->mask.type = CAIRO_GL_OPERAND_SPANS; + if (mask) + setup->mask = *mask; } void -_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, - cairo_region_t *clip_region) +_cairo_gl_composite_set_spans (cairo_gl_composite_t *setup) { - setup->clip_region = clip_region; + setup->spans = TRUE; } -static void -_cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, - cairo_gl_operand_t *operand, - cairo_gl_tex_t tex_unit) +void +_cairo_gl_composite_set_clip_region (cairo_gl_composite_t *setup, + cairo_region_t *clip_region) { - char uniform_name[50]; - char *custom_part; - static const char *names[] = { "source", "mask" }; - - strcpy (uniform_name, names[tex_unit]); - custom_part = uniform_name + strlen (names[tex_unit]); - - switch (operand->type) { - default: - case CAIRO_GL_OPERAND_COUNT: - ASSERT_NOT_REACHED; - case CAIRO_GL_OPERAND_NONE: - case CAIRO_GL_OPERAND_SPANS: - break; - case CAIRO_GL_OPERAND_CONSTANT: - strcpy (custom_part, "_constant"); - _cairo_gl_shader_bind_vec4 (ctx, - uniform_name, - operand->constant.color[0], - operand->constant.color[1], - operand->constant.color[2], - operand->constant.color[3]); - break; - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: - strcpy (custom_part, "_a"); - _cairo_gl_shader_bind_float (ctx, - uniform_name, - operand->gradient.a); - /* fall through */ - case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: - strcpy (custom_part, "_circle_d"); - _cairo_gl_shader_bind_vec3 (ctx, - uniform_name, - operand->gradient.circle_d.center.x, - operand->gradient.circle_d.center.y, - operand->gradient.circle_d.radius); - strcpy (custom_part, "_radius_0"); - _cairo_gl_shader_bind_float (ctx, - uniform_name, - 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; - } - strcpy (custom_part, "_texdims"); - _cairo_gl_shader_bind_vec2 (ctx, uniform_name, width, height); - } - break; - } + setup->clip_region = clip_region; } static void @@ -500,6 +130,29 @@ _cairo_gl_composite_bind_to_shader (cairo_gl_context_t *ctx, _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, @@ -534,66 +187,6 @@ _cairo_gl_texture_set_extend (cairo_gl_context_t *ctx, } } -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 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: - case CAIRO_GL_OPERAND_SPANS: - 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; -} static void _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, @@ -629,11 +222,6 @@ _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; - case CAIRO_GL_OPERAND_SPANS: - dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4, - GL_UNSIGNED_BYTE, GL_TRUE, vertex_size, - (void *) (uintptr_t) vertex_offset); - dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); /* fall through */ case CAIRO_GL_OPERAND_CONSTANT: break; @@ -668,6 +256,19 @@ _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, } } +static void +_cairo_gl_context_setup_spans (cairo_gl_context_t *ctx, + unsigned int vertex_size, + unsigned int vertex_offset) +{ + cairo_gl_dispatch_t *dispatch = &ctx->dispatch; + + dispatch->VertexAttribPointer (CAIRO_GL_COLOR_ATTRIB_INDEX, 4, + GL_UNSIGNED_BYTE, GL_TRUE, vertex_size, + (void *) (uintptr_t) vertex_offset); + dispatch->EnableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); +} + void _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, cairo_gl_tex_t tex_unit) @@ -681,8 +282,6 @@ _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: break; - case CAIRO_GL_OPERAND_SPANS: - dispatch->DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); /* fall through */ case CAIRO_GL_OPERAND_CONSTANT: break; @@ -764,27 +363,6 @@ _cairo_gl_set_operator (cairo_gl_context_t *ctx, } } -static unsigned int -_cairo_gl_operand_get_vertex_size (cairo_gl_operand_type_t type) -{ - switch (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_SPANS: - return 4 * sizeof (GLbyte); - case CAIRO_GL_OPERAND_TEXTURE: - 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 2 * sizeof (GLfloat); - } -} - static cairo_status_t _cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx, cairo_gl_composite_t *setup) @@ -872,6 +450,7 @@ _cairo_gl_composite_begin_component_alpha (cairo_gl_context_t *ctx, 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)) @@ -919,10 +498,12 @@ _cairo_gl_composite_begin (cairo_gl_composite_t *setup, } status = _cairo_gl_get_shader_by_type (ctx, - &setup->src, - &setup->mask, - component_alpha ? CAIRO_GL_SHADER_IN_CA_SOURCE - : CAIRO_GL_SHADER_IN_NORMAL, + &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; @@ -954,10 +535,12 @@ _cairo_gl_composite_begin (cairo_gl_composite_t *setup, _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_SOURCE, &setup->src, vertex_size, dst_size); _cairo_gl_context_setup_operand (ctx, CAIRO_GL_TEX_MASK, &setup->mask, vertex_size, dst_size + src_size); + if (setup->spans) + _cairo_gl_context_setup_spans (ctx, vertex_size, dst_size + src_size); + else + ctx->dispatch.DisableVertexAttribArray (CAIRO_GL_COLOR_ATTRIB_INDEX); - _cairo_gl_set_operator (ctx, - setup->op, - component_alpha); + _cairo_gl_set_operator (ctx, setup->op, component_alpha); ctx->vertex_size = vertex_size; @@ -1064,62 +647,6 @@ _cairo_gl_composite_prepare_buffer (cairo_gl_context_t *ctx, } } -static inline void -_cairo_gl_operand_emit (cairo_gl_operand_t *operand, - GLfloat ** vb, - GLfloat x, - GLfloat y, - uint8_t alpha) -{ - 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_SPANS: - { - union fi { - float f; - GLbyte bytes[4]; - } fi; - - fi.bytes[0] = 0; - fi.bytes[1] = 0; - fi.bytes[2] = 0; - fi.bytes[3] = alpha; - *(*vb)++ = fi.f; - } - 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: - { - 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: - { - 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; - } -} - static inline void _cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx, GLfloat x, @@ -1134,6 +661,19 @@ _cairo_gl_composite_emit_vertex (cairo_gl_context_t *ctx, _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_SOURCE], &vb, x, y, alpha); _cairo_gl_operand_emit (&ctx->operands[CAIRO_GL_TEX_MASK ], &vb, x, y, alpha); + if (ctx->spans) { + union fi { + float f; + GLbyte bytes[4]; + } fi; + + 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; } @@ -1229,517 +769,3 @@ _cairo_gl_composite_init (cairo_gl_composite_t *setup, return CAIRO_STATUS_SUCCESS; } - -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; - - 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_gl_surface_t *surface; - cairo_image_surface_t *image; - int tx, ty; -}; - -static cairo_bool_t image_upload_box (cairo_box_t *box, void *closure) -{ - const struct image_upload_box *iub = closure; - int x = _cairo_fixed_integer_part (box->p1.x); - int y = _cairo_fixed_integer_part (box->p1.y); - int w = _cairo_fixed_integer_part (box->p2.x - box->p1.x); - int h = _cairo_fixed_integer_part (box->p2.y - box->p1.y); - - return _cairo_gl_surface_draw_image (iub->surface, - iub->image, - x + iub->tx, y + iub->ty, - w, h, - x, y) == CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_upload_image_inplace (cairo_gl_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; - 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; - - image = (cairo_image_surface_t *) pattern->surface; - if (image->format == CAIRO_FORMAT_INVALID) - 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; - - iub.surface = surface; - iub.image = image; - iub.tx = tx; - iub.ty = ty; - cairo_boxes_for_each_box (boxes, image_upload_box, &iub); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_bool_t composite_box (cairo_box_t *box, void *closure) -{ - _cairo_gl_composite_emit_rect (closure, - _cairo_fixed_integer_part (box->p1.x), - _cairo_fixed_integer_part (box->p1.y), - _cairo_fixed_integer_part (box->p2.x), - _cairo_fixed_integer_part (box->p2.y), - 0); - return TRUE; -} - -static cairo_status_t -_composite_boxes (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - cairo_bool_t need_clip_mask = FALSE; - cairo_gl_composite_t setup; - cairo_gl_context_t *ctx; - cairo_surface_pattern_t mask; - 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 (extents->clip->path && - (! extents->is_bounded || op == CAIRO_OPERATOR_SOURCE)) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - if (! extents->is_bounded) - return CAIRO_INT_STATUS_UNSUPPORTED; - - status = _cairo_gl_composite_init (&setup, op, dst, FALSE, - &extents->bounded); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_source (&setup, src, - extents->bounded.x, - extents->bounded.y, - extents->bounded.x, - extents->bounded.y, - extents->bounded.width, - extents->bounded.height); - if (unlikely (status)) - goto CLEANUP; - - need_clip_mask = extents->clip->path != NULL; - if (need_clip_mask) { - cairo_surface_t *clip_surface; - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (extents->clip, - &dst->base, - &clip_x, &clip_y); - if (unlikely (clip_surface->status)) { - status = clip_surface->status; - need_clip_mask = FALSE; - goto CLEANUP; - } - - _cairo_pattern_init_for_surface (&mask, clip_surface); - mask.base.filter = CAIRO_FILTER_NEAREST; - cairo_matrix_init_translate (&mask.base.matrix, - -clip_x, - -clip_y); - cairo_surface_destroy (clip_surface); - - if (op == CAIRO_OPERATOR_CLEAR) { - src = NULL; - op = CAIRO_OPERATOR_DEST_OUT; - } - - status = _cairo_gl_composite_set_mask (&setup, &mask.base, - extents->bounded.x, - extents->bounded.y, - extents->bounded.x, - extents->bounded.y, - extents->bounded.width, - extents->bounded.height); - if (unlikely (status)) - goto CLEANUP; - } - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - cairo_boxes_for_each_box (boxes, composite_box, ctx); - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - if (need_clip_mask) - _cairo_pattern_fini (&mask.base); - - _cairo_gl_composite_fini (&setup); - return status; -} - -static cairo_bool_t converter_add_box (cairo_box_t *box, void *closure) -{ - return _cairo_rectangular_scan_converter_add_box (closure, box, 1) == CAIRO_STATUS_SUCCESS; -} - -typedef struct _cairo_gl_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_gl_composite_t setup; - - int xmin, xmax; - int ymin, ymax; - - cairo_gl_context_t *ctx; -} cairo_gl_surface_span_renderer_t; - -static cairo_status_t -_cairo_gl_render_bounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - _cairo_gl_composite_emit_rect (renderer->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_render_unbounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (y > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, y, - 0); - } - - if (num_spans == 0) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - renderer->xmax, y + height, - 0); - } else { - if (spans[0].x != renderer->xmin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != renderer->xmax) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - renderer->xmax, y + height, - 0); - } - } - - renderer->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_unbounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (renderer->ymax > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, renderer->ymax, - 0); - } - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_bounded_spans (void *abstract_renderer) -{ - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_composite_unaligned_boxes (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *composite) -{ - cairo_rectangular_scan_converter_t converter; - cairo_gl_surface_span_renderer_t renderer; - const cairo_rectangle_int_t *extents; - cairo_status_t status; - - if (composite->clip->path) - return CAIRO_INT_STATUS_UNSUPPORTED; - - /* XXX for ADD or if we know the boxes are disjoint we can do a simpler - * pass, but for now... */ - - _cairo_rectangular_scan_converter_init (&converter, &composite->bounded); - cairo_boxes_for_each_box (boxes, converter_add_box, &converter); - - if (composite->is_bounded) { - renderer.base.render_rows = _cairo_gl_render_bounded_spans; - renderer.base.finish = _cairo_gl_finish_bounded_spans; - extents = &composite->bounded; - } else { - renderer.base.render_rows = _cairo_gl_render_unbounded_spans; - renderer.base.finish = _cairo_gl_finish_unbounded_spans; - extents = &composite->unbounded; - } - renderer.xmin = extents->x; - renderer.xmax = extents->x + extents->width; - renderer.ymin = extents->y; - renderer.ymax = extents->y + extents->height; - - status = _cairo_gl_composite_init (&renderer.setup, - op, dst, - FALSE, extents); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_set_source (&renderer.setup, pattern, - extents->x, extents->y, - extents->x, extents->y, - extents->width, extents->height); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_mask_spans (&renderer.setup); - if (! composite->is_bounded) - _cairo_gl_composite_set_clip_region (&renderer.setup, - _cairo_clip_get_region (composite->clip)); - - status = _cairo_gl_composite_begin (&renderer.setup, &renderer.ctx); - if (unlikely (status)) - goto FAIL; - - status = converter.base.generate (&converter.base, &renderer.base); - - converter.base.destroy (&converter.base); - renderer.base.finish (&renderer.base); - - status = _cairo_gl_context_release (renderer.ctx, status); -FAIL: - _cairo_gl_composite_fini (&renderer.setup); - return status; -} - -/* XXX _cairo_gl_clip_and_composite_polygon() */ -cairo_int_status_t -_cairo_gl_surface_polygon (cairo_gl_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, - const cairo_composite_rectangles_t *extents) -{ - cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); - - if (! _cairo_clip_is_region (extents->clip)) - return UNSUPPORTED ("a clip surface would be required"); - - if (! _cairo_surface_check_span_renderer (op, src, &dst->base, antialias)) - return UNSUPPORTED ("no span renderer"); - - if (op == CAIRO_OPERATOR_SOURCE) - return UNSUPPORTED ("SOURCE compositing doesn't work in GL"); - if (op == CAIRO_OPERATOR_CLEAR) { - op = CAIRO_OPERATOR_DEST_OUT; - src = &_cairo_pattern_white.base; - } - - return _cairo_surface_composite_polygon (&dst->base, - op, - src, - fill_rule, - antialias, - extents, - polygon, - clip_region); -} - -cairo_int_status_t -_cairo_gl_clip_and_composite_boxes (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_boxes_t *boxes, - cairo_composite_rectangles_t *extents) -{ - cairo_int_status_t status; - - if (boxes->num_boxes == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - if (! _cairo_gl_operator_is_supported (op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - 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; -#if 0 - dst->deferred_clear = FALSE; -#endif - } - - 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 (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 = _cairo_gl_surface_polygon (dst, op, src, - &polygon, - fill_rule, - antialias, - 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 0 - if (dst->deferred_clear) { - status = _cairo_gl_surface_clear (dst); - if (unlikely (status)) - return status; - } -#endif - - 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; - } - - /* 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; - - status = _composite_unaligned_boxes (dst, op, src, boxes, extents); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - - /* Otherwise XXX */ - return CAIRO_INT_STATUS_UNSUPPORTED; -} diff --git a/src/cairo-gl-device.c b/src/cairo-gl-device.c index 170cf9620..f20850e74 100644 --- a/src/cairo-gl-device.c +++ b/src/cairo-gl-device.c @@ -113,18 +113,20 @@ static void _gl_destroy (void *device) { cairo_gl_context_t *ctx = device; - cairo_scaled_font_t *scaled_font, *next_scaled_font; int n; ctx->acquire (ctx); - cairo_list_foreach_entry_safe (scaled_font, - next_scaled_font, - cairo_scaled_font_t, - &ctx->fonts, - link) - { - _cairo_scaled_font_revoke_ownership (scaled_font); + 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); } for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) @@ -161,6 +163,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx) _cairo_device_init (&ctx->base, &_cairo_gl_device_backend); + ctx->compositor = _cairo_gl_span_compositor_get (); + memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache)); cairo_list_init (&ctx->fonts); diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index 0772d2a03..ee5c543c4 100644 --- a/src/cairo-gl-glyphs.c +++ b/src/cairo-gl-glyphs.c @@ -40,6 +40,7 @@ #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" @@ -50,11 +51,26 @@ #define GLYPH_CACHE_MIN_SIZE 4 #define GLYPH_CACHE_MAX_SIZE 128 -typedef struct _cairo_gl_glyph_private { +typedef struct _cairo_gl_glyph { cairo_rtree_node_t node; + cairo_scaled_glyph_private_t base; cairo_gl_glyph_cache_t *cache; struct { float x, y; } p1, p2; -} cairo_gl_glyph_private_t; +} cairo_gl_glyph_t; + +static void +_cairo_gl_glyph_fini (cairo_scaled_glyph_private_t *_priv, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_font_t *scaled_font) +{ + cairo_gl_glyph_t *priv = cairo_container_of (_priv, cairo_gl_glyph_t, base); + + priv->node.owner = NULL; + if (! priv->node.pinned) { + /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ + _cairo_rtree_node_remove (&priv->cache->rtree, &priv->node); + } +} static cairo_int_status_t _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, @@ -63,7 +79,7 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, { cairo_image_surface_t *glyph_surface = scaled_glyph->surface; cairo_gl_surface_t *cache_surface; - cairo_gl_glyph_private_t *glyph_private; + cairo_gl_glyph_t *glyph_private; cairo_rtree_node_t *node = NULL; cairo_int_status_t status; int width, height; @@ -101,11 +117,15 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, if (unlikely (status)) return status; - scaled_glyph->surface_private = node; - node->owner = &scaled_glyph->surface_private; - - glyph_private = (cairo_gl_glyph_private_t *) node; + glyph_private = (cairo_gl_glyph_t *) node; glyph_private->cache = cache; + _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; @@ -122,11 +142,11 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_context_t *ctx, return CAIRO_STATUS_SUCCESS; } -static cairo_gl_glyph_private_t * +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->surface_private); + return _cairo_rtree_pin (&cache->rtree, scaled_glyph->dev_private); } static cairo_status_t @@ -183,63 +203,29 @@ _cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache) _cairo_rtree_unpin (&cache->rtree); } -static cairo_bool_t -_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface, - cairo_scaled_font_t *scaled_font) -{ - cairo_device_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &_cairo_gl_surface_backend) || - (font_private != NULL && font_private != surface->base.device)) - { - return FALSE; - } - - return TRUE; -} - -void -_cairo_gl_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +static void +_cairo_gl_font_fini (cairo_scaled_font_private_t *_priv, + cairo_scaled_font_t *scaled_font) { - cairo_list_del (&scaled_font->link); -} + cairo_gl_font_t *priv = (cairo_gl_font_t *)_priv; -void -_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_gl_glyph_private_t *glyph_private; - - glyph_private = scaled_glyph->surface_private; - if (glyph_private != NULL) { - glyph_private->node.owner = NULL; - if (! glyph_private->node.pinned) { - /* XXX thread-safety? Probably ok due to the frozen scaled-font. */ - _cairo_rtree_node_remove (&glyph_private->cache->rtree, - &glyph_private->node); - } - } + cairo_list_del (&priv->link); + free (priv); } static cairo_status_t -_render_glyphs (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_composite_rectangles_t *extents, - cairo_bool_t *has_component_alpha, - int *remaining_glyphs) +render_glyphs (cairo_gl_surface_t *dst, + int dst_x, int dst_y, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_composite_glyphs_info_t *info, + cairo_bool_t *has_component_alpha) { cairo_format_t last_format = CAIRO_FORMAT_INVALID; cairo_gl_glyph_cache_t *cache = NULL; cairo_gl_context_t *ctx; cairo_gl_composite_t setup; - cairo_status_t status; + cairo_int_status_t status; int i = 0; *has_component_alpha = FALSE; @@ -248,45 +234,30 @@ _render_glyphs (cairo_gl_surface_t *dst, if (unlikely (status)) return status; - _cairo_scaled_font_freeze_cache (scaled_font); - - status = _cairo_gl_composite_init (&setup, op, dst, - TRUE, &extents->bounded); - + status = _cairo_gl_composite_init (&setup, op, dst, TRUE, &info->extents); if (unlikely (status)) goto FINISH; - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FINISH; - } - status = _cairo_gl_composite_set_source (&setup, source, - extents->bounded.x, - extents->bounded.y, + info->extents.x, + info->extents.y, dst_x, dst_y, - extents->bounded.width, - extents->bounded.height); + info->extents.width, + info->extents.height); if (unlikely (status)) goto FINISH; - if (scaled_font->surface_private == NULL) { - scaled_font->surface_private = ctx; - scaled_font->surface_backend = &_cairo_gl_surface_backend; - cairo_list_add (&scaled_font->link, &ctx->fonts); - } - _cairo_gl_composite_set_clip_region (&setup, - _cairo_clip_get_region (extents->clip)); + //_cairo_gl_composite_set_clip_region (&setup, _cairo_clip_get_region (extents->clip)); - for (i = 0; i < num_glyphs; i++) { + for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; - cairo_gl_glyph_private_t *glyph; + cairo_gl_glyph_t *glyph; double x_offset, y_offset; double x1, x2, y1, y2; - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, + status = _cairo_scaled_glyph_lookup (info->font, + info->glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_SURFACE, &scaled_glyph); if (unlikely (status)) @@ -297,13 +268,6 @@ _render_glyphs (cairo_gl_surface_t *dst, { continue; } - if (scaled_glyph->surface->width > GLYPH_CACHE_MAX_SIZE || - scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE) - { - status = CAIRO_INT_STATUS_UNSUPPORTED; - goto FINISH; - } - if (scaled_glyph->surface->format != last_format) { status = cairo_gl_context_get_glyph_cache (ctx, scaled_glyph->surface->format, @@ -331,25 +295,35 @@ _render_glyphs (cairo_gl_surface_t *dst, goto FINISH; } - if (scaled_glyph->surface_private == NULL) { - status = _cairo_gl_glyph_cache_add_glyph (ctx, cache, scaled_glyph); + if (scaled_glyph->dev_private_key != cache) { + cairo_scaled_glyph_private_t *priv; - 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); + 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 (unlikely (_cairo_status_is_error (status))) - goto FINISH; + 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 (glyphs[i].x - x_offset); - y1 = _cairo_lround (glyphs[i].y - y_offset); + x1 = _cairo_lround (info->glyphs[i].x - x_offset); + y1 = _cairo_lround (info->glyphs[i].y - y_offset); x2 = x1 + scaled_glyph->surface->width; y2 = y1 + scaled_glyph->surface->height; @@ -362,75 +336,59 @@ _render_glyphs (cairo_gl_surface_t *dst, status = CAIRO_STATUS_SUCCESS; FINISH: - _cairo_scaled_font_thaw_cache (scaled_font); - status = _cairo_gl_context_release (ctx, status); _cairo_gl_composite_fini (&setup); - - *remaining_glyphs = num_glyphs - i; return status; } static cairo_int_status_t -_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_composite_rectangles_t *extents, - int *remaining_glyphs) +render_glyphs_via_mask (cairo_gl_surface_t *dst, + cairo_operator_t op, + const cairo_surface_t *source, + cairo_composite_glyphs_info_t *info) { cairo_surface_t *mask; cairo_status_t status; cairo_bool_t has_component_alpha; - cairo_clip_t *saved_clip; int i; /* 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, - extents->bounded.width, - extents->bounded.height); + info->extents.width, + info->extents.height); if (unlikely (mask->status)) return mask->status; - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x -= extents->bounded.x; - glyphs[i].y -= extents->bounded.y; + for (i = 0; i < info->num_glyphs; i++) { + info->glyphs[i].x -= info->extents.x; + info->glyphs[i].y -= info->extents.y; } - - saved_clip = extents->clip; - extents->clip = NULL; - status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, - CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, - glyphs, num_glyphs, scaled_font, - extents, - &has_component_alpha, - remaining_glyphs); - extents->clip = saved_clip; - + status = render_glyphs ((cairo_gl_surface_t *) mask, 0, 0, + CAIRO_OPERATOR_ADD, + &_cairo_pattern_white.base, + info, &has_component_alpha); if (likely (status == CAIRO_STATUS_SUCCESS)) { + /* XXX composite */ +#if 0 cairo_surface_pattern_t mask_pattern; - mask->is_clear = FALSE; _cairo_pattern_init_for_surface (&mask_pattern, mask); mask_pattern.base.has_component_alpha = has_component_alpha; cairo_matrix_init_translate (&mask_pattern.base.matrix, - -extents->bounded.x, -extents->bounded.y); + -info->extents.x, -info->extents.y); status = _cairo_surface_mask (&dst->base, op, source, &mask_pattern.base, - extents->clip); + NULL); _cairo_pattern_fini (&mask_pattern.base); +#endif } else { - for (i = 0; i < num_glyphs; i++) { - glyphs[i].x += extents->bounded.x; - glyphs[i].y += extents->bounded.y; + for (i = 0; i < info->num_glyphs; i++) { + info->glyphs[i].x += info->extents.x; + info->glyphs[i].y += info->extents.y; } - *remaining_glyphs = num_glyphs; } cairo_surface_destroy (mask); @@ -438,143 +396,62 @@ _cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t *dst, 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"); -cairo_private cairo_bool_t -_cairo_gl_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle); + return CAIRO_STATUS_SUCCESS; +} cairo_int_status_t -_cairo_gl_surface_show_glyphs (void *abstract_dst, - 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, - int *remaining_glyphs) +_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_gl_surface_t *dst = abstract_dst; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_bool_t overlap, use_mask = FALSE; + cairo_gl_surface_t *dst = _dst; cairo_bool_t has_component_alpha; - cairo_status_t status; int i; - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - - if (! _cairo_operator_bounded_by_mask (op)) - use_mask |= TRUE; - /* If any of the glyphs are component alpha, we have to go through a mask, * since only _cairo_gl_surface_composite() currently supports component * alpha. */ - if (!use_mask && op != CAIRO_OPERATOR_OVER) { - for (i = 0; i < num_glyphs; i++) { + if (!dst->base.is_clear && ! info->use_mask && op != CAIRO_OPERATOR_OVER) { + for (i = 0; i < info->num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_SURFACE, - &scaled_glyph); - if (!_cairo_status_is_error (status) && + if (_cairo_scaled_glyph_lookup (info->font, info->glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_SURFACE, + &scaled_glyph) == CAIRO_INT_STATUS_SUCCESS && scaled_glyph->surface->format == CAIRO_FORMAT_ARGB32) { - use_mask = TRUE; + info->use_mask = TRUE; break; } } } - /* 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 (op == CAIRO_OPERATOR_CLEAR) { - source = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; - } - - /* For SOURCE, cairo's rendering equation is: - * (mask IN clip) ? src OP dest : dest - * or more simply: - * (mask IN clip) ? src : dest. - * - * If we just used the Render equation, we would get: - * (src IN mask IN clip) OP dest - * or: - * (src IN mask IN clip) bounded by extents of the drawing. - * - * The trick is that for GL blending, we only get our 4 source values - * into the blender, and since we need all 4 components of source, we - * can't also get the mask IN clip into the blender. But if we did - * two passes we could make it work: - * dest = (mask IN clip) DEST_OUT dest - * dest = src IN mask IN clip ADD dest - * - * But for now, composite via an intermediate mask. - */ - if (op == CAIRO_OPERATOR_SOURCE) - use_mask |= TRUE; - - /* XXX we don't need ownership of the font as we use a global - * glyph cache -- but we do need scaled_glyph eviction notification. :-( - */ - if (! _cairo_gl_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("do not control font"); - - _cairo_gl_surface_get_extents (dst, &unbounded); - status = _cairo_composite_rectangles_init_for_glyphs (&extents, - &unbounded, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, &overlap); - if (unlikely (status)) - return status; - - /* If the glyphs overlap, we need to build an intermediate mask rather - * then perform the compositing directly. - */ - use_mask |= overlap; - use_mask |= ! _cairo_clip_is_region (extents.clip); - - if (use_mask) { - status = _cairo_gl_surface_show_glyphs_via_mask (dst, op, - source, - glyphs, num_glyphs, - scaled_font, - &extents, - remaining_glyphs); + if (info->use_mask) { + return render_glyphs_via_mask (dst, op, _src, info); } else { - status = _render_glyphs (dst, extents.bounded.x, extents.bounded.y, - op, source, - glyphs, num_glyphs, scaled_font, - &extents, - &has_component_alpha, - remaining_glyphs); + return render_glyphs (dst, dst_x, dst_y, + op, _src, info, + &has_component_alpha); } - - _cairo_composite_rectangles_fini (&extents); - return status; } void @@ -584,7 +461,7 @@ _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache) GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, GLYPH_CACHE_MIN_SIZE, - sizeof (cairo_gl_glyph_private_t)); + sizeof (cairo_gl_glyph_t)); } void @@ -598,4 +475,3 @@ _cairo_gl_glyph_cache_fini (cairo_gl_context_t *ctx, cache->pattern.surface = NULL; } } - diff --git a/src/cairo-gl-operand.c b/src/cairo-gl-operand.c new file mode 100644 index 000000000..8b345bbda --- /dev/null +++ b/src/cairo-gl-operand.c @@ -0,0 +1,538 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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" + +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); +} + +/* + * Like cairo_pattern_acquire_surface(), but returns a matrix that transforms + * from dest to src coords. + */ +static cairo_status_t +_cairo_gl_pattern_texture_setup (cairo_gl_operand_t *operand, + const cairo_pattern_t *src, + cairo_gl_surface_t *dst, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height) +{ + cairo_status_t status; + cairo_matrix_t m; + cairo_gl_surface_t *surface; + cairo_surface_attributes_t *attributes; + + attributes = &operand->texture.attributes; + +#if 0 + status = _cairo_pattern_acquire_surface (src, &dst->base, + src_x, src_y, + width, height, + CAIRO_PATTERN_ACQUIRE_NONE, + (cairo_surface_t **) + &surface, + attributes); + if (unlikely (status)) + return status; +#endif + + if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device) && + (attributes->extend == CAIRO_EXTEND_REPEAT || + attributes->extend == CAIRO_EXTEND_REFLECT)) + { + return UNSUPPORTED ("EXT_texture_rectangle with repeat/reflect"); + } + + assert (_cairo_gl_surface_is_texture (surface)); + + operand->type = CAIRO_GL_OPERAND_TEXTURE; + operand->texture.surface = surface; + operand->texture.tex = surface->tex; + /* Translate the matrix from + * (unnormalized src -> unnormalized src) to + * (unnormalized dst -> unnormalized src) + */ + cairo_matrix_init_translate (&m, + src_x - dst_x + attributes->x_offset, + src_y - dst_y + attributes->y_offset); + cairo_matrix_multiply (&attributes->matrix, + &m, + &attributes->matrix); + + + /* Translate the matrix from + * (unnormalized dst -> unnormalized src) to + * (unnormalized dst -> normalized src) + */ + if (_cairo_gl_device_requires_power_of_two_textures (dst->base.device)) { + cairo_matrix_init_scale (&m, + 1.0, + 1.0); + } else { + cairo_matrix_init_scale (&m, + 1.0 / surface->width, + 1.0 / surface->height); + } + cairo_matrix_multiply (&attributes->matrix, + &attributes->matrix, + &m); + + return CAIRO_STATUS_SUCCESS; +} + +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; +} + +static cairo_status_t +_cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, + const cairo_pattern_t *pattern, + cairo_gl_surface_t *dst, + int src_x, int src_y, + int dst_x, int dst_y) +{ + 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); + } + + cairo_matrix_translate (&operand->gradient.m, src_x - dst_x, src_y - dst_y); + + operand->gradient.extend = pattern->extend; + + return CAIRO_STATUS_SUCCESS; +} + +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: + 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, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height) +{ + cairo_int_status_t status; + + 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_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + status = _cairo_gl_gradient_operand_init (operand, + pattern, dst, + src_x, src_y, + dst_x, dst_y); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; + + /* fall through */ + + default: + case CAIRO_PATTERN_TYPE_MESH: + case CAIRO_PATTERN_TYPE_SURFACE: + return _cairo_gl_pattern_texture_setup (operand, + pattern, dst, + src_x, src_y, + dst_x, dst_y, + width, height); + } +} + +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) +{ + char uniform_name[50]; + char *custom_part; + static const char *names[] = { "source", "mask" }; + + strcpy (uniform_name, names[tex_unit]); + custom_part = uniform_name + strlen (names[tex_unit]); + + switch (operand->type) { + default: + case CAIRO_GL_OPERAND_COUNT: + ASSERT_NOT_REACHED; + case CAIRO_GL_OPERAND_NONE: + break; + case CAIRO_GL_OPERAND_CONSTANT: + strcpy (custom_part, "_constant"); + _cairo_gl_shader_bind_vec4 (ctx, + uniform_name, + operand->constant.color[0], + operand->constant.color[1], + operand->constant.color[2], + operand->constant.color[3]); + break; + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: + strcpy (custom_part, "_a"); + _cairo_gl_shader_bind_float (ctx, + uniform_name, + operand->gradient.a); + /* fall through */ + case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: + strcpy (custom_part, "_circle_d"); + _cairo_gl_shader_bind_vec3 (ctx, + uniform_name, + operand->gradient.circle_d.center.x, + operand->gradient.circle_d.center.y, + operand->gradient.circle_d.radius); + strcpy (custom_part, "_radius_0"); + _cairo_gl_shader_bind_float (ctx, + uniform_name, + 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; + } + strcpy (custom_part, "_texdims"); + _cairo_gl_shader_bind_vec2 (ctx, uniform_name, width, height); + } + break; + } +} + + +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 (cairo_gl_operand_type_t type) +{ + switch (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: + 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 2 * sizeof (GLfloat); + } +} + +void +_cairo_gl_operand_emit (cairo_gl_operand_t *operand, + GLfloat ** vb, + GLfloat x, + GLfloat y, + uint8_t alpha) +{ + 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: + { + 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: + { + 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; + } +} diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index ee79b23ae..e1005ba81 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -48,16 +48,17 @@ #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 -#include "cairo-gl.h" - #if CAIRO_HAS_GL_SURFACE #include #include @@ -138,7 +139,6 @@ typedef enum cairo_gl_operand_type { CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0, CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE, CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT, - CAIRO_GL_OPERAND_SPANS, CAIRO_GL_OPERAND_COUNT } cairo_gl_operand_type_t; @@ -161,11 +161,10 @@ typedef enum cairo_gl_shader_in { typedef enum cairo_gl_var_type { CAIRO_GL_VAR_NONE, CAIRO_GL_VAR_TEXCOORDS, - CAIRO_GL_VAR_COVERAGE } 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) +#define cairo_gl_var_type_hash(src,mask,spans,dest) ((spans) << 3) | ((mask) << 2 | (src << 1) | (dest)) +#define CAIRO_GL_VAR_TYPE_MAX ((CAIRO_GL_VAR_TEXCOORDS << 3) | (CAIRO_GL_VAR_TEXCOORDS << 2) | (CAIRO_GL_VAR_TEXCOORDS << 1) | CAIRO_GL_VAR_TEXCOORDS) /* This union structure describes a potential source or mask operand to the * compositing equation. @@ -259,6 +258,8 @@ typedef struct _cairo_gl_dispatch { struct _cairo_gl_context { cairo_device_t base; + const cairo_compositor_t *compositor; + GLuint texture_load_pbo; GLuint vbo; GLint max_framebuffer_size; @@ -283,6 +284,7 @@ struct _cairo_gl_context { cairo_gl_shader_t *current_shader; cairo_gl_operand_t operands[2]; + cairo_bool_t spans; char *vb; char *vb_mem; @@ -311,8 +313,15 @@ typedef struct _cairo_gl_composite { cairo_gl_operand_t src; cairo_gl_operand_t mask; + cairo_bool_t spans; } cairo_gl_composite_t; +typedef struct _cairo_gl_font { + cairo_scaled_font_private_t base; + cairo_device_t *device; + cairo_list_t link; +} cairo_gl_font_t; + cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend; static cairo_always_inline GLenum @@ -432,6 +441,13 @@ _cairo_gl_composite_set_source (cairo_gl_composite_t *setup, int src_x, int src_y, int dst_x, int dst_y, int width, int height); +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, @@ -441,7 +457,11 @@ _cairo_gl_composite_set_mask (cairo_gl_composite_t *setup, int width, int height); 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 cairo_status_t _cairo_gl_composite_begin (cairo_gl_composite_t *setup, @@ -480,13 +500,6 @@ _cairo_gl_get_image_format_and_type (cairo_gl_flavor_t flavor, 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); @@ -529,6 +542,7 @@ cairo_private 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); @@ -585,6 +599,17 @@ 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, + int src_x, int src_y, + int dst_x, int dst_y, + int width, int height); +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); @@ -594,25 +619,50 @@ _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 (cairo_gl_operand_type_t type); + cairo_private cairo_bool_t -_cairo_gl_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle); +_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, + uint8_t alpha); + +cairo_private void +_cairo_gl_operand_destroy (cairo_gl_operand_t *operand); + +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_surface_polygon (cairo_gl_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, - const cairo_composite_rectangles_t *extents); +_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_clip_and_composite_boxes (cairo_gl_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src, - cairo_boxes_t *boxes, - cairo_composite_rectangles_t *extents); +_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); slim_hidden_proto (cairo_gl_surface_create); slim_hidden_proto (cairo_gl_surface_create_for_texture); diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index 7d60c0f32..69609dfba 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -108,6 +108,7 @@ _cairo_gl_shader_compile (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); /* OpenGL Core 2.0 API. */ @@ -426,6 +427,7 @@ _cairo_gl_context_init_shaders (cairo_gl_context_t *ctx) &ctx->fill_rectangles_shader, CAIRO_GL_VAR_NONE, CAIRO_GL_VAR_NONE, + FALSE, fill_fs_source); if (unlikely (status)) return status; @@ -475,8 +477,6 @@ cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type) case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: case CAIRO_GL_OPERAND_TEXTURE: return CAIRO_GL_VAR_TEXCOORDS; - case CAIRO_GL_OPERAND_SPANS: - return CAIRO_GL_VAR_COVERAGE; } } @@ -491,13 +491,8 @@ cairo_gl_shader_emit_variable (cairo_output_stream_t *stream, case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, - "varying vec2 %s_texcoords;\n", - operand_names[name]); - break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n", + _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n", operand_names[name]); break; } @@ -514,21 +509,29 @@ cairo_gl_shader_emit_vertex (cairo_output_stream_t *stream, case CAIRO_GL_VAR_NONE: break; case CAIRO_GL_VAR_TEXCOORDS: - _cairo_output_stream_printf (stream, + _cairo_output_stream_printf (stream, " %s_texcoords = MultiTexCoord%d.xy;\n", operand_names[name], name); break; - case CAIRO_GL_VAR_COVERAGE: - _cairo_output_stream_printf (stream, - " %s_coverage = Color.a;\n", - 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) { @@ -539,6 +542,8 @@ cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, 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" @@ -552,6 +557,8 @@ cairo_gl_shader_get_vertex_source (cairo_gl_var_type_t src, 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); @@ -774,15 +781,6 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, namestr, namestr, namestr, namestr, namestr, namestr, namestr, namestr, rectstr, namestr); break; - case CAIRO_GL_OPERAND_SPANS: - _cairo_output_stream_printf (stream, - "varying float %s_coverage;\n" - "vec4 get_%s()\n" - "{\n" - " return vec4(0, 0, 0, %s_coverage);\n" - "}\n", - namestr, namestr, namestr); - break; } } @@ -842,6 +840,7 @@ 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) { @@ -849,6 +848,7 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, unsigned char *source; unsigned long length; cairo_status_t status; + const char *coverage_str; _cairo_output_stream_printf (stream, "#ifdef GL_ES\n" @@ -865,6 +865,10 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, 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) + coverage_str = " * coverage.a"; + _cairo_output_stream_printf (stream, "void main()\n" "{\n"); @@ -874,15 +878,18 @@ cairo_gl_shader_get_fragment_source (cairo_gl_context_t *ctx, ASSERT_NOT_REACHED; case CAIRO_GL_SHADER_IN_NORMAL: _cairo_output_stream_printf (stream, - " gl_FragColor = get_source() * get_mask().a;\n"); + " 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();\n"); + " 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();\n"); + " gl_FragColor = get_source().a * get_mask()%s;\n", + coverage_str); break; } @@ -902,6 +909,7 @@ _cairo_gl_shader_compile (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) { unsigned int vertex_shader; @@ -909,12 +917,14 @@ _cairo_gl_shader_compile (cairo_gl_context_t *ctx, assert (shader->program == 0); - vertex_shader = cairo_gl_var_type_hash (src, mask, CAIRO_GL_VAR_NONE); + 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)) @@ -1042,6 +1052,7 @@ 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) { @@ -1071,6 +1082,7 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, in, source, mask, + use_coverage, CAIRO_GL_OPERAND_NONE, &fs_source); if (unlikely (status)) @@ -1090,6 +1102,7 @@ _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, &entry->shader, cairo_gl_operand_get_var_type (source->type), cairo_gl_operand_get_var_type (mask->type), + use_coverage, fs_source); free (fs_source); diff --git a/src/cairo-gl-spans-compositor.c b/src/cairo-gl-spans-compositor.c new file mode 100644 index 000000000..57cccc9f9 --- /dev/null +++ b/src/cairo-gl-spans-compositor.c @@ -0,0 +1,502 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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; + + 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; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + _cairo_gl_composite_emit_rect (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; + + if (num_spans == 0) + return CAIRO_STATUS_SUCCESS; + + do { + if (spans[0].coverage) { + _cairo_gl_composite_emit_rect (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; + + if (y > r->ymin) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, r->ymin, + r->xmax, y, + 0); + } + + if (num_spans == 0) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, y, + r->xmax, y + height, + 0); + } else { + if (spans[0].x != r->xmin) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, y, + spans[0].x, y + height, + 0); + } + + do { + _cairo_gl_composite_emit_rect (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) { + _cairo_gl_composite_emit_rect (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; + + if (y > r->ymin) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, r->ymin, + r->xmax, y, + 0); + } + + if (num_spans == 0) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, y, + r->xmax, y + height, + 0); + } else { + if (spans[0].x != r->xmin) { + _cairo_gl_composite_emit_rect (r->ctx, + r->xmin, y, + spans[0].x, y + height, + 0); + } + + do { + _cairo_gl_composite_emit_rect (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) { + _cairo_gl_composite_emit_rect (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; + + if (r->ymax > r->ymin) { + _cairo_gl_composite_emit_rect (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; + 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); + _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2, 0); + } + } +} + +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, NULL); + 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; +} + +typedef struct cairo_gl_source { + cairo_surface_t base; + + cairo_gl_operand_t operand; +} cairo_gl_source_t; + +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 */ +}; + +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) +{ + cairo_gl_source_t *source; + cairo_int_status_t status; + + 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, + extents->x, extents->y, + extents->x, extents->y, + extents->width, extents->height); + if (unlikely (status)) { + cairo_surface_destroy (&source->base); + return _cairo_surface_create_in_error (status); + } + + return &source->base; +} + +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->operand; +} + +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, extents); + if (unlikely (status)) + goto FAIL; + + _cairo_gl_composite_set_source_operand (&setup, + source_to_operand (abstract_src)); + + _cairo_gl_composite_set_mask_operand (&setup, + source_to_operand (abstract_mask)); + + 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 +_cairo_gl_span_renderer_init (cairo_abstract_span_renderer_t *_r, + const cairo_composite_rectangles_t *composite, + 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; + + /* 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, &composite->unbounded); + if (unlikely (status)) + goto FAIL; + + status = _cairo_gl_composite_set_source (&r->setup, source, + composite->unbounded.x, + composite->unbounded.y, + composite->unbounded.x, + composite->unbounded.y, + composite->unbounded.width, + composite->unbounded.height); + 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->unbounded.x, + composite->unbounded.y, + composite->unbounded.x, + composite->unbounded.y, + composite->unbounded.width, + composite->unbounded.height); + 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; + + 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_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 compositor; + + if (compositor.base.delegate == NULL) { + /* The fallback to traps here is essentially just for glyphs... */ + _cairo_spans_compositor_init (&compositor, + _cairo_gl_traps_compositor_get()); + + compositor.fill_boxes = fill_boxes; + //compositor.check_composite_boxes = check_composite_boxes; + compositor.pattern_to_surface = pattern_to_surface; + compositor.composite_boxes = composite_boxes; + //compositor.check_span_renderer = check_span_renderer; + compositor.renderer_init = _cairo_gl_span_renderer_init; + compositor.renderer_fini = _cairo_gl_span_renderer_fini; + } + + return &compositor.base; +} diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c index ec7082049..dec4b82a3 100644 --- a/src/cairo-gl-surface.c +++ b/src/cairo-gl-surface.c @@ -43,31 +43,11 @@ #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" - -static cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects); - -static cairo_int_status_t -_cairo_gl_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - 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); +#include "cairo-surface-backend-private.h" static cairo_status_t _cairo_gl_surface_flush (void *abstract_surface); @@ -306,6 +286,9 @@ _cairo_gl_get_image_format_and_type_gl (pixman_format_code_t pixman_format, case PIXMAN_yv12: case PIXMAN_x2r10g10b10: case PIXMAN_a2r10g10b10: + case PIXMAN_r8g8b8x8: + case PIXMAN_r8g8b8a8: + case PIXMAN_x14r6g6b6: default: return FALSE; } @@ -509,10 +492,8 @@ cairo_gl_surface_create (cairo_device_t *abstract_device, 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 (_cairo_format_from_content (content), - width, height); - } + 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); @@ -545,7 +526,6 @@ cairo_gl_surface_create (cairo_device_t *abstract_device, } slim_hidden_def (cairo_gl_surface_create); - /** * cairo_gl_surface_create_for_texture: * @content: type of content in the surface @@ -592,7 +572,7 @@ cairo_gl_surface_create_for_texture (cairo_device_t *abstract_device, 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)); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_DEVICE_TYPE_MISMATCH)); status = _cairo_gl_context_acquire (abstract_device, &ctx); if (unlikely (status)) @@ -614,20 +594,19 @@ cairo_gl_surface_set_size (cairo_surface_t *abstract_surface, int height) { cairo_gl_surface_t *surface = (cairo_gl_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)); + _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)) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; } @@ -664,19 +643,18 @@ void cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) { cairo_gl_surface_t *surface = (cairo_gl_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)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (! _cairo_surface_is_gl (abstract_surface)) { - status = _cairo_surface_set_error (abstract_surface, - CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + _cairo_surface_set_error (abstract_surface, + CAIRO_STATUS_SURFACE_TYPE_MISMATCH); return; } @@ -694,10 +672,20 @@ cairo_gl_surface_swapbuffers (cairo_surface_t *abstract_surface) status = _cairo_gl_context_release (ctx, status); if (status) - status = _cairo_surface_set_error (abstract_surface, status); + status = _cairo_surface_set_error (abstract_surface, status); } } +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 width > 0 && height > 0 && + width <= ctx->max_framebuffer_size && + height <= ctx->max_framebuffer_size; +} + static cairo_surface_t * _cairo_gl_surface_create_similar (void *abstract_surface, cairo_content_t content, @@ -708,24 +696,15 @@ _cairo_gl_surface_create_similar (void *abstract_surface, cairo_gl_context_t *ctx; cairo_status_t status; - if (width < 1 || height < 1) - return cairo_image_surface_create (_cairo_format_from_content (content), - width, height); + 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); - if (width > ctx->max_framebuffer_size || - height > ctx->max_framebuffer_size) - { - surface = NULL; - goto RELEASE; - } - surface = _cairo_gl_surface_create_scratch (ctx, content, width, height); -RELEASE: status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (surface); @@ -735,6 +714,43 @@ _cairo_gl_surface_create_similar (void *abstract_surface, return surface; } +static cairo_int_status_t +_cairo_gl_surface_fill_alpha_channel (void *abstract_dst, + int x, int y, + int width, int height) +{ + cairo_gl_surface_t *dst = abstract_dst; + cairo_gl_composite_t setup; + cairo_gl_context_t *ctx; + 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, NULL); + 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_composite_emit_rect (ctx, x, y, x + width, y + height, 0); + + 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, @@ -797,8 +813,8 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, * 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 + * b. the row stride cannot be handled by GL itself using a 4 byte + * alignment constraint */ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && (src->width * cpp < src->stride - 3 || @@ -826,7 +842,6 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, data_start_gles2 != NULL ? data_start_gles2 : data_start); - free (data_start_gles2); /* If we just treated some rgb-only data as rgba, then we have to @@ -834,21 +849,9 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, * texture data. */ if (!has_alpha) { - cairo_rectangle_int_t rect; - - rect.x = dst_x; - rect.y = dst_y; - rect.width = width; - rect.height = height; - - _cairo_gl_composite_flush (ctx); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - _cairo_gl_surface_fill_rectangles (dst, - CAIRO_OPERATOR_SOURCE, - CAIRO_COLOR_BLACK, - &rect, 1); - _cairo_gl_composite_flush (ctx); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + _cairo_gl_surface_fill_alpha_channel (dst, + dst_x, dst_y, + width, height); } } else { cairo_surface_t *tmp; @@ -869,15 +872,10 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, cairo_surface_pattern_t tmp_pattern; _cairo_pattern_init_for_surface (&tmp_pattern, tmp); - _cairo_gl_surface_composite (CAIRO_OPERATOR_SOURCE, - &tmp_pattern.base, - NULL, - dst, - 0, 0, - 0, 0, - dst_x, dst_y, - width, height, - NULL); + status = _cairo_surface_paint (&dst->base, + CAIRO_OPERATOR_SOURCE, + &tmp_pattern.base, + NULL); _cairo_pattern_fini (&tmp_pattern.base); } @@ -896,17 +894,53 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst, 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_get_image (cairo_gl_surface_t *surface, - cairo_rectangle_int_t *interest, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *rect_out) +_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->depth) + ctx->dispatch.DeleteFramebuffers (1, &surface->depth); + if (surface->fb) + ctx->dispatch.DeleteFramebuffers (1, &surface->fb); + if (surface->owns_tex) + glDeleteTextures (1, &surface->tex); + + return _cairo_gl_context_release (ctx, status); +} + +static cairo_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 invert; cairo_status_t status; /* Want to use a switch statement here but the compiler gets whiny. */ @@ -927,22 +961,18 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t *surface, cpp = 1; } else { ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; + return NULL; } - status = _cairo_gl_context_acquire (surface->base.device, &ctx); - if (unlikely (status)) - return status; - /* * GLES2 supports only RGBA, UNSIGNED_BYTE so use that. * We are also using this format for ALPHA as 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 (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES) { + if (_cairo_gl_surface_flavor (surface) == CAIRO_GL_FLAVOR_ES) { format = GL_RGBA; - if (!_cairo_is_little_endian ()) { + if (! _cairo_is_little_endian ()) { if (surface->base.content == CAIRO_CONTENT_COLOR) pixman_format = PIXMAN_r8g8b8x8; else @@ -960,11 +990,20 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t *surface, image = (cairo_image_surface_t*) _cairo_image_surface_create_with_pixman_format (NULL, pixman_format, - interest->width, - interest->height, + extents->width, + extents->height, -1); if (unlikely (image->base.status)) - return _cairo_gl_context_release (ctx, image->base.status); + return &image->base; + + if (surface->base.serial == 0) + return &image->base; + + status = _cairo_gl_context_acquire (surface->base.device, &ctx); + if (unlikely (status)) { + cairo_surface_destroy (&image->base); + return _cairo_surface_create_in_error (status); + } /* 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 @@ -973,60 +1012,28 @@ _cairo_gl_surface_get_image (cairo_gl_surface_t *surface, _cairo_gl_composite_flush (ctx); _cairo_gl_context_set_destination (ctx, surface); + invert = ! _cairo_gl_surface_is_texture (surface) && + 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 (! _cairo_gl_surface_is_texture (surface) && - ctx->has_mesa_pack_invert) + if (invert) glPixelStorei (GL_PACK_INVERT_MESA, 1); - glReadPixels (interest->x, interest->y, - interest->width, interest->height, + glReadPixels (extents->x, extents->y, + extents->width, extents->height, format, type, image->data); - if (! _cairo_gl_surface_is_texture (surface) && - ctx->has_mesa_pack_invert) + if (invert) glPixelStorei (GL_PACK_INVERT_MESA, 0); status = _cairo_gl_context_release (ctx, status); if (unlikely (status)) { cairo_surface_destroy (&image->base); - return status; + image = (cairo_image_surface_t *) + _cairo_surface_create_in_error (status); } - *image_out = image; - if (rect_out != NULL) - *rect_out = *interest; - - return CAIRO_STATUS_SUCCESS; -} - -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->depth) - ctx->dispatch.DeleteFramebuffers (1, &surface->depth); - if (surface->fb) - ctx->dispatch.DeleteFramebuffers (1, &surface->fb); - if (surface->owns_tex) - glDeleteTextures (1, &surface->tex); - - return _cairo_gl_context_release (ctx, status); + return &image->base; } static cairo_status_t @@ -1042,7 +1049,10 @@ _cairo_gl_surface_acquire_source_image (void *abstract_surface, extents.x = extents.y = 0; extents.width = surface->width; extents.height = surface->height; - return _cairo_gl_surface_get_image (surface, &extents, image_out, NULL); + + *image_out = (cairo_image_surface_t *) + _cairo_gl_surface_map_to_image (surface, &extents); + return (*image_out)->base.status; } static void @@ -1053,536 +1063,18 @@ _cairo_gl_surface_release_source_image (void *abstract_surface, cairo_surface_destroy (&image->base); } -static cairo_status_t -_cairo_gl_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_gl_surface_t *surface = abstract_surface; - - *image_extra = NULL; - return _cairo_gl_surface_get_image (surface, interest_rect, image_out, - image_rect_out); -} - -static void -_cairo_gl_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - cairo_status_t status; - - status = _cairo_gl_surface_draw_image (abstract_surface, image, - 0, 0, - image->width, image->height, - image_rect->x, image_rect->y); - /* as we created the image, its format should be directly applicable */ - assert (status == CAIRO_STATUS_SUCCESS); - - cairo_surface_destroy (&image->base); -} - -static cairo_status_t -_cairo_gl_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_gl_surface_t *surface = abstract_surface; - - /* XXX: Use GLCopyTexImage2D to clone non-texture-surfaces */ - if (src->device == surface->base.device && - _cairo_gl_surface_is_texture ((cairo_gl_surface_t *) src)) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - cairo_gl_surface_t *clone; - cairo_status_t status; - - clone = (cairo_gl_surface_t *) - _cairo_gl_surface_create_similar (&surface->base, - src->content, - width, height); - if (clone == NULL) - return UNSUPPORTED ("create_similar failed"); - if (clone->base.status) - return clone->base.status; - - status = _cairo_gl_surface_draw_image (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (status) { - cairo_surface_destroy (&clone->base); - return status; - } - - *clone_out = &clone->base; - *clone_offset_x = src_x; - *clone_offset_y = src_y; - - return CAIRO_STATUS_SUCCESS; - } - - return UNSUPPORTED ("unknown src surface type in clone_similar"); -} - -/** Creates a cairo-gl pattern surface for the given trapezoids */ -static cairo_status_t -_cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst, - int dst_x, int dst_y, - int width, int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_antialias_t antialias, - cairo_surface_pattern_t *pattern) -{ - pixman_format_code_t pixman_format; - pixman_image_t *image; - cairo_surface_t *surface; - int i; - - pixman_format = antialias != CAIRO_ANTIALIAS_NONE ? PIXMAN_a8 : PIXMAN_a1, - image = pixman_image_create_bits (pixman_format, width, height, NULL, 0); - if (unlikely (image == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - for (i = 0; i < num_traps; i++) { - pixman_trapezoid_t trap; - - trap.top = _cairo_fixed_to_16_16 (traps[i].top); - trap.bottom = _cairo_fixed_to_16_16 (traps[i].bottom); - - trap.left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x); - trap.left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y); - trap.left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x); - trap.left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y); - - trap.right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x); - trap.right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y); - trap.right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x); - trap.right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y); - - pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); - } - - surface = _cairo_image_surface_create_for_pixman_image (image, - pixman_format); - if (unlikely (surface->status)) { - pixman_image_unref (image); - return surface->status; - } - - _cairo_pattern_init_for_surface (pattern, surface); - cairo_surface_destroy (surface); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_int_status_t -_cairo_gl_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - 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_gl_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) { - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_context_t *ctx; - cairo_int_status_t status; - cairo_gl_composite_t setup; - cairo_rectangle_int_t rect = { dst_x, dst_y, width, height }; - int dx, dy; - - if (op == CAIRO_OPERATOR_SOURCE && - mask == NULL && - src->type == CAIRO_PATTERN_TYPE_SURFACE && - _cairo_surface_is_image (((cairo_surface_pattern_t *) src)->surface) && - _cairo_matrix_is_integer_translation (&src->matrix, &dx, &dy)) { - cairo_image_surface_t *image = (cairo_image_surface_t *) - ((cairo_surface_pattern_t *) src)->surface; - dx += src_x; - dy += src_y; - if (dx >= 0 && - dy >= 0 && - dx + width <= (unsigned int) image->width && - dy + height <= (unsigned int) image->height) { - status = _cairo_gl_surface_draw_image (dst, image, - dx, dy, - width, height, - dst_x, dst_y); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - } - - status = _cairo_gl_composite_init (&setup, op, dst, - mask && mask->has_component_alpha, - &rect); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_source (&setup, src, - src_x, src_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, mask, - mask_x, mask_y, - dst_x, dst_y, - width, height); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - if (clip_region != NULL) { - int i, num_rectangles; - - num_rectangles = cairo_region_num_rectangles (clip_region); - - for (i = 0; i < num_rectangles; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, i, &rect); - _cairo_gl_composite_emit_rect (ctx, - rect.x, rect.y, - rect.x + rect.width, rect.y + rect.height, - 0); - } - } else { - _cairo_gl_composite_emit_rect (ctx, - dst_x, dst_y, - dst_x + width, dst_y + height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -static cairo_int_status_t -_cairo_gl_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_gl_surface_t *dst = abstract_dst; - cairo_surface_pattern_t traps_pattern; - cairo_int_status_t status; - - if (! _cairo_gl_operator_is_supported (op)) - return UNSUPPORTED ("unsupported operator"); - - status = _cairo_gl_get_traps_pattern (dst, - dst_x, dst_y, width, height, - traps, num_traps, antialias, - &traps_pattern); - if (unlikely (status)) - return status; - - status = _cairo_gl_surface_composite (op, - pattern, &traps_pattern.base, dst, - src_x, src_y, - 0, 0, - dst_x, dst_y, - width, height, - clip_region); - - _cairo_pattern_fini (&traps_pattern.base); - - assert (status != CAIRO_INT_STATUS_UNSUPPORTED); - return status; -} - -static cairo_int_status_t -_cairo_gl_surface_fill_rectangles (void *abstract_dst, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_solid_pattern_t solid; - cairo_gl_context_t *ctx; - cairo_status_t status; - cairo_gl_composite_t setup; - int i; - - status = _cairo_gl_composite_init (&setup, op, dst, - FALSE, - /* XXX */ NULL); - if (unlikely (status)) - goto CLEANUP; - - _cairo_pattern_init_solid (&solid, color); - status = _cairo_gl_composite_set_source (&setup, &solid.base, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_set_mask (&setup, NULL, - 0, 0, - 0, 0, - 0, 0); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_gl_composite_begin (&setup, &ctx); - if (unlikely (status)) - goto CLEANUP; - - for (i = 0; i < num_rects; i++) { - _cairo_gl_composite_emit_rect (ctx, - rects[i].x, - rects[i].y, - rects[i].x + rects[i].width, - rects[i].y + rects[i].height, - 0); - } - - status = _cairo_gl_context_release (ctx, status); - - CLEANUP: - _cairo_gl_composite_fini (&setup); - - return status; -} - -typedef struct _cairo_gl_surface_span_renderer { - cairo_span_renderer_t base; - - cairo_gl_composite_t setup; - - int xmin, xmax; - int ymin, ymax; - - cairo_gl_context_t *ctx; -} cairo_gl_surface_span_renderer_t; - -static cairo_status_t -_cairo_gl_render_bounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - do { - if (spans[0].coverage) { - _cairo_gl_composite_emit_rect (renderer->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_render_unbounded_spans (void *abstract_renderer, - int y, int height, - const cairo_half_open_span_t *spans, - unsigned num_spans) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (y > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, y, - 0); - } - - if (num_spans == 0) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - renderer->xmax, y + height, - 0); - } else { - if (spans[0].x != renderer->xmin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, y, - spans[0].x, y + height, - 0); - } - - do { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - spans[1].x, y + height, - spans[0].coverage); - spans++; - } while (--num_spans > 1); - - if (spans[0].x != renderer->xmax) { - _cairo_gl_composite_emit_rect (renderer->ctx, - spans[0].x, y, - renderer->xmax, y + height, - 0); - } - } - - renderer->ymin = y + height; - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_gl_finish_unbounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (renderer->ymax > renderer->ymin) { - _cairo_gl_composite_emit_rect (renderer->ctx, - renderer->xmin, renderer->ymin, - renderer->xmax, renderer->ymax, - 0); - } - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static cairo_status_t -_cairo_gl_finish_bounded_spans (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - return _cairo_gl_context_release (renderer->ctx, CAIRO_STATUS_SUCCESS); -} - -static void -_cairo_gl_surface_span_renderer_destroy (void *abstract_renderer) -{ - cairo_gl_surface_span_renderer_t *renderer = abstract_renderer; - - if (!renderer) - return; - - _cairo_gl_composite_fini (&renderer->setup); - - free (renderer); + return _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); } static cairo_bool_t -_cairo_gl_surface_check_span_renderer (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias) -{ - if (! _cairo_gl_operator_is_supported (op)) - return FALSE; - - return TRUE; - - (void) pattern; - (void) abstract_dst; - (void) antialias; -} - -static cairo_span_renderer_t * -_cairo_gl_surface_create_span_renderer (cairo_operator_t op, - const cairo_pattern_t *src, - void *abstract_dst, - cairo_antialias_t antialias, - const cairo_composite_rectangles_t *rects, - cairo_region_t *clip_region) -{ - cairo_gl_surface_t *dst = abstract_dst; - cairo_gl_surface_span_renderer_t *renderer; - cairo_status_t status; - const cairo_rectangle_int_t *extents; - - renderer = calloc (1, sizeof (*renderer)); - if (unlikely (renderer == NULL)) - return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY); - - renderer->base.destroy = _cairo_gl_surface_span_renderer_destroy; - if (rects->is_bounded) { - renderer->base.render_rows = _cairo_gl_render_bounded_spans; - renderer->base.finish = _cairo_gl_finish_bounded_spans; - extents = &rects->bounded; - } else { - renderer->base.render_rows = _cairo_gl_render_unbounded_spans; - renderer->base.finish = _cairo_gl_finish_unbounded_spans; - extents = &rects->unbounded; - } - renderer->xmin = extents->x; - renderer->xmax = extents->x + extents->width; - renderer->ymin = extents->y; - renderer->ymax = extents->y + extents->height; - - status = _cairo_gl_composite_init (&renderer->setup, - op, dst, - FALSE, extents); - if (unlikely (status)) - goto FAIL; - - status = _cairo_gl_composite_set_source (&renderer->setup, src, - extents->x, extents->y, - extents->x, extents->y, - extents->width, extents->height); - if (unlikely (status)) - goto FAIL; - - _cairo_gl_composite_set_mask_spans (&renderer->setup); - _cairo_gl_composite_set_clip_region (&renderer->setup, clip_region); - - status = _cairo_gl_composite_begin (&renderer->setup, &renderer->ctx); - if (unlikely (status)) - goto FAIL; - - return &renderer->base; - -FAIL: - _cairo_gl_composite_fini (&renderer->setup); - free (renderer); - return _cairo_span_renderer_create_in_error (status); -} - -cairo_bool_t _cairo_gl_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) { @@ -1596,16 +1088,6 @@ _cairo_gl_surface_get_extents (void *abstract_surface, return TRUE; } -static void -_cairo_gl_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_ON); -} - static cairo_status_t _cairo_gl_surface_flush (void *abstract_surface) { @@ -1627,18 +1109,20 @@ _cairo_gl_surface_flush (void *abstract_surface) return _cairo_gl_context_release (ctx, 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 *abstract_surface, +_cairo_gl_surface_paint (void *surface, cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) + const cairo_pattern_t *source, + const cairo_clip_t *clip) { - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_boxes_t boxes; - cairo_status_t status; - +#if 0 /* simplify the common case of clearing the surface */ if (clip == NULL) { if (op == CAIRO_OPERATOR_CLEAR) @@ -1650,93 +1134,43 @@ _cairo_gl_surface_paint (void *abstract_surface, &((cairo_solid_pattern_t *) source)->color); } } +#endif - _cairo_gl_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, - op, source, - clip); - if (unlikely (status)) - return status; - - status = _cairo_clip_to_boxes(extents.clip, &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _cairo_gl_clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - } - - _cairo_composite_rectangles_fini (&extents); + return _cairo_compositor_paint (get_compositor (surface), surface, + op, source, clip); +} - return status; +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 *abstract_surface, +_cairo_gl_surface_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, - const 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, - const cairo_clip_t *clip) + const cairo_clip_t *clip) { - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_int_status_t status; - - _cairo_gl_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, - &unbounded, - op, source, - path, style, ctm, - clip); - 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 = _cairo_gl_clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - } - _cairo_boxes_fini (&boxes); - } - - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - 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 = _cairo_gl_surface_polygon (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, antialias, - &extents); - } - _cairo_polygon_fini (&polygon); - } - - _cairo_composite_rectangles_fini (&extents); - - return status; + 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 *abstract_surface, +_cairo_gl_surface_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t*path, @@ -1745,51 +1179,24 @@ _cairo_gl_surface_fill (void *abstract_surface, cairo_antialias_t antialias, const cairo_clip_t *clip) { - cairo_gl_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_int_status_t status; - - _cairo_gl_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_fill (&extents, - &unbounded, - op, source, path, - clip); - 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 = _cairo_gl_clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - } - _cairo_boxes_fini (&boxes); - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - cairo_polygon_t polygon; - - _cairo_polygon_init_with_clip (&polygon, extents.clip); - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { - status = _cairo_gl_surface_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents); - } - _cairo_polygon_fini (&polygon); - } - - _cairo_composite_rectangles_fini (&extents); + return _cairo_compositor_fill (get_compositor (surface), surface, + op, source, path, + fill_rule, tolerance, antialias, + clip); +} - return status; +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); } const cairo_surface_backend_t _cairo_gl_surface_backend = { @@ -1799,34 +1206,26 @@ const cairo_surface_backend_t _cairo_gl_surface_backend = { _cairo_gl_surface_create_similar, NULL, /* similar image */ - NULL, /* map to image */ - NULL, /* unmap image */ + _cairo_gl_surface_map_to_image, + _cairo_gl_surface_unmap_image, _cairo_gl_surface_acquire_source_image, _cairo_gl_surface_release_source_image, - _cairo_gl_surface_acquire_dest_image, - _cairo_gl_surface_release_dest_image, - - _cairo_gl_surface_clone_similar, - _cairo_gl_surface_composite, - _cairo_gl_surface_fill_rectangles, - _cairo_gl_surface_composite_trapezoids, - _cairo_gl_surface_create_span_renderer, - _cairo_gl_surface_check_span_renderer, + NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ + _cairo_gl_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_gl_surface_get_font_options, + _cairo_image_surface_get_font_options, + _cairo_gl_surface_flush, NULL, /* mark_dirty_rectangle */ - _cairo_gl_surface_scaled_font_fini, - _cairo_gl_surface_scaled_glyph_fini, + _cairo_gl_surface_paint, - NULL, /* mask */ + _cairo_gl_surface_mask, _cairo_gl_surface_stroke, _cairo_gl_surface_fill, - _cairo_gl_surface_show_glyphs, /* show_glyphs */ - NULL /* snapshot */ + NULL, /* fill/stroke */ + _cairo_gl_surface_glyphs, }; diff --git a/src/cairo-gl-traps-compositor.c b/src/cairo-gl-traps-compositor.c new file mode 100644 index 000000000..afc74d0ed --- /dev/null +++ b/src/cairo-gl-traps-compositor.c @@ -0,0 +1,550 @@ +/* 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 + * Carl Worth + * Chris Wilson + * Eric Anholt + */ + +#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" + +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); + 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; + 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); + _cairo_gl_composite_emit_rect (ctx, x1, y1, x2, y2, 0); + } + } +} + +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, NULL); + 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; +} + +typedef struct cairo_gl_source { + cairo_surface_t base; + + cairo_gl_operand_t operand; +} cairo_gl_source_t; + +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 */ +}; + +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) +{ + cairo_gl_source_t *source; + cairo_int_status_t status; + + 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, + extents->x, extents->y, + extents->x, extents->y, + extents->width, extents->height); + if (unlikely (status)) { + cairo_surface_destroy (&source->base); + return _cairo_surface_create_in_error (status); + } + + return &source->base; +} + +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->operand; +} + +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, extents); + if (unlikely (status)) + goto FAIL; + + _cairo_gl_composite_set_source_operand (&setup, + source_to_operand (abstract_src)); + + _cairo_gl_composite_set_mask_operand (&setup, + source_to_operand (abstract_mask)); + + 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, NULL); + if (unlikely (status)) + goto FAIL; + + _cairo_gl_composite_set_source_operand (&setup, + source_to_operand (abstract_src)); + + _cairo_gl_composite_set_mask_operand (&setup, + source_to_operand (abstract_mask)); + + status = _cairo_gl_composite_begin (&setup, &ctx); + if (unlikely (status)) + goto FAIL; + + /* XXX clip */ + _cairo_gl_composite_emit_rect (ctx, dst_x, dst_y, dst_x+width, dst_y+height, 0); + 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, src, mask, + src_x, src_y, + mask_x, mask_y, + 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_gl_surface_t * +traps_to_surface (void *_dst, + const cairo_rectangle_int_t *extents, + cairo_antialias_t antialias, + cairo_traps_t *traps) +{ + 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_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 (cairo_gl_surface_t *)image; + } + + mask = _cairo_surface_create_similar_scratch (_dst, CAIRO_CONTENT_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); + 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_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_gl_surface_t *mask; + cairo_int_status_t status; + + mask = traps_to_surface (_dst, extents, antialias, traps); + if (unlikely (mask->base.status)) + return mask->base.status; + + status = _cairo_gl_composite_init (&setup, op, _dst, FALSE, NULL); + 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_composite_emit_rect (ctx, + dst_x, dst_y, + dst_x+extents->width, + dst_y+extents->height, 0); + status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); + +FAIL: + _cairo_gl_composite_fini (&setup); + cairo_surface_destroy (&mask->base); + 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_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); + 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, NULL); + 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_composite_emit_rect (ctx, + dst_x, dst_y, + dst_x+extents->width, + dst_y+extents->height, 0); + status = _cairo_gl_context_release (ctx, CAIRO_STATUS_SUCCESS); + +FAIL: + _cairo_gl_composite_fini (&setup); + cairo_surface_destroy (&mask->base); + return status; +} + +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 = 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_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; +} diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 1b0658d8b..c37fd8b40 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -41,6 +41,7 @@ #include "cairo-error-private.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) @@ -60,7 +61,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate); 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, @@ -800,11 +801,21 @@ _cairo_gstate_backend_to_user_rectangle (cairo_gstate_t *gstate, { cairo_matrix_t matrix_inverse; - cairo_matrix_multiply (&matrix_inverse, - &gstate->target->device_transform_inverse, - &gstate->ctm_inverse); - _cairo_matrix_transform_bounding_box (&matrix_inverse, - x1, y1, x2, y2, is_tight); + 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); + _cairo_matrix_transform_bounding_box (&matrix_inverse, + x1, y1, x2, y2, is_tight); + } + + else + { + if (is_tight) + *is_tight = TRUE; + } } /* XXX: NYI @@ -1346,45 +1357,29 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate) } static void -_cairo_gstate_traps_extents_to_user_rectangle (cairo_gstate_t *gstate, - cairo_traps_t *traps, - double *x1, double *y1, - double *x2, double *y2) +_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); + double px1, py1, px2, py2; - 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); - if (x1) - *x1 = px1; - if (y1) - *y1 = py1; - if (x2) - *x2 = px2; - if (y2) - *y2 = py2; - } + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + if (x1) + *x1 = px1; + if (y1) + *y1 = py1; + if (x2) + *x2 = px2; + if (y2) + *y2 = py2; } cairo_status_t @@ -1393,35 +1388,57 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate, 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) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; - return CAIRO_STATUS_SUCCESS; - } + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; - _cairo_traps_init (&traps); + if (gstate->stroke_style.line_width <= 0.0) + return CAIRO_STATUS_SUCCESS; - status = _cairo_path_fixed_stroke_to_traps (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, - x1, y1, x2, y2); + 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_fini (&traps); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + cairo_traps_t traps; + + _cairo_traps_init (&traps); + status = _cairo_path_fixed_stroke_to_traps (path, + &gstate->stroke_style, + &gstate->ctm, + &gstate->ctm_inverse, + gstate->tolerance, + &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); + } return status; } @@ -1433,32 +1450,54 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate, double *x2, double *y2) { cairo_status_t status; - cairo_traps_t traps; + cairo_box_t extents; + cairo_bool_t empty; - if (_cairo_path_fixed_fill_is_empty (path)) { - if (x1) - *x1 = 0.0; - if (y1) - *y1 = 0.0; - if (x2) - *x2 = 0.0; - if (y2) - *y2 = 0.0; + if (x1) + *x1 = 0.0; + if (y1) + *y1 = 0.0; + if (x2) + *x2 = 0.0; + if (y2) + *y2 = 0.0; + + if (_cairo_path_fixed_fill_is_empty (path)) return CAIRO_STATUS_SUCCESS; - } - _cairo_traps_init (&traps); + 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; - status = _cairo_path_fixed_fill_to_traps (path, - gstate->fill_rule, - gstate->tolerance, - &traps); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_gstate_traps_extents_to_user_rectangle (gstate, &traps, - x1, y1, x2, y2); + _cairo_traps_init (&traps); + + status = _cairo_path_fixed_fill_to_traps (path, + gstate->fill_rule, + gstate->tolerance, + &traps); + empty = traps.num_traps == 0; + if (! empty) + _cairo_traps_extents (&traps, &extents); + + _cairo_traps_fini (&traps); } - _cairo_traps_fini (&traps); + if (! empty) { + _cairo_gstate_extents_to_user_rectangle (gstate, &extents, + x1, y1, x2, y2); + } return status; } @@ -1834,12 +1873,12 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, int num_glyphs, cairo_glyph_text_info_t *info) { - cairo_pattern_union_t source_pattern; - 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 = NULL; + cairo_pattern_union_t source_pattern; + cairo_glyph_t *transformed_glyphs; + const cairo_pattern_t *pattern; + cairo_text_cluster_t *transformed_clusters; cairo_operator_t op; cairo_status_t status; @@ -1858,16 +1897,15 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, return status; transformed_glyphs = stack_transformed_glyphs; + transformed_clusters = stack_transformed_clusters; + 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); } if (info != NULL) { - transformed_clusters = stack_transformed_clusters; if (info->num_clusters > ARRAY_LENGTH (stack_transformed_clusters)) { transformed_clusters = cairo_text_cluster_allocate (info->num_clusters); if (unlikely (transformed_clusters == NULL)) { @@ -1876,24 +1914,24 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t *gstate, } } - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - info->clusters, - info->num_clusters, - info->cluster_flags, - transformed_glyphs, - &num_glyphs, - transformed_clusters); + _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + info->clusters, + info->num_clusters, + info->cluster_flags, + transformed_glyphs, + &num_glyphs, + transformed_clusters); } else { - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - NULL, 0, 0, - transformed_glyphs, - &num_glyphs, - NULL); + _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); @@ -1971,35 +2009,32 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate, int num_glyphs, cairo_path_fixed_t *path) { - cairo_status_t status; - cairo_glyph_t *transformed_glyphs; cairo_glyph_t stack_transformed_glyphs[CAIRO_STACK_ARRAY_LENGTH (cairo_glyph_t)]; + cairo_glyph_t *transformed_glyphs; + cairo_status_t status; status = _cairo_gstate_ensure_scaled_font (gstate); if (unlikely (status)) return status; if (num_glyphs < ARRAY_LENGTH (stack_transformed_glyphs)) { - transformed_glyphs = stack_transformed_glyphs; + transformed_glyphs = stack_transformed_glyphs; } else { transformed_glyphs = cairo_glyph_allocate (num_glyphs); if (unlikely (transformed_glyphs == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - status = _cairo_gstate_transform_glyphs_to_backend (gstate, - glyphs, num_glyphs, - NULL, 0, 0, - transformed_glyphs, - &num_glyphs, NULL); - if (unlikely (status)) - goto CLEANUP_GLYPHS; + _cairo_gstate_transform_glyphs_to_backend (gstate, + glyphs, num_glyphs, + NULL, 0, 0, + transformed_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); @@ -2039,7 +2074,7 @@ _cairo_gstate_get_antialias (cairo_gstate_t *gstate) * 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, @@ -2050,44 +2085,40 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, 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 */ - } else { - double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); - if (surface_extents.width == 0 || surface_extents.height == 0) { - /* No visible area. Don't draw anything */ - *num_transformed_glyphs = 0; - return CAIRO_STATUS_SUCCESS; - } - /* XXX We currently drop any glyphs that has its position outside - * of the surface boundaries by a safety margin depending on the - * font scale. This however can fail in extreme cases where the - * font has really long swashes for example... We can correctly - * handle that by looking the glyph up and using its device bbox - * to device if it's going to be visible, but I'm not inclined to - * do that now. - */ - x1 = surface_extents.x - scale10; - y1 = surface_extents.y - scale10; - x2 = surface_extents.x + (int) surface_extents.width + scale10; - y2 = surface_extents.y + (int) surface_extents.height + scale10; + drop = TRUE; + if (! _cairo_gstate_int_clip_extents (gstate, &surface_extents)) { + drop = FALSE; /* unbounded surface */ + } else { + double scale10 = 10 * _cairo_scaled_font_get_max_scale (gstate->scaled_font); + if (surface_extents.width == 0 || surface_extents.height == 0) { + /* No visible area. Don't draw anything */ + *num_transformed_glyphs = 0; + return; } + /* XXX We currently drop any glyphs that has its position outside + * of the surface boundaries by a safety margin depending on the + * font scale. This however can fail in extreme cases where the + * font has really long swashes for example... We can correctly + * handle that by looking the glyph up and using its device bbox + * to device if it's going to be visible, but I'm not inclined to + * do that now. + */ + x1 = surface_extents.x - scale10; + y1 = surface_extents.y - scale10; + x2 = surface_extents.x + (int) surface_extents.width + scale10; + y2 = surface_extents.y + (int) surface_extents.height + scale10; + } - if (!drop) - *num_transformed_glyphs = num_glyphs; - } else - num_transformed_glyphs = &j; + if (!drop) + *num_transformed_glyphs = num_glyphs; #define KEEP_GLYPH(glyph) (x1 <= glyph.x && glyph.x <= x2 && y1 <= glyph.y && glyph.y <= y2) @@ -2156,6 +2187,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, 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; @@ -2209,6 +2242,8 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, 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; @@ -2252,6 +2287,4 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t *gstate, transformed_glyphs[j] = tmp; } } - - return CAIRO_STATUS_SUCCESS; } diff --git a/src/cairo-image-compositor.c b/src/cairo-image-compositor.c new file mode 100644 index 000000000..35472de3d --- /dev/null +++ b/src/cairo-image-compositor.c @@ -0,0 +1,1545 @@ +/* -*- 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 + * Chris Wilson + */ + +/* 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" + +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; + + 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, + 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_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; + + if (fill_reduces_to_source (op, color, dst) && + color_to_pixel (color, dst->pixman_format, &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; + + if (fill_reduces_to_source (op, color, dst) && + color_to_pixel (color, dst->pixman_format, &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; + 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; + +#if PIXMAN_HAS_OP_LERP + pixman_image_composite32 (PIXMAN_OP_LERP, + 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 */ + 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 */ + 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; + struct _cairo_boxes_chunk *chunk; + int i; + + /* XXX consider using a region? saves multiple prepare-composite */ + + 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); + } + } + + 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; + + /* 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; +} + +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; + + 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; +} + +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_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; + + 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]; + cairo_bool_t component_alpha = FALSE; + uint8_t buf[2048]; + pixman_image_t *mask; + cairo_status_t status; + int i; + + /* 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. + */ + i = (info->extents.width + 3) & ~3; + if (i * info->extents.height > (int) sizeof (buf)) { + mask = pixman_image_create_bits (PIXMAN_a8, + info->extents.width, + info->extents.height, + NULL, 0); + } else { + memset (buf, 0, i * info->extents.height); + mask = pixman_image_create_bits (PIXMAN_a8, + info->extents.width, + info->extents.height, + (uint32_t *)buf, i); + } + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memset (glyph_cache, 0, sizeof (glyph_cache)); + status = CAIRO_STATUS_SUCCESS; + + 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 cache_index = glyph_index % ARRAY_LENGTH (glyph_cache); + 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); + 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 && + ! component_alpha) { + pixman_image_t *ca_mask; + + ca_mask = pixman_image_create_bits (PIXMAN_a8r8g8b8, + info->extents.width, + info->extents.height, + NULL, 0); + if (unlikely (ca_mask == NULL)) { + pixman_image_unref (mask); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + pixman_image_composite32 (PIXMAN_OP_SRC, + mask, 0, ca_mask, + 0, 0, + 0, 0, + 0, 0, + info->extents.width, + info->extents.height); + pixman_image_unref (mask); + mask = ca_mask; + component_alpha = TRUE; + } + + /* 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 - info->extents.x, y - info->extents.y, + glyph_surface->width, + glyph_surface->height); + } + } + + if (component_alpha) + 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); + + 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; + + 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; +} + +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; + compositor.composite_tristrip = composite_tristrip; + 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; + + if (op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_white.base; + op = PIXMAN_OP_OUT_REVERSE; + } 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; + } 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; + + 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; + + cairo_rectangle_int_t extents; + + float opacity; + int stride; + uint8_t *data; + + const cairo_composite_rectangles_t *composite; + pixman_image_t *src, *mask; + int src_x, src_y; + + uint8_t op; + + uint8_t buf[sizeof(cairo_abstract_span_renderer_t)-128]; +} 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->data + (y - r->extents.y) * r->stride; + mask += spans[0].x - r->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->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->data; + if (y > r->extents.y) { + len = (y - r->extents.y) * r->stride; + memset (mask, 0, len); + mask += len; + } + + r->extents.y = y + height; + r->data = mask + height * r->stride; + if (num_spans == 0) { + memset (mask, 0, height * r->stride); + } else { + uint8_t *row = mask; + + if (spans[0].x != r->extents.x) { + len = spans[0].x - r->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->extents.x + r->extents.width) { + len = r->extents.x + r->extents.width - spans[0].x; + memset (row, 0, len); + } + + row = mask; + while (--height) { + mask += r->stride; + memcpy (mask, row, r->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->extents.y < r->extents.height) + memset (r->data, 0, (r->extents.height - r->extents.y) * r->stride); + + 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; + + r->composite = composite; + r->mask = NULL; + r->src = NULL; + + if (needs_clip) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_white.base; + op = PIXMAN_OP_OUT_REVERSE; + } 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 PIXMAN_HAS_OP_LERP + op = PIXMAN_OP_LERP; +#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->src_x, &r->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->src_x = mask_x; + r->src_y = mask_y; + mask = NULL; + } + + if (mask) { + pixman_image_unref (mask); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + r->extents = composite->unbounded; + r->stride = (r->extents.width + 3) & ~3; + if (r->extents.height * r->stride > (int)sizeof (r->buf)) { + r->mask = pixman_image_create_bits (PIXMAN_a8, + r->extents.width, + r->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->extents.width, + r->extents.height, + (uint32_t *)r->buf, r->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->data = (uint8_t *) pixman_image_get_data (r->mask); + r->stride = pixman_image_get_stride (r->mask); + + r->extents.height += r->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; + + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + const cairo_composite_rectangles_t *composite = r->composite; + + if (r->base.finish) + r->base.finish (r); + + pixman_image_composite32 (r->op, r->src, r->mask, + to_pixman_image (composite->surface), + composite->unbounded.x + r->src_x, + composite->unbounded.y + r->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 compositor; + + if (compositor.base.delegate == NULL) { + _cairo_spans_compositor_init (&compositor, + _cairo_image_traps_compositor_get()); + + //compositor.acquire = acquire; + //compositor.release = release; + compositor.fill_boxes = fill_boxes; + compositor.pattern_to_surface = _cairo_image_source_create_for_pattern; + //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; +} diff --git a/src/cairo-image-mask-compositor.c b/src/cairo-image-mask-compositor.c new file mode 100644 index 000000000..33fd6dd87 --- /dev/null +++ b/src/cairo-image-mask-compositor.c @@ -0,0 +1,408 @@ +/* -*- 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 + * Chris Wilson + */ + +/* This compositor is slightly pointless. Just exists for testing + * and as skeleton code. + */ + +#include "cairoint.h" + +#include "cairo-image-surface-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-region-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_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_bool_t +has_snapshot (void *_dst, + const cairo_pattern_t *pattern) +{ + return FALSE; +} + +static cairo_int_status_t +draw_image (void *_dst, + cairo_image_surface_t *image, + int src_x, int src_y, + int width, int height, + int dst_x, int dst_y) +{ + cairo_image_surface_t *dst = (cairo_image_surface_t *)_dst; + + pixman_image_composite32 (PIXMAN_OP_SRC, + image->pixman_image, NULL, dst->pixman_image, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, height); + 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, + double opacity, + pixman_format_code_t format, + uint32_t *pixel) +{ + cairo_color_t opacity_color; + 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; + } + + if (opacity != 1.0) { + _cairo_color_init_rgba (&opacity_color, + color->red, + color->green, + color->blue, + color->alpha * opacity); + color = &opacity_color; + } + 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 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; + + if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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); + } + + 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; + + assert (boxes->is_pixel_aligned); + + if (! color_to_pixel (color, 1.0, dst->pixman_format, &pixel)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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_fill ((uint32_t *) dst->data, dst->stride / sizeof (uint32_t), + PIXMAN_FORMAT_BPP (dst->pixman_format), + x1, y1, x2 - x1, y2 - y1, + pixel); + } + } + + return CAIRO_STATUS_SUCCESS; +} + +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_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_surface_t *dst = _dst; + cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src; + cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask; + if (mask) { + pixman_image_composite32 (_pixman_operator (op), + src->pixman_image, mask->pixman_image, dst->pixman_image, + 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, dst->pixman_image, + src_x, src_y, + 0, 0, + dst_x, dst_y, + width, 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) +{ + cairo_image_surface_t *dst = _dst; + cairo_pixman_source_t *src = (cairo_pixman_source_t *)abstract_src; + cairo_pixman_source_t *mask = (cairo_pixman_source_t *)abstract_mask; + struct _cairo_boxes_chunk *chunk; + int i; + + assert (boxes->is_pixel_aligned); + + 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); + + if (mask) { + pixman_image_composite32 (op, + src->pixman_image, mask->pixman_image, dst->pixman_image, + x1 + src_x, y1 + src_y, + x1 + mask_x, y1 + mask_y, + x1 + dst_x, y1 + dst_y, + x2 - x1, y2 - y1); + } else { + pixman_image_composite32 (op, + src->pixman_image, NULL, dst->pixman_image, + x1 + src_x, y1 + src_y, + 0, 0, + x1 + dst_x, y1 + dst_y, + x2 - x1, y2 - y1); + } + } + } + + return CAIRO_STATUS_SUCCESS; +} + +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_pixman_source_create_for_pattern; + compositor.has_snapshot = has_snapshot; + compositor.draw_image = draw_image; + compositor.fill_rectangles = fill_rectangles; + compositor.fill_boxes = fill_boxes; + //compositor.check_composite = check_composite; + compositor.composite = composite; + //compositor.check_composite_boxes = check_composite_boxes; + compositor.composite_boxes = composite_boxes; + } + + return &compositor.base; +} diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c new file mode 100644 index 000000000..7c1eb8260 --- /dev/null +++ b/src/cairo-image-source.c @@ -0,0 +1,975 @@ +/* -*- 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 + * Chris Wilson + */ + +/* 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-private.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-observer-private.h" +#include "cairo-surface-snapshot-private.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; + + 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; +} + +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) +{ + return _pixman_image_for_color (CAIRO_COLOR_TRANSPARENT); +} + +static pixman_image_t * +_pixman_black_image (void) +{ + return _pixman_image_for_color (CAIRO_COLOR_BLACK); +} + +static pixman_image_t * +_pixman_white_image (void) +{ + 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; + + 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; + + *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 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_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; +} + +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; + 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; + + *ix = *iy = 0; + + source = pattern->surface; + if (_cairo_surface_is_subsurface (source)) + source = _cairo_surface_subsurface_get_target_with_offset (source, &tx, &ty); + if (_cairo_surface_is_snapshot (source)) + source = _cairo_surface_snapshot_get_target (source); + if (_cairo_surface_is_observer (source)) + source = _cairo_surface_observer_get_target (source); + if (_cairo_surface_is_paginated (source)) + source = _cairo_paginated_surface_get_target (source); + + extend = pattern->base.extend; + if (_cairo_surface_get_extents (source, &limit)) { + if (sample->x >= limit.x && + sample->y >= limit.y && + sample->x + sample->width <= limit.x + limit.width && + sample->y + sample->height <= limit.y + limit.height) + { + extend = CAIRO_EXTEND_NONE; + } + else if (extend == CAIRO_EXTEND_NONE && + (sample->x + sample->width <= limit.x || + sample->x >= limit.x + limit.width || + sample->y + sample->height <= limit.y || + sample->y >= limit.y + limit.height)) + { + return _pixman_transparent_image (); + } + } else + extend = CAIRO_EXTEND_NONE; + + if (extents == CAIRO_EXTEND_NONE) + limit = *extents; + + clone = cairo_image_surface_create (dst->format, limit.width, limit.height); + cairo_surface_set_device_offset (clone, limit.x, limit.y); + + m = NULL; + if (extend == CAIRO_EXTEND_NONE) { + m = &matrix; + cairo_matrix_multiply (m, + &dst->base.device_transform, + &pattern->base.matrix); + if (tx | ty) + cairo_matrix_translate (m, tx, ty); + } else { + /* XXX extract scale factor for repeating patterns */ + } + + status = _cairo_recording_surface_replay_with_clip (source, m, clone, NULL); + if (unlikely (status)) { + cairo_surface_destroy (clone); + return NULL; + } + + pixman_image = pixman_image_ref (((cairo_image_surface_t *)clone)->pixman_image); + cairo_surface_destroy (clone); + + 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; + + *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_image_surface_t *source = (cairo_image_surface_t *) pattern->surface; + cairo_surface_type_t type; + + if (_cairo_surface_is_snapshot (&source->base)) + source = (cairo_image_surface_t *) _cairo_surface_snapshot_get_target (&source->base); + + 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 + { + pixman_image = _pixel_to_solid (source, + sample->x, sample->y); + if (pixman_image) + return pixman_image; + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + /* avoid allocating a 'pattern' image if we can reuse the original */ + *ix = *iy = 0; + if (extend == CAIRO_EXTEND_NONE && + _cairo_matrix_is_pixman_translation (&pattern->base.matrix, + pattern->base.filter, + ix, iy)) + { + 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)) + 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) { + 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) { + 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; + } + } + } + +#if PIXMAN_HAS_ATOMIC_OPS + *ix = *iy = 0; +#endif + 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->x >= 0 && sample->y >= 0 && + sample->x + sample->width <= image->width && + sample->y + sample->height <= image->height) + { + extend = CAIRO_EXTEND_NONE; + } + + 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); + if (pixman_image) { + _cairo_surface_release_source_image (pattern->surface, image, extra); + return pixman_image; + } + } + } + + 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; +} + +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; + + 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); + + } +} + +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; +} + +static 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; + + 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; +} diff --git a/src/cairo-image-spans-compositor.c b/src/cairo-image-spans-compositor.c new file mode 100644 index 000000000..5718b5553 --- /dev/null +++ b/src/cairo-image-spans-compositor.c @@ -0,0 +1,131 @@ +/* -*- 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 + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-spans-compositor-private.h" + +typedef struct _cairo_image_span_renderer { + cairo_span_renderer_t base; + + pixman_image_compositor_t *compositor; + pixman_image_t *src; + float opacity; + cairo_rectangle_int_t extents; +} cairo_image_span_renderer_t; + +static cairo_status_t +_cairo_image_span_renderer_init (cairo_abstract_span_renderer_t *_r, + cairo_surface_t *dst, + cairo_operator_t op, + cairo_surface_t *src, + int src_x, int src_y; + float opacity, + const cairo_composite_rectangles_t *composite, + cairo_bool_t needs_clip) +{ + cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *)_r; + cairo_pixman_source_t *src = (cairo_pixman_source_t *)_src; + int src_x, src_y; + + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + pattern = NULL; + } + + r->src = ((cairo_pixman_source_t *) src)->pixman_image; + r->opacity = opacity; + + if (composite->is_bounded) { + if (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 (_pixman_operator (op), + r->src, NULL, dst->pixman_image, + composite->bounded.x + src_x, + composite->bounded.y + src_y, + 0, 0, + composite->bounded.x, + composite->bounded.y, + composite->bounded.width, + composite->bounded.height); + if (unlikely (r->compositor == NULL)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_image_span_renderer_fini (cairo_abstract_span_renderer_t *_r) +{ + cairo_image_span_renderer_t *r = (cairo_image_span_renderer_t *) r; + pixman_image_compositor_destroy (r->compositor); +} + +const cairo_compositor_t * +_cairo_image_spans_compositor_get (void) +{ + static cairo_spans_compositor_t compositor; + + if (compositor.base.delegate == NULL) { + /* Can't fallback to the mask compositor as that will simply + * call the spans-compositor again to render the mask! + */ + _cairo_spans_compositor_init (&compositor, + _cairo_image_traps_compositor_get()); + + } + + return &compositor.base; +} diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h index 56a1dc404..227a447c3 100644 --- a/src/cairo-image-surface-private.h +++ b/src/cairo-image-surface-private.h @@ -44,9 +44,13 @@ 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; + pixman_format_code_t pixman_format; cairo_format_t format; unsigned char *data; @@ -56,20 +60,55 @@ struct _cairo_image_surface { int stride; int depth; - pixman_image_t *pixman_image; - unsigned owns_data : 1; unsigned transparency : 2; unsigned color : 2; }; -extern const cairo_private cairo_surface_backend_t _cairo_image_surface_backend; +/* 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 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); 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_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_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); @@ -78,9 +117,52 @@ 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_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) +{ + return surface->backend == &_cairo_image_surface_backend; +} + CAIRO_END_DECLS #endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */ diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index bda05eb9f..6adbdd61f 100644 --- a/src/cairo-image-surface.c +++ b/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,6 +42,7 @@ #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-private.h" @@ -57,7 +58,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 @@ -80,9 +80,6 @@ * @Since: 1.8 */ -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) { @@ -165,6 +162,8 @@ _cairo_image_surface_init (cairo_image_surface_t *surface, 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 * @@ -391,7 +390,7 @@ cairo_image_surface_create (cairo_format_t format, } slim_hidden_def (cairo_image_surface_create); -cairo_surface_t * + cairo_surface_t * _cairo_image_surface_create_with_content (cairo_content_t content, int width, int height) @@ -427,7 +426,7 @@ _cairo_image_surface_create_with_content (cairo_content_t content, * * Since: 1.6 **/ -int + int cairo_format_stride_for_width (cairo_format_t format, int width) { @@ -489,7 +488,7 @@ slim_hidden_def (cairo_format_stride_for_width); * See cairo_surface_set_user_data() for a means of attaching a * destroy-notification fallback to the surface if necessary. **/ -cairo_surface_t * + cairo_surface_t * cairo_image_surface_create_for_data (unsigned char *data, cairo_format_t format, int width, @@ -545,7 +544,7 @@ slim_hidden_def (cairo_image_surface_create_for_data); * * Since: 1.2 **/ -unsigned char * + unsigned char * cairo_image_surface_get_data (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; @@ -569,7 +568,7 @@ slim_hidden_def (cairo_image_surface_get_data); * * Since: 1.2 **/ -cairo_format_t + cairo_format_t cairo_image_surface_get_format (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; @@ -591,7 +590,7 @@ slim_hidden_def (cairo_image_surface_get_format); * * Return value: the width of the surface in pixels. **/ -int + int cairo_image_surface_get_width (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; @@ -613,7 +612,7 @@ slim_hidden_def (cairo_image_surface_get_width); * * Return value: the height of the surface in pixels. **/ -int + int cairo_image_surface_get_height (cairo_surface_t *surface) { cairo_image_surface_t *image_surface = (cairo_image_surface_t *) surface; @@ -640,7 +639,7 @@ slim_hidden_def (cairo_image_surface_get_height); * * Since: 1.2 **/ -int + int cairo_image_surface_get_stride (cairo_surface_t *surface) { @@ -655,7 +654,7 @@ cairo_image_surface_get_stride (cairo_surface_t *surface) } slim_hidden_def (cairo_image_surface_get_stride); -cairo_format_t + cairo_format_t _cairo_format_from_content (cairo_content_t content) { switch (content) { @@ -671,7 +670,7 @@ _cairo_format_from_content (cairo_content_t content) return CAIRO_FORMAT_INVALID; } -cairo_content_t + cairo_content_t _cairo_content_from_format (cairo_format_t format) { switch (format) { @@ -694,7 +693,7 @@ _cairo_content_from_format (cairo_format_t format) return CAIRO_CONTENT_COLOR_ALPHA; } -int + int _cairo_format_bits_per_pixel (cairo_format_t format) { switch (format) { @@ -715,7 +714,7 @@ _cairo_format_bits_per_pixel (cairo_format_t format) } } -static cairo_surface_t * + static cairo_surface_t * _cairo_image_surface_create_similar (void *abstract_other, cairo_content_t content, int width, @@ -737,7 +736,7 @@ _cairo_image_surface_create_similar (void *abstract_other, width, height); } -static cairo_surface_t * +cairo_surface_t * _cairo_image_surface_snapshot (void *abstract_surface) { cairo_image_surface_t *image = abstract_surface; @@ -766,8 +765,7 @@ _cairo_image_surface_snapshot (void *abstract_surface) return &clone->base; } - -static cairo_surface_t * +cairo_surface_t * _cairo_image_surface_map_to_image (void *abstract_other, const cairo_rectangle_int_t *extents) { @@ -790,7 +788,7 @@ _cairo_image_surface_map_to_image (void *abstract_other, return surface; } -static cairo_int_status_t +cairo_int_status_t _cairo_image_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { @@ -821,7 +819,7 @@ _cairo_image_surface_assume_ownership_of_data (cairo_image_surface_t *surface) surface->owns_data = TRUE; } -static cairo_status_t +cairo_status_t _cairo_image_surface_acquire_source_image (void *abstract_surface, cairo_image_surface_t **image_out, void **image_extra) @@ -832,4091 +830,204 @@ _cairo_image_surface_acquire_source_image (void *abstract_sur return CAIRO_STATUS_SUCCESS; } -static void +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); -} - -#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; - - 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) +/* high level image interface */ +cairo_bool_t +_cairo_image_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) { - 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; + cairo_image_surface_t *surface = abstract_surface; - if (_cairo_atomic_ptr_cmpxchg (&__pixman_black_image, - NULL, image)) - { - pixman_image_ref (image); - } - } else { - pixman_image_ref (image); - } + rectangle->x = 0; + rectangle->y = 0; + rectangle->width = surface->width; + rectangle->height = surface->height; - return image; + return TRUE; } -static pixman_image_t * -_pixman_white_image (void) +static 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) { - 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; + cairo_image_surface_t *surface = abstract_surface; + return _cairo_compositor_paint (surface->compositor, + &surface->base, op, source, clip); } -static uint32_t -hars_petruska_f54_1_random (void) +static 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) { -#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 + cairo_image_surface_t *surface = abstract_surface; + return _cairo_compositor_mask (surface->compositor, + &surface->base, op, source, mask, clip); } -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) +static 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) { - return _pixman_image_for_solid (&_cairo_pattern_clear); + cairo_image_surface_t *surface = abstract_surface; + return _cairo_compositor_stroke (surface->compositor, &surface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); } -static pixman_image_t * -_pixman_black_image (void) +static 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) { - return _pixman_image_for_solid (&_cairo_pattern_black); + cairo_image_surface_t *surface = abstract_surface; + return _cairo_compositor_fill (surface->compositor, &surface->base, + op, source, path, + fill_rule, tolerance, antialias, + clip); } -static pixman_image_t * -_pixman_white_image (void) +static 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) { - return _pixman_image_for_solid (&_cairo_pattern_white); + cairo_image_surface_t *surface = abstract_surface; + return _cairo_compositor_glyphs (surface->compositor, &surface->base, + op, source, + glyphs, num_glyphs, scaled_font, + clip); } -#endif /* !PIXMAN_HAS_ATOMIC_OPS */ 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_solid (const cairo_solid_pattern_t *pattern) +_cairo_image_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) { - pixman_color_t color; - pixman_image_t *image; - -#if PIXMAN_HAS_ATOMIC_OPS - int i; - - 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 (); - } - } - - 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; - } - } -#endif - - 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 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 = pattern->color; + _cairo_font_options_init_default (options); -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_image_solid_cache_mutex); -#endif - return image; + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } -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; - - 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); - } +const cairo_surface_backend_t _cairo_image_surface_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_image_surface_finish, - if (pixman_stops != pixman_stops_static) - free (pixman_stops); + _cairo_default_context_create, - if (unlikely (pixman_image == NULL)) - return NULL; + _cairo_image_surface_create_similar, + NULL, /* create similar image */ + _cairo_image_surface_map_to_image, + _cairo_image_surface_unmap_image, - *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; - } - } + _cairo_image_surface_acquire_source_image, + _cairo_image_surface_release_source_image, + _cairo_image_surface_snapshot, - { - 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; - } + NULL, /* copy_page */ + NULL, /* show_page */ - pixman_image_set_repeat (pixman_image, pixman_repeat); - } + _cairo_image_surface_get_extents, + _cairo_image_surface_get_font_options, - return pixman_image; -} + NULL, /* flush */ + NULL, /* mark dirty */ -struct acquire_source_cleanup { - cairo_surface_t *surface; - cairo_image_surface_t *image; - void *image_extra; + _cairo_image_surface_paint, + _cairo_image_surface_mask, + _cairo_image_surface_stroke, + _cairo_image_surface_fill, + NULL, /* fill-stroke */ + _cairo_image_surface_glyphs, }; -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) +/* A convenience function for when one needs to coerce an image + * surface to an alternate format. */ +cairo_image_surface_t * +_cairo_image_surface_coerce (cairo_image_surface_t *surface) { - int offset = 16 - bits; - while (offset > 0) { - v |= v >> bits; - offset -= bits; - bits += bits; - } - return v; + return _cairo_image_surface_coerce_to_format (surface, + _cairo_format_from_content (surface->base.content)); } -static pixman_image_t * -_pixel_to_solid (cairo_image_surface_t *image, int x, int y) +/* A convenience function for when one needs to coerce an image + * surface to an alternate format. */ +cairo_image_surface_t * +_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, + cairo_format_t format) { - uint32_t pixel; - pixman_color_t color; - - switch (image->format) { - default: - ASSERT_NOT_REACHED; - return NULL; - - case CAIRO_FORMAT_INVALID: - return NULL; + cairo_image_surface_t *clone; + cairo_status_t status; - 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 (); + status = surface->base.status; + if (unlikely (status)) + return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); - 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 (); + if (surface->format == format) + return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); - color.red = color.green = color.blue = 0; - return pixman_image_create_solid_fill (&color); + clone = (cairo_image_surface_t *) + cairo_image_surface_create (format, surface->width, surface->height); + if (unlikely (clone->base.status)) + return clone; - 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); + pixman_image_composite32 (PIXMAN_OP_SRC, + surface->pixman_image, NULL, clone->pixman_image, + 0, 0, + 0, 0, + 0, 0, + surface->width, surface->height); + clone->base.is_clear = FALSE; - 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); + clone->base.device_transform = + surface->base.device_transform; + clone->base.device_transform_inverse = + surface->base.device_transform_inverse; - 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); - } + return clone; } -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, - cairo_matrix_t *dst_device_transform, - int *ix, int *iy) +cairo_image_transparency_t +_cairo_image_analyze_transparency (cairo_image_surface_t *image) { - pixman_transform_t pixman_transform; - pixman_image_t *pixman_image; - cairo_matrix_t m; - cairo_rectangle_int_t sample; - cairo_extend_t extend; - cairo_filter_t filter; - cairo_int_status_t status; - cairo_bool_t undo_src_transform = FALSE; - - extend = pattern->base.extend; - filter = sampled_area (pattern, extents, &sample); - - *ix = *iy = 0; - 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 (_cairo_surface_is_snapshot (&source->base)) - source = (cairo_image_surface_t *) _cairo_surface_snapshot_get_target (&source->base); - - 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 - { - pixman_image = _pixel_to_solid (source, sample.x, sample.y); - if (pixman_image) - return pixman_image; - } - } + int x, y; -#if PIXMAN_HAS_ATOMIC_OPS - /* avoid allocating a 'pattern' image if we can reuse the original */ - *ix = *iy = 0; - if (extend == CAIRO_EXTEND_NONE && - _cairo_matrix_is_pixman_translation (&pattern->base.matrix, - filter, ix, iy)) - { - return pixman_image_ref (source->pixman_image); - } -#endif + if (image->transparency != CAIRO_IMAGE_UNKNOWN) + return image->transparency; - 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 ((image->base.content & CAIRO_CONTENT_ALPHA) == 0) + return image->transparency = CAIRO_IMAGE_IS_OPAQUE; - 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, - 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) { - 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; - } - } - } - -#if PIXMAN_HAS_ATOMIC_OPS - *ix = *iy = 0; -#endif - - 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_transformed (pattern->surface, dst_device_transform, &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); - if (pixman_image) - { - _cairo_surface_release_source_image (pattern->surface, image, extra); - return pixman_image; - } - } - } - - 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); - undo_src_transform = TRUE; - } - - m = pattern->base.matrix; - if (undo_src_transform) { - cairo_matrix_t sm; - - cairo_matrix_init_scale (&sm, - dst_device_transform->xx, - dst_device_transform->yy); - cairo_matrix_multiply (&m, &m, &sm); - } - - status = _cairo_matrix_to_pixman_matrix_offset (&m, 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))) - { - pixman_image_unref (pixman_image); - return NULL; - } - 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, - cairo_matrix_t *dst_device_transform, - 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, dst_device_transform, tx, ty); - - case CAIRO_PATTERN_TYPE_MESH: { - cairo_surface_t *image; - pixman_image_t *r; - void * data; - int width, height, stride; - - *tx = -extents->x; - *ty = -extents->y; - width = extents->width; - height = extents->height; - - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - if (unlikely (image->status)) - return NULL; - - stride = cairo_image_surface_get_stride (image); - data = cairo_image_surface_get_data (image); - - _cairo_mesh_pattern_rasterize ((cairo_mesh_pattern_t *) pattern, - data, width, height, stride, *tx, *ty); - r = pixman_image_ref (((cairo_image_surface_t *)image)->pixman_image); - cairo_surface_destroy (image); - return r; - } - } -} - -static cairo_status_t -_cairo_image_surface_fixup_unbounded (cairo_image_surface_t *dst, - const cairo_composite_rectangles_t *rects, - cairo_clip_t *clip) -{ - cairo_surface_t *clip_surface = NULL; - pixman_image_t *mask = NULL; - pixman_box32_t boxes[4]; - int i, mask_x = 0, mask_y = 0, n_boxes = 0; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - - if (clip != NULL) { - 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)) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - cairo_surface_destroy (clip_surface); - return status; - } - - /* 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)) - { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - cairo_surface_destroy (clip_surface); - return status; -} - -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, 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)) { - 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); - } - } - } - - _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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents); - -static pixman_image_t * -_create_composite_mask_pattern (cairo_composite_rectangles_t *extents, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst) -{ - cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); - cairo_bool_t need_clip_surface = ! _cairo_clip_is_region (extents->clip); - pixman_image_t *mask; - cairo_status_t status; - - if (clip_region != NULL && cairo_region_num_rectangles (clip_region) == 1) - clip_region = NULL; - - mask = pixman_image_create_bits (PIXMAN_a8, extents->bounded.width, extents->bounded.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->bounded.x, -extents->bounded.y); - ret = pixman_image_set_clip_region32 (mask, &clip_region->rgn); - pixman_region32_translate (&clip_region->rgn, extents->bounded.x, extents->bounded.y); - - if (! ret) { - pixman_image_unref (mask); - return NULL; - } - } - - status = draw_func (draw_closure, - mask, PIXMAN_a8, - CAIRO_OPERATOR_ADD, NULL, - extents->bounded.x, extents->bounded.y, - &dst->base.device_transform, - extents); - 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 (extents->clip, tmp, - extents->bounded.x, - extents->bounded.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_composite_rectangles_t *extents, - cairo_operator_t op, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst) -{ - pixman_image_t *mask; - - mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst); - 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->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.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->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.height); - pixman_image_unref (src); - } - } else { - pixman_image_t *src; - int src_x, src_y; - - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, - &dst->base.device_transform, - &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->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); - } - - 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_composite_rectangles_t *extents, - cairo_operator_t op, - const cairo_pattern_t *src, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst) -{ - 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->bounded.width, - extents->bounded.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->bounded.x, extents->bounded.y, - &dst->base.device_transform, - extents); - } 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->bounded.x, - extents->bounded.y, - 0, 0, - 0, 0, - extents->bounded.width, - extents->bounded.height); - } - - status = (*draw_func) (draw_closure, - tmp, dst->pixman_format, - op, src, - extents->bounded.x, extents->bounded.y, - &dst->base.device_transform, - extents); - } - if (unlikely (status)) - goto CLEANUP_SURFACE; - - clip_surface = _cairo_clip_get_surface (extents->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->bounded.x - clip_x, - extents->bounded.y - clip_y, - extents->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.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->bounded.x - clip_x, - extents->bounded.y - clip_y, - 0, 0, - extents->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.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->bounded.x - clip_x, - extents->bounded.y - clip_y, - extents->bounded.x, extents->bounded.y, - extents->bounded.width, extents->bounded.height); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - tmp, - ((cairo_image_surface_t *) clip_surface)->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_surface); - CLEANUP_SURFACE: - pixman_image_unref (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 (cairo_composite_rectangles_t *extents, - const cairo_pattern_t *pattern, - image_draw_func_t draw_func, - void *draw_closure, - cairo_image_surface_t *dst) -{ - pixman_image_t *mask, *src; - int src_x, src_y; - - if (pattern == NULL) { - cairo_status_t status; - - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - CAIRO_OPERATOR_SOURCE, NULL, - extents->bounded.x, extents->bounded.y, - &dst->base.device_transform, - extents); - if (unlikely (status)) - return status; - - if (! _cairo_clip_is_region (extents->clip)) - status = _cairo_clip_combine_with_surface (extents->clip, - &dst->base, 0, 0); - - return status; - } - - /* Create a surface that is mask IN clip */ - mask = _create_composite_mask_pattern (extents, draw_func, draw_closure, dst); - if (unlikely (mask == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, - &dst->base.device_transform, - &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->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) */ - pixman_image_composite32 (PIXMAN_OP_OUT_REVERSE, - mask, NULL, dst->pixman_image, - 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' */ - pixman_image_composite32 (PIXMAN_OP_ADD, - 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); -#endif - } else { - pixman_image_composite32 (PIXMAN_OP_SRC, - 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); - pixman_image_unref (mask); - - 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_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, - unsigned int need_clip) -{ - cairo_region_t *clip_region = NULL; - cairo_status_t status; - - if (need_clip & NEED_CLIP_REGION) { - clip_region = _cairo_clip_get_region (extents->clip); - if ((need_clip & FORCE_CLIP_REGION) == 0 && - cairo_region_contains_rectangle (clip_region, - &extents->unbounded) == CAIRO_REGION_OVERLAP_IN) - clip_region = NULL; - 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 (extents, src, - draw_func, draw_closure, dst); - } else { - if (op == CAIRO_OPERATOR_CLEAR) { - src = NULL; - op = CAIRO_OPERATOR_DEST_OUT; - } - - if (need_clip & NEED_CLIP_SURFACE) { - if (extents->is_bounded) { - status = _clip_and_composite_with_mask (extents, op, src, - draw_func, draw_closure, - dst); - } else { - status = _clip_and_composite_combine (extents, op, src, - draw_func, draw_closure, - dst); - } - } else { - status = draw_func (draw_closure, - dst->pixman_image, dst->pixman_format, - op, src, - 0, 0, - &dst->base.device_transform, - extents); - } - } - - if (status == CAIRO_STATUS_SUCCESS && ! extents->is_bounded) { - status = _cairo_image_surface_fixup_unbounded (dst, extents, - need_clip & NEED_CLIP_SURFACE ? extents->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) -{ - 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) -{ - 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_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 (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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - 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->bounded, - dst_device_transform, - &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = pixman_image_create_bits (format, - extents->bounded.width, - extents->bounded.height, - NULL, 0); - if (unlikely (mask == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto CLEANUP_SOURCE; - } - - _pixman_image_add_traps (mask, extents->bounded.x, extents->bounded.y, info); - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->bounded.x + src_x, extents->bounded.y + src_y, - 0, 0, - extents->bounded.x - dst_x, extents->bounded.y - dst_y, - extents->bounded.width, extents->bounded.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) -{ - 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 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) -{ - if (op == CAIRO_OPERATOR_CLEAR) { - *pixel = 0; - return TRUE; - } - - 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) -{ - 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) -{ - cairo_image_surface_span_renderer_t *renderer = abstract_renderer; - uint8_t *row; - unsigned i; - - if (num_spans == 0) - return CAIRO_STATUS_SUCCESS; - - /* 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; - - /* The below code breaks for operator source. This can best be seen with - * multiple boxes where black is drawn to dst outside of the boxes. */ - if (op == CAIRO_OPERATOR_SOURCE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - 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, - &dst->base.device_transform, - &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_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; - 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_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; - 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_bool_t -op_reduces_to_source (cairo_operator_t op, - cairo_image_surface_t *dst) -{ - if (op == CAIRO_OPERATOR_SOURCE) - return TRUE; - - if (dst->base.is_clear) - return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD; - - return FALSE; -} - -static cairo_bool_t -recording_pattern_contains_sample (const cairo_pattern_t *pattern, - const cairo_rectangle_int_t *extents) -{ - cairo_rectangle_int_t area; - 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; - - sampled_area ((cairo_surface_pattern_t *) pattern, extents, &area); - if (area.x >= surface->extents.x && - area.y >= surface->extents.y && - area.x + area.width <= surface->extents.x + surface->extents.width && - area.y + area.height <= surface->extents.y + surface->extents.height) - { - return TRUE; - } - - return FALSE; -} - -static cairo_status_t -_composite_boxes (cairo_image_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *pattern, - cairo_boxes_t *boxes, - const cairo_composite_rectangles_t *extents) -{ - cairo_region_t *clip_region = _cairo_clip_get_region (extents->clip); - cairo_bool_t need_clip_mask = extents->clip->path != NULL; - cairo_status_t status; - struct _cairo_boxes_chunk *chunk; - uint32_t pixel; - int i; - - 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 (! 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); - } - } - - /* Are we just copying a recording surface? */ - if (! need_clip_mask && - op_reduces_to_source (op, dst) && - recording_pattern_contains_sample (pattern, &extents->bounded)) - { - cairo_clip_t *recording_clip; - - /* XXX could also do tiling repeat modes... */ - - /* first clear the area about to be overwritten */ - if (! dst->base.is_clear) { - 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_part (box[i].p1.x); - int y1 = _cairo_fixed_integer_part (box[i].p1.y); - int x2 = _cairo_fixed_integer_part (box[i].p2.x); - int y2 = _cairo_fixed_integer_part (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, - 0); - } - } - } - - recording_clip = _cairo_clip_from_boxes (boxes); - status = _cairo_recording_surface_replay_with_clip (recording_pattern_get_surface (pattern), - &pattern->matrix, - &dst->base, - recording_clip); - _cairo_clip_destroy (recording_clip); - - return status; - } - - 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_part (box[i].p1.x); - int y1 = _cairo_fixed_integer_part (box[i].p1.y); - int x2 = _cairo_fixed_integer_part (box[i].p2.x); - int y2 = _cairo_fixed_integer_part (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 - { - cairo_surface_t *clip_surface = NULL; - 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) { - int clip_x, clip_y; - - clip_surface = _cairo_clip_get_surface (extents->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, - &dst->base.device_transform, - &src_x, &src_y); - if (unlikely (src == NULL)) { - cairo_surface_destroy (clip_surface); - 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_part (box[i].p1.x); - int y1 = _cairo_fixed_integer_part (box[i].p1.y); - int x2 = _cairo_fixed_integer_part (box[i].p2.x); - int y2 = _cairo_fixed_integer_part (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); - } - } - - cairo_surface_destroy (clip_surface); - 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_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); - -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_composite_rectangles_t *extents) -{ - cairo_traps_t traps; - cairo_int_status_t status; - composite_traps_info_t info; - - if (boxes->num_boxes == 0 && extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - /* 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); - 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 (dst, op, src, - &polygon, - fill_rule, - antialias, - 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; - } - - /* 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; - - /* 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 = CAIRO_ANTIALIAS_DEFAULT; - status = _clip_and_composite (dst, op, src, - _composite_traps, &info, - extents, need_unbounded_clip (extents)); - - _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) -{ - 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; - - need_clip_surface = ! _cairo_clip_is_region (extents->clip); - - 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, extents); - } - - /* 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, need_unbounded_clip (extents)); -} - -/* high level image interface */ -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 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_image_surface_t *surface = abstract_surface; - cairo_rectangle_int_t unbounded; - cairo_composite_rectangles_t extents; - cairo_status_t status; - cairo_boxes_t boxes; - - _cairo_image_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, - op, source, - clip); - 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... - */ - - status = _cairo_clip_to_boxes (extents.clip, &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - _cairo_boxes_fini (&boxes); - } - - _cairo_composite_rectangles_fini (&extents); - - 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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - 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->bounded, - dst_device_transform, - &src_x, &src_y); - if (unlikely (src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - mask = _pixman_image_for_pattern (mask_pattern, TRUE, &extents->bounded, - dst_device_transform, - &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->bounded, - dst_device_transform, - &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->bounded.x + src_x, extents->bounded.y + src_y, - extents->bounded.x + mask_x, extents->bounded.y + mask_y, - extents->bounded.x - dst_x, extents->bounded.y - dst_y, - extents->bounded.width, extents->bounded.height); - - if (mask != NULL) - pixman_image_unref (mask); - pixman_image_unref (src); - - return CAIRO_STATUS_SUCCESS; -} - -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 composite_opacity_info { - uint8_t op; - pixman_image_t *dst; - pixman_image_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; - pixman_color_t color; - pixman_image_t *mask; - - color.red = 0; - color.green = 0; - color.blue = 0; - color.alpha = coverage * info->opacity; - - mask = pixman_image_create_solid_fill (&color); - if (mask == NULL) - return; - - if (info->src) { - pixman_image_composite32 (info->op, - info->src, - mask, - info->dst, - x + info->src_x, y + info->src_y, - 0, 0, - x, y, - w, h); - } else { - pixman_image_composite32 (info->op, - mask, - NULL, - info->dst, - 0, 0, - 0, 0, - x, y, - w, h); - } - - pixman_image_unref (mask); -} - -static cairo_status_t -_composite_opacity_boxes (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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - const cairo_solid_pattern_t *mask_pattern = closure; - struct composite_opacity_info info; - int i; - - info.op = _pixman_operator (op); - info.dst = dst; - - if (src_pattern != NULL) { - info.src = _pixman_image_for_pattern (src_pattern, TRUE, &extents->bounded, - dst_device_transform, - &info.src_x, &info.src_y); - if (unlikely (info.src == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else - info.src = NULL; - - info.opacity = mask_pattern->color.alpha; - - /* XXX for lots of boxes create a clip region for the fully opaque areas */ - for (i = 0; i < extents->clip->num_boxes; i++) - do_unaligned_box(composite_opacity, &info, - &extents->clip->boxes[i], dst_x, dst_y); - if (info.src) - pixman_image_unref (info.src); - - return CAIRO_STATUS_SUCCESS; -} - - -static 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_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_status_t status; - - _cairo_image_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded, - op, source, mask, clip); - if (unlikely (status)) - return status; - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID && - extents.clip->path == NULL && - ! _cairo_clip_is_region (extents.clip)) - { - status = _clip_and_composite (surface, op, source, - _composite_opacity_boxes, (void *) mask, - &extents, need_bounded_clip (&extents)); - } else { - status = _clip_and_composite (surface, op, source, - _composite_mask, (void *) mask, - &extents, need_bounded_clip (&extents)); - } - - _cairo_composite_rectangles_fini (&extents); - - 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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - 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; - const cairo_rectangle_int_t *r = &extents->bounded; - cairo_status_t status; - -#if USE_BOTOR_SCAN_CONVERTER - 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); - _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 (r->x, r->y, - r->x + r->width, - r->y + r->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 (r->width, 8); - uint8_t *data = mask_buf; - - if (r->height * stride <= (int) sizeof (mask_buf)) - memset (data, 0, r->height * stride); - else - data = NULL, stride = 0; - - mask = pixman_image_create_bits (PIXMAN_a8, - r->width, - r->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 -= r->y * renderer.mask_stride + r->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, r, - dst_device_transform, - &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, - r->x + src_x, r->y + src_y, - 0, 0, /* mask.x, mask.y */ - r->x - dst_x, r->y - dst_y, - r->width, r->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_status_t status; - - if (_cairo_polygon_is_empty (polygon)) { - cairo_boxes_t boxes; - - if (extents->is_bounded) - return CAIRO_STATUS_SUCCESS; - - status = _cairo_clip_to_boxes (extents->clip, &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - extents->is_bounded = _cairo_operator_bounded_by_either (op); - extents->mask = extents->bounded = extents->unbounded; - status = _clip_and_composite_boxes (dst, - CAIRO_OPERATOR_CLEAR, - &_cairo_pattern_clear.base, - &boxes, extents); - _cairo_boxes_fini (&boxes); - } - - 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, need_unbounded_clip (extents)); - } 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); - } - - _cairo_traps_fini (&traps); - } - - return status; -} - -static 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_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_int_status_t status; - - _cairo_image_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded, - op, source, - path, style, ctm, - clip); - 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 (surface, op, source, - &boxes, &extents); - } - _cairo_boxes_fini (&boxes); - } - - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - 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 (surface, op, source, &polygon, - CAIRO_FILL_RULE_WINDING, - antialias, - &extents); - } - _cairo_polygon_fini (&polygon); - } - - _cairo_composite_rectangles_fini (&extents); - - return status; -} - -static 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_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - cairo_status_t status; - - _cairo_image_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded, - op, source, path, - clip); - 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_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - } - _cairo_boxes_fini (&boxes); - } else { - cairo_polygon_t polygon; - - assert (! _cairo_path_fixed_fill_is_empty (path)); - - _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 = _clip_and_composite_polygon (surface, op, source, &polygon, - fill_rule, antialias, - &extents); - } - _cairo_polygon_fini (&polygon); - } - - _cairo_composite_rectangles_fini (&extents); - - 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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - 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 = CAIRO_STATUS_SUCCESS; /* silence gcc */ - int src_x, src_y; - int i; - - src = _pixman_image_for_pattern (pattern, FALSE, &extents->bounded, - dst_device_transform, - &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->bounded.width, - extents->bounded.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->bounded.width, - extents->bounded.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->bounded.width, - extents->bounded.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->bounded.x, y - extents->bounded.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->bounded.x, y - extents->bounded.y, - glyph_surface->width, - glyph_surface->height); - } - } - - pixman_image_composite32 (_pixman_operator (op), - src, mask, dst, - extents->bounded.x + src_x, extents->bounded.y + src_y, - 0, 0, - extents->bounded.x - dst_x, extents->bounded.y - dst_y, - extents->bounded.width, extents->bounded.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, - cairo_matrix_t *dst_device_transform, - const cairo_composite_rectangles_t *extents) -{ - 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->bounded, - dst_device_transform, - &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->bounded.x) - x1 = extents->bounded.x; - x2 = x + glyph_surface->width; - if (x2 > extents->bounded.x + extents->bounded.width) - x2 = extents->bounded.x + extents->bounded.width; - - y1 = y; - if (y1 < extents->bounded.y) - y1 = extents->bounded.y; - y2 = y + glyph_surface->height; - if (y2 > extents->bounded.y + extents->bounded.height) - y2 = extents->bounded.y + extents->bounded.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_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, - int *num_remaining) -{ - cairo_image_surface_t *surface = abstract_surface; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - composite_glyphs_info_t glyph_info; - cairo_status_t status; - cairo_bool_t overlap; - unsigned int flags; - - _cairo_image_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - if (unlikely (status)) - return status; - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - flags = 0; - if (extents.mask.width > extents.unbounded.width || - extents.mask.height > extents.unbounded.height) - { - flags |= FORCE_CLIP_REGION; - } - status = _clip_and_composite (surface, op, source, - overlap || extents.is_bounded == 0 ? - _composite_glyphs_via_mask : - _composite_glyphs, - &glyph_info, - &extents, - need_bounded_clip (&extents) | flags); - - _cairo_composite_rectangles_fini (&extents); - - *num_remaining = 0; - return status; -} - -void -_cairo_image_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_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; - - image_rect_out->x = 0; - image_rect_out->y = 0; - image_rect_out->width = surface->width; - image_rect_out->height = surface->height; - - *image_out = surface; - *image_extra = NULL; - - 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, - &dst->base.device_transform, - &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, - &dst->base.device_transform, - &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, - &dst->base.device_transform, - &extents); - - 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, - &renderer->dst->base.device_transform, - &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_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_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, - - 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 */ - - _cairo_image_surface_paint, - _cairo_image_surface_mask, - _cairo_image_surface_stroke, - _cairo_image_surface_fill, - _cairo_image_surface_glyphs, - _cairo_image_surface_snapshot, - NULL, /* is_similar */ -}; - -/* A convenience function for when one needs to coerce an image - * surface to an alternate format. */ -cairo_image_surface_t * -_cairo_image_surface_coerce (cairo_image_surface_t *surface) -{ - 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 - * surface to an alternate format. */ -cairo_image_surface_t * -_cairo_image_surface_coerce_to_format (cairo_image_surface_t *surface, - cairo_format_t format) -{ - cairo_image_surface_t *clone; - cairo_status_t status; - - status = surface->base.status; - if (unlikely (status)) - return (cairo_image_surface_t *)_cairo_surface_create_in_error (status); - - if (surface->format == format) - return (cairo_image_surface_t *)cairo_surface_reference(&surface->base); - - clone = (cairo_image_surface_t *) - cairo_image_surface_create (format, surface->width, surface->height); - if (unlikely (clone->base.status)) - return clone; - - pixman_image_composite32 (PIXMAN_OP_SRC, - surface->pixman_image, NULL, clone->pixman_image, - 0, 0, - 0, 0, - 0, 0, - surface->width, surface->height); - clone->base.is_clear = FALSE; - - clone->base.device_transform = - surface->base.device_transform; - clone->base.device_transform_inverse = - surface->base.device_transform_inverse; - - return clone; -} - -cairo_image_transparency_t -_cairo_image_analyze_transparency (cairo_image_surface_t *image) -{ - int x, y; - - if (image->transparency != CAIRO_IMAGE_UNKNOWN) - return image->transparency; - - 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) { diff --git a/src/cairo-mask-compositor.c b/src/cairo-mask-compositor.c new file mode 100644 index 000000000..b34ffa257 --- /dev/null +++ b/src/cairo-mask-compositor.c @@ -0,0 +1,1412 @@ +/* -*- 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 + * Joonas Pihlaja + * Chris Wilson + */ + +/* This compositor renders the shape to a mask using an image surface + * then calls composite. + */ + +#include "cairoint.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-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_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + CAIRO_COLOR_TRANSPARENT); + if (unlikely (surface->status)) + return surface; + + 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)) + return surface; + } + + /* 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)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + 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)) { + cairo_surface_destroy (surface); + return _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 (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; +} + +/* 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; + + 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 = _cairo_clip_get_image (extents->clip, dst, &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 { + /* Punch the clip out of the destination */ + compositor->composite (dst, CAIRO_OPERATOR_DEST_OUT, clip, NULL, + 0, 0, + 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, + 0, 0, + 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_clip_t *clip = extents->clip; + cairo_surface_t *mask; + int mask_x, mask_y; + + mask = _cairo_clip_get_image (clip, dst, &extents->unbounded); + if (unlikely (mask->status)) + return mask->status; + + mask_x = -extents->unbounded.x; + mask_y = -extents->unbounded.y; + + /* 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; + const cairo_surface_pattern_t *pattern; + cairo_surface_t *src; + cairo_rectangle_int_t limit; + cairo_int_status_t status; + int tx, ty; + + pattern = (const cairo_surface_pattern_t *) source; + src = pattern->surface; + 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; + + if (_cairo_surface_is_snapshot (src)) + src = _cairo_surface_snapshot_get_target (src); + if (_cairo_surface_is_observer (src)) + src = _cairo_surface_observer_get_target (src); + if (_cairo_surface_is_subsurface (src)) { + _cairo_surface_subsurface_offset (src, &tx, &ty); + src = _cairo_surface_subsurface_get_target (src); + } + + /* Check that the data is entirely within the image */ + if (extents->bounded.x + tx < 0 || extents->bounded.y + ty < 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_surface_get_extents (pattern->surface, &limit); + if (extents->bounded.x + extents->bounded.width + tx > limit.width || + extents->bounded.y + extents->bounded.height + ty > limit.height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->surface->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 = _cairo_clip_get_image (extents->clip, dst, + &extents->bounded); + if (unlikely (mask->status)) + return mask->status; + + if (op == CAIRO_OPERATOR_CLEAR) { + source = NULL; + op = CAIRO_OPERATOR_DEST_OUT; + } + + mask_x = -extents->bounded.x; + mask_y = -extents->bounded.y; + } + + 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; + + _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; + + 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; + + 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) { + 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; + + 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) { + 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) +{ + cairo_surface_t *mask; + cairo_surface_pattern_t pattern; + cairo_int_status_t status; + + 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; +} diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index 195438c82..252113579 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -889,6 +889,9 @@ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, { 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, @@ -1043,6 +1046,9 @@ _cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, 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; diff --git a/src/cairo-mesh-pattern-rasterizer.c b/src/cairo-mesh-pattern-rasterizer.c index 9c9d0ece5..82b16e73c 100644 --- a/src/cairo-mesh-pattern-rasterizer.c +++ b/src/cairo-mesh-pattern-rasterizer.c @@ -36,6 +36,7 @@ #include "cairoint.h" +#include "cairo-array-private.h" #include "cairo-pattern-private.h" /* diff --git a/src/cairo-mime-surface.c b/src/cairo-mime-surface.c index f23c451a7..e19852f75 100644 --- a/src/cairo-mime-surface.c +++ b/src/cairo-mime-surface.c @@ -97,6 +97,7 @@ #include "cairoint.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" typedef struct _cairo_mime_surface { cairo_surface_t base; @@ -221,32 +222,25 @@ static const cairo_surface_backend_t cairo_mime_surface_backend = { _cairo_mime_surface_acquire_source_image, _cairo_mime_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_mime_surface_snapshot, + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_mime_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, /* glyphs */ - - _cairo_mime_surface_snapshot, }; cairo_surface_t * diff --git a/src/cairo-mono-scan-converter.c b/src/cairo-mono-scan-converter.c new file mode 100644 index 000000000..98b7631fb --- /dev/null +++ b/src/cairo-mono-scan-converter.c @@ -0,0 +1,607 @@ +/* -*- 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 +#include +#include + +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; + + 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->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) { + 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) { + 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; + + status = polygon_init (c->polygon, ymin, ymax); + if (unlikely (status)) + return status; + + if (xmax - xmin > ARRAY_LENGTH(c->spans_embedded)) { + c->spans = _cairo_malloc_ab (xmax - xmin, + 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 = INT_MIN; + c->head.prev = NULL; + c->head.next = &c->tail; + c->tail.prev = &c->head; + c->tail.next = NULL; + c->tail.x.quo = 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 >= 2 && 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); +} diff --git a/src/cairo-no-compositor.c b/src/cairo-no-compositor.c new file mode 100644 index 000000000..1602a12f6 --- /dev/null +++ b/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 + * Joonas Pihlaja + * Chris Wilson + */ + +#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, +}; diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c index 0adda365c..cc7e300c9 100644 --- a/src/cairo-output-stream.c +++ b/src/cairo-output-stream.c @@ -37,6 +37,8 @@ #include "cairoint.h" #include "cairo-output-stream-private.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #include "cairo-compiler-private.h" diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c index d9c6c2f93..0418e67b6 100644 --- a/src/cairo-paginated-surface.c +++ b/src/cairo-paginated-surface.c @@ -347,11 +347,10 @@ _paint_page (cairo_paginated_surface_t *surface) CAIRO_PAGINATED_MODE_ANALYZE); status = _cairo_recording_surface_replay_and_create_regions (surface->recording_surface, analysis); - if (status || analysis->status) { - if (status == CAIRO_INT_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; @@ -674,33 +673,23 @@ static const cairo_surface_backend_t cairo_paginated_surface_backend = { _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, /* show_glyphs */ - _cairo_paginated_surface_snapshot, - NULL, /* is_similar */ NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* show_glyphs */ _cairo_paginated_surface_has_show_text_glyphs, _cairo_paginated_surface_show_text_glyphs }; diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index 1fc9a06a9..087a7d0e2 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -40,7 +40,6 @@ #include "cairo-error-private.h" #include "cairo-path-fixed-private.h" - typedef struct _cairo_path_bounder { cairo_point_t current_point; cairo_bool_t has_extents; @@ -161,8 +160,9 @@ _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, cairo_box_t box_extents; double dx, dy; + _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy); + box_extents = path->extents; - _cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy); 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); @@ -183,23 +183,17 @@ _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, 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, - stroke_style, - ctm, - ctm_inverse, - tolerance, - &traps); - - _cairo_traps_extents (&traps, &bbox); - _cairo_traps_fini (&traps); - - _cairo_box_round_to_rectangle (&bbox, extents); + _cairo_polygon_init (&polygon, NULL, 0); + status = _cairo_path_fixed_stroke_to_polygon (path, + stroke_style, + ctm, ctm_inverse, + tolerance, + &polygon); + _cairo_box_round_to_rectangle (&polygon.extents, extents); + _cairo_polygon_fini (&polygon); return status; } diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c index ce4c86a45..83393070b 100644 --- a/src/cairo-path-fill.c +++ b/src/cairo-path-fill.c @@ -40,9 +40,14 @@ #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_box_t limit; + cairo_bool_t has_limits; cairo_point_t current_point; cairo_point_t last_move_to; @@ -86,13 +91,38 @@ _cairo_filler_move_to (void *closure, if (unlikely (status)) return status; - /* make sure that the closure represents a degenerate path */ + /* 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 *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_spline_add_point_func_t)_cairo_filler_line_to, filler, + &filler->current_point, p1, p2, p3)) + { + return _cairo_filler_line_to (closure, p3); + } + + return _cairo_spline_decompose (&spline, filler->tolerance); +} + cairo_status_t _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, double tolerance, @@ -102,18 +132,25 @@ _cairo_path_fixed_fill_to_polygon (const cairo_path_fixed_t *path, cairo_status_t status; 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_flat (path, - _cairo_filler_move_to, - _cairo_filler_line_to, - _cairo_filler_close, - &filler, - tolerance); + status = _cairo_path_fixed_interpret (path, + _cairo_filler_move_to, + _cairo_filler_line_to, + _cairo_filler_curve_to, + _cairo_filler_close, + &filler); if (unlikely (status)) return status; @@ -179,7 +216,7 @@ _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path, cairo_status_t status; if (antialias != CAIRO_ANTIALIAS_NONE) - return _cairo_path_fixed_fill_to_polygon (path, 0., polygon); + return _cairo_path_fixed_fill_to_polygon (path, 0., polygon); filler.polygon = polygon; @@ -213,22 +250,12 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path, return CAIRO_STATUS_SUCCESS; _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); - - status = _cairo_path_fixed_fill_to_polygon (path, - tolerance, - &polygon); + status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); if (unlikely (status || polygon.num_edges == 0)) goto CLEANUP; - if (_cairo_path_fixed_fill_is_rectilinear (path)) { - status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps, - &polygon, - fill_rule); - } else { - status = _cairo_bentley_ottmann_tessellate_polygon (traps, - &polygon, - fill_rule); - } + status = _cairo_bentley_ottmann_tessellate_polygon (traps, + &polygon, fill_rule); CLEANUP: _cairo_polygon_fini (&polygon); diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h index e24b1c795..9b7b40373 100644 --- a/src/cairo-path-fixed-private.h +++ b/src/cairo-path-fixed-private.h @@ -182,4 +182,8 @@ _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path) 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); + #endif /* CAIRO_PATH_FIXED_PRIVATE_H */ diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c index d67954f84..c257d73bd 100644 --- a/src/cairo-path-fixed.c +++ b/src/cairo-path-fixed.c @@ -584,6 +584,18 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, 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); @@ -1270,6 +1282,51 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path, return FALSE; } +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 && + buf->points[2].y == buf->points[3].y && + buf->points[3].x == buf->points[0].x) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + if (buf->points[0].x == buf->points[1].x && + buf->points[1].y == buf->points[2].y && + buf->points[2].x == buf->points[3].x && + buf->points[3].y == buf->points[0].y) + { + _canonical_box (box, &buf->points[0], &buf->points[2]); + return TRUE; + } + + return FALSE; +} + /* * Check whether the given path contains a single rectangle * that is logically equivalent to: diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c index de3a628ae..794250b11 100644 --- a/src/cairo-path-stroke-boxes.c +++ b/src/cairo-path-stroke-boxes.c @@ -234,29 +234,31 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) } /* 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 (lengthen_final) - b->x += half_line_width; - } else { - if (lengthen_initial) - a->x += half_line_width; - if (lengthen_final) - b->x -= half_line_width; - } - } else { - if (a->y < b->y) { - if (lengthen_initial) - a->y -= half_line_width; - if (lengthen_final) - b->y += half_line_width; + if (lengthen_initial | lengthen_final) { + if (a->y == b->y) { + if (a->x < b->x) { + if (lengthen_initial) + a->x -= half_line_width; + if (lengthen_final) + b->x += half_line_width; + } else { + if (lengthen_initial) + a->x += half_line_width; + if (lengthen_final) + b->x -= half_line_width; + } } else { - if (lengthen_initial) - a->y += half_line_width; - if (lengthen_final) - b->y -= half_line_width; + if (a->y < b->y) { + if (lengthen_initial) + a->y -= half_line_width; + if (lengthen_final) + b->y += half_line_width; + } else { + if (lengthen_initial) + a->y += half_line_width; + if (lengthen_final) + b->y -= half_line_width; + } } } @@ -291,7 +293,6 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) } stroker->num_segments = 0; - return CAIRO_STATUS_SUCCESS; } @@ -606,6 +607,7 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; + cairo_box_t box; assert (_cairo_path_fixed_stroke_is_rectilinear (path)); @@ -616,6 +618,46 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, return CAIRO_INT_STATUS_UNSUPPORTED; } + if (! rectilinear_stroker.dash.dashed && + _cairo_path_fixed_is_stroke_box (path, &box)) + { + cairo_box_t b; + + /* top */ + b.p1.x = box.p1.x - rectilinear_stroker.half_line_width; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_width; + b.p1.y = box.p1.y - rectilinear_stroker.half_line_width; + b.p2.y = box.p1.y + rectilinear_stroker.half_line_width; + 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_width; + b.p2.x = box.p1.x + rectilinear_stroker.half_line_width; + b.p1.y = box.p1.y + rectilinear_stroker.half_line_width; + b.p2.y = box.p2.y - rectilinear_stroker.half_line_width; + 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_width; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_width; + b.p1.y = box.p1.y + rectilinear_stroker.half_line_width; + b.p2.y = box.p2.y - rectilinear_stroker.half_line_width; + status = _cairo_boxes_add (boxes, antialias, &b); + assert (status == CAIRO_INT_STATUS_SUCCESS); + + /* bottom */ + b.p1.x = box.p1.x - rectilinear_stroker.half_line_width; + b.p2.x = box.p2.x + rectilinear_stroker.half_line_width; + b.p1.y = box.p2.y - rectilinear_stroker.half_line_width; + b.p2.y = box.p2.y + rectilinear_stroker.half_line_width; + 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, @@ -647,8 +689,8 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, if (unlikely (status)) goto BAIL; +done: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); - return CAIRO_STATUS_SUCCESS; BAIL: diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c index 3b8a67dae..9128a0037 100644 --- a/src/cairo-path-stroke-polygon.c +++ b/src/cairo-path-stroke-polygon.c @@ -104,6 +104,7 @@ 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); } @@ -115,6 +116,7 @@ contour_add_point (struct stroker *stroker, 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 @@ -789,7 +791,7 @@ outer_join (struct stroker *stroker, * Make sure the miter point line lies between the two * faces by comparing the slopes */ - if (1 || slope_compare_sgn (fdx1, fdy1, mdx, mdy) != + if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; @@ -1091,6 +1093,7 @@ line_to (void *closure, cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; + int move_last = 0; stroker->has_initial_sub_path = TRUE; @@ -1105,15 +1108,21 @@ line_to (void *closure, 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 */ - 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); + int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, + &start.dev_vector); + if (clockwise == 0) { + move_last = 1; + } else { + 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) { @@ -1134,8 +1143,13 @@ line_to (void *closure, 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); + if (move_last) { + *_cairo_contour_last_point (&stroker->cw.contour) = stroker->current_face.cw; + *_cairo_contour_last_point (&stroker->ccw.contour) = stroker->current_face.ccw; + } else { + contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); + contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); + } return CAIRO_STATUS_SUCCESS; } @@ -1187,6 +1201,37 @@ spline_to (void *closure, } 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; + 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, inpt, outpt, + clockwise, outer); + } + contour_add_point (stroker, &stroker->cw, &face.cw); contour_add_point (stroker, &stroker->ccw, &face.ccw); } @@ -1328,6 +1373,8 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, 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); diff --git a/src/cairo-path-stroke-tristrip.c b/src/cairo-path-stroke-tristrip.c new file mode 100644 index 000000000..337d814b0 --- /dev/null +++ b/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 + * Chris Wilson + */ + +#define _BSD_SOURCE /* for hypot() */ +#include "cairoint.h" + +#include "cairo-box-private.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 && out->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 && out->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; +} diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index c43829123..f25cfe4f4 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -45,6 +45,7 @@ #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 { cairo_stroke_style_t style; @@ -125,6 +126,7 @@ _cairo_stroker_init (cairo_stroker_t *stroker, static void _cairo_stroker_limit (cairo_stroker_t *stroker, + const cairo_path_fixed_t *path, const cairo_box_t *boxes, int num_boxes) { @@ -139,8 +141,8 @@ _cairo_stroker_limit (cairo_stroker_t *stroker, * 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); + _cairo_stroke_style_max_distance_from_path (&stroker->style, path, + stroker->ctm, &dx, &dy); fdx = _cairo_fixed_from_double (dx); fdy = _cairo_fixed_from_double (dy); @@ -1293,7 +1295,8 @@ _cairo_path_fixed_stroke_dashed_to_polygon (const cairo_path_fixed_t *path, stroker.closure = polygon; if (polygon->num_limits) - _cairo_stroker_limit (&stroker, polygon->limits, polygon->num_limits); + _cairo_stroker_limit (&stroker, path, + polygon->limits, polygon->num_limits); status = _cairo_path_fixed_interpret (path, _cairo_stroker_move_to, @@ -1328,7 +1331,6 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path, cairo_polygon_t polygon; _cairo_polygon_init (&polygon, traps->limits, traps->num_limits); - status = _cairo_path_fixed_stroke_to_polygon (path, stroke_style, ctm, diff --git a/src/cairo-pattern-private.h b/src/cairo-pattern-private.h index 46a8ad6e7..8be319e13 100644 --- a/src/cairo-pattern-private.h +++ b/src/cairo-pattern-private.h @@ -40,6 +40,8 @@ #include "cairo-types-private.h" #include "cairo-list-private.h" +#include /* FILE* */ + CAIRO_BEGIN_DECLS typedef struct _cairo_pattern_observer cairo_pattern_observer_t; @@ -59,19 +61,19 @@ struct _cairo_pattern_observer { }; 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_list_t observers; + + cairo_pattern_type_t type; - cairo_matrix_t matrix; cairo_filter_t filter; cairo_extend_t extend; - double opacity; - cairo_bool_t has_component_alpha; - cairo_list_t observers; + cairo_matrix_t matrix; + double opacity; }; struct _cairo_solid_pattern { @@ -265,46 +267,19 @@ _cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, double *out_xmax, double *out_ymax); -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_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); @@ -353,7 +328,8 @@ _cairo_mesh_pattern_rasterize (const cairo_mesh_pattern_t *mesh, double x_offset, double y_offset); - +cairo_private void +_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern); CAIRO_END_DECLS diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 19aa97830..f91de2658 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -29,11 +29,14 @@ */ #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-path-private.h" #include "cairo-pattern-private.h" -#include "cairo-image-surface-private.h" +#include "cairo-recording-surface-private.h" #include @@ -61,55 +64,85 @@ 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 */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ + CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ + FALSE, /* has component alpha */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + 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 */ - { 1., 0., 0., 1., 0., 0., }, /* matrix */ + { NULL, NULL }, /* observers */ + + CAIRO_PATTERN_TYPE_SOLID, /* type */ CAIRO_FILTER_DEFAULT, /* filter */ - CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */ + CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */ + FALSE, /* has component alpha */ + { 1., 0., 0., 1., 0., 0., }, /* matrix */ + 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) */ }; @@ -249,7 +282,6 @@ _cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern, *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)); @@ -477,7 +509,6 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern_out, return CAIRO_STATUS_SUCCESS; } - void _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, const cairo_color_t *color) @@ -2049,477 +2080,6 @@ _cairo_pattern_transform (cairo_pattern_t *pattern, 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) -{ - 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; - - /* 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. - */ - point0 = pattern->pd1; - point1 = pattern->pd2; - - _cairo_matrix_get_affine (&pattern->base.base.matrix, - &a, &b, &c, &d, &tx, &ty); - - dx = point1.x - point0.x; - dy = point1.y - point0.y; - scale = dx * dx + dy * dy; - scale = (scale) ? 1.0 / scale : 1.0; - - start = dx * point0.x + dy * point0.y; - - 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. - * - * Horizontal works the same way with the two left corners. - */ - - *is_vertical = factors[1] == factors[0]; - *is_horizontal = factors[2] == factors[0]; -} - -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_circle_double_t extremes[2]; - pixman_point_fixed_t p1, p2; - cairo_int_status_t status; - cairo_bool_t repeat = FALSE; - int ix, iy; - 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; - - if (CAIRO_INJECT_FAULT ()) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - 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); - } - - 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 _cairo_error (CAIRO_STATUS_NO_MEMORY); - - 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; - } - - 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; - - *out = &image->base; - - return CAIRO_STATUS_SUCCESS; - } - - if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { - cairo_bool_t is_horizontal; - cairo_bool_t is_vertical; - - _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. - */ - if (is_vertical && width > 8) { - width = 8; - repeat = TRUE; - } - } - - if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, - NULL, 0)) - { - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - 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; - } - - ix = x; - iy = y; - status = _cairo_matrix_to_pixman_matrix_offset (&matrix, - pattern->base.filter, - width/2., 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)) - { - cairo_surface_destroy (&image->base); - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - } - - 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; - } - - pixman_image_composite32 (PIXMAN_OP_SRC, - pixman_image, - NULL, - image->pixman_image, - ix, iy, - 0, 0, - 0, 0, - width, height); - - pixman_image_unref (pixman_image); - - _cairo_debug_check_image_surface_is_defined (&image->base); - - 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; -} - -static cairo_int_status_t -_cairo_pattern_acquire_surface_for_mesh (const cairo_mesh_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_surface_t *image; - void *data; - cairo_status_t status; - int clone_offset_x, clone_offset_y; - int stride; - - image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - if (unlikely (image->status)) - return image->status; - - stride = cairo_image_surface_get_stride (image); - data = cairo_image_surface_get_data (image); - - _cairo_mesh_pattern_rasterize (pattern, data, width, height, stride, -x, -y); - - attr->x_offset = -x; - attr->y_offset = -y; - attr->filter = CAIRO_FILTER_NEAREST; - attr->extend = pattern->base.extend; - cairo_matrix_init_identity (&attr->matrix); - attr->has_component_alpha = pattern->base.has_component_alpha; - - if (_cairo_surface_is_image (dst)) { - *out = image; - - return CAIRO_STATUS_SUCCESS; - } - - status = _cairo_surface_clone_similar (dst, image, - 0, 0, width, height, - &clone_offset_x, - &clone_offset_y, out); - - cairo_surface_destroy (image); - - return status; -} - -/* 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; - -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; - - if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1) - return FALSE; - - if (! _cairo_surface_is_similar (cache->surface, dst)) - return FALSE; - - return TRUE; -} - -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); -} - -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; - - cairo_surface_t *surface, *to_destroy = NULL; - cairo_status_t status; - - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* Check cache first */ - if (i < solid_surface_cache.size && - _cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], - pattern, - dst)) - { - goto DONE; - } - - 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; - } - } - - /* 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)) - { - /* Reuse the surface instead of evicting */ - status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); - if (unlikely (status)) - goto EVICT; - - cairo_surface_reference (surface); - } - 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))) - { - /* In the rare event of a substitute surface being returned, - * don't cache the fallback. - */ - *out = surface; - goto NOCACHE; - } - } - - if (i == solid_surface_cache.size) - solid_surface_cache.size++; - - to_destroy = solid_surface_cache.cache[i].surface; - solid_surface_cache.cache[i].surface = surface; - solid_surface_cache.cache[i].color = pattern->color; - -DONE: - *out = cairo_surface_reference (solid_surface_cache.cache[i].surface); - -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; - -UNLOCK: - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); - - if (to_destroy) - cairo_surface_destroy (to_destroy); - - return status; -} - -static void -_cairo_pattern_reset_solid_surface_cache (void) -{ - CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); - - /* 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; - - 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); - } - - CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); -} - static cairo_bool_t _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) { @@ -3546,7 +3106,7 @@ _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) 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) { if (pattern->surface->content & CAIRO_CONTENT_ALPHA) return FALSE; @@ -3554,16 +3114,16 @@ _surface_is_opaque (const cairo_surface_pattern_t *pattern, if (pattern->base.extend != CAIRO_EXTEND_NONE) return TRUE; - if (r != NULL) { + if (sample != 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->x >= extents.x && + sample->y >= extents.y && + sample->x + sample->width <= extents.x + extents.width && + sample->y + sample->height <= extents.y + extents.height) { return TRUE; } @@ -3587,7 +3147,7 @@ _surface_is_clear (const cairo_surface_pattern_t *pattern) 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; @@ -3608,14 +3168,14 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, if (_linear_pattern_is_degenerate (linear)) return FALSE; - if (extents == NULL) + if (sample == NULL) return FALSE; _cairo_linear_pattern_box_to_parameter (linear, - extents->x, - extents->y, - extents->x + extents->width, - extents->y + extents->height, + sample->x, + sample->y, + sample->x + sample->width, + sample->y + sample->height, t); if (t[0] < 0.0 || t[1] > 1.0) @@ -3642,7 +3202,7 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, **/ 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; @@ -3654,10 +3214,10 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, 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_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; } @@ -3749,437 +3309,53 @@ _cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, return optimized_filter; } - -static double -_pixman_nearest_sample (double d) -{ - 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_filter_t +_cairo_pattern_sampled_area (const cairo_pattern_t *pattern, + const cairo_rectangle_int_t *extents, + cairo_rectangle_int_t *sample) { - 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_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; - } - 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; - } + filter = _cairo_pattern_analyze_filter (pattern, &pad); + if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) { + *sample = *extents; + return filter; } - /* 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, - &x1, &y1, &x2, &y2, - NULL); - } - - 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; - - sampled_area.x += tx; - sampled_area.y += ty; - - if ( _cairo_surface_get_extents (surface, &extents)) { - if (attr->extend == CAIRO_EXTEND_NONE) { - /* Never acquire a larger area than the source itself */ - _cairo_rectangle_intersect (&extents, &sampled_area); - } else { - int trim = 0; - - 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; - } - - 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; - } - - if (trim == 0x3) { - /* source is wholly contained within extents, drop the REPEAT */ - attr->extend = CAIRO_EXTEND_NONE; - } - } - } - - 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; - } + x1 = extents->x; + y1 = extents->y; + x2 = extents->x + (int) extents->width; + y2 = extents->y + (int) extents->height; - 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); - - case CAIRO_PATTERN_TYPE_MESH: - return _cairo_pattern_acquire_surface_for_mesh ((cairo_mesh_pattern_t *) pattern, - dst, x, y, width, height, - 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; - } + _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; - status = _cairo_pattern_acquire_surface (src, dst, - src_x, src_y, - width, height, - flags, - src_out, src_attributes); - if (unlikely (status)) - goto BAIL; + if (y1 > CAIRO_RECT_INT_MIN) + sample->y = floor (y1 - pad); + else + sample->y = CAIRO_RECT_INT_MIN; - if (mask == NULL) { - *mask_out = NULL; - goto BAIL; - } + if (x2 < CAIRO_RECT_INT_MAX) + sample->width = ceil (x2 + pad); + else + sample->width = CAIRO_RECT_INT_MAX; - 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); + if (y2 < CAIRO_RECT_INT_MAX) + sample->height = ceil (y2 + pad); + else + sample->height = CAIRO_RECT_INT_MAX; - BAIL: - if (src == &src_tmp.base) - _cairo_pattern_fini (&src_tmp.base); + sample->width -= sample->x; + sample->height -= sample->y; - return status; + return filter; } /** @@ -4381,7 +3557,7 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern, * * Return the "target-space" inked extents of @pattern in @extents. **/ -void +cairo_int_status_t _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, cairo_rectangle_int_t *extents) { @@ -4392,33 +3568,28 @@ _cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, (const cairo_surface_pattern_t *) pattern; cairo_surface_t *surface = surface_pattern->surface; - if (surface->type == CAIRO_SURFACE_TYPE_RECORDING) { + if (_cairo_surface_is_recording (surface)) { cairo_matrix_t imatrix; - double x1, y1, x2, y2, width, height; + cairo_box_t box; cairo_status_t status; - cairo_recording_surface_ink_extents (surface, &x1, &y1, &width, &height); imatrix = pattern->matrix; status = cairo_matrix_invert (&imatrix); /* cairo_pattern_set_matrix ensures the matrix is invertible */ assert (status == CAIRO_STATUS_SUCCESS); - x2 = x1 + width; - y2 = y1 + height; - _cairo_matrix_transform_bounding_box (&imatrix, - &x1, &y1, &x2, &y2, - NULL); - x1 = floor (x1); - y1 = floor (y1); - x2 = ceil (x2); - y2 = ceil (y2); - extents->x = x1; extents->width = x2 - x1; - extents->y = y1; extents->height = y2 - y1; - return; + 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; } } - return _cairo_pattern_get_extents (pattern, extents); + _cairo_pattern_get_extents (pattern, extents); + return CAIRO_STATUS_SUCCESS; } static unsigned long @@ -5200,6 +4371,88 @@ _cairo_pattern_reset_static_data (void) for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) _freed_pool_reset (&freed_pattern_pool[i]); +} + +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_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; + default: s = "invalid"; ASSERT_NOT_REACHED; break; + } - _cairo_pattern_reset_solid_surface_cache (); + 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_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; + } } diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c index 58c647219..fceaf1cc4 100644 --- a/src/cairo-pdf-operators.c +++ b/src/cairo-pdf-operators.c @@ -769,7 +769,7 @@ _cairo_pdf_operators_emit_stroke (cairo_pdf_operators_t *pdf_operators, 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; diff --git a/src/cairo-pdf-shading.c b/src/cairo-pdf-shading.c index 6b7bcea16..646e2cd49 100644 --- a/src/cairo-pdf-shading.c +++ b/src/cairo-pdf-shading.c @@ -39,6 +39,8 @@ #if CAIRO_HAS_PDF_OPERATORS #include "cairo-pdf-shading-private.h" + +#include "cairo-array-private.h" #include "cairo-error-private.h" #include diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c index b115fd1e8..3f2d0470d 100644 --- a/src/cairo-pdf-surface.c +++ b/src/cairo-pdf-surface.c @@ -41,10 +41,13 @@ #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" @@ -1122,17 +1125,19 @@ _get_source_surface_size (cairo_surface_t *source, *height = extents->height; } else { cairo_rectangle_int_t surf_extents; - double x, y, w, h; + cairo_box_t box; cairo_bool_t bounded; if (_cairo_surface_is_snapshot (source)) source = _cairo_surface_snapshot_get_target (source); - cairo_recording_surface_ink_extents (source, &x, &y,&w, &h); - extents->x = floor (x); - extents->y = floor (y); - extents->width = ceil (x + w) - extents->x; - extents->height = ceil (y + h) - extents->y; + status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)source, + &box, NULL); + if (unlikely (status)) + return status; + + _cairo_box_round_to_rectangle (&box, extents); + bounded = _cairo_surface_get_extents (source, &surf_extents); *width = surf_extents.width; *height = surf_extents.height; @@ -2358,16 +2363,9 @@ _cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t *surface, _cairo_pattern_init_for_surface (&pad_pattern, &image->base); cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y); pad_pattern.base.extend = CAIRO_EXTEND_PAD; - status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE, - &pad_pattern.base, - NULL, - pad_image, - 0, 0, - 0, 0, - 0, 0, - rect.width, - rect.height, - NULL); + status = _cairo_surface_paint (pad_image, + CAIRO_OPERATOR_SOURCE, &pad_pattern.base, + NULL); _cairo_pattern_fini (&pad_pattern.base); if (unlikely (status)) goto BAIL; @@ -5917,11 +5915,10 @@ _cairo_pdf_surface_paint (void *abstract_surface, cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_paint (&extents, + &surface->base, op, source, clip); if (unlikely (status)) return status; @@ -6037,11 +6034,10 @@ _cairo_pdf_surface_mask (void *abstract_surface, cairo_pdf_surface_t *surface = abstract_surface; cairo_pdf_smask_group_t *group; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_mask (&extents, + &surface->base, op, source, mask, clip); if (unlikely (status)) return status; @@ -6166,11 +6162,10 @@ _cairo_pdf_surface_stroke (void *abstract_surface, cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, op, source, path, style, ctm, clip); @@ -6305,10 +6300,9 @@ _cairo_pdf_surface_fill (void *abstract_surface, cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_fill (&extents, + &surface->base, op, source, path, clip); if (unlikely (status)) @@ -6471,7 +6465,6 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, cairo_int_status_t status; cairo_pdf_resource_t fill_pattern_res, stroke_pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; /* During analysis we return unsupported and let the _fill and * _stroke functions that are on the fallback path do the analysis @@ -6498,8 +6491,8 @@ _cairo_pdf_surface_fill_stroke (void *abstract_surface, /* Compute the operation extents using the stroke which will naturally * be larger than the fill extents. */ - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, stroke_op, stroke_source, path, stroke_style, stroke_ctm, clip); @@ -6630,12 +6623,11 @@ _cairo_pdf_surface_show_text_glyphs (void *abstract_surface, cairo_pdf_smask_group_t *group; cairo_pdf_resource_t pattern_res, gstate_res; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_bool_t overlap; cairo_int_status_t status; - _cairo_pdf_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->base, op, source, scaled_font, glyphs, num_glyphs, @@ -6798,37 +6790,24 @@ static const cairo_surface_backend_t cairo_pdf_surface_backend = { 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, /* _cairo_pdf_surface_copy_page */ _cairo_pdf_surface_show_page, + _cairo_pdf_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_pdf_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here are the drawing functions */ - _cairo_pdf_surface_paint, _cairo_pdf_surface_mask, _cairo_pdf_surface_stroke, _cairo_pdf_surface_fill, - NULL, /* show_glyphs */ - NULL, /* snapshot */ - - NULL, /* is_compatible */ _cairo_pdf_surface_fill_stroke, - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ + NULL, /* show_glyphs */ _cairo_pdf_surface_has_show_text_glyphs, _cairo_pdf_surface_show_text_glyphs, }; diff --git a/src/cairo-polygon-intersect.c b/src/cairo-polygon-intersect.c index e2ed6e970..d9d0cf261 100644 --- a/src/cairo-polygon-intersect.c +++ b/src/cairo-polygon-intersect.c @@ -1454,13 +1454,78 @@ _cairo_polygon_intersect (cairo_polygon_t *a, int winding_a, } assert (j == num_events); - //fprintf(stderr, "a "); _cairo_debug_print_polygon(stderr,a); - //fprintf(stderr, "b "); _cairo_debug_print_polygon(stderr,b); +#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++) { + 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; } diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c index 6436712b4..a44cba3f5 100644 --- a/src/cairo-polygon.c +++ b/src/cairo-polygon.c @@ -127,19 +127,19 @@ _cairo_polygon_init_boxes (cairo_polygon_t *polygon, 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); - } + 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; @@ -491,3 +491,29 @@ _cairo_polygon_add_contour (cairo_polygon_t *polygon, return polygon->status; } + +void +_cairo_polygon_translate (cairo_polygon_t *polygon, int dx, int dy) +{ + int n; + + dx = _cairo_fixed_from_int (dx); + dy = _cairo_fixed_from_int (dy); + + polygon->extents.p1.x += dx; + polygon->extents.p2.x += dx; + polygon->extents.p1.y += dy; + polygon->extents.p2.y += dy; + + for (n = 0; n < polygon->num_edges; n++) { + cairo_edge_t *e = &polygon->edges[n]; + + e->top += dy; + e->bottom += dy; + + e->line.p1.x += dx; + e->line.p2.x += dx; + e->line.p1.y += dy; + e->line.p2.y += dy; + } +} diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c index a8048871f..dcd216126 100644 --- a/src/cairo-ps-surface.c +++ b/src/cairo-ps-surface.c @@ -55,10 +55,14 @@ #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" @@ -79,7 +83,7 @@ #include #include -#define DEBUG_PS 0 +#define DEBUG_PS 1 #if DEBUG_PS #define DEBUG_FALLBACK(s) \ @@ -1153,41 +1157,40 @@ _extract_ps_surface (cairo_surface_t *surface, cairo_ps_surface_t **ps_surface) { cairo_surface_t *target; - cairo_status_t status_ignored; if (surface->status) return FALSE; if (surface->finished) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_paginated (surface)) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _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) - status_ignored = _cairo_surface_set_error (surface, target->status); + _cairo_surface_set_error (surface, target->status); return FALSE; } if (target->finished) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return FALSE; } if (! _cairo_surface_is_ps (target)) { if (set_error_on_failure) - status_ignored = _cairo_surface_set_error (surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _cairo_surface_set_error (surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return FALSE; } @@ -1783,7 +1786,7 @@ mask_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *mask) 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, surface_pattern) == CAIRO_STATUS_SUCCESS) { + if (_cairo_ps_surface_analyze_surface_pattern_transparency (surface, surface_pattern) == CAIRO_INT_STATUS_SUCCESS) { surface->ps_level_used = CAIRO_PS_LEVEL_3; return TRUE; } @@ -3780,17 +3783,18 @@ _cairo_ps_surface_paint (void *abstract_surface, cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_status_t status; - _cairo_ps_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, + 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) - return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + 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)); @@ -3801,7 +3805,7 @@ _cairo_ps_surface_paint (void *abstract_surface, status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; if (source->type == CAIRO_PATTERN_TYPE_SURFACE && (source->extend == CAIRO_EXTEND_NONE || @@ -3809,26 +3813,28 @@ _cairo_ps_surface_paint (void *abstract_surface, { status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "q\n"); status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, &extents.bounded, op, FALSE); if (unlikely (status)) - return 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)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "0 0 %f %f rectfill\n", surface->width, surface->height); } - return CAIRO_STATUS_SUCCESS; +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t @@ -3841,17 +3847,18 @@ _cairo_ps_surface_mask (void *abstract_surface, cairo_ps_surface_t *surface = abstract_surface; cairo_output_stream_t *stream = surface->stream; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_status_t status; - _cairo_ps_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded, + 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) - return _cairo_ps_surface_analyze_operation (surface, op, source, mask, &extents.bounded); + 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)); @@ -3862,22 +3869,24 @@ _cairo_ps_surface_mask (void *abstract_surface, status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "q\n"); status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) mask, &extents.bounded, op, TRUE); if (unlikely (status)) - return status; + goto cleanup_composite; _cairo_output_stream_printf (stream, "Q\n"); - return CAIRO_STATUS_SUCCESS; +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); + return status; } static cairo_int_status_t @@ -3894,11 +3903,10 @@ _cairo_ps_surface_stroke (void *abstract_surface, { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; - _cairo_ps_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, op, source, path, style, ctm, clip); @@ -3906,21 +3914,27 @@ _cairo_ps_surface_stroke (void *abstract_surface, return status; /* use the more accurate extents */ - if (extents.is_bounded) { + { + cairo_rectangle_int_t r; + cairo_box_t b; + status = _cairo_path_fixed_stroke_extents (path, style, ctm, ctm_inverse, tolerance, - &extents.mask); + &r); if (unlikely (status)) - return status; + goto cleanup_composite; - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _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) - return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + 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)); @@ -3931,17 +3945,21 @@ _cairo_ps_surface_stroke (void *abstract_surface, status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return status; + goto cleanup_composite; + + status = _cairo_pdf_operators_stroke (&surface->pdf_operators, + path, + style, + ctm, + ctm_inverse); - return _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 @@ -3956,29 +3974,35 @@ _cairo_ps_surface_fill (void *abstract_surface, { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; - _cairo_ps_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded, + 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 r; + cairo_box_t b; + _cairo_path_fixed_fill_extents (path, fill_rule, tolerance, - &extents.mask); + &r); - if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask)) - return CAIRO_STATUS_SUCCESS; + _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) - return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + 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)); @@ -3989,11 +4013,11 @@ _cairo_ps_surface_fill (void *abstract_surface, status = _cairo_pdf_operators_flush (&surface->pdf_operators); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; if (source->type == CAIRO_PATTERN_TYPE_SURFACE && (source->extend == CAIRO_EXTEND_NONE || @@ -4005,26 +4029,28 @@ _cairo_ps_surface_fill (void *abstract_surface, path, fill_rule); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_paint_surface (surface, (cairo_surface_pattern_t *) source, &extents.bounded, op, FALSE); if (unlikely (status)) - return 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)) - return status; + goto cleanup_composite; status = _cairo_pdf_operators_fill (&surface->pdf_operators, path, fill_rule); } +cleanup_composite: + _cairo_composite_rectangles_fini (&extents); return status; } @@ -4050,26 +4076,23 @@ _cairo_ps_surface_show_text_glyphs (void *abstract_surface, { cairo_ps_surface_t *surface = abstract_surface; cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_bool_t overlap; cairo_status_t status; - _cairo_ps_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_glyphs (&extents, + &surface->base, op, source, scaled_font, glyphs, num_glyphs, clip, &overlap); - if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) - return CAIRO_INT_STATUS_SUCCESS; - + if (unlikely (status)) return status; - } - if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) - return _cairo_ps_surface_analyze_operation (surface, op, source, NULL, &extents.bounded); + 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)); @@ -4080,18 +4103,22 @@ _cairo_ps_surface_show_text_glyphs (void *abstract_surface, status = _cairo_ps_surface_set_clip (surface, &extents); if (unlikely (status)) - return status; + goto cleanup_composite; status = _cairo_ps_surface_emit_pattern (surface, source, &extents.bounded, op); if (unlikely (status)) - return 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); - return _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 void @@ -4222,23 +4249,16 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { 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, /* cairo_ps_surface_copy_page */ _cairo_ps_surface_show_page, + _cairo_ps_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_ps_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* Here are the drawing functions */ @@ -4246,13 +4266,8 @@ static const cairo_surface_backend_t cairo_ps_surface_backend = { _cairo_ps_surface_mask, _cairo_ps_surface_stroke, _cairo_ps_surface_fill, + NULL, /* fill-stroke */ NULL, /* show_glyphs */ - NULL, /* snapshot */ - - NULL, /* is_compatible */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ _cairo_ps_surface_has_show_text_glyphs, _cairo_ps_surface_show_text_glyphs, }; diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c index bbe5fa7e8..b04c42130 100644 --- a/src/cairo-quartz-image-surface.c +++ b/src/cairo-quartz-image-surface.c @@ -60,13 +60,22 @@ _cairo_quartz_image_surface_create_similar (void *asurface, int width, int height) { - cairo_surface_t *result; cairo_surface_t *isurf = _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status(isurf)) - return isurf; + cairo_surface_t *result = cairo_quartz_image_surface_create (isurf); + cairo_surface_destroy (isurf); - result = cairo_quartz_image_surface_create (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; @@ -96,23 +105,19 @@ _cairo_quartz_image_surface_acquire_source_image (void *asurface, return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_quartz_image_surface_acquire_dest_image (void *asurface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect, - void **image_extra) +static cairo_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_create_for_rectangle_int (surface->imageSurface, extents); +} - *image_out = surface->imageSurface; - image_rect->x = 0; - image_rect->y = 0; - image_rect->width = surface->width; - image_rect->height = surface->height; - *image_extra = NULL; - - return CAIRO_STATUS_SUCCESS; +static cairo_int_status_t +_cairo_quartz_image_surface_unmap_image (void *asurface, + cairo_image_surface_t *image) +{ + cairo_surface_destroy (&image->base); } static cairo_bool_t @@ -123,7 +128,7 @@ _cairo_quartz_image_surface_get_extents (void *asurface, extents->x = 0; extents->y = 0; - extents->width = surface->width; + extents->width = surface->width; extents->height = surface->height; return TRUE; } @@ -139,6 +144,8 @@ _cairo_quartz_image_surface_flush (void *asurface) CGImageRef oldImage = surface->image; CGImageRef newImage = NULL; + /* XXX only flush if the image has been modified. */ + /* To be released by the ReleaseCallback */ cairo_surface_reference ((cairo_surface_t*) surface->imageSurface); @@ -158,6 +165,82 @@ _cairo_quartz_image_surface_flush (void *asurface) 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_glyphs (&surface->imageSurface->base, + op, source, + glyphs, num_glyphs, scaled_font, + clip, num_remaining); +} + + static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { CAIRO_SURFACE_TYPE_QUARTZ_IMAGE, _cairo_quartz_image_surface_finish, @@ -165,39 +248,29 @@ static const cairo_surface_backend_t cairo_quartz_image_surface_backend = { _cairo_default_context_create, _cairo_quartz_image_surface_create_similar, - NULL, /* create_similar_image */ - NULL, /* map_to_image */ - NULL, /* unmap_image */ + _cairo_quartz_image_surface_create_similar_image, + _cairo_quartz_image_surface_map_to_image, + _cairo_quartz_image_surface_unmap_image, _cairo_quartz_image_surface_acquire_source_image, NULL, /* release_source_image */ - _cairo_quartz_image_surface_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_quartz_image_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + _cairo_quartz_image_surface_flush, NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - NULL, /* surface_show_glyphs */ - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL /* fill_stroke */ + _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, }; /** @@ -227,6 +300,9 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface) cairo_format_t format; unsigned char *data; + if (surface->status) + return surface; + if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_IMAGE) return SURFACE_ERROR_TYPE_MISMATCH; diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index 0f015c81d..60ce8d174 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -39,10 +39,13 @@ #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-private.h" #include "cairo-pattern-private.h" +#include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" #include @@ -500,8 +503,7 @@ _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op) { CGBlendMode blendmode; - if (op == CAIRO_OPERATOR_DEST) - return CAIRO_INT_STATUS_NOTHING_TO_DO; + 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 @@ -538,8 +540,7 @@ _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo * fallbacks, but we have to workaround operators which behave * differently in Quartz. */ if (surface->base.content == CAIRO_CONTENT_ALPHA) { - if (op == CAIRO_OPERATOR_ATOP) - return CAIRO_INT_STATUS_NOTHING_TO_DO; + assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */ if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_IN || @@ -794,7 +795,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; if (IS_EMPTY (surface)) { *image_out = NULL; - return CAIRO_STATUS_SUCCESS; + return CAIRO_INT_STATUS_NOTHING_TO_DO; } if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { @@ -924,8 +925,6 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t status = _cairo_surface_to_cgimage (pat_surf, &image); if (unlikely (status)) return status; - if (unlikely (image == NULL)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; info = malloc (sizeof (SurfacePatternDrawInfo)); if (unlikely (!info)) @@ -1082,11 +1081,12 @@ _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, static cairo_int_status_t _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, - cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) + cairo_composite_rectangles_int_t *extents) { + cairo_quartz_surface_t *surface = extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *source = &extents->surface_pattern.base; + const cairo_clip_t *clip = extents->clip; cairo_bool_t needs_temp; cairo_status_t status; @@ -1205,8 +1205,6 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, status = _cairo_surface_to_cgimage (pat_surf, &img); if (unlikely (status)) return status; - if (unlikely (img == NULL)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; state->image = img; @@ -1318,8 +1316,10 @@ _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state, static void _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state, - cairo_quartz_surface_t *surface) + cairo_composite_rectangles_int_t *extents) { + cairo_quartz_surface_t *surfce = (cairo_quartz_surface_t *)extents; + if (state->layer) { CGContextDrawLayerInRect (surface->cgContext, state->clipRect, @@ -1383,7 +1383,6 @@ _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state, } } - /* * get source/dest image implementation */ @@ -1552,39 +1551,28 @@ _cairo_quartz_surface_release_source_image (void *abstract_surface, } -static cairo_status_t -_cairo_quartz_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) +static cairo_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; - cairo_int_status_t status; - - ND ((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface)); + cairo_image_surface_t *image; + cairo_surface_t *surface; - status = _cairo_quartz_get_image (surface, image_out); + status = _cairo_quartz_get_image (surface, &image); if (unlikely (status)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return _cairo_surace_create_in_error (status); - *image_rect = surface->extents; - *image_extra = NULL; + surface = _cairo_surface_create_for_rectangle_int (&image->base, extents); + cairo_surface_destroy (&image->base); - return CAIRO_STATUS_SUCCESS; + return surface; } -static void -_cairo_quartz_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_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; - - //ND ((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); - cairo_surface_destroy (&image->base); } @@ -1624,173 +1612,218 @@ _cairo_quartz_surface_create_similar (void *abstract_surface, return similar; } -static cairo_status_t -_cairo_quartz_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) +static cairo_bool_t +_cairo_quartz_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) { - cairo_quartz_surface_t *new_surface = NULL; - cairo_format_t new_format; - CGImageRef quartz_image = NULL; - cairo_status_t status; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - *clone_out = NULL; + *extents = surface->extents; + return TRUE; +} - // verify width and height of surface - if (!_cairo_quartz_verify_surface_size (width, height)) - return CAIRO_INT_STATUS_UNSUPPORTED; +static cairo_int_status_t +_cairo_quartz_cg_paint (const cairo_compositor_t *compositor, + cairo_composite_rectangles_int_t *extents) +{ + cairo_quartz_drawing_state_t state; + cairo_int_status_t rv; - if (width == 0 || height == 0) { - *clone_out = &_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA, - width, height)->base; - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } + ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type)); - if (_cairo_surface_is_quartz (src)) { - cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src; + rv = _cairo_quartz_setup_state (&state, extents); + if (unlikely (rv)) + goto BAIL; - if (IS_EMPTY (qsurf)) { - *clone_out = &_cairo_quartz_surface_create_internal (NULL, - CAIRO_CONTENT_COLOR_ALPHA, - qsurf->extents.width, - qsurf->extents.height)->base; - *clone_offset_x = 0; - *clone_offset_y = 0; - return CAIRO_STATUS_SUCCESS; - } - } + _cairo_quartz_draw_source (&state, extents->op); + +BAIL: + _cairo_quartz_teardown_state (&state, extents); + + ND ((stderr, "-- paint\n")); + return rv; +} - status = _cairo_surface_to_cgimage (src, &quartz_image); +static cairo_int_status_t +_cairo_quartz_cg_mask_with_surface (cairo_composite_extents_t *extents, + cairo_surface_t *mask_surf, + const cairo_matrix_t *mask_mat, + CGInterpolationQuality filter) +{ + cairo_quartz_surface_t *surface = extents->surface; + cairo_operator_t op = extents->op; + const cairo_pattern_t *source = &extents->surface_pattern.base; + cairo_clip_t *clip = extents->clip; + CGRect rect; + CGImageRef img; + cairo_status_t status; + CGAffineTransform mask_matrix; + cairo_quartz_drawing_state_t state; + + status = _cairo_surface_to_cgimage (mask_surf, &img); if (unlikely (status)) - return CAIRO_INT_STATUS_UNSUPPORTED; + return status; - new_format = CAIRO_FORMAT_ARGB32; /* assumed */ - if (_cairo_surface_is_image (src)) - new_format = ((cairo_image_surface_t *) src)->format; + status = _cairo_quartz_setup_state (&state, extents); + if (unlikely (status)) + goto BAIL; - new_surface = (cairo_quartz_surface_t *) - cairo_quartz_surface_create (new_format, width, height); + rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); + _cairo_quartz_cairo_matrix_to_quartz (mask_mat, &mask_matrix); - if (quartz_image == NULL) - goto FINISH; + /* 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); - if (!new_surface || new_surface->base.status) { - CGImageRelease (quartz_image); - return CAIRO_INT_STATUS_UNSUPPORTED; - } + state.filter = filter; - CGContextSaveGState (new_surface->cgContext); + CGContextSetInterpolationQuality (state.cgMaskContext, filter); + CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone); - _cairo_quartz_surface_set_cairo_operator (new_surface, CAIRO_OPERATOR_SOURCE); + CGContextClipToMask (state.cgMaskContext, rect, img); - CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y); - CGContextDrawImage (new_surface->cgContext, - CGRectMake (0, 0, CGImageGetWidth (quartz_image), CGImageGetHeight (quartz_image)), - quartz_image); + CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0); + CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height); + CGContextConcatCTM (state.cgMaskContext, mask_matrix); - CGContextRestoreGState (new_surface->cgContext); + _cairo_quartz_draw_source (&state, extents->op); - CGImageRelease (quartz_image); +BAIL: + _cairo_quartz_teardown_state (&state, extents); -FINISH: - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &new_surface->base; + CGImageRelease (img); - return CAIRO_STATUS_SUCCESS; + return status; } -static cairo_bool_t -_cairo_quartz_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *extents) +static cairo_int_status_t +_cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface, + cairo_composite_rectangles_t *extents) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; + cairo_quartz_drawing_state_t state; + double alpha = extents->mask_pattern.solid.color.alpha; + cairo_status_t status; - *extents = surface->extents; - return TRUE; + 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_surface_paint_cg (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) +_cairo_quartz_cg_mask (const cairo_compositor_t *compositor, + cairo_composite_rectangles_int_t *extents) { - cairo_quartz_drawing_state_t state; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface; + cairo_operator_t op = extents->op; + 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_paint op %d source->type %d\n", surface, op, source->type)); + ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type)); - if (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; + if (mask->type == CAIRO_PATTERN_TYPE_SOLID) + return _cairo_quartz_cg_mask_with_solid (surface, extents); - rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); - if (unlikely (rv)) - goto BAIL; + need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE || + mask->extend != CAIRO_EXTEND_NONE); - _cairo_quartz_draw_source (&state, op); + filter = _cairo_quartz_filter_to_quartz (source->filter); -BAIL: - _cairo_quartz_teardown_state (&state, surface); + if (! need_temp) { + mask_surf = extents->mask_pattern.surface.surface; - ND ((stderr, "-- paint\n")); - return rv; -} + /* 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); + } -static cairo_int_status_t -_cairo_quartz_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; + if (! need_temp) { + CGInterpolationQuality mask_filter; + cairo_bool_t simple_transform; - rv = _cairo_quartz_surface_paint_cg (surface, - op, - source, - clip); + matrix = mask->matrix; - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; + 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); + } - rv = _cairo_quartz_get_image (surface, &image); - if (likely (rv == CAIRO_STATUS_SUCCESS)) { - rv = _cairo_surface_paint (&image->base, op, source, clip); - cairo_surface_destroy (&image->base); + /* 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; } - return rv; + 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_surface_fill_cg (cairo_quartz_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_quartz_cg_fill (const cairo_compositor_t *compositor, + cairo_composite_rectangles_int_t *extents, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) { + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface; 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", surface, op, source->type)); - if (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); + rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; @@ -1810,75 +1843,34 @@ _cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface, else CGContextEOClip (state.cgMaskContext); - _cairo_quartz_draw_source (&state, op); + _cairo_quartz_draw_source (&state, extents->op); } BAIL: - _cairo_quartz_teardown_state (&state, surface); + _cairo_quartz_teardown_state (&state, extents); ND ((stderr, "-- fill\n")); return rv; } static cairo_int_status_t -_cairo_quartz_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_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_fill_cg (surface, - op, - source, - path, - fill_rule, - tolerance, - antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (likely (rv == CAIRO_STATUS_SUCCESS)) { - rv = _cairo_surface_fill (&image->base, op, source, - path, fill_rule, tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - -static cairo_int_status_t -_cairo_quartz_surface_stroke_cg (cairo_quartz_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_quartz_cg_stroke (const cairo_compositor_t *compositor, + cairo_composite_rectangles_int_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_surface_t *surface = (cairo_quartz_surface_t *)extents->surface; 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", surface, op, source->type)); - if (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); + rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; @@ -1930,63 +1922,26 @@ _cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface, _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform); CGContextConcatCTM (state.cgMaskContext, invStrokeTransform); - _cairo_quartz_draw_source (&state, op); + _cairo_quartz_draw_source (&state, extents->op); } BAIL: - _cairo_quartz_teardown_state (&state, surface); + _cairo_quartz_teardown_state (&state, extents); ND ((stderr, "-- stroke\n")); return rv; } -static cairo_int_status_t -_cairo_quartz_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_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_stroke_cg (surface, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (likely (rv == CAIRO_STATUS_SUCCESS)) { - rv = _cairo_surface_stroke (&image->base, op, source, - path, style, ctm, ctm_inverse, - tolerance, antialias, - clip); - cairo_surface_destroy (&image->base); - } - - return rv; -} - #if CAIRO_HAS_QUARTZ_FONT static cairo_int_status_t -_cairo_quartz_surface_show_glyphs_cg (cairo_quartz_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, - int *remaining_glyphs) +_cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor, + cairo_composite_rectangles_int_t *extents, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_bool_t overlap) { + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)_surface; CGAffineTransform textTransform, invTextTransform; CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)]; @@ -1995,23 +1950,17 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize)); cairo_quartz_drawing_state_t state; - cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; + 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 (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - if (num_glyphs <= 0) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) return CAIRO_INT_STATUS_UNSUPPORTED; - rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); + rv = _cairo_quartz_setup_state (&state, extents); if (unlikely (rv)) goto BAIL; @@ -2106,13 +2055,13 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y); if (state.action != DO_DIRECT) - _cairo_quartz_draw_source (&state, op); + _cairo_quartz_draw_source (&state, extents->op); BAIL: if (didForceFontSmoothing) CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); - _cairo_quartz_teardown_state (&state, surface); + _cairo_quartz_teardown_state (&state, extents); if (cg_glyphs != glyphs_static) free (cg_glyphs); @@ -2121,254 +2070,90 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, } #endif /* CAIRO_HAS_QUARTZ_FONT */ -static cairo_int_status_t -_cairo_quartz_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, - int *remaining_glyphs) -{ - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED; - cairo_image_surface_t *image; +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 - rv = _cairo_quartz_surface_show_glyphs_cg (surface, op, source, - glyphs, num_glyphs, - scaled_font, clip, remaining_glyphs); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - + _cairo_quartz_cg_glyphs, +#else + NULL, #endif +}; - rv = _cairo_quartz_get_image (surface, &image); - if (likely (rv == CAIRO_STATUS_SUCCESS)) { - rv = _cairo_surface_show_text_glyphs (&image->base, op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, 0, - scaled_font, clip); - cairo_surface_destroy (&image->base); - } - - return rv; +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_with_surface (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - cairo_surface_t *mask_surf, - const cairo_matrix_t *mask_mat, - CGInterpolationQuality filter, - const cairo_clip_t *clip) +_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) { - CGRect rect; - CGImageRef img; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - CGAffineTransform mask_matrix; - cairo_quartz_drawing_state_t state; - - if (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - status = _cairo_surface_to_cgimage (mask_surf, &img); - if (unlikely (status)) - return status; - - status = _cairo_quartz_setup_state (&state, surface, op, source, clip); - if (unlikely (status)) - goto BAIL; - - if (unlikely (img == NULL)) - goto BAIL; - - rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img)); - _cairo_quartz_cairo_matrix_to_quartz (mask_mat, &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, op); - -BAIL: - _cairo_quartz_teardown_state (&state, surface); - - CGImageRelease (img); - - return status; + return _cairo_compositor_mask (&_cairo_quartz_cg_compositor, + surface, op, source, mask, + clip); } static cairo_int_status_t -_cairo_quartz_surface_mask_with_solid (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - double alpha, - const cairo_clip_t *clip) +_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) { - cairo_quartz_drawing_state_t state; - cairo_status_t status; - - status = _cairo_quartz_setup_state (&state, surface, op, source, clip); - if (unlikely (status)) - goto BAIL; - - CGContextSetAlpha (surface->cgContext, alpha); - _cairo_quartz_draw_source (&state, op); - -BAIL: - _cairo_quartz_teardown_state (&state, surface); - - return status; + 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_mask_cg (cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip) +_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) { - 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", surface, op, source->type, mask->type)); - - if (IS_EMPTY (surface)) - return CAIRO_INT_STATUS_NOTHING_TO_DO; - - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) { - const cairo_solid_pattern_t *mask_solid; - - mask_solid = (const cairo_solid_pattern_t *) mask; - return _cairo_quartz_surface_mask_with_solid (surface, op, source, - mask_solid->color.alpha, - clip); - } - - 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 = ((const cairo_surface_pattern_t *) mask)->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_surface_mask_with_surface (surface, op, source, - mask_surf, - &matrix, - filter, - clip); - -BAIL: - - if (need_temp) - cairo_surface_destroy (mask_surf); - - return status; + 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_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_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, + int *remaining_glyphs) { - cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; - cairo_int_status_t rv; - cairo_image_surface_t *image; - - rv = _cairo_quartz_surface_mask_cg (surface, - op, - source, - mask, - clip); - - if (likely (rv != CAIRO_INT_STATUS_UNSUPPORTED)) - return rv; - - rv = _cairo_quartz_get_image (surface, &image); - if (likely (rv == CAIRO_STATUS_SUCCESS)) { - rv = _cairo_surface_mask (&image->base, op, source, mask, clip); - cairo_surface_destroy (&image->base); - } - - return rv; + return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor, + surface, op, source, + glyphs, num_glyphs, scaled_font, + clip, remaining_glyphs); } static cairo_status_t @@ -2422,38 +2207,28 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = { _cairo_quartz_surface_create_similar, NULL, /* similar image */ - NULL, /* map to image */ - NULL, /* unmap image */ + _cairo_quartz_surface_map_to_image, + _cairo_quartz_surface_unmap_image, _cairo_quartz_surface_acquire_source_image, _cairo_quartz_surface_release_source_image, - _cairo_quartz_surface_acquire_dest_image, - _cairo_quartz_surface_release_dest_image, - _cairo_quartz_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ + _cairo_quartz_surface_snapshot, + NULL, /* copy_page */ NULL, /* show_page */ + _cairo_quartz_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_quartz_surface_paint, _cairo_quartz_surface_mask, _cairo_quartz_surface_stroke, _cairo_quartz_surface_fill, - _cairo_quartz_surface_show_glyphs, - - _cairo_quartz_surface_snapshot, - NULL, /* is_similar */ - NULL /* fill_stroke */ + NULL /* fill-stroke */ + _cairo_quartz_surface_glyphs, }; cairo_quartz_surface_t * @@ -2578,7 +2353,6 @@ cairo_quartz_surface_create (cairo_format_t format, int stride; int bitsPerComponent; - // 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)); diff --git a/src/cairo-recording-surface-private.h b/src/cairo-recording-surface-private.h index ea38c198d..6e93eab00 100644 --- a/src/cairo-recording-surface-private.h +++ b/src/cairo-recording-surface-private.h @@ -40,6 +40,7 @@ #include "cairoint.h" #include "cairo-path-fixed-private.h" #include "cairo-pattern-private.h" +#include "cairo-surface-backend-private.h" typedef enum { /* The 5 basic drawing operations. */ @@ -178,6 +179,11 @@ _cairo_recording_surface_get_bbox (cairo_recording_surface_t *recording, cairo_box_t *bbox, const cairo_matrix_t *transform); +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); + static inline cairo_bool_t _cairo_recording_surface_get_bounds (cairo_surface_t *surface, cairo_rectangle_t *extents) diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c index 6d4cb3792..7a18b53ac 100644 --- a/src/cairo-recording-surface.c +++ b/src/cairo-recording-surface.c @@ -77,6 +77,8 @@ */ #include "cairoint.h" + +#include "cairo-array-private.h" #include "cairo-analysis-surface-private.h" #include "cairo-clip-private.h" #include "cairo-combsort-private.h" @@ -86,6 +88,7 @@ #include "cairo-image-surface-private.h" #include "cairo-recording-surface-private.h" #include "cairo-surface-wrapper-private.h" +#include "cairo-traps-private.h" typedef enum { CAIRO_RECORDING_REPLAY, @@ -493,42 +496,6 @@ _cairo_recording_surface_finish (void *abstract_surface) return CAIRO_STATUS_SUCCESS; } -static cairo_status_t -_cairo_recording_surface_acquire_source_image_transformed (void *abstract_surface, - cairo_matrix_t *device_transform, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_status_t status; - cairo_recording_surface_t *surface = abstract_surface; - cairo_surface_t *image; - double width; - double height; - - width = surface->extents.width * device_transform->xx; - height = surface->extents.height * device_transform->yy; - image = _cairo_image_surface_create_with_content (surface->base.content, - width, height); - if (unlikely (image->status)) - return image->status; - - cairo_surface_set_device_offset (image, - -surface->extents.x, - -surface->extents.y); - - _cairo_surface_set_device_scale (image, - device_transform->xx, device_transform->yy); - status = _cairo_recording_surface_replay (&surface->base, image); - if (unlikely (status)) { - cairo_surface_destroy (image); - return status; - } - - *image_out = (cairo_image_surface_t *) image; - *image_extra = NULL; - return CAIRO_STATUS_SUCCESS; -} - struct proxy { cairo_surface_t base; cairo_surface_t *image; @@ -680,15 +647,6 @@ _command_init (cairo_recording_surface_t *surface, return status; } -static const cairo_rectangle_int_t * -_cairo_recording_surface_extents (cairo_recording_surface_t *surface) -{ - if (surface->unbounded) - return &_cairo_unbounded_rectangle; - - return &surface->extents; -} - static void _cairo_recording_surface_break_self_copy_loop (cairo_recording_surface_t *surface) { @@ -713,7 +671,6 @@ _cairo_recording_surface_paint (void *abstract_surface, cairo_recording_surface_t *surface = abstract_surface; cairo_command_paint_t *command; cairo_composite_rectangles_t composite; - const cairo_rectangle_int_t *extents; if (op == CAIRO_OPERATOR_CLEAR && clip == NULL) { if (surface->optimize_clears) { @@ -723,8 +680,8 @@ _cairo_recording_surface_paint (void *abstract_surface, } } - extents = _cairo_recording_surface_extents (surface); - status = _cairo_composite_rectangles_init_for_paint (&composite, extents, + status = _cairo_composite_rectangles_init_for_paint (&composite, + &surface->base, op, source, clip); if (unlikely (status)) @@ -776,10 +733,9 @@ _cairo_recording_surface_mask (void *abstract_surface, cairo_recording_surface_t *surface = abstract_surface; cairo_command_mask_t *command; cairo_composite_rectangles_t composite; - const cairo_rectangle_int_t *extents; - extents = _cairo_recording_surface_extents (surface); - status = _cairo_composite_rectangles_init_for_mask (&composite, extents, + status = _cairo_composite_rectangles_init_for_mask (&composite, + &surface->base, op, source, mask, clip); if (unlikely (status)) @@ -842,10 +798,9 @@ _cairo_recording_surface_stroke (void *abstract_surface, cairo_recording_surface_t *surface = abstract_surface; cairo_command_stroke_t *command; cairo_composite_rectangles_t composite; - const cairo_rectangle_int_t *extents; - extents = _cairo_recording_surface_extents (surface); - status = _cairo_composite_rectangles_init_for_stroke (&composite, extents, + status = _cairo_composite_rectangles_init_for_stroke (&composite, + &surface->base, op, source, path, style, ctm, clip); @@ -918,10 +873,9 @@ _cairo_recording_surface_fill (void *abstract_surface, cairo_recording_surface_t *surface = abstract_surface; cairo_command_fill_t *command; cairo_composite_rectangles_t composite; - const cairo_rectangle_int_t *extents; - extents = _cairo_recording_surface_extents (surface); - status = _cairo_composite_rectangles_init_for_fill (&composite, extents, + status = _cairo_composite_rectangles_init_for_fill (&composite, + &surface->base, op, source, path, clip); if (unlikely (status)) @@ -996,10 +950,9 @@ _cairo_recording_surface_show_text_glyphs (void *abstract_surface, cairo_recording_surface_t *surface = abstract_surface; cairo_command_show_text_glyphs_t *command; cairo_composite_rectangles_t composite; - const cairo_rectangle_int_t *extents; - extents = _cairo_recording_surface_extents (surface); - status = _cairo_composite_rectangles_init_for_glyphs (&composite, extents, + status = _cairo_composite_rectangles_init_for_glyphs (&composite, + &surface->base, op, source, scaled_font, glyphs, num_glyphs, @@ -1121,6 +1074,9 @@ _cairo_recording_surface_snapshot (void *abstract_other) 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 *)); status = _cairo_recording_surface_replay (&other->base, &surface->base); if (unlikely (status)) { @@ -1128,9 +1084,6 @@ _cairo_recording_surface_snapshot (void *abstract_other) return _cairo_surface_create_in_error (status); } - surface->indices = NULL; - surface->num_indices = 0; - return &surface->base; } @@ -1160,23 +1113,16 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { _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 @@ -1187,18 +1133,10 @@ static const cairo_surface_backend_t cairo_recording_surface_backend = { _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_acquire_source_image_transformed }; cairo_int_status_t @@ -1723,3 +1661,30 @@ _cairo_recording_surface_get_bbox (cairo_recording_surface_t *surface, 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_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; +} diff --git a/src/cairo-rectangular-scan-converter.c b/src/cairo-rectangular-scan-converter.c index 56e552b7a..b4214c88d 100644 --- a/src/cairo-rectangular-scan-converter.c +++ b/src/cairo-rectangular-scan-converter.c @@ -379,18 +379,20 @@ _active_edges_to_spans (sweep_line_t *sweep) 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; } 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 @@ _active_edges_to_spans (sweep_line_t *sweep) 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 @@ generate (cairo_rectangular_scan_converter_t *self, 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 @@ generate (cairo_rectangular_scan_converter_t *self, 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,16 +575,9 @@ generate (cairo_rectangular_scan_converter_t *self, 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; @@ -611,18 +607,18 @@ static void generate_row(cairo_span_renderer_t *renderer, } if (! _cairo_fixed_is_integer (r->right)) { - spans[num_spans].x = x2; + spans[num_spans].x = x2++; spans[num_spans].coverage = coverage * _cairo_fixed_fractional_part (r->right) >> 8; num_spans++; } } else { - spans[num_spans].x = x1; + spans[num_spans].x = x2++; spans[num_spans].coverage = coverage * (r->right - r->left) >> 8; num_spans++; } - spans[num_spans].x = x2 + 1; + spans[num_spans].x = x2; spans[num_spans].coverage = 0; num_spans++; @@ -668,7 +664,8 @@ _cairo_rectangular_scan_converter_generate (void *converter, 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); } @@ -743,17 +740,22 @@ _cairo_rectangular_scan_converter_add_box (cairo_rectangular_scan_converter_t *s 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); - - self->num_rectangles++; + 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; } @@ -775,14 +777,9 @@ _cairo_rectangular_scan_converter_init (cairo_rectangular_scan_converter_t *self 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; diff --git a/src/cairo-reference-count-private.h b/src/cairo-reference-count-private.h index 0cb5695dd..75fdf3538 100644 --- a/src/cairo-reference-count-private.h +++ b/src/cairo-reference-count-private.h @@ -45,6 +45,7 @@ typedef struct { } 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)) diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h index 029377b17..da7b34698 100644 --- a/src/cairo-scaled-font-private.h +++ b/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,20 +114,70 @@ struct _cairo_scaled_font { 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; 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_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_font_revoke_ownership (cairo_scaled_font_t *scaled_font); +_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 */ diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c index 5b7754695..be612a9b9 100644 --- a/src/cairo-scaled-font.c +++ b/src/cairo-scaled-font.c @@ -43,6 +43,7 @@ #include "cairo-image-surface-private.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) @@ -199,10 +200,13 @@ static void _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; - - if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL) - surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font); + 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 (scaled_glyph->surface != NULL) cairo_surface_destroy (&scaled_glyph->surface->base); @@ -243,8 +247,7 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = { { NULL, NULL }, /* pages */ FALSE, /* cache_frozen */ FALSE, /* global_cache_frozen */ - NULL, /* surface_backend */ - NULL, /* surface_private */ + { NULL, NULL }, /* privates */ NULL /* backend */ }; @@ -445,6 +448,8 @@ _cairo_scaled_glyph_page_destroy (void *closure) cairo_scaled_font_t *scaled_font; unsigned int n; + assert (! cairo_list_is_empty (&page->link)); + scaled_font = (cairo_scaled_font_t *) page->cache_entry.hash; for (n = 0; n < page->num_glyphs; n++) { _cairo_hash_table_remove (scaled_font->glyphs, @@ -734,8 +739,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t *scaled_font, 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); @@ -826,9 +830,13 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) 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); @@ -836,30 +844,77 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font) _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); +} + +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; - if (scaled_font->surface_backend->scaled_font_fini != NULL) - scaled_font->surface_backend->scaled_font_fini (scaled_font); + 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; + } + } - scaled_font->surface_backend = NULL; - scaled_font->surface_private = NULL; + 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; } /** @@ -2209,6 +2264,8 @@ _cairo_scaled_font_glyph_approximate_extents (cairo_scaled_font_t *scaled_font, extents->height = ceil (y1 + pad) - extents->y; } +#if 0 +/* XXX win32 */ cairo_status_t _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, cairo_operator_t op, @@ -2399,6 +2456,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font, 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 @@ -2844,6 +2902,7 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, 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 = diff --git a/src/cairo-script-private.h b/src/cairo-script-private.h index 698a54e30..5b506f500 100644 --- a/src/cairo-script-private.h +++ b/src/cairo-script-private.h @@ -37,10 +37,11 @@ #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-script.h" +#include "cairo-types-private.h" CAIRO_BEGIN_DECLS diff --git a/src/cairo-script-surface.c b/src/cairo-script-surface.c index 56e9d80aa..7199cd131 100644 --- a/src/cairo-script-surface.c +++ b/src/cairo-script-surface.c @@ -81,7 +81,7 @@ 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_surface_font_private cairo_script_surface_font_private_t; +typedef struct _cairo_script_font cairo_script_font_t; typedef struct _operand { enum { @@ -121,8 +121,9 @@ struct _cairo_script_context { cairo_list_t defines; }; -struct _cairo_script_surface_font_private { - cairo_script_context_t *ctx; +struct _cairo_script_font { + cairo_scaled_font_private_t base; + cairo_bool_t has_sfnt; unsigned long id; unsigned long subset_glyph_index; @@ -173,7 +174,8 @@ _cairo_script_surface_create_internal (cairo_script_context_t *ctx, cairo_surface_t *passthrough); static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); +_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); @@ -2022,14 +2024,11 @@ _device_destroy (void *abstract_device) cairo_status_t status; while (! cairo_list_is_empty (&ctx->fonts)) { - cairo_script_surface_font_private_t *font; + cairo_script_font_t *font; - font = cairo_list_first_entry (&ctx->fonts, - cairo_script_surface_font_private_t, - link); + font = cairo_list_first_entry (&ctx->fonts, cairo_script_font_t, link); + cairo_list_del (&font->base.link); cairo_list_del (&font->link); - if (font->parent->surface_private == font) - font->parent->surface_private = NULL; free (font); } @@ -2733,31 +2732,39 @@ _emit_font_options (cairo_script_surface_t *surface, } static void -_cairo_script_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) +_cairo_script_scaled_font_fini (cairo_scaled_font_private_t *abstract_private, + cairo_scaled_font_t *scaled_font) { - cairo_script_surface_font_private_t *font_private; + 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; - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_status_t status; - cairo_device_t *device; + 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); - status = cairo_device_acquire (device = &font_private->ctx->base); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - _cairo_output_stream_printf (font_private->ctx->stream, - "/f%lu undef /sf%lu undef\n", - font_private->id, - font_private->id); + _bitmap_release_id (&ctx->font_id, priv->id); + cairo_device_release (&ctx->base); + } - _bitmap_release_id (&font_private->ctx->font_id, font_private->id); - cairo_list_del (&font_private->link); - free (font_private); + cairo_list_del (&priv->link); + cairo_list_del (&priv->base.link); + free (priv); +} - cairo_device_release (device); - } +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); +} - scaled_font->surface_private = NULL; - } +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 @@ -2766,7 +2773,6 @@ _emit_type42_font (cairo_script_surface_t *surface, { cairo_script_context_t *ctx = to_context (surface); const cairo_scaled_font_backend_t *backend; - cairo_script_surface_font_private_t *font_private; cairo_output_stream_t *base85_stream; cairo_output_stream_t *zlib_stream; cairo_status_t status, status2; @@ -2824,27 +2830,29 @@ _emit_type42_font (cairo_script_surface_t *surface, if (status == CAIRO_STATUS_SUCCESS) status = status2; - font_private = scaled_font->surface_private; _cairo_output_stream_printf (ctx->stream, "~> >> font dup /f%lu exch def set-font-face", - font_private->id); + _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_scaled_font_t *scaled_font, + cairo_script_font_t **font_out) { cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *font_private; cairo_int_status_t status; - font_private = malloc (sizeof (cairo_script_surface_font_private_t)); + font_private = malloc (sizeof (cairo_script_font_t)); if (unlikely (font_private == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - font_private->ctx = ctx; + _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; @@ -2858,16 +2866,17 @@ _emit_scaled_font_init (cairo_script_surface_t *surface, return status; } - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &_cairo_script_surface_backend; - status = _emit_context (surface); - if (unlikely (status)) + if (unlikely (status)) { + free (font_private); return status; + } status = _emit_type42_font (surface, scaled_font); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + *font_out = font_private; return status; + } font_private->has_sfnt = FALSE; _cairo_output_stream_printf (ctx->stream, @@ -2883,6 +2892,7 @@ _emit_scaled_font_init (cairo_script_surface_t *surface, scaled_font->fs_extents.max_y_advance, font_private->id); + *font_out = font_private; return CAIRO_STATUS_SUCCESS; } @@ -2895,7 +2905,7 @@ _emit_scaled_font (cairo_script_surface_t *surface, cairo_font_options_t options; cairo_bool_t matrix_updated = FALSE; cairo_status_t status; - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *font_private; cairo_scaled_font_get_ctm (scaled_font, &matrix); status = _emit_scaling_matrix (surface, &matrix, &matrix_updated); @@ -2907,13 +2917,7 @@ _emit_scaled_font (cairo_script_surface_t *surface, surface->cr.current_scaled_font = scaled_font; - if (! (scaled_font->surface_backend == NULL || - scaled_font->surface_backend == &_cairo_script_surface_backend)) - { - _cairo_scaled_font_revoke_ownership (scaled_font); - } - - font_private = scaled_font->surface_private; + 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); @@ -2925,13 +2929,10 @@ _emit_scaled_font (cairo_script_surface_t *surface, if (unlikely (status)) return status; - status = _emit_scaled_font_init (surface, scaled_font); + status = _emit_scaled_font_init (surface, scaled_font, &font_private); if (unlikely (status)) return status; - font_private = scaled_font->surface_private; - assert (font_private != NULL); - assert (target_is_active (surface)); _cairo_output_stream_printf (ctx->stream, " /scaled-font get /sf%lu exch def\n", @@ -2948,17 +2949,17 @@ _emit_scaled_font (cairo_script_surface_t *surface, 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_surface_font_private_t *font_private; cairo_script_implicit_context_t old_cr; cairo_status_t status; unsigned long index; - font_private = scaled_font->surface_private; index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" @@ -2997,16 +2998,16 @@ _emit_scaled_glyph_vector (cairo_script_surface_t *surface, 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_script_surface_font_private_t *font_private; cairo_status_t status; unsigned long index; - font_private = scaled_font->surface_private; index = ++font_private->subset_glyph_index; - scaled_glyph->surface_private = (void *) index; + scaled_glyph->dev_private_key = ctx; + scaled_glyph->dev_private = (void *) index; _cairo_output_stream_printf (ctx->stream, "%lu <<\n" @@ -3049,15 +3050,10 @@ static cairo_status_t _emit_scaled_glyph_prologue (cairo_script_surface_t *surface, cairo_scaled_font_t *scaled_font) { - cairo_script_surface_font_private_t *font_private; - - assert (scaled_font->surface_backend == &_cairo_script_surface_backend); - - font_private = scaled_font->surface_private; + cairo_script_context_t *ctx = to_context (surface); - _cairo_output_stream_printf (to_context (surface)->stream, - "f%lu /glyphs get\n", - font_private->id); + _cairo_output_stream_printf (ctx->stream, "f%lu /glyphs get\n", + _cairo_script_font_id (ctx, scaled_font)); return CAIRO_STATUS_SUCCESS; } @@ -3068,7 +3064,8 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, cairo_glyph_t *glyphs, unsigned int num_glyphs) { - cairo_script_surface_font_private_t *font_private; + 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; @@ -3076,7 +3073,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, if (num_glyphs == 0) return CAIRO_STATUS_SUCCESS; - font_private = scaled_font->surface_private; + font_private = _cairo_script_font_get (ctx, scaled_font); if (font_private->has_sfnt) return CAIRO_STATUS_SUCCESS; @@ -3091,7 +3088,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, if (unlikely (status)) break; - if (scaled_glyph->surface_private != NULL) + if (scaled_glyph->dev_private_key == ctx) continue; status = _cairo_scaled_glyph_lookup (scaled_font, @@ -3111,7 +3108,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, } status = _emit_scaled_glyph_vector (surface, - scaled_font, + scaled_font, font_private, scaled_glyph); if (unlikely (status)) break; @@ -3137,6 +3134,7 @@ _emit_scaled_glyphs (cairo_script_surface_t *surface, status = _emit_scaled_glyph_bitmap (surface, scaled_font, + font_private, scaled_glyph); if (unlikely (status)) break; @@ -3234,7 +3232,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, { cairo_script_surface_t *surface = abstract_surface; cairo_script_context_t *ctx = to_context (surface); - cairo_script_surface_font_private_t *font_private; + cairo_script_font_t *font_private; cairo_scaled_glyph_t *scaled_glyph; cairo_matrix_t matrix; cairo_status_t status; @@ -3289,7 +3287,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, iy -= scaled_font->font_matrix.y0; _cairo_scaled_font_freeze_cache (scaled_font); - font_private = scaled_font->surface_private; + font_private = _cairo_script_font_get (ctx, scaled_font); _cairo_output_stream_printf (ctx->stream, "[%f %f ", @@ -3309,7 +3307,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, goto BAIL; } - if ((long unsigned) scaled_glyph->surface_private > 256) + if ((long unsigned) scaled_glyph->dev_private > 256) break; } } @@ -3378,7 +3376,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, if (font_private->has_sfnt) c = glyphs[n].index; else - c = (uint8_t) (long unsigned) scaled_glyph->surface_private; + c = (uint8_t) (long unsigned) scaled_glyph->dev_private; _cairo_output_stream_write (base85_stream, &c, 1); } else { @@ -3387,7 +3385,7 @@ _cairo_script_surface_show_text_glyphs (void *abstract_surface, glyphs[n].index); else _cairo_output_stream_printf (ctx->stream, " %lu", - (long unsigned) scaled_glyph->surface_private); + (long unsigned) scaled_glyph->dev_private); } dx = scaled_glyph->metrics.x_advance; @@ -3512,40 +3510,23 @@ _cairo_script_surface_backend = { _cairo_script_surface_acquire_source_image, _cairo_script_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_script_surface_snapshot, + _cairo_script_surface_copy_page, _cairo_script_surface_show_page, + _cairo_script_surface_get_extents, - NULL, /* old_show_glyphs */ NULL, /* get_font_options */ + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - _cairo_script_surface_scaled_font_fini, - NULL, /* scaled_glyph_fini */ - /* The 5 high level operations */ _cairo_script_surface_paint, _cairo_script_surface_mask, _cairo_script_surface_stroke, _cairo_script_surface_fill, - NULL, - - _cairo_script_surface_snapshot, - - NULL, /* is_similar */ - /* XXX need fill-stroke for passthrough */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - /* The alternate high-level text operation */ + NULL, /* fill/stroke */ + NULL, /* glyphs */ _cairo_script_surface_has_show_text_glyphs, _cairo_script_surface_show_text_glyphs }; diff --git a/src/cairo-slope.c b/src/cairo-slope.c index 827037f76..cc5f30cb0 100644 --- a/src/cairo-slope.c +++ b/src/cairo-slope.c @@ -89,9 +89,9 @@ _cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b) */ 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. */ diff --git a/src/cairo-spans-compositor-private.h b/src/cairo-spans-compositor-private.h new file mode 100644 index 000000000..417563920 --- /dev/null +++ b/src/cairo-spans-compositor-private.h @@ -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 © 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 + */ + +#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[2048]; +} cairo_abstract_span_renderer_t; + +struct cairo_spans_compositor { + cairo_compositor_t base; + + /* 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_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_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 */ diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c new file mode 100644 index 000000000..4320a3ecc --- /dev/null +++ b/src/cairo-spans-compositor.c @@ -0,0 +1,1007 @@ +/* -*- 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 + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-compositor-private.h" +#include "cairo-clip-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-private.h" +#include "cairo-region-private.h" +#include "cairo-recording-surface-private.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.limits = NULL; + polygon.num_limits = 0; + + antialias = clip_path->antialias; + fill_rule = clip_path->fill_rule; + 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_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; + + clip = get_clip_surface (compositor, extents->surface, extents->clip, + &extents->unbounded); + if (unlikely (clip->status)) + return clip->status; + + status = _cairo_composite_rectangles_init_for_boxes (&composite, + extents->surface, + CAIRO_OPERATOR_DEST_OUT, + &_cairo_pattern_white.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; + + /* 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_DEST_OUT, + &_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); +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); + + 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); + } + + /* 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; + } + + /* 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 { + status = compositor->fill_boxes (extents->surface, + CAIRO_OPERATOR_CLEAR, + CAIRO_COLOR_TRANSPARENT, + &clear); + } + +error: + _cairo_boxes_fini (&clear); + return status; +} + +static cairo_surface_t * +unwrap_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); + if (_cairo_surface_is_observer (surface)) + surface = _cairo_surface_observer_get_target (surface); + return surface; +} + +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_surface (pattern); + if (surface->unbounded) + return TRUE; + + if (sample->x >= surface->extents.x && + sample->y >= surface->extents.y && + sample->x + sample->width <= surface->extents.x + surface->extents.width && + sample->y + sample->height <= surface->extents.y + surface->extents.height) + { + return TRUE; + } + + return FALSE; +} + +static cairo_bool_t +op_reduces_to_source (const 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_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 = extents->clip->path != NULL; + cairo_bool_t op_is_source; + cairo_bool_t no_mask; + cairo_bool_t inplace; + + if (need_clip_mask && ! extents->is_bounded) + return CAIRO_INT_STATUS_UNSUPPORTED; + + op_is_source = op_reduces_to_source (extents); + no_mask = extents->mask_pattern.base.type == CAIRO_PATTERN_TYPE_SOLID && + CAIRO_ALPHA_IS_OPAQUE (extents->mask_pattern.solid.color.alpha); + inplace = ! need_clip_mask && op_is_source && no_mask; + + if (op == CAIRO_OPERATOR_SOURCE && (need_clip_mask || ! no_mask)) { + /* SOURCE with a mask is actually a LERP in cairo semantics */ + /* XXX push this choice down to the backend */ + 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); + + recording_clip = _cairo_clip_from_boxes (boxes); + status = _cairo_recording_surface_replay_with_clip (unwrap_surface (source), + &source->matrix, + dst, recording_clip); + _cairo_clip_destroy (recording_clip); + + return status; + } + + 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); +#if 0 + } else if (inplace && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + status = upload_inplace (compositor, extents, boxes); +#endif + } else { + 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; + } + } + + if (mask && op == CAIRO_OPERATOR_CLEAR) { + source = &_cairo_pattern_white.base; + op = CAIRO_OPERATOR_DEST_OUT; + } + + 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 *composit, + const cairo_box_t *extents) +{ + cairo_bool_t needs_clip; + + needs_clip = ! composit->is_bounded; + if (needs_clip) + needs_clip = ! _cairo_clip_contains_box (composit->clip, extents); + + return needs_clip; +} + +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; + + _cairo_box_from_rectangle (&box, &extents->bounded); + if (composite_needs_clip (extents, &box)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_rectangular_scan_converter_init (&converter, &extents->bounded); + 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, 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; + + needs_clip = composite_needs_clip (extents, &polygon->extents); + if (needs_clip) { + 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->bounded; + + 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, 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; + + 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); + 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; + + if (_cairo_polygon_is_empty (polygon)) { + cairo_boxes_t boxes; + + if (extents->is_bounded) + return CAIRO_STATUS_SUCCESS; + + _cairo_boxes_init (&boxes); + return fixup_unbounded_boxes (compositor, extents, &boxes); + } + +#if 0 /* XXX not accurate currently... */ + if (antialias == CAIRO_ANTIALIAS_NONE) + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif + + if (extents->is_bounded && extents->clip->path) { + cairo_polygon_t clipper; + cairo_antialias_t clip_antialias; + cairo_fill_rule_t clip_fill_rule; + + 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); + } else { + _cairo_polygon_fini (&clipper); + } + } + } + + status = trim_extents_to_polygon (extents, polygon); + if (unlikely (status)) + return status; + + 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; + + _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; + + _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; + + 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; + + if (extents->mask.width > extents->unbounded.width || + extents->mask.height > extents->unbounded.height) + { + _cairo_polygon_init_with_clip (&polygon, extents->clip); + } + else + { + _cairo_polygon_init_with_clip (&polygon, NULL); + } + 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_FILL_RULE_WINDING, + antialias); + } + _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; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + if (_cairo_path_fixed_fill_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_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 (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); + } + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { + if (extents->is_bounded) { + if (extents->clip->boxes != &extents->clip->embedded_box) + free (extents->clip->boxes); + + extents->clip->num_boxes = 1; + extents->clip->boxes = &extents->clip->embedded_box; + extents->clip->boxes[0] = polygon.extents; + } + status = clip_and_composite_polygon (compositor, extents, &polygon, + fill_rule, antialias); + } + _cairo_polygon_fini (&polygon); + } + + 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; +} diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h index 00a4df868..c42b5afa7 100644 --- a/src/cairo-spans-private.h +++ b/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 @@ struct _cairo_scan_converter { /* 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 @@ _cairo_tor_scan_converter_create (int xmin, int ymin, int xmax, int ymax, - cairo_fill_rule_t fill_rule); + 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; diff --git a/src/cairo-spans.c b/src/cairo-spans.c index f6586506b..b8d41800e 100644 --- a/src/cairo-spans.c +++ b/src/cairo-spans.c @@ -32,118 +32,12 @@ #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; - cairo_clip_path_t *clip_path = rects->clip->path; - - if (rects->is_bounded) { - if (polygon->num_edges == 0) - return CAIRO_STATUS_SUCCESS; - - if (clip_path) { /* XXX */ - cairo_polygon_t clipper; - cairo_fill_rule_t clipper_fill_rule; - cairo_antialias_t clipper_antialias; - - if (_cairo_clip_get_polygon (rects->clip, &clipper, - &clipper_fill_rule, - &clipper_antialias) == CAIRO_INT_STATUS_SUCCESS) { - if (clipper_antialias != CAIRO_ANTIALIAS_NONE && - _cairo_polygon_intersect (polygon, fill_rule, - &clipper, clipper_fill_rule) == CAIRO_STATUS_SUCCESS) - { - rects->clip->path = NULL; - } - - _cairo_polygon_fini (&clipper); - } - } - } - - 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); - rects->clip->path = clip_path; - return status; -} - static void _cairo_nil_destroy (void *abstract) { (void) abstract; } -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) @@ -168,8 +62,6 @@ _cairo_scan_converter_set_error (void *abstract_converter, 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; } diff --git a/src/cairo-spline.c b/src/cairo-spline.c index 2467178fd..34ad58560 100644 --- a/src/cairo-spline.c +++ b/src/cairo-spline.c @@ -36,8 +36,50 @@ #include "cairoint.h" +#include "cairo-box-private.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, @@ -45,6 +87,10 @@ _cairo_spline_init (cairo_spline_t *spline, 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; @@ -69,6 +115,8 @@ _cairo_spline_init (cairo_spline_t *spline, else 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; } @@ -213,7 +261,6 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) { cairo_spline_knots_t s1; cairo_status_t status; - cairo_slope_t slope; s1 = spline->knots; spline->last_point = s1.a; @@ -221,8 +268,8 @@ _cairo_spline_decompose (cairo_spline_t *spline, double tolerance) if (unlikely (status)) return status; - _cairo_slope_init (&slope, &spline->knots.c, &spline->knots.d); - return spline->add_point_func (spline->closure, &spline->knots.d, &slope); + 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. */ diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c index b9c3a0db9..9b7e407ca 100644 --- a/src/cairo-stroke-style.c +++ b/src/cairo-stroke-style.c @@ -101,6 +101,7 @@ _cairo_stroke_style_fini (cairo_stroke_style_t *style) */ 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) { @@ -110,6 +111,7 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, 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; @@ -117,8 +119,12 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style, style_expansion *= style->line_width; - *dx = style_expansion * hypot (ctm->xx, ctm->xy); - *dy = style_expansion * hypot (ctm->yy, ctm->yx); + 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); + } } /* diff --git a/src/cairo-surface-backend-private.h b/src/cairo-surface-backend-private.h new file mode 100644 index 000000000..cdcd3f8cf --- /dev/null +++ b/src/cairo-surface-backend-private.h @@ -0,0 +1,196 @@ +/* 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 + */ + +#ifndef CAIRO_SURFACE_BACKEND_PRIVATE_H +#define CAIRO_SURFACE_BACKEND_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_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_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); + + 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); +}; + +CAIRO_END_DECLS + +#endif /* CAIRO_SURFACE_BACKEND_PRIVATE_H */ diff --git a/src/cairo-surface-fallback-private.h b/src/cairo-surface-fallback-private.h index ffb6f9199..ecf7b0edf 100644 --- a/src/cairo-surface-fallback-private.h +++ b/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 + * Joonas Pihlaja + * Chris Wilson */ #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, 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, const cairo_clip_t *clip); -cairo_private cairo_status_t -_cairo_surface_fallback_stroke (cairo_surface_t *surface, - cairo_operator_t op, +cairo_private cairo_int_status_t +_cairo_surface_fallback_stroke (void *abstract_surface, + 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, + 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_status_t -_cairo_surface_fallback_fill (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_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, - const cairo_clip_t *clip); +cairo_private cairo_int_status_t +_cairo_surface_fallback_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_surface_t * -_cairo_surface_fallback_snapshot (cairo_surface_t *surface); - -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_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, + const cairo_clip_t *clip); -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); +CAIRO_END_DECLS -#endif +#endif /* CAIRO_SURFACE_FALLBACK_PRIVATE_H */ diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c index ec6946be5..a0af15969 100644 --- a/src/cairo-surface-fallback.c +++ b/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,1545 +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-image-surface-private.h" -#include "cairo-pattern-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_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 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_color_t color; - cairo_rectangle_int_t rect; - - if (coverage == 0xffff) - return; - - _cairo_color_init_rgba (&color, 0, 0, 0, coverage / (double) 0xffff); - - rect.x = x; - rect.y = y; - rect.width = w; - rect.height = h; - - _cairo_surface_fill_rectangles (closure, CAIRO_OPERATOR_IN, - &color, &rect, 1); -} - -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_status_t status; - cairo_region_t *clip_region; - int i; - - /* 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; - - clip_region = _cairo_clip_get_region (clip); - if (clip_region && (extents->x | extents->y)) - cairo_region_translate (clip_region, -extents->x, -extents->y); - - status = draw_func (draw_closure, CAIRO_OPERATOR_ADD, - &_cairo_pattern_white.base, mask, - extents->x, extents->y, - extents, - clip_region); - - if (clip_region && (extents->x | extents->y)) - cairo_region_translate (clip_region, extents->x, extents->y); - - if (unlikely (status)) - goto CLEANUP; - - if (clip) { - 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, mask, b, extents->x, extents->y); - } - } - - if (clip->path != NULL) { - status = _cairo_clip_combine_with_surface (clip, mask, - extents->x, extents->y); - if (unlikely (status)) - goto CLEANUP; - } - } - - _cairo_pattern_init_for_surface (mask_pattern, mask); - - CLEANUP: - 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; - - 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 = _cairo_clip_get_region (clip); - cairo_status_t 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; -} - -/** - * _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 (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 { - if (! _cairo_clip_is_region (clip)) { - 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, - _cairo_clip_get_region (clip)); - } - } - - 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_bool_t clip_surface = ! _cairo_clip_is_region (clip); - cairo_int_status_t status; - - if (traps->num_traps == 0 && _cairo_operator_bounded_by_mask (op)) - return CAIRO_STATUS_SUCCESS; - - /* 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, antialias, &trap_region); - if (unlikely (_cairo_int_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 (_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_INT_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_int_status_t +_cairo_surface_fallback_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_boxes_t boxes; - cairo_int_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_paint (&extents, &rect, - op, source, - clip); - if (unlikely (status)) - return status; - - status = _cairo_clip_to_boxes (extents.clip, &boxes); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { - cairo_traps_t traps; - - /* meh, surface-fallback is dying anyway... */ - status = _cairo_traps_init_boxes (&traps, &boxes); - if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, CAIRO_ANTIALIAS_DEFAULT, - extents.clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - _cairo_traps_fini (&traps); - } - } - - _cairo_composite_rectangles_fini (&extents); - - return status; + return _cairo_compositor_paint (&_cairo_fallback_compositor, + surface, op, source, clip); } -static cairo_status_t -_cairo_surface_mask_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_pattern_t *mask = closure; - cairo_int_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_int_status_t +_cairo_surface_fallback_mask (void *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_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, - op, source, mask, clip); - if (unlikely (status)) - return status; - - status = _clip_and_composite (extents.clip, op, source, - _cairo_surface_mask_draw_func, - (void *) mask, - surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - - _cairo_composite_rectangles_fini (&extents); - - return status; + return _cairo_compositor_mask (&_cairo_fallback_compositor, + surface, op, source, mask, clip); } -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, - const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, + 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_polygon_t polygon; - cairo_traps_t traps; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_int_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_stroke (&extents, &rect, - op, source, - path, stroke_style, ctm, - clip); - if (unlikely (status)) - return status; - - _cairo_polygon_init_with_clip (&polygon, extents.clip); - _cairo_traps_init_with_clip (&traps, extents.clip); - - status = CAIRO_INT_STATUS_UNSUPPORTED; - if (_cairo_path_fixed_stroke_is_rectilinear (path)) { - cairo_boxes_t boxes; - - /* Did I mention, that I need to rewrite surface-fallback? */ - _cairo_boxes_init_with_clip (&boxes, extents.clip); - status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path, - stroke_style, - ctm, - antialias, - &boxes); - if (status == CAIRO_INT_STATUS_SUCCESS) - status = _cairo_traps_init_boxes (&traps, &boxes); - _cairo_boxes_fini (&boxes); - - if (unlikely (_cairo_int_status_is_error (status))) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_path_fixed_stroke_to_polygon (path, - stroke_style, - ctm, ctm_inverse, - tolerance, - &polygon); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &polygon.extents); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_bentley_ottmann_tessellate_polygon (&traps, - &polygon, - CAIRO_FILL_RULE_WINDING); - if (unlikely (status)) - goto CLEANUP; - } - - status = _clip_and_composite_trapezoids (source, op, surface, - &traps, antialias, - extents.clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - - _cairo_composite_rectangles_fini (&extents); - - return status; -} - -cairo_status_t -_cairo_surface_fallback_fill (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_polygon_t polygon; - cairo_traps_t traps; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_int_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_fill (&extents, &rect, - op, source, path, - clip); - if (unlikely (status)) - return status; - - _cairo_traps_init_with_clip (&traps, extents.clip); - _cairo_polygon_init_with_clip (&polygon, extents.clip); - - if (_cairo_path_fixed_fill_is_empty (path)) - goto DO_TRAPS; - - status = CAIRO_INT_STATUS_UNSUPPORTED; - if (_cairo_path_fixed_fill_is_rectilinear (path)) { - cairo_boxes_t boxes; - - /* meh, surface-fallback is dying anyway... */ - _cairo_boxes_init_with_clip (&boxes, extents.clip); - status = _cairo_path_fixed_fill_rectilinear_to_boxes (path, - fill_rule, - antialias, - &boxes); - if (unlikely (status)) - goto CLEANUP; - - if (status == CAIRO_INT_STATUS_SUCCESS) - status = _cairo_traps_init_boxes (&traps, &boxes); - _cairo_boxes_fini (&boxes); - if (unlikely (_cairo_int_status_is_error (status))) - goto CLEANUP; - } - - /* Fall back to trapezoid fills. */ - if (status == CAIRO_INT_STATUS_UNSUPPORTED) { - status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon); - if (unlikely (status)) - goto CLEANUP; - - status = _cairo_composite_rectangles_intersect_mask_extents (&extents, - &polygon.extents); - if (unlikely (status)) - goto CLEANUP; - - 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, - extents.clip, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - CLEANUP: - _cairo_traps_fini (&traps); - _cairo_polygon_fini (&polygon); - - _cairo_composite_rectangles_fini (&extents); - - 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_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) + const cairo_clip_t *clip) { - cairo_show_glyphs_info_t *glyph_info = closure; - cairo_int_status_t status; - - /* 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); - } - - return status; + return _cairo_compositor_stroke (&_cairo_fallback_compositor, + surface, op, source, path, + style, ctm,ctm_inverse, + tolerance, antialias, clip); } -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, - const cairo_clip_t *clip) +cairo_int_status_t +_cairo_surface_fallback_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_show_glyphs_info_t glyph_info; - cairo_composite_rectangles_t extents; - cairo_rectangle_int_t rect; - cairo_int_status_t status; - - if (!_cairo_surface_get_extents (surface, &rect)) - ASSERT_NOT_REACHED; - - status = _cairo_composite_rectangles_init_for_glyphs (&extents, &rect, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - NULL); - if (unlikely (status)) - return status; - - glyph_info.font = scaled_font; - glyph_info.glyphs = glyphs; - glyph_info.num_glyphs = num_glyphs; - - status = _clip_and_composite (extents.clip, op, source, - _cairo_surface_old_show_glyphs_draw_func, - &glyph_info, surface, - extents.is_bounded ? &extents.bounded : &extents.unbounded); - - _cairo_composite_rectangles_fini (&extents); - - return status; + return _cairo_compositor_fill (&_cairo_fallback_compositor, + surface, op, source, path, + fill_rule, tolerance, antialias, + clip); } -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: - 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; +cairo_int_status_t +_cairo_surface_fallback_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_fallback_compositor, + surface, op, source, + glyphs, num_glyphs, scaled_font, + clip); } diff --git a/src/cairo-surface-observer-private.h b/src/cairo-surface-observer-private.h index 61507b21d..1b5d6d949 100644 --- a/src/cairo-surface-observer-private.h +++ b/src/cairo-surface-observer-private.h @@ -33,14 +33,16 @@ * Chris Wilson */ -#ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H -#define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H +#ifndef CAIRO_SURFACE_OBSERVER_PRIVATE_H +#define CAIRO_SURFACE_OBSERVER_PRIVATE_H -#include "cairoint.h" /* cairo_surface_backend_t */ +#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 { @@ -180,11 +182,27 @@ struct _cairo_device_observer { 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; }; static inline cairo_surface_t * diff --git a/src/cairo-surface-observer.c b/src/cairo-surface-observer.c index 3314b8236..f2c416460 100644 --- a/src/cairo-surface-observer.c +++ b/src/cairo-surface-observer.c @@ -37,6 +37,7 @@ #include "cairo-surface-observer-private.h" +#include "cairo-array-private.h" #include "cairo-combsort-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" @@ -385,18 +386,38 @@ _cairo_surface_create_observer_internal (cairo_device_t *device, 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); @@ -630,11 +651,11 @@ add_record (cairo_observation_t *log, static void sync (cairo_surface_t *target, int x, int y) { - cairo_rectangle_t extents; + cairo_rectangle_int_t extents; extents.x = x; extents.y = y; - extents.width = 1; + extents.width = 1; extents.height = 1; cairo_surface_unmap_image (target, @@ -687,7 +708,6 @@ _cairo_surface_observer_paint (void *abstract_surface, cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; cairo_int_status_t status; cairo_time_t t; int x, y; @@ -704,9 +724,8 @@ _cairo_surface_observer_paint (void *abstract_surface, add_pattern (&device->log.paint.source, source, surface->target); add_clip (&device->log.paint.clip, clip); - _cairo_surface_get_extents (surface->target, &extents); status = _cairo_composite_rectangles_init_for_paint (&composite, - &extents, + surface->target, op, source, clip); if (unlikely (status)) { @@ -722,9 +741,9 @@ _cairo_surface_observer_paint (void *abstract_surface, _cairo_composite_rectangles_fini (&composite); t = _cairo_time_get (); - status = _cairo_surface_paint (surface->target, - op, source, - clip); + status = _cairo_surface_paint (surface->target, + op, source, + clip); if (unlikely (status)) return status; @@ -734,6 +753,8 @@ _cairo_surface_observer_paint (void *abstract_surface, 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; } @@ -773,7 +794,6 @@ _cairo_surface_observer_mask (void *abstract_surface, cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; cairo_int_status_t status; cairo_time_t t; int x, y; @@ -790,9 +810,8 @@ _cairo_surface_observer_mask (void *abstract_surface, add_pattern (&device->log.mask.mask, mask, surface->target); add_clip (&device->log.mask.clip, clip); - _cairo_surface_get_extents (surface->target, &extents); status = _cairo_composite_rectangles_init_for_mask (&composite, - &extents, + surface->target, op, source, mask, clip); if (unlikely (status)) { @@ -824,6 +843,8 @@ _cairo_surface_observer_mask (void *abstract_surface, surface->target, op, source, mask, clip, t); + do_callbacks (surface, &surface->mask_callbacks); + return CAIRO_STATUS_SUCCESS; } @@ -875,7 +896,6 @@ _cairo_surface_observer_fill (void *abstract_surface, cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; cairo_int_status_t status; cairo_time_t t; int x, y; @@ -896,9 +916,8 @@ _cairo_surface_observer_fill (void *abstract_surface, add_path (&device->log.fill.path, path, TRUE); add_clip (&device->log.fill.clip, clip); - _cairo_surface_get_extents (surface->target, &extents); status = _cairo_composite_rectangles_init_for_fill (&composite, - &extents, + surface->target, op, source, path, clip); if (unlikely (status)) { @@ -934,6 +953,8 @@ _cairo_surface_observer_fill (void *abstract_surface, fill_rule, tolerance, antialias, clip, t); + do_callbacks (surface, &surface->fill_callbacks); + return CAIRO_STATUS_SUCCESS; } @@ -990,7 +1011,6 @@ _cairo_surface_observer_stroke (void *abstract_surface, cairo_surface_observer_t *surface = abstract_surface; cairo_device_observer_t *device = to_device (surface); cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; cairo_int_status_t status; cairo_time_t t; int x, y; @@ -1013,9 +1033,8 @@ _cairo_surface_observer_stroke (void *abstract_surface, add_path (&device->log.stroke.path, path, FALSE); add_clip (&device->log.stroke.clip, clip); - _cairo_surface_get_extents (surface->target, &extents); status = _cairo_composite_rectangles_init_for_stroke (&composite, - &extents, + surface->target, op, source, path, style, ctm, clip); @@ -1055,6 +1074,8 @@ _cairo_surface_observer_stroke (void *abstract_surface, tolerance, antialias, clip, t); + do_callbacks (surface, &surface->stroke_callbacks); + return CAIRO_STATUS_SUCCESS; } @@ -1101,13 +1122,11 @@ _cairo_surface_observer_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *remaining_glyphs) + 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_rectangle_int_t extents; cairo_int_status_t status; cairo_glyph_t *dev_glyphs; cairo_time_t t; @@ -1123,9 +1142,8 @@ _cairo_surface_observer_glyphs (void *abstract_surface, add_pattern (&device->log.glyphs.source, source, surface->target); add_clip (&device->log.glyphs.clip, clip); - _cairo_surface_get_extents (surface->target, &extents); status = _cairo_composite_rectangles_init_for_glyphs (&composite, - &extents, + surface->target, op, source, scaled_font, glyphs, num_glyphs, @@ -1150,7 +1168,6 @@ _cairo_surface_observer_glyphs (void *abstract_surface, return _cairo_error (CAIRO_STATUS_NO_MEMORY); memcpy (dev_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); - *remaining_glyphs = 0; t = _cairo_time_get (); status = _cairo_surface_show_text_glyphs (surface->target, op, source, NULL, 0, @@ -1175,6 +1192,8 @@ _cairo_surface_observer_glyphs (void *abstract_surface, glyphs, num_glyphs, scaled_font, clip, t); + do_callbacks (surface, &surface->glyphs_callbacks); + return CAIRO_STATUS_SUCCESS; } @@ -1183,6 +1202,8 @@ _cairo_surface_observer_flush (void *abstract_surface) { cairo_surface_observer_t *surface = abstract_surface; + do_callbacks (surface, &surface->flush_callbacks); + cairo_surface_flush (surface->target); return surface->target->status; } @@ -1314,33 +1335,23 @@ static const cairo_surface_backend_t _cairo_surface_observer_backend = { _cairo_surface_observer_acquire_source_image, _cairo_surface_observer_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_observer_snapshot, _cairo_surface_observer_copy_page, _cairo_surface_observer_show_page, _cairo_surface_observer_get_extents, - NULL, /* old_show_glyphs */ _cairo_surface_observer_get_font_options, + _cairo_surface_observer_flush, _cairo_surface_observer_mark_dirty, - NULL, /* font_fini */ - NULL, /* glyph_fini */ _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_observer_snapshot, }; /** @@ -1385,6 +1396,150 @@ cairo_surface_create_observer (cairo_surface_t *target, 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) { diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h index 31ddb8c7b..0bebfde71 100644 --- a/src/cairo-surface-private.h +++ b/src/cairo-surface-private.h @@ -61,6 +61,7 @@ struct _cairo_surface { cairo_reference_count_t ref_count; cairo_status_t status; unsigned int unique_id; + unsigned int serial; unsigned finished : 1; unsigned is_clear : 1; diff --git a/src/cairo-surface-snapshot-private.h b/src/cairo-surface-snapshot-private.h index 0b1c8d34b..b7a4d05f6 100644 --- a/src/cairo-surface-snapshot-private.h +++ b/src/cairo-surface-snapshot-private.h @@ -36,9 +36,8 @@ #ifndef CAIRO_SURFACE_SNAPSHOT_PRIVATE_H #define CAIRO_SURFACE_SNAPSHOT_PRIVATE_H -#include "cairoint.h" /* for cairo_surface_backend_t */ - #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" struct _cairo_surface_snapshot { cairo_surface_t base; diff --git a/src/cairo-surface-snapshot.c b/src/cairo-surface-snapshot.c index 16153e216..4bc40e7eb 100644 --- a/src/cairo-surface-snapshot.c +++ b/src/cairo-surface-snapshot.c @@ -109,18 +109,14 @@ static const cairo_surface_backend_t _cairo_surface_snapshot_backend = { _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, /* old-show-glyphs */ NULL, /* get-font-options */ + _cairo_surface_snapshot_flush, }; @@ -141,8 +137,10 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) if (snapshot->target->backend->snapshot != NULL) { clone = snapshot->target->backend->snapshot (snapshot->target); - if (clone != NULL) + if (clone != NULL) { + assert (clone->status || ! _cairo_surface_is_snapshot (clone)); goto done; + } } /* XXX copy to a similar surface, leave acquisition till later? @@ -160,7 +158,6 @@ _cairo_surface_snapshot_copy_on_write (cairo_surface_t *surface) done: status = _cairo_surface_set_error (surface, clone->status); - assert (! _cairo_surface_is_snapshot (clone)); snapshot->target = snapshot->clone = clone; snapshot->base.type = clone->type; } diff --git a/src/cairo-surface-subsurface-private.h b/src/cairo-surface-subsurface-private.h index ada09a171..6a43c8fa1 100644 --- a/src/cairo-surface-subsurface-private.h +++ b/src/cairo-surface-subsurface-private.h @@ -36,9 +36,8 @@ #ifndef CAIRO_SURFACE_SUBSURFACE_PRIVATE_H #define CAIRO_SURFACE_SUBSURFACE_PRIVATE_H -#include "cairoint.h" /* for cairo_surface_backend_t */ - #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" struct _cairo_surface_subsurface { cairo_surface_t base; @@ -54,6 +53,25 @@ _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) { diff --git a/src/cairo-surface-subsurface.c b/src/cairo-surface-subsurface.c index 73be5f2d7..48d10fb14 100644 --- a/src/cairo-surface-subsurface.c +++ b/src/cairo-surface-subsurface.c @@ -202,8 +202,7 @@ _cairo_surface_subsurface_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const 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 }; @@ -216,7 +215,6 @@ _cairo_surface_subsurface_glyphs (void *abstract_surface, op, source, scaled_font, glyphs, num_glyphs, target_clip); - *remaining_glyphs = 0; _cairo_clip_destroy (target_clip); return status; } @@ -489,30 +487,23 @@ static const cairo_surface_backend_t _cairo_surface_subsurface_backend = { _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, }; /** diff --git a/src/cairo-surface-wrapper-private.h b/src/cairo-surface-wrapper-private.h index fbe5d80f4..6529ebc11 100644 --- a/src/cairo-surface-wrapper-private.h +++ b/src/cairo-surface-wrapper-private.h @@ -41,6 +41,7 @@ #include "cairoint.h" #include "cairo-types-private.h" +#include "cairo-surface-backend-private.h" CAIRO_BEGIN_DECLS diff --git a/src/cairo-surface-wrapper.c b/src/cairo-surface-wrapper.c index 5e6286906..d2d971dec 100644 --- a/src/cairo-surface-wrapper.c +++ b/src/cairo-surface-wrapper.c @@ -446,7 +446,7 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, 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; } @@ -454,7 +454,9 @@ _cairo_surface_wrapper_show_text_glyphs (cairo_surface_wrapper_t *wrapper, 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); diff --git a/src/cairo-surface.c b/src/cairo-surface.c index d15b94b74..848838016 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -38,7 +38,7 @@ #include "cairoint.h" -#include "cairo-surface-fallback-private.h" +#include "cairo-array-private.h" #include "cairo-clip-private.h" #include "cairo-device-private.h" #include "cairo-error-private.h" @@ -103,6 +103,7 @@ const cairo_surface_t name = { \ CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \ status, /* status */ \ 0, /* unique id */ \ + 0, /* serial */ \ FALSE, /* finished */ \ TRUE, /* is_clear */ \ FALSE, /* has_font_options */ \ @@ -182,7 +183,7 @@ _cairo_surface_set_error (cairo_surface_t *surface, /* 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); } @@ -367,7 +368,6 @@ _cairo_surface_has_snapshot (cairo_surface_t *surface, cairo_list_foreach_entry (snapshot, cairo_surface_t, &surface->snapshots, snapshot) { - /* XXX is_similar? */ if (snapshot->backend == backend) return snapshot; } @@ -375,15 +375,6 @@ _cairo_surface_has_snapshot (cairo_surface_t *surface, 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_surface_begin_modification (cairo_surface_t *surface) { @@ -413,6 +404,7 @@ _cairo_surface_init (cairo_surface_t *surface, surface->unique_id = _cairo_surface_allocate_unique_id (); surface->finished = FALSE; surface->is_clear = FALSE; + surface->serial = 0; surface->owns_device = (device != NULL); _cairo_user_data_array_init (&surface->user_data); @@ -461,12 +453,15 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other, 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); @@ -518,18 +513,16 @@ cairo_surface_create_similar (cairo_surface_t *other, 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); 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 @@ -564,7 +557,7 @@ cairo_surface_create_similar_image (cairo_surface_t *other, { cairo_surface_t *image; - if (other->status) + 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); @@ -572,7 +565,7 @@ cairo_surface_create_similar_image (cairo_surface_t *other, 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_error (CAIRO_STATUS_INVALID_FORMAT)); + return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_FORMAT); image = NULL; if (other->backend->create_similar_image) @@ -580,8 +573,12 @@ cairo_surface_create_similar_image (cairo_surface_t *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: @@ -608,7 +605,7 @@ cairo_surface_create_similar_image (cairo_surface_t *other, **/ cairo_surface_t * cairo_surface_map_to_image (cairo_surface_t *surface, - const cairo_rectangle_t *extents) + const cairo_rectangle_int_t *extents) { cairo_rectangle_int_t rect; cairo_surface_t *image; @@ -621,32 +618,35 @@ cairo_surface_map_to_image (cairo_surface_t *surface, 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; - _cairo_rectangle_int_from_double (&rect, 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 (rect.x < surface_extents.x || - rect.y < surface_extents.y || - rect.x + rect.width > surface_extents.x + surface_extents.width || - rect.y + rect.height > surface_extents.y + surface_extents.height)) + if (unlikely (extents->x < surface_extents.x || + extents->y < surface_extents.y || + extents->x + extents->width > surface_extents.x + surface_extents.width || + extents->y + extents->height > surface_extents.y + surface_extents.height)) return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE); } } image = NULL; if (surface->backend->map_to_image) - image = surface->backend->map_to_image (surface, &rect); + image = surface->backend->map_to_image (surface, extents); if (image == NULL) { cairo_surface_pattern_t pattern; cairo_status_t status; - image = cairo_image_surface_create (_cairo_format_from_content (surface->content), - rect.width, rect.height); - cairo_surface_set_device_offset (image, -rect.y, -rect.y); + image = cairo_surface_create_similar_image (surface, + _cairo_format_from_content (surface->content), + extents->width, + extents->height); + cairo_surface_set_device_offset (image, -extents->y, -extents->y); _cairo_pattern_init_for_surface (&pattern, surface); pattern.base.filter = CAIRO_FILTER_NEAREST; @@ -711,6 +711,12 @@ cairo_surface_unmap_image (cairo_surface_t *surface, goto error; } + /* If the image is untouched just skip the update */ + if (image->serial == 0) { + status = CAIRO_STATUS_SUCCESS; + goto error; + } + status = CAIRO_INT_STATUS_UNSUPPORTED; if (surface->backend->unmap_image) status = surface->backend->unmap_image (surface, (cairo_image_surface_t *) image); @@ -731,9 +737,8 @@ cairo_surface_unmap_image (cairo_surface_t *surface, /* And we also have to clip the operation to the image's extents */ extents.x = image->device_transform_inverse.x0; extents.y = image->device_transform_inverse.y0; - extents.width = img->width; + extents.width = img->width; extents.height = img->height; - clip = _cairo_clip_intersect_rectangle (NULL, &extents); status = _cairo_surface_paint (surface, @@ -758,8 +763,7 @@ _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; @@ -767,10 +771,7 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, 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); @@ -786,51 +787,6 @@ _cairo_surface_create_similar_solid (cairo_surface_t *other, 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 @@ -967,7 +923,7 @@ cairo_surface_finish (cairo_surface_t *surface) if (surface->backend->finish) { status = surface->backend->finish (surface); if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); + _cairo_surface_set_error (surface, status); } assert (surface->snapshot_of == NULL); @@ -1012,8 +968,7 @@ 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); + return _cairo_user_data_array_get_data (&surface->user_data, key); } /** @@ -1192,7 +1147,7 @@ cairo_surface_set_mime_data (cairo_surface_t *surface, 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); @@ -1279,16 +1234,13 @@ void _cairo_surface_set_font_options (cairo_surface_t *surface, cairo_font_options_t *options) { - cairo_status_t status; - if (surface->status) return; 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; } @@ -1366,7 +1318,7 @@ cairo_surface_flush (cairo_surface_t *surface) if (surface->backend->flush) { status = surface->backend->flush (surface); if (unlikely (status)) - status = _cairo_surface_set_error (surface, status); + _cairo_surface_set_error (surface, status); } } slim_hidden_def (cairo_surface_flush); @@ -1411,13 +1363,13 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, { 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; } @@ -1428,6 +1380,7 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, assert (! _cairo_surface_has_mime_data (surface)); surface->is_clear = FALSE; + surface->serial++; if (surface->backend->mark_dirty_rectangle != NULL) { /* XXX: FRAGILE: We're ignoring the scaling component of @@ -1441,7 +1394,7 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface, 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); @@ -1470,13 +1423,13 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface, { 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; } @@ -1520,13 +1473,13 @@ cairo_surface_set_device_offset (cairo_surface_t *surface, { 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; } @@ -1605,15 +1558,13 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, double x_pixels_per_inch, double y_pixels_per_inch) { - 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; } @@ -1621,7 +1572,7 @@ cairo_surface_set_fallback_resolution (cairo_surface_t *surface, /* 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; } @@ -1685,7 +1636,7 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, { cairo_status_t status; - if (surface->status) + if (unlikely (surface->status)) return surface->status; assert (!surface->finished); @@ -1703,33 +1654,6 @@ _cairo_surface_acquire_source_image (cairo_surface_t *surface, return CAIRO_STATUS_SUCCESS; } -cairo_status_t -_cairo_surface_acquire_source_image_transformed (cairo_surface_t *surface, - cairo_matrix_t *device_transform, - cairo_image_surface_t **image_out, - void **image_extra) -{ - cairo_status_t status; - - if (surface->status) - return surface->status; - - assert (!surface->finished); - - if (surface->backend->acquire_source_image_transformed == NULL) - return _cairo_surface_acquire_source_image (surface, - image_out, image_extra); - - status = surface->backend->acquire_source_image_transformed (surface, device_transform, - image_out, 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_source_image: * @surface: a #cairo_surface_t @@ -1748,469 +1672,6 @@ _cairo_surface_release_source_image (cairo_surface_t *surface, 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_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) -{ - assert (_cairo_surface_is_writable (surface)); - - if (surface->backend->release_dest_image) - surface->backend->release_dest_image (surface, interest_rect, - image, image_rect, image_extra); -} - -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) - { - 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; - } - } 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_int_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; - } - -#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_INT_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 CAIRO_STATUS_SUCCESS; -} - -/** - * _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_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 && - surface->content == CAIRO_CONTENT_ALPHA && - CAIRO_COLOR_IS_OPAQUE (color)) - { - 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) { @@ -2248,6 +1709,9 @@ nothing_to_do (cairo_surface_t *surface, 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; } @@ -2265,26 +1729,20 @@ _cairo_surface_paint (cairo_surface_t *surface, if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; - if (nothing_to_do (surface, op, source)) - return CAIRO_STATUS_SUCCESS; - status = _pattern_has_error (source); 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 (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_fallback_paint (surface, op, source, clip); + _cairo_surface_begin_modification (surface); - FINISH: - if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + status = surface->backend->paint (surface, op, source, clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL; + surface->serial++; + } return _cairo_surface_set_error (surface, status); } @@ -2304,9 +1762,6 @@ _cairo_surface_mask (cairo_surface_t *surface, if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; - if (nothing_to_do (surface, op, source)) - 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)) @@ -2322,19 +1777,16 @@ _cairo_surface_mask (cairo_surface_t *surface, if (unlikely (status)) return status; - _cairo_surface_begin_modification (surface); - - if (surface->backend->mask != NULL) { - status = surface->backend->mask (surface, op, source, mask, clip); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - goto FINISH; - } + if (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_fallback_mask (surface, op, source, mask, clip); + _cairo_surface_begin_modification (surface); - FINISH: - if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + status = surface->backend->mask (surface, op, source, mask, clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; + surface->serial++; + } return _cairo_surface_set_error (surface, status); } @@ -2413,23 +1865,25 @@ _cairo_surface_fill_stroke (cairo_surface_t *surface, goto FINISH; FINISH: - if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; + surface->serial++; + } return _cairo_surface_set_error (surface, status); } cairo_status_t -_cairo_surface_stroke (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_path_fixed_t *path, +_cairo_surface_stroke (cairo_surface_t *surface, + 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, - const cairo_clip_t *clip) + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_int_status_t status; @@ -2439,48 +1893,37 @@ _cairo_surface_stroke (cairo_surface_t *surface, if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; - if (nothing_to_do (surface, op, source)) - return CAIRO_STATUS_SUCCESS; - status = _pattern_has_error (source); 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 (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_fallback_stroke (surface, op, source, - path, stroke_style, - ctm, ctm_inverse, - tolerance, antialias, - clip); + _cairo_surface_begin_modification (surface); - FINISH: - if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + status = surface->backend->stroke (surface, op, source, + path, stroke_style, + ctm, ctm_inverse, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; + surface->serial++; + } return _cairo_surface_set_error (surface, status); } cairo_status_t -_cairo_surface_fill (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source, +_cairo_surface_fill (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_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) { cairo_int_status_t status; @@ -2490,129 +1933,25 @@ _cairo_surface_fill (cairo_surface_t *surface, if (_cairo_clip_is_all_clipped (clip)) return CAIRO_STATUS_SUCCESS; - if (nothing_to_do (surface, op, source)) - return CAIRO_STATUS_SUCCESS; - status = _pattern_has_error (source); 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 (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; - status = _cairo_surface_fallback_fill (surface, op, source, - path, fill_rule, - tolerance, antialias, - clip); + _cairo_surface_begin_modification (surface); - FINISH: - if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + status = surface->backend->fill (surface, op, source, + path, fill_rule, + tolerance, antialias, + clip); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { 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); + surface->serial++; } - 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; + return _cairo_surface_set_error (surface, status); } /** @@ -2632,16 +1971,13 @@ _cairo_surface_check_span_renderer (cairo_operator_t op, 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; } @@ -2649,8 +1985,7 @@ cairo_surface_copy_page (cairo_surface_t *surface) 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); @@ -2669,14 +2004,11 @@ slim_hidden_def (cairo_surface_copy_page); void cairo_surface_show_page (cairo_surface_t *surface) { - cairo_status_t status_ignored; - - 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; } @@ -2686,8 +2018,7 @@ cairo_surface_show_page (cairo_surface_t *surface) 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); @@ -2755,14 +2086,11 @@ _cairo_surface_get_extents (cairo_surface_t *surface, 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; } @@ -2814,13 +2142,13 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, 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 (nothing_to_do (surface, op, source)) + return CAIRO_STATUS_SUCCESS; + _cairo_surface_begin_modification (surface); if (_cairo_surface_has_device_transform (surface) && @@ -2861,32 +2189,20 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, 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_INT_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 @@ -2906,269 +2222,15 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, } } - 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) + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { surface->is_clear = FALSE; - - 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) -{ - if (unlikely (dst->status)) - return dst->status; - - assert (_cairo_surface_is_writable (dst)); - - if (dst->backend->old_show_glyphs == NULL) - return CAIRO_INT_STATUS_UNSUPPORTED; - - return _cairo_surface_set_error - (dst, 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)); -} - -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; + surface->serial++; } - 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); + return _cairo_surface_set_error (surface, status); } /** diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c index 2cf91eab3..6f607d3c4 100644 --- a/src/cairo-svg-surface.c +++ b/src/cairo-svg-surface.c @@ -41,7 +41,10 @@ #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" @@ -2474,8 +2477,7 @@ _cairo_svg_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const 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; @@ -2587,31 +2589,23 @@ static const cairo_surface_backend_t cairo_svg_surface_backend = { 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 }; static cairo_status_t diff --git a/src/cairo-time-private.h b/src/cairo-time-private.h index f7c48f389..06dc912b4 100644 --- a/src/cairo-time-private.h +++ b/src/cairo-time-private.h @@ -33,15 +33,16 @@ #include "cairo-compiler-private.h" #include "cairo-wideint-private.h" -typedef cairo_uint64_t cairo_time_t; +/* Make the base type signed for easier arithmetic */ +typedef cairo_int64_t cairo_time_t; -#define _cairo_time_add _cairo_uint64_add -#define _cairo_time_sub _cairo_uint64_sub -#define _cairo_time_gt _cairo_uint64_gt -#define _cairo_time_lt _cairo_uint64_lt +#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_uint64_to_double -#define _cairo_time_from_double _cairo_double_to_uint64 +#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, @@ -75,7 +76,7 @@ _cairo_time_to_ns (cairo_time_t t) static cairo_always_inline cairo_time_t _cairo_time_max (cairo_time_t a, cairo_time_t b) { - if (_cairo_uint64_gt (a, b)) + if (_cairo_int64_gt (a, b)) return a; else return b; @@ -84,7 +85,7 @@ _cairo_time_max (cairo_time_t a, cairo_time_t b) static cairo_always_inline cairo_time_t _cairo_time_min (cairo_time_t a, cairo_time_t b) { - if (_cairo_uint64_lt (a, b)) + if (_cairo_int64_lt (a, b)) return a; else return b; diff --git a/src/cairo-time.c b/src/cairo-time.c index 32749f5f1..9a594e8e6 100644 --- a/src/cairo-time.c +++ b/src/cairo-time.c @@ -34,7 +34,7 @@ #include "cairo-time-private.h" -#if HAVE_CLOCKGETTIME +#if HAVE_CLOCK_GETTIME #if defined(CLOCK_MONOTONIC_RAW) #define CAIRO_CLOCK CLOCK_MONOTONIC_RAW #elif defined(CLOCK_MONOTONIC) @@ -79,12 +79,12 @@ cairo_time_t _cairo_time_get (void) { QWORD t; - cairo_uint64_t r; + cairo_int64_t r; DosTmrQueryTime (&t); - r = _cairo_uint64_lsl (_cairo_uint32_to_uint64 (t.ulHi), 32); - r = _cairo_uint64_add (r, _cairo_uint32_to_uint64 (t.ulLo)); + r = _cairo_int64_lsl (_cairo_int32_to_int64 (t.ulHi), 32); + r = _cairo_int64_add (r, _cairo_int32_to_int64 (t.ulLo)); return r; } @@ -130,9 +130,9 @@ _cairo_time_get (void) clock_gettime (CAIRO_CLOCK, &t); - r = _cairo_double_to_uint64 (_cairo_time_1s ()); - r = _cairo_uint64_mul (r, _cairo_uint32_to_uint64 (t.tv_sec)); - r = _cairo_uint64_add (r, _cairo_uint32_to_uint64 (t.tv_nsec)); + 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; } @@ -154,9 +154,9 @@ _cairo_time_get (void) gettimeofday (&t, NULL); - r = _cairo_double_to_uint64 (_cairo_time_1s ()); - r = _cairo_uint64_mul (r, _cairo_uint32_to_uint64 (t.tv_sec)); - r = _cairo_uint64_add (r, _cairo_uint32_to_uint64 (t.tv_usec)); + 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; } @@ -167,17 +167,8 @@ int _cairo_time_cmp (const void *a, const void *b) { - const cairo_time_t *ta, *tb; - - ta = a; - tb = b; - - if (_cairo_time_gt (*ta, *tb)) - return 1; - else if (_cairo_time_lt (*ta, *tb)) - return -1; - else - return 0; + const cairo_time_t *ta = a, *tb = b; + return _cairo_int64_cmp (*ta, *tb); } static double @@ -205,11 +196,11 @@ _cairo_time_s_per_tick (void) double _cairo_time_to_s (cairo_time_t t) { - return _cairo_uint64_to_double (t) * _cairo_time_s_per_tick (); + return _cairo_int64_to_double (t) * _cairo_time_s_per_tick (); } cairo_time_t _cairo_time_from_s (double t) { - return _cairo_double_to_uint64 (t * _cairo_time_ticks_per_sec ()); + return _cairo_double_to_int64 (t * _cairo_time_ticks_per_sec ()); } diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c index d571f6e60..b001bbd63 100644 --- a/src/cairo-tor-scan-converter.c +++ b/src/cairo-tor-scan-converter.c @@ -115,8 +115,10 @@ 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 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. */ @@ -163,15 +165,6 @@ glitter_scan_converter_reset( 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 void -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, @@ -348,10 +341,7 @@ struct edge { grid_scaled_y_t dy; }; -/* 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) +#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 @@ -707,16 +697,14 @@ cell_list_find (struct cell_list *cells, int x) { struct cell *tail = cells->cursor; - assert (x >= tail->x); - if (tail->x == x) return tail; while (1) { UNROLL3({ - if (tail->next->x > x) - break; - tail = tail->next; + if (tail->next->x > x) + break; + tail = tail->next; }); } @@ -738,15 +726,12 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) { struct cell_pair pair; - assert (x2 > x1); - assert (x1 >= cells->cursor->x); - pair.cell1 = cells->cursor; while (1) { UNROLL3({ - if (pair.cell1->next->x > x1) - break; - pair.cell1 = pair.cell1->next; + if (pair.cell1->next->x > x1) + break; + pair.cell1 = pair.cell1->next; }); } if (pair.cell1->x != x1) @@ -755,9 +740,9 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2) pair.cell2 = pair.cell1; while (1) { UNROLL3({ - if (pair.cell2->next->x > x2) - break; - pair.cell2 = pair.cell2->next; + if (pair.cell2->next->x > x2) + break; + pair.cell2 = pair.cell2->next; }); } if (pair.cell2->x != x2) @@ -954,12 +939,11 @@ polygon_reset (struct polygon *polygon, 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) @@ -978,16 +962,15 @@ polygon_reset (struct polygon *polygon, polygon->ymax = ymax; return GLITTER_STATUS_SUCCESS; - bail_no_mem: +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) +_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]; @@ -1191,7 +1174,7 @@ sort_edges (struct edge *list, return remaining; } -static struct edge * + static struct edge * merge_unsorted_edges (struct edge *head, struct edge *unsorted) { sort_edges (unsorted, UINT_MAX, &unsorted); @@ -1283,47 +1266,6 @@ polygon_fill_buckets (struct active_list *active, 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) -{ - grid_scaled_x_t prev_x = INT_MIN; - struct edge *edge = active->head.next; - - while (edge != &active->tail) { - struct edge *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) { - 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; - } - - edge = next; - } -} - inline static void sub_row (struct active_list *active, struct cell_list *coverages, @@ -1481,9 +1423,9 @@ int_to_grid_scaled(int i, int scale) I glitter_status_t glitter_scan_converter_reset( - glitter_scan_converter_t *converter, - int xmin, int ymin, - int xmax, int ymax) + glitter_scan_converter_t *converter, + int xmin, int ymin, + int xmax, int ymax) { glitter_status_t status; @@ -1536,11 +1478,16 @@ glitter_scan_converter_reset( #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__; \ + 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) @@ -1566,18 +1513,6 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, 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 void step_edges (struct active_list *active, int count) { @@ -1594,11 +1529,11 @@ step_edges (struct active_list *active, int count) } static glitter_status_t -blit (struct cell_list *cells, - cairo_span_renderer_t *renderer, - cairo_half_open_span_t *spans, - int y, int height, - int xmin, int xmax) +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; @@ -1661,10 +1596,80 @@ blit (struct cell_list *cells, 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; @@ -1682,9 +1687,6 @@ glitter_scan_converter_render(glitter_scan_converter_t *converter, if (xmin_i >= xmax_i) return; - /* Let the coverage blitter initialise itself. */ - GLITTER_BLIT_COVERAGES_BEGIN; - /* Render each pixel row. */ for (i = 0; i < h; i = j) { int do_full_row = 0; @@ -1693,13 +1695,12 @@ glitter_scan_converter_render(glitter_scan_converter_t *converter, /* 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 (! 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++) ; - GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i); continue; } @@ -1740,15 +1741,16 @@ glitter_scan_converter_render(glitter_scan_converter_t *converter, } } - blit (coverages, renderer, converter->spans, - i+ymin_i, j-i, xmin_i, xmax_i); + 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; } - - /* Clean up the coverage blitter. */ - GLITTER_BLIT_COVERAGES_END; } struct _cairo_tor_scan_converter { @@ -1756,6 +1758,7 @@ struct _cairo_tor_scan_converter { glitter_scan_converter_t converter[1]; cairo_fill_rule_t fill_rule; + cairo_antialias_t antialias; jmp_buf jmp; }; @@ -1773,36 +1776,21 @@ _cairo_tor_scan_converter_destroy (void *converter) 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_edge_t edge; - - edge.line.p1 = *p1; - edge.line.p2 = *p2; - edge.top = top; - edge.bottom = bottom; - edge.dir = dir; - - glitter_scan_converter_add_edge (self->converter, &edge); - 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; 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]); + glitter_scan_converter_add_edge (self->converter, &polygon->edges[i]); return CAIRO_STATUS_SUCCESS; } @@ -1819,6 +1807,7 @@ _cairo_tor_scan_converter_generate (void *converter, 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; } @@ -1828,20 +1817,19 @@ _cairo_tor_scan_converter_create (int xmin, 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; } 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; _glitter_scan_converter_init (self->converter, &self->jmp); @@ -1851,6 +1839,7 @@ _cairo_tor_scan_converter_create (int xmin, goto bail; self->fill_rule = fill_rule; + self->antialias = antialias; return &self->base; diff --git a/src/cairo-tor22-scan-converter.c b/src/cairo-tor22-scan-converter.c new file mode 100644 index 000000000..2f930306e --- /dev/null +++ b/src/cairo-tor22-scan-converter.c @@ -0,0 +1,1707 @@ +/* -*- 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 +#include +#include +#include + +/*------------------------------------------------------------------------- + * 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< +#include +#include + +/* 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 == 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) + 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); +} + +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); +} diff --git a/src/cairo-traps-compositor.c b/src/cairo-traps-compositor.c new file mode 100644 index 000000000..92c8665a8 --- /dev/null +++ b/src/cairo-traps-compositor.c @@ -0,0 +1,2032 @@ +/* -*- 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 + * Joonas Pihlaja + * Chris Wilson + */ + +#include "cairoint.h" + +#include "cairo-box-private.h" +#include "cairo-boxes-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-pattern-private.h" +#include "cairo-paginated-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-surface-subsurface-private.h" +#include "cairo-surface-snapshot-private.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 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; + int src_x, src_y; + cairo_int_status_t status; + + 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_surface_destroy (src); + _cairo_traps_fini (&traps); + + return status; +} + +static cairo_surface_t * +traps_get_clip_surface (const cairo_traps_compositor_t *compositor, + cairo_surface_t *target, + const cairo_clip_t *clip, + const cairo_rectangle_int_t *extents) +{ + cairo_surface_t *surface; + cairo_int_status_t status; + + surface = _cairo_surface_create_similar_solid (target, + CAIRO_CONTENT_ALPHA, + extents->width, + extents->height, + CAIRO_COLOR_WHITE); + if (unlikely (surface->status)) + return surface; + + status = compositor->acquire (surface); + if (unlikely (status)) { + cairo_surface_destroy (surface); + return _cairo_surface_create_in_error (status); + } + + status = combine_clip_as_traps (compositor, surface, clip, extents); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + status = _cairo_clip_combine_with_surface (clip, surface, + extents->x, extents->y); + } + compositor->release (surface); + + return surface; +} + +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; + struct blt_in info; + int src_x, src_y; + int i; + + surface = _cairo_surface_create_similar_solid (dst, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + CAIRO_COLOR_TRANSPARENT); + 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)) + 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 (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_UNSUPPORTED)) { + cairo_surface_destroy (src); + goto out; + } + } + + /* 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); + cairo_surface_destroy (src); + if (unlikely (status)) + goto error; + + info.compositor = compositor; + info.dst = surface; + _cairo_boxes_init (&info.boxes); + info.boxes.num_boxes = 1; + 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 = 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; + } + +out: + compositor->release (surface); + surface->is_clear = FALSE; + return surface; + +error: + cairo_surface_destroy (surface); + surface = _cairo_surface_create_in_error (status); + compositor->release (surface); + 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; + + mask = create_composite_mask (compositor, dst, draw_closure, + draw_func, mask_func, + extents); + if (unlikely (mask->status)) + return mask->status; + + 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); + } + 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; + + 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, dst, extents->clip, + &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; + + /* 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 (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); + } + + 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_clip_t *clip = extents->clip; + cairo_surface_t *mask; + + /* XXX can we avoid querying the clip surface again? */ + mask = traps_get_clip_surface (compositor, dst, clip, &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; + + 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->unbounded.width < extents->destination.width || + extents->unbounded.height < extents->destination.height) + { + 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_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; + + if (reduce_alpha_op (extents)) { + op = CAIRO_OPERATOR_ADD; + source = NULL; + } + + if (op == CAIRO_OPERATOR_CLEAR) { + op = CAIRO_OPERATOR_DEST_OUT; + source = NULL; + } + + src = compositor->pattern_to_surface (dst, source, FALSE, + &extents->bounded, + &extents->source_sample_area, + &src_x, &src_y); + if (unlikely (src->status)) + return src->status; + + 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 (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); + } + } + + 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); + } + + if (clip_region) + compositor->set_clip_region (dst, NULL); + + cairo_surface_destroy (src); + 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; + + 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; + + 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; + 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_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; + 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_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; + + if (sample->x >= surface->extents.x && + sample->y >= surface->extents.y && + sample->x + sample->width <= surface->extents.x + surface->extents.width && + sample->y + sample->height <= surface->extents.y + surface->extents.height) + { + return TRUE; + } + + return FALSE; +} + +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 = extents->clip->path != NULL; + cairo_bool_t op_is_source; + cairo_status_t status; + + 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 = _cairo_clip_get_surface (extents->clip, dst, + &mask_x, &mask_y); + if (unlikely (mask->status)) + return mask->status; + + mask_x = -mask_x; + mask_y = -mask_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; + const cairo_surface_pattern_t *pattern; + cairo_surface_t *src; + cairo_rectangle_int_t limit; + cairo_int_status_t status; + int tx, ty; + + pattern = (const cairo_surface_pattern_t *) source; + src = pattern->surface; + 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; + + if (_cairo_surface_is_snapshot (src)) + src = _cairo_surface_snapshot_get_target (src); + if (_cairo_surface_is_observer (src)) + src = _cairo_surface_observer_get_target (src); + if (_cairo_surface_is_subsurface (src)) { + _cairo_surface_subsurface_offset (src, &tx, &ty); + src = _cairo_surface_subsurface_get_target (src); + } + + /* Check that the data is entirely within the image */ + if (extents->bounded.x + tx < 0 || extents->bounded.y + ty < 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_surface_get_extents (pattern->surface, &limit); + if (extents->bounded.x + extents->bounded.width + tx > limit.width || + extents->bounded.y + extents->bounded.height + ty > limit.height) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->surface->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) +{ + 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; + + 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); + } + } + } + + _cairo_traps_init (&traps.traps); + + 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; + + 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) +{ + const cairo_boxes_t *boxes = closure; + const struct _cairo_boxes_chunk *chunk; + struct composite_opacity_info info; + int i; + + info.compositor = compositor; + info.op = op; + info.dst = dst; + + info.src = src; + info.src_x = src_x; + info.src_y = src_y; + + info.opacity = 1. / (double) 0xffff; + + /* XXX for lots of boxes create a clip region for the fully opaque areas */ + for (chunk = &boxes->chunks; chunk != NULL; chunk = chunk->next) { + for (i = 0; i < chunk->count; i++) + do_unaligned_box(composite_opacity, &info, + &chunk->base[i], dst_x, dst_y); + } + + return CAIRO_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) +{ + cairo_int_status_t status; + + 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); + 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); + + 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, composite_boxes, 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; + + 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) +{ + cairo_int_status_t status; + + status = trim_extents_to_traps (extents, &info->traps); + if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) + return status; + + status = composite_traps_as_boxes (compositor, extents, info); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + 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 (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; + + 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; + + 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; + + 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; + + 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; + + 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; + + _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; + + 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); + + 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; + + 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) { + composite_traps_info_t info; + + info.antialias = antialias; + _cairo_traps_init_with_clip (&info.traps, extents->clip); + status = _cairo_path_fixed_stroke_to_traps (path, style, + ctm, ctm_inverse, + tolerance, + &info.traps); + if (likely (status == CAIRO_INT_STATUS_SUCCESS)) + status = clip_and_composite_traps (compositor, extents, &info); + _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; + + 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); + } + _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; + + 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; + + _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; + unsigned flags = 0; + + info.font = scaled_font; + info.glyphs = glyphs; + info.num_glyphs = num_glyphs; + info.use_mask = overlap || ! extents->is_bounded; + info.extents = extents->bounded; + + if (extents->mask.width > extents->bounded.width || + extents->mask.height > extents->bounded.height) + { + flags |= FORCE_CLIP_REGION; + } + + status = clip_and_composite (compositor, extents, + composite_glyphs, NULL, &info, + need_bounded_clip (extents) | + flags); + } + _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; +} diff --git a/src/cairo-traps-private.h b/src/cairo-traps-private.h new file mode 100644 index 000000000..5b17719fc --- /dev/null +++ b/src/cairo-traps-private.h @@ -0,0 +1,126 @@ +/* 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 + */ + +#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; + + 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 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_END_DECLS + +#endif /* CAIRO_TRAPS_PRIVATE_H */ diff --git a/src/cairo-traps.c b/src/cairo-traps.c index 42e2eb553..a6e7f94ee 100644 --- a/src/cairo-traps.c +++ b/src/cairo-traps.c @@ -43,6 +43,7 @@ #include "cairo-error-private.h" #include "cairo-region-private.h" #include "cairo-slope-private.h" +#include "cairo-traps-private.h" /* private functions */ @@ -608,6 +609,66 @@ _cairo_traps_extract_region (cairo_traps_t *traps, 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) diff --git a/src/cairo-tristrip-private.h b/src/cairo-tristrip-private.h new file mode 100644 index 000000000..ccd28799e --- /dev/null +++ b/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 + */ + +#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 */ diff --git a/src/cairo-tristrip.c b/src/cairo-tristrip.c new file mode 100644 index 000000000..bb4972f50 --- /dev/null +++ b/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 + */ + +#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; + } +} diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c index 88c4ee365..d0d6ab7f5 100644 --- a/src/cairo-truetype-subset.c +++ b/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 diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c index 5284cfed0..4a657413e 100644 --- a/src/cairo-type1-fallback.c +++ b/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 diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c index a637e24f3..efe3028a8 100644 --- a/src/cairo-type1-subset.c +++ b/src/cairo-type1-subset.c @@ -41,6 +41,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 diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c index 07554154c..ee2d0e346 100644 --- a/src/cairo-type3-glyph-surface.c +++ b/src/cairo-type3-glyph-surface.c @@ -287,8 +287,7 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const 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; @@ -325,38 +324,32 @@ static const cairo_surface_backend_t cairo_type3_glyph_surface_backend = { CAIRO_INTERNAL_SURFACE_TYPE_TYPE3_GLYPH, _cairo_type3_glyph_surface_finish, - _cairo_default_context_create, + _cairo_default_context_create, /* XXX usable through a context? */ - NULL, /* _cairo_type3_glyph_surface_create_similar */ - NULL, /* _cairo_type3_glyph_surface_create_similar_image */ - NULL, /* _cairo_type3_glyph_surface_create_map_to_image */ - NULL, /* _cairo_type3_glyph_surface_create_unmap_image */ + NULL, /* create similar */ + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ 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 diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index f18a20ea3..2fdd0a19a 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -80,7 +80,6 @@ typedef struct _cairo_output_stream cairo_output_stream_t; 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; @@ -89,11 +88,24 @@ 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); @@ -248,8 +260,6 @@ typedef enum _cairo_internal_device_type { } 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; @@ -413,24 +423,6 @@ typedef struct _cairo_unscaled_font { cairo_reference_count_t ref_count; const cairo_unscaled_font_backend_t *backend; } cairo_unscaled_font_t; - -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; - CAIRO_END_DECLS #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/src/cairo-wideint-private.h b/src/cairo-wideint-private.h index f6ae5bcfc..24c506a7d 100644 --- a/src/cairo-wideint-private.h +++ b/src/cairo-wideint-private.h @@ -56,6 +56,9 @@ _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 (uint64_t i); +cairo_int64_t I _cairo_double_to_int64 (double i); +double I _cairo_int64_to_double (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); @@ -115,6 +118,9 @@ _cairo_uint64_divrem (cairo_uint64_t num, cairo_uint64_t den) 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)) diff --git a/src/cairo-wideint.c b/src/cairo-wideint.c index b6e22808d..59af2c8c8 100644 --- a/src/cairo-wideint.c +++ b/src/cairo-wideint.c @@ -99,6 +99,22 @@ _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) { diff --git a/src/cairo-win32-printing-surface.c b/src/cairo-win32-printing-surface.c index 99aca4a9b..f6ce2028c 100644 --- a/src/cairo-win32-printing-surface.c +++ b/src/cairo-win32-printing-surface.c @@ -56,6 +56,7 @@ #include "cairo-scaled-font-subsets-private.h" #include "cairo-image-info-private.h" #include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" #include "cairo-surface-clipper-private.h" #include @@ -1873,21 +1874,17 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { 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 */ _cairo_win32_printing_surface_show_page, + _cairo_win32_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_win32_printing_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ + NULL, /* scaled_font_fini */ NULL, /* scaled_glyph_fini */ @@ -1895,10 +1892,8 @@ static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { NULL, /* mask */ _cairo_win32_printing_surface_stroke, _cairo_win32_printing_surface_fill, + NULL, /* fill/stroke */ _cairo_win32_printing_surface_show_glyphs, - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ }; static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c index b1d1c6160..274df3ff4 100644 --- a/src/cairo-win32-surface.c +++ b/src/cairo-win32-surface.c @@ -2213,7 +2213,7 @@ _cairo_win32_save_initial_clip (HDC hdc, cairo_win32_surface_t *surface) * set. GetClipBox returns values in logical (transformed) coordinates; * it's unclear what GetClipRgn returns, because the region is empty in the * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. - * Similarily, IntersectClipRect works in logical units, whereas SelectClipRgn + * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn * works in device units. * * So, avoid the whole mess and get rid of the world transform diff --git a/src/cairo-xcb-connection.c b/src/cairo-xcb-connection.c index b25264848..2808395e1 100644 --- a/src/cairo-xcb-connection.c +++ b/src/cairo-xcb-connection.c @@ -527,7 +527,7 @@ _device_finish (void *device) font = cairo_list_first_entry (&connection->fonts, cairo_xcb_font_t, link); - _cairo_xcb_font_finish (font); + _cairo_xcb_font_close (font); } while (! cairo_list_is_empty (&connection->screens)) { diff --git a/src/cairo-xcb-private.h b/src/cairo-xcb-private.h index e87f0e88c..9223d670c 100644 --- a/src/cairo-xcb-private.h +++ b/src/cairo-xcb-private.h @@ -48,6 +48,7 @@ #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" @@ -167,6 +168,7 @@ typedef struct _cairo_xcb_font_glyphset_info { } 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]; @@ -321,7 +323,7 @@ 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, diff --git a/src/cairo-xcb-surface-core.c b/src/cairo-xcb-surface-core.c index c28857500..447529d53 100644 --- a/src/cairo-xcb-surface-core.c +++ b/src/cairo-xcb-surface-core.c @@ -34,6 +34,7 @@ #include "cairo-boxes-private.h" #include "cairo-xcb-private.h" #include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" /* XXX dithering */ diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index aa9100fc7..f9d33c71b 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -31,6 +31,8 @@ #include "cairoint.h" +#include "cairo-xcb-private.h" + #include "cairo-boxes-private.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" @@ -39,7 +41,7 @@ #include "cairo-surface-offset-private.h" #include "cairo-surface-snapshot-private.h" #include "cairo-surface-subsurface-private.h" -#include "cairo-xcb-private.h" +#include "cairo-traps-private.h" #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ @@ -2501,10 +2503,10 @@ _composite_boxes (cairo_xcb_surface_t *dst, return CAIRO_INT_STATUS_UNSUPPORTED; } - if (cairo_region_contains_rectangle (clip_region, &extents->bounded) == CAIRO_REGION_OVERLAP_IN) { - clip = NULL; + if (clip_region != NULL && + cairo_region_contains_rectangle (clip_region, + &extents->bounded) == CAIRO_REGION_OVERLAP_IN) clip_region = NULL; - } status = _cairo_xcb_connection_acquire (dst->connection); if (unlikely (status)) @@ -3396,7 +3398,6 @@ _cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_boxes_t boxes; cairo_status_t status; @@ -3427,18 +3428,16 @@ _cairo_xcb_surface_render_paint (cairo_xcb_surface_t *surface, return CAIRO_STATUS_SUCCESS; } - _cairo_xcb_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_paint (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_paint (&extents, + &surface->base, op, source, clip); if (unlikely (status)) return status; - status = _cairo_clip_to_boxes(extents.clip, &boxes); - if (likely (status == CAIRO_STATUS_SUCCESS)) { - status = _clip_and_composite_boxes (surface, op, source, - &boxes, &extents); - } + _cairo_clip_steal_boxes(extents.clip, &boxes); + status = _clip_and_composite_boxes (surface, op, source, &boxes, &extents); + _cairo_clip_unsteal_boxes (extents.clip, &boxes); _cairo_composite_rectangles_fini (&extents); @@ -3453,7 +3452,6 @@ _cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) @@ -3462,8 +3460,7 @@ _cairo_xcb_surface_render_mask (cairo_xcb_surface_t *surface, if ((surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xcb_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_mask (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_mask (&extents, &surface->base, op, source, mask, clip); if (unlikely (status)) return status; @@ -3599,7 +3596,6 @@ _cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) @@ -3611,8 +3607,8 @@ _cairo_xcb_surface_render_stroke (cairo_xcb_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - _cairo_xcb_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_stroke (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_stroke (&extents, + &surface->base, op, source, path, style, ctm, clip); @@ -3750,7 +3746,6 @@ _cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; if (unlikely (! _operator_is_supported (surface->connection->flags, op))) @@ -3762,8 +3757,7 @@ _cairo_xcb_surface_render_fill (cairo_xcb_surface_t *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - _cairo_xcb_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_fill (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_fill (&extents, &surface->base, op, source, path, clip); if (unlikely (status)) @@ -3916,7 +3910,7 @@ _can_composite_glyphs (cairo_xcb_surface_t *dst, valid_glyphs = glyphs; for (glyphs_end = glyphs + *num_glyphs; glyphs != glyphs_end; glyphs++) { double x1, y1, x2, y2; - cairo_scaled_glyph_t *scaled_glyph; + cairo_scaled_glyph_t *glyph; cairo_box_t *bbox; int width, height, len; int g; @@ -3926,12 +3920,12 @@ _can_composite_glyphs (cairo_xcb_surface_t *dst, status = _cairo_scaled_glyph_lookup (scaled_font, glyphs->index, CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); + &glyph); if (unlikely (status)) break; glyph_cache[g] = glyphs->index; - bbox_cache[g] = scaled_glyph->bbox; + bbox_cache[g] = glyph->bbox; } bbox = &bbox_cache[g]; @@ -4013,170 +4007,115 @@ typedef struct { } x_glyph_elt_t; #define _cairo_sz_x_glyph_elt_t (sizeof (x_glyph_elt_t) + 4) -static cairo_xcb_font_glyphset_info_t * -_cairo_xcb_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) -{ - return scaled_glyph->surface_private; -} - -static void -_cairo_xcb_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, - cairo_xcb_font_glyphset_info_t *glyphset_info) -{ - scaled_glyph->surface_private = glyphset_info; -} - -static cairo_status_t -_cairo_xcb_surface_font_init (cairo_xcb_connection_t *connection, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_font_t *font_private; - int i; - - font_private = malloc (sizeof (cairo_xcb_font_t)); - if (unlikely (font_private == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->scaled_font = scaled_font; - font_private->connection = _cairo_xcb_connection_reference (connection); - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xcb_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; - switch (i) { - case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; - case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; - case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; - default: ASSERT_NOT_REACHED; break; - } - glyphset_info->xrender_format = 0; - glyphset_info->glyphset = XCB_NONE; - glyphset_info->pending_free_glyphs = NULL; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &_cairo_xcb_surface_backend; - - cairo_list_add (&font_private->link, &connection->fonts); - - return CAIRO_STATUS_SUCCESS; -} - 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 *glyphset_info; + cairo_xcb_font_glyphset_info_t *info; - glyphset_info = &font->glyphset_info[i]; - - free (glyphset_info->pending_free_glyphs); + 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); } -void -_cairo_xcb_font_finish (cairo_xcb_font_t *font) -{ - cairo_scaled_font_t *scaled_font; - - scaled_font = font->scaled_font; - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - scaled_font->surface_private = NULL; - _cairo_scaled_font_reset_cache (scaled_font); - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - _cairo_xcb_font_destroy (font); -} - -void -_cairo_xcb_surface_scaled_font_fini (cairo_scaled_font_t *scaled_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 *font_private = (cairo_xcb_font_t *)abstract_private; cairo_xcb_connection_t *connection; cairo_bool_t have_connection; cairo_status_t status; int i; - font_private = scaled_font->surface_private; - if (font_private == NULL) - return; - 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 *glyphset_info; + cairo_xcb_font_glyphset_info_t *info; - glyphset_info = &font_private->glyphset_info[i]; - if (glyphset_info->glyphset && status == CAIRO_STATUS_SUCCESS) { + info = &font_private->glyphset_info[i]; + if (info->glyphset && status == CAIRO_STATUS_SUCCESS) { _cairo_xcb_connection_render_free_glyph_set (connection, - glyphset_info->glyphset); + info->glyphset); } } if (have_connection) _cairo_xcb_connection_release (connection); - _cairo_xcb_font_destroy (font_private); } -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); -} -void -_cairo_xcb_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) +static cairo_xcb_font_t * +_cairo_xcb_font_create (cairo_xcb_connection_t *connection, + cairo_scaled_font_t *font) { - cairo_xcb_font_t *font_private; - cairo_xcb_font_glyphset_info_t *glyphset_info; + cairo_xcb_font_t *priv; + int i; - if (scaled_font->finished) - return; + priv = malloc (sizeof (cairo_xcb_font_t)); + if (unlikely (priv == NULL)) + return NULL; - font_private = scaled_font->surface_private; - glyphset_info = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph); - if (font_private != NULL && glyphset_info != NULL) { - cairo_xcb_font_glyphset_free_glyphs_t *to_free; + _cairo_scaled_font_attach_private (font, &priv->base, connection, + _cairo_xcb_font_fini); - to_free = glyphset_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 = glyphset_info->pending_free_glyphs = NULL; + 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; + } - 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 */ - } + return priv; +} - to_free->glyphset = glyphset_info->glyphset; - to_free->glyph_count = 0; - glyphset_info->pending_free_glyphs = to_free; - } +void +_cairo_xcb_font_close (cairo_xcb_font_t *font) +{ + cairo_scaled_font_t *scaled_font; - to_free->glyph_indices[to_free->glyph_count++] = - _cairo_scaled_glyph_index (scaled_glyph); - } + scaled_font = font->scaled_font; + + CAIRO_MUTEX_LOCK (scaled_font->mutex); + //scaled_font->surface_private = NULL; + _cairo_scaled_font_reset_cache (scaled_font); + CAIRO_MUTEX_UNLOCK (scaled_font->mutex); + + _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 @@ -4191,42 +4130,57 @@ _cairo_xcb_get_glyphset_index_for_format (cairo_format_t format) 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_scaled_font_t *scaled_font, +_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 *font_private; - cairo_xcb_font_glyphset_info_t *glyphset_info; + 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); - font_private = scaled_font->surface_private; - glyphset_info = &font_private->glyphset_info[glyphset_index]; - if (glyphset_info->glyphset == XCB_NONE) { - cairo_xcb_connection_t *connection = font_private->connection; - glyphset_info->glyphset = _cairo_xcb_connection_get_xid (font_private->connection); - glyphset_info->xrender_format = - connection->standard_formats[glyphset_info->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 (font_private->connection, - glyphset_info->glyphset, - glyphset_info->xrender_format); + _cairo_xcb_connection_render_create_glyph_set (c, + info->glyphset, + info->xrender_format); } - return glyphset_info; + return info; } static cairo_bool_t _cairo_xcb_glyphset_info_has_pending_free_glyph ( - cairo_xcb_font_glyphset_info_t *glyphset_info, + cairo_xcb_font_glyphset_info_t *info, unsigned long glyph_index) { - if (glyphset_info->pending_free_glyphs != NULL) { + if (info->pending_free_glyphs != NULL) { cairo_xcb_font_glyphset_free_glyphs_t *to_free; int i; - to_free = glyphset_info->pending_free_glyphs; + 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--; @@ -4241,44 +4195,115 @@ _cairo_xcb_glyphset_info_has_pending_free_glyph ( 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_scaled_font_t *scaled_font, +_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 *font_private; + cairo_xcb_font_t *priv; int i; - font_private = scaled_font->surface_private; - if (font_private == NULL) + 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 ( - &font_private->glyphset_info[i], + &priv->glyphset_info[i], glyph_index)) { - return &font_private->glyphset_info[i]; + return &priv->glyphset_info[i]; } } else { for (i = 0; i < NUM_GLYPHSETS; i++) { if (_cairo_xcb_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], + &priv->glyphset_info[i], glyph_index)) { - return &font_private->glyphset_info[i]; + 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 *scaled_font, + cairo_scaled_font_t *font, cairo_scaled_glyph_t **scaled_glyph_out) { xcb_render_glyphinfo_t glyph_info; @@ -4288,19 +4313,17 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, 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 *glyphset_info; + 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 */ - glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); - if (glyphset_info != NULL) { - _cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - return CAIRO_STATUS_SUCCESS; - } + 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 (scaled_font, + status = _cairo_scaled_glyph_lookup (font, glyph_index, CAIRO_SCALED_GLYPH_INFO_METRICS | CAIRO_SCALED_GLYPH_INFO_SURFACE, @@ -4315,22 +4338,22 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, already_had_glyph_surface = TRUE; } - if (scaled_font->surface_private == NULL) { - status = _cairo_xcb_surface_font_init (connection, scaled_font); - if (unlikely (status)) - return status; + 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; } - glyphset_info = _cairo_xcb_scaled_font_get_glyphset_info_for_format (scaled_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 (glyphset_info->format, 1, 1); + tmp_surface = cairo_image_surface_create (info->format, 1, 1); status = tmp_surface->status; if (unlikely (status)) goto BAIL; @@ -4340,14 +4363,15 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, 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 != glyphset_info->format) { + if (glyph_surface->format != info->format) { glyph_surface = _cairo_image_surface_coerce_to_format (glyph_surface, - glyphset_info->format); + info->format); status = glyph_surface->base.status; if (unlikely (status)) goto BAIL; @@ -4423,7 +4447,7 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, /* XXX assume X server wants pixman padding. Xft assumes this as well */ _cairo_xcb_connection_render_add_glyphs (connection, - glyphset_info->glyphset, + info->glyphset, 1, &glyph_index, &glyph_info, glyph_surface->stride * glyph_surface->height, data); @@ -4431,7 +4455,7 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, if (data != glyph_surface->data) free (data); - _cairo_xcb_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); + status = _cairo_xcb_glyph_attach (connection, scaled_glyph, info); BAIL: if (glyph_surface != scaled_glyph->surface) @@ -4444,7 +4468,7 @@ _cairo_xcb_surface_add_glyph (cairo_xcb_connection_t *connection, * the cache */ if (! already_had_glyph_surface) - _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); + _cairo_scaled_glyph_set_surface (scaled_glyph, font, NULL); return status; } @@ -4471,7 +4495,7 @@ _emit_glyphs_chunk (cairo_xcb_surface_t *dst, int num_glyphs, int width, int estimated_req_size, - cairo_xcb_font_glyphset_info_t *glyphset_info, + cairo_xcb_font_glyphset_info_t *info, xcb_render_pictformat_t mask_format) { cairo_xcb_render_composite_text_func_t composite_text_func; @@ -4529,7 +4553,7 @@ _emit_glyphs_chunk (cairo_xcb_surface_t *dst, src->picture, dst->picture, mask_format, - glyphset_info->glyphset, + info->glyphset, src->x + glyphs[0].i.x, src->y + glyphs[0].i.y, len, buf); @@ -4577,43 +4601,43 @@ _composite_glyphs (void *closure, memset (glyph_cache, 0, sizeof (glyph_cache)); for (i = 0; i < info->num_glyphs; i++) { - cairo_scaled_glyph_t *scaled_glyph; + 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; - scaled_glyph = glyph_cache[cache_index]; - if (scaled_glyph == NULL || - _cairo_scaled_glyph_index (scaled_glyph) != glyph_index) + 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, - &scaled_glyph); + &glyph); if (unlikely (status)) { cairo_surface_destroy (&src->base); return status; } /* Send unseen glyphs to the server */ - if (_cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { + if (glyph->dev_private_key != dst->connection) { status = _cairo_xcb_surface_add_glyph (dst->connection, info->font, - &scaled_glyph); + &glyph); if (unlikely (status)) { cairo_surface_destroy (&src->base); return status; } } - glyph_cache[cache_index] = scaled_glyph; + 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 = _cairo_xcb_scaled_glyph_get_glyphset_info (scaled_glyph); + this_glyphset_info = glyph->dev_private; if (glyphset_info == NULL) glyphset_info = this_glyphset_info; @@ -4689,8 +4713,8 @@ _composite_glyphs (void *closure, request_size += _cairo_sz_x_glyph_elt_t; /* adjust current-position */ - x = this_x + scaled_glyph->x_advance; - y = this_y + scaled_glyph->y_advance; + x = this_x + glyph->x_advance; + y = this_y + glyph->y_advance; request_size += width; } @@ -4708,23 +4732,6 @@ _composite_glyphs (void *closure, return status; } -static cairo_bool_t -_surface_owns_font (cairo_xcb_surface_t *dst, - cairo_scaled_font_t *scaled_font) -{ - cairo_xcb_font_t *font_private; - - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != dst->base.backend) || - (font_private != NULL && font_private->connection != dst->connection)) - { - return FALSE; - } - - return TRUE; -} - cairo_int_status_t _cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, cairo_operator_t op, @@ -4735,7 +4742,6 @@ _cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, const cairo_clip_t *clip) { cairo_composite_rectangles_t extents; - cairo_rectangle_int_t unbounded; cairo_int_status_t status; cairo_bool_t overlap; @@ -4745,8 +4751,7 @@ _cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, if ((surface->connection->flags & (CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS | CAIRO_XCB_RENDER_HAS_COMPOSITE)) == 0) return CAIRO_INT_STATUS_UNSUPPORTED; - _cairo_xcb_surface_get_extents (surface, &unbounded); - status = _cairo_composite_rectangles_init_for_glyphs (&extents, &unbounded, + status = _cairo_composite_rectangles_init_for_glyphs (&extents, &surface->base, op, source, scaled_font, glyphs, num_glyphs, @@ -4755,40 +4760,38 @@ _cairo_xcb_surface_render_glyphs (cairo_xcb_surface_t *surface, return status; status = CAIRO_INT_STATUS_UNSUPPORTED; - if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS) { + if (surface->connection->flags & CAIRO_XCB_RENDER_HAS_COMPOSITE_GLYPHS && 0) { _cairo_scaled_font_freeze_cache (scaled_font); - if (_surface_owns_font (surface, scaled_font)) { - status = _can_composite_glyphs (surface, &extents.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 || - ! extents.is_bounded || - ! _cairo_clip_is_region(extents.clip); - - if (extents.mask.width > extents.unbounded.width || - extents.mask.height > extents.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, &extents, - need_bounded_clip (&extents) | - flags); + status = _can_composite_glyphs (surface, &extents.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 || + ! extents.is_bounded || + ! _cairo_clip_is_region(extents.clip); + + if (extents.mask.width > extents.unbounded.width || + extents.mask.height > extents.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, &extents, + need_bounded_clip (&extents) | + flags); } _cairo_scaled_font_thaw_cache (scaled_font); diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c index 9639f9162..fe4d62863 100644 --- a/src/cairo-xcb-surface.c +++ b/src/cairo-xcb-surface.c @@ -45,6 +45,7 @@ #include "cairo-default-context-private.h" #include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" #if CAIRO_HAS_XLIB_XCB_FUNCTIONS slim_hidden_proto (cairo_xcb_surface_create); @@ -822,14 +823,11 @@ _cairo_xcb_surface_glyphs (void *abstract_surface, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *num_remaining) + const cairo_clip_t *clip) { cairo_xcb_surface_t *surface = abstract_surface; cairo_int_status_t status; - *num_remaining = 0; - if (surface->fallback == NULL) { status = _cairo_xcb_surface_cairo_glyphs (surface, op, source, @@ -864,36 +862,28 @@ const cairo_surface_backend_t _cairo_xcb_surface_backend = { _cairo_xcb_surface_create_similar, _cairo_xcb_surface_create_similar_image, - _cairo_xcb_surface_map_to_image, _cairo_xcb_surface_unmap, _cairo_xcb_surface_acquire_source_image, _cairo_xcb_surface_release_source_image, + NULL, /* snapshot */ - NULL, NULL, NULL, /* dest acquire/release/clone */ - - NULL, /* composite */ - NULL, /* fill */ - NULL, /* trapezoids */ - NULL, /* span */ - NULL, /* check-span */ NULL, /* copy_page */ NULL, /* show_page */ + _cairo_xcb_surface_get_extents, - NULL, /* old-glyphs */ _cairo_xcb_surface_get_font_options, _cairo_xcb_surface_flush, NULL, - _cairo_xcb_surface_scaled_font_fini, - _cairo_xcb_surface_scaled_glyph_fini, _cairo_xcb_surface_paint, _cairo_xcb_surface_mask, _cairo_xcb_surface_stroke, _cairo_xcb_surface_fill, + NULL, /* fill-stroke */ _cairo_xcb_surface_glyphs, }; @@ -1231,26 +1221,25 @@ cairo_xcb_surface_set_size (cairo_surface_t *abstract_surface, int height) { cairo_xcb_surface_t *surface; - cairo_status_t status_ignored; if (unlikely (abstract_surface->status)) return; if (unlikely (abstract_surface->finished)) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); return; } if (abstract_surface->type != CAIRO_SURFACE_TYPE_XCB) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + _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) { - status_ignored = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); return; } diff --git a/src/cairo-xlib-core-compositor.c b/src/cairo-xlib-core-compositor.c new file mode 100644 index 000000000..4d59c1f19 --- /dev/null +++ b/src/cairo-xlib-core-compositor.c @@ -0,0 +1,524 @@ +/* -*- 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 + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , 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" + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" + +#include "cairo-boxes-private.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; +}; + +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 cairo_int_status_t +fill_boxes (cairo_xlib_surface_t *dst, + const cairo_color_t *color, + cairo_boxes_t *boxes) +{ + cairo_surface_t *dither = NULL; + cairo_status_t status; + struct _fill_box fb; + + 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) { + cairo_solid_pattern_t solid; + cairo_surface_attributes_t attrs; + + _cairo_pattern_init_solid (&solid, color); +#if 0 + 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; + } +#endif + + 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); + } else { + //XChangeGC (fb.dpy, fb.gc, GCForeground, color_to_pixel (&color)); + } + + _cairo_boxes_for_each_box (boxes, fill_box, &fb); + _cairo_xlib_surface_put_gc (dst->display, dst, fb.gc); + + cairo_surface_destroy (dither); + + 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_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; + + /* 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; + + pattern = (const cairo_surface_pattern_t *) source; + if (pattern->surface->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; + + if (! _cairo_xlib_surface_same_screen (dst, src)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* XXX subsurface */ + + 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 (! _cairo_boxes_for_each_box (boxes, copy_box, &cb)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_xlib_surface_put_gc (dst->display, dst, cb.gc); + + return CAIRO_STATUS_SUCCESS; +} + +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 (_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 = 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 (_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 (_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; +} diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c index 3acc89372..fd7253cc2 100644 --- a/src/cairo-xlib-display.c +++ b/src/cairo-xlib-display.c @@ -47,52 +47,8 @@ typedef int (*cairo_xlib_error_func_t) (Display *display, XErrorEvent *event); -struct _cairo_xlib_job { - cairo_xlib_job_t *next; - enum { - RESOURCE, - WORK - } type; - union { - struct { - cairo_xlib_notify_resource_func notify; - XID xid; - } resource; - struct { - cairo_xlib_notify_func notify; - void *data; - void (*destroy) (void *); - } work; - } func; -}; - static cairo_xlib_display_t *_cairo_xlib_display_list; -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook); - -static void -_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display) -{ - cairo_xlib_screen_t *screen; - cairo_xlib_hook_t *hook; - - cairo_list_foreach_entry (screen, cairo_xlib_screen_t, &display->screens, link) - _cairo_xlib_screen_close_display (display, screen); - - while (TRUE) { - hook = display->close_display_hooks; - if (hook == NULL) - break; - - _cairo_xlib_remove_close_display_hook_internal (display, hook); - - hook->func (display, hook); - } - display->closed = TRUE; -} - static int _noop_error_handler (Display *display, XErrorEvent *event) @@ -100,59 +56,6 @@ _noop_error_handler (Display *display, return False; /* return value is ignored */ } -static void -_cairo_xlib_display_notify (cairo_xlib_display_t *display) -{ - cairo_xlib_job_t *jobs, *job, *freelist; - Display *dpy = display->display; - - /* Optimistic atomic pointer read -- don't care if it is wrong due to - * contention as we will check again very shortly. - */ - if (display->workqueue == NULL) - return; - - jobs = display->workqueue; - while (jobs != NULL) { - display->workqueue = NULL; - - /* reverse the list to obtain FIFO order */ - job = NULL; - do { - cairo_xlib_job_t *next = jobs->next; - jobs->next = job; - job = jobs; - jobs = next; - } while (jobs != NULL); - freelist = jobs = job; - - do { - job = jobs; - jobs = job->next; - - switch (job->type){ - case WORK: - job->func.work.notify (dpy, job->func.work.data); - if (job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - break; - - case RESOURCE: - job->func.resource.notify (dpy, job->func.resource.xid); - break; - } - } while (jobs != NULL); - - do { - job = freelist; - freelist = job->next; - _cairo_freelist_free (&display->wq_freelist, job); - } while (freelist != NULL); - - jobs = display->workqueue; - } -} - static void _cairo_xlib_display_finish (void *abstract_display) { @@ -166,11 +69,18 @@ _cairo_xlib_display_finish (void *abstract_display) XSync (dpy, False); old_handler = XSetErrorHandler (_noop_error_handler); - _cairo_xlib_display_notify (display); - _cairo_xlib_call_close_display_hooks (display); + while (! cairo_list_is_empty (&display->fonts)) { + _cairo_xlib_font_close (cairo_list_first_entry (&display->fonts, + cairo_xlib_font_t, + link)); + } - /* catch any that arrived before marking the display as closed */ - _cairo_xlib_display_notify (display); + 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); @@ -184,24 +94,6 @@ _cairo_xlib_display_destroy (void *abstract_display) { cairo_xlib_display_t *display = abstract_display; - /* destroy all outstanding notifies */ - while (display->workqueue != NULL) { - cairo_xlib_job_t *job = display->workqueue; - display->workqueue = job->next; - - if (job->type == WORK && job->func.work.destroy != NULL) - job->func.work.destroy (job->func.work.data); - - _cairo_freelist_free (&display->wq_freelist, job); - } - _cairo_freelist_fini (&display->wq_freelist); - - while (! cairo_list_is_empty (&display->screens)) { - _cairo_xlib_screen_destroy (cairo_list_first_entry (&display->screens, - cairo_xlib_screen_t, - link)); - } - free (display); } @@ -340,15 +232,18 @@ _cairo_xlib_device_create (Display *dpy) XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display); - _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t)); - cairo_device_reference (&display->base); /* add one for the CloseDisplay */ display->display = dpy; cairo_list_init (&display->screens); - display->workqueue = NULL; - display->close_display_hooks = NULL; + cairo_list_init (&display->fonts); display->closed = FALSE; + 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)); @@ -436,6 +331,13 @@ _cairo_xlib_device_create (Display *dpy) display->buggy_pad_reflect = TRUE; } + 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 (); + display->next = _cairo_xlib_display_list; _cairo_xlib_display_list = display; @@ -446,92 +348,6 @@ _cairo_xlib_device_create (Display *dpy) return device; } -void -_cairo_xlib_add_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - hook->prev = NULL; - hook->next = display->close_display_hooks; - if (hook->next != NULL) - hook->next->prev = hook; - display->close_display_hooks = hook; -} - -static void -_cairo_xlib_remove_close_display_hook_internal (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - if (display->close_display_hooks == hook) - display->close_display_hooks = hook->next; - else if (hook->prev != NULL) - hook->prev->next = hook->next; - - if (hook->next != NULL) - hook->next->prev = hook->prev; - - hook->prev = NULL; - hook->next = NULL; -} - -void -_cairo_xlib_remove_close_display_hook (cairo_xlib_display_t *display, - cairo_xlib_hook_t *hook) -{ - _cairo_xlib_remove_close_display_hook_internal (display, hook); -} - -cairo_status_t -_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display, - cairo_xlib_notify_resource_func notify, - XID xid) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = RESOURCE; - job->func.resource.xid = xid; - job->func.resource.notify = notify; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - -cairo_status_t -_cairo_xlib_display_queue_work (cairo_xlib_display_t *display, - cairo_xlib_notify_func notify, - void *data, - void (*destroy) (void *)) -{ - cairo_xlib_job_t *job; - cairo_status_t status = CAIRO_STATUS_NO_MEMORY; - - if (display->closed == FALSE) { - job = _cairo_freelist_alloc (&display->wq_freelist); - if (job != NULL) { - job->type = WORK; - job->func.work.data = data; - job->func.work.notify = notify; - job->func.work.destroy = destroy; - - job->next = display->workqueue; - display->workqueue = job; - - status = CAIRO_STATUS_SUCCESS; - } - } - - return status; -} - cairo_status_t _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **display) { @@ -542,7 +358,6 @@ _cairo_xlib_display_acquire (cairo_device_t *device, cairo_xlib_display_t **disp return status; *display = (cairo_xlib_display_t *) device; - _cairo_xlib_display_notify (*display); return status; } @@ -719,14 +534,6 @@ _cairo_xlib_display_get_screen (cairo_xlib_display_t *display, return NULL; } -void -_cairo_xlib_display_get_xrender_version (cairo_xlib_display_t *display, - int *major, int *minor) -{ - *major = display->render_major; - *minor = display->render_minor; -} - cairo_bool_t _cairo_xlib_display_has_repeat (cairo_device_t *device) { diff --git a/src/test-null-surface.h b/src/cairo-xlib-fallback-compositor.c similarity index 66% rename from src/test-null-surface.h rename to src/cairo-xlib-fallback-compositor.c index 3c05190f4..539192684 100644 --- a/src/test-null-surface.h +++ b/src/cairo-xlib-fallback-compositor.c @@ -1,6 +1,9 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ /* cairo - a vector graphics library with display and print output * - * Copyright © 2009 Chris Wilson + * 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 @@ -27,20 +30,25 @@ * * The Original Code is the cairo graphics library. * + * The Initial Developer of the Original Code is University of Southern + * California. + * * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod * Chris Wilson + * Karl Tomlinson , Mozilla Corporation */ -#ifndef TEST_NULL_SURFACE_H -#define TEST_NULL_SURFACE_H - -#include "cairo.h" - -CAIRO_BEGIN_DECLS +#include "cairoint.h" -cairo_public cairo_surface_t * -_cairo_test_null_surface_create (cairo_content_t content); +#include "cairo-xlib-private.h" -CAIRO_END_DECLS +#include "cairo-compositor-private.h" -#endif /* TEST_NULL_SURFACE_H */ +const cairo_compositor_t * +_cairo_xlib_fallback_compositor_get (void) +{ + /* XXX Do something interesting here to mitigate fallbacks ala xcb */ + return &_cairo_fallback_compositor; +} diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h index 3a32eff26..452283233 100644 --- a/src/cairo-xlib-private.h +++ b/src/cairo-xlib-private.h @@ -46,21 +46,15 @@ #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 typedef struct _cairo_xlib_display cairo_xlib_display_t; typedef struct _cairo_xlib_screen cairo_xlib_screen_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); -}; +typedef struct _cairo_xlib_source cairo_xlib_source_t; +typedef struct _cairo_xlib_surface cairo_xlib_surface_t; /* size of color cube */ #define CUBE_SIZE 6 @@ -76,20 +70,43 @@ struct _cairo_xlib_display { Display *display; cairo_list_t screens; + cairo_list_t fonts; + + 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; - unsigned int buggy_gradients :1; - unsigned int buggy_pad_reflect :1; - unsigned int buggy_repeat :1; + 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; unsigned int closed :1; }; @@ -120,6 +137,79 @@ struct _cairo_xlib_screen { 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; + + const cairo_compositor_t *compositor; + + cairo_xlib_display_t *display; + cairo_xlib_screen_t *screen; + cairo_list_t link; + + Display *dpy; /* only valid between acquire/release */ + Drawable drawable; + 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; + Display *dpy; + + unsigned int filter:3; + unsigned int extend:3; + unsigned int has_matrix:1; + unsigned int has_component_alpha:1; + } embedded_source; +}; + +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); @@ -127,29 +217,10 @@ 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); @@ -177,21 +248,17 @@ _cairo_xlib_screen_get (Display *dpy, 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_t *info); +_cairo_xlib_screen_destroy (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info); cairo_private GC _cairo_xlib_screen_get_gc (cairo_xlib_display_t *display, 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, + cairo_xlib_screen_t *info, int depth, GC gc); @@ -213,4 +280,105 @@ _cairo_xlib_visual_info_create (Display *dpy, 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; +} + +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); +} + #endif /* CAIRO_XLIB_PRIVATE_H */ diff --git a/src/cairo-xlib-render-compositor.c b/src/cairo-xlib-render-compositor.c new file mode 100644 index 000000000..6e53f1339 --- /dev/null +++ b/src/cairo-xlib-render-compositor.c @@ -0,0 +1,1685 @@ +/* -*- 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 + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ + +#include "cairoint.h" + +#include "cairo-xlib-private.h" + +#include "cairo-compositor-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-traps-private.h" +#include "cairo-tristrip-private.h" + +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 +draw_image_boxes (void *_dst, + cairo_image_surface_t *image, + cairo_boxes_t *boxes, + int dx, int dy) +{ + 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 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)) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + 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_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; + 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 (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, src->drawable, 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, YSorted); + + XCopyArea (dst->dpy, src->drawable, 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); + } + + _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)) + return op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD; + + 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)); + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + if (fill_reduces_to_source (op, color, dst)) + op = CAIRO_OPERATOR_SOURCE; + + _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; + + render_color.red = color->red_short; + render_color.green = color->green_short; + render_color.blue = color->blue_short; + render_color.alpha = color->alpha_short; + + if (fill_reduces_to_source (op, color, dst)) + op = CAIRO_OPERATOR_SOURCE; + + _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, + src_y + elts[0].yOff, + elts[0].xOff - dst_x, elts[0].yOff - dst_y, + (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; + + 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 = 0, y = 0; + 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.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)); + + 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; +} diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c index 356131fa7..7bfdf1581 100644 --- a/src/cairo-xlib-screen.c +++ b/src/cairo-xlib-screen.c @@ -269,8 +269,8 @@ _cairo_xlib_init_screen_font_options (Display *dpy, } void -_cairo_xlib_screen_close_display (cairo_xlib_display_t *display, - cairo_xlib_screen_t *info) +_cairo_xlib_screen_destroy (cairo_xlib_display_t *display, + cairo_xlib_screen_t *info) { Display *dpy; int i; @@ -292,11 +292,7 @@ _cairo_xlib_screen_close_display (cairo_xlib_display_t *display, info->gc_depths[i] = 0; } } -} -void -_cairo_xlib_screen_destroy (cairo_xlib_screen_t *info) -{ while (! cairo_list_is_empty (&info->visuals)) { _cairo_xlib_visual_info_destroy (cairo_list_first_entry (&info->visuals, cairo_xlib_visual_info_t, @@ -403,19 +399,9 @@ _cairo_xlib_screen_put_gc (cairo_xlib_display_t *display, } if (i == ARRAY_LENGTH (info->gc)) { - cairo_status_t status; - /* perform random substitution to ensure fair caching over depths */ i = rand () % ARRAY_LENGTH (info->gc); - status = - _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) XFreeGC, - info->gc[i], - NULL); - if (unlikely (status)) { - /* leak the server side resource... */ - XFree ((char *) info->gc[i]); - } + XFreeGC(display->display, info->gc[i]); } info->gc[i] = gc; diff --git a/src/cairo-xlib-source.c b/src/cairo-xlib-source.c new file mode 100644 index 000000000..dc5aa7a0a --- /dev/null +++ b/src/cairo-xlib-source.c @@ -0,0 +1,938 @@ +/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2002 University of Southern California + * Copyright © 2005 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Behdad Esfahbod + * Chris Wilson + * Karl Tomlinson , Mozilla Corporation + */ +#include "cairoint.h" + +#if !CAIRO_HAS_XLIB_XCB_FUNCTIONS + +#include "cairo-xlib-private.h" +#include "cairo-xlib-surface-private.h" + +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-paginated-private.h" +#include "cairo-pattern-private.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-private.h" + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + +static cairo_surface_t * +unwrap_surface (cairo_surface_t *surface, int *tx, int *ty) +{ + *tx = *ty = 0; + + 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); + if (_cairo_surface_is_observer (surface)) + surface = _cairo_surface_observer_get_target (surface); + return surface; +} + +static cairo_status_t +_cairo_xlib_source_finish (void *abstract_surface) +{ + cairo_xlib_source_t *source = abstract_surface; + + XRenderFreePicture (source->dpy, source->picture); + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t cairo_xlib_source_backend = { + CAIRO_SURFACE_TYPE_IMAGE, + _cairo_xlib_source_finish, + NULL, /* read-only wrapper */ +}; + +static cairo_surface_t * +source (cairo_xlib_surface_t *dst, Picture picture) +{ + cairo_xlib_source_t *source; + + if (picture == None) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + source = malloc (sizeof (cairo_image_surface_t)); + if (unlikely (source == NULL)) { + XRenderFreePicture (dst->display->display, picture); + 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->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_surface_t *image; + cairo_status_t status; + + 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 None; + } + + image = cairo_surface_map_to_image (&src->base, NULL); + status = _cairo_surface_offset_paint (image, extents->x, extents->y, + CAIRO_OPERATOR_SOURCE, pattern, + NULL); + cairo_surface_unmap_image (&src->base, image); + 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); +} + +static cairo_surface_t * +color_source (cairo_xlib_surface_t *dst, const cairo_color_t *color) +{ + XRenderColor xrender_color; + + xrender_color.red = color->red_short; + xrender_color.green = color->green_short; + xrender_color.blue = color->blue_short; + xrender_color.alpha = color->alpha_short; + + return source (dst, + XRenderCreateSolidFill (dst->display->display, + &xrender_color)); +} + +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_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_xlib_surface_t *) pattern->surface; + cairo_xlib_source_t *source; + Display *dpy = dst->display->display; + cairo_bool_t is_contained = FALSE; + cairo_int_status_t status; + XTransform xtransform; + XRenderPictureAttributes pa; + int mask = 0; + + src = (cairo_xlib_surface_t *) + unwrap_surface (pattern->surface, src_x, src_y); + + if (sample->x >= 0 && sample->y >= 0 && + sample->x + sample->width <= src->width && + sample->y + sample->height <= src->height) + { + is_contained = TRUE; + } + + if (pattern->base.filter == CAIRO_FILTER_NEAREST && is_contained) { + *src_x += pattern->base.matrix.x0; + *src_y += pattern->base.matrix.y0; + _cairo_xlib_surface_ensure_picture (src); + return cairo_surface_reference (&src->base); + } + + /* As these are frequent and meant to be fast we track pictures for + * enative surface and minimse update requests. + */ + source = &src->embedded_source; + if (source->picture == None) { + Picture picture; + XRenderPictureAttributes pa; + int mask = 0; + + pa.subwindow_mode = IncludeInferiors; + mask |= CPSubwindowMode; + + picture = XRenderCreatePicture (dpy, + src->drawable, + src->xrender_format, + mask, &pa); + + _cairo_surface_init (&source->base, + &cairo_xlib_source_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + source->picture = picture; + + 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, 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 cairo_surface_reference (&source->base); +} + +#if 0 +/* It is general quicker if we let the application choose which images + * to cache for itself and only upload the fragments required for this + * operation. + */ +static cairo_surface_t * +image_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_image_surface_t *src = (cairo_image_surface_t *) pattern->surface; + cairo_xlib_surface_t *snapshot; + cairo_surface_pattern_t local_pattern; + cairo_status_t status; + + snapshot = (cairo_xlib_surface_t *) + _cairo_surface_has_snapshot (&src->base, dst->base.backend); + if (snapshot == NULL || snapshot->screen != dst->screen) { + if (snapshot) + _cairo_surface_detach_snapshot (&snapshot->base); + + snapshot = (cairo_xlib_surface_t *) + _cairo_surface_create_similar_scratch (&dst->base, + src->base.content, + src->width, + src->height); + if (snapshot->base.type != CAIRO_SURFACE_TYPE_XLIB) { + cairo_surface_destroy (&snapshot->base); + return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY); + } + + status = _cairo_xlib_surface_draw_image (snapshot, src, + 0, 0, + src->width, src->height, + 0, 0); + if (unlikely (status)) { + cairo_surface_destroy (&snapshot->base); + return _cairo_surface_create_in_error (status); + } + + _cairo_surface_attach_snapshot (&src->base, + &snapshot->base, + cairo_surface_finish); + + /* reference remains held by the snapshot from image */ + cairo_surface_destroy (&snapshot->base); + } + + local_pattern = *pattern; + local_pattern.surface = &snapshot->base; + + return native_source (dst, &local_pattern, extents, src_x, src_y); +} +#endif + +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_xlib_surface_t *src; + cairo_surface_t *image; + cairo_surface_pattern_t local_pattern; + cairo_status_t status; + cairo_rectangle_int_t upload, limit; + cairo_matrix_t m; + + 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_pattern_init_for_surface (&local_pattern, pattern->surface); + cairo_matrix_init_translate (&local_pattern.base.matrix, + upload.x, upload.y); + + image = cairo_surface_map_to_image (&src->base, NULL); + status = _cairo_surface_paint (image, + CAIRO_OPERATOR_SOURCE, + &local_pattern.base, + NULL); + cairo_surface_unmap_image (&src->base, image); + _cairo_pattern_fini (&local_pattern.base); + + if (unlikely (status)) { + cairo_surface_destroy (&src->base); + return _cairo_surface_create_in_error (status); + } + + local_pattern.base.matrix = pattern->base.matrix; + if (upload.x | upload.y) { + 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 (src); + if (! picture_set_properties (src->display, + src->picture, + &pattern->base, + &local_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_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; + int tx, ty; + + if (pattern == NULL || pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + *src_x = *src_y = 0; + + 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, + (cairo_xlib_surface_t *)unwrap_surface (spattern->surface, &tx, &ty))) + 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); +#if 0 + if (spattern->surface->type == CAIRO_SURFACE_TYPE_IMAGE) + return image_source (dst, spattern, extents, src_x, src_y); +#endif + + 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 */ diff --git a/src/cairo-xlib-surface-private.h b/src/cairo-xlib-surface-private.h index b56b245c7..87db6962b 100644 --- a/src/cairo-xlib-surface-private.h +++ b/src/cairo-xlib-surface-private.h @@ -38,76 +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; - cairo_list_t link; - - 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 */ diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index e82de6505..1806e64b2 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -51,6 +51,8 @@ #include "cairo-xlib-private.h" #include "cairo-xlib-surface-private.h" + +#include "cairo-compositor-private.h" #include "cairo-clip-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" @@ -64,7 +66,6 @@ #include /* for XDestroyImage */ #define XLIB_COORD_MAX 32767 -#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ #define DEBUG 0 @@ -169,16 +170,6 @@ _cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, static cairo_bool_t _cairo_surface_is_xlib (cairo_surface_t *surface); -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *remaining_glyphs); - /* * Instead of taking two round trips for each blending request, * assume that if a particular drawable fails GetImage that it will @@ -193,37 +184,6 @@ static const XTransform identity = { { { 0x00000, 0x00000, 1 << 16 }, } }; -#define CAIRO_SURFACE_RENDER_AT_LEAST(surface, major, minor) \ - (((surface)->render_major > major) || \ - (((surface)->render_major == major) && ((surface)->render_minor >= minor))) - -#define CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) -#define CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 0) - -#define CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 1) - -#define CAIRO_SURFACE_RENDER_HAS_DISJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) -#define CAIRO_SURFACE_RENDER_HAS_CONJOINT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 2) - -#define CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIANGLES(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRISTRIP(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) -#define CAIRO_SURFACE_RENDER_HAS_TRIFAN(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 4) - -#define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) -#define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6) - -#define CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) -#define CAIRO_SURFACE_RENDER_HAS_GRADIENTS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 10) - -#define CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 11) - -#define CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR(surface, op) \ - ((op) <= CAIRO_OPERATOR_SATURATE || \ - (CAIRO_SURFACE_RENDER_HAS_PDF_OPERATORS(surface) && \ - (op) <= CAIRO_OPERATOR_HSL_LUMINOSITY)) - static Visual * _visual_for_xrender_format(Screen *screen, XRenderPictFormat *xrender_format) @@ -274,62 +234,6 @@ _visual_for_xrender_format(Screen *screen, return NULL; } -static cairo_status_t -_cairo_xlib_surface_set_clip_region (cairo_xlib_surface_t *surface, - cairo_region_t *region) -{ - cairo_bool_t had_clip_rects = surface->clip_region != NULL; - - if (had_clip_rects == FALSE && region == NULL) - return CAIRO_STATUS_SUCCESS; - - if (surface->clip_region == region) - return CAIRO_STATUS_SUCCESS; - - if (cairo_region_equal (surface->clip_region, region)) - return CAIRO_STATUS_SUCCESS; - - cairo_region_destroy (surface->clip_region); - surface->clip_region = cairo_region_reference (region); - - if (surface->clip_rects != surface->embedded_clip_rects) { - free (surface->clip_rects); - surface->clip_rects = surface->embedded_clip_rects; - } - surface->num_clip_rects = 0; - - if (region != NULL) { - XRectangle *rects = NULL; - int n_rects, i; - - n_rects = cairo_region_num_rectangles (region); - if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); - if (unlikely (rects == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } else { - rects = surface->embedded_clip_rects; - } - - for (i = 0; i < n_rects; i++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (region, i, &rect); - - rects[i].x = rect.x; - rects[i].y = rect.y; - rects[i].width = rect.width; - rects[i].height = rect.height; - } - - surface->clip_rects = rects; - surface->num_clip_rects = n_rects; - } - - surface->clip_dirty = CAIRO_XLIB_SURFACE_CLIP_DIRTY_ALL; - return CAIRO_STATUS_SUCCESS; -} - static cairo_content_t _xrender_format_to_content (XRenderPictFormat *xrender_format) { @@ -373,9 +277,6 @@ _cairo_xlib_surface_create_similar (void *abstract_src, if (width == 0 || height == 0) return NULL; - if (! CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (src)) - return NULL; - if (_cairo_xlib_display_acquire (src->base.device, &display)) return NULL; @@ -384,21 +285,25 @@ _cairo_xlib_surface_create_similar (void *abstract_src, * constructing a cairo_format_t instead, (which will fairly * arbitrarily pick a visual/depth for the similar surface. */ - xrender_format = src->xrender_format; - if ((xrender_format != NULL && - _xrender_format_to_content (xrender_format) == content) || - (xrender_format = - _cairo_xlib_display_get_xrender_format (display, - _cairo_format_from_content (content)))) + 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 <= 0 ? 1 : width, height <= 0 ? 1 : height, - xrender_format->depth); + width, height, xrender_format->depth); if (xrender_format == src->xrender_format) visual = src->visual; @@ -407,15 +312,13 @@ _cairo_xlib_surface_create_similar (void *abstract_src, xrender_format); surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (src->screen, pix, - visual, + _cairo_xlib_surface_create_internal (src->screen, pix, visual, xrender_format, width, height, xrender_format->depth); } else { -#ifdef DEBUG_FORCE_FALLBACKS Screen *screen = src->screen->screen; int depth; @@ -437,20 +340,12 @@ _cairo_xlib_surface_create_similar (void *abstract_src, DefaultVisualOfScreen (screen), NULL, width, height, depth); -#else - /* No compatabile XRenderFormat, just say no. */ - cairo_device_release (&display->base); - return NULL; -#endif } - if (unlikely (surface->base.status)) { + 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; - } - - surface->owns_pixmap = TRUE; cairo_device_release (&display->base); @@ -472,53 +367,19 @@ _cairo_xlib_surface_finish (void *abstract_surface) if (unlikely (status)) return status; - if (surface->owns_pixmap) { - cairo_status_t status2; - - if (surface->dst_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->dst_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - if (surface->src_picture != None) { - status2 = _cairo_xlib_display_queue_resource (display, - XRenderFreePicture, - surface->src_picture); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } - - status2 = _cairo_xlib_display_queue_resource (display, - (cairo_xlib_notify_resource_func) XFreePixmap, - surface->drawable); - if (status == CAIRO_STATUS_SUCCESS) - status = status2; - } else { - if (surface->dst_picture != None) - XRenderFreePicture (display->display, surface->dst_picture); - - if (surface->src_picture != None) - XRenderFreePicture (display->display, surface->src_picture); - } - - if (surface->clip_rects != surface->embedded_clip_rects) - free (surface->clip_rects); - - if (display->display != NULL) - _cairo_xlib_remove_close_display_hook (display, - &surface->close_display_hook); + if (surface->embedded_source.picture) + XRenderFreePicture (display->display, surface->embedded_source.picture); + if (surface->picture) + XRenderFreePicture (display->display, surface->picture); + if (surface->owns_pixmap) + XFreePixmap (display->display, surface->drawable); cairo_device_release (&display->base); - cairo_region_destroy (surface->clip_region); - return status; } -static cairo_status_t +cairo_status_t _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, GC *gc) @@ -533,17 +394,6 @@ _cairo_xlib_surface_get_gc (cairo_xlib_display_t *display, return CAIRO_STATUS_SUCCESS; } -static void -_cairo_xlib_surface_put_gc (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - GC gc) -{ - _cairo_xlib_screen_put_gc (display, - surface->screen, - surface->depth, - gc); -} - static int _noop_error_handler (Display *display, XErrorEvent *event) @@ -714,7 +564,6 @@ _characterize_field (uint32_t mask, int *width, int *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 @@ -806,7 +655,6 @@ _pseudocolor_to_rgb888 (cairo_xlib_visual_info_t *visual_info, (b ); } - /* should range from -128 to 127 */ #define X 16 static const int8_t dither_pattern[4][4] = { @@ -817,7 +665,6 @@ static const int8_t dither_pattern[4][4] = { }; #undef X - static cairo_surface_t * _get_image_surface (cairo_xlib_surface_t *surface, const cairo_rectangle_int_t *extents) @@ -834,6 +681,20 @@ _get_image_surface (cairo_xlib_surface_t *surface, assert (extents->x + extents->width <= surface->width); assert (extents->y + extents->height <= surface->height); + if (surface->base.serial == 0) { + xlib_masks.bpp = (surface->depth + 7) & ~7; + xlib_masks.alpha_mask = surface->a_mask; + xlib_masks.red_mask = surface->r_mask; + xlib_masks.green_mask = surface->g_mask; + xlib_masks.blue_mask = surface->b_mask; + _pixman_format_from_masks (&xlib_masks, &pixman_format); + return _cairo_image_surface_create_with_pixman_format (NULL, + pixman_format, + extents->width, + extents->height, + 0); + } + status = _cairo_xlib_display_acquire (surface->base.device, &display); if (status) return _cairo_surface_create_in_error (status); @@ -1046,48 +907,11 @@ _get_image_surface (cairo_xlib_surface_t *surface, return &image->base; } -static void -_cairo_xlib_surface_ensure_src_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (!surface->src_picture) { - XRenderPictureAttributes pa; - int mask = 0; - - pa.subwindow_mode = IncludeInferiors; - mask |= CPSubwindowMode; - - surface->src_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - mask, &pa); - } -} - -static void -_cairo_xlib_surface_set_picture_clip_rects (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) -{ - if (surface->clip_region != NULL) { - XRenderSetPictureClipRectangles (display->display, surface->dst_picture, - 0, 0, - surface->clip_rects, - surface->num_clip_rects); - } else { - XRenderPictureAttributes pa; - pa.clip_mask = None; - XRenderChangePicture (display->display, surface->dst_picture, - CPClipMask, &pa); - } - - surface->clip_dirty &= ~CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE; -} - -static void -_cairo_xlib_surface_set_precision (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, +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) @@ -1111,37 +935,46 @@ _cairo_xlib_surface_set_precision (cairo_xlib_display_t *display, XRenderPictureAttributes pa; pa.poly_mode = precision; - XRenderChangePicture (display->display, surface->dst_picture, + XRenderChangePicture (display->display, surface->picture, CPPolyMode, &pa); surface->precision = precision; } } -static void -_cairo_xlib_surface_ensure_dst_picture (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface) +void +_cairo_xlib_surface_ensure_picture (cairo_xlib_surface_t *surface) { - if (!surface->dst_picture) { - surface->dst_picture = XRenderCreatePicture (display->display, - surface->drawable, - surface->xrender_format, - 0, NULL); - } + cairo_xlib_display_t *display = surface->display; + XRenderPictureAttributes pa; + int mask = 0; - if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_PICTURE) - _cairo_xlib_surface_set_picture_clip_rects (display, surface); -} + if (surface->picture) + return; -static cairo_status_t -_draw_image_surface (cairo_xlib_surface_t *surface, - cairo_image_surface_t *image, - int src_x, - int src_y, - int width, - int height, - int dst_x, - int dst_y) + 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; @@ -1400,7 +1233,7 @@ _cairo_xlib_surface_snapshot (void *abstract_surface) extents.width = surface->width; extents.height = surface->height; - /* XXX notice the duplication */ + /* XXX notice the duplication with acquire source */ return _get_image_surface (surface, &extents); } @@ -1428,3465 +1261,778 @@ static cairo_int_status_t _cairo_xlib_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { - return _draw_image_surface (abstract_surface, image, - 0, 0, - image->width, image->height, - image->base.device_transform_inverse.x0, - image->base.device_transform_inverse.y0); + return _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); } -static cairo_status_t -_cairo_xlib_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t **image_out, - cairo_rectangle_int_t *image_rect_out, - void **image_extra) +static cairo_bool_t +_cairo_xlib_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) { - if (image_rect_out) - *image_rect_out = *interest_rect; - *image_extra = NULL; - *image_out = (cairo_image_surface_t *) - _get_image_surface (abstract_surface, interest_rect); - return (*image_out)->base.status; + 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_release_dest_image (void *abstract_surface, - cairo_rectangle_int_t *interest_rect, - cairo_image_surface_t *image, - cairo_rectangle_int_t *image_rect, - void *image_extra) +_cairo_xlib_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) { cairo_xlib_surface_t *surface = abstract_surface; - cairo_status_t status; - status = _draw_image_surface (surface, image, - 0, 0, image->width, image->height, - image_rect->x, image_rect->y); - status = _cairo_surface_set_error (&surface->base, status); + *options = *_cairo_xlib_screen_get_font_options (surface->screen); +} - cairo_surface_destroy (&image->base); + +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; + return _cairo_compositor_paint (surface->compositor, + &surface->base, op, source, + clip); } -/* - * 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) +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) { - return dst->screen == src->screen; + cairo_xlib_surface_t *surface = _surface; + return _cairo_compositor_mask (surface->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; + return _cairo_compositor_stroke (surface->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; + return _cairo_compositor_fill (surface->compositor, + &surface->base, op, source, + path, fill_rule, tolerance, antialias, + clip); } -static cairo_xlib_surface_t * -_cairo_xlib_surface_create_similar_for_image(cairo_xlib_surface_t *other, - cairo_image_surface_t *image, - int width, int height) +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) { - XRenderPictFormat *xrender_format; - cairo_xlib_display_t *display; - cairo_xlib_surface_t *surface; - cairo_status_t status; - Visual *visual; - Pixmap pix; + cairo_xlib_surface_t *surface = _surface; + return _cairo_compositor_glyphs (surface->compositor, + &surface->base, op, source, + glyphs, num_glyphs, scaled_font, + clip); +} - /* Try to create a surface using an xrender format that exactly matches - * the image data so that we can upload the pixels and perform any - * conversion on the GPU. - */ +static const cairo_surface_backend_t cairo_xlib_surface_backend = { + CAIRO_SURFACE_TYPE_XLIB, + _cairo_xlib_surface_finish, - if (!CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (other)) { -fallback: - return (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_similar (other, - image->base.content, - width, height); - } + _cairo_default_context_create, - status = _cairo_xlib_display_acquire (other->base.device, &display); - if (unlikely (status)) - return (cairo_xlib_surface_t *) _cairo_surface_create_in_error(status); + _cairo_xlib_surface_create_similar, + NULL, //_cairo_xlib_surface_create_similar_image, /* XXX shm */ + _cairo_xlib_surface_map_to_image, + _cairo_xlib_surface_unmap_image, - if (image->format != CAIRO_FORMAT_INVALID) - xrender_format = - _cairo_xlib_display_get_xrender_format (display, image->format); - else - xrender_format = - _cairo_xlib_display_get_xrender_format_for_pixman (display, - image->pixman_format); - if (xrender_format == NULL) { - cairo_device_release (&display->base); - goto fallback; - } + _cairo_xlib_surface_acquire_source_image, + _cairo_xlib_surface_release_source_image, + _cairo_xlib_surface_snapshot, - pix = XCreatePixmap (display->display, other->drawable, - width, height, xrender_format->depth); + NULL, /* copy_page */ + NULL, /* show_page */ - if (xrender_format == other->xrender_format) - visual = other->visual; - else - visual = _visual_for_xrender_format(other->screen->screen, - xrender_format); + _cairo_xlib_surface_get_extents, + _cairo_xlib_surface_get_font_options, - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->screen, pix, visual, - xrender_format, - width, height, - xrender_format->depth); + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ - if (unlikely (surface->base.status)) { - XFreePixmap (display->display, pix); - cairo_device_release (&display->base); - return surface; - } + _cairo_xlib_surface_paint, + _cairo_xlib_surface_mask, + _cairo_xlib_surface_stroke, + _cairo_xlib_surface_fill, + NULL, /* fill-stroke */ + _cairo_xlib_surface_glyphs, +}; - surface->owns_pixmap = TRUE; - cairo_device_release (&display->base); - return surface; +/** + * _cairo_surface_is_xlib: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a #cairo_xlib_surface_t + * + * Return value: True if the surface is an xlib surface + **/ +static cairo_bool_t +_cairo_surface_is_xlib (cairo_surface_t *surface) +{ + return surface->backend == &cairo_xlib_surface_backend; } -static cairo_status_t -_cairo_xlib_surface_clone_similar (void *abstract_surface, - cairo_surface_t *src, - int src_x, - int src_y, - int width, - int height, - int *clone_offset_x, - int *clone_offset_y, - cairo_surface_t **clone_out) +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 = abstract_surface; - cairo_xlib_surface_t *clone; + cairo_xlib_surface_t *surface; + cairo_xlib_display_t *display; cairo_status_t status; - if (src->backend == surface->base.backend ) { - cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src; + 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 (_cairo_xlib_surface_same_screen (surface, xlib_src)) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); + if (visual == DefaultVisualOfScreen (scr)) { + depth = DefaultDepthOfScreen (scr); + } else { + int j, k; - return CAIRO_STATUS_SUCCESS; - } - } else if (_cairo_surface_is_image (src)) { - cairo_image_surface_t *image_src = (cairo_image_surface_t *)src; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX) - return UNSUPPORTED ("roi too large for xlib"); - - clone = _cairo_xlib_surface_create_similar_for_image(surface, - image_src, - width, height); - if (clone == NULL) - return UNSUPPORTED ("unhandled image format, no similar surface"); - - if (unlikely (clone->base.status)) - return clone->base.status; - - status = _draw_image_surface (clone, image_src, - src_x, src_y, - width, height, - 0, 0); - if (unlikely (status)) { - cairo_surface_destroy (&clone->base); - return status; + /* 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; + } + } + } + } } - *clone_offset_x = src_x; - *clone_offset_y = src_y; - *clone_out = &clone->base; + if (depth == 0) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - return CAIRO_STATUS_SUCCESS; +found: + ; } - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_surface_t * -_cairo_xlib_surface_create_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - /* This function's only responsibility is to create a proper surface - * for when XRender is not available. The proper surface is a xlib - * surface (as opposed to image surface which is what create_similar - * returns in those cases) and the size of the dithering pattern, not - * 1x1. This surface can then be used in - * _cairo_xlib_surface_solid_fill_rectangles() to do dithered "solid" - * fills using core protocol */ - - cairo_xlib_surface_t *other = abstract_surface; - cairo_image_surface_t *image; - cairo_xlib_surface_t *surface = NULL; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_display_t *display; - - int width = ARRAY_LENGTH (dither_pattern[0]); - int height = ARRAY_LENGTH (dither_pattern); - - Pixmap pixmap = None; - - if (CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other)) - return NULL; + surface = malloc (sizeof (cairo_xlib_surface_t)); + if (unlikely (surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - image = (cairo_image_surface_t *) - _cairo_image_surface_create_with_content (_cairo_color_get_content (&solid_pattern->color), - width, height); - status = image->base.status; - if (unlikely (status)) - goto BAIL; + status = _cairo_xlib_display_acquire (screen->device, &display); + if (unlikely (status)) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (status)); + } - status = _cairo_xlib_display_acquire (other->base.device, &display); - if (unlikely (status)) - goto BAIL; + 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); + } + } + } - pixmap = XCreatePixmap (display->display, - other->drawable, - width, height, - other->depth); cairo_device_release (&display->base); - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (other->screen, - pixmap, - other->visual, - other->xrender_format, - width, height, - other->depth); - status = surface->base.status; - if (unlikely (status)) - goto BAIL; - - status = _cairo_surface_paint (&image->base, - CAIRO_OPERATOR_SOURCE, - &solid_pattern->base, - NULL); - if (unlikely (status)) - goto BAIL; - - status = _draw_image_surface (surface, image, - 0, 0, - width, height, - 0, 0); - if (unlikely (status)) - goto BAIL; - - BAIL: - cairo_surface_destroy (&image->base); + _cairo_surface_init (&surface->base, + &cairo_xlib_surface_backend, + screen->device, + _xrender_format_to_content (xrender_format)); - if (status) { - if (pixmap != None) { - if (!_cairo_xlib_display_acquire (other->base.device, &display)) { - XFreePixmap (display->display, pixmap); - cairo_device_release (&display->base); - } - } - cairo_surface_destroy (&surface->base); + surface->screen = screen; + surface->compositor = display->compositor; - return _cairo_surface_create_in_error (status); - } + surface->drawable = drawable; + surface->owns_pixmap = FALSE; + surface->use_pixmap = 0; + surface->width = width; + surface->height = height; - surface->owns_pixmap = TRUE; - return &surface->base; -} + surface->picture = None; + surface->precision = PolyModePrecise; -static cairo_bool_t -_cairo_xlib_surface_can_repaint_solid_pattern_surface (void *abstract_surface, - const cairo_solid_pattern_t *solid_pattern) -{ - cairo_xlib_surface_t *other = abstract_surface; - return CAIRO_SURFACE_RENDER_HAS_COMPOSITE (other); -} + surface->embedded_source.picture = None; -static cairo_int_status_t -_cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - 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; + surface->visual = visual; + surface->xrender_format = xrender_format; + surface->depth = depth; - /* Casting between pixman_transform_t and XTransform is safe because - * they happen to be the exact same type. + /* + * 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. */ - 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) - status = CAIRO_INT_STATUS_SUCCESS; - if (unlikely (status != CAIRO_INT_STATUS_SUCCESS)) - return status; - - if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) - return CAIRO_STATUS_SUCCESS; - - if (! CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface)) - return UNSUPPORTED ("XRender does not support picture transforms"); - - XRenderSetPictureTransform (display->display, surface->src_picture, &xtransform); - surface->xtransform = xtransform; - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_cairo_xlib_surface_set_filter (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - cairo_filter_t filter) -{ - const char *render_filter; - - if (surface->filter == filter) - return CAIRO_STATUS_SUCCESS; - - if (!CAIRO_SURFACE_RENDER_HAS_FILTERS (surface)) { - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - return CAIRO_STATUS_SUCCESS; - - return UNSUPPORTED ("XRender does not support filter"); - } - - switch (filter) { - case CAIRO_FILTER_FAST: - render_filter = FilterFast; - break; - case CAIRO_FILTER_GOOD: - render_filter = FilterGood; - break; - case CAIRO_FILTER_BEST: - render_filter = FilterBest; - break; - case CAIRO_FILTER_NEAREST: - render_filter = FilterNearest; - break; - case CAIRO_FILTER_BILINEAR: - render_filter = FilterBilinear; - break; - case CAIRO_FILTER_GAUSSIAN: - /* XXX: The GAUSSIAN value has no implementation in cairo - * whatsoever, so it was really a mistake to have it in the - * API. We could fix this by officially deprecating it, or - * else inventing semantics and providing an actual - * implementation for it. */ - default: - render_filter = FilterBest; - break; + 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; } - XRenderSetPictureFilter (display->display, surface->src_picture, - (char *) render_filter, NULL, 0); - surface->filter = filter; + cairo_list_add (&surface->link, &screen->surfaces); - return CAIRO_STATUS_SUCCESS; + return &surface->base; } -static cairo_status_t -_cairo_xlib_surface_set_repeat (cairo_xlib_surface_t *surface, - cairo_extend_t extend, - unsigned long *mask, - XRenderPictureAttributes *pa) +static Screen * +_cairo_xlib_screen_from_visual (Display *dpy, Visual *visual) { - int repeat; + int s, d, v; - if (surface->extend == extend) - return CAIRO_STATUS_SUCCESS; + for (s = 0; s < ScreenCount (dpy); s++) { + Screen *screen; - switch (extend) { - case CAIRO_EXTEND_NONE: - repeat = RepeatNone; - break; - case CAIRO_EXTEND_REPEAT: - repeat = RepeatNormal; - break; - case CAIRO_EXTEND_REFLECT: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy reflect"); + screen = ScreenOfDisplay (dpy, s); + if (visual == DefaultVisualOfScreen (screen)) + return screen; - repeat = RepeatReflect; - break; - case CAIRO_EXTEND_PAD: - if (surface->buggy_pad_reflect) - return UNSUPPORTED ("buggy pad"); + for (d = 0; d < screen->ndepths; d++) { + Depth *depth; - repeat = RepeatPad; - break; - default: - ASSERT_NOT_REACHED; - return CAIRO_INT_STATUS_UNSUPPORTED; + depth = &screen->depths[d]; + for (v = 0; v < depth->nvisuals; v++) + if (visual == &depth->visuals[v]) + return screen; + } } - *mask |= CPRepeat; - pa->repeat = repeat; - - surface->extend = extend; - return CAIRO_STATUS_SUCCESS; + return NULL; } -static cairo_status_t -_cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, - cairo_bool_t ca, - unsigned long *mask, - XRenderPictureAttributes *pa) +static cairo_bool_t valid_size (int width, int height) { - if (surface->has_component_alpha == ca) - return CAIRO_STATUS_SUCCESS; - - *mask |= CPComponentAlpha; - pa->component_alpha = ca; - - surface->has_component_alpha = ca; - return CAIRO_STATUS_SUCCESS; + return width > 0 && width <= XLIB_COORD_MAX && height > 0 && height <= XLIB_COORD_MAX; } -static 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_xlib_surface_create: + * @dpy: an X Display + * @drawable: an X Drawable, (a Pixmap or a Window) + * @visual: the visual to use for drawing to @drawable. The depth + * of the visual must match the depth of the drawable. + * Currently, only TrueColor visuals are fully supported. + * @width: the current width of @drawable. + * @height: the current height of @drawable. + * + * Creates an Xlib surface that draws to the given drawable. + * The way that colors are represented in the drawable is specified + * by the provided visual. + * + * Note: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size() must be called whenever the size of the + * window changes. + * + * When @drawable is a Window containing child windows then drawing to + * the created surface will be clipped by those child windows. When + * the created surface is used as a source, the contents of the + * children will be included. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create (Display *dpy, + Drawable drawable, + Visual *visual, + int width, + int height) { - cairo_int_status_t status; - XRenderPictureAttributes pa; - unsigned long mask = 0; - - _cairo_xlib_surface_ensure_src_picture (display, surface); - - status = _cairo_xlib_surface_set_matrix (display, surface, - &attributes->matrix, - attributes->filter, - xc, yc, - &attributes->x_offset, - &attributes->y_offset); - if (unlikely (status)) - return status; + Screen *scr; + cairo_xlib_screen_t *screen; + cairo_status_t status; - status = _cairo_xlib_surface_set_repeat (surface, attributes->extend, - &mask, &pa); - if (unlikely (status)) - return 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)); + } - status = _cairo_xlib_surface_set_component_alpha (surface, - attributes->has_component_alpha, - &mask, &pa); - if (unlikely (status)) - return status; + 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_surface_set_filter (display, surface, attributes->filter); + status = _cairo_xlib_screen_get (dpy, scr, &screen); if (unlikely (status)) - return status; + return _cairo_surface_create_in_error (status); - if (mask) - XRenderChangePicture (display->display, surface->src_picture, mask, &pa); + X_DEBUG ((dpy, "create (drawable=%x)", (unsigned int) drawable)); - return CAIRO_STATUS_SUCCESS; + return _cairo_xlib_surface_create_internal (screen, drawable, + visual, NULL, + width, height, 0); } -/* Checks whether we can can directly draw from src to dst with - * the core protocol: either with CopyArea or using src as a - * a tile in a GC. - */ -static cairo_bool_t -_surfaces_compatible (cairo_xlib_surface_t *dst, - cairo_xlib_surface_t *src) +/** + * cairo_xlib_surface_create_for_bitmap: + * @dpy: an X Display + * @bitmap: an X Drawable, (a depth-1 Pixmap) + * @screen: the X Screen associated with @bitmap + * @width: the current width of @bitmap. + * @height: the current height of @bitmap. + * + * Creates an Xlib surface that draws to the given bitmap. + * This will be drawn to as a %CAIRO_FORMAT_A1 object. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_for_bitmap (Display *dpy, + Pixmap bitmap, + Screen *scr, + int width, + int height) { - /* same screen */ - if (! _cairo_xlib_surface_same_screen (dst, src)) - return FALSE; - - /* same depth (for core) */ - if (src->depth != dst->depth) - return FALSE; - - /* if Render is supported, match picture formats */ - if (src->xrender_format != dst->xrender_format) - return FALSE; - else if (src->xrender_format != NULL) - return TRUE; - - /* Without Render, match visuals instead */ - if (src->visual == dst->visual) - return TRUE; + cairo_xlib_screen_t *screen; + cairo_status_t status; - return FALSE; -} + if (! valid_size (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); -static cairo_bool_t -_surface_has_alpha (cairo_xlib_surface_t *surface) -{ - if (surface->xrender_format) { - if (surface->xrender_format->type == PictTypeDirect && - surface->xrender_format->direct.alphaMask != 0) - return TRUE; - else - return FALSE; - } else { - /* In the no-render case, we never have alpha */ - return FALSE; - } -} + status = _cairo_xlib_screen_get (dpy, scr, &screen); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); -/* Returns true if the given operator and source-alpha combination - * requires alpha compositing to complete. - */ -static cairo_bool_t -_operator_needs_alpha_composite (cairo_operator_t op, - cairo_bool_t destination_has_alpha, - cairo_bool_t source_has_alpha) -{ - if (op == CAIRO_OPERATOR_SOURCE || - (! source_has_alpha && - (op == CAIRO_OPERATOR_OVER || - op == CAIRO_OPERATOR_ATOP || - op == CAIRO_OPERATOR_IN))) - return destination_has_alpha; + X_DEBUG ((dpy, "create_for_bitmap (drawable=%x)", (unsigned int) bitmap)); - return TRUE; + return _cairo_xlib_surface_create_internal (screen, bitmap, + NULL, NULL, + width, height, 1); } -/* There is a bug in most older X servers with compositing using a - * untransformed repeating source pattern when the source is in off-screen - * video memory, and another with repeated transformed images using a - * general transform matrix. When these bugs could be triggered, we need a - * fallback: in the common case where we have no transformation and the - * source and destination have the same format/visual, we can do the - * operation using the core protocol for the first bug, otherwise, we need - * a software fallback. +#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. * - * We can also often optimize a compositing operation by calling XCopyArea - * for some common cases where there is no alpha compositing to be done. - * We figure that out here as well. - */ -typedef enum { - DO_RENDER, /* use render */ - DO_XCOPYAREA, /* core protocol XCopyArea optimization/fallback */ - DO_XTILE, /* core protocol XSetTile optimization/fallback */ - DO_UNSUPPORTED /* software fallback */ -} composite_operation_t; - -/* Initial check for the render bugs; we need to recheck for the - * offscreen-memory bug after we turn patterns into surfaces, since that - * may introduce a repeating pattern for gradient patterns. We don't need - * to check for the repeat+transform bug because gradient surfaces aren't - * transformed. + * 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. * - * All we do here is reject cases where we *know* are going to - * hit the bug and won't be able to use a core protocol fallback. - */ -static composite_operation_t -_categorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_bool_t have_mask) - + * Note: If @drawable is a Window, then the function + * cairo_xlib_surface_set_size() must be called whenever the size of the + * window changes. + * + * Return value: the newly created surface + **/ +cairo_surface_t * +cairo_xlib_surface_create_with_xrender_format (Display *dpy, + Drawable drawable, + Screen *scr, + XRenderPictFormat *format, + int width, + int height) { - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (dst, op)) - return DO_UNSUPPORTED; + cairo_xlib_screen_t *screen; + cairo_status_t status; - if (! dst->buggy_repeat) - return DO_RENDER; - - if (src_pattern->type != CAIRO_PATTERN_TYPE_SOLID && - src_pattern->extend == CAIRO_EXTEND_REPEAT) - { - /* Check for the bug with repeat patterns nad general transforms. */ - if (! _cairo_matrix_is_integer_translation (&src_pattern->matrix, - NULL, NULL)) - { - return DO_UNSUPPORTED; - } - - if (have_mask || - !(op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER)) - { - return DO_UNSUPPORTED; - } - - if (src_pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { - cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) src_pattern; - - /* This is the case where we have the bug involving - * untransformed repeating source patterns with off-screen - * video memory; reject some cases where a core protocol - * fallback is impossible. - */ - if (_cairo_surface_is_xlib (surface_pattern->surface)) { - cairo_xlib_surface_t *src = (cairo_xlib_surface_t *) surface_pattern->surface; - - if (op == CAIRO_OPERATOR_OVER && _surface_has_alpha (src)) - return DO_UNSUPPORTED; - - /* If these are on the same screen but otherwise incompatible, - * make a copy as core drawing can't cross depths and doesn't - * work right across visuals of the same depth - */ - if (_cairo_xlib_surface_same_screen (dst, src) && - !_surfaces_compatible (dst, src)) - { - return DO_UNSUPPORTED; - } - } - } - } - - return DO_RENDER; -} - -/* Recheck for composite-repeat once we've turned patterns into Xlib surfaces - * If we end up returning DO_UNSUPPORTED here, we're throwing away work we - * did to turn gradients into a pattern, but most of the time we can handle - * that case with core protocol fallback. - * - * Also check here if we can just use XCopyArea, instead of going through - * Render. - */ -static composite_operation_t -_recategorize_composite_operation (cairo_xlib_surface_t *dst, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *src_attr, - cairo_bool_t have_mask) -{ - /* Can we use the core protocol? */ - if (! have_mask && - src->owns_pixmap && - src->depth == dst->depth && - _cairo_matrix_is_integer_translation (&src_attr->matrix, NULL, NULL) && - ! _operator_needs_alpha_composite (op, - _surface_has_alpha (dst), - _surface_has_alpha (src))) - { - if (src_attr->extend == CAIRO_EXTEND_NONE) - return DO_XCOPYAREA; - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) - return DO_XTILE; - } - - if (dst->buggy_repeat && src_attr->extend == CAIRO_EXTEND_REPEAT) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src)) - return DO_UNSUPPORTED; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return DO_UNSUPPORTED; - - return DO_RENDER; -} - -static int -_render_operator (cairo_operator_t op) -{ - switch (op) { - case CAIRO_OPERATOR_CLEAR: - return PictOpClear; - - case CAIRO_OPERATOR_SOURCE: - return PictOpSrc; - case CAIRO_OPERATOR_OVER: - return PictOpOver; - case CAIRO_OPERATOR_IN: - return PictOpIn; - case CAIRO_OPERATOR_OUT: - return PictOpOut; - case CAIRO_OPERATOR_ATOP: - return PictOpAtop; - - case CAIRO_OPERATOR_DEST: - return PictOpDst; - case CAIRO_OPERATOR_DEST_OVER: - return PictOpOverReverse; - case CAIRO_OPERATOR_DEST_IN: - return PictOpInReverse; - case CAIRO_OPERATOR_DEST_OUT: - return PictOpOutReverse; - case CAIRO_OPERATOR_DEST_ATOP: - return PictOpAtopReverse; - - case CAIRO_OPERATOR_XOR: - return PictOpXor; - case CAIRO_OPERATOR_ADD: - return PictOpAdd; - case CAIRO_OPERATOR_SATURATE: - return PictOpSaturate; - - case CAIRO_OPERATOR_MULTIPLY: - return PictOpMultiply; - case CAIRO_OPERATOR_SCREEN: - return PictOpScreen; - case CAIRO_OPERATOR_OVERLAY: - return PictOpOverlay; - case CAIRO_OPERATOR_DARKEN: - return PictOpDarken; - case CAIRO_OPERATOR_LIGHTEN: - return PictOpLighten; - case CAIRO_OPERATOR_COLOR_DODGE: - return PictOpColorDodge; - case CAIRO_OPERATOR_COLOR_BURN: - return PictOpColorBurn; - case CAIRO_OPERATOR_HARD_LIGHT: - return PictOpHardLight; - case CAIRO_OPERATOR_SOFT_LIGHT: - return PictOpSoftLight; - case CAIRO_OPERATOR_DIFFERENCE: - return PictOpDifference; - case CAIRO_OPERATOR_EXCLUSION: - return PictOpExclusion; - case CAIRO_OPERATOR_HSL_HUE: - return PictOpHSLHue; - case CAIRO_OPERATOR_HSL_SATURATION: - return PictOpHSLSaturation; - case CAIRO_OPERATOR_HSL_COLOR: - return PictOpHSLColor; - case CAIRO_OPERATOR_HSL_LUMINOSITY: - return PictOpHSLLuminosity; - - default: - ASSERT_NOT_REACHED; - return PictOpOver; - } -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surface (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *pattern, - int x, int y, - int width, int height, - cairo_xlib_surface_t **surface_out, - cairo_surface_attributes_t *attributes) -{ - switch (pattern->type) { - case CAIRO_PATTERN_TYPE_LINEAR: - case CAIRO_PATTERN_TYPE_RADIAL: - { - cairo_gradient_pattern_t *gradient = - (cairo_gradient_pattern_t *) pattern; - cairo_matrix_t matrix = pattern->matrix; - cairo_xlib_surface_t *surface; - char buf[CAIRO_STACK_BUFFER_SIZE]; - cairo_circle_double_t extremes[2]; - XFixed *stops; - XRenderColor *colors; - XRenderPictFormat *format; - Picture picture; - unsigned int i, n_stops; - - if (dst->buggy_gradients) - break; - - /* 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 *) gradient)) - break; - - 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_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 (pattern->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); - - if (unlikely (picture == None)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - /* Wrap the remote Picture in an xlib surface. */ - format = _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_ARGB32); - - surface = (cairo_xlib_surface_t *) - _cairo_xlib_surface_create_internal (dst->screen, None, - NULL, format, - /* what could possibly go wrong? */ - XLIB_COORD_MAX, XLIB_COORD_MAX, 32); - if (unlikely (surface->base.status)) { - XRenderFreePicture (display->display, picture); - return surface->base.status; - } - - surface->src_picture = picture; - - attributes->matrix = matrix; - attributes->extend = pattern->extend; - attributes->filter = CAIRO_FILTER_NEAREST; - attributes->x_offset = 0; - attributes->y_offset = 0; - attributes->has_component_alpha = FALSE; - - *surface_out = surface; - return CAIRO_STATUS_SUCCESS; - } - default: - ASSERT_NOT_REACHED; - case CAIRO_PATTERN_TYPE_SOLID: - case CAIRO_PATTERN_TYPE_SURFACE: - case CAIRO_PATTERN_TYPE_MESH: - break; - } - - return _cairo_pattern_acquire_surface (pattern, &dst->base, - x, y, width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) surface_out, - attributes); -} - -static cairo_int_status_t -_cairo_xlib_surface_acquire_pattern_surfaces (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - const cairo_pattern_t *src, - const cairo_pattern_t *mask, - int src_x, - int src_y, - int mask_x, - int mask_y, - unsigned int width, - unsigned int height, - cairo_xlib_surface_t **src_out, - cairo_xlib_surface_t **mask_out, - cairo_surface_attributes_t *src_attr, - cairo_surface_attributes_t *mask_attr) -{ - if (! dst->buggy_gradients && - (src->type == CAIRO_PATTERN_TYPE_LINEAR || - src->type == CAIRO_PATTERN_TYPE_RADIAL || - (mask && (mask->type == CAIRO_PATTERN_TYPE_LINEAR || - mask->type == CAIRO_PATTERN_TYPE_RADIAL)))) - { - cairo_int_status_t status; - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src, - src_x, src_y, - width, height, - src_out, - src_attr); - if (unlikely (status)) - return status; - - if (mask) { - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, mask, - mask_x, - mask_y, - width, - height, - mask_out, - mask_attr); - if (unlikely (status)) { - _cairo_pattern_release_surface (src, &(*src_out)->base, - src_attr); - return status; - } - } else { - *mask_out = NULL; - } - - return CAIRO_STATUS_SUCCESS; - } - - return _cairo_pattern_acquire_surfaces (src, mask, - &dst->base, - src_x, src_y, - mask_x, mask_y, - width, height, - dst->buggy_pad_reflect ? - CAIRO_PATTERN_ACQUIRE_NO_REFLECT : - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) src_out, - (cairo_surface_t **) mask_out, - src_attr, mask_attr); -} - -static cairo_int_status_t -_cairo_xlib_surface_upload(cairo_xlib_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *pattern, - int src_x, int src_y, - int dst_x, int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_image_surface_t *image; - cairo_rectangle_int_t extents; - cairo_status_t status; - int tx, ty; - - if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - image = (cairo_image_surface_t *) ((cairo_surface_pattern_t *) pattern)->surface; - if (image->base.type != CAIRO_SURFACE_TYPE_IMAGE) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (! (op == CAIRO_OPERATOR_SOURCE || - (op == CAIRO_OPERATOR_OVER && - (image->base.content & CAIRO_CONTENT_ALPHA) == 0))) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (image->base.backend->type != CAIRO_SURFACE_TYPE_IMAGE) { - if (_cairo_surface_is_snapshot (&image->base)) { - image = (cairo_image_surface_t *) ((cairo_surface_snapshot_t *) image)->target; - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } else if (image->base.backend->type == CAIRO_SURFACE_TYPE_SUBSURFACE) { - cairo_surface_subsurface_t *sub = (cairo_surface_subsurface_t *) image; - image = (cairo_image_surface_t *) sub->target; - src_x += sub->extents.x; - src_y += sub->extents.y; - extents = sub->extents; - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - } else { - extents.x = extents.y = 0; - extents.width = image->width; - extents.height = image->height; - } - - 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 (&pattern->matrix, &tx, &ty)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - src_x += tx; - src_y += ty; - - /* XXX for EXTEND_NONE perform unbounded fixups? */ - if (src_x < extents.x || - src_y < extents.y || - src_x + width > (unsigned) extents.width || - src_y + height > (unsigned) extents.height) - { - return CAIRO_INT_STATUS_UNSUPPORTED; - } - - status = cairo_device_acquire (surface->base.device); - if (unlikely (status)) - return status; - - if (clip_region != NULL) { - int n, num_rect; - - src_x -= dst_x; - src_y -= dst_y; - - num_rect = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rect; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - status = _draw_image_surface (surface, image, - rect.x + src_x, rect.y + src_y, - rect.width, rect.height, - rect.x, rect.y); - if (unlikely (status)) - break; - } - } else { - status = _draw_image_surface (surface, image, - src_x, src_y, - width, height, - dst_x, dst_y); - } - - cairo_device_release (surface->base.device); - - return status; -} - -static cairo_int_status_t -_cairo_xlib_surface_composite (cairo_operator_t op, - const cairo_pattern_t *src_pattern, - const cairo_pattern_t *mask_pattern, - void *abstract_dst, - int src_x, - int src_y, - int mask_x, - int mask_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_region_t *clip_region) -{ - cairo_surface_attributes_t src_attr, mask_attr; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_surface_t *mask; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int itx, ity; - cairo_bool_t is_integer_translation; - GC gc; - - if (mask_pattern != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst)) - return UNSUPPORTED ("no support for masks"); - - operation = _categorize_composite_operation (dst, op, src_pattern, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - X_DEBUG ((display->display, "composite (dst=%x)", (unsigned int) dst->drawable)); - - if (mask_pattern == NULL) { - /* Can we do a simple upload in-place? */ - status = _cairo_xlib_surface_upload(dst, op, src_pattern, - src_x, src_y, - dst_x, dst_y, - width, height, - clip_region); - if (status != CAIRO_INT_STATUS_UNSUPPORTED) - return status; - } - - status = _cairo_xlib_display_acquire (dst-> base.device, &display); - if (unlikely (status)) - return status; - - status = - _cairo_xlib_surface_acquire_pattern_surfaces (display, dst, - src_pattern, mask_pattern, - src_x, src_y, - mask_x, mask_y, - width, height, - &src, &mask, - &src_attr, &mask_attr); - if (unlikely (status)) - goto BAIL0; - - /* check for fallback surfaces that we cannot handle ... */ - assert (_cairo_surface_is_xlib (&src->base)); - assert (mask == NULL || _cairo_surface_is_xlib (&mask->base)); - - if (mask != NULL && ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (mask)) { - status = UNSUPPORTED ("unsupported mask"); - goto BAIL; - } - - operation = _recategorize_composite_operation (dst, op, src, &src_attr, - mask_pattern != NULL); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - switch (operation) - { - case DO_RENDER: - status = _cairo_xlib_surface_set_attributes (display, - src, &src_attr, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - if (mask) { - status = _cairo_xlib_surface_set_attributes (display, - mask, &mask_attr, - dst_x + width / 2., - dst_y + height/ 2.); - if (unlikely (status)) - goto BAIL; - - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - mask->src_picture, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - mask_x + mask_attr.x_offset, - mask_y + mask_attr.y_offset, - dst_x, dst_y, - width, height); - } else { - XRenderComposite (display->display, - _render_operator (op), - src->src_picture, - 0, - dst->dst_picture, - src_x + src_attr.x_offset, - src_y + src_attr.y_offset, - 0, 0, - dst_x, dst_y, - width, height); - } - - break; - - case DO_XCOPYAREA: - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XCOPYAREA. */ - assert (is_integer_translation); - - if (clip_region == NULL) { - XCopyArea (display->display, src->drawable, dst->drawable, gc, - src_x + src_attr.x_offset + itx, - src_y + src_attr.y_offset + ity, - width, height, - dst_x, dst_y); - } else { - int n, num_rects, x, y; - - x = src_x + src_attr.x_offset + itx - dst_x; - y = src_y + src_attr.y_offset + ity - dst_y; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XCopyArea (display->display, src->drawable, dst->drawable, gc, - rect.x + x, rect.y + y, - rect.width, rect.height, - rect.x, rect.y); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_XTILE: - /* This case is only used for bug fallbacks, though we also use it for - * the case where we don't have the RENDER extension, by forcing - * buggy_repeat to TRUE. - * - * We've checked that we have a repeating unscaled source in - * _recategorize_composite_operation. - */ - - status = _cairo_xlib_surface_get_gc (display, dst, &gc); - if (unlikely (status)) - goto BAIL; - - is_integer_translation = - _cairo_matrix_is_integer_translation (&src_attr.matrix, &itx, &ity); - /* This is a pre-condition for DO_XTILE. */ - assert (is_integer_translation); - - XSetTSOrigin (display->display, gc, - - (itx + src_attr.x_offset), - (ity + src_attr.y_offset)); - XSetTile (display->display, gc, src->drawable); - - if (clip_region == NULL) { - XFillRectangle (display->display, dst->drawable, gc, - dst_x, dst_y, width, height); - } else { - int n, num_rects; - - num_rects = cairo_region_num_rectangles (clip_region); - for (n = 0; n < num_rects; n++) { - cairo_rectangle_int_t rect; - - cairo_region_get_rectangle (clip_region, n, &rect); - XFillRectangle (display->display, dst->drawable, gc, - rect.x, rect.y, rect.width, rect.height); - } - } - - _cairo_xlib_surface_put_gc (display, dst, gc); - break; - - case DO_UNSUPPORTED: - default: - ASSERT_NOT_REACHED; - } - - if (!_cairo_operator_bounded_by_source (op)) - status = _cairo_surface_composite_fixup_unbounded (&dst->base, - &src_attr, src->width, src->height, - mask ? &mask_attr : NULL, - mask ? mask->width : 0, - mask ? mask->height : 0, - src_x, src_y, - mask_x, mask_y, - dst_x, dst_y, width, height, - clip_region); - - BAIL: - if (mask) - _cairo_pattern_release_surface (mask_pattern, &mask->base, &mask_attr); - - _cairo_pattern_release_surface (src_pattern, &src->base, &src_attr); - - BAIL0: - cairo_device_release (&display->base); - - return status; -} - -/* XXX move this out of core and into acquire_pattern_surface() above. */ -static cairo_int_status_t -_cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t *surface, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_status_t status; - cairo_solid_pattern_t solid; - cairo_surface_t *solid_surface = NULL; - cairo_surface_attributes_t attrs; - cairo_xlib_display_t *display; - GC gc; - int i; - - _cairo_pattern_init_solid (&solid, color); - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - status = _cairo_xlib_surface_get_gc (display, surface, &gc); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "solid_fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); - - status = _cairo_pattern_acquire_surface (&solid.base, &surface->base, - 0, 0, - ARRAY_LENGTH (dither_pattern[0]), - ARRAY_LENGTH (dither_pattern), - CAIRO_PATTERN_ACQUIRE_NONE, - &solid_surface, - &attrs); - if (unlikely (status)) { - _cairo_xlib_surface_put_gc (display, surface, gc); - cairo_device_release (&display->base); - return status; - } - - assert (_cairo_surface_is_xlib (solid_surface)); - - XSetTSOrigin (display->display, gc, - - (surface->base.device_transform.x0 + attrs.x_offset), - - (surface->base.device_transform.y0 + attrs.y_offset)); - XSetTile (display->display, gc, - ((cairo_xlib_surface_t *) solid_surface)->drawable); - - for (i = 0; i < num_rects; i++) { - XFillRectangle (display->display, surface->drawable, gc, - rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - } - - _cairo_xlib_surface_put_gc (display, surface, gc); - - _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs); - - cairo_device_release (&display->base); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_int_status_t -_cairo_xlib_surface_fill_rectangles (void *abstract_surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_rectangle_int_t *rects, - int num_rects) -{ - cairo_xlib_surface_t *surface = abstract_surface; - cairo_xlib_display_t *display; - XRenderColor render_color; - cairo_status_t status; - int i; - - if (!CAIRO_SURFACE_RENDER_SUPPORTS_OPERATOR (surface, op)) - return CAIRO_INT_STATUS_UNSUPPORTED; - - if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - if (op == CAIRO_OPERATOR_CLEAR || - ((op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_OVER) && - CAIRO_COLOR_IS_OPAQUE (color))) - { - return _cairo_xlib_surface_solid_fill_rectangles (surface, color, - rects, num_rects); - } - - return UNSUPPORTED ("no support for FillRectangles with this op"); - } - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "fill_rectangles (dst=%x)", (unsigned int) surface->drawable)); - - render_color.red = color->red_short; - render_color.green = color->green_short; - render_color.blue = color->blue_short; - render_color.alpha = color->alpha_short; - - status = _cairo_xlib_surface_set_clip_region (surface, NULL); - assert (status == CAIRO_STATUS_SUCCESS); - - _cairo_xlib_surface_ensure_dst_picture (display, surface); - if (num_rects == 1) { - /* Take advantage of the protocol compaction that libXrender performs - * to amalgamate sequences of XRenderFillRectangle(). - */ - XRenderFillRectangle (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, - rects->x, - rects->y, - rects->width, - rects->height); - } else { - XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)]; - XRectangle *xrects = static_xrects; - - if (num_rects > ARRAY_LENGTH (static_xrects)) { - xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle)); - if (unlikely (xrects == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_rects; i++) { - xrects[i].x = rects[i].x; - xrects[i].y = rects[i].y; - xrects[i].width = rects[i].width; - xrects[i].height = rects[i].height; - } - - XRenderFillRectangles (display->display, - _render_operator (op), - surface->dst_picture, - &render_color, xrects, num_rects); - - if (xrects != static_xrects) - free (xrects); - } - -BAIL: - cairo_device_release (&display->base); - return status; -} - -#define CAIRO_FIXED_16_16_MIN -32768 -#define CAIRO_FIXED_16_16_MAX 32767 - -static cairo_bool_t -_line_exceeds_16_16 (const cairo_line_t *line) -{ - return - line->p1.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.x < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.x > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p1.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p1.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX) || - line->p2.y < _cairo_fixed_from_int (CAIRO_FIXED_16_16_MIN) || - line->p2.y > _cairo_fixed_from_int (CAIRO_FIXED_16_16_MAX); -} - -static void -_project_line_x_onto_16_16 (const cairo_line_t *line, - cairo_fixed_t top, - cairo_fixed_t bottom, - XLineFixed *out) -{ - cairo_point_double_t p1, p2; - double m; - - p1.x = _cairo_fixed_to_double (line->p1.x); - p1.y = _cairo_fixed_to_double (line->p1.y); - - p2.x = _cairo_fixed_to_double (line->p2.x); - p2.y = _cairo_fixed_to_double (line->p2.y); - - m = (p2.x - p1.x) / (p2.y - p1.y); - out->p1.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (top - line->p1.y)); - out->p2.x = _cairo_fixed_16_16_from_double (p1.x + m * _cairo_fixed_to_double (bottom - line->p1.y)); -} - -static cairo_int_status_t -_cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, - const cairo_pattern_t *pattern, - void *abstract_dst, - cairo_antialias_t antialias, - int src_x, - int src_y, - int dst_x, - int dst_y, - unsigned int width, - unsigned int height, - cairo_trapezoid_t *traps, - int num_traps, - cairo_region_t *clip_region) -{ - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *dst = abstract_dst; - cairo_xlib_surface_t *src; - cairo_xlib_display_t *display; - cairo_int_status_t status; - composite_operation_t operation; - int render_reference_x, render_reference_y; - int render_src_x, render_src_y; - XRenderPictFormat *pict_format; - XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)]; - XTrapezoid *xtraps = xtraps_stack; - int i; - - if (! CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst)) - return UNSUPPORTED ("XRender does not support CompositeTrapezoids"); - - operation = _categorize_composite_operation (dst, op, pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported operation"); - - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; - - X_DEBUG ((display->display, "composite_trapezoids (dst=%x)", (unsigned int) dst->drawable)); - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, - pattern, - src_x, src_y, - width, height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported operation"); - goto BAIL; - } - - pict_format = - _cairo_xlib_display_get_xrender_format (display, - antialias == CAIRO_ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : CAIRO_FORMAT_A8); - - status = _cairo_xlib_surface_set_clip_region (dst, clip_region); - if (unlikely (status)) - goto BAIL; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - _cairo_xlib_surface_set_precision (display, dst, antialias); - - status = _cairo_xlib_surface_set_attributes (display, - src, &attributes, - dst_x + width / 2., - dst_y + height / 2.); - if (unlikely (status)) - goto BAIL; - - if (num_traps > ARRAY_LENGTH (xtraps_stack)) { - xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid)); - if (unlikely (xtraps == NULL)) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - goto BAIL; - } - } - - for (i = 0; i < num_traps; i++) { - /* top/bottom will be clamped to surface bounds */ - xtraps[i].top = _cairo_fixed_to_16_16(traps[i].top); - xtraps[i].bottom = _cairo_fixed_to_16_16(traps[i].bottom); - - /* However, all the other coordinates will have been left untouched so - * as not to introduce numerical error. Recompute them if they - * exceed the 16.16 limits. - */ - if (unlikely (_line_exceeds_16_16 (&traps[i].left))) { - _project_line_x_onto_16_16 (&traps[i].left, - traps[i].top, - traps[i].bottom, - &xtraps[i].left); - xtraps[i].left.p1.y = xtraps[i].top; - xtraps[i].left.p2.y = xtraps[i].bottom; - } else { - xtraps[i].left.p1.x = _cairo_fixed_to_16_16(traps[i].left.p1.x); - xtraps[i].left.p1.y = _cairo_fixed_to_16_16(traps[i].left.p1.y); - xtraps[i].left.p2.x = _cairo_fixed_to_16_16(traps[i].left.p2.x); - xtraps[i].left.p2.y = _cairo_fixed_to_16_16(traps[i].left.p2.y); - } - - if (unlikely (_line_exceeds_16_16 (&traps[i].right))) { - _project_line_x_onto_16_16 (&traps[i].right, - traps[i].top, - traps[i].bottom, - &xtraps[i].right); - xtraps[i].right.p1.y = xtraps[i].top; - xtraps[i].right.p2.y = xtraps[i].bottom; - } else { - xtraps[i].right.p1.x = _cairo_fixed_to_16_16(traps[i].right.p1.x); - xtraps[i].right.p1.y = _cairo_fixed_to_16_16(traps[i].right.p1.y); - xtraps[i].right.p2.x = _cairo_fixed_to_16_16(traps[i].right.p2.x); - xtraps[i].right.p2.y = _cairo_fixed_to_16_16(traps[i].right.p2.y); - } - } - - if (xtraps[0].left.p1.y < xtraps[0].left.p2.y) { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p1.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p1.y); - } else { - render_reference_x = _cairo_fixed_16_16_floor (xtraps[0].left.p2.x); - render_reference_y = _cairo_fixed_16_16_floor (xtraps[0].left.p2.y); - } - - render_src_x = src_x + render_reference_x - dst_x; - render_src_y = src_y + render_reference_y - dst_y; - - XRenderCompositeTrapezoids (display->display, - _render_operator (op), - src->src_picture, dst->dst_picture, - pict_format, - render_src_x + attributes.x_offset, - render_src_y + attributes.y_offset, - xtraps, num_traps); - - if (xtraps != xtraps_stack) - free (xtraps); - - if (! _cairo_operator_bounded_by_mask (op)) { - cairo_traps_t _traps; - cairo_box_t box; - cairo_rectangle_int_t extents; - - /* XRenderCompositeTrapezoids() creates a mask only large enough for the - * trapezoids themselves, but if the operator is unbounded, then we need - * to actually composite all the way out to the bounds. - */ - /* XXX: update the interface to pass composite rects */ - _traps.traps = traps; - _traps.num_traps = num_traps; - _cairo_traps_extents (&_traps, &box); - _cairo_box_round_to_rectangle (&box, &extents); - - status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base, - &attributes, - src->width, src->height, - extents.width, extents.height, - src_x, src_y, - -extents.x + dst_x, -extents.y + dst_y, - dst_x, dst_y, - width, height, - clip_region); - } - - BAIL: - _cairo_pattern_release_surface (pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); - - return status; -} - -static cairo_bool_t -_cairo_xlib_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - cairo_xlib_surface_t *surface = abstract_surface; - - rectangle->x = 0; - rectangle->y = 0; - - rectangle->width = surface->width; - rectangle->height = surface->height; - - return TRUE; -} - -static void -_cairo_xlib_surface_get_font_options (void *abstract_surface, - cairo_font_options_t *options) -{ - cairo_xlib_surface_t *surface = abstract_surface; - - *options = *_cairo_xlib_screen_get_font_options (surface->screen); -} - -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font); - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font); - -static cairo_bool_t -_cairo_xlib_surface_is_similar (void *surface_a, - void *surface_b) -{ - return _cairo_xlib_surface_same_screen (surface_a, surface_b); -} - -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, - NULL, //_cairo_xlib_surface_create_similar_image, /* XXX shm */ - _cairo_xlib_surface_map_to_image, - _cairo_xlib_surface_unmap_image, - - _cairo_xlib_surface_acquire_source_image, - _cairo_xlib_surface_release_source_image, - - _cairo_xlib_surface_acquire_dest_image, - _cairo_xlib_surface_release_dest_image, - _cairo_xlib_surface_clone_similar, - _cairo_xlib_surface_composite, - _cairo_xlib_surface_fill_rectangles, - _cairo_xlib_surface_composite_trapezoids, - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _cairo_xlib_surface_get_extents, - NULL, /* old_show_glyphs */ - _cairo_xlib_surface_get_font_options, - NULL, /* flush */ - NULL, /* mark_dirty_rectangle */ - _cairo_xlib_surface_scaled_font_fini, - _cairo_xlib_surface_scaled_glyph_fini, - - NULL, /* paint */ - NULL, /* mask */ - NULL, /* stroke */ - NULL, /* fill */ - _cairo_xlib_surface_show_glyphs, - - _cairo_xlib_surface_snapshot, - _cairo_xlib_surface_is_similar, - - NULL, /* fill_stroke */ - - _cairo_xlib_surface_create_solid_pattern_surface, - _cairo_xlib_surface_can_repaint_solid_pattern_surface -}; - -/** - * _cairo_surface_is_xlib: - * @surface: a #cairo_surface_t - * - * Checks if a surface is a #cairo_xlib_surface_t - * - * Return value: True if the surface is an xlib surface - **/ -static cairo_bool_t -_cairo_surface_is_xlib (cairo_surface_t *surface) -{ - return surface->backend == &cairo_xlib_surface_backend; -} - -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_detach_display (cairo_xlib_display_t *display, void *data) -{ - cairo_xlib_surface_t *surface = cairo_container_of (data, - cairo_xlib_surface_t, - close_display_hook); - Display *dpy; - - dpy = display->display; - - X_DEBUG ((dpy, "detach (drawable=%x)", (unsigned int) surface->drawable)); - - if (surface->dst_picture != None) { - XRenderFreePicture (dpy, surface->dst_picture); - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - XRenderFreePicture (dpy, surface->src_picture); - surface->src_picture = None; - } - - if (surface->owns_pixmap) { - XFreePixmap (dpy, surface->drawable); - surface->drawable = None; - surface->owns_pixmap = FALSE; - } -} - -static cairo_surface_t * -_cairo_xlib_surface_create_internal (cairo_xlib_screen_t *screen, - Drawable drawable, - Visual *visual, - XRenderPictFormat *xrender_format, - int width, - int height, - int depth) -{ - cairo_xlib_surface_t *surface; - cairo_xlib_display_t *display; - cairo_status_t status; - - if (depth == 0) { - if (xrender_format) { - depth = xrender_format->depth; - - /* XXX find matching visual for core/dithering fallbacks? */ - } else if (visual) { - Screen *scr = screen->screen; - - if (visual == DefaultVisualOfScreen (scr)) { - depth = DefaultDepthOfScreen (scr); - } else { - int j, k; - - /* This is ugly, but we have to walk over all visuals - * for the display to find the correct depth. - */ - depth = 0; - for (j = 0; j < scr->ndepths; j++) { - Depth *d = &scr->depths[j]; - for (k = 0; k < d->nvisuals; k++) { - if (&d->visuals[k] == visual) { - depth = d->depth; - goto found; - } - } - } - } - } - - if (depth == 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_VISUAL)); - -found: - ; - } - - surface = malloc (sizeof (cairo_xlib_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - status = _cairo_xlib_display_acquire (screen->device, &display); - if (unlikely (status)) { - free (surface); - return _cairo_surface_create_in_error (_cairo_error (status)); - } - - _cairo_xlib_display_get_xrender_version (display, - &surface->render_major, - &surface->render_minor); - if (CAIRO_SURFACE_RENDER_HAS_CREATE_PICTURE (surface)) { - if (!xrender_format) { - if (visual) { - xrender_format = XRenderFindVisualFormat (display->display, visual); - } else if (depth == 1) { - xrender_format = - _cairo_xlib_display_get_xrender_format (display, - CAIRO_FORMAT_A1); - } - } - } else { - /* we cannot use XRender for this surface, so ensure we don't try */ - surface->render_major = -1; - surface->render_minor = -1; - } - - /* initialize and hook into the CloseDisplay callback */ - surface->close_display_hook.func = _cairo_xlib_surface_detach_display; - _cairo_xlib_add_close_display_hook (display, - &surface->close_display_hook); - - cairo_device_release (&display->base); - - _cairo_surface_init (&surface->base, - &cairo_xlib_surface_backend, - screen->device, - _xrender_format_to_content (xrender_format)); - - surface->screen = screen; - - surface->drawable = drawable; - surface->owns_pixmap = FALSE; - surface->use_pixmap = 0; - surface->width = width; - surface->height = height; - - surface->buggy_repeat = ! _cairo_xlib_display_has_repeat (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLES (surface)) { - /* so we can use the XTile fallback */ - surface->buggy_repeat = TRUE; - } - - surface->buggy_pad_reflect = ! _cairo_xlib_display_has_reflect (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_EXTENDED_REPEAT (surface)) - surface->buggy_pad_reflect = TRUE; - - surface->buggy_gradients = ! _cairo_xlib_display_has_gradients (screen->device); - if (! CAIRO_SURFACE_RENDER_HAS_GRADIENTS (surface)) - surface->buggy_gradients = TRUE; - - surface->dst_picture = None; - surface->src_picture = None; - - surface->visual = visual; - surface->xrender_format = xrender_format; - surface->depth = depth; - surface->filter = CAIRO_FILTER_NEAREST; - surface->extend = CAIRO_EXTEND_NONE; - surface->has_component_alpha = FALSE; - surface->precision = PolyModePrecise; - surface->xtransform = identity; - - surface->clip_region = NULL; - surface->clip_rects = surface->embedded_clip_rects; - surface->num_clip_rects = 0; - surface->clip_dirty = 0; - - /* - * Compute the pixel format masks from either a XrenderFormat or - * else from a visual; failing that we assume the drawable is an - * alpha-only pixmap as it could only have been created that way - * through the cairo_xlib_surface_create_for_bitmap function. - */ - if (xrender_format) { - surface->a_mask = (unsigned long) - surface->xrender_format->direct.alphaMask - << surface->xrender_format->direct.alpha; - surface->r_mask = (unsigned long) - surface->xrender_format->direct.redMask - << surface->xrender_format->direct.red; - surface->g_mask = (unsigned long) - surface->xrender_format->direct.greenMask - << surface->xrender_format->direct.green; - surface->b_mask = (unsigned long) - surface->xrender_format->direct.blueMask - << surface->xrender_format->direct.blue; - } else if (visual) { - surface->a_mask = 0; - surface->r_mask = visual->red_mask; - surface->g_mask = visual->green_mask; - surface->b_mask = visual->blue_mask; - } else { - if (depth < 32) - surface->a_mask = (1 << depth) - 1; - else - surface->a_mask = 0xffffffff; - surface->r_mask = 0; - surface->g_mask = 0; - surface->b_mask = 0; - } - - 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; -} - -/** - * cairo_xlib_surface_create: - * @dpy: an X Display - * @drawable: an X Drawable, (a Pixmap or a Window) - * @visual: the visual to use for drawing to @drawable. The depth - * of the visual must match the depth of the drawable. - * Currently, only TrueColor visuals are fully supported. - * @width: the current width of @drawable. - * @height: the current height of @drawable. - * - * Creates an Xlib surface that draws to the given drawable. - * The way that colors are represented in the drawable is specified - * by the provided visual. - * - * Note: If @drawable is a Window, then the function - * cairo_xlib_surface_set_size() must be called whenever the size of the - * window changes. - * - * When @drawable is a Window containing child windows then drawing to - * the created surface will be clipped by those child windows. When - * the created surface is used as a source, the contents of the - * children will be included. - * - * Return value: the newly created surface - **/ -cairo_surface_t * -cairo_xlib_surface_create (Display *dpy, - Drawable drawable, - Visual *visual, - int width, - int height) -{ - Screen *scr; - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { - /* 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 - **/ -cairo_surface_t * -cairo_xlib_surface_create_for_bitmap (Display *dpy, - Pixmap bitmap, - Screen *scr, - int width, - int height) -{ - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) - 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 - **/ -cairo_surface_t * -cairo_xlib_surface_create_with_xrender_format (Display *dpy, - Drawable drawable, - Screen *scr, - XRenderPictFormat *format, - int width, - int height) -{ - cairo_xlib_screen_t *screen; - cairo_status_t status; - - if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + 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. - **/ -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)) { - 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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { - status = _cairo_surface_set_error (abstract_surface, - _cairo_error (CAIRO_STATUS_INVALID_SIZE)); - return; - } - - surface->width = width; - surface->height = height; -} -/** - * cairo_xlib_surface_set_drawable: - * @surface: a #cairo_surface_t for the XLib backend - * @drawable: the new drawable for the surface - * @width: the width of the new drawable - * @height: the height of the new drawable - * - * Informs cairo of a new X Drawable underlying the - * surface. The drawable must match the display, screen - * and format of the existing drawable or the application - * will get X protocol errors and will probably terminate. - * No checks are done by this function to ensure this - * compatibility. - **/ -void -cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, - Drawable drawable, - int width, - int height) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; - cairo_status_t status; - - if (unlikely (abstract_surface->status)) - return; - if (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 (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX || width <= 0 || height <= 0) { - 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; - - if (surface->drawable != drawable) { - cairo_xlib_display_t *display; - - status = _cairo_xlib_display_acquire (surface->base.device, &display); - if (unlikely (status)) - return; - - X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); - - if (surface->dst_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->dst_picture); - if (unlikely (status)) { - status = _cairo_surface_set_error (&surface->base, status); - return; - } - - surface->dst_picture = None; - } - - if (surface->src_picture != None) { - status = _cairo_xlib_display_queue_resource ( - display, - XRenderFreePicture, - surface->src_picture); - if (unlikely (status)) { - status = _cairo_surface_set_error (&surface->base, status); - return; - } - - surface->src_picture = None; - } - - cairo_device_release (&display->base); - - surface->drawable = drawable; - } - surface->width = width; - surface->height = height; -} - -/** - * cairo_xlib_surface_get_display: - * @surface: a #cairo_xlib_surface_t - * - * Get the X Display for the underlying X Drawable. - * - * Return value: the display. - * - * Since: 1.2 - **/ -Display * -cairo_xlib_surface_get_display (cairo_surface_t *abstract_surface) -{ - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return ((cairo_xlib_display_t *) abstract_surface->device)->display; -} - -/** - * cairo_xlib_surface_get_drawable: - * @surface: a #cairo_xlib_surface_t - * - * Get the underlying X Drawable used for the surface. - * - * Return value: the drawable. - * - * Since: 1.2 - **/ -Drawable -cairo_xlib_surface_get_drawable (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->drawable; -} - -/** - * cairo_xlib_surface_get_screen: - * @surface: a #cairo_xlib_surface_t - * - * Get the X Screen for the underlying X Drawable. - * - * Return value: the screen. - * - * Since: 1.2 - **/ -Screen * -cairo_xlib_surface_get_screen (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return NULL; - } - - return surface->screen->screen; -} - -/** - * cairo_xlib_surface_get_visual: - * @surface: a #cairo_xlib_surface_t - * - * 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; - } + X_DEBUG ((dpy, "create_with_xrender_format (drawable=%x)", (unsigned int) drawable)); - return xlib_surface->visual; + return _cairo_xlib_surface_create_internal (screen, drawable, + _visual_for_xrender_format (scr, format), + format, width, height, 0); } /** - * cairo_xlib_surface_get_depth: - * @surface: a #cairo_xlib_surface_t + * cairo_xlib_surface_get_xrender_format: + * @surface: an xlib surface * - * Get the number of bits used to represent each pixel value. + * 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 depth of the surface in bits. + * 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.2 + * Since: 1.6 **/ -int -cairo_xlib_surface_get_depth (cairo_surface_t *abstract_surface) +XRenderPictFormat * +cairo_xlib_surface_get_xrender_format (cairo_surface_t *surface) { - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; + cairo_xlib_surface_t *xlib_surface = (cairo_xlib_surface_t *) surface; - if (! _cairo_surface_is_xlib (abstract_surface)) { + /* Throw an error for a non-xlib surface */ + if (! _cairo_surface_is_xlib (surface)) { _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; + return NULL; } - return surface->depth; + return xlib_surface->xrender_format; } +#endif /** - * cairo_xlib_surface_get_width: - * @surface: a #cairo_xlib_surface_t - * - * Get the width of the X Drawable underlying the surface in pixels. + * 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 * - * Return value: the width of the surface in pixels. + * 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.) * - * Since: 1.2 + * A Pixmap can never change size, so it is never necessary to call + * this function on a surface created for a Pixmap. **/ -int -cairo_xlib_surface_get_width (cairo_surface_t *abstract_surface) +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; - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; + if (unlikely (abstract_surface->status)) + return; + if (unlikely (abstract_surface->finished)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_FINISHED)); + return; } - return surface->width; -} - -/** - * cairo_xlib_surface_get_height: - * @surface: a #cairo_xlib_surface_t - * - * Get the height of the X Drawable underlying the surface in pixels. - * - * Return value: the height of the surface in pixels. - * - * Since: 1.2 - **/ -int -cairo_xlib_surface_get_height (cairo_surface_t *abstract_surface) -{ - cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - if (! _cairo_surface_is_xlib (abstract_surface)) { - _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); - return 0; - } - - return surface->height; -} - -enum { - GLYPHSET_INDEX_ARGB32, - GLYPHSET_INDEX_A8, - GLYPHSET_INDEX_A1, - NUM_GLYPHSETS -}; - -typedef struct _cairo_xlib_font_glyphset_free_glyphs { - GlyphSet glyphset; - int glyph_count; - unsigned long glyph_indices[128]; -} cairo_xlib_font_glyphset_free_glyphs_t; - -typedef struct _cairo_xlib_font_glyphset_info { - GlyphSet glyphset; - cairo_format_t format; - XRenderPictFormat *xrender_format; - cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs; -} cairo_xlib_font_glyphset_info_t; - -typedef struct _cairo_xlib_surface_font_private { - cairo_scaled_font_t *scaled_font; - cairo_xlib_hook_t close_display_hook; - cairo_device_t *device; - cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS]; -} cairo_xlib_surface_font_private_t; - -/* callback from CloseDisplay */ -static void -_cairo_xlib_surface_remove_scaled_font (cairo_xlib_display_t *display, - void *data) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_scaled_font_t *scaled_font; - - font_private = cairo_container_of (data, - cairo_xlib_surface_font_private_t, - close_display_hook); - scaled_font = font_private->scaled_font; - - CAIRO_MUTEX_LOCK (scaled_font->mutex); - font_private = scaled_font->surface_private; - scaled_font->surface_private = NULL; - - _cairo_scaled_font_reset_cache (scaled_font); - CAIRO_MUTEX_UNLOCK (scaled_font->mutex); - - if (font_private != NULL) { - int i; - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - if (glyphset_info->glyphset) - XRenderFreeGlyphSet (display->display, glyphset_info->glyphset); - - free (glyphset_info->pending_free_glyphs); - } - - cairo_device_destroy (font_private->device); - free (font_private); - } -} - -static cairo_status_t -_cairo_xlib_surface_font_init (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = malloc (sizeof (cairo_xlib_surface_font_private_t)); - if (unlikely (font_private == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - - font_private->scaled_font = scaled_font; - font_private->device = cairo_device_reference (&display->base); - - /* initialize and hook into the CloseDisplay callback */ - font_private->close_display_hook.func = - _cairo_xlib_surface_remove_scaled_font; - _cairo_xlib_add_close_display_hook (display, - &font_private->close_display_hook); - - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i]; - switch (i) { - case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break; - case GLYPHSET_INDEX_A8: glyphset_info->format = CAIRO_FORMAT_A8; break; - case GLYPHSET_INDEX_A1: glyphset_info->format = CAIRO_FORMAT_A1; break; - default: ASSERT_NOT_REACHED; break; - } - glyphset_info->xrender_format = NULL; - glyphset_info->glyphset = None; - glyphset_info->pending_free_glyphs = NULL; - } - - scaled_font->surface_private = font_private; - scaled_font->surface_backend = &cairo_xlib_surface_backend; - - return CAIRO_STATUS_SUCCESS; -} - -static void -_cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_status_t status; - - font_private = scaled_font->surface_private; - if (font_private != NULL) { - cairo_xlib_display_t *display; - int i; - - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status) - goto BAIL; - - _cairo_xlib_remove_close_display_hook (display, - &font_private->close_display_hook); - - for (i = 0; i < NUM_GLYPHSETS; i++) { - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyphset_info = &font_private->glyphset_info[i]; - - free (glyphset_info->pending_free_glyphs); - - if (glyphset_info->glyphset) { - status = _cairo_xlib_display_queue_resource (display, - XRenderFreeGlyphSet, - glyphset_info->glyphset); - (void) status; /* XXX cannot propagate failure */ - } - } - - cairo_device_release (&display->base); -BAIL: - cairo_device_destroy (&display->base); - free (font_private); - } -} - -static void -_cairo_xlib_render_free_glyphs (Display *dpy, - cairo_xlib_font_glyphset_free_glyphs_t *to_free) -{ - XRenderFreeGlyphs (dpy, - to_free->glyphset, - to_free->glyph_indices, - to_free->glyph_count); -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_glyph_get_glyphset_info (cairo_scaled_glyph_t *scaled_glyph) -{ - return scaled_glyph->surface_private; -} - -static void -_cairo_xlib_scaled_glyph_set_glyphset_info (cairo_scaled_glyph_t *scaled_glyph, - cairo_xlib_font_glyphset_info_t *glyphset_info) -{ - scaled_glyph->surface_private = glyphset_info; -} - -static void -_cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph, - cairo_scaled_font_t *scaled_font) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - if (scaled_font->finished) + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); return; - - font_private = scaled_font->surface_private; - glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (font_private != NULL && glyphset_info != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - cairo_status_t status; - - to_free = glyphset_info->pending_free_glyphs; - if (to_free != NULL && - to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices)) - { - cairo_xlib_display_t *display; - - status = _cairo_xlib_display_acquire (font_private->device, &display); - if (status == CAIRO_STATUS_SUCCESS) { - status = _cairo_xlib_display_queue_work (display, - (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs, - to_free, - free); - cairo_device_release (&display->base); - } - /* XXX cannot propagate failure */ - if (unlikely (status)) - free (to_free); - - to_free = glyphset_info->pending_free_glyphs = NULL; - } - - if (to_free == NULL) { - to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t)); - if (unlikely (to_free == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); - return; /* XXX cannot propagate failure */ - } - - to_free->glyphset = glyphset_info->glyphset; - to_free->glyph_count = 0; - glyphset_info->pending_free_glyphs = to_free; - } - - to_free->glyph_indices[to_free->glyph_count++] = - _cairo_scaled_glyph_index (scaled_glyph); - } -} - -static int -_cairo_xlib_get_glyphset_index_for_format (cairo_format_t format) -{ - if (format == CAIRO_FORMAT_A8) - return GLYPHSET_INDEX_A8; - if (format == CAIRO_FORMAT_A1) - return GLYPHSET_INDEX_A1; - - assert (format == CAIRO_FORMAT_ARGB32); - return GLYPHSET_INDEX_ARGB32; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font, - cairo_format_t format) -{ - cairo_xlib_surface_font_private_t *font_private; - cairo_xlib_font_glyphset_info_t *glyphset_info; - int glyphset_index; - - glyphset_index = _cairo_xlib_get_glyphset_index_for_format (format); - font_private = scaled_font->surface_private; - glyphset_info = &font_private->glyphset_info[glyphset_index]; - if (glyphset_info->glyphset == None) { - cairo_xlib_display_t *display; - - if (_cairo_xlib_display_acquire (font_private->device, &display)) - return NULL; - - glyphset_info->xrender_format = - _cairo_xlib_display_get_xrender_format (display, - glyphset_info->format); - glyphset_info->glyphset = XRenderCreateGlyphSet (display->display, - glyphset_info->xrender_format); - - cairo_device_release (&display->base); - } - - return glyphset_info; -} - -static cairo_bool_t -_cairo_xlib_glyphset_info_has_pending_free_glyph ( - cairo_xlib_font_glyphset_info_t *glyphset_info, - unsigned long glyph_index) -{ - if (glyphset_info->pending_free_glyphs != NULL) { - cairo_xlib_font_glyphset_free_glyphs_t *to_free; - int i; - - to_free = glyphset_info->pending_free_glyphs; - for (i = 0; i < to_free->glyph_count; i++) { - if (to_free->glyph_indices[i] == glyph_index) { - to_free->glyph_count--; - memmove (&to_free->glyph_indices[i], - &to_free->glyph_indices[i+1], - (to_free->glyph_count - i) * sizeof (to_free->glyph_indices[0])); - return TRUE; - } - } - } - - return FALSE; -} - -static cairo_xlib_font_glyphset_info_t * -_cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph ( - cairo_scaled_font_t *scaled_font, - unsigned long glyph_index, - cairo_image_surface_t *surface) -{ - cairo_xlib_surface_font_private_t *font_private; - int i; - - font_private = scaled_font->surface_private; - if (font_private == NULL) - return NULL; - - if (surface != NULL) { - i = _cairo_xlib_get_glyphset_index_for_format (surface->format); - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } else { - for (i = 0; i < NUM_GLYPHSETS; i++) { - if (_cairo_xlib_glyphset_info_has_pending_free_glyph ( - &font_private->glyphset_info[i], - glyph_index)) - { - return &font_private->glyphset_info[i]; - } - } - } - - return NULL; -} - -static cairo_status_t -_cairo_xlib_surface_add_glyph (cairo_xlib_display_t *display, - cairo_scaled_font_t *scaled_font, - cairo_scaled_glyph_t **pscaled_glyph) -{ - XGlyphInfo glyph_info; - unsigned long glyph_index; - unsigned char *data; - cairo_status_t status = CAIRO_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph = *pscaled_glyph; - cairo_image_surface_t *glyph_surface = scaled_glyph->surface; - cairo_bool_t already_had_glyph_surface; - cairo_xlib_font_glyphset_info_t *glyphset_info; - - glyph_index = _cairo_scaled_glyph_index (scaled_glyph); - - /* check to see if we have a pending XRenderFreeGlyph for this glyph */ - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_pending_free_glyph (scaled_font, glyph_index, glyph_surface); - if (glyphset_info != NULL) { - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - return CAIRO_STATUS_SUCCESS; - } - - if (!glyph_surface) { - status = _cairo_scaled_glyph_lookup (scaled_font, - glyph_index, - CAIRO_SCALED_GLYPH_INFO_METRICS | - CAIRO_SCALED_GLYPH_INFO_SURFACE, - pscaled_glyph); - if (unlikely (status)) - return status; - - scaled_glyph = *pscaled_glyph; - glyph_surface = scaled_glyph->surface; - already_had_glyph_surface = FALSE; - } else { - already_had_glyph_surface = TRUE; - } - - if (scaled_font->surface_private == NULL) { - status = _cairo_xlib_surface_font_init (display, scaled_font); - if (unlikely (status)) - return status; - } - - glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font, - glyph_surface->format); - - /* XRenderAddGlyph does not handle a glyph surface larger than the extended maximum XRequest size. */ - { - int len = cairo_format_stride_for_width (glyphset_info->format, glyph_surface->width) * glyph_surface->height; - int max_request_size = (XExtendedMaxRequestSize (display->display) ? XExtendedMaxRequestSize (display->display) - : XMaxRequestSize (display->display)) * 4 - - sz_xRenderAddGlyphsReq - - sz_xGlyphInfo - - 8; - if (len >= max_request_size) - return UNSUPPORTED ("glyph too large for XRequest"); - } - - /* If the glyph surface has zero height or width, we create - * a clear 1x1 surface, to avoid various X server bugs. - */ - if (glyph_surface->width == 0 || glyph_surface->height == 0) { - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - } - - /* If the glyph format does not match the font format, then we - * create a temporary surface for the glyph image with the font's - * format. - */ - if (glyph_surface->format != glyphset_info->format) { - cairo_surface_pattern_t pattern; - cairo_surface_t *tmp_surface; - - tmp_surface = cairo_image_surface_create (glyphset_info->format, - glyph_surface->width, - glyph_surface->height); - status = tmp_surface->status; - if (unlikely (status)) - goto BAIL; - - tmp_surface->device_transform = glyph_surface->base.device_transform; - tmp_surface->device_transform_inverse = glyph_surface->base.device_transform_inverse; - - _cairo_pattern_init_for_surface (&pattern, &glyph_surface->base); - status = _cairo_surface_paint (tmp_surface, - CAIRO_OPERATOR_SOURCE, &pattern.base, - NULL); - _cairo_pattern_fini (&pattern.base); - - glyph_surface = (cairo_image_surface_t *) tmp_surface; - - if (unlikely (status)) - goto BAIL; - } - - /* XXX: FRAGILE: We're ignore device_transform scaling here. A bug? */ - glyph_info.x = _cairo_lround (glyph_surface->base.device_transform.x0); - glyph_info.y = _cairo_lround (glyph_surface->base.device_transform.y0); - glyph_info.width = glyph_surface->width; - glyph_info.height = glyph_surface->height; - glyph_info.xOff = scaled_glyph->x_advance; - glyph_info.yOff = scaled_glyph->y_advance; - - data = glyph_surface->data; - - /* flip formats around */ - switch (_cairo_xlib_get_glyphset_index_for_format (scaled_glyph->surface->format)) { - case GLYPHSET_INDEX_A1: - /* local bitmaps are always stored with bit == byte */ - if (_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, glyphset_info->glyphset, - &glyph_index, &glyph_info, 1, - (char *) data, - glyph_surface->stride * glyph_surface->height); - - if (data != glyph_surface->data) - free (data); - - _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info); - BAIL: - if (glyph_surface != scaled_glyph->surface) - cairo_surface_destroy (&glyph_surface->base); - - /* if the scaled glyph didn't already have a surface attached - * to it, release the created surface now that we have it - * uploaded to the X server. If the surface has already been - * there (eg. because image backend requested it), leave it in - * the cache - */ - if (!already_had_glyph_surface) - _cairo_scaled_glyph_set_surface (scaled_glyph, scaled_font, NULL); + if (! valid_size (width, height)) { + _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; + } - return status; + surface->width = width; + surface->height = height; } - -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) +/** + * 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 * - * These same conditions need to be mirrored between - * _cairo_xlib_surface_emit_glyphs and _emit_glyph_chunks - */ -#define _start_new_glyph_elt(count, glyph) \ - (((count) & 127) == 0 || (glyph)->i.x || (glyph)->i.y) - -static cairo_status_t -_emit_glyphs_chunk (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - /* info for this chunk */ - int num_elts, - int width, - cairo_xlib_font_glyphset_info_t *glyphset_info) + * Informs cairo of a new X Drawable underlying the + * surface. The drawable must match the display, screen + * and format of the existing drawable or the application + * will get X protocol errors and will probably terminate. + * No checks are done by this function to ensure this + * compatibility. + **/ +void +cairo_xlib_surface_set_drawable (cairo_surface_t *abstract_surface, + Drawable drawable, + int width, + int height) { - /* 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); - } + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *)abstract_surface; + cairo_status_t status; - /* 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); + 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; } - /* Fill them in */ - nelt = 0; - n = 0; - j = 0; - for (i = 0; i < num_glyphs; i++) { - - /* Start a new element for first output glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _cairo_xlib_surface_emit_glyphs() - */ - if (_start_new_glyph_elt (j, &glyphs[i])) { - if (j) { - elts[nelt].nchars = n; - nelt++; - n = 0; - } - elts[nelt].chars = char8 + size * j; - elts[nelt].glyphset = glyphset_info->glyphset; - elts[nelt].xOff = glyphs[i].i.x; - elts[nelt].yOff = glyphs[i].i.y; - } - - switch (width) { - case 1: char8 [j] = (char) glyphs[i].index; break; - case 2: char16[j] = (unsigned short) glyphs[i].index; break; - default: - case 4: char32[j] = (unsigned int) glyphs[i].index; break; - } - - n++; - j++; + if (! _cairo_surface_is_xlib (abstract_surface)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH)); + return; } - if (n) { - elts[nelt].nchars = n; - nelt++; + if (! valid_size (width, height)) { + status = _cairo_surface_set_error (abstract_surface, + _cairo_error (CAIRO_STATUS_INVALID_SIZE)); + return; } - /* Check that we agree with _cairo_xlib_surface_emit_glyphs() on the - * expected number of xGlyphElts. */ - assert (nelt == num_elts); - - composite_text_func (display->display, - _render_operator (op), - src->src_picture, - dst->dst_picture, - glyphset_info->xrender_format, - attributes->x_offset + elts[0].xOff, - attributes->y_offset + elts[0].yOff, - elts[0].xOff, elts[0].yOff, - (XGlyphElt8 *) elts, nelt); - - if (elts != stack_elts) - free (elts); - - return CAIRO_STATUS_SUCCESS; -} - - -/* sz_xGlyphtElt required alignment to a 32-bit boundary, so ensure we have - * enough room for padding */ -#define _cairo_sz_xGlyphElt (sz_xGlyphElt + 4) + /* XXX: and what about this case? */ + if (surface->owns_pixmap) + return; -static cairo_status_t -_cairo_xlib_surface_emit_glyphs (cairo_xlib_display_t *display, - cairo_xlib_surface_t *dst, - cairo_xlib_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - cairo_operator_t op, - cairo_xlib_surface_t *src, - cairo_surface_attributes_t *attributes, - int *remaining_glyphs) -{ - int i; - cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; - cairo_scaled_glyph_t *scaled_glyph; - cairo_fixed_t x = 0, y = 0; - cairo_xlib_font_glyphset_info_t *glyphset_info = NULL, *this_glyphset_info; - - unsigned long max_index = 0; - int width = 1; - int num_elts = 0; - int num_out_glyphs = 0; - - int max_request_size = XMaxRequestSize (display->display) * 4 - - MAX (sz_xRenderCompositeGlyphs8Req, - MAX(sz_xRenderCompositeGlyphs16Req, - sz_xRenderCompositeGlyphs32Req)); - int request_size = 0; - - _cairo_xlib_surface_ensure_dst_picture (display, dst); - - for (i = 0; i < num_glyphs; i++) { - int this_x, this_y; - int old_width; - - status = _cairo_scaled_glyph_lookup (scaled_font, - glyphs[i].index, - CAIRO_SCALED_GLYPH_INFO_METRICS, - &scaled_glyph); - if (unlikely (status)) - return status; + if (surface->drawable != drawable) { + cairo_xlib_display_t *display; - /* 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 (glyphs[i].d.x > INT16_MAX || glyphs[i].d.y > INT16_MAX || - glyphs[i].d.x < INT16_MIN || glyphs[i].d.y < INT16_MIN) - { - break; - } + status = _cairo_xlib_display_acquire (surface->base.device, &display); + if (unlikely (status)) + return; - this_x = _cairo_lround (glyphs[i].d.x); - this_y = _cairo_lround (glyphs[i].d.y); + X_DEBUG ((display->display, "set_drawable (drawable=%x)", (unsigned int) drawable)); - /* Send unsent glyphs to the server */ - if (_cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph) == NULL) { - status = _cairo_xlib_surface_add_glyph (display, - scaled_font, - &scaled_glyph); + if (surface->picture != None) { + XRenderFreePicture (display->display, surface->picture); if (unlikely (status)) { - if (status == CAIRO_INT_STATUS_UNSUPPORTED) - /* Break so we flush glyphs so far and let fallback code - * handle the rest */ - break; - - return status; + status = _cairo_surface_set_error (&surface->base, status); + return; } - } - - this_glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph); - if (!glyphset_info) - glyphset_info = this_glyphset_info; - - /* The invariant here is that we can always flush the glyphs - * accumulated before this one, using old_width, and they - * would fit in the request. - */ - old_width = width; - - /* Update max glyph index */ - if (glyphs[i].index > max_index) { - max_index = glyphs[i].index; - if (max_index >= 65536) - width = 4; - else if (max_index >= 256) - width = 2; - if (width != old_width) - request_size += (width - old_width) * num_out_glyphs; - } - - /* If we will pass the max request size by adding this glyph, - * flush current glyphs. Note that we account for a - * possible element being added below. - * - * Also flush if changing glyphsets, as Xrender limits one mask - * format per request, so we can either break up, or use a - * wide-enough mask format. We do the former. One reason to - * prefer the latter is the fact that Xserver ADDs all glyphs - * to the mask first, and then composes that to final surface, - * though it's not a big deal. - * - * If 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_info)) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, old_width, glyphset_info); - if (unlikely (status)) - return status; - - glyphs += i; - num_glyphs -= i; - i = 0; - max_index = glyphs[i].index; - width = max_index < 256 ? 1 : max_index < 65536 ? 2 : 4; - request_size = 0; - num_elts = 0; - num_out_glyphs = 0; - x = y = 0; - glyphset_info = this_glyphset_info; - } - /* Convert absolute glyph position to relative-to-current-point - * position */ - glyphs[i].i.x = this_x - x; - glyphs[i].i.y = this_y - y; - - /* Start a new element for the first glyph, - * or for any glyph that has unexpected position, - * or if current element has too many glyphs. - * - * These same conditions are mirrored in _emit_glyphs_chunk(). - */ - if (_start_new_glyph_elt (num_out_glyphs, &glyphs[i])) { - num_elts++; - request_size += _cairo_sz_xGlyphElt; + surface->picture = None; } - /* adjust current-position */ - x = this_x + scaled_glyph->x_advance; - y = this_y + scaled_glyph->y_advance; + cairo_device_release (&display->base); - num_out_glyphs++; - request_size += width; + surface->drawable = drawable; } + surface->width = width; + surface->height = height; +} - if (num_elts) { - status = _emit_glyphs_chunk (display, dst, glyphs, i, - scaled_font, op, src, attributes, - num_elts, width, glyphset_info); +/** + * 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; } - *remaining_glyphs = num_glyphs - i; - if (*remaining_glyphs != 0 && status == CAIRO_INT_STATUS_SUCCESS) - status = CAIRO_INT_STATUS_UNSUPPORTED; - - return status; + return ((cairo_xlib_display_t *) abstract_surface->device)->display; } -static cairo_bool_t -_cairo_xlib_surface_owns_font (cairo_xlib_surface_t *dst, - cairo_scaled_font_t *scaled_font) +/** + * 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_font_private_t *font_private; + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - font_private = scaled_font->surface_private; - if ((scaled_font->surface_backend != NULL && - scaled_font->surface_backend != &cairo_xlib_surface_backend) || - (font_private != NULL && font_private->device != dst->base.device)) - { - return FALSE; + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; } - return TRUE; + return surface->drawable; } -static cairo_int_status_t -_cairo_xlib_surface_show_glyphs (void *abstract_dst, - cairo_operator_t op, - const cairo_pattern_t *src_pattern, - cairo_glyph_t *glyphs, - int num_glyphs, - cairo_scaled_font_t *scaled_font, - const cairo_clip_t *clip, - int *remaining_glyphs) +/** + * 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_int_status_t status = CAIRO_STATUS_SUCCESS; - cairo_xlib_surface_t *dst = (cairo_xlib_surface_t*) abstract_dst; - composite_operation_t operation; - cairo_surface_attributes_t attributes; - cairo_xlib_surface_t *src = NULL; - cairo_xlib_display_t *display; - - if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE_TEXT (dst)) - return UNSUPPORTED ("XRender does not support CompositeText"); - - /* Just let unbounded operators go through the fallback code - * instead of trying to do the fixups here */ - if (! _cairo_operator_bounded_by_mask (op)) - return UNSUPPORTED ("unsupported unbounded op"); - - /* Render <= 0.10 seems to have a bug with PictOpSrc and glyphs -- - * the solid source seems to be multiplied by the glyph mask, and - * then the entire thing is copied to the destination surface, - * including the fully transparent "background" of the rectangular - * glyph surface. */ - if (op == CAIRO_OPERATOR_SOURCE) - return UNSUPPORTED ("known bug in Render"); - - /* We can only use our code if we either have no clip or - * have a real native clip region set. If we're using - * fallback clip masking, we have to go through the full - * fallback path. - */ - if (!_cairo_clip_is_region (clip)) - return UNSUPPORTED ("clip mask required"); + cairo_xlib_surface_t *surface = (cairo_xlib_surface_t *) abstract_surface; - operation = _categorize_composite_operation (dst, op, src_pattern, TRUE); - if (operation == DO_UNSUPPORTED) - return UNSUPPORTED ("unsupported op"); + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } - if (! _cairo_xlib_surface_owns_font (dst, scaled_font)) - return UNSUPPORTED ("unowned font"); + return surface->screen->screen; +} - status = _cairo_xlib_display_acquire (dst->base.device, &display); - if (unlikely (status)) - return status; +/** + * 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; - X_DEBUG ((display->display, "show_glyphs (dst=%x)", (unsigned int) dst->drawable)); + if (! _cairo_surface_is_xlib (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } - status = _cairo_xlib_surface_set_clip_region (dst, - _cairo_clip_get_region (clip)); - if (unlikely (status)) - goto BAIL0; + return xlib_surface->visual; +} - /* After passing all those tests, we're now committed to rendering - * these glyphs or to fail trying. We first upload any glyphs to - * the X server that it doesn't have already, then we draw - * them. - */ +/** + * 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; - /* PictOpClear doesn't seem to work with CompositeText; it seems to ignore - * the mask (the glyphs). This code below was executed as a side effect - * of going through the _clip_and_composite fallback code for old_show_glyphs, - * so PictOpClear was never used with CompositeText before. - */ - if (op == CAIRO_OPERATOR_CLEAR) { - src_pattern = &_cairo_pattern_white.base; - op = CAIRO_OPERATOR_DEST_OUT; + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; } - if (src_pattern->type == CAIRO_PATTERN_TYPE_SOLID) { - status = _cairo_pattern_acquire_surface (src_pattern, &dst->base, - 0, 0, 1, 1, - CAIRO_PATTERN_ACQUIRE_NONE, - (cairo_surface_t **) &src, - &attributes); - if (unlikely (status)) - goto BAIL0; - } else { - cairo_rectangle_int_t glyph_extents; + return surface->depth; +} - status = _cairo_scaled_font_glyph_device_extents (scaled_font, - glyphs, - num_glyphs, - &glyph_extents, - NULL); - if (unlikely (status)) - goto BAIL0; - - status = _cairo_xlib_surface_acquire_pattern_surface (display, - dst, src_pattern, - glyph_extents.x, - glyph_extents.y, - glyph_extents.width, - glyph_extents.height, - &src, &attributes); - if (unlikely (status)) - goto BAIL0; - } +/** + * 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; - operation = _recategorize_composite_operation (dst, op, src, - &attributes, TRUE); - if (operation == DO_UNSUPPORTED) { - status = UNSUPPORTED ("unsupported op"); - goto BAIL1; + if (! _cairo_surface_is_xlib (abstract_surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; } - status = _cairo_xlib_surface_set_attributes (display, src, &attributes, 0, 0); - if (unlikely (status)) - goto BAIL1; - - _cairo_scaled_font_freeze_cache (scaled_font); - if (_cairo_xlib_surface_owns_font (dst, scaled_font)) { - status = _cairo_xlib_surface_emit_glyphs (display, - dst, - (cairo_xlib_glyph_t *) glyphs, - num_glyphs, - scaled_font, - op, - src, - &attributes, - remaining_glyphs); - } else { - status = UNSUPPORTED ("unowned font"); - } - _cairo_scaled_font_thaw_cache (scaled_font); + return surface->width; +} - BAIL1: - if (src) - _cairo_pattern_release_surface (src_pattern, &src->base, &attributes); - BAIL0: - cairo_device_release (&display->base); +/** + * 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; - return status; + 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 */ diff --git a/src/cairo.c b/src/cairo.c index 6f6d00e69..cbc397459 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -46,6 +46,7 @@ #include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-surface-private.h" +#include "cairo-surface-backend-private.h" #include diff --git a/src/cairo.h b/src/cairo.h index 710d99783..f5450fec3 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -2123,7 +2123,7 @@ cairo_surface_create_similar_image (cairo_surface_t *other, cairo_public cairo_surface_t * cairo_surface_map_to_image (cairo_surface_t *surface, - const cairo_rectangle_t *extents); + const cairo_rectangle_int_t *extents); cairo_public void cairo_surface_unmap_image (cairo_surface_t *surface, @@ -2145,6 +2145,45 @@ 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 void cairo_surface_observer_print (cairo_surface_t *surface, cairo_write_func_t write_func, @@ -2426,6 +2465,10 @@ cairo_recording_surface_ink_extents (cairo_surface_t *surface, double *width, double *height); +cairo_public cairo_bool_t +cairo_recording_surface_get_extents (cairo_surface_t *surface, + cairo_rectangle_t *extents); + /* Mime-surface (callback) functions */ typedef cairo_surface_t *(*cairo_mime_surface_acquire_t) (cairo_surface_t *mime_surface, diff --git a/src/cairoint.h b/src/cairoint.h index 5ae8b7a5c..28f3e9f31 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -287,16 +287,14 @@ _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 extern const cairo_rectangle_int_t _cairo_empty_rectangle; -cairo_private extern const cairo_rectangle_int_t _cairo_unbounded_rectangle; cairo_private_no_warn cairo_bool_t _cairo_rectangle_intersect (cairo_rectangle_int_t *dst, @@ -327,47 +325,12 @@ cairo_private cairo_bool_t _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line) cairo_pure; -/* 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_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; typedef struct { const cairo_user_data_key_t *key; @@ -407,10 +370,6 @@ _cairo_user_data_array_foreach (cairo_user_data_array_t *array, cairo_private unsigned long _cairo_hash_string (const char *c); -cairo_private void -_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern, - cairo_rectangle_int_t *extents); - cairo_private unsigned long _cairo_hash_bytes (unsigned long hash, const void *bytes, @@ -518,21 +477,6 @@ struct _cairo_scaled_font_backend { 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 @@ -657,301 +601,6 @@ extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_fa #endif -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_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_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_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, - 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 - (*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, - 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, - 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_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, - const cairo_clip_t *clip); - - cairo_warn cairo_status_t - (*acquire_source_image_transformed) (void *abstract_surface, - cairo_matrix_t *device_transform, - cairo_image_surface_t **image_out, - void **image_extra); -}; - #define CAIRO_EXTEND_SURFACE_DEFAULT CAIRO_EXTEND_NONE #define CAIRO_EXTEND_GRADIENT_DEFAULT CAIRO_EXTEND_PAD #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_GOOD @@ -970,23 +619,6 @@ struct _cairo_surface_attributes { void *extra; }; -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 @@ -1403,6 +1035,14 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path, double tolerance, cairo_polygon_t *polygon); +cairo_private 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); + 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, @@ -1569,6 +1209,7 @@ _cairo_stroke_style_fini (cairo_stroke_style_t *style); 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); @@ -1601,7 +1242,7 @@ cairo_private cairo_status_t _cairo_surface_copy_mime_data (cairo_surface_t *dst, cairo_surface_t *src); -cairo_private cairo_int_status_t +cairo_private_no_warn cairo_int_status_t _cairo_surface_set_error (cairo_surface_t *surface, cairo_int_status_t status); @@ -1625,17 +1266,7 @@ _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); - -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); + const cairo_color_t *color); cairo_private void _cairo_surface_init (cairo_surface_t *surface, @@ -1647,34 +1278,6 @@ cairo_private void _cairo_surface_set_font_options (cairo_surface_t *surface, 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_region (cairo_surface_t *surface, - cairo_operator_t op, - const cairo_color_t *color, - cairo_region_t *region); - -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_status_t _cairo_surface_paint (cairo_surface_t *surface, cairo_operator_t op, @@ -1741,76 +1344,16 @@ _cairo_surface_show_text_glyphs (cairo_surface_t *surface, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip); -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); -cairo_private cairo_status_t -_cairo_surface_acquire_source_image_transformed (cairo_surface_t *surface, - cairo_matrix_t *device_trasnform, - cairo_image_surface_t **image_out, - void **image_extra); - cairo_private void _cairo_surface_release_source_image (cairo_surface_t *surface, 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); @@ -1826,67 +1369,10 @@ _cairo_surface_has_snapshot (cairo_surface_t *surface, 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_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, @@ -1999,22 +1485,12 @@ cairo_private cairo_image_surface_t * _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_image_color_t _cairo_image_analyze_color (cairo_image_surface_t *image); -cairo_private cairo_bool_t -_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure; - /* cairo-pen.c */ cairo_private cairo_status_t _cairo_pen_init (cairo_pen_t *pen, @@ -2079,6 +1555,9 @@ cairo_private cairo_status_t _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_reduce (cairo_polygon_t *polygon, cairo_fill_rule_t fill_rule); @@ -2087,6 +1566,12 @@ cairo_private cairo_status_t _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) { @@ -2180,44 +1665,6 @@ _cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, 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_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 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, @@ -2250,23 +1697,6 @@ _cairo_bentley_ottmann_tessellate_rectilinear_polygon_to_boxes (const cairo_poly 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_antialias_t antialias, - 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, @@ -2422,6 +1852,7 @@ slim_hidden_proto (cairo_status); 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); diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp index 828f6e596..e5d482807 100644 --- a/src/skia/cairo-skia-context.cpp +++ b/src/skia/cairo-skia-context.cpp @@ -50,6 +50,7 @@ #include "cairo-path-private.h" #include "cairo-pattern-private.h" #include "cairo-skia-private.h" +#include "cairo-surface-backend-private.h" #include #include diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp index 4c10625ad..56b4dca12 100644 --- a/src/skia/cairo-skia-surface.cpp +++ b/src/skia/cairo-skia-surface.cpp @@ -41,6 +41,8 @@ #include "cairo-composite-rectangles-private.h" #include "cairo-error-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-surface-fallback-private.h" static cairo_skia_surface_t * _cairo_skia_surface_create_internal (SkBitmap::Config config, @@ -161,198 +163,6 @@ _cairo_skia_surface_get_font_options (void *abstract_surface, _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); } -static cairo_rectangle_t * -to_rectangle (cairo_rectangle_t *rf, - cairo_rectangle_int_t *ri) -{ - rf->x = ri->x; - rf->y = ri->y; - rf->width = ri->width; - rf->height = ri->height; - return rf; -} - -static cairo_int_status_t -_cairo_foreign_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - cairo_surface_t *surface = (cairo_surface_t *) abstract_surface; - cairo_surface_t *image; - cairo_rectangle_int_t extents; - cairo_rectangle_t rect; - cairo_composite_rectangles_t composite; - cairo_int_status_t status; - - _cairo_surface_get_extents (surface, &extents); - status = _cairo_composite_rectangles_init_for_paint (&composite, &extents, - op, source, - clip); - if (unlikely (status)) - return status; - - image = cairo_surface_map_to_image (surface, - to_rectangle(&rect, &composite.unbounded)); - status = (cairo_int_status_t) - _cairo_surface_paint (image, op, source, clip); - cairo_surface_unmap_image (surface, image); - - _cairo_composite_rectangles_fini (&composite); - - return status; -} - -static cairo_int_status_t -_cairo_foreign_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 =(cairo_surface_t *) abstract_surface; - cairo_surface_t *image; - cairo_rectangle_int_t extents; - cairo_rectangle_t rect; - cairo_composite_rectangles_t composite; - cairo_int_status_t status; - - _cairo_surface_get_extents (surface, &extents); - status = _cairo_composite_rectangles_init_for_mask (&composite, &extents, - op, source, mask, - clip); - if (unlikely (status)) - return status; - - image = cairo_surface_map_to_image (surface, - to_rectangle(&rect, &composite.unbounded)); - status = (cairo_int_status_t) - _cairo_surface_mask (image, op, source, mask, clip); - cairo_surface_unmap_image (surface, image); - - _cairo_composite_rectangles_fini (&composite); - - return status; -} - -static cairo_int_status_t -_cairo_foreign_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 =(cairo_surface_t *) abstract_surface; - cairo_surface_t *image; - cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; - cairo_rectangle_t rect; - cairo_int_status_t status; - - _cairo_surface_get_extents (surface, &extents); - status = _cairo_composite_rectangles_init_for_stroke (&composite, &extents, - op, source, - path, style, ctm, - clip); - if (unlikely (status)) - return status; - - image = cairo_surface_map_to_image (surface, - to_rectangle(&rect, &composite.unbounded)); - status = (cairo_int_status_t) - _cairo_surface_stroke (image, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); - cairo_surface_unmap_image (surface, image); - - _cairo_composite_rectangles_fini (&composite); - - return status; -} - -static cairo_int_status_t -_cairo_foreign_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 =(cairo_surface_t *) abstract_surface; - cairo_surface_t *image; - cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; - cairo_rectangle_t rect; - cairo_int_status_t status; - - _cairo_surface_get_extents (surface, &extents); - status = _cairo_composite_rectangles_init_for_fill (&composite, &extents, - op, source, path, - clip); - if (unlikely (status)) - return status; - - image = cairo_surface_map_to_image (surface, - to_rectangle(&rect, &composite.unbounded)); - status = (cairo_int_status_t) - _cairo_surface_fill (image, op, source, path, fill_rule, tolerance, antialias, clip); - cairo_surface_unmap_image (surface, image); - - _cairo_composite_rectangles_fini (&composite); - - return status; -} - -static cairo_int_status_t -_cairo_foreign_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, - int *num_remaining) -{ - cairo_surface_t *surface =(cairo_surface_t *) abstract_surface; - cairo_surface_t *image; - cairo_composite_rectangles_t composite; - cairo_rectangle_int_t extents; - cairo_rectangle_t rect; - cairo_int_status_t status; - cairo_bool_t overlap; - - _cairo_surface_get_extents (surface, &extents); - status = _cairo_composite_rectangles_init_for_glyphs (&composite, &extents, - op, source, - scaled_font, - glyphs, num_glyphs, - clip, - &overlap); - if (unlikely (status)) - return status; - - image = cairo_surface_map_to_image (surface, - to_rectangle(&rect, &composite.unbounded)); - status = (cairo_int_status_t) - _cairo_surface_show_text_glyphs (image, - op, source, - NULL, 0, - glyphs, num_glyphs, - NULL, 0, (cairo_text_cluster_flags_t)0, - scaled_font, - clip); - cairo_surface_unmap_image (surface, image); - _cairo_composite_rectangles_fini (&composite); - - *num_remaining = 0; - return status; -} - static const struct _cairo_surface_backend cairo_skia_surface_backend = { CAIRO_SURFACE_TYPE_SKIA, @@ -366,33 +176,26 @@ cairo_skia_surface_backend = { _cairo_skia_surface_unmap_image, _cairo_skia_surface_acquire_source_image, + NULL, /* acquire transformed */ _cairo_skia_surface_release_source_image, - - NULL, NULL, - 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_skia_surface_get_extents, - NULL, /* old_show_glyphs */ _cairo_skia_surface_get_font_options, + NULL, /* flush */ NULL, /* mark_dirty_rectangle */ - NULL, /* scaled_font_fini */ - NULL, /* scaled_glyph_fini */ /* XXX native surface functions? */ - _cairo_foreign_surface_paint, - _cairo_foreign_surface_mask, - _cairo_foreign_surface_stroke, - _cairo_foreign_surface_fill, - _cairo_foreign_surface_glyphs + _cairo_surface_fallback_paint, + _cairo_surface_fallback_mask, + _cairo_surface_fallback_stroke, + _cairo_surface_fallback_fill, + NULL, /* fill/stroke */ + _cairo_surface_fallback_glyphs }; /* @@ -426,6 +229,7 @@ sk_config_to_pixman_format_code (SkBitmap::Config config, return (pixman_format_code_t) -1; } } + static cairo_skia_surface_t * _cairo_skia_surface_create_internal (SkBitmap::Config config, bool opaque, diff --git a/src/test-base-compositor-surface.c b/src/test-base-compositor-surface.c new file mode 100644 index 000000000..9d957a458 --- /dev/null +++ b/src/test-base-compositor-surface.c @@ -0,0 +1,942 @@ +/* -*- 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 + * Joonas Pihlaja + * Chris Wilson + */ + +#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 void +_pixman_image_add_boxes (pixman_image_t *image, + int dst_x, int dst_y, + cairo_box_t *box, + int count) +{ + while (count--) { + pixman_trapezoid_t trap; + + trap.top = _cairo_fixed_to_16_16 (box->p1.y); + trap.bottom = _cairo_fixed_to_16_16 (box->p2.y); + + trap.left.p1.x = _cairo_fixed_to_16_16 (box->p1.x); + trap.left.p1.y = _cairo_fixed_to_16_16 (box->p1.y); + trap.left.p2.x = _cairo_fixed_to_16_16 (box->p1.x); + trap.left.p2.y = _cairo_fixed_to_16_16 (box->p2.y); + + trap.right.p1.x = _cairo_fixed_to_16_16 (box->p2.x); + trap.right.p1.y = _cairo_fixed_to_16_16 (box->p1.y); + trap.right.p2.x = _cairo_fixed_to_16_16 (box->p2.x); + trap.right.p2.y = _cairo_fixed_to_16_16 (box->p2.y); + + pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y); + box++; + } +} + +static cairo_status_t +combine_in_boxes (cairo_image_surface_t *dst, + cairo_box_t *box, int count, + const cairo_rectangle_int_t *extents) +{ + pixman_image_t *mask; + + mask = pixman_image_create_bits (PIXMAN_a8, + extents->width, extents->height, + NULL, 0); + if (unlikely (mask == NULL)) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + _pixman_image_add_boxes (mask, extents->x, extents->y, box, count); + pixman_image_composite32 (PIXMAN_OP_IN, + mask, NULL, dst->pixman_image, + 0, 0, + 0, 0, + 0, 0, + extents->width, extents->height); + pixman_image_unref (mask); + + return CAIRO_STATUS_SUCCESS; +} + +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; + + surface = (cairo_image_surface_t *) + _cairo_surface_create_similar_solid (&dst->base, + CAIRO_CONTENT_ALPHA, + extents->bounded.width, + extents->bounded.height, + CAIRO_COLOR_TRANSPARENT); + 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; + + if (extents->clip->boxes) { + status = combine_in_boxes (surface, + extents->clip->boxes, + extents->clip->num_boxes, + &extents->bounded); + if (unlikely (status)) + goto error; + } + + if (extents->clip->path) { + 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; + + 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; + + tmp = (cairo_image_surface_t *) + _cairo_surface_create_similar_solid (&dst->base, dst->base.content, + extents->bounded.width, + extents->bounded.height, + CAIRO_COLOR_TRANSPARENT); + 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; + + 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; + + if (! _cairo_clip_is_region (extents->clip)) { + cairo_surface_t *clip; + + clip = _cairo_clip_get_surface (extents->clip, &dst->base, + &mask_x, &mask_y); + if (unlikely (clip->status)) + return clip->status; + + mask = pixman_image_ref (((cairo_image_surface_t *)clip)->pixman_image); + cairo_surface_destroy (clip); + } 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; + + _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); + + 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) +{ + 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; + + _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) +{ + 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; + +#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)); +} + +static 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 (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; + + _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; + + info.antialias = antialias; + _cairo_traps_init_with_clip (&info.traps, extents->clip); + status = _cairo_path_fixed_stroke_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; + + 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; + + 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; + + 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); +} diff --git a/src/test-wrapping-surface.h b/src/test-compositor-surface-private.h similarity index 76% rename from src/test-wrapping-surface.h rename to src/test-compositor-surface-private.h index 4c1e28d7f..47e2a4326 100644 --- a/src/test-wrapping-surface.h +++ b/src/test-compositor-surface-private.h @@ -1,7 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2005 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 @@ -28,24 +27,27 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Red Hat, Inc. + * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): - * Carl Worth * Chris Wilson */ -#ifndef TEST_WRAPPING_SURFACE_H -#define TEST_WRAPPING_SURFACE_H +#ifndef TEST_COMPOSITOR_SURFACE_PRIVATE_H +#define TEST_COMPOSITOR_SURFACE_PRIVATE_H #include "cairo.h" +#include "test-compositor-surface.h" + CAIRO_BEGIN_DECLS -cairo_surface_t * -_cairo_test_wrapping_surface_create (cairo_surface_t *target); +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_WRAPPING_SURFACE_H */ - +#endif /* TEST_COMPOSITOR_SURFACE_PRIVATE H */ diff --git a/src/test-compositor-surface.c b/src/test-compositor-surface.c new file mode 100644 index 000000000..356c16c6b --- /dev/null +++ b/src/test-compositor-surface.c @@ -0,0 +1,259 @@ +/* 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 + */ + +#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; + 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_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); +} diff --git a/src/test-fallback-surface.h b/src/test-compositor-surface.h similarity index 61% rename from src/test-fallback-surface.h rename to src/test-compositor-surface.h index e70715113..8d8af2d54 100644 --- a/src/test-fallback-surface.h +++ b/src/test-compositor-surface.h @@ -1,6 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * 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 @@ -27,24 +27,45 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Red Hat, Inc. + * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): - * Carl Worth + * Chris Wilson */ -#ifndef TEST_FALLBACK_SURFACE_H -#define TEST_FALLBACK_SURFACE_H +#ifndef TEST_COMPOSITOR_SURFACE_H +#define TEST_COMPOSITOR_SURFACE_H #include "cairo.h" CAIRO_BEGIN_DECLS cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height); +_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_FALLBACK_SURFACE_H */ +#endif /* TEST_COMPOSITOR_SURFACE_H */ diff --git a/src/test-fallback-surface.c b/src/test-fallback-surface.c deleted file mode 100644 index 1dce221d0..000000000 --- a/src/test-fallback-surface.c +++ /dev/null @@ -1,244 +0,0 @@ -/* 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 - */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to test a mythical backend that uses nothing but - * fallbacks. - * - * The defining feature of this backend is that it has as many %NULL - * backend function entries as possible. The ones that aren't %NULL are - * simply those that must be implemented to have working fallbacks. - * (Except for create_similar---fallbacks would work fine without - * that---I implemented it here in order to create as many surfaces as - * possible of type test_fallback_surface_t during the test suite - * run). - * - * It's possible that this code might serve as a good starting point - * for someone working on bringing up a new backend, starting with the - * minimal all-fallbacks approach and working up gradually from - * there. - */ - -#include "cairoint.h" - -#include "test-fallback-surface.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" - -typedef struct _test_fallback_surface { - cairo_surface_t base; - - /* This is a cairo_image_surface to hold the actual contents. */ - cairo_surface_t *backing; -} test_fallback_surface_t; - -static const cairo_surface_backend_t test_fallback_surface_backend; - -slim_hidden_proto (_cairo_test_fallback_surface_create); - -cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height) -{ - test_fallback_surface_t *surface; - cairo_surface_t *backing; - - backing = _cairo_image_surface_create_with_content (content, width, height); - if (cairo_surface_status (backing)) - return backing; - - surface = malloc (sizeof (test_fallback_surface_t)); - if (unlikely (surface == NULL)) { - cairo_surface_destroy (backing); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (&surface->base, - &test_fallback_surface_backend, - NULL, /* device */ - content); - - surface->backing = backing; - - return &surface->base; -} -slim_hidden_def (_cairo_test_fallback_surface_create); - -static cairo_surface_t * -_test_fallback_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - assert (CAIRO_CONTENT_VALID (content)); - - return _cairo_test_fallback_surface_create (content, - width, height); -} - -static cairo_status_t -_test_fallback_surface_finish (void *abstract_surface) -{ - test_fallback_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->backing); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_fallback_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_source_image (surface->backing, - image_out, image_extra); -} - -static void -_test_fallback_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->backing, - image, image_extra); -} - -static cairo_status_t -_test_fallback_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) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_dest_image (surface->backing, - interest_rect, - image_out, - image_rect_out, - image_extra); -} - -static void -_test_fallback_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) -{ - test_fallback_surface_t *surface = abstract_surface; - - _cairo_surface_release_dest_image (surface->backing, - interest_rect, - image, - image_rect, - image_extra); -} - -static cairo_status_t -_test_fallback_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) -{ - test_fallback_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } - - return CAIRO_INT_STATUS_UNSUPPORTED; -} - -static cairo_bool_t -_test_fallback_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_fallback_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->backing, rectangle); -} - -static const cairo_surface_backend_t test_fallback_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, - _test_fallback_surface_finish, - _cairo_default_context_create, - - _test_fallback_surface_create_similar, - NULL, /* create similar image */ - NULL, /* map_to_image */ - NULL, /* unmap_image */ - - _test_fallback_surface_acquire_source_image, - _test_fallback_surface_release_source_image, - _test_fallback_surface_acquire_dest_image, - _test_fallback_surface_release_dest_image, - _test_fallback_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_fallback_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 */ - NULL /* snapshot */ -}; diff --git a/src/test-fallback16-surface.c b/src/test-fallback16-surface.c deleted file mode 100644 index 5ec501a09..000000000 --- a/src/test-fallback16-surface.c +++ /dev/null @@ -1,241 +0,0 @@ -/* 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 - * Chris Wilson - */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to force use of a non-standard pixman image fallback - as - * may be exposed by xlib fallbacks with weird xservers, for example. - */ - -#include "cairoint.h" - -#include "test-fallback16-surface.h" -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" - -typedef struct _test_fallback16_surface { - cairo_surface_t base; - - /* This is a cairo_image_surface to hold the actual contents. */ - cairo_surface_t *backing; -} test_fallback16_surface_t; - -static const cairo_surface_backend_t test_fallback16_surface_backend; - -slim_hidden_proto (_cairo_test_fallback16_surface_create); - -cairo_surface_t * -_cairo_test_fallback16_surface_create (cairo_content_t content, - int width, - int height) -{ - test_fallback16_surface_t *surface; - cairo_surface_t *backing; - pixman_format_code_t format; - - format = content & CAIRO_CONTENT_ALPHA ? PIXMAN_a1r5g5b5: PIXMAN_r5g6b5; - - backing = _cairo_image_surface_create_with_pixman_format (NULL, format, - width, height, - -1); - if (cairo_surface_status (backing)) - return backing; - - surface = malloc (sizeof (test_fallback16_surface_t)); - if (unlikely (surface == NULL)) { - cairo_surface_destroy (backing); - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (&surface->base, - &test_fallback16_surface_backend, - NULL, /* device */ - content); - - surface->backing = backing; - - return &surface->base; -} -slim_hidden_def (_cairo_test_fallback16_surface_create); - -static cairo_surface_t * -_test_fallback16_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - assert (CAIRO_CONTENT_VALID (content)); - - return _cairo_test_fallback16_surface_create (content, width, height); -} - -static cairo_status_t -_test_fallback16_surface_finish (void *abstract_surface) -{ - test_fallback16_surface_t *surface = abstract_surface; - - cairo_surface_destroy (surface->backing); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_fallback16_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_fallback16_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_source_image (surface->backing, - image_out, image_extra); -} - -static void -_test_fallback16_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_fallback16_surface_t *surface = abstract_surface; - - _cairo_surface_release_source_image (surface->backing, - image, image_extra); -} - -static cairo_status_t -_test_fallback16_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) -{ - test_fallback16_surface_t *surface = abstract_surface; - - return _cairo_surface_acquire_dest_image (surface->backing, - interest_rect, - image_out, - image_rect_out, - image_extra); -} - -static void -_test_fallback16_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) -{ - test_fallback16_surface_t *surface = abstract_surface; - - _cairo_surface_release_dest_image (surface->backing, - interest_rect, - image, - image_rect, - image_extra); -} - -static cairo_status_t -_test_fallback16_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) -{ - test_fallback16_surface_t *surface = abstract_surface; - - if (src->backend == surface->base.backend) { - *clone_offset_x = 0; - *clone_offset_y = 0; - *clone_out = cairo_surface_reference (src); - - return CAIRO_STATUS_SUCCESS; - } else { - return _cairo_surface_clone_similar (surface->backing, src, - src_x, src_y, - width, height, - clone_offset_x, clone_offset_y, - clone_out); - } -} - -static cairo_bool_t -_test_fallback16_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_fallback16_surface_t *surface = abstract_surface; - - return _cairo_surface_get_extents (surface->backing, rectangle); -} - -static const cairo_surface_backend_t test_fallback16_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_FALLBACK, - _test_fallback16_surface_finish, - _cairo_default_context_create, - - _test_fallback16_surface_create_similar, - NULL, /* create similar image */ - NULL, /* map to image */ - NULL, /* unmap image */ - - _test_fallback16_surface_acquire_source_image, - _test_fallback16_surface_release_source_image, - _test_fallback16_surface_acquire_dest_image, - _test_fallback16_surface_release_dest_image, - _test_fallback16_surface_clone_similar, - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_fallback16_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 */ - NULL /* snapshot */ -}; diff --git a/src/test-null-compositor-surface.c b/src/test-null-compositor-surface.c new file mode 100644 index 000000000..021dfe753 --- /dev/null +++ b/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 + */ + + +#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_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_bool_t +has_snapshot (void *_dst, + const cairo_pattern_t *pattern) +{ + return FALSE; +} + +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_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 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.has_snapshot = has_snapshot; + 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); +} diff --git a/src/test-fallback16-surface.h b/src/test-null-compositor-surface.h similarity index 70% rename from src/test-fallback16-surface.h rename to src/test-null-compositor-surface.h index 51a78d303..52d864b28 100644 --- a/src/test-fallback16-surface.h +++ b/src/test-null-compositor-surface.h @@ -1,7 +1,6 @@ /* cairo - a vector graphics library with display and print output * - * Copyright © 2005 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 @@ -28,25 +27,34 @@ * * The Original Code is the cairo graphics library. * - * The Initial Developer of the Original Code is Red Hat, Inc. + * The Initial Developer of the Original Code is Intel Corporation * * Contributor(s): - * Carl Worth * Chris Wilson */ -#ifndef TEST_FALLBACK16_SURFACE_H -#define TEST_FALLBACK16_SURFACE_H +#ifndef TEST_NULL_COMPOSITOR_SURFACE_H +#define TEST_NULL_COMPOSITOR_SURFACE_H #include "cairo.h" CAIRO_BEGIN_DECLS cairo_surface_t * -_cairo_test_fallback16_surface_create (cairo_content_t content, - int width, - int height); +_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_FALLBACK16_SURFACE_H */ +#endif /* TEST_NULL_COMPOSITOR_SURFACE_H */ diff --git a/src/test-null-surface.c b/src/test-null-surface.c deleted file mode 100644 index 1eae89c12..000000000 --- a/src/test-null-surface.c +++ /dev/null @@ -1,195 +0,0 @@ -/* 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. - * - * Contributor(s): - * Chris Wilson - */ - -/* This isn't a "real" surface, but just something to be used by the - * test suite to test a mythical backend that does nothing, i.e. it - * solely useful for measuring the overhead of the cairo public API. - */ - -#include "cairoint.h" - -#include "test-null-surface.h" - -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" - -slim_hidden_proto (_cairo_test_null_surface_create); - -static cairo_int_status_t -_return_success (void) -{ - return CAIRO_STATUS_SUCCESS; -} - -/* These typedefs are just to silence the compiler... */ -typedef cairo_int_status_t -(*_paint_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_mask_func) (void *surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_pattern_t *mask, - const cairo_clip_t *clip); - -typedef cairo_int_status_t -(*_stroke_func) (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); - -typedef cairo_int_status_t -(*_fill_func) (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); - -typedef cairo_int_status_t -(*_show_glyphs_func) (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, - int *remaining_glyphs); - -typedef cairo_int_status_t -(*_show_text_glyphs_func) (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); - -static cairo_surface_t * -_cairo_null_surface_create_similar (void *other, - cairo_content_t content, - int width, int height) -{ - return _cairo_test_null_surface_create (content); -} - -static cairo_bool_t -_cairo_null_surface_get_extents (void *surface, - cairo_rectangle_int_t *extents) -{ - return FALSE; -} - -static cairo_bool_t -_cairo_null_surface_has_show_text_glyphs (void *surface) -{ - return TRUE; -} - -static const cairo_surface_backend_t null_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_NULL, - NULL, /* finish */ - _cairo_default_context_create, - - _cairo_null_surface_create_similar, - NULL, /* create similar image */ - NULL, /* map_to_image */ - NULL, /* unmap image */ - - 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, /* copy_page */ - NULL, /* show_page */ - _cairo_null_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 */ - (_paint_func) _return_success, /* paint */ - (_mask_func) _return_success, /* mask */ - (_stroke_func) _return_success, /* stroke */ - (_fill_func) _return_success, /* fill */ - (_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 */ - _cairo_null_surface_has_show_text_glyphs, - (_show_text_glyphs_func) _return_success, /* show_text_glyphs */ -}; - -cairo_surface_t * -_cairo_test_null_surface_create (cairo_content_t content) -{ - cairo_surface_t *surface; - - surface = malloc (sizeof (cairo_surface_t)); - if (unlikely (surface == NULL)) { - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - } - - _cairo_surface_init (surface, - &null_surface_backend, - NULL, /* device */ - content); - - return surface; -} -slim_hidden_def (_cairo_test_null_surface_create); diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c index 1e95c5fc5..e3c8a917b 100644 --- a/src/test-paginated-surface.c +++ b/src/test-paginated-surface.c @@ -52,6 +52,7 @@ #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; @@ -254,23 +255,16 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { 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 */ + _test_paginated_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 is the more "modern" section of the surface backend * interface which is mostly just drawing functions */ @@ -279,14 +273,8 @@ static const cairo_surface_backend_t test_paginated_surface_backend = { _test_paginated_surface_mask, _test_paginated_surface_stroke, _test_paginated_surface_fill, + NULL, /* fill-stroke */ NULL, /* replaced by show_text_glyphs */ - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - _test_paginated_surface_has_show_text_glyphs, _test_paginated_surface_show_text_glyphs }; diff --git a/src/test-wrapping-surface.c b/src/test-wrapping-surface.c deleted file mode 100644 index 7552886bb..000000000 --- a/src/test-wrapping-surface.c +++ /dev/null @@ -1,281 +0,0 @@ -/* cairo - a vector graphics library with display and print output - * - * Copyright © 2005 Red Hat, Inc - * Copyright © 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 - * Chris Wilson - */ - -/* Another mythical surface that exists to simply wrap another - do nothing - * itself but forward the calls onto a target surface. - */ - -#include "cairoint.h" - -#include "test-wrapping-surface.h" - -#include "cairo-default-context-private.h" -#include "cairo-error-private.h" -#include "cairo-surface-wrapper-private.h" - -typedef struct _test_wrapping_surface { - cairo_surface_t base; - cairo_surface_wrapper_t wrapper; -} test_wrapping_surface_t; - -static const cairo_surface_backend_t test_wrapping_surface_backend; - -slim_hidden_proto (_cairo_test_wrapping_surface_create); - -cairo_surface_t * -_cairo_test_wrapping_surface_create (cairo_surface_t *target) -{ - test_wrapping_surface_t *surface; - - if (unlikely (target->status)) - return _cairo_surface_create_in_error (target->status); - - surface = malloc (sizeof (test_wrapping_surface_t)); - if (unlikely (surface == NULL)) - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); - - _cairo_surface_init (&surface->base, - &test_wrapping_surface_backend, - NULL, /* device */ - target->content); - - _cairo_surface_wrapper_init (&surface->wrapper, target); - - return &surface->base; -} -slim_hidden_def (_cairo_test_wrapping_surface_create); - -static cairo_surface_t * -_test_wrapping_surface_create_similar (void *abstract_surface, - cairo_content_t content, - int width, - int height) -{ - - test_wrapping_surface_t *surface = abstract_surface; - - return _cairo_test_wrapping_surface_create ( - _cairo_surface_wrapper_create_similar (&surface->wrapper, - content, width, height)); -} - -static cairo_status_t -_test_wrapping_surface_finish (void *abstract_surface) -{ - test_wrapping_surface_t *surface = abstract_surface; - - _cairo_surface_wrapper_fini (&surface->wrapper); - - return CAIRO_STATUS_SUCCESS; -} - -static cairo_status_t -_test_wrapping_surface_acquire_source_image (void *abstract_surface, - cairo_image_surface_t **image_out, - void **image_extra) -{ - test_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_acquire_source_image (&surface->wrapper, - image_out, image_extra); -} - -static void -_test_wrapping_surface_release_source_image (void *abstract_surface, - cairo_image_surface_t *image, - void *image_extra) -{ - test_wrapping_surface_t *surface = abstract_surface; - - _cairo_surface_wrapper_release_source_image (&surface->wrapper, - image, image_extra); -} - -static cairo_bool_t -_test_wrapping_surface_get_extents (void *abstract_surface, - cairo_rectangle_int_t *rectangle) -{ - test_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_get_extents (&surface->wrapper, rectangle); -} - -static cairo_int_status_t -_test_wrapping_surface_paint (void *abstract_surface, - cairo_operator_t op, - const cairo_pattern_t *source, - const cairo_clip_t *clip) -{ - test_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_paint (&surface->wrapper, op, source, clip); -} - -static cairo_int_status_t -_test_wrapping_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_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_mask (&surface->wrapper, - op, source, mask, clip); -} - -static cairo_int_status_t -_test_wrapping_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_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_stroke (&surface->wrapper, - op, source, - path, style, - ctm, ctm_inverse, - tolerance, antialias, - clip); -} - -static cairo_int_status_t -_test_wrapping_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_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_fill (&surface->wrapper, - op, source, - path, fill_rule, - tolerance, antialias, - clip); -} - -static cairo_bool_t -_test_wrapping_surface_has_show_text_glyphs (void *abstract_surface) -{ - test_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_has_show_text_glyphs (&surface->wrapper); -} - -static cairo_int_status_t -_test_wrapping_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_wrapping_surface_t *surface = abstract_surface; - - return _cairo_surface_wrapper_show_text_glyphs (&surface->wrapper, - op, source, - utf8, utf8_len, - glyphs, num_glyphs, - clusters, num_clusters, - cluster_flags, - scaled_font, - clip); -} - -static const cairo_surface_backend_t test_wrapping_surface_backend = { - CAIRO_INTERNAL_SURFACE_TYPE_TEST_WRAPPING, - _test_wrapping_surface_finish, - _cairo_default_context_create, - - _test_wrapping_surface_create_similar, - NULL, /* create similar image */ - NULL, /* map to image */ - NULL, /* unmap image */ - - _test_wrapping_surface_acquire_source_image, - _test_wrapping_surface_release_source_image, - NULL, NULL, /* dest_image */ - NULL, /* clone_similar */ - NULL, /* composite */ - NULL, /* fill_rectangles */ - NULL, /* composite_trapezoids */ - NULL, /* create_span_renderer */ - NULL, /* check_span_renderer */ - NULL, /* copy_page */ - NULL, /* show_page */ - _test_wrapping_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 */ - - _test_wrapping_surface_paint, - _test_wrapping_surface_mask, - _test_wrapping_surface_stroke, - _test_wrapping_surface_fill, - NULL, /* replaced by show_text_glyphs */ - - NULL, /* snapshot */ - NULL, /* is_similar */ - NULL, /* fill_stroke */ - NULL, /* create_solid_pattern_surface */ - NULL, /* can_repaint_solid_pattern_surface */ - - _test_wrapping_surface_has_show_text_glyphs, - _test_wrapping_surface_show_text_glyphs - - /* XXX wrap fill-stroke and show_glyphs */ -}; diff --git a/test/Makefile.refs b/test/Makefile.refs index b09f78103..68f721aca 100644 --- a/test/Makefile.refs +++ b/test/Makefile.refs @@ -6,6 +6,8 @@ REFERENCE_IMAGES = \ a1-bug.ref.png \ a1-bug.xlib.ref.png \ a1-clip-fill-equal.ref.png \ + a1-clip-fill-rule.argb32.ref.png \ + a1-clip-fill-rule.rgb24.ref.png \ a1-clip-fill.ref.png \ a1-clip-paint.ref.png \ a1-clip-stroke.ref.png \ @@ -18,6 +20,7 @@ REFERENCE_IMAGES = \ a1-rasterisation-rectangles.ref.png \ a1-rasterisation-triangles.quartz.xfail.png \ a1-rasterisation-triangles.ref.png \ + a1-rectilinear-grid.ref.png \ a1-sample.ref.png \ a1-traps-sample.quartz.xfail.png \ a1-traps-sample.ref.png \ @@ -885,6 +888,8 @@ REFERENCE_IMAGES = \ over-between-source.svg12.rgb24.xfail.png \ over-between-source.xlib.ref.png \ over-between-source.xlib.rgb24.ref.png \ + overlapping-boxes.argb32.ref.png \ + overlapping-boxes.rgb24.ref.png \ overlapping-dash-caps.ref.png \ overlapping-glyphs.argb32.ref.png \ overlapping-glyphs.pdf.argb32.xfail.png \ @@ -1155,6 +1160,7 @@ REFERENCE_IMAGES = \ set-source.ref.png \ set-source.rgb24.ref.png \ shape-general-convex.ref.png \ + shape-sierpinski.ref.png \ show-glyphs-advance.image16.ref.png \ show-glyphs-advance.ps.ref.png \ show-glyphs-advance.quartz.ref.png \ @@ -1299,9 +1305,6 @@ REFERENCE_IMAGES = \ svg-surface-source.rgb24.ref.png \ svg-surface-source.svg12.argb32.xfail.png \ svg-surface-source.svg12.rgb24.xfail.png \ - test-fallback16-surface-source.ps.ref.png \ - test-fallback16-surface-source.svg12.argb32.xfail.png \ - test-fallback16-surface-source.svg12.rgb24.xfail.png \ text-antialias-gray.image16.ref.png \ text-antialias-gray.quartz.ref.png \ text-antialias-gray.ref.png \ diff --git a/test/Makefile.sources b/test/Makefile.sources index 526ac8a8e..05bcdd0c1 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -211,6 +211,7 @@ test_sources = \ over-around-source.c \ over-below-source.c \ over-between-source.c \ + overlapping-boxes.c \ overlapping-glyphs.c \ overlapping-dash-caps.c \ paint.c \ @@ -273,6 +274,7 @@ test_sources = \ show-glyphs-many.c \ show-text-current-point.c \ shape-general-convex.c \ + shape-sierpinski.c \ skew-extreme.c \ smask.c \ smask-fill.c \ @@ -373,9 +375,6 @@ svg_surface_test_sources = \ svg-clip.c \ svg-surface-source.c -test_fallback16_surface_test_sources = \ - test-fallback16-surface-source.c - xcb_surface_test_sources = \ xcb-surface-source.c diff --git a/test/a1-clip-fill-rule.argb32.ref.png b/test/a1-clip-fill-rule.argb32.ref.png new file mode 100644 index 000000000..c3ba9dd5f Binary files /dev/null and b/test/a1-clip-fill-rule.argb32.ref.png differ diff --git a/test/a1-clip-fill-rule.rgb24.ref.png b/test/a1-clip-fill-rule.rgb24.ref.png new file mode 100644 index 000000000..6fe9346ba Binary files /dev/null and b/test/a1-clip-fill-rule.rgb24.ref.png differ diff --git a/test/a1-rectilinear-grid.ref.png b/test/a1-rectilinear-grid.ref.png new file mode 100644 index 000000000..2dfb85e13 Binary files /dev/null and b/test/a1-rectilinear-grid.ref.png differ diff --git a/test/cairo-test-trace.c b/test/cairo-test-trace.c index 146ca6fe5..296a229d6 100644 --- a/test/cairo-test-trace.c +++ b/test/cairo-test-trace.c @@ -93,8 +93,10 @@ #define DEBUG 0 +#define ignore_image_differences 0 /* XXX make me a cmdline option! */ + #define DATA_SIZE (256 << 20) -#define SHM_PATH_XXX "/shmem-cairo-trace" +#define SHM_PATH_XXX "/.shmem-cairo-trace" typedef struct _test_trace { /* Options from command-line */ @@ -163,6 +165,8 @@ struct surface_tag { }; static const cairo_user_data_key_t surface_tag; +#define TARGET_NAME(T) ((T) ? (T)->name : "recording") + #if CAIRO_HAS_REAL_PTHREAD #define tr_die(t) t->is_recording ? pthread_exit(NULL) : exit(1) #else @@ -253,7 +257,7 @@ send_recording_surface (test_runner_t *tr, closure->end_line, -1, width, height, - cairo_surface_get_type (closure->surface) == CAIRO_SURFACE_TYPE_IMAGE ? 0 : (long) closure->surface, + (long) closure->surface, }; unsigned long offset; unsigned long serial; @@ -320,7 +324,8 @@ send_surface (test_runner_t *tr, unsigned long serial; if (DEBUG > 1) { - printf ("send-surface: '%s'\n", tr->name); + printf ("send-surface: '%s', is-recording? %d\n", + tr->name, tr->is_recording); } if (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE) { @@ -390,7 +395,8 @@ send_surface (test_runner_t *tr, static cairo_surface_t * _surface_create (void *closure, cairo_content_t content, - double width, double height) + double width, double height, + long uid) { test_runner_t *tr = closure; cairo_surface_t *surface; @@ -416,6 +422,13 @@ _context_create (void *closure, cairo_surface_t *surface) test_runner_t *tr = closure; struct context_closure *l; + if (DEBUG) { + fprintf (stderr, "%s: starting context %lu on line %d\n", + tr->name ? tr->name : "recording" , + tr->context_id + 1, + cairo_script_interpreter_get_line_number (tr->csi)); + } + l = xmalloc (sizeof (*l)); l->next = tr->contexts; l->start_line = cairo_script_interpreter_get_line_number (tr->csi); @@ -438,6 +451,12 @@ _context_destroy (void *closure, void *ptr) while ((l = *prev) != NULL) { if (l->context == ptr) { + if (DEBUG) { + fprintf (stderr, "%s: context %lu complete on line %d\n", + tr->name ? tr->name : "recording" , + tr->context_id, + cairo_script_interpreter_get_line_number (tr->csi)); + } l->end_line = cairo_script_interpreter_get_line_number (tr->csi); if (cairo_surface_status (l->surface) == CAIRO_STATUS_SUCCESS) { @@ -623,7 +642,7 @@ record (void *arg) * 2. Runs in the same process, but separate thread. */ static pid_t -spawn_recorder (const char *socket_path, const char *trace) +spawn_recorder (const char *socket_path, const char *trace, test_runner_t **out) { test_runner_t *tr; pthread_t id; @@ -652,7 +671,7 @@ spawn_recorder (const char *socket_path, const char *trace) tr->trace = trace; tr->surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, - 0, 0); + NULL); if (tr->surface == NULL) { cleanup_recorder (tr); return -1; @@ -667,6 +686,8 @@ spawn_recorder (const char *socket_path, const char *trace) } pthread_attr_destroy (&attr); + + *out = tr; return pid; } #endif @@ -814,6 +835,11 @@ matches_reference (struct slave *slave) bb += stride; } break; + + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_RGB16_565: + case CAIRO_FORMAT_INVALID: + assert (0); } return TRUE; @@ -825,6 +851,9 @@ check_images (struct slave *slaves, int num_slaves) { int n; + if (ignore_image_differences) + return TRUE; + for (n = 0; n < num_slaves; n++) { if (slaves[n].reference == NULL) continue; @@ -860,30 +889,60 @@ write_images (const char *trace, struct slave *slave, int num_slaves) } } +static void +write_result (const char *trace, struct slave *slave) +{ + static int index; + char *filename; + + xasprintf (&filename, "%s-%s-pass-%d-%d-%d.png", + trace, slave->target->name, ++index, + slave->start_line, slave->end_line); + cairo_surface_write_to_png (slave->image, filename); + free (filename); +} + static void write_trace (const char *trace, struct slave *slave) { #if CAIRO_HAS_SCRIPT_SURFACE - cairo_device_t *ctx, - cairo_surface_t *script; + cairo_device_t *script; char *filename; - cairo_t *cr; + + assert (slave->is_recording); xasprintf (&filename, "%s-fail.trace", trace); - ctx = cairo_script_create (filename); - script = cairo_script_surface_create (ctx, - cairo_surface_get_content (slave->image), - slave->width, - slave->height); - cairo_device_destroy (ctx); + + script = cairo_script_create (filename); + cairo_script_from_recording_surface (script, slave->image); + cairo_device_destroy (script); + free (filename); +#endif +} - cr = cairo_create (slave->image); - cairo_set_source_surface (cr, script, 0, 0); - cairo_paint (cr); - cairo_destroy (cr); +static void +dump_traces (test_runner_t *tr, + const char *trace, + const char *target, + const char *fail) +{ +#if CAIRO_HAS_SCRIPT_SURFACE + struct context_closure *c; + + for (c = tr->contexts; c; c = c->next) { + cairo_device_t *script; + char *filename; + + xasprintf (&filename, "%s-%s-%s.%lu.trace", + trace, target, fail, c->start_line); + + script = cairo_script_create (filename); + cairo_script_from_recording_surface (script, c->surface); + cairo_device_destroy (script); - cairo_surface_destroy (script); + free (filename); + } #endif } @@ -907,14 +966,16 @@ allocate_image_for_slave (uint8_t *base, slave->height = rq.height; if (DEBUG > 1) { - printf ("allocate-image-for-slave: %s %lu [%lu, %lu] %ldx%ld=> %lu\n", - slave->target->name, + printf ("allocate-image-for-slave: %s %lu [%lu, %lu] %ldx%ld stride=%lu => %lu, is-recording? %d\n", + TARGET_NAME (slave->target), slave->image_serial, slave->start_line, slave->end_line, slave->width, slave->height, - offset); + rq.stride, + offset, + slave->is_recording); } if (slave->is_recording) { @@ -1031,14 +1092,14 @@ test_run (void *base, offset = image, &slaves[i]); if (! writen (pfd[n].fd, &offset, sizeof (offset))) - goto out; + goto done; } else { readn (pfd[n].fd, &slaves[i].image_ready, sizeof (slaves[i].image_ready)); if (DEBUG) { printf ("slave '%s' reports completion on %lu (expecting %lu)\n", - slaves[i].target->name, + TARGET_NAME (slaves[i].target), slaves[i].image_ready, slaves[i].image_serial); } @@ -1062,7 +1123,8 @@ test_run (void *base, if (DEBUG > 1) { printf ("all saves report completion\n"); } - if (! check_images (slaves, num_slaves)) { + if (slaves[0].end_line >= slaves[0].start_line && + ! check_images (slaves, num_slaves)) { error->context_id = slaves[0].image_serial; error->start_line = slaves[0].start_line; error->end_line = slaves[0].end_line; @@ -1082,6 +1144,8 @@ test_run (void *base, goto out; } + if (0) write_result (trace, &slaves[1]); + /* ack */ for (i = 0; i < num_slaves; i++) { cairo_surface_destroy (slaves[i].image); @@ -1089,7 +1153,7 @@ test_run (void *base, if (DEBUG > 1) { printf ("sending continuation to '%s'\n", - slaves[i].target->name); + TARGET_NAME (slaves[i].target)); } if (! writen (slaves[i].fd, &slaves[i].image_serial, @@ -1129,7 +1193,6 @@ test_run (void *base, slaves[n].image_serial = 0; slaves[n].image_ready = 0; - ret = FALSE; } free (pfd); @@ -1137,51 +1200,6 @@ test_run (void *base, return ret; } -/* Paginated surfaces require finalization and external converters and so - * are not suitable for this basic technique. - */ -static cairo_bool_t -target_is_measurable (const cairo_boilerplate_target_t *target) -{ - if (target->content != CAIRO_CONTENT_COLOR_ALPHA) - return FALSE; - - switch (target->expected_type) { - case CAIRO_SURFACE_TYPE_IMAGE: - if (strcmp (target->name, "pdf") == 0 || - strcmp (target->name, "ps") == 0) - { - return FALSE; - } - else - { - return TRUE; - } - case CAIRO_SURFACE_TYPE_XLIB: - case CAIRO_SURFACE_TYPE_XCB: - case CAIRO_SURFACE_TYPE_GLITZ: - case CAIRO_SURFACE_TYPE_QUARTZ: - case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: - case CAIRO_SURFACE_TYPE_WIN32: - case CAIRO_SURFACE_TYPE_BEOS: - case CAIRO_SURFACE_TYPE_DIRECTFB: - case CAIRO_SURFACE_TYPE_OS2: - case CAIRO_SURFACE_TYPE_QT: - return TRUE; - - case CAIRO_SURFACE_TYPE_PDF: - case CAIRO_SURFACE_TYPE_PS: - case CAIRO_SURFACE_TYPE_SCRIPT: - case CAIRO_SURFACE_TYPE_SVG: - case CAIRO_SURFACE_TYPE_WIN32_PRINTING: - case CAIRO_SURFACE_TYPE_RECORDING: - default: - return FALSE; - } - - return TRUE; -} - static int server_socket (const char *socket_path) { @@ -1241,6 +1259,7 @@ _test_trace (test_trace_t *test, const char *shm_path = SHM_PATH_XXX; const cairo_boilerplate_target_t *target, *image; struct slave *slaves, *s; + test_runner_t *recorder = NULL; pid_t slave; char socket_dir[] = "/tmp/cairo-test-trace.XXXXXX"; char *socket_path; @@ -1249,6 +1268,9 @@ _test_trace (test_trace_t *test, void *base; cairo_bool_t ret = FALSE; + if (DEBUG) + printf ("setting up trace '%s'\n", trace); + /* create a socket to control the test runners */ if (mkdtemp (socket_dir) == NULL) { fprintf (stderr, "Unable to create temporary name for socket\n"); @@ -1265,8 +1287,8 @@ _test_trace (test_trace_t *test, /* allocate some shared memory */ fd = server_shm (shm_path); if (fd == -1) { - fprintf (stderr, "Unable to create shared memory '%s'\n", - shm_path); + fprintf (stderr, "Unable to create shared memory '%s': %s\n", + shm_path, strerror (errno)); goto cleanup_sk; } @@ -1277,7 +1299,7 @@ _test_trace (test_trace_t *test, #if CAIRO_HAS_REAL_PTHREAD /* set-up a recording-surface to reconstruct errors */ - slave = spawn_recorder (socket_path, trace); + slave = spawn_recorder (socket_path, trace, &recorder); if (slave < 0) { fprintf (stderr, "Unable to create recording surface\n"); goto cleanup_sk; @@ -1297,7 +1319,12 @@ _test_trace (test_trace_t *test, struct slave *master; target = test->targets[i]; - if (target == image || ! target_is_measurable (target)) + + if (DEBUG) + printf ("setting up target[%d]? '%s' (image? %d, measurable? %d)\n", + i, target->name, target == image, target->is_measurable); + + if (target == image || ! target->is_measurable) continue; /* find a matching slave to use as a reference for this target */ @@ -1356,14 +1383,22 @@ _test_trace (test_trace_t *test, while (s-- > slaves) { int status; + if (s->fd != -1) + close (s->fd); + + cairo_surface_destroy (s->image); + cairo_surface_destroy (s->difference); + if (s->is_recording) /* in-process */ continue; kill (s->pid, SIGKILL); waitpid (s->pid, &status, 0); - ret &= WIFEXITED (status) && WEXITSTATUS (status) == 0; - if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) + if (WIFSIGNALED (status) && WTERMSIG(status) != SIGKILL) { fprintf (stderr, "%s crashed\n", s->target->name); + if (recorder) + dump_traces (recorder, trace, s->target->name, "crash"); + } } free (slaves); shm_unlink (shm_path); @@ -1630,6 +1665,8 @@ main (int argc, char *argv[]) parse_options (&test, argc, argv); + shm_unlink (SHM_PATH_XXX); + if (getenv ("CAIRO_TRACE_DIR") != NULL) trace_dir = getenv ("CAIRO_TRACE_DIR"); diff --git a/test/clear-source.c b/test/clear-source.c index a4c3db2fa..5410932fc 100644 --- a/test/clear-source.c +++ b/test/clear-source.c @@ -128,7 +128,7 @@ static operation_t operations[] = { glyphs }; - static cairo_test_status_t +static cairo_test_status_t draw (cairo_t *cr, int width, int height) { cairo_content_t contents[] = { CAIRO_CONTENT_COLOR_ALPHA, CAIRO_CONTENT_COLOR, CAIRO_CONTENT_ALPHA }; diff --git a/test/clip-fill-rule.c b/test/clip-fill-rule.c index 8351d8680..035b9a836 100644 --- a/test/clip-fill-rule.c +++ b/test/clip-fill-rule.c @@ -66,9 +66,22 @@ draw (cairo_t *cr, int width, int height) return CAIRO_TEST_SUCCESS; } +static cairo_test_status_t +a1_draw (cairo_t *cr, int width, int height) +{ + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + return draw (cr, width, height); +} + CAIRO_TEST (clip_fill_rule, "Tests interaction of clipping with cairo_set_fill_rule", "clip", /* keywords */ NULL, /* requirements */ STAR_SIZE * 2 + 2, STAR_SIZE + 2, NULL, draw) +CAIRO_TEST (a1_clip_fill_rule, + "Tests interaction of clipping with cairo_set_fill_rule", + "clip", /* keywords */ + NULL, /* requirements */ + STAR_SIZE * 2 + 2, STAR_SIZE + 2, + NULL, a1_draw) diff --git a/test/map-to-image.c b/test/map-to-image.c index b7d6df8a5..0262245a8 100644 --- a/test/map-to-image.c +++ b/test/map-to-image.c @@ -89,7 +89,7 @@ static cairo_test_status_t bit (cairo_t *cr, int width, int height) { cairo_surface_t *surface; - cairo_rectangle_t extents; + cairo_rectangle_int_t extents; cairo_format_t format; uint8_t *data; @@ -117,7 +117,7 @@ static cairo_test_status_t fill (cairo_t *cr, int width, int height) { cairo_surface_t *surface; - cairo_rectangle_t extents; + cairo_rectangle_int_t extents; cairo_t *cr2; extents.x = extents.y = extents.width = extents.height = 1; diff --git a/test/overlapping-boxes.argb32.ref.png b/test/overlapping-boxes.argb32.ref.png new file mode 100644 index 000000000..278e62a84 Binary files /dev/null and b/test/overlapping-boxes.argb32.ref.png differ diff --git a/test/overlapping-boxes.c b/test/overlapping-boxes.c new file mode 100644 index 000000000..92211ee06 --- /dev/null +++ b/test/overlapping-boxes.c @@ -0,0 +1,96 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +/* Not strictly overlapping, but it does highlight the error in + * an optimisation of fill-box handling that I frequently am + * tempted to write. + */ + +#include "cairo-test.h" + +#define WIDTH (20) +#define HEIGHT (20) + +static void +border (cairo_t *cr) +{ + cairo_rectangle (cr, 1, 1, 8, 8); + cairo_rectangle (cr, 1.25, 1.25, 7.5, 7.5); + cairo_rectangle (cr, 1.75, 1.75, 6.5, 6.5); + cairo_rectangle (cr, 2, 2, 6, 6); +} + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + border (cr); + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + cairo_fill (cr); + + cairo_translate (cr, 10, 0); + + border (cr); + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_fill (cr); + + cairo_translate (cr, 0, 10); + + cairo_rectangle (cr, 0, 0, 10, 10); + cairo_clip (cr); + + border (cr); + cairo_set_source_rgb (cr, 0, 1, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_IN); + cairo_fill (cr); + + cairo_reset_clip (cr); + + cairo_translate (cr, -10, 0); + + cairo_rectangle (cr, 0, 0, 10, 10); + cairo_clip (cr); + + border (cr); + cairo_set_source_rgb (cr, 0, 0, 1); + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + cairo_fill (cr); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (overlapping_boxes, + "A sub-pixel double border to highlight the danger in an easy optimisation", + "fill", /* keywords */ + NULL, /* requirements */ + WIDTH, HEIGHT, + NULL, draw) diff --git a/test/overlapping-boxes.rgb24.ref.png b/test/overlapping-boxes.rgb24.ref.png new file mode 100644 index 000000000..f35d0e6b3 Binary files /dev/null and b/test/overlapping-boxes.rgb24.ref.png differ diff --git a/test/rectilinear-grid.c b/test/rectilinear-grid.c index 1baadf732..8c6b7fcad 100644 --- a/test/rectilinear-grid.c +++ b/test/rectilinear-grid.c @@ -70,9 +70,23 @@ draw (cairo_t *cr, int width, int height) return CAIRO_TEST_SUCCESS; } +static cairo_test_status_t +aligned (cairo_t *cr, int width, int height) +{ + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + return draw (cr, width, height); +} + CAIRO_TEST (rectilinear_grid, "Test rectilinear rasterizer (covering partial pixels)", "rectilinear", /* keywords */ NULL, /* requirements */ SIZE, SIZE, NULL, draw) + +CAIRO_TEST (a1_rectilinear_grid, + "Test rectilinear rasterizer (covering whole pixels)", + "rectilinear", /* keywords */ + NULL, /* requirements */ + SIZE, SIZE, + NULL, aligned) diff --git a/test/shape-sierpinski.c b/test/shape-sierpinski.c new file mode 100644 index 000000000..9d2017067 --- /dev/null +++ b/test/shape-sierpinski.c @@ -0,0 +1,85 @@ +/* + * Copyright © 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. + * + * Author: Chris Wilson + */ + +/* I thought I spied a bug... */ + +#include "cairo-test.h" + +#define WIDTH (1024) +#define HEIGHT (600) + +static const double m_1_sqrt_3 = 0.577359269; + +static void +T (cairo_t *cr, int size) +{ + cairo_move_to (cr, 0, 0); + cairo_line_to (cr, size, 0); + cairo_line_to (cr, size/2, size*m_1_sqrt_3); + + size /= 2; + if (size >= 4) { + T (cr, size); + cairo_save (cr); { + cairo_translate (cr, size, 0); + T (cr, size); + } cairo_restore (cr); + cairo_save (cr); { + cairo_translate (cr, size/2, size*m_1_sqrt_3); + T (cr, size); + } cairo_restore (cr); + } +} + +static cairo_test_status_t +draw (cairo_t *cr, int width, int height) +{ + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + + cairo_translate (cr, 0, 8); + + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_line_width (cr, 1.); + + T (cr, WIDTH); + + cairo_translate (cr, 0, 2*HEIGHT-16); + cairo_scale (cr, 1, -1); + + T (cr, WIDTH); + + cairo_stroke (cr); + + return CAIRO_TEST_SUCCESS; +} + +CAIRO_TEST (shape_sierpinski, + "A fractal triangle", + "stroke", /* keywords */ + NULL, /* requirements */ + WIDTH, 2*HEIGHT, + NULL, draw) diff --git a/test/shape-sierpinski.ref.png b/test/shape-sierpinski.ref.png new file mode 100644 index 000000000..69755d27a Binary files /dev/null and b/test/shape-sierpinski.ref.png differ diff --git a/test/test-fallback16-surface-source.c b/test/test-fallback16-surface-source.c deleted file mode 100644 index 7e9f920fe..000000000 --- a/test/test-fallback16-surface-source.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2009 Chris Wilson - * - * 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 - * Chris Wilson not be used in advertising or publicity pertaining to - * distribution of the software without specific, written prior - * permission. Chris Wilson makes no representations about the - * suitability of this software for any purpose. It is provided "as - * is" without express or implied warranty. - * - * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL CHRIS WILSON 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. - * - * Author: Chris Wilson - */ - -#include "cairo-test.h" -#include - -#include "surface-source.c" - -static cairo_surface_t * -create_source_surface (int size) -{ - return _cairo_test_fallback16_surface_create (CAIRO_CONTENT_COLOR_ALPHA, - size, size); -} - -CAIRO_TEST (test_fallback16_surface_source, - "Test using a 16-bit image (e.g. a low bit-depth XServer) surface as the source", - "source", /* keywords */ - NULL, /* requirements */ - SIZE, SIZE, - preamble, draw) diff --git a/test/test-fallback16-surface-source.ps.ref.png b/test/test-fallback16-surface-source.ps.ref.png deleted file mode 100644 index f686a0687..000000000 Binary files a/test/test-fallback16-surface-source.ps.ref.png and /dev/null differ diff --git a/test/test-fallback16-surface-source.svg12.argb32.xfail.png b/test/test-fallback16-surface-source.svg12.argb32.xfail.png deleted file mode 100644 index 6ebcaf9a1..000000000 Binary files a/test/test-fallback16-surface-source.svg12.argb32.xfail.png and /dev/null differ diff --git a/test/test-fallback16-surface-source.svg12.rgb24.xfail.png b/test/test-fallback16-surface-source.svg12.rgb24.xfail.png deleted file mode 100644 index 6ebcaf9a1..000000000 Binary files a/test/test-fallback16-surface-source.svg12.rgb24.xfail.png and /dev/null differ diff --git a/test/zero-mask.c b/test/zero-mask.c index 29edd0a55..3d2f34d0e 100644 --- a/test/zero-mask.c +++ b/test/zero-mask.c @@ -92,7 +92,7 @@ mask_with_alpha_surface (cairo_t *cr) cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT); cairo_mask (cr, pattern); - + cairo_pattern_destroy (pattern); cairo_surface_destroy (surface); } @@ -106,7 +106,7 @@ mask_with_nonclear_surface (cairo_t *cr) 16, 8, 4); cairo_mask_surface (cr, surface, 0, 0); - + cairo_surface_destroy (surface); } @@ -175,7 +175,7 @@ draw (cairo_t *cr, int width, int height) for (op = 0; op < ARRAY_LENGTH (operators); op++) { cairo_set_operator (cr, operators[op]); - + for (i = 0; i < ARRAY_LENGTH (mask_funcs); i++) { cairo_save (cr); cairo_translate (cr, i * (RECT + SPACE), op * (RECT + SPACE)); diff --git a/util/cairo-script/cairo-script-interpreter.c b/util/cairo-script/cairo-script-interpreter.c index a5c812553..bdd525542 100644 --- a/util/cairo-script/cairo-script-interpreter.c +++ b/util/cairo-script/cairo-script-interpreter.c @@ -396,6 +396,7 @@ _csi_init (csi_t *ctx) ctx->status = CSI_STATUS_SUCCESS; ctx->ref_count = 1; + ctx->scanner.line_number = -1; status = _csi_hash_table_init (&ctx->strings, _intern_string_equal); if (status) diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c index 36dbbb906..ad1a2015d 100644 --- a/util/cairo-script/cairo-script-operators.c +++ b/util/cairo-script/cairo-script-operators.c @@ -3630,8 +3630,7 @@ _map_to_image (csi_t *ctx) csi_object_t obj; csi_array_t *array; csi_status_t status; - cairo_rectangle_t extents; - cairo_rectangle_t *r; + cairo_rectangle_int_t extents, *r; cairo_surface_t *surface; check (2); @@ -3647,11 +3646,12 @@ _map_to_image (csi_t *ctx) switch (array->stack.len) { case 0: r = NULL; + break; case 4: - extents.x = _csi_object_as_real (&array->stack.objects[0]); - extents.y = _csi_object_as_real (&array->stack.objects[1]); - extents.width = _csi_object_as_real (&array->stack.objects[2]); - extents.height = _csi_object_as_real (&array->stack.objects[3]); + extents.x = floor (_csi_object_as_real (&array->stack.objects[0])); + extents.y = floor (_csi_object_as_real (&array->stack.objects[1])); + extents.width = ceil (_csi_object_as_real (&array->stack.objects[2])); + extents.height = ceil (_csi_object_as_real (&array->stack.objects[3])); r = &extents; break; default: diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c index a6de76f1a..65f8fbac7 100644 --- a/util/cairo-trace/trace.c +++ b/util/cairo-trace/trace.c @@ -3625,7 +3625,7 @@ cairo_surface_create_similar_image (cairo_surface_t *other, cairo_surface_t * cairo_surface_map_to_image (cairo_surface_t *surface, - const cairo_rectangle_t *extents) + const cairo_rectangle_int_t *extents) { cairo_surface_t *ret; @@ -3638,18 +3638,14 @@ cairo_surface_map_to_image (cairo_surface_t *surface, Object *obj = _create_surface (ret); if (extents) { - _trace_printf ("[ %f %f %f %f ] map-to-image dup /s%ld exch def\n", + _trace_printf ("[%d %d %d %d] map-to-image\n", extents->x, extents->y, - extents->width, - extents->height, - obj->token); - obj->width = extents->width; + extents->width, extents->height); + obj->width = extents->width; obj->height = extents->height; } else { - _trace_printf ("[ ] map-to-image dup /s%ld exch def\n", - obj->token); + _trace_printf ("[ ] map-to-image\n"); } - obj->defined = TRUE; _push_object (obj); _write_unlock (); } @@ -3669,6 +3665,7 @@ cairo_surface_unmap_image (cairo_surface_t *surface, _trace_printf ("/s%ld /s%ld unmap-image\n", _get_surface_id (surface), _get_surface_id (image)); + _consume_operand (); _write_unlock (); } @@ -4892,24 +4889,52 @@ cairo_script_surface_create_for_target (cairo_device_t *device, #endif #if CAIRO_HAS_TEST_SURFACES -#include +#include +cairo_surface_t * +_cairo_test_paginated_surface_create (cairo_surface_t *surface) +{ + cairo_surface_t *ret; + + _enter_trace (); + + ret = DLCALL (_cairo_test_paginated_surface_create, surface); + + _emit_line_info (); + if (_write_lock ()) { + Object *obj = _create_surface (ret); + + /* XXX store initial data? */ + _trace_printf ("dict\n" + " /type /test-paginated set\n" + " /target s%ld set\n" + " surface dup /s%ld exch def\n", + _get_surface_id (surface), + obj->token); + _push_object (obj); + _write_unlock (); + } + + _exit_trace (); + return ret; +} + +#include + cairo_surface_t * -_cairo_test_fallback_surface_create (cairo_content_t content, - int width, - int height) +_cairo_test_fallback_compositor_surface_create (cairo_content_t content, int width, int height) { cairo_surface_t *ret; _enter_trace (); - ret = DLCALL (_cairo_test_fallback_surface_create, content, width, height); + ret = DLCALL (_cairo_test_fallback_compositor_surface_create, content, width, height); _emit_line_info (); if (_write_lock ()) { Object *obj = _create_surface (ret); _trace_printf ("dict\n" - " /type /test-fallback set\n" + " /type /test-fallback-compositor set\n" " /content //%s set\n" " /width %d set\n" " /height %d set\n" @@ -4917,8 +4942,6 @@ _cairo_test_fallback_surface_create (cairo_content_t content, _content_to_string (content), width, height, obj->token); - obj->width = width; - obj->height = height; obj->defined = TRUE; _push_object (obj); _write_unlock (); @@ -4928,27 +4951,29 @@ _cairo_test_fallback_surface_create (cairo_content_t content, return ret; } -#include cairo_surface_t * -_cairo_test_paginated_surface_create (cairo_surface_t *surface) +_cairo_test_mask_compositor_surface_create (cairo_content_t content, int width, int height) { cairo_surface_t *ret; _enter_trace (); - ret = DLCALL (_cairo_test_paginated_surface_create, surface); + ret = DLCALL (_cairo_test_mask_compositor_surface_create, content, width, height); _emit_line_info (); if (_write_lock ()) { Object *obj = _create_surface (ret); - /* XXX store initial data? */ _trace_printf ("dict\n" - " /type /test-paginated set\n" - " /target s%ld set\n" + " /type /test-mask-compositor set\n" + " /content //%s set\n" + " /width %d set\n" + " /height %d set\n" " surface dup /s%ld exch def\n", - _get_surface_id (surface), + _content_to_string (content), + width, height, obj->token); + obj->defined = TRUE; _push_object (obj); _write_unlock (); } @@ -4957,26 +4982,58 @@ _cairo_test_paginated_surface_create (cairo_surface_t *surface) return ret; } +cairo_surface_t * +_cairo_test_spans_compositor_surface_create (cairo_content_t content, int width, int height) +{ + cairo_surface_t *ret; + + _enter_trace (); + + ret = DLCALL (_cairo_test_spans_compositor_surface_create, content, width, height); + + _emit_line_info (); + if (_write_lock ()) { + Object *obj = _create_surface (ret); + + _trace_printf ("dict\n" + " /type /test-spans-compositor set\n" + " /content //%s set\n" + " /width %d set\n" + " /height %d set\n" + " surface dup /s%ld exch def\n", + _content_to_string (content), + width, height, + obj->token); + obj->defined = TRUE; + _push_object (obj); + _write_unlock (); + } + + _exit_trace (); + return ret; +} -#include cairo_surface_t * -_cairo_test_null_surface_create (cairo_content_t content) +_cairo_test_traps_compositor_surface_create (cairo_content_t content, int width, int height) { cairo_surface_t *ret; _enter_trace (); - ret = DLCALL (_cairo_test_null_surface_create, content); + ret = DLCALL (_cairo_test_traps_compositor_surface_create, content, width, height); _emit_line_info (); if (_write_lock ()) { Object *obj = _create_surface (ret); _trace_printf ("dict\n" - " /type /test-null set\n" + " /type /test-traps-compositor set\n" " /content //%s set\n" + " /width %d set\n" + " /height %d set\n" " surface dup /s%ld exch def\n", _content_to_string (content), + width, height, obj->token); obj->defined = TRUE; _push_object (obj); @@ -4986,6 +5043,7 @@ _cairo_test_null_surface_create (cairo_content_t content) _exit_trace (); return ret; } + #endif cairo_surface_t * diff --git a/util/show-polygon.c b/util/show-polygon.c index 9571363bd..fc1444dbb 100644 --- a/util/show-polygon.c +++ b/util/show-polygon.c @@ -49,6 +49,8 @@ typedef struct _PolygonViewClass { G_DEFINE_TYPE (PolygonView, polygon_view, GTK_TYPE_WIDGET) +double highlight = -1; + static void draw_edges (cairo_t *cr, polygon_t *p, gdouble sf, int dir) { int n; @@ -59,8 +61,8 @@ static void draw_edges (cairo_t *cr, polygon_t *p, gdouble sf, int dir) if (e->dir != dir) continue; - cairo_arc (cr, e->p1.x, e->p1.y, 3/sf, 0, 2*M_PI); - cairo_arc (cr, e->p2.x, e->p2.y, 3/sf, 0, 2*M_PI); + cairo_arc (cr, e->p1.x, e->p1.y, 2/sf, 0, 2*M_PI); + cairo_arc (cr, e->p2.x, e->p2.y, 2/sf, 0, 2*M_PI); cairo_fill (cr); } @@ -140,6 +142,17 @@ pixmap_create (PolygonView *self, cairo_surface_t *target) draw_polygon (cr, polygon, sf); } + + if (highlight != -1) { + cairo_move_to (cr, extents.p1.x, highlight); + cairo_line_to (cr, extents.p2.x, highlight); + cairo_set_source_rgb (cr, 0, .7, 0); + cairo_save (cr); + cairo_identity_matrix (cr); + cairo_set_line_width (cr, 1.); + cairo_stroke (cr); + cairo_restore (cr); + } } cairo_restore (cr); cairo_destroy (cr); @@ -228,6 +241,17 @@ polygon_view_draw (PolygonView *self, cairo_t *cr) draw_polygon (cr, polygon, zoom); } + + if (highlight != -1) { + cairo_move_to (cr, extents.p1.x, highlight); + cairo_line_to (cr, extents.p2.x, highlight); + cairo_set_source_rgb (cr, 0, .7, 0); + cairo_save (cr); + cairo_identity_matrix (cr); + cairo_set_line_width (cr, 1.); + cairo_stroke (cr); + cairo_restore (cr); + } } cairo_restore (cr); /* grid */ @@ -563,6 +587,9 @@ main (int argc, char **argv) fclose (file); } + if (argc > 2) + highlight = atof (argv[2]); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);