279 lines · 7.8 KB · C
RawDownload
/* SPDX-License-Identifier: GPL-2.0-only * *  Generic bit area filler and twister engine for packed pixel framebuffers * *	Rewritten by: *	Copyright (C)  2025 Zsolt Kajtar (soci@c64.rulez.org) * *	Based on earlier work of: *	Copyright (C)  2000 James Simmons (jsimmons@linux-fbdev.org) *	Michal Januszewski <spock@gentoo.org> *	Anton Vorontsov <avorontsov@ru.mvista.com> *	Pavel Pisa <pisa@cmp.felk.cvut.cz> *	Antonino A. Daplas <adaplas@gmail.com> *	Geert Uytterhoeven *	and others * * NOTES: * * Handles native and foreign byte order on both endians, standard and * reverse pixel order in a byte (<8 BPP), word length of 32/64 bits, * bits per pixel from 1 to the word length. Handles line lengths at byte * granularity while maintaining aligned accesses. * * Optimized path for power of two bits per pixel modes. */#include "fb_draw.h" /* inverts bits at a given offset */static inline void fb_invert_offset(unsigned long pat, int offset, const struct fb_address *dst){	fb_write_offset(fb_read_offset(offset, dst) ^ pat, offset, dst);} /* state for pattern generator and whether swapping is necessary */struct fb_pattern {	unsigned long pixels;	int left, right;	struct fb_reverse reverse;}; /* used to get the pattern in native order */static unsigned long fb_pattern_get(struct fb_pattern *pattern){	return pattern->pixels;} /* used to get the pattern in reverse order */static unsigned long fb_pattern_get_reverse(struct fb_pattern *pattern){	return swab_long(pattern->pixels);} /* next static pattern */static void fb_pattern_static(struct fb_pattern *pattern){	/* nothing to do */} /* next rotating pattern */static void fb_pattern_rotate(struct fb_pattern *pattern){	pattern->pixels = fb_left(pattern->pixels, pattern->left)		| fb_right(pattern->pixels, pattern->right);} #define FB_PAT(i) (((1UL<<(BITS_PER_LONG-1)/(i)*(i))/((1<<(i))-1)<<(i))|1) /* create the filling pattern from a given color */static unsigned long pixel_to_pat(int bpp, u32 color){	static const unsigned long mulconst[BITS_PER_LONG/4] = {		0, ~0UL, FB_PAT(2), FB_PAT(3),		FB_PAT(4), FB_PAT(5), FB_PAT(6), FB_PAT(7),#if BITS_PER_LONG == 64		FB_PAT(8), FB_PAT(9), FB_PAT(10), FB_PAT(11),		FB_PAT(12), FB_PAT(13), FB_PAT(14), FB_PAT(15),#endif	};	unsigned long pattern; 	switch (bpp) {	case 0 ... BITS_PER_LONG/4-1:		pattern = mulconst[bpp] * color;		break;	case BITS_PER_LONG/4 ... BITS_PER_LONG/2-1:		pattern = color;		pattern = pattern | pattern << bpp;		pattern = pattern | pattern << bpp*2;		break;	case BITS_PER_LONG/2 ... BITS_PER_LONG-1:		pattern = color;		pattern = pattern | pattern << bpp;		break;	default:		return color;	}#ifndef __LITTLE_ENDIAN	pattern <<= (BITS_PER_LONG % bpp);	pattern |= pattern >> bpp;#endif	return pattern;} /* overwrite bits according to a pattern in a line */static __always_inline void bitfill(const struct fb_address *dst,				    struct fb_pattern *pattern,				    unsigned long (*get)(struct fb_pattern *pattern),				    void (*rotate)(struct fb_pattern *pattern),				    int end){	unsigned long first, last; 	end += dst->bits;	first = fb_pixel_mask(dst->bits, pattern->reverse);	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse); 	if (end <= BITS_PER_LONG) {		last = last ? (last & first) : first;		first = get(pattern);		if (last == ~0UL)			fb_write_offset(first, 0, dst);		else if (last)			fb_modify_offset(first, last, 0, dst);	} else {		int offset = first != ~0UL; 		if (offset) {			fb_modify_offset(get(pattern), first, 0, dst);			rotate(pattern);		}		end /= BITS_PER_LONG;		for (; offset + 4 <= end; offset += 4) {			fb_write_offset(get(pattern), offset + 0, dst);			rotate(pattern);			fb_write_offset(get(pattern), offset + 1, dst);			rotate(pattern);			fb_write_offset(get(pattern), offset + 2, dst);			rotate(pattern);			fb_write_offset(get(pattern), offset + 3, dst);			rotate(pattern);		}		while (offset < end) {			fb_write_offset(get(pattern), offset++, dst);			rotate(pattern);		} 		if (last)			fb_modify_offset(get(pattern), last, offset, dst);	}} /* inverts bits according to a pattern in a line */static __always_inline void bitinvert(const struct fb_address *dst,				      struct fb_pattern *pattern,				      unsigned long (*get)(struct fb_pattern *pattern),				      void (*rotate)(struct fb_pattern *pattern),				      int end){	unsigned long first, last;	int offset; 	end += dst->bits;	first = fb_pixel_mask(dst->bits, pattern->reverse);	last = ~fb_pixel_mask(end & (BITS_PER_LONG-1), pattern->reverse); 	if (end <= BITS_PER_LONG) {		offset = 0;		last = last ? (last & first) : first;	} else {		offset = first != ~0UL; 		if (offset) {			first &= get(pattern);			if (first)				fb_invert_offset(first, 0, dst);			rotate(pattern);		} 		end /= BITS_PER_LONG;		for (; offset + 4 <= end; offset += 4) {			fb_invert_offset(get(pattern), offset + 0, dst);			rotate(pattern);			fb_invert_offset(get(pattern), offset + 1, dst);			rotate(pattern);			fb_invert_offset(get(pattern), offset + 2, dst);			rotate(pattern);			fb_invert_offset(get(pattern), offset + 3, dst);			rotate(pattern);		}		while (offset < end) {			fb_invert_offset(get(pattern), offset++, dst);			rotate(pattern);		}	} 	last &= get(pattern);	if (last)		fb_invert_offset(last, offset, dst);} /* pattern doesn't change. 1, 2, 4, 8, 16, 32, 64 bpp */static inline void fb_fillrect_static(const struct fb_fillrect *rect, int bpp,				      struct fb_address *dst, struct fb_pattern *pattern,				      unsigned int bits_per_line){	u32 height = rect->height;	int width = rect->width * bpp; 	if (bpp > 8 && pattern->reverse.byte)		pattern->pixels = swab_long(pattern->pixels); 	if (rect->rop == ROP_XOR)		while (height--) {			bitinvert(dst, pattern, fb_pattern_get, fb_pattern_static, width);			fb_address_forward(dst, bits_per_line);		}	else		while (height--) {			bitfill(dst, pattern, fb_pattern_get, fb_pattern_static, width);			fb_address_forward(dst, bits_per_line);		}} /* rotate pattern to the correct position */static inline unsigned long fb_rotate(unsigned long pattern, int shift, int bpp){	shift %= bpp;	return fb_right(pattern, shift) | fb_left(pattern, bpp - shift);} /* rotating pattern, for example 24 bpp */static __always_inline void fb_fillrect_rotating(const struct fb_fillrect *rect,						 int bpp, struct fb_address *dst,						 struct fb_pattern *pattern,						 unsigned long (*get)(struct fb_pattern *pattern),						 unsigned int bits_per_line){	unsigned long pat = pattern->pixels;	u32 height = rect->height;	int width = rect->width * bpp; 	if (rect->rop == ROP_XOR)		while (height--) {			pattern->pixels = fb_rotate(pat, dst->bits, bpp);			bitinvert(dst, pattern, get, fb_pattern_rotate, width);			fb_address_forward(dst, bits_per_line);		}	else		while (height--) {			pattern->pixels = fb_rotate(pat, dst->bits, bpp);			bitfill(dst, pattern, get, fb_pattern_rotate, width);			fb_address_forward(dst, bits_per_line);		}} static inline void fb_fillrect(struct fb_info *p, const struct fb_fillrect *rect){	int bpp = p->var.bits_per_pixel;	unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);	const u32 *palette = fb_palette(p);	struct fb_address dst = fb_address_init(p);	struct fb_pattern pattern; 	fb_address_forward(&dst, rect->dy * bits_per_line + rect->dx * bpp); 	pattern.pixels = pixel_to_pat(bpp, palette ? palette[rect->color] : rect->color);	pattern.reverse = fb_reverse_init(p);	pattern.left = BITS_PER_LONG % bpp;	if (pattern.left) {		pattern.right = bpp - pattern.left;		if (pattern.reverse.byte)			fb_fillrect_rotating(rect, bpp, &dst, &pattern,					     fb_pattern_get_reverse, bits_per_line);		else			fb_fillrect_rotating(rect, bpp, &dst, &pattern,					     fb_pattern_get, bits_per_line);	} else		fb_fillrect_static(rect, bpp, &dst, &pattern, bits_per_line);}