diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-04-24 00:50:33 +0100 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2011-08-13 09:58:02 +0100 |
commit | 1ccd269a3f33684bfbedcd94ad9bca56b1404143 (patch) | |
tree | 0563b9114a6ddd752142232e0ae55f3e75816a90 | |
parent | e849e7c9291d57c3749f499c7e410e7be452b455 (diff) |
skia: Update to use cairo_backend_t interface
Still hopelessly broken. Requires compiling cairo to use static linking
and then still requires manual linkage to workaround libtool. Lots of
functionality is still absent - we need to either find analogues to some
Cairo operations or implement fallbacks - but it is sufficient to
investigate how Skia functions in direct comparison with Cairo for
tessellation/rasterisation.
Caveat emptor.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r-- | build/Makefile.am.changelog | 2 | ||||
-rw-r--r-- | configure.ac | 15 | ||||
-rw-r--r-- | perf/Makefile.am | 5 | ||||
-rw-r--r-- | src/Makefile.sources | 5 | ||||
-rw-r--r-- | src/cairo-arc-private.h | 4 | ||||
-rw-r--r-- | src/cairo-backend-private.h | 1 | ||||
-rw-r--r-- | src/cairo-clip-private.h | 1 | ||||
-rw-r--r-- | src/cairo-composite-rectangles-private.h | 2 | ||||
-rw-r--r-- | src/cairo-error-private.h | 8 | ||||
-rw-r--r-- | src/cairo-freed-pool-private.h | 4 | ||||
-rw-r--r-- | src/cairo-skia.h | 18 | ||||
-rw-r--r-- | src/cairo-types-private.h | 11 | ||||
-rw-r--r-- | src/cairoint.h | 1 | ||||
-rw-r--r-- | src/skia/cairo-skia-context.cpp | 1740 | ||||
-rw-r--r-- | src/skia/cairo-skia-private.h | 110 | ||||
-rw-r--r-- | src/skia/cairo-skia-surface.cpp | 525 | ||||
-rw-r--r-- | test/Makefile.am | 4 |
17 files changed, 2422 insertions, 34 deletions
diff --git a/build/Makefile.am.changelog b/build/Makefile.am.changelog index 07e60369..861e5318 100644 --- a/build/Makefile.am.changelog +++ b/build/Makefile.am.changelog @@ -54,7 +54,7 @@ DISTCLEANFILES += ChangeLog.cache-* ChangeLog.cache-*..: .git -ChangeLog%: $(srcdir)/ChangeLog% +#ChangeLog%: $(srcdir)/ChangeLog% $(srcdir)/ChangeLog.cache-% $(srcdir)/ChangeLog.pre-%: @echo Creating $@ diff --git a/configure.ac b/configure.ac index 8e6bc4d1..0782baf2 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,8 @@ AC_GNU_SOURCE AC_USE_SYSTEM_EXTENSIONS AC_CONFIG_SRCDIR(src/cairo.h) AC_CONFIG_HEADERS(config.h) -AM_INIT_AUTOMAKE([1.9.6 gnu -Wall no-define]) +#AM_INIT_AUTOMAKE([1.9.6 gnu -Wall no-define]) +AM_INIT_AUTOMAKE([1.9.6]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_LIBTOOL_WIN32_DLL dnl Must be called before AC_PROG_LIBTOOL AC_PROG_LIBTOOL dnl ([1.4]) Don't remove! @@ -199,9 +200,17 @@ CAIRO_ENABLE_SURFACE_BACKEND(skia, Skia, no, [ [AS_HELP_STRING([--with-skia=/path/to/skia], [directory to find compiled skia sources])], [skia_DIR="$withval"], - [skia_DIR="`pwd`/../mesa"]) + [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])], + [skia_BUILD="$withval"], + [skia_BUILD="Release"]) skia_NONPKGCONFIG_CFLAGS="-I$skia_DIR/include/config -I$skia_DIR/include/core -I$skia_DIR/include/effects" - skia_NONPKGCONFIG_LIBS="$skia_DIR/out/libskia.a" + if test "x$(skia_BUILD)" = x"Relese"; 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" AC_SUBST(skia_DIR) ]) diff --git a/perf/Makefile.am b/perf/Makefile.am index d41891f6..f3eb734f 100644 --- a/perf/Makefile.am +++ b/perf/Makefile.am @@ -43,12 +43,13 @@ libcairoperf_la_SOURCES = \ $(libcairoperf_headers) libcairoperf_la_LIBADD = $(CAIROPERF_LIBS) -cairo_perf_trace_SOURCES = \ +cairo_perf_trace_SOURCES = a.cpp \ $(cairo_perf_trace_sources) \ $(cairo_perf_trace_external_sources) cairo_perf_trace_LDADD = \ $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \ - $(LDADD) + $(LDADD) $(CAIRO_LIBS) +skia = -Wl,--start-group @skia_DIR@/out/Debug/obj.target/gyp/libeffects.a @skia_DIR@/out/Debug/obj.target/gyp/libimages.a @skia_DIR@/out/Debug/obj.target/gyp/libutils.a @skia_DIR@/out/Debug/obj.target/gyp/libopts.a @skia_DIR@/out/Debug/obj.target/gyp/libcore.a -Wl,--end-group cairo_perf_trace_DEPENDENCIES = \ $(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \ $(LDADD) diff --git a/src/Makefile.sources b/src/Makefile.sources index 2af733e9..5b116c6b 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -306,7 +306,10 @@ cairo_win32_sources = cairo-win32-surface.c cairo-win32-printing-surface.c cairo_win32_font_sources = cairo-win32-font.c cairo_skia_headers = cairo-skia.h -cairo_skia_cxx_sources = cairo-skia-surface.cpp +cairo_skia_cxx_sources = \ + skia/cairo-skia-context.cpp \ + skia/cairo-skia-surface.cpp \ + $(NULL) cairo_os2_headers = cairo-os2.h cairo_os2_private = cairo-os2-private.h diff --git a/src/cairo-arc-private.h b/src/cairo-arc-private.h index 018a14b4..a22c01ac 100644 --- a/src/cairo-arc-private.h +++ b/src/cairo-arc-private.h @@ -38,6 +38,8 @@ #include "cairoint.h" +CAIRO_BEGIN_DECLS + cairo_private void _cairo_arc_path (cairo_t *cr, double xc, @@ -54,4 +56,6 @@ _cairo_arc_path_negative (cairo_t *cr, double angle1, double angle2); +CAIRO_END_DECLS + #endif /* CAIRO_ARC_PRIVATE_H */ diff --git a/src/cairo-backend-private.h b/src/cairo-backend-private.h index e09b4365..1dd5ea08 100644 --- a/src/cairo-backend-private.h +++ b/src/cairo-backend-private.h @@ -40,6 +40,7 @@ typedef enum _cairo_backend_type { CAIRO_TYPE_DEFAULT, + CAIRO_TYPE_SKIA, } cairo_backend_type_t; struct _cairo_backend { diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h index 53024716..f2cfd63c 100644 --- a/src/cairo-clip-private.h +++ b/src/cairo-clip-private.h @@ -41,6 +41,7 @@ #include "cairo-boxes-private.h" #include "cairo-compiler-private.h" +#include "cairo-error-private.h" #include "cairo-path-fixed-private.h" #include "cairo-reference-count-private.h" diff --git a/src/cairo-composite-rectangles-private.h b/src/cairo-composite-rectangles-private.h index 777279bb..c6dac309 100644 --- a/src/cairo-composite-rectangles-private.h +++ b/src/cairo-composite-rectangles-private.h @@ -111,4 +111,6 @@ _cairo_composite_rectangles_intersect_mask_extents (cairo_composite_rectangles_t cairo_private void _cairo_composite_rectangles_fini (cairo_composite_rectangles_t *extents); +CAIRO_END_DECLS + #endif /* CAIRO_COMPOSITE_RECTANGLES_PRIVATE_H */ diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h index 1c82ae68..a548a353 100644 --- a/src/cairo-error-private.h +++ b/src/cairo-error-private.h @@ -46,6 +46,10 @@ CAIRO_BEGIN_DECLS +/* Sure wish C had a real enum type so that this would be distinct + * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 + * offset. We want to keep it fit in int8_t as the compiler may choose + * that for #cairo_status_t */ enum _cairo_int_status { CAIRO_INT_STATUS_SUCCESS = 0, @@ -97,6 +101,8 @@ enum _cairo_int_status { CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN, }; +typedef enum _cairo_int_status cairo_int_status_t; + #define _cairo_status_is_error(status) \ (status != CAIRO_STATUS_SUCCESS && status < CAIRO_STATUS_LAST_STATUS) @@ -107,7 +113,7 @@ static inline cairo_status_t _cairo_public_status (cairo_int_status_t status) { assert (status <= CAIRO_INT_STATUS_LAST_STATUS); - return status; + return (cairo_status_t) status; } cairo_private cairo_status_t diff --git a/src/cairo-freed-pool-private.h b/src/cairo-freed-pool-private.h index c3267ef0..259c57db 100644 --- a/src/cairo-freed-pool-private.h +++ b/src/cairo-freed-pool-private.h @@ -40,6 +40,8 @@ #include "cairoint.h" #include "cairo-atomic-private.h" +CAIRO_BEGIN_DECLS + #if HAS_ATOMIC_OPS /* Keep a stash of recently freed clip_paths, since we need to * reallocate them frequently. @@ -130,4 +132,6 @@ typedef int freed_pool_t; #endif +CAIRO_END_DECLS + #endif /* CAIRO_FREED_POOL_PRIVATE_H */ diff --git a/src/cairo-skia.h b/src/cairo-skia.h index f6282352..99b92865 100644 --- a/src/cairo-skia.h +++ b/src/cairo-skia.h @@ -55,24 +55,6 @@ cairo_skia_surface_create_for_data (unsigned char *data, int height, int stride); -cairo_public unsigned char * -cairo_skia_surface_get_data (cairo_surface_t *surface); - -cairo_public cairo_format_t -cairo_skia_surface_get_format (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_width (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_height (cairo_surface_t *surface); - -cairo_public int -cairo_skia_surface_get_stride (cairo_surface_t *surface); - -cairo_public cairo_surface_t * -cairo_skia_surface_get_image (cairo_surface_t *surface); - CAIRO_END_DECLS #else diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h index b1b0db3a..4933cf26 100644 --- a/src/cairo-types-private.h +++ b/src/cairo-types-private.h @@ -44,6 +44,8 @@ #include "cairo-list-private.h" #include "cairo-reference-count-private.h" +CAIRO_BEGIN_DECLS + /** * SECTION:cairo-types * @Title: Types @@ -225,12 +227,6 @@ typedef enum _cairo_paginated_mode { CAIRO_PAGINATED_MODE_FALLBACK /* paint fallback images */ } cairo_paginated_mode_t; -/* Sure wish C had a real enum type so that this would be distinct - * from #cairo_status_t. Oh well, without that, I'll use this bogus 100 - * offset. We want to keep it fit in int8_t as the compiler may choose - * that for #cairo_status_t */ -typedef enum _cairo_int_status cairo_int_status_t; - typedef enum _cairo_internal_surface_type { CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT = 0x1000, CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED, @@ -419,4 +415,7 @@ typedef struct _cairo_scaled_glyph { void *surface_private; /* for the surface backend */ } cairo_scaled_glyph_t; + +CAIRO_END_DECLS + #endif /* CAIRO_TYPES_PRIVATE_H */ diff --git a/src/cairoint.h b/src/cairoint.h index 4c9d9c08..86688a21 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -71,6 +71,7 @@ #include <pixman.h> #include "cairo-compiler-private.h" +#include "cairo-error-private.h" #if CAIRO_HAS_PS_SURFACE || CAIRO_HAS_SCRIPT_SURFACE || CAIRO_HAS_XML_SURFACE #define CAIRO_HAS_DEFLATE_STREAM 1 diff --git a/src/skia/cairo-skia-context.cpp b/src/skia/cairo-skia-context.cpp new file mode 100644 index 00000000..828f6e59 --- /dev/null +++ b/src/skia/cairo-skia-context.cpp @@ -0,0 +1,1740 @@ +/* -*- 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 © 2010 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + */ + +#include "cairoint.h" + +#include "cairo-private.h" +#include "cairo-error-private.h" +#include "cairo-arc-private.h" +#include "cairo-backend-private.h" +#include "cairo-default-context-private.h" +#include "cairo-freed-pool-private.h" +#include "cairo-gstate-private.h" +#include "cairo-path-private.h" +#include "cairo-pattern-private.h" +#include "cairo-skia-private.h" + +#include <SkShader.h> +#include <SkColorShader.h> +#include <SkGradientShader.h> +#include <SkDashPathEffect.h> + +#if !defined(INFINITY) +#define INFINITY HUGE_VAL +#endif + +#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED) +# define CAIRO_FIXED_TO_SK_SCALAR(x) (x) +#elif defined(SK_SCALAR_IS_FIXED) +/* This can be done better, but this will do for now */ +# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x)) +#else +# define CAIRO_FIXED_TO_SK_SCALAR(x) SkFloatToScalar(_cairo_fixed_to_double(x)) +#endif + +#define UNSUPPORTED + + +static freed_pool_t context_pool; + +static void +_cairo_skia_context_destroy (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->path->reset (); + cr->paint->reset (); + + delete cr->canvas; + + cairo_surface_destroy (&cr->target->image.base); + cairo_surface_destroy (&cr->original->image.base); + + if (cr->source != NULL) { + if (cr->source_image != NULL) { + _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra); + cr->source_image = NULL; + } + cairo_surface_destroy (cr->source); + cr->source = NULL; + } + + _cairo_fini (&cr->base); + + _freed_pool_put (&context_pool, cr); +} + +static cairo_surface_t * +_cairo_skia_context_get_original_target (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return &cr->original->image.base; +} + +static cairo_surface_t * +_cairo_skia_context_get_current_target (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return &cr->target->image.base; +} + +static cairo_status_t +_cairo_skia_context_save (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->canvas->save (); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_restore (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->canvas->restore (); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_surface_t *group_surface; + cairo_status_t status; + int width, height; + + //clip = _cairo_gstate_get_clip (cr->gstate); + width = cr->target->image.width; + height = cr->target->image.height; + group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base, + content, width, height); + +#if 0 + /* Set device offsets on the new surface so that logically it appears at + * the same location on the parent surface -- when we pop_group this, + * the source pattern will get fixed up for the appropriate target surface + * device offsets, so we want to set our own surface offsets from /that/, + * and not from the device origin. */ + cairo_surface_set_device_offset (group_surface, + parent_surface->device_transform.x0 - extents.x, + parent_surface->device_transform.y0 - extents.y); + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just applied. */ + _cairo_path_fixed_transform (cr->path, + &group_surface->device_transform); +#endif + + status = _cairo_skia_context_save (cr); + if (unlikely (status)) { + cairo_surface_destroy (group_surface); + return status; + } + + cairo_surface_destroy (&cr->target->image.base); + cr->target = (cairo_skia_surface_t *) group_surface; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_pattern_t * +_cairo_skia_context_pop_group (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_surface_t *group_surface; + cairo_pattern_t *group_pattern; + cairo_status_t status; + + group_surface = cairo_surface_reference (&cr->target->image.base); + + status = _cairo_skia_context_restore (cr); + if (unlikely (status)) { + group_pattern = _cairo_pattern_create_in_error (status); + goto done; + } + + group_pattern = cairo_pattern_create_for_surface (group_surface); + status = group_pattern->status; + if (unlikely (status)) + goto done; + +#if 0 + _cairo_gstate_get_matrix (cr->gstate, &group_matrix); + /* Transform by group_matrix centered around device_transform so that when + * we call _cairo_gstate_copy_transformed_pattern the result is a pattern + * with a matrix equivalent to the device_transform of group_surface. */ + if (_cairo_surface_has_device_transform (group_surface)) { + cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform); + _cairo_pattern_transform (group_pattern, &group_matrix); + _cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse); + } else { + cairo_pattern_set_matrix (group_pattern, &group_matrix); + } + + /* If we have a current path, we need to adjust it to compensate for + * the device offset just removed. */ + _cairo_path_fixed_transform (cr->path, + &group_surface->device_transform_inverse); +#endif + +done: + cairo_surface_destroy (group_surface); + + return group_pattern; +} + +static inline cairo_surface_t * +surface_from_pattern (const cairo_pattern_t *pattern) +{ + return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface; +} + +static inline bool +surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap) +{ + cairo_image_surface_t *img = (cairo_image_surface_t *) surface; + SkBitmap::Config config; + bool opaque; + + if (unlikely (! format_to_sk_config (img->format, config, opaque))) + return false; + + bitmap.reset (); + bitmap.setConfig (config, img->width, img->height, img->stride); + bitmap.setIsOpaque (opaque); + bitmap.setPixels (img->data); + + return true; +} + +static inline SkMatrix +matrix_to_sk (const cairo_matrix_t& mat) +{ + SkMatrix skm; + + skm.reset (); + skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx)); + skm.set (SkMatrix::kMSkewX, SkFloatToScalar (mat.xy)); + skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0)); + skm.set (SkMatrix::kMSkewY, SkFloatToScalar (mat.yx)); + skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy)); + skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0)); + + /* + skm[6] = SkFloatToScalar (0.0); + skm[7] = SkFloatToScalar (0.0); + skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself. It wants Sk_Fract1 (2.30), not Sk_Scalar1 + */ + + return skm; +} + +static inline SkMatrix +matrix_inverse_to_sk (const cairo_matrix_t& mat) +{ + cairo_matrix_t inv = mat; + cairo_status_t status = cairo_matrix_invert (&inv); + assert (status == CAIRO_STATUS_SUCCESS); + return matrix_to_sk (inv); +} + +static SkShader::TileMode +extend_to_sk (cairo_extend_t extend) +{ + static const SkShader::TileMode modeMap[] = { + SkShader::kClamp_TileMode, // NONE behaves like PAD, because noone wants NONE + SkShader::kRepeat_TileMode, + SkShader::kMirror_TileMode, + SkShader::kClamp_TileMode + }; + + return modeMap[extend]; +} + +static inline SkColor +color_to_sk (const cairo_color_t& c) +{ + /* Need unpremultiplied 1-byte values */ + return SkColorSetARGB ((U8CPU) (c.alpha * 255), + (U8CPU) (c.red * 255), + (U8CPU) (c.green * 255), + (U8CPU) (c.blue * 255)); +} + +static inline SkColor +color_stop_to_sk (const cairo_color_stop_t& c) +{ + /* Need unpremultiplied 1-byte values */ + return SkColorSetARGB ((U8CPU) (c.alpha * 255), + (U8CPU) (c.red * 255), + (U8CPU) (c.green * 255), + (U8CPU) (c.blue * 255)); +} + +static SkShader* +source_to_sk_shader (cairo_skia_context_t *cr, + const cairo_pattern_t *pattern) +{ + SkShader *shader = NULL; + + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; + return new SkColorShader (color_to_sk (solid->color)); + } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_t *surface = surface_from_pattern (pattern); + + cr->source = cairo_surface_reference (surface); + + if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + + shader = SkShader::CreateBitmapShader (*esurf->bitmap, + extend_to_sk (pattern->extend), + extend_to_sk (pattern->extend)); + } else { + SkBitmap bitmap; + + if (! _cairo_surface_is_image (surface)) { + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (surface, + &cr->source_image, + &cr->source_extra); + if (status) + return NULL; + + surface = &cr->source_image->base; + } + + if (unlikely (! surface_to_sk_bitmap (surface, bitmap))) + return NULL; + + shader = SkShader::CreateBitmapShader (bitmap, + extend_to_sk (pattern->extend), + extend_to_sk (pattern->extend)); + } + } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR + /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */) + { + cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; + SkColor colors_stack[10]; + SkScalar pos_stack[10]; + SkColor *colors = colors_stack; + SkScalar *pos = pos_stack; + + if (gradient->n_stops > 10) { + colors = new SkColor[gradient->n_stops]; + pos = new SkScalar[gradient->n_stops]; + } + + for (unsigned int i = 0; i < gradient->n_stops; i++) { + pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset); + colors[i] = color_stop_to_sk (gradient->stops[i].color); + } + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) { + cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; + SkPoint points[2]; + + points[0].set (SkFloatToScalar (linear->pd1.x), + SkFloatToScalar (linear->pd1.y)); + points[1].set (SkFloatToScalar (linear->pd2.x), + SkFloatToScalar (linear->pd2.y)); + shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops, + extend_to_sk (pattern->extend)); + } else { + // XXX todo -- implement real radial shaders in Skia + } + + if (gradient->n_stops > 10) { + delete [] colors; + delete [] pos; + } + } + + if (shader && ! _cairo_matrix_is_identity (&pattern->matrix)) + shader->setLocalMatrix (matrix_inverse_to_sk (pattern->matrix)); + + return shader; +} + +static inline bool +pattern_filter_to_sk (const cairo_pattern_t *pattern) +{ + switch (pattern->filter) { + case CAIRO_FILTER_GOOD: + case CAIRO_FILTER_BEST: + case CAIRO_FILTER_BILINEAR: + case CAIRO_FILTER_GAUSSIAN: + return true; + default: + case CAIRO_FILTER_FAST: + case CAIRO_FILTER_NEAREST: + return false; + } +} + +static inline bool +pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color) +{ + if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return false; + + color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color); + return true; +} + +static cairo_status_t +_cairo_skia_context_set_source (void *abstract_cr, + cairo_pattern_t *source) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkColor color; + + if (cr->source != NULL) { + if (cr->source_image != NULL) { + _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra); + cr->source_image = NULL; + } + cairo_surface_destroy (cr->source); + cr->source = NULL; + } + + if (pattern_to_sk_color (source, color)) { + cr->paint->setColor (color); + } else { + SkShader *shader = source_to_sk_shader (cr, source); + if (shader == NULL) { + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; + } + + cr->paint->setShader (shader); + shader->unref (); + + cr->paint->setFilterBitmap (pattern_filter_to_sk (source)); + } + + /* XXX change notification */ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* Need unpremultiplied 1-byte values */ + cr->paint->setARGB ((U8CPU) (alpha * 255), + (U8CPU) (red * 255), + (U8CPU) (green * 255), + (U8CPU) (blue * 255)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_source_surface (void *abstract_cr, + cairo_surface_t *surface, + double x, + double y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_pattern_t *pattern; + cairo_matrix_t matrix; + cairo_status_t status; + + if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + SkShader *shader; + + shader = SkShader::CreateBitmapShader (*esurf->bitmap, + SkShader::kClamp_TileMode, /* XXX */ + SkShader::kClamp_TileMode); + + cr->paint->setShader (shader); + shader->unref (); + + cr->paint->setFilterBitmap (true); + + return CAIRO_STATUS_SUCCESS; + } + + pattern = cairo_pattern_create_for_surface (surface); + if (unlikely (pattern->status)) + return pattern->status; + + cairo_matrix_init_translate (&matrix, -x, -y); + cairo_pattern_set_matrix (pattern, &matrix); + + status = _cairo_skia_context_set_source (cr, pattern); + cairo_pattern_destroy (pattern); + + return status; +} + +static cairo_pattern_t * +_cairo_skia_context_get_source (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return NULL; +} + +static cairo_status_t +_cairo_skia_context_set_tolerance (void *abstract_cr, + double tolerance) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX ignored */ + return CAIRO_STATUS_SUCCESS; +} + +static inline SkXfermode::Mode +operator_to_sk (cairo_operator_t op) +{ + static const SkXfermode::Mode modeMap[] = { + SkXfermode::kClear_Mode, + + SkXfermode::kSrc_Mode, + SkXfermode::kSrcOver_Mode, + SkXfermode::kSrcIn_Mode, + SkXfermode::kSrcOut_Mode, + SkXfermode::kSrcATop_Mode, + + SkXfermode::kDst_Mode, + SkXfermode::kDstOver_Mode, + SkXfermode::kDstIn_Mode, + SkXfermode::kDstOut_Mode, + SkXfermode::kDstATop_Mode, + + SkXfermode::kXor_Mode, + SkXfermode::kPlus_Mode, // XXX Add? + SkXfermode::kPlus_Mode, // XXX SATURATE + + SkXfermode::kPlus_Mode, + SkXfermode::kMultiply_Mode, + SkXfermode::kScreen_Mode, + SkXfermode::kOverlay_Mode, + SkXfermode::kDarken_Mode, + SkXfermode::kLighten_Mode, + SkXfermode::kColorDodge_Mode, + SkXfermode::kColorBurn_Mode, + SkXfermode::kHardLight_Mode, + SkXfermode::kSoftLight_Mode, + SkXfermode::kDifference_Mode, + SkXfermode::kExclusion_Mode, + + SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE + SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION, + SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR, + SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY + }; + + return modeMap[op]; +} + +static cairo_status_t +_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setXfermodeMode (operator_to_sk (op)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_opacity (void *abstract_cr, double opacity) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX */ + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_fill_rule (void *abstract_cr, + cairo_fill_rule_t fill_rule) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_line_width (void *abstract_cr, + double line_width) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setStrokeWidth (SkFloatToScalar (line_width)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_line_cap (void *abstract_cr, + cairo_line_cap_t line_cap) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + static const SkPaint::Cap map[] = { + SkPaint::kButt_Cap, + SkPaint::kRound_Cap, + SkPaint::kSquare_Cap + }; + cr->paint->setStrokeCap (map[line_cap]); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_line_join (void *abstract_cr, + cairo_line_join_t line_join) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + static const SkPaint::Join map[] = { + SkPaint::kMiter_Join, + SkPaint::kRound_Join, + SkPaint::kBevel_Join + }; + cr->paint->setStrokeJoin (map[line_join]); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_dash (void *abstract_cr, + const double *dashes, + int num_dashes, + double offset) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkScalar intervals_static[20]; + SkScalar *intervals = intervals_static; + + if (num_dashes == 0) { + cr->paint->setPathEffect (NULL); + return CAIRO_STATUS_SUCCESS; + } + + int loop = 0; + if ((num_dashes & 1) != 0) { + loop = 1; + num_dashes <<= 1; + } + + if (num_dashes > 20) + intervals = new SkScalar[num_dashes]; + + int i = 0; + do { + for (int j = 0; i < num_dashes; j++) + intervals[i++] = SkFloatToScalar (dashes[j]); + } while (loop--); + + SkDashPathEffect *dash = new SkDashPathEffect (intervals, num_dashes, SkFloatToScalar (offset)); + + cr->paint->setPathEffect (dash); + dash->unref (); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setStrokeMiter (SkFloatToScalar (limit)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_antialias_t +_cairo_skia_context_get_antialias (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE; +} + +static void +_cairo_skia_context_get_dash (void *abstract_cr, + double *dashes, + int *num_dashes, + double *offset) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + *num_dashes = 0; + /* XXX */ +} + +static cairo_fill_rule_t +_cairo_skia_context_get_fill_rule (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkPath::FillType ft; + + ft = cr->path->getFillType (); + if (ft == SkPath::kWinding_FillType) + return CAIRO_FILL_RULE_WINDING; + if (ft == SkPath::kEvenOdd_FillType) + return CAIRO_FILL_RULE_EVEN_ODD;; + + UNSUPPORTED; + return CAIRO_FILL_RULE_WINDING; +} + +static double +_cairo_skia_context_get_line_width (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return /* ScalarToFloat */ cr->paint->getStrokeWidth (); +} + +static cairo_line_cap_t +_cairo_skia_context_get_line_cap (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + static const cairo_line_cap_t map[] = { + CAIRO_LINE_CAP_BUTT, + CAIRO_LINE_CAP_ROUND, + CAIRO_LINE_CAP_SQUARE + }; + return map[cr->paint->getStrokeCap ()]; +} + +static cairo_line_join_t +_cairo_skia_context_get_line_join (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + static const cairo_line_join_t map[] = { + CAIRO_LINE_JOIN_MITER, + CAIRO_LINE_JOIN_ROUND, + CAIRO_LINE_JOIN_BEVEL + }; + return map[cr->paint->getStrokeJoin ()]; +} + +static double +_cairo_skia_context_get_miter_limit (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return /* SkScalarToFloat */ cr->paint->getStrokeMiter (); +} + +static cairo_operator_t +_cairo_skia_context_get_operator (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + //cr->paint->getXfermode (); + return CAIRO_OPERATOR_OVER; +} + +static double +_cairo_skia_context_get_opacity (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return 1.; +} + +static double +_cairo_skia_context_get_tolerance (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX */ + return CAIRO_GSTATE_TOLERANCE_DEFAULT; +} + + +/* Current tranformation matrix */ + +static cairo_status_t +_cairo_skia_context_translate (void *abstract_cr, + double tx, + double ty) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_translate (&cr->matrix, tx, ty); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_scale (void *abstract_cr, + double sx, + double sy) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_scale (&cr->matrix, sx, sy); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rotate (void *abstract_cr, + double theta) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_rotate (&cr->matrix, theta); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_transform (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_matrix (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->matrix = *matrix; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_identity_matrix (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_init_identity (&cr->matrix); + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_context_get_matrix (void *abstract_cr, + cairo_matrix_t *matrix) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + *matrix = cr->matrix; +} + +static void +_cairo_skia_context_user_to_device (void *abstract_cr, + double *x, + double *y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_transform_point (&cr->matrix, x, y); +} + +static void +_cairo_skia_context_user_to_device_distance (void *abstract_cr, + double *dx, + double *dy) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cairo_matrix_transform_distance (&cr->matrix, dx, dy); +} + +static void +_cairo_skia_context_device_to_user (void *abstract_cr, + double *x, + double *y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_matrix_t inverse; + cairo_status_t status; + + inverse = cr->matrix; + status = cairo_matrix_invert (&inverse); + assert (CAIRO_STATUS_SUCCESS == status); + + cairo_matrix_transform_point (&inverse, x, y); +} + +static void +_cairo_skia_context_device_to_user_distance (void *abstract_cr, + double *dx, + double *dy) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_matrix_t inverse; + cairo_status_t status; + + inverse = cr->matrix; + status = cairo_matrix_invert (&inverse); + assert (CAIRO_STATUS_SUCCESS == status); + + cairo_matrix_transform_distance (&inverse, dx, dy); +} + +/* Path constructor */ + +static cairo_status_t +_cairo_skia_context_new_path (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->path->reset (); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_new_sub_path (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->path->rMoveTo (0, 0); /* XXX */ + return CAIRO_STATUS_SUCCESS; +} + +static void +user_to_device_point (cairo_skia_context_t *cr, double *x, double *y) +{ + cairo_matrix_transform_point (&cr->matrix, x, y); + cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y); +} + +static void +user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy) +{ + cairo_matrix_transform_distance (&cr->matrix, dx, dy); + cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy); +} + +static cairo_status_t +_cairo_skia_context_move_to (void *abstract_cr, double x, double y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_point (cr, &x, &y); + cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_line_to (void *abstract_cr, double x, double y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_point (cr, &x, &y); + cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_curve_to (void *abstract_cr, + double x1, double y1, + double x2, double y2, + double x3, double y3) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_point (cr, &x1, &y1); + user_to_device_point (cr, &x2, &y2); + user_to_device_point (cr, &x3, &y3); + cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1), + SkFloatToScalar (x2), SkFloatToScalar (y2), + SkFloatToScalar (x3), SkFloatToScalar (y3)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_arc_to (void *abstract_cr, + double x1, double y1, + double x2, double y2, + double radius) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + +#if 0 + user_to_device_point (cr, &x1, &y1); + user_to_device_point (cr, &x2, &y2); + user_to_device_distance (cr, &radius, &radius); +#endif + + cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1), + SkFloatToScalar (x2), SkFloatToScalar (y2), + SkFloatToScalar (radius)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_distance (cr, &dx, &dy); + cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_distance (cr, &dx, &dy); + cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rel_curve_to (void *abstract_cr, + double dx1, double dy1, + double dx2, double dy2, + double dx3, double dy3) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + user_to_device_distance (cr, &dx1, &dy1); + user_to_device_distance (cr, &dx2, &dy2); + user_to_device_distance (cr, &dx3, &dy3); + cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1), + SkFloatToScalar (dx2), SkFloatToScalar (dy2), + SkFloatToScalar (dx3), SkFloatToScalar (dy3)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rel_arc_to (void *abstract_cr, + double dx1, double dy1, + double dx2, double dy2, + double radius) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + +#if 0 + user_to_device_point (cr, &x1, &y1); + user_to_device_point (cr, &x2, &y2); + user_to_device_distance (cr, &radius, &radius); +#endif + + cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1), + SkFloatToScalar (dx2), SkFloatToScalar (dy2), + SkFloatToScalar (radius)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_close_path (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->path->close (); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_rectangle (void *abstract_cr, + double x, double y, + double width, double height) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + double x1, y1, x2, y2; + + /* XXX assume no rotation! */ + x1 = x, y1 = y; + user_to_device_point (cr, &x1, &y1); + + x2 = x + width, y2 = y + height; + user_to_device_point (cr, &x2, &y2); + + cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1), + SkFloatToScalar (x2), SkFloatToScalar (y2)); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_arc (void *abstract_cr, + double xc, double yc, double radius, + double angle1, double angle2, + cairo_bool_t forward) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_status_t status; + + /* XXX cr->path->arc() */ + + /* Do nothing, successfully, if radius is <= 0 */ + if (radius <= 0.0) { + status = _cairo_skia_context_line_to (cr, xc, yc); + if (unlikely (status)) + return status; + + status = _cairo_skia_context_line_to (cr, xc, yc); + if (unlikely (status)) + return status; + + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_skia_context_line_to (cr, + xc + radius * cos (angle1), + yc + radius * sin (angle1)); + + if (unlikely (status)) + return status; + + if (forward) + _cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2); + else + _cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_context_path_extents (void *abstract_cr, + double *x1, + double *y1, + double *x2, + double *y2) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkRect rect; + + rect = cr->path->getBounds (); + + UNSUPPORTED; + /* XXX transform SkScalar rect to user */ +} + +static cairo_bool_t +_cairo_skia_context_has_current_point (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return TRUE; +} + +static cairo_bool_t +_cairo_skia_context_get_current_point (void *abstract_cr, + double *x, + double *y) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkPoint pt; + + cr->path->getLastPt (&pt); + //*x = SkScalarToFloat (pt.x); + //*y = SkScalarToFloat (pt.y); + //_cairo_gstate_backend_to_user (cr->gstate, x, y); + + return TRUE; +} + +static cairo_path_t * +_cairo_skia_context_copy_path (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX iterate */ + UNSUPPORTED; + return NULL; +} + +static cairo_path_t * +_cairo_skia_context_copy_path_flat (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX iterate and decompose */ + UNSUPPORTED; + return NULL; +} + +static cairo_status_t +_cairo_skia_context_append_path (void *abstract_cr, + const cairo_path_t *path) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + // return _cairo_path_append_to_context (path, cr); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_stroke_to_path (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setStyle (SkPaint::kStroke_Style); + cr->paint->getFillPath (*cr->path, cr->path); + return CAIRO_STATUS_SUCCESS; +} + + +static cairo_status_t +_cairo_skia_context_paint (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + +#if 0 + if (cr->source != NULL) { + SkBitmap bitmap; + SkMatrix bitmapMatrix; + + if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) { + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type; + + bitmap = *esurf->bitmap; + } else { + surface_to_sk_bitmap (&cr->source_image->base, bitmap); + } + + // XXX pattern->matrix, pattern->filter, pattern->extend + cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint); + } else { + cr->canvas->drawPaint (*cr->paint); + } +#else + cr->canvas->drawPaint (*cr->paint); +#endif + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_paint_with_alpha (void *abstract_cr, + double alpha) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + if (CAIRO_ALPHA_IS_OPAQUE (alpha)) + return _cairo_skia_context_paint (cr); + + /*XXX */ + return _cairo_skia_context_paint (cr); +} + +static cairo_status_t +_cairo_skia_context_mask (void *abstract_cr, + cairo_pattern_t *mask) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX */ + //UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_stroke_preserve (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setStyle (SkPaint::kStroke_Style); + + /* XXX pen transformation? */ + //assert (_cairo_matrix_is_identity (&cr->matrix)); + cr->canvas->drawPath (*cr->path, *cr->paint); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_stroke (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_status_t status; + + status = _cairo_skia_context_stroke_preserve (cr); + if (unlikely (status)) + return status; + + return _cairo_skia_context_new_path (cr); +} + +static cairo_status_t +_cairo_skia_context_in_stroke (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_stroke_extents (void *abstract_cr, + double *x1, double *y1, double *x2, double *y2) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_fill_preserve (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->paint->setStyle (SkPaint::kFill_Style); + cr->canvas->drawPath (*cr->path, *cr->paint); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_fill (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_status_t status; + + status = _cairo_skia_context_fill_preserve (cr); + if (unlikely (status)) + return status; + + return _cairo_skia_context_new_path (cr); +} + +static cairo_status_t +_cairo_skia_context_in_fill (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_fill_extents (void *abstract_cr, + double *x1, double *y1, double *x2, double *y2) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_clip_preserve (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + cr->canvas->clipPath (*cr->path); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_clip (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + cairo_status_t status; + + status = _cairo_skia_context_clip_preserve (cr); + if (unlikely (status)) + return status; + + return _cairo_skia_context_new_path (cr); +} + +static cairo_status_t +_cairo_skia_context_in_clip (void *abstract_cr, + double x, double y, + cairo_bool_t *inside) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_reset_clip (void *abstract_cr) +{ + cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + SkRegion rgn(SkIRect::MakeWH (cr->target->bitmap->width (), + cr->target->bitmap->height ())); + + cr->canvas->setClipRegion(rgn); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_clip_extents (void *abstract_cr, + double *x1, double *y1, + double *x2, double *y2) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_rectangle_list_t * +_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return NULL; +} + +static cairo_status_t +_cairo_skia_context_copy_page (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_show_page (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_font_face (void *abstract_cr, + cairo_font_face_t *font_face) +{ + // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + //return _cairo_gstate_set_font_face (cr->gstate, font_face); + return CAIRO_STATUS_SUCCESS; +} + +static cairo_font_face_t * +_cairo_skia_context_get_font_face (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return NULL; +} + +static cairo_status_t +_cairo_skia_context_font_extents (void *abstract_cr, + cairo_font_extents_t *extents) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_font_size (void *abstract_cr, + double size) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_set_font_matrix (void *abstract_cr, + const cairo_matrix_t *matrix) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_context_get_font_matrix (void *abstract_cr, + cairo_matrix_t *matrix) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; +} + +static cairo_status_t +_cairo_skia_context_set_font_options (void *abstract_cr, + const cairo_font_options_t *options) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_context_get_font_options (void *abstract_cr, + cairo_font_options_t *options) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; +} + +static cairo_status_t +_cairo_skia_context_set_scaled_font (void *abstract_cr, + cairo_scaled_font_t *scaled_font) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_scaled_font_t * +_cairo_skia_context_get_scaled_font (void *abstract_cr) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return _cairo_scaled_font_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); +} + +static cairo_status_t +_cairo_skia_context_glyphs (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_glyph_text_info_t *info) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + /* XXX */ + //UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_glyph_path (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_context_glyph_extents (void *abstract_cr, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_text_extents_t *extents) +{ + //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; + + UNSUPPORTED; + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_backend_t _cairo_skia_context_backend = { + CAIRO_TYPE_SKIA, + _cairo_skia_context_destroy, + + _cairo_skia_context_get_original_target, + _cairo_skia_context_get_current_target, + + _cairo_skia_context_save, + _cairo_skia_context_restore, + + _cairo_skia_context_push_group, + _cairo_skia_context_pop_group, + + _cairo_skia_context_set_source_rgba, + _cairo_skia_context_set_source_surface, + _cairo_skia_context_set_source, + _cairo_skia_context_get_source, + + _cairo_skia_context_set_antialias, + _cairo_skia_context_set_dash, + _cairo_skia_context_set_fill_rule, + _cairo_skia_context_set_line_cap, + _cairo_skia_context_set_line_join, + _cairo_skia_context_set_line_width, + _cairo_skia_context_set_miter_limit, + _cairo_skia_context_set_opacity, + _cairo_skia_context_set_operator, + _cairo_skia_context_set_tolerance, + _cairo_skia_context_get_antialias, + _cairo_skia_context_get_dash, + _cairo_skia_context_get_fill_rule, + _cairo_skia_context_get_line_cap, + _cairo_skia_context_get_line_join, + _cairo_skia_context_get_line_width, + _cairo_skia_context_get_miter_limit, + _cairo_skia_context_get_opacity, + _cairo_skia_context_get_operator, + _cairo_skia_context_get_tolerance, + + _cairo_skia_context_translate, + _cairo_skia_context_scale, + _cairo_skia_context_rotate, + _cairo_skia_context_transform, + _cairo_skia_context_set_matrix, + _cairo_skia_context_set_identity_matrix, + _cairo_skia_context_get_matrix, + _cairo_skia_context_user_to_device, + _cairo_skia_context_user_to_device_distance, + _cairo_skia_context_device_to_user, + _cairo_skia_context_device_to_user_distance, + + _cairo_skia_context_new_path, + _cairo_skia_context_new_sub_path, + _cairo_skia_context_move_to, + _cairo_skia_context_rel_move_to, + _cairo_skia_context_line_to, + _cairo_skia_context_rel_line_to, + _cairo_skia_context_curve_to, + _cairo_skia_context_rel_curve_to, + _cairo_skia_context_arc_to, + _cairo_skia_context_rel_arc_to, + _cairo_skia_context_close_path, + _cairo_skia_context_arc, + _cairo_skia_context_rectangle, + _cairo_skia_context_path_extents, + _cairo_skia_context_has_current_point, + _cairo_skia_context_get_current_point, + _cairo_skia_context_copy_path, + _cairo_skia_context_copy_path_flat, + _cairo_skia_context_append_path, + + _cairo_skia_stroke_to_path, + + _cairo_skia_context_clip, + _cairo_skia_context_clip_preserve, + _cairo_skia_context_in_clip, + _cairo_skia_context_clip_extents, + _cairo_skia_context_reset_clip, + _cairo_skia_context_copy_clip_rectangle_list, + + _cairo_skia_context_paint, + _cairo_skia_context_paint_with_alpha, + _cairo_skia_context_mask, + + _cairo_skia_context_stroke, + _cairo_skia_context_stroke_preserve, + _cairo_skia_context_in_stroke, + _cairo_skia_context_stroke_extents, + + _cairo_skia_context_fill, + _cairo_skia_context_fill_preserve, + _cairo_skia_context_in_fill, + _cairo_skia_context_fill_extents, + + _cairo_skia_context_set_font_face, + _cairo_skia_context_get_font_face, + _cairo_skia_context_set_font_size, + _cairo_skia_context_set_font_matrix, + _cairo_skia_context_get_font_matrix, + _cairo_skia_context_set_font_options, + _cairo_skia_context_get_font_options, + _cairo_skia_context_set_scaled_font, + _cairo_skia_context_get_scaled_font, + _cairo_skia_context_font_extents, + + _cairo_skia_context_glyphs, + _cairo_skia_context_glyph_path, + _cairo_skia_context_glyph_extents, + + _cairo_skia_context_copy_page, + _cairo_skia_context_show_page, +}; + +cairo_t * +_cairo_skia_context_create (void *target) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target; + cairo_skia_context_t *cr; + + cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool); + if (unlikely (cr == NULL)) { + cr = new cairo_skia_context_t; + if (unlikely (cr == NULL)) + return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + cr->path = new SkPath; + cr->paint = new SkPaint; + } + + _cairo_init (&cr->base, &_cairo_skia_context_backend); + + cr->source = NULL; + cr->source_image = NULL; + + cr->paint->setStrokeWidth (SkFloatToScalar (2.0)); + + cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target); + cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target); + cr->canvas = new SkCanvas (*surface->bitmap); + cr->canvas->save (); + + cairo_matrix_init_identity (&cr->matrix); + + return &cr->base; +} + +#if 0 +void +_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint) +{ + *cr->paint = paint; +} + +void +_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path) +{ + *cr->path = path; +} +#endif diff --git a/src/skia/cairo-skia-private.h b/src/skia/cairo-skia-private.h new file mode 100644 index 00000000..cbd8c888 --- /dev/null +++ b/src/skia/cairo-skia-private.h @@ -0,0 +1,110 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Carl D. Worth <cworth@redhat.com> + */ + +#ifndef CAIRO_SKIA_CONTEXT_PRIVATE_H +#define CAIRO_SKIA_CONTEXT_PRIVATE_H + +#include "cairo-private.h" +#include "cairo-image-surface-private.h" + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkPath.h> + +typedef struct _cairo_skia_context cairo_skia_context_t; +typedef struct _cairo_skia_surface cairo_skia_surface_t; + +struct _cairo_skia_context { + cairo_t base; + + cairo_skia_surface_t *original; + cairo_skia_surface_t *target; + + cairo_matrix_t matrix; + + SkCanvas *canvas; + SkPaint *paint; + SkPath *path; + + cairo_surface_t *source; + cairo_image_surface_t *source_image; + void *source_extra; +}; + +struct _cairo_skia_surface { + cairo_image_surface_t image; + + SkBitmap *bitmap; +}; + +static inline bool +format_to_sk_config (cairo_format_t format, + SkBitmap::Config& config, + bool& opaque) +{ + opaque = false; + + switch (format) { + case CAIRO_FORMAT_ARGB32: + config = SkBitmap::kARGB_8888_Config; + break; + case CAIRO_FORMAT_RGB24: + config = SkBitmap::kARGB_8888_Config; + opaque = true; + break; + case CAIRO_FORMAT_RGB16_565: + config = SkBitmap::kRGB_565_Config; + opaque = true; + break; + case CAIRO_FORMAT_A8: + config = SkBitmap::kA8_Config; + break; + case CAIRO_FORMAT_A1: + config = SkBitmap::kA1_Config; + break; + case CAIRO_FORMAT_RGB30: + case CAIRO_FORMAT_INVALID: + default: + return false; + } + + return true; +} + +cairo_private cairo_t * +_cairo_skia_context_create (void *target); + +#endif /* CAIRO_SKIA_CONTEXT_PRIVATE_H */ diff --git a/src/skia/cairo-skia-surface.cpp b/src/skia/cairo-skia-surface.cpp new file mode 100644 index 00000000..4c10625a --- /dev/null +++ b/src/skia/cairo-skia-surface.cpp @@ -0,0 +1,525 @@ +/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2007 Mozilla Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * + * Contributor(s): + * Vladimir Vukicevic <vladimir@mozilla.com> + */ + +#include "cairoint.h" + +#include "cairo-skia.h" +#include "cairo-skia-private.h" + +#include "cairo-composite-rectangles-private.h" +#include "cairo-error-private.h" + +static cairo_skia_surface_t * +_cairo_skia_surface_create_internal (SkBitmap::Config config, + bool opaque, + unsigned char *data, + int width, + int height, + int stride); + +static cairo_surface_t * +_cairo_skia_surface_create_similar (void *asurface, + cairo_content_t content, + int width, + int height) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + SkBitmap::Config config; + bool opaque; + + if (content == surface->image.base.content) + { + config = surface->bitmap->getConfig (); + opaque = surface->bitmap->isOpaque (); + } + else if (! format_to_sk_config (_cairo_format_from_content (content), + config, opaque)) + { + return NULL; + } + + return &_cairo_skia_surface_create_internal (config, opaque, + NULL, + width, height, + 0)->image.base; +} + +static cairo_status_t +_cairo_skia_surface_finish (void *asurface) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + cairo_surface_finish (&surface->image.base); + delete surface->bitmap; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_skia_surface_map_to_image (void *asurface, + const cairo_rectangle_int_t *extents) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + surface->bitmap->lockPixels (); + + if (extents->width < surface->image.width || + extents->height < surface->image.height) + { + return _cairo_surface_create_for_rectangle_int (&surface->image.base, + extents); + } + + return cairo_surface_reference (&surface->image.base); +} + +static cairo_int_status_t +_cairo_skia_surface_unmap_image (void *asurface, + cairo_image_surface_t *image) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + surface->bitmap->unlockPixels (); + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_skia_surface_acquire_source_image (void *asurface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + surface->bitmap->lockPixels (); + + *image_out = &surface->image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_surface_release_source_image (void *asurface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + surface->bitmap->unlockPixels (); +} + +static cairo_bool_t +_cairo_skia_surface_get_extents (void *asurface, + cairo_rectangle_int_t *extents) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + extents->x = extents->y = 0; + extents->width = surface->image.width; + extents->height = surface->image.height; + return TRUE; +} + +static void +_cairo_skia_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_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, + _cairo_skia_surface_finish, + + _cairo_skia_context_create, + + _cairo_skia_surface_create_similar, + NULL, //_cairo_skia_surface_create_similar_image, + _cairo_skia_surface_map_to_image, + _cairo_skia_surface_unmap_image, + + _cairo_skia_surface_acquire_source_image, + _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, /* 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 +}; + +/* + * Surface constructors + */ + +static inline pixman_format_code_t +sk_config_to_pixman_format_code (SkBitmap::Config config, + bool opaque) +{ + switch (config) { + case SkBitmap::kARGB_8888_Config: + return opaque ? PIXMAN_x8r8g8b8 : PIXMAN_a8r8g8b8; + + case SkBitmap::kA8_Config: + return PIXMAN_a8; + + case SkBitmap::kA1_Config: + return PIXMAN_a1; + case SkBitmap::kRGB_565_Config: + return PIXMAN_r5g6b5; + case SkBitmap::kARGB_4444_Config: + return PIXMAN_a4r4g4b4; + + case SkBitmap::kNo_Config: + case SkBitmap::kIndex8_Config: + case SkBitmap::kRLE_Index8_Config: + case SkBitmap::kConfigCount: + default: + ASSERT_NOT_REACHED; + return (pixman_format_code_t) -1; + } +} +static cairo_skia_surface_t * +_cairo_skia_surface_create_internal (SkBitmap::Config config, + bool opaque, + unsigned char *data, + int width, + int height, + int stride) +{ + cairo_skia_surface_t *surface; + pixman_image_t *pixman_image; + pixman_format_code_t pixman_format; + + surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t)); + if (unlikely (surface == NULL)) + return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + pixman_format = sk_config_to_pixman_format_code (config, opaque); + pixman_image = pixman_image_create_bits (pixman_format, + width, height, + (uint32_t *) data, stride); + if (unlikely (pixman_image == NULL)) + return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + _cairo_surface_init (&surface->image.base, + &cairo_skia_surface_backend, + NULL, /* device */ + _cairo_content_from_pixman_format (pixman_format)); + + _cairo_image_surface_init (&surface->image, pixman_image, pixman_format); + + surface->bitmap = new SkBitmap; + surface->bitmap->setConfig (config, width, height, surface->image.stride); + surface->bitmap->setIsOpaque (opaque); + surface->bitmap->setPixels (surface->image.data); + + surface->image.base.is_clear = data == NULL; + + return surface; +} + +cairo_surface_t * +cairo_skia_surface_create (cairo_format_t format, + int width, + int height) +{ + SkBitmap::Config config; + bool opaque; + + if (! CAIRO_FORMAT_VALID (format) || + ! format_to_sk_config (format, config, opaque)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + return &_cairo_skia_surface_create_internal (config, opaque, NULL, width, height, 0)->image.base; +} + +cairo_surface_t * +cairo_skia_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride) +{ + SkBitmap::Config config; + bool opaque; + + if (! CAIRO_FORMAT_VALID (format) || + ! format_to_sk_config (format, config, opaque)) + { + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + } + + return &_cairo_skia_surface_create_internal (config, opaque, data, width, height, stride)->image.base; +} + +/*** + +Todo: + +*** Skia: + +- mask() + +*** Sk: + +High: +- antialiased clipping? + +Medium: +- implement clip path reset (to avoid restore/save) +- implement complex radial patterns (2 centers and 2 radii) + +Low: +- implement EXTEND_NONE + +***/ diff --git a/test/Makefile.am b/test/Makefile.am index e4e30639..d525ae3b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -433,8 +433,8 @@ run: check-valgrind: $(MAKE) $(AM_MAKEFLAGS) check TESTS_ENVIRONMENT='$(TESTS_ENVIRONMENT) CAIRO_TEST_MODE="$(MODE),foreground CAIRO_TEST_TIMEOUT=0" $(top_builddir)/libtool --mode=execute valgrind $(VALGRIND_FLAGS)' 2>&1 | tee valgrind-log -%.log: %.c cairo-test-suite - -./cairo-test-suite $(<:.c=) +#%.log: %.c cairo-test-suite +#-./cairo-test-suite $(<:.c=) NOLOG_TESTS_LOG = $(NOLOG_TESTS:=.log) |