changeset 482:d38aca825822 Android_Skia

Import more functions for Skia graphic engine.
author Thinker K.F. Li <thinker@branda.to>
date Sun, 15 Nov 2009 16:23:21 +0800
parents 273307d061b7
children d984a6672be7
files include/mb_graph_engine_skia.h src/graph_engine_skia.cpp
diffstat 2 files changed, 736 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/include/mb_graph_engine_skia.h	Sun Nov 15 16:21:09 2009 +0800
+++ b/include/mb_graph_engine_skia.h	Sun Nov 15 16:23:21 2009 +0800
@@ -2,6 +2,7 @@
 #define __MB_GE_SKIA_H_
 
 #include <stdio.h>
+#include "mb_basic_types.h"
 #include "mb_img_ldr.h"
 
 /*! \defgroup mb_ge_skia MadButterfly Graphic Engine with Skia
@@ -11,8 +12,6 @@
 #define MBE_OPERATOR_SOURCE 1
 #define MBE_STATUS_SUCCESS 0
 
-typedef float co_aix;
-
 struct _mbe_text_extents_t {
     co_aix x_bearing;
     co_aix y_bearing;
@@ -34,20 +33,20 @@
 typedef struct _mbe_pattern_t mbe_pattern_t;
 typedef struct _mbe_t mbe_t;
 
-extern void mbe_pattern_add_color_stop_rgba(mbe_pattern_t *ptn,
-					    co_aix offset,
-					    co_aix r, co_aix g, co_aix b,
-					    co_aix a);
 extern mbe_pattern_t *mbe_pattern_create_for_surface(mbe_surface_t *surface);
 extern mbe_pattern_t *mbe_pattern_create_radial(co_aix cx0, co_aix cy0,
 						co_aix radius0,
 						co_aix cx1, co_aix cy1,
-						co_aix radius1);
+						co_aix radius1,
+						grad_stop_t *stops,
+						int stop_cnt);
 extern mbe_pattern_t *mbe_pattern_create_linear(co_aix x0, co_aix y0,
-						co_aix x1, co_aix y1);
+						co_aix x1, co_aix y1,
+						grad_stop_t *stops,
+						int stop_cnt);
 extern void mbe_pattern_set_matrix(mbe_pattern_t *ptn,
 				   const co_aix matrix[6]);
-extern void mbe_pattern_destroy(mbe_pattern_t *canvas);
+extern void mbe_pattern_destroy(mbe_pattern_t *ptn);
 
 extern int mbe_image_surface_get_stride(mbe_surface_t *surface);
 extern int mbe_image_surface_get_height(mbe_surface_t *surface);
--- a/src/graph_engine_skia.cpp	Sun Nov 15 16:21:09 2009 +0800
+++ b/src/graph_engine_skia.cpp	Sun Nov 15 16:23:21 2009 +0800
@@ -1,7 +1,45 @@
+/*! \page ge_layer Graphic Engine Layer
+ *
+ * Graphic Engine Layer is an abstract of graphic engine; likes Cairo
+ * and Skia.  It provides portability for the rest of MadButterfly.
+ *
+ * The basic stratage of interface of graphic engine layer is defined
+ * according purpose of MadButterfly.  For example, MadButterfly wants
+ * a function that can clear a canvas, we define a clear function.
+ * Never define a indirectly way to finish the function.  Never define
+ * a way to finish the function for the reason that some engine
+ * require you to finish the task in that procedure.  It avoids
+ * binding graphic engine layer with any behavior of a graphic engine,
+ * and provides more compatible with other engines, to define
+ * interface of graphic engine layer according purpose of
+ * MadButterfly.
+ *
+ * \section ge_mem Graphic Engine Layer Memory Management
+ *
+ * MadButterfly is responsible for management of objects and memory
+ * blocks returned by graphic engine layer, even for graphic engines
+ * that have management model.  MadButterfly supposes memory blocks
+ * only be released when they are no more used.  MadBufferfly is
+ * responsible for release them.  So, even a graphic engine has
+ * reference count with objects, MadButterfly still keep a reference
+ * for every object returned by the engine until no one will use it.
+ *
+ * \section ge_transform Transformation of Coordination System
+ *
+ * Points of pathes are transformed when it is added to the canvas
+ * with the transformation matrix at the time.  So, changes of
+ * transformation matrix of an canvas will not affect points that had
+ * been added.  It only affects points been added when the matrix is
+ * setted.
+ */
 #include <stdio.h>
 #include <SkCanvas.h>
 #include <SkBitmap.h>
 #include <SkShader.h>
