/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
 *
 * Copyright © 2002 University of Southern California
 * Copyright © 2005 Red Hat, Inc.
 * Copyright © 2010 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it either under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation
 * (the "LGPL") or, at your option, under the terms of the Mozilla
 * Public License Version 1.1 (the "MPL"). If you do not alter this
 * notice, a recipient may use your version of this file under either
 * the MPL or the LGPL.
 *
 * You should have received a copy of the LGPL along with this library
 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * You should have received a copy of the MPL along with this library
 * in the file COPYING-MPL-1.1
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
 * the specific language governing rights and limitations.
 *
 * The Original Code is the cairo graphics library.
 *
 * The Initial Developer of the Original Code is University of Southern
 * California.
 *
 * Contributor(s):
 *	Carl D. Worth <cworth@cworth.org>
 *	Chris Wilson <chris@chris-wilson.co.uk>
 */

#include "cairoint.h"

#include "cairo-private.h"
#include "cairo-error-private.h"
#include "cairo-arc-private.h"
#include "cairo-backend-private.h"
#include "cairo-default-context-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-gstate-private.h"
#include "cairo-image-surface-inline.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
#include "cairo-skia-private.h"
#include "cairo-surface-backend-private.h"

#include <SkPaint.h>
#include <SkShader.h>
#include <SkColorShader.h>
#include <SkGradientShader.h>
#include <SkDashPathEffect.h>

#if !defined(INFINITY)
#define INFINITY HUGE_VAL
#endif

#if (CAIRO_FIXED_BITS == 32) && (CAIRO_FIXED_FRAC_BITS == 16) && defined(SK_SCALAR_IS_FIXED)
# define CAIRO_FIXED_TO_SK_SCALAR(x)  (x)
#elif defined(SK_SCALAR_IS_FIXED)
/* This can be done better, but this will do for now */
# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
#else
# define CAIRO_FIXED_TO_SK_SCALAR(x)  SkFloatToScalar(_cairo_fixed_to_double(x))
#endif

#define UNSUPPORTED


static freed_pool_t context_pool;

static void
_cairo_skia_context_destroy (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->path->reset ();
    cr->paint->reset ();

    delete cr->canvas;

    cairo_surface_destroy (&cr->target->image.base);
    cairo_surface_destroy (&cr->original->image.base);

    if (cr->source != NULL) {
	if (cr->source_image != NULL) {
	    _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
	    cr->source_image = NULL;
	}
	cairo_surface_destroy (cr->source);
	cr->source = NULL;
    }

    _cairo_fini (&cr->base);

    _freed_pool_put (&context_pool, cr);
}

static cairo_surface_t *
_cairo_skia_context_get_original_target (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return &cr->original->image.base;
}

static cairo_surface_t *
_cairo_skia_context_get_current_target (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return &cr->target->image.base;
}

