Skip to content
This repository was archived by the owner on Jul 26, 2025. It is now read-only.

fix: fix bug with transparent circle fill #480

Merged
merged 5 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/draw/__tests__/drawCircleOnImage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,40 @@ test('default options', () => {
[0, 1, 0],
]);
});
test('draw circle image with transparent color', () => {
const image = testUtils.createGreyaImage([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]);
const center = { row: 5, column: 2 };
const radius = 2;
const received = image.drawCircle(center, radius, {
fill: [255, 125],
color: [255, 255],
});

const expected = testUtils.createGreyaImage([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0],
[255, 255, 255, 125, 255, 125, 255, 125, 255, 255, 0, 0, 0, 0, 0, 0],
[255, 255, 255, 125, 255, 125, 255, 125, 255, 255, 0, 0, 0, 0, 0, 0],
[255, 255, 255, 125, 255, 125, 255, 125, 255, 255, 0, 0, 0, 0, 0, 0],
[0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]);

expect(received).toMatchImage(expected);
});
40 changes: 33 additions & 7 deletions src/draw/drawCircleOnImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,51 @@ export function drawCircleOnImage(
if (radius === 1) {
setBlendedVisiblePixel(newImage, center.column, center.row, fill);
}
//Starting points for the top and bottom row of the circle.
let prevRow = center.row + radius;

let index = 0;
circle(center.column, center.row, radius, (column: number, row: number) => {
setBlendedVisiblePixel(newImage, column, row, color);

//todo: fill is not optimal we can fill symmetrically
if (column - 1 > center.column) {
// Filling the first line of the circle.
if (index === 0) {
newImage.drawLine(
{ row, column: column - 1 },
{ row, column: center.column },
{
row,
column: center.column - (column - center.column - 1),
},
{ strokeColor: fill, out: newImage },
);
} else if (column + 1 < center.column) {
}
// The algorithm used is Bresenham's circle algorithm (@link https://www.geeksforgeeks.org/bresenhams-circle-drawing-algorithm/) to find points that constitute the circle outline. However, in this algorithm The circle is divided in 4 parts instead of 8: top, right, bottom and left.
// The algorithm draws a point per quadrant until the circle is complete.
// We use bottom (index % 4 === 1, quadrant 2) point of the outline to fill the circle with color.
// Filling half of the circle.
if (index % 4 === 1 && prevRow !== row) {
// For quadrant 2, column < center.column
newImage.drawLine(
{ row, column: column + 1 },
{ row, column: center.column },
{
row,
column: center.column - (column - center.column + 1),
},
{ strokeColor: fill, out: newImage },
);
prevRow = row;
// Filling top half of the circle.
newImage.drawLine(
{ row: center.row - (row - center.row), column: column + 1 },
{
row: center.row - (row - center.row),
column: center.column - (column - center.column + 1),
},
{ strokeColor: fill, out: newImage },
);
}

index++;
});
}

return newImage;
}
Loading