#include "plutovg-private.h"
#include "plutovg-utils.h"

int plutovg_version(void)
{
    return PLUTOVG_VERSION;
}

const char* plutovg_version_string(void)
{
    return PLUTOVG_VERSION_STRING;
}

#define PLUTOVG_DEFAULT_STROKE_STYLE ((plutovg_stroke_style_t){1.f, PLUTOVG_LINE_CAP_BUTT, PLUTOVG_LINE_JOIN_MITER, 10.f})

static plutovg_state_t* plutovg_state_create(void)
{
    plutovg_state_t* state = malloc(sizeof(plutovg_state_t));
    state->paint = NULL;
    state->font_face = NULL;
    state->color = PLUTOVG_BLACK_COLOR;
    state->matrix = PLUTOVG_IDENTITY_MATRIX;
    state->stroke.style = PLUTOVG_DEFAULT_STROKE_STYLE;
    state->stroke.dash.offset = 0.f;
    plutovg_array_init(state->stroke.dash.array);
    plutovg_span_buffer_init(&state->clip_spans);
    state->winding = PLUTOVG_FILL_RULE_NON_ZERO;
    state->op = PLUTOVG_OPERATOR_SRC_OVER;
    state->font_size = 12.f;
    state->opacity = 1.f;
    state->clipping = false;
    state->next = NULL;
    return state;
}

static void plutovg_state_reset(plutovg_state_t* state)
{
    plutovg_paint_destroy(state->paint);
    plutovg_font_face_destroy(state->font_face);
    state->paint = NULL;
    state->font_face = NULL;
    state->color = PLUTOVG_BLACK_COLOR;
    state->matrix = PLUTOVG_IDENTITY_MATRIX;
    state->stroke.style = PLUTOVG_DEFAULT_STROKE_STYLE;
    state->stroke.dash.offset = 0.f;
    plutovg_array_clear(state->stroke.dash.array);
    plutovg_span_buffer_reset(&state->clip_spans);
    state->winding = PLUTOVG_FILL_RULE_NON_ZERO;
    state->op = PLUTOVG_OPERATOR_SRC_OVER;
    state->font_size = 12.f;
    state->opacity = 1.f;
    state->clipping = false;
}

static void plutovg_state_copy(plutovg_state_t* state, const plutovg_state_t* source)
{
    state->paint = plutovg_paint_reference(source->paint);
    state->font_face = plutovg_font_face_reference(source->font_face);
    state->color = source->color;
    state->matrix = source->matrix;
    state->stroke.style = source->stroke.style;
    state->stroke.dash.offset = source->stroke.dash.offset;
    plutovg_array_clear(state->stroke.dash.array);
    plutovg_array_append(state->stroke.dash.array, source->stroke.dash.array);
    plutovg_span_buffer_copy(&state->clip_spans, &source->clip_spans);
    state->winding = source->winding;
    state->op = source->op;
    state->font_size = source->font_size;
    state->opacity = source->opacity;
    state->clipping = source->clipping;
}

static void plutovg_state_destroy(plutovg_state_t* state)
{
    plutovg_paint_destroy(state->paint);
    plutovg_font_face_destroy(state->font_face);
    plutovg_array_destroy(state->stroke.dash.array);
    plutovg_span_buffer_destroy(&state->clip_spans);
    free(state);
}

plutovg_canvas_t* plutovg_canvas_create(plutovg_surface_t* surface)
{
    plutovg_canvas_t* canvas = malloc(sizeof(plutovg_canvas_t));
    plutovg_init_reference(canvas);
    canvas->surface = plutovg_surface_reference(surface);
    canvas->path = plutovg_path_create();
    canvas->state = plutovg_state_create();
    canvas->freed_state = NULL;
    canvas->face_cache = NULL;
    canvas->clip_rect = PLUTOVG_MAKE_RECT((float) 0, (float) 0, (float) surface->width, (float) surface->height);
    plutovg_span_buffer_init(&canvas->clip_spans);
    plutovg_span_buffer_init(&canvas->fill_spans);
    return canvas;
}

