/* -*- 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 */ #include "cairoint.h" #include "cairo-skia.h" #include "cairo-surface-clipper-private.h" #include "cairo-image-surface-inline.h" #include #include #include #include #include #include #include /** * SECTION:cairo-skia * @Title: Skia Surfaces * @Short_Description: Rendering to Skia surfaces * @See_Also: #cairo_surface_t * * Originally written by Vladimir Vukicevic to investigate using Skia for * Mozilla, it provides a nice integration with a rather interesting code * base. By hooking Skia underneath Cairo it allows us to directly compare * code paths... which is interesting. **/ /** * CAIRO_HAS_SKIA_SURFACE: * * Defined if the Skia surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.10 **/ #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 DEBUG_SKIA 0 #if DEBUG_SKIA #define UNSUPPORTED(reason) ({ \ fprintf (stderr, \ "cairo-skia : hit unsupported operation in %s(), line %d: %s\n", \ __FUNCTION__, __LINE__, reason); \ return CAIRO_INT_STATUS_UNSUPPORTED; \ }) #else #define UNSUPPORTED(reason) ({ \ return CAIRO_INT_STATUS_UNSUPPORTED; \ })#endif 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 (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 (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 (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 (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 (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)) { 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; } 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 UNSUPPORTED("pattern to bitmap and shader conversion"); 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 UNSUPPORTED("pattern to shader conversion"); 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 UNSUPPORTED("pattern to shader conversion"); 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 */