|
|
|
|
@ -1,4 +1,7 @@ |
|
|
|
|
#include "monoformat_structured.hpp" |
|
|
|
|
#include "monoformat_fontreader.hpp" |
|
|
|
|
|
|
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
|
|
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<std::byte> 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<CustomFontSection const*> 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<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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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<CustomFontSection const*> customFonts) { |
|
|
|
|
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) { |
|
|
|
|
@ -421,8 +473,108 @@ std::size_t HScrollImageElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> 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<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) { |
|
|
|
|
@ -565,8 +717,108 @@ std::size_t VScrollImageElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> 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<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) { |
|
|
|
|
@ -678,8 +930,30 @@ std::size_t LineElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) { |
|
|
|
|
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) { |
|
|
|
|
@ -775,8 +1049,34 @@ std::size_t ClippedTextElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) { |
|
|
|
|
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) { |
|
|
|
|
@ -887,8 +1187,114 @@ std::size_t HScrollTextElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) { |
|
|
|
|
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) { |
|
|
|
|
@ -1000,8 +1406,67 @@ std::size_t CurrentTimeElement::serializeTo(std::span<std::byte> 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<CustomFontSection const*> customFonts) { |
|
|
|
|
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) { |
|
|
|
|
@ -1172,9 +1637,9 @@ std::expected<std::unique_ptr<AlwaysDrawnSection>, ParseError> AlwaysDrawnSectio |
|
|
|
|
auto sectionData = buffer.subspan(0, *size - 8); |
|
|
|
|
buffer = buffer.subspan(*size - 8); |
|
|
|
|
auto section = std::make_unique<AlwaysDrawnSection>(); |
|
|
|
|
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<std::unique_ptr<TimeBasedDrawnSection>, ParseError> TimeBasedDrawn |
|
|
|
|
auto sectionData = buffer.subspan(0, *size - 24); |
|
|
|
|
buffer = buffer.subspan(*size - 24); |
|
|
|
|
auto section = std::make_unique<TimeBasedDrawnSection>(); |
|
|
|
|
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<std::int64_t>(*startTimestamp)); |
|
|
|
|
section->setEndTimestamp(std::bit_cast<std::int64_t>(*endTimestamp)); |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!section) { |
|
|
|
|
return std::unexpected(section.error()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
result.sections.push_back(std::move(*section)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|