xref: /linux/drivers/video/fbdev/core/fb_imageblit.h (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
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 
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 
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 
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 */
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 */
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) */
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) */
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 */
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 
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 */
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 */
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 
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 
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