Optimize GfxRenderer drawArc/fillArc and drawRoundedRect stroke performance

This commit is contained in:
jpirnay
2026-03-30 17:58:36 +02:00
parent 5b43639f98
commit ea00584aa5
+62 -17
View File
@@ -338,17 +338,38 @@ 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;
}
while (xInner > 0 && (xInner * xInner + 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);
}
}
};
@@ -467,15 +488,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);
}
}
}