Util: Add mPainterDrawLine

This commit is contained in:
Vicki Pfau 2023-07-24 21:00:47 -07:00
parent 4ca8ffe3f4
commit ba49175697
3 changed files with 295 additions and 1 deletions

View File

@ -136,6 +136,7 @@ void mImageCompositeWithAlpha(struct mImage* image, const struct mImage* source,
void mPainterInit(struct mPainter*, struct mImage* backing); void mPainterInit(struct mPainter*, struct mImage* backing);
void mPainterDrawRectangle(struct mPainter*, int x, int y, int width, int height); void mPainterDrawRectangle(struct mPainter*, int x, int y, int width, int height);
void mPainterDrawLine(struct mPainter*, int x1, int y1, int x2, int y2);
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to); uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to);
uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to); uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to);

View File

@ -614,6 +614,75 @@ void mPainterDrawRectangle(struct mPainter* painter, int x, int y, int width, in
} }
} }
void mPainterDrawLine(struct mPainter* painter, int x1, int y1, int x2, int y2) {
if (!painter->strokeWidth) {
return;
}
int dx = x2 - x1;
int dy = y2 - y1;
int x, y;
int xi = 1;
int yi = 1;
int residual;
int mx = dx;
int my = dy;
if (mx < 0) {
mx = -mx;
}
if (my < 0) {
my = -my;
}
if (dx < 0) {
xi = -1;
dx = -dx;
}
if (dy < 0) {
yi = -1;
dy = -dy;
}
unsigned i;
uint32_t color = painter->strokeColor;
if (mx > my) {
residual = 2 * dy - dx;
y = y1;
for (x = x1; x != x2 + xi; x += xi) {
for (i = 0; i < painter->strokeWidth; ++i) {
if (painter->blend) {
color = mColorMixARGB8(painter->strokeColor, mImageGetPixel(painter->backing, x, y - painter->strokeWidth / 2 + i));
}
mImageSetPixel(painter->backing, x, y - painter->strokeWidth / 2 + i, color);
}
if (residual > 0) {
y += yi;
residual -= 2 * dx;
}
residual += 2 * dy;
}
} else {
residual = 2 * dx - dy;
x = x1;
for (y = y1; y != y2 + yi; y += yi) {
for (i = 0; i < painter->strokeWidth; ++i) {
if (painter->blend) {
color = mColorMixARGB8(painter->strokeColor, mImageGetPixel(painter->backing, x - painter->strokeWidth / 2 + i, y));
}
mImageSetPixel(painter->backing, x - painter->strokeWidth / 2 + i, y, color);
}
if (residual > 0) {
x += xi;
residual -= 2 * dy;
}
residual += 2 * dx;
}
}
// TODO: Draw endcaps for widths >2
}
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to) { uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to) {
if (from == to) { if (from == to) {
return color; return color;

View File

@ -1568,7 +1568,7 @@ M_TEST_DEFINE(painterStrokeRectangleBlend) {
} }
M_TEST_DEFINE(painterDrawRectangle) { M_TEST_DEFINE(painterDrawRectangle) {
struct mImage* image; struct mImage* image;
struct mPainter painter; struct mPainter painter;
image = mImageCreate(3, 3, mCOLOR_ARGB8); image = mImageCreate(3, 3, mCOLOR_ARGB8);
@ -1628,6 +1628,227 @@ M_TEST_DEFINE(painterDrawRectangle) {
mImageDestroy(image); mImageDestroy(image);
} }
M_TEST_DEFINE(painterDrawLineOctants) {
struct mImage* image;
struct mPainter painter;
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 0, 2, 2);
COMPARE3X(0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00,
0x00, 0x00, 0xFF);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 2, 2, 0, 0);
COMPARE3X(0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00,
0x00, 0x00, 0xFF);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 2, 0, 0, 2);
COMPARE3X(0x00, 0x00, 0xFF,
0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 2, 2, 0);
COMPARE3X(0x00, 0x00, 0xFF,
0x00, 0xFF, 0x00,
0xFF, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 0, 2, 1);
COMPARE3X(0xFF, 0xFF, 0x00,
0x00, 0x00, 0xFF,
0x00, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 2, 1, 0, 0);
COMPARE3X(0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 0, 1, 2);
COMPARE3X(0xFF, 0x00, 0x00,
0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 1, 2, 0, 0);
COMPARE3X(0xFF, 0x00, 0x00,
0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00);
mImageDestroy(image);
}
M_TEST_DEFINE(painterDrawLineWidth) {
struct mImage* image;
struct mPainter painter;
image = mImageCreate(4, 4, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 2;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 0, 3, 3);
COMPARE4X(0xFF, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0xFF, 0xFF);
mImageDestroy(image);
image = mImageCreate(4, 4, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 3;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 0, 3, 3);
COMPARE4X(0xFF, 0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0x00,
0x00, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF);
mImageDestroy(image);
image = mImageCreate(4, 4, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 2;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 3, 0, 0, 3);
COMPARE4X(0x00, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0x00, 0x00,
0xFF, 0x00, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(4, 4, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 3;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 3, 0, 0, 3);
COMPARE4X(0x00, 0x00, 0xFF, 0xFF,
0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 2;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 1, 0, 1, 2);
COMPARE3X(0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0x00,
0xFF, 0xFF, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 3;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 1, 0, 1, 2);
COMPARE3X(0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 2;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 1, 2, 1);
COMPARE3X(0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_XRGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 3;
painter.strokeColor = 0xFF0000FF;
mPainterDrawLine(&painter, 0, 1, 2, 1);
COMPARE3X(0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF);
mImageDestroy(image);
}
M_TEST_DEFINE(painterDrawLineBlend) {
struct mImage* image;
struct mPainter painter;
image = mImageCreate(3, 3, mCOLOR_ARGB8);
mPainterInit(&painter, image);
painter.blend = false;
painter.strokeWidth = 1;
painter.strokeColor = 0x400000FF;
mPainterDrawLine(&painter, 0, 0, 2, 2);
painter.strokeColor = 0x4000FF00;
mPainterDrawLine(&painter, 0, 2, 2, 0);
COMPARE3(0x400000FF, 0x00000000, 0x4000FF00,
0x00000000, 0x4000FF00, 0x00000000,
0x4000FF00, 0x00000000, 0x400000FF);
mImageDestroy(image);
image = mImageCreate(3, 3, mCOLOR_ARGB8);
mPainterInit(&painter, image);
painter.blend = true;
painter.strokeWidth = 1;
painter.strokeColor = 0x400000FF;
mPainterDrawLine(&painter, 0, 0, 2, 2);
painter.strokeColor = 0x4000FF00;
mPainterDrawLine(&painter, 0, 2, 2, 0);
COMPARE3(0x400000FF, 0x00000000, 0x4000FF00,
0x00000000, 0x6F00916D, 0x00000000,
0x4000FF00, 0x00000000, 0x400000FF);
mImageDestroy(image);
}
#undef COMPARE3X #undef COMPARE3X
#undef COMPARE3 #undef COMPARE3
#undef COMPARE4X #undef COMPARE4X
@ -1663,4 +1884,7 @@ M_TEST_SUITE_DEFINE(Image,
cmocka_unit_test(painterStrokeRectangleWidth), cmocka_unit_test(painterStrokeRectangleWidth),
cmocka_unit_test(painterStrokeRectangleBlend), cmocka_unit_test(painterStrokeRectangleBlend),
cmocka_unit_test(painterDrawRectangle), cmocka_unit_test(painterDrawRectangle),
cmocka_unit_test(painterDrawLineOctants),
cmocka_unit_test(painterDrawLineWidth),
cmocka_unit_test(painterDrawLineBlend),
) )