Merge branch 'main' into feat/typescript

pull/1/head
flop 4 weeks ago
commit bc3e7cc7e4
  1. 4
      Specification.rst
  2. 7
      cpp/README.txt
  3. 147
      cpp/clivis/main.cpp
  4. 4
      cpp/src/CMakeLists.txt
  5. 6
      cpp/src/monoformat_fontreader.cpp
  6. 4
      cpp/src/monoformat_fontreader.hpp
  7. 123
      cpp/src/monoformat_parsehelpers.hpp
  8. 1053
      cpp/src/monoformat_parseonly.cpp
  9. 15
      cpp/src/monoformat_parseonly.hpp
  10. 103
      cpp/src/monoformat_schema.cpp
  11. 77
      cpp/src/monoformat_schema.hpp
  12. 1814
      cpp/src/monoformat_structured.cpp
  13. 96
      cpp/src/monoformat_structured.hpp
  14. 2
      cpp/src/monoformat_utf8.hpp
  15. 201
      php/monoformat.php

@ -379,7 +379,7 @@ Horizontally Scrolling Text
+------------------------+------------------------+------------------------+------------------------+
8 | Height | Flags | Scroll Speed |
+------------------------+------------------------+------------------------+------------------------+
12 | Font Index | Text ... |
12 | Font Index | Text Length |
+------------------------+------------------------+------------------------+------------------------+
... | ... Text | Padding (if required) |
+------------------------+------------------------+------------------------+------------------------+
@ -389,7 +389,7 @@ Current Time
0 1 2 3
+------------------------+------------------------+------------------------+------------------------+
0 | Type: 16 | X Offset |
0 | Type: 32 | X Offset |
+------------------------+------------------------+------------------------+------------------------+
4 | Y Offset | Width |
+------------------------+------------------------+------------------------+------------------------+

@ -2,13 +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
- Properly define a list of standard fonts and their indexes
- Properly declare license information for font parser

