Migrated the OS X uiArea. Now to fix build issues and test.

This commit is contained in:
Pietro Gagliardi 2015-10-08 11:49:24 -04:00
parent 447396d18d
commit 52c6888a6b
1 changed files with 210 additions and 101 deletions

View File

@ -1,16 +1,99 @@
// 6 september 2015
#include "area.h"
// TODO some pixel thick lines aren't actually pixel thick
struct uiDrawPath {
CGMutablePathRef path;
uiDrawFillMode fillMode;
BOOL ended;
};
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
{
uiDrawPath *p;
// TODO uiNew
p = malloc(sizeof (uiDrawPath));
p->path = CGPathCreateMutable()
p->fillMode = mode;
p->ended = NO;
return p;
}
void uiDrawFreePath(uiDrawPath *p)
{
CGPathRelease((CGPathRef) (p->path));
// TODO uiFree
free(p);
}
// TODO
#define complain(...) ;
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
{
if (p->ended)
complain("attempt to add figure to ended path in uiDrawPathNewFigure()");
CGPathMoveToPoint(p->path, NULL, x, y);
}
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double endAngle)
{
if (p->ended)
complain("attempt to add figure to ended path in uiDrawPathNewFigureWithArc()");
// TODO
CGPathMoveToPoint(p->path, NULL, startx, starty);
uiDrawPathArcTo(p, xCenter, yCenter, radius, startAngle, endAngle);
}
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
{
if (p->ended)
complain("attempt to add line to ended path in uiDrawPathLineTo()");
CGPathAddLineToPoint(p->path, NULL, x, y);
}
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double endAngle)
{
if (p->ended)
complain("attempt to add arc to ended path in uiDrawPathArcTo()");
CGPathAddArc(p->path, NULL,
xCenter, yCenter,
radius,
startAngle, endAngle,
NO);
}
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
{
if (p->ended)
complain("attempt to add bezier to ended path in uiDrawPathBezierTo()");
CGPathAddCurveToPoint(p->path, NULL,
c1x, c1y,
c2x, c2y,
endX, endY);
}
void uiDrawPathCloseFigure(uiDrawPath *p)
{
if (p->ended)
complain("attempt to close figure of ended path in uiDrawPathCloseFigure()");
CGPathCloseSubpath(p->path);
}
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
{
if (p->ended)
complain("attempt to add rectangle to ended path in uiDrawPathAddRectangle()");
CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height));
}
void uiDrawPathEnd(uiDrawPath *p)
{
p->ended = TRUE;
}
struct uiDrawContext {
CGContextRef c;
BOOL useRGBA;
CGFloat r;
CGFloat g;
CGFloat b;
CGFloat a;
};
uiDrawContext *newContext(CGContextRef ctxt)
@ -23,126 +106,152 @@ uiDrawContext *newContext(CGContextRef ctxt)
return c;
}
void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b)
// a stroke is identical to a fill of a stroked path
// we need to do this in order to stroke with a gradient; see http://stackoverflow.com/a/25034854/3408572
// doing this for other brushes works too
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
{
c->useRGBA = YES;
c->r = ((CGFloat) r) / 255;
c->g = ((CGFloat) g) / 255;
c->b = ((CGFloat) b) / 255;
c->a = 1.0;
CGContextBeginPath(c->c);
}
CGLineCap cap;
CGLineJoin join;
uiDrawPath p2;
void uiDrawBeginPathRGBA(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
c->useRGBA = YES;
c->r = ((CGFloat) r) / 255;
c->g = ((CGFloat) g) / 255;
c->b = ((CGFloat) b) / 255;
c->a = ((CGFloat) a) / 255;
CGContextBeginPath(c->c);
}
if (!path->ended)
complain("path not ended in uiDrawStroke()");
// TODO 0.25 for retina? some say yes, some say no
// TODO same adjustment for cairo
#define topoint(x) (((CGFloat) x) + 0.5)
void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y)
{
CGContextMoveToPoint(c->c, topoint(x), topoint(y));
}
void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y)
{
CGContextAddLineToPoint(c->c, topoint(x), topoint(y));
}
// TODO width-1/height-1? (also for cairo)
void uiDrawRectangle(uiDrawContext *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height)
{
CGContextAddRect(c->c, CGRectMake(topoint(x), topoint(y), width, height));
}
void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t radius, double startAngle, double endAngle, int lineFromCurrentPointToStart)
{
if (!lineFromCurrentPointToStart) {
// see http://stackoverflow.com/questions/31489157/extra-line-when-drawing-an-arc-in-swift
// TODO verify correctness
CGFloat x, y;
x = topoint(xCenter);
y = topoint(yCenter);
x += radius * cos(startAngle);
y -= radius * sin(startAngle);
CGContextMoveToPoint(c->c, x, y);
}
CGContextAddArc(c->c,
topoint(xCenter), topoint(yCenter),
radius,
startAngle, endAngle,
0);
}
void uiDrawBezierTo(uiDrawContext *c, intmax_t c1x, intmax_t c1y, intmax_t c2x, intmax_t c2y, intmax_t endX, intmax_t endY)
{
CGContextAddCurveToPoint(c->c,
topoint(c1x), topoint(c1y),
topoint(c2x), topoint(c2y),
topoint(endX), topoint(endY));
}
void uiDrawCloseFigure(uiDrawContext *c)
{
CGContextClosePath(c->c);
}
void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p)
{
switch (p->Cap) {
case uiDrawLineCapFlat:
CGContextSetLineCap(c->c, kCGLineCapButt);
cap = kCGLineCapButt;
break;
case uiDrawLineCapRound:
CGContextSetLineCap(c->c, kCGLineCapRound);
cap = kCGLineCapRound;
break;
case uiDrawLineCapSquare:
CGContextSetLineCap(c->c, kCGLineCapSquare);
cap = kCGLineCapSquare;
break;
}
switch (p->Join) {
case uiDrawLineJoinMiter:
CGContextSetLineJoin(c->c, kCGLineJoinMiter);
join = kCGLineJoinMiter;
CGContextSetMiterLimit(c->c, p->MiterLimit);
break;
case uiDrawLineJoinRound:
CGContextSetLineJoin(c->c, kCGLineJoinRound);
join = kCGLineJoinRound;
break;
case uiDrawLineJoinBevel:
CGContextSetLineJoin(c->c, kCGLineJoinBevel);
join = kCGLineJoinBevel;
break;
}
CGContextSetLineWidth(c->c, p->Thickness);
if (c->useRGBA)
CGContextSetRGBStrokeColor(c->c, c->r, c->g, c->b, c->a);
else {
// TODO
}
CGContextStrokePath(c->c);
// create a temporary path identical to the previous one
// the cast is safe; we never modify the CGPathRef and always cast it back to a CGPathRef anyway
p2.path = (CGMutablePathRef) CGPathCreateCopyByStrokingPath(path->path,
NULL,
p->Thickness,
cap,
join,
p->MiterLimit);
p2.fillMode = path->fillMode;
p2.ended = path->ended;
uiDrawFill(c, &p2, b);
// and clean up
CGPathRelease((CGPathRef) (p2.path));
}
void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode)
// for a solid fill, we can merely have Core Graphics fill directly
static void fillSolid(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
{
if (c->useRGBA)
CGContextSetRGBFillColor(c->c, c->r, c->g, c->b, c->a);
else {
// TODO
}
switch (mode) {
CGContextSetRGBFillColor(ctxt, b->R, b->G, b->B, b->A);
switch (p->fillMode) {
case uiDrawFillModeWinding:
CGContextFillPath(c->c);
CGContextFillPath(ctxt);
break;
case uiDrawFillModeAlternate:
CGContextEOFillPath(c->c);
CGContextEOFillPath(ctxt);
break;
}
}
// for a gradient fill, we need to clip to the path and then draw the gradient
// see http://stackoverflow.com/a/25034854/3408572
static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
{
CGGradientRef gradient;
CGColorSpaceRef colorspace;
CGFloat *colors;
CGFloat *locations;
// gradients need a color space
// for consistency with windows, use sRGB
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
// make the gradient
// TODO uiAlloc
colors = malloc(b->NumStops * 4 * sizeof (CGFloat));
locations = malloc(b->NumStops * sizeof (CGFloat));
for (i = 0; i < b->NumStops; i++) {
colors[i * 4 + 0] = b->Stops[i].R;
colors[i * 4 + 1] = b->Stops[i].G;
colors[i * 4 + 2] = b->Stops[i].B;
colors[i * 4 + 3] = b->Stops[i].A;
locations[i] = b->Stops[i].Pos;
}
gradient = CGGradientCreateWithColorComponents(colorspace, colors, locations, b->NumStops);
// TODO uiFree
free(locations);
free(colors);
// clip
switch (p->fillMode) {
case uiDrawFillModeWinding:
CGContextClip(ctxt);
break;
case uiDrawFillModeAlternate:
CGContextEOClip(ctxt);
break;
}
// draw the gradient
switch (b->Type) {
case uiDrawBrushTypeLinearGradient:
CGContextDrawLinearGradient(ctxt,
gradient,
CGPointMake(b->X0, b->Y0),
CGPointMake(b->X1, b->Y1),
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
break;
case uiDrawBrushTypeRadialGradient:
CGContextDrawRadialGradient(ctxt,
gradient,
CGPointMake(b->X0, b->Y0),
// make the start circle radius 0 to make it a point
0,
CGPointMake(b->X1, b->Y1),
b->OuterRadius,
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
break;
}
// and clean up
CGGradientRelease(gradient);
CGColorSpaceRelease(colorspace);
}
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
{
if (!path->ended)
complain("path not ended in uiDrawFill()");
CGContextAddPath(c->c, (CGPathRef) (p->path));
switch (b->Type) {
case uiDrawBrushTypeSolid:
fillSolid(c->c, path, b);
return;
case uiDrawBrushTypeLinearGradient:
case uiDrawBrushTypeRadialGradient:
fillGradient(c->c, path, b);
return;
// case uiDrawBrushTypeImage:
// TODO
return;
}
complain("unknown brush type %d in uiDrawFill()", b->Type);
}