| 716 | |
| 717 | template <Color C> |
| 718 | void GfxRenderer::fillRectImpl(const int x, const int y, const int width, const int height) const { |
| 719 | if constexpr (C == Color::Clear) return; |
| 720 | if (width <= 0 || height <= 0) return; |
| 721 | if (fontCacheManager_ && fontCacheManager_->isScanning()) return; |
| 722 | |
| 723 | // Clip in logical space. |
| 724 | const int screenW = getScreenWidth(); |
| 725 | const int screenH = getScreenHeight(); |
| 726 | const int lx0 = std::max(0, x); |
| 727 | const int ly0 = std::max(0, y); |
| 728 | const int lx1 = std::min(screenW, x + width); |
| 729 | const int ly1 = std::min(screenH, y + height); |
| 730 | if (lx0 >= lx1 || ly0 >= ly1) return; |
| 731 | |
| 732 | // Rotate the two opposing logical corners into physical-framebuffer space. |
| 733 | // The bounding rect in physical space is the rect we need to fill — rotation |
| 734 | // is rigid (no shear/stretch) so the bbox of the two corners IS the rect. |
| 735 | int paX, paY, pbX, pbY; |
| 736 | rotateCoordinates(orientation, lx0, ly0, &paX, &paY, panelWidth, panelHeight); |
| 737 | rotateCoordinates(orientation, lx1 - 1, ly1 - 1, &pbX, &pbY, panelWidth, panelHeight); |
| 738 | |
| 739 | const int phyX0 = std::min(paX, pbX); |
| 740 | const int phyX1 = std::max(paX, pbX); // inclusive |
| 741 | int phyY0 = std::min(paY, pbY); |
| 742 | int phyY1 = std::max(paY, pbY); |
| 743 | |
| 744 | // Strip mode: clip Y range to the active band and redirect writes. |
| 745 | uint8_t* target = getWriteTarget(); |
| 746 | const int originY = getWriteOriginY(); |
| 747 | const int writeRows = getWriteRows(); |
| 748 | phyY0 = std::max(phyY0, originY); |
| 749 | phyY1 = std::min(phyY1, originY + writeRows - 1); |
| 750 | if (phyY0 > phyY1) return; |
| 751 | |
| 752 | // Bit/byte layout: MSB-first within a byte, so phyX → bit (7 - (phyX & 7)). |
| 753 | // Head and tail masks cover only the in-rect bits of the first/last byte. |
| 754 | const int byteStart = phyX0 >> 3; |
| 755 | const int byteEnd = phyX1 >> 3; // inclusive |
| 756 | const uint8_t headMask = static_cast<uint8_t>(0xFFu >> (phyX0 & 7)); |
| 757 | const uint8_t tailMask = static_cast<uint8_t>(0xFFu << (7 - (phyX1 & 7))); |
| 758 | const int32_t panelStride = static_cast<int32_t>(panelWidthBytes); |
| 759 | |
| 760 | if constexpr (C == Color::Black || C == Color::White) { |
| 761 | // Solid fill. Framebuffer: 0 = black, 1 = white. |
| 762 | const uint8_t fillByte = (C == Color::Black) ? 0x00u : 0xFFu; |
| 763 | for (int py = phyY0; py <= phyY1; ++py) { |
| 764 | uint8_t* row = target + static_cast<int32_t>(py - originY) * panelStride; |
| 765 | if (byteStart == byteEnd) { |
| 766 | const uint8_t mask = headMask & tailMask; |
| 767 | if constexpr (C == Color::Black) { |
| 768 | row[byteStart] &= static_cast<uint8_t>(~mask); |
| 769 | } else { |
| 770 | row[byteStart] |= mask; |
| 771 | } |
| 772 | } else { |
| 773 | if constexpr (C == Color::Black) { |
| 774 | row[byteStart] &= static_cast<uint8_t>(~headMask); |
| 775 | if (byteEnd > byteStart + 1) { |
nothing calls this directly
no test coverage detected