diff --git a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp index d2188b0..2012b32 100644 --- a/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp +++ b/Firmware/sdk/apps/gameboy/src/gameboy_app.cpp @@ -102,49 +102,6 @@ void appendEmbeddedRoms(std::vector& out) { } } -int measureVerticalText(std::string_view text, int scale = 1, int letterSpacing = 1) { - if (text.empty()) - return 0; - const int advance = (font16x8::kGlyphWidth + letterSpacing) * scale; - return static_cast(text.size()) * advance - letterSpacing * scale; -} - -void drawGlyphRotated(Framebuffer& fb, int x, int y, char ch, bool clockwise, int scale = 1, bool on = true) { - const auto& rows = font16x8::glyphBitmap(ch); - for (int row = 0; row < font16x8::kGlyphHeight; ++row) { - const uint8_t rowBits = rows[row]; - for (int col = 0; col < font16x8::kGlyphWidth; ++col) { - const uint8_t mask = static_cast(1u << (font16x8::kGlyphWidth - 1 - col)); - if ((rowBits & mask) == 0) - continue; - for (int sx = 0; sx < scale; ++sx) { - for (int sy = 0; sy < scale; ++sy) { - int dstX; - int dstY; - if (clockwise) { - dstX = x + row * scale + sx; - dstY = y + (font16x8::kGlyphWidth - 1 - col) * scale + sy; - } else { - dstX = x + (font16x8::kGlyphHeight - 1 - row) * scale + sx; - dstY = y + col * scale + sy; - } - fb.drawPixel(dstX, dstY, on); - } - } - } - } -} - -void drawTextRotated(Framebuffer& fb, int x, int y, std::string_view text, bool clockwise, int scale = 1, - bool on = true, int letterSpacing = 1) { - int cursor = y; - const int advance = (font16x8::kGlyphWidth + letterSpacing) * scale; - for (char ch: text) { - drawGlyphRotated(fb, x, cursor, ch, clockwise, scale, on); - cursor += advance; - } -} - class GameboyApp final : public cardboy::sdk::IApp { public: explicit GameboyApp(AppContext& ctx) : @@ -1081,18 +1038,26 @@ private: if (!activeRomName.empty()) { const std::string rotatedRomName(activeRomName.rbegin(), activeRomName.rend()); - const int textHeight = measureVerticalText(rotatedRomName, textScale); + const auto textBounds = + font16x8::measureTextBounds(rotatedRomName, textScale, 1, + font16x8::Rotation::Clockwise90); + const int textHeight = textBounds.height; const int maxOrigin = std::max(0, screenHeight - textHeight); int leftX = 8; int leftY = std::clamp((screenHeight - textHeight) / 2, 0, maxOrigin); - drawTextRotated(framebuffer, leftX, leftY, rotatedRomName, true, textScale, true, 1); + font16x8::drawText(framebuffer, leftX, leftY, rotatedRomName, textScale, true, 1, + font16x8::Rotation::Clockwise90); if (!statusMessage.empty()) { const std::string rotatedStatusMessage(statusMessage.rbegin(), statusMessage.rend()); - const int textHeight = measureVerticalText(rotatedStatusMessage, textScale); + const auto statusBounds = + font16x8::measureTextBounds(rotatedStatusMessage, textScale, 1, + font16x8::Rotation::Clockwise90); + const int textHeight = statusBounds.height; const int maxOrigin = std::max(0, screenHeight - textHeight); leftX = leftX + 20; leftY = std::clamp((screenHeight - textHeight) / 2, 0, maxOrigin); - drawTextRotated(framebuffer, leftX, leftY, rotatedStatusMessage, true, textScale, true, 1); + font16x8::drawText(framebuffer, leftX, leftY, rotatedStatusMessage, textScale, true, 1, + font16x8::Rotation::Clockwise90); } } @@ -1110,7 +1075,10 @@ private: std::string rotated(text.rbegin(), text.rend()); if (totalRightHeight > 0) totalRightHeight += gap; - totalRightHeight += measureVerticalText(rotated, textScale); + const auto bounds = + font16x8::measureTextBounds(rotated, textScale, 1, + font16x8::Rotation::Clockwise90); + totalRightHeight += bounds.height; } const int maxRightOrigin = std::max(0, screenHeight - totalRightHeight); @@ -1125,8 +1093,12 @@ private: if (text.empty()) continue; std::string rotated(text.rbegin(), text.rend()); - rightY = screenHeight - measureVerticalText(rotated, textScale) - 8; - drawTextRotated(framebuffer, rightX, rightY, rotated, true, textScale, true, 1); + const auto bounds = + font16x8::measureTextBounds(rotated, textScale, 1, + font16x8::Rotation::Clockwise90); + rightY = screenHeight - bounds.height - 8; + font16x8::drawText(framebuffer, rightX, rightY, rotated, textScale, true, 1, + font16x8::Rotation::Clockwise90); rightX -= 20; } diff --git a/Firmware/sdk/core/include/cardboy/gfx/font16x8.hpp b/Firmware/sdk/core/include/cardboy/gfx/font16x8.hpp index dd975d1..96a8ee7 100644 --- a/Firmware/sdk/core/include/cardboy/gfx/font16x8.hpp +++ b/Firmware/sdk/core/include/cardboy/gfx/font16x8.hpp @@ -26,22 +26,69 @@ inline const std::array& glyphBitmap(char ch) { return fonts_Terminess_Powerline[uc]; } +enum class Rotation { + None, + Clockwise90, + CounterClockwise90, +}; + +struct TextBounds { + int width = 0; + int height = 0; +}; + template -inline void drawGlyph(Framebuffer& fb, int x, int y, char ch, int scale = 1, bool on = true) { +inline void drawGlyph(Framebuffer& fb, int x, int y, char ch, int scale = 1, bool on = true, + Rotation rotation = Rotation::None) { const auto& rows = glyphBitmap(ch); for (int row = 0; row < kGlyphHeight; ++row) { const uint8_t rowBits = rows[row]; for (int col = 0; col < kGlyphWidth; ++col) { const uint8_t mask = static_cast(1u << (kGlyphWidth - 1 - col)); - if (rowBits & mask) { - for (int sx = 0; sx < scale; ++sx) - for (int sy = 0; sy < scale; ++sy) - fb.drawPixel(x + col * scale + sx, y + row * scale + sy, on); + if ((rowBits & mask) == 0) + continue; + for (int sx = 0; sx < scale; ++sx) { + for (int sy = 0; sy < scale; ++sy) { + int dstX = x; + int dstY = y; + switch (rotation) { + case Rotation::None: + dstX += col * scale + sx; + dstY += row * scale + sy; + break; + case Rotation::Clockwise90: + dstX += row * scale + sx; + dstY += (kGlyphWidth - 1 - col) * scale + sy; + break; + case Rotation::CounterClockwise90: + dstX += (kGlyphHeight - 1 - row) * scale + sx; + dstY += col * scale + sy; + break; + } + fb.drawPixel(dstX, dstY, on); + } } } } } +inline TextBounds measureTextBounds(std::string_view text, int scale = 1, int letterSpacing = 1, + Rotation rotation = Rotation::None) { + if (text.empty()) + return {}; + const int advance = (kGlyphWidth + letterSpacing) * scale; + const int extent = static_cast(text.size()) * advance - letterSpacing * scale; + const int height = kGlyphHeight * scale; + switch (rotation) { + case Rotation::None: + return {extent, height}; + case Rotation::Clockwise90: + case Rotation::CounterClockwise90: + return {height, extent}; + } + return {extent, height}; +} + inline int measureText(std::string_view text, int scale = 1, int letterSpacing = 1) { if (text.empty()) return 0; @@ -51,11 +98,22 @@ inline int measureText(std::string_view text, int scale = 1, int letterSpacing = template inline void drawText(Framebuffer& fb, int x, int y, std::string_view text, int scale = 1, bool on = true, - int letterSpacing = 1) { - int cursor = x; - for (char ch: text) { - drawGlyph(fb, cursor, y, ch, scale, on); - cursor += (kGlyphWidth + letterSpacing) * scale; + int letterSpacing = 1, Rotation rotation = Rotation::None) { + if (text.empty()) + return; + const int advance = (kGlyphWidth + letterSpacing) * scale; + if (rotation == Rotation::None) { + int cursor = x; + for (char ch: text) { + drawGlyph(fb, cursor, y, ch, scale, on, rotation); + cursor += advance; + } + } else { + int cursor = y; + for (char ch: text) { + drawGlyph(fb, x, cursor, ch, scale, on, rotation); + cursor += advance; + } } }