plutovg_canvas_t* plutovg_canvas_reference(plutovg_canvas_t* canvas)
{
    plutovg_increment_reference(canvas);
    return canvas;
}

void plutovg_canvas_destroy(plutovg_canvas_t* canvas)
{
    if(plutovg_destroy_reference(canvas)) {
        while(canvas->state) {
            plutovg_state_t* state = canvas->state;
            canvas->state = state->next;
            plutovg_state_destroy(state);
        }

        while(canvas->freed_state) {
            plutovg_state_t* state = canvas->freed_state;
            canvas->freed_state = state->next;
            plutovg_state_destroy(state);
        }

        plutovg_font_face_cache_destroy(canvas->face_cache);
        plutovg_span_buffer_destroy(&canvas->fill_spans);
        plutovg_span_buffer_destroy(&canvas->clip_spans);
        plutovg_surface_destroy(canvas->surface);
        plutovg_path_destroy(canvas->path);
        free(canvas);
    }
}

int plutovg_canvas_get_reference_count(const plutovg_canvas_t* canvas)
{
    return plutovg_get_reference_count(canvas);
}

plutovg_surface_t* plutovg_canvas_get_surface(const plutovg_canvas_t* canvas)
{
    return canvas->surface;
}

void plutovg_canvas_save(plutovg_canvas_t* canvas)
{
    plutovg_state_t* new_state = canvas->freed_state;
    if(new_state == NULL)
        new_state = plutovg_state_create();
    else
        canvas->freed_state = new_state->next;
    plutovg_state_copy(new_state, canvas->state);
    new_state->next = canvas->state;
    canvas->state = new_state;
}

void plutovg_canvas_restore(plutovg_canvas_t* canvas)
{
    if(canvas->state->next == NULL)
        return;
    plutovg_state_t* old_state = canvas->state;
    canvas->state = old_state->next;
    plutovg_state_reset(old_state);
    old_state->next = canvas->freed_state;
    canvas->freed_state = old_state;
}

void plutovg_canvas_set_rgb(plutovg_canvas_t* canvas, float r, float g, float b)
{
    plutovg_canvas_set_rgba(canvas, r, g, b, 1.f);
}

void plutovg_canvas_set_rgba(plutovg_canvas_t* canvas, float r, float g, float b, float a)
{
    plutovg_color_init_rgba(&canvas->state->color, r, g, b, a);
    plutovg_canvas_set_paint(canvas, NULL);
}

void plutovg_canvas_set_color(plutovg_canvas_t* canvas, const plutovg_color_t* color)
{
    plutovg_canvas_set_rgba(canvas, color->r, color->g, color->b, color->a);
}

