diff --git a/gtkarea/area.h b/gtkarea/area.h index 242b25ec..f75d874d 100644 --- a/gtkarea/area.h +++ b/gtkarea/area.h @@ -5,6 +5,7 @@ #define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4 #include #include +#include #define areaWidgetType (areaWidget_get_type()) #define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget)) diff --git a/gtkarea/draw.c b/gtkarea/draw.c index 4dfcedf0..5bb85301 100644 --- a/gtkarea/draw.c +++ b/gtkarea/draw.c @@ -1,6 +1,190 @@ // 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) +{ + struct piece piece; + + piece.type = newFigure; + piece.d[0] = x; + piece.d[1] = y; + add(p, &piece); +} + +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) +{ + 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); +} + +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) +{ + 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); +} + +void uiDrawPathEnd(uiDrawPath *p) +{ + p->ended = TRUE; +} + +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; }; @@ -15,72 +199,51 @@ uiDrawContext *newContext(cairo_t *cr) return c; } -void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b) +// TODO replace all g_error with complain +static cairo_pattern_t *mkbrush(uiDrawBrush *b) { - cairo_set_source_rgb(c->cr, - ((double) r) / 255, - ((double) g) / 255, - ((double) b) / 255); - cairo_new_path(c->cr); + 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 uiDrawBeginPathRGBA(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p) { - cairo_set_source_rgba(c->cr, - ((double) r) / 255, - ((double) g) / 255, - ((double) b) / 255, - ((double) a) / 255); - cairo_new_path(c->cr); -} + cairo_pattern_t *pat; -void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y) -{ - cairo_move_to(c->cr, ((double) x) + 0.5, ((double) y) + 0.5); -} - -void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y) -{ - cairo_line_to(c->cr, ((double) x) + 0.5, ((double) y) + 0.5); -} - -void uiDrawRectangle(uiDrawContext *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height) -{ - cairo_rectangle(c->cr, ((double) x) + 0.5, ((double) y) + 0.5, width, height); -} - -void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t radius, double startAngle, double endAngle, int lineFromCurrentPointToStart) -{ - if (!lineFromCurrentPointToStart) - cairo_new_sub_path(c->cr); - // 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(c->cr, - ((double) xCenter) + 0.5, - ((double) yCenter) + 0.5, - radius, - endAngle, - startAngle); -} - -void uiDrawBezierTo(uiDrawContext *c, intmax_t c1x, intmax_t c1y, intmax_t c2x, intmax_t c2y, intmax_t endX, intmax_t endY) -{ - cairo_curve_to(c->cr, - ((double) c1x) + 0.5, - ((double) c1y) + 0.5, - ((double) c2x) + 0.5, - ((double) c2y) + 0.5, - ((double) endX) + 0.5, - ((double) endY) + 0.5); -} - -void uiDrawCloseFigure(uiDrawContext *c) -{ - cairo_close_path(c->cr); -} - -void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p) -{ + 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); @@ -106,11 +269,17 @@ void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p) } cairo_set_line_width(c->cr, p->Thickness); cairo_stroke(c->cr); + cairo_pattern_destroy(pat); } -void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode) +void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { - switch (mode) { + cairo_pattern_t *pat; + + runPath(path, c->cr); + pat = mkbrush(b); + cairo_set_source(c->cr, pat); + switch (path->fillMode) { case uiDrawFillModeWinding: cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING); break; @@ -119,4 +288,5 @@ void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode) break; } cairo_fill(c->cr); + cairo_pattern_destroy(pat); } diff --git a/gtkarea/events.c b/gtkarea/events.c index f2749f19..6544b396 100644 --- a/gtkarea/events.c +++ b/gtkarea/events.c @@ -1,6 +1,7 @@ // 29 march 2014 // TODO remove #include +#include #include "ui.h" #include "uipriv.h"