From b43ec18561ed4e3142679dc2c7f85db64c932911 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Oct 2015 08:04:23 -0400 Subject: [PATCH] Started bringing the new drawing code over to the other backends. --- gtkarea/main.c | 205 +++++++++++++++++++++++++++++++++++++-------- gtkarea/ui.h | 81 ++++++++++++++---- macarea/alt/main.m | 205 +++++++++++++++++++++++++++++++++++++-------- macarea/alt/ui.h | 81 ++++++++++++++---- 4 files changed, 478 insertions(+), 94 deletions(-) diff --git a/gtkarea/main.c b/gtkarea/main.c index d1a09765..40bd8168 100644 --- a/gtkarea/main.c +++ b/gtkarea/main.c @@ -17,77 +17,216 @@ static GtkWidget *textstr; static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { + uiDrawPath *path; + uiDrawBrush brush; uiDrawStrokeParams sp; - uiDrawBeginPathRGB(p->Context, 0xFF, 0x00, 0x00); - uiDrawMoveTo(p->Context, p->ClipX + 5, p->ClipY + 5); - uiDrawLineTo(p->Context, (p->ClipX + p->ClipWidth) - 5, (p->ClipY + p->ClipHeight) - 5); + brush.Type = uiDrawBrushTypeSolid; + brush.A = 1; + + brush.R = 1; + brush.G = 0; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, p->ClipX + 5, p->ClipY + 5); + uiDrawPathLineTo(path, (p->ClipX + p->ClipWidth) - 5, (p->ClipY + p->ClipHeight) - 5); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x00, 0x00, 0xC0); - uiDrawMoveTo(p->Context, p->ClipX, p->ClipY); - uiDrawLineTo(p->Context, p->ClipX + p->ClipWidth, p->ClipY); - uiDrawLineTo(p->Context, 50, 150); - uiDrawLineTo(p->Context, 50, 50); - uiDrawCloseFigure(p->Context); + brush.R = 0; + brush.G = 0; + brush.B = 0.75; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, p->ClipX, p->ClipY); + uiDrawPathLineTo(path, p->ClipX + p->ClipWidth, p->ClipY); + uiDrawPathLineTo(path, 50, 150); + uiDrawPathLineTo(path, 50, 50); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinRound; sp.Thickness = 5; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGBA(p->Context, 0x00, 0xC0, 0x00, 0x80); - uiDrawRectangle(p->Context, 120, 80, 50, 50); - uiDrawFill(p->Context, uiDrawFillModeWinding); + brush.R = 0; + brush.G = 0.75; + brush.B = 0; + brush.A = 0.5; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, 120, 80, 50, 50); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, &brush); + uiDrawFreePath(path); + brush.A = 1; - uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0x00); - uiDrawMoveTo(p->Context, 5, 10); - uiDrawLineTo(p->Context, 5, 50); + brush.R = 0; + brush.G = 0.5; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 5.5, 10.5); + uiDrawPathLineTo(path, 5.5, 50.5); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x80, 0xC0, 0x00); - uiDrawMoveTo(p->Context, 400, 100); - uiDrawArcTo(p->Context, + brush.R = 0.5; + brush.G = 0.75; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 400, 100); + uiDrawPathArcTo(path, 400, 100, 50, 30. * (M_PI / 180.), // note the end angle here // in GDI, the second angle to AngleArc() is relative to the start, not to 0 - 330. * (M_PI / 180.), - 1); + 330. * (M_PI / 180.)); // TODO add a checkbox for this - uiDrawLineTo(p->Context, 400, 100); - uiDrawArcTo(p->Context, + uiDrawPathLineTo(path, 400, 100); + uiDrawPathNewFigureWithArc(path, 510, 100, 50, 30. * (M_PI / 180.), - 330. * (M_PI / 180.), - 0); - uiDrawCloseFigure(p->Context); + 330. * (M_PI / 180.)); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0xC0); - uiDrawMoveTo(p->Context, 300, 300); - uiDrawBezierTo(p->Context, + brush.R = 0; + brush.G = 0.5; + brush.B = 0.75; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 300, 300); + uiDrawPathBezierTo(path, 350, 320, 310, 390, 435, 372); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); + + // based on https://msdn.microsoft.com/en-us/library/windows/desktop/dd756682%28v=vs.85%29.aspx + path = uiDrawNewPath(uiDrawFillModeWinding); +#define XO 50 +#define YO 250 + uiDrawPathNewFigure(path, 0 + XO, 0 + YO); + uiDrawPathLineTo(path, 200 + XO, 0 + YO); + uiDrawPathBezierTo(path, + 150 + XO, 50 + YO, + 150 + XO, 150 + YO, + 200 + XO, 200 + YO); + uiDrawPathLineTo(path, 0 + XO, 200 + YO); + uiDrawPathBezierTo(path, + 50 + XO, 150 + YO, + 50 + XO, 50 + YO, + 0 + XO, 0 + YO); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); + // first the stroke + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0; + brush.G = 0; + brush.B = 0; + brush.A = 1; + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.MiterLimit = uiDrawDefaultMiterLimit; + sp.Thickness = 10; + uiDrawStroke(p->Context, path, &brush, &sp); + // and now the fill + { + uiDrawBrushGradientStop stops[2]; + + stops[0].Pos = 0.0; + stops[0].R = 0.0; + stops[0].G = 1.0; + stops[0].B = 1.0; + stops[0].A = 0.25; + stops[1].Pos = 1.0; + stops[1].R = 0.0; + stops[1].G = 0.0; + stops[1].B = 1.0; + stops[1].A = 1.0; + brush.Type = uiDrawBrushTypeLinearGradient; + brush.X0 = 100 + XO; + brush.Y0 = 0 + YO; + brush.X1 = 100 + XO; + brush.Y1 = 200 + YO; + brush.Stops = stops; + brush.NumStops = 2; + uiDrawFill(p->Context, path, &brush); + } +#undef YO +#undef XO + uiDrawFreePath(path); + + // based on https://msdn.microsoft.com/en-us/library/windows/desktop/dd756679%28v=vs.85%29.aspx + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 585, 235); + uiDrawPathArcTo(path, + 510, 235, + 75, + 0, + // TODO why doesn't 360° work + 2 * M_PI - 0.1); + uiDrawPathEnd(path); + // first the stroke + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0; + brush.G = 0; + brush.B = 0; + brush.A = 1; + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.MiterLimit = uiDrawDefaultMiterLimit; + sp.Thickness = 1; + uiDrawStroke(p->Context, path, &brush, &sp); + // then the fill + { + uiDrawBrushGradientStop stops[2]; + + stops[0].Pos = 0.0; + stops[0].R = 1.0; + stops[0].G = 1.0; + stops[0].B = 0.0; + stops[0].A = 1.0; + stops[1].Pos = 1.0; + stops[1].R = ((double) 0x22) / 255.0; + stops[1].G = ((double) 0x8B) / 255.0; + stops[1].B = ((double) 0x22) / 255.0; + stops[1].A = 1.0; + brush.Type = uiDrawBrushTypeRadialGradient; + // start point + brush.X0 = 510; + brush.Y0 = 235; + // outer circle's center + brush.X1 = 510; + brush.Y1 = 235; + brush.OuterRadius = 75; + brush.Stops = stops; + brush.NumStops = 2; + uiDrawFill(p->Context, path, &brush); + } + uiDrawFreePath(path); } static uintmax_t handlerHScrollMax(uiAreaHandler *a, uiArea *area) diff --git a/gtkarea/ui.h b/gtkarea/ui.h index 86cb1e98..461cb8b1 100644 --- a/gtkarea/ui.h +++ b/gtkarea/ui.h @@ -36,14 +36,26 @@ struct uiAreaDrawParams { intmax_t VScrollPos; }; -// TODO proper sources // TODO dotting/dashing +typedef struct uiDrawPath uiDrawPath; +typedef struct uiDrawBrush uiDrawBrush; typedef struct uiDrawStrokeParams uiDrawStrokeParams; + +typedef enum uiDrawBrushType uiDrawBrushType; +typedef struct uiDrawBrushGradientStop uiDrawBrushGradientStop; + typedef enum uiDrawLineCap uiDrawLineCap; typedef enum uiDrawLineJoin uiDrawLineJoin; typedef enum uiDrawFillMode uiDrawFillMode; +enum uiDrawBrushType { + uiDrawBrushTypeSolid, + uiDrawBrushTypeLinearGradient, + uiDrawBrushTypeRadialGradient, + uiDrawBrushTypeImage, +}; + enum uiDrawLineCap { uiDrawLineCapFlat, uiDrawLineCapRound, @@ -66,28 +78,69 @@ enum uiDrawFillMode { uiDrawFillModeAlternate, }; +struct uiDrawBrush { + uiDrawBrushType Type; + + // solid brushes + double R; + double G; + double B; + double A; + + // gradient brushes + double X0; // linear: start X, radial: start X + double Y0; // linear: start Y, radial: start Y + double X1; // linear: end X, radial: outer circle center X + double Y1; // linear: end Y, radial: outer circle center Y + double OuterRadius; // radial gradients only + uiDrawBrushGradientStop *Stops; + size_t NumStops; + // TODO extend mode + // cairo: none, repeat, reflect, pad; no individual control + // Direct2D: repeat, reflect, pad; no individual control + // Core Graphics: none, pad; before and after individually + // TODO cairo documentation is inconsistent about pad + + // TODO images + + // TODO transforms +}; + +struct uiDrawBrushGradientStop { + double Pos; + double R; + double G; + double B; + double A; +}; + struct uiDrawStrokeParams { uiDrawLineCap Cap; uiDrawLineJoin Join; - intmax_t Thickness; + double Thickness; double MiterLimit; }; -void uiDrawBeginPathRGB(uiDrawContext *, uint8_t, uint8_t, uint8_t); -// TODO verify these aren't alpha premultiplied anywhere -void uiDrawBeginPathRGBA(uiDrawContext *, uint8_t, uint8_t, uint8_t, uint8_t); +uiDrawPath *uiDrawNewPath(uiDrawFillMode); +void uiDrawFreePath(uiDrawPath *); -void uiDrawMoveTo(uiDrawContext *, intmax_t, intmax_t); -void uiDrawLineTo(uiDrawContext *, intmax_t, intmax_t); -void uiDrawRectangle(uiDrawContext *, intmax_t, intmax_t, intmax_t, intmax_t); +void uiDrawPathNewFigure(uiDrawPath *, double, double); +void uiDrawPathNewFigureWithArc(uiDrawPath *, double, double, double, double, double); +void uiDrawPathLineTo(uiDrawPath *, double, double); // notes: angles are both relative to 0 and go counterclockwise -void uiDrawArcTo(uiDrawContext *, intmax_t, intmax_t, intmax_t, double, double, int); -// TODO behavior when there is no initial point on Windows and OS X -void uiDrawBezierTo(uiDrawContext *, intmax_t, intmax_t, intmax_t, intmax_t, intmax_t, intmax_t); -void uiDrawCloseFigure(uiDrawContext *); +// TODO is the initial line segment on cairo and OS X a proper join? +void uiDrawPathArcTo(uiDrawPath *, double, double, double, double, double); +void uiDrawPathBezierTo(uiDrawPath *, double, double, double, double, double, double); +// TODO quadratic bezier +void uiDrawPathCloseFigure(uiDrawPath *); -void uiDrawStroke(uiDrawContext *, uiDrawStrokeParams *); -void uiDrawFill(uiDrawContext *, uiDrawFillMode); +// TODO effect of these when a figure is already started +void uiDrawPathAddRectangle(uiDrawPath *, double, double, double, double); + +void uiDrawPathEnd(uiDrawPath *); + +void uiDrawStroke(uiDrawContext *, uiDrawPath *, uiDrawBrush *, uiDrawStrokeParams *); +void uiDrawFill(uiDrawContext *, uiDrawPath *, uiDrawBrush *); // TODO primitives: // - rounded rectangles diff --git a/macarea/alt/main.m b/macarea/alt/main.m index ca0e61fa..04bd217a 100644 --- a/macarea/alt/main.m +++ b/macarea/alt/main.m @@ -16,77 +16,216 @@ static struct handler h; static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { + uiDrawPath *path; + uiDrawBrush brush; uiDrawStrokeParams sp; - uiDrawBeginPathRGB(p->Context, 0xFF, 0x00, 0x00); - uiDrawMoveTo(p->Context, p->ClipX + 5, p->ClipY + 5); - uiDrawLineTo(p->Context, (p->ClipX + p->ClipWidth) - 5, (p->ClipY + p->ClipHeight) - 5); + brush.Type = uiDrawBrushTypeSolid; + brush.A = 1; + + brush.R = 1; + brush.G = 0; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, p->ClipX + 5, p->ClipY + 5); + uiDrawPathLineTo(path, (p->ClipX + p->ClipWidth) - 5, (p->ClipY + p->ClipHeight) - 5); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x00, 0x00, 0xC0); - uiDrawMoveTo(p->Context, p->ClipX, p->ClipY); - uiDrawLineTo(p->Context, p->ClipX + p->ClipWidth, p->ClipY); - uiDrawLineTo(p->Context, 50, 150); - uiDrawLineTo(p->Context, 50, 50); - uiDrawCloseFigure(p->Context); + brush.R = 0; + brush.G = 0; + brush.B = 0.75; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, p->ClipX, p->ClipY); + uiDrawPathLineTo(path, p->ClipX + p->ClipWidth, p->ClipY); + uiDrawPathLineTo(path, 50, 150); + uiDrawPathLineTo(path, 50, 50); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinRound; sp.Thickness = 5; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGBA(p->Context, 0x00, 0xC0, 0x00, 0x80); - uiDrawRectangle(p->Context, 120, 80, 50, 50); - uiDrawFill(p->Context, uiDrawFillModeWinding); + brush.R = 0; + brush.G = 0.75; + brush.B = 0; + brush.A = 0.5; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, 120, 80, 50, 50); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, &brush); + uiDrawFreePath(path); + brush.A = 1; - uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0x00); - uiDrawMoveTo(p->Context, 5, 10); - uiDrawLineTo(p->Context, 5, 50); + brush.R = 0; + brush.G = 0.5; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 5.5, 10.5); + uiDrawPathLineTo(path, 5.5, 50.5); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x80, 0xC0, 0x00); - uiDrawMoveTo(p->Context, 400, 100); - uiDrawArcTo(p->Context, + brush.R = 0.5; + brush.G = 0.75; + brush.B = 0; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 400, 100); + uiDrawPathArcTo(path, 400, 100, 50, 30. * (M_PI / 180.), // note the end angle here // in GDI, the second angle to AngleArc() is relative to the start, not to 0 - 330. * (M_PI / 180.), - 1); + 330. * (M_PI / 180.)); // TODO add a checkbox for this - uiDrawLineTo(p->Context, 400, 100); - uiDrawArcTo(p->Context, + uiDrawPathLineTo(path, 400, 100); + uiDrawPathNewFigureWithArc(path, 510, 100, 50, 30. * (M_PI / 180.), - 330. * (M_PI / 180.), - 0); - uiDrawCloseFigure(p->Context); + 330. * (M_PI / 180.)); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); - uiDrawBeginPathRGB(p->Context, 0x00, 0x80, 0xC0); - uiDrawMoveTo(p->Context, 300, 300); - uiDrawBezierTo(p->Context, + brush.R = 0; + brush.G = 0.5; + brush.B = 0.75; + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 300, 300); + uiDrawPathBezierTo(path, 350, 320, 310, 390, 435, 372); + uiDrawPathEnd(path); sp.Cap = uiDrawLineCapFlat; sp.Join = uiDrawLineJoinMiter; sp.Thickness = 1; sp.MiterLimit = uiDrawDefaultMiterLimit; - uiDrawStroke(p->Context, &sp); + uiDrawStroke(p->Context, path, &brush, &sp); + uiDrawFreePath(path); + + // based on https://msdn.microsoft.com/en-us/library/windows/desktop/dd756682%28v=vs.85%29.aspx + path = uiDrawNewPath(uiDrawFillModeWinding); +#define XO 50 +#define YO 250 + uiDrawPathNewFigure(path, 0 + XO, 0 + YO); + uiDrawPathLineTo(path, 200 + XO, 0 + YO); + uiDrawPathBezierTo(path, + 150 + XO, 50 + YO, + 150 + XO, 150 + YO, + 200 + XO, 200 + YO); + uiDrawPathLineTo(path, 0 + XO, 200 + YO); + uiDrawPathBezierTo(path, + 50 + XO, 150 + YO, + 50 + XO, 50 + YO, + 0 + XO, 0 + YO); + uiDrawPathCloseFigure(path); + uiDrawPathEnd(path); + // first the stroke + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0; + brush.G = 0; + brush.B = 0; + brush.A = 1; + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.MiterLimit = uiDrawDefaultMiterLimit; + sp.Thickness = 10; + uiDrawStroke(p->Context, path, &brush, &sp); + // and now the fill + { + uiDrawBrushGradientStop stops[2]; + + stops[0].Pos = 0.0; + stops[0].R = 0.0; + stops[0].G = 1.0; + stops[0].B = 1.0; + stops[0].A = 0.25; + stops[1].Pos = 1.0; + stops[1].R = 0.0; + stops[1].G = 0.0; + stops[1].B = 1.0; + stops[1].A = 1.0; + brush.Type = uiDrawBrushTypeLinearGradient; + brush.X0 = 100 + XO; + brush.Y0 = 0 + YO; + brush.X1 = 100 + XO; + brush.Y1 = 200 + YO; + brush.Stops = stops; + brush.NumStops = 2; + uiDrawFill(p->Context, path, &brush); + } +#undef YO +#undef XO + uiDrawFreePath(path); + + // based on https://msdn.microsoft.com/en-us/library/windows/desktop/dd756679%28v=vs.85%29.aspx + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, 585, 235); + uiDrawPathArcTo(path, + 510, 235, + 75, + 0, + // TODO why doesn't 360° work + 2 * M_PI - 0.1); + uiDrawPathEnd(path); + // first the stroke + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0; + brush.G = 0; + brush.B = 0; + brush.A = 1; + sp.Cap = uiDrawLineCapFlat; + sp.Join = uiDrawLineJoinMiter; + sp.MiterLimit = uiDrawDefaultMiterLimit; + sp.Thickness = 1; + uiDrawStroke(p->Context, path, &brush, &sp); + // then the fill + { + uiDrawBrushGradientStop stops[2]; + + stops[0].Pos = 0.0; + stops[0].R = 1.0; + stops[0].G = 1.0; + stops[0].B = 0.0; + stops[0].A = 1.0; + stops[1].Pos = 1.0; + stops[1].R = ((double) 0x22) / 255.0; + stops[1].G = ((double) 0x8B) / 255.0; + stops[1].B = ((double) 0x22) / 255.0; + stops[1].A = 1.0; + brush.Type = uiDrawBrushTypeRadialGradient; + // start point + brush.X0 = 510; + brush.Y0 = 235; + // outer circle's center + brush.X1 = 510; + brush.Y1 = 235; + brush.OuterRadius = 75; + brush.Stops = stops; + brush.NumStops = 2; + uiDrawFill(p->Context, path, &brush); + } + uiDrawFreePath(path); } static uintmax_t handlerHScrollMax(uiAreaHandler *a, uiArea *area) diff --git a/macarea/alt/ui.h b/macarea/alt/ui.h index 86cb1e98..461cb8b1 100644 --- a/macarea/alt/ui.h +++ b/macarea/alt/ui.h @@ -36,14 +36,26 @@ struct uiAreaDrawParams { intmax_t VScrollPos; }; -// TODO proper sources // TODO dotting/dashing +typedef struct uiDrawPath uiDrawPath; +typedef struct uiDrawBrush uiDrawBrush; typedef struct uiDrawStrokeParams uiDrawStrokeParams; + +typedef enum uiDrawBrushType uiDrawBrushType; +typedef struct uiDrawBrushGradientStop uiDrawBrushGradientStop; + typedef enum uiDrawLineCap uiDrawLineCap; typedef enum uiDrawLineJoin uiDrawLineJoin; typedef enum uiDrawFillMode uiDrawFillMode; +enum uiDrawBrushType { + uiDrawBrushTypeSolid, + uiDrawBrushTypeLinearGradient, + uiDrawBrushTypeRadialGradient, + uiDrawBrushTypeImage, +}; + enum uiDrawLineCap { uiDrawLineCapFlat, uiDrawLineCapRound, @@ -66,28 +78,69 @@ enum uiDrawFillMode { uiDrawFillModeAlternate, }; +struct uiDrawBrush { + uiDrawBrushType Type; + + // solid brushes + double R; + double G; + double B; + double A; + + // gradient brushes + double X0; // linear: start X, radial: start X + double Y0; // linear: start Y, radial: start Y + double X1; // linear: end X, radial: outer circle center X + double Y1; // linear: end Y, radial: outer circle center Y + double OuterRadius; // radial gradients only + uiDrawBrushGradientStop *Stops; + size_t NumStops; + // TODO extend mode + // cairo: none, repeat, reflect, pad; no individual control + // Direct2D: repeat, reflect, pad; no individual control + // Core Graphics: none, pad; before and after individually + // TODO cairo documentation is inconsistent about pad + + // TODO images + + // TODO transforms +}; + +struct uiDrawBrushGradientStop { + double Pos; + double R; + double G; + double B; + double A; +}; + struct uiDrawStrokeParams { uiDrawLineCap Cap; uiDrawLineJoin Join; - intmax_t Thickness; + double Thickness; double MiterLimit; }; -void uiDrawBeginPathRGB(uiDrawContext *, uint8_t, uint8_t, uint8_t); -// TODO verify these aren't alpha premultiplied anywhere -void uiDrawBeginPathRGBA(uiDrawContext *, uint8_t, uint8_t, uint8_t, uint8_t); +uiDrawPath *uiDrawNewPath(uiDrawFillMode); +void uiDrawFreePath(uiDrawPath *); -void uiDrawMoveTo(uiDrawContext *, intmax_t, intmax_t); -void uiDrawLineTo(uiDrawContext *, intmax_t, intmax_t); -void uiDrawRectangle(uiDrawContext *, intmax_t, intmax_t, intmax_t, intmax_t); +void uiDrawPathNewFigure(uiDrawPath *, double, double); +void uiDrawPathNewFigureWithArc(uiDrawPath *, double, double, double, double, double); +void uiDrawPathLineTo(uiDrawPath *, double, double); // notes: angles are both relative to 0 and go counterclockwise -void uiDrawArcTo(uiDrawContext *, intmax_t, intmax_t, intmax_t, double, double, int); -// TODO behavior when there is no initial point on Windows and OS X -void uiDrawBezierTo(uiDrawContext *, intmax_t, intmax_t, intmax_t, intmax_t, intmax_t, intmax_t); -void uiDrawCloseFigure(uiDrawContext *); +// TODO is the initial line segment on cairo and OS X a proper join? +void uiDrawPathArcTo(uiDrawPath *, double, double, double, double, double); +void uiDrawPathBezierTo(uiDrawPath *, double, double, double, double, double, double); +// TODO quadratic bezier +void uiDrawPathCloseFigure(uiDrawPath *); -void uiDrawStroke(uiDrawContext *, uiDrawStrokeParams *); -void uiDrawFill(uiDrawContext *, uiDrawFillMode); +// TODO effect of these when a figure is already started +void uiDrawPathAddRectangle(uiDrawPath *, double, double, double, double); + +void uiDrawPathEnd(uiDrawPath *); + +void uiDrawStroke(uiDrawContext *, uiDrawPath *, uiDrawBrush *, uiDrawStrokeParams *); +void uiDrawFill(uiDrawContext *, uiDrawPath *, uiDrawBrush *); // TODO primitives: // - rounded rectangles