+#include <SkDevice.h>
+#include <SkGradientShader.h>
+#include <SkXfermode.h>
+#include <SkColorFilter.h>
 
 #define C_START extern "C" {
 #define C_END }
@@ -10,6 +48,21 @@
 
 #include "mb_graph_engine_skia.h"
 #include "mb_shapes.h"
+#include "mb_img_ldr.h"
+
+/*! \brief Source pattern
+ *
+ * For Skia, source pattern is SkShader with some decoration.  Since
+ * SkShade will repeative tiling or extenting edge color, it can not
+ * stop tiling and extenting for fixed size bitmap.  So, we need to
+ * translate mbe_paint() into a drawing of a rectangle.
+ */
+struct _mbe_pattern_t {
+    SkShader *shader;
+    int w, h;
+    int has_size;
+    co_aix matrix[6];
+};
 
 struct _mbe_scaled_font_t {
     struct _mb_font_face_t *face;
@@ -17,46 +70,409 @@
     co_aix ctm[6];
 };
 struct _mbe_font_face_t {};
+/*! \brief MadButterfly Graphic Engine Context.
+ *
+ * A context comprises source pattern, target surface, path,
+ * line-width, and transform matrix.
+ */
 struct _mbe_t {
     SkCanvas *canvas;
-    SkShader *shader;
-    int shader_owned;
+    SkPath *path, *subpath;
+    SkPaint *paint;
+    SkRegion *saved_region;
+    
+    struct _mbe_states_t *states;
+};
+
+struct _mbe_states_t {
+    mbe_pattern_t *ptn;
+    int ptn_owned;
+    co_aix line_width;
+    co_aix matrix[6];
+    struct _mbe_states_t *next;
 };
 
 #ifndef ASSERT
 #define ASSERT(x)
 #endif
 
