From 9237b2b1ac79bd1730b12bf1ec0cb1212c5183f3 Mon Sep 17 00:00:00 2001 From: Christian Seiler Date: Fri, 15 May 2026 08:36:15 +0200 Subject: [PATCH] C++: implement drawing code for all elements --- cpp/README.txt | 5 - cpp/src/monoformat_fontreader.hpp | 2 +- cpp/src/monoformat_parsehelpers.hpp | 2 +- cpp/src/monoformat_structured.cpp | 521 ++++++++++++++++++++++++++-- cpp/src/monoformat_structured.hpp | 33 +- cpp/src/monoformat_utf8.hpp | 2 +- 6 files changed, 522 insertions(+), 43 deletions(-) diff --git a/cpp/README.txt b/cpp/README.txt index b3978b6..18830af 100644 --- a/cpp/README.txt +++ b/cpp/README.txt @@ -2,11 +2,6 @@ This is the C++ implementation of the mono display format library. TODO: - - Structured: fully implement the current spec (including changes to - spec since the initial implementation) - - Line support - - Text support - - Current time support - Write low-RAM parser / renderer for the format (the structured renderer exists for high-level software) - CMake: automatically convert standard fonts into importable data diff --git a/cpp/src/monoformat_fontreader.hpp b/cpp/src/monoformat_fontreader.hpp index a10c495..96e3233 100644 --- a/cpp/src/monoformat_fontreader.hpp +++ b/cpp/src/monoformat_fontreader.hpp @@ -122,8 +122,8 @@ private: friend class GlyphSearcher; std::span m_data; - bool m_supportsBackgroundColor{}; std::uint8_t m_glyphCount{}; + bool m_supportsBackgroundColor{}; std::uint8_t m_m0{}; std::uint8_t m_m1{}; std::uint8_t m_bitCountW{}; diff --git a/cpp/src/monoformat_parsehelpers.hpp b/cpp/src/monoformat_parsehelpers.hpp index 5cd269c..5901595 100644 --- a/cpp/src/monoformat_parsehelpers.hpp +++ b/cpp/src/monoformat_parsehelpers.hpp @@ -123,7 +123,7 @@ inline std::expected peekU32LE(std::span readU32LE(std::span& buffer) { auto result = peekU32LE(buffer); if (result) { - buffer = buffer.subspan(8); + buffer = buffer.subspan(4); } return result; } diff --git a/cpp/src/monoformat_structured.cpp b/cpp/src/monoformat_structured.cpp index 511d3e5..0bd3f36 100644 --- a/cpp/src/monoformat_structured.cpp +++ b/cpp/src/monoformat_structured.cpp @@ -1,4 +1,7 @@ #include "monoformat_structured.hpp" +#include "monoformat_fontreader.hpp" + +#include namespace monoformat { @@ -72,6 +75,32 @@ void ConstMemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool va std::ignore = value; } +ClippedImage::ClippedImage(OneBitBufferInterface* underlying, std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height) + : m_underlying{underlying} + , m_x{x} + , m_y{y} + , m_width{width} + , m_height{height} +{ +} + +ClippedImage::~ClippedImage() { +} + +bool ClippedImage::isPixelSet(std::uint16_t x, std::uint16_t y) const { + if (x >= m_width || y >= m_height) { + return false; + } + return m_underlying->isPixelSet(x + m_x, y + m_y); +} + +void ClippedImage::setPixel(std::uint16_t x, std::uint16_t y, bool value) { + if (x >= m_width || y >= m_height) { + return; + } + m_underlying->setPixel(x + m_x, y + m_y, value); +} + ImageElement::ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height) : m_x{x} , m_y{y} @@ -139,8 +168,16 @@ std::size_t ImageElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: draw +void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = animationTick; + std::ignore = currentTimestamp; + std::ignore = customFonts; + auto ownImage = image(); + for (std::uint16_t dy = 0; dy < m_height; ++dy) { + for (std::uint16_t dx = 0; dx < m_width; ++dx) { + imageBuffer->setPixel(m_x + dx, m_y + dy, ownImage.isPixelSet(dx, dy)); + } + } } std::expected, ParseError> ImageElement::parse(std::span& buffer) { @@ -281,8 +318,23 @@ std::size_t AnimationElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: implement this +void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = currentTimestamp; + std::ignore = customFonts; + + if (m_numberOfFrames == 0) { + return; + } + + std::size_t const tick = animationTick / (static_cast(m_updateInterval) + 1); + std::size_t const frameIndex = tick % m_numberOfFrames; + + auto ownImage = image(frameIndex); + for (std::uint16_t dy = 0; dy < m_height; ++dy) { + for (std::uint16_t dx = 0; dx < m_width; ++dx) { + imageBuffer->setPixel(m_x + dx, m_y + dy, ownImage.isPixelSet(dx, dy)); + } + } } std::expected, ParseError> AnimationElement::parse(std::span& buffer) { @@ -421,8 +473,108 @@ std::size_t HScrollImageElement::serializeTo(std::span target) const return alignNextWrite(target, pos, 4); } -void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME draw +void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = currentTimestamp; + std::ignore = customFonts; + + auto ownImage = image(); + ClippedImage target{imageBuffer, m_x, m_y, m_width, m_height}; + + if (m_contentWidth == 0) { + return; + } + + bool restarting = (m_flags & 0x01) == 0x00; + bool invert = (m_flags & 0x02) == 0x02; + bool padBefore = (m_flags & 0x04) == 0x04; + bool padAfter = (m_flags & 0x08) == 0x08; + + if (!padBefore && !padAfter && m_contentWidth < m_width) { + std::int16_t offset = invert ? (m_width - m_contentWidth) : 0; + for (std::size_t dy = 0; dy < m_height; ++dy) { + for (std::size_t dx = 0; dx < m_contentWidth; ++dx) { + target.setPixel(dx + offset, dy, ownImage.isPixelSet(dx, dy)); + } + } + return; + } + + std::int32_t totalSize = m_contentWidth; + if (padBefore) { + totalSize += m_width; + } + if (padAfter) { + totalSize += m_width; + } + + // FIXME: handle overflow here... + std::int32_t offset = static_cast(animationTick * (m_scrollSpeed + 1) / 8) % totalSize; + if (invert) { + offset = totalSize - offset - 1; + } + if (restarting) { + offset = std::min(offset, totalSize - m_width); + } + + std::int32_t partOffset = 0; + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_contentWidth; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, ownImage.isPixelSet(dx, dy)); + } + partOffset += m_contentWidth; + } + if (padAfter) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + if (!restarting) { + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_contentWidth; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, ownImage.isPixelSet(dx, dy)); + } + } + partOffset += m_contentWidth; + } } std::expected, ParseError> HScrollImageElement::parse(std::span& buffer) { @@ -565,8 +717,108 @@ std::size_t VScrollImageElement::serializeTo(std::span target) const return alignNextWrite(target, pos, 4); } -void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME draw +void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = currentTimestamp; + std::ignore = customFonts; + + auto ownImage = image(); + ClippedImage target{imageBuffer, m_x, m_y, m_width, m_height}; + + if (m_contentHeight == 0) { + return; + } + + bool restarting = (m_flags & 0x01) == 0x00; + bool invert = (m_flags & 0x02) == 0x02; + bool padBefore = (m_flags & 0x04) == 0x04; + bool padAfter = (m_flags & 0x08) == 0x08; + + if (!padBefore && !padAfter && m_contentHeight < m_height) { + std::int16_t offset = invert ? (m_height - m_contentHeight) : 0; + for (std::size_t dy = 0; dy < m_contentHeight; ++dy) { + for (std::size_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, dy + offset, ownImage.isPixelSet(dx, dy)); + } + } + return; + } + + std::int32_t totalSize = m_contentHeight; + if (padBefore) { + totalSize += m_height; + } + if (padAfter) { + totalSize += m_height; + } + + // FIXME: handle overflow here... + std::int32_t offset = static_cast(animationTick * (m_scrollSpeed + 1) / 8) % totalSize; + if (invert) { + offset = totalSize - offset - 1; + } + if (restarting) { + offset = std::min(offset, totalSize - m_height); + } + + std::int32_t partOffset = 0; + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + std::int32_t ddy = dy + partOffset; + if ((ddy - offset) < 0) { + continue; + } + for (std::int32_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, ddy - offset, false); + } + } + partOffset += m_height; + } + for (std::int32_t dy = 0; dy < m_contentHeight; ++dy) { + std::int32_t ddy = dy + partOffset; + if ((ddy - offset) < 0) { + continue; + } + for (std::int32_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, ddy - offset, ownImage.isPixelSet(dx, dy)); + } + partOffset += m_contentHeight; + } + if (padAfter) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + std::int32_t ddy = dy + partOffset; + if ((ddy - offset) < 0) { + continue; + } + for (std::int32_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, ddy - offset, false); + } + } + partOffset += m_height; + } + if (!restarting) { + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + std::int32_t ddy = dy + partOffset; + if ((ddy - offset) < 0) { + continue; + } + for (std::int32_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, ddy - offset, false); + } + } + partOffset += m_height; + } + for (std::int32_t dy = 0; dy < m_contentHeight; ++dy) { + std::int32_t ddy = dy + partOffset; + if ((ddy - offset) < 0) { + continue; + } + for (std::int32_t dx = 0; dx < m_width; ++dx) { + target.setPixel(dx, ddy - offset, ownImage.isPixelSet(dx, dy)); + } + } + partOffset += m_contentHeight; + } } std::expected, ParseError> VScrollImageElement::parse(std::span& buffer) { @@ -678,8 +930,30 @@ std::size_t LineElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: draw +void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = animationTick; + std::ignore = currentTimestamp; + std::ignore = customFonts; + + std::int32_t dx = static_cast(m_targetX) - static_cast(m_originX); + std::int32_t dy = static_cast(m_targetY) - static_cast(m_originY); + bool value = true; + + if (dx == 0) { + for (std::int32_t i = 0; i <= dy; ++i) { + imageBuffer->setPixel(m_originX, m_originY + i, value); + } + } else if (dy == 0) { + for (std::int32_t i = 0; i <= dx; ++i) { + imageBuffer->setPixel(m_originX + i, m_originY, value); + } + } else { + for (std::int32_t i = 0; i <= dx; ++i) { + std::int32_t x = m_originX + i; + std::int32_t y = i * dy / dx + m_originY; + imageBuffer->setPixel(static_cast(x), static_cast(y), value); + } + } } std::expected, ParseError> LineElement::parse(std::span& buffer) { @@ -775,8 +1049,34 @@ std::size_t ClippedTextElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: draw +void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = animationTick; + std::ignore = currentTimestamp; + + std::span fontData; + if (m_fontIndex & 0x8000u) { + std::size_t fontIndex = m_fontIndex & ~0x8000u; + if (fontIndex >= customFonts.size()) { + return; + } + fontData = customFonts[fontIndex]->fontData(); + } else { + if (m_fontIndex == 0) { + fontData = findEmbeddedFont("NokiaSmallPlain_tf"); + } else if (m_fontIndex == 1) { + fontData = findEmbeddedFont("5x7_mf"); + } else { + return; + } + } + if (fontData.size() < 23) { + return; + } + + ClippedImage target{imageBuffer, m_x, m_y, m_width, m_height}; + + auto renderer = FontRenderer{fontData}.withIgnoreUnknownChars(true); + renderer.render(m_text, Point{0, static_cast(renderer.lineHeight() - 1)}, target, true, false); } std::expected, ParseError> ClippedTextElement::parse(std::span& buffer) { @@ -887,8 +1187,114 @@ std::size_t HScrollTextElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: draw +void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = currentTimestamp; + + std::span fontData; + if (m_fontIndex & 0x8000u) { + std::size_t fontIndex = m_fontIndex & ~0x8000u; + if (fontIndex >= customFonts.size()) { + return; + } + fontData = customFonts[fontIndex]->fontData(); + } else { + if (m_fontIndex == 0) { + fontData = findEmbeddedFont("NokiaSmallPlain_tf"); + } else if (m_fontIndex == 1) { + fontData = findEmbeddedFont("5x7_mf"); + } else { + return; + } + } + if (fontData.size() < 23) { + return; + } + + auto renderer = FontRenderer{fontData}.withIgnoreUnknownChars(true); + auto dimensions = renderer.getRenderedDimensions(m_text, {0, static_cast(renderer.lineHeight() - 1)}); + if (!dimensions || !dimensions->boundingBox) { + return; + } + + std::uint16_t contentWidth = dimensions->boundingBox->size.width; + + ClippedImage target{imageBuffer, m_x, m_y, m_width, m_height}; + + if (contentWidth == 0) { + return; + } + + bool restarting = (m_flags & 0x01) == 0x00; + bool invert = (m_flags & 0x02) == 0x02; + bool padBefore = (m_flags & 0x04) == 0x04; + bool padAfter = (m_flags & 0x08) == 0x08; + + if (!padBefore && !padAfter && contentWidth < m_width) { + std::uint16_t offset = invert ? (m_width - contentWidth) : 0; + renderer.render(m_text, Point{offset, static_cast(renderer.lineHeight() - 1)}, target, true, false); + return; + } + + std::int32_t totalSize = contentWidth; + if (padBefore) { + totalSize += m_width; + } + if (padAfter) { + totalSize += m_width; + } + + // FIXME: handle overflow here... + std::int32_t offset = static_cast(animationTick * (m_scrollSpeed + 1) / 8) % totalSize; + + if (invert) { + offset = totalSize - offset - 1; + } + if (restarting) { + offset = std::min(offset, totalSize - m_width); + } + + std::int32_t partOffset = 0; + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + renderer.render(m_text, Point{partOffset - offset, static_cast(renderer.lineHeight() - 1)}, target, true, false); + partOffset += contentWidth; + if (padAfter) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + if (!restarting) { + if (padBefore) { + for (std::int32_t dy = 0; dy < m_height; ++dy) { + for (std::int32_t dx = 0; dx < m_width; ++dx) { + std::int32_t ddx = dx + partOffset; + if ((ddx - offset) < 0) { + continue; + } + target.setPixel(ddx - offset, dy, false); + } + } + partOffset += m_width; + } + renderer.render(m_text, Point{partOffset - offset, static_cast(renderer.lineHeight() - 1)}, target, true, false); + } } std::expected, ParseError> HScrollTextElement::parse(std::span& buffer) { @@ -1000,8 +1406,67 @@ std::size_t CurrentTimeElement::serializeTo(std::span target) const { return alignNextWrite(target, pos, 4); } -void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { - // FIXME: draw +void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) { + std::ignore = animationTick; + + std::span fontData; + if (m_fontIndex & 0x8000u) { + std::size_t fontIndex = m_fontIndex & ~0x8000u; + if (fontIndex >= customFonts.size()) { + return; + } + fontData = customFonts[fontIndex]->fontData(); + } else { + if (m_fontIndex == 0) { + fontData = findEmbeddedFont("NokiaSmallPlain_tf"); + } else if (m_fontIndex == 1) { + fontData = findEmbeddedFont("5x7_mf"); + } else { + return; + } + } + if (fontData.size() < 23) { + return; + } + + bool use12h = (m_flags & 0x01u) == 0x01u; + bool showHours = (m_flags & 0x02u) == 0x02u; + bool showMinutes = (m_flags & 0x04u) == 0x04u; + bool showSeconds = (m_flags & 0x08u) == 0x08u; + if (showHours && showSeconds) { + showMinutes = true; + } + + ClippedImage target{imageBuffer, m_x, m_y, m_width, m_height}; + + auto renderer = FontRenderer{fontData}.withIgnoreUnknownChars(true); + + std::uint32_t seconds = (currentTimestamp + static_cast(m_utcOffset) * 60) % 86400; + std::uint32_t hours = seconds / 3600; + std::uint32_t minutes = (seconds - hours * 3600) / 60; + seconds = seconds % 60; + if (use12h) { + hours = hours % 12; + if (hours == 0) { + hours = 12; + } + } + std::string text; + if (showHours && showMinutes && showSeconds) { + text = std::format("{:02d}:{:02d}:{:02d}", hours, minutes, seconds); + } else if (showHours && showMinutes) { + text = std::format("{:02d}:{:02d}", hours, minutes); + } else if (showHours) { + text = std::format("{:02d}", hours); + } else if (showMinutes && showSeconds) { + text = std::format("{:02d}:{:02d}", minutes, seconds); + } else if (showSeconds) { + text = std::format("{:02d}", seconds); + } else { + return; + } + + renderer.render(text, Point{0, static_cast(renderer.lineHeight() - 1)}, target, true, false); } std::expected, ParseError> CurrentTimeElement::parse(std::span& buffer) { @@ -1172,9 +1637,9 @@ std::expected, ParseError> AlwaysDrawnSectio auto sectionData = buffer.subspan(0, *size - 8); buffer = buffer.subspan(*size - 8); auto section = std::make_unique(); - section->setDrawOnFront(((*flags) >> 0u) & 0x01u == 0x01u); - section->setDrawOnBack(((*flags) >> 1u) & 0x01u == 0x01u); - section->setClearBeforeDrawing(((*flags) >> 2u) & 0x01u == 0x01u); + section->setDrawOnFront((((*flags) >> 0u) & 0x01u) == 0x01u); + section->setDrawOnBack((((*flags) >> 1u) & 0x01u) == 0x01u); + section->setClearBeforeDrawing((((*flags) >> 2u) & 0x01u) == 0x01u); section->m_elements.reserve(*elementCount); for (std::size_t i = 0; i < *elementCount; ++i) { auto type = peekU16LE(sectionData); @@ -1356,9 +1821,9 @@ std::expected, ParseError> TimeBasedDrawn auto sectionData = buffer.subspan(0, *size - 24); buffer = buffer.subspan(*size - 24); auto section = std::make_unique(); - section->setDrawOnFront(((*flags) >> 0u) & 0x01u == 0x01u); - section->setDrawOnBack(((*flags) >> 1u) & 0x01u == 0x01u); - section->setClearBeforeDrawing(((*flags) >> 2u) & 0x01u == 0x01u); + section->setDrawOnFront((((*flags) >> 0u) & 0x01u) == 0x01u); + section->setDrawOnBack((((*flags) >> 1u) & 0x01u) == 0x01u); + section->setClearBeforeDrawing((((*flags) >> 2u) & 0x01u) == 0x01u); section->setStartTimestamp(std::bit_cast(*startTimestamp)); section->setEndTimestamp(std::bit_cast(*endTimestamp)); section->m_elements.reserve(*elementCount); @@ -1509,10 +1974,14 @@ std::expected parseFile(std::span data) { std::expected, ParseError> section; switch (static_cast(*sectionType)) { - case SectionType::AlwaysDrawn: section = AlwaysDrawnSection::parse(data); break; - case SectionType::TimeBasedDrawn: section = TimeBasedDrawnSection::parse(data); break; - case SectionType::CustomFont: section = CustomFontSection::parse(data); break; - default: return std::unexpected(ParseError::InvalidValue); + case SectionType::AlwaysDrawn: section = AlwaysDrawnSection::parse(data); break; + case SectionType::TimeBasedDrawn: section = TimeBasedDrawnSection::parse(data); break; + case SectionType::CustomFont: section = CustomFontSection::parse(data); break; + default: return std::unexpected(ParseError::InvalidValue); + } + + if (!section) { + return std::unexpected(section.error()); } result.sections.push_back(std::move(*section)); diff --git a/cpp/src/monoformat_structured.hpp b/cpp/src/monoformat_structured.hpp index 6ade4a9..6de27b5 100644 --- a/cpp/src/monoformat_structured.hpp +++ b/cpp/src/monoformat_structured.hpp @@ -26,6 +26,8 @@ protected: Section() = default; }; +class CustomFontSection; + enum class ElementType { Image = 1, Animation = 2, @@ -50,7 +52,7 @@ struct Element { virtual ~Element() = default; virtual ElementType elementType() const = 0; virtual std::size_t serializeTo(std::span target) const = 0; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) = 0; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) = 0; protected: Element() = default; @@ -78,6 +80,19 @@ private: std::span m_buffer; }; +struct ClippedImage : public OneBitBufferInterface { + ClippedImage(OneBitBufferInterface* underlying, std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height); + virtual ~ClippedImage() override; + virtual bool isPixelSet(std::uint16_t x, std::uint16_t y) const override; + virtual void setPixel(std::uint16_t x, std::uint16_t y, bool value) override; +private: + OneBitBufferInterface* m_underlying{}; + std::uint16_t m_x{}; + std::uint16_t m_y{}; + std::uint16_t m_width{}; + std::uint16_t m_height{}; +}; + struct ImageElement : public Element { ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height); @@ -94,7 +109,7 @@ struct ImageElement : public Element { virtual ~ImageElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -126,7 +141,7 @@ struct AnimationElement : public Element { virtual ~AnimationElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -159,7 +174,7 @@ struct HScrollImageElement : public Element { virtual ~HScrollImageElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -193,7 +208,7 @@ struct VScrollImageElement : public Element { virtual ~VScrollImageElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -225,7 +240,7 @@ struct LineElement : public Element { virtual ~LineElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -251,7 +266,7 @@ struct ClippedTextElement : public Element { virtual ~ClippedTextElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -279,7 +294,7 @@ struct HScrollTextElement : public Element { virtual ~HScrollTextElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); @@ -308,7 +323,7 @@ struct CurrentTimeElement : public Element { virtual ~CurrentTimeElement() override; virtual ElementType elementType() const override; virtual std::size_t serializeTo(std::span target) const override; - virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) override; + virtual void drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span customFonts) override; static std::expected, ParseError> parse(std::span& buffer); diff --git a/cpp/src/monoformat_utf8.hpp b/cpp/src/monoformat_utf8.hpp index 25eff58..2ceb11d 100644 --- a/cpp/src/monoformat_utf8.hpp +++ b/cpp/src/monoformat_utf8.hpp @@ -200,7 +200,7 @@ inline UTF8Iterator begin(UTF8Iterator const& a) noexcept { return a; } -inline UTF8Iterator end(UTF8Iterator const& b) noexcept { +inline UTF8Iterator end(UTF8Iterator const&) noexcept { return UTF8Iterator{}; }