libui/gtkarea/draw.c

293 lines
5.9 KiB
C
Raw Normal View History

// 6 september 2015
#include "area.h"
struct uiDrawPath {
GArray *pieces;
uiDrawFillMode fillMode;
gboolean ended;
};
struct piece {
int type;
double d[8];
};
enum {
newFigure,
newFigureArc,
lineTo,
arcTo,
bezierTo,
closeFigure,
addRect,
};
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
{
uiDrawPath *p;
// TODO uiNew
p = g_malloc0(sizeof (uiDrawPath));
p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece));
p->fillMode = mode;
return p;
}
void uiDrawFreePath(uiDrawPath *p)
{
g_array_free(p->pieces, TRUE);
// TODO uiFree
g_free(p);
}
static void add(uiDrawPath *p, struct piece *piece)
{
if (p->ended)
g_error("path ended in add()");
g_array_append_vals(p->pieces, piece, 1);
}
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
2015-09-07 09:25:59 -05:00
{
struct piece piece;
piece.type = newFigure;
piece.d[0] = x;
piece.d[1] = y;
add(p, &piece);
2015-09-07 09:25:59 -05:00
}
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double endAngle)
{
struct piece piece;
piece.type = newFigureArc;
piece.d[0] = xCenter;
piece.d[1] = yCenter;
piece.d[2] = radius;
piece.d[3] = startAngle;
piece.d[4] = endAngle;
add(p, &piece);
}
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
{
struct piece piece;
piece.type = lineTo;
piece.d[0] = x;
piece.d[1] = y;
add(p, &piece);
}
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double endAngle)
{
struct piece piece;
piece.type = arcTo;
piece.d[0] = xCenter;
piece.d[1] = yCenter;
piece.d[2] = radius;
piece.d[3] = startAngle;
piece.d[4] = endAngle;
add(p, &piece);
}
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
2015-09-07 09:25:59 -05:00
{
struct piece piece;
piece.type = bezierTo;
piece.d[0] = c1x;
piece.d[1] = c1y;
piece.d[2] = c2x;
piece.d[3] = c2y;
piece.d[4] = endX;
piece.d[5] = endY;
add(p, &piece);
2015-09-07 09:25:59 -05:00
}
void uiDrawPathCloseFigure(uiDrawPath *p)
{
struct piece piece;
piece.type = closeFigure;
add(p, &piece);
}
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
2015-09-07 18:29:42 -05:00
{
struct piece piece;
piece.type = addRect;
piece.d[0] = x;
piece.d[1] = y;
piece.d[2] = width;
piece.d[3] = height;
add(p, &piece);
2015-09-07 18:29:42 -05:00
}
void uiDrawPathEnd(uiDrawPath *p)
2015-09-06 21:48:25 -05:00
{
p->ended = TRUE;
2015-09-06 21:48:25 -05:00
}
static void runPath(uiDrawPath *p, cairo_t *cr)
{
guint i;
struct piece *piece;
if (!p->ended)
g_error("path not ended in runPath()");
cairo_new_path(cr);
for (i = 0; i < p->pieces->len; i++) {
piece = &g_array_index(p->pieces, struct piece, i);
switch (piece->type) {
case newFigure:
cairo_move_to(cr, piece->d[0], piece->d[1]);
break;
case newFigureArc:
cairo_new_sub_path(cr);
// fall through
case arcTo:
// the Windows AngleArc() function only goes counterclockwise, so our uiDrawArc() function does too
// simulate it in cairo by drawing a negative arc from end to start
cairo_arc_negative(cr,
piece->d[0],
piece->d[1],
piece->d[2],
piece->d[4],
piece->d[3]);
break;
case lineTo:
cairo_line_to(cr, piece->d[0], piece->d[1]);
break;
case bezierTo:
cairo_curve_to(cr,
piece->d[0],
piece->d[1],
piece->d[2],
piece->d[3],
piece->d[4],
piece->d[5]);
break;
case closeFigure:
cairo_close_path(cr);
break;
case addRect:
cairo_rectangle(cr,
piece->d[0],
piece->d[1],
piece->d[2],
piece->d[3]);
break;
}
}
}
struct uiDrawContext {
cairo_t *cr;
};
uiDrawContext *newContext(cairo_t *cr)
{
uiDrawContext *c;
// TODO use uiNew
c = (uiDrawContext *) g_malloc0(sizeof (uiDrawContext));
c->cr = cr;
return c;
}
// TODO replace all g_error with complain
static cairo_pattern_t *mkbrush(uiDrawBrush *b)
{
cairo_pattern_t *pat;
size_t i;
switch (b->Type) {
case uiDrawBrushTypeSolid:
pat = cairo_pattern_create_rgba(b->R, b->G, b->B, b->A);
break;
case uiDrawBrushTypeLinearGradient:
pat = cairo_pattern_create_linear(b->X0, b->Y0, b->X1, b->Y1);
break;
case uiDrawBrushTypeRadialGradient:
// make the start circle radius 0 to make it a point
pat = cairo_pattern_create_radial(
b->X0, b->Y0, 0,
b->X1, b->Y1, b->OuterRadius);
break;
// case uiDrawBrushTypeImage:
}
if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS)
g_error("error creating pattern in mkbrush(): %s",
cairo_status_to_string(cairo_pattern_status(pat)));
switch (b->Type) {
case uiDrawBrushTypeLinearGradient:
case uiDrawBrushTypeRadialGradient:
for (i = 0; i < b->NumStops; i++)
cairo_pattern_add_color_stop_rgba(pat,
b->Stops[i].Pos,
b->Stops[i].R,
b->Stops[i].G,
b->Stops[i].B,
b->Stops[i].A);
}
return pat;
}
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
{
cairo_pattern_t *pat;
runPath(path, c->cr);
pat = mkbrush(b);
cairo_set_source(c->cr, pat);
switch (p->Cap) {
case uiDrawLineCapFlat:
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_BUTT);
break;
case uiDrawLineCapRound:
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_ROUND);
break;
case uiDrawLineCapSquare:
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_SQUARE);
break;
}
switch (p->Join) {
case uiDrawLineJoinMiter:
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_MITER);
cairo_set_miter_limit(c->cr, p->MiterLimit);
break;
case uiDrawLineJoinRound:
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_ROUND);
break;
case uiDrawLineJoinBevel:
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_BEVEL);
break;
}
2015-09-10 22:54:54 -05:00
cairo_set_line_width(c->cr, p->Thickness);
cairo_stroke(c->cr);
cairo_pattern_destroy(pat);
}
2015-09-07 09:25:59 -05:00
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
2015-09-07 09:25:59 -05:00
{
cairo_pattern_t *pat;
runPath(path, c->cr);
pat = mkbrush(b);
cairo_set_source(c->cr, pat);
switch (path->fillMode) {
2015-09-07 09:25:59 -05:00
case uiDrawFillModeWinding:
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
break;
case uiDrawFillModeAlternate:
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_EVEN_ODD);
break;
}
cairo_fill(c->cr);
cairo_pattern_destroy(pat);
2015-09-07 09:25:59 -05:00
}