File Index
Files and directories
Sign in
FILE

fb_imageblit.h

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/core/fb_imageblit.h C
  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 */
 31struct fb_bitmap_iter {
 32	const u8 *data;
 33	unsigned long colors[2];
 34	int width, i;
 35};
 36
 37static 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 */
 54struct 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
 62static 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 */
 82struct 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
 91static 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 */
127static __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 */
168static 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) */
215static 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) */
256static 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 */
281static 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
327static 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 */
337static 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 */
388static 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
441static 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
481static 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}
~/ Read-only file preview.