void plutovg_canvas_set_linear_gradient(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
{
    plutovg_paint_t* paint = plutovg_paint_create_linear_gradient(x1, y1, x2, y2, spread, stops, nstops, matrix);
    plutovg_canvas_set_paint(canvas, paint);
    plutovg_paint_destroy(paint);
}

void plutovg_canvas_set_radial_gradient(plutovg_canvas_t* canvas, float cx, float cy, float cr, float fx, float fy, float fr, plutovg_spread_method_t spread, const plutovg_gradient_stop_t* stops, int nstops, const plutovg_matrix_t* matrix)
{
    plutovg_paint_t* paint = plutovg_paint_create_radial_gradient(cx, cy, cr, fx, fy, fr, spread, stops, nstops, matrix);
    plutovg_canvas_set_paint(canvas, paint);
    plutovg_paint_destroy(paint);
}

void plutovg_canvas_set_texture(plutovg_canvas_t* canvas, plutovg_surface_t* surface, plutovg_texture_type_t type, float opacity, const plutovg_matrix_t* matrix)
{
    plutovg_paint_t* paint = plutovg_paint_create_texture(surface, type, opacity, matrix);
    plutovg_canvas_set_paint(canvas, paint);
    plutovg_paint_destroy(paint);
}

void plutovg_canvas_set_paint(plutovg_canvas_t* canvas, plutovg_paint_t* paint)
{
    paint = plutovg_paint_reference(paint);
    plutovg_paint_destroy(canvas->state->paint);
    canvas->state->paint = paint;
}

plutovg_paint_t* plutovg_canvas_get_paint(const plutovg_canvas_t* canvas, plutovg_color_t* color)
{
    if(color)
        *color = canvas->state->color;
    return canvas->state->paint;
}

void plutovg_canvas_set_font_face_cache(plutovg_canvas_t* canvas, plutovg_font_face_cache_t* cache)
{
    cache = plutovg_font_face_cache_reference(cache);
    plutovg_font_face_cache_destroy(canvas->face_cache);
    canvas->face_cache = cache;
}

plutovg_font_face_cache_t* plutovg_canvas_get_font_face_cache(const plutovg_canvas_t* canvas)
{
    return canvas->face_cache;
}

void plutovg_canvas_add_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, plutovg_font_face_t* face)
{
    if(canvas->face_cache == NULL)
        canvas->face_cache = plutovg_font_face_cache_create();
    plutovg_font_face_cache_add(canvas->face_cache, family, bold, italic, face);
}

bool plutovg_canvas_add_font_file(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic, const char* filename, int ttcindex)
{
    if(canvas->face_cache == NULL)
        canvas->face_cache = plutovg_font_face_cache_create();
    return plutovg_font_face_cache_add_file(canvas->face_cache, family, bold, italic, filename, ttcindex);
}

bool plutovg_canvas_select_font_face(plutovg_canvas_t* canvas, const char* family, bool bold, bool italic)
{
    if(canvas->face_cache == NULL)
        return false;
    plutovg_font_face_t* face = plutovg_font_face_cache_get(canvas->face_cache, family, bold, italic);
    if(face == NULL)
        return false;
    plutovg_canvas_set_font_face(canvas, face);
    return true;
}

void plutovg_canvas_set_font(plutovg_canvas_t* canvas, plutovg_font_face_t* face, float size)
{
    plutovg_canvas_set_font_face(canvas, face);
    plutovg_canvas_set_font_size(canvas, size);
}

void plutovg_canvas_set_font_face(plutovg_canvas_t* canvas, plutovg_font_face_t* face)
{
    face = plutovg_font_face_reference(face);
    plutovg_font_face_destroy(canvas->state->font_face);
    canvas->state->font_face = face;
}

plutovg_font_face_t* plutovg_canvas_get_font_face(const plutovg_canvas_t* canvas)
{
    return canvas->state->font_face;
}

void plutovg_canvas_set_font_size(plutovg_canvas_t* canvas, float size)
{
    canvas->state->font_size = size;
}

float plutovg_canvas_get_font_size(const plutovg_canvas_t* canvas)
{
    return canvas->state->font_size;
}

void plutovg_canvas_set_fill_rule(plutovg_canvas_t* canvas, plutovg_fill_rule_t winding)
{
    canvas->state->winding = winding;
}

plutovg_fill_rule_t plutovg_canvas_get_fill_rule(const plutovg_canvas_t* canvas)
{
    return canvas->state->winding;
}

void plutovg_canvas_set_operator(plutovg_canvas_t* canvas, plutovg_operator_t op)
{
    canvas->state->op = op;
}

plutovg_operator_t plutovg_canvas_get_operator(const plutovg_canvas_t* canvas)
{
    return canvas->state->op;
}

void plutovg_canvas_set_opacity(plutovg_canvas_t* canvas, float opacity)
{
    canvas->state->opacity = plutovg_clamp(opacity, 0.f, 1.f);
}

float plutovg_canvas_get_opacity(const plutovg_canvas_t* canvas)
{
    return canvas->state->opacity;
}