-void mbe_pattern_add_color_stop_rgba(mbe_pattern_t *ptn,
-					    co_aix offset,
-					    co_aix r, co_aix g, co_aix b,
-					    co_aix a) {}
-mbe_pattern_t *mbe_pattern_create_for_surface(mbe_surface_t *surface) {}
-mbe_pattern_t *mbe_pattern_create_radial(co_aix cx0, co_aix cy0,
-						co_aix radius0,
-						co_aix cx1, co_aix cy1,
-						co_aix radius1) {}
-mbe_pattern_t *mbe_pattern_create_linear(co_aix x0, co_aix y0,
-						co_aix x1, co_aix y1) {}
-void mbe_pattern_set_matrix(mbe_pattern_t *ptn,
-				   const co_aix matrix[6]) {}
-void mbe_pattern_destroy(mbe_pattern_t *canvas) {}
+#define PI 3.1415926535897931
+
+#define CO_AIX_2_SKSCALAR(a) ((a) * 65536)
+#define SKSCALAR_2_CO_AIX(a) ((co_aix)(a) / 65536)
+#define MB_MATRIX_2_SKMATRIX(sk, mb) {			\
+	(sk).setScaleX(CO_AIX_2_SKSCALAR((mb)[0]));	\
+	(sk).setSkewX(CO_AIX_2_SKSCALAR((mb)[1]));	\
+	(sk).setTranslateX(CO_AIX_2_SKSCALAR((mb)[2]));	\
+	(sk).setSkewY(CO_AIX_2_SKSCALAR((mb)[3]));	\
+	(sk).setScaleY(CO_AIX_2_SKSCALAR((mb)[4]));	\
+	(sk).setTranslateY(CO_AIX_2_SKSCALAR((mb)[5]));	\
+    }
+#define SKMATRIX_2_MB_MATRIX(mb, sk) {				\
+	(mb)[0] = SKSCALAR_2_CO_AIX((sk).getScaleX());		\
+	(mb)[1] = SKSCALAR_2_CO_AIX((sk).getSkewX());		\
+	(mb)[2] = SKSCALAR_2_CO_AIX((sk).getTranslateX());	\
+	(mb)[3] = SKSCALAR_2_CO_AIX((sk).getSkewY());		\
+	(mb)[4] = SKSCALAR_2_CO_AIX((sk).getScaleY());		\
+	(mb)[5] = SKSCALAR_2_CO_AIX((sk).getTranslateY());	\
+    }
+#define MBSTOP_2_SKCOLOR(c)			\
+    ((((int)((c)->a * 255)) << 24) |		\
+     (((int)((c)->r * 255)) << 16) |		\
+     (((int)((c)->g * 255)) << 8) |		\
+     (((int)((c)->b * 255))))
+
+static const co_aix id_matrix[6] = { 1, 0, 0, 0, 1, 0 };
+
+static void
+_prepare_sized_pattern(mbe_t *mbe, mbe_pattern_t *ptn) {
+    SkCanvas *canvas = mbe->canvas;
+    SkPath path;
+    co_aix x, y;
+    co_aix reverse[6];
+    
+    *mbe->saved_region = canvas->getTotalClip();
+    
+    compute_reverse(ptn->matrix, reverse);
+    x = 0; y = 0;
+    matrix_trans_pos(reverse, &x, &y);
+    path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y));
+    x = 0; y = ptn->h;
+    matrix_trans_pos(reverse, &x, &y);
+    path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y));
+    x = ptn->w; y = ptn->h;
+    matrix_trans_pos(reverse, &x, &y);
+    path.moveTo(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y));
+    path.close();
+    
+    canvas->clipPath(path, SkRegion::kIntersect_Op);
+}
+
+static void
+_finish_sized_pattern(mbe_t *mbe) {
+    SkCanvas *canvas = mbe->canvas;
+    
+    canvas->setClipRegion(*mbe->saved_region);
+}
+
+static void
+_canvas_device_region(SkCanvas *canvas, SkRegion *region) {
+    SkDevice *device;
+    int w, h;
+
+    device = canvas->getDevice();
+    w = device->width();
+    h = device->height();
+    region->setRect(0, 0, w, h);
+}
+
+static void
+_update_path(mbe_t *mbe) {
+    SkPath *path = mbe->path;
+    SkPath *subpath = mbe->subpath;
+    SkMatrix canvas_matrix;
+    SkPoint point;
+
+    MB_MATRIX_2_SKMATRIX(canvas_matrix, mbe->states->matrix);
+    path->addPath(*subpath, canvas_matrix);
+    
+    subpath->getLastPt(&point);
+    subpath->rewind();
+    subpath->moveTo(point);
+}
+
+/*
+ * When a function want to use the paint associated with a canvas to
+ * draw, it should call _prepare_paint() can make the paint ready.
+ * And, call _finish_paint() when the paint is no more used.
+ */
+static void
+_prepare_paint(mbe_t *mbe, SkPaint::Style style) {
+    SkPaint *paint = mbe->paint;
+    mbe_pattern_t *ptn = mbe->states->ptn;
+    SkShader *shader = ptn->shader;
+    co_aix matrix[6];
+    SkMatrix skmatrix;
+
+    paint->setStyle(style);
+    
+    /* Local matrix of SkShader is a mapping from source pattern to
+     * user space.  Unlikely, for Cairo is a mapping from user space
+     * to source pattern.
+     */
+    matrix_mul(mbe->states->matrix, ptn->matrix, matrix);
+    MB_MATRIX_2_SKMATRIX(skmatrix, matrix);
+    shader->setLocalMatrix(skmatrix);
+    paint->setShader(shader);
+
+    if(style == SkPaint::kStroke_Style)
+	paint->setStrokeWidth(CO_AIX_2_SKSCALAR(mbe->states->line_width));
+
+    if(ptn->has_size)
+	_prepare_sized_pattern(mbe, ptn);
+}
+
+static void
+_finish_paint(mbe_t *mbe) {
+    mbe_pattern_t *ptn = mbe->states->ptn;
+    
+    mbe->paint->reset();
+    if(ptn->has_size)
+	_finish_sized_pattern(mbe);
+}
+
+mbe_pattern_t *mbe_pattern_create_for_surface(mbe_surface_t *surface) {
+    mbe_pattern_t *ptn;
+    SkBitmap *bitmap = (SkBitmap *)surface;
 
-int mbe_image_surface_get_stride(mbe_surface_t *surface) {}
-int mbe_image_surface_get_height(mbe_surface_t *surface) {}
-int mbe_image_surface_get_width(mbe_surface_t *surface) {}
-unsigned char *mbe_image_surface_get_data(mbe_surface_t *surface) {}
+    ptn = (mbe_pattern_t *)malloc(sizeof(mbe_pattern_t));
+    ptn->shader = SkShader::CreateBitmapShader(*bitmap,
+					       SkShader::kClamp_TileMode,
+					       SkShader::kClamp_TileMode);
+    if(ptn->shader == NULL) {
+	free(ptn);
+	return NULL;
+    }
+    
+    ptn->has_size = 1;
+    ptn->w = bitmap->width();
+    ptn->h = bitmap->height();
+
+    memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
+    
+    return ptn;
+}
+
+mbe_pattern_t *
+mbe_pattern_create_radial(co_aix cx0, co_aix cy0, co_aix radius0,
+			  co_aix cx1, co_aix cy1, co_aix radius1,
+			  grad_stop_t *stops, int stop_cnt) {
+    mbe_pattern_t *ptn;
+    SkColor *colors;
+    SkScalar *poses;
+    grad_stop_t *stop;
+    SkPoint center;
+    int i;
+
+    ptn = (mbe_pattern_t *)malloc(sizeof(mbe_pattern_t));
+    colors = new SkColor[stop_cnt];
+    poses = new SkScalar[stop_cnt];
+    if(ptn == NULL || colors == NULL || poses == NULL)
+	goto fail;
+
+    center.set(CO_AIX_2_SKSCALAR(cx1), CO_AIX_2_SKSCALAR(cy1));
+    
+    stop = stops;
+    for(i = 0; i < stop_cnt; i++) {
+	colors[i] = MBSTOP_2_SKCOLOR(stop);
+	poses[i] = CO_AIX_2_SKSCALAR(stop->offset);
+    }
+
+    /*
+     * cx0, cy0 and radius0 is not used.  Since Skia is still not
+     * support two circles radial.  And, SVG 1.2 is also not support
+     * two circles.
+     */
+    ptn->shader =
+	SkGradientShader::CreateRadial(center, CO_AIX_2_SKSCALAR(radius1),
+				       colors, poses, stop_cnt,
+				       SkShader::kClamp_TileMode);
+    if(ptn->shader == NULL)
+	goto fail;
+
+    memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
+    
+    delete colors;
+    delete poses;
+    return ptn;
+    
+ fail:
+    if(ptn) free(ptn);
+    if(colors) delete colors;
+    if(poses) delete poses;
+    return NULL;
+}
+
+mbe_pattern_t *
+mbe_pattern_create_linear(co_aix x0, co_aix y0,
+			  co_aix x1, co_aix y1,
+			  grad_stop_t *stops, int stop_cnt) {
+    mbe_pattern_t *ptn;
+    SkColor *colors;
+    SkScalar *poses;
+    grad_stop_t *stop;
+    SkPoint points[2];
+    int i;
+
+    ptn = (mbe_pattern_t *)malloc(sizeof(mbe_pattern_t));
+    colors = new SkColor[stop_cnt];
+    poses = new SkScalar[stop_cnt];
+    if(ptn == NULL || colors == NULL || poses == NULL)
+	goto fail;
+
+    points[0].set(CO_AIX_2_SKSCALAR(x0), CO_AIX_2_SKSCALAR(y0));
+    points[1].set(CO_AIX_2_SKSCALAR(x1), CO_AIX_2_SKSCALAR(y1));
+    
+    stop = stops;
+    for(i = 0; i < stop_cnt; i++) {
+	colors[i] = MBSTOP_2_SKCOLOR(stop);
+	poses[i] = CO_AIX_2_SKSCALAR(stop->offset);
+    }
+
+    /*
+     * cx0, cy0 and radius0 is not used.  Since Skia is still not
+     * support two circles radial.  And, SVG 1.2 is also not support
+     * two circles.
+     */
+    ptn->shader =
+	SkGradientShader::CreateLinear(points, colors, poses, stop_cnt,
+				       SkShader::kClamp_TileMode);
+    if(ptn->shader == NULL)
+	goto fail;
+
+    memcpy(ptn->matrix, id_matrix, sizeof(co_aix) * 6);
+    
+    delete colors;
+    delete poses;
+    return ptn;
+    
+ fail:
+    if(ptn) free(ptn);
+    if(colors) delete colors;
+    if(poses) delete poses;
+    return NULL;
+}
+
+void mbe_pattern_set_matrix(mbe_pattern_t *ptn, const co_aix matrix[6]) {
+    SkMatrix skmatrix;
+
+    MB_MATRIX_2_SKMATRIX(skmatrix, matrix);
+
+    ptn->shader->setLocalMatrix(skmatrix);
+}
+
+void mbe_pattern_destroy(mbe_pattern_t *ptn) {
+    if(ptn->shader)
+	delete ptn->shader;
+    free(ptn);
+}
+
+int mbe_image_surface_get_stride(mbe_surface_t *surface) {
+    return ((SkBitmap *)surface)->rowBytes();
+}
+
+int mbe_image_surface_get_height(mbe_surface_t *surface) {
+    return ((SkBitmap *)surface)->height();
+}
+
+int mbe_image_surface_get_width(mbe_surface_t *surface) {
+    return ((SkBitmap *)surface)->width();
+}
+
+unsigned char *mbe_image_surface_get_data(mbe_surface_t *surface) {
+    return (unsigned char *)((SkBitmap *)surface)->getPixels();
+}
+
 mbe_surface_t *mbe_image_surface_create_from_png(const char *filename) {}
+
 mbe_surface_t *
 mbe_image_surface_create_for_data(unsigned char *data,
 				  mb_img_fmt_t fmt,
 				  int width, int height,
-				  int stride) {}
-mb_img_fmt_t mbe_image_surface_get_format(mbe_surface_t *surface) {}
+				  int stride) {
+    SkBitmap *bitmap;
+    SkBitmap::Config cfg;
+
+    switch(fmt) {
+    case MB_IFMT_ARGB32:
+	cfg = SkBitmap::kARGB_8888_Config; break;
+	
+    case MB_IFMT_A8:
+	cfg = SkBitmap::kA8_Config; break;
+	
+    case MB_IFMT_A1:
+	cfg = SkBitmap::kA1_Config; break;
+	
+    case MB_IFMT_RGB16_565:
+	cfg = SkBitmap::kRGB_565_Config; break;
+	
+    case MB_IFMT_RGB24:
+    default:
+	return NULL;
+    }
+    
+    bitmap = new SkBitmap();
+    if(bitmap == NULL)
+	return NULL;
+    
+    bitmap->setConfig(cfg, width, height, stride);
+    bitmap->setPixels(data);
+
+    return (mbe_surface_t *)bitmap;
+}
+
+mb_img_fmt_t mbe_image_surface_get_format(mbe_surface_t *surface) {
+    SkBitmap *bitmap = (SkBitmap *)surface;
+    mb_img_fmt_t fmt;
+    SkBitmap::Config cfg;
+    
+    cfg = bitmap->getConfig();
+    switch(cfg) {
+    case SkBitmap::kARGB_8888_Config:
+	fmt = MB_IFMT_ARGB32; break;
+
+    case SkBitmap::kA8_Config:
+	fmt = MB_IFMT_A8; break;
+
+    case SkBitmap::kA1_Config:
+	fmt = MB_IFMT_A1; break;
+
+    case SkBitmap::kRGB_565_Config:
+	fmt = MB_IFMT_RGB16_565; break;
+
+    default:
+	fmt = MB_IFMT_DUMMY;
+    }
+
+    return fmt;
+}
+
 mbe_surface_t *