static cairo_status_t
_cairo_skia_context_save (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->canvas->save ();
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_restore (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->canvas->restore ();
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_push_group (void *abstract_cr, cairo_content_t content)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_surface_t *group_surface;
    cairo_status_t status;
    int width, height;

    //clip = _cairo_gstate_get_clip (cr->gstate);
    width = cr->target->image.width;
    height = cr->target->image.height;
    group_surface = cr->target->image.base.backend->create_similar (&cr->target->image.base,
								    content, width, height);

#if 0
    /* Set device offsets on the new surface so that logically it appears at
     * the same location on the parent surface -- when we pop_group this,
     * the source pattern will get fixed up for the appropriate target surface
     * device offsets, so we want to set our own surface offsets from /that/,
     * and not from the device origin. */
    cairo_surface_set_device_offset (group_surface,
				     parent_surface->device_transform.x0 - extents.x,
				     parent_surface->device_transform.y0 - extents.y);

    /* If we have a current path, we need to adjust it to compensate for
     * the device offset just applied. */
    _cairo_path_fixed_transform (cr->path,
				 &group_surface->device_transform);
#endif

    status = _cairo_skia_context_save (cr);
    if (unlikely (status)) {
	cairo_surface_destroy (group_surface);
	return status;
    }

    cairo_surface_destroy (&cr->target->image.base);
    cr->target = (cairo_skia_surface_t *) group_surface;

    return CAIRO_STATUS_SUCCESS;
}

static cairo_pattern_t *
_cairo_skia_context_pop_group (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_surface_t *group_surface;
    cairo_pattern_t *group_pattern;
    cairo_status_t status;

    group_surface = cairo_surface_reference (&cr->target->image.base);

    status = _cairo_skia_context_restore (cr);
    if (unlikely (status)) {
	group_pattern = _cairo_pattern_create_in_error (status);
	goto done;
    }

    group_pattern = cairo_pattern_create_for_surface (group_surface);
    status = group_pattern->status;
    if (unlikely (status))
        goto done;

#if 0
    _cairo_gstate_get_matrix (cr->gstate, &group_matrix);
    /* Transform by group_matrix centered around device_transform so that when
     * we call _cairo_gstate_copy_transformed_pattern the result is a pattern
     * with a matrix equivalent to the device_transform of group_surface. */
    if (_cairo_surface_has_device_transform (group_surface)) {
	cairo_pattern_set_matrix (group_pattern, &group_surface->device_transform);
	_cairo_pattern_transform (group_pattern, &group_matrix);
	_cairo_pattern_transform (group_pattern, &group_surface->device_transform_inverse);
    } else {
	cairo_pattern_set_matrix (group_pattern, &group_matrix);
    }

    /* If we have a current path, we need to adjust it to compensate for
     * the device offset just removed. */
    _cairo_path_fixed_transform (cr->path,
				 &group_surface->device_transform_inverse);
#endif

done:
    cairo_surface_destroy (group_surface);

    return group_pattern;
}

static inline cairo_surface_t *
surface_from_pattern (const cairo_pattern_t *pattern)
{
    return (reinterpret_cast <const cairo_surface_pattern_t *> (pattern))->surface;
}

static inline bool
surface_to_sk_bitmap (cairo_surface_t *surface, SkBitmap& bitmap)
{
    cairo_image_surface_t *img = (cairo_image_surface_t *) surface;
    SkBitmap::Config config;
    SkColorType colorType;
    bool opaque;

    if (unlikely (! format_to_sk_config (img->format, config, opaque)))
	return false;

    bitmap.reset ();
    bitmap.setAlphaType (opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
    colorType = SkBitmapConfigToColorType(config);
    bitmap.setInfo (SkImageInfo::Make(img->width, img->height, colorType, kPremul_SkAlphaType), img->stride);
    bitmap.setPixels (img->data);


    return true;
}

static inline SkMatrix
matrix_to_sk (const cairo_matrix_t& mat)
{
    SkMatrix skm;

    skm.reset ();
    skm.set (SkMatrix::kMScaleX, SkFloatToScalar (mat.xx));
    skm.set (SkMatrix::kMSkewX,  SkFloatToScalar (mat.xy));
    skm.set (SkMatrix::kMTransX, SkFloatToScalar (mat.x0));
    skm.set (SkMatrix::kMSkewY,  SkFloatToScalar (mat.yx));
    skm.set (SkMatrix::kMScaleY, SkFloatToScalar (mat.yy));
    skm.set (SkMatrix::kMTransY, SkFloatToScalar (mat.y0));

    /*
    skm[6] = SkFloatToScalar (0.0);
    skm[7] = SkFloatToScalar (0.0);
    skm[8] = SkFloatToScalar (1.0); -- this isn't right, it wants a magic value in there that it'll set itself.  It wants Sk_Fract1 (2.30), not Sk_Scalar1
    */

    return skm;
}

static inline SkMatrix
matrix_inverse_to_sk (const cairo_matrix_t& mat)
{
    cairo_matrix_t inv = mat;
    cairo_status_t status = cairo_matrix_invert (&inv);
    assert (status == CAIRO_STATUS_SUCCESS);
    return matrix_to_sk (inv);
}

static SkShader::TileMode
extend_to_sk (cairo_extend_t extend)
{
    static const SkShader::TileMode modeMap[] = {
	SkShader::kClamp_TileMode,  // NONE behaves like PAD, because noone wants NONE
	SkShader::kRepeat_TileMode,
	SkShader::kMirror_TileMode,
	SkShader::kClamp_TileMode
    };

    return modeMap[extend];
}

static inline SkColor
color_to_sk (const cairo_color_t& c)
{
    /* Need unpremultiplied 1-byte values */
    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
			   (U8CPU) (c.red * 255),
			   (U8CPU) (c.green * 255),
			   (U8CPU) (c.blue * 255));
}

static inline SkColor
color_stop_to_sk (const cairo_color_stop_t& c)
{
    /* Need unpremultiplied 1-byte values */
    return SkColorSetARGB ((U8CPU) (c.alpha * 255),
			   (U8CPU) (c.red * 255),
			   (U8CPU) (c.green * 255),
			   (U8CPU) (c.blue * 255));
}

static SkShader*
source_to_sk_shader (cairo_skia_context_t *cr,
		     const cairo_pattern_t *pattern)
{
    SkShader *shader = NULL;

    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
	return new SkColorShader (color_to_sk (solid->color));
    } else if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
	cairo_surface_t *surface = surface_from_pattern (pattern);

	cr->source = cairo_surface_reference (surface);

	if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
	    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;

		if (! _cairo_matrix_is_identity (&pattern->matrix))
		{
			SkMatrix localMatrix =  matrix_inverse_to_sk (pattern->matrix);
			shader = SkShader::CreateBitmapShader (*esurf->bitmap,
					extend_to_sk (pattern->extend),
					extend_to_sk (pattern->extend),
					&localMatrix);
		} else {
			shader = SkShader::CreateBitmapShader (*esurf->bitmap,
					extend_to_sk (pattern->extend),
					extend_to_sk (pattern->extend));
		}
	} else {
	    SkBitmap bitmap;

	    if (! _cairo_surface_is_image (surface)) {
		cairo_status_t status;

		status = _cairo_surface_acquire_source_image (surface,
							      &cr->source_image,
							      &cr->source_extra);
		if (status)
		    return NULL;

		surface = &cr->source_image->base;
	    }

	    if (unlikely (! surface_to_sk_bitmap (surface, bitmap)))
		return NULL;

		if (! _cairo_matrix_is_identity (&pattern->matrix))
		{
			SkMatrix localMatrix = matrix_inverse_to_sk (pattern->matrix);
			shader = SkShader::CreateBitmapShader (bitmap,
					extend_to_sk (pattern->extend),
					extend_to_sk (pattern->extend),
					&localMatrix);
		} else {
			shader = SkShader::CreateBitmapShader (bitmap,
			extend_to_sk (pattern->extend),
			extend_to_sk (pattern->extend));
		}
	}
    } else if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR
	       /* || pattern->type == CAIRO_PATTERN_TYPE_RADIAL */)
    {
	cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
	SkColor colors_stack[10];
	SkScalar pos_stack[10];
	SkColor *colors = colors_stack;
	SkScalar *pos = pos_stack;

	if (gradient->n_stops > 10) {
	    colors = new SkColor[gradient->n_stops];
	    pos = new SkScalar[gradient->n_stops];
	}

	for (unsigned int i = 0; i < gradient->n_stops; i++) {
	    pos[i] = CAIRO_FIXED_TO_SK_SCALAR (gradient->stops[i].offset);
	    colors[i] = color_stop_to_sk (gradient->stops[i].color);
	}

	if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
	    cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
	    SkPoint points[2];

	    points[0].set (SkFloatToScalar (linear->pd1.x),
			   SkFloatToScalar (linear->pd1.y));
	    points[1].set (SkFloatToScalar (linear->pd2.x),
			   SkFloatToScalar (linear->pd2.y));

	    if(! _cairo_matrix_is_identity (&pattern->matrix))
	    {
		SkMatrix localMatrix = matrix_inverse_to_sk (pattern->matrix);
	        shader = SkGradientShader::CreateLinear (points, colors, pos, gradient->n_stops,
						     extend_to_sk (pattern->extend),
						     0, &localMatrix);
	    } else {
	        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;
	}
    }

    return shader;
}

