1 /* SPDX-License-Identifier: GPL-2.0-only
2 *
3 * Generic bitmap / 8 bpp image bitstreamer for packed pixel framebuffers
4 *
5 * Rewritten by:
6 * Copyright (C) 2025 Zsolt Kajtar (soci@c64.rulez.org)
7 *
8 * Based on previous work of:
9 * Copyright (C) June 1999 James Simmons
10 * Anton Vorontsov <avorontsov@ru.mvista.com>
11 * Pavel Pisa <pisa@cmp.felk.cvut.cz>
12 * Antonino A. Daplas <adaplas@gmail.com>
13 * and others
14 *
15 * NOTES:
16 *
17 * Handles native and foreign byte order on both endians, standard and
18 * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits,
19 * bits per pixel from 1 to the word length. Handles line lengths at byte
20 * granularity while maintaining aligned accesses.
21 *
22 * Optimized routines for word aligned 1, 2, 4 pixel per word for high
23 * bpp modes and 4 pixel at a time operation for low bpp.
24 *
25 * The color image is expected to be one byte per pixel, and values should
26 * not exceed the bitdepth or the pseudo palette (if used).
27 */
28 #include "fb_draw.h"
29
30 /* bitmap image iterator, one pixel at a time */
31 struct fb_bitmap_iter {
32 const u8 *data;
33 unsigned long colors[2];
34 int width, i;
35 };
36
fb_bitmap_image(void * iterator,unsigned long * pixels,int * bits)37 static bool fb_bitmap_image(void *iterator, unsigned long *pixels, int *bits)
38 {
39 struct fb_bitmap_iter *iter = iterator;
40
41 if (iter->i < iter->width) {
42 int bit = ~iter->i & (BITS_PER_BYTE-1);
43 int byte = iter->i++ / BITS_PER_BYTE;
44
45 *pixels = iter->colors[(iter->data[byte] >> bit) & 1];
46 return true;
47 }
48 iter->data += BITS_TO_BYTES(iter->width);
49 iter->i = 0;
50 return false;
51 }
52
53 /* color image iterator, one pixel at a time */
54 struct fb_color_iter {
55 const u8 *data;
56 const u32 *palette;
57 struct fb_reverse reverse;
58 int shift;
59 int width, i;
60 };
61
fb_color_image(void * iterator,unsigned long * pixels,int * bits)62 static bool fb_color_image(void *iterator, unsigned long *pixels, int *bits)
63 {
64 struct fb_color_iter *iter = iterator;
65
66 if (iter->i < iter->width) {
67 unsigned long color = iter->data[iter->i++];
68
69 if (iter->palette)
70 color = iter->palette[color];
71 *pixels = color << iter->shift;
72 if (iter->reverse.pixel)
73 *pixels = fb_reverse_bits_long(*pixels);
74 return true;
75 }
76 iter->data += iter->width;
77 iter->i = 0;
78 return false;
79 }
80
81 /* bitmap image iterator, 4 pixels at a time */
82 struct fb_bitmap4x_iter {
83 const u8 *data;
84 u32 fgxcolor, bgcolor;
85 int width, i;
86 const u32 *expand;
87 int bpp;
88 bool top;
89 };
90
fb_bitmap4x_image(void * iterator,unsigned long * pixels,int * bits)91 static bool fb_bitmap4x_image(void *iterator, unsigned long *pixels, int *bits)
92 {
93 struct fb_bitmap4x_iter *iter = iterator;
94 u8 data;
95
96 if (iter->i >= BITS_PER_BYTE/2) {
97 iter->i -= BITS_PER_BYTE/2;
98 iter->top = !iter->top;
99 if (iter->top)
100 data = *iter->data++ >> BITS_PER_BYTE/2;
101 else
102 data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1);
103 } else if (iter->i != 0) {
104 *bits = iter->bpp * iter->i;
105 if (iter->top)
106 data = iter->data[-1] & ((1 << BITS_PER_BYTE/2)-1);
107 else
108 data = *iter->data++ >> BITS_PER_BYTE/2;
109 #ifndef __LITTLE_ENDIAN
110 data >>= BITS_PER_BYTE/2 - iter->i;
111 #endif
112 iter->i = 0;
113 } else {
114 *bits = iter->bpp * BITS_PER_BYTE/2;
115 iter->i = iter->width;
116 iter->top = false;
117 return false;
118 }
119 *pixels = (iter->fgxcolor & iter->expand[data]) ^ iter->bgcolor;
120 #ifndef __LITTLE_ENDIAN
121 *pixels <<= BITS_PER_LONG - *bits;
122 #endif
123 return true;
124 }
125
126 /* draw a line a group of pixels at a time */
fb_bitblit(bool (* get)(void * iter,unsigned long * pixels,int * bits),void * iter,int bits,struct fb_address * dst,struct fb_reverse reverse)127 static __always_inline void fb_bitblit(bool (*get)(void *iter, unsigned long *pixels,
128 int *bits),
129 void *iter, int bits, struct fb_address *dst,
130 struct fb_reverse reverse)
131 {
132 unsigned long pixels, val, mask, old;
133 int offset = 0;
134 int shift = dst->bits;
135
136 if (shift) {
137 old = fb_read_offset(0, dst);
138 val = fb_reverse_long(old, reverse);
139 val &= ~fb_right(~0UL, shift);
140 } else {
141 old = 0;
142 val = 0;
143 }
144
145 while (get(iter, &pixels, &bits)) {
146 val |= fb_right(pixels, shift);
147 shift += bits;
148
149 if (shift < BITS_PER_LONG)
150 continue;
151
152 val = fb_reverse_long(val, reverse);
153 fb_write_offset(val, offset++, dst);
154 shift &= BITS_PER_LONG - 1;
155 val = !shift ? 0 : fb_left(pixels, bits - shift);
156 }
157
158 if (shift) {
159 mask = ~fb_pixel_mask(shift, reverse);
160 val = fb_reverse_long(val, reverse);
161 if (offset || !dst->bits)
162 old = fb_read_offset(offset, dst);
163 fb_write_offset(fb_comp(val, old, mask), offset, dst);
164 }
165 }
166
167 /* draw a color image a pixel at a time */
fb_color_imageblit(const struct fb_image * image,struct fb_address * dst,unsigned int bits_per_line,const u32 * palette,int bpp,struct fb_reverse reverse)168 static inline void fb_color_imageblit(const struct fb_image *image, struct fb_address *dst,
169 unsigned int bits_per_line, const u32 *palette, int bpp,
170 struct fb_reverse reverse)
171 {
172 struct fb_color_iter iter;
173 u32 height;
174
175 iter.data = (const u8 *)image->data;
176 iter.palette = palette;
177 iter.reverse = reverse;
178 #ifdef __LITTLE_ENDIAN
179 if (reverse.pixel)
180 iter.shift = BITS_PER_BYTE - bpp;
181 else
182 iter.shift = 0;
183 #else
184 if (reverse.pixel)
185 iter.shift = BITS_PER_LONG - BITS_PER_BYTE;
186 else
187 iter.shift = BITS_PER_LONG - bpp;
188 #endif
189 iter.width = image->width;
190 iter.i = 0;
191
192 height = image->height;
193 while (height--) {
194 fb_bitblit(fb_color_image, &iter, bpp, dst, reverse);
195 fb_address_forward(dst, bits_per_line);
196 }
197 }
198
199 #ifdef __LITTLE_ENDIAN
200 #define FB_GEN(a, b) (((a)/8+(((a)&4)<<((b)-2)) \
201 +(((a)&2)<<((b)*2-1))+(((a)&1u)<<((b)*3)))*((1<<(b))-1))
202 #define FB_GEN1(a) ((a)/8+((a)&4)/2+((a)&2)*2+((a)&1)*8)
203 #else
204 #define FB_GEN(a, b) (((((a)/8)<<((b)*3))+(((a)&4)<<((b)*2-2)) \
205 +(((a)&2)<<(b-1))+((a)&1u))*((1<<(b))-1))
206 #define FB_GEN1(a) (a)
207 #endif
208
209 #define FB_GENx(a) { FB_GEN(0, (a)), FB_GEN(1, (a)), FB_GEN(2, (a)), FB_GEN(3, (a)), \
210 FB_GEN(4, (a)), FB_GEN(5, (a)), FB_GEN(6, (a)), FB_GEN(7, (a)), \
211 FB_GEN(8, (a)), FB_GEN(9, (a)), FB_GEN(10, (a)), FB_GEN(11, (a)), \
212 FB_GEN(12, (a)), FB_GEN(13, (a)), FB_GEN(14, (a)), FB_GEN(15, (a)) }
213
214 /* draw a 2 color image four pixels at a time (for 1-8 bpp only) */
fb_bitmap4x_imageblit(const struct fb_image * image,struct fb_address * dst,unsigned long fgcolor,unsigned long bgcolor,int bpp,unsigned int bits_per_line,struct fb_reverse reverse)215 static inline void fb_bitmap4x_imageblit(const struct fb_image *image, struct fb_address *dst,
216 unsigned long fgcolor, unsigned long bgcolor, int bpp,
217 unsigned int bits_per_line, struct fb_reverse reverse)
218 {
219 static const u32 mul[BITS_PER_BYTE] = {
220 0xf, 0x55, 0x249, 0x1111, 0x8421, 0x41041, 0x204081, 0x01010101
221 };
222 static const u32 expand[BITS_PER_BYTE][1 << 4] = {
223 {
224 FB_GEN1(0), FB_GEN1(1), FB_GEN1(2), FB_GEN1(3),
225 FB_GEN1(4), FB_GEN1(5), FB_GEN1(6), FB_GEN1(7),
226 FB_GEN1(8), FB_GEN1(9), FB_GEN1(10), FB_GEN1(11),
227 FB_GEN1(12), FB_GEN1(13), FB_GEN1(14), FB_GEN1(15)
228 },
229 FB_GENx(2), FB_GENx(3), FB_GENx(4),
230 FB_GENx(5), FB_GENx(6), FB_GENx(7), FB_GENx(8),
231 };
232 struct fb_bitmap4x_iter iter;
233 u32 height;
234
235 iter.data = (const u8 *)image->data;
236 if (reverse.pixel) {
237 fgcolor = fb_reverse_bits_long(fgcolor << (BITS_PER_BYTE - bpp));
238 bgcolor = fb_reverse_bits_long(bgcolor << (BITS_PER_BYTE - bpp));
239 }
240 iter.fgxcolor = (fgcolor ^ bgcolor) * mul[bpp-1];
241 iter.bgcolor = bgcolor * mul[bpp-1];
242 iter.width = image->width;
243 iter.i = image->width;
244 iter.expand = expand[bpp-1];
245 iter.bpp = bpp;
246 iter.top = false;
247
248 height = image->height;
249 while (height--) {
250 fb_bitblit(fb_bitmap4x_image, &iter, bpp * BITS_PER_BYTE/2, dst, reverse);
251 fb_address_forward(dst, bits_per_line);
252 }
253 }
254
255 /* draw a bitmap image 1 pixel at a time (for >8 bpp) */
fb_bitmap1x_imageblit(const struct fb_image * image,struct fb_address * dst,unsigned long fgcolor,unsigned long bgcolor,int bpp,unsigned int bits_per_line,struct fb_reverse reverse)256 static inline void fb_bitmap1x_imageblit(const struct fb_image *image, struct fb_address *dst,
257 unsigned long fgcolor, unsigned long bgcolor, int bpp,
258 unsigned int bits_per_line, struct fb_reverse reverse)
259 {
260 struct fb_bitmap_iter iter;
261 u32 height;
262
263 iter.colors[0] = bgcolor;
264 iter.colors[1] = fgcolor;
265 #ifndef __LITTLE_ENDIAN
266 iter.colors[0] <<= BITS_PER_LONG - bpp;
267 iter.colors[1] <<= BITS_PER_LONG - bpp;
268 #endif
269 iter.data = (const u8 *)image->data;
270 iter.width = image->width;
271 iter.i = 0;
272
273 height = image->height;
274 while (height--) {
275 fb_bitblit(fb_bitmap_image, &iter, bpp, dst, reverse);
276 fb_address_forward(dst, bits_per_line);
277 }
278 }
279
280 /* one pixel per word, 64/32 bpp blitting */
fb_bitmap_1ppw(const struct fb_image * image,struct fb_address * dst,unsigned long fgcolor,unsigned long bgcolor,int words_per_line,struct fb_reverse reverse)281 static inline void fb_bitmap_1ppw(const struct fb_image *image, struct fb_address *dst,
282 unsigned long fgcolor, unsigned long bgcolor,
283 int words_per_line, struct fb_reverse reverse)
284 {
285 unsigned long tab[2];
286 const u8 *src = (u8 *)image->data;
287 int width = image->width;
288 int offset;
289 u32 height;
290
291 if (reverse.byte) {
292 tab[0] = swab_long(bgcolor);
293 tab[1] = swab_long(fgcolor);
294 } else {
295 tab[0] = bgcolor;
296 tab[1] = fgcolor;
297 }
298
299 height = image->height;
300 while (height--) {
301 for (offset = 0; offset + 8 <= width; offset += 8) {
302 unsigned int srcbyte = *src++;
303
304 fb_write_offset(tab[(srcbyte >> 7) & 1], offset + 0, dst);
305 fb_write_offset(tab[(srcbyte >> 6) & 1], offset + 1, dst);
306 fb_write_offset(tab[(srcbyte >> 5) & 1], offset + 2, dst);
307 fb_write_offset(tab[(srcbyte >> 4) & 1], offset + 3, dst);
308 fb_write_offset(tab[(srcbyte >> 3) & 1], offset + 4, dst);
309 fb_write_offset(tab[(srcbyte >> 2) & 1], offset + 5, dst);
310 fb_write_offset(tab[(srcbyte >> 1) & 1], offset + 6, dst);
311 fb_write_offset(tab[(srcbyte >> 0) & 1], offset + 7, dst);
312 }
313
314 if (offset < width) {
315 unsigned int srcbyte = *src++;
316
317 while (offset < width) {
318 fb_write_offset(tab[(srcbyte >> 7) & 1], offset, dst);
319 srcbyte <<= 1;
320 offset++;
321 }
322 }
323 fb_address_move_long(dst, words_per_line);
324 }
325 }
326
fb_pack(unsigned long left,unsigned long right,int bits)327 static inline unsigned long fb_pack(unsigned long left, unsigned long right, int bits)
328 {
329 #ifdef __LITTLE_ENDIAN
330 return left | right << bits;
331 #else
332 return right | left << bits;
333 #endif
334 }
335
336 /* aligned 32/16 bpp blitting */
fb_bitmap_2ppw(const struct fb_image * image,struct fb_address * dst,unsigned long fgcolor,unsigned long bgcolor,int words_per_line,struct fb_reverse reverse)337 static inline void fb_bitmap_2ppw(const struct fb_image *image, struct fb_address *dst,
338 unsigned long fgcolor, unsigned long bgcolor,
339 int words_per_line, struct fb_reverse reverse)
340 {
341 unsigned long tab[4];
342 const u8 *src = (u8 *)image->data;
343 int width = image->width / 2;
344 int offset;
345 u32 height;
346
347 tab[0] = fb_pack(bgcolor, bgcolor, BITS_PER_LONG/2);
348 tab[1] = fb_pack(bgcolor, fgcolor, BITS_PER_LONG/2);
349 tab[2] = fb_pack(fgcolor, bgcolor, BITS_PER_LONG/2);
350 tab[3] = fb_pack(fgcolor, fgcolor, BITS_PER_LONG/2);
351
352 if (reverse.byte) {
353 tab[0] = swab_long(tab[0]);
354 tab[1] = swab_long(tab[1]);
355 tab[2] = swab_long(tab[2]);
356 tab[3] = swab_long(tab[3]);
357 }
358
359 height = image->height;
360 while (height--) {
361 for (offset = 0; offset + 4 <= width; offset += 4) {
362 unsigned int srcbyte = *src++;
363
364 fb_write_offset(tab[(srcbyte >> 6) & 3], offset + 0, dst);
365 fb_write_offset(tab[(srcbyte >> 4) & 3], offset + 1, dst);
366 fb_write_offset(tab[(srcbyte >> 2) & 3], offset + 2, dst);
367 fb_write_offset(tab[(srcbyte >> 0) & 3], offset + 3, dst);
368 }
369
370 if (offset < width) {
371 unsigned int srcbyte = *src++;
372
373 while (offset < width) {
374 fb_write_offset(tab[(srcbyte >> 6) & 3], offset, dst);
375 srcbyte <<= 2;
376 offset++;
377 }
378 }
379 fb_address_move_long(dst, words_per_line);
380 }
381 }
382
383 #define FB_PATP(a, b) (((a)<<((b)*BITS_PER_LONG/4))*((1UL<<BITS_PER_LONG/4)-1UL))
384 #define FB_PAT4(a) (FB_PATP((a)&1, 0)|FB_PATP(((a)&2)/2, 1)| \
385 FB_PATP(((a)&4)/4, 2)|FB_PATP(((a)&8)/8, 3))
386
387 /* aligned 16/8 bpp blitting */
fb_bitmap_4ppw(const struct fb_image * image,struct fb_address * dst,unsigned long fgcolor,unsigned long bgcolor,int words_per_line,struct fb_reverse reverse)388 static inline void fb_bitmap_4ppw(const struct fb_image *image, struct fb_address *dst,
389 unsigned long fgcolor, unsigned long bgcolor,
390 int words_per_line, struct fb_reverse reverse)
391 {
392 static const unsigned long tab16_be[] = {
393 0, FB_PAT4(1UL), FB_PAT4(2UL), FB_PAT4(3UL),
394 FB_PAT4(4UL), FB_PAT4(5UL), FB_PAT4(6UL), FB_PAT4(7UL),
395 FB_PAT4(8UL), FB_PAT4(9UL), FB_PAT4(10UL), FB_PAT4(11UL),
396 FB_PAT4(12UL), FB_PAT4(13UL), FB_PAT4(14UL), ~0UL
397 };
398 static const unsigned long tab16_le[] = {
399 0, FB_PAT4(8UL), FB_PAT4(4UL), FB_PAT4(12UL),
400 FB_PAT4(2UL), FB_PAT4(10UL), FB_PAT4(6UL), FB_PAT4(14UL),
401 FB_PAT4(1UL), FB_PAT4(9UL), FB_PAT4(5UL), FB_PAT4(13UL),
402 FB_PAT4(3UL), FB_PAT4(11UL), FB_PAT4(7UL), ~0UL
403 };
404 const unsigned long *tab;
405 const u8 *src = (u8 *)image->data;
406 int width = image->width / 4;
407 int offset;
408 u32 height;
409
410 fgcolor = fgcolor | fgcolor << BITS_PER_LONG/4;
411 bgcolor = bgcolor | bgcolor << BITS_PER_LONG/4;
412 fgcolor = fgcolor | fgcolor << BITS_PER_LONG/2;
413 bgcolor = bgcolor | bgcolor << BITS_PER_LONG/2;
414 fgcolor ^= bgcolor;
415
416 if (BITS_PER_LONG/4 > BITS_PER_BYTE && reverse.byte) {
417 fgcolor = swab_long(fgcolor);
418 bgcolor = swab_long(bgcolor);
419 }
420
421 #ifdef __LITTLE_ENDIAN
422 tab = reverse.byte ? tab16_be : tab16_le;
423 #else
424 tab = reverse.byte ? tab16_le : tab16_be;
425 #endif
426
427 height = image->height;
428 while (height--) {
429 for (offset = 0; offset + 2 <= width; offset += 2, src++) {
430 fb_write_offset((fgcolor & tab[*src >> 4]) ^ bgcolor, offset + 0, dst);
431 fb_write_offset((fgcolor & tab[*src & 0xf]) ^ bgcolor, offset + 1, dst);
432 }
433
434 if (offset < width)
435 fb_write_offset((fgcolor & tab[*src++ >> 4]) ^ bgcolor, offset, dst);
436
437 fb_address_move_long(dst, words_per_line);
438 }
439 }
440
fb_bitmap_imageblit(const struct fb_image * image,struct fb_address * dst,unsigned int bits_per_line,const u32 * palette,int bpp,struct fb_reverse reverse)441 static inline void fb_bitmap_imageblit(const struct fb_image *image, struct fb_address *dst,
442 unsigned int bits_per_line, const u32 *palette, int bpp,
443 struct fb_reverse reverse)
444 {
445 unsigned long fgcolor, bgcolor;
446
447 if (palette) {
448 fgcolor = palette[image->fg_color];
449 bgcolor = palette[image->bg_color];
450 } else {
451 fgcolor = image->fg_color;
452 bgcolor = image->bg_color;
453 }
454
455 if (!dst->bits && !(bits_per_line & (BITS_PER_LONG-1))) {
456 if (bpp == BITS_PER_LONG && BITS_PER_LONG == 32) {
457 fb_bitmap_1ppw(image, dst, fgcolor, bgcolor,
458 bits_per_line / BITS_PER_LONG, reverse);
459 return;
460 }
461 if (bpp == BITS_PER_LONG/2 && !(image->width & 1)) {
462 fb_bitmap_2ppw(image, dst, fgcolor, bgcolor,
463 bits_per_line / BITS_PER_LONG, reverse);
464 return;
465 }
466 if (bpp == BITS_PER_LONG/4 && !(image->width & 3)) {
467 fb_bitmap_4ppw(image, dst, fgcolor, bgcolor,
468 bits_per_line / BITS_PER_LONG, reverse);
469 return;
470 }
471 }
472
473 if (bpp > 0 && bpp <= BITS_PER_BYTE)
474 fb_bitmap4x_imageblit(image, dst, fgcolor, bgcolor, bpp,
475 bits_per_line, reverse);
476 else if (bpp > BITS_PER_BYTE && bpp <= BITS_PER_LONG)
477 fb_bitmap1x_imageblit(image, dst, fgcolor, bgcolor, bpp,
478 bits_per_line, reverse);
479 }
480
fb_imageblit(struct fb_info * p,const struct fb_image * image)481 static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image)
482 {
483 int bpp = p->var.bits_per_pixel;
484 unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
485 struct fb_address dst = fb_address_init(p);
486 struct fb_reverse reverse = fb_reverse_init(p);
487 const u32 *palette = fb_palette(p);
488
489 fb_address_forward(&dst, image->dy * bits_per_line + image->dx * bpp);
490
491 if (image->depth == 1)
492 fb_bitmap_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
493 else
494 fb_color_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
495 }
496