libui/winarea_d2d/draw.c

265 lines
6.6 KiB
C

// 7 september 2015
#include "area.h"
static ID2D1Factory *d2dfactory = NULL;
HRESULT initDraw(void)
{
D2D1_FACTORY_OPTIONS opts;
ZeroMemory(&opts, sizeof (D2D1_FACTORY_OPTIONS));
// TODO make this an option
opts.debugLevel = D2D1_DEBUG_LEVEL_NONE;
return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
&IID_ID2D1Factory,
&opts,
(void **) (&d2dfactory));
}
void uninitDraw(void)
{
ID2D1Factory_Release(d2dfactory);
}
ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd)
{
D2D1_RENDER_TARGET_PROPERTIES props;
D2D1_HWND_RENDER_TARGET_PROPERTIES hprops;
HDC dc;
RECT r;
ID2D1HwndRenderTarget *rt;
HRESULT hr;
// we need a DC for the DPI
// we *could* just use the screen DPI but why when we have a window handle and its DC has a DPI
dc = GetDC(hwnd);
if (dc == NULL)
logLastError("error getting DC to find DPI in makeHWNDRenderTarget()");
ZeroMemory(&props, sizeof (D2D1_RENDER_TARGET_PROPERTIES));
props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
props.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
props.dpiX = GetDeviceCaps(dc, LOGPIXELSX);
props.dpiY = GetDeviceCaps(dc, LOGPIXELSY);
props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
if (ReleaseDC(hwnd, dc) == 0)
logLastError("error releasing DC for finding DPI in makeHWNDRenderTarget()");
if (GetClientRect(hwnd, &r) == 0)
logLastError("error getting current size of window in makeHWNDRenderTarget()");
ZeroMemory(&hprops, sizeof (D2D1_HWND_RENDER_TARGET_PROPERTIES));
hprops.hwnd = hwnd;
hprops.pixelSize.width = r.right - r.left;
hprops.pixelSize.height = r.bottom - r.top;
hprops.presentOptions = D2D1_PRESENT_OPTIONS_NONE;
hr = ID2D1Factory_CreateHwndRenderTarget(d2dfactory,
&props,
&hprops,
&rt);
if (hr != S_OK)
logHRESULT("error creating area HWND render target in makeHWNDRenderTarget()", hr);
return rt;
}
struct uiDrawContext {
ID2D1RenderTarget *rt;
ID2D1PathGeometry *path;
ID2D1GeometrySink *sink;
BOOL inFigure;
// source color
BOOL useRGB;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
uiDrawContext *newContext(ID2D1RenderTarget *rt)
{
uiDrawContext *c;
// TODO use uiNew
c = (uiDrawContext *) malloc(sizeof (uiDrawContext));
c->rt = rt;
return c;
}
void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b)
{
uiDrawBeginPathRGBA(c, r, g, b, 1);
}
void uiDrawBeginPathRGBA(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
HRESULT hr;
c->useRGB = TRUE;
c->r = r;
c->g = g;
c->b = b;
c->a = a;
hr = ID2D1Factory_CreatePathGeometry(d2dfactory, &(c->path));
if (hr != S_OK)
logHRESULT("error creating path in uiDrawBeginPathRGBA()", hr);
hr = ID2D1PathGeometry_Open(c->path, &(c->sink));
if (hr != S_OK)
logHRESULT("error opening path sink in uiDrawBeginPathRGBA()", hr);
c->inFigure = FALSE;
}
void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y)
{
D2D1_POINT_2F p;
if (c->inFigure)
ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_OPEN);
p.x = ((FLOAT) x) + 0.5;
p.y = ((FLOAT) y) + 0.5;
ID2D1GeometrySink_BeginFigure(c->sink,
p,
D2D1_FIGURE_BEGIN_FILLED);
c->inFigure = TRUE;
}
void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y)
{
D2D1_POINT_2F p;
p.x = ((FLOAT) x) + 0.5;
p.y = ((FLOAT) y) + 0.5;
ID2D1GeometrySink_AddLine(c->sink, p);
}
void uiDrawRectangle(uiDrawContext *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height)
{
// TODO
}
void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t radius, double startAngle, double endAngle, int lineFromCurrentPointToStart)
{
if (!lineFromCurrentPointToStart) {
int bx, by, bx2, by2;
int sx, sy, ex, ey;
// see http://stackoverflow.com/questions/32465446/how-do-i-inhibit-the-initial-line-segment-of-an-anglearc
// the idea for floor(x + 0.5) is inspired by wine
// TODO make sure this is an accurate imitation of AngleArc()
bx = xCenter - radius;
by = yCenter - radius;
bx2 = xCenter + radius;
by2 = yCenter + radius;
sx = xCenter + floor((double) radius * cos(startAngle));
sy = yCenter - floor((double) radius * sin(startAngle));
ex = xCenter + floor((double) radius * cos(endAngle));
ey = yCenter - floor((double) radius * sin(endAngle));
/* TODO
if (Arc(c->dc, bx, by, bx2, by2, sx, sy, ex, ey) == 0)
logLastError("error drawing current point arc in uiDrawArc()");
return;
*/
}
/* TODO
// AngleArc() expects degrees
startAngle *= (180.0 / M_PI);
endAngle *= (180.0 / M_PI);
if (AngleArc(c->dc,
xCenter, yCenter,
radius,
startAngle,
// the "sweep angle" is relative to the start angle, not to 0
endAngle - startAngle) == 0)
logLastError("error drawing arc in uiDrawArc()");
*/
}
void uiDrawBezierTo(uiDrawContext *c, intmax_t c1x, intmax_t c1y, intmax_t c2x, intmax_t c2y, intmax_t endX, intmax_t endY)
{
// TODO
}
void uiDrawCloseFigure(uiDrawContext *c)
{
ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_CLOSED);
c->inFigure = FALSE;
}
static ID2D1SolidColorBrush *makeBrush(uiDrawContext *c)
{
D2D1_COLOR_F color;
D2D1_BRUSH_PROPERTIES props;
ID2D1SolidColorBrush *brush;
HRESULT hr;
color.r = ((FLOAT) (c->r)) / 255;
color.g = ((FLOAT) (c->g)) / 255;
color.b = ((FLOAT) (c->b)) / 255;
color.a = ((FLOAT) (c->a)) / 255;
ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
props.opacity = 1.0;
// identity matrix
props.transform._11 = 1;
props.transform._22 = 1;
hr = ID2D1RenderTarget_CreateSolidColorBrush(c->rt,
&color,
&props,
&brush);
if (hr != S_OK)
logHRESULT("error creating brush in makeBrush()", hr);
return brush;
}
void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p)
{
ID2D1SolidColorBrush *brush;
HRESULT hr;
if (c->inFigure)
ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_OPEN);
hr = ID2D1GeometrySink_Close(c->sink);
if (hr != S_OK)
logHRESULT("error closing geometry sink in uiDrawStroke()", hr);
brush = makeBrush(c);
ID2D1RenderTarget_DrawGeometry(c->rt,
(ID2D1Geometry *) (c->path),
(ID2D1Brush *) brush,
p->Thickness,
NULL);
ID2D1SolidColorBrush_Release(brush);
ID2D1GeometrySink_Release(c->sink);
ID2D1PathGeometry_Release(c->path);
}
void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode)
{
ID2D1SolidColorBrush *brush;
HRESULT hr;
if (c->inFigure)
ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_OPEN);
hr = ID2D1GeometrySink_Close(c->sink);
if (hr != S_OK)
logHRESULT("error closing geometry sink in uiDrawStroke()", hr);
brush = makeBrush(c);
ID2D1RenderTarget_FillGeometry(c->rt,
(ID2D1Geometry *) (c->path),
(ID2D1Brush *) brush,
NULL);
ID2D1SolidColorBrush_Release(brush);
ID2D1GeometrySink_Release(c->sink);
ID2D1PathGeometry_Release(c->path);
}