static inline bool
pattern_filter_to_sk (const cairo_pattern_t *pattern)
{
    switch (pattern->filter) {
    case CAIRO_FILTER_GOOD:
    case CAIRO_FILTER_BEST:
    case CAIRO_FILTER_BILINEAR:
    case CAIRO_FILTER_GAUSSIAN:
	return true;
    default:
    case CAIRO_FILTER_FAST:
    case CAIRO_FILTER_NEAREST:
	return false;
    }
}

static inline bool
pattern_to_sk_color (const cairo_pattern_t *pattern, SkColor& color)
{
    if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
	return false;

    color = color_to_sk (((cairo_solid_pattern_t *) pattern)->color);
    return true;
}

static cairo_status_t
_cairo_skia_context_set_source (void *abstract_cr,
				cairo_pattern_t *source)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkColor color;

    if (cr->source != NULL) {
	if (cr->source_image != NULL) {
	    _cairo_surface_release_source_image (cr->source, cr->source_image, cr->source_extra);
	    cr->source_image = NULL;
	}
	cairo_surface_destroy (cr->source);
	cr->source = NULL;
    }

    if (pattern_to_sk_color (source, color)) {
	cr->paint->setColor (color);
    } else {
	SkShader *shader = source_to_sk_shader (cr, source);
	bool fLevel = pattern_filter_to_sk (source);
	if (shader == NULL) {
	    UNSUPPORTED;
	    return CAIRO_STATUS_SUCCESS;
	}

	cr->paint->setShader (shader);
	shader->unref ();

	cr->paint->setFilterLevel (fLevel ?
				(SkPaint::kLow_FilterLevel) : (SkPaint::kNone_FilterLevel));
    }

    /* XXX change notification */
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_source_rgba (void *abstract_cr, double red, double green, double blue, double alpha)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* Need unpremultiplied 1-byte values */
    cr->paint->setARGB ((U8CPU) (alpha * 255),
			(U8CPU) (red * 255),
			(U8CPU) (green * 255),
			(U8CPU) (blue * 255));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_source_surface (void *abstract_cr,
					cairo_surface_t *surface,
					double	   x,
					double	   y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_pattern_t *pattern;
    cairo_matrix_t matrix;
    cairo_status_t status;

    if (surface->type == CAIRO_SURFACE_TYPE_SKIA) {
	cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) surface;
	SkShader *shader;

	shader = SkShader::CreateBitmapShader (*esurf->bitmap,
					       SkShader::kClamp_TileMode, /* XXX */
					       SkShader::kClamp_TileMode);

	cr->paint->setShader (shader);
	shader->unref ();

	cr->paint->setFilterLevel (true ?
		(SkPaint::kLow_FilterLevel) : (SkPaint::kNone_FilterLevel));

	return CAIRO_STATUS_SUCCESS;
    }

    pattern = cairo_pattern_create_for_surface (surface);
    if (unlikely (pattern->status))
	return pattern->status;

    cairo_matrix_init_translate (&matrix, -x, -y);
    cairo_pattern_set_matrix (pattern, &matrix);

    status = _cairo_skia_context_set_source (cr, pattern);
    cairo_pattern_destroy (pattern);

    return status;
}

static cairo_pattern_t *
_cairo_skia_context_get_source (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return NULL;
}

