diff options
author | hk57.kim <hk57.kim@samsung.com> | 2015-06-03 15:16:56 +0900 |
---|---|---|
committer | hk57.kim <hk57.kim@samsung.com> | 2015-06-03 15:16:56 +0900 |
commit | 4078c98a5d481778482f52d3aaf7a1777ffe6088 (patch) | |
tree | c18ae9d21e2b29c349231c5b61d742e9fca9c1d9 /src/cairo-skia-surface.cpp | |
parent | cce6a0e298fac08c588204b085e7a807fa75813d (diff) | |
download | cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.gz cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.tar.bz2 cairo-4078c98a5d481778482f52d3aaf7a1777ffe6088.zip |
Cairo 1.12.14
Change-Id: Ibc39e63896ec42cab29fbbbf615a46f2d58319a8
Signed-off-by: hk57.kim <hk57.kim@samsung.com>
Diffstat (limited to 'src/cairo-skia-surface.cpp')
-rwxr-xr-x | src/cairo-skia-surface.cpp | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/src/cairo-skia-surface.cpp b/src/cairo-skia-surface.cpp new file mode 100755 index 000000000..bf6b14a5e --- /dev/null +++ b/src/cairo-skia-surface.cpp @@ -0,0 +1,1175 @@ +/* -*- 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express 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-surface-clipper-private.h" +#include "cairo-image-surface-inline.h" + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkPath.h> + +#include <SkColorShader.h> + +#include <SkGradientShader.h> +#include <SkDashPathEffect.h> + +#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 + +#ifndef CAIRO_INT_STATUS_SUCCESS +# define CAIRO_INT_STATUS_SUCCESS ((cairo_int_status_t) CAIRO_STATUS_SUCCESS) +#endif + +#define CAIRO_MAYBE_UNSUPPORTED CAIRO_INT_STATUS_UNSUPPORTED +//#define CAIRO_MAYBE_UNSUPPORTED _skia_unsupported () + +static cairo_int_status_t _skia_unsupported () { + printf ("unsupported!\n"); + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +typedef struct cairo_skia_surface { + cairo_surface_t base; + + SkBitmap *bitmap; + SkCanvas *canvas; + + cairo_surface_clipper_t clipper; + + cairo_image_surface_t *_image_surface; /* wrapper around bitmap */ +} cairo_skia_surface_t; + +static cairo_skia_surface_t * +_cairo_skia_surface_create_internal (SkBitmap::Config config, + bool opaque, + unsigned char *data, + int width, + int height, + int stride); + +/* + * conversion methods + */ + +/* + * format conversion + */ +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_A8: + config = SkBitmap::kA8_Config; + break; + case CAIRO_FORMAT_A1: + config = SkBitmap::kA1_Config; + break; + default: + return false; + } + + return true; +} + +static inline cairo_format_t +sk_config_to_format (SkBitmap::Config config, + bool opaque) +{ + switch (config) { + case SkBitmap::kARGB_8888_Config: + if (opaque) + return CAIRO_FORMAT_RGB24; + return CAIRO_FORMAT_ARGB32; + + case SkBitmap::kA8_Config: + return CAIRO_FORMAT_A8; + + case SkBitmap::kA1_Config: + return CAIRO_FORMAT_A1; + + case SkBitmap::kNo_Config: + case SkBitmap::kIndex8_Config: + case SkBitmap::kRLE_Index8_Config: + case SkBitmap::kRGB_565_Config: + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kConfigCount: + default: + return (cairo_format_t) -1; + } +} + +/* + * image surface wrapping + */ +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; +} + +/* + * operator conversion + */ + +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]; +} + +/* + * tiling mode conversion + */ +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]; +} + +/* + * color conversion + */ +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)); +} + +/* + * matrix conversion + */ +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); +} + +/* + * pattern conversion + */ +static inline cairo_surface_t * +surface_from_pattern (const cairo_pattern_t *pattern) +{ + return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface; +} + +static SkShader* +pattern_to_sk_shader (cairo_skia_surface_t *dst, const cairo_pattern_t *pattern, + cairo_image_surface_t **image, void **image_extra) +{ + 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); + + 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, + image, image_extra); + if (status) + return NULL; + + surface = &(*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_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; +} + +/* + * path conversion + */ + +struct cpc { + SkPath skPath; + cairo_matrix_t *matrix; +}; + +static cairo_status_t +cpc_move_to (void *closure, const cairo_point_t *point) +{ + struct cpc *cpc = static_cast <struct cpc *> (closure); + if (cpc->matrix) { + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (cpc->matrix, &x, &y); + cpc->skPath.moveTo (SkFloatToScalar (x), SkFloatToScalar (y)); + } else { + cpc->skPath.moveTo (CAIRO_FIXED_TO_SK_SCALAR (point->x), + CAIRO_FIXED_TO_SK_SCALAR (point->y)); + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cpc_line_to (void *closure, const cairo_point_t *point) +{ + struct cpc *cpc = static_cast <struct cpc *> (closure); + if (cpc->matrix) { + double x = _cairo_fixed_to_double (point->x); + double y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (cpc->matrix, &x, &y); + cpc->skPath.lineTo (SkFloatToScalar (x), SkFloatToScalar (y)); + } else { + cpc->skPath.lineTo (CAIRO_FIXED_TO_SK_SCALAR (point->x), + CAIRO_FIXED_TO_SK_SCALAR (point->y)); + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cpc_curve_to (void *closure, + const cairo_point_t *p0, + const cairo_point_t *p1, + const cairo_point_t *p2) +{ + struct cpc *cpc = static_cast <struct cpc *> (closure); + if (cpc->matrix) { + double x0 = _cairo_fixed_to_double (p0->x); + double y0 = _cairo_fixed_to_double (p0->y); + double x1 = _cairo_fixed_to_double (p1->x); + double y1 = _cairo_fixed_to_double (p1->y); + double x2 = _cairo_fixed_to_double (p2->x); + double y2 = _cairo_fixed_to_double (p2->y); + cairo_matrix_transform_point (cpc->matrix, &x0, &y0); + cairo_matrix_transform_point (cpc->matrix, &x1, &y1); + cairo_matrix_transform_point (cpc->matrix, &x2, &y2); + + cpc->skPath.cubicTo (SkFloatToScalar (x0), + SkFloatToScalar (y0), + SkFloatToScalar (x1), + SkFloatToScalar (y1), + SkFloatToScalar (x2), + SkFloatToScalar (y2)); + } else { + cpc->skPath.cubicTo (CAIRO_FIXED_TO_SK_SCALAR (p0->x), + CAIRO_FIXED_TO_SK_SCALAR (p0->y), + CAIRO_FIXED_TO_SK_SCALAR (p1->x), + CAIRO_FIXED_TO_SK_SCALAR (p1->y), + CAIRO_FIXED_TO_SK_SCALAR (p2->x), + CAIRO_FIXED_TO_SK_SCALAR (p2->y)); + } + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +cpc_close_path (void *closure) +{ + struct cpc *cpc = static_cast <struct cpc *> (closure); + cpc->skPath.close (); + return CAIRO_STATUS_SUCCESS; +} + +static inline SkPath +path_to_sk (cairo_path_fixed_t *path, + cairo_matrix_t *mat = NULL) +{ + struct cpc data; + cairo_status_t status; + + if (mat && _cairo_matrix_is_identity (mat)) + mat = NULL; + data.matrix = mat; + + status = _cairo_path_fixed_interpret (path, + cpc_move_to, + cpc_line_to, + cpc_curve_to, + cpc_close_path, + &data); + assert (status == CAIRO_STATUS_SUCCESS); + + return data.skPath; +} + +static inline SkPath +path_to_sk (cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + cairo_matrix_t *mat = NULL) +{ + SkPath skPath = path_to_sk (path, mat); + + if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD) + skPath.setFillType (SkPath::kEvenOdd_FillType); + else + skPath.setFillType (SkPath::kWinding_FillType); + + return skPath; +} + +/* + * cairo surface methods + */ + +static cairo_surface_t * +_cairo_skia_surface_create_similar (void *asurface, + cairo_content_t content, + int width, + int height) +{ + SkBitmap::Config config; + bool opaque; + + if (! format_to_sk_config (_cairo_format_from_content (content), + config, opaque)) + { + _skia_unsupported (); + return NULL; + } + + return &_cairo_skia_surface_create_internal (config, opaque, + NULL, + width, height, + 0)->base; +} + +static cairo_status_t +_cairo_skia_surface_finish (void *asurface) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + _cairo_surface_clipper_reset (&surface->clipper); + cairo_surface_destroy (&surface->_image_surface->base); + + delete surface->canvas; + delete surface->bitmap; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_image_surface_t * +_get_image_surface (cairo_skia_surface_t *surface) +{ + if (! surface->_image_surface) { + SkBitmap *bitmap = surface->bitmap; + surface->_image_surface = (cairo_image_surface_t *) + cairo_image_surface_create_for_data ((unsigned char *) bitmap->getPixels (), + sk_config_to_format (bitmap->config (), + bitmap->isOpaque ()), + bitmap->width (), + bitmap->height (), + bitmap->rowBytes ()); + } + + return surface->_image_surface; +} + +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; + cairo_image_surface_t *image = _get_image_surface (surface); + + if (unlikely (image->base.status)) + return image->base.status; + + surface->bitmap->lockPixels (); + + *image_out = 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_status_t +_cairo_skia_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) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + cairo_image_surface_t *image = _get_image_surface (surface); + + if (unlikely (image->base.status)) + return image->base.status; + + image_rect->x = 0; + image_rect->y = 0; + image_rect->width = image->width; + image_rect->height = image->height; + + surface->bitmap->lockPixels (); + + *image_out = image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_skia_surface_release_dest_image (void *asurface, + cairo_rectangle_int_t *interest_rect, + cairo_image_surface_t *image, + cairo_rectangle_int_t *image_rect, + void *image_extra) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + + surface->bitmap->notifyPixelsChanged (); + surface->bitmap->unlockPixels (); +} + +#if 0 +static cairo_status_t +_cairo_skia_surface_clone_similar (void *asurface, + cairo_surface_t *src, + cairo_content_t content, + int src_x, + int src_y, + int width, + int height, + int *clone_offset_x, + int *clone_offset_y, + cairo_surface_t **clone_out) +{ + if (src->type == CAIRO_SURFACE_TYPE_SKIA || _cairo_surface_is_image (src)) { + *clone_offset_x = 0; + *clone_offset_y = 0; + *clone_out = cairo_surface_reference (src); + return CAIRO_STATUS_SUCCESS; + } + + return (cairo_status_t) CAIRO_INT_STATUS_UNSUPPORTED; +} +#endif + +static cairo_status_t +_cairo_skia_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_skia_surface_t *surface = cairo_container_of (clipper, + cairo_skia_surface_t, + clipper); + + if (path == NULL) { + /* XXX TODO: teach Skia how to reset the clip path */ + surface->canvas->restore (); + surface->canvas->save (); + } else { + surface->canvas->clipPath (path_to_sk (path, fill_rule)); + } + + return CAIRO_STATUS_SUCCESS; +} + +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 = 0; + extents->y = 0; + extents->width = surface->bitmap->width (); + extents->height = surface->bitmap->height (); + + return TRUE; +} + +/* + * Core drawing operations + */ + +static SkBitmap * +pattern_to_sk_bitmap (cairo_skia_surface_t *dst, + const cairo_pattern_t *pattern, + SkMatrix *matrix, + cairo_image_surface_t **image, + void **image_extra) +{ + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return NULL; + + if (pattern->extend != CAIRO_EXTEND_NONE) + return NULL; + + cairo_surface_t *surface = surface_from_pattern (pattern); + SkBitmap *bitmap; + + if (surface->type == CAIRO_SURFACE_TYPE_SKIA) { + bitmap = new SkBitmap (*((cairo_skia_surface_t *) surface)->bitmap); + } else { + if (surface->type != CAIRO_SURFACE_TYPE_IMAGE) { + cairo_status_t status; + + status = _cairo_surface_acquire_source_image (surface, + image, image_extra); + if (unlikely (status)) + return NULL; + + surface = &(*image)->base; + } + + bitmap = new SkBitmap; + if (unlikely (! surface_to_sk_bitmap (surface, *bitmap))) + return NULL; + } + + *matrix = matrix_inverse_to_sk (pattern->matrix); + return bitmap; +} + +static cairo_int_status_t +_cairo_skia_surface_paint (void *asurface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_clip_t *clip) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + cairo_image_surface_t *image = NULL; + cairo_status_t status; + void *image_extra; + SkColor color; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return (cairo_int_status_t) status; + + if (pattern_to_sk_color (source, color)) { + surface->canvas->drawColor (color, operator_to_sk (op)); + return CAIRO_INT_STATUS_SUCCESS; + } + + SkMatrix bitmapMatrix; + SkBitmap *bitmap = pattern_to_sk_bitmap (surface, source, &bitmapMatrix, + &image, &image_extra); + SkShader *shader = NULL; + if (!bitmap) + shader = pattern_to_sk_shader (surface, source, &image, &image_extra); + + if (!bitmap && !shader) + return CAIRO_MAYBE_UNSUPPORTED; + + SkPaint paint; + paint.setFilterBitmap (pattern_filter_to_sk (source)); + paint.setXfermodeMode (operator_to_sk (op)); + + if (shader) { + paint.setShader (shader); + surface->canvas->drawPaint (paint); + } else { + surface->canvas->drawBitmapMatrix (*bitmap, bitmapMatrix, &paint); + } + + if (bitmap) + delete bitmap; + if (shader) + shader->unref (); + + if (image != NULL) { + _cairo_surface_release_source_image (&surface->base, + image, image_extra); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_skia_surface_stroke (void *asurface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_stroke_style_t *style, + cairo_matrix_t *ctm, + cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + cairo_image_surface_t *image = NULL; + cairo_status_t status; + void *image_extra; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return (cairo_int_status_t) status; + + SkPaint paint; + paint.setStyle (SkPaint::kStroke_Style); + + SkColor color; + if (pattern_to_sk_color (source, color)) { + paint.setColor (color); + } else { + SkShader *shader = pattern_to_sk_shader (surface, + source, &image, &image_extra); + if (shader == NULL) + return CAIRO_MAYBE_UNSUPPORTED; + + paint.setShader (shader); + shader->unref (); + + paint.setFilterBitmap (pattern_filter_to_sk (source)); + } + + paint.setXfermodeMode (operator_to_sk (op)); + paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE); + + /* Convert the various stroke rendering bits */ + paint.setStrokeWidth (SkFloatToScalar (style->line_width)); + paint.setStrokeMiter (SkFloatToScalar (style->miter_limit)); + + static const SkPaint::Cap capMap[] = { + SkPaint::kButt_Cap, + SkPaint::kRound_Cap, + SkPaint::kSquare_Cap + }; + paint.setStrokeCap (capMap[style->line_cap]); + + static const SkPaint::Join joinMap[] = { + SkPaint::kMiter_Join, + SkPaint::kRound_Join, + SkPaint::kBevel_Join + }; + paint.setStrokeJoin (joinMap[style->line_join]); + + /* If we have a dash pattern, we need to + * create a SkDashPathEffect and set it on the Paint. + */ + if (style->dash != NULL) { + SkScalar intervals_static[20]; + SkScalar *intervals = intervals_static; + + int loop = 0; + unsigned int dash_count = style->num_dashes; + if ((dash_count & 1) != 0) { + loop = 1; + dash_count <<= 1; + } + + if (dash_count > 20) + intervals = new SkScalar[dash_count]; + + unsigned int i = 0; + do { + for (unsigned int j = 0; i < style->num_dashes; j++) + intervals[i++] = SkFloatToScalar (style->dash[j]); + } while (loop--); + + SkDashPathEffect *dash = new SkDashPathEffect (intervals, + dash_count, + SkFloatToScalar (style->dash_offset)); + + paint.setPathEffect (dash); + dash->unref (); + } + + surface->canvas->save (SkCanvas::kMatrix_SaveFlag); + surface->canvas->concat (matrix_to_sk (*ctm)); + surface->canvas->drawPath (path_to_sk (path, ctm_inverse), paint); + surface->canvas->restore (); + + if (image != NULL) { + _cairo_surface_release_source_image (&surface->base, + image, image_extra); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_skia_surface_fill (void *asurface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + cairo_clip_t *clip) +{ + cairo_skia_surface_t *surface = (cairo_skia_surface_t *) asurface; + cairo_image_surface_t *image = NULL; + cairo_status_t status; + void *image_extra; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return (cairo_int_status_t) status; + + + SkPaint paint; + paint.setStyle (SkPaint::kFill_Style); + + SkColor color; + if (pattern_to_sk_color (source, color)) { + paint.setColor (color); + } else { + SkShader *shader = pattern_to_sk_shader (surface, + source, &image, &image_extra); + if (shader == NULL) + return CAIRO_MAYBE_UNSUPPORTED; + + paint.setShader (shader); + shader->unref (); + + paint.setFilterBitmap (pattern_filter_to_sk (source)); + } + + paint.setXfermodeMode (operator_to_sk (op)); + paint.setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE); + + surface->canvas->drawPath (path_to_sk (path, fill_rule), paint); + + if (image != NULL) { + _cairo_surface_release_source_image (&surface->base, + image, image_extra); + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +static const struct _cairo_surface_backend +cairo_skia_surface_backend = { + CAIRO_SURFACE_TYPE_SKIA, + _cairo_skia_surface_create_similar, + _cairo_skia_surface_finish, + _cairo_skia_surface_acquire_source_image, + _cairo_skia_surface_release_source_image, + _cairo_skia_surface_acquire_dest_image, + _cairo_skia_surface_release_dest_image, + + NULL, // _cairo_skia_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 */ + + _cairo_skia_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_skia_surface_paint, + NULL, /* mask? */ + _cairo_skia_surface_stroke, + _cairo_skia_surface_fill, + NULL, /* show_glyphs */ + + NULL, /* snapshot */ + NULL, /* is_similar */ + NULL, /* reset */ +}; + +/* + * Surface constructors + */ + +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; + cairo_format_t format; + + surface = (cairo_skia_surface_t *) malloc (sizeof (cairo_skia_surface_t)); + if (surface == NULL) + return (cairo_skia_surface_t *) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + memset (surface, 0, sizeof (cairo_skia_surface_t)); + + format = sk_config_to_format (config, opaque); + assert (format != -1); + + _cairo_surface_init (&surface->base, + &cairo_skia_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format)); + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_skia_surface_clipper_intersect_clip_path); + + surface->bitmap = new SkBitmap; + if (data == NULL) + stride = cairo_format_stride_for_width (format, width); + surface->bitmap->setConfig (config, width, height, stride); + surface->bitmap->setIsOpaque (opaque); + if (data != NULL) + surface->bitmap->setPixels (data); + else + surface->bitmap->allocPixels (); + + surface->canvas = new SkCanvas (*surface->bitmap); + //surface->canvas->translate (SkIntToScalar (0), SkIntToScalar (height)); + //surface->canvas->scale (SkIntToScalar (1), SkIntToScalar (-1)); + surface->canvas->save (); + + 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)->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)->base; +} + +unsigned char * +cairo_skia_surface_get_data (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return NULL; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return (unsigned char *) esurf->bitmap->getPixels (); +} + +cairo_format_t +cairo_skia_surface_get_format (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return (cairo_format_t) -1; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return sk_config_to_format (esurf->bitmap->config (), + esurf->bitmap->isOpaque ()); +} + +int +cairo_skia_surface_get_width (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return 0; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return esurf->bitmap->width (); +} + +int +cairo_skia_surface_get_height (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return 0; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return esurf->bitmap->height (); +} + +int +cairo_skia_surface_get_stride (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return 0; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return esurf->bitmap->rowBytes (); +} + +cairo_surface_t * +cairo_skia_surface_get_image (cairo_surface_t *surface) +{ + if (surface->type != CAIRO_SURFACE_TYPE_SKIA) + return NULL; + + cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface; + return &_get_image_surface (esurf)->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 + +*/ |