Cherry-pick merge from pablohc/crosspoint-reader@2d8cbcf, based on upstream PR #556 by martinbrook with pablohc's refresh optimization. - Add JPEG decoder (picojpeg) and PNG decoder (PNGdec) with 4-level grayscale Bayer dithering for e-ink display - Add pixel caching system (.pxc files) for fast image re-rendering - Integrate image extraction from EPUB HTML parser (<img> tag support) - Add ImageBlock/PageImage types with serialization support - Add image-aware refresh optimization (double FAST_REFRESH technique) - Add experimental displayWindow() partial refresh support - Bump section cache version 12->13 to invalidate stale caches - Resolve TAG_PageImage=3 to avoid conflict with mod's TAG_PageTableRow=2 Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
1.3 KiB
C
41 lines
1.3 KiB
C
#pragma once
|
|
|
|
#include <GfxRenderer.h>
|
|
#include <stdint.h>
|
|
|
|
// 4x4 Bayer matrix for ordered dithering
|
|
inline const uint8_t bayer4x4[4][4] = {
|
|
{0, 8, 2, 10},
|
|
{12, 4, 14, 6},
|
|
{3, 11, 1, 9},
|
|
{15, 7, 13, 5},
|
|
};
|
|
|
|
// Apply Bayer dithering and quantize to 4 levels (0-3)
|
|
// Stateless - works correctly with any pixel processing order
|
|
inline uint8_t applyBayerDither4Level(uint8_t gray, int x, int y) {
|
|
int bayer = bayer4x4[y & 3][x & 3];
|
|
int dither = (bayer - 8) * 5; // Scale to +/-40 (half of quantization step 85)
|
|
|
|
int adjusted = gray + dither;
|
|
if (adjusted < 0) adjusted = 0;
|
|
if (adjusted > 255) adjusted = 255;
|
|
|
|
if (adjusted < 64) return 0;
|
|
if (adjusted < 128) return 1;
|
|
if (adjusted < 192) return 2;
|
|
return 3;
|
|
}
|
|
|
|
// Draw a pixel respecting the current render mode for grayscale support
|
|
inline void drawPixelWithRenderMode(GfxRenderer& renderer, int x, int y, uint8_t pixelValue) {
|
|
GfxRenderer::RenderMode renderMode = renderer.getRenderMode();
|
|
if (renderMode == GfxRenderer::BW && pixelValue < 3) {
|
|
renderer.drawPixel(x, y, true);
|
|
} else if (renderMode == GfxRenderer::GRAYSCALE_MSB && (pixelValue == 1 || pixelValue == 2)) {
|
|
renderer.drawPixel(x, y, false);
|
|
} else if (renderMode == GfxRenderer::GRAYSCALE_LSB && pixelValue == 1) {
|
|
renderer.drawPixel(x, y, false);
|
|
}
|
|
}
|