mirror of
https://github.com/crosspoint-reader/crosspoint-reader.git
synced 2026-04-29 10:26:52 -07:00
refactor: Refactor drawArc / fillArc for faster execution (#1540)
## Summary * **What is the goal of this PR?** Replace the o(r^2) routines with a o(r) scanline logic - will make fillArc roughly 50% faster and drawArc roughly 5x faster. Still probably unnoticeable. * **What changes are included?** ## Additional Context --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< NO >**_
This commit is contained in:
@@ -343,17 +343,40 @@ void GfxRenderer::drawArc(const int maxRadius, const int cx, const int cy, const
|
||||
const int lineWidth, const bool state) const {
|
||||
const int stroke = std::min(lineWidth, maxRadius);
|
||||
const int innerRadius = std::max(maxRadius - stroke, 0);
|
||||
const int outerRadiusSq = maxRadius * maxRadius;
|
||||
const int outerRadius = maxRadius;
|
||||
|
||||
if (outerRadius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int outerRadiusSq = outerRadius * outerRadius;
|
||||
const int innerRadiusSq = innerRadius * innerRadius;
|
||||
for (int dy = 0; dy <= maxRadius; ++dy) {
|
||||
for (int dx = 0; dx <= maxRadius; ++dx) {
|
||||
const int distSq = dx * dx + dy * dy;
|
||||
if (distSq > outerRadiusSq || distSq < innerRadiusSq) {
|
||||
continue;
|
||||
}
|
||||
const int px = cx + xDir * dx;
|
||||
const int py = cy + yDir * dy;
|
||||
drawPixel(px, py, state);
|
||||
|
||||
int xOuter = outerRadius;
|
||||
int xInner = innerRadius;
|
||||
|
||||
for (int dy = 0; dy <= outerRadius; ++dy) {
|
||||
while (xOuter > 0 && (xOuter * xOuter + dy * dy) > outerRadiusSq) {
|
||||
--xOuter;
|
||||
}
|
||||
// Keep the smallest x that still lies outside/at the inner radius,
|
||||
// i.e. (x^2 + y^2) >= innerRadiusSq.
|
||||
while (xInner > 0 && ((xInner - 1) * (xInner - 1) + dy * dy) >= innerRadiusSq) {
|
||||
--xInner;
|
||||
}
|
||||
|
||||
if (xOuter < xInner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int x0 = cx + xDir * xInner;
|
||||
const int x1 = cx + xDir * xOuter;
|
||||
const int left = std::min(x0, x1);
|
||||
const int width = std::abs(x1 - x0) + 1;
|
||||
const int py = cy + yDir * dy;
|
||||
|
||||
if (width > 0) {
|
||||
fillRect(left, py, width, 1, state);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -472,15 +495,39 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons
|
||||
|
||||
template <Color color>
|
||||
void GfxRenderer::fillArc(const int maxRadius, const int cx, const int cy, const int xDir, const int yDir) const {
|
||||
if (maxRadius <= 0) return;
|
||||
|
||||
if constexpr (color == Color::Clear) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int radiusSq = maxRadius * maxRadius;
|
||||
|
||||
// Avoid sqrt by scanning from outer radius inward while y grows.
|
||||
int x = maxRadius;
|
||||
for (int dy = 0; dy <= maxRadius; ++dy) {
|
||||
for (int dx = 0; dx <= maxRadius; ++dx) {
|
||||
const int distSq = dx * dx + dy * dy;
|
||||
const int px = cx + xDir * dx;
|
||||
const int py = cy + yDir * dy;
|
||||
if (distSq <= radiusSq) {
|
||||
drawPixelDither<color>(px, py);
|
||||
}
|
||||
while (x > 0 && (x * x + dy * dy) > radiusSq) {
|
||||
--x;
|
||||
}
|
||||
if (x < 0) break;
|
||||
|
||||
const int py = cy + yDir * dy;
|
||||
if (py < 0 || py >= getScreenHeight()) continue;
|
||||
|
||||
int x0 = cx;
|
||||
int x1 = cx + xDir * x;
|
||||
if (x0 > x1) std::swap(x0, x1);
|
||||
const int width = x1 - x0 + 1;
|
||||
|
||||
if (width <= 0) continue;
|
||||
|
||||
if constexpr (color == Color::Black) {
|
||||
fillRect(x0, py, width, 1, true);
|
||||
} else if constexpr (color == Color::White) {
|
||||
fillRect(x0, py, width, 1, false);
|
||||
} else {
|
||||
// LightGray / DarkGray: use existing dithered fill path.
|
||||
fillRectDither(x0, py, width, 1, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user