-mbe_image_surface_create(mb_img_fmt_t fmt, int width, int height) {}
+mbe_image_surface_create(mb_img_fmt_t fmt, int width, int height) {
+    SkBitmap *bitmap;
+    SkBitmap::Config cfg;
 
-mbe_scaled_font_t *mbe_scaled_font_reference(mbe_scaled_font_t *scaled) {}
+    switch(fmt) {
+    case MB_IFMT_ARGB32:
+	cfg = SkBitmap::kARGB_8888_Config; break;
+	
+    case MB_IFMT_A8:
+	cfg = SkBitmap::kA8_Config; break;
+	
+    case MB_IFMT_A1:
+	cfg = SkBitmap::kA1_Config; break;
+	
+    case MB_IFMT_RGB16_565:
+	cfg = SkBitmap::kRGB_565_Config; break;
+	
+    case MB_IFMT_RGB24:
+    default:
+	return NULL;
+    }
+    
+    bitmap = new SkBitmap();
+    if(bitmap == NULL)
+	return NULL;
+    
+    bitmap->setConfig(cfg, width, height);
+    bitmap->allocPixels();
+
+    return (mbe_surface_t *)bitmap;
+}
+
+mbe_scaled_font_t *mbe_scaled_font_reference(mbe_scaled_font_t *scaled) {
+}
+
 void mbe_scaled_font_destroy(mbe_scaled_font_t *scaled) {}
 mbe_font_face_t *mbe_font_face_reference(mbe_font_face_t *face) {}
 mbe_scaled_font_t *
@@ -68,75 +484,333 @@
 					 mbe_text_extents_t *extents) {}
 
 void mbe_font_face_destroy(mbe_font_face_t *face) {}