static cairo_status_t
_cairo_skia_context_set_tolerance (void *abstract_cr,
				   double tolerance)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX ignored */
    return CAIRO_STATUS_SUCCESS;
}

static inline SkXfermode::Mode
operator_to_sk (cairo_operator_t op)
{
    static const SkXfermode::Mode modeMap[] = {
	SkXfermode::kClear_Mode,

	SkXfermode::kSrc_Mode,
	SkXfermode::kSrcOver_Mode,
	SkXfermode::kSrcIn_Mode,
	SkXfermode::kSrcOut_Mode,
	SkXfermode::kSrcATop_Mode,

	SkXfermode::kDst_Mode,
	SkXfermode::kDstOver_Mode,
	SkXfermode::kDstIn_Mode,
	SkXfermode::kDstOut_Mode,
	SkXfermode::kDstATop_Mode,

	SkXfermode::kXor_Mode,
	SkXfermode::kPlus_Mode, // XXX Add?
	SkXfermode::kPlus_Mode, // XXX SATURATE

	SkXfermode::kPlus_Mode,
	SkXfermode::kMultiply_Mode,
	SkXfermode::kScreen_Mode,
	SkXfermode::kOverlay_Mode,
	SkXfermode::kDarken_Mode,
	SkXfermode::kLighten_Mode,
	SkXfermode::kColorDodge_Mode,
	SkXfermode::kColorBurn_Mode,
	SkXfermode::kHardLight_Mode,
	SkXfermode::kSoftLight_Mode,
	SkXfermode::kDifference_Mode,
	SkXfermode::kExclusion_Mode,

	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_HUE
	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_SATURATION,
	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_COLOR,
	SkXfermode::kSrcOver_Mode, // XXX: CAIRO_OPERATOR_HSL_LUMINOSITY
    };

    return modeMap[op];
}

static cairo_status_t
_cairo_skia_context_set_operator (void *abstract_cr, cairo_operator_t op)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setXfermodeMode (operator_to_sk (op));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_opacity (void *abstract_cr, double opacity)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX */
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_antialias (void *abstract_cr, cairo_antialias_t antialias)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setAntiAlias (antialias != CAIRO_ANTIALIAS_NONE);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_fill_rule (void *abstract_cr,
				   cairo_fill_rule_t fill_rule)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->path->setFillType (fill_rule == CAIRO_FILL_RULE_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_line_width (void *abstract_cr,
				    double line_width)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setStrokeWidth (SkFloatToScalar (line_width));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_line_cap (void *abstract_cr,
				  cairo_line_cap_t line_cap)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    static const SkPaint::Cap map[] = {
	SkPaint::kButt_Cap,
	SkPaint::kRound_Cap,
	SkPaint::kSquare_Cap
    };
    cr->paint->setStrokeCap (map[line_cap]);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_line_join (void *abstract_cr,
				   cairo_line_join_t line_join)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    static const SkPaint::Join map[] = {
	SkPaint::kMiter_Join,
	SkPaint::kRound_Join,
	SkPaint::kBevel_Join
    };
    cr->paint->setStrokeJoin (map[line_join]);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_dash (void *abstract_cr,
			      const double *dashes,
			      int	      num_dashes,
			      double	      offset)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkScalar intervals_static[20];
    SkScalar *intervals = intervals_static;

    if (num_dashes == 0) {
	cr->paint->setPathEffect (NULL);
	return CAIRO_STATUS_SUCCESS;
    }

    int loop = 0;
    if ((num_dashes & 1) != 0) {
	loop = 1;
	num_dashes <<= 1;
    }

    if (num_dashes > 20)
	intervals = new SkScalar[num_dashes];

    int i = 0;
    do {
	for (int j = 0; i < num_dashes; j++)
	    intervals[i++] = SkFloatToScalar (dashes[j]);
    } while (loop--);

    SkDashPathEffect *dash = SkDashPathEffect::Create (intervals, num_dashes, SkFloatToScalar (offset));

    cr->paint->setPathEffect (dash);
    dash->unref ();

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_miter_limit (void *abstract_cr, double limit)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setStrokeMiter (SkFloatToScalar (limit));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_antialias_t
_cairo_skia_context_get_antialias (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return cr->paint->isAntiAlias () ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE;
}

static void
_cairo_skia_context_get_dash (void *abstract_cr,
			      double *dashes,
			      int *num_dashes,
			      double *offset)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    *num_dashes = 0;
    /* XXX */
}

static cairo_fill_rule_t
_cairo_skia_context_get_fill_rule (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkPath::FillType ft;

    ft = cr->path->getFillType ();
    if (ft == SkPath::kWinding_FillType)
	return CAIRO_FILL_RULE_WINDING;
    if (ft == SkPath::kEvenOdd_FillType)
	return CAIRO_FILL_RULE_EVEN_ODD;;

    UNSUPPORTED;
    return CAIRO_FILL_RULE_WINDING;
}

static double
_cairo_skia_context_get_line_width (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return /* ScalarToFloat */ cr->paint->getStrokeWidth ();
}