void plutovg_canvas_set_line_width(plutovg_canvas_t* canvas, float line_width)
{
    canvas->state->stroke.style.width = line_width;
}

float plutovg_canvas_get_line_width(const plutovg_canvas_t* canvas)
{
    return canvas->state->stroke.style.width;
}

void plutovg_canvas_set_line_cap(plutovg_canvas_t* canvas, plutovg_line_cap_t line_cap)
{
    canvas->state->stroke.style.cap = line_cap;
}

plutovg_line_cap_t plutovg_canvas_get_line_cap(const plutovg_canvas_t* canvas)
{
    return canvas->state->stroke.style.cap;
}

void plutovg_canvas_set_line_join(plutovg_canvas_t* canvas, plutovg_line_join_t line_join)
{
    canvas->state->stroke.style.join = line_join;
}

plutovg_line_join_t plutovg_canvas_get_line_join(const plutovg_canvas_t* canvas)
{
    return canvas->state->stroke.style.join;
}

void plutovg_canvas_set_miter_limit(plutovg_canvas_t* canvas, float miter_limit)
{
    canvas->state->stroke.style.miter_limit = miter_limit;
}

float plutovg_canvas_get_miter_limit(const plutovg_canvas_t* canvas)
{
    return canvas->state->stroke.style.miter_limit;
}

void plutovg_canvas_set_dash(plutovg_canvas_t* canvas, float offset, const float* dashes, int ndashes)
{
    plutovg_canvas_set_dash_offset(canvas, offset);
    plutovg_canvas_set_dash_array(canvas, dashes, ndashes);
}

void plutovg_canvas_set_dash_offset(plutovg_canvas_t* canvas, float offset)
{
    canvas->state->stroke.dash.offset = offset;
}

float plutovg_canvas_get_dash_offset(const plutovg_canvas_t* canvas)
{
    return canvas->state->stroke.dash.offset;
}

void plutovg_canvas_set_dash_array(plutovg_canvas_t* canvas, const float* dashes, int ndashes)
{
    plutovg_array_clear(canvas->state->stroke.dash.array);
    plutovg_array_append_data(canvas->state->stroke.dash.array, dashes, ndashes);
}

int plutovg_canvas_get_dash_array(const plutovg_canvas_t* canvas, const float** dashes)
{
    if(dashes)
        *dashes = canvas->state->stroke.dash.array.data;
    return canvas->state->stroke.dash.array.size;
}

void plutovg_canvas_translate(plutovg_canvas_t* canvas, float tx, float ty)
{
    plutovg_matrix_translate(&canvas->state->matrix, tx, ty);
}

void plutovg_canvas_scale(plutovg_canvas_t* canvas, float sx, float sy)
{
    plutovg_matrix_scale(&canvas->state->matrix, sx, sy);
}

void plutovg_canvas_shear(plutovg_canvas_t* canvas, float shx, float shy)
{
    plutovg_matrix_shear(&canvas->state->matrix, shx, shy);
}

void plutovg_canvas_rotate(plutovg_canvas_t* canvas, float angle)
{
    plutovg_matrix_rotate(&canvas->state->matrix, angle);
}

void plutovg_canvas_transform(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix)
{
    plutovg_matrix_multiply(&canvas->state->matrix, matrix, &canvas->state->matrix);
}

void plutovg_canvas_reset_matrix(plutovg_canvas_t* canvas)
{
    plutovg_matrix_init_identity(&canvas->state->matrix);
}

void plutovg_canvas_set_matrix(plutovg_canvas_t* canvas, const plutovg_matrix_t* matrix)
{
    canvas->state->matrix = matrix ? *matrix : PLUTOVG_IDENTITY_MATRIX;
}

void plutovg_canvas_get_matrix(const plutovg_canvas_t* canvas, plutovg_matrix_t* matrix)
{
    *matrix = canvas->state->matrix;
}

