diff --git a/cpp/src/monoformat_parsehelpers.hpp b/cpp/src/monoformat_parsehelpers.hpp index 9e2ca4c..5cd269c 100644 --- a/cpp/src/monoformat_parsehelpers.hpp +++ b/cpp/src/monoformat_parsehelpers.hpp @@ -11,6 +11,7 @@ namespace monoformat { enum class ParseError { BufferSizeMismatch = 1, + InvalidValue = 2, }; inline std::expected peekU8LE(std::span buffer) { diff --git a/cpp/src/monoformat_structured.cpp b/cpp/src/monoformat_structured.cpp index 0ac5475..511d3e5 100644 --- a/cpp/src/monoformat_structured.cpp +++ b/cpp/src/monoformat_structured.cpp @@ -114,6 +114,12 @@ ConstMemoryOneBitBuffer ImageElement::image() const noexcept { return ConstMemoryOneBitBuffer{m_buffer, m_width, m_height}; } +void ImageElement::updateBuffer(std::span newBufferData) { + std::size_t n = std::min(m_buffer.size(), newBufferData.size()); + std::copy(newBufferData.begin(), newBufferData.begin() + n, m_buffer.begin()); + std::fill(m_buffer.begin() + n, m_buffer.end(), std::byte{}); +} + ImageElement::~ImageElement() { } @@ -138,7 +144,46 @@ void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animat } std::expected, ParseError> ImageElement::parse(std::span& buffer) { - // FIXME: parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::Image)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto reserved_ = readU16LE(buffer); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + std::size_t imageSize = ((*width) * (*height) + 8 - 1) / 8; + auto imageData = readBuffer(buffer, imageSize); + if (!imageData) { + return std::unexpected(reserved_.error()); + } + std::size_t rounded = (imageSize + 4 - 1) / 4 * 4; + if (imageSize < rounded) { + buffer = buffer.subspan(rounded - imageSize); + } + + auto result = std::make_unique(*x, *y, *width, *height); + result->updateBuffer(*imageData); + return result; } AnimationElement::AnimationElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t numberOfFrames, std::uint16_t updateInterval) @@ -209,6 +254,12 @@ ConstMemoryOneBitBuffer AnimationElement::image(std::uint16_t frameIndex) const return ConstMemoryOneBitBuffer{buffer(frameIndex), m_width, m_height}; } +void AnimationElement::updateBuffer(std::span newBufferData) { + std::size_t n = std::min(m_buffer.size(), newBufferData.size()); + std::copy(newBufferData.begin(), newBufferData.begin() + n, m_buffer.begin()); + std::fill(m_buffer.begin() + n, m_buffer.end(), std::byte{}); +} + AnimationElement::~AnimationElement() { } @@ -235,7 +286,54 @@ void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t an } std::expected, ParseError> AnimationElement::parse(std::span& buffer) { - // FIXME: implement this + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::Animation)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto numberOfFrames = readU16LE(buffer); + if (!numberOfFrames) { + return std::unexpected(numberOfFrames.error()); + } + auto updateInterval = readU16LE(buffer); + if (!updateInterval) { + return std::unexpected(updateInterval.error()); + } + auto reserved_ = readU16LE(buffer); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + std::size_t imageSize = ((*width) * (*height) + 8 - 1) / 8; + auto imageData = readBuffer(buffer, imageSize); + if (!imageData) { + return std::unexpected(reserved_.error()); + } + std::size_t rounded = (imageSize + 4 - 1) / 4 * 4; + if (imageSize < rounded) { + buffer = buffer.subspan(rounded - imageSize); + } + + auto result = std::make_unique(*x, *y, *width, *height, *numberOfFrames, *updateInterval); + result->updateBuffer(*imageData); + return result; } HScrollImageElement::HScrollImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t contentWidth, std::uint8_t flags, std::uint8_t scrollSpeed) @@ -295,6 +393,12 @@ ConstMemoryOneBitBuffer HScrollImageElement::image() const noexcept { return ConstMemoryOneBitBuffer{m_buffer, m_contentWidth, m_height}; } +void HScrollImageElement::updateBuffer(std::span newBufferData) { + std::size_t n = std::min(m_buffer.size(), newBufferData.size()); + std::copy(newBufferData.begin(), newBufferData.begin() + n, m_buffer.begin()); + std::fill(m_buffer.begin() + n, m_buffer.end(), std::byte{}); +} + HScrollImageElement::~HScrollImageElement() { } @@ -322,7 +426,58 @@ void HScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t } std::expected, ParseError> HScrollImageElement::parse(std::span& buffer) { - // FIXME parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::HScrollImage)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto contentWidth = readU16LE(buffer); + if (!contentWidth) { + return std::unexpected(contentWidth.error()); + } + auto flags = readU8LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto scrollSpeed = readU8LE(buffer); + if (!scrollSpeed) { + return std::unexpected(scrollSpeed.error()); + } + auto reserved_ = readU16LE(buffer); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + std::size_t imageSize = ((*contentWidth) * (*height) + 8 - 1) / 8; + auto imageData = readBuffer(buffer, imageSize); + if (!imageData) { + return std::unexpected(reserved_.error()); + } + std::size_t rounded = (imageSize + 4 - 1) / 4 * 4; + if (imageSize < rounded) { + buffer = buffer.subspan(rounded - imageSize); + } + + auto result = std::make_unique(*x, *y, *width, *height, *contentWidth, *flags, *scrollSpeed); + result->updateBuffer(*imageData); + return result; } VScrollImageElement::VScrollImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t contentHeight, std::uint8_t flags, std::uint8_t scrollSpeed) @@ -382,6 +537,12 @@ ConstMemoryOneBitBuffer VScrollImageElement::image() const noexcept { return ConstMemoryOneBitBuffer{m_buffer, m_width, m_contentHeight}; } +void VScrollImageElement::updateBuffer(std::span newBufferData) { + std::size_t n = std::min(m_buffer.size(), newBufferData.size()); + std::copy(newBufferData.begin(), newBufferData.begin() + n, m_buffer.begin()); + std::fill(m_buffer.begin() + n, m_buffer.end(), std::byte{}); +} + VScrollImageElement::~VScrollImageElement() { } @@ -409,7 +570,58 @@ void VScrollImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t } std::expected, ParseError> VScrollImageElement::parse(std::span& buffer) { - // FIXME parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::VScrollImage)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto contentHeight = readU16LE(buffer); + if (!contentHeight) { + return std::unexpected(contentHeight.error()); + } + auto flags = readU8LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto scrollSpeed = readU8LE(buffer); + if (!scrollSpeed) { + return std::unexpected(scrollSpeed.error()); + } + auto reserved_ = readU16LE(buffer); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + std::size_t imageSize = ((*width) * (*contentHeight) + 8 - 1) / 8; + auto imageData = readBuffer(buffer, imageSize); + if (!imageData) { + return std::unexpected(reserved_.error()); + } + std::size_t rounded = (imageSize + 4 - 1) / 4 * 4; + if (imageSize < rounded) { + buffer = buffer.subspan(rounded - imageSize); + } + + auto result = std::make_unique(*x, *y, *width, *height, *contentHeight, *flags, *scrollSpeed); + result->updateBuffer(*imageData); + return result; } @@ -471,7 +683,42 @@ void LineElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animati } std::expected, ParseError> LineElement::parse(std::span& buffer) { - // FIXME: parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::Line)) { + return std::unexpected(ParseError::InvalidValue); + } + auto originX = readU16LE(buffer); + if (!originX) { + return std::unexpected(originX.error()); + } + auto originY = readU16LE(buffer); + if (!originY) { + return std::unexpected(originY.error()); + } + auto targetX = readU16LE(buffer); + if (!targetX) { + return std::unexpected(targetX.error()); + } + auto targetY = readU16LE(buffer); + if (!targetY) { + return std::unexpected(targetY.error()); + } + auto lineStyle = readU8LE(buffer); + if (!lineStyle) { + return std::unexpected(lineStyle.error()); + } + if (*lineStyle != static_cast(LineStyle::Solid)) { + return std::unexpected(ParseError::InvalidValue); + } + auto flags = readU8LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto result = std::make_unique(*originX, *originY, *targetX, *targetY, static_cast(*lineStyle), *flags); + return result; } ClippedTextElement::ClippedTextElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t fontIndex, std::string text) @@ -533,7 +780,45 @@ void ClippedTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t } std::expected, ParseError> ClippedTextElement::parse(std::span& buffer) { - // FIXME: parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::ClippedText)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto fontIndex = readU16LE(buffer); + if (!fontIndex) { + return std::unexpected(fontIndex.error()); + } + auto textLength = readU16LE(buffer); + if (!textLength) { + return std::unexpected(textLength.error()); + } + std::size_t alignedRoundedUp = (*textLength + 2 + 4 - 1) / 4 * 4 - 2; + auto textData = readBuffer(buffer, alignedRoundedUp); + if (!textData) { + return std::unexpected(textData.error()); + } + std::string_view text{reinterpret_cast(textData->data()), *textLength}; + auto result = std::make_unique(*x, *y, *width, *height, *fontIndex, std::string{text}); + return result; } HScrollTextElement::HScrollTextElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint8_t flags, std::uint8_t scrollSpeed, std::uint16_t fontIndex, std::string text) @@ -607,7 +892,53 @@ void HScrollTextElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t } std::expected, ParseError> HScrollTextElement::parse(std::span& buffer) { - // FIXME: parse + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::HScrollText)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto flags = readU8LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto scrollSpeed = readU8LE(buffer); + if (!scrollSpeed) { + return std::unexpected(scrollSpeed.error()); + } + auto fontIndex = readU16LE(buffer); + if (!fontIndex) { + return std::unexpected(fontIndex.error()); + } + auto textLength = readU16LE(buffer); + if (!textLength) { + return std::unexpected(textLength.error()); + } + std::size_t alignedRoundedUp = (*textLength + 4 - 1) / 4 * 4; + auto textData = readBuffer(buffer, alignedRoundedUp); + if (!textData) { + return std::unexpected(textData.error()); + } + std::string_view text{reinterpret_cast(textData->data()), *textLength}; + auto result = std::make_unique(*x, *y, *width, *height, *flags, *scrollSpeed, *fontIndex, std::string{text}); + return result; } CurrentTimeElement::CurrentTimeElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t fontIndex, std::uint16_t utcOffset, std::uint16_t flags) @@ -674,7 +1005,43 @@ void CurrentTimeElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t } std::expected, ParseError> CurrentTimeElement::parse(std::span& buffer) { - // FIXME: current time + auto type = readU16LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(ElementType::CurrentTime)) { + return std::unexpected(ParseError::InvalidValue); + } + auto x = readU16LE(buffer); + if (!x) { + return std::unexpected(x.error()); + } + auto y = readU16LE(buffer); + if (!y) { + return std::unexpected(y.error()); + } + auto width = readU16LE(buffer); + if (!width) { + return std::unexpected(width.error()); + } + auto height = readU16LE(buffer); + if (!height) { + return std::unexpected(height.error()); + } + auto fontIndex = readU16LE(buffer); + if (!fontIndex) { + return std::unexpected(fontIndex.error()); + } + auto utcOffset = readU16LE(buffer); + if (!utcOffset) { + return std::unexpected(utcOffset.error()); + } + auto flags = readU16LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto result = std::make_unique(*x, *y, *width, *height, *fontIndex, *utcOffset, *flags); + return result; } AlwaysDrawnSection::~AlwaysDrawnSection() { @@ -780,7 +1147,59 @@ std::size_t AlwaysDrawnSection::serializeTo(std::span target) const { } std::expected, ParseError> AlwaysDrawnSection::parse(std::span& buffer) { - // FIXME: parse + auto type = readU8LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(SectionType::AlwaysDrawn)) { + return std::unexpected(ParseError::InvalidValue); + } + auto size = readU24LE(buffer); + if (!size) { + return std::unexpected(size.error()); + } + if (*size < 8) { + return std::unexpected(ParseError::InvalidValue); + } + auto flags = readU16LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto elementCount = readU16LE(buffer); + if (!elementCount) { + return std::unexpected(elementCount.error()); + } + 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->m_elements.reserve(*elementCount); + for (std::size_t i = 0; i < *elementCount; ++i) { + auto type = peekU16LE(sectionData); + if (!type) { + return std::unexpected(type.error()); + } + std::expected, ParseError> element; + switch (static_cast(*type)) { + case ElementType::Image: element = ImageElement::parse(sectionData); break; + case ElementType::Animation: element = AnimationElement::parse(sectionData); break; + case ElementType::HScrollImage: element = HScrollImageElement::parse(sectionData); break; + case ElementType::VScrollImage: element = VScrollImageElement::parse(sectionData); break; + case ElementType::Line: element = LineElement::parse(sectionData); break; + case ElementType::ClippedText: element = ClippedTextElement::parse(sectionData); break; + case ElementType::HScrollText: element = HScrollTextElement::parse(sectionData); break; + case ElementType::CurrentTime: element = CurrentTimeElement::parse(sectionData); break; + default: + return std::unexpected(ParseError::InvalidValue); + } + if (!element) { + return std::unexpected(element.error()); + } + section->m_elements.push_back(std::move(*element)); + } + return section; } TimeBasedDrawnSection::~TimeBasedDrawnSection() { @@ -904,7 +1323,69 @@ std::size_t TimeBasedDrawnSection::serializeTo(std::span target) cons } std::expected, ParseError> TimeBasedDrawnSection::parse(std::span& buffer) { - // FIXME: parse + auto type = readU8LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(SectionType::TimeBasedDrawn)) { + return std::unexpected(ParseError::InvalidValue); + } + auto size = readU24LE(buffer); + if (!size) { + return std::unexpected(size.error()); + } + if (*size < 24) { + return std::unexpected(ParseError::InvalidValue); + } + auto flags = readU16LE(buffer); + if (!flags) { + return std::unexpected(flags.error()); + } + auto elementCount = readU16LE(buffer); + if (!elementCount) { + return std::unexpected(elementCount.error()); + } + auto startTimestamp = readU64LE(buffer); + if (!startTimestamp) { + return std::unexpected(startTimestamp.error()); + } + auto endTimestamp = readU64LE(buffer); + if (!endTimestamp) { + return std::unexpected(endTimestamp.error()); + } + 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->setStartTimestamp(std::bit_cast(*startTimestamp)); + section->setEndTimestamp(std::bit_cast(*endTimestamp)); + section->m_elements.reserve(*elementCount); + for (std::size_t i = 0; i < *elementCount; ++i) { + auto type = peekU16LE(sectionData); + if (!type) { + return std::unexpected(type.error()); + } + std::expected, ParseError> element; + switch (static_cast(*type)) { + case ElementType::Image: element = ImageElement::parse(sectionData); break; + case ElementType::Animation: element = AnimationElement::parse(sectionData); break; + case ElementType::HScrollImage: element = HScrollImageElement::parse(sectionData); break; + case ElementType::VScrollImage: element = VScrollImageElement::parse(sectionData); break; + case ElementType::Line: element = LineElement::parse(sectionData); break; + case ElementType::ClippedText: element = ClippedTextElement::parse(sectionData); break; + case ElementType::HScrollText: element = HScrollTextElement::parse(sectionData); break; + case ElementType::CurrentTime: element = CurrentTimeElement::parse(sectionData); break; + default: + return std::unexpected(ParseError::InvalidValue); + } + if (!element) { + return std::unexpected(element.error()); + } + section->m_elements.push_back(std::move(*element)); + } + return section; } CustomFontSection::~CustomFontSection() { @@ -937,7 +1418,39 @@ std::size_t CustomFontSection::serializeTo(std::span target) const { } std::expected, ParseError> CustomFontSection::parse(std::span& buffer) { - // FIXME: parse + auto type = readU8LE(buffer); + if (!type) { + return std::unexpected(type.error()); + } + if (*type != static_cast(SectionType::CustomFont)) { + return std::unexpected(ParseError::InvalidValue); + } + auto size = readU24LE(buffer); + if (!size) { + return std::unexpected(size.error()); + } + if (*size < 8) { + return std::unexpected(ParseError::InvalidValue); + } + auto actualSize = readU24LE(buffer); + if (!actualSize) { + return std::unexpected(actualSize.error()); + } + auto reserved_ = readU8LE(buffer); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + auto fontData = buffer.subspan(0, *size - 8); + buffer = buffer.subspan(*size - 8); + + auto actualFontData = readBuffer(fontData, *actualSize); + if (!actualFontData) { + return std::unexpected(actualFontData.error()); + } + + auto section = std::make_unique(); + section->setFontData(*actualFontData); + return section; } std::size_t serializeFile(std::span& buffer, File const& file) { @@ -961,7 +1474,51 @@ std::size_t serializeFile(std::span& buffer, File const& file) { } std::expected parseFile(std::span data) { - // FIXME: parse + auto magic = readU32LE(data); + if (!magic) { + return std::unexpected(magic.error()); + } + if (*magic != UINT32_C(0x632B7EAF)) { + return std::unexpected(ParseError::InvalidValue); + } + auto version = readU32LE(data); + if (!version) { + return std::unexpected(version.error()); + } + if (*version != 1) { + return std::unexpected(ParseError::InvalidValue); + } + auto sectionCount = readU16LE(data); + if (!sectionCount) { + return std::unexpected(sectionCount.error()); + } + auto reserved_ = readU16LE(data); + if (!reserved_) { + return std::unexpected(reserved_.error()); + } + + File result; + result.sections.reserve(*sectionCount); + + for (std::size_t i = 0; i < *sectionCount; ++i) { + auto sectionType = peekU8LE(data); + if (!sectionType) { + return std::unexpected(sectionType.error()); + } + + 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); + } + + result.sections.push_back(std::move(*section)); + } + + return result; } } // namespace monoformat diff --git a/cpp/src/monoformat_structured.hpp b/cpp/src/monoformat_structured.hpp index e3a6436..6ade4a9 100644 --- a/cpp/src/monoformat_structured.hpp +++ b/cpp/src/monoformat_structured.hpp @@ -89,6 +89,7 @@ struct ImageElement : public Element { std::span buffer() const noexcept; MemoryOneBitBuffer image() noexcept; ConstMemoryOneBitBuffer image() const noexcept; + void updateBuffer(std::span newBufferData); virtual ~ImageElement() override; virtual ElementType elementType() const override; @@ -120,6 +121,7 @@ struct AnimationElement : public Element { std::span buffer(std::uint16_t frameIndex) const noexcept; MemoryOneBitBuffer image(std::uint16_t frameIndex) noexcept; ConstMemoryOneBitBuffer image(std::uint16_t frameIndex) const noexcept; + void updateBuffer(std::span newBufferData); virtual ~AnimationElement() override; virtual ElementType elementType() const override; @@ -152,6 +154,7 @@ struct HScrollImageElement : public Element { std::span buffer() const noexcept; MemoryOneBitBuffer image() noexcept; ConstMemoryOneBitBuffer image() const noexcept; + void updateBuffer(std::span newBufferData); virtual ~HScrollImageElement() override; virtual ElementType elementType() const override; @@ -185,6 +188,7 @@ struct VScrollImageElement : public Element { std::span buffer() const noexcept; MemoryOneBitBuffer image() noexcept; ConstMemoryOneBitBuffer image() const noexcept; + void updateBuffer(std::span newBufferData); virtual ~VScrollImageElement() override; virtual ElementType elementType() const override;