#include "uipriv_windows.hpp"
#include "draw.hpp"
struct uiDrawPath {
ID2D1PathGeometry *path;
ID2D1GeometrySink *sink;
BOOL inFigure;
};
uiDrawPath *uiDrawNewPath(uiDrawFillMode fillmode)
{
uiDrawPath *p;
HRESULT hr;
p = uiprivNew(uiDrawPath);
hr = d2dfactory->CreatePathGeometry(&(p->path));
if (hr != S_OK)
logHRESULT(L"error creating path", hr);
hr = p->path->Open(&(p->sink));
if (hr != S_OK)
logHRESULT(L"error opening path", hr);
switch (fillmode) {
case uiDrawFillModeWinding:
p->sink->SetFillMode(D2D1_FILL_MODE_WINDING);
break;
case uiDrawFillModeAlternate:
p->sink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
break;
}
return p;
}
void uiDrawFreePath(uiDrawPath *p)
{
if (p->inFigure)
p->sink->EndFigure(D2D1_FIGURE_END_OPEN);
if (p->sink != NULL)
p->sink->Release();
p->path->Release();
uiprivFree(p);
}
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
{
D2D1_POINT_2F pt;
if (p->inFigure)
p->sink->EndFigure(D2D1_FIGURE_END_OPEN);
pt.x = x;
pt.y = y;
p->sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_FILLED);
p->inFigure = TRUE;
}
struct arc {
double xCenter;
double yCenter;
double radius;
double startAngle;
double sweep;
int negative;
};
#define aerMax 6 * DBL_EPSILON
static void drawArc(uiDrawPath *p, struct arc *a, void (*startFunction)(uiDrawPath *, double, double))
{
double sinx, cosx;
double startX, startY;
double endX, endY;
D2D1_ARC_SEGMENT as;
BOOL fullCircle;
double absSweep;
fullCircle = FALSE;
absSweep = fabs(a->sweep);
if (absSweep > (2 * uiPi)) fullCircle = TRUE;
else {
double aerDiff;
aerDiff = fabs(absSweep - (2 * uiPi));
fullCircle = aerDiff <= absSweep * aerMax;
}
if (fullCircle) {
a->sweep = uiPi;
drawArc(p, a, startFunction);
a->startAngle += uiPi;
drawArc(p, a, NULL);
return;
}
sinx = sin(a->startAngle);
cosx = cos(a->startAngle);
startX = a->xCenter + a->radius * cosx;
startY = a->yCenter + a->radius * sinx;
sinx = sin(a->startAngle + a->sweep);
cosx = cos(a->startAngle + a->sweep);
endX = a->xCenter + a->radius * cosx;
endY = a->yCenter + a->radius * sinx;
if (startFunction != NULL)
(*startFunction)(p, startX, startY);
as.point.x = endX;
as.point.y = endY;
as.size.width = a->radius;
as.size.height = a->radius;
as.rotationAngle = 0; if (a->negative)
as.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;
else
as.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE;
if (!a->negative)
if (a->sweep > uiPi)
as.arcSize = D2D1_ARC_SIZE_LARGE;
else
as.arcSize = D2D1_ARC_SIZE_SMALL;
else
if (a->sweep > uiPi)
as.arcSize = D2D1_ARC_SIZE_SMALL;
else
as.arcSize = D2D1_ARC_SIZE_LARGE;
p->sink->AddArc(&as);
}
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
{
struct arc a;
a.xCenter = xCenter;
a.yCenter = yCenter;
a.radius = radius;
a.startAngle = startAngle;
a.sweep = sweep;
a.negative = negative;
drawArc(p, &a, uiDrawPathNewFigure);
}
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
{
D2D1_POINT_2F pt;
pt.x = x;
pt.y = y;
p->sink->AddLine(pt);
}
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
{
struct arc a;
a.xCenter = xCenter;
a.yCenter = yCenter;
a.radius = radius;
a.startAngle = startAngle;
a.sweep = sweep;
a.negative = negative;
drawArc(p, &a, uiDrawPathLineTo);
}
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
{
D2D1_BEZIER_SEGMENT s;
s.point1.x = c1x;
s.point1.y = c1y;
s.point2.x = c2x;
s.point2.y = c2y;
s.point3.x = endX;
s.point3.y = endY;
p->sink->AddBezier(&s);
}
void uiDrawPathCloseFigure(uiDrawPath *p)
{
p->sink->EndFigure(D2D1_FIGURE_END_CLOSED);
p->inFigure = FALSE;
}
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
{
uiDrawPathNewFigure(p, x, y);
uiDrawPathLineTo(p, x + width, y);
uiDrawPathLineTo(p, x + width, y + height);
uiDrawPathLineTo(p, x, y + height);
uiDrawPathCloseFigure(p);
}
void uiDrawPathEnd(uiDrawPath *p)
{
HRESULT hr;
if (p->inFigure) {
p->sink->EndFigure(D2D1_FIGURE_END_OPEN);
p->inFigure = FALSE;
}
hr = p->sink->Close();
if (hr != S_OK)
logHRESULT(L"error closing path", hr);
p->sink->Release();
p->sink = NULL;
}
ID2D1PathGeometry *pathGeometry(uiDrawPath *p)
{
if (p->sink != NULL)
uiprivUserBug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p);
return p->path;
}