More Direct2D stuff. Not quite rendering yet...

This commit is contained in:
Pietro Gagliardi 2015-09-17 13:10:38 -04:00
parent 8dc72ddc5c
commit 873763451a
3 changed files with 101 additions and 271 deletions

View File

@ -16,7 +16,7 @@ struct uiArea {
ID2D1HwndRenderTarget *rt; ID2D1HwndRenderTarget *rt;
}; };
static HRESULT doPaint(uiArea *a, ID2DRenderTarget *rt, RECT *client, RECT *clip) static HRESULT doPaint(uiArea *a, ID2D1RenderTarget *rt, RECT *client, RECT *clip)
{ {
uiAreaHandler *ah = a->ah; uiAreaHandler *ah = a->ah;
uiAreaDrawParams dp; uiAreaDrawParams dp;
@ -526,8 +526,8 @@ static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
else { else {
if (GetClientRect(a->hwnd, &client) == 0) if (GetClientRect(a->hwnd, &client) == 0)
logLastError("error getting client rect for resizing Direct2D render target in areaWndProc()"); logLastError("error getting client rect for resizing Direct2D render target in areaWndProc()");
size.width = r.right - r.left; size.width = client.right - client.left;
size.height = r.bottom - r.top; size.height = client.bottom - client.top;
// don't track the error; we'll get that in EndDraw() // don't track the error; we'll get that in EndDraw()
// see https://msdn.microsoft.com/en-us/library/windows/desktop/dd370994%28v=vs.85%29.aspx // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd370994%28v=vs.85%29.aspx
ID2D1HwndRenderTarget_Resize(a->rt, &size); ID2D1HwndRenderTarget_Resize(a->rt, &size);

View File

@ -16,6 +16,6 @@ extern HRESULT logLastError(const char *);
extern HRESULT logHRESULT(const char *, HRESULT); extern HRESULT logHRESULT(const char *, HRESULT);
extern HRESULT logMemoryExhausted(const char *); extern HRESULT logMemoryExhausted(const char *);
extern uiDrawContext *newContext(HDC); extern uiDrawContext *newContext(ID2D1RenderTarget *);
extern void areaUpdateScroll(HWND); extern void areaUpdateScroll(HWND);

View File

@ -67,66 +67,81 @@ ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd)
} }
struct uiDrawContext { struct uiDrawContext {
HDC dc; ID2D1RenderTarget *rt;
ID2D1PathGeometry *path;
ID2D1GeometrySink *sink;
BOOL inFigure;
// source color // source color
BOOL useRGB; BOOL useRGB;
BOOL useAlpha;
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
uint8_t a; uint8_t a;
}; };
uiDrawContext *newContext(HDC dc) uiDrawContext *newContext(ID2D1RenderTarget *rt)
{ {
uiDrawContext *c; uiDrawContext *c;
// TODO use uiNew // TODO use uiNew
c = (uiDrawContext *) malloc(sizeof (uiDrawContext)); c = (uiDrawContext *) malloc(sizeof (uiDrawContext));
c->dc = dc; c->rt = rt;
return c; return c;
} }
void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b) void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b)
{ {
c->useRGB = TRUE; uiDrawBeginPathRGBA(c, r, g, b, 1);
c->useAlpha = FALSE;
c->r = r;
c->g = g;
c->b = b;
if (BeginPath(c->dc) == 0)
logLastError("error beginning new path in uiDrawBeginPathRGB()");
} }
void uiDrawBeginPathRGBA(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b, uint8_t a) void uiDrawBeginPathRGBA(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{ {
HRESULT hr;
c->useRGB = TRUE; c->useRGB = TRUE;
c->useAlpha = TRUE;
c->r = r; c->r = r;
c->g = g; c->g = g;
c->b = b; c->b = b;
c->a = a; c->a = a;
if (BeginPath(c->dc) == 0)
logLastError("error beginning new path in uiDrawBeginPathRGB()"); 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) void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y)
{ {
if (MoveToEx(c->dc, x, y, NULL) == 0) D2D1_POINT_2F p;
logLastError("error moving to point in uiDrawMoveTo()");
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) void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y)
{ {
if (LineTo(c->dc, x, y) == 0) D2D1_POINT_2F p;
logLastError("error drawing line in uiDrawLineTo()");
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) void uiDrawRectangle(uiDrawContext *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height)
{ {
if (Rectangle(c->dc, x, y, x + width, y + height) == 0) // TODO
logLastError("error drawing rectangle in uiDrawRectangle()");
} }
void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t radius, double startAngle, double endAngle, int lineFromCurrentPointToStart) void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t radius, double startAngle, double endAngle, int lineFromCurrentPointToStart)
@ -146,10 +161,13 @@ void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t
sy = yCenter - floor((double) radius * sin(startAngle)); sy = yCenter - floor((double) radius * sin(startAngle));
ex = xCenter + floor((double) radius * cos(endAngle)); ex = xCenter + floor((double) radius * cos(endAngle));
ey = yCenter - floor((double) radius * sin(endAngle)); ey = yCenter - floor((double) radius * sin(endAngle));
/* TODO
if (Arc(c->dc, bx, by, bx2, by2, sx, sy, ex, ey) == 0) if (Arc(c->dc, bx, by, bx2, by2, sx, sy, ex, ey) == 0)
logLastError("error drawing current point arc in uiDrawArc()"); logLastError("error drawing current point arc in uiDrawArc()");
return; return;
*/
} }
/* TODO
// AngleArc() expects degrees // AngleArc() expects degrees
startAngle *= (180.0 / M_PI); startAngle *= (180.0 / M_PI);
endAngle *= (180.0 / M_PI); endAngle *= (180.0 / M_PI);
@ -160,275 +178,87 @@ void uiDrawArcTo(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t
// the "sweep angle" is relative to the start angle, not to 0 // the "sweep angle" is relative to the start angle, not to 0
endAngle - startAngle) == 0) endAngle - startAngle) == 0)
logLastError("error drawing arc in uiDrawArc()"); 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) void uiDrawBezierTo(uiDrawContext *c, intmax_t c1x, intmax_t c1y, intmax_t c2x, intmax_t c2y, intmax_t endX, intmax_t endY)
{ {
POINT points[3]; // TODO
points[0].x = c1x;
points[0].y = c1y;
points[1].x = c2x;
points[1].y = c2y;
points[2].x = endX;
points[2].y = endY;
if (PolyBezierTo(c->dc, points, 3) == 0)
logLastError("error drawing bezier curve in uiDrawBezierTo()");
} }
void uiDrawCloseFigure(uiDrawContext *c) void uiDrawCloseFigure(uiDrawContext *c)
{ {
if (CloseFigure(c->dc) == 0) ID2D1GeometrySink_EndFigure(c->sink,
logLastError("error closing figure in uiDrawCloseFigure()"); D2D1_FIGURE_END_CLOSED);
c->inFigure = FALSE;
} }
static HPEN toPen(uiDrawContext *c, uiDrawStrokeParams *p) static ID2D1SolidColorBrush *makeBrush(uiDrawContext *c)
{ {
DWORD style; D2D1_COLOR_F color;
LOGBRUSH lb; D2D1_BRUSH_PROPERTIES props;
HPEN pen; ID2D1SolidColorBrush *brush;
HRESULT hr;
style = PS_GEOMETRIC; color.r = ((FLOAT) (c->r)) / 255;
switch (p->Cap) { color.g = ((FLOAT) (c->g)) / 255;
case uiDrawLineCapFlat: color.b = ((FLOAT) (c->b)) / 255;
style |= PS_ENDCAP_FLAT; color.a = ((FLOAT) (c->a)) / 255;
break;
case uiDrawLineCapRound:
style |= PS_ENDCAP_ROUND;
break;
case uiDrawLineCapSquare:
style |= PS_ENDCAP_SQUARE;
break;
}
switch (p->Join) {
case uiDrawLineJoinMiter:
style |= PS_JOIN_MITER;
break;
case uiDrawLineJoinRound:
style |= PS_JOIN_ROUND;
break;
case uiDrawLineJoinBevel:
style |= PS_JOIN_BEVEL;
break;
}
ZeroMemory(&lb, sizeof (LOGBRUSH));
if (c->useRGB) {
lb.lbStyle = BS_SOLID;
// we don't need to worry about alpha premultiplication; see below
lb.lbColor = RGB(c->r, c->g, c->b);
} else {
// TODO
}
pen = ExtCreatePen(style,
p->Thickness,
&lb,
0,
NULL);
if (pen == NULL)
logLastError("error creating pen in toPen()");
return pen;
}
// unfortunately alpha blending in GDI is limited to bitmap on bitmap ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
// to do alpha vector graphics, we have to fake it by drawing to an offscreen bitmap first props.opacity = 1.0;
// also we can't just use regions, we have to copy the path... // identity matrix
// we don't need to worry about alpha premultiplication; see below props.transform._11 = 1;
struct alpha { props.transform._22 = 1;
HDC dc;
HBITMAP bitmap;
VOID *ppvBits;
HBITMAP prevbitmap;
RECT r;
};
static void startAlpha(uiDrawContext *c, struct alpha *a) hr = ID2D1RenderTarget_CreateSolidColorBrush(c->rt,
{ &color,
int n; &props,
POINT *points; &brush);
BYTE *ops; if (hr != S_OK)
HRGN region; logHRESULT("error creating brush in makeBrush()", hr);
BITMAPINFO bi; return brush;
POINT prevWindowOrigin;
DWORD le;
ZeroMemory(a, sizeof (struct alpha));
// first, get the path
// we need to do this first because PathToRegion() clears the path
SetLastError(0);
n = GetPath(c->dc, NULL, NULL, 0);
if (n == 0) {
le = GetLastError();
SetLastError(le); // just in case
if (le != 0)
logLastError("error getting path point count in startAlpha()");
}
// TODO switch to uiAlloc
points = (POINT *) malloc(n * sizeof (POINT));
ops = (BYTE *) malloc(n * sizeof (BYTE));
if (GetPath(c->dc, points, ops, n) != n)
logLastError("error getting path in startAlpha()");
// next we need to know the bounding box of the path so we can create the bitmap
region = PathToRegion(c->dc);
if (region == NULL)
logLastError("error getting path region in startAlpha()");
if (GetRgnBox(region, &(a->r)) == 0)
logLastError("error getting region bounding box in startAlpha()");
if (DeleteObject(region) == 0)
logLastError("error deleting path region in startAlpha()");
// now create a DC compatible with the original that we can copy the path to and AlphaBlend() from
a->dc = CreateCompatibleDC(c->dc);
if (a->dc == NULL)
logLastError("error creating compatible DC in startAlpha()");
// now create and select in a *device-independent* bitmap that will hold the data that we're going to alpha blend
ZeroMemory(&bi, sizeof (BITMAPINFO));
bi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bi.bmiHeader.biWidth = a->r.right - a->r.left;
bi.bmiHeader.biHeight = -(a->r.bottom - a->r.top); // negative to draw top-down
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
a->bitmap = CreateDIBSection(c->dc, &bi, DIB_RGB_COLORS,
&(a->ppvBits), NULL, 0);
if (a->bitmap == NULL)
logLastError("error creating bitmap in startAlpha()");
a->prevbitmap = SelectObject(a->dc, a->bitmap);
if (a->prevbitmap == NULL)
logLastError("error selecting bitmap into DC in startAlpha()");
// now we can finally copy the path like we were going to earlier
// we need to change the window origin so the path draws in the right place
if (SetWindowOrgEx(a->dc, a->r.left, a->r.top, &prevWindowOrigin) == 0)
logLastError("error setting window origin in startAlpha()");
if (BeginPath(a->dc) == 0)
logLastError("error beginning path in startAlpha()");
if (PolyDraw(a->dc, points, ops, n) == 0)
logLastError("error copying path in startAlpha()");
if (EndPath(a->dc) == 0)
logLastError("error ending path in startAlpha()");
if (SetWindowOrgEx(a->dc, prevWindowOrigin.x, prevWindowOrigin.y, NULL) == 0)
logLastError("error resetting window origin in startAlpha()");
free(points);
free(ops);
}
static void finishAlpha(uiDrawContext *c, struct alpha *a)
{
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
// fortunately blending this is easy; we don't have to touch the pixels at all because BLENDFUNCTION has a way to take care of this...
bf.SourceConstantAlpha = c->a;
bf.AlphaFormat = 0; // no per-pixel apha
// a consequence of this setup is that the image data doesn't need to be alpha-premultiplied, because SourceConstantAlpha will be multiplied for us (according to the docs, anyway)
// TODO is this actually the case?
// and we can finally alpha-blend this!
if (AlphaBlend(c->dc, a->r.left, a->r.top,
a->r.right - a->r.left, a->r.bottom - a->r.top,
a->dc, 0, 0,
a->r.right - a->r.left, a->r.bottom - a->r.top,
bf) == FALSE)
logLastError("error alpha blending in finishAlpha()");
// and clean it all up
if (SelectObject(a->dc, a->prevbitmap) != a->bitmap)
logLastError("error deselecting bitmap in finishAlpha()");
if (DeleteObject(a->bitmap) == 0)
logLastError("error deleting object in finishAlpha()");
if (DeleteDC(a->dc) == 0)
logLastError("error deleting DC in finishAlpha()");
} }
void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p) void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p)
{ {
HPEN pen, prevpen; ID2D1SolidColorBrush *brush;
HDC strokeDC; HRESULT hr;
struct alpha alpha;
if (EndPath(c->dc) == 0) if (c->inFigure)
logLastError("error ending path in uiDrawStroke()"); ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_OPEN);
if (c->useAlpha) { hr = ID2D1GeometrySink_Close(c->sink);
startAlpha(c, &alpha); if (hr != S_OK)
strokeDC = alpha.dc; logHRESULT("error closing geometry sink in uiDrawStroke()", hr);
} else // no alpha; just stroke directly brush = makeBrush(c);
strokeDC = c->dc; ID2D1RenderTarget_DrawGeometry(c->rt,
(ID2D1Geometry *) (c->path),
pen = toPen(c, p); (ID2D1Brush *) brush,
if (p->Join == uiDrawLineJoinMiter) p->Thickness,
if (SetMiterLimit(strokeDC, p->MiterLimit, NULL) == 0) NULL);
logLastError("error setting miter limit in uiDrawStroke()"); ID2D1SolidColorBrush_Release(brush);
prevpen = SelectObject(strokeDC, pen); ID2D1GeometrySink_Release(c->sink);
if (prevpen == NULL) ID2D1PathGeometry_Release(c->path);
logLastError("error selecting pen into DC in uiDrawStroke()");
if (StrokePath(strokeDC) == 0)
logLastError("error stroking path in uiDrawStroke()");
if (SelectObject(strokeDC, prevpen) != pen)
logLastError("error deselecting pen from DC in uiDrawStroke()");
if (DeleteObject(pen) == 0)
logLastError("error deleting pen in uiDrawStroke()");
if (c->useAlpha)
finishAlpha(c, &alpha);
}
static HBRUSH toBrush(uiDrawContext *c)
{
HBRUSH brush;
if (c->useRGB) {
brush = CreateSolidBrush(RGB(c->r, c->g, c->b));
if (brush == NULL)
logLastError("error creating solid brush in toBrush()");
return brush;
}
// TODO
return NULL;
} }
void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode) void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode)
{ {
HBRUSH brush, prevbrush; ID2D1SolidColorBrush *brush;
HDC fillDC; HRESULT hr;
struct alpha alpha;
int pfm;
if (EndPath(c->dc) == 0) if (c->inFigure)
logLastError("error ending path in uiDrawFill()"); ID2D1GeometrySink_EndFigure(c->sink,
D2D1_FIGURE_END_OPEN);
if (c->useAlpha) { hr = ID2D1GeometrySink_Close(c->sink);
startAlpha(c, &alpha); if (hr != S_OK)
fillDC = alpha.dc; logHRESULT("error closing geometry sink in uiDrawStroke()", hr);
} else // no alpha; just stroke directly brush = makeBrush(c);
fillDC = c->dc; ID2D1RenderTarget_FillGeometry(c->rt,
(ID2D1Geometry *) (c->path),
switch (mode) { (ID2D1Brush *) brush,
case uiDrawFillModeWinding: NULL);
pfm = WINDING; ID2D1SolidColorBrush_Release(brush);
break; ID2D1GeometrySink_Release(c->sink);
case uiDrawFillModeAlternate: ID2D1PathGeometry_Release(c->path);
pfm = ALTERNATE;
break;
}
if (SetPolyFillMode(fillDC, pfm) == 0)
logLastError("error setting fill mode in uiDrawFill()");
brush = toBrush(c);
prevbrush = SelectObject(fillDC, brush);
if (prevbrush == NULL)
logLastError("error selecting brush into DC in uiDrawFill()");
if (FillPath(fillDC) == 0)
logLastError("error filling path in uiDrawFill()");
if (SelectObject(fillDC, prevbrush) != brush)
logLastError("error deselecting brush from DC in uiDrawFill()");
if (DeleteObject(brush) == 0)
logLastError("error deleting pen in uiDrawStroke()");
if (c->useAlpha)
finishAlpha(c, &alpha);
} }