Chandra

 view release on metacpan or  search on metacpan

xs/canvas.xs  view on Meta::CPAN

    RETVAL = sv_bless(newRV_noinc((SV *)self_hv),
                      gv_stashpv(class, GV_ADD));
}
OUTPUT:
    RETVAL

# ========================================================================
# Accessors
# ========================================================================

int
width(self, ...)
    SV *self
CODE:
{
    HV *self_hv = (HV *)SvRV(self);
    if (items > 1) {
        (void)hv_stores(self_hv, "width", newSViv(SvIV(ST(1))));
    }
    SV **svp = hv_fetchs(self_hv, "width", 0);
    RETVAL = (svp && SvIOK(*svp)) ? SvIV(*svp) : 800;
}
OUTPUT:
    RETVAL

int
height(self, ...)
    SV *self
CODE:
{
    HV *self_hv = (HV *)SvRV(self);
    if (items > 1) {
        (void)hv_stores(self_hv, "height", newSViv(SvIV(ST(1))));
    }
    SV **svp = hv_fetchs(self_hv, "height", 0);
    RETVAL = (svp && SvIOK(*svp)) ? SvIV(*svp) : 600;
}
OUTPUT:
    RETVAL

SV *
id(self)
    SV *self
CODE:
{
    HV *self_hv = (HV *)SvRV(self);
    SV **svp = hv_fetchs(self_hv, "id", 0);
    RETVAL = (svp && SvPOK(*svp)) ? newSVsv(*svp) : newSVpvs("");
}
OUTPUT:
    RETVAL

# ========================================================================
# Style Methods
# ========================================================================

void
fill_style(self, color)
    SV *self
    const char *color
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1s(aTHX_ buf, CANVAS_OP_FILL_STYLE, color);
    XPUSHs(self);
}

void
stroke_style(self, color)
    SV *self
    const char *color
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1s(aTHX_ buf, CANVAS_OP_STROKE_STYLE, color);
    XPUSHs(self);
}

void
line_width(self, width)
    SV *self
    NV width
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1nv(aTHX_ buf, CANVAS_OP_LINE_WIDTH, width);
    XPUSHs(self);
}

void
global_alpha(self, alpha)
    SV *self
    NV alpha
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1nv(aTHX_ buf, CANVAS_OP_GLOBAL_ALPHA, alpha);
    XPUSHs(self);
}

void
line_cap(self, cap)
    SV *self
    const char *cap
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1s(aTHX_ buf, CANVAS_OP_LINE_CAP, cap);
    XPUSHs(self);
}

void
line_join(self, join)
    SV *self
    const char *join
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1s(aTHX_ buf, CANVAS_OP_LINE_JOIN, join);
    XPUSHs(self);
}

# ========================================================================
# Drawing Methods
# ========================================================================

void
clear(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_CLEAR);
    XPUSHs(self);
}

void
fill_rect(self, x, y, w, h)
    SV *self
    NV x
    NV y
    NV w
    NV h
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_FILL_RECT, x, y, w, h);
    XPUSHs(self);
}

void
stroke_rect(self, x, y, w, h)
    SV *self
    NV x
    NV y
    NV w
    NV h
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_STROKE_RECT, x, y, w, h);
    XPUSHs(self);
}

void
clear_rect(self, x, y, w, h)
    SV *self
    NV x
    NV y
    NV w
    NV h
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_CLEAR_RECT, x, y, w, h);
    XPUSHs(self);
}

void
fill_circle(self, x, y, r)
    SV *self
    NV x
    NV y
    NV r
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_3nv(aTHX_ buf, CANVAS_OP_FILL_CIRCLE, x, y, r);
    XPUSHs(self);
}

void
stroke_circle(self, x, y, r)
    SV *self
    NV x
    NV y
    NV r
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_3nv(aTHX_ buf, CANVAS_OP_STROKE_CIRCLE, x, y, r);
    XPUSHs(self);
}

# ========================================================================
# Path Methods
# ========================================================================

void
begin_path(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_BEGIN_PATH);
    XPUSHs(self);
}

void
close_path(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_CLOSE_PATH);
    XPUSHs(self);
}

void
move_to(self, x, y)
    SV *self
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_MOVE_TO, x, y);
    XPUSHs(self);
}

void
line_to(self, x, y)
    SV *self
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_LINE_TO, x, y);
    XPUSHs(self);
}

