From 41eabba0d43fbfa641e7b6ce15761330fe94b978 Mon Sep 17 00:00:00 2001 From: Martin Brook Date: Mon, 26 Jan 2026 22:16:30 +0000 Subject: [PATCH] style: apply clang-format-21 formatting --- lib/Epub/Epub/Page.cpp | 2 +- lib/Epub/Epub/Page.h | 4 +- lib/Epub/Epub/blocks/ImageBlock.cpp | 22 +++--- lib/Epub/Epub/blocks/ImageBlock.h | 12 +-- .../Epub/converters/FramebufferWriter.cpp | 8 +- lib/Epub/Epub/converters/FramebufferWriter.h | 14 ++-- .../Epub/converters/ImageDecoderFactory.cpp | 11 ++- .../Epub/converters/ImageDecoderFactory.h | 4 +- .../converters/ImageToFramebufferDecoder.cpp | 9 ++- .../converters/ImageToFramebufferDecoder.h | 10 +-- .../converters/JpegToFramebufferConverter.cpp | 71 +++++++++--------- .../converters/JpegToFramebufferConverter.h | 19 ++--- .../converters/PngToFramebufferConverter.cpp | 75 +++++++++---------- .../converters/PngToFramebufferConverter.h | 11 +-- .../Epub/parsers/ChapterHtmlSlimParser.cpp | 36 ++++----- lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h | 6 +- 16 files changed, 150 insertions(+), 164 deletions(-) diff --git a/lib/Epub/Epub/Page.cpp b/lib/Epub/Epub/Page.cpp index 53f9ec8..e8b98a3 100644 --- a/lib/Epub/Epub/Page.cpp +++ b/lib/Epub/Epub/Page.cpp @@ -61,7 +61,7 @@ bool Page::serialize(FsFile& file) const { for (const auto& el : elements) { // Use getTag() method to determine type serialization::writePod(file, static_cast(el->getTag())); - + if (!el->serialize(file)) { return false; } diff --git a/lib/Epub/Epub/Page.h b/lib/Epub/Epub/Page.h index bbe8e6a..6985584 100644 --- a/lib/Epub/Epub/Page.h +++ b/lib/Epub/Epub/Page.h @@ -4,8 +4,8 @@ #include #include -#include "blocks/TextBlock.h" #include "blocks/ImageBlock.h" +#include "blocks/TextBlock.h" enum PageElementTag : uint8_t { TAG_PageLine = 1, @@ -41,7 +41,7 @@ class PageLine final : public PageElement { class PageImage final : public PageElement { std::shared_ptr imageBlock; -public: + public: PageImage(std::shared_ptr block, const int16_t xPos, const int16_t yPos) : PageElement(xPos, yPos), imageBlock(std::move(block)) {} void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override; diff --git a/lib/Epub/Epub/blocks/ImageBlock.cpp b/lib/Epub/Epub/blocks/ImageBlock.cpp index dcbd6e9..7c52dd8 100644 --- a/lib/Epub/Epub/blocks/ImageBlock.cpp +++ b/lib/Epub/Epub/blocks/ImageBlock.cpp @@ -1,10 +1,12 @@ #include "ImageBlock.h" -#include "../converters/ImageDecoderFactory.h" + +#include #include #include -#include #include -#include +#include + +#include "../converters/ImageDecoderFactory.h" // Cache file format: // - uint16_t width @@ -19,8 +21,7 @@ bool ImageBlock::imageExists() const { return SdMan.openFileForRead("IMG", imagePath, file); } -void ImageBlock::layout(GfxRenderer& renderer) { -} +void ImageBlock::layout(GfxRenderer& renderer) {} static std::string getCachePath(const std::string& imagePath) { // Replace extension with .pxc (pixel cache) @@ -31,7 +32,8 @@ static std::string getCachePath(const std::string& imagePath) { return imagePath + ".pxc"; } -static bool renderFromCache(GfxRenderer& renderer, const std::string& cachePath, int x, int y, int expectedWidth, int expectedHeight) { +static bool renderFromCache(GfxRenderer& renderer, const std::string& cachePath, int x, int y, int expectedWidth, + int expectedHeight) { FsFile cacheFile; if (!SdMan.openFileForRead("IMG", cachePath, cacheFile)) { return false; @@ -47,8 +49,8 @@ static bool renderFromCache(GfxRenderer& renderer, const std::string& cachePath, int widthDiff = abs(cachedWidth - expectedWidth); int heightDiff = abs(cachedHeight - expectedHeight); if (widthDiff > 1 || heightDiff > 1) { - Serial.printf("[%lu] [IMG] Cache dimension mismatch: %dx%d vs %dx%d\n", millis(), - cachedWidth, cachedHeight, expectedWidth, expectedHeight); + Serial.printf("[%lu] [IMG] Cache dimension mismatch: %dx%d vs %dx%d\n", millis(), cachedWidth, cachedHeight, + expectedWidth, expectedHeight); cacheFile.close(); return false; } @@ -99,8 +101,8 @@ void ImageBlock::render(GfxRenderer& renderer, const int x, const int y) { // Bounds check render position using logical screen dimensions if (x < 0 || y < 0 || x + width > screenWidth || y + height > screenHeight) { - Serial.printf("[%lu] [IMG] Invalid render position: (%d,%d) size (%dx%d) screen (%dx%d)\n", - millis(), x, y, width, height, screenWidth, screenHeight); + Serial.printf("[%lu] [IMG] Invalid render position: (%d,%d) size (%dx%d) screen (%dx%d)\n", millis(), x, y, width, + height, screenWidth, screenHeight); return; } diff --git a/lib/Epub/Epub/blocks/ImageBlock.h b/lib/Epub/Epub/blocks/ImageBlock.h index 40ed63f..8331dbc 100644 --- a/lib/Epub/Epub/blocks/ImageBlock.h +++ b/lib/Epub/Epub/blocks/ImageBlock.h @@ -1,26 +1,26 @@ #pragma once +#include + #include #include -#include - #include "Block.h" class ImageBlock final : public Block { - public: + public: ImageBlock(const std::string& imagePath, int16_t width, int16_t height); ~ImageBlock() override = default; const std::string& getImagePath() const { return imagePath; } int16_t getWidth() const { return width; } int16_t getHeight() const { return height; } - + bool imageExists() const; - + void layout(GfxRenderer& renderer) override; BlockType getType() override { return IMAGE_BLOCK; } bool isEmpty() override { return false; } - + void render(GfxRenderer& renderer, const int x, const int y); bool serialize(FsFile& file); static std::unique_ptr deserialize(FsFile& file); diff --git a/lib/Epub/Epub/converters/FramebufferWriter.cpp b/lib/Epub/Epub/converters/FramebufferWriter.cpp index 2aac86d..8db13e5 100644 --- a/lib/Epub/Epub/converters/FramebufferWriter.cpp +++ b/lib/Epub/Epub/converters/FramebufferWriter.cpp @@ -4,10 +4,10 @@ void FramebufferWriter::setPixel(int x, int y, bool isBlack) { if (x < 0 || x >= DISPLAY_WIDTH || y < 0 || y >= DISPLAY_HEIGHT) { return; } - + const uint16_t byteIndex = y * DISPLAY_WIDTH_BYTES + (x / 8); const uint8_t bitPosition = 7 - (x % 8); - + if (isBlack) { frameBuffer[byteIndex] &= ~(1 << bitPosition); } else { @@ -19,10 +19,10 @@ void FramebufferWriter::setPixel2Bit(int x, int y, uint8_t value) { if (x < 0 || x >= DISPLAY_WIDTH || y < 0 || y >= DISPLAY_HEIGHT || value > 3) { return; } - + const uint16_t byteIndex = y * DISPLAY_WIDTH_BYTES + (x / 8); const uint8_t bitPosition = 7 - (x % 8); - + if (value < 2) { frameBuffer[byteIndex] &= ~(1 << bitPosition); } else { diff --git a/lib/Epub/Epub/converters/FramebufferWriter.h b/lib/Epub/Epub/converters/FramebufferWriter.h index 62a7162..5fa592a 100644 --- a/lib/Epub/Epub/converters/FramebufferWriter.h +++ b/lib/Epub/Epub/converters/FramebufferWriter.h @@ -2,18 +2,18 @@ #include class FramebufferWriter { -private: + private: uint8_t* frameBuffer; static constexpr int DISPLAY_WIDTH = 800; - static constexpr int DISPLAY_WIDTH_BYTES = DISPLAY_WIDTH / 8; // 100 + static constexpr int DISPLAY_WIDTH_BYTES = DISPLAY_WIDTH / 8; // 100 static constexpr int DISPLAY_HEIGHT = 480; - -public: + + public: explicit FramebufferWriter(uint8_t* framebuffer) : frameBuffer(framebuffer) {} - + // Simple pixel setting for 1-bit rendering void setPixel(int x, int y, bool isBlack); - + // 2-bit grayscale pixel setting (for dual-pass rendering) - void setPixel2Bit(int x, int y, uint8_t value); // value: 0-3 + void setPixel2Bit(int x, int y, uint8_t value); // value: 0-3 }; \ No newline at end of file diff --git a/lib/Epub/Epub/converters/ImageDecoderFactory.cpp b/lib/Epub/Epub/converters/ImageDecoderFactory.cpp index e67a883..8b26b6e 100644 --- a/lib/Epub/Epub/converters/ImageDecoderFactory.cpp +++ b/lib/Epub/Epub/converters/ImageDecoderFactory.cpp @@ -1,21 +1,24 @@ #include "ImageDecoderFactory.h" -#include "JpegToFramebufferConverter.h" -#include "PngToFramebufferConverter.h" + #include + #include #include #include +#include "JpegToFramebufferConverter.h" +#include "PngToFramebufferConverter.h" + std::unique_ptr ImageDecoderFactory::jpegDecoder = nullptr; std::unique_ptr ImageDecoderFactory::pngDecoder = nullptr; bool ImageDecoderFactory::initialized = false; void ImageDecoderFactory::initialize() { if (initialized) return; - + jpegDecoder = std::unique_ptr(new JpegToFramebufferConverter()); pngDecoder = std::unique_ptr(new PngToFramebufferConverter()); - + initialized = true; Serial.printf("[%lu] [DEC] Image decoder factory initialized\n", millis()); } diff --git a/lib/Epub/Epub/converters/ImageDecoderFactory.h b/lib/Epub/Epub/converters/ImageDecoderFactory.h index f6208fd..27c7a14 100644 --- a/lib/Epub/Epub/converters/ImageDecoderFactory.h +++ b/lib/Epub/Epub/converters/ImageDecoderFactory.h @@ -10,14 +10,14 @@ class JpegToFramebufferConverter; class PngToFramebufferConverter; class ImageDecoderFactory { -public: + public: static void initialize(); // Returns non-owning pointer - factory owns the decoder lifetime static ImageToFramebufferDecoder* getDecoder(const std::string& imagePath); static bool isFormatSupported(const std::string& imagePath); static std::vector getSupportedFormats(); -private: + private: static std::unique_ptr jpegDecoder; static std::unique_ptr pngDecoder; static bool initialized; diff --git a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.cpp b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.cpp index e07e486..cbf55f5 100644 --- a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.cpp +++ b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.cpp @@ -1,17 +1,18 @@ #include "ImageToFramebufferDecoder.h" -#include + #include +#include bool ImageToFramebufferDecoder::validateImageDimensions(int width, int height, const std::string& format) { if (width > MAX_SOURCE_WIDTH || height > MAX_SOURCE_HEIGHT) { - Serial.printf("[%lu] [IMG] Image too large (%dx%d %s), max supported: %dx%d\n", - millis(), width, height, format.c_str(), MAX_SOURCE_WIDTH, MAX_SOURCE_HEIGHT); + Serial.printf("[%lu] [IMG] Image too large (%dx%d %s), max supported: %dx%d\n", millis(), width, height, + format.c_str(), MAX_SOURCE_WIDTH, MAX_SOURCE_HEIGHT); return false; } return true; } void ImageToFramebufferDecoder::warnUnsupportedFeature(const std::string& feature, const std::string& imagePath) { - Serial.printf("[%lu] [IMG] Warning: Unsupported feature '%s' in image '%s'. Image may not display correctly.\n", + Serial.printf("[%lu] [IMG] Warning: Unsupported feature '%s' in image '%s'. Image may not display correctly.\n", millis(), feature.c_str(), imagePath.c_str()); } \ No newline at end of file diff --git a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h index c12f7bf..92dc474 100644 --- a/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h +++ b/lib/Epub/Epub/converters/ImageToFramebufferDecoder.h @@ -21,21 +21,17 @@ struct RenderConfig { }; class ImageToFramebufferDecoder { -public: + public: virtual ~ImageToFramebufferDecoder() = default; - virtual bool decodeToFramebuffer( - const std::string& imagePath, - GfxRenderer& renderer, - const RenderConfig& config - ) = 0; + virtual bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) = 0; virtual bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const = 0; virtual bool supportsFormat(const std::string& extension) const = 0; virtual const char* getFormatName() const = 0; -protected: + protected: // Size validation helpers static constexpr int MAX_SOURCE_WIDTH = 2048; static constexpr int MAX_SOURCE_HEIGHT = 1536; diff --git a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp index e0b27a5..b46cb7f 100644 --- a/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp +++ b/lib/Epub/Epub/converters/JpegToFramebufferConverter.cpp @@ -1,9 +1,11 @@ #include "JpegToFramebufferConverter.h" + #include #include -#include #include +#include #include + #include #include @@ -68,8 +70,8 @@ struct PixelCache { cacheFile.write(buffer, bytesPerRow * height); cacheFile.close(); - Serial.printf("[%lu] [JPG] Cache written: %s (%dx%d, %d bytes)\n", millis(), - cachePath.c_str(), width, height, 4 + bytesPerRow * height); + Serial.printf("[%lu] [JPG] Cache written: %s (%dx%d, %d bytes)\n", millis(), cachePath.c_str(), width, height, + 4 + bytesPerRow * height); return true; } @@ -110,9 +112,9 @@ bool JpegToFramebufferConverter::getDimensionsStatic(const std::string& imagePat uint8_t JpegToFramebufferConverter::applyAtkinsonDithering(uint8_t gray, int x, int y, int width) { int16_t error = gray - (gray < 128 ? 0 : 255); uint8_t newGray = gray - error; - + int8_t fraction = error >> 3; - + if (x + 1 < width && y + 1 < 512) ditherErrors[y + 1][(x + 1) % 3] += fraction; if (x + 2 < width && y + 1 < 512) ditherErrors[y + 1][(x + 2) % 3] += fraction; if (x + 1 < width) ditherErrors[y][(x + 1) % 3] += fraction; @@ -120,11 +122,11 @@ uint8_t JpegToFramebufferConverter::applyAtkinsonDithering(uint8_t gray, int x, if (x - 1 >= 0 && x + 1 < width && y + 1 < 512) ditherErrors[y + 1][(x - 1 + 1) % 3] += fraction; if (x - 1 >= 0 && y + 1 < 512) ditherErrors[y + 1][(x - 1) % 3] += fraction; if (x + 1 < width && y + 2 < 512) ditherErrors[y + 2][(x + 1) % 3] += fraction; - + int16_t adjustedGray = newGray + ditherErrors[y][x % 3]; if (adjustedGray < 0) adjustedGray = 0; if (adjustedGray > 255) adjustedGray = 255; - + uint8_t outputGray; if (adjustedGray < 64) { outputGray = 0; @@ -135,17 +137,14 @@ uint8_t JpegToFramebufferConverter::applyAtkinsonDithering(uint8_t gray, int x, } else { outputGray = 3; } - + ditherErrors[y][x % 3] = adjustedGray - (outputGray * 85); - + return outputGray; } -bool JpegToFramebufferConverter::decodeToFramebuffer( - const std::string& imagePath, - GfxRenderer& renderer, - const RenderConfig& config -) { +bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, + const RenderConfig& config) { Serial.printf("[%lu] [JPG] Decoding JPEG: %s\n", millis(), imagePath.c_str()); FsFile file; @@ -172,12 +171,11 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( } // Calculate scale factor to fit within maxWidth/maxHeight - float scaleX = (config.maxWidth > 0 && imageInfo.m_width > config.maxWidth) - ? (float)config.maxWidth / imageInfo.m_width - : 1.0f; + float scaleX = + (config.maxWidth > 0 && imageInfo.m_width > config.maxWidth) ? (float)config.maxWidth / imageInfo.m_width : 1.0f; float scaleY = (config.maxHeight > 0 && imageInfo.m_height > config.maxHeight) - ? (float)config.maxHeight / imageInfo.m_height - : 1.0f; + ? (float)config.maxHeight / imageInfo.m_height + : 1.0f; float scale = (scaleX < scaleY) ? scaleX : scaleY; if (scale > 1.0f) scale = 1.0f; @@ -185,8 +183,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( int destHeight = (int)(imageInfo.m_height * scale); Serial.printf("[%lu] [JPG] JPEG %dx%d -> %dx%d (scale %.2f), scan type: %d, MCU: %dx%d\n", millis(), - imageInfo.m_width, imageInfo.m_height, destWidth, destHeight, scale, - imageInfo.m_scanType, imageInfo.m_MCUWidth, imageInfo.m_MCUHeight); + imageInfo.m_width, imageInfo.m_height, destWidth, destHeight, scale, imageInfo.m_scanType, + imageInfo.m_MCUWidth, imageInfo.m_MCUHeight); if (!imageInfo.m_pMCUBufR || !imageInfo.m_pMCUBufG || !imageInfo.m_pMCUBufB) { Serial.printf("[%lu] [JPG] Null buffer pointers in imageInfo\n", millis()); @@ -236,7 +234,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( int destX = config.x + (int)(srcX * scale); if (destX >= screenWidth || destX >= config.x + destWidth) continue; uint8_t gray = imageInfo.m_pMCUBufR[row * 8 + col]; - uint8_t dithered = config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; + uint8_t dithered = + config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; if (dithered > 3) dithered = 3; renderer.drawPixel(destX, destY, dithered < 2); if (caching) cache.setPixel(destX, destY, dithered); @@ -257,7 +256,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( uint8_t g = imageInfo.m_pMCUBufG[row * 8 + col]; uint8_t b = imageInfo.m_pMCUBufB[row * 8 + col]; uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); - uint8_t dithered = config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; + uint8_t dithered = + config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; if (dithered > 3) dithered = 3; renderer.drawPixel(destX, destY, dithered < 2); if (caching) cache.setPixel(destX, destY, dithered); @@ -280,7 +280,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( uint8_t g = imageInfo.m_pMCUBufG[blockIndex * 64 + pixelIndex]; uint8_t b = imageInfo.m_pMCUBufB[blockIndex * 64 + pixelIndex]; uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); - uint8_t dithered = config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; + uint8_t dithered = + config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; if (dithered > 3) dithered = 3; renderer.drawPixel(destX, destY, dithered < 2); if (caching) cache.setPixel(destX, destY, dithered); @@ -303,7 +304,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( uint8_t g = imageInfo.m_pMCUBufG[blockIndex * 128 + pixelIndex]; uint8_t b = imageInfo.m_pMCUBufB[blockIndex * 128 + pixelIndex]; uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); - uint8_t dithered = config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; + uint8_t dithered = + config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; if (dithered > 3) dithered = 3; renderer.drawPixel(destX, destY, dithered < 2); if (caching) cache.setPixel(destX, destY, dithered); @@ -329,7 +331,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( uint8_t g = imageInfo.m_pMCUBufG[blockOffset + pixelIndex]; uint8_t b = imageInfo.m_pMCUBufB[blockOffset + pixelIndex]; uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); - uint8_t dithered = config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; + uint8_t dithered = + config.useDithering ? applyAtkinsonDithering(gray, destX, destY, screenWidth) : gray / 85; if (dithered > 3) dithered = 3; renderer.drawPixel(destX, destY, dithered < 2); if (caching) cache.setPixel(destX, destY, dithered); @@ -356,14 +359,10 @@ bool JpegToFramebufferConverter::decodeToFramebuffer( return true; } -unsigned char JpegToFramebufferConverter::jpegReadCallback( - unsigned char* pBuf, - unsigned char buf_size, - unsigned char* pBytes_actually_read, - void* pCallback_data -) { +unsigned char JpegToFramebufferConverter::jpegReadCallback(unsigned char* pBuf, unsigned char buf_size, + unsigned char* pBytes_actually_read, void* pCallback_data) { JpegContext* context = reinterpret_cast(pCallback_data); - + if (context->bufferPos >= context->bufferFilled) { int readCount = context->file.read(context->buffer, sizeof(context->buffer)); if (readCount <= 0) { @@ -373,14 +372,14 @@ unsigned char JpegToFramebufferConverter::jpegReadCallback( context->bufferFilled = readCount; context->bufferPos = 0; } - + unsigned int bytesAvailable = context->bufferFilled - context->bufferPos; unsigned int bytesToCopy = (bytesAvailable < buf_size) ? bytesAvailable : buf_size; - + memcpy(pBuf, &context->buffer[context->bufferPos], bytesToCopy); context->bufferPos += bytesToCopy; *pBytes_actually_read = bytesToCopy; - + return 0; } diff --git a/lib/Epub/Epub/converters/JpegToFramebufferConverter.h b/lib/Epub/Epub/converters/JpegToFramebufferConverter.h index 21cd7eb..edf9dce 100644 --- a/lib/Epub/Epub/converters/JpegToFramebufferConverter.h +++ b/lib/Epub/Epub/converters/JpegToFramebufferConverter.h @@ -1,18 +1,15 @@ #pragma once #include + #include #include "ImageToFramebufferDecoder.h" class JpegToFramebufferConverter final : public ImageToFramebufferDecoder { -public: + public: static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out); - bool decodeToFramebuffer( - const std::string& imagePath, - GfxRenderer& renderer, - const RenderConfig& config - ) override; + bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) override; bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override { return getDimensionsStatic(imagePath, dims); @@ -21,12 +18,8 @@ public: bool supportsFormat(const std::string& extension) const override; const char* getFormatName() const override { return "JPEG"; } -private: + private: uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width); - static unsigned char jpegReadCallback( - unsigned char* pBuf, - unsigned char buf_size, - unsigned char* pBytes_actually_read, - void* pCallback_data - ); + static unsigned char jpegReadCallback(unsigned char* pBuf, unsigned char buf_size, + unsigned char* pBytes_actually_read, void* pCallback_data); }; diff --git a/lib/Epub/Epub/converters/PngToFramebufferConverter.cpp b/lib/Epub/Epub/converters/PngToFramebufferConverter.cpp index 1083f2d..5cc17ed 100644 --- a/lib/Epub/Epub/converters/PngToFramebufferConverter.cpp +++ b/lib/Epub/Epub/converters/PngToFramebufferConverter.cpp @@ -1,18 +1,16 @@ #include "PngToFramebufferConverter.h" + #include #include -#include -#include #include +#include +#include static FsFile* gPngFile = nullptr; -static void* pngOpenForDims(const char* filename, int32_t* size) { - return gPngFile; -} +static void* pngOpenForDims(const char* filename, int32_t* size) { return gPngFile; } -static void pngCloseForDims(void* handle) { -} +static void pngCloseForDims(void* handle) {} static int32_t pngReadForDims(PNGFILE* pFile, uint8_t* pBuf, int32_t len) { if (!gPngFile) return 0; @@ -91,9 +89,9 @@ static void cacheSetPixel(int screenX, int screenY, uint8_t value) { static uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width) { int16_t error = gray - (gray < 128 ? 0 : 255); uint8_t newGray = gray - error; - + int8_t fraction = error >> 3; - + if (x + 1 < width) ditherErrors[y + 1][(x + 1) % 3] += fraction; if (x + 2 < width) ditherErrors[y + 1][(x + 2) % 3] += fraction; if (x + 1 < width) ditherErrors[y][(x + 1) % 3] += fraction; @@ -101,11 +99,11 @@ static uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width) { if (x - 1 >= 0 && x + 1 < width) ditherErrors[y + 1][(x - 1 + 1) % 3] += fraction; if (x - 1 >= 0) ditherErrors[y + 1][(x - 1) % 3] += fraction; if (x + 1 < width) ditherErrors[y + 2][(x + 1) % 3] += fraction; - + int16_t adjustedGray = newGray + ditherErrors[y][x % 3]; if (adjustedGray < 0) adjustedGray = 0; if (adjustedGray > 255) adjustedGray = 255; - + uint8_t outputGray; if (adjustedGray < 64) { outputGray = 0; @@ -116,9 +114,9 @@ static uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width) { } else { outputGray = 3; } - + ditherErrors[y][x % 3] = adjustedGray - (outputGray * 85); - + return outputGray; } @@ -157,30 +155,27 @@ static uint8_t getGrayFromPixel(uint8_t* pPixels, int x, int pixelType, uint8_t* case PNG_PIXEL_GRAYSCALE: return pPixels[x]; - case PNG_PIXEL_TRUECOLOR: - { - uint8_t* p = &pPixels[x * 3]; + case PNG_PIXEL_TRUECOLOR: { + uint8_t* p = &pPixels[x * 3]; + return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); + } + + case PNG_PIXEL_INDEXED: { + uint8_t paletteIndex = pPixels[x]; + if (palette) { + uint8_t* p = &palette[paletteIndex * 3]; return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); } - - case PNG_PIXEL_INDEXED: - { - uint8_t paletteIndex = pPixels[x]; - if (palette) { - uint8_t* p = &palette[paletteIndex * 3]; - return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); - } - return paletteIndex; - } + return paletteIndex; + } case PNG_PIXEL_GRAY_ALPHA: return pPixels[x * 2]; - case PNG_PIXEL_TRUECOLOR_ALPHA: - { - uint8_t* p = &pPixels[x * 4]; - return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); - } + case PNG_PIXEL_TRUECOLOR_ALPHA: { + uint8_t* p = &pPixels[x * 4]; + return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); + } default: return 128; @@ -232,11 +227,8 @@ int pngDrawCallback(PNGDRAW* pDraw) { return 1; } -bool PngToFramebufferConverter::decodeToFramebuffer( - const std::string& imagePath, - GfxRenderer& renderer, - const RenderConfig& config -) { +bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, + const RenderConfig& config) { Serial.printf("[%lu] [PNG] Decoding PNG: %s\n", millis(), imagePath.c_str()); FsFile file; @@ -280,8 +272,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer( gDstHeight = (int)(gSrcHeight * gScale); gLastDstY = -1; // Reset row tracking - Serial.printf("[%lu] [PNG] PNG %dx%d -> %dx%d (scale %.2f), bpp: %d\n", millis(), - gSrcWidth, gSrcHeight, gDstWidth, gDstHeight, gScale, png.getBpp()); + Serial.printf("[%lu] [PNG] PNG %dx%d -> %dx%d (scale %.2f), bpp: %d\n", millis(), gSrcWidth, gSrcHeight, gDstWidth, + gDstHeight, gScale, png.getBpp()); if (png.getBpp() != 8) { warnUnsupportedFeature("bit depth (" + std::to_string(png.getBpp()) + "bpp)", imagePath); @@ -303,7 +295,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer( gCacheBuffer = (uint8_t*)malloc(bufferSize); if (gCacheBuffer) { memset(gCacheBuffer, 0, bufferSize); - Serial.printf("[%lu] [PNG] Allocated cache buffer: %d bytes for %dx%d\n", millis(), bufferSize, gCacheWidth, gCacheHeight); + Serial.printf("[%lu] [PNG] Allocated cache buffer: %d bytes for %dx%d\n", millis(), bufferSize, gCacheWidth, + gCacheHeight); } else { Serial.printf("[%lu] [PNG] Failed to allocate cache buffer, continuing without caching\n", millis()); caching = false; @@ -338,8 +331,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer( cacheFile.write(&h, 2); cacheFile.write(gCacheBuffer, gCacheBytesPerRow * gCacheHeight); cacheFile.close(); - Serial.printf("[%lu] [PNG] Cache written: %s (%dx%d, %d bytes)\n", millis(), - config.cachePath.c_str(), gCacheWidth, gCacheHeight, 4 + gCacheBytesPerRow * gCacheHeight); + Serial.printf("[%lu] [PNG] Cache written: %s (%dx%d, %d bytes)\n", millis(), config.cachePath.c_str(), + gCacheWidth, gCacheHeight, 4 + gCacheBytesPerRow * gCacheHeight); } else { Serial.printf("[%lu] [PNG] Failed to open cache file for writing: %s\n", millis(), config.cachePath.c_str()); } diff --git a/lib/Epub/Epub/converters/PngToFramebufferConverter.h b/lib/Epub/Epub/converters/PngToFramebufferConverter.h index a360583..1083d86 100644 --- a/lib/Epub/Epub/converters/PngToFramebufferConverter.h +++ b/lib/Epub/Epub/converters/PngToFramebufferConverter.h @@ -1,17 +1,14 @@ #pragma once -#include "ImageToFramebufferDecoder.h" #include +#include "ImageToFramebufferDecoder.h" + class PngToFramebufferConverter final : public ImageToFramebufferDecoder { -public: + public: static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out); - bool decodeToFramebuffer( - const std::string& imagePath, - GfxRenderer& renderer, - const RenderConfig& config - ) override; + bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) override; bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override { return getDimensionsStatic(imagePath, dims); diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp index 8363bd0..9138d04 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.cpp @@ -5,10 +5,10 @@ #include #include -#include "../Page.h" -#include "../converters/ImageToFramebufferDecoder.h" -#include "../converters/ImageDecoderFactory.h" #include "../../Epub.h" +#include "../Page.h" +#include "../converters/ImageDecoderFactory.h" +#include "../converters/ImageToFramebufferDecoder.h" const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"}; constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]); @@ -91,22 +91,22 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* alt = atts[i + 1]; } } - + if (!src.empty()) { Serial.printf("[%lu] [EHP] Found image: src=%s\n", millis(), src.c_str()); - + // Get the spine item's href to resolve the relative path size_t lastUnderscore = self->filepath.rfind('_'); if (lastUnderscore != std::string::npos && lastUnderscore > 0) { std::string indexStr = self->filepath.substr(lastUnderscore + 1); indexStr.resize(indexStr.find('.')); int spineIndex = atoi(indexStr.c_str()); - + const auto& spineItem = self->epub->getSpineItem(spineIndex); std::string htmlHref = spineItem.href; size_t lastSlash = htmlHref.find_last_of('/'); std::string htmlDir = (lastSlash != std::string::npos) ? htmlHref.substr(0, lastSlash + 1) : ""; - + // Resolve the image path relative to the HTML file std::string imageHref = src; while (imageHref.find("../") == 0) { @@ -117,15 +117,16 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } std::string resolvedPath = htmlDir + imageHref; - + // Create a unique filename for the cached image std::string ext; size_t extPos = resolvedPath.rfind('.'); if (extPos != std::string::npos) { ext = resolvedPath.substr(extPos); } - std::string cachedImagePath = self->epub->getCachePath() + "/img_" + std::to_string(spineIndex) + "_" + std::to_string(self->imageCounter++) + ext; - + std::string cachedImagePath = self->epub->getCachePath() + "/img_" + std::to_string(spineIndex) + "_" + + std::to_string(self->imageCounter++) + ext; + // Extract image to cache file FsFile cachedImageFile; bool extractSuccess = false; @@ -135,7 +136,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* cachedImageFile.close(); delay(50); // Give SD card time to sync } - + if (extractSuccess) { // Get image dimensions ImageDimensions dims = {0, 0}; @@ -154,8 +155,9 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* int displayWidth = (int)(dims.width * scale); int displayHeight = (int)(dims.height * scale); - Serial.printf("[%lu] [EHP] Display size: %dx%d (scale %.2f)\n", millis(), displayWidth, displayHeight, scale); - + Serial.printf("[%lu] [EHP] Display size: %dx%d (scale %.2f)\n", millis(), displayWidth, displayHeight, + scale); + // Create page for image if (self->currentPage && !self->currentPage->elements.empty()) { self->completePageFn(std::move(self->currentPage)); @@ -173,7 +175,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } self->currentPageNextY = 0; } - + // Create ImageBlock and add to page auto imageBlock = std::make_shared(cachedImagePath, displayWidth, displayHeight); if (!imageBlock) { @@ -188,7 +190,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } self->currentPage->elements.push_back(pageImage); self->currentPageNextY += displayHeight; - + self->depth += 1; return; } else { @@ -200,7 +202,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* } } } - + // Fallback to alt text if image processing fails if (!alt.empty()) { alt = "[Image: " + alt + "]"; @@ -210,7 +212,7 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* self->characterData(userData, alt.c_str(), alt.length()); return; } - + // No alt text, skip self->skipUntilDepth = self->depth; self->depth += 1; diff --git a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h index f199a61..aeaaed3 100644 --- a/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h +++ b/lib/Epub/Epub/parsers/ChapterHtmlSlimParser.h @@ -7,8 +7,8 @@ #include #include "../ParsedText.h" -#include "../blocks/TextBlock.h" #include "../blocks/ImageBlock.h" +#include "../blocks/TextBlock.h" class Page; class GfxRenderer; @@ -50,8 +50,8 @@ class ChapterHtmlSlimParser { static void XMLCALL endElement(void* userData, const XML_Char* name); public: - explicit ChapterHtmlSlimParser(std::shared_ptr epub, const std::string& filepath, GfxRenderer& renderer, const int fontId, - const float lineCompression, const bool extraParagraphSpacing, + explicit ChapterHtmlSlimParser(std::shared_ptr epub, const std::string& filepath, GfxRenderer& renderer, + const int fontId, const float lineCompression, const bool extraParagraphSpacing, const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint16_t viewportHeight, const bool hyphenationEnabled, const std::function)>& completePageFn,