void plutovg_canvas_map(const plutovg_canvas_t* canvas, float x, float y, float* xx, float* yy)
{
    plutovg_matrix_map(&canvas->state->matrix, x, y, xx, yy);
}

void plutovg_canvas_map_point(const plutovg_canvas_t* canvas, const plutovg_point_t* src, plutovg_point_t* dst)
{
    plutovg_matrix_map_point(&canvas->state->matrix, src, dst);
}

void plutovg_canvas_map_rect(const plutovg_canvas_t* canvas, const plutovg_rect_t* src, plutovg_rect_t* dst)
{
    plutovg_matrix_map_rect(&canvas->state->matrix, src, dst);
}

void plutovg_canvas_move_to(plutovg_canvas_t* canvas, float x, float y)
{
    plutovg_path_move_to(canvas->path, x, y);
}

void plutovg_canvas_line_to(plutovg_canvas_t* canvas, float x, float y)
{
    plutovg_path_line_to(canvas->path, x, y);
}

void plutovg_canvas_quad_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2)
{
    plutovg_path_quad_to(canvas->path, x1, y1, x2, y2);
}

void plutovg_canvas_cubic_to(plutovg_canvas_t* canvas, float x1, float y1, float x2, float y2, float x3, float y3)
{
    plutovg_path_cubic_to(canvas->path, x1, y1, x2, y2, x3, y3);
}

void plutovg_canvas_arc_to(plutovg_canvas_t* canvas, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
{
    plutovg_path_arc_to(canvas->path, rx, ry, angle, large_arc_flag, sweep_flag, x, y);
}

void plutovg_canvas_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
{
    plutovg_path_add_rect(canvas->path, x, y, w, h);
}

void plutovg_canvas_round_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h, float rx, float ry)
{
    plutovg_path_add_round_rect(canvas->path, x, y, w, h, rx, ry);
}

void plutovg_canvas_ellipse(plutovg_canvas_t* canvas, float cx, float cy, float rx, float ry)
{
    plutovg_path_add_ellipse(canvas->path, cx, cy, rx, ry);
}

void plutovg_canvas_circle(plutovg_canvas_t* canvas, float cx, float cy, float r)
{
    plutovg_path_add_circle(canvas->path, cx, cy, r);
}

void plutovg_canvas_arc(plutovg_canvas_t* canvas, float cx, float cy, float r, float a0, float a1, bool ccw)
{
    plutovg_path_add_arc(canvas->path, cx, cy, r, a0, a1, ccw);
}

void plutovg_canvas_add_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
{
    plutovg_path_add_path(canvas->path, path, NULL);
}

void plutovg_canvas_new_path(plutovg_canvas_t* canvas)
{
    plutovg_path_reset(canvas->path);
}

void plutovg_canvas_close_path(plutovg_canvas_t* canvas)
{
    plutovg_path_close(canvas->path);
}

void plutovg_canvas_get_current_point(const plutovg_canvas_t* canvas, float* x, float* y)
{
    plutovg_path_get_current_point(canvas->path, x, y);
}

plutovg_path_t* plutovg_canvas_get_path(const plutovg_canvas_t* canvas)
{
    return canvas->path;
}

bool plutovg_canvas_fill_contains(plutovg_canvas_t* canvas, float x, float y)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
    return plutovg_span_buffer_contains(&canvas->fill_spans, x, y);
}

bool plutovg_canvas_stroke_contains(plutovg_canvas_t* canvas, float x, float y)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
    return plutovg_span_buffer_contains(&canvas->fill_spans, x, y);
}

bool plutovg_canvas_clip_contains(plutovg_canvas_t* canvas, float x, float y)
{
    if(canvas->state->clipping) {
        return plutovg_span_buffer_contains(&canvas->state->clip_spans, x, y);
    }

    float l = canvas->clip_rect.x;
    float t = canvas->clip_rect.y;
    float r = canvas->clip_rect.x + canvas->clip_rect.w;
    float b = canvas->clip_rect.y + canvas->clip_rect.h;

    return x >= l && x <= r && y >= t && y <= b;
}