void
arc(self, x, y, r, start_angle, end_angle, ...)
    SV *self
    NV x
    NV y
    NV r
    NV start_angle
    NV end_angle
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    NV ccw = (items > 6 && SvTRUE(ST(6))) ? 1 : 0;
    _canvas_push_op_6nv(aTHX_ buf, CANVAS_OP_ARC, x, y, r, start_angle, end_angle, ccw);
    XPUSHs(self);
}

void
rect(self, x, y, w, h)
    SV *self
    NV x
    NV y
    NV w
    NV h
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_RECT, x, y, w, h);
    XPUSHs(self);
}

void
fill(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_FILL);
    XPUSHs(self);
}

void
stroke(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_STROKE);
    XPUSHs(self);
}

# ========================================================================
# State Methods
# ========================================================================

void
save(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_SAVE);
    XPUSHs(self);
}

void
restore(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_RESTORE);
    XPUSHs(self);
}

# ========================================================================
# Transform Methods
# ========================================================================

void
translate(self, x, y)
    SV *self
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_TRANSLATE, x, y);
    XPUSHs(self);
}

void
rotate(self, angle)
    SV *self
    NV angle
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1nv(aTHX_ buf, CANVAS_OP_ROTATE, angle);
    XPUSHs(self);
}

void
scale(self, x, y)
    SV *self
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_SCALE, x, y);
    XPUSHs(self);
}

# ========================================================================
# Phase 2: Advanced Path Methods
# ========================================================================

void
arc_to(self, x1, y1, x2, y2, radius)
    SV *self
    NV x1
    NV y1
    NV x2
    NV y2
    NV radius
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_5nv(aTHX_ buf, CANVAS_OP_ARC_TO, x1, y1, x2, y2, radius);
    XPUSHs(self);
}

void
bezier_curve_to(self, cp1x, cp1y, cp2x, cp2y, x, y)
    SV *self
    NV cp1x
    NV cp1y
    NV cp2x
    NV cp2y
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_6nv(aTHX_ buf, CANVAS_OP_BEZIER_CURVE_TO, cp1x, cp1y, cp2x, cp2y, x, y);
    XPUSHs(self);
}

void
quadratic_curve_to(self, cpx, cpy, x, y)
    SV *self
    NV cpx
    NV cpy
    NV x
    NV y
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_QUADRATIC_CURVE_TO, cpx, cpy, x, y);
    XPUSHs(self);
}

void
clip(self, ...)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    AV *cmd = newAV();
    av_push(cmd, newSViv(CANVAS_OP_CLIP));
    /* Optional fill rule: 'nonzero' (default) or 'evenodd' */
    if (items > 1 && SvOK(ST(1))) {
        av_push(cmd, newSVsv(ST(1)));
    }
    av_push(buf, newRV_noinc((SV *)cmd));
    XPUSHs(self);
}

# ========================================================================
# Phase 2: Advanced Transform Methods
# ========================================================================

void
transform(self, a, b, c, d, e, f)
    SV *self
    NV a
    NV b
    NV c
    NV d
    NV e
    NV f
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_6nv(aTHX_ buf, CANVAS_OP_TRANSFORM, a, b, c, d, e, f);
    XPUSHs(self);
}

void
set_transform(self, a, b, c, d, e, f)
    SV *self
    NV a
    NV b
    NV c
    NV d
    NV e
    NV f
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_6nv(aTHX_ buf, CANVAS_OP_SET_TRANSFORM, a, b, c, d, e, f);
    XPUSHs(self);
}

void
reset_transform(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_RESET_TRANSFORM);
    XPUSHs(self);
}

void
miter_limit(self, limit)
    SV *self
    NV limit
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1nv(aTHX_ buf, CANVAS_OP_MITER_LIMIT, limit);
    XPUSHs(self);
}

void
global_composite_operation(self, op)
    SV *self
    const char *op
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_1s(aTHX_ buf, CANVAS_OP_GLOBAL_COMP_OP, op);
    XPUSHs(self);
}

# ========================================================================
# Phase 2: Convenience Shape Methods
# ========================================================================

void
line(self, x1, y1, x2, y2)
    SV *self
    NV x1
    NV y1
    NV x2
    NV y2
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_4nv(aTHX_ buf, CANVAS_OP_LINE, x1, y1, x2, y2);
    XPUSHs(self);
}

void
rounded_rect(self, x, y, w, h, radius)
    SV *self
    NV x
    NV y
    NV w
    NV h
    NV radius
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_5nv(aTHX_ buf, CANVAS_OP_ROUNDED_RECT, x, y, w, h, radius);
    XPUSHs(self);
}

