1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_HAVE_BLIT_1
24
25#include "SDL_surface_c.h"
26#include "SDL_sysvideo.h"
27
28// Functions to blit from 8-bit surfaces to other surfaces
29
30static void Blit1to1(SDL_BlitInfo *info)
31{
32#ifndef USE_DUFFS_LOOP
33 int c;
34#endif
35 int width, height;
36 Uint8 *src, *map, *dst;
37 int srcskip, dstskip;
38
39 // Set up some basic variables
40 width = info->dst_w;
41 height = info->dst_h;
42 src = info->src;
43 srcskip = info->src_skip;
44 dst = info->dst;
45 dstskip = info->dst_skip;
46 map = info->table;
47
48 while (height--) {
49#ifdef USE_DUFFS_LOOP
50 /* *INDENT-OFF* */ // clang-format off
51 DUFFS_LOOP_TRIVIAL(
52 {
53 *dst = map[*src];
54 }
55 dst++;
56 src++;
57 , width);
58 /* *INDENT-ON* */ // clang-format on
59#else
60 for (c = width; c; --c) {
61 *dst = map[*src];
62 dst++;
63 src++;
64 }
65#endif
66 src += srcskip;
67 dst += dstskip;
68 }
69}
70
71// This is now endian dependent
72#ifndef USE_DUFFS_LOOP
73#if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
74#define HI 1
75#define LO 0
76#else // ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
77#define HI 0
78#define LO 1
79#endif
80#endif
81static void Blit1to2(SDL_BlitInfo *info)
82{
83#ifndef USE_DUFFS_LOOP
84 int c;
85#endif
86 int width, height;
87 Uint8 *src, *dst;
88 Uint16 *map;
89 int srcskip, dstskip;
90
91 // Set up some basic variables
92 width = info->dst_w;
93 height = info->dst_h;
94 src = info->src;
95 srcskip = info->src_skip;
96 dst = info->dst;
97 dstskip = info->dst_skip;
98 map = (Uint16 *)info->table;
99
100#ifdef USE_DUFFS_LOOP
101 while (height--) {
102 /* *INDENT-OFF* */ // clang-format off
103 DUFFS_LOOP_TRIVIAL(
104 {
105 *(Uint16 *)dst = map[*src++];
106 dst += 2;
107 },
108 width);
109 /* *INDENT-ON* */ // clang-format on
110 src += srcskip;
111 dst += dstskip;
112 }
113#else
114 // Memory align at 4-byte boundary, if necessary
115 if ((long)dst & 0x03) {
116 // Don't do anything if width is 0
117 if (width == 0) {
118 return;
119 }
120 --width;
121
122 while (height--) {
123 // Perform copy alignment
124 *(Uint16 *)dst = map[*src++];
125 dst += 2;
126
127 // Copy in 4 pixel chunks
128 for (c = width / 4; c; --c) {
129 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
130 src += 2;
131 dst += 4;
132 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
133 src += 2;
134 dst += 4;
135 }
136 // Get any leftovers
137 switch (width & 3) {
138 case 3:
139 *(Uint16 *)dst = map[*src++];
140 dst += 2;
141 SDL_FALLTHROUGH;
142 case 2:
143 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
144 src += 2;
145 dst += 4;
146 break;
147 case 1:
148 *(Uint16 *)dst = map[*src++];
149 dst += 2;
150 break;
151 }
152 src += srcskip;
153 dst += dstskip;
154 }
155 } else {
156 while (height--) {
157 // Copy in 4 pixel chunks
158 for (c = width / 4; c; --c) {
159 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
160 src += 2;
161 dst += 4;
162 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
163 src += 2;
164 dst += 4;
165 }
166 // Get any leftovers
167 switch (width & 3) {
168 case 3:
169 *(Uint16 *)dst = map[*src++];
170 dst += 2;
171 SDL_FALLTHROUGH;
172 case 2:
173 *(Uint32 *)dst = (map[src[HI]] << 16) | (map[src[LO]]);
174 src += 2;
175 dst += 4;
176 break;
177 case 1:
178 *(Uint16 *)dst = map[*src++];
179 dst += 2;
180 break;
181 }
182 src += srcskip;
183 dst += dstskip;
184 }
185 }
186#endif // USE_DUFFS_LOOP
187}
188
189static void Blit1to3(SDL_BlitInfo *info)
190{
191#ifndef USE_DUFFS_LOOP
192 int c;
193#endif
194 int o;
195 int width, height;
196 Uint8 *src, *map, *dst;
197 int srcskip, dstskip;
198
199 // Set up some basic variables
200 width = info->dst_w;
201 height = info->dst_h;
202 src = info->src;
203 srcskip = info->src_skip;
204 dst = info->dst;
205 dstskip = info->dst_skip;
206 map = info->table;
207
208 while (height--) {
209#ifdef USE_DUFFS_LOOP
210 /* *INDENT-OFF* */ // clang-format off
211 DUFFS_LOOP(
212 {
213 o = *src * 4;
214 dst[0] = map[o++];
215 dst[1] = map[o++];
216 dst[2] = map[o++];
217 }
218 src++;
219 dst += 3;
220 , width);
221 /* *INDENT-ON* */ // clang-format on
222#else
223 for (c = width; c; --c) {
224 o = *src * 4;
225 dst[0] = map[o++];
226 dst[1] = map[o++];
227 dst[2] = map[o++];
228 src++;
229 dst += 3;
230 }
231#endif // USE_DUFFS_LOOP
232 src += srcskip;
233 dst += dstskip;
234 }
235}
236
237static void Blit1to4(SDL_BlitInfo *info)
238{
239#ifndef USE_DUFFS_LOOP
240 int c;
241#endif
242 int width, height;
243 Uint8 *src;
244 Uint32 *map, *dst;
245 int srcskip, dstskip;
246
247 // Set up some basic variables
248 width = info->dst_w;
249 height = info->dst_h;
250 src = info->src;
251 srcskip = info->src_skip;
252 dst = (Uint32 *)info->dst;
253 dstskip = info->dst_skip / 4;
254 map = (Uint32 *)info->table;
255
256 while (height--) {
257#ifdef USE_DUFFS_LOOP
258 /* *INDENT-OFF* */ // clang-format off
259 DUFFS_LOOP_TRIVIAL(
260 *dst++ = map[*src++];
261 , width);
262 /* *INDENT-ON* */ // clang-format on
263#else
264 for (c = width / 4; c; --c) {
265 *dst++ = map[*src++];
266 *dst++ = map[*src++];
267 *dst++ = map[*src++];
268 *dst++ = map[*src++];
269 }
270 switch (width & 3) {
271 case 3:
272 *dst++ = map[*src++];
273 SDL_FALLTHROUGH;
274 case 2:
275 *dst++ = map[*src++];
276 SDL_FALLTHROUGH;
277 case 1:
278 *dst++ = map[*src++];
279 }
280#endif // USE_DUFFS_LOOP
281 src += srcskip;
282 dst += dstskip;
283 }
284}
285
286static void Blit1to1Key(SDL_BlitInfo *info)
287{
288 int width = info->dst_w;
289 int height = info->dst_h;
290 Uint8 *src = info->src;
291 int srcskip = info->src_skip;
292 Uint8 *dst = info->dst;
293 int dstskip = info->dst_skip;
294 Uint8 *palmap = info->table;
295 Uint32 ckey = info->colorkey;
296
297 if (palmap) {
298 while (height--) {
299 /* *INDENT-OFF* */ // clang-format off
300 DUFFS_LOOP_TRIVIAL(
301 {
302 if ( *src != ckey ) {
303 *dst = palmap[*src];
304 }
305 dst++;
306 src++;
307 },
308 width);
309 /* *INDENT-ON* */ // clang-format on
310 src += srcskip;
311 dst += dstskip;
312 }
313 } else {
314 while (height--) {
315 /* *INDENT-OFF* */ // clang-format off
316 DUFFS_LOOP_TRIVIAL(
317 {
318 if ( *src != ckey ) {
319 *dst = *src;
320 }
321 dst++;
322 src++;
323 },
324 width);
325 /* *INDENT-ON* */ // clang-format on
326 src += srcskip;
327 dst += dstskip;
328 }
329 }
330}
331
332static void Blit1to2Key(SDL_BlitInfo *info)
333{
334 int width = info->dst_w;
335 int height = info->dst_h;
336 Uint8 *src = info->src;
337 int srcskip = info->src_skip;
338 Uint16 *dstp = (Uint16 *)info->dst;
339 int dstskip = info->dst_skip;
340 Uint16 *palmap = (Uint16 *)info->table;
341 Uint32 ckey = info->colorkey;
342
343 // Set up some basic variables
344 dstskip /= 2;
345
346 while (height--) {
347 /* *INDENT-OFF* */ // clang-format off
348 DUFFS_LOOP_TRIVIAL(
349 {
350 if ( *src != ckey ) {
351 *dstp=palmap[*src];
352 }
353 src++;
354 dstp++;
355 },
356 width);
357 /* *INDENT-ON* */ // clang-format on
358 src += srcskip;
359 dstp += dstskip;
360 }
361}
362
363static void Blit1to3Key(SDL_BlitInfo *info)
364{
365 int width = info->dst_w;
366 int height = info->dst_h;
367 Uint8 *src = info->src;
368 int srcskip = info->src_skip;
369 Uint8 *dst = info->dst;
370 int dstskip = info->dst_skip;
371 Uint8 *palmap = info->table;
372 Uint32 ckey = info->colorkey;
373 int o;
374
375 while (height--) {
376 /* *INDENT-OFF* */ // clang-format off
377 DUFFS_LOOP(
378 {
379 if ( *src != ckey ) {
380 o = *src * 4;
381 dst[0] = palmap[o++];
382 dst[1] = palmap[o++];
383 dst[2] = palmap[o++];
384 }
385 src++;
386 dst += 3;
387 },
388 width);
389 /* *INDENT-ON* */ // clang-format on
390 src += srcskip;
391 dst += dstskip;
392 }
393}
394
395static void Blit1to4Key(SDL_BlitInfo *info)
396{
397 int width = info->dst_w;
398 int height = info->dst_h;
399 Uint8 *src = info->src;
400 int srcskip = info->src_skip;
401 Uint32 *dstp = (Uint32 *)info->dst;
402 int dstskip = info->dst_skip;
403 Uint32 *palmap = (Uint32 *)info->table;
404 Uint32 ckey = info->colorkey;
405
406 // Set up some basic variables
407 dstskip /= 4;
408
409 while (height--) {
410 /* *INDENT-OFF* */ // clang-format off
411 DUFFS_LOOP_TRIVIAL(
412 {
413 if ( *src != ckey ) {
414 *dstp = palmap[*src];
415 }
416 src++;
417 dstp++;
418 },
419 width);
420 /* *INDENT-ON* */ // clang-format on
421 src += srcskip;
422 dstp += dstskip;
423 }
424}
425
426static void Blit1toNAlpha(SDL_BlitInfo *info)
427{
428 int width = info->dst_w;
429 int height = info->dst_h;
430 Uint8 *src = info->src;
431 int srcskip = info->src_skip;
432 Uint8 *dst = info->dst;
433 int dstskip = info->dst_skip;
434 const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
435 const SDL_Color *srcpal = info->src_pal->colors;
436 int dstbpp;
437 Uint32 pixel;
438 unsigned sR, sG, sB, sA;
439 unsigned dR, dG, dB, dA;
440 const unsigned A = info->a;
441
442 // Set up some basic variables
443 dstbpp = dstfmt->bytes_per_pixel;
444
445 while (height--) {
446 /* *INDENT-OFF* */ // clang-format off
447 DUFFS_LOOP(
448 {
449 sR = srcpal[*src].r;
450 sG = srcpal[*src].g;
451 sB = srcpal[*src].b;
452 sA = (srcpal[*src].a * A) / 255;
453 DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
454 ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
455 ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
456 src++;
457 dst += dstbpp;
458 },
459 width);
460 /* *INDENT-ON* */ // clang-format on
461 src += srcskip;
462 dst += dstskip;
463 }
464}
465
466static void Blit1toNAlphaKey(SDL_BlitInfo *info)
467{
468 int width = info->dst_w;
469 int height = info->dst_h;
470 Uint8 *src = info->src;
471 int srcskip = info->src_skip;
472 Uint8 *dst = info->dst;
473 int dstskip = info->dst_skip;
474 const SDL_PixelFormatDetails *dstfmt = info->dst_fmt;
475 const SDL_Color *srcpal = info->src_pal->colors;
476 Uint32 ckey = info->colorkey;
477 int dstbpp;
478 Uint32 pixel;
479 unsigned sR, sG, sB, sA;
480 unsigned dR, dG, dB, dA;
481 const unsigned A = info->a;
482
483 // Set up some basic variables
484 dstbpp = dstfmt->bytes_per_pixel;
485
486 while (height--) {
487 /* *INDENT-OFF* */ // clang-format off
488 DUFFS_LOOP(
489 {
490 if ( *src != ckey ) {
491 sR = srcpal[*src].r;
492 sG = srcpal[*src].g;
493 sB = srcpal[*src].b;
494 sA = (srcpal[*src].a * A) / 255;
495 DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
496 ALPHA_BLEND_RGBA(sR, sG, sB, sA, dR, dG, dB, dA);
497 ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
498 }
499 src++;
500 dst += dstbpp;
501 },
502 width);
503 /* *INDENT-ON* */ // clang-format on
504 src += srcskip;
505 dst += dstskip;
506 }
507}
508
509static const SDL_BlitFunc one_blit[] = {
510 (SDL_BlitFunc)NULL, Blit1to1, Blit1to2, Blit1to3, Blit1to4
511};
512
513static const SDL_BlitFunc one_blitkey[] = {
514 (SDL_BlitFunc)NULL, Blit1to1Key, Blit1to2Key, Blit1to3Key, Blit1to4Key
515};
516
517SDL_BlitFunc SDL_CalculateBlit1(SDL_Surface *surface)
518{
519 int which;
520
521 if (SDL_BITSPERPIXEL(surface->map.info.dst_fmt->format) < 8) {
522 which = 0;
523 } else {
524 which = SDL_BYTESPERPIXEL(surface->map.info.dst_fmt->format);
525 }
526
527 switch (surface->map.info.flags & ~SDL_COPY_RLE_MASK) {
528 case 0:
529 if (which < SDL_arraysize(one_blit)) {
530 return one_blit[which];
531 }
532 break;
533
534 case SDL_COPY_COLORKEY:
535 if (which < SDL_arraysize(one_blitkey)) {
536 return one_blitkey[which];
537 }
538 break;
539
540 case SDL_COPY_COLORKEY | SDL_COPY_BLEND: // this is not super-robust but handles a specific case we found sdl12-compat.
541 if (surface->map.info.a == 255) {
542 if (which < SDL_arraysize(one_blitkey)) {
543 return one_blitkey[which];
544 }
545 } else {
546 return which >= 2 ? Blit1toNAlphaKey : (SDL_BlitFunc)NULL;
547 }
548 break;
549
550 case SDL_COPY_BLEND:
551 case SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
552 /* Supporting 8bpp->8bpp alpha is doable but requires lots of
553 tables which consume space and takes time to precompute,
554 so is better left to the user */
555 return which >= 2 ? Blit1toNAlpha : (SDL_BlitFunc)NULL;
556
557 case SDL_COPY_COLORKEY | SDL_COPY_MODULATE_ALPHA | SDL_COPY_BLEND:
558 return which >= 2 ? Blit1toNAlphaKey : (SDL_BlitFunc)NULL;
559 }
560 return (SDL_BlitFunc)NULL;
561}
562
563#endif // SDL_HAVE_BLIT_1
564