@ -1,47 +1,52 @@
#include <monoformat_fontreader.hpp>
#include <monoformat_structured.hpp>
#include <monoformat_parseonly.hpp>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iostream>
#include <vector>
#include <cstdint>
#include <filesystem>
#include <stdexcept>
#include <atomic>
#include <csignal>
#include <thread>
#include <chrono>
using namespace monoformat;
constexpr static std::uint16_t const ScreenWidth = 120;
constexpr static std::uint16_t const ScreenHeight = 60;
int main() {
auto fontData = findEmbeddedFont("NokiaSmallPlain_tf");
if (!fontData.size()) {
std::cerr << "Error loading font!\n" << std::flush;
return 1;
}
auto fontData2 = findEmbeddedFont("5x7_mf");
if (!fontData2.size()) {
std::cerr << "Error loading font!\n" << std::flush;
return 1;
}
std::vector<std::byte> memory;
memory.resize((ScreenWidth * ScreenHeight + 7) / 8);
MemoryOneBitBuffer screen{memory, ScreenWidth, ScreenHeight};
static std::atomic<bool> g_ctrlCPressed;
auto renderer = FontRenderer{fontData}.withIgnoreUnknownChars(true);
auto ret = renderer.render("Hallo Welt! ggg äöüß é è ê", {0, renderer.lineHeight() - 1}, screen, true, false);
if (!ret) {
std::cerr << "Error rendering.\n" << std::flush;
return 2;
static void handleCtrlC(int) {
g_ctrlCPressed.store(true, std::memory_order_relaxed);
}
auto renderer2 = FontRenderer{fontData2}.withIgnoreUnknownChars(true);
ret = renderer2.render("Hallo2", {0, renderer2.lineHeight() - 1 + renderer.lineHeight()}, screen, true, false);
if (!ret) {
std::cerr << "Error rendering.\n" << std::flush;
return 2;
template<typename Container>
static Container readEntireFile(std::filesystem::path const& fileName) {
std::ifstream fileReader(fileName, std::ios::binary | std::ios::ate | std::ios::in);
if (!fileReader) {
std::ostringstream buf;
buf << "Could not open \"" << fileName << "\" for reading";
throw std::runtime_error(buf.str());
}
auto fileSize = fileReader.tellg();
fileReader.seekg(std::ios::beg);
typename Container::size_type scalarSize = sizeof(typename Container::value_type);
Container content((static_cast<typename Container::size_type>(fileSize) + scalarSize - 1) / scalarSize,
static_cast<typename Container::value_type>(0));
fileReader.read(reinterpret_cast<char*>(&content[0]), static_cast<std::size_t>(fileSize));
return content;
}
std::cerr << "Bbox height: " << ret->boundingBox->size.height << std::endl;
static void clearScreen() {
std::cout << "\x1b[1J\x1b[1;1H" << std::flush;
}
static void renderScreeen(monoformat::OneBitBufferInterface const& imageBuffer) {
std::uint16_t const w = ScreenWidth;
std::uint16_t const h = ScreenHeight / 2;
@ -54,8 +59,8 @@ int main() {
for (std::uint16_t y = 0; y < h; ++y) {
buf << "\xE2\x94\x82";
for (std::uint16_t x = 0; x < w; ++x) {
bool const pxUpperSet = screen.isPixelSet(x, 2 * y + 0);
bool const pxLowerSet = screen.isPixelSet(x, 2 * y + 1);
bool const pxUpperSet = imageBuffer.isPixelSet(x, 2 * y + 0);
bool const pxLowerSet = imageBuffer.isPixelSet(x, 2 * y + 1);
if (pxUpperSet && pxLowerSet) {
buf << "\xE2\x96\x88";
} else if (pxUpperSet) {
@ -71,7 +76,7 @@ int main() {
if (ScreenHeight % 1) {
buf << "\xE2\x94\x82";
for (std::uint16_t x = 0; x < w; ++x) {
bool const pxUpperSet = screen.isPixelSet(x, ScreenHeight - 1);
bool const pxUpperSet = imageBuffer.isPixelSet(x, ScreenHeight - 1);
if (pxUpperSet) {
buf << "\xF0\x9F\xAC\x8E";
} else {
@ -86,6 +91,88 @@ int main() {
}
buf << "\xE2\x94\x98\n";
std::cout << buf.str();
}
static void showContents(monoformat::File const& file, std::size_t tick, std::int64_t timestamp, std::span<monoformat::CustomFontSection const*> customFonts) {
monoformat::ImageElement imageBufferElement{0, 0, ScreenWidth, ScreenHeight};
auto imageBuffer = imageBufferElement.image();
for (auto const& section : file.sections) {
if (auto sec = dynamic_cast<monoformat::AlwaysDrawnSection*>(section.get())) {
for (std::size_t i = 0; i < sec->elementCount(); ++i) {
sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts);
}
} else if (auto sec = dynamic_cast<monoformat::TimeBasedDrawnSection*>(section.get())) {
for (std::size_t i = 0; i < sec->elementCount(); ++i) {
sec->elementAt(i)->drawTo(&imageBuffer, tick, timestamp, customFonts);
}
}
}
renderScreeen(imageBuffer);
}
static void showContents2(std::span<std::byte const> data, std::size_t tick, std::int64_t timestamp) {
std::vector<std::byte> buffer;
buffer.resize((ScreenWidth * ScreenHeight + 8 - 1) / 8);
MemoryOneBitBuffer imageBuffer{buffer, ScreenWidth, ScreenHeight};
auto ret = parseAndApply(data, &imageBuffer, ScreenWidth, ScreenHeight, true, tick, timestamp);
if (!ret) {
std::cerr << "Parse & apply error\n";
}
renderScreeen(imageBuffer);
}
int main(int argc, char** argv) {
if (argc != 2 || !argv[1]) {
std::ostringstream buf;
buf << "Usage: " << argv[0] << " filename\n";
std::cerr << buf.str();
return 1;
}
if (std::string_view{argv[1]} == "--help") {
std::ostringstream buf;
buf << "Usage: " << argv[0] << " filename\n";
std::cout << buf.str() << std::flush;
return 0;
}
try {
auto contents = readEntireFile<std::vector<std::byte>>(argv[1]);
auto file = monoformat::parseFile(contents);
if (!file) {
std::ostringstream buf;
buf << "Parse error while processing the file \"" << argv[1] << "\"";
throw std::runtime_error(buf.str());
}
std::vector<monoformat::CustomFontSection const*> customFonts;
for (auto const& section : file->sections) {
if (auto fs = dynamic_cast<monoformat::CustomFontSection const*>(section.get())) {
customFonts.push_back(fs);
}
}
std::signal(SIGINT, &handleCtrlC);
std::signal(SIGTERM, &handleCtrlC);
std::size_t tick = 0;
while (!g_ctrlCPressed.load(std::memory_order_relaxed)) {
clearScreen();
//showContents(*file, tick, time(nullptr), customFonts);
showContents2(contents, tick, time(nullptr));
std::ostringstream buf;
buf << "Animation Tick: " << tick << "\n";
std::cout << buf.str() << std::flush;
std::this_thread::sleep_for(std::chrono::milliseconds{40});
++tick;
}
} catch (std::exception& e) {
std::ostringstream buf;
buf << argv[0] << ": " << e.what() << "\n";
std::cerr << buf.str();
return 2;
}
return 0;
}

@ -1,10 +1,14 @@
set(monoformat_SOURCES
monoformat_schema.cpp
monoformat_structured.cpp
monoformat_parseonly.cpp
monoformat_fontreader.cpp
)
set(monoformat_HEADERS
monoformat_schema.hpp
monoformat_structured.hpp
monoformat_parsehelpers.hpp
monoformat_parseonly.hpp
monoformat_fontreader.hpp
monoformat_gfx.hpp
monoformat_utf8.hpp

@ -5,10 +5,10 @@ namespace monoformat {
#include "u8g2_font_NokiaSmallPlain_tf.inc.cpp"
#include "u8g2_font_5x7_mf.inc.cpp"
std::span<std::byte const> findEmbeddedFont(std::string_view name) {
if (name == "NokiaSmallPlain_tf") {
std::span<std::byte const> findEmbeddedFont(std::size_t fontIndex) {
if (fontIndex == 0) {
return std::as_bytes(std::span{u8g2_font_NokiaSmallPlain_tf_u8g2font, u8g2_font_NokiaSmallPlain_tf_u8g2font_len});
} else if (name == "5x7_mf") {
} else if (fontIndex == 1) {
return std::as_bytes(std::span{u8g2_font_5x7_mf_u8g2font, u8g2_font_5x7_mf_u8g2font_len});
} else {
return {};

@ -122,8 +122,8 @@ private:
friend class GlyphSearcher;
std::span<std::byte const> 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{};
@ -652,7 +652,7 @@ inline FontRenderer::FontRenderer(std::span<std::byte const> fontData)
{
}
extern std::span<std::byte const> findEmbeddedFont(std::string_view name);
extern std::span<std::byte const> findEmbeddedFont(std::size_t fontIndex);
} // namespace monoformat

@ -5,11 +5,13 @@
#include <cstddef>
#include <expected>
#include <span>
#include <tuple> // for std::ignore
namespace monoformat {
enum class ParseError {
BufferSizeMismatch = 1,
InvalidValue = 2,
};
inline std::expected<std::uint8_t, ParseError> peekU8LE(std::span<std::byte const> buffer) {
@ -121,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) {
auto result = peekU32LE(buffer);
if (result) {
buffer = buffer.subspan(8);
buffer = buffer.subspan(4);
}
return result;
}
@ -228,6 +230,125 @@ inline std::pair<std::uint8_t, bool> overflowingShl(std::uint8_t value, std::uin
return {value << bits, overflow};
}
inline std::size_t writeU8LE(std::span<std::byte>& target, std::size_t pos, std::uint8_t value) {
if (pos < target.size()) {
target[pos] = static_cast<std::byte>(value);
}
return pos + 1;
}
inline std::size_t writeU8BE(std::span<std::byte>& target, std::size_t pos, std::uint8_t value) {
if (pos < target.size()) {
target[pos] = static_cast<std::byte>(value);
}
return pos + 1;
}
inline std::size_t writeU16LE(std::span<std::byte>& target, std::size_t pos, std::uint16_t value) {
if ((pos + 1) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 0u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 8u) & 0xffu);
}
return pos + 2;
}
inline std::size_t writeU16BE(std::span<std::byte>& target, std::size_t pos, std::uint16_t value) {
if ((pos + 1) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 0u) & 0xffu);
}
return pos + 2;
}
inline std::size_t writeU24LE(std::span<std::byte>& target, std::size_t pos, std::uint32_t value) {
if ((pos + 2) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 0u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 16u) & 0xffu);
}
return pos + 3;
}
inline std::size_t writeU24BE(std::span<std::byte>& target, std::size_t pos, std::uint32_t value) {
if ((pos + 2) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 16u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 0u) & 0xffu);
}
return pos + 3;
}
inline std::size_t writeU32LE(std::span<std::byte>& target, std::size_t pos, std::uint32_t value) {
if ((pos + 3) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 0u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 16u) & 0xffu);
target[pos + 3] = static_cast<std::byte>((value >> 24u) & 0xffu);
}
return pos + 4;
}
inline std::size_t writeU32BE(std::span<std::byte>& target, std::size_t pos, std::uint32_t value) {
if ((pos + 3) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 24u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 16u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 3] = static_cast<std::byte>((value >> 0u) & 0xffu);
}
return pos + 4;
}
inline std::size_t writeU64LE(std::span<std::byte>& target, std::size_t pos, std::uint64_t value) {
if ((pos + 7) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 0u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 16u) & 0xffu);
target[pos + 3] = static_cast<std::byte>((value >> 24u) & 0xffu);
target[pos + 4] = static_cast<std::byte>((value >> 32u) & 0xffu);
target[pos + 5] = static_cast<std::byte>((value >> 40u) & 0xffu);
target[pos + 6] = static_cast<std::byte>((value >> 48u) & 0xffu);
target[pos + 7] = static_cast<std::byte>((value >> 56u) & 0xffu);
}
return pos + 8;
}
inline std::size_t writeU64BE(std::span<std::byte>& target, std::size_t pos, std::uint64_t value) {
if ((pos + 3) < target.size()) {
target[pos + 0] = static_cast<std::byte>((value >> 56u) & 0xffu);
target[pos + 1] = static_cast<std::byte>((value >> 48u) & 0xffu);
target[pos + 2] = static_cast<std::byte>((value >> 40u) & 0xffu);
target[pos + 3] = static_cast<std::byte>((value >> 32u) & 0xffu);
target[pos + 4] = static_cast<std::byte>((value >> 24u) & 0xffu);
target[pos + 5] = static_cast<std::byte>((value >> 16u) & 0xffu);
target[pos + 6] = static_cast<std::byte>((value >> 8u) & 0xffu);
target[pos + 7] = static_cast<std::byte>((value >> 0u) & 0xffu);
}
return pos + 8;
}
inline std::size_t writeBuffer(std::span<std::byte>& target, std::size_t pos, std::span<std::byte const> value) {
if ((pos + value.size()) > target.size()) {
if (pos < target.size()) {
std::size_t const n = target.size() - pos;
std::copy(value.begin(), value.begin() + n, target.begin() + pos);
}
} else {
std::copy(value.begin(), value.end(), target.begin() + pos);
}
return pos + value.size();
}
inline std::size_t alignNextWrite(std::span<std::byte>& target, std::size_t pos, std::size_t alignment) {
std::ignore = target;
if (alignment <= 1) {
return pos;
}
if (pos % alignment == 0) {
return pos;
}
return ((pos + alignment - 1) / alignment) * alignment;
}
} // namespace monoformat
#endif // MONOFORMAT_PARSEHELPERS_HPP

File diff suppressed because it is too large Load Diff

@ -0,0 +1,15 @@
#ifndef MONOFORMAT_PARSEONLY_HPP
#define MONOFORMAT_PARSEONLY_HPP
#include "monoformat_parsehelpers.hpp"
#include "monoformat_schema.hpp"
namespace monoformat {
std::expected<void, ParseError> parseAndApply(std::span<std::byte const> data, OneBitBufferInterface* buffer,
std::uint16_t screenWidth, std::uint16_t screenHeight,
bool isFront, std::size_t animationTick, std::int64_t currentTimestamp);
} // namespace monoformat
#endif // MONOFORMAT_PARSEONLY_HPP

@ -0,0 +1,103 @@
#include "monoformat_schema.hpp"
#include <tuple>
namespace monoformat {
MemoryOneBitBuffer::MemoryOneBitBuffer(std::span<std::byte> buffer, std::uint16_t width, std::uint16_t height)
: m_width{width}
, m_height{height}
, m_buffer{buffer}
{
std::size_t expectedSize = (m_width * m_height + 8 - 1) / 8;
if (m_buffer.size() < expectedSize) {
m_width = 0;
m_height = 0;
}
}
MemoryOneBitBuffer::~MemoryOneBitBuffer() {
}
bool MemoryOneBitBuffer::isPixelSet(std::uint16_t x, std::uint16_t y) const {
if (x >= m_width || y >= m_height) {
return false;
}
std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
std::size_t byteIndex = pxIndex / 8;
std::size_t bitIndex = pxIndex % 8;
return ((static_cast<std::uint8_t>(m_buffer[byteIndex]) >> bitIndex) & 0x01) == 0x01;
}
void MemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool value) {
if (x >= m_width || y >= m_height) {
return;
}
std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
std::size_t byteIndex = pxIndex / 8;
std::size_t bitIndex = pxIndex % 8;
if (value) {
m_buffer[byteIndex] = static_cast<std::byte>(static_cast<std::uint8_t>(m_buffer[byteIndex]) | (1u << bitIndex));
} else {
m_buffer[byteIndex] = static_cast<std::byte>(static_cast<std::uint8_t>(m_buffer[byteIndex]) & ~(1u << bitIndex));
}
}
ConstMemoryOneBitBuffer::ConstMemoryOneBitBuffer(std::span<std::byte const> buffer, std::uint16_t width, std::uint16_t height)
: m_width{width}
, m_height{height}
, m_buffer{buffer}
{
std::size_t expectedSize = (m_width * m_height + 8 - 1) / 8;
if (m_buffer.size() < expectedSize) {
m_width = 0;
m_height = 0;
}
}
ConstMemoryOneBitBuffer::~ConstMemoryOneBitBuffer() {
}
bool ConstMemoryOneBitBuffer::isPixelSet(std::uint16_t x, std::uint16_t y) const {
if (x >= m_width || y >= m_height) {
return false;
}
std::size_t pxIndex = static_cast<std::size_t>(y) * m_width + x;
std::size_t byteIndex = pxIndex / 8;
std::size_t bitIndex = pxIndex % 8;
return ((static_cast<std::uint8_t>(m_buffer[byteIndex]) >> bitIndex) & 0x01) == 0x01;
}
void ConstMemoryOneBitBuffer::setPixel(std::uint16_t x, std::uint16_t y, bool value) {
std::ignore = x;
std::ignore = y;
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);
}
} // namespace monoformat

