Skip to content

Commit 5298937

Browse files
authored
Added support for filling rounded rectangles (#2398)
1 parent e8aa94e commit 5298937

File tree

2 files changed

+173
-20
lines changed

2 files changed

+173
-20
lines changed

src/nanoFramework.Graphics/Graphics/Core/Graphics.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ struct GraphicsDriver
689689
GFX_Pen &pen,
690690
GFX_Brush &brush,
691691
const GFX_Rect &rectangle);
692+
static void FillRectangleNative(const PAL_GFX_Bitmap &bitmap, GFX_Brush &brush, const GFX_Rect &rectangle);
692693
static void DrawRoundedRectangleNative(
693694
const PAL_GFX_Bitmap &bitmap,
694695
GFX_Pen &pen,
@@ -731,6 +732,14 @@ struct GraphicsDriver
731732

732733
static void DrawBresLineNative(const PAL_GFX_Bitmap &bitmap, int x0, int y0, int x1, int y1, GFX_Pen &pen);
733734

735+
static void DrawScanlineNative(
736+
const PAL_GFX_Bitmap &bitmap,
737+
int x1,
738+
int x2,
739+
int y,
740+
CLR_UINT32 color,
741+
CLR_UINT16 opacity);
742+
734743
static CLR_UINT32 NativeColorInterpolate(CLR_UINT32 colorTo, CLR_UINT32 colorFrom, CLR_UINT16 scalar);
735744

736745
__inline static CLR_UINT8 NativeColorRValue(CLR_UINT32 color)
@@ -818,6 +827,12 @@ struct GraphicsDriver
818827

819828
static void Draw4PointsEllipse(const PAL_GFX_Bitmap &bitmap, int offsetX, int offsetY, void *params);
820829
static void Draw4PointsRoundedRect(const PAL_GFX_Bitmap &bitmap, int offsetX, int offsetY, void *params);
830+
static void Fill4PointLinesRoundedRect(const PAL_GFX_Bitmap &bitmap, int offsetX, int offsetY, void *params);
831+
static void GradientFill4PointLinesRoundedRect(
832+
const PAL_GFX_Bitmap &bitmap,
833+
int offsetX,
834+
int offsetY,
835+
void *params);
821836
};
822837

823838
// The PAL Graphics API uses the 24bit BGR color space, the one that's used for the

src/nanoFramework.Graphics/Graphics/Core/GraphicsDriver.cpp

Lines changed: 158 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,6 @@ void GraphicsDriver::DrawRectangleNative(
247247

248248
int stride = GetWidthInWords(bitmap.width) * 2;
249249

250-
const CLR_UINT16 opacity = brush.opacity;
251-
252250
int xSrc = 0, ySrc = 0;
253251
// If the outset rect is completely outside of the drawing region, we can safely return (inset rect is always inside
254252
// the outset rect)
@@ -290,6 +288,37 @@ void GraphicsDriver::DrawRectangleNative(
290288
}
291289
}
292290