void
fill_rounded_rect(self, x, y, w, h, radius)
    SV *self
    NV x
    NV y
    NV w
    NV h
    NV radius
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    _canvas_push_op_5nv(aTHX_ buf, CANVAS_OP_FILL_ROUNDED_RECT, x, y, w, h, radius);
    XPUSHs(self);
}

void
polygon(self, points_av)
    SV *self
    SV *points_av
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    AV *points;
    SSize_t len, i;

    if (!SvROK(points_av) || SvTYPE(SvRV(points_av)) != SVt_PVAV)
        croak("polygon requires arrayref of [x,y] pairs");

    points = (AV *)SvRV(points_av);
    len = av_len(points) + 1;

    if (len < 2)
        croak("polygon requires at least 2 points");

    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_BEGIN_PATH);

    for (i = 0; i < len; i++) {
        SV **pt_svp = av_fetch(points, i, 0);
        AV *pt;
        NV x, y;

        if (!pt_svp || !SvROK(*pt_svp) || SvTYPE(SvRV(*pt_svp)) != SVt_PVAV)
            croak("polygon point %ld must be [x,y] arrayref", (long)i);

        pt = (AV *)SvRV(*pt_svp);
        x = SvNV(*av_fetch(pt, 0, 0));
        y = SvNV(*av_fetch(pt, 1, 0));

        if (i == 0) {
            _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_MOVE_TO, x, y);
        } else {
            _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_LINE_TO, x, y);
        }
    }

    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_CLOSE_PATH);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_STROKE);
    XPUSHs(self);
}

void
fill_polygon(self, points_av)
    SV *self
    SV *points_av
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    AV *buf = _canvas_get_buffer(aTHX_ self_hv);
    AV *points;
    SSize_t len, i;

    if (!SvROK(points_av) || SvTYPE(SvRV(points_av)) != SVt_PVAV)
        croak("fill_polygon requires arrayref of [x,y] pairs");

    points = (AV *)SvRV(points_av);
    len = av_len(points) + 1;

    if (len < 2)
        croak("fill_polygon requires at least 2 points");

    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_BEGIN_PATH);

    for (i = 0; i < len; i++) {
        SV **pt_svp = av_fetch(points, i, 0);
        AV *pt;
        NV x, y;

        if (!pt_svp || !SvROK(*pt_svp) || SvTYPE(SvRV(*pt_svp)) != SVt_PVAV)
            croak("fill_polygon point %ld must be [x,y] arrayref", (long)i);

        pt = (AV *)SvRV(*pt_svp);
        x = SvNV(*av_fetch(pt, 0, 0));
        y = SvNV(*av_fetch(pt, 1, 0));

        if (i == 0) {
            _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_MOVE_TO, x, y);
        } else {
            _canvas_push_op_2nv(aTHX_ buf, CANVAS_OP_LINE_TO, x, y);
        }
    }

    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_CLOSE_PATH);
    _canvas_push_op_0(aTHX_ buf, CANVAS_OP_FILL);
    XPUSHs(self);
}

# ========================================================================
# Buffer / Render Methods
# ========================================================================

SV *
_serialize_buffer(self)
    SV *self
CODE:
{
    HV *self_hv = (HV *)SvRV(self);
    RETVAL = _canvas_serialize_buffer(aTHX_ self_hv);
}
OUTPUT:
    RETVAL

void
_clear_buffer(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    _canvas_clear_buffer(aTHX_ self_hv);
    XPUSHs(self);
}

SV *
render(self)
    SV *self
CODE:
{
    HV *self_hv = (HV *)SvRV(self);
    RETVAL = _canvas_gen_html(aTHX_ self_hv);
}
OUTPUT:
    RETVAL

void
flush(self)
    SV *self
PPCODE:
{
    HV *self_hv = (HV *)SvRV(self);
    SV *js = _canvas_serialize_buffer(aTHX_ self_hv);

    /* Call Chandra::eval_js if available */
    dSP;
    ENTER;
    SAVETMPS;
    PUSHMARK(SP);
    XPUSHs(sv_2mortal(newSVpvs("Chandra")));
    XPUSHs(sv_2mortal(js));
    PUTBACK;
    call_method("eval_js", G_DISCARD);
    FREETMPS;
    LEAVE;

    /* Clear the buffer */
    _canvas_clear_buffer(aTHX_ self_hv);
    XPUSHs(self);
}



( run in 0.587 second using v1.01-cache-2.11-cpan-71847e10f99 )