@ -0,0 +1,77 @@
#ifndef MONOFORMAT_SCHEMA_HPP
#define MONOFORMAT_SCHEMA_HPP
#include <cstddef>
#include <cstdint>
#include <span>
namespace monoformat {
enum class SectionType {
AlwaysDrawn = 1,
TimeBasedDrawn = 2,
CustomFont = 32,
};
enum class ElementType {
Image = 1,
Animation = 2,
HScrollImage = 3,
VScrollImage = 4,
Line = 5,
ClippedText = 16,
HScrollText = 17,
//VScrollText = 18,
CurrentTime = 32,
};
enum class LineStyle : std::uint8_t {
Solid = 0,
};
struct OneBitBufferInterface {
using Color = bool;
virtual ~OneBitBufferInterface() = default;
virtual bool isPixelSet(std::uint16_t x, std::uint16_t y) const = 0;
virtual void setPixel(std::uint16_t x, std::uint16_t y, bool value) = 0;
};
struct MemoryOneBitBuffer : public OneBitBufferInterface {
MemoryOneBitBuffer(std::span<std::byte> buffer, std::uint16_t width, std::uint16_t height);
virtual ~MemoryOneBitBuffer() 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:
std::uint16_t m_width{};
std::uint16_t m_height{};
std::span<std::byte> m_buffer;
};
struct ConstMemoryOneBitBuffer : public OneBitBufferInterface {
ConstMemoryOneBitBuffer(std::span<std::byte const> buffer, std::uint16_t width, std::uint16_t height);
virtual ~ConstMemoryOneBitBuffer() 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:
std::uint16_t m_width{};
std::uint16_t m_height{};
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{};
};
} // namespace monoformat
#endif // MONOFORMAT_SCHEMA_HPP