291+
// Fill
292+
if (brush.opacity != PAL_GFX_Bitmap::c_OpacityTransparent)
293+
{
294+
GFX_Rect insetRect;
295+
296+
insetRect.left = insetX;
297+
insetRect.top = insetY;
298+
insetRect.right = insetX + insetWidth - 1;
299+
insetRect.bottom = insetY + insetHeight - 1;
300+
301+
GraphicsDriver::FillRectangleNative(bitmap, brush, insetRect);
302+
}
303+
}
304+
305+
void GraphicsDriver::FillRectangleNative(const PAL_GFX_Bitmap &bitmap, GFX_Brush &brush, const GFX_Rect &rectangle)
306+
{
307+
int x = rectangle.left;
308+
int y = rectangle.top;
309+
int width = rectangle.Width();
310+
int height = rectangle.Height();
311+
312+
int xSrc = 0, ySrc = 0;
313+
if (ClipToVisible(bitmap, x, y, width, height, NULL, xSrc, ySrc) == false)
314+
{
315+
return;
316+
}
317+
318+
int stride = GetWidthInWords(bitmap.width) * 2;
319+
320+
const CLR_UINT16 opacity = brush.opacity;
321+
293322
// Fills (Gradient / Translucent / Solid)
294323
if (opacity != 0)
295324
{
@@ -301,9 +330,9 @@ void GraphicsDriver::DrawRectangleNative(
301330
// Solid fill (including Translucent fill)
302331
CLR_UINT32 fillColor = brush.gradientStartColor;
303332

304-
if (insetWidth > 0 && insetHeight > 0)
333+
if (width > 0 && height > 0)
305334
{
306-
CLR_UINT16 *curRow = ((CLR_UINT16 *)bitmap.data) + insetY * stride + insetX;
335+
CLR_UINT16 *curRow = ((CLR_UINT16 *)bitmap.data) + y * stride + x;
307336
CLR_UINT16 *curPixel = curRow;
308337

309338
if (opacity == PAL_GFX_Bitmap::c_OpacityOpaque)
@@ -313,33 +342,36 @@ void GraphicsDriver::DrawRectangleNative(
313342
CLR_UINT16 *startRow = curRow;
314343

315344
// Draw the first row
316-
for (int col = 0; col < insetWidth; col++, curPixel++)
345+
for (int col = 0; col < width; col++, curPixel++)
317346
{
318347
*curPixel = fillColor;
319348
}
320349

321350
// Just memcpy the first row to all subsequent rows, which is moderately faster
322-
for (int row = 1; row < insetHeight; row++)
351+
for (int row = 1; row < height; row++)
323352
{
324353
curRow += stride;
325354

326-
memcpy(curRow, startRow, insetWidth * 2);
355+
memcpy(curRow, startRow, width * 2);
327356
}
328357
}
329358
else
330359
{
331360
CLR_UINT16 lastPixel = *curPixel;
332361
CLR_UINT16 interpolated = g_GraphicsDriver.NativeColorInterpolate(fillColor, lastPixel, opacity);
333-
for (int row = 0; row < insetHeight; row++, curRow += stride)
362+
363+
for (int row = 0; row < height; row++, curRow += stride)
334364
{
335365
curPixel = curRow;
336-
for (int col = 0; col < insetWidth; col++, curPixel++)
366+
367+
for (int col = 0; col < width; col++, curPixel++)
337368
{
338369
if (*curPixel != lastPixel)
339370
{
340371
lastPixel = *curPixel;
341372
interpolated = g_GraphicsDriver.NativeColorInterpolate(fillColor, lastPixel, opacity);
342373
}
374+
343375
*curPixel = interpolated;
344376
}
345377
}
@@ -375,7 +407,7 @@ void GraphicsDriver::DrawRectangleNative(
375407

376408
const int LIMIT = 1 << 12;
377409

378-
CLR_UINT16 *curRow = ((CLR_UINT16 *)bitmap.data) + insetY * stride + insetX;
410+
CLR_UINT16 *curRow = ((CLR_UINT16 *)bitmap.data) + y * stride + x;
379411

380412
CLR_UINT32 scalar = (gradientDeltaY * gradientDeltaY * LIMIT) /
381413
(gradientDeltaY * gradientDeltaY + gradientDeltaX * gradientDeltaX);
@@ -399,16 +431,16 @@ void GraphicsDriver::DrawRectangleNative(
399431
scaleGradientTopLeft = 0;
400432
scaleGradientTopRight = LIMIT - scalar;
401433
scaleGradientBottomLeft = scalar;
402-
gradientTopLeftX = brush.gradientStartX - insetX;
403-
gradientTopLeftY = brush.gradientStartY - insetY;
434+
gradientTopLeftX = brush.gradientStartX - x;
435+
gradientTopLeftY = brush.gradientStartY - y;
404436
}
405437
else
406438
{
407439
scaleGradientTopLeft = scalar;
408440
scaleGradientTopRight = LIMIT;
409441
scaleGradientBottomLeft = 0;
410-
gradientTopLeftX = brush.gradientStartX - insetX;
411-
gradientTopLeftY = brush.gradientEndY - insetY;
442+
gradientTopLeftX = brush.gradientStartX - x;
443+
gradientTopLeftY = brush.gradientEndY - y;
412444
}
413445

414446
int diffX = 0;
@@ -424,12 +456,13 @@ void GraphicsDriver::DrawRectangleNative(
424456
DivHelper widthDivHelper(scaleGradientTopRight - scaleGradientTopLeft, gradientDeltaX, 0);
425457
DivHelper heightDivHelper(scaleGradientBottomLeft - scaleGradientTopLeft, gradientDeltaY, scaleLeft);
426458

427-
for (int j = 0; j < insetHeight; j++, curRow += stride)
459+
for (int j = 0; j < height; j++, curRow += stride)
428460
{
429461
widthDivHelper.Reset(heightDivHelper.Next());
430462

431463
CLR_UINT16 *curPixel = curRow;
432-
for (int i = 0; i < insetWidth; i++, curPixel++)
464+
465+
for (int i = 0; i < width; i++, curPixel++)
433466
{
434467
int scale = widthDivHelper.Next();
435468

@@ -468,7 +501,7 @@ void GraphicsDriver::DrawRectangleNative(
468501
}
469502
}
470503
}
471-
} //(opacity != 0)
504+
}
472505
}
473506

474507
void GraphicsDriver::DrawRoundedRectangleNative(
@@ -507,16 +540,39 @@ void GraphicsDriver::DrawRoundedRectangleNative(
507540
return;
508541

509542
params.pen = &pen;
510-
params.brush = NULL;
543+
params.brush = &brush;
544+
545+
// Fill
546+
if (brush.opacity != PAL_GFX_Bitmap::c_OpacityTransparent)
547+
{
548+
int gradientDeltaY = brush.gradientEndY - brush.gradientStartY;
549+
int gradientDeltaX = brush.gradientEndX - brush.gradientStartX;
550+
551+
if (brush.gradientStartColor == brush.gradientEndColor || (gradientDeltaX == 0 && gradientDeltaY == 0))
552+
{
553+
EllipseAlgorithm(bitmap, radiusX, radiusY, &params, &Fill4PointLinesRoundedRect);
554+
}
555+
else
556+
{
557+
EllipseAlgorithm(bitmap, radiusX, radiusY, &params, &GradientFill4PointLinesRoundedRect);
558+
}
559+
560+
GFX_Rect fillRect;
561+
562+
fillRect.left = x + pen.thickness;
563+
fillRect.top = rectangle.top + radiusY + 1;
564+
fillRect.right = x2 - 1 - pen.thickness;
565+
fillRect.bottom = rectangle.bottom - radiusY - 1;
566+
567+
FillRectangleNative(bitmap, brush, fillRect);
568+
}
511569

512570
EllipseAlgorithm(bitmap, radiusX, radiusY, &params, &Draw4PointsRoundedRect);
513571

514572
DrawBresLineNative(bitmap, params.x1, y, params.x2, y, pen);
515573
DrawBresLineNative(bitmap, x, params.y1, x, params.y2, pen);
516574
DrawBresLineNative(bitmap, x2, params.y1, x2, params.y2, pen);
517575
DrawBresLineNative(bitmap, params.x1, y2, params.x2, y2, pen);
518-
519-
// TODO - fill rounded rectangle
520576
}
521577
}
522578

@@ -546,6 +602,48 @@ void GraphicsDriver::Draw4PointsRoundedRect(const PAL_GFX_Bitmap &bitmap, int of
546602
}
547603
}
548604

605+
void GraphicsDriver::Fill4PointLinesRoundedRect(const PAL_GFX_Bitmap &bitmap, int offsetX, int offsetY, void *params)
606+
{
607+
Draw4PointsRoundedRectParams *p = (Draw4PointsRoundedRectParams *)params;
608+
609+
CLR_UINT32 color = p->brush->gradientStartColor;
610+
CLR_UINT16 opacity = p->brush->opacity;
611+
612+
// Top line
613+
DrawScanlineNative(bitmap, p->x1 - offsetX, p->x2 + offsetX, p->y1 - offsetY, color, opacity);
614+
615+
// Bottom line
616+
DrawScanlineNative(bitmap, p->x1 - offsetX, p->x2 + offsetX, p->y2 + offsetY, color, opacity);
617+
}
618+
619+
void GraphicsDriver::GradientFill4PointLinesRoundedRect(
620+
const PAL_GFX_Bitmap &bitmap,
621+
int offsetX,
622+
int offsetY,
623+
void *params)
624+
{
625+
Draw4PointsRoundedRectParams *p = (Draw4PointsRoundedRectParams *)params;
626+
627+
// Using the rectangle code for gradient fills. A version of DrawScanlineNative that supports gradients would be
628+
// faster
629+
630+
GFX_Rect rectangle;
631+
632+
// Top line
633+
rectangle.left = p->x1 - offsetX;
634+
rectangle.right = p->x2 + offsetX - 1;
635+
rectangle.top = p->y1 - offsetY;
636+
rectangle.bottom = rectangle.top;
637+
638+
FillRectangleNative(bitmap, *(p->brush), rectangle);
639+
640+
// Bottom line
641+
rectangle.top = p->y2 + offsetY;
642+
rectangle.bottom = rectangle.top;
643+
644+
FillRectangleNative(bitmap, *(p->brush), rectangle);
645+
}
646+
549647
void GraphicsDriver::DrawEllipseNative(
550648
const PAL_GFX_Bitmap &bitmap,
551649
GFX_Pen &pen,
@@ -1220,6 +1318,46 @@ void GraphicsDriver::DrawBresLineNative(const PAL_GFX_Bitmap &bitmap, int x0, in
12201318
}
12211319
}
12221320

1321+
void GraphicsDriver::DrawScanlineNative(
1322+
const PAL_GFX_Bitmap &bitmap,
1323+
int x1,
1324+
int x2,
1325+
int y,
1326+
CLR_UINT32 color,
1327+
CLR_UINT16 opacity)
1328+
{
1329+
// Check clipping
1330+
if ((y < bitmap.clipping.top) || (y >= bitmap.clipping.bottom))
1331+
{
1332+
return;
1333+
}
1334+
1335+
if (x1 < bitmap.clipping.left)
1336+
x1 = bitmap.clipping.left;
1337+
1338+
if (x2 >= bitmap.clipping.right)
1339+
x2 = bitmap.clipping.right - 1;
1340+
1341+
int stride = GetWidthInWords(bitmap.width) * 2;
1342+
1343+
CLR_UINT16 *curPixel = ((CLR_UINT16 *)bitmap.data) + (stride * y) + x1;
1344+
1345+
if (opacity == PAL_GFX_Bitmap::c_OpacityOpaque)
1346+
{
1347+
for (int x = x1; x <= x2; x++, curPixel++)
1348+
{
1349+
*curPixel = color;
1350+
}
1351+
}
1352+
else
1353+
{
1354+
for (int x = x1; x <= x2; x++, curPixel++)
1355+
{
1356+
*curPixel = g_GraphicsDriver.NativeColorInterpolate(color, *curPixel, opacity);
1357+
}
1358+
}
1359+
}
1360+
12231361
void GraphicsDriver::EllipseAlgorithm(
12241362
const PAL_GFX_Bitmap &bitmap,
12251363
int radiusX,

0 commit comments

Comments
 (0)