static cairo_line_cap_t
_cairo_skia_context_get_line_cap (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    static const cairo_line_cap_t map[] = {
	CAIRO_LINE_CAP_BUTT,
	CAIRO_LINE_CAP_ROUND,
	CAIRO_LINE_CAP_SQUARE
    };
    return map[cr->paint->getStrokeCap ()];
}

static cairo_line_join_t
_cairo_skia_context_get_line_join (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    static const cairo_line_join_t map[] = {
	CAIRO_LINE_JOIN_MITER,
	CAIRO_LINE_JOIN_ROUND,
	CAIRO_LINE_JOIN_BEVEL
    };
    return map[cr->paint->getStrokeJoin ()];
}

static double
_cairo_skia_context_get_miter_limit (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return /* SkScalarToFloat */ cr->paint->getStrokeMiter ();
}

static cairo_operator_t
_cairo_skia_context_get_operator (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    //cr->paint->getXfermode ();
    return CAIRO_OPERATOR_OVER;
}

static double
_cairo_skia_context_get_opacity (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return 1.;
}

static double
_cairo_skia_context_get_tolerance (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX */
    return CAIRO_GSTATE_TOLERANCE_DEFAULT;
}


/* Current tranformation matrix */

static cairo_status_t
_cairo_skia_context_translate (void *abstract_cr,
			       double tx,
			       double ty)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_translate (&cr->matrix, tx, ty);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_scale (void *abstract_cr,
			   double sx,
			      double sy)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_scale (&cr->matrix, sx, sy);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rotate (void *abstract_cr,
			    double theta)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_rotate (&cr->matrix, theta);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_transform (void *abstract_cr,
			       const cairo_matrix_t *matrix)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_multiply (&cr->matrix, &cr->matrix, matrix);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_matrix (void *abstract_cr,
				const cairo_matrix_t *matrix)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->matrix = *matrix;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_identity_matrix (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_init_identity (&cr->matrix);
    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_skia_context_get_matrix (void *abstract_cr,
				cairo_matrix_t *matrix)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    *matrix = cr->matrix;
}

static void
_cairo_skia_context_user_to_device (void *abstract_cr,
				    double *x,
				    double *y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_transform_point (&cr->matrix, x, y);
}

static void
_cairo_skia_context_user_to_device_distance (void *abstract_cr,
					     double *dx,
					     double *dy)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
}

static void
_cairo_skia_context_device_to_user (void *abstract_cr,
				    double *x,
				    double *y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_matrix_t inverse;
    cairo_status_t status;

    inverse = cr->matrix;
    status = cairo_matrix_invert (&inverse);
    assert (CAIRO_STATUS_SUCCESS == status);

    cairo_matrix_transform_point (&inverse, x, y);
}

static void
_cairo_skia_context_device_to_user_distance (void *abstract_cr,
					     double *dx,
					     double *dy)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_matrix_t inverse;
    cairo_status_t status;

    inverse = cr->matrix;
    status = cairo_matrix_invert (&inverse);
    assert (CAIRO_STATUS_SUCCESS == status);

    cairo_matrix_transform_distance (&inverse, dx, dy);
}

/* Path constructor */

