## Summary
* Consolidate chapter page data into single file
* Header structure of the file stays the same, following the page count,
we now put a LUT offset
* The page data is all then appended to this file
* Finally the LUT is appended to the end of the file, and the page count
is updated
* This will also significantly improve the duration of cache cleanup
which takes a while to scan the directory and cleanup content
* Remove page file version as it's all tied up into the section file now
* Bumped section file version to 7
* Moved section content into sub directory
* Updated docs
## Additional Context
* Benchmarks:
* Generating 74 pages of content from a chapter in Jade Legacy took:
* master: 6,229ms
* this PR: 1,305ms
* Speedup of 79%
* Generating 207 pages of content from Livesuit book:
* With progress bar UI updates:
* master: 24,250ms
* this PR: 8,063ms
* Speedup of 67%
* Without progress bar UI updates:
* master: 13,055ms
* this PR: 3,600ms
* Speedup of 72%
75 lines
2.4 KiB
C++
75 lines
2.4 KiB
C++
#include "TextBlock.h"
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <Serialization.h>
|
|
|
|
void TextBlock::render(const GfxRenderer& renderer, const int fontId, const int x, const int y) const {
|
|
// Validate iterator bounds before rendering
|
|
if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) {
|
|
Serial.printf("[%lu] [TXB] Render skipped: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(),
|
|
(uint32_t)words.size(), (uint32_t)wordXpos.size(), (uint32_t)wordStyles.size());
|
|
return;
|
|
}
|
|
|
|
auto wordIt = words.begin();
|
|
auto wordStylesIt = wordStyles.begin();
|
|
auto wordXposIt = wordXpos.begin();
|
|
|
|
for (size_t i = 0; i < words.size(); i++) {
|
|
renderer.drawText(fontId, *wordXposIt + x, y, wordIt->c_str(), true, *wordStylesIt);
|
|
|
|
std::advance(wordIt, 1);
|
|
std::advance(wordStylesIt, 1);
|
|
std::advance(wordXposIt, 1);
|
|
}
|
|
}
|
|
|
|
bool TextBlock::serialize(File& file) const {
|
|
if (words.size() != wordXpos.size() || words.size() != wordStyles.size()) {
|
|
Serial.printf("[%lu] [TXB] Serialization failed: size mismatch (words=%u, xpos=%u, styles=%u)\n", millis(),
|
|
words.size(), wordXpos.size(), wordStyles.size());
|
|
return false;
|
|
}
|
|
|
|
// Word data
|
|
serialization::writePod(file, static_cast<uint32_t>(words.size()));
|
|
for (const auto& w : words) serialization::writeString(file, w);
|
|
for (auto x : wordXpos) serialization::writePod(file, x);
|
|
for (auto s : wordStyles) serialization::writePod(file, s);
|
|
|
|
// Block style
|
|
serialization::writePod(file, style);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<TextBlock> TextBlock::deserialize(File& file) {
|
|
uint32_t wc;
|
|
std::list<std::string> words;
|
|
std::list<uint16_t> wordXpos;
|
|
std::list<EpdFontStyle> wordStyles;
|
|
BLOCK_STYLE style;
|
|
|
|
// Word count
|
|
serialization::readPod(file, wc);
|
|
|
|
// Sanity check: prevent allocation of unreasonably large lists (max 10000 words per block)
|
|
if (wc > 10000) {
|
|
Serial.printf("[%lu] [TXB] Deserialization failed: word count %u exceeds maximum\n", millis(), wc);
|
|
return nullptr;
|
|
}
|
|
|
|
// Word data
|
|
words.resize(wc);
|
|
wordXpos.resize(wc);
|
|
wordStyles.resize(wc);
|
|
for (auto& w : words) serialization::readString(file, w);
|
|
for (auto& x : wordXpos) serialization::readPod(file, x);
|
|
for (auto& s : wordStyles) serialization::readPod(file, s);
|
|
|
|
// Block style
|
|
serialization::readPod(file, style);
|
|
|
|
return std::unique_ptr<TextBlock>(new TextBlock(std::move(words), std::move(wordXpos), std::move(wordStyles), style));
|
|
}
|