File diff suppressed because it is too large Load Diff

@ -2,6 +2,7 @@
#define MONOFORMAT_STRUCTURED_HPP
#include "monoformat_parsehelpers.hpp"
#include "monoformat_schema.hpp"
#include <cstddef>
#include <cstdint>
@ -11,12 +12,6 @@
namespace monoformat {
enum class SectionType {
AlwaysDrawn = 1,
TimeBasedDrawn = 2,
CustomFont = 32,
};
struct Section {
virtual ~Section() = default;
virtual SectionType sectionType() const = 0;
@ -26,58 +21,18 @@ protected:
Section() = default;
};
enum class ElementType {
Image = 1,
Animation = 2,
HScrollImage = 3,
VScrollImage = 4,
Line = 5,
ClippedText = 16,
HScrollText = 17,
//VScrollText = 18,
CurrentTime = 32,
};
struct OneBitBufferInterface {
using Color = bool;
virtual ~OneBitBufferInterface() = default;
virtual bool isPixelSet(std::uint16_t x, std::uint16_t y) const = 0;
virtual void setPixel(std::uint16_t x, std::uint16_t y, bool value) = 0;
};
class CustomFontSection;
struct Element {
virtual ~Element() = default;
virtual ElementType elementType() 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:
Element() = default;
};
struct MemoryOneBitBuffer : public OneBitBufferInterface {
MemoryOneBitBuffer(std::span<std::byte> buffer, std::uint16_t width, std::uint16_t height);
virtual ~MemoryOneBitBuffer() 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:
std::uint16_t m_width{};
std::uint16_t m_height{};
std::span<std::byte> m_buffer;
};
struct ConstMemoryOneBitBuffer : public OneBitBufferInterface {
ConstMemoryOneBitBuffer(std::span<std::byte const> buffer, std::uint16_t width, std::uint16_t height);
virtual ~ConstMemoryOneBitBuffer() 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:
std::uint16_t m_width{};
std::uint16_t m_height{};
std::span<std::byte const> m_buffer;
};
struct ImageElement : public Element {
ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height);
@ -89,11 +44,12 @@ struct ImageElement : public Element {
std::span<std::byte const> buffer() const noexcept;
MemoryOneBitBuffer image() noexcept;
ConstMemoryOneBitBuffer image() const noexcept;
void updateBuffer(std::span<std::byte const> newBufferData);
virtual ~ImageElement() override;
virtual ElementType elementType() 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);
@ -120,11 +76,12 @@ struct AnimationElement : public Element {
std::span<std::byte const> 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<std::byte const> newBufferData);
virtual ~AnimationElement() override;
virtual ElementType elementType() 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);
@ -152,11 +109,12 @@ struct HScrollImageElement : public Element {
std::span<std::byte const> buffer() const noexcept;
MemoryOneBitBuffer image() noexcept;
ConstMemoryOneBitBuffer image() const noexcept;
void updateBuffer(std::span<std::byte const> newBufferData);
virtual ~HScrollImageElement() override;
virtual ElementType elementType() 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);
@ -185,11 +143,12 @@ struct VScrollImageElement : public Element {
std::span<std::byte const> buffer() const noexcept;
MemoryOneBitBuffer image() noexcept;
ConstMemoryOneBitBuffer image() const noexcept;
void updateBuffer(std::span<std::byte const> newBufferData);
virtual ~VScrollImageElement() override;
virtual ElementType elementType() 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);
@ -204,10 +163,6 @@ private:
std::vector<std::byte> m_buffer;
};
enum class LineStyle : std::uint8_t {
Solid = 0,
};
struct LineElement : public Element {
LineElement(std::uint16_t originX, std::uint16_t originY, std::uint16_t targetX, std::uint16_t targetY, LineStyle lineStyle, std::uint8_t flags);
@ -221,7 +176,7 @@ struct LineElement : public Element {
virtual ~LineElement() override;
virtual ElementType elementType() 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);
@ -247,7 +202,7 @@ struct ClippedTextElement : public Element {
virtual ~ClippedTextElement() override;
virtual ElementType elementType() 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);
@ -275,7 +230,7 @@ struct HScrollTextElement : public Element {
virtual ~HScrollTextElement() override;
virtual ElementType elementType() 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);
@ -304,7 +259,7 @@ struct CurrentTimeElement : public Element {
virtual ~CurrentTimeElement() override;
virtual ElementType elementType() 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);
@ -331,7 +286,7 @@ struct AlwaysDrawnSection : public Section {
void setClearBeforeDrawing(bool value) noexcept;
std::size_t elementCount() const noexcept;
std::unique_ptr<Element> elementAt(std::size_t index) const;
Element* elementAt(std::size_t index) const;
void appendElement(std::unique_ptr<Element> element);
void insertElement(std::size_t index, std::unique_ptr<Element> element);
@ -363,17 +318,17 @@ struct TimeBasedDrawnSection : public Section {
void setClearBeforeDrawing(bool value) noexcept;
std::size_t elementCount() const noexcept;
std::unique_ptr<Element> elementAt(std::size_t index) const;
Element* elementAt(std::size_t index) const;
void appendElement(std::unique_ptr<Element> element);
void insertElement(std::size_t index, std::unique_ptr<Element> element);
std::unique_ptr<Element> replaceElement(std::size_t index, std::unique_ptr<Element> element);
std::unique_ptr<Element> eraseElement(std::size_t index);
std::uint64_t startTimestamp() const noexcept;
std::uint64_t endTimestamp() const noexcept;
void setStartTimestamp(std::uint64_t value);
void setEndTimestamp(std::uint64_t value);
std::int64_t startTimestamp() const noexcept;
std::int64_t endTimestamp() const noexcept;
void setStartTimestamp(std::int64_t value);
void setEndTimestamp(std::int64_t value);
virtual SectionType sectionType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override;
@ -382,8 +337,8 @@ struct TimeBasedDrawnSection : public Section {
private:
std::vector<std::unique_ptr<Element>> m_elements;
std::uint64_t m_startTimestamp{};
std::uint64_t m_endTimestamp{};
std::int64_t m_startTimestamp{};
std::int64_t m_endTimestamp{};
bool m_drawOnFront{true};
bool m_drawOnBack{true};
bool m_clearBeforeDrawing{true};
@ -393,6 +348,9 @@ struct CustomFontSection : public Section {
CustomFontSection() = default;
virtual ~CustomFontSection() override;
std::span<std::byte const> fontData() const;
void setFontData(std::span<std::byte const> fontData);
virtual SectionType sectionType() const override;
virtual std::size_t serializeTo(std::span<std::byte> target) const override;
@ -406,8 +364,8 @@ struct File {
std::vector<std::unique_ptr<Section>> sections;
};
extern std::size_t serializeFile(std::span<std::byte>& buffer, File const& file);
extern std::expected<File, ParseError> parseFile(std::span<std::byte const> data);
extern std::size_t serializeFile(std::span<std::byte> buffer);
} // namespace monoformat

@ -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{};
}

@ -0,0 +1,201 @@
<?php
namespace monoformat {
enum SectionType : int {
case AlwaysDrawn = 1;
case TimeBasedDrawn = 2;
case CustomFont = 32;
}
enum ElementType : int {
case Image = 1;
case Animation = 2;
case HScrollImage = 3;
case VScrollImage = 4;
case Line = 5;
case ClippedText = 16;
case HScrollText = 17;
case CurrentTime = 32;
}
enum LineStyle : int {
case Solid = 0;
}
class Element {
public function serialize() {
return "";
}
}
class HScrollTextElement extends Element {
public $x = 0;
public $y = 0;
public $width = 0;
public $height = 0;
public $flags = 0;
public $scrollSpeed = 0;
public $fontIndex = 0;
public $text = "";
public function serialize() {
$len = strlen($this->text);
$result = pack("vvvvvCCvv",
17,
$this->x,
$this->y,
$this->width,
$this->height,
$this->flags,
$this->scrollSpeed,
$this->fontIndex,
$len);
$result .= (string) $this->text;
if ($len % 4 != 0) {
$n = 4 - ($len % 4);
for ($i = 0; $i < $n; ++$i) {
$result .= chr(0);
}
}
return $result;
}
}
class CurrentTimeElement extends Element {
public $x = 0;
public $y = 0;
public $width = 0;
public $height = 0;
public $fontIndex = 0;
public $utcOffset = 0;
public $flags = 0;
public function serialize() {
$result = pack("vvvvvvvv",
32,
$this->x,
$this->y,
$this->width,
$this->height,
$this->fontIndex,
$this->utcOffset,
$this->flags);
return $result;
}
}
class Section {
public function serialize() {
return "";
}
}
class AlwaysDrawnSection extends Section {
public $drawOnFront = false;
public $drawOnBack = false;
public $clearBeforeDrawing = false;
public $elements = [];
public function serialize() {
$flags = 0;
if ($this->drawOnFront) {
$flags |= 0x01;
}
if ($this->drawOnBack) {
$flags |= 0x02;
}
if ($this->clearBeforeDrawing) {
$flags |= 0x04;
}
$inner = pack("vv", $flags, count($this->elements));
foreach ($this->elements as $element) {
$inner .= $element->serialize();
}
$len = strlen($inner) + 4;
$len = substr(pack("V", $len), 0, 3);
return pack("C", 1) . $len . $inner;
}
}
class File {
public $sections = [];
public function serialize() {
$nSections = count($this->sections);
$result = "\xAF\x7E\x2B\x63";
$result .= pack("Vvv", 1, $nSections, 0);
foreach ($this->sections as $section) {
$result .= $section->serialize();
}
return $result;
}
}
} // namespace monoformat
namespace {
$roomName= "BOOL";
$data = json_decode(file_get_contents("https://cfp.cttue.de/tdf5/schedule/export/schedule.json"), true);
$talks = [];
$now = new DateTimeImmutable("now");
foreach ($data["schedule"]["conference"]["days"] as $day) {
foreach ($day["rooms"][$roomName] as $t) {
[$h, $m] = explode(":", $t["duration"]);
$duration = $h * 3600 + $m * 60;
$duration = DateInterval::createFromDateString("$duration sec");
$talk = [
"date" => new DateTimeImmutable($t["date"]),
"duration" => $duration,
"title" => $t["title"],
];
$talkEnd = $talk["date"]->add($talk["duration"]);
if ($talkEnd < $now) {
continue;
}
if ($talk["date"] > $now && count($talks) > 2) {
break;
}
array_push($talks, $talk);
}
}
$elements = [];
function putText($x, $y, $width, $height, $text) {
global $elements;
$e = new monoformat\HScrollTextElement();
$e->x = $x;
$e->y = $y;
$e->width = $width;
$e->height = $height;
$e->flags = 0;
$e->scrollSpeed = 15;
$e->fontIndex = 0;
$e->text = $text;
array_push($elements, $e);
}
$i = 0;
foreach ($talks as $talk) {
putText(10, $i * 20, 25, 20, $talk["date"]->format("H:i"));
putText(35, $i * 20, 85, 20, $talk["title"]);
++$i;
}
$section = new monoformat\AlwaysDrawnSection();
$section->drawOnFront = true;
$section->drawOnBack = true;
$section->clearBeforeDrawing = true;
$section->elements = $elements;
$file = new monoformat\File();
array_push($file->sections, $section);
print($file->serialize());
} // global
?>
Loading…
Cancel
Save