static cairo_status_t
_cairo_skia_context_new_path (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->path->reset ();
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_new_sub_path (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->path->rMoveTo (0, 0); /* XXX */
    return CAIRO_STATUS_SUCCESS;
}

static void
user_to_device_point (cairo_skia_context_t *cr, double *x, double *y)
{
    cairo_matrix_transform_point (&cr->matrix, x, y);
    cairo_matrix_transform_point (&cr->target->image.base.device_transform, x, y);
}

static void
user_to_device_distance (cairo_skia_context_t *cr, double *dx, double *dy)
{
    cairo_matrix_transform_distance (&cr->matrix, dx, dy);
    cairo_matrix_transform_distance (&cr->target->image.base.device_transform, dx, dy);
}

static cairo_status_t
_cairo_skia_context_move_to (void *abstract_cr, double x, double y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_point (cr, &x, &y);
    cr->path->moveTo (SkFloatToScalar (x), SkFloatToScalar (y));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_line_to (void *abstract_cr, double x, double y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_point (cr, &x, &y);
    cr->path->lineTo (SkFloatToScalar (x), SkFloatToScalar (y));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_curve_to (void *abstract_cr,
			      double x1, double y1,
			      double x2, double y2,
			      double x3, double y3)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_point (cr, &x1, &y1);
    user_to_device_point (cr, &x2, &y2);
    user_to_device_point (cr, &x3, &y3);
    cr->path->cubicTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
		       SkFloatToScalar (x2), SkFloatToScalar (y2),
		       SkFloatToScalar (x3), SkFloatToScalar (y3));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_arc_to (void *abstract_cr,
			    double x1, double y1,
			    double x2, double y2,
			    double radius)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

#if 0
    user_to_device_point (cr, &x1, &y1);
    user_to_device_point (cr, &x2, &y2);
    user_to_device_distance (cr, &radius, &radius);
#endif

    cr->path->arcTo (SkFloatToScalar (x1), SkFloatToScalar (y1),
		     SkFloatToScalar (x2), SkFloatToScalar (y2),
		     SkFloatToScalar (radius));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rel_move_to (void *abstract_cr, double dx, double dy)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_distance (cr, &dx, &dy);
    cr->path->rMoveTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rel_line_to (void *abstract_cr, double dx, double dy)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_distance (cr, &dx, &dy);
    cr->path->rLineTo (SkFloatToScalar (dx), SkFloatToScalar (dy));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rel_curve_to (void *abstract_cr,
				  double dx1, double dy1,
				  double dx2, double dy2,
				  double dx3, double dy3)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    user_to_device_distance (cr, &dx1, &dy1);
    user_to_device_distance (cr, &dx2, &dy2);
    user_to_device_distance (cr, &dx3, &dy3);
    cr->path->rCubicTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
			SkFloatToScalar (dx2), SkFloatToScalar (dy2),
			SkFloatToScalar (dx3), SkFloatToScalar (dy3));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rel_arc_to (void *abstract_cr,
				double dx1, double dy1,
				double dx2, double dy2,
				double radius)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

#if 0
    user_to_device_point (cr, &x1, &y1);
    user_to_device_point (cr, &x2, &y2);
    user_to_device_distance (cr, &radius, &radius);
#endif

    cr->path->arcTo (SkFloatToScalar (dx1), SkFloatToScalar (dy1),
		     SkFloatToScalar (dx2), SkFloatToScalar (dy2),
		     SkFloatToScalar (radius));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_close_path (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->path->close ();
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_rectangle (void *abstract_cr,
			       double x, double y,
			       double width, double height)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    double x1, y1, x2, y2;

    /* XXX assume no rotation! */
    x1 = x, y1 = y;
    user_to_device_point (cr, &x1, &y1);

    x2 = x + width, y2 = y + height;
    user_to_device_point (cr, &x2, &y2);

    cr->path->addRect (SkFloatToScalar (x1), SkFloatToScalar (y1),
		       SkFloatToScalar (x2), SkFloatToScalar (y2));
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_arc (void *abstract_cr,
			 double xc, double yc, double radius,
			 double angle1, double angle2,
			 cairo_bool_t forward)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_status_t status;

    /* XXX cr->path->arc() */

    /* Do nothing, successfully, if radius is <= 0 */
    if (radius <= 0.0) {
	status = _cairo_skia_context_line_to (cr, xc, yc);
	if (unlikely (status))
	    return status;

	status = _cairo_skia_context_line_to (cr, xc, yc);
	if (unlikely (status))
	    return status;

	return CAIRO_STATUS_SUCCESS;
    }

    status = _cairo_skia_context_line_to (cr,
					  xc + radius * cos (angle1),
					  yc + radius * sin (angle1));

    if (unlikely (status))
	return status;

    if (forward)
	_cairo_arc_path (&cr->base, xc, yc, radius, angle1, angle2);
    else
	_cairo_arc_path_negative (&cr->base, xc, yc, radius, angle1, angle2);

    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_skia_context_path_extents (void *abstract_cr,
				  double *x1,
				  double *y1,
				  double *x2,
				  double *y2)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkRect rect;

    rect = cr->path->getBounds ();

    UNSUPPORTED;
    /* XXX transform SkScalar rect to user */
}

static cairo_bool_t
_cairo_skia_context_has_current_point (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return TRUE;
}

static cairo_bool_t
_cairo_skia_context_get_current_point (void *abstract_cr,
				       double *x,
				       double *y)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkPoint pt;

    cr->path->getLastPt (&pt);
    //*x = SkScalarToFloat (pt.x);
    //*y = SkScalarToFloat (pt.y);
    //_cairo_gstate_backend_to_user (cr->gstate, x, y);

    return TRUE;
}

static cairo_path_t *
_cairo_skia_context_copy_path (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX iterate */
    UNSUPPORTED;
    return NULL;
}

static cairo_path_t *
_cairo_skia_context_copy_path_flat (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX iterate and decompose */
    UNSUPPORTED;
    return NULL;
}

static cairo_status_t
_cairo_skia_context_append_path (void *abstract_cr,
				 const cairo_path_t *path)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

   // return _cairo_path_append_to_context (path, cr);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_stroke_to_path (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setStyle (SkPaint::kStroke_Style);
    cr->paint->getFillPath (*cr->path, cr->path);
    return CAIRO_STATUS_SUCCESS;
}


static cairo_status_t
_cairo_skia_context_paint (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

#if 0
    if (cr->source != NULL) {
	SkBitmap bitmap;
	SkMatrix bitmapMatrix;

	if (cr->source->type == CAIRO_SURFACE_TYPE_SKIA) {
	    cairo_skia_surface_t *esurf = (cairo_skia_surface_t *) cr->source->type;

	    bitmap = *esurf->bitmap;
	} else {
	    surface_to_sk_bitmap (&cr->source_image->base, bitmap);
	}

	// XXX pattern->matrix, pattern->filter, pattern->extend
	cr->canvas->drawBitmapMatrix (bitmap, bitmapMatrix, cr->paint);
    } else {
	cr->canvas->drawPaint (*cr->paint);
    }
#else
    cr->canvas->drawPaint (*cr->paint);
#endif
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_paint_with_alpha (void *abstract_cr,
				      double alpha)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_status_t status;

    if (CAIRO_ALPHA_IS_OPAQUE (alpha))
	return _cairo_skia_context_paint (cr);

    cr->paint->setAlpha(SkScalarRoundToInt(255*alpha));
    status = _cairo_skia_context_paint (cr);
    cr->paint->setAlpha(255);

    return status;
}

