Finished the Windows draw.c.
This commit is contained in:
parent
717e8dbc2d
commit
436290d4ed
|
@ -56,7 +56,6 @@ enum uiDrawLineJoin {
|
||||||
#define uiDrawDefaultMiterLimit 10.0
|
#define uiDrawDefaultMiterLimit 10.0
|
||||||
|
|
||||||
// TODOs
|
// TODOs
|
||||||
// - windows: SetPolyFillMode
|
|
||||||
// - os x: FillPath/EOFillPath functions
|
// - os x: FillPath/EOFillPath functions
|
||||||
enum uiDrawFillMode {
|
enum uiDrawFillMode {
|
||||||
uiDrawFillModeWinding,
|
uiDrawFillModeWinding,
|
||||||
|
|
261
winarea/draw.c
261
winarea/draw.c
|
@ -2,14 +2,7 @@
|
||||||
#include "area.h"
|
#include "area.h"
|
||||||
|
|
||||||
struct uiDrawContext {
|
struct uiDrawContext {
|
||||||
HDC mainDC;
|
HDC dc;
|
||||||
|
|
||||||
// when drawing in RGBA mode, we need to use this
|
|
||||||
HDC alphaDC;
|
|
||||||
HBITMAP alphaBitmap;
|
|
||||||
|
|
||||||
// and this is the DC we actually draw to, either mainDC or alphaDC
|
|
||||||
HDC currentDC;
|
|
||||||
|
|
||||||
// source color
|
// source color
|
||||||
BOOL useRGB;
|
BOOL useRGB;
|
||||||
|
@ -25,7 +18,7 @@ uiDrawContext *newContext(HDC dc)
|
||||||
uiDrawContext *c;
|
uiDrawContext *c;
|
||||||
|
|
||||||
c = (uiDrawContext *) g_malloc0(sizeof (uiDrawContext));
|
c = (uiDrawContext *) g_malloc0(sizeof (uiDrawContext));
|
||||||
c->mainDC = dc;
|
c->dc = dc;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,31 +29,37 @@ void uiDrawBeginPathRGB(uiDrawContext *c, uint8_t r, uint8_t g, uint8_t b)
|
||||||
c->r = r;
|
c->r = r;
|
||||||
c->g = g;
|
c->g = g;
|
||||||
c->b = b;
|
c->b = b;
|
||||||
c->currentDC = c->mainDC;
|
if (BeginPath(c->dc) == 0)
|
||||||
if (BeginPath(c->currentDC) == 0)
|
|
||||||
logLastError("error beginning new path in uiDrawBeginPathRGB()");
|
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)
|
||||||
{
|
{
|
||||||
// TODO
|
c->useRGB = TRUE;
|
||||||
|
c->useAlpha = TRUE;
|
||||||
|
c->r = r;
|
||||||
|
c->g = g;
|
||||||
|
c->b = b;
|
||||||
|
c->a = a;
|
||||||
|
if (BeginPath(c->dc) == 0)
|
||||||
|
logLastError("error beginning new path in uiDrawBeginPathRGB()");
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y)
|
void uiDrawMoveTo(uiDrawContext *c, intmax_t x, intmax_t y)
|
||||||
{
|
{
|
||||||
if (MoveToEx(c->currentDC, x, y, NULL) == 0)
|
if (MoveToEx(c->dc, x, y, NULL) == 0)
|
||||||
logLastError("error moving to point in uiDrawMoveTo()");
|
logLastError("error moving to point in uiDrawMoveTo()");
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y)
|
void uiDrawLineTo(uiDrawContext *c, intmax_t x, intmax_t y)
|
||||||
{
|
{
|
||||||
if (LineTo(c->currentDC, x, y) == 0)
|
if (LineTo(c->dc, x, y) == 0)
|
||||||
logLastError("error drawing line in uiDrawLineTo()");
|
logLastError("error drawing line in uiDrawLineTo()");
|
||||||
}
|
}
|
||||||
|
|
||||||
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->currentDC, x, y, x + width, y + height) == 0)
|
if (Rectangle(c->dc, x, y, x + width, y + height) == 0)
|
||||||
logLastError("error drawing rectangle in uiDrawRectangle()");
|
logLastError("error drawing rectangle in uiDrawRectangle()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ void uiDrawArc(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t ra
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
// TODO convert radians to degrees
|
// TODO convert radians to degrees
|
||||||
if (AngleArc(c->currentDC,
|
if (AngleArc(c->dc,
|
||||||
xCenter, yCenter
|
xCenter, yCenter
|
||||||
radius,
|
radius,
|
||||||
startAngle,
|
startAngle,
|
||||||
|
@ -79,42 +78,266 @@ void uiDrawArc(uiDrawContext *c, intmax_t xCenter, intmax_t yCenter, intmax_t ra
|
||||||
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)
|
||||||
|
{
|
||||||
|
POINT points[3];
|
||||||
|
|
||||||
|
points[0].x = c1x;
|
||||||
|
points[0].y = c1y;
|
||||||
|
points[1].x = c2x;
|
||||||
|
points[2].y = c2y;
|
||||||
|
points[3].x = endX;
|
||||||
|
points[3].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->currentDC) == 0)
|
if (CloseFigure(c->currentDC) == 0)
|
||||||
logLastError("error closing figure in uiDrawCloseFigure()");
|
logLastError("error closing figure in uiDrawCloseFigure()");
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDrawStroke(uiDrawContext *c, uiDrawStrokeParams *p)
|
static HPEN toPen(uiDrawContext *c, uiDrawStrokeParams *p)
|
||||||
{
|
{
|
||||||
// TODO
|
DWORD style;
|
||||||
|
LOGBRUSH lb;
|
||||||
|
HPEN pen;
|
||||||
|
|
||||||
|
style = PS_GEOMETRIC;
|
||||||
switch (p->Cap) {
|
switch (p->Cap) {
|
||||||
case uiDrawLineCapFlat:
|
case uiDrawLineCapFlat:
|
||||||
|
style |= PS_ENDCAP_FLAT;
|
||||||
break;
|
break;
|
||||||
case uiDrawLineCapRound:
|
case uiDrawLineCapRound:
|
||||||
|
style |= PS_ENDCAP_ROUND;
|
||||||
break;
|
break;
|
||||||
case uiDrawLineCapSquare:
|
case uiDrawLineCapSquare:
|
||||||
|
style |= PS_ENDCAP_SQUARE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (p->Join) {
|
switch (p->Join) {
|
||||||
case uiDrawLineJoinMiter:
|
case uiDrawLineJoinMiter:
|
||||||
|
style |= PS_JOIN_MITER;
|
||||||
break;
|
break;
|
||||||
case uiDrawLineJoinRound:
|
case uiDrawLineJoinRound:
|
||||||
|
style |= PS_JOIN_ROUND;
|
||||||
break;
|
break;
|
||||||
case uiDrawLineJoinBevel:
|
case uiDrawLineJoinBevel:
|
||||||
|
style |= PS_JOIN_BEVEL;
|
||||||
break;
|
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
|
||||||
|
// to do alpha vector graphics, we have to fake it by drawing to an offscreen bitmap first
|
||||||
|
// also we can't just use regions, we have to copy the path...
|
||||||
|
// we don't need to worry about alpha premultiplication; see below
|
||||||
|
struct alpha {
|
||||||
|
HDC dc;
|
||||||
|
HBITMAP bitmap;
|
||||||
|
VOID *ppvBits;
|
||||||
|
HBITMAP prevbitmap;
|
||||||
|
RECT r;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void startAlpha(uiDrawContext *c, struct alpha *a)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
POINT *points;
|
||||||
|
BYTE *ops;
|
||||||
|
HRGN region;
|
||||||
|
BITMAPINFO bi;
|
||||||
|
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(&bmi, 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
|
||||||
|
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()");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
HPEN pen, prevpen;
|
||||||
|
HDC strokeDC;
|
||||||
|
struct alpha alpha;
|
||||||
|
|
||||||
|
if (EndPath(c->dc) == 0)
|
||||||
|
logLastError("error ending path in uiDrawStroke()");
|
||||||
|
|
||||||
|
if (c->useAlpha) {
|
||||||
|
startAlpha(c, &alpha);
|
||||||
|
strokeDC = alpha.dc;
|
||||||
|
} else // no alpha; just stroke directly
|
||||||
|
strokeDC = c->dc;
|
||||||
|
|
||||||
|
pen = toPen(c, p);
|
||||||
|
if (p->Join == uiDrawLineJoinMiter)
|
||||||
|
if (SetMiterLimit(strokeDC, p->MiterLimit, NULL) == 0)
|
||||||
|
logLastError("error setting miter limit in uiDrawStroke()");
|
||||||
|
prevpen = SelectObject(strokeDC, pen);
|
||||||
|
if (prevpen == NULL)
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode)
|
void uiDrawFill(uiDrawContext *c, uiDrawFillMode mode)
|
||||||
{
|
{
|
||||||
// TODO
|
HBRUSH brush, prevbrush;
|
||||||
|
HDC fillDC;
|
||||||
|
struct alpha alpha;
|
||||||
|
int pfm;
|
||||||
|
|
||||||
|
if (EndPath(c->dc) == 0)
|
||||||
|
logLastError("error ending path in uiDrawFill()");
|
||||||
|
|
||||||
|
if (c->useAlpha) {
|
||||||
|
startAlpha(c, &alpha);
|
||||||
|
fillDC = alpha.dc;
|
||||||
|
} else // no alpha; just stroke directly
|
||||||
|
fillDC = c->dc;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case uiDrawFillModeWinding:
|
case uiDrawFillModeWinding:
|
||||||
|
pfm = WINDING;
|
||||||
break;
|
break;
|
||||||
case uiDrawFillModeAlternate:
|
case uiDrawFillModeAlternate:
|
||||||
|
pfm = ALTERNATE;
|
||||||
break;
|
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(strokeDC, 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ enum uiDrawLineJoin {
|
||||||
#define uiDrawDefaultMiterLimit 10.0
|
#define uiDrawDefaultMiterLimit 10.0
|
||||||
|
|
||||||
// TODOs
|
// TODOs
|
||||||
// - windows: SetPolyFillMode
|
|
||||||
// - os x: FillPath/EOFillPath functions
|
// - os x: FillPath/EOFillPath functions
|
||||||
enum uiDrawFillMode {
|
enum uiDrawFillMode {
|
||||||
uiDrawFillModeWinding,
|
uiDrawFillModeWinding,
|
||||||
|
|
Loading…
Reference in New Issue