summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/SDL_surface.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/video/SDL_surface.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/SDL_surface.c')
-rw-r--r--contrib/SDL-3.2.8/src/video/SDL_surface.c2995
1 files changed, 2995 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/SDL_surface.c b/contrib/SDL-3.2.8/src/video/SDL_surface.c
new file mode 100644
index 0000000..945fda6
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/SDL_surface.c
@@ -0,0 +1,2995 @@
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#include "SDL_sysvideo.h"
24#include "SDL_video_c.h"
25#include "SDL_RLEaccel_c.h"
26#include "SDL_pixels_c.h"
27#include "SDL_stb_c.h"
28#include "SDL_yuv_c.h"
29#include "../render/SDL_sysrender.h"
30
31#include "SDL_surface_c.h"
32
33
34// Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t
35SDL_COMPILE_TIME_ASSERT(surface_size_assumptions,
36 sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32));
37
38SDL_COMPILE_TIME_ASSERT(can_indicate_overflow, SDL_SIZE_MAX > SDL_MAX_SINT32);
39
40// Magic!
41static char SDL_surface_magic;
42
43// Public routines
44
45bool SDL_SurfaceValid(SDL_Surface *surface)
46{
47 return (surface && surface->reserved == &SDL_surface_magic);
48}
49
50void SDL_UpdateSurfaceLockFlag(SDL_Surface *surface)
51{
52 if (SDL_SurfaceHasRLE(surface)) {
53 surface->flags |= SDL_SURFACE_LOCK_NEEDED;
54 } else {
55 surface->flags &= ~SDL_SURFACE_LOCK_NEEDED;
56 }
57}
58
59/*
60 * Calculate the pad-aligned scanline width of a surface.
61 *
62 * for FOURCC, use SDL_CalculateYUVSize()
63 */
64static bool SDL_CalculateRGBSize(Uint32 format, size_t width, size_t height, size_t *size, size_t *pitch, bool minimal)
65{
66 if (SDL_BITSPERPIXEL(format) >= 8) {
67 if (!SDL_size_mul_check_overflow(width, SDL_BYTESPERPIXEL(format), pitch)) {
68 return SDL_SetError("width * bpp would overflow");
69 }
70 } else {
71 if (!SDL_size_mul_check_overflow(width, SDL_BITSPERPIXEL(format), pitch)) {
72 return SDL_SetError("width * bpp would overflow");
73 }
74 if (!SDL_size_add_check_overflow(*pitch, 7, pitch)) {
75 return SDL_SetError("aligning pitch would overflow");
76 }
77 *pitch /= 8;
78 }
79 if (!minimal) {
80 // 4-byte aligning for speed
81 if (!SDL_size_add_check_overflow(*pitch, 3, pitch)) {
82 return SDL_SetError("aligning pitch would overflow");
83 }
84 *pitch &= ~3;
85 }
86
87 if (!SDL_size_mul_check_overflow(height, *pitch, size)) {
88 return SDL_SetError("height * pitch would overflow");
89 }
90
91 return true;
92}
93
94bool SDL_CalculateSurfaceSize(SDL_PixelFormat format, int width, int height, size_t *size, size_t *pitch, bool minimalPitch)
95{
96 size_t p = 0, sz = 0;
97
98 if (size) {
99 *size = 0;
100 }
101
102 if (pitch) {
103 *pitch = 0;
104 }
105
106 if (SDL_ISPIXELFORMAT_FOURCC(format)) {
107 if (!SDL_CalculateYUVSize(format, width, height, &sz, &p)) {
108 // Overflow...
109 return false;
110 }
111 } else {
112 if (!SDL_CalculateRGBSize(format, width, height, &sz, &p, minimalPitch)) {
113 // Overflow...
114 return false;
115 }
116 }
117
118 if (size) {
119 *size = sz;
120 }
121
122 if (pitch) {
123 *pitch = p;
124 }
125
126 return true;
127}
128
129static bool SDL_InitializeSurface(SDL_Surface *surface, int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, SDL_PropertiesID props, void *pixels, int pitch, bool onstack)
130{
131 SDL_zerop(surface);
132
133 surface->flags = SDL_SURFACE_PREALLOCATED;
134 surface->format = format;
135 surface->w = width;
136 surface->h = height;
137 surface->pixels = pixels;
138 surface->pitch = pitch;
139 surface->reserved = &SDL_surface_magic;
140
141 if (onstack) {
142 surface->internal_flags |= SDL_INTERNAL_SURFACE_STACK;
143 }
144
145 surface->fmt = SDL_GetPixelFormatDetails(format);
146 if (!surface->fmt) {
147 SDL_DestroySurface(surface);
148 return false;
149 }
150
151 // Initialize the clip rect
152 surface->clip_rect.w = width;
153 surface->clip_rect.h = height;
154
155 // Allocate an empty mapping
156 surface->map.info.r = 0xFF;
157 surface->map.info.g = 0xFF;
158 surface->map.info.b = 0xFF;
159 surface->map.info.a = 0xFF;
160
161 if (colorspace == SDL_COLORSPACE_UNKNOWN) {
162 surface->colorspace = SDL_GetDefaultColorspaceForFormat(format);
163 } else {
164 surface->colorspace = colorspace;
165 }
166
167 if (props) {
168 if (!SDL_CopyProperties(props, SDL_GetSurfaceProperties(surface))) {
169 SDL_DestroySurface(surface);
170 return false;
171 }
172 }
173
174 // By default surfaces with an alpha mask are set up for blending
175 if (SDL_ISPIXELFORMAT_ALPHA(surface->format)) {
176 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
177 }
178
179 // The surface is ready to go
180 surface->refcount = 1;
181 return true;
182}
183
184/*
185 * Create an empty surface of the appropriate depth using the given format
186 */
187SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format)
188{
189 size_t pitch, size;
190 SDL_Surface *surface;
191
192 if (width < 0) {
193 SDL_InvalidParamError("width");
194 return NULL;
195 }
196
197 if (height < 0) {
198 SDL_InvalidParamError("height");
199 return NULL;
200 }
201
202 if (!SDL_CalculateSurfaceSize(format, width, height, &size, &pitch, false /* not minimal pitch */)) {
203 // Overflow...
204 return NULL;
205 }
206
207 // Allocate and initialize the surface
208 surface = (SDL_Surface *)SDL_malloc(sizeof(*surface));
209 if (!surface) {
210 return NULL;
211 }
212
213 if (!SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, NULL, (int)pitch, false)) {
214 return NULL;
215 }
216
217 if (surface->w && surface->h) {
218 surface->flags &= ~SDL_SURFACE_PREALLOCATED;
219 surface->pixels = SDL_aligned_alloc(SDL_GetSIMDAlignment(), size);
220 if (!surface->pixels) {
221 SDL_DestroySurface(surface);
222 return NULL;
223 }
224 surface->flags |= SDL_SURFACE_SIMD_ALIGNED;
225
226 // This is important for bitmaps
227 SDL_memset(surface->pixels, 0, size);
228 }
229 return surface;
230}
231
232/*
233 * Create an RGB surface from an existing memory buffer using the given
234 * enum SDL_PIXELFORMAT_* format
235 */
236SDL_Surface *SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format, void *pixels, int pitch)
237{
238 if (width < 0) {
239 SDL_InvalidParamError("width");
240 return NULL;
241 }
242
243 if (height < 0) {
244 SDL_InvalidParamError("height");
245 return NULL;
246 }
247
248 if (pitch == 0 && !pixels) {
249 // The application will fill these in later with valid values
250 } else {
251 size_t minimalPitch;
252
253 if (!SDL_CalculateSurfaceSize(format, width, height, NULL, &minimalPitch, true /* minimal pitch */)) {
254 // Overflow...
255 return NULL;
256 }
257
258 if (pitch < 0 || (size_t)pitch < minimalPitch) {
259 SDL_InvalidParamError("pitch");
260 return NULL;
261 }
262 }
263
264 // Allocate and initialize the surface
265 SDL_Surface *surface = (SDL_Surface *)SDL_malloc(sizeof(*surface));
266 if (!surface ||
267 !SDL_InitializeSurface(surface, width, height, format, SDL_COLORSPACE_UNKNOWN, 0, pixels, pitch, false)) {
268 return NULL;
269 }
270 return surface;
271}
272
273SDL_PropertiesID SDL_GetSurfaceProperties(SDL_Surface *surface)
274{
275 if (!SDL_SurfaceValid(surface)) {
276 SDL_InvalidParamError("surface");
277 return 0;
278 }
279
280 if (!surface->props) {
281 surface->props = SDL_CreateProperties();
282 }
283 return surface->props;
284}
285
286bool SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace)
287{
288 if (!SDL_SurfaceValid(surface)) {
289 return SDL_InvalidParamError("surface");
290 }
291
292 surface->colorspace = colorspace;
293 return true;
294}
295
296SDL_Colorspace SDL_GetSurfaceColorspace(SDL_Surface *surface)
297{
298 if (!SDL_SurfaceValid(surface)) {
299 return SDL_COLORSPACE_UNKNOWN;
300 }
301
302 return surface->colorspace;
303}
304
305float SDL_GetDefaultSDRWhitePoint(SDL_Colorspace colorspace)
306{
307 return SDL_GetSurfaceSDRWhitePoint(NULL, colorspace);
308}
309
310float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace)
311{
312 SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace);
313
314 if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR ||
315 transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
316 SDL_PropertiesID props;
317 float default_value = 1.0f;
318
319 if (SDL_SurfaceValid(surface)) {
320 props = surface->props;
321 } else {
322 props = 0;
323 }
324 if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
325 /* The older standards use an SDR white point of 100 nits.
326 * ITU-R BT.2408-6 recommends using an SDR white point of 203 nits.
327 * This is the default Chrome uses, and what a lot of game content
328 * assumes, so we'll go with that.
329 */
330 const float DEFAULT_PQ_SDR_WHITE_POINT = 203.0f;
331 default_value = DEFAULT_PQ_SDR_WHITE_POINT;
332 }
333 return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, default_value);
334 }
335 return 1.0f;
336}
337
338float SDL_GetDefaultHDRHeadroom(SDL_Colorspace colorspace)
339{
340 return SDL_GetSurfaceHDRHeadroom(NULL, colorspace);
341}
342
343float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace)
344{
345 SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace);
346
347 if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR ||
348 transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) {
349 SDL_PropertiesID props;
350 float default_value = 0.0f;
351
352 if (SDL_SurfaceValid(surface)) {
353 props = surface->props;
354 } else {
355 props = 0;
356 }
357 return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value);
358 }
359 return 1.0f;
360}
361
362SDL_Palette *SDL_CreateSurfacePalette(SDL_Surface *surface)
363{
364 SDL_Palette *palette;
365
366 if (!SDL_SurfaceValid(surface)) {
367 SDL_InvalidParamError("surface");
368 return NULL;
369 }
370
371 if (!SDL_ISPIXELFORMAT_INDEXED(surface->format)) {
372 SDL_SetError("The surface is not indexed format");
373 return NULL;
374 }
375
376 palette = SDL_CreatePalette((1 << SDL_BITSPERPIXEL(surface->format)));
377 if (!palette) {
378 return NULL;
379 }
380
381 if (palette->ncolors == 2) {
382 // Create a black and white bitmap palette
383 palette->colors[0].r = 0xFF;
384 palette->colors[0].g = 0xFF;
385 palette->colors[0].b = 0xFF;
386 palette->colors[1].r = 0x00;
387 palette->colors[1].g = 0x00;
388 palette->colors[1].b = 0x00;
389 }
390
391 if (!SDL_SetSurfacePalette(surface, palette)) {
392 SDL_DestroyPalette(palette);
393 return NULL;
394 }
395
396 // The surface has retained the palette, we can remove the reference here
397 SDL_assert(palette->refcount == 2);
398 SDL_DestroyPalette(palette);
399 return palette;
400}
401
402bool SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette)
403{
404 if (!SDL_SurfaceValid(surface)) {
405 return SDL_InvalidParamError("surface");
406 }
407
408 if (palette && palette->ncolors > (1 << SDL_BITSPERPIXEL(surface->format))) {
409 return SDL_SetError("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format");
410 }
411
412 if (palette != surface->palette) {
413 if (surface->palette) {
414 SDL_DestroyPalette(surface->palette);
415 }
416
417 surface->palette = palette;
418
419 if (surface->palette) {
420 ++surface->palette->refcount;
421 }
422 }
423
424 SDL_InvalidateMap(&surface->map);
425
426 return true;
427}
428
429SDL_Palette *SDL_GetSurfacePalette(SDL_Surface *surface)
430{
431 if (!SDL_SurfaceValid(surface)) {
432 return NULL;
433 }
434
435 return surface->palette;
436}
437
438bool SDL_AddSurfaceAlternateImage(SDL_Surface *surface, SDL_Surface *image)
439{
440 if (!SDL_SurfaceValid(surface)) {
441 return SDL_InvalidParamError("surface");
442 }
443
444 if (!SDL_SurfaceValid(image)) {
445 return SDL_InvalidParamError("image");
446 }
447
448 SDL_Surface **images = (SDL_Surface **)SDL_realloc(surface->images, (surface->num_images + 1) * sizeof(*images));
449 if (!images) {
450 return false;
451 }
452 images[surface->num_images] = image;
453 surface->images = images;
454 ++surface->num_images;
455 ++image->refcount;
456 return true;
457}
458
459bool SDL_SurfaceHasAlternateImages(SDL_Surface *surface)
460{
461 if (!SDL_SurfaceValid(surface)) {
462 return false;
463 }
464
465 return (surface->num_images > 0);
466}
467
468SDL_Surface **SDL_GetSurfaceImages(SDL_Surface *surface, int *count)
469{
470 if (count) {
471 *count = 0;
472 }
473
474 if (!SDL_SurfaceValid(surface)) {
475 SDL_InvalidParamError("surface");
476 return NULL;
477 }
478
479 int num_images = 1 + surface->num_images;
480 SDL_Surface **images = (SDL_Surface **)SDL_malloc((num_images + 1) * sizeof(*images));
481 if (!images) {
482 return NULL;
483 }
484 images[0] = surface;
485 if (surface->num_images > 0) {
486 SDL_memcpy(&images[1], surface->images, (surface->num_images * sizeof(images[1])));
487 }
488 images[num_images] = NULL;
489
490 if (count) {
491 *count = num_images;
492 }
493 return images;
494}
495
496SDL_Surface *SDL_GetSurfaceImage(SDL_Surface *surface, float display_scale)
497{
498 if (!SDL_SurfaceValid(surface)) {
499 SDL_InvalidParamError("surface");
500 return NULL;
501 }
502
503 if (!SDL_SurfaceHasAlternateImages(surface)) {
504 ++surface->refcount;
505 return surface;
506 }
507
508 // This surface has high DPI images, pick the best one available, or scale one to the correct size
509 SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL);
510 if (!images) {
511 // Failure, fall back to the existing surface
512 ++surface->refcount;
513 return surface;
514 }
515
516 // Find closest image. Images that are larger than the
517 // desired size are preferred over images that are smaller.
518 SDL_Surface *closest = NULL;
519 int desired_w = (int)SDL_round(surface->w * display_scale);
520 int desired_h = (int)SDL_round(surface->h * display_scale);
521 int desired_size = desired_w * desired_h;
522 int closest_distance = -1;
523 int closest_size = -1;
524 for (int i = 0; images[i]; ++i) {
525 SDL_Surface *candidate = images[i];
526 int size = candidate->w * candidate->h;
527 int delta_w = (candidate->w - desired_w);
528 int delta_h = (candidate->h - desired_h);
529 int distance = (delta_w * delta_w) + (delta_h * delta_h);
530 if (closest_distance < 0 || distance < closest_distance ||
531 (size > desired_size && closest_size < desired_size)) {
532 closest = candidate;
533 closest_distance = distance;
534 closest_size = size;
535 }
536 }
537 SDL_free(images);
538 SDL_assert(closest != NULL); // We should always have at least one surface
539
540 if (closest->w == desired_w && closest->h == desired_h) {
541 ++closest->refcount;
542 return closest;
543 }
544
545 // We need to scale the image to the correct size. To maintain good image quality, downscaling
546 // is done in steps, never reducing the width and height by more than half each time.
547 SDL_Surface *scaled = closest;
548 do {
549 int next_scaled_w = SDL_max(desired_w, (scaled->w + 1) / 2);
550 int next_scaled_h = SDL_max(desired_h, (scaled->h + 1) / 2);
551 SDL_Surface *next_scaled = SDL_ScaleSurface(scaled, next_scaled_w, next_scaled_h, SDL_SCALEMODE_LINEAR);
552 if (scaled != closest) {
553 SDL_DestroySurface(scaled);
554 }
555 scaled = next_scaled;
556 if (!scaled) {
557 // Failure, fall back to the closest surface
558 ++closest->refcount;
559 return closest;
560 }
561 } while (scaled->w != desired_w || scaled->h != desired_h);
562
563 return scaled;
564}
565
566void SDL_RemoveSurfaceAlternateImages(SDL_Surface *surface)
567{
568 if (!SDL_SurfaceValid(surface)) {
569 return;
570 }
571
572 if (surface->num_images > 0) {
573 for (int i = 0; i < surface->num_images; ++i) {
574 SDL_DestroySurface(surface->images[i]);
575 }
576 SDL_free(surface->images);
577 surface->images = NULL;
578 surface->num_images = 0;
579 }
580}
581
582bool SDL_SetSurfaceRLE(SDL_Surface *surface, bool enabled)
583{
584 int flags;
585
586 if (!SDL_SurfaceValid(surface)) {
587 return SDL_InvalidParamError("surface");
588 }
589
590 flags = surface->map.info.flags;
591 if (enabled) {
592 surface->map.info.flags |= SDL_COPY_RLE_DESIRED;
593 } else {
594 surface->map.info.flags &= ~SDL_COPY_RLE_DESIRED;
595 }
596 if (surface->map.info.flags != flags) {
597 SDL_InvalidateMap(&surface->map);
598 }
599 SDL_UpdateSurfaceLockFlag(surface);
600 return true;
601}
602
603bool SDL_SurfaceHasRLE(SDL_Surface *surface)
604{
605 if (!SDL_SurfaceValid(surface)) {
606 return false;
607 }
608
609 if (!(surface->map.info.flags & SDL_COPY_RLE_DESIRED)) {
610 return false;
611 }
612
613 return true;
614}
615
616bool SDL_SetSurfaceColorKey(SDL_Surface *surface, bool enabled, Uint32 key)
617{
618 int flags;
619
620 if (!SDL_SurfaceValid(surface)) {
621 return SDL_InvalidParamError("surface");
622 }
623
624 if (surface->palette && key >= ((Uint32)surface->palette->ncolors)) {
625 return SDL_InvalidParamError("key");
626 }
627
628 flags = surface->map.info.flags;
629 if (enabled) {
630 surface->map.info.flags |= SDL_COPY_COLORKEY;
631 surface->map.info.colorkey = key;
632 } else {
633 surface->map.info.flags &= ~SDL_COPY_COLORKEY;
634 }
635 if (surface->map.info.flags != flags) {
636 SDL_InvalidateMap(&surface->map);
637 }
638
639 return true;
640}
641
642bool SDL_SurfaceHasColorKey(SDL_Surface *surface)
643{
644 if (!SDL_SurfaceValid(surface)) {
645 return false;
646 }
647
648 if (!(surface->map.info.flags & SDL_COPY_COLORKEY)) {
649 return false;
650 }
651
652 return true;
653}
654
655bool SDL_GetSurfaceColorKey(SDL_Surface *surface, Uint32 *key)
656{
657 if (key) {
658 *key = 0;
659 }
660
661 if (!SDL_SurfaceValid(surface)) {
662 return SDL_InvalidParamError("surface");
663 }
664
665 if (!(surface->map.info.flags & SDL_COPY_COLORKEY)) {
666 return SDL_SetError("Surface doesn't have a colorkey");
667 }
668
669 if (key) {
670 *key = surface->map.info.colorkey;
671 }
672 return true;
673}
674
675/* This is a fairly slow function to switch from colorkey to alpha
676 NB: it doesn't handle bpp 1 or 3, because they have no alpha channel */
677static void SDL_ConvertColorkeyToAlpha(SDL_Surface *surface, bool ignore_alpha)
678{
679 int x, y, bpp;
680
681 if (!SDL_SurfaceValid(surface)) {
682 return;
683 }
684
685 if (!(surface->map.info.flags & SDL_COPY_COLORKEY) ||
686 !SDL_ISPIXELFORMAT_ALPHA(surface->format)) {
687 return;
688 }
689
690 bpp = SDL_BYTESPERPIXEL(surface->format);
691
692 SDL_LockSurface(surface);
693
694 if (bpp == 2) {
695 Uint16 *row, *spot;
696 Uint16 ckey = (Uint16)surface->map.info.colorkey;
697 Uint16 mask = (Uint16)(~surface->fmt->Amask);
698
699 // Ignore, or not, alpha in colorkey comparison
700 if (ignore_alpha) {
701 ckey &= mask;
702 row = (Uint16 *)surface->pixels;
703 for (y = surface->h; y--;) {
704 spot = row;
705 for (x = surface->w; x--;) {
706 if ((*spot & mask) == ckey) {
707 *spot &= mask;
708 }
709 ++spot;
710 }
711 row += surface->pitch / 2;
712 }
713 } else {
714 row = (Uint16 *)surface->pixels;
715 for (y = surface->h; y--;) {
716 spot = row;
717 for (x = surface->w; x--;) {
718 if (*spot == ckey) {
719 *spot &= mask;
720 }
721 ++spot;
722 }
723 row += surface->pitch / 2;
724 }
725 }
726 } else if (bpp == 4) {
727 Uint32 *row, *spot;
728 Uint32 ckey = surface->map.info.colorkey;
729 Uint32 mask = ~surface->fmt->Amask;
730
731 // Ignore, or not, alpha in colorkey comparison
732 if (ignore_alpha) {
733 ckey &= mask;
734 row = (Uint32 *)surface->pixels;
735 for (y = surface->h; y--;) {
736 spot = row;
737 for (x = surface->w; x--;) {
738 if ((*spot & mask) == ckey) {
739 *spot &= mask;
740 }
741 ++spot;
742 }
743 row += surface->pitch / 4;
744 }
745 } else {
746 row = (Uint32 *)surface->pixels;
747 for (y = surface->h; y--;) {
748 spot = row;
749 for (x = surface->w; x--;) {
750 if (*spot == ckey) {
751 *spot &= mask;
752 }
753 ++spot;
754 }
755 row += surface->pitch / 4;
756 }
757 }
758 }
759
760 SDL_UnlockSurface(surface);
761
762 SDL_SetSurfaceColorKey(surface, false, 0);
763 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
764}
765
766bool SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
767{
768 int flags;
769
770 if (!SDL_SurfaceValid(surface)) {
771 return SDL_InvalidParamError("surface");
772 }
773
774 surface->map.info.r = r;
775 surface->map.info.g = g;
776 surface->map.info.b = b;
777
778 flags = surface->map.info.flags;
779 if (r != 0xFF || g != 0xFF || b != 0xFF) {
780 surface->map.info.flags |= SDL_COPY_MODULATE_COLOR;
781 } else {
782 surface->map.info.flags &= ~SDL_COPY_MODULATE_COLOR;
783 }
784 if (surface->map.info.flags != flags) {
785 SDL_InvalidateMap(&surface->map);
786 }
787 return true;
788}
789
790bool SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b)
791{
792 if (!SDL_SurfaceValid(surface)) {
793 if (r) {
794 *r = 255;
795 }
796 if (g) {
797 *g = 255;
798 }
799 if (b) {
800 *b = 255;
801 }
802 return SDL_InvalidParamError("surface");
803 }
804
805 if (r) {
806 *r = surface->map.info.r;
807 }
808 if (g) {
809 *g = surface->map.info.g;
810 }
811 if (b) {
812 *b = surface->map.info.b;
813 }
814 return true;
815}
816
817bool SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha)
818{
819 int flags;
820
821 if (!SDL_SurfaceValid(surface)) {
822 return SDL_InvalidParamError("surface");
823 }
824
825 surface->map.info.a = alpha;
826
827 flags = surface->map.info.flags;
828 if (alpha != 0xFF) {
829 surface->map.info.flags |= SDL_COPY_MODULATE_ALPHA;
830 } else {
831 surface->map.info.flags &= ~SDL_COPY_MODULATE_ALPHA;
832 }
833 if (surface->map.info.flags != flags) {
834 SDL_InvalidateMap(&surface->map);
835 }
836 return true;
837}
838
839bool SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha)
840{
841 if (!SDL_SurfaceValid(surface)) {
842 if (alpha) {
843 *alpha = 255;
844 }
845 return SDL_InvalidParamError("surface");
846 }
847
848 if (alpha) {
849 *alpha = surface->map.info.a;
850 }
851 return true;
852}
853
854bool SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode)
855{
856 int flags;
857 bool result = true;
858
859 if (!SDL_SurfaceValid(surface)) {
860 return SDL_InvalidParamError("surface");
861 }
862
863 if (blendMode == SDL_BLENDMODE_INVALID) {
864 return SDL_InvalidParamError("blendMode");
865 }
866
867 flags = surface->map.info.flags;
868 surface->map.info.flags &= ~SDL_COPY_BLEND_MASK;
869 switch (blendMode) {
870 case SDL_BLENDMODE_NONE:
871 break;
872 case SDL_BLENDMODE_BLEND:
873 surface->map.info.flags |= SDL_COPY_BLEND;
874 break;
875 case SDL_BLENDMODE_BLEND_PREMULTIPLIED:
876 surface->map.info.flags |= SDL_COPY_BLEND_PREMULTIPLIED;
877 break;
878 case SDL_BLENDMODE_ADD:
879 surface->map.info.flags |= SDL_COPY_ADD;
880 break;
881 case SDL_BLENDMODE_ADD_PREMULTIPLIED:
882 surface->map.info.flags |= SDL_COPY_ADD_PREMULTIPLIED;
883 break;
884 case SDL_BLENDMODE_MOD:
885 surface->map.info.flags |= SDL_COPY_MOD;
886 break;
887 case SDL_BLENDMODE_MUL:
888 surface->map.info.flags |= SDL_COPY_MUL;
889 break;
890 default:
891 result = SDL_Unsupported();
892 break;
893 }
894
895 if (surface->map.info.flags != flags) {
896 SDL_InvalidateMap(&surface->map);
897 }
898
899 return result;
900}
901
902bool SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode)
903{
904 if (blendMode) {
905 *blendMode = SDL_BLENDMODE_INVALID;
906 }
907
908 if (!SDL_SurfaceValid(surface)) {
909 return SDL_InvalidParamError("surface");
910 }
911
912 if (!blendMode) {
913 return true;
914 }
915
916 switch (surface->map.info.flags & SDL_COPY_BLEND_MASK) {
917 case SDL_COPY_BLEND:
918 *blendMode = SDL_BLENDMODE_BLEND;
919 break;
920 case SDL_COPY_BLEND_PREMULTIPLIED:
921 *blendMode = SDL_BLENDMODE_BLEND_PREMULTIPLIED;
922 break;
923 case SDL_COPY_ADD:
924 *blendMode = SDL_BLENDMODE_ADD;
925 break;
926 case SDL_COPY_ADD_PREMULTIPLIED:
927 *blendMode = SDL_BLENDMODE_ADD_PREMULTIPLIED;
928 break;
929 case SDL_COPY_MOD:
930 *blendMode = SDL_BLENDMODE_MOD;
931 break;
932 case SDL_COPY_MUL:
933 *blendMode = SDL_BLENDMODE_MUL;
934 break;
935 default:
936 *blendMode = SDL_BLENDMODE_NONE;
937 break;
938 }
939 return true;
940}
941
942bool SDL_SetSurfaceClipRect(SDL_Surface *surface, const SDL_Rect *rect)
943{
944 SDL_Rect full_rect;
945
946 // Don't do anything if there's no surface to act on
947 if (!SDL_SurfaceValid(surface)) {
948 return false;
949 }
950
951 // Set up the full surface rectangle
952 full_rect.x = 0;
953 full_rect.y = 0;
954 full_rect.w = surface->w;
955 full_rect.h = surface->h;
956
957 // Set the clipping rectangle
958 if (!rect) {
959 surface->clip_rect = full_rect;
960 return true;
961 }
962 return SDL_GetRectIntersection(rect, &full_rect, &surface->clip_rect);
963}
964
965bool SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect)
966{
967 if (!SDL_SurfaceValid(surface)) {
968 if (rect) {
969 SDL_zerop(rect);
970 }
971 return SDL_InvalidParamError("surface");
972 }
973 if (!rect) {
974 return SDL_InvalidParamError("rect");
975 }
976 *rect = surface->clip_rect;
977 return true;
978}
979
980/*
981 * Set up a blit between two surfaces -- split into three parts:
982 * The upper part, SDL_BlitSurface(), performs clipping and rectangle
983 * verification. The lower part is a pointer to a low level
984 * accelerated blitting function.
985 *
986 * These parts are separated out and each used internally by this
987 * library in the optimum places. They are exported so that if
988 * you know exactly what you are doing, you can optimize your code
989 * by calling the one(s) you need.
990 */
991bool SDL_BlitSurfaceUnchecked(SDL_Surface *src, const SDL_Rect *srcrect,
992 SDL_Surface *dst, const SDL_Rect *dstrect)
993{
994 // Check to make sure the blit mapping is valid
995 if (!SDL_ValidateMap(src, dst)) {
996 return false;
997 }
998 return src->map.blit(src, srcrect, dst, dstrect);
999}
1000
1001bool SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)
1002{
1003 SDL_Rect r_src, r_dst;
1004
1005 // Make sure the surfaces aren't locked
1006 if (!SDL_SurfaceValid(src)) {
1007 return SDL_InvalidParamError("src");
1008 } else if (!SDL_SurfaceValid(dst)) {
1009 return SDL_InvalidParamError("dst");
1010 } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
1011 return SDL_SetError("Surfaces must not be locked during blit");
1012 }
1013
1014 // Full src surface
1015 r_src.x = 0;
1016 r_src.y = 0;
1017 r_src.w = src->w;
1018 r_src.h = src->h;
1019
1020 if (dstrect) {
1021 r_dst.x = dstrect->x;
1022 r_dst.y = dstrect->y;
1023 } else {
1024 r_dst.x = 0;
1025 r_dst.y = 0;
1026 }
1027
1028 // clip the source rectangle to the source surface
1029 if (srcrect) {
1030 SDL_Rect tmp;
1031 if (SDL_GetRectIntersection(srcrect, &r_src, &tmp) == false) {
1032 return true;
1033 }
1034
1035 // Shift dstrect, if srcrect origin has changed
1036 r_dst.x += tmp.x - srcrect->x;
1037 r_dst.y += tmp.y - srcrect->y;
1038
1039 // Update srcrect
1040 r_src = tmp;
1041 }
1042
1043 // There're no dstrect.w/h parameters. It's the same as srcrect
1044 r_dst.w = r_src.w;
1045 r_dst.h = r_src.h;
1046
1047 // clip the destination rectangle against the clip rectangle
1048 {
1049 SDL_Rect tmp;
1050 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &tmp) == false) {
1051 return true;
1052 }
1053
1054 // Shift srcrect, if dstrect has changed
1055 r_src.x += tmp.x - r_dst.x;
1056 r_src.y += tmp.y - r_dst.y;
1057 r_src.w = tmp.w;
1058 r_src.h = tmp.h;
1059
1060 // Update dstrect
1061 r_dst = tmp;
1062 }
1063
1064 if (r_dst.w <= 0 || r_dst.h <= 0) {
1065 // No-op.
1066 return true;
1067 }
1068
1069 // Switch back to a fast blit if we were previously stretching
1070 if (src->map.info.flags & SDL_COPY_NEAREST) {
1071 src->map.info.flags &= ~SDL_COPY_NEAREST;
1072 SDL_InvalidateMap(&src->map);
1073 }
1074
1075 return SDL_BlitSurfaceUnchecked(src, &r_src, dst, &r_dst);
1076}
1077
1078bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
1079{
1080 SDL_Rect *clip_rect;
1081 double src_x0, src_y0, src_x1, src_y1;
1082 double dst_x0, dst_y0, dst_x1, dst_y1;
1083 SDL_Rect final_src, final_dst;
1084 double scaling_w, scaling_h;
1085 int src_w, src_h;
1086 int dst_w, dst_h;
1087
1088 // Make sure the surfaces aren't locked
1089 if (!SDL_SurfaceValid(src)) {
1090 return SDL_InvalidParamError("src");
1091 } else if (!SDL_SurfaceValid(dst)) {
1092 return SDL_InvalidParamError("dst");
1093 } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
1094 return SDL_SetError("Surfaces must not be locked during blit");
1095 } else if (scaleMode != SDL_SCALEMODE_NEAREST &&
1096 scaleMode != SDL_SCALEMODE_LINEAR) {
1097 return SDL_InvalidParamError("scaleMode");
1098 }
1099
1100 if (!srcrect) {
1101 src_w = src->w;
1102 src_h = src->h;
1103 } else {
1104 src_w = srcrect->w;
1105 src_h = srcrect->h;
1106 }
1107
1108 if (!dstrect) {
1109 dst_w = dst->w;
1110 dst_h = dst->h;
1111 } else {
1112 dst_w = dstrect->w;
1113 dst_h = dstrect->h;
1114 }
1115
1116 if (dst_w == src_w && dst_h == src_h) {
1117 // No scaling, defer to regular blit
1118 return SDL_BlitSurface(src, srcrect, dst, dstrect);
1119 }
1120
1121 scaling_w = (double)dst_w / src_w;
1122 scaling_h = (double)dst_h / src_h;
1123
1124 if (!dstrect) {
1125 dst_x0 = 0;
1126 dst_y0 = 0;
1127 dst_x1 = dst_w;
1128 dst_y1 = dst_h;
1129 } else {
1130 dst_x0 = dstrect->x;
1131 dst_y0 = dstrect->y;
1132 dst_x1 = dst_x0 + dst_w;
1133 dst_y1 = dst_y0 + dst_h;
1134 }
1135
1136 if (!srcrect) {
1137 src_x0 = 0;
1138 src_y0 = 0;
1139 src_x1 = src_w;
1140 src_y1 = src_h;
1141 } else {
1142 src_x0 = srcrect->x;
1143 src_y0 = srcrect->y;
1144 src_x1 = src_x0 + src_w;
1145 src_y1 = src_y0 + src_h;
1146
1147 // Clip source rectangle to the source surface
1148
1149 if (src_x0 < 0) {
1150 dst_x0 -= src_x0 * scaling_w;
1151 src_x0 = 0;
1152 }
1153
1154 if (src_x1 > src->w) {
1155 dst_x1 -= (src_x1 - src->w) * scaling_w;
1156 src_x1 = src->w;
1157 }
1158
1159 if (src_y0 < 0) {
1160 dst_y0 -= src_y0 * scaling_h;
1161 src_y0 = 0;
1162 }
1163
1164 if (src_y1 > src->h) {
1165 dst_y1 -= (src_y1 - src->h) * scaling_h;
1166 src_y1 = src->h;
1167 }
1168 }
1169
1170 // Clip destination rectangle to the clip rectangle
1171 clip_rect = &dst->clip_rect;
1172
1173 // Translate to clip space for easier calculations
1174 dst_x0 -= clip_rect->x;
1175 dst_x1 -= clip_rect->x;
1176 dst_y0 -= clip_rect->y;
1177 dst_y1 -= clip_rect->y;
1178
1179 if (dst_x0 < 0) {
1180 src_x0 -= dst_x0 / scaling_w;
1181 dst_x0 = 0;
1182 }
1183
1184 if (dst_x1 > clip_rect->w) {
1185 src_x1 -= (dst_x1 - clip_rect->w) / scaling_w;
1186 dst_x1 = clip_rect->w;
1187 }
1188
1189 if (dst_y0 < 0) {
1190 src_y0 -= dst_y0 / scaling_h;
1191 dst_y0 = 0;
1192 }
1193
1194 if (dst_y1 > clip_rect->h) {
1195 src_y1 -= (dst_y1 - clip_rect->h) / scaling_h;
1196 dst_y1 = clip_rect->h;
1197 }
1198
1199 // Translate back to surface coordinates
1200 dst_x0 += clip_rect->x;
1201 dst_x1 += clip_rect->x;
1202 dst_y0 += clip_rect->y;
1203 dst_y1 += clip_rect->y;
1204
1205 final_src.x = (int)SDL_round(src_x0);
1206 final_src.y = (int)SDL_round(src_y0);
1207 final_src.w = (int)SDL_round(src_x1 - src_x0);
1208 final_src.h = (int)SDL_round(src_y1 - src_y0);
1209
1210 final_dst.x = (int)SDL_round(dst_x0);
1211 final_dst.y = (int)SDL_round(dst_y0);
1212 final_dst.w = (int)SDL_round(dst_x1 - dst_x0);
1213 final_dst.h = (int)SDL_round(dst_y1 - dst_y0);
1214
1215 // Clip again
1216 {
1217 SDL_Rect tmp;
1218 tmp.x = 0;
1219 tmp.y = 0;
1220 tmp.w = src->w;
1221 tmp.h = src->h;
1222 SDL_GetRectIntersection(&tmp, &final_src, &final_src);
1223 }
1224
1225 // Clip again
1226 SDL_GetRectIntersection(clip_rect, &final_dst, &final_dst);
1227
1228 if (final_dst.w == 0 || final_dst.h == 0 ||
1229 final_src.w < 0 || final_src.h < 0) {
1230 // No-op.
1231 return true;
1232 }
1233
1234 return SDL_BlitSurfaceUncheckedScaled(src, &final_src, dst, &final_dst, scaleMode);
1235}
1236
1237/**
1238 * This is a semi-private blit function and it performs low-level surface
1239 * scaled blitting only.
1240 */
1241bool SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
1242{
1243 static const Uint32 complex_copy_flags = (SDL_COPY_MODULATE_MASK | SDL_COPY_BLEND_MASK | SDL_COPY_COLORKEY);
1244
1245 if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 ||
1246 dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) {
1247 return SDL_SetError("Size too large for scaling");
1248 }
1249
1250 if (!(src->map.info.flags & SDL_COPY_NEAREST)) {
1251 src->map.info.flags |= SDL_COPY_NEAREST;
1252 SDL_InvalidateMap(&src->map);
1253 }
1254
1255 if (scaleMode == SDL_SCALEMODE_NEAREST) {
1256 if (!(src->map.info.flags & complex_copy_flags) &&
1257 src->format == dst->format &&
1258 !SDL_ISPIXELFORMAT_INDEXED(src->format) &&
1259 SDL_BYTESPERPIXEL(src->format) <= 4) {
1260 return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST);
1261 } else if (SDL_BITSPERPIXEL(src->format) < 8) {
1262 // Scaling bitmap not yet supported, convert to RGBA for blit
1263 bool result = false;
1264 SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888);
1265 if (tmp) {
1266 result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST);
1267 SDL_DestroySurface(tmp);
1268 }
1269 return result;
1270 } else {
1271 return SDL_BlitSurfaceUnchecked(src, srcrect, dst, dstrect);
1272 }
1273 } else {
1274 if (!(src->map.info.flags & complex_copy_flags) &&
1275 src->format == dst->format &&
1276 !SDL_ISPIXELFORMAT_INDEXED(src->format) &&
1277 SDL_BYTESPERPIXEL(src->format) == 4 &&
1278 src->format != SDL_PIXELFORMAT_ARGB2101010) {
1279 // fast path
1280 return SDL_StretchSurface(src, srcrect, dst, dstrect, SDL_SCALEMODE_LINEAR);
1281 } else if (SDL_BITSPERPIXEL(src->format) < 8) {
1282 // Scaling bitmap not yet supported, convert to RGBA for blit
1283 bool result = false;
1284 SDL_Surface *tmp = SDL_ConvertSurface(src, SDL_PIXELFORMAT_ARGB8888);
1285 if (tmp) {
1286 result = SDL_BlitSurfaceUncheckedScaled(tmp, srcrect, dst, dstrect, scaleMode);
1287 SDL_DestroySurface(tmp);
1288 }
1289 return result;
1290 } else {
1291 // Use intermediate surface(s)
1292 SDL_Surface *tmp1 = NULL;
1293 bool result;
1294 SDL_Rect srcrect2;
1295 int is_complex_copy_flags = (src->map.info.flags & complex_copy_flags);
1296
1297 Uint8 r, g, b;
1298 Uint8 alpha;
1299 SDL_BlendMode blendMode;
1300
1301 // Save source infos
1302 SDL_GetSurfaceColorMod(src, &r, &g, &b);
1303 SDL_GetSurfaceAlphaMod(src, &alpha);
1304 SDL_GetSurfaceBlendMode(src, &blendMode);
1305 srcrect2.x = srcrect->x;
1306 srcrect2.y = srcrect->y;
1307 srcrect2.w = srcrect->w;
1308 srcrect2.h = srcrect->h;
1309
1310 // Change source format if not appropriate for scaling
1311 if (SDL_BYTESPERPIXEL(src->format) != 4 || src->format == SDL_PIXELFORMAT_ARGB2101010) {
1312 SDL_Rect tmprect;
1313 SDL_PixelFormat fmt;
1314 tmprect.x = 0;
1315 tmprect.y = 0;
1316 tmprect.w = src->w;
1317 tmprect.h = src->h;
1318 if (SDL_BYTESPERPIXEL(dst->format) == 4 && dst->format != SDL_PIXELFORMAT_ARGB2101010) {
1319 fmt = dst->format;
1320 } else {
1321 fmt = SDL_PIXELFORMAT_ARGB8888;
1322 }
1323 tmp1 = SDL_CreateSurface(src->w, src->h, fmt);
1324 SDL_BlitSurfaceUnchecked(src, srcrect, tmp1, &tmprect);
1325
1326 srcrect2.x = 0;
1327 srcrect2.y = 0;
1328 SDL_SetSurfaceColorMod(tmp1, r, g, b);
1329 SDL_SetSurfaceAlphaMod(tmp1, alpha);
1330 SDL_SetSurfaceBlendMode(tmp1, blendMode);
1331
1332 src = tmp1;
1333 }
1334
1335 // Intermediate scaling
1336 if (is_complex_copy_flags || src->format != dst->format) {
1337 SDL_Rect tmprect;
1338 SDL_Surface *tmp2 = SDL_CreateSurface(dstrect->w, dstrect->h, src->format);
1339 SDL_StretchSurface(src, &srcrect2, tmp2, NULL, SDL_SCALEMODE_LINEAR);
1340
1341 SDL_SetSurfaceColorMod(tmp2, r, g, b);
1342 SDL_SetSurfaceAlphaMod(tmp2, alpha);
1343 SDL_SetSurfaceBlendMode(tmp2, blendMode);
1344
1345 tmprect.x = 0;
1346 tmprect.y = 0;
1347 tmprect.w = dstrect->w;
1348 tmprect.h = dstrect->h;
1349 result = SDL_BlitSurfaceUnchecked(tmp2, &tmprect, dst, dstrect);
1350 SDL_DestroySurface(tmp2);
1351 } else {
1352 result = SDL_StretchSurface(src, &srcrect2, dst, dstrect, SDL_SCALEMODE_LINEAR);
1353 }
1354
1355 SDL_DestroySurface(tmp1);
1356 return result;
1357 }
1358 }
1359}
1360
1361bool SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)
1362{
1363 SDL_Rect r_src, r_dst;
1364
1365 // Make sure the surfaces aren't locked
1366 if (!SDL_SurfaceValid(src)) {
1367 return SDL_InvalidParamError("src");
1368 } else if (!SDL_SurfaceValid(dst)) {
1369 return SDL_InvalidParamError("dst");
1370 } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
1371 return SDL_SetError("Surfaces must not be locked during blit");
1372 }
1373
1374 // Full src surface
1375 r_src.x = 0;
1376 r_src.y = 0;
1377 r_src.w = src->w;
1378 r_src.h = src->h;
1379
1380 if (dstrect) {
1381 r_dst.x = dstrect->x;
1382 r_dst.y = dstrect->y;
1383 r_dst.w = dstrect->w;
1384 r_dst.h = dstrect->h;
1385 } else {
1386 r_dst.x = 0;
1387 r_dst.y = 0;
1388 r_dst.w = dst->w;
1389 r_dst.h = dst->h;
1390 }
1391
1392 // clip the source rectangle to the source surface
1393 if (srcrect) {
1394 if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) {
1395 return true;
1396 }
1397
1398 // For tiling we don't adjust the destination rectangle
1399 }
1400
1401 // clip the destination rectangle against the clip rectangle
1402 {
1403 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) {
1404 return true;
1405 }
1406
1407 // For tiling we don't adjust the source rectangle
1408 }
1409
1410 // Switch back to a fast blit if we were previously stretching
1411 if (src->map.info.flags & SDL_COPY_NEAREST) {
1412 src->map.info.flags &= ~SDL_COPY_NEAREST;
1413 SDL_InvalidateMap(&src->map);
1414 }
1415
1416 int rows = r_dst.h / r_src.h;
1417 int cols = r_dst.w / r_src.w;
1418 int remaining_w = r_dst.w % r_src.w;
1419 int remaining_h = r_dst.h % r_src.h;
1420 SDL_Rect curr_src, curr_dst;
1421
1422 SDL_copyp(&curr_src, &r_src);
1423 curr_dst.y = r_dst.y;
1424 curr_dst.w = r_src.w;
1425 curr_dst.h = r_src.h;
1426 for (int y = 0; y < rows; ++y) {
1427 curr_dst.x = r_dst.x;
1428 for (int x = 0; x < cols; ++x) {
1429 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) {
1430 return false;
1431 }
1432 curr_dst.x += curr_dst.w;
1433 }
1434 if (remaining_w) {
1435 curr_src.w = remaining_w;
1436 curr_dst.w = remaining_w;
1437 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) {
1438 return false;
1439 }
1440 curr_src.w = r_src.w;
1441 curr_dst.w = r_src.w;
1442 }
1443 curr_dst.y += curr_dst.h;
1444 }
1445 if (remaining_h) {
1446 curr_src.h = remaining_h;
1447 curr_dst.h = remaining_h;
1448 curr_dst.x = r_dst.x;
1449 for (int x = 0; x < cols; ++x) {
1450 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) {
1451 return false;
1452 }
1453 curr_dst.x += curr_dst.w;
1454 }
1455 if (remaining_w) {
1456 curr_src.w = remaining_w;
1457 curr_dst.w = remaining_w;
1458 if (!SDL_BlitSurfaceUnchecked(src, &curr_src, dst, &curr_dst)) {
1459 return false;
1460 }
1461 }
1462 }
1463 return true;
1464}
1465
1466bool SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect)
1467{
1468 SDL_Rect r_src, r_dst;
1469
1470 // Make sure the surfaces aren't locked
1471 if (!SDL_SurfaceValid(src)) {
1472 return SDL_InvalidParamError("src");
1473 } else if (!SDL_SurfaceValid(dst)) {
1474 return SDL_InvalidParamError("dst");
1475 } else if ((src->flags & SDL_SURFACE_LOCKED) || (dst->flags & SDL_SURFACE_LOCKED)) {
1476 return SDL_SetError("Surfaces must not be locked during blit");
1477 }
1478
1479 if (scale <= 0.0f) {
1480 return SDL_InvalidParamError("scale");
1481 }
1482
1483 // Full src surface
1484 r_src.x = 0;
1485 r_src.y = 0;
1486 r_src.w = src->w;
1487 r_src.h = src->h;
1488
1489 if (dstrect) {
1490 r_dst.x = dstrect->x;
1491 r_dst.y = dstrect->y;
1492 r_dst.w = dstrect->w;
1493 r_dst.h = dstrect->h;
1494 } else {
1495 r_dst.x = 0;
1496 r_dst.y = 0;
1497 r_dst.w = dst->w;
1498 r_dst.h = dst->h;
1499 }
1500
1501 // clip the source rectangle to the source surface
1502 if (srcrect) {
1503 if (SDL_GetRectIntersection(srcrect, &r_src, &r_src) == false) {
1504 return true;
1505 }
1506
1507 // For tiling we don't adjust the destination rectangle
1508 }
1509
1510 // clip the destination rectangle against the clip rectangle
1511 {
1512 if (SDL_GetRectIntersection(&r_dst, &dst->clip_rect, &r_dst) == false) {
1513 return true;
1514 }
1515
1516 // For tiling we don't adjust the source rectangle
1517 }
1518
1519 // Switch back to a fast blit if we were previously stretching
1520 if (src->map.info.flags & SDL_COPY_NEAREST) {
1521 src->map.info.flags &= ~SDL_COPY_NEAREST;
1522 SDL_InvalidateMap(&src->map);
1523 }
1524
1525 int tile_width = (int)(r_src.w * scale);
1526 int tile_height = (int)(r_src.h * scale);
1527 int rows = r_dst.h / tile_height;
1528 int cols = r_dst.w / tile_width;
1529 int remaining_dst_w = (r_dst.w - cols * tile_width);
1530 int remaining_dst_h = (r_dst.h - rows * tile_height);
1531 int remaining_src_w = (int)(remaining_dst_w / scale);
1532 int remaining_src_h = (int)(remaining_dst_h / scale);
1533 SDL_Rect curr_src, curr_dst;
1534
1535 SDL_copyp(&curr_src, &r_src);
1536 curr_dst.y = r_dst.y;
1537 curr_dst.w = tile_width;
1538 curr_dst.h = tile_height;
1539 for (int y = 0; y < rows; ++y) {
1540 curr_dst.x = r_dst.x;
1541 for (int x = 0; x < cols; ++x) {
1542 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1543 return false;
1544 }
1545 curr_dst.x += curr_dst.w;
1546 }
1547 if (remaining_dst_w > 0) {
1548 curr_src.w = remaining_src_w;
1549 curr_dst.w = remaining_dst_w;
1550 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1551 return false;
1552 }
1553 curr_src.w = r_src.w;
1554 curr_dst.w = tile_width;
1555 }
1556 curr_dst.y += curr_dst.h;
1557 }
1558 if (remaining_dst_h > 0) {
1559 curr_src.h = remaining_src_h;
1560 curr_dst.h = remaining_dst_h;
1561 curr_dst.x = r_dst.x;
1562 for (int x = 0; x < cols; ++x) {
1563 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1564 return false;
1565 }
1566 curr_dst.x += curr_dst.w;
1567 }
1568 if (remaining_dst_w > 0) {
1569 curr_src.w = remaining_src_w;
1570 curr_dst.w = remaining_dst_w;
1571 if (!SDL_BlitSurfaceUncheckedScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1572 return false;
1573 }
1574 }
1575 }
1576 return true;
1577}
1578
1579bool SDL_BlitSurface9Grid(SDL_Surface *src, const SDL_Rect *srcrect, int left_width, int right_width, int top_height, int bottom_height, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect)
1580{
1581 SDL_Rect full_src, full_dst;
1582 SDL_Rect curr_src, curr_dst;
1583 int dst_left_width;
1584 int dst_right_width;
1585 int dst_top_height;
1586 int dst_bottom_height;
1587
1588 // Make sure the surfaces aren't locked
1589 if (!SDL_SurfaceValid(src)) {
1590 return SDL_InvalidParamError("src");
1591 } else if (!SDL_SurfaceValid(dst)) {
1592 return SDL_InvalidParamError("dst");
1593 }
1594
1595 if (!srcrect) {
1596 full_src.x = 0;
1597 full_src.y = 0;
1598 full_src.w = src->w;
1599 full_src.h = src->h;
1600 srcrect = &full_src;
1601 }
1602
1603 if (!dstrect) {
1604 full_dst.x = 0;
1605 full_dst.y = 0;
1606 full_dst.w = dst->w;
1607 full_dst.h = dst->h;
1608 dstrect = &full_dst;
1609 }
1610
1611 if (scale <= 0.0f || scale == 1.0f) {
1612 dst_left_width = left_width;
1613 dst_right_width = right_width;
1614 dst_top_height = top_height;
1615 dst_bottom_height = bottom_height;
1616 } else {
1617 dst_left_width = (int)SDL_roundf(left_width * scale);
1618 dst_right_width = (int)SDL_roundf(right_width * scale);
1619 dst_top_height = (int)SDL_roundf(top_height * scale);
1620 dst_bottom_height = (int)SDL_roundf(bottom_height * scale);
1621 }
1622
1623 // Upper-left corner
1624 curr_src.x = srcrect->x;
1625 curr_src.y = srcrect->y;
1626 curr_src.w = left_width;
1627 curr_src.h = top_height;
1628 curr_dst.x = dstrect->x;
1629 curr_dst.y = dstrect->y;
1630 curr_dst.w = dst_left_width;
1631 curr_dst.h = dst_top_height;
1632 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1633 return false;
1634 }
1635
1636 // Upper-right corner
1637 curr_src.x = srcrect->x + srcrect->w - right_width;
1638 curr_src.w = right_width;
1639 curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
1640 curr_dst.w = dst_right_width;
1641 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1642 return false;
1643 }
1644
1645 // Lower-right corner
1646 curr_src.y = srcrect->y + srcrect->h - bottom_height;
1647 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
1648 curr_dst.h = dst_bottom_height;
1649 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1650 return false;
1651 }
1652
1653 // Lower-left corner
1654 curr_src.x = srcrect->x;
1655 curr_src.w = left_width;
1656 curr_dst.x = dstrect->x;
1657 curr_dst.w = dst_left_width;
1658 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1659 return false;
1660 }
1661
1662 // Left
1663 curr_src.y = srcrect->y + top_height;
1664 curr_src.h = srcrect->h - top_height - bottom_height;
1665 curr_dst.y = dstrect->y + dst_top_height;
1666 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
1667 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1668 return false;
1669 }
1670
1671 // Right
1672 curr_src.x = srcrect->x + srcrect->w - right_width;
1673 curr_src.w = right_width;
1674 curr_dst.x = dstrect->x + dstrect->w - dst_right_width;
1675 curr_dst.w = dst_right_width;
1676 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1677 return false;
1678 }
1679
1680 // Top
1681 curr_src.x = srcrect->x + left_width;
1682 curr_src.y = srcrect->y;
1683 curr_src.w = srcrect->w - left_width - right_width;
1684 curr_src.h = top_height;
1685 curr_dst.x = dstrect->x + dst_left_width;
1686 curr_dst.y = dstrect->y;
1687 curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
1688 curr_dst.h = dst_top_height;
1689 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1690 return false;
1691 }
1692
1693 // Bottom
1694 curr_src.y = srcrect->y + srcrect->h - bottom_height;
1695 curr_dst.y = dstrect->y + dstrect->h - dst_bottom_height;
1696 curr_dst.h = dst_bottom_height;
1697 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1698 return false;
1699 }
1700
1701 // Center
1702 curr_src.x = srcrect->x + left_width;
1703 curr_src.y = srcrect->y + top_height;
1704 curr_src.w = srcrect->w - left_width - right_width;
1705 curr_src.h = srcrect->h - top_height - bottom_height;
1706 curr_dst.x = dstrect->x + dst_left_width;
1707 curr_dst.y = dstrect->y + dst_top_height;
1708 curr_dst.w = dstrect->w - dst_left_width - dst_right_width;
1709 curr_dst.h = dstrect->h - dst_top_height - dst_bottom_height;
1710 if (!SDL_BlitSurfaceScaled(src, &curr_src, dst, &curr_dst, scaleMode)) {
1711 return false;
1712 }
1713
1714 return true;
1715}
1716
1717/*
1718 * Lock a surface to directly access the pixels
1719 */
1720bool SDL_LockSurface(SDL_Surface *surface)
1721{
1722 if (!SDL_SurfaceValid(surface)) {
1723 return SDL_InvalidParamError("surface");
1724 }
1725
1726 if (!surface->locked) {
1727#ifdef SDL_HAVE_RLE
1728 // Perform the lock
1729 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) {
1730 SDL_UnRLESurface(surface, true);
1731 surface->internal_flags |= SDL_INTERNAL_SURFACE_RLEACCEL; // save accel'd state
1732 }
1733#endif
1734 }
1735
1736 // Increment the surface lock count, for recursive locks
1737 ++surface->locked;
1738 surface->flags |= SDL_SURFACE_LOCKED;
1739
1740 // Ready to go..
1741 return true;
1742}
1743
1744/*
1745 * Unlock a previously locked surface
1746 */
1747void SDL_UnlockSurface(SDL_Surface *surface)
1748{
1749 if (!SDL_SurfaceValid(surface)) {
1750 return;
1751 }
1752
1753 // Only perform an unlock if we are locked
1754 if (!surface->locked || (--surface->locked > 0)) {
1755 return;
1756 }
1757
1758#ifdef SDL_HAVE_RLE
1759 // Update RLE encoded surface with new data
1760 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) {
1761 surface->internal_flags &= ~SDL_INTERNAL_SURFACE_RLEACCEL; // stop lying
1762 SDL_RLESurface(surface);
1763 }
1764#endif
1765
1766 surface->flags &= ~SDL_SURFACE_LOCKED;
1767}
1768
1769static bool SDL_FlipSurfaceHorizontal(SDL_Surface *surface)
1770{
1771 bool isstack;
1772 Uint8 *row, *a, *b, *tmp;
1773 int i, j, bpp;
1774
1775 if (SDL_BITSPERPIXEL(surface->format) < 8) {
1776 // We could implement this if needed, but we'd have to flip sets of bits within a byte
1777 return SDL_Unsupported();
1778 }
1779
1780 if (surface->h <= 0) {
1781 return true;
1782 }
1783
1784 if (surface->w <= 1) {
1785 return true;
1786 }
1787
1788 bpp = SDL_BYTESPERPIXEL(surface->format);
1789 row = (Uint8 *)surface->pixels;
1790 tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack);
1791 if (!tmp) {
1792 return false;
1793 }
1794 for (i = surface->h; i--; ) {
1795 a = row;
1796 b = a + (surface->w - 1) * bpp;
1797 for (j = surface->w / 2; j--; ) {
1798 SDL_memcpy(tmp, a, bpp);
1799 SDL_memcpy(a, b, bpp);
1800 SDL_memcpy(b, tmp, bpp);
1801 a += bpp;
1802 b -= bpp;
1803 }
1804 row += surface->pitch;
1805 }
1806 SDL_small_free(tmp, isstack);
1807 return true;
1808}
1809
1810static bool SDL_FlipSurfaceVertical(SDL_Surface *surface)
1811{
1812 bool isstack;
1813 Uint8 *a, *b, *tmp;
1814 int i;
1815
1816 if (surface->h <= 1) {
1817 return true;
1818 }
1819
1820 a = (Uint8 *)surface->pixels;
1821 b = a + (surface->h - 1) * surface->pitch;
1822 tmp = SDL_small_alloc(Uint8, surface->pitch, &isstack);
1823 if (!tmp) {
1824 return false;
1825 }
1826 for (i = surface->h / 2; i--; ) {
1827 SDL_memcpy(tmp, a, surface->pitch);
1828 SDL_memcpy(a, b, surface->pitch);
1829 SDL_memcpy(b, tmp, surface->pitch);
1830 a += surface->pitch;
1831 b -= surface->pitch;
1832 }
1833 SDL_small_free(tmp, isstack);
1834 return true;
1835}
1836
1837bool SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip)
1838{
1839 if (!SDL_SurfaceValid(surface)) {
1840 return SDL_InvalidParamError("surface");
1841 }
1842 if (!surface->pixels) {
1843 return true;
1844 }
1845
1846 switch (flip) {
1847 case SDL_FLIP_HORIZONTAL:
1848 return SDL_FlipSurfaceHorizontal(surface);
1849 case SDL_FLIP_VERTICAL:
1850 return SDL_FlipSurfaceVertical(surface);
1851 default:
1852 return SDL_InvalidParamError("flip");
1853 }
1854}
1855
1856SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props)
1857{
1858 SDL_Palette *temp_palette = NULL;
1859 SDL_Surface *convert = NULL;
1860 SDL_Colorspace src_colorspace;
1861 SDL_PropertiesID src_properties;
1862 Uint32 copy_flags;
1863 SDL_Color copy_color;
1864 SDL_Rect bounds;
1865 bool result;
1866 bool palette_ck_transform = false;
1867 Uint8 palette_ck_value = 0;
1868 Uint8 *palette_saved_alpha = NULL;
1869 int palette_saved_alpha_ncolors = 0;
1870
1871 if (!SDL_SurfaceValid(surface)) {
1872 SDL_InvalidParamError("surface");
1873 goto error;
1874 }
1875
1876 if (format == SDL_PIXELFORMAT_UNKNOWN) {
1877 SDL_InvalidParamError("format");
1878 goto error;
1879 }
1880
1881 // Check for empty destination palette! (results in empty image)
1882 if (palette) {
1883 int i;
1884 for (i = 0; i < palette->ncolors; ++i) {
1885 if ((palette->colors[i].r != 0xFF) || (palette->colors[i].g != 0xFF) || (palette->colors[i].b != 0xFF)) {
1886 break;
1887 }
1888 }
1889 if (i == palette->ncolors) {
1890 SDL_SetError("Empty destination palette");
1891 goto error;
1892 }
1893 } else if (SDL_ISPIXELFORMAT_INDEXED(format)) {
1894 // Create a dither palette for conversion
1895 temp_palette = SDL_CreatePalette(1 << SDL_BITSPERPIXEL(format));
1896 if (temp_palette) {
1897 SDL_DitherPalette(temp_palette);
1898 palette = temp_palette;
1899 }
1900 }
1901
1902 src_colorspace = surface->colorspace;
1903 src_properties = surface->props;
1904
1905 // Create a new surface with the desired format
1906 convert = SDL_CreateSurface(surface->w, surface->h, format);
1907 if (!convert) {
1908 goto error;
1909 }
1910 if (SDL_ISPIXELFORMAT_INDEXED(format)) {
1911 SDL_SetSurfacePalette(convert, palette);
1912 }
1913
1914 if (colorspace == SDL_COLORSPACE_UNKNOWN) {
1915 colorspace = src_colorspace;
1916 }
1917 SDL_SetSurfaceColorspace(convert, colorspace);
1918
1919 if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
1920 if (!SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format, colorspace, props, convert->pixels, convert->pitch)) {
1921 goto error;
1922 }
1923
1924 // Save the original copy flags
1925 copy_flags = surface->map.info.flags;
1926
1927 goto end;
1928 }
1929
1930 // Save the original copy flags
1931 copy_flags = surface->map.info.flags;
1932 copy_color.r = surface->map.info.r;
1933 copy_color.g = surface->map.info.g;
1934 copy_color.b = surface->map.info.b;
1935 copy_color.a = surface->map.info.a;
1936 surface->map.info.r = 0xFF;
1937 surface->map.info.g = 0xFF;
1938 surface->map.info.b = 0xFF;
1939 surface->map.info.a = 0xFF;
1940 surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY));
1941 SDL_InvalidateMap(&surface->map);
1942
1943 // Copy over the image data
1944 bounds.x = 0;
1945 bounds.y = 0;
1946 bounds.w = surface->w;
1947 bounds.h = surface->h;
1948
1949 /* Source surface has a palette with no real alpha (0 or OPAQUE).
1950 * Destination format has alpha.
1951 * -> set alpha channel to be opaque */
1952 if (surface->palette && SDL_ISPIXELFORMAT_ALPHA(format)) {
1953 bool set_opaque = false;
1954
1955 bool is_opaque, has_alpha_channel;
1956 SDL_DetectPalette(surface->palette, &is_opaque, &has_alpha_channel);
1957
1958 if (is_opaque) {
1959 if (!has_alpha_channel) {
1960 set_opaque = true;
1961 }
1962 }
1963
1964 // Set opaque and backup palette alpha values
1965 if (set_opaque) {
1966 int i;
1967 palette_saved_alpha_ncolors = surface->palette->ncolors;
1968 if (palette_saved_alpha_ncolors > 0) {
1969 palette_saved_alpha = SDL_stack_alloc(Uint8, palette_saved_alpha_ncolors);
1970 for (i = 0; i < palette_saved_alpha_ncolors; i++) {
1971 palette_saved_alpha[i] = surface->palette->colors[i].a;
1972 surface->palette->colors[i].a = SDL_ALPHA_OPAQUE;
1973 }
1974 }
1975 }
1976 }
1977
1978 // Transform colorkey to alpha. for cases where source palette has duplicate values, and colorkey is one of them
1979 if (copy_flags & SDL_COPY_COLORKEY) {
1980 if (surface->palette && !palette) {
1981 palette_ck_transform = true;
1982 palette_ck_value = surface->palette->colors[surface->map.info.colorkey].a;
1983 surface->palette->colors[surface->map.info.colorkey].a = SDL_ALPHA_TRANSPARENT;
1984 }
1985 }
1986
1987 result = SDL_BlitSurfaceUnchecked(surface, &bounds, convert, &bounds);
1988
1989 // Restore colorkey alpha value
1990 if (palette_ck_transform) {
1991 surface->palette->colors[surface->map.info.colorkey].a = palette_ck_value;
1992 }
1993
1994 // Restore palette alpha values
1995 if (palette_saved_alpha) {
1996 int i;
1997 for (i = 0; i < palette_saved_alpha_ncolors; i++) {
1998 surface->palette->colors[i].a = palette_saved_alpha[i];
1999 }
2000 SDL_stack_free(palette_saved_alpha);
2001 }
2002
2003 // Clean up the original surface, and update converted surface
2004 convert->map.info.r = copy_color.r;
2005 convert->map.info.g = copy_color.g;
2006 convert->map.info.b = copy_color.b;
2007 convert->map.info.a = copy_color.a;
2008 convert->map.info.flags =
2009 (copy_flags &
2010 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY |
2011 SDL_COPY_RLE_ALPHAKEY));
2012 surface->map.info.r = copy_color.r;
2013 surface->map.info.g = copy_color.g;
2014 surface->map.info.b = copy_color.b;
2015 surface->map.info.a = copy_color.a;
2016 surface->map.info.flags = copy_flags;
2017 SDL_InvalidateMap(&surface->map);
2018
2019 // SDL_BlitSurfaceUnchecked failed, and so the conversion
2020 if (!result) {
2021 goto error;
2022 }
2023
2024 if (copy_flags & SDL_COPY_COLORKEY) {
2025 bool set_colorkey_by_color = false;
2026 bool convert_colorkey = true;
2027
2028 if (surface->palette) {
2029 if (palette &&
2030 surface->palette->ncolors <= palette->ncolors &&
2031 (SDL_memcmp(surface->palette->colors, palette->colors,
2032 surface->palette->ncolors * sizeof(SDL_Color)) == 0)) {
2033 // The palette is identical, just set the same colorkey
2034 SDL_SetSurfaceColorKey(convert, true, surface->map.info.colorkey);
2035 } else if (!palette) {
2036 if (SDL_ISPIXELFORMAT_ALPHA(format)) {
2037 // No need to add the colorkey, transparency is in the alpha channel
2038 } else {
2039 // Only set the colorkey information
2040 set_colorkey_by_color = true;
2041 convert_colorkey = false;
2042 }
2043 } else {
2044 set_colorkey_by_color = true;
2045 }
2046 } else {
2047 set_colorkey_by_color = true;
2048 }
2049
2050 if (set_colorkey_by_color) {
2051 SDL_Surface *tmp;
2052 SDL_Surface *tmp2;
2053 int converted_colorkey = 0;
2054
2055 // Create a dummy surface to get the colorkey converted
2056 tmp = SDL_CreateSurface(1, 1, surface->format);
2057 if (!tmp) {
2058 goto error;
2059 }
2060
2061 // Share the palette, if any
2062 if (surface->palette) {
2063 SDL_SetSurfacePalette(tmp, surface->palette);
2064 }
2065
2066 SDL_FillSurfaceRect(tmp, NULL, surface->map.info.colorkey);
2067
2068 tmp->map.info.flags &= ~SDL_COPY_COLORKEY;
2069
2070 // Conversion of the colorkey
2071 tmp2 = SDL_ConvertSurfaceAndColorspace(tmp, format, palette, colorspace, props);
2072 if (!tmp2) {
2073 SDL_DestroySurface(tmp);
2074 goto error;
2075 }
2076
2077 // Get the converted colorkey
2078 SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->fmt->bytes_per_pixel);
2079
2080 SDL_DestroySurface(tmp);
2081 SDL_DestroySurface(tmp2);
2082
2083 // Set the converted colorkey on the new surface
2084 SDL_SetSurfaceColorKey(convert, true, converted_colorkey);
2085
2086 // This is needed when converting for 3D texture upload
2087 if (convert_colorkey) {
2088 SDL_ConvertColorkeyToAlpha(convert, true);
2089 }
2090 }
2091 }
2092
2093end:
2094 if (temp_palette) {
2095 SDL_DestroyPalette(temp_palette);
2096 }
2097
2098 SDL_SetSurfaceClipRect(convert, &surface->clip_rect);
2099
2100 /* Enable alpha blending by default if the new surface has an
2101 * alpha channel or alpha modulation */
2102 if (SDL_ISPIXELFORMAT_ALPHA(format) ||
2103 (copy_flags & SDL_COPY_MODULATE_ALPHA)) {
2104 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND);
2105 }
2106 if (copy_flags & SDL_COPY_RLE_DESIRED) {
2107 SDL_SetSurfaceRLE(convert, true);
2108 }
2109
2110 // Copy alternate images
2111 for (int i = 0; i < surface->num_images; ++i) {
2112 if (!SDL_AddSurfaceAlternateImage(convert, surface->images[i])) {
2113 goto error;
2114 }
2115 }
2116
2117 // We're ready to go!
2118 return convert;
2119
2120error:
2121 if (temp_palette) {
2122 SDL_DestroyPalette(temp_palette);
2123 }
2124 if (convert) {
2125 SDL_DestroySurface(convert);
2126 }
2127 return NULL;
2128}
2129
2130SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface)
2131{
2132 if (!SDL_SurfaceValid(surface)) {
2133 SDL_InvalidParamError("surface");
2134 return NULL;
2135 }
2136
2137 return SDL_ConvertSurfaceAndColorspace(surface, surface->format, surface->palette, surface->colorspace, surface->props);
2138}
2139
2140SDL_Surface *SDL_ScaleSurface(SDL_Surface *surface, int width, int height, SDL_ScaleMode scaleMode)
2141{
2142 SDL_Surface *convert = NULL;
2143 Uint32 copy_flags;
2144 SDL_Color copy_color;
2145 bool rc;
2146
2147 if (!SDL_SurfaceValid(surface)) {
2148 SDL_InvalidParamError("surface");
2149 goto error;
2150 }
2151
2152 if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2153 // We can't directly scale a YUV surface (yet!)
2154 SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888);
2155 if (!tmp) {
2156 return NULL;
2157 }
2158
2159 SDL_Surface *scaled = SDL_ScaleSurface(tmp, width, height, scaleMode);
2160 SDL_DestroySurface(tmp);
2161 if (!scaled) {
2162 return NULL;
2163 }
2164 tmp = scaled;
2165
2166 SDL_Surface *result = SDL_ConvertSurfaceAndColorspace(tmp, surface->format, NULL, surface->colorspace, surface->props);
2167 SDL_DestroySurface(tmp);
2168 return result;
2169 }
2170
2171 // Create a new surface with the desired size
2172 convert = SDL_CreateSurface(width, height, surface->format);
2173 if (!convert) {
2174 goto error;
2175 }
2176 SDL_SetSurfacePalette(convert, surface->palette);
2177 SDL_SetSurfaceColorspace(convert, surface->colorspace);
2178
2179 // Save the original copy flags
2180 copy_flags = surface->map.info.flags;
2181 copy_color.r = surface->map.info.r;
2182 copy_color.g = surface->map.info.g;
2183 copy_color.b = surface->map.info.b;
2184 copy_color.a = surface->map.info.a;
2185 surface->map.info.r = 0xFF;
2186 surface->map.info.g = 0xFF;
2187 surface->map.info.b = 0xFF;
2188 surface->map.info.a = 0xFF;
2189 surface->map.info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY));
2190 SDL_InvalidateMap(&surface->map);
2191
2192 rc = SDL_BlitSurfaceScaled(surface, NULL, convert, NULL, scaleMode);
2193
2194 // Clean up the original surface, and update converted surface
2195 convert->map.info.r = copy_color.r;
2196 convert->map.info.g = copy_color.g;
2197 convert->map.info.b = copy_color.b;
2198 convert->map.info.a = copy_color.a;
2199 convert->map.info.flags = (copy_flags & ~(SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY));
2200 surface->map.info.r = copy_color.r;
2201 surface->map.info.g = copy_color.g;
2202 surface->map.info.b = copy_color.b;
2203 surface->map.info.a = copy_color.a;
2204 surface->map.info.flags = copy_flags;
2205 SDL_InvalidateMap(&surface->map);
2206
2207 // SDL_BlitSurfaceScaled failed, and so the conversion
2208 if (!rc) {
2209 goto error;
2210 }
2211
2212 // We're ready to go!
2213 return convert;
2214
2215error:
2216 if (convert) {
2217 SDL_DestroySurface(convert);
2218 }
2219 return NULL;
2220}
2221
2222SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, SDL_PixelFormat format)
2223{
2224 if (!SDL_SurfaceValid(surface)) {
2225 SDL_InvalidParamError("surface");
2226 return NULL;
2227 }
2228
2229 return SDL_ConvertSurfaceAndColorspace(surface, format, NULL, SDL_GetDefaultColorspaceForFormat(format), surface->props);
2230}
2231
2232SDL_Surface *SDL_DuplicatePixels(int width, int height, SDL_PixelFormat format, SDL_Colorspace colorspace, void *pixels, int pitch)
2233{
2234 SDL_Surface *surface = SDL_CreateSurface(width, height, format);
2235 if (surface) {
2236 int length = width * SDL_BYTESPERPIXEL(format);
2237 Uint8 *src = (Uint8 *)pixels;
2238 Uint8 *dst = (Uint8 *)surface->pixels;
2239 int rows = height;
2240 while (rows--) {
2241 SDL_memcpy(dst, src, length);
2242 dst += surface->pitch;
2243 src += pitch;
2244 }
2245
2246 SDL_SetSurfaceColorspace(surface, colorspace);
2247 }
2248 return surface;
2249}
2250
2251bool SDL_ConvertPixelsAndColorspace(int width, int height,
2252 SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch,
2253 SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)
2254{
2255 SDL_Surface src_surface;
2256 SDL_Surface dst_surface;
2257 SDL_Rect rect;
2258 void *nonconst_src = (void *)src;
2259 bool result;
2260
2261 if (!src) {
2262 return SDL_InvalidParamError("src");
2263 }
2264 if (!src_pitch) {
2265 return SDL_InvalidParamError("src_pitch");
2266 }
2267 if (!dst) {
2268 return SDL_InvalidParamError("dst");
2269 }
2270 if (!dst_pitch) {
2271 return SDL_InvalidParamError("dst_pitch");
2272 }
2273
2274 if (src_colorspace == SDL_COLORSPACE_UNKNOWN) {
2275 src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format);
2276 }
2277 if (dst_colorspace == SDL_COLORSPACE_UNKNOWN) {
2278 dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format);
2279 }
2280
2281 if (src_format == SDL_PIXELFORMAT_MJPG) {
2282 return SDL_ConvertPixels_STB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
2283 }
2284
2285#ifdef SDL_HAVE_YUV
2286 if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
2287 return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
2288 } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
2289 return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
2290 } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
2291 return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch);
2292 }
2293#else
2294 if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
2295 return SDL_SetError("SDL not built with YUV support");
2296 }
2297#endif
2298
2299 // Fast path for same format copy
2300 if (src_format == dst_format && src_colorspace == dst_colorspace) {
2301 if (src_pitch == dst_pitch) {
2302 SDL_memcpy(dst, src, height * src_pitch);
2303 } else {
2304 int i;
2305 const int bpp = SDL_BYTESPERPIXEL(src_format);
2306 width *= bpp;
2307 for (i = height; i--;) {
2308 SDL_memcpy(dst, src, width);
2309 src = (const Uint8 *)src + src_pitch;
2310 dst = (Uint8 *)dst + dst_pitch;
2311 }
2312 }
2313 return true;
2314 }
2315
2316 if (!SDL_InitializeSurface(&src_surface, width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, true)) {
2317 return false;
2318 }
2319 SDL_SetSurfaceBlendMode(&src_surface, SDL_BLENDMODE_NONE);
2320
2321 if (!SDL_InitializeSurface(&dst_surface, width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, true)) {
2322 return false;
2323 }
2324
2325 // Set up the rect and go!
2326 rect.x = 0;
2327 rect.y = 0;
2328 rect.w = width;
2329 rect.h = height;
2330 result = SDL_BlitSurfaceUnchecked(&src_surface, &rect, &dst_surface, &rect);
2331
2332 SDL_DestroySurface(&src_surface);
2333 SDL_DestroySurface(&dst_surface);
2334
2335 return result;
2336}
2337
2338bool SDL_ConvertPixels(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch)
2339{
2340 return SDL_ConvertPixelsAndColorspace(width, height,
2341 src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch,
2342 dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch);
2343}
2344
2345/*
2346 * Premultiply the alpha on a block of pixels
2347 *
2348 * Here are some ideas for optimization:
2349 * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha
2350 * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data
2351 */
2352
2353static void SDL_PremultiplyAlpha_AXYZ8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2354{
2355 int c;
2356 Uint32 srcpixel;
2357 Uint32 srcR, srcG, srcB, srcA;
2358 Uint32 dstpixel;
2359 Uint32 dstR, dstG, dstB, dstA;
2360
2361 while (height--) {
2362 const Uint32 *src_px = (const Uint32 *)src;
2363 Uint32 *dst_px = (Uint32 *)dst;
2364 for (c = width; c; --c) {
2365 // Component bytes extraction.
2366 srcpixel = *src_px++;
2367 RGBA_FROM_ARGB8888(srcpixel, srcR, srcG, srcB, srcA);
2368
2369 // Alpha pre-multiplication of each component.
2370 dstA = srcA;
2371 dstR = (srcA * srcR) / 255;
2372 dstG = (srcA * srcG) / 255;
2373 dstB = (srcA * srcB) / 255;
2374
2375 // ARGB8888 pixel recomposition.
2376 ARGB8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA);
2377 *dst_px++ = dstpixel;
2378 }
2379 src = (const Uint8 *)src + src_pitch;
2380 dst = (Uint8 *)dst + dst_pitch;
2381 }
2382}
2383
2384static void SDL_PremultiplyAlpha_XYZA8888(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2385{
2386 int c;
2387 Uint32 srcpixel;
2388 Uint32 srcR, srcG, srcB, srcA;
2389 Uint32 dstpixel;
2390 Uint32 dstR, dstG, dstB, dstA;
2391
2392 while (height--) {
2393 const Uint32 *src_px = (const Uint32 *)src;
2394 Uint32 *dst_px = (Uint32 *)dst;
2395 for (c = width; c; --c) {
2396 // Component bytes extraction.
2397 srcpixel = *src_px++;
2398 RGBA_FROM_RGBA8888(srcpixel, srcR, srcG, srcB, srcA);
2399
2400 // Alpha pre-multiplication of each component.
2401 dstA = srcA;
2402 dstR = (srcA * srcR) / 255;
2403 dstG = (srcA * srcG) / 255;
2404 dstB = (srcA * srcB) / 255;
2405
2406 // RGBA8888 pixel recomposition.
2407 RGBA8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA);
2408 *dst_px++ = dstpixel;
2409 }
2410 src = (const Uint8 *)src + src_pitch;
2411 dst = (Uint8 *)dst + dst_pitch;
2412 }
2413}
2414
2415static void SDL_PremultiplyAlpha_AXYZ128(int width, int height, const void *src, int src_pitch, void *dst, int dst_pitch)
2416{
2417 int c;
2418 float flR, flG, flB, flA;
2419
2420 while (height--) {
2421 const float *src_px = (const float *)src;
2422 float *dst_px = (float *)dst;
2423 for (c = width; c; --c) {
2424 flA = *src_px++;
2425 flR = *src_px++;
2426 flG = *src_px++;
2427 flB = *src_px++;
2428
2429 // Alpha pre-multiplication of each component.
2430 flR *= flA;
2431 flG *= flA;
2432 flB *= flA;
2433
2434 *dst_px++ = flA;
2435 *dst_px++ = flR;
2436 *dst_px++ = flG;
2437 *dst_px++ = flB;
2438 }
2439 src = (const Uint8 *)src + src_pitch;
2440 dst = (Uint8 *)dst + dst_pitch;
2441 }
2442}
2443
2444static bool SDL_PremultiplyAlphaPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch, bool linear)
2445{
2446 SDL_Surface *convert = NULL;
2447 void *final_dst = dst;
2448 int final_dst_pitch = dst_pitch;
2449 SDL_PixelFormat format;
2450 SDL_Colorspace colorspace;
2451 bool result = false;
2452
2453 if (!src) {
2454 return SDL_InvalidParamError("src");
2455 }
2456 if (!src_pitch) {
2457 return SDL_InvalidParamError("src_pitch");
2458 }
2459 if (!dst) {
2460 return SDL_InvalidParamError("dst");
2461 }
2462 if (!dst_pitch) {
2463 return SDL_InvalidParamError("dst_pitch");
2464 }
2465
2466 // Use a high precision format if we're converting to linear colorspace or using high precision pixel formats
2467 if (linear ||
2468 SDL_ISPIXELFORMAT_10BIT(src_format) || SDL_BITSPERPIXEL(src_format) > 32 ||
2469 SDL_ISPIXELFORMAT_10BIT(dst_format) || SDL_BITSPERPIXEL(dst_format) > 32) {
2470 if (src_format == SDL_PIXELFORMAT_ARGB128_FLOAT ||
2471 src_format == SDL_PIXELFORMAT_ABGR128_FLOAT) {
2472 format = src_format;
2473 } else {
2474 format = SDL_PIXELFORMAT_ARGB128_FLOAT;
2475 }
2476 } else {
2477 if (src_format == SDL_PIXELFORMAT_ARGB8888 ||
2478 src_format == SDL_PIXELFORMAT_ABGR8888 ||
2479 src_format == SDL_PIXELFORMAT_RGBA8888 ||
2480 src_format == SDL_PIXELFORMAT_BGRA8888) {
2481 format = src_format;
2482 } else {
2483 format = SDL_PIXELFORMAT_ARGB8888;
2484 }
2485 }
2486 if (linear) {
2487 colorspace = SDL_COLORSPACE_SRGB_LINEAR;
2488 } else {
2489 colorspace = SDL_COLORSPACE_SRGB;
2490 }
2491
2492 if (src_format != format || src_colorspace != colorspace) {
2493 convert = SDL_CreateSurface(width, height, format);
2494 if (!convert) {
2495 goto done;
2496 }
2497 if (!SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, format, colorspace, 0, convert->pixels, convert->pitch)) {
2498 goto done;
2499 }
2500
2501 src = convert->pixels;
2502 src_pitch = convert->pitch;
2503 dst = convert->pixels;
2504 dst_pitch = convert->pitch;
2505
2506 } else if (dst_format != format || dst_colorspace != colorspace) {
2507 convert = SDL_CreateSurface(width, height, format);
2508 if (!convert) {
2509 goto done;
2510 }
2511 dst = convert->pixels;
2512 dst_pitch = convert->pitch;
2513 }
2514
2515 switch (format) {
2516 case SDL_PIXELFORMAT_ARGB8888:
2517 case SDL_PIXELFORMAT_ABGR8888:
2518 SDL_PremultiplyAlpha_AXYZ8888(width, height, src, src_pitch, dst, dst_pitch);
2519 break;
2520 case SDL_PIXELFORMAT_RGBA8888:
2521 case SDL_PIXELFORMAT_BGRA8888:
2522 SDL_PremultiplyAlpha_XYZA8888(width, height, src, src_pitch, dst, dst_pitch);
2523 break;
2524 case SDL_PIXELFORMAT_ARGB128_FLOAT:
2525 case SDL_PIXELFORMAT_ABGR128_FLOAT:
2526 SDL_PremultiplyAlpha_AXYZ128(width, height, src, src_pitch, dst, dst_pitch);
2527 break;
2528 default:
2529 SDL_SetError("Unexpected internal pixel format");
2530 goto done;
2531 }
2532
2533 if (dst != final_dst) {
2534 if (!SDL_ConvertPixelsAndColorspace(width, height, format, colorspace, 0, convert->pixels, convert->pitch, dst_format, dst_colorspace, dst_properties, final_dst, final_dst_pitch)) {
2535 goto done;
2536 }
2537 }
2538 result = true;
2539
2540done:
2541 if (convert) {
2542 SDL_DestroySurface(convert);
2543 }
2544 return result;
2545}
2546
2547bool SDL_PremultiplyAlpha(int width, int height,
2548 SDL_PixelFormat src_format, const void *src, int src_pitch,
2549 SDL_PixelFormat dst_format, void *dst, int dst_pitch, bool linear)
2550{
2551 SDL_Colorspace src_colorspace = SDL_GetDefaultColorspaceForFormat(src_format);
2552 SDL_Colorspace dst_colorspace = SDL_GetDefaultColorspaceForFormat(dst_format);
2553
2554 return SDL_PremultiplyAlphaPixelsAndColorspace(width, height, src_format, src_colorspace, 0, src, src_pitch, dst_format, dst_colorspace, 0, dst, dst_pitch, linear);
2555}
2556
2557bool SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, bool linear)
2558{
2559 SDL_Colorspace colorspace;
2560
2561 if (!SDL_SurfaceValid(surface)) {
2562 return SDL_InvalidParamError("surface");
2563 }
2564
2565 colorspace = surface->colorspace;
2566
2567 return SDL_PremultiplyAlphaPixelsAndColorspace(surface->w, surface->h, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, surface->format, colorspace, surface->props, surface->pixels, surface->pitch, linear);
2568}
2569
2570bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a)
2571{
2572 SDL_Rect clip_rect;
2573 bool result = false;
2574
2575 if (!SDL_SurfaceValid(surface)) {
2576 return SDL_InvalidParamError("surface");
2577 }
2578
2579 SDL_GetSurfaceClipRect(surface, &clip_rect);
2580 SDL_SetSurfaceClipRect(surface, NULL);
2581
2582 if (!SDL_ISPIXELFORMAT_FOURCC(surface->format) &&
2583 SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32)) {
2584 Uint32 color;
2585
2586 color = SDL_MapSurfaceRGBA(surface,
2587 (Uint8)SDL_roundf(SDL_clamp(r, 0.0f, 1.0f) * 255.0f),
2588 (Uint8)SDL_roundf(SDL_clamp(g, 0.0f, 1.0f) * 255.0f),
2589 (Uint8)SDL_roundf(SDL_clamp(b, 0.0f, 1.0f) * 255.0f),
2590 (Uint8)SDL_roundf(SDL_clamp(a, 0.0f, 1.0f) * 255.0f));
2591 result = SDL_FillSurfaceRect(surface, NULL, color);
2592 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2593 // We can't directly set an RGB value on a YUV surface
2594 SDL_Surface *tmp = SDL_CreateSurface(surface->w, surface->h, SDL_PIXELFORMAT_ARGB8888);
2595 if (!tmp) {
2596 goto done;
2597 }
2598
2599 if (SDL_ClearSurface(tmp, r, g, b, a)) {
2600 result = SDL_ConvertPixelsAndColorspace(surface->w, surface->h, tmp->format, tmp->colorspace, tmp->props, tmp->pixels, tmp->pitch, surface->format, surface->colorspace, surface->props, surface->pixels, surface->pitch);
2601 }
2602 SDL_DestroySurface(tmp);
2603 } else {
2604 // Take advantage of blit color conversion
2605 SDL_Surface *tmp = SDL_CreateSurface(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT);
2606 if (!tmp) {
2607 goto done;
2608 }
2609 SDL_SetSurfaceColorspace(tmp, surface->colorspace);
2610 SDL_SetSurfaceBlendMode(tmp, SDL_BLENDMODE_NONE);
2611
2612 float *pixels = (float *)tmp->pixels;
2613 pixels[0] = r;
2614 pixels[1] = g;
2615 pixels[2] = b;
2616 pixels[3] = a;
2617
2618 result = SDL_BlitSurfaceScaled(tmp, NULL, surface, NULL, SDL_SCALEMODE_NEAREST);
2619 SDL_DestroySurface(tmp);
2620 }
2621
2622done:
2623 SDL_SetSurfaceClipRect(surface, &clip_rect);
2624
2625 return result;
2626}
2627
2628Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)
2629{
2630 return SDL_MapSurfaceRGBA(surface, r, g, b, SDL_ALPHA_OPAQUE);
2631}
2632
2633Uint32 SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2634{
2635 if (!SDL_SurfaceValid(surface)) {
2636 SDL_InvalidParamError("surface");
2637 return true;
2638 }
2639 return SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a);
2640}
2641
2642// This function Copyright 2023 Collabora Ltd., contributed to SDL under the ZLib license
2643bool SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)
2644{
2645 Uint32 pixel = 0;
2646 size_t bytes_per_pixel;
2647 Uint8 unused;
2648 Uint8 *p;
2649 bool result = false;
2650
2651 if (r) {
2652 *r = 0;
2653 } else {
2654 r = &unused;
2655 }
2656
2657 if (g) {
2658 *g = 0;
2659 } else {
2660 g = &unused;
2661 }
2662
2663 if (b) {
2664 *b = 0;
2665 } else {
2666 b = &unused;
2667 }
2668
2669 if (a) {
2670 *a = 0;
2671 } else {
2672 a = &unused;
2673 }
2674
2675 if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
2676 return SDL_InvalidParamError("surface");
2677 }
2678
2679 if (x < 0 || x >= surface->w) {
2680 return SDL_InvalidParamError("x");
2681 }
2682
2683 if (y < 0 || y >= surface->h) {
2684 return SDL_InvalidParamError("y");
2685 }
2686
2687 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format);
2688
2689 if (SDL_MUSTLOCK(surface)) {
2690 if (!SDL_LockSurface(surface)) {
2691 return false;
2692 }
2693 }
2694
2695 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel;
2696
2697 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2698 /* Fill the appropriate number of least-significant bytes of pixel,
2699 * leaving the most-significant bytes set to zero */
2700#if SDL_BYTEORDER == SDL_BIG_ENDIAN
2701 SDL_memcpy(((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), p, bytes_per_pixel);
2702#else
2703 SDL_memcpy(&pixel, p, bytes_per_pixel);
2704#endif
2705 SDL_GetRGBA(pixel, surface->fmt, surface->palette, r, g, b, a);
2706 result = true;
2707 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2708 // FIXME: We need code to extract a single macroblock from a YUV surface
2709 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
2710 if (converted) {
2711 result = SDL_ReadSurfacePixel(converted, x, y, r, g, b, a);
2712 SDL_DestroySurface(converted);
2713 }
2714 } else {
2715 // This is really slow, but it gets the job done
2716 Uint8 rgba[4];
2717
2718 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, surface->colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba))) {
2719 *r = rgba[0];
2720 *g = rgba[1];
2721 *b = rgba[2];
2722 *a = rgba[3];
2723 result = true;
2724 }
2725 }
2726
2727 if (SDL_MUSTLOCK(surface)) {
2728 SDL_UnlockSurface(surface);
2729 }
2730 return result;
2731}
2732
2733bool SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a)
2734{
2735 float unused;
2736 bool result = false;
2737
2738 if (r) {
2739 *r = 0.0f;
2740 } else {
2741 r = &unused;
2742 }
2743
2744 if (g) {
2745 *g = 0.0f;
2746 } else {
2747 g = &unused;
2748 }
2749
2750 if (b) {
2751 *b = 0.0f;
2752 } else {
2753 b = &unused;
2754 }
2755
2756 if (a) {
2757 *a = 0.0f;
2758 } else {
2759 a = &unused;
2760 }
2761
2762 if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
2763 return SDL_InvalidParamError("surface");
2764 }
2765
2766 if (x < 0 || x >= surface->w) {
2767 return SDL_InvalidParamError("x");
2768 }
2769
2770 if (y < 0 || y >= surface->h) {
2771 return SDL_InvalidParamError("y");
2772 }
2773
2774 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2775 Uint8 r8, g8, b8, a8;
2776
2777 if (SDL_ReadSurfacePixel(surface, x, y, &r8, &g8, &b8, &a8)) {
2778 *r = (float)r8 / 255.0f;
2779 *g = (float)g8 / 255.0f;
2780 *b = (float)b8 / 255.0f;
2781 *a = (float)a8 / 255.0f;
2782 result = true;
2783 }
2784 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2785 // FIXME: We need code to extract a single macroblock from a YUV surface
2786 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_ARGB8888);
2787 if (converted) {
2788 result = SDL_ReadSurfacePixelFloat(converted, x, y, r, g, b, a);
2789 SDL_DestroySurface(converted);
2790 }
2791 } else {
2792 // This is really slow, but it gets the job done
2793 float rgba[4];
2794 Uint8 *p;
2795
2796 if (SDL_MUSTLOCK(surface)) {
2797 if (!SDL_LockSurface(surface)) {
2798 return false;
2799 }
2800 }
2801
2802 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format);
2803
2804 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) {
2805 SDL_memcpy(rgba, p, sizeof(rgba));
2806 result = true;
2807 } else {
2808 SDL_Colorspace src_colorspace = surface->colorspace;
2809 SDL_Colorspace dst_colorspace = (src_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB);
2810
2811 if (SDL_ConvertPixelsAndColorspace(1, 1, surface->format, src_colorspace, surface->props, p, surface->pitch, SDL_PIXELFORMAT_RGBA128_FLOAT, dst_colorspace, 0, rgba, sizeof(rgba))) {
2812 result = true;
2813 }
2814 }
2815
2816 if (result) {
2817 *r = rgba[0];
2818 *g = rgba[1];
2819 *b = rgba[2];
2820 *a = rgba[3];
2821 }
2822
2823 if (SDL_MUSTLOCK(surface)) {
2824 SDL_UnlockSurface(surface);
2825 }
2826 }
2827 return result;
2828}
2829
2830bool SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
2831{
2832 Uint32 pixel = 0;
2833 size_t bytes_per_pixel;
2834 Uint8 *p;
2835 bool result = false;
2836
2837 if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
2838 return SDL_InvalidParamError("surface");
2839 }
2840
2841 if (x < 0 || x >= surface->w) {
2842 return SDL_InvalidParamError("x");
2843 }
2844
2845 if (y < 0 || y >= surface->h) {
2846 return SDL_InvalidParamError("y");
2847 }
2848
2849 bytes_per_pixel = SDL_BYTESPERPIXEL(surface->format);
2850
2851 if (SDL_MUSTLOCK(surface)) {
2852 if (!SDL_LockSurface(surface)) {
2853 return false;
2854 }
2855 }
2856
2857 p = (Uint8 *)surface->pixels + y * surface->pitch + x * bytes_per_pixel;
2858
2859 if (bytes_per_pixel <= sizeof(pixel) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2860 pixel = SDL_MapRGBA(surface->fmt, surface->palette, r, g, b, a);
2861#if SDL_BYTEORDER == SDL_BIG_ENDIAN
2862 SDL_memcpy(p, ((Uint8 *)&pixel) + (sizeof(pixel) - bytes_per_pixel), bytes_per_pixel);
2863#else
2864 SDL_memcpy(p, &pixel, bytes_per_pixel);
2865#endif
2866 result = true;
2867 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2868 result = SDL_Unsupported();
2869 } else {
2870 // This is really slow, but it gets the job done
2871 Uint8 rgba[4];
2872
2873 rgba[0] = r;
2874 rgba[1] = g;
2875 rgba[2] = b;
2876 rgba[3] = a;
2877 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba), surface->format, surface->colorspace, surface->props, p, surface->pitch);
2878 }
2879
2880 if (SDL_MUSTLOCK(surface)) {
2881 SDL_UnlockSurface(surface);
2882 }
2883 return result;
2884}
2885
2886bool SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a)
2887{
2888 bool result = false;
2889
2890 if (!SDL_SurfaceValid(surface) || !surface->format || !surface->pixels) {
2891 return SDL_InvalidParamError("surface");
2892 }
2893
2894 if (x < 0 || x >= surface->w) {
2895 return SDL_InvalidParamError("x");
2896 }
2897
2898 if (y < 0 || y >= surface->h) {
2899 return SDL_InvalidParamError("y");
2900 }
2901
2902 if (SDL_BYTESPERPIXEL(surface->format) <= sizeof(Uint32) && !SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2903 Uint8 r8, g8, b8, a8;
2904
2905 r8 = (Uint8)SDL_round(SDL_clamp(r, 0.0f, 1.0f) * 255.0f);
2906 g8 = (Uint8)SDL_round(SDL_clamp(g, 0.0f, 1.0f) * 255.0f);
2907 b8 = (Uint8)SDL_round(SDL_clamp(b, 0.0f, 1.0f) * 255.0f);
2908 a8 = (Uint8)SDL_round(SDL_clamp(a, 0.0f, 1.0f) * 255.0f);
2909 if (SDL_WriteSurfacePixel(surface, x, y, r8, g8, b8, a8)) {
2910 result = true;
2911 }
2912 } else if (SDL_ISPIXELFORMAT_FOURCC(surface->format)) {
2913 result = SDL_Unsupported();
2914 } else {
2915 // This is really slow, but it gets the job done
2916 float rgba[4];
2917 Uint8 *p;
2918
2919 if (SDL_MUSTLOCK(surface)) {
2920 if (!SDL_LockSurface(surface)) {
2921 return false;
2922 }
2923 }
2924
2925 p = (Uint8 *)surface->pixels + y * surface->pitch + x * SDL_BYTESPERPIXEL(surface->format);
2926
2927 rgba[0] = r;
2928 rgba[1] = g;
2929 rgba[2] = b;
2930 rgba[3] = a;
2931
2932 if (surface->format == SDL_PIXELFORMAT_RGBA128_FLOAT) {
2933 SDL_memcpy(p, rgba, sizeof(rgba));
2934 result = true;
2935 } else {
2936 SDL_Colorspace dst_colorspace = surface->colorspace;
2937 SDL_Colorspace src_colorspace = (dst_colorspace == SDL_COLORSPACE_SRGB_LINEAR ? SDL_COLORSPACE_SRGB_LINEAR : SDL_COLORSPACE_SRGB);
2938
2939 result = SDL_ConvertPixelsAndColorspace(1, 1, SDL_PIXELFORMAT_RGBA128_FLOAT, src_colorspace, 0, rgba, sizeof(rgba), surface->format, dst_colorspace, surface->props, p, surface->pitch);
2940 }
2941
2942 if (SDL_MUSTLOCK(surface)) {
2943 SDL_UnlockSurface(surface);
2944 }
2945 }
2946 return result;
2947}
2948
2949/*
2950 * Free a surface created by the above function.
2951 */
2952void SDL_DestroySurface(SDL_Surface *surface)
2953{
2954 if (!SDL_SurfaceValid(surface)) {
2955 return;
2956 }
2957 if (surface->internal_flags & SDL_INTERNAL_SURFACE_DONTFREE) {
2958 return;
2959 }
2960 if (--surface->refcount > 0) {
2961 return;
2962 }
2963
2964 SDL_RemoveSurfaceAlternateImages(surface);
2965
2966 SDL_DestroyProperties(surface->props);
2967
2968 SDL_InvalidateMap(&surface->map);
2969
2970 while (surface->locked > 0) {
2971 SDL_UnlockSurface(surface);
2972 }
2973#ifdef SDL_HAVE_RLE
2974 if (surface->internal_flags & SDL_INTERNAL_SURFACE_RLEACCEL) {
2975 SDL_UnRLESurface(surface, false);
2976 }
2977#endif
2978 SDL_SetSurfacePalette(surface, NULL);
2979
2980 if (surface->flags & SDL_SURFACE_PREALLOCATED) {
2981 // Don't free
2982 } else if (surface->flags & SDL_SURFACE_SIMD_ALIGNED) {
2983 // Free aligned
2984 SDL_aligned_free(surface->pixels);
2985 } else {
2986 // Normal
2987 SDL_free(surface->pixels);
2988 }
2989
2990 surface->reserved = NULL;
2991
2992 if (!(surface->internal_flags & SDL_INTERNAL_SURFACE_STACK)) {
2993 SDL_free(surface);
2994 }
2995}