-void mbe_paint_with_alpha(mbe_t *canvas, co_aix alpha) {}
+
+void mbe_paint_with_alpha(mbe_t *canvas, co_aix alpha) {
+    SkPaint *paint = canvas->paint;
+    SkColorFilter *filter;
+    SkColor color;
+
+    color = ((uint32_t)(alpha * 255)) << 24;
+    filter =
+	SkColorFilter::CreatePorterDuffFilter(color,
+					      SkPorterDuff::kSrcOver_Mode);
+    mbe_paint(canvas);
+    
+}
+
 void mbe_surface_destroy(mbe_surface_t *surface) {}
 void mbe_set_source_rgba(mbe_t *canvas,
 				co_aix r, co_aix g, co_aix b, co_aix a) {}
 void mbe_set_scaled_font(mbe_t *canvas,
 				const mbe_scaled_font_t *scaled) {}
 void mbe_set_source_rgb(mbe_t *canvas, co_aix r, co_aix g, co_aix b) {}
-void mbe_set_line_width(mbe_t *canvas, co_aix width) {}
+
+void mbe_set_line_width(mbe_t *canvas, co_aix width) {
+    canvas->states->line_width = width;
+}
+
 mbe_font_face_t *mbe_get_font_face(mbe_t *canvas) {}
-void mbe_fill_preserve(mbe_t *canvas) {}
-void mbe_set_source(mbe_t *canvas, mbe_pattern_t *source) {}
-void mbe_reset_clip(mbe_t *canvas) {}
-mbe_surface_t *mbe_get_target(mbe_t *canvas) {}
-void mbe_close_path(mbe_t *canvas) {}
+
+void mbe_fill_preserve(mbe_t *canvas) {
+    mbe_pattern_t *ptn = canvas->states->ptn;
+    SkPaint *paint = canvas->paint;
+    SkPath *path = canvas->path;
+    SkRegion *saved_clip = NULL;
+    co_aix x, y;
+
+    ASSERT(paint);
+    ASSERT(ptn);
+    ASSERT(path);
+
+    if(!canvas->subpath->isEmpty())
+	_update_path(canvas);
+    
+    _prepare_paint(canvas, SkPaint::kFill_Style);
+
+    canvas->canvas->drawPath(*path, *paint);
+
+    _finish_paint(canvas);
+}
+
+void mbe_set_source(mbe_t *canvas, mbe_pattern_t *source) {
+    canvas->states->ptn = source;
+}
+
+void mbe_reset_clip(mbe_t *canvas) {
+    SkRegion clip;
+
+    _canvas_device_region(canvas->canvas, &clip);
+    canvas->canvas->setClipRegion(clip);
+}
+
+mbe_surface_t *mbe_get_target(mbe_t *canvas) {
+    return (mbe_surface_t *)&canvas->canvas->getDevice()->accessBitmap(false);
+}
+
+void mbe_close_path(mbe_t *canvas) {
+    canvas->subpath->close();
+}
+
 void mbe_text_path(mbe_t *canvas, const char *txt) {}