void plutovg_canvas_fill_extents(plutovg_canvas_t *canvas, plutovg_rect_t* extents)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, NULL, canvas->state->winding);
    plutovg_span_buffer_extents(&canvas->fill_spans, extents);
}

void plutovg_canvas_stroke_extents(plutovg_canvas_t *canvas, plutovg_rect_t* extents)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, NULL, &canvas->state->stroke, PLUTOVG_FILL_RULE_NON_ZERO);
    plutovg_span_buffer_extents(&canvas->fill_spans, extents);
}

void plutovg_canvas_clip_extents(plutovg_canvas_t* canvas, plutovg_rect_t* extents)
{
    if(canvas->state->clipping) {
        plutovg_span_buffer_extents(&canvas->state->clip_spans, extents);
    } else {
        extents->x = canvas->clip_rect.x;
        extents->y = canvas->clip_rect.y;
        extents->w = canvas->clip_rect.w;
        extents->h = canvas->clip_rect.h;
    }
}

void plutovg_canvas_fill(plutovg_canvas_t* canvas)
{
    plutovg_canvas_fill_preserve(canvas);
    plutovg_canvas_new_path(canvas);
}

void plutovg_canvas_stroke(plutovg_canvas_t* canvas)
{
    plutovg_canvas_stroke_preserve(canvas);
    plutovg_canvas_new_path(canvas);
}

void plutovg_canvas_clip(plutovg_canvas_t* canvas)
{
    plutovg_canvas_clip_preserve(canvas);
    plutovg_canvas_new_path(canvas);
}

void plutovg_canvas_paint(plutovg_canvas_t* canvas)
{
    if(canvas->state->clipping) {
        plutovg_blend(canvas, &canvas->state->clip_spans);
    } else {
        plutovg_span_buffer_init_rect(&canvas->clip_spans, 0, 0, canvas->surface->width, canvas->surface->height);
        plutovg_blend(canvas, &canvas->clip_spans);
    }
}

void plutovg_canvas_fill_preserve(plutovg_canvas_t* canvas)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
    if(canvas->state->clipping) {
        plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
        plutovg_blend(canvas, &canvas->clip_spans);
    } else {
        plutovg_blend(canvas, &canvas->fill_spans);
    }
}

void plutovg_canvas_stroke_preserve(plutovg_canvas_t* canvas)
{
    plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, &canvas->state->stroke, PLUTOVG_FILL_RULE_NON_ZERO);
    if(canvas->state->clipping) {
        plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
        plutovg_blend(canvas, &canvas->clip_spans);
    } else {
        plutovg_blend(canvas, &canvas->fill_spans);
    }
}

void plutovg_canvas_clip_preserve(plutovg_canvas_t* canvas)
{
    if(canvas->state->clipping) {
        plutovg_rasterize(&canvas->fill_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
        plutovg_span_buffer_intersect(&canvas->clip_spans, &canvas->fill_spans, &canvas->state->clip_spans);
        plutovg_span_buffer_copy(&canvas->state->clip_spans, &canvas->clip_spans);
    } else {
        plutovg_rasterize(&canvas->state->clip_spans, canvas->path, &canvas->state->matrix, &canvas->clip_rect, NULL, canvas->state->winding);
        canvas->state->clipping = true;
    }
}

void plutovg_canvas_fill_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_rect(canvas, x, y, w, h);
    plutovg_canvas_fill(canvas);
}

void plutovg_canvas_fill_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_add_path(canvas, path);
    plutovg_canvas_fill(canvas);
}

void plutovg_canvas_stroke_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_rect(canvas, x, y, w, h);
    plutovg_canvas_stroke(canvas);
}

void plutovg_canvas_stroke_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_add_path(canvas, path);
    plutovg_canvas_stroke(canvas);
}