static cairo_status_t
_cairo_skia_context_mask (void *abstract_cr,
			  cairo_pattern_t *mask)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX */
    //UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_stroke_preserve (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setStyle (SkPaint::kStroke_Style);

    /* XXX pen transformation? */
    //assert (_cairo_matrix_is_identity (&cr->matrix));
    cr->canvas->drawPath (*cr->path, *cr->paint);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_stroke (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_status_t status;

    status = _cairo_skia_context_stroke_preserve (cr);
    if (unlikely (status))
	return status;

    return _cairo_skia_context_new_path (cr);
}

static cairo_status_t
_cairo_skia_context_in_stroke (void *abstract_cr,
			       double x, double y,
			       cairo_bool_t *inside)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_stroke_extents (void *abstract_cr,
				    double *x1, double *y1, double *x2, double *y2)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_fill_preserve (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->paint->setStyle (SkPaint::kFill_Style);
    cr->canvas->drawPath (*cr->path, *cr->paint);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_fill (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_status_t status;

    status = _cairo_skia_context_fill_preserve (cr);
    if (unlikely (status))
	return status;

    return _cairo_skia_context_new_path (cr);
}

static cairo_status_t
_cairo_skia_context_in_fill (void *abstract_cr,
				double x, double y,
				cairo_bool_t *inside)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_fill_extents (void *abstract_cr,
				     double *x1, double *y1, double *x2, double *y2)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_clip_preserve (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    cr->canvas->clipPath (*cr->path);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_clip (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    cairo_status_t status;

    status = _cairo_skia_context_clip_preserve (cr);
    if (unlikely (status))
	return status;

    return _cairo_skia_context_new_path (cr);
}

static cairo_status_t
_cairo_skia_context_in_clip (void *abstract_cr,
			     double x, double y,
			     cairo_bool_t *inside)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_reset_clip (void *abstract_cr)
{
    cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    SkRegion rgn(SkIRect::MakeWH (cr->target->bitmap->width (),
				  cr->target->bitmap->height ()));

    cr->canvas->setClipRegion(rgn);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_clip_extents (void *abstract_cr,
				  double *x1, double *y1,
				  double *x2, double *y2)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_rectangle_list_t *
_cairo_skia_context_copy_clip_rectangle_list (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return NULL;
}

static cairo_status_t
_cairo_skia_context_copy_page (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_show_page (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;
    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_font_face (void *abstract_cr,
				   cairo_font_face_t *font_face)
{
   // cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    //return _cairo_gstate_set_font_face (cr->gstate, font_face);
    return CAIRO_STATUS_SUCCESS;
}

static cairo_font_face_t *
_cairo_skia_context_get_font_face (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return NULL;
}

static cairo_status_t
_cairo_skia_context_font_extents (void *abstract_cr,
				  cairo_font_extents_t *extents)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_font_size (void *abstract_cr,
				   double size)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_set_font_matrix (void *abstract_cr,
				     const cairo_matrix_t *matrix)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_skia_context_get_font_matrix (void *abstract_cr,
				     cairo_matrix_t *matrix)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
}

static cairo_status_t
_cairo_skia_context_set_font_options (void *abstract_cr,
				      const cairo_font_options_t *options)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static void
_cairo_skia_context_get_font_options (void *abstract_cr,
				      cairo_font_options_t *options)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
}

static cairo_status_t
_cairo_skia_context_set_scaled_font (void *abstract_cr,
					cairo_scaled_font_t *scaled_font)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_scaled_font_t *
_cairo_skia_context_get_scaled_font (void *abstract_cr)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return _cairo_scaled_font_create_in_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
}

static cairo_status_t
_cairo_skia_context_glyphs (void *abstract_cr,
			    const cairo_glyph_t *glyphs,
			    int num_glyphs,
			    cairo_glyph_text_info_t *info)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    /* XXX */
    //UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_glyph_path (void *abstract_cr,
				const cairo_glyph_t *glyphs,
				int num_glyphs)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_skia_context_glyph_extents (void                *abstract_cr,
				   const cairo_glyph_t    *glyphs,
				   int                    num_glyphs,
				   cairo_text_extents_t   *extents)
{
    //cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr;

    UNSUPPORTED;
    return CAIRO_STATUS_SUCCESS;
}

static const cairo_backend_t _cairo_skia_context_backend = {
    CAIRO_TYPE_SKIA,
    _cairo_skia_context_destroy,

    _cairo_skia_context_get_original_target,
    _cairo_skia_context_get_current_target,

    _cairo_skia_context_save,
    _cairo_skia_context_restore,

    _cairo_skia_context_push_group,
    _cairo_skia_context_pop_group,

    _cairo_skia_context_set_source_rgba,
    _cairo_skia_context_set_source_surface,
    _cairo_skia_context_set_source,
    _cairo_skia_context_get_source,

    _cairo_skia_context_set_antialias,
    _cairo_skia_context_set_dash,
    _cairo_skia_context_set_fill_rule,
    _cairo_skia_context_set_line_cap,
    _cairo_skia_context_set_line_join,
    _cairo_skia_context_set_line_width,
    _cairo_skia_context_set_miter_limit,
    _cairo_skia_context_set_opacity,
    _cairo_skia_context_set_operator,
    _cairo_skia_context_set_tolerance,
    _cairo_skia_context_get_antialias,
    _cairo_skia_context_get_dash,
    _cairo_skia_context_get_fill_rule,
    _cairo_skia_context_get_line_cap,
    _cairo_skia_context_get_line_join,
    _cairo_skia_context_get_line_width,
    _cairo_skia_context_get_miter_limit,
    _cairo_skia_context_get_opacity,
    _cairo_skia_context_get_operator,
    _cairo_skia_context_get_tolerance,

    _cairo_skia_context_translate,
    _cairo_skia_context_scale,
    _cairo_skia_context_rotate,
    _cairo_skia_context_transform,
    _cairo_skia_context_set_matrix,
    _cairo_skia_context_set_identity_matrix,
    _cairo_skia_context_get_matrix,
    _cairo_skia_context_user_to_device,
    _cairo_skia_context_user_to_device_distance,
    _cairo_skia_context_device_to_user,
    _cairo_skia_context_device_to_user_distance,
    _cairo_skia_context_user_to_device, /* XXX backend */
    _cairo_skia_context_user_to_device_distance, /* XXX backend */
    _cairo_skia_context_device_to_user, /* XXX backend */
    _cairo_skia_context_device_to_user_distance, /* XXX backend */

    _cairo_skia_context_new_path,
    _cairo_skia_context_new_sub_path,
    _cairo_skia_context_move_to,
    _cairo_skia_context_rel_move_to,
    _cairo_skia_context_line_to,
    _cairo_skia_context_rel_line_to,
    _cairo_skia_context_curve_to,
    _cairo_skia_context_rel_curve_to,
    _cairo_skia_context_arc_to,
    _cairo_skia_context_rel_arc_to,
    _cairo_skia_context_close_path,
    _cairo_skia_context_arc,
    _cairo_skia_context_rectangle,
    _cairo_skia_context_path_extents,
    _cairo_skia_context_has_current_point,
    _cairo_skia_context_get_current_point,
    _cairo_skia_context_copy_path,
    _cairo_skia_context_copy_path_flat,
    _cairo_skia_context_append_path,

    _cairo_skia_stroke_to_path,

    _cairo_skia_context_clip,
    _cairo_skia_context_clip_preserve,
    _cairo_skia_context_in_clip,
    _cairo_skia_context_clip_extents,
    _cairo_skia_context_reset_clip,
    _cairo_skia_context_copy_clip_rectangle_list,

    _cairo_skia_context_paint,
    _cairo_skia_context_paint_with_alpha,
    _cairo_skia_context_mask,

    _cairo_skia_context_stroke,
    _cairo_skia_context_stroke_preserve,
    _cairo_skia_context_in_stroke,
    _cairo_skia_context_stroke_extents,

    _cairo_skia_context_fill,
    _cairo_skia_context_fill_preserve,
    _cairo_skia_context_in_fill,
    _cairo_skia_context_fill_extents,

    _cairo_skia_context_set_font_face,
    _cairo_skia_context_get_font_face,
    _cairo_skia_context_set_font_size,
    _cairo_skia_context_set_font_matrix,
    _cairo_skia_context_get_font_matrix,
    _cairo_skia_context_set_font_options,
    _cairo_skia_context_get_font_options,
    _cairo_skia_context_set_scaled_font,
    _cairo_skia_context_get_scaled_font,
    _cairo_skia_context_font_extents,

    _cairo_skia_context_glyphs,
    _cairo_skia_context_glyph_path,
    _cairo_skia_context_glyph_extents,

    _cairo_skia_context_copy_page,
    _cairo_skia_context_show_page,
};

cairo_t *
_cairo_skia_context_create (void *target)
{
    cairo_skia_surface_t *surface = (cairo_skia_surface_t *) target;
    cairo_skia_context_t *cr;

    cr = (cairo_skia_context_t *) _freed_pool_get (&context_pool);
    if (unlikely (cr == NULL)) {
	    cr = new cairo_skia_context_t;
	    if (unlikely (cr == NULL))
		return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));

	    cr->path = new SkPath;
	    cr->paint = new SkPaint;
    }

    _cairo_init (&cr->base, &_cairo_skia_context_backend);

    cr->source = NULL;
    cr->source_image = NULL;

    cr->paint->setStrokeWidth (SkFloatToScalar (2.0));

    cr->target = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
    cr->original = (cairo_skia_surface_t *) cairo_surface_reference ((cairo_surface_t *) target);
    cr->canvas = new SkCanvas (*surface->bitmap);
    cr->canvas->save ();

    cairo_matrix_init_identity (&cr->matrix);

    return &cr->base;
}

#if 0
void
_cairo_skia_context_set_SkPaint (cairo_t *cr, SkPaint paint)
{
    *cr->paint = paint;
}

void
_cairo_skia_context_set_SkPath (cairo_t *cr, SkPath path)
{
    *cr->path = path;
}
#endif
