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