Logo Search packages:      
Sourcecode: cairo version File versions  Download package

cairo-skia-surface.cpp

/* -*- 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 <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;
}

00074 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 (CAIRO_FIXED_TO_SK_SCALAR (linear->p1.x),
                     CAIRO_FIXED_TO_SK_SCALAR (linear->p1.y));
          points[1].set (CAIRO_FIXED_TO_SK_SCALAR (linear->p2.x),
                     CAIRO_FIXED_TO_SK_SCALAR (linear->p2.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
 */

00411 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,
                                CAIRO_DIRECTION_FORWARD,
                                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

***/

Generated by  Doxygen 1.6.0   Back to index