2121
2222
2323/* *
24- * @brief ditherGetPixelBmp calculates dither for given pixel in bmp
25- * images
24+ * @brief ditherGetPixelBmp calculates dither for given pixel using the
25+ * currently selected kernel. The dither buffer is a circular
26+ * buffer indexed by j & ditherRowMask so error propagates
27+ * naturally across row and block boundaries without any swap step.
2628 *
2729 * @param uint32_t px
28- * pixel value with color information
30+ * pixel value (8-bit grayscale or palette index)
2931 * @param int i
30- * ditherBuffer width plane position
32+ * absolute column position
33+ * @param int j
34+ * absolute row position
3135 * @param int w
32- * image width
36+ * image width (used for error-diffusion boundary clamping)
3337 * @param bool paletted
34- * 1 if paletted image, 0 if not
38+ * true if px is a palette index that should be looked up first
3539 *
36- * @return new pixel value (dithered pixel )
40+ * @return new dithered pixel value (3-bit grayscale, 0-7 )
3741 */
3842uint8_t Image::ditherGetPixelBmp (uint32_t px, int i, int j, int w, bool paletted)
3943{
4044 if (paletted)
4145 px = ditherPalette[px];
4246
43- if (_inkplate-> getDisplayMode () == INKPLATE_1BIT)
44- px = ( uint16_t )px >> 1 ;
47+ const int rowIdx = j & ditherRowMask;
48+ int16_t *row = ditherBuffer[rowIdx] ;
4549
46- uint8_t oldPixel = min ((uint16_t )0xFF , (uint16_t )((uint16_t )ditherBuffer[0 ][i] + px));
50+ int16_t oldPixel = (int16_t )px + row[i];
51+ row[i] = 0 ; // clear after reading
4752
48- uint8_t newPixel = oldPixel & (_inkplate->getDisplayMode () == INKPLATE_1BIT ? B10000000 : B11100000);
49- uint8_t quantError = oldPixel - newPixel;
53+ oldPixel = max ((int16_t )0 , min ((int16_t )255 , oldPixel));
5054
51- ditherBuffer[1 ][i + 0 ] += (quantError * 5 ) >> 4 ;
52- if (i != w - 1 )
53- {
54- ditherBuffer[0 ][i + 1 ] += (quantError * 7 ) >> 4 ;
55- ditherBuffer[1 ][i + 1 ] += (quantError * 1 ) >> 4 ;
56- }
57- if (i != 0 )
58- ditherBuffer[1 ][i - 1 ] += (quantError * 3 ) >> 4 ;
55+ // 1-bit: snap to 0x00 or 0xFF so that >>5 produces 0 or 7 (full display range).
56+ // 3-bit: quantise to the top 3 bits; >>5 produces 0-7.
57+ uint8_t newPixel = (_inkplate->getDisplayMode () == INKPLATE_1BIT) ? ((oldPixel >= 128 ) ? 0xFF : 0x00 )
58+ : ((uint8_t )oldPixel & B11100000);
59+ int16_t quantError = oldPixel - newPixel;
5960
60- return newPixel >> 5 ;
61- }
61+ const int minOffset = max (-currentKernel-> x , -i) ;
62+ const int maxOffset = min (currentKernel-> width - currentKernel-> x - 1 , w - 1 - i);
6263
63- /* *
64- * @brief ditherGetPixelJpeg calculates dither for given pixel in jpeg
65- * images
66- *
67- * @param uint8_t px
68- * pixel value with color information
69- * @param int i
70- * ditherBuffer width plane position
71- * @param int j
72- * ditherBuffer height plane position
73- * @param int x
74- * x image starting position
75- * @param int y
76- * y image starting position
77- * @param int w
78- * image width
79- * @param int h
80- * image height
81- *
82- * @return new pixel value (dithered pixel)
83- */
84- uint8_t Image::ditherGetPixelJpeg (uint8_t px, int i, int j, int x, int y, int w, int h)
85- {
86- if (blockW == -1 )
64+ for (int k = 0 ; k < currentKernel->height ; ++k)
8765 {
88- blockW = w;
89- blockH = h;
66+ int16_t *nextRow = ditherBuffer[(rowIdx + k) & ditherRowMask];
67+ for (int l = minOffset; l <= maxOffset; ++l)
68+ {
69+ const int weight = currentKernel->data [k * currentKernel->width + (l + currentKernel->x )];
70+ if (!weight)
71+ continue ;
72+ nextRow[i + l] += (weight * quantError) / currentKernel->coef ;
73+ }
9074 }
9175
92- if (_inkplate->getDisplayMode () == INKPLATE_1BIT)
93- px = (uint16_t )px >> 1 ;
94-
95- uint16_t oldPixel = min ((uint16_t )0xFF , (uint16_t )((uint16_t )px + (uint16_t )jpegDitherBuffer[j + 1 ][i + 1 ] +
96- (j ? (uint16_t )0 : (uint16_t )ditherBuffer[0 ][x + i])));
97-
98- uint8_t newPixel = oldPixel & (_inkplate->getDisplayMode () == INKPLATE_1BIT ? B10000000 : B11100000);
99- uint8_t quantError = oldPixel - newPixel;
100-
101- jpegDitherBuffer[j + 1 + 1 ][i + 0 + 1 ] += (quantError * 5 ) >> 4 ;
102-
103- jpegDitherBuffer[j + 0 + 1 ][i + 1 + 1 ] += (quantError * 7 ) >> 4 ;
104- jpegDitherBuffer[j + 1 + 1 ][i + 1 + 1 ] += (quantError * 1 ) >> 4 ;
105-
106- jpegDitherBuffer[j + 1 + 1 ][i - 1 + 1 ] += (quantError * 3 ) >> 4 ;
107-
10876 return newPixel >> 5 ;
10977}
11078
111- /* *
112- * @brief ditherSwap swaps ditherBuffer values
113- *
114- * @param int w
115- * screen width
116- */
117- void Image::ditherSwap (int w)
118- {
119- for (int i = 0 ; i < w; ++i)
120- {
121- ditherBuffer[0 ][i] = ditherBuffer[1 ][i];
122- ditherBuffer[1 ][i] = 0 ;
123- }
124- }
125-
126- /* *
127- * @brief ditherSwapBlockJpeg swaps ditherBuffer values
128- *
129- * @param int x
130- * x plane image starting point
131- */
132- void Image::ditherSwapBlockJpeg (int x)
133- {
134- for (int i = 0 ; i < 18 ; ++i)
135- {
136- if (x + i)
137- ditherBuffer[1 ][x + i - 1 ] += jpegDitherBuffer[blockH - 1 + 2 ][i];
138- jpegDitherBuffer[i][0 + 1 ] = jpegDitherBuffer[i][blockW - 1 + 2 ];
139- }
140- for (int j = 0 ; j < 18 ; ++j)
141- for (int i = 0 ; i < 18 ; ++i)
142- if (i != 1 )
143- jpegDitherBuffer[j][i] = 0 ;
144-
145- jpegDitherBuffer[17 ][1 ] = 0 ;
146- }
147- #endif
79+ #endif
0 commit comments