feat: render grayscale epub images without text aa (#2393)
This commit is contained in:
+20
-3
@@ -6,6 +6,20 @@
|
||||
|
||||
#include <new>
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Predicate>
|
||||
void renderFilteredPageElements(const std::vector<std::shared_ptr<PageElement>>& elements, GfxRenderer& renderer,
|
||||
const int fontId, const int xOffset, const int yOffset, Predicate&& predicate) {
|
||||
for (const auto& element : elements) {
|
||||
if (predicate(*element)) {
|
||||
element->render(renderer, fontId, xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void PageLine::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
|
||||
block->render(renderer, fontId, xPos + xOffset, yPos + yOffset);
|
||||
}
|
||||
@@ -93,9 +107,12 @@ std::unique_ptr<PageHorizontalRule> PageHorizontalRule::deserialize(HalFile& fil
|
||||
}
|
||||
|
||||
void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) const {
|
||||
for (auto& element : elements) {
|
||||
element->render(renderer, fontId, xOffset, yOffset);
|
||||
}
|
||||
renderFilteredPageElements(elements, renderer, fontId, xOffset, yOffset, [](const PageElement&) { return true; });
|
||||
}
|
||||
|
||||
void Page::renderImages(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) const {
|
||||
renderFilteredPageElements(elements, renderer, fontId, xOffset, yOffset,
|
||||
[](const PageElement& element) { return element.getTag() == TAG_PageImage; });
|
||||
}
|
||||
|
||||
bool Page::serialize(HalFile& file) const {
|
||||
|
||||
@@ -88,6 +88,7 @@ class Page {
|
||||
}
|
||||
|
||||
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
void renderImages(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) const;
|
||||
bool serialize(HalFile& file) const;
|
||||
static std::unique_ptr<Page> deserialize(HalFile& file);
|
||||
|
||||
|
||||
@@ -976,22 +976,31 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
const int orientedMarginRight, const int orientedMarginBottom,
|
||||
const int orientedMarginLeft) {
|
||||
const auto t0 = millis();
|
||||
const int fontId = SETTINGS.getReaderFontId();
|
||||
|
||||
// Font prewarm: scan pass accumulates text, then prewarm, then real render
|
||||
auto* fcm = renderer.getFontCacheManager();
|
||||
auto scope = fcm->createPrewarmScope();
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop); // scan pass
|
||||
page->render(renderer, fontId, orientedMarginLeft, orientedMarginTop); // scan pass
|
||||
scope.endScanAndPrewarm();
|
||||
const auto tPrewarm = millis();
|
||||
|
||||
// Force special handling for pages with images when anti-aliasing is on
|
||||
bool imagePageWithAA = page->hasImages() && SETTINGS.textAntiAliasing;
|
||||
const bool pageHasImages = page->hasImages();
|
||||
const bool needsTextGrayscale = SETTINGS.textAntiAliasing;
|
||||
const bool needsAnyGrayscale = needsTextGrayscale || pageHasImages;
|
||||
auto renderGrayscalePass = [&]() {
|
||||
if (needsTextGrayscale) {
|
||||
page->render(renderer, fontId, orientedMarginLeft, orientedMarginTop);
|
||||
} else {
|
||||
page->renderImages(renderer, fontId, orientedMarginLeft, orientedMarginTop);
|
||||
}
|
||||
};
|
||||
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, fontId, orientedMarginLeft, orientedMarginTop);
|
||||
renderStatusBar();
|
||||
const auto tBwRender = millis();
|
||||
|
||||
if (imagePageWithAA) {
|
||||
if (pageHasImages) {
|
||||
// Double FAST_REFRESH with selective image blanking (pablohc's technique):
|
||||
// HALF_REFRESH sets particles too firmly for the grayscale LUT to adjust.
|
||||
// Instead, blank only the image area and do two fast refreshes.
|
||||
@@ -1004,7 +1013,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
|
||||
// Re-render page content to restore images into the blanked area
|
||||
// Status bar is not re-rendered here to avoid reading stale dynamic values (e.g. battery %)
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
page->render(renderer, fontId, orientedMarginLeft, orientedMarginTop);
|
||||
renderer.displayBuffer(HalDisplay::FAST_REFRESH);
|
||||
} else {
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
@@ -1028,7 +1037,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
// per plane, but renderCharImpl culls out-of-band glyphs before decode so the
|
||||
// cost stays close to one render. Both text (drawPixel) and images
|
||||
// (DirectPixelWriter) honor the active strip target.
|
||||
if (SETTINGS.textAntiAliasing && renderer.supportsStripGrayscale()) {
|
||||
if (needsAnyGrayscale && renderer.supportsStripGrayscale()) {
|
||||
constexpr int STRIP_ROWS = 80;
|
||||
const int gh = renderer.getDisplayHeight();
|
||||
const int gwBytes = renderer.getDisplayWidthBytes();
|
||||
@@ -1044,7 +1053,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
const int rows = (gh - y < STRIP_ROWS) ? (gh - y) : STRIP_ROWS;
|
||||
renderer.beginStripTarget(scratch.get(), y, rows);
|
||||
renderer.clearScreen(0x00);
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
renderGrayscalePass();
|
||||
renderer.endStripTarget();
|
||||
renderer.writeGrayscalePlaneStrip(true, scratch.get(), y, rows);
|
||||
}
|
||||
@@ -1056,7 +1065,7 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
const int rows = (gh - y < STRIP_ROWS) ? (gh - y) : STRIP_ROWS;
|
||||
renderer.beginStripTarget(scratch.get(), y, rows);
|
||||
renderer.clearScreen(0x00);
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
renderGrayscalePass();
|
||||
renderer.endStripTarget();
|
||||
renderer.writeGrayscalePlaneStrip(false, scratch.get(), y, rows);
|
||||
}
|
||||
@@ -1081,22 +1090,28 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
} else {
|
||||
// Fallback path for a controller without strip support. grayscale rendering
|
||||
// TODO: Only do this if font supports it
|
||||
if (SETTINGS.textAntiAliasing) {
|
||||
if (needsAnyGrayscale) {
|
||||
// Save the BW frame before the grayscale passes overwrite it, restore
|
||||
// after. Only needed when grayscale actually renders.
|
||||
renderer.storeBwBuffer();
|
||||
if (!renderer.storeBwBuffer()) {
|
||||
LOG_ERR("ERS", "Failed to store BW buffer for grayscale render; skipping grayscale this page");
|
||||
const auto tEnd = millis();
|
||||
LOG_DBG("ERS", "Page render: prewarm=%lums bw_render=%lums display=%lums total=%lums", tPrewarm - t0,
|
||||
tBwRender - tPrewarm, tDisplay - tBwRender, tEnd - t0);
|
||||
return;
|
||||
}
|
||||
const auto tBwStore = millis();
|
||||
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_LSB);
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
renderGrayscalePass();
|
||||
renderer.copyGrayscaleLsbBuffers();
|
||||
const auto tGrayLsb = millis();
|
||||
|
||||
// Render and copy to MSB buffer
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setRenderMode(GfxRenderer::GRAYSCALE_MSB);
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
renderGrayscalePass();
|
||||
renderer.copyGrayscaleMsbBuffers();
|
||||
const auto tGrayMsb = millis();
|
||||
|
||||
@@ -1114,8 +1129,8 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
tPrewarm - t0, tBwRender - tPrewarm, tDisplay - tBwRender, tBwStore - tDisplay, tGrayLsb - tBwStore,
|
||||
tGrayMsb - tGrayLsb, tGrayDisplay - tGrayMsb, tBwRestore - tGrayDisplay, tEnd - t0);
|
||||
} else {
|
||||
// No anti-aliasing: BW frame already displayed above, no grayscale to
|
||||
// render, so no save/restore.
|
||||
// No text AA and no images: BW frame already displayed above, no grayscale
|
||||
// to render, so no save/restore.
|
||||
const auto tEnd = millis();
|
||||
LOG_DBG("ERS", "Page render: prewarm=%lums bw_render=%lums display=%lums total=%lums", tPrewarm - t0,
|
||||
tBwRender - tPrewarm, tDisplay - tBwRender, tEnd - t0);
|
||||
|
||||
Reference in New Issue
Block a user