C++: implement drawing code for all elements

pull/1/head^2
Christian Seiler 4 weeks ago
parent 41738b4a3b
commit 9237b2b1ac
  1. 5
      cpp/README.txt
  2. 2
      cpp/src/monoformat_fontreader.hpp
  3. 2
      cpp/src/monoformat_parsehelpers.hpp
  4. 513
      cpp/src/monoformat_structured.cpp
  5. 33
      cpp/src/monoformat_structured.hpp
  6. 2
      cpp/src/monoformat_utf8.hpp

@ -2,11 +2,6 @@ This is the C++ implementation of the mono display format library.
TODO: 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 - Write low-RAM parser / renderer for the format (the structured renderer
exists for high-level software) exists for high-level software)
- CMake: automatically convert standard fonts into importable data - CMake: automatically convert standard fonts into importable data

@ -122,8 +122,8 @@ private:
friend class GlyphSearcher; friend class GlyphSearcher;
std::span<std::byte const> m_data; std::span<std::byte const> m_data;
bool m_supportsBackgroundColor{};
std::uint8_t m_glyphCount{}; std::uint8_t m_glyphCount{};
bool m_supportsBackgroundColor{};
std::uint8_t m_m0{}; std::uint8_t m_m0{};
std::uint8_t m_m1{}; std::uint8_t m_m1{};
std::uint8_t m_bitCountW{}; std::uint8_t m_bitCountW{};

