A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 >**_

authored by

jpirnay and committed by
GitHub
0c5dee3c 81ae9dd7

+64 -17
+64 -17
lib/GfxRenderer/GfxRenderer.cpp
··· 343 343 const int lineWidth, const bool state) const { 344 344 const int stroke = std::min(lineWidth, maxRadius); 345 345 const int innerRadius = std::max(maxRadius - stroke, 0); 346 - const int outerRadiusSq = maxRadius * maxRadius; 346 + const int outerRadius = maxRadius; 347 + 348 + if (outerRadius <= 0) { 349 + return; 350 + } 351 + 352 + const int outerRadiusSq = outerRadius * outerRadius; 347 353 const int innerRadiusSq = innerRadius * innerRadius; 348 - for (int dy = 0; dy <= maxRadius; ++dy) { 349 - for (int dx = 0; dx <= maxRadius; ++dx) { 350 - const int distSq = dx * dx + dy * dy; 351 - if (distSq > outerRadiusSq || distSq < innerRadiusSq) { 352 - continue; 353 - } 354 - const int px = cx + xDir * dx; 355 - const int py = cy + yDir * dy; 356 - drawPixel(px, py, state); 354 + 355 + int xOuter = outerRadius; 356 + int xInner = innerRadius; 357 + 358 + for (int dy = 0; dy <= outerRadius; ++dy) { 359 + while (xOuter > 0 && (xOuter * xOuter + dy * dy) > outerRadiusSq) { 360 + --xOuter; 361 + } 362 + // Keep the smallest x that still lies outside/at the inner radius, 363 + // i.e. (x^2 + y^2) >= innerRadiusSq. 364 + while (xInner > 0 && ((xInner - 1) * (xInner - 1) + dy * dy) >= innerRadiusSq) { 365 + --xInner; 366 + } 367 + 368 + if (xOuter < xInner) { 369 + continue; 370 + } 371 + 372 + const int x0 = cx + xDir * xInner; 373 + const int x1 = cx + xDir * xOuter; 374 + const int left = std::min(x0, x1); 375 + const int width = std::abs(x1 - x0) + 1; 376 + const int py = cy + yDir * dy; 377 + 378 + if (width > 0) { 379 + fillRect(left, py, width, 1, state); 357 380 } 358 381 } 359 382 }; ··· 472 495 473 496 template <Color color> 474 497 void GfxRenderer::fillArc(const int maxRadius, const int cx, const int cy, const int xDir, const int yDir) const { 498 + if (maxRadius <= 0) return; 499 + 500 + if constexpr (color == Color::Clear) { 501 + return; 502 + } 503 + 475 504 const int radiusSq = maxRadius * maxRadius; 505 + 506 + // Avoid sqrt by scanning from outer radius inward while y grows. 507 + int x = maxRadius; 476 508 for (int dy = 0; dy <= maxRadius; ++dy) { 477 - for (int dx = 0; dx <= maxRadius; ++dx) { 478 - const int distSq = dx * dx + dy * dy; 479 - const int px = cx + xDir * dx; 480 - const int py = cy + yDir * dy; 481 - if (distSq <= radiusSq) { 482 - drawPixelDither<color>(px, py); 483 - } 509 + while (x > 0 && (x * x + dy * dy) > radiusSq) { 510 + --x; 511 + } 512 + if (x < 0) break; 513 + 514 + const int py = cy + yDir * dy; 515 + if (py < 0 || py >= getScreenHeight()) continue; 516 + 517 + int x0 = cx; 518 + int x1 = cx + xDir * x; 519 + if (x0 > x1) std::swap(x0, x1); 520 + const int width = x1 - x0 + 1; 521 + 522 + if (width <= 0) continue; 523 + 524 + if constexpr (color == Color::Black) { 525 + fillRect(x0, py, width, 1, true); 526 + } else if constexpr (color == Color::White) { 527 + fillRect(x0, py, width, 1, false); 528 + } else { 529 + // LightGray / DarkGray: use existing dithered fill path. 530 + fillRectDither(x0, py, width, 1, color); 484 531 } 485 532 } 486 533 }