style: apply clang-format-21 formatting

This commit is contained in:
Martin Brook 2026-01-26 22:16:30 +00:00
parent 99702a342c
commit 41eabba0d4
16 changed files with 150 additions and 164 deletions

View File

@ -4,8 +4,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "blocks/TextBlock.h"
#include "blocks/ImageBlock.h" #include "blocks/ImageBlock.h"
#include "blocks/TextBlock.h"
enum PageElementTag : uint8_t { enum PageElementTag : uint8_t {
TAG_PageLine = 1, TAG_PageLine = 1,
@ -41,7 +41,7 @@ class PageLine final : public PageElement {
class PageImage final : public PageElement { class PageImage final : public PageElement {
std::shared_ptr<ImageBlock> imageBlock; std::shared_ptr<ImageBlock> imageBlock;
public: public:
PageImage(std::shared_ptr<ImageBlock> block, const int16_t xPos, const int16_t yPos) PageImage(std::shared_ptr<ImageBlock> block, const int16_t xPos, const int16_t yPos)
: PageElement(xPos, yPos), imageBlock(std::move(block)) {} : PageElement(xPos, yPos), imageBlock(std::move(block)) {}
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override; void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;

View File

@ -1,10 +1,12 @@
#include "ImageBlock.h" #include "ImageBlock.h"
#include "../converters/ImageDecoderFactory.h"
#include <FsHelpers.h>
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <Serialization.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include <FsHelpers.h> #include <Serialization.h>
#include "../converters/ImageDecoderFactory.h"
// Cache file format: // Cache file format:
// - uint16_t width // - uint16_t width
@ -19,8 +21,7 @@ bool ImageBlock::imageExists() const {
return SdMan.openFileForRead("IMG", imagePath, file); return SdMan.openFileForRead("IMG", imagePath, file);
} }
void ImageBlock::layout(GfxRenderer& renderer) { void ImageBlock::layout(GfxRenderer& renderer) {}
}
static std::string getCachePath(const std::string& imagePath) { static std::string getCachePath(const std::string& imagePath) {
// Replace extension with .pxc (pixel cache) // Replace extension with .pxc (pixel cache)
@ -31,7 +32,8 @@ static std::string getCachePath(const std::string& imagePath) {
return imagePath + ".pxc"; 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; FsFile cacheFile;
if (!SdMan.openFileForRead("IMG", cachePath, cacheFile)) { if (!SdMan.openFileForRead("IMG", cachePath, cacheFile)) {
return false; return false;
@ -47,8 +49,8 @@ static bool renderFromCache(GfxRenderer& renderer, const std::string& cachePath,
int widthDiff = abs(cachedWidth - expectedWidth); int widthDiff = abs(cachedWidth - expectedWidth);
int heightDiff = abs(cachedHeight - expectedHeight); int heightDiff = abs(cachedHeight - expectedHeight);
if (widthDiff > 1 || heightDiff > 1) { if (widthDiff > 1 || heightDiff > 1) {
Serial.printf("[%lu] [IMG] Cache dimension mismatch: %dx%d vs %dx%d\n", millis(), Serial.printf("[%lu] [IMG] Cache dimension mismatch: %dx%d vs %dx%d\n", millis(), cachedWidth, cachedHeight,
cachedWidth, cachedHeight, expectedWidth, expectedHeight); expectedWidth, expectedHeight);
cacheFile.close(); cacheFile.close();
return false; 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 // Bounds check render position using logical screen dimensions
if (x < 0 || y < 0 || x + width > screenWidth || y + height > screenHeight) { 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", Serial.printf("[%lu] [IMG] Invalid render position: (%d,%d) size (%dx%d) screen (%dx%d)\n", millis(), x, y, width,
millis(), x, y, width, height, screenWidth, screenHeight); height, screenWidth, screenHeight);
return; return;
} }

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <SdFat.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <SdFat.h>
#include "Block.h" #include "Block.h"
class ImageBlock final : public Block { class ImageBlock final : public Block {

View File

@ -2,13 +2,13 @@
#include <stdint.h> #include <stdint.h>
class FramebufferWriter { class FramebufferWriter {
private: private:
uint8_t* frameBuffer; uint8_t* frameBuffer;
static constexpr int DISPLAY_WIDTH = 800; 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; static constexpr int DISPLAY_HEIGHT = 480;
public: public:
explicit FramebufferWriter(uint8_t* framebuffer) : frameBuffer(framebuffer) {} explicit FramebufferWriter(uint8_t* framebuffer) : frameBuffer(framebuffer) {}
// Simple pixel setting for 1-bit rendering // Simple pixel setting for 1-bit rendering

View File

@ -1,11 +1,14 @@
#include "ImageDecoderFactory.h" #include "ImageDecoderFactory.h"
#include "JpegToFramebufferConverter.h"
#include "PngToFramebufferConverter.h"
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "JpegToFramebufferConverter.h"
#include "PngToFramebufferConverter.h"
std::unique_ptr<JpegToFramebufferConverter> ImageDecoderFactory::jpegDecoder = nullptr; std::unique_ptr<JpegToFramebufferConverter> ImageDecoderFactory::jpegDecoder = nullptr;
std::unique_ptr<PngToFramebufferConverter> ImageDecoderFactory::pngDecoder = nullptr; std::unique_ptr<PngToFramebufferConverter> ImageDecoderFactory::pngDecoder = nullptr;
bool ImageDecoderFactory::initialized = false; bool ImageDecoderFactory::initialized = false;

View File

@ -10,14 +10,14 @@ class JpegToFramebufferConverter;
class PngToFramebufferConverter; class PngToFramebufferConverter;
class ImageDecoderFactory { class ImageDecoderFactory {
public: public:
static void initialize(); static void initialize();
// Returns non-owning pointer - factory owns the decoder lifetime // Returns non-owning pointer - factory owns the decoder lifetime
static ImageToFramebufferDecoder* getDecoder(const std::string& imagePath); static ImageToFramebufferDecoder* getDecoder(const std::string& imagePath);
static bool isFormatSupported(const std::string& imagePath); static bool isFormatSupported(const std::string& imagePath);
static std::vector<std::string> getSupportedFormats(); static std::vector<std::string> getSupportedFormats();
private: private:
static std::unique_ptr<JpegToFramebufferConverter> jpegDecoder; static std::unique_ptr<JpegToFramebufferConverter> jpegDecoder;
static std::unique_ptr<PngToFramebufferConverter> pngDecoder; static std::unique_ptr<PngToFramebufferConverter> pngDecoder;
static bool initialized; static bool initialized;

View File

@ -1,11 +1,12 @@
#include "ImageToFramebufferDecoder.h" #include "ImageToFramebufferDecoder.h"
#include <HardwareSerial.h>
#include <Arduino.h> #include <Arduino.h>
#include <HardwareSerial.h>
bool ImageToFramebufferDecoder::validateImageDimensions(int width, int height, const std::string& format) { bool ImageToFramebufferDecoder::validateImageDimensions(int width, int height, const std::string& format) {
if (width > MAX_SOURCE_WIDTH || height > MAX_SOURCE_HEIGHT) { if (width > MAX_SOURCE_WIDTH || height > MAX_SOURCE_HEIGHT) {
Serial.printf("[%lu] [IMG] Image too large (%dx%d %s), max supported: %dx%d\n", Serial.printf("[%lu] [IMG] Image too large (%dx%d %s), max supported: %dx%d\n", millis(), width, height,
millis(), width, height, format.c_str(), MAX_SOURCE_WIDTH, MAX_SOURCE_HEIGHT); format.c_str(), MAX_SOURCE_WIDTH, MAX_SOURCE_HEIGHT);
return false; return false;
} }
return true; return true;

View File

@ -21,21 +21,17 @@ struct RenderConfig {
}; };
class ImageToFramebufferDecoder { class ImageToFramebufferDecoder {
public: public:
virtual ~ImageToFramebufferDecoder() = default; virtual ~ImageToFramebufferDecoder() = default;
virtual bool decodeToFramebuffer( virtual bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) = 0;
const std::string& imagePath,
GfxRenderer& renderer,
const RenderConfig& config
) = 0;
virtual bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const = 0; virtual bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const = 0;
virtual bool supportsFormat(const std::string& extension) const = 0; virtual bool supportsFormat(const std::string& extension) const = 0;
virtual const char* getFormatName() const = 0; virtual const char* getFormatName() const = 0;
protected: protected:
// Size validation helpers // Size validation helpers
static constexpr int MAX_SOURCE_WIDTH = 2048; static constexpr int MAX_SOURCE_WIDTH = 2048;
static constexpr int MAX_SOURCE_HEIGHT = 1536; static constexpr int MAX_SOURCE_HEIGHT = 1536;

View File

@ -1,9 +1,11 @@
#include "JpegToFramebufferConverter.h" #include "JpegToFramebufferConverter.h"
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <SdFat.h>
#include <SDCardManager.h> #include <SDCardManager.h>
#include <SdFat.h>
#include <picojpeg.h> #include <picojpeg.h>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
@ -68,8 +70,8 @@ struct PixelCache {
cacheFile.write(buffer, bytesPerRow * height); cacheFile.write(buffer, bytesPerRow * height);
cacheFile.close(); cacheFile.close();
Serial.printf("[%lu] [JPG] Cache written: %s (%dx%d, %d bytes)\n", millis(), Serial.printf("[%lu] [JPG] Cache written: %s (%dx%d, %d bytes)\n", millis(), cachePath.c_str(), width, height,
cachePath.c_str(), width, height, 4 + bytesPerRow * height); 4 + bytesPerRow * height);
return true; return true;
} }
@ -141,11 +143,8 @@ uint8_t JpegToFramebufferConverter::applyAtkinsonDithering(uint8_t gray, int x,
return outputGray; return outputGray;
} }
bool JpegToFramebufferConverter::decodeToFramebuffer( bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer,
const std::string& imagePath, const RenderConfig& config) {
GfxRenderer& renderer,
const RenderConfig& config
) {
Serial.printf("[%lu] [JPG] Decoding JPEG: %s\n", millis(), imagePath.c_str()); Serial.printf("[%lu] [JPG] Decoding JPEG: %s\n", millis(), imagePath.c_str());
FsFile file; FsFile file;
@ -172,9 +171,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(
} }
// Calculate scale factor to fit within maxWidth/maxHeight // Calculate scale factor to fit within maxWidth/maxHeight
float scaleX = (config.maxWidth > 0 && imageInfo.m_width > config.maxWidth) float scaleX =
? (float)config.maxWidth / imageInfo.m_width (config.maxWidth > 0 && imageInfo.m_width > config.maxWidth) ? (float)config.maxWidth / imageInfo.m_width : 1.0f;
: 1.0f;
float scaleY = (config.maxHeight > 0 && imageInfo.m_height > config.maxHeight) float scaleY = (config.maxHeight > 0 && imageInfo.m_height > config.maxHeight)
? (float)config.maxHeight / imageInfo.m_height ? (float)config.maxHeight / imageInfo.m_height
: 1.0f; : 1.0f;
@ -185,8 +183,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(
int destHeight = (int)(imageInfo.m_height * scale); 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(), 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_width, imageInfo.m_height, destWidth, destHeight, scale, imageInfo.m_scanType,
imageInfo.m_scanType, imageInfo.m_MCUWidth, imageInfo.m_MCUHeight); imageInfo.m_MCUWidth, imageInfo.m_MCUHeight);
if (!imageInfo.m_pMCUBufR || !imageInfo.m_pMCUBufG || !imageInfo.m_pMCUBufB) { if (!imageInfo.m_pMCUBufR || !imageInfo.m_pMCUBufG || !imageInfo.m_pMCUBufB) {
Serial.printf("[%lu] [JPG] Null buffer pointers in imageInfo\n", millis()); 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); int destX = config.x + (int)(srcX * scale);
if (destX >= screenWidth || destX >= config.x + destWidth) continue; if (destX >= screenWidth || destX >= config.x + destWidth) continue;
uint8_t gray = imageInfo.m_pMCUBufR[row * 8 + col]; 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; if (dithered > 3) dithered = 3;
renderer.drawPixel(destX, destY, dithered < 2); renderer.drawPixel(destX, destY, dithered < 2);
if (caching) cache.setPixel(destX, destY, dithered); 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 g = imageInfo.m_pMCUBufG[row * 8 + col];
uint8_t b = imageInfo.m_pMCUBufB[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 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; if (dithered > 3) dithered = 3;
renderer.drawPixel(destX, destY, dithered < 2); renderer.drawPixel(destX, destY, dithered < 2);
if (caching) cache.setPixel(destX, destY, dithered); 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 g = imageInfo.m_pMCUBufG[blockIndex * 64 + pixelIndex];
uint8_t b = imageInfo.m_pMCUBufB[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 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; if (dithered > 3) dithered = 3;
renderer.drawPixel(destX, destY, dithered < 2); renderer.drawPixel(destX, destY, dithered < 2);
if (caching) cache.setPixel(destX, destY, dithered); 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 g = imageInfo.m_pMCUBufG[blockIndex * 128 + pixelIndex];
uint8_t b = imageInfo.m_pMCUBufB[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 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; if (dithered > 3) dithered = 3;
renderer.drawPixel(destX, destY, dithered < 2); renderer.drawPixel(destX, destY, dithered < 2);
if (caching) cache.setPixel(destX, destY, dithered); if (caching) cache.setPixel(destX, destY, dithered);
@ -329,7 +331,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(
uint8_t g = imageInfo.m_pMCUBufG[blockOffset + pixelIndex]; uint8_t g = imageInfo.m_pMCUBufG[blockOffset + pixelIndex];
uint8_t b = imageInfo.m_pMCUBufB[blockOffset + pixelIndex]; uint8_t b = imageInfo.m_pMCUBufB[blockOffset + pixelIndex];
uint8_t gray = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8); 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; if (dithered > 3) dithered = 3;
renderer.drawPixel(destX, destY, dithered < 2); renderer.drawPixel(destX, destY, dithered < 2);
if (caching) cache.setPixel(destX, destY, dithered); if (caching) cache.setPixel(destX, destY, dithered);
@ -356,12 +359,8 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(
return true; return true;
} }
unsigned char JpegToFramebufferConverter::jpegReadCallback( unsigned char JpegToFramebufferConverter::jpegReadCallback(unsigned char* pBuf, unsigned char buf_size,
unsigned char* pBuf, unsigned char* pBytes_actually_read, void* pCallback_data) {
unsigned char buf_size,
unsigned char* pBytes_actually_read,
void* pCallback_data
) {
JpegContext* context = reinterpret_cast<JpegContext*>(pCallback_data); JpegContext* context = reinterpret_cast<JpegContext*>(pCallback_data);
if (context->bufferPos >= context->bufferFilled) { if (context->bufferPos >= context->bufferFilled) {

View File

@ -1,18 +1,15 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include "ImageToFramebufferDecoder.h" #include "ImageToFramebufferDecoder.h"
class JpegToFramebufferConverter final : public ImageToFramebufferDecoder { class JpegToFramebufferConverter final : public ImageToFramebufferDecoder {
public: public:
static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out); static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out);
bool decodeToFramebuffer( bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) override;
const std::string& imagePath,
GfxRenderer& renderer,
const RenderConfig& config
) override;
bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override { bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override {
return getDimensionsStatic(imagePath, dims); return getDimensionsStatic(imagePath, dims);
@ -21,12 +18,8 @@ public:
bool supportsFormat(const std::string& extension) const override; bool supportsFormat(const std::string& extension) const override;
const char* getFormatName() const override { return "JPEG"; } const char* getFormatName() const override { return "JPEG"; }
private: private:
uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width); uint8_t applyAtkinsonDithering(uint8_t gray, int x, int y, int width);
static unsigned char jpegReadCallback( static unsigned char jpegReadCallback(unsigned char* pBuf, unsigned char buf_size,
unsigned char* pBuf, unsigned char* pBytes_actually_read, void* pCallback_data);
unsigned char buf_size,
unsigned char* pBytes_actually_read,
void* pCallback_data
);
}; };

View File

@ -1,18 +1,16 @@
#include "PngToFramebufferConverter.h" #include "PngToFramebufferConverter.h"
#include <GfxRenderer.h> #include <GfxRenderer.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <SdFat.h>
#include <SDCardManager.h>
#include <PNGdec.h> #include <PNGdec.h>
#include <SDCardManager.h>
#include <SdFat.h>
static FsFile* gPngFile = nullptr; static FsFile* gPngFile = nullptr;
static void* pngOpenForDims(const char* filename, int32_t* size) { static void* pngOpenForDims(const char* filename, int32_t* size) { return gPngFile; }
return gPngFile;
}
static void pngCloseForDims(void* handle) { static void pngCloseForDims(void* handle) {}
}
static int32_t pngReadForDims(PNGFILE* pFile, uint8_t* pBuf, int32_t len) { static int32_t pngReadForDims(PNGFILE* pFile, uint8_t* pBuf, int32_t len) {
if (!gPngFile) return 0; if (!gPngFile) return 0;
@ -157,14 +155,12 @@ static uint8_t getGrayFromPixel(uint8_t* pPixels, int x, int pixelType, uint8_t*
case PNG_PIXEL_GRAYSCALE: case PNG_PIXEL_GRAYSCALE:
return pPixels[x]; return pPixels[x];
case PNG_PIXEL_TRUECOLOR: case PNG_PIXEL_TRUECOLOR: {
{
uint8_t* p = &pPixels[x * 3]; uint8_t* p = &pPixels[x * 3];
return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8);
} }
case PNG_PIXEL_INDEXED: case PNG_PIXEL_INDEXED: {
{
uint8_t paletteIndex = pPixels[x]; uint8_t paletteIndex = pPixels[x];
if (palette) { if (palette) {
uint8_t* p = &palette[paletteIndex * 3]; uint8_t* p = &palette[paletteIndex * 3];
@ -176,8 +172,7 @@ static uint8_t getGrayFromPixel(uint8_t* pPixels, int x, int pixelType, uint8_t*
case PNG_PIXEL_GRAY_ALPHA: case PNG_PIXEL_GRAY_ALPHA:
return pPixels[x * 2]; return pPixels[x * 2];
case PNG_PIXEL_TRUECOLOR_ALPHA: case PNG_PIXEL_TRUECOLOR_ALPHA: {
{
uint8_t* p = &pPixels[x * 4]; uint8_t* p = &pPixels[x * 4];
return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8); return (uint8_t)((p[0] * 77 + p[1] * 150 + p[2] * 29) >> 8);
} }
@ -232,11 +227,8 @@ int pngDrawCallback(PNGDRAW* pDraw) {
return 1; return 1;
} }
bool PngToFramebufferConverter::decodeToFramebuffer( bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer,
const std::string& imagePath, const RenderConfig& config) {
GfxRenderer& renderer,
const RenderConfig& config
) {
Serial.printf("[%lu] [PNG] Decoding PNG: %s\n", millis(), imagePath.c_str()); Serial.printf("[%lu] [PNG] Decoding PNG: %s\n", millis(), imagePath.c_str());
FsFile file; FsFile file;
@ -280,8 +272,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer(
gDstHeight = (int)(gSrcHeight * gScale); gDstHeight = (int)(gSrcHeight * gScale);
gLastDstY = -1; // Reset row tracking gLastDstY = -1; // Reset row tracking
Serial.printf("[%lu] [PNG] PNG %dx%d -> %dx%d (scale %.2f), bpp: %d\n", millis(), Serial.printf("[%lu] [PNG] PNG %dx%d -> %dx%d (scale %.2f), bpp: %d\n", millis(), gSrcWidth, gSrcHeight, gDstWidth,
gSrcWidth, gSrcHeight, gDstWidth, gDstHeight, gScale, png.getBpp()); gDstHeight, gScale, png.getBpp());
if (png.getBpp() != 8) { if (png.getBpp() != 8) {
warnUnsupportedFeature("bit depth (" + std::to_string(png.getBpp()) + "bpp)", imagePath); warnUnsupportedFeature("bit depth (" + std::to_string(png.getBpp()) + "bpp)", imagePath);
@ -303,7 +295,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer(
gCacheBuffer = (uint8_t*)malloc(bufferSize); gCacheBuffer = (uint8_t*)malloc(bufferSize);
if (gCacheBuffer) { if (gCacheBuffer) {
memset(gCacheBuffer, 0, bufferSize); 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 { } else {
Serial.printf("[%lu] [PNG] Failed to allocate cache buffer, continuing without caching\n", millis()); Serial.printf("[%lu] [PNG] Failed to allocate cache buffer, continuing without caching\n", millis());
caching = false; caching = false;
@ -338,8 +331,8 @@ bool PngToFramebufferConverter::decodeToFramebuffer(
cacheFile.write(&h, 2); cacheFile.write(&h, 2);
cacheFile.write(gCacheBuffer, gCacheBytesPerRow * gCacheHeight); cacheFile.write(gCacheBuffer, gCacheBytesPerRow * gCacheHeight);
cacheFile.close(); cacheFile.close();
Serial.printf("[%lu] [PNG] Cache written: %s (%dx%d, %d bytes)\n", millis(), Serial.printf("[%lu] [PNG] Cache written: %s (%dx%d, %d bytes)\n", millis(), config.cachePath.c_str(),
config.cachePath.c_str(), gCacheWidth, gCacheHeight, 4 + gCacheBytesPerRow * gCacheHeight); gCacheWidth, gCacheHeight, 4 + gCacheBytesPerRow * gCacheHeight);
} else { } else {
Serial.printf("[%lu] [PNG] Failed to open cache file for writing: %s\n", millis(), config.cachePath.c_str()); Serial.printf("[%lu] [PNG] Failed to open cache file for writing: %s\n", millis(), config.cachePath.c_str());
} }

View File

@ -1,17 +1,14 @@
#pragma once #pragma once
#include "ImageToFramebufferDecoder.h"
#include <PNGdec.h> #include <PNGdec.h>
#include "ImageToFramebufferDecoder.h"
class PngToFramebufferConverter final : public ImageToFramebufferDecoder { class PngToFramebufferConverter final : public ImageToFramebufferDecoder {
public: public:
static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out); static bool getDimensionsStatic(const std::string& imagePath, ImageDimensions& out);
bool decodeToFramebuffer( bool decodeToFramebuffer(const std::string& imagePath, GfxRenderer& renderer, const RenderConfig& config) override;
const std::string& imagePath,
GfxRenderer& renderer,
const RenderConfig& config
) override;
bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override { bool getDimensions(const std::string& imagePath, ImageDimensions& dims) const override {
return getDimensionsStatic(imagePath, dims); return getDimensionsStatic(imagePath, dims);

View File

@ -5,10 +5,10 @@
#include <SDCardManager.h> #include <SDCardManager.h>
#include <expat.h> #include <expat.h>
#include "../Page.h"
#include "../converters/ImageToFramebufferDecoder.h"
#include "../converters/ImageDecoderFactory.h"
#include "../../Epub.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"}; const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"};
constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]); constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
@ -124,7 +124,8 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
if (extPos != std::string::npos) { if (extPos != std::string::npos) {
ext = resolvedPath.substr(extPos); 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 // Extract image to cache file
FsFile cachedImageFile; FsFile cachedImageFile;
@ -154,7 +155,8 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
int displayWidth = (int)(dims.width * scale); int displayWidth = (int)(dims.width * scale);
int displayHeight = (int)(dims.height * 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 // Create page for image
if (self->currentPage && !self->currentPage->elements.empty()) { if (self->currentPage && !self->currentPage->elements.empty()) {

View File

@ -7,8 +7,8 @@
#include <memory> #include <memory>
#include "../ParsedText.h" #include "../ParsedText.h"
#include "../blocks/TextBlock.h"
#include "../blocks/ImageBlock.h" #include "../blocks/ImageBlock.h"
#include "../blocks/TextBlock.h"
class Page; class Page;
class GfxRenderer; class GfxRenderer;
@ -50,8 +50,8 @@ class ChapterHtmlSlimParser {
static void XMLCALL endElement(void* userData, const XML_Char* name); static void XMLCALL endElement(void* userData, const XML_Char* name);
public: public:
explicit ChapterHtmlSlimParser(std::shared_ptr<Epub> epub, const std::string& filepath, GfxRenderer& renderer, const int fontId, explicit ChapterHtmlSlimParser(std::shared_ptr<Epub> epub, const std::string& filepath, GfxRenderer& renderer,
const float lineCompression, const bool extraParagraphSpacing, const int fontId, const float lineCompression, const bool extraParagraphSpacing,
const uint8_t paragraphAlignment, const uint16_t viewportWidth, const uint8_t paragraphAlignment, const uint16_t viewportWidth,
const uint16_t viewportHeight, const bool hyphenationEnabled, const uint16_t viewportHeight, const bool hyphenationEnabled,
const std::function<void(std::unique_ptr<Page>)>& completePageFn, const std::function<void(std::unique_ptr<Page>)>& completePageFn,