commit
25df6ad780
@ -0,0 +1,3 @@ |
||||
build/ |
||||
*.o |
||||
*~ |
||||
@ -0,0 +1,419 @@ |
||||
Mono Display File Format Specification |
||||
###################################### |
||||
|
||||
Generic Considerations |
||||
====================== |
||||
|
||||
Fundamental Unit |
||||
---------------- |
||||
|
||||
The fundamental data unit of a file in this format is an octet, i.e. 8 bits. |
||||
|
||||
Endianness |
||||
---------- |
||||
|
||||
All numbers larger than a single octet are stored in **little endian** format. |
||||
|
||||
Pixel Data |
||||
---------- |
||||
|
||||
Pixel data is always stored as raw 2D mono (1-bit) pixel images in the following manner: |
||||
|
||||
- The raw 2D stream has the size ``(width_i * height_i + 8 - 1) / 8`` octets, where ``width_i`` is the width of the image, ``height_i`` the height of the image, and ``/`` is the standard integer division that always rounds down. (In Python 3 this would be the ``//`` operator.) This rounds the size of the raw 2D stream to a full octet. The excess pixels SHOULD be set to ``0`` by software writing this format, but MUST be ignored by software reading this format. |
||||
- We define a pixel index ``px_i = y_i * width_i + x_i``, where ``x_i`` is the x position of the pixel in the image, ``y_i`` the y position within the image, and ``width_i`` the width of the image. |
||||
- Each pixel index ``px_i`` can be decomposed into an octed index, ``px_i_o``, and a bit index ``px_i_b``, which are given by the formulas ``px_i_o = px_i / 8`` and ``px_i_b = px_i % 8``, where ``/`` and ``%`` are the integer division and modulo, respectively. A bit index of ``0`` indicates the least significant bit, and a bit index of ``7`` the most significant bit in the octet. |
||||
- Accessing the value of a pixel index can be done via ``value = (data_i[px_i_o] >> px_i_b) & 0x01``, where ``data_i`` is the array of octets containing the 2D data. |
||||
- If more than one 2D image is stored consecutively (for example for an animation) each 2D image will always start at an octet boundary. Hence while a new line within an image might not start at the boundary of an octet, images are always aligned to entire octets. |
||||
|
||||
Draw Area |
||||
--------- |
||||
|
||||
The area that is drawn to is defined by the device and is irrespective of the data in this file. If the image data in the file exceeds the dimensions of the draw area of the device then the images will be clipped when drawn (but this is not considered to be an error). |
||||
|
||||
Text |
||||
---- |
||||
|
||||
Text is stored as UTF-8 strings with preceding length information in some manner. |
||||
|
||||
Alignment |
||||
--------- |
||||
|
||||
All sections and elements are aligned to 32 bits. In doubt padding bytes with values ``0`` MUST be inserted. |
||||
|
||||
Fonts |
||||
----- |
||||
|
||||
? |
||||
|
||||
File Header |
||||
=========== |
||||
|
||||
The file header of a mono display file looks like the following: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Magic 0xAF | Magic 0x7E | Magic 0x2B | Magic 0x63 | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Version | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Number of Sections | Reserved | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
Magic |
||||
----- |
||||
|
||||
The file magic is the sequence of bytes ``0xAF 0x7E 0x2B 0x63``. |
||||
|
||||
File Version |
||||
------------ |
||||
|
||||
This field indicates the version of the file. This allows a reader to determine if it supports the specific file. Any reader that supports a specific version MUST implement all mandatory features of that version. |
||||
|
||||
The following possible values for the version field exist: |
||||
|
||||
+-----------+--------------------------------------------+ |
||||
| Value | Description | |
||||
+===========+============================================+ |
||||
| ``0`` | Illegal | |
||||
+-----------+--------------------------------------------+ |
||||
| ``1`` | The current version of this specification. | |
||||
+-----------+--------------------------------------------+ |
||||
| ``2`` .. | Reserved for future use | |
||||
+-----------+--------------------------------------------+ |
||||
|
||||
A value of ``0`` is illegal for the version field and indivates a problem with the file. The current specification version is ``1``. Writers that conform to this specification **MUST** use this value. |
||||
|
||||
Number of Sections |
||||
------------------ |
||||
|
||||
This field indicates how many sections are present in this file. A value of ``0`` is valid, but indicates the file is empty. |
||||
|
||||
Reserved |
||||
-------- |
||||
|
||||
This field is reserved for future use. Writers MUST set this field to ``0``. |
||||
|
||||
Sections |
||||
======== |
||||
|
||||
A section consists of a section header, followed by the data of the section. The section header has the following structure: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Section Type | Size of the Section | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
Section Type |
||||
------------ |
||||
|
||||
The section type specifies what kind of section this is. The following section types are defined: |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Value | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | Illegal | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` | List of drawn elements (always) | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``2`` | List of drawn elements (timespan) | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``3`` .. | Reserved for future use | |
||||
| ``31`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``32`` | Custom Font | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``33`` .. | Reserved for future use | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
See below for the individual sections. |
||||
|
||||
Size of the Section |
||||
------------------- |
||||
|
||||
This 24bit value indicates the number of octets that contains the size of the section (including the 4 octets of the section header). This allows reader implementations to easily skip sections to iterate over the entire file. |
||||
|
||||
List of drawn elements (always) |
||||
------------------------------- |
||||
|
||||
This section contains a list of elements that are to be drawn. The section contains the following additional header: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Flags | Number of Elements | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
Flags |
||||
````` |
||||
|
||||
The following flags are currently valid (bit ``0` is the least significant bit): |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Bit | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | Draw on the front side | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` | Draw on the back side | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``2`` | Clear the image buffer(s) before drawing | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``3`` .. | Reserved for future use | |
||||
| ``15`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
If neither the bits ``0`` or ``1`` are set the section will have no effect and will be ignored. If the bit ``2`` is set, any image buffer the elements of this section will be drawn to will be cleared (set to ``0``). For example, if both bits ``0`` and ``2`` are set (i.e. the flags have the value ``0x0005``), only the front side will be cleared before the image is drawn, while the back side will not be touched. |
||||
|
||||
Writers MUST set reserved flags to zero. |
||||
|
||||
Number of Elements |
||||
`````````````````` |
||||
|
||||
This contains the number of elements to be drawn in the section. A value of ``0`` is valid - in combination with the clear flag this allows a section to simply clear buffer without doing anything else. |
||||
|
||||
List of drawn elements (timespan) |
||||
--------------------------------- |
||||
|
||||
This section is similar to the "List of drawn elements (always)" section, but the header contains two additional fields: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Flags | Number of Elements | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Start Timestamp (Low) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Start Timestamp (High) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
12 | End Timestamp (Low) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
16 | End Timestamp (High) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
The additional fields are two 64bit POSIX timestamps containing the start and end time during which this section is to be drawn. If the current timestamp is larger than or equal to the start timestamp, and the current timestamp is smaller than the end timestamp, the section will be shown, otherwise it will be skipped. |
||||
|
||||
Custom Font |
||||
----------- |
||||
|
||||
This section defines a custom font that is to be used within this file. Any custom font used by draw elements MUST occur in the file before the font is used. |
||||
|
||||
font support t.b.d. |
||||
|
||||
Elements |
||||
======== |
||||
|
||||
Each element is defined by an element header that contains the type of the element. |
||||
|
||||
2D Image |
||||
-------- |
||||
|
||||
This contains a 2D image that is to be drawn at a specific position: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 1 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Reserved | |
||||
+------------------------+------------------------+-------------------------------------------------+ |
||||
|
||||
After the header the 2D image data follows immediately. |
||||
|
||||
Animation |
||||
--------- |
||||
|
||||
This contains a sequence of 2D images of which one will be drawn at a specific position: |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 2 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Number of Frames | |
||||
+------------------------+------------------------+-------------------------------------------------+ |
||||
12 | Update Interval | Reserved | |
||||
+-------------------------------------------------+-------------------------------------------------+ |
||||
|
||||
This is followed by ``N`` 2D images (where ``N`` is the number of frames specified). A value of ``0`` is not allowed for the number of frames. |
||||
|
||||
The update interval is an unsigned 16bit integer that indicates the update interval. A value of ``0`` indicates the animation will be updated every tick, a value of ``1`` every second tick, etc., and a value of ``65535`` every 65536th tick (~ every 43 minutes). |
||||
|
||||
Horizontal Scroll |
||||
----------------- |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 3 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Content Width | |
||||
+------------------------+------------------------+-------------------------------------------------+ |
||||
12 | Flags | Scroll Speed (SS) | Reserved | |
||||
+-------------------------------------------------+-------------------------------------------------+ |
||||
|
||||
Flags |
||||
````` |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Bit | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | Endless | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` | Invert Direction | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``2`` | Pad Left | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``3`` | Pad Right | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``4`` .. | Reserved for future use | |
||||
| ``7`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
Scroll Speed |
||||
```````````` |
||||
|
||||
Every tick move by ``(SS + 1) / 16`` pixels. |
||||
|
||||
Vertical Scroll |
||||
--------------- |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 4 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Content Height | |
||||
+------------------------+------------------------+-------------------------------------------------+ |
||||
12 | Flags | Scroll Speed (SS) | Reserved | |
||||
+-------------------------------------------------+-------------------------------------------------+ |
||||
|
||||
Flags |
||||
````` |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Bit | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | Endless | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` | Invert Direction | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``2`` | Pad Top | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``3`` | Pad Bottom | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``4`` .. | Reserved for future use | |
||||
| ``7`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
Scroll Speed |
||||
```````````` |
||||
|
||||
Every tick move by ``(SS + 1) / 16`` pixels. |
||||
|
||||
Line |
||||
---- |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 5 | X Origin | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Origin | X Target | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Y Target | Line Style | Flags | |
||||
+------------------------+------------------------+-------------------------------------------------+ |
||||
|
||||
Flags |
||||
````` |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Bit | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | Invert Pixel Values | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` .. | Reserved for future use | |
||||
| ``7`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
Clipped Text |
||||
------------ |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 16 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Font Index | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
12 | Text Length | Text ... | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
... | ... Text | Padding (if required) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
Font Index |
||||
`````````` |
||||
|
||||
0 .. 32767 built-in font (may not exist) |
||||
0x8000 + custom font in file (index - 0x8000 is the number within the file) |
||||
0x8000 is the first custom font in file |
||||
0x8001 the second |
||||
etc. |
||||
|
||||
Horizontally Scrolling Text |
||||
--------------------------- |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 17 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Flags | Scroll Speed | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
12 | Font Name Size | Text Length | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
16 | Font Name ... | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
... | ... Font Name | Text ... | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
... | ... Text | Padding (if required) | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
Current Time |
||||
------------ |
||||
|
||||
0 1 2 3 |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
0 | Type: 16 | X Offset | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
4 | Y Offset | Width | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
8 | Height | Font Index | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
12 | UTC Offset (Minutes) | Flags | |
||||
+------------------------+------------------------+------------------------+------------------------+ |
||||
|
||||
UTC Offset |
||||
`````````` |
||||
|
||||
- signed 16 bit integer in minutes |
||||
|
||||
Flags |
||||
````` |
||||
|
||||
+-----------+---------------------------------------------------+ |
||||
| Bit | Description | |
||||
+===========+===================================================+ |
||||
| ``0`` | 12h clock (instead of 24h clock) | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``1`` | Show hours | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``2`` | Show minutes | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``3`` | Show seconds | |
||||
+-----------+---------------------------------------------------+ |
||||
| ``4`` .. | Reserved for future use | |
||||
| ``15`` | | |
||||
+-----------+---------------------------------------------------+ |
||||
|
||||
@ -0,0 +1,14 @@ |
||||
cmake_minimum_required(VERSION 3.15) |
||||
|
||||
project(libmonoformat CXX) |
||||
enable_testing() |
||||
|
||||
find_package(Catch2 REQUIRED) |
||||
|
||||
option(BUILD_CLI_VIS "Build CLI visualization tool" FALSE) |
||||
|
||||
add_subdirectory(src) |
||||
add_subdirectory(test) |
||||
if(${BUILD_CLI_VIS}) |
||||
add_subdirectory(clivis) |
||||
endif() |
||||
@ -0,0 +1,13 @@ |
||||
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 |
||||
@ -0,0 +1,6 @@ |
||||
set(show_monoformat_SOURCES |
||||
main.cpp |
||||
) |
||||
|
||||
add_executable(show_monoformat ${show_monoformat_SOURCES}) |
||||
target_link_libraries(show_monoformat monoformat) |
||||
@ -0,0 +1,91 @@ |
||||
#include <monoformat_fontreader.hpp> |
||||
#include <monoformat_structured.hpp> |
||||
|
||||
#include <iostream> |
||||
#include <sstream> |
||||
#include <vector> |
||||
|
||||
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}; |
||||
|
||||
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; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
std::cerr << "Bbox height: " << ret->boundingBox->size.height << std::endl; |
||||
|
||||
std::uint16_t const w = ScreenWidth; |
||||
std::uint16_t const h = ScreenHeight / 2; |
||||
|
||||
std::ostringstream buf; |
||||
buf << "\xE2\x94\x8C"; |
||||
for (std::uint16_t i = 0; i < w; ++i) { |
||||
buf << "\xE2\x94\x80"; |
||||
} |
||||
buf << "\xE2\x94\x90\n"; |
||||
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); |
||||
if (pxUpperSet && pxLowerSet) { |
||||
buf << "\xE2\x96\x88"; |
||||
} else if (pxUpperSet) { |
||||
buf << "\xF0\x9F\xAC\x8E"; |
||||
} else if (pxLowerSet) { |
||||
buf << "\xE2\x96\x84"; |
||||
} else { |
||||
buf << " "; |
||||
} |
||||
} |
||||
buf << "\xE2\x94\x82\n"; |
||||
} |
||||
if (ScreenHeight % 1) { |
||||
buf << "\xE2\x94\x82"; |
||||
for (std::uint16_t x = 0; x < w; ++x) { |
||||
bool const pxUpperSet = screen.isPixelSet(x, ScreenHeight - 1); |
||||
if (pxUpperSet) { |
||||
buf << "\xF0\x9F\xAC\x8E"; |
||||
} else { |
||||
buf << " "; |
||||
} |
||||
} |
||||
buf << "\xE2\x94\x82\n"; |
||||
} |
||||
buf << "\xE2\x94\x94"; |
||||
for (std::uint16_t i = 0; i < w; ++i) { |
||||
buf << "\xE2\x94\x80"; |
||||
} |
||||
buf << "\xE2\x94\x98\n"; |
||||
std::cout << buf.str(); |
||||
|
||||
return 0; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
set(monoformat_SOURCES |
||||
monoformat_structured.cpp |
||||
monoformat_fontreader.cpp |
||||
) |
||||
set(monoformat_HEADERS |
||||
monoformat_structured.hpp |
||||
monoformat_parsehelpers.hpp |
||||
monoformat_fontreader.hpp |
||||
monoformat_gfx.hpp |
||||
monoformat_utf8.hpp |
||||
) |
||||
|
||||
add_library(monoformat ${monoformat_SOURCES} ${monoformat_HEADERS}) |
||||
target_compile_features(monoformat PUBLIC cxx_std_23) |
||||
target_include_directories(monoformat PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) |
||||
|
||||
|
||||
install(TARGETS monoformat |
||||
RUNTIME DESTINATION bin |
||||
ARCHIVE DESTINATION lib |
||||
LIBRARY DESTINATION lib) |
||||
install(FILES ${monoformat_HEADERS} |
||||
DESTINATION include) |
||||
@ -0,0 +1,18 @@ |
||||
#include "monoformat_fontreader.hpp" |
||||
|
||||
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") { |
||||
return std::as_bytes(std::span{u8g2_font_NokiaSmallPlain_tf_u8g2font, u8g2_font_NokiaSmallPlain_tf_u8g2font_len}); |
||||
} else if (name == "5x7_mf") { |
||||
return std::as_bytes(std::span{u8g2_font_5x7_mf_u8g2font, u8g2_font_5x7_mf_u8g2font_len}); |
||||
} else { |
||||
return {}; |
||||
} |
||||
} |
||||
|
||||
} // namespace monoformat
|
||||
@ -0,0 +1,659 @@ |
||||
/* This is a port of <https://github.com/Finomnis/u8g2-fonts/blob/main/src/font_reader/> to C++ from Rust.
|
||||
* It is not complete but implements the most important parts. |
||||
*/ |
||||
|
||||
#ifndef MONOFORMAT_FONTREADER_HPP |
||||
#define MONOFORMAT_FONTREADER_HPP |
||||
|
||||
#include "monoformat_parsehelpers.hpp" |
||||
#include "monoformat_gfx.hpp" |
||||
#include "monoformat_utf8.hpp" |
||||
|
||||
#include <cassert> |
||||
#include <cstdint> |
||||
#include <cstddef> |
||||
#include <optional> |
||||
#include <span> |
||||
#include <string_view> |
||||
#include <limits> |
||||
|
||||
namespace monoformat { |
||||
|
||||
class GlyphReader; |
||||
|
||||
enum class LookupError { |
||||
GlyphNotFound = 0, |
||||
InvalidUnicodeSequence = 1, |
||||
}; |
||||
|
||||
enum class RenderError { |
||||
Unknown = -1, |
||||
}; |
||||
|
||||
struct UnicodeJumptableEntry { |
||||
std::uint16_t jumpDistance{}; |
||||
std::uint16_t characterUpperLimit{}; |
||||
}; |
||||
|
||||
class UnicodeJumptableReader { |
||||
public: |
||||
explicit UnicodeJumptableReader(std::span<std::byte const> data) |
||||
: m_data{data} |
||||
{ |
||||
} |
||||
|
||||
std::optional<std::size_t> calculateJumpOffset(std::uint16_t encoding) { |
||||
std::size_t jumpOffset = 0; |
||||
|
||||
std::optional<UnicodeJumptableEntry> entry; |
||||
do { |
||||
entry = nextEntry(); |
||||
if (!entry) { |
||||
return std::nullopt; |
||||
} |
||||
|
||||
jumpOffset += entry->jumpDistance; |
||||
} while (entry->characterUpperLimit < encoding); |
||||
|
||||
return jumpOffset; |
||||
} |
||||
|
||||
std::optional<UnicodeJumptableEntry> nextEntry() { |
||||
auto jumpDistance_ = readU16BE(m_data); |
||||
auto characterUpperLimit_ = readU16BE(m_data); |
||||
if (!jumpDistance_ || !characterUpperLimit_) { |
||||
return std::nullopt; |
||||
} |
||||
return UnicodeJumptableEntry{*jumpDistance_, *characterUpperLimit_}; |
||||
} |
||||
|
||||
private: |
||||
std::span<std::byte const> m_data; |
||||
}; |
||||
|
||||
template<std::size_t CharWidth> |
||||
class GlyphSearcher; |
||||
|
||||
class FontReader { |
||||
public: |
||||
explicit FontReader(std::span<std::byte const> data); |
||||
|
||||
bool supportsBackgroundColor() const noexcept { return m_supportsBackgroundColor; } |
||||
std::uint8_t glyphCount() const noexcept { return m_glyphCount; } |
||||
std::uint8_t m0() const noexcept { return m_m0; } |
||||
std::uint8_t m1() const noexcept { return m_m1; } |
||||
std::uint8_t bitCountW() const noexcept { return m_bitCountW; } |
||||
std::uint8_t bitCountH() const noexcept { return m_bitCountH; } |
||||
std::uint8_t bitCountX() const noexcept { return m_bitCountX; } |
||||
std::uint8_t bitCountY() const noexcept { return m_bitCountY; } |
||||
std::uint8_t bitCountD() const noexcept { return m_bitCountD; } |
||||
std::uint8_t fontBoundingBoxWidth() const noexcept { return m_fontBoundingBoxWidth; } |
||||
std::int8_t fontBoundingBoxHeight() const noexcept { return m_fontBoundingBoxHeight; } |
||||
std::int8_t fontBoundingBoxXOffset() const noexcept { return m_fontBoundingBoxXOffset; } |
||||
std::int8_t fontBoundingBoxYOffset() const noexcept { return m_fontBoundingBoxYOffset; } |
||||
std::int8_t ascent() const noexcept { return m_ascent; } |
||||
std::int8_t descent() const noexcept { return m_descent; } |
||||
std::int8_t ascentOfParentheses() const noexcept { return m_ascentOfParentheses; } |
||||
std::int8_t descentOfParentheses() const noexcept { return m_descentOfParentheses; } |
||||
std::uint16_t arrayOffsetLowerA() const noexcept { return m_arrayOffsetLowerA; } |
||||
std::uint16_t arrayOffsetUpperA() const noexcept { return m_arrayOffsetUpperA; } |
||||
std::uint16_t arrayOffset0x0100() const noexcept { return m_arrayOffset0x0100; } |
||||
bool doesIgnoreUnknownGlyphs() const noexcept { return m_ignoreUnknownGlyphs; } |
||||
std::uint32_t lineHeight() const noexcept { return m_lineHeight; } |
||||
|
||||
std::uint8_t getDefaultLineHeight() const; |
||||
|
||||
FontReader withIgnoreUnknownGlyphs(bool ignore) && { |
||||
m_ignoreUnknownGlyphs = ignore; |
||||
return *this; |
||||
} |
||||
|
||||
FontReader withLineHeight(std::uint32_t lineHeight) && { |
||||
m_lineHeight = lineHeight; |
||||
return *this; |
||||
} |
||||
|
||||
std::expected<std::optional<GlyphReader>, LookupError> tryRetrieveGlyphData(std::uint32_t ch) const; |
||||
std::expected<GlyphReader, LookupError> retrieveGlyphData(std::uint32_t ch) const; |
||||
|
||||
private: |
||||
friend class GlyphReader; |
||||
template<std::size_t CharWidth> |
||||
friend class GlyphSearcher; |
||||
|
||||
std::span<std::byte const> m_data; |
||||
bool m_supportsBackgroundColor{}; |
||||
std::uint8_t m_glyphCount{}; |
||||
std::uint8_t m_m0{}; |
||||
std::uint8_t m_m1{}; |
||||
std::uint8_t m_bitCountW{}; |
||||
std::uint8_t m_bitCountH{}; |
||||
std::uint8_t m_bitCountX{}; |
||||
std::uint8_t m_bitCountY{}; |
||||
std::uint8_t m_bitCountD{}; |
||||
std::int8_t m_fontBoundingBoxWidth{}; |
||||
std::int8_t m_fontBoundingBoxHeight{}; |
||||
std::int8_t m_fontBoundingBoxXOffset{}; |
||||
std::int8_t m_fontBoundingBoxYOffset{}; |
||||
std::int8_t m_ascent{}; |
||||
std::int8_t m_descent{}; |
||||
std::int8_t m_ascentOfParentheses{}; |
||||
std::int8_t m_descentOfParentheses{}; |
||||
std::uint16_t m_arrayOffsetUpperA{}; |
||||
std::uint16_t m_arrayOffsetLowerA{}; |
||||
std::uint16_t m_arrayOffset0x0100{}; |
||||
bool m_ignoreUnknownGlyphs{}; |
||||
std::uint32_t m_lineHeight{}; |
||||
}; |
||||
|
||||
class GlyphReader { |
||||
public: |
||||
explicit GlyphReader(std::span<std::byte const> data, FontReader const& font) |
||||
: m_data{data} |
||||
, m_bitCount0{font.m_m0} |
||||
, m_bitCount1{font.m_m1} |
||||
{ |
||||
m_glyphWidth = readUnsigned(font.m_bitCountW); |
||||
m_glyphHeight = readUnsigned(font.m_bitCountH); |
||||
m_offsetX = readSigned(font.m_bitCountX); |
||||
m_offsetY = readSigned(font.m_bitCountY); |
||||
m_advance = readSigned(font.m_bitCountD); |
||||
} |
||||
|
||||
std::uint8_t readUnsigned(std::uint8_t bits) { |
||||
std::uint8_t const bitStart = m_bitPos; |
||||
std::uint8_t bitEnd = bitStart + bits; |
||||
|
||||
auto value = overflowingShr(m_currentByte, bitStart).first; |
||||
if (bitEnd >= 8) { |
||||
auto value2 = static_cast<std::uint8_t>(m_data[0]); |
||||
m_data = m_data.subspan(1); |
||||
bitEnd -= 8; |
||||
m_currentByte = value2; |
||||
|
||||
value |= overflowingShl(value2, 8 - bitStart).first; |
||||
} |
||||
|
||||
m_bitPos = bitEnd; |
||||
|
||||
return value & static_cast<std::uint8_t>((std::uint16_t{1} << bits) - 1); |
||||
} |
||||
|
||||
std::int8_t readSigned(std::uint8_t bits) { |
||||
return static_cast<std::int8_t>(readUnsigned(bits) - static_cast<std::uint8_t>(1 << (bits - 1))); |
||||
} |
||||
|
||||
Point topLeft(Point pos) const { |
||||
return { |
||||
pos.x + m_offsetX, |
||||
pos.y - (m_glyphHeight + m_offsetY), |
||||
}; |
||||
} |
||||
|
||||
std::int32_t left(std::int32_t posX) const { |
||||
return posX + m_offsetX; |
||||
} |
||||
|
||||
Size size() const { |
||||
return {m_glyphWidth, m_glyphHeight}; |
||||
} |
||||
|
||||
std::uint32_t width() const { |
||||
return m_glyphWidth; |
||||
} |
||||
|
||||
std::uint32_t advance() const { |
||||
return m_advance; |
||||
} |
||||
|
||||
std::uint8_t readRunLength0() { |
||||
return readUnsigned(m_bitCount0); |
||||
} |
||||
|
||||
std::uint8_t readRunLength1() { |
||||
return readUnsigned(m_bitCount1); |
||||
} |
||||
|
||||
private: |
||||
std::span<std::byte const> m_data; |
||||
// Start at 8 to mark the current byte as invalid
|
||||
std::uint8_t m_bitPos{8}; |
||||
std::uint8_t m_currentByte{0}; |
||||
std::uint8_t m_glyphWidth{}; |
||||
std::uint8_t m_glyphHeight{}; |
||||
std::int8_t m_offsetX{}; |
||||
std::int8_t m_offsetY{}; |
||||
std::int8_t m_advance{}; |
||||
std::uint8_t m_bitCount0{}; |
||||
std::uint8_t m_bitCount1{}; |
||||
}; |
||||
|
||||
template<std::size_t CharWidth> |
||||
class GlyphSearcherBase { |
||||
public: |
||||
void jumpBy(std::size_t offset) { |
||||
m_data = m_data.subspan(offset); |
||||
} |
||||
|
||||
std::uint8_t getOffset() const { |
||||
return static_cast<std::uint8_t>(m_data[CharWidth]); |
||||
} |
||||
|
||||
bool jumpToNext() { |
||||
auto offset = getOffset(); |
||||
if (offset == 0) { |
||||
return false; |
||||
} else { |
||||
jumpBy(offset); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
GlyphReader intoGlyphReader() && { |
||||
return GlyphReader{m_data.subspan(CharWidth + 1), m_font}; |
||||
} |
||||
|
||||
protected: |
||||
GlyphSearcherBase(std::span<std::byte const> data, FontReader const& font) |
||||
: m_data{data} |
||||
, m_font{font} |
||||
{ |
||||
} |
||||
|
||||
std::span<std::byte const> m_data; |
||||
FontReader const& m_font; |
||||
}; |
||||
|
||||
template<> |
||||
class GlyphSearcher<2> : public GlyphSearcherBase<2> { |
||||
public: |
||||
std::uint16_t getCh() const { |
||||
return peekU16BE(m_data).value(); |
||||
} |
||||
|
||||
private: |
||||
explicit GlyphSearcher(std::span<std::byte const> data, FontReader const& font) |
||||
: GlyphSearcherBase<2>{data, font} |
||||
{ |
||||
} |
||||
|
||||
template<std::size_t CharWidth> |
||||
friend class GlyphSearcher; |
||||
}; |
||||
|
||||
template<> |
||||
class GlyphSearcher<1> : public GlyphSearcherBase<1> { |
||||
constexpr static std::size_t const U8G2FontDataStructSize{23}; |
||||
|
||||
public: |
||||
explicit GlyphSearcher(FontReader const& font) |
||||
: GlyphSearcherBase<1>{font.m_data.subspan(U8G2FontDataStructSize), font} |
||||
{ |
||||
} |
||||
|
||||
std::uint8_t getCh() const { |
||||
return static_cast<std::uint8_t>(m_data[0]); |
||||
} |
||||
|
||||
std::pair<GlyphSearcher<2>, UnicodeJumptableReader> intoUnicodeMode(std::uint16_t offset) && { |
||||
jumpBy(offset); |
||||
return {GlyphSearcher<2>{m_data, m_font}, UnicodeJumptableReader{m_data}}; |
||||
} |
||||
}; |
||||
|
||||
inline FontReader::FontReader(std::span<std::byte const> data) |
||||
: m_data{data} |
||||
, m_glyphCount{static_cast<std::uint8_t>(data[0])} |
||||
, m_supportsBackgroundColor{data[1] != std::byte{}} |
||||
, m_m0{static_cast<std::uint8_t>(data[2])} |
||||
, m_m1{static_cast<std::uint8_t>(data[3])} |
||||
, m_bitCountW{static_cast<std::uint8_t>(data[4])} |
||||
, m_bitCountH{static_cast<std::uint8_t>(data[5])} |
||||
, m_bitCountX{static_cast<std::uint8_t>(data[6])} |
||||
, m_bitCountY{static_cast<std::uint8_t>(data[7])} |
||||
, m_bitCountD{static_cast<std::uint8_t>(data[8])} |
||||
, m_fontBoundingBoxWidth{static_cast<std::int8_t>(data[9])} |
||||
, m_fontBoundingBoxHeight{static_cast<std::int8_t>(data[10])} |
||||
, m_fontBoundingBoxXOffset{static_cast<std::int8_t>(data[11])} |
||||
, m_fontBoundingBoxYOffset{static_cast<std::int8_t>(data[12])} |
||||
, m_ascent{static_cast<std::int8_t>(data[13])} |
||||
, m_descent{static_cast<std::int8_t>(data[14])} |
||||
, m_ascentOfParentheses{static_cast<std::int8_t>(data[15])} |
||||
, m_descentOfParentheses{static_cast<std::int8_t>(data[16])} |
||||
, m_arrayOffsetUpperA{*peekU16BE(data.subspan(17, 2))} |
||||
, m_arrayOffsetLowerA{*peekU16BE(data.subspan(19, 2))} |
||||
, m_arrayOffset0x0100{*peekU16BE(data.subspan(21, 2))} |
||||
, m_ignoreUnknownGlyphs{false} |
||||
{ |
||||
m_lineHeight = getDefaultLineHeight(); |
||||
} |
||||
|
||||
inline std::uint8_t FontReader::getDefaultLineHeight() const { |
||||
assert(m_fontBoundingBoxHeight >= 0); |
||||
return static_cast<std::uint8_t>(m_fontBoundingBoxHeight) + 1; |
||||
} |
||||
|
||||
inline std::expected<std::optional<GlyphReader>, LookupError> FontReader::tryRetrieveGlyphData(std::uint32_t ch) const { |
||||
auto ret = retrieveGlyphData(ch); |
||||
if (!ret) { |
||||
if (m_ignoreUnknownGlyphs && ret.error() == LookupError::GlyphNotFound) { |
||||
return std::nullopt; |
||||
} |
||||
return std::unexpected(ret.error()); |
||||
} |
||||
return *ret; |
||||
} |
||||
|
||||
inline std::expected<GlyphReader, LookupError> FontReader::retrieveGlyphData(std::uint32_t ch) const { |
||||
if (ch > std::numeric_limits<std::uint16_t>::max()) { |
||||
return std::unexpected(LookupError::GlyphNotFound); |
||||
} |
||||
|
||||
auto glyph = GlyphSearcher<1>(*this); |
||||
|
||||
if (ch <= 255) { |
||||
if (ch >= 'a') { |
||||
glyph.jumpBy(m_arrayOffsetLowerA); |
||||
} else if (ch >= 'A') { |
||||
glyph.jumpBy(m_arrayOffsetUpperA); |
||||
} |
||||
|
||||
while (glyph.getCh() != ch) { |
||||
bool ok = glyph.jumpToNext(); |
||||
if (!ok) { |
||||
return std::unexpected(LookupError::GlyphNotFound); |
||||
} |
||||
} |
||||
|
||||
return std::move(glyph).intoGlyphReader(); |
||||
} else { |
||||
auto [gl, unicodeJumpTable] = std::move(glyph).intoUnicodeMode(m_arrayOffset0x0100); |
||||
|
||||
auto jumpOffset_ = unicodeJumpTable.calculateJumpOffset(ch); |
||||
if (!jumpOffset_) { |
||||
return std::unexpected(LookupError::GlyphNotFound); |
||||
} |
||||
|
||||
gl.jumpBy(*jumpOffset_); |
||||
|
||||
while (true) { |
||||
auto glCh = gl.getCh(); |
||||
if (glCh == 0) { |
||||
return std::unexpected(LookupError::GlyphNotFound); |
||||
} |
||||
if (glCh == ch) { |
||||
break; |
||||
} |
||||
if (!gl.jumpToNext()) { |
||||
return std::unexpected(LookupError::GlyphNotFound); |
||||
} |
||||
} |
||||
|
||||
return std::move(gl).intoGlyphReader(); |
||||
} |
||||
} |
||||
|
||||
class GlyphRenderer { |
||||
public: |
||||
explicit GlyphRenderer(GlyphReader const& glyph) |
||||
: m_glyph{glyph} |
||||
{ |
||||
} |
||||
|
||||
Rectangle getBoundingBox(Point position) const { |
||||
return Rectangle{m_glyph.topLeft(position), m_glyph.size()}; |
||||
} |
||||
|
||||
template<typename DrawTarget> |
||||
std::expected<Rectangle, RenderError> renderAsBoxFill(Point position, DrawTarget& display, typename DrawTarget::Color fgColor, typename DrawTarget::Color bgColor) { |
||||
auto boundingBox = getBoundingBox(position); |
||||
int numZeros = m_glyph.readRunLength0(); |
||||
int numOnes = m_glyph.readRunLength1(); |
||||
int numZerosLeftOver = numZeros; |
||||
int numOnesLeftOver = numOnes; |
||||
auto colorIter = [&] () -> std::optional<typename DrawTarget::Color> { |
||||
if (numZerosLeftOver == 0 && numOnesLeftOver == 0) { |
||||
bool repeat = m_glyph.readUnsigned(1) != 0; |
||||
if (!repeat) { |
||||
numZeros = m_glyph.readRunLength0(); |
||||
numOnes = m_glyph.readRunLength1(); |
||||
} |
||||
numZerosLeftOver = numZeros; |
||||
numOnesLeftOver = numOnes; |
||||
} |
||||
|
||||
if (numZerosLeftOver > 0) { |
||||
--numZerosLeftOver; |
||||
return bgColor; |
||||
} else { |
||||
--numOnesLeftOver; |
||||
return fgColor; |
||||
} |
||||
}; |
||||
|
||||
for (std::uint32_t dy = 0; dy < boundingBox.size.height; ++dy) { |
||||
std::int32_t y = dy + boundingBox.topLeft.y; |
||||
for (std::uint32_t dx = 0; dx < boundingBox.size.width; ++dx) { |
||||
std::int32_t x = dx + boundingBox.topLeft.x; |
||||
auto color = colorIter().value_or(bgColor); |
||||
display.setPixel(x, y, color); |
||||
} |
||||
} |
||||
|
||||
return boundingBox; |
||||
} |
||||
|
||||
template<typename DrawTarget> |
||||
std::expected<Rectangle, RenderError> renderTransparent(Point position, DrawTarget& display, typename DrawTarget::Color fgColor) { |
||||
auto boundingBox = getBoundingBox(position); |
||||
auto colorIter = [&] () { |
||||
auto numZeros = m_glyph.readRunLength0(); |
||||
auto numOnes = m_glyph.readRunLength1(); |
||||
auto numZerosLeftOver = numZeros; |
||||
auto numOnesLeftOver = numOnes; |
||||
return [&] () -> std::optional<typename DrawTarget::Color> { |
||||
if (numZerosLeftOver == 0 || numOnesLeftOver == 0) { |
||||
bool repeat = m_glyph.readUnsigned(1) != 0; |
||||
if (!repeat) { |
||||
numZeros = m_glyph.readRunLength0(); |
||||
numOnes = m_glyph.readRunLength1(); |
||||
} |
||||
numZerosLeftOver = numZeros; |
||||
numOnesLeftOver = numOnes; |
||||
} |
||||
|
||||
if (numZerosLeftOver > 0) { |
||||
--numZerosLeftOver; |
||||
return std::nullopt; |
||||
} else { |
||||
--numOnesLeftOver; |
||||
return fgColor; |
||||
} |
||||
}; |
||||
}(); |
||||
|
||||
for (std::uint32_t dy = 0; dy < boundingBox.size.height; ++dy) { |
||||
std::int32_t y = dy + boundingBox.topLeft.y; |
||||
for (std::uint32_t dx = 0; dx < boundingBox.size.width; ++dx) { |
||||
std::int32_t x = dx + boundingBox.topLeft.x; |
||||
if (auto color = colorIter()) { |
||||
display.setPixel(x, y, *color); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return boundingBox; |
||||
} |
||||
|
||||
private: |
||||
GlyphReader m_glyph; |
||||
}; |
||||
|
||||
struct RenderedDimensions { |
||||
Point advance; |
||||
std::optional<Rectangle> boundingBox; |
||||
}; |
||||
|
||||
class FontRenderer { |
||||
public: |
||||
explicit FontRenderer(std::span<std::byte const> fontData); |
||||
|
||||
FontRenderer withIgnoreUnknownChars(bool ignore) && { |
||||
FontRenderer result{std::move(m_font).withIgnoreUnknownGlyphs(ignore)}; |
||||
return result; |
||||
} |
||||
|
||||
FontRenderer withLineHeight(std::uint32_t lineHeight) && { |
||||
FontRenderer result{std::move(m_font).withLineHeight(lineHeight)}; |
||||
return result; |
||||
} |
||||
|
||||
std::uint32_t lineHeight() const noexcept { |
||||
return m_font.lineHeight(); |
||||
} |
||||
|
||||
template<typename Display> |
||||
std::expected<RenderedDimensions, LookupError> render(std::string_view content, Point position, Display& display, typename Display::Color fontColor, std::optional<typename Display::Color> bgColor) const { |
||||
Point advance{0, 0}; |
||||
std::optional<Rectangle> boundingBox; |
||||
|
||||
position.y += 0; // Top alignment
|
||||
|
||||
for (std::uint32_t cp : UTF8Iterator{content}) { |
||||
if (cp == ~std::uint32_t{0}) { |
||||
if (!m_font.doesIgnoreUnknownGlyphs()) { |
||||
return std::unexpected(LookupError::InvalidUnicodeSequence); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// FIXME: newline support
|
||||
if (cp == '\n') { |
||||
cp = ' '; |
||||
} |
||||
|
||||
auto dims = renderGlyph(cp, position + advance, m_font, display, fontColor, bgColor); |
||||
if (!dims) { |
||||
return std::unexpected(dims.error()); |
||||
} |
||||
advance += dims->advance; |
||||
boundingBox = combineBoundingBoxes(boundingBox, dims->boundingBox); |
||||
} |
||||
|
||||
return RenderedDimensions{advance, boundingBox}; |
||||
} |
||||
|
||||
std::expected<RenderedDimensions, LookupError> getRenderedDimensions(std::string_view content, Point position) const { |
||||
Point advance{0, 0}; |
||||
std::optional<Rectangle> boundingBox; |
||||
|
||||
position.y += 0; // Top alignment
|
||||
|
||||
for (std::uint32_t cp : UTF8Iterator{content}) { |
||||
if (cp == ~std::uint32_t{0}) { |
||||
if (!m_font.doesIgnoreUnknownGlyphs()) { |
||||
return std::unexpected(LookupError::InvalidUnicodeSequence); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
// FIXME: newline support
|
||||
if (cp == '\n') { |
||||
cp = ' '; |
||||
} |
||||
|
||||
auto dims = computeGlyphDimensions(cp, position + advance, m_font); |
||||
if (!dims) { |
||||
return std::unexpected(dims.error()); |
||||
} |
||||
advance += dims->advance; |
||||
boundingBox = combineBoundingBoxes(boundingBox, dims->boundingBox); |
||||
} |
||||
|
||||
return RenderedDimensions{advance, boundingBox}; |
||||
} |
||||
|
||||
private: |
||||
explicit FontRenderer(FontReader&& font) |
||||
: m_font{std::move(font)} |
||||
{ |
||||
} |
||||
|
||||
static std::expected<RenderedDimensions, LookupError> computeGlyphDimensions(std::uint32_t cp, Point position, FontReader const& font) { |
||||
auto glyph_ = font.tryRetrieveGlyphData(cp); |
||||
if (!glyph_) { |
||||
return std::unexpected(glyph_.error()); |
||||
} |
||||
if (!*glyph_) { |
||||
return RenderedDimensions{}; |
||||
} |
||||
auto glyph = **glyph_; |
||||
auto advance = glyph.advance(); |
||||
auto size = glyph.size(); |
||||
|
||||
std::optional<Rectangle> boundingBox; |
||||
if (size.width > 0 && size.height > 0) { |
||||
auto renderer = GlyphRenderer{glyph}; |
||||
boundingBox = renderer.getBoundingBox(position); |
||||
} |
||||
|
||||
return RenderedDimensions{Point{static_cast<std::int32_t>(advance), 0}, boundingBox}; |
||||
} |
||||
|
||||
static std::optional<Rectangle> combineBoundingBoxes(std::optional<Rectangle> a, std::optional<Rectangle> b) { |
||||
if (!a && !b) { |
||||
return std::nullopt; |
||||
} else if (a && !b) { |
||||
return a; |
||||
} else if (b && !a) { |
||||
return b; |
||||
} else { |
||||
auto topLeftA = a->topLeft; |
||||
auto topLeftB = b->topLeft; |
||||
auto bottomRightA = a->getBottomRight(); |
||||
auto bottomRightB = b->getBottomRight(); |
||||
|
||||
Point topLeft{std::min(topLeftA.x, topLeftB.x), std::min(topLeftA.y, topLeftB.y)}; |
||||
Point bottomRight{std::max(bottomRightA.x, bottomRightB.x), std::max(bottomRightA.y, bottomRightB.y)}; |
||||
return Rectangle::fromCorners(topLeft, bottomRight); |
||||
} |
||||
} |
||||
|
||||
template<typename Display> |
||||
static std::expected<RenderedDimensions, LookupError> renderGlyph(std::uint32_t cp, Point position, FontReader const& font, Display& display, typename Display::Color fontColor, std::optional<typename Display::Color> bgColor) { |
||||
auto glyph_ = font.tryRetrieveGlyphData(cp); |
||||
if (!glyph_) { |
||||
return std::unexpected(glyph_.error()); |
||||
} |
||||
if (!*glyph_) { |
||||
return RenderedDimensions{}; |
||||
} |
||||
auto glyph = **glyph_; |
||||
auto advance = glyph.advance(); |
||||
auto size = glyph.size(); |
||||
|
||||
std::optional<Rectangle> boundingBox; |
||||
if (size.width > 0 && size.height > 0) { |
||||
auto renderer = GlyphRenderer{glyph}; |
||||
auto bbox = bgColor |
||||
? renderer.renderAsBoxFill(position, display, fontColor, *bgColor) |
||||
: renderer.renderTransparent(position, display, fontColor); |
||||
boundingBox = renderer.getBoundingBox(position); |
||||
} |
||||
|
||||
return RenderedDimensions{Point{static_cast<std::int32_t>(advance), 0}, boundingBox}; |
||||
} |
||||
|
||||
FontReader m_font; |
||||
}; |
||||
|
||||
inline FontRenderer::FontRenderer(std::span<std::byte const> fontData) |
||||
: m_font{fontData} |
||||
{ |
||||
} |
||||
|
||||
extern std::span<std::byte const> findEmbeddedFont(std::string_view name); |
||||
|
||||
} // namespace monoformat
|
||||
|
||||
#endif // MONOFORMAT_FONTREADER_HPP
|
||||
@ -0,0 +1,54 @@ |
||||
#ifndef MONOFORMAT_GFX_HPP |
||||
#define MONOFORMAT_GFX_HPP |
||||
|
||||
#include <cstdint> |
||||
|
||||
namespace monoformat { |
||||
|
||||
struct Point { |
||||
std::int32_t x{}; |
||||
std::int32_t y{}; |
||||
|
||||
friend inline Point operator+(Point const& a, Point const& b) noexcept { |
||||
return {a.x + b.x, a.y + b.y}; |
||||
} |
||||
|
||||
friend inline Point operator-(Point const& a, Point const& b) noexcept { |
||||
return {a.x - b.x, a.y - b.y}; |
||||
} |
||||
|
||||
inline Point& operator+=(Point const& b) noexcept { |
||||
x += b.x; |
||||
y += b.y; |
||||
return *this; |
||||
} |
||||
|
||||
inline Point& operator-=(Point const& b) noexcept { |
||||
x -= b.x; |
||||
y -= b.y; |
||||
return *this; |
||||
} |
||||
}; |
||||
|
||||
struct Size { |
||||
std::uint32_t width{}; |
||||
std::uint32_t height{}; |
||||
}; |
||||
|
||||
struct Rectangle { |
||||
Point topLeft{}; |
||||
Size size{}; |
||||
|
||||
Point getBottomRight() const noexcept { |
||||
return {topLeft.x + static_cast<std::int32_t>(size.width) - 1, topLeft.y + static_cast<std::int32_t>(size.height) - 1}; |
||||
} |
||||
|
||||
static Rectangle fromCorners(Point topLeft, Point bottomRight) { |
||||
Size sz{static_cast<std::uint32_t>(bottomRight.x - topLeft.x + 1), static_cast<std::uint32_t>(bottomRight.y - topLeft.y + 1)}; |
||||
return Rectangle{topLeft, sz}; |
||||
} |
||||
}; |
||||
|
||||
} // namespace monoformat
|
||||
|
||||
#endif // MONOFORMAT_GFX_HPP
|
||||
@ -0,0 +1,233 @@ |
||||
#ifndef MONOFORMAT_PARSEHELPERS_HPP |
||||
#define MONOFORMAT_PARSEHELPERS_HPP |
||||
|
||||
#include <cstdint> |
||||
#include <cstddef> |
||||
#include <expected> |
||||
#include <span> |
||||
|
||||
namespace monoformat { |
||||
|
||||
enum class ParseError { |
||||
BufferSizeMismatch = 1, |
||||
}; |
||||
|
||||
inline std::expected<std::uint8_t, ParseError> peekU8LE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 1) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return static_cast<std::uint8_t>(buffer[0]); |
||||
} |
||||
|
||||
inline std::expected<std::uint8_t, ParseError> readU8LE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU8LE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(1); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint8_t, ParseError> peekU8BE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 1) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return static_cast<std::uint8_t>(buffer[0]); |
||||
} |
||||
|
||||
inline std::expected<std::uint8_t, ParseError> readU8BE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU8BE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(1); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint16_t, ParseError> peekU16LE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 2) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint16_t>(buffer[0]) << 0u) |
||||
| (static_cast<std::uint16_t>(buffer[1]) << 8u); |
||||
} |
||||
|
||||
inline std::expected<std::uint16_t, ParseError> readU16LE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU16LE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(2); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint16_t, ParseError> peekU16BE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 2) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint16_t>(buffer[1]) << 0u) |
||||
| (static_cast<std::uint16_t>(buffer[0]) << 8u); |
||||
} |
||||
|
||||
inline std::expected<std::uint16_t, ParseError> readU16BE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU16BE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(2); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> peekU24LE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 3) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint32_t>(buffer[0]) << 0u) |
||||
| (static_cast<std::uint32_t>(buffer[1]) << 8u) |
||||
| (static_cast<std::uint32_t>(buffer[2]) << 16u); |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> readU24LE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU24LE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(3); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> peekU24BE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 3) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint32_t>(buffer[2]) << 0u) |
||||
| (static_cast<std::uint32_t>(buffer[1]) << 8u) |
||||
| (static_cast<std::uint32_t>(buffer[0]) << 16u); |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> readU24BE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU24BE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(3); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> peekU32LE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 4) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint32_t>(buffer[0]) << 0u) |
||||
| (static_cast<std::uint32_t>(buffer[1]) << 8u) |
||||
| (static_cast<std::uint32_t>(buffer[2]) << 16u) |
||||
| (static_cast<std::uint32_t>(buffer[3]) << 24u); |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> readU32LE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU32LE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(8); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> peekU32BE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 4) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint32_t>(buffer[3]) << 0u) |
||||
| (static_cast<std::uint32_t>(buffer[2]) << 8u) |
||||
| (static_cast<std::uint32_t>(buffer[1]) << 16u) |
||||
| (static_cast<std::uint32_t>(buffer[0]) << 24u); |
||||
} |
||||
|
||||
inline std::expected<std::uint32_t, ParseError> readU32BE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU32BE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(8); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint64_t, ParseError> peekU64LE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 8) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint64_t>(buffer[0]) << 0u) |
||||
| (static_cast<std::uint64_t>(buffer[1]) << 8u) |
||||
| (static_cast<std::uint64_t>(buffer[2]) << 16u) |
||||
| (static_cast<std::uint64_t>(buffer[3]) << 24u) |
||||
| (static_cast<std::uint64_t>(buffer[4]) << 32u) |
||||
| (static_cast<std::uint64_t>(buffer[5]) << 40u) |
||||
| (static_cast<std::uint64_t>(buffer[6]) << 48u) |
||||
| (static_cast<std::uint64_t>(buffer[7]) << 56u); |
||||
} |
||||
|
||||
inline std::expected<std::uint64_t, ParseError> readU64LE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU64LE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(8); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::uint64_t, ParseError> peekU64BE(std::span<std::byte const> buffer) { |
||||
if (buffer.size() < 8) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return (static_cast<std::uint64_t>(buffer[7]) << 0u) |
||||
| (static_cast<std::uint64_t>(buffer[6]) << 8u) |
||||
| (static_cast<std::uint64_t>(buffer[5]) << 16u) |
||||
| (static_cast<std::uint64_t>(buffer[4]) << 24u) |
||||
| (static_cast<std::uint64_t>(buffer[3]) << 32u) |
||||
| (static_cast<std::uint64_t>(buffer[2]) << 40u) |
||||
| (static_cast<std::uint64_t>(buffer[1]) << 48u) |
||||
| (static_cast<std::uint64_t>(buffer[0]) << 56u); |
||||
} |
||||
|
||||
inline std::expected<std::uint64_t, ParseError> readU64BE(std::span<std::byte const>& buffer) { |
||||
auto result = peekU64BE(buffer); |
||||
if (result) { |
||||
buffer = buffer.subspan(8); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<std::span<std::byte const>, ParseError> peekBuffer(std::span<std::byte const> buffer, std::size_t n) { |
||||
if (buffer.size() < n) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return buffer.subspan(0, n); |
||||
} |
||||
|
||||
inline std::expected<std::span<std::byte const>, ParseError> readBuffer(std::span<std::byte const>& buffer, std::size_t n) { |
||||
auto result = peekBuffer(buffer, n); |
||||
if (result) { |
||||
buffer = buffer.subspan(n); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
inline std::expected<void, ParseError> expectEnd(std::span<std::byte const> buffer) { |
||||
if (buffer.size() > 0) { |
||||
return std::unexpected(ParseError::BufferSizeMismatch); |
||||
} |
||||
return {}; |
||||
} |
||||
|
||||
inline std::pair<std::uint8_t, bool> overflowingShr(std::uint8_t value, std::uint8_t bits) { |
||||
bool overflow = false; |
||||
if (bits >= 8) { |
||||
bits = bits & 0x07; |
||||
overflow = true; |
||||
} |
||||
return {value >> bits, overflow}; |
||||
} |
||||
|
||||
inline std::pair<std::uint8_t, bool> overflowingShl(std::uint8_t value, std::uint8_t bits) { |
||||
bool overflow = false; |
||||
if (bits >= 8) { |
||||
bits = bits & 0x07; |
||||
overflow = true; |
||||
} |
||||
return {value << bits, overflow}; |
||||
} |
||||
|
||||
} // namespace monoformat
|
||||
|
||||
#endif // MONOFORMAT_PARSEHELPERS_HPP
|
||||
@ -0,0 +1,223 @@ |
||||
#include "monoformat_structured.hpp" |
||||
|
||||
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; |
||||
} |
||||
|
||||
ImageElement::ImageElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height) |
||||
: m_x{x} |
||||
, m_y{y} |
||||
, m_width{width} |
||||
, m_height{height} |
||||
{ |
||||
std::size_t const bufferSize = (m_width * m_height + 8 - 1) / 8;\
|
||||
m_buffer.resize(bufferSize); |
||||
} |
||||
|
||||
std::uint16_t ImageElement::x() const noexcept { |
||||
return m_x; |
||||
} |
||||
|
||||
std::uint16_t ImageElement::y() const noexcept { |
||||
return m_y; |
||||
} |
||||
|
||||
std::uint16_t ImageElement::width() const noexcept { |
||||
return m_width; |
||||
} |
||||
|
||||
std::uint16_t ImageElement::height() const noexcept { |
||||
return m_height; |
||||
} |
||||
|
||||
std::span<std::byte> ImageElement::buffer() noexcept { |
||||
return m_buffer; |
||||
} |
||||
|
||||
std::span<std::byte const> ImageElement::buffer() const noexcept { |
||||
return m_buffer; |
||||
} |
||||
|
||||
MemoryOneBitBuffer ImageElement::image() noexcept { |
||||
return MemoryOneBitBuffer{m_buffer, m_width, m_height}; |
||||
} |
||||
|
||||
ConstMemoryOneBitBuffer ImageElement::image() const noexcept { |
||||
return ConstMemoryOneBitBuffer{m_buffer, m_width, m_height}; |
||||
} |
||||
|
||||
ImageElement::~ImageElement() { |
||||
} |
||||
|
||||
ElementType ImageElement::elementType() const { |
||||
return ElementType::Image; |
||||
} |
||||
|
||||
std::size_t ImageElement::serializeTo(std::span<std::byte> target) const { |
||||
// FIXME: serialize
|
||||
} |
||||
|
||||
void ImageElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick) { |
||||
// FIXME: draw
|
||||
} |
||||
|
||||
std::expected<std::unique_ptr<ImageElement>, ParseError> ImageElement::parse(std::span<std::byte const>& buffer) { |
||||
// FIXME: parse
|
||||
} |
||||
|
||||
AnimationElement::AnimationElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t numberOfFrames, std::uint8_t updateInterval) |
||||
: m_x{x} |
||||
, m_y{y} |
||||
, m_width{width} |
||||
, m_height{height} |
||||
, m_numberOfFrames{numberOfFrames} |
||||
, m_updateInterval{updateInterval} |
||||
{ |
||||
std::size_t const imageBufferSize = (m_width * m_height + 8 - 1) / 8;\
|
||||
m_buffer.resize(imageBufferSize * numberOfFrames); |
||||
} |
||||
|
||||
std::uint16_t AnimationElement::x() const noexcept { |
||||
return m_x; |
||||
} |
||||
|
||||
std::uint16_t AnimationElement::y() const noexcept { |
||||
return m_y; |
||||
} |
||||
|
||||
std::uint16_t AnimationElement::width() const noexcept { |
||||
return m_width; |
||||
} |
||||
|
||||
std::uint16_t AnimationElement::height() const noexcept { |
||||
return m_height; |
||||
} |
||||
|
||||
std::uint16_t AnimationElement::numberOfFrames() const noexcept { |
||||
return m_numberOfFrames; |
||||
} |
||||
|
||||
std::uint8_t AnimationElement::updateInterval() const noexcept { |
||||
return m_updateInterval; |
||||
} |
||||
|
||||
std::span<std::byte> AnimationElement::buffers() noexcept { |
||||
return m_buffer; |
||||
} |
||||
|
||||
std::span<std::byte const> AnimationElement::buffers() const noexcept { |
||||
return m_buffer; |
||||
} |
||||
|
||||
std::span<std::byte> AnimationElement::buffer(std::uint16_t frameIndex) noexcept { |
||||
if (frameIndex >= m_numberOfFrames) { |
||||
return {}; |
||||
} |
||||
std::size_t const imageBufferSize = (m_width * m_height + 8 - 1) / 8; |
||||
return std::span{m_buffer}.subspan(frameIndex * imageBufferSize, imageBufferSize); |
||||
} |
||||
|
||||
std::span<std::byte const> AnimationElement::buffer(std::uint16_t frameIndex) const noexcept { |
||||
if (frameIndex >= m_numberOfFrames) { |
||||
return {}; |
||||
} |
||||
std::size_t const imageBufferSize = (m_width * m_height + 8 - 1) / 8; |
||||
return std::span{m_buffer}.subspan(frameIndex * imageBufferSize, imageBufferSize); |
||||
} |
||||
|
||||
MemoryOneBitBuffer AnimationElement::image(std::uint16_t frameIndex) noexcept { |
||||
return MemoryOneBitBuffer{buffer(frameIndex), m_width, m_height}; |
||||
} |
||||
|
||||
ConstMemoryOneBitBuffer AnimationElement::image(std::uint16_t frameIndex) const noexcept { |
||||
return ConstMemoryOneBitBuffer{buffer(frameIndex), m_width, m_height}; |
||||
} |
||||
|
||||
AnimationElement::~AnimationElement() { |
||||
} |
||||
|
||||
ElementType AnimationElement::elementType() const { |
||||
return ElementType::Animation; |
||||
} |
||||
|
||||
std::size_t AnimationElement::serializeTo(std::span<std::byte> target) const { |
||||
// FIXME: implement this
|
||||
} |
||||
|
||||
void AnimationElement::drawTo(OneBitBufferInterface* imageBuffer, std::size_t animationTick) { |
||||
// FIXME: implement this
|
||||
} |
||||
|
||||
std::expected<std::unique_ptr<AnimationElement>, ParseError> AnimationElement::parse(std::span<std::byte const>& buffer) { |
||||
// FIXME: implement this
|
||||
} |
||||
|
||||
} // namespace monoformat
|
||||
@ -0,0 +1,317 @@ |
||||
#ifndef MONOFORMAT_STRUCTURED_HPP |
||||
#define MONOFORMAT_STRUCTURED_HPP |
||||
|
||||
#include "monoformat_parsehelpers.hpp" |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <memory> |
||||
#include <vector> |
||||
#include <span> |
||||
|
||||
namespace monoformat { |
||||
|
||||
enum class SectionType { |
||||
AlwaysDrawn = 1, |
||||
TimeBasedDrawn = 2, |
||||
CustomFont = 32, |
||||
}; |
||||
|
||||
struct Section { |
||||
virtual ~Section() = default; |
||||
virtual SectionType sectionType() const = 0; |
||||
virtual std::size_t serializeTo(std::span<std::byte> target) const = 0; |
||||
|
||||
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; |
||||
}; |
||||
|
||||
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) = 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); |
||||
|
||||
std::uint16_t x() const noexcept; |
||||
std::uint16_t y() const noexcept; |
||||
std::uint16_t width() const noexcept; |
||||
std::uint16_t height() const noexcept; |
||||
std::span<std::byte> buffer() noexcept; |
||||
std::span<std::byte const> buffer() const noexcept; |
||||
MemoryOneBitBuffer image() noexcept; |
||||
ConstMemoryOneBitBuffer image() const noexcept; |
||||
|
||||
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) override; |
||||
|
||||
static std::expected<std::unique_ptr<ImageElement>, ParseError> parse(std::span<std::byte const>& buffer); |
||||
|
||||
private: |
||||
std::uint16_t m_x{}; |
||||
std::uint16_t m_y{}; |
||||
std::uint16_t m_width{}; |
||||
std::uint16_t m_height{}; |
||||
std::vector<std::byte> m_buffer; |
||||
}; |
||||
|
||||
struct AnimationElement : public Element { |
||||
AnimationElement(std::uint16_t x, std::uint16_t y, std::uint16_t width, std::uint16_t height, std::uint16_t numberOfFrames, std::uint8_t updateInterval); |
||||
|
||||
std::uint16_t x() const noexcept; |
||||
std::uint16_t y() const noexcept; |
||||
std::uint16_t width() const noexcept; |
||||
std::uint16_t height() const noexcept; |
||||
std::uint16_t numberOfFrames() const noexcept; |
||||
std::uint8_t updateInterval() const noexcept; |
||||
std::span<std::byte> buffers() noexcept; |
||||
std::span<std::byte const> buffers() const noexcept; |
||||
std::span<std::byte> buffer(std::uint16_t frameIndex) noexcept; |
||||
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; |
||||
|
||||
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) override; |
||||
|
||||
static std::expected<std::unique_ptr<AnimationElement>, ParseError> parse(std::span<std::byte const>& buffer); |
||||
|
||||
private: |
||||
std::uint16_t m_x{}; |
||||
std::uint16_t m_y{}; |
||||
std::uint16_t m_width{}; |
||||
std::uint16_t m_height{}; |
||||
std::uint16_t m_numberOfFrames{}; |
||||
std::uint8_t m_updateInterval{}; |
||||
std::vector<std::byte> m_buffer; |
||||
}; |
||||
|
||||
struct HScrollImageElement : public Element { |
||||
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); |
||||
|
||||
std::uint16_t x() const noexcept; |
||||
std::uint16_t y() const noexcept; |
||||
std::uint16_t width() const noexcept; |
||||
std::uint16_t height() const noexcept; |
||||
std::uint16_t contentWidth() const noexcept; |
||||
std::uint8_t flags() const noexcept; |
||||
std::uint8_t scrollSpeed() const noexcept; |
||||
std::span<std::byte> buffer() noexcept; |
||||
std::span<std::byte const> buffer() const noexcept; |
||||
MemoryOneBitBuffer image() noexcept; |
||||
ConstMemoryOneBitBuffer image() const noexcept; |
||||
|
||||
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) override; |
||||
|
||||
static std::expected<std::unique_ptr<HScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer); |
||||
|
||||
private: |
||||
std::uint16_t m_x{}; |
||||
std::uint16_t m_y{}; |
||||
std::uint16_t m_width{}; |
||||
std::uint16_t m_height{}; |
||||
std::uint16_t m_contentWidth{}; |
||||
std::uint8_t m_flags{}; |
||||
std::uint8_t m_scrollSpeed{}; |
||||
std::vector<std::byte> m_buffer; |
||||
}; |
||||
|
||||
struct VScrollImageElement : public Element { |
||||
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); |
||||
|
||||
std::uint16_t x() const noexcept; |
||||
std::uint16_t y() const noexcept; |
||||
std::uint16_t width() const noexcept; |
||||
std::uint16_t height() const noexcept; |
||||
std::uint16_t contentHeight() const noexcept; |
||||
std::uint8_t flags() const noexcept; |
||||
std::uint8_t scrollSpeed() const noexcept; |
||||
std::span<std::byte> buffer() noexcept; |
||||
std::span<std::byte const> buffer() const noexcept; |
||||
MemoryOneBitBuffer image() noexcept; |
||||
ConstMemoryOneBitBuffer image() const noexcept; |
||||
|
||||
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) override; |
||||
|
||||
static std::expected<std::unique_ptr<VScrollImageElement>, ParseError> parse(std::span<std::byte const>& buffer); |
||||
|
||||
private: |
||||
std::uint16_t m_x{}; |
||||
std::uint16_t m_y{}; |
||||
std::uint16_t m_width{}; |
||||
std::uint16_t m_height{}; |
||||
std::uint16_t m_contentHeight{}; |
||||
std::uint8_t m_flags{}; |
||||
std::uint8_t m_scrollSpeed{}; |
||||
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); |
||||
|
||||
std::uint16_t originX() const noexcept; |
||||
std::uint16_t originY() const noexcept; |
||||
std::uint16_t targetX() const noexcept; |
||||
std::uint16_t targetY() const noexcept; |
||||
LineStyle lineStyle() const noexcept; |
||||
std::uint8_t flags() const noexcept; |
||||
|
||||
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) override; |
||||
|
||||
static std::expected<std::unique_ptr<LineElement>, ParseError> parse(std::span<std::byte const>& buffer); |
||||
|
||||
private: |
||||
std::uint16_t m_originX{}; |
||||
std::uint16_t m_originY{}; |
||||
std::uint16_t m_targetX{}; |
||||
std::uint16_t m_targetY{}; |
||||
LineStyle m_lineStyle{}; |
||||
std::uint8_t m_flags{}; |
||||
}; |
||||
|
||||
// FIXME: text elements
|
||||
|
||||
struct AlwaysDrawnSection : public Section { |
||||
AlwaysDrawnSection() = default; |
||||
virtual ~AlwaysDrawnSection() override; |
||||
|
||||
bool doesDrawOnFront() const noexcept; |
||||
bool doesDrawOnBack() const noexcept; |
||||
bool doesClearBeforeDrawing() const noexcept; |
||||
|
||||
void setDrawOnFront(bool value) noexcept; |
||||
void setDrawOnBack(bool value) noexcept; |
||||
void setClearBeforeDrawing(bool value) noexcept; |
||||
|
||||
std::size_t elementCount() const noexcept; |
||||
std::unique_ptr<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); |
||||
|
||||
virtual SectionType sectionType() const override; |
||||
virtual std::size_t serializeTo(std::span<std::byte> target) const override; |
||||
|
||||
private: |
||||
std::vector<std::unique_ptr<Element>> m_elements; |
||||
bool m_drawOnFront{true}; |
||||
bool m_drawOnBack{true}; |
||||
bool m_clearBeforeDrawing{true}; |
||||
}; |
||||
|
||||
struct TimeBasedDrawnSection : public Section { |
||||
TimeBasedDrawnSection() = default; |
||||
virtual ~TimeBasedDrawnSection() override; |
||||
|
||||
bool doesDrawOnFront() const noexcept; |
||||
bool doesDrawOnBack() const noexcept; |
||||
bool doesClearBeforeDrawing() const noexcept; |
||||
|
||||
void setDrawOnFront(bool value) noexcept; |
||||
void setDrawOnBack(bool value) noexcept; |
||||
void setClearBeforeDrawing(bool value) noexcept; |
||||
|
||||
std::size_t elementCount() const noexcept; |
||||
std::unique_ptr<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); |
||||
|
||||
virtual SectionType sectionType() const override; |
||||
virtual std::size_t serializeTo(std::span<std::byte> target) const override; |
||||
|
||||
private: |
||||
std::vector<std::unique_ptr<Element>> m_elements; |
||||
std::uint64_t m_startTimestamp{}; |
||||
std::uint64_t m_endTimestamp{}; |
||||
bool m_drawOnFront{true}; |
||||
bool m_drawOnBack{true}; |
||||
bool m_clearBeforeDrawing{true}; |
||||
}; |
||||
|
||||
// FIXME: custom font sections
|
||||
|
||||
struct File { |
||||
std::vector<std::unique_ptr<Section>> sections; |
||||
}; |
||||
|
||||
extern std::expected<File, ParseError> parseFile(std::span<std::byte const> data); |
||||
extern std::size_t serializeFile(std::span<std::byte> buffer); |
||||
|
||||
} // namespace monoformat
|
||||
|
||||
#endif // MONOFORMAT_STRUCTURED_HPP
|
||||
@ -0,0 +1,209 @@ |
||||
#ifndef MONOFORMAT_UTF8_HPP |
||||
#define MONOFORMAT_UTF8_HPP |
||||
|
||||
#include <cstdint> |
||||
#include <cstddef> |
||||
#include <string_view> |
||||
#include <span> |
||||
#include <utility> |
||||
|
||||
namespace monoformat { |
||||
|
||||
inline std::pair<std::uint32_t, std::uint32_t> const UTF8CodePointRanges[] = { |
||||
{0x000000u, 0x00007fu}, |
||||
{0x000080u, 0x0007ffu}, |
||||
{0x000800u, 0x00ffffu}, |
||||
{0x010000u, 0x10ffffu}, |
||||
}; |
||||
|
||||
class UTF8Iterator { |
||||
public: |
||||
UTF8Iterator() = default; |
||||
|
||||
explicit UTF8Iterator(std::string_view buffer) noexcept |
||||
: UTF8Iterator{buffer.data(), buffer.data() + buffer.size()} |
||||
{ |
||||
} |
||||
|
||||
explicit UTF8Iterator(std::span<char const> buffer) noexcept |
||||
: UTF8Iterator{buffer.data(), buffer.data() + buffer.size()} |
||||
{ |
||||
} |
||||
|
||||
explicit UTF8Iterator(std::span<unsigned char const> buffer) noexcept |
||||
: UTF8Iterator{buffer.data(), buffer.data() + buffer.size()} |
||||
{ |
||||
} |
||||
|
||||
explicit UTF8Iterator(std::span<signed char const> buffer) noexcept |
||||
: UTF8Iterator{buffer.data(), buffer.data() + buffer.size()} |
||||
{ |
||||
} |
||||
|
||||
explicit UTF8Iterator(std::span<std::byte const> buffer) noexcept |
||||
: UTF8Iterator{buffer.data(), buffer.data() + buffer.size()} |
||||
{ |
||||
} |
||||
|
||||
explicit UTF8Iterator(char const* ptr, char const* end) noexcept |
||||
: m_ptr{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_next{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_end{reinterpret_cast<std::uint8_t const*>(end)} |
||||
{ |
||||
findNext(); |
||||
} |
||||
|
||||
explicit UTF8Iterator(unsigned char const* ptr, unsigned char const* end) noexcept |
||||
: m_ptr{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_next{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_end{reinterpret_cast<std::uint8_t const*>(end)} |
||||
{ |
||||
findNext(); |
||||
} |
||||
|
||||
explicit UTF8Iterator(signed char const* ptr, signed char const* end) noexcept |
||||
: m_ptr{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_next{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_end{reinterpret_cast<std::uint8_t const*>(end)} |
||||
{ |
||||
findNext(); |
||||
} |
||||
|
||||
explicit UTF8Iterator(std::byte const* ptr, std::byte const* end) noexcept |
||||
: m_ptr{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_next{reinterpret_cast<std::uint8_t const*>(ptr)} |
||||
, m_end{reinterpret_cast<std::uint8_t const*>(end)} |
||||
{ |
||||
findNext(); |
||||
} |
||||
|
||||
UTF8Iterator operator++(int) noexcept { |
||||
UTF8Iterator result{*this}; |
||||
operator++(); |
||||
return result; |
||||
} |
||||
|
||||
UTF8Iterator& operator++() noexcept { |
||||
m_ptr = m_next; |
||||
findNext(); |
||||
return *this; |
||||
} |
||||
|
||||
friend inline bool operator==(UTF8Iterator const& a, UTF8Iterator const& b) noexcept { |
||||
return a.m_ptr == b.m_ptr |
||||
|| (a.m_ptr == a.m_end && b.m_ptr == nullptr) |
||||
|| (b.m_ptr == b.m_end && a.m_ptr == nullptr); |
||||
} |
||||
|
||||
friend inline bool operator<(UTF8Iterator const& a, UTF8Iterator const& b) noexcept { |
||||
if (a == b) { |
||||
return false; |
||||
} |
||||
if (a.m_ptr == a.m_end || !a.m_ptr) { |
||||
return false; |
||||
} |
||||
if (b.m_ptr == b.m_end || !b.m_ptr) { |
||||
return true; |
||||
} |
||||
return a.m_ptr < b.m_ptr; |
||||
} |
||||
|
||||
inline std::uint32_t operator*() const noexcept { |
||||
return m_codePoint; |
||||
} |
||||
|
||||
private: |
||||
void findNext() noexcept { |
||||
if (!m_ptr || m_ptr == m_end) { |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
return; |
||||
} |
||||
m_ptr = m_next; |
||||
if (!m_ptr || m_ptr == m_end) { |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
return; |
||||
} |
||||
|
||||
int t = 0; |
||||
int n = -1; |
||||
m_codePoint = 0; |
||||
m_next = m_ptr; |
||||
while (n != 0 && m_next < m_end) { |
||||
std::uint8_t value = *m_next++; |
||||
if (n == -1) { |
||||
if ((value & 0b1111'1000u) == 0b1111'0000u) { |
||||
t = 4; |
||||
n = 3; |
||||
m_codePoint = (value & 0b0000'0111u); |
||||
} else if ((value & 0b1111'0000u) == 0b1110'0000u) { |
||||
t = 3; |
||||
n = 2; |
||||
m_codePoint = (value & 0b0000'1111u); |
||||
} else if ((value & 0b1110'0000u) == 0b1100'0000u) { |
||||
t = 2; |
||||
n = 1; |
||||
m_codePoint = (value & 0b0001'1111u); |
||||
} else if ((value & 0b1000'0000u) == 0b0000'0000u) { |
||||
t = 1; |
||||
n = 0; |
||||
m_codePoint = value; |
||||
} else { |
||||
// Illegal sequence, stop here
|
||||
t = 1; |
||||
n = 0; |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
} |
||||
} else { |
||||
if ((value & 0b1100'0000u) != 0b1000'0000u) { |
||||
// Illegal sequence, stop here
|
||||
t = 1; |
||||
n = 0; |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
} |
||||
|
||||
m_codePoint <<= 6; |
||||
m_codePoint |= value & 0b0011'1111u; |
||||
--n; |
||||
} |
||||
} |
||||
|
||||
if (n == 0) { |
||||
if (m_codePoint < UTF8CodePointRanges[t - 1].first || m_codePoint > UTF8CodePointRanges[t - 1].second) { |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
return; |
||||
} |
||||
|
||||
// Surrogates are illegal
|
||||
if (m_codePoint >= 0xd800u && m_codePoint <= 0xdfffu) { |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
return; |
||||
} |
||||
|
||||
// Code point is OK
|
||||
return; |
||||
} else { |
||||
/* We've reached the end of the string inside a sequence, so make
|
||||
* this an illegal character |
||||
*/ |
||||
m_codePoint = ~std::uint32_t{0}; |
||||
return; |
||||
} |
||||
} |
||||
|
||||
std::uint8_t const* m_ptr{nullptr}; |
||||
std::uint8_t const* m_next{nullptr}; |
||||
std::uint8_t const* m_end{nullptr}; |
||||
std::uint32_t m_codePoint{}; |
||||
}; |
||||
|
||||
inline UTF8Iterator begin(UTF8Iterator const& a) noexcept { |
||||
return a; |
||||
} |
||||
|
||||
inline UTF8Iterator end(UTF8Iterator const& b) noexcept { |
||||
return UTF8Iterator{}; |
||||
} |
||||
|
||||
} // monoformat
|
||||
|
||||
#endif // MONOFORMAT_UTF8_HPP
|
||||
@ -0,0 +1,163 @@ |
||||
unsigned char u8g2_font_5x7_mf_u8g2font[] = { |
||||
0xbf, 0x02, 0x03, 0x02, 0x03, 0x03, 0x01, 0x01, 0x04, 0x05, 0x07, 0x00, |
||||
0xff, 0x06, 0xff, 0x06, 0x00, 0x01, 0x2a, 0x02, 0x72, 0x07, 0x5a, 0x20, |
||||
0x05, 0x7d, 0x7d, 0x1e, 0x21, 0x08, 0x7d, 0xad, 0xb0, 0x1d, 0xca, 0x01, |
||||
0x22, 0x09, 0x7d, 0x9d, 0x52, 0x12, 0x25, 0x39, 0x03, 0x23, 0x0b, 0x7d, |
||||
0xed, 0x24, 0x19, 0x94, 0xca, 0xa0, 0x94, 0x01, 0x24, 0x09, 0x7d, 0xed, |
||||
0xa5, 0xb6, 0x25, 0xc9, 0x0c, 0x25, 0x09, 0x7d, 0x8d, 0x30, 0xca, 0x9a, |
||||
0xc2, 0x18, 0x26, 0x0a, 0x7d, 0xed, 0x2c, 0xc9, 0x2a, 0x59, 0x12, 0x03, |
||||
0x27, 0x07, 0x7d, 0xad, 0xb0, 0xce, 0x02, 0x28, 0x08, 0x7d, 0xad, 0x2c, |
||||
0x6c, 0xcd, 0x01, 0x29, 0x09, 0x7d, 0x9d, 0x34, 0x6c, 0xcb, 0x11, 0x00, |
||||
0x2a, 0x09, 0x7d, 0xed, 0x24, 0xcb, 0xb6, 0x4a, 0x0c, 0x2b, 0x09, 0x7d, |
||||
0xfd, 0x30, 0x1a, 0xa4, 0x30, 0x07, 0x2c, 0x07, 0x7d, 0x7d, 0x16, 0xad, |
||||
0x06, 0x2d, 0x08, 0x7d, 0x7d, 0xca, 0x90, 0x93, 0x00, 0x2e, 0x07, 0x7d, |
||||
0x7d, 0xba, 0xa6, 0x03, 0x2f, 0x07, 0x7d, 0x7d, 0x24, 0x6b, 0x27, 0x30, |
||||
0x0c, 0x7d, 0xad, 0x2c, 0x89, 0x92, 0x28, 0x89, 0x92, 0x2c, 0x07, 0x31, |
||||
0x08, 0x7d, 0xad, 0x4c, 0x6c, 0x9b, 0x01, 0x32, 0x09, 0x7d, 0x1d, 0xa9, |
||||
0x98, 0xd5, 0x86, 0x18, 0x33, 0x0b, 0x7d, 0x8d, 0x21, 0x8c, 0xd4, 0x24, |
||||
0x8a, 0x74, 0x00, 0x34, 0x0b, 0x7d, 0xad, 0x4c, 0x4a, 0xa2, 0x21, 0x0b, |
||||
0x73, 0x00, 0x35, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xd7, 0x24, 0x8a, 0x74, |
||||
0x00, 0x36, 0x0a, 0x7d, 0x1d, 0x29, 0x9c, 0x2a, 0x51, 0xa4, 0x03, 0x37, |
||||
0x0b, 0x7d, 0x8d, 0x21, 0xcc, 0xc2, 0x2c, 0xcc, 0x11, 0x00, 0x38, 0x0a, |
||||
0x7d, 0x1d, 0xa9, 0x24, 0x55, 0xa2, 0x48, 0x07, 0x39, 0x0a, 0x7d, 0x1d, |
||||
0xa9, 0x12, 0x45, 0x63, 0xa4, 0x03, 0x3a, 0x08, 0x7d, 0x6d, 0x4d, 0x47, |
||||
0x34, 0x1d, 0x3b, 0x09, 0x7d, 0x6d, 0x4d, 0x47, 0xb4, 0x22, 0x00, 0x3c, |
||||
0x08, 0x7d, 0x7d, 0x24, 0xab, 0x96, 0x01, 0x3d, 0x09, 0x7d, 0x7d, 0x6c, |
||||
0x88, 0x87, 0x1c, 0x04, 0x3e, 0x08, 0x7d, 0xed, 0xb4, 0x56, 0x47, 0x00, |
||||
0x3f, 0x0a, 0x7d, 0xad, 0x2c, 0x09, 0xb3, 0x1c, 0xca, 0x01, 0x40, 0x0b, |
||||
0x7d, 0x1d, 0xa9, 0x92, 0x28, 0x89, 0x92, 0xea, 0x00, 0x41, 0x0c, 0x7d, |
||||
0x1d, 0xa9, 0x12, 0x25, 0x43, 0x12, 0x25, 0x51, 0x0c, 0x42, 0x0a, 0x7d, |
||||
0x8d, 0xa9, 0x32, 0x55, 0xa2, 0x64, 0x07, 0x43, 0x09, 0x7d, 0x1d, 0xa9, |
||||
0x12, 0x96, 0x22, 0x1d, 0x44, 0x0c, 0x7d, 0x8d, 0xa9, 0x12, 0x25, 0x51, |
||||
0x12, 0x25, 0x3b, 0x00, 0x45, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0x30, |
||||
0x1c, 0x62, 0x00, 0x46, 0x0a, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0xb0, 0x0e, |
||||
0x01, 0x47, 0x0b, 0x7d, 0x1d, 0xa9, 0x12, 0x26, 0x4a, 0x14, 0xcd, 0x00, |
||||
0x48, 0x0e, 0x7d, 0x8d, 0x28, 0x89, 0x92, 0x21, 0x89, 0x92, 0x28, 0x89, |
||||
0x62, 0x00, 0x49, 0x08, 0x7d, 0x9d, 0x2d, 0x6c, 0x9b, 0x01, 0x4a, 0x09, |
||||
0x7d, 0xbd, 0xb0, 0x25, 0x8a, 0x74, 0x00, 0x4b, 0x0b, 0x7d, 0x8d, 0x28, |
||||
0x29, 0x69, 0x5a, 0x12, 0x95, 0x01, 0x4c, 0x08, 0x7d, 0x8d, 0xb0, 0xe3, |
||||
0x10, 0x03, 0x4d, 0x0e, 0x7d, 0x8d, 0x28, 0x19, 0x92, 0x21, 0x89, 0x92, |
||||
0x28, 0x89, 0x62, 0x00, 0x4e, 0x0d, 0x7d, 0x8d, 0x28, 0x51, 0x12, 0xa5, |
||||
0xa2, 0x24, 0x4a, 0x14, 0x03, 0x4f, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, |
||||
0x51, 0x12, 0x45, 0x3a, 0x00, 0x50, 0x0b, 0x7d, 0x8d, 0xa9, 0x12, 0x25, |
||||
0x53, 0x98, 0x43, 0x00, 0x51, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, 0x51, |
||||
0xa2, 0x44, 0x6a, 0x02, 0x52, 0x0b, 0x7d, 0x8d, 0xa9, 0x12, 0x25, 0x53, |
||||
0x12, 0x95, 0x01, 0x53, 0x09, 0x7d, 0x1d, 0xa9, 0x35, 0x2a, 0xe9, 0x00, |
||||
0x54, 0x07, 0x7d, 0x9d, 0x2d, 0xec, 0x0e, 0x55, 0x0d, 0x7d, 0x8d, 0x28, |
||||
0x89, 0x92, 0x28, 0x89, 0x92, 0x28, 0xd2, 0x01, 0x56, 0x0c, 0x7d, 0x8d, |
||||
0x28, 0x89, 0x92, 0x28, 0x89, 0x22, 0x4d, 0x07, 0x57, 0x0e, 0x7d, 0x8d, |
||||
0x28, 0x89, 0x92, 0x28, 0x19, 0x92, 0x21, 0x89, 0x62, 0x00, 0x58, 0x0c, |
||||
0x7d, 0x8d, 0x28, 0x89, 0x22, 0x4d, 0xaa, 0x44, 0x31, 0x00, 0x59, 0x0a, |
||||
0x7d, 0x9d, 0x52, 0x12, 0x25, 0x59, 0x58, 0x07, 0x5a, 0x09, 0x7d, 0x8d, |
||||
0x21, 0xcc, 0x1a, 0x87, 0x18, 0x5b, 0x08, 0x7d, 0x9d, 0x29, 0x6c, 0x9c, |
||||
0x01, 0x5c, 0x07, 0x7d, 0xdd, 0xee, 0x20, 0x00, 0x5d, 0x07, 0x7d, 0x9d, |
||||
0xb1, 0xd3, 0x0c, 0x5e, 0x08, 0x7d, 0xad, 0x2c, 0xc9, 0x59, 0x01, 0x5f, |
||||
0x07, 0x7d, 0x7d, 0xc6, 0x21, 0x06, 0x60, 0x07, 0x7d, 0x9d, 0x34, 0x67, |
||||
0x06, 0x61, 0x0b, 0x7d, 0x7d, 0x70, 0x89, 0x92, 0x44, 0x4a, 0x62, 0x00, |
||||
0x62, 0x0a, 0x7d, 0x8d, 0x30, 0x9c, 0x2a, 0x51, 0xb2, 0x03, 0x63, 0x08, |
||||
0x7d, 0x7d, 0x50, 0x0a, 0x53, 0x1d, 0x64, 0x0b, 0x7d, 0xbd, 0x30, 0x5a, |
||||
0xa2, 0x24, 0x8a, 0x66, 0x00, 0x65, 0x09, 0x7d, 0x7d, 0x50, 0x4a, 0x8c, |
||||
0x3a, 0x00, 0x66, 0x0b, 0x7d, 0xad, 0x2c, 0x89, 0xb2, 0x2d, 0xcc, 0x11, |
||||
0x00, 0x67, 0x0a, 0x7d, 0x7d, 0x70, 0x89, 0x22, 0x29, 0x5d, 0x00, 0x68, |
||||
0x0b, 0x7d, 0x8d, 0x30, 0x9c, 0x2a, 0x51, 0x12, 0xc5, 0x00, 0x69, 0x09, |
||||
0x7d, 0xad, 0x1c, 0x11, 0x6b, 0x33, 0x00, 0x6a, 0x0a, 0x7d, 0xbd, 0x1c, |
||||
0x0a, 0x4b, 0x49, 0x16, 0x01, 0x6b, 0x0a, 0x7d, 0x8d, 0xb0, 0x12, 0x69, |
||||
0x49, 0x54, 0x06, 0x6c, 0x07, 0x7d, 0x1d, 0xb1, 0xdb, 0x0c, 0x6d, 0x0b, |
||||
0x7d, 0x7d, 0x2c, 0x89, 0x86, 0x24, 0x4a, 0xa2, 0x18, 0x6e, 0x0a, 0x7d, |
||||
0x7d, 0x6c, 0xaa, 0x44, 0x49, 0x14, 0x03, 0x6f, 0x09, 0x7d, 0x7d, 0x50, |
||||
0xaa, 0x44, 0x91, 0x0e, 0x70, 0x0a, 0x7d, 0x7d, 0x6c, 0xaa, 0x44, 0xc9, |
||||
0x14, 0x02, 0x71, 0x0b, 0x7d, 0x7d, 0x70, 0x89, 0x92, 0x28, 0x1a, 0x13, |
||||
0x00, 0x72, 0x09, 0x7d, 0x7d, 0x6c, 0xaa, 0x84, 0x39, 0x04, 0x73, 0x08, |
||||
0x7d, 0x7d, 0x70, 0x51, 0x95, 0x1d, 0x74, 0x09, 0x7d, 0x9d, 0x30, 0xdb, |
||||
0xc2, 0x54, 0x06, 0x75, 0x0b, 0x7d, 0x7d, 0x2c, 0x4a, 0xa2, 0x24, 0x8a, |
||||
0x66, 0x00, 0x76, 0x0b, 0x7d, 0x7d, 0x30, 0x89, 0x92, 0x28, 0xc9, 0x72, |
||||
0x00, 0x77, 0x0b, 0x7d, 0x7d, 0x2c, 0x4a, 0xa2, 0x64, 0x48, 0x86, 0x18, |
||||
0x78, 0x09, 0x7d, 0x7d, 0x2c, 0x8a, 0x34, 0xa9, 0x0c, 0x79, 0x0a, 0x7d, |
||||
0x7d, 0x2c, 0x4a, 0xa2, 0x4a, 0x56, 0x03, 0x7a, 0x09, 0x7d, 0x7d, 0x6c, |
||||
0xc8, 0x6a, 0x43, 0x0c, 0x7b, 0x08, 0x7d, 0xbd, 0x9a, 0x58, 0x8d, 0x01, |
||||
0x7c, 0x07, 0x7d, 0xad, 0xb0, 0x77, 0x00, 0x7d, 0x0a, 0x7d, 0x9d, 0x34, |
||||
0xd4, 0xc2, 0x2c, 0x47, 0x00, 0x7e, 0x07, 0x7d, 0x9d, 0xee, 0xcc, 0x00, |
||||
0xa0, 0x05, 0x7d, 0x7d, 0x1e, 0xa1, 0x08, 0x7d, 0xad, 0x1c, 0x0a, 0xdb, |
||||
0x01, 0xa2, 0x0a, 0x7d, 0xfd, 0x6c, 0x29, 0x25, 0xd9, 0x16, 0x01, 0xa3, |
||||
0x09, 0x7d, 0x7d, 0x29, 0xdb, 0x2a, 0x32, 0x00, 0xa4, 0x0b, 0x7d, 0xdd, |
||||
0x2c, 0x99, 0x92, 0x68, 0xc9, 0x52, 0x00, 0xa5, 0x0a, 0x7d, 0x9d, 0x52, |
||||
0x92, 0x65, 0x5b, 0x98, 0x03, 0xa6, 0x08, 0x7d, 0xfd, 0x30, 0x87, 0xc2, |
||||
0x1c, 0xa7, 0x0b, 0x7d, 0x2d, 0x29, 0xd4, 0x92, 0x4c, 0x8c, 0x24, 0x00, |
||||
0xa8, 0x07, 0x7d, 0x9d, 0x3a, 0x37, 0x00, 0xa9, 0x0c, 0x7d, 0x9d, 0x25, |
||||
0x53, 0x92, 0x49, 0x49, 0xb4, 0x64, 0x01, 0xaa, 0x09, 0x7d, 0x1d, 0x29, |
||||
0xc9, 0x74, 0x16, 0x00, 0xab, 0x09, 0x7d, 0x7d, 0x30, 0x92, 0xda, 0x31, |
||||
0x00, 0xac, 0x09, 0x7d, 0x7d, 0xca, 0x10, 0xe6, 0x20, 0x00, 0xad, 0x07, |
||||
0x7d, 0x7d, 0xd2, 0x4e, 0x02, 0xae, 0x0b, 0x7d, 0x9d, 0x25, 0x1b, 0x92, |
||||
0x69, 0xd2, 0x92, 0x05, 0xaf, 0x07, 0x7d, 0x8d, 0x21, 0xe7, 0x06, 0xb0, |
||||
0x09, 0x7d, 0xad, 0x2c, 0xc9, 0x72, 0x16, 0x00, 0xb1, 0x0b, 0x7d, 0xad, |
||||
0x30, 0x1a, 0xa4, 0x30, 0x1a, 0x54, 0x00, 0xb2, 0x09, 0x7d, 0x1d, 0x31, |
||||
0x0b, 0x75, 0x1a, 0x00, 0xb3, 0x09, 0x7d, 0x1d, 0x4d, 0xcc, 0x74, 0x1a, |
||||
0x00, 0xb4, 0x07, 0x7d, 0xad, 0x2c, 0xe7, 0x00, 0xb5, 0x0b, 0x7d, 0x7d, |
||||
0x2c, 0x4a, 0xa2, 0x24, 0x4a, 0xa6, 0x10, 0xb6, 0x0d, 0x7d, 0x9d, 0x45, |
||||
0x49, 0x94, 0x28, 0x89, 0x92, 0x28, 0x89, 0x01, 0xb7, 0x08, 0x7d, 0x7d, |
||||
0x50, 0xd3, 0x69, 0x00, 0xb8, 0x07, 0x7d, 0x7d, 0xe6, 0x2c, 0x03, 0xb9, |
||||
0x09, 0x7d, 0xad, 0x4c, 0xcc, 0x76, 0x12, 0x00, 0xba, 0x09, 0x7d, 0x9d, |
||||
0x2c, 0xc9, 0x72, 0x26, 0x00, 0xbb, 0x09, 0x7d, 0x7d, 0x2c, 0x2a, 0x49, |
||||
0x39, 0x08, 0xbc, 0x09, 0x7d, 0x8d, 0xb0, 0x29, 0x93, 0xc6, 0x04, 0xbd, |
||||
0x09, 0x7d, 0x8d, 0xb0, 0x45, 0xcc, 0x42, 0x05, 0xbe, 0x0b, 0x7d, 0x0d, |
||||
0x4d, 0xcc, 0x94, 0x4c, 0x1a, 0x13, 0x00, 0xbf, 0x0a, 0x7d, 0xad, 0x1c, |
||||
0xca, 0xc2, 0x24, 0xcb, 0x01, 0xc0, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, |
||||
0x43, 0x12, 0x25, 0x51, 0x0c, 0xc1, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, |
||||
0x43, 0x12, 0x25, 0x51, 0x0c, 0xc2, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, |
||||
0x43, 0x12, 0x25, 0x51, 0x0c, 0xc3, 0x0c, 0x7d, 0x1d, 0xa9, 0x12, 0x25, |
||||
0x43, 0x12, 0x25, 0x51, 0x0c, 0xc4, 0x0c, 0x7d, 0x8d, 0x28, 0x92, 0x2a, |
||||
0x43, 0x12, 0x25, 0x51, 0x0c, 0xc5, 0x0c, 0x7d, 0x1d, 0x4d, 0xaa, 0x0c, |
||||
0x49, 0x94, 0x44, 0x31, 0x00, 0xc6, 0x0c, 0x7d, 0x9d, 0xa5, 0x94, 0x28, |
||||
0x53, 0x12, 0x25, 0x32, 0x00, 0xc7, 0x0a, 0x7d, 0x1d, 0xa9, 0x12, 0x96, |
||||
0x22, 0x2d, 0x03, 0xc8, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0x30, 0x1c, |
||||
0x62, 0x00, 0xc9, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0x30, 0x1c, 0x62, |
||||
0x00, 0xca, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0x30, 0x1c, 0x62, 0x00, |
||||
0xcb, 0x0b, 0x7d, 0x8d, 0x21, 0x09, 0xa7, 0x30, 0x1c, 0x62, 0x00, 0xcc, |
||||
0x08, 0x7d, 0x9d, 0x2d, 0x6c, 0x9b, 0x01, 0xcd, 0x08, 0x7d, 0x9d, 0x2d, |
||||
0x6c, 0x9b, 0x01, 0xce, 0x08, 0x7d, 0x9d, 0x2d, 0x6c, 0x9b, 0x01, 0xcf, |
||||
0x08, 0x7d, 0x9d, 0x2d, 0x6c, 0x9b, 0x01, 0xd0, 0x0c, 0x7d, 0x8d, 0x2d, |
||||
0x49, 0x94, 0x28, 0x89, 0x92, 0x64, 0x07, 0xd1, 0x0d, 0x7d, 0x8d, 0x44, |
||||
0x89, 0x12, 0xa5, 0xa2, 0x24, 0x4a, 0x14, 0x03, 0xd2, 0x0c, 0x7d, 0x1d, |
||||
0xa9, 0x12, 0x25, 0x51, 0x12, 0x45, 0x3a, 0x00, 0xd3, 0x0c, 0x7d, 0x1d, |
||||
0xa9, 0x12, 0x25, 0x51, 0x12, 0x45, 0x3a, 0x00, 0xd4, 0x0c, 0x7d, 0x1d, |
||||
0xa9, 0x12, 0x25, 0x51, 0x12, 0x45, 0x3a, 0x00, 0xd5, 0x0c, 0x7d, 0x1d, |
||||
0xa9, 0x12, 0x25, 0x51, 0x12, 0x45, 0x3a, 0x00, 0xd6, 0x0c, 0x7d, 0x8d, |
||||
0x28, 0x92, 0x2a, 0x51, 0x12, 0x45, 0x3a, 0x00, 0xd7, 0x09, 0x7d, 0x7d, |
||||
0x2c, 0x8a, 0x34, 0xa9, 0x0c, 0xd8, 0x0c, 0x7d, 0x9d, 0x25, 0x51, 0x12, |
||||
0x4b, 0xa2, 0x24, 0x3b, 0x00, 0xd9, 0x0d, 0x7d, 0x8d, 0x28, 0x89, 0x92, |
||||
0x28, 0x89, 0x92, 0x28, 0xd2, 0x01, 0xda, 0x0d, 0x7d, 0x8d, 0x28, 0x89, |
||||
0x92, 0x28, 0x89, 0x92, 0x28, 0xd2, 0x01, 0xdb, 0x0d, 0x7d, 0x8d, 0x28, |
||||
0x89, 0x92, 0x28, 0x89, 0x92, 0x28, 0xd2, 0x01, 0xdc, 0x0b, 0x7d, 0x8d, |
||||
0x28, 0x8e, 0x92, 0x28, 0x89, 0x22, 0x1d, 0xdd, 0x0a, 0x7d, 0x9d, 0x52, |
||||
0x12, 0x25, 0x59, 0x58, 0x07, 0xde, 0x0a, 0x7d, 0x8d, 0x70, 0xaa, 0x4c, |
||||
0x61, 0x0e, 0x01, 0xdf, 0x0a, 0x7d, 0x1d, 0xa9, 0x52, 0xaa, 0x44, 0x49, |
||||
0x1d, 0xe0, 0x0b, 0x7d, 0x9d, 0x34, 0x5b, 0xa2, 0x24, 0x91, 0x92, 0x18, |
||||
0xe1, 0x0b, 0x7d, 0xad, 0x2c, 0x5c, 0xa2, 0x24, 0x91, 0x92, 0x18, 0xe2, |
||||
0x0c, 0x7d, 0xad, 0x2c, 0x89, 0x96, 0x28, 0x49, 0xa4, 0x24, 0x06, 0xe3, |
||||
0x0b, 0x7d, 0x9d, 0x6e, 0x4b, 0x94, 0x24, 0x52, 0x12, 0x03, 0xe4, 0x0b, |
||||
0x7d, 0x9d, 0xfa, 0x12, 0x25, 0x89, 0x94, 0xc4, 0x00, 0xe5, 0x0b, 0x7d, |
||||
0x1d, 0x4d, 0x5b, 0xa2, 0x24, 0x91, 0x92, 0x18, 0xe6, 0x09, 0x7d, 0x7d, |
||||
0x70, 0x49, 0x94, 0xda, 0x0c, 0xe7, 0x09, 0x7d, 0x7d, 0x54, 0x0a, 0x53, |
||||
0x2d, 0x02, 0xe8, 0x09, 0x7d, 0x9d, 0x34, 0x93, 0x12, 0xa3, 0x0e, 0xe9, |
||||
0x09, 0x7d, 0xad, 0x2c, 0x94, 0x12, 0xa3, 0x0e, 0xea, 0x0a, 0x7d, 0x9d, |
||||
0x2c, 0xc9, 0xa4, 0xc4, 0xa8, 0x03, 0xeb, 0x0a, 0x7d, 0x8d, 0x24, 0x47, |
||||
0xa4, 0xc4, 0xa8, 0x03, 0xec, 0x09, 0x7d, 0x9d, 0x34, 0x13, 0x6b, 0x33, |
||||
0x00, 0xed, 0x09, 0x7d, 0xad, 0x2c, 0x14, 0x6b, 0x33, 0x00, 0xee, 0x09, |
||||
0x7d, 0xad, 0x2c, 0x89, 0xc4, 0xda, 0x0c, 0xef, 0x08, 0x7d, 0x9d, 0xba, |
||||
0x58, 0x9b, 0x01, 0xf0, 0x0a, 0x7d, 0x9d, 0x54, 0x92, 0x2a, 0x51, 0xa4, |
||||
0x03, 0xf1, 0x0a, 0x7d, 0x9d, 0x4e, 0x53, 0x25, 0x4a, 0xa2, 0x18, 0xf2, |
||||
0x0a, 0x7d, 0x9d, 0x34, 0x93, 0x2a, 0x51, 0xa4, 0x03, 0xf3, 0x0a, 0x7d, |
||||
0xad, 0x2c, 0x94, 0x2a, 0x51, 0xa4, 0x03, 0xf4, 0x0a, 0x7d, 0x1d, 0x1d, |
||||
0x91, 0x2a, 0x51, 0xa4, 0x03, 0xf5, 0x0a, 0x7d, 0x9d, 0x6e, 0x52, 0x25, |
||||
0x8a, 0x74, 0x00, 0xf6, 0x09, 0x7d, 0x9d, 0xba, 0x54, 0x89, 0x22, 0x1d, |
||||
0xf7, 0x08, 0x7d, 0x6d, 0x7d, 0xc8, 0x75, 0x00, 0xf8, 0x09, 0x7d, 0x7d, |
||||
0x70, 0x49, 0x2c, 0xc9, 0x0e, 0xf9, 0x0b, 0x7d, 0x9d, 0x34, 0xaa, 0x44, |
||||
0x49, 0x14, 0xcd, 0x00, 0xfa, 0x0b, 0x7d, 0xad, 0xac, 0x94, 0x44, 0x49, |
||||
0x14, 0xcd, 0x00, 0xfb, 0x0b, 0x7d, 0x1d, 0x3d, 0x4a, 0xa2, 0x24, 0x8a, |
||||
0x66, 0x00, 0xfc, 0x0b, 0x7d, 0x9d, 0x72, 0x94, 0x44, 0x49, 0x14, 0xcd, |
||||
0x00, 0xfd, 0x0a, 0x7d, 0xad, 0xac, 0x94, 0x44, 0x95, 0xac, 0x06, 0xfe, |
||||
0x0a, 0x7d, 0xdd, 0x70, 0xaa, 0x44, 0xc9, 0x14, 0x02, 0xff, 0x0a, 0x7d, |
||||
0x9d, 0x72, 0x94, 0x44, 0x95, 0xac, 0x06, 0x00, 0x00, 0x00, 0x04, 0xff, |
||||
0xff, 0x00, 0x00 |
||||
}; |
||||
unsigned int u8g2_font_5x7_mf_u8g2font_len = 1911; |
||||
@ -0,0 +1,119 @@ |
||||
unsigned char u8g2_font_NokiaSmallPlain_tf_u8g2font[] = { |
||||
0x94, 0x00, 0x02, 0x02, 0x03, 0x04, 0x02, 0x04, 0x05, 0x07, 0x08, 0x00, |
||||
0xff, 0x07, 0xff, 0x07, 0xff, 0x01, 0x22, 0x02, 0x2b, 0x05, 0x48, 0x20, |
||||
0x05, 0x00, 0x71, 0x02, 0x21, 0x07, 0x39, 0x51, 0x32, 0x54, 0x00, 0x22, |
||||
0x07, 0x13, 0x9b, 0x92, 0x54, 0x00, 0x23, 0x0d, 0x3d, 0xd1, 0x56, 0xa6, |
||||
0x34, 0x54, 0x35, 0x54, 0x99, 0x12, 0x00, 0x24, 0x0a, 0x44, 0xaf, 0x1a, |
||||
0x47, 0x4a, 0xf3, 0xd0, 0x09, 0x25, 0x0a, 0x3c, 0xb1, 0xa2, 0x56, 0x8c, |
||||
0xb2, 0x5a, 0x02, 0x26, 0x0c, 0x3d, 0xd1, 0x96, 0x53, 0x4c, 0xb9, 0x4a, |
||||
0x32, 0xaa, 0x00, 0x27, 0x05, 0x11, 0x5b, 0x22, 0x28, 0x07, 0x42, 0x6f, |
||||
0xa6, 0x7a, 0x06, 0x29, 0x08, 0x42, 0x6f, 0x12, 0x53, 0x97, 0x02, 0x2a, |
||||
0x0a, 0x2d, 0xd3, 0x56, 0xc7, 0x21, 0x73, 0x4a, 0x00, 0x2b, 0x0a, 0x2d, |
||||
0xd3, 0x9a, 0x51, 0x1c, 0x32, 0xa3, 0x08, 0x2c, 0x06, 0x12, 0x6f, 0xa6, |
||||
0x00, 0x2d, 0x06, 0x0c, 0xb7, 0x32, 0x02, 0x2e, 0x05, 0x09, 0x51, 0x12, |
||||
0x2f, 0x09, 0x3b, 0x91, 0x5a, 0xc5, 0x2a, 0x46, 0x00, 0x30, 0x09, 0x3c, |
||||
0xb1, 0xa6, 0xa2, 0x67, 0x52, 0x00, 0x31, 0x06, 0xba, 0x91, 0xb6, 0x7a, |
||||
0x32, 0x0a, 0x3c, 0xb1, 0xb2, 0x51, 0x4e, 0x2a, 0xe7, 0x11, 0x33, 0x0b, |
||||
0x3c, 0xb1, 0xb2, 0x51, 0x4e, 0x1a, 0xe5, 0x91, 0x00, 0x34, 0x09, 0x3c, |
||||
0xb1, 0x1e, 0x55, 0x92, 0x43, 0x17, 0x35, 0x0a, 0x3c, 0xb1, 0xb2, 0xf2, |
||||
0x46, 0x39, 0x8f, 0x04, 0x36, 0x0a, 0x3c, 0xb1, 0xa6, 0xf2, 0x8a, 0x66, |
||||
0x52, 0x00, 0x37, 0x0a, 0x3c, 0xb1, 0x32, 0x72, 0xcc, 0x31, 0x97, 0x00, |
||||
0x38, 0x0b, 0x3c, 0xb1, 0xa6, 0xa2, 0x4c, 0x2a, 0xca, 0xa4, 0x00, 0x39, |
||||
0x0a, 0x3c, 0xb1, 0xa6, 0xa2, 0x99, 0x76, 0x52, 0x00, 0x3a, 0x06, 0xa1, |
||||
0x71, 0x12, 0x03, 0x3b, 0x07, 0x2a, 0x6f, 0x96, 0xa1, 0x02, 0x3c, 0x0a, |
||||
0x3c, 0xb1, 0x1e, 0xdb, 0x28, 0xa3, 0x8c, 0x02, 0x3d, 0x08, 0x1c, 0xb3, |
||||
0x32, 0x32, 0x1a, 0x01, 0x3e, 0x0b, 0x3c, 0xb1, 0x92, 0x51, 0x46, 0x19, |
||||
0xc5, 0x36, 0x00, 0x3f, 0x0a, 0x3c, 0xb1, 0xb2, 0x51, 0xac, 0x33, 0x15, |
||||
0x01, 0x40, 0x0c, 0x3d, 0xd1, 0xb6, 0xb2, 0x1a, 0x6a, 0xa9, 0x9c, 0x16, |
||||
0x00, 0x41, 0x09, 0x3c, 0xb1, 0xa6, 0xa2, 0x39, 0xa6, 0x0c, 0x42, 0x0a, |
||||
0x3c, 0xb1, 0xb2, 0xe2, 0x48, 0xd1, 0x1c, 0x09, 0x43, 0x08, 0x3c, 0xb1, |
||||
0x36, 0x72, 0x37, 0x1a, 0x44, 0x09, 0x3c, 0xb1, 0xb2, 0xa2, 0xe7, 0x48, |
||||
0x00, 0x45, 0x09, 0x3c, 0xb1, 0x32, 0xf4, 0xca, 0xf5, 0x08, 0x46, 0x09, |
||||
0x3c, 0xb1, 0x32, 0xf4, 0xca, 0x6d, 0x00, 0x47, 0x09, 0x3c, 0xb1, 0x36, |
||||
0x72, 0x9a, 0x66, 0x1a, 0x48, 0x09, 0x3c, 0xb1, 0x12, 0xe5, 0x98, 0xce, |
||||
0x00, 0x49, 0x06, 0x39, 0x51, 0x72, 0x04, 0x4a, 0x07, 0x3b, 0x91, 0xda, |
||||
0x73, 0x01, 0x4b, 0x0a, 0x3c, 0xb1, 0x12, 0x55, 0x92, 0x59, 0xa6, 0x32, |
||||
0x4c, 0x08, 0x3c, 0xb1, 0x92, 0x7b, 0x8f, 0x00, 0x4d, 0x0a, 0x3d, 0xd1, |
||||
0x92, 0xd7, 0x4a, 0x2a, 0x69, 0x3b, 0x4e, 0x0a, 0x3d, 0xd1, 0x92, 0xa7, |
||||
0x4a, 0x72, 0xdb, 0x01, 0x4f, 0x09, 0x3d, 0xd1, 0xb6, 0xb2, 0x77, 0x5a, |
||||
0x00, 0x50, 0x0a, 0x3c, 0xb1, 0xb2, 0xa2, 0x39, 0x52, 0xce, 0x00, 0x51, |
||||
0x0a, 0x45, 0xcf, 0xb6, 0xb2, 0x57, 0xb5, 0x61, 0x00, 0x52, 0x09, 0x3c, |
||||
0xb1, 0xb2, 0xa2, 0x39, 0x52, 0x33, 0x53, 0x0b, 0x3c, 0xb1, 0x36, 0x72, |
||||
0x46, 0x1a, 0xe5, 0x91, 0x00, 0x54, 0x0d, 0x3d, 0xd1, 0x32, 0x64, 0x46, |
||||
0x19, 0x65, 0x94, 0x51, 0x46, 0x11, 0x55, 0x08, 0x3c, 0xb1, 0x12, 0x7d, |
||||
0x26, 0x05, 0x56, 0x0b, 0x3d, 0xd1, 0x92, 0xed, 0x54, 0xa6, 0x9c, 0x51, |
||||
0x04, 0x57, 0x0f, 0x3f, 0x11, 0x93, 0xa1, 0x86, 0x29, 0xc7, 0x54, 0xa6, |
||||
0x3a, 0x65, 0x54, 0x02, 0x58, 0x0a, 0x3d, 0xd1, 0x92, 0x75, 0xaa, 0xab, |
||||
0x5a, 0x07, 0x59, 0x0c, 0x3d, 0xd1, 0x92, 0x75, 0xaa, 0x33, 0xca, 0x28, |
||||
0xa3, 0x08, 0x5a, 0x09, 0x3c, 0xb1, 0x32, 0x72, 0x6c, 0xe7, 0x11, 0x5c, |
||||
0x09, 0x3b, 0x91, 0x12, 0x73, 0xac, 0x63, 0x00, 0x5f, 0x06, 0x0d, 0xcf, |
||||
0x32, 0x04, 0x61, 0x09, 0x2c, 0xb1, 0xa6, 0x51, 0x1a, 0x31, 0x0d, 0x62, |
||||
0x0a, 0x3c, 0xb1, 0x92, 0xf3, 0x8a, 0xe6, 0x48, 0x00, 0x63, 0x07, 0x2b, |
||||
0x91, 0x36, 0x6b, 0x01, 0x64, 0x09, 0x3c, 0xb1, 0x5e, 0x8d, 0x68, 0xa6, |
||||
0x01, 0x65, 0x09, 0x2c, 0xb1, 0xa6, 0xe2, 0xd8, 0x48, 0x01, 0x66, 0x07, |
||||
0x3a, 0x71, 0xa6, 0x56, 0x0b, 0x67, 0x0a, 0x34, 0xaf, 0x36, 0xa2, 0x4c, |
||||
0x3b, 0x29, 0x00, 0x68, 0x09, 0x3c, 0xb1, 0x92, 0xf3, 0x8a, 0xce, 0x00, |
||||
0x69, 0x07, 0x39, 0x51, 0x92, 0x86, 0x00, 0x6a, 0x08, 0x42, 0x6f, 0x96, |
||||
0x53, 0x4b, 0x01, 0x6b, 0x0a, 0x3c, 0xb1, 0x92, 0x4b, 0x95, 0x64, 0x2a, |
||||
0x03, 0x6c, 0x06, 0x39, 0x51, 0x72, 0x04, 0x6d, 0x0b, 0x2d, 0xd1, 0x32, |
||||
0x52, 0xa5, 0x92, 0x4a, 0xaa, 0x00, 0x6e, 0x07, 0x2c, 0xb1, 0xb2, 0xa2, |
||||
0x33, 0x6f, 0x09, 0x2c, 0xb1, 0xa6, 0xa2, 0x99, 0x14, 0x00, 0x70, 0x0a, |
||||
0x34, 0xaf, 0xb2, 0xa2, 0x1c, 0x29, 0x67, 0x00, 0x71, 0x09, 0x34, 0xaf, |
||||
0x36, 0xa2, 0x4c, 0xbb, 0x00, 0x72, 0x08, 0x2b, 0x91, 0x92, 0x86, 0x2c, |
||||
0x01, 0x73, 0x07, 0x2b, 0x91, 0xb6, 0xf3, 0x02, 0x74, 0x08, 0x3a, 0x71, |
||||
0x92, 0xd2, 0x2a, 0x03, 0x75, 0x07, 0x2c, 0xb1, 0x12, 0x9d, 0x69, 0x76, |
||||
0x0a, 0x2d, 0xd1, 0x92, 0x75, 0x2a, 0x53, 0x8e, 0x00, 0x77, 0x0a, 0x2d, |
||||
0xd1, 0x92, 0x55, 0x52, 0x9d, 0x29, 0x01, 0x78, 0x09, 0x2c, 0xb1, 0x12, |
||||
0x65, 0x52, 0x51, 0x06, 0x79, 0x09, 0x34, 0xaf, 0x12, 0xcd, 0xb4, 0x93, |
||||
0x02, 0x7a, 0x08, 0x2c, 0xb1, 0x32, 0x62, 0x7b, 0x04, 0x7e, 0x08, 0x3d, |
||||
0xd1, 0x72, 0x7b, 0x8f, 0x01, 0xa0, 0x05, 0x00, 0x71, 0x02, 0xa1, 0x07, |
||||
0x39, 0x51, 0x92, 0x86, 0x00, 0xa3, 0x0a, 0x3c, 0xb1, 0x36, 0x72, 0x9e, |
||||
0x39, 0x8e, 0x00, 0xa4, 0x0a, 0x34, 0xb1, 0x12, 0x93, 0x8a, 0x32, 0xa9, |
||||
0x18, 0xa5, 0x0c, 0x3d, 0xd1, 0x92, 0x53, 0x35, 0x64, 0xde, 0x19, 0x45, |
||||
0x00, 0xa7, 0x0a, 0x43, 0x8f, 0x56, 0xad, 0xa4, 0xd2, 0xaa, 0x00, 0xbf, |
||||
0x09, 0x3c, 0xb1, 0x9a, 0xa9, 0x1c, 0x6b, 0x34, 0xc0, 0x0a, 0x3c, 0xb1, |
||||
0x96, 0x51, 0x54, 0x71, 0x4c, 0x19, 0xc1, 0x09, 0x3c, 0xb1, 0x5a, 0xab, |
||||
0x38, 0xa6, 0x0c, 0xc2, 0x0a, 0x3c, 0xb1, 0xa6, 0x62, 0x52, 0x71, 0x4c, |
||||
0x19, 0xc3, 0x0a, 0x3c, 0xb1, 0x96, 0x54, 0x54, 0x71, 0x4c, 0x19, 0xc4, |
||||
0x0a, 0x3c, 0xb1, 0x12, 0x33, 0x54, 0x71, 0x4c, 0x19, 0xc5, 0x09, 0x3c, |
||||
0xb1, 0x26, 0xad, 0x38, 0xa6, 0x0c, 0xc6, 0x0b, 0x3e, 0xf1, 0x3a, 0x52, |
||||
0x9d, 0xf2, 0x48, 0xb1, 0x73, 0xc7, 0x09, 0x44, 0xaf, 0x36, 0x72, 0x37, |
||||
0x9a, 0x09, 0xc8, 0x0a, 0x3c, 0xb1, 0x96, 0x51, 0x1a, 0x7a, 0xe5, 0x11, |
||||
0xc9, 0x09, 0x3c, 0xb1, 0x5a, 0x0e, 0xbd, 0xf2, 0x08, 0xca, 0x09, 0x3c, |
||||
0xb1, 0x5a, 0x8d, 0xbd, 0xf2, 0x08, 0xcb, 0x0a, 0x3c, 0xb1, 0x56, 0xa3, |
||||
0xa1, 0x57, 0x1e, 0x01, 0xcc, 0x08, 0x3a, 0x71, 0x12, 0x55, 0x17, 0x00, |
||||
0xcd, 0x07, 0x3a, 0x71, 0xa6, 0x7a, 0x01, 0xce, 0x07, 0x3b, 0x91, 0xd6, |
||||
0xd9, 0x05, 0xcf, 0x09, 0x3b, 0x91, 0x92, 0x32, 0x8a, 0x5d, 0x00, 0xd1, |
||||
0x0a, 0x3d, 0xd1, 0x66, 0xa6, 0x3c, 0x55, 0x92, 0x3b, 0xd2, 0x0a, 0x3d, |
||||
0xd1, 0x9a, 0x61, 0x5c, 0xd9, 0x4e, 0x0b, 0xd3, 0x09, 0x3d, 0xd1, 0x5e, |
||||
0xaf, 0x6c, 0xa7, 0x05, 0xd4, 0x0a, 0x3d, 0xd1, 0x9a, 0x53, 0x1a, 0xdb, |
||||
0x4e, 0x0b, 0xd6, 0x0a, 0x3d, 0xd1, 0x56, 0x53, 0x2b, 0xdb, 0x69, 0x01, |
||||
0xd8, 0x0a, 0x3d, 0xd1, 0x36, 0xe4, 0x95, 0xe6, 0xa1, 0x00, 0xd9, 0x09, |
||||
0x3c, 0xb1, 0x96, 0x51, 0xe9, 0x4c, 0x0a, 0xda, 0x08, 0x3c, 0xb1, 0xda, |
||||
0x74, 0x26, 0x05, 0xdb, 0x0a, 0x3c, 0xb1, 0xa6, 0x62, 0x46, 0xd1, 0x4c, |
||||
0x0a, 0xdc, 0x0a, 0x3c, 0xb1, 0x12, 0x33, 0x8a, 0xce, 0xa4, 0x00, 0xdf, |
||||
0x0a, 0x43, 0x8f, 0x56, 0xa9, 0x95, 0xd4, 0x8a, 0x00, 0xe0, 0x0b, 0x3c, |
||||
0xb1, 0x96, 0x51, 0xd4, 0x28, 0x8d, 0x98, 0x06, 0xe1, 0x0a, 0x3c, 0xb1, |
||||
0x1e, 0xa3, 0x46, 0x69, 0xc4, 0x34, 0xe2, 0x0b, 0x3c, 0xb1, 0xa6, 0x62, |
||||
0xd2, 0x28, 0x8d, 0x98, 0x06, 0xe3, 0x0b, 0x3c, 0xb1, 0x96, 0x54, 0xd4, |
||||
0x28, 0x8d, 0x98, 0x06, 0xe4, 0x0a, 0x3c, 0xb1, 0x56, 0x43, 0x8d, 0xd2, |
||||
0x88, 0x69, 0xe5, 0x0a, 0x3c, 0xb1, 0x5a, 0xc5, 0x8c, 0xd2, 0x88, 0x69, |
||||
0xe6, 0x0a, 0x2e, 0xf1, 0xa6, 0x32, 0xaa, 0xc6, 0xcc, 0x4a, 0xe7, 0x08, |
||||
0x33, 0x8f, 0x36, 0x6b, 0x95, 0x00, 0xe8, 0x0b, 0x3c, 0xb1, 0x96, 0x51, |
||||
0x54, 0x71, 0x6c, 0xa4, 0x00, 0xe9, 0x0a, 0x3c, 0xb1, 0x5a, 0xab, 0x38, |
||||
0x36, 0x52, 0x00, 0xea, 0x0b, 0x3c, 0xb1, 0xa6, 0x62, 0x52, 0x71, 0x6c, |
||||
0xa4, 0x00, 0xeb, 0x0a, 0x3c, 0xb1, 0x56, 0x43, 0x15, 0xc7, 0x46, 0x0a, |
||||
0xec, 0x08, 0x3a, 0x71, 0x12, 0x55, 0x17, 0x00, 0xed, 0x07, 0x3a, 0x71, |
||||
0xa6, 0x53, 0x0b, 0xee, 0x07, 0x3b, 0x91, 0xd6, 0xd9, 0x05, 0xef, 0x09, |
||||
0x3b, 0x91, 0x92, 0x32, 0x8a, 0x5d, 0x00, 0xf1, 0x09, 0x3c, 0xb1, 0x96, |
||||
0x54, 0x5a, 0xd1, 0x19, 0xf2, 0x0a, 0x3c, 0xb1, 0x96, 0x51, 0x54, 0xd1, |
||||
0x4c, 0x0a, 0xf3, 0x09, 0x3c, 0xb1, 0x5a, 0xab, 0x68, 0x26, 0x05, 0xf4, |
||||
0x0a, 0x3c, 0xb1, 0xa6, 0x62, 0x52, 0xd1, 0x4c, 0x0a, 0xf6, 0x0a, 0x3c, |
||||
0xb1, 0x12, 0x33, 0x54, 0xd1, 0x4c, 0x0a, 0xf8, 0x0a, 0x2c, 0xb1, 0x36, |
||||
0xa2, 0x1a, 0x69, 0x24, 0x00, 0xf9, 0x09, 0x3c, 0xb1, 0x96, 0x51, 0xe9, |
||||
0x4c, 0x03, 0xfa, 0x08, 0x3c, 0xb1, 0xda, 0x74, 0xa6, 0x01, 0xfb, 0x0a, |
||||
0x3c, 0xb1, 0xa6, 0x62, 0x46, 0xd1, 0x4c, 0x03, 0xfc, 0x09, 0x3c, 0xb1, |
||||
0x12, 0x33, 0x8a, 0xce, 0x34, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0x00, |
||||
0x00 |
||||
}; |
||||
unsigned int u8g2_font_NokiaSmallPlain_tf_u8g2font_len = 1381; |
||||
@ -0,0 +1,9 @@ |
||||
set(test_monoformat_SOURCES |
||||
test_font.cpp |
||||
test_utf8.cpp |
||||
) |
||||
|
||||
add_executable(test_monoformat ${test_monoformat_SOURCES}) |
||||
target_link_libraries(test_monoformat monoformat) |
||||
target_link_libraries(test_monoformat Catch2::Catch2WithMain) |
||||
|
||||
@ -0,0 +1,45 @@ |
||||
#include <catch2/catch_all.hpp> |
||||
|
||||
#include <monoformat_fontreader.hpp> |
||||
|
||||
static std::uint8_t const SampleFontData[] = { |
||||
0, 0, 4, 4, 8, 8, 8, 8, 8, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 2, // Header
|
||||
'\n', 0, // First glyph
|
||||
0, 4, 255, 255, // Unicode Table
|
||||
0, '\n', 0, // Unicode entry
|
||||
}; |
||||
|
||||
TEST_CASE("TestFontReader.CanReadFontProperties") { |
||||
auto font = monoformat::FontReader{std::as_bytes(std::span{SampleFontData})}; |
||||
|
||||
CHECK(font.supportsBackgroundColor() == false); |
||||
CHECK(font.glyphCount() == 0); |
||||
CHECK(font.m0() == 4); |
||||
CHECK(font.m1() == 4); |
||||
CHECK(font.bitCountW() == 8); |
||||
CHECK(font.bitCountH() == 8); |
||||
CHECK(font.bitCountX() == 8); |
||||
CHECK(font.bitCountY() == 8); |
||||
CHECK(font.bitCountD() == 8); |
||||
CHECK(font.fontBoundingBoxWidth() == 1); |
||||
CHECK(font.fontBoundingBoxHeight() == 2); |
||||
CHECK(font.fontBoundingBoxXOffset() == 3); |
||||
CHECK(font.fontBoundingBoxYOffset() == 4); |
||||
CHECK(font.ascent() == 5); |
||||
CHECK(font.descent() == 6); |
||||
CHECK(font.ascentOfParentheses() == 7); |
||||
CHECK(font.descentOfParentheses() == 8); |
||||
CHECK(font.arrayOffsetUpperA() == 0); |
||||
CHECK(font.arrayOffsetLowerA() == 0); |
||||
CHECK(font.arrayOffset0x0100() == 2); |
||||
CHECK(font.doesIgnoreUnknownGlyphs() == false); |
||||
CHECK(font.lineHeight() == 3); |
||||
} |
||||
|
||||
TEST_CASE("TestFontReader.CanHandleUnicodeNextIsZero") { |
||||
auto font = monoformat::FontReader{std::as_bytes(std::span{SampleFontData})}; |
||||
|
||||
auto glyph = font.retrieveGlyphData(0x2603); |
||||
REQUIRE(!!glyph == false); |
||||
CHECK(glyph.error() == monoformat::LookupError::GlyphNotFound); |
||||
} |
||||
@ -0,0 +1,41 @@ |
||||
#include <catch2/catch_all.hpp> |
||||
|
||||
#include <monoformat_utf8.hpp> |
||||
|
||||
TEST_CASE("TestUTF8.SimpleIterator") { |
||||
int i = 0; |
||||
for (std::uint32_t cp : monoformat::UTF8Iterator(std::string_view{"abc"})) { |
||||
if (i == 0) { |
||||
CHECK(cp == 'a'); |
||||
} else if (i == 1) { |
||||
CHECK(cp == 'b'); |
||||
} else if (i == 2) { |
||||
CHECK(cp == 'c'); |
||||
} |
||||
++i; |
||||
} |
||||
CHECK(i == 3); |
||||
i = 0; |
||||
for (std::uint32_t cp : monoformat::UTF8Iterator(std::string_view{"\xe2\x98\x83"})) { |
||||
if (i == 0) { |
||||
CHECK(cp == 0x2603); |
||||
} |
||||
++i; |
||||
} |
||||
CHECK(i == 1); |
||||
} |
||||
|
||||
TEST_CASE("TestUTF8.Invalid") { |
||||
int i = 0; |
||||
for (std::uint32_t cp : monoformat::UTF8Iterator(std::string_view{"\x80\xe2\x98\x83\xe2\x98"})) { |
||||
if (i == 0) { |
||||
CHECK(cp == ~std::uint32_t{0}); |
||||
} else if (i == 1) { |
||||
CHECK(cp == 0x2603); |
||||
} else if (i == 2) { |
||||
CHECK(cp == ~std::uint32_t{0}); |
||||
} |
||||
++i; |
||||
} |
||||
CHECK(i == 3); |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue