Use viewport sizes over margins in epub rendering
This commit is contained in:
parent
c6c09522ce
commit
cd4ccade9f
@ -7,7 +7,9 @@ namespace {
|
||||
constexpr uint8_t PAGE_FILE_VERSION = 3;
|
||||
}
|
||||
|
||||
void PageLine::render(GfxRenderer& renderer, const int fontId) { block->render(renderer, fontId, xPos, yPos); }
|
||||
void PageLine::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
|
||||
block->render(renderer, fontId, xPos + xOffset, yPos + yOffset);
|
||||
}
|
||||
|
||||
void PageLine::serialize(File& file) {
|
||||
serialization::writePod(file, xPos);
|
||||
@ -27,9 +29,9 @@ std::unique_ptr<PageLine> PageLine::deserialize(File& file) {
|
||||
return std::unique_ptr<PageLine>(new PageLine(std::move(tb), xPos, yPos));
|
||||
}
|
||||
|
||||
void Page::render(GfxRenderer& renderer, const int fontId) const {
|
||||
void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) const {
|
||||
for (auto& element : elements) {
|
||||
element->render(renderer, fontId);
|
||||
element->render(renderer, fontId, xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ class PageElement {
|
||||
int16_t yPos;
|
||||
explicit PageElement(const int16_t xPos, const int16_t yPos) : xPos(xPos), yPos(yPos) {}
|
||||
virtual ~PageElement() = default;
|
||||
virtual void render(GfxRenderer& renderer, int fontId) = 0;
|
||||
virtual void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) = 0;
|
||||
virtual void serialize(File& file) = 0;
|
||||
};
|
||||
|
||||
@ -28,7 +28,7 @@ class PageLine final : public PageElement {
|
||||
public:
|
||||
PageLine(std::shared_ptr<TextBlock> block, const int16_t xPos, const int16_t yPos)
|
||||
: PageElement(xPos, yPos), block(std::move(block)) {}
|
||||
void render(GfxRenderer& renderer, int fontId) override;
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;
|
||||
void serialize(File& file) override;
|
||||
static std::unique_ptr<PageLine> deserialize(File& file);
|
||||
};
|
||||
@ -37,7 +37,7 @@ class Page {
|
||||
public:
|
||||
// the list of block index and line numbers on this page
|
||||
std::vector<std::shared_ptr<PageElement>> elements;
|
||||
void render(GfxRenderer& renderer, int fontId) const;
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
void serialize(File& file) const;
|
||||
static std::unique_ptr<Page> deserialize(File& file);
|
||||
};
|
||||
|
||||
@ -18,14 +18,14 @@ void ParsedText::addWord(std::string word, const EpdFontStyle fontStyle) {
|
||||
}
|
||||
|
||||
// Consumes data to minimize memory usage
|
||||
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const int horizontalMargin,
|
||||
void ParsedText::layoutAndExtractLines(const GfxRenderer& renderer, const int fontId, const int viewportWidth,
|
||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
||||
const bool includeLastLine) {
|
||||
if (words.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int pageWidth = renderer.getScreenWidth() - horizontalMargin;
|
||||
const int pageWidth = viewportWidth;
|
||||
const int spaceWidth = renderer.getSpaceWidth(fontId);
|
||||
const auto wordWidths = calculateWordWidths(renderer, fontId);
|
||||
const auto lineBreakIndices = computeLineBreaks(pageWidth, spaceWidth, wordWidths);
|
||||
|
||||
@ -34,7 +34,7 @@ class ParsedText {
|
||||
TextBlock::BLOCK_STYLE getStyle() const { return style; }
|
||||
size_t size() const { return words.size(); }
|
||||
bool isEmpty() const { return words.empty(); }
|
||||
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, int horizontalMargin,
|
||||
void layoutAndExtractLines(const GfxRenderer& renderer, int fontId, int viewportWidth,
|
||||
const std::function<void(std::shared_ptr<TextBlock>)>& processLine,
|
||||
bool includeLastLine = true);
|
||||
};
|
||||
|
||||
@ -26,10 +26,8 @@ void Section::onPageComplete(std::unique_ptr<Page> page) {
|
||||
pageCount++;
|
||||
}
|
||||
|
||||
void Section::writeCacheMetadata(const int fontId, const float lineCompression, const int marginTop,
|
||||
const int marginRight, const int marginBottom, const int marginLeft,
|
||||
const bool extraParagraphSpacing, const int screenWidth,
|
||||
const int screenHeight) const {
|
||||
void Section::writeCacheMetadata(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||
const int viewportWidth, const int viewportHeight) const {
|
||||
File outputFile;
|
||||
if (!FsHelpers::openFileForWrite("SCT", cachePath + "/section.bin", outputFile)) {
|
||||
return;
|
||||
@ -37,20 +35,15 @@ void Section::writeCacheMetadata(const int fontId, const float lineCompression,
|
||||
serialization::writePod(outputFile, SECTION_FILE_VERSION);
|
||||
serialization::writePod(outputFile, fontId);
|
||||
serialization::writePod(outputFile, lineCompression);
|
||||
serialization::writePod(outputFile, marginTop);
|
||||
serialization::writePod(outputFile, marginRight);
|
||||
serialization::writePod(outputFile, marginBottom);
|
||||
serialization::writePod(outputFile, marginLeft);
|
||||
serialization::writePod(outputFile, extraParagraphSpacing);
|
||||
serialization::writePod(outputFile, screenWidth);
|
||||
serialization::writePod(outputFile, screenHeight);
|
||||
serialization::writePod(outputFile, viewportWidth);
|
||||
serialization::writePod(outputFile, viewportHeight);
|
||||
serialization::writePod(outputFile, pageCount);
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
bool Section::loadCacheMetadata(const int fontId, const float lineCompression, const int marginTop,
|
||||
const int marginRight, const int marginBottom, const int marginLeft,
|
||||
const bool extraParagraphSpacing, const int screenWidth, const int screenHeight) {
|
||||
bool Section::loadCacheMetadata(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||
const int viewportWidth, const int viewportHeight) {
|
||||
const auto sectionFilePath = cachePath + "/section.bin";
|
||||
File inputFile;
|
||||
if (!FsHelpers::openFileForRead("SCT", sectionFilePath, inputFile)) {
|
||||
@ -68,24 +61,18 @@ bool Section::loadCacheMetadata(const int fontId, const float lineCompression, c
|
||||
return false;
|
||||
}
|
||||
|
||||
int fileFontId, fileMarginTop, fileMarginRight, fileMarginBottom, fileMarginLeft;
|
||||
int fileFontId, fileViewportWidth, fileViewportHeight;
|
||||
float fileLineCompression;
|
||||
bool fileExtraParagraphSpacing;
|
||||
int fileScreenWidth, fileScreenHeight;
|
||||
serialization::readPod(inputFile, fileFontId);
|
||||
serialization::readPod(inputFile, fileLineCompression);
|
||||
serialization::readPod(inputFile, fileMarginTop);
|
||||
serialization::readPod(inputFile, fileMarginRight);
|
||||
serialization::readPod(inputFile, fileMarginBottom);
|
||||
serialization::readPod(inputFile, fileMarginLeft);
|
||||
serialization::readPod(inputFile, fileExtraParagraphSpacing);
|
||||
serialization::readPod(inputFile, fileScreenWidth);
|
||||
serialization::readPod(inputFile, fileScreenHeight);
|
||||
serialization::readPod(inputFile, fileViewportWidth);
|
||||
serialization::readPod(inputFile, fileViewportHeight);
|
||||
|
||||
if (fontId != fileFontId || lineCompression != fileLineCompression || marginTop != fileMarginTop ||
|
||||
marginRight != fileMarginRight || marginBottom != fileMarginBottom || marginLeft != fileMarginLeft ||
|
||||
extraParagraphSpacing != fileExtraParagraphSpacing || screenWidth != fileScreenWidth ||
|
||||
screenHeight != fileScreenHeight) {
|
||||
if (fontId != fileFontId || lineCompression != fileLineCompression ||
|
||||
extraParagraphSpacing != fileExtraParagraphSpacing || viewportWidth != fileViewportWidth ||
|
||||
viewportHeight != fileViewportHeight) {
|
||||
inputFile.close();
|
||||
Serial.printf("[%lu] [SCT] Deserialization failed: Parameters do not match\n", millis());
|
||||
clearCache();
|
||||
@ -120,9 +107,8 @@ bool Section::clearCache() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const int marginTop,
|
||||
const int marginRight, const int marginBottom, const int marginLeft,
|
||||
const bool extraParagraphSpacing, const int screenWidth, const int screenHeight,
|
||||
bool Section::persistPageDataToSD(const int fontId, const float lineCompression, const bool extraParagraphSpacing,
|
||||
const int viewportWidth, const int viewportHeight,
|
||||
const std::function<void()>& progressSetupFn,
|
||||
const std::function<void(int)>& progressFn) {
|
||||
constexpr size_t MIN_SIZE_FOR_PROGRESS = 50 * 1024; // 50KB
|
||||
@ -171,8 +157,8 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
|
||||
}
|
||||
|
||||
ChapterHtmlSlimParser visitor(
|
||||
tmpHtmlPath, renderer, fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
|
||||
extraParagraphSpacing, [this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }, progressFn);
|
||||
tmpHtmlPath, renderer, fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight,
|
||||
[this](std::unique_ptr<Page> page) { this->onPageComplete(std::move(page)); }, progressFn);
|
||||
success = visitor.parseAndBuildPages();
|
||||
|
||||
SD.remove(tmpHtmlPath.c_str());
|
||||
@ -181,8 +167,7 @@ bool Section::persistPageDataToSD(const int fontId, const float lineCompression,
|
||||
return false;
|
||||
}
|
||||
|
||||
writeCacheMetadata(fontId, lineCompression, marginTop, marginRight, marginBottom, marginLeft, extraParagraphSpacing,
|
||||
screenWidth, screenHeight);
|
||||
writeCacheMetadata(fontId, lineCompression, extraParagraphSpacing, viewportWidth, viewportHeight);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13,8 +13,8 @@ class Section {
|
||||
GfxRenderer& renderer;
|
||||
std::string cachePath;
|
||||
|
||||
void writeCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
|
||||
int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight) const;
|
||||
void writeCacheMetadata(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight) const;
|
||||
void onPageComplete(std::unique_ptr<Page> page);
|
||||
|
||||
public:
|
||||
@ -27,13 +27,12 @@ class Section {
|
||||
renderer(renderer),
|
||||
cachePath(epub->getCachePath() + "/" + std::to_string(spineIndex)) {}
|
||||
~Section() = default;
|
||||
bool loadCacheMetadata(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
|
||||
int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight);
|
||||
bool loadCacheMetadata(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight);
|
||||
void setupCacheDir() const;
|
||||
bool clearCache() const;
|
||||
bool persistPageDataToSD(int fontId, float lineCompression, int marginTop, int marginRight, int marginBottom,
|
||||
int marginLeft, bool extraParagraphSpacing, int screenWidth, int screenHeight,
|
||||
const std::function<void()>& progressSetupFn = nullptr,
|
||||
bool persistPageDataToSD(int fontId, float lineCompression, bool extraParagraphSpacing, int viewportWidth,
|
||||
int viewportHeight, const std::function<void()>& progressSetupFn = nullptr,
|
||||
const std::function<void(int)>& progressFn = nullptr);
|
||||
std::unique_ptr<Page> loadPageFromSD() const;
|
||||
};
|
||||
|
||||
@ -155,7 +155,7 @@ void XMLCALL ChapterHtmlSlimParser::characterData(void* userData, const XML_Char
|
||||
if (self->currentTextBlock->size() > 750) {
|
||||
Serial.printf("[%lu] [EHP] Text block too long, splitting into multiple pages\n", millis());
|
||||
self->currentTextBlock->layoutAndExtractLines(
|
||||
self->renderer, self->fontId, self->marginLeft + self->marginRight,
|
||||
self->renderer, self->fontId, self->viewportWidth,
|
||||
[self](const std::shared_ptr<TextBlock>& textBlock) { self->addLineToPage(textBlock); }, false);
|
||||
}
|
||||
}
|
||||
@ -301,15 +301,14 @@ bool ChapterHtmlSlimParser::parseAndBuildPages() {
|
||||
|
||||
void ChapterHtmlSlimParser::addLineToPage(std::shared_ptr<TextBlock> line) {
|
||||
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
||||
const int pageHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
||||
|
||||
if (currentPageNextY + lineHeight > pageHeight) {
|
||||
if (currentPageNextY + lineHeight > viewportHeight) {
|
||||
completePageFn(std::move(currentPage));
|
||||
currentPage.reset(new Page());
|
||||
currentPageNextY = marginTop;
|
||||
currentPageNextY = 0;
|
||||
}
|
||||
|
||||
currentPage->elements.push_back(std::make_shared<PageLine>(line, marginLeft, currentPageNextY));
|
||||
currentPage->elements.push_back(std::make_shared<PageLine>(line, 0, currentPageNextY));
|
||||
currentPageNextY += lineHeight;
|
||||
}
|
||||
|
||||
@ -321,12 +320,12 @@ void ChapterHtmlSlimParser::makePages() {
|
||||
|
||||
if (!currentPage) {
|
||||
currentPage.reset(new Page());
|
||||
currentPageNextY = marginTop;
|
||||
currentPageNextY = 0;
|
||||
}
|
||||
|
||||
const int lineHeight = renderer.getLineHeight(fontId) * lineCompression;
|
||||
currentTextBlock->layoutAndExtractLines(
|
||||
renderer, fontId, marginLeft + marginRight,
|
||||
renderer, fontId, viewportWidth,
|
||||
[this](const std::shared_ptr<TextBlock>& textBlock) { addLineToPage(textBlock); });
|
||||
// Extra paragraph spacing if enabled
|
||||
if (extraParagraphSpacing) {
|
||||
|
||||
@ -32,11 +32,9 @@ class ChapterHtmlSlimParser {
|
||||
int16_t currentPageNextY = 0;
|
||||
int fontId;
|
||||
float lineCompression;
|
||||
int marginTop;
|
||||
int marginRight;
|
||||
int marginBottom;
|
||||
int marginLeft;
|
||||
bool extraParagraphSpacing;
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
|
||||
void startNewTextBlock(TextBlock::BLOCK_STYLE style);
|
||||
void makePages();
|
||||
@ -47,19 +45,17 @@ class ChapterHtmlSlimParser {
|
||||
|
||||
public:
|
||||
explicit ChapterHtmlSlimParser(const std::string& filepath, GfxRenderer& renderer, const int fontId,
|
||||
const float lineCompression, const int marginTop, const int marginRight,
|
||||
const int marginBottom, const int marginLeft, const bool extraParagraphSpacing,
|
||||
const float lineCompression, const bool extraParagraphSpacing, const int viewportWidth,
|
||||
const int viewportHeight,
|
||||
const std::function<void(std::unique_ptr<Page>)>& completePageFn,
|
||||
const std::function<void(int)>& progressFn = nullptr)
|
||||
: filepath(filepath),
|
||||
renderer(renderer),
|
||||
fontId(fontId),
|
||||
lineCompression(lineCompression),
|
||||
marginTop(marginTop),
|
||||
marginRight(marginRight),
|
||||
marginBottom(marginBottom),
|
||||
marginLeft(marginLeft),
|
||||
extraParagraphSpacing(extraParagraphSpacing),
|
||||
viewportWidth(viewportWidth),
|
||||
viewportHeight(viewportHeight),
|
||||
completePageFn(completePageFn),
|
||||
progressFn(progressFn) {}
|
||||
~ChapterHtmlSlimParser() = default;
|
||||
|
||||
@ -36,7 +36,7 @@ class GfxRenderer {
|
||||
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
||||
EpdFontStyle style) const;
|
||||
void freeBwBufferChunks();
|
||||
void rotateCoordinates(int x, int y, int *rotatedX, int *rotatedY) const;
|
||||
void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const;
|
||||
|
||||
public:
|
||||
explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW) {}
|
||||
|
||||
@ -244,9 +244,12 @@ void EpubReaderActivity::renderScreen() {
|
||||
const auto filepath = epub->getSpineItem(currentSpineIndex).href;
|
||||
Serial.printf("[%lu] [ERS] Loading file: %s, index: %d\n", millis(), filepath.c_str(), currentSpineIndex);
|
||||
section = std::unique_ptr<Section>(new Section(epub, currentSpineIndex, renderer));
|
||||
if (!section->loadCacheMetadata(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom, marginLeft,
|
||||
SETTINGS.extraParagraphSpacing, renderer.getScreenWidth(),
|
||||
renderer.getScreenHeight())) {
|
||||
|
||||
const auto viewportWidth = renderer.getScreenWidth() - marginLeft - marginRight;
|
||||
const auto viewportHeight = renderer.getScreenHeight() - marginTop - marginBottom;
|
||||
|
||||
if (!section->loadCacheMetadata(READER_FONT_ID, lineCompression, SETTINGS.extraParagraphSpacing, viewportWidth,
|
||||
viewportHeight)) {
|
||||
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||
|
||||
// Progress bar dimensions
|
||||
@ -292,9 +295,8 @@ void EpubReaderActivity::renderScreen() {
|
||||
renderer.displayBuffer(EInkDisplay::FAST_REFRESH);
|
||||
};
|
||||
|
||||
if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, marginTop, marginRight, marginBottom,
|
||||
marginLeft, SETTINGS.extraParagraphSpacing, renderer.getScreenWidth(),
|
||||
renderer.getScreenHeight(), progressSetup, progressCallback)) {
|
||||
if (!section->persistPageDataToSD(READER_FONT_ID, lineCompression, SETTINGS.extraParagraphSpacing, viewportWidth,
|
||||
viewportHeight, progressSetup, progressCallback)) {
|
||||
Serial.printf("[%lu] [ERS] Failed to persist page data to SD\n", millis());
|
||||
section.reset();
|
||||
return;
|
||||
@ -354,7 +356,7 @@ void EpubReaderActivity::renderScreen() {
|
||||
}
|
||||
|
||||
void EpubReaderActivity::renderContents(std::unique_ptr<Page> page) {
|
||||
page->render(renderer, READER_FONT_ID);
|
||||
page->render(renderer, READER_FONT_ID, marginLeft, marginTop);
|
||||
renderStatusBar();
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
@ -372,13 +374,13 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page) {
|
||||
{
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_LSB);
|
||||
page->render(renderer, READER_FONT_ID);
|
||||
page->render(renderer, READER_FONT_ID, marginLeft, marginTop);
|
||||
renderer.copyGrayscaleLsbBuffers();
|
||||
|
||||
// Render and copy to MSB buffer
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_MSB);
|
||||
page->render(renderer, READER_FONT_ID);
|
||||
page->render(renderer, READER_FONT_ID, marginLeft, marginTop);
|
||||
renderer.copyGrayscaleMsbBuffers();
|
||||
|
||||
// display grayscale part
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user