+
 void mbe_rectangle(mbe_t *canvas, co_aix x, co_aix y,
-			  co_aix width, co_aix height) {}
-int mbe_in_stroke(mbe_t *canvas, co_aix x, co_aix y) {}
-void mbe_new_path(mbe_t *canvas) {}
+			  co_aix width, co_aix height) {
+    SkPath *subpath = canvas->subpath;
+    
+    subpath->addRect(CO_AIX_2_SKSCALAR(x), CO_AIX_2_SKSCALAR(y),
+		     CO_AIX_2_SKSCALAR(x + width),
+		     CO_AIX_2_SKSCALAR(y + height));
+}
+
+int mbe_in_stroke(mbe_t *canvas, co_aix x, co_aix y) {
+    return 0;
+}
+
+void mbe_new_path(mbe_t *canvas) {
+    canvas->subpath->reset();
+}
+
 void mbe_curve_to(mbe_t *canvas, co_aix x1, co_aix y1,
 			 co_aix x2, co_aix y2,
-			 co_aix x3, co_aix y3) {}
-void mbe_restore(mbe_t *canvas) {}
-void mbe_move_to(mbe_t *canvas, co_aix x, co_aix y) {}
-void mbe_line_to(mbe_t *canvas, co_aix x, co_aix y) {}
-int mbe_in_fill(mbe_t *canvas, co_aix x, co_aix y) {}
-void mbe_stroke(mbe_t *canvas) {}
+			 co_aix x3, co_aix y3) {
+    SkPath *subpath = canvas->subpath;
+
+    subpath->cubicTo(CO_AIX_2_SKSCALAR(x1), CO_AIX_2_SKSCALAR(y1),
+		     CO_AIX_2_SKSCALAR(x2), CO_AIX_2_SKSCALAR(y2),
+		     CO_AIX_2_SKSCALAR(x3), CO_AIX_2_SKSCALAR(y3));
+}
+
+void mbe_restore(mbe_t *canvas) {
+    struct _mbe_states_t *states;
+
+    _update_path(canvas);
+    
+    states = canvas->states;
+    ASSERT(states->next);
+    canvas->states = states->next;
+    free(states);
+}
+
+void mbe_move_to(mbe_t *canvas, co_aix x, co_aix y) {
+    canvas->subpath->moveTo(CO_AIX_2_SKSCALAR(x),
+			 CO_AIX_2_SKSCALAR(y));
+}
+
+void mbe_line_to(mbe_t *canvas, co_aix x, co_aix y) {
+    canvas->subpath->lineTo(CO_AIX_2_SKSCALAR(x),
+			    CO_AIX_2_SKSCALAR(y));
+}
+
+int mbe_in_fill(mbe_t *canvas, co_aix x, co_aix y) {
+    SkRegion region, dev_region;
+    bool in_fill;
+
+    if(!canvas->subpath->isEmpty())
+	_update_path(canvas);
+    
+    _canvas_device_region(canvas->canvas, &dev_region);
+    region.setPath(*canvas->path, dev_region);
+    
+    in_fill = region.contains(x, y);
+
+    return in_fill;
+}
+
+void mbe_stroke(mbe_t *canvas) {
+    SkPath *path = canvas->path;
+    SkPaint *paint = canvas->paint;
+
+    ASSERT(ptn);
+    ASSERT(path);
+    ASSERT(paint);
+
+    if(!canvas->subpath->isEmpty())
+	_update_path(canvas);
+
+    _prepare_paint(canvas, SkPaint::kStroke_Style);
+
+    canvas->canvas->drawPath(*path, *paint);
+
+    _finish_paint(canvas);
+
+    path->reset();
+}
 
 mbe_t *mbe_create(mbe_surface_t *target) {
     mbe_t *mbe;
+    struct _mbe_states_t *states;
     SkBitmap *bitmap = (SkBitmap *)target;
 
     mbe = (mbe_t *)malloc(sizeof(mbe_t));
     if(mbe == NULL)
 	return NULL;
     
-    mbe->canvas = new SkCanvas(*bitmap);
-    if(mbe->canvas == NULL) {
+    mbe->states = (struct _mbe_states_t *)
+	malloc(sizeof(struct _mbe_states_t));
+    states = mbe->states;
+    if(states == NULL) {
 	free(mbe);
 	return NULL;
     }
     
-    mbe->shader = NULL;
-    mbe->shader_owned = 0;
+    mbe->canvas = new SkCanvas(*bitmap);
+    mbe->path = new SkPath();
+    mbe->subpath = new SkPath();
+    mbe->saved_region = new SkRegion();
+    mbe->paint = new SkPaint();
+    states->ptn = NULL;
+    states->ptn_owned = 0;
+    states->line_width = 0;
+    states->next = NULL;
+
+    if(mbe->canvas == NULL || mbe->path == NULL ||
+       mbe->subpath == NULL || mbe->paint == NULL ||
+       mbe->saved_region == NULL)
+	goto fail;
 
+    memcpy(states->matrix, id_matrix, sizeof(co_aix) * 6);
+    
     return mbe;
+
+ fail:
+    if(mbe->canvas) delete mbe->canvas;
+    if(mbe->path) delete mbe->path;
+    if(mbe->subpath) delete mbe->subpath;
+    if(mbe->paint) delete mbe->paint;
+    if(mbe->saved_region) delete mbe->saved_region;
+    free(states);
+    free(mbe);
+    
+    return NULL;
 }
 
 void mbe_destroy(mbe_t *canvas) {
+    struct _mbe_states_t *states;
+    
     delete canvas->canvas;
-    if(canvas->shader && canvas->shader_owned)
-	delete canvas->shader;
+    delete canvas->path;
+    delete canvas->subpath;
+    delete canvas->paint;
+    delete canvas->saved_region;
+    while(canvas->states) {
+	states = canvas->states;
+	canvas->states = states->next;
+	
+	if(states->ptn && states->ptn_owned)
+	    mbe_pattern_destroy(states->ptn);
+	free(states);
+    }
     free(canvas);
 }
 
-void mbe_paint(mbe_t *canvas) {}
-void mbe_save(mbe_t *canvas) {}
-void mbe_fill(mbe_t *canvas) {}
-void mbe_clip(mbe_t *canvas) {}
+void mbe_paint(mbe_t *canvas) {
+    SkPaint *paint = canvas->paint;
+
+    ASSERT(paint);
+    
+    _prepare_paint(canvas, SkPaint::kFill_Style);
+    
+    canvas->canvas->drawPaint(*paint);
+
+    _finish_paint(canvas);
+}
+
+void mbe_save(mbe_t *canvas) {
+    struct _mbe_states_t *states;
+
+    states = (struct _mbe_states_t *)malloc(sizeof(struct _mbe_states_t));
+    ASSERT(states);
+    
+    memcpy(states, canvas->states, sizeof(struct _mbe_states_t));
+    states->next = canvas->states;
+    canvas->states = states;
+}
+
+void mbe_fill(mbe_t *canvas) {
+    mbe_fill_preserve(canvas);
+    canvas->path->reset();
+}
+
+void mbe_clip(mbe_t *canvas) {
+    if(!canvas->subpath->isEmpty())
+	_update_path(canvas);
+    
+    canvas->canvas->clipPath(*canvas->path, SkRegion::kIntersect_Op);
+    canvas->path->reset();
+}
 
 mbe_font_face_t * mbe_query_font_face(const char *family,
 					     int slant, int weight) {}
 void mbe_free_font_face(mbe_font_face_t *face) {}
 
-void mbe_clear(mbe_t *canvas) {}
-void mbe_copy_source(mbe_t *canvas) {}
-void mbe_transform(mbe_t *mbe, co_aix matrix[6]) {}
+void mbe_clear(mbe_t *canvas) {
+    SkColor color = 0;
+
+    canvas->canvas->drawColor(color, SkPorterDuff::kClear_Mode);
+}
+
+void mbe_copy_source(mbe_t *canvas) {
+    SkPaint *paint = canvas->paint;
+    SkXfermode *mode;
+
+    _prepare_paint(canvas, SkPaint::kFill_Style);
+    mode = SkPorterDuff::CreateXfermode(SkPorterDuff::kSrc_Mode);
+    paint->setXfermode(mode);
+    mode->unref();
+
+    canvas->canvas->drawPaint(*paint);
+
+    _finish_paint(canvas);
+}
+
+void mbe_transform(mbe_t *mbe, co_aix matrix[6]) {
+    _update_path(mbe);
+    
+    matrix_mul(matrix, mbe->states->matrix, mbe->states->matrix);
+}
+
 void mbe_arc(mbe_t *mbe, co_aix x, co_aix y, co_aix radius,
-		    co_aix angle_start, co_aix angle_stop) {}
+		    co_aix angle_start, co_aix angle_stop) {
+    SkPoint point;
+    SkPath *subpath = mbe->subpath;
+    SkRect rect;
+    SkScalar x0, y0;
+    SkScalar ang_start, ang_stop;
+    SkScalar sweep;
+    SkScalar r;			/* radius */
+
+    subpath->getLastPt(&point);
+    x0 = point.fX;
+    y0 = point.fX;
+    r = CO_AIX_2_SKSCALAR(radius);
+    ang_start = CO_AIX_2_SKSCALAR(angle_start * 180 / PI);
+    ang_stop = CO_AIX_2_SKSCALAR(angle_stop * 180 / PI);
+    
+    /* Skia can only draw an arc in clockwise directly.  We negative
+     * start and stop point to draw the arc in the mirror along x-axis
+     * in a sub-path.  Then, the sub-path are reflected along x-axis,
+     * again.  We get a right path, and add it to the path of mbe_t.
+     */
+    if(ang_start > ang_stop) {
+	SkPath tmppath;
+	SkMatrix matrix;
+	co_aix reflect[6] = { 1, 0, 0,
+			      0, -1, 0};
+	
+	rect.set(-r, -r, r, r);
+	sweep = ang_start - ang_stop;
+	tmppath.arcTo(rect, -ang_start, sweep, false);
+
+	reflect[2] = x;
+	reflect[5] = y;
+	MB_MATRIX_2_SKMATRIX(matrix, reflect);
+	subpath->addPath(tmppath, matrix);
+    } else {
+	rect.set(x0 - r, y0 - r, x0 + r, y0 + r);
+	sweep = ang_stop - ang_start;
+	subpath->arcTo(rect, ang_start, sweep, false);
+    }
+}
 
 
 C_END