void plutovg_canvas_clip_rect(plutovg_canvas_t* canvas, float x, float y, float w, float h)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_rect(canvas, x, y, w, h);
    plutovg_canvas_clip(canvas);
}

void plutovg_canvas_clip_path(plutovg_canvas_t* canvas, const plutovg_path_t* path)
{
    plutovg_canvas_new_path(canvas);
    plutovg_canvas_add_path(canvas, path);
    plutovg_canvas_clip(canvas);
}

float plutovg_canvas_add_glyph(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float x, float y)
{
    plutovg_state_t* state = canvas->state;
    if(state->font_face && state->font_size > 0.f)
        return plutovg_font_face_get_glyph_path(state->font_face, state->font_size, x, y, codepoint, canvas->path);
    return 0.f;
}

float plutovg_canvas_add_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
{
    plutovg_state_t* state = canvas->state;
    if(state->font_face == NULL || state->font_size <= 0.f)
        return 0.f;
    plutovg_text_iterator_t it;
    plutovg_text_iterator_init(&it, text, length, encoding);
    float advance_width = 0.f;
    while(plutovg_text_iterator_has_next(&it)) {
        plutovg_codepoint_t codepoint = plutovg_text_iterator_next(&it);
        advance_width += plutovg_font_face_get_glyph_path(state->font_face, state->font_size, x + advance_width, y, codepoint, canvas->path);
    }

    return advance_width;
}

float plutovg_canvas_fill_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
{
    plutovg_canvas_new_path(canvas);
    float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
    plutovg_canvas_fill(canvas);
    return advance_width;
}

float plutovg_canvas_stroke_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
{
    plutovg_canvas_new_path(canvas);
    float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
    plutovg_canvas_stroke(canvas);
    return advance_width;
}

float plutovg_canvas_clip_text(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, float x, float y)
{
    plutovg_canvas_new_path(canvas);
    float advance_width = plutovg_canvas_add_text(canvas, text, length, encoding, x, y);
    plutovg_canvas_clip(canvas);
    return advance_width;
}

void plutovg_canvas_font_metrics(const plutovg_canvas_t* canvas, float* ascent, float* descent, float* line_gap, plutovg_rect_t* extents)
{
    plutovg_state_t* state = canvas->state;
    if(state->font_face && state->font_size > 0.f) {
        plutovg_font_face_get_metrics(state->font_face, state->font_size, ascent, descent, line_gap, extents);
        return;
    }

    if(ascent) *ascent = 0.f;
    if(descent) *descent = 0.f;
    if(line_gap) *line_gap = 0.f;
    if(extents) {
        extents->x = 0.f;
        extents->y = 0.f;
        extents->w = 0.f;
        extents->h = 0.f;
    }
}

void plutovg_canvas_glyph_metrics(plutovg_canvas_t* canvas, plutovg_codepoint_t codepoint, float* advance_width, float* left_side_bearing, plutovg_rect_t* extents)
{
    plutovg_state_t* state = canvas->state;
    if(state->font_face && state->font_size > 0.f) {
        plutovg_font_face_get_glyph_metrics(state->font_face, state->font_size, codepoint, advance_width, left_side_bearing, extents);
        return;
    }

    if(advance_width) *advance_width = 0.f;
    if(left_side_bearing) *left_side_bearing = 0.f;
    if(extents) {
        extents->x = 0.f;
        extents->y = 0.f;
        extents->w = 0.f;
        extents->h = 0.f;
    }
}

float plutovg_canvas_text_extents(plutovg_canvas_t* canvas, const void* text, int length, plutovg_text_encoding_t encoding, plutovg_rect_t* extents)
{
    plutovg_state_t* state = canvas->state;
    if(state->font_face && state->font_size > 0.f) {
        return plutovg_font_face_text_extents(state->font_face, state->font_size, text, length, encoding, extents);
    }

    if(extents) {
        extents->x = 0.f;
        extents->y = 0.f;
        extents->w = 0.f;
        extents->h = 0.f;
    }

    return 0.f;
}