@ -123,7 +123,7 @@ inline std::expected<std::uint32_t, ParseError> peekU32LE(std::span<std::byte co
inline std::expected<std::uint32_t, ParseError> readU32LE(std::span<std::byte const>& buffer) { inline std::expected<std::uint32_t, ParseError> readU32LE(std::span<std::byte const>& buffer) {
auto result = peekU32LE(buffer); auto result = peekU32LE(buffer);
if (result) { if (result) {
buffer = buffer.subspan(8); buffer = buffer.subspan(4);
} }
return result; return result;
} }

@ -1,4 +1,7 @@
#include "monoformat_structured.hpp" #include "monoformat_structured.hpp"
#include "monoformat_fontreader.hpp"
#include <iostream>
namespace monoformat { namespace monoformat {
@ -72,6 +75,32 @@ void ConstMemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool va
std::ignore = value; 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) ImageElement::ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height)
: m_x{x} : m_x{x}
, m_y{y} , m_y{y}
@ -139,8 +168,16 @@ std::size_t ImageElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: draw 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<std::unique_ptr<ImageElement>, ParseError> ImageElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<ImageElement>, ParseError> ImageElement::parse(std::span<std::byte const>& buffer) {
@ -281,8 +318,23 @@ std::size_t AnimationElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: implement this std::ignore = currentTimestamp;
std::ignore = customFonts;
if (m_numberOfFrames == 0) {
return;
}
std::size_t const tick = animationTick / (static_cast<std::size_t>(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<std::unique_ptr<AnimationElement>, ParseError> AnimationElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<AnimationElement>, ParseError> AnimationElement::parse(std::span<std::byte const>& buffer) {
@ -421,8 +473,108 @@ std::size_t HScrollImageElement::serializeTo(std::span<std::byte> target) const
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME draw 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<std::int32_t>(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<std::unique_ptr<HScrollImageElement>, ParseError> HScrollImageElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<HScrollImageElement>, ParseError> HScrollImageElement::parse(std::span<std::byte const>& buffer) {
@ -565,8 +717,108 @@ std::size_t VScrollImageElement::serializeTo(std::span<std::byte> target) const
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME draw 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<std::int32_t>(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<std::unique_ptr<VScrollImageElement>, ParseError> VScrollImageElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<VScrollImageElement>, ParseError> VScrollImageElement::parse(std::span<std::byte const>& buffer) {
@ -678,8 +930,30 @@ std::size_t LineElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: draw std::ignore = animationTick;
std::ignore = currentTimestamp;
std::ignore = customFonts;
std::int32_t dx = static_cast<std::int32_t>(m_targetX) - static_cast<std::int32_t>(m_originX);
std::int32_t dy = static_cast<std::int32_t>(m_targetY) - static_cast<std::int32_t>(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<std::uint16_t>(x), static_cast<std::uint16_t>(y), value);
}
}
} }
std::expected<std::unique_ptr<LineElement>, ParseError> LineElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<LineElement>, ParseError> LineElement::parse(std::span<std::byte const>& buffer) {
@ -775,8 +1049,34 @@ std::size_t ClippedTextElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: draw std::ignore = animationTick;
std::ignore = currentTimestamp;
std::span<std::byte const> 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<std::int32_t>(renderer.lineHeight() - 1)}, target, true, false);
} }
std::expected<std::unique_ptr<ClippedTextElement>, ParseError> ClippedTextElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<ClippedTextElement>, ParseError> ClippedTextElement::parse(std::span<std::byte const>& buffer) {
@ -887,8 +1187,114 @@ std::size_t HScrollTextElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: draw std::ignore = currentTimestamp;
std::span<std::byte const> 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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(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<std::int32_t>(renderer.lineHeight() - 1)}, target, true, false);
}
} }
std::expected<std::unique_ptr<HScrollTextElement>, ParseError> HScrollTextElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<HScrollTextElement>, ParseError> HScrollTextElement::parse(std::span<std::byte const>& buffer) {
@ -1000,8 +1406,67 @@ std::size_t CurrentTimeElement::serializeTo(std::span<std::byte> target) const {
return alignNextWrite(target, pos, 4); return alignNextWrite(target, pos, 4);
} }
void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp) { void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick, std::int64_t currentTimestamp, std::span<CustomFontSection const*> customFonts) {
// FIXME: draw std::ignore = animationTick;
std::span<std::byte const> 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<std::int64_t>(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<std::int32_t>(renderer.lineHeight() - 1)}, target, true, false);
} }
std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> CurrentTimeElement::parse(std::span<std::byte const>& buffer) { std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> CurrentTimeElement::parse(std::span<std::byte const>& buffer) {
@ -1172,9 +1637,9 @@ std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> AlwaysDrawnSectio
auto sectionData = buffer.subspan(0, *size - 8); auto sectionData = buffer.subspan(0, *size - 8);
buffer = buffer.subspan(*size - 8); buffer = buffer.subspan(*size - 8);
auto section = std::make_unique<AlwaysDrawnSection>(); auto section = std::make_unique<AlwaysDrawnSection>();
section->setDrawOnFront(((*flags) >> 0u) & 0x01u == 0x01u); section->setDrawOnFront((((*flags) >> 0u) & 0x01u) == 0x01u);
section->setDrawOnBack(((*flags) >> 1u) & 0x01u == 0x01u); section->setDrawOnBack((((*flags) >> 1u) & 0x01u) == 0x01u);
section->setClearBeforeDrawing(((*flags) >> 2u) & 0x01u == 0x01u); section->setClearBeforeDrawing((((*flags) >> 2u) & 0x01u) == 0x01u);
section->m_elements.reserve(*elementCount); section->m_elements.reserve(*elementCount);
for (std::size_t i = 0; i < *elementCount; ++i) { for (std::size_t i = 0; i < *elementCount; ++i) {
auto type = peekU16LE(sectionData); auto type = peekU16LE(sectionData);
@ -1356,9 +1821,9 @@ std::expected<std::unique_ptr<TimeBasedDrawnSection>, ParseError> TimeBasedDrawn
auto sectionData = buffer.subspan(0, *size - 24); auto sectionData = buffer.subspan(0, *size - 24);
buffer = buffer.subspan(*size - 24); buffer = buffer.subspan(*size - 24);
auto section = std::make_unique<TimeBasedDrawnSection>(); auto section = std::make_unique<TimeBasedDrawnSection>();
section->setDrawOnFront(((*flags) >> 0u) & 0x01u == 0x01u); section->setDrawOnFront((((*flags) >> 0u) & 0x01u) == 0x01u);
section->setDrawOnBack(((*flags) >> 1u) & 0x01u == 0x01u); section->setDrawOnBack((((*flags) >> 1u) & 0x01u) == 0x01u);
section->setClearBeforeDrawing(((*flags) >> 2u) & 0x01u == 0x01u); section->setClearBeforeDrawing((((*flags) >> 2u) & 0x01u) == 0x01u);
section->setStartTimestamp(std::bit_cast<std::int64_t>(*startTimestamp)); section->setStartTimestamp(std::bit_cast<std::int64_t>(*startTimestamp));
section->setEndTimestamp(std::bit_cast<std::int64_t>(*endTimestamp)); section->setEndTimestamp(std::bit_cast<std::int64_t>(*endTimestamp));
section->m_elements.reserve(*elementCount); section->m_elements.reserve(*elementCount);
@ -1515,6 +1980,10 @@ std::expected<File, ParseError> parseFile(std::span<std::byte const> data) {
default: return std::unexpected(ParseError::InvalidValue); default: return std::unexpected(ParseError::InvalidValue);
} }
if (!section) {
return std::unexpected(section.error());
}
result.sections.push_back(std::move(*section)); result.sections.push_back(std::move(*section));
} }

@ -26,6 +26,8 @@ protected:
Section() = default; Section() = default;
}; };
class CustomFontSection;
enum class ElementType { enum class ElementType {
Image = 1, Image = 1,
Animation = 2, Animation = 2,
@ -50,7 +52,7 @@ struct Element {
virtual ~Element() = default; virtual ~Element() = default;
virtual ElementType elementType() const = 0; virtual ElementType elementType() const = 0;
virtual std::size_t serializeTo(std::span<std::byte> target) const = 0; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) = 0;
protected: protected:
Element() = default; Element() = default;
@ -78,6 +80,19 @@ private:
std::span<std::byte const> m_buffer; std::span<std::byte const> 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 { struct ImageElement : public Element {
ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height); 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 ~ImageElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<ImageElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<ImageElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -126,7 +141,7 @@ struct AnimationElement : public Element {
virtual ~AnimationElement() override; virtual ~AnimationElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<AnimationElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<AnimationElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -159,7 +174,7 @@ struct HScrollImageElement : public Element {
virtual ~HScrollImageElement() override; virtual ~HScrollImageElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<HScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<HScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -193,7 +208,7 @@ struct VScrollImageElement : public Element {
virtual ~VScrollImageElement() override; virtual ~VScrollImageElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<VScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<VScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -225,7 +240,7 @@ struct LineElement : public Element {
virtual ~LineElement() override; virtual ~LineElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<LineElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<LineElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -251,7 +266,7 @@ struct ClippedTextElement : public Element {
virtual ~ClippedTextElement() override; virtual ~ClippedTextElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<ClippedTextElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<ClippedTextElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -279,7 +294,7 @@ struct HScrollTextElement : public Element {
virtual ~HScrollTextElement() override; virtual ~HScrollTextElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<HScrollTextElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<HScrollTextElement>, ParseError> parse(std::span<std::byte const>& buffer);
@ -308,7 +323,7 @@ struct CurrentTimeElement : public Element {
virtual ~CurrentTimeElement() override; virtual ~CurrentTimeElement() override;
virtual ElementType elementType() const override; virtual ElementType elementType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override; virtual std::size_t serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) override;
static std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> parse(std::span<std::byte const>& buffer); static std::expected<std::unique_ptr<CurrentTimeElement>, ParseError> parse(std::span<std::byte const>& buffer);

@ -200,7 +200,7 @@ inline UTF8Iterator begin(UTF8Iterator const& a) noexcept {
return a; return a;
} }
inline UTF8Iterator end(UTF8Iterator const& b) noexcept { inline UTF8Iterator end(UTF8Iterator const&) noexcept {
return UTF8Iterator{}; return UTF8Iterator{};
} }

Loading…
Cancel
Save