1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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_video.h"
24 #include "SDL_sysvideo.h"
25 #include "SDL_blit.h"
26 #include "SDL_RLEaccel_c.h"
27 #include "SDL_pixels_c.h"
28 #include "SDL_yuv_c.h"
29 #include "../render/SDL_sysrender.h"
30
31
32 /* Check to make sure we can safely check multiplication of surface w and pitch and it won't overflow size_t */
33 SDL_COMPILE_TIME_ASSERT(surface_size_assumptions,
34 sizeof(int) == sizeof(Sint32) && sizeof(size_t) >= sizeof(Sint32));
35
36 /* Public routines */
37
38 /*
39 * Calculate the pad-aligned scanline width of a surface
40 */
41 static Sint64
SDL_CalculatePitch(Uint32 format,int width)42 SDL_CalculatePitch(Uint32 format, int width)
43 {
44 Sint64 pitch;
45
46 if (SDL_ISPIXELFORMAT_FOURCC(format) || SDL_BITSPERPIXEL(format) >= 8) {
47 pitch = ((Sint64)width * SDL_BYTESPERPIXEL(format));
48 } else {
49 pitch = (((Sint64)width * SDL_BITSPERPIXEL(format)) + 7) / 8;
50 }
51 pitch = (pitch + 3) & ~3; /* 4-byte aligning for speed */
52 return pitch;
53 }
54
55 /*
56 * Create an empty RGB surface of the appropriate depth using the given
57 * enum SDL_PIXELFORMAT_* format
58 */
59 SDL_Surface *
SDL_CreateRGBSurfaceWithFormat(Uint32 flags,int width,int height,int depth,Uint32 format)60 SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth,
61 Uint32 format)
62 {
63 Sint64 pitch;
64 SDL_Surface *surface;
65
66 /* The flags are no longer used, make the compiler happy */
67 (void)flags;
68
69 pitch = SDL_CalculatePitch(format, width);
70 if (pitch < 0 || pitch > SDL_MAX_SINT32) {
71 /* Overflow... */
72 SDL_OutOfMemory();
73 return NULL;
74 }
75
76 /* Allocate the surface */
77 surface = (SDL_Surface *) SDL_calloc(1, sizeof(*surface));
78 if (surface == NULL) {
79 SDL_OutOfMemory();
80 return NULL;
81 }
82
83 surface->format = SDL_AllocFormat(format);
84 if (!surface->format) {
85 SDL_FreeSurface(surface);
86 return NULL;
87 }
88 surface->w = width;
89 surface->h = height;
90 surface->pitch = (int)pitch;
91 SDL_SetClipRect(surface, NULL);
92
93 if (SDL_ISPIXELFORMAT_INDEXED(surface->format->format)) {
94 SDL_Palette *palette =
95 SDL_AllocPalette((1 << surface->format->BitsPerPixel));
96 if (!palette) {
97 SDL_FreeSurface(surface);
98 return NULL;
99 }
100 if (palette->ncolors == 2) {
101 /* Create a black and white bitmap palette */
102 palette->colors[0].r = 0xFF;
103 palette->colors[0].g = 0xFF;
104 palette->colors[0].b = 0xFF;
105 palette->colors[1].r = 0x00;
106 palette->colors[1].g = 0x00;
107 palette->colors[1].b = 0x00;
108 }
109 SDL_SetSurfacePalette(surface, palette);
110 SDL_FreePalette(palette);
111 }
112
113 /* Get the pixels */
114 if (surface->w && surface->h) {
115 /* Assumptions checked in surface_size_assumptions assert above */
116 Sint64 size = ((Sint64)surface->h * surface->pitch);
117 if (size < 0 || size > SDL_MAX_SINT32) {
118 /* Overflow... */
119 SDL_FreeSurface(surface);
120 SDL_OutOfMemory();
121 return NULL;
122 }
123
124 surface->pixels = SDL_SIMDAlloc((size_t)size);
125 if (!surface->pixels) {
126 SDL_FreeSurface(surface);
127 SDL_OutOfMemory();
128 return NULL;
129 }
130 surface->flags |= SDL_SIMD_ALIGNED;
131 /* This is important for bitmaps */
132 SDL_memset(surface->pixels, 0, surface->h * surface->pitch);
133 }
134
135 /* Allocate an empty mapping */
136 surface->map = SDL_AllocBlitMap();
137 if (!surface->map) {
138 SDL_FreeSurface(surface);
139 return NULL;
140 }
141
142 /* By default surface with an alpha mask are set up for blending */
143 if (surface->format->Amask) {
144 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
145 }
146
147 /* The surface is ready to go */
148 surface->refcount = 1;
149 return surface;
150 }
151
152 /*
153 * Create an empty RGB surface of the appropriate depth
154 */
155 SDL_Surface *
SDL_CreateRGBSurface(Uint32 flags,int width,int height,int depth,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)156 SDL_CreateRGBSurface(Uint32 flags,
157 int width, int height, int depth,
158 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
159 {
160 Uint32 format;
161
162 /* Get the pixel format */
163 format = SDL_MasksToPixelFormatEnum(depth, Rmask, Gmask, Bmask, Amask);
164 if (format == SDL_PIXELFORMAT_UNKNOWN) {
165 SDL_SetError("Unknown pixel format");
166 return NULL;
167 }
168
169 return SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format);
170 }
171
172 /*
173 * Create an RGB surface from an existing memory buffer
174 */
175 SDL_Surface *
SDL_CreateRGBSurfaceFrom(void * pixels,int width,int height,int depth,int pitch,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)176 SDL_CreateRGBSurfaceFrom(void *pixels,
177 int width, int height, int depth, int pitch,
178 Uint32 Rmask, Uint32 Gmask, Uint32 Bmask,
179 Uint32 Amask)
180 {
181 SDL_Surface *surface;
182
183 surface = SDL_CreateRGBSurface(0, 0, 0, depth, Rmask, Gmask, Bmask, Amask);
184 if (surface != NULL) {
185 surface->flags |= SDL_PREALLOC;
186 surface->pixels = pixels;
187 surface->w = width;
188 surface->h = height;
189 surface->pitch = pitch;
190 SDL_SetClipRect(surface, NULL);
191 }
192 return surface;
193 }
194
195 /*
196 * Create an RGB surface from an existing memory buffer using the given given
197 * enum SDL_PIXELFORMAT_* format
198 */
199 SDL_Surface *
SDL_CreateRGBSurfaceWithFormatFrom(void * pixels,int width,int height,int depth,int pitch,Uint32 format)200 SDL_CreateRGBSurfaceWithFormatFrom(void *pixels,
201 int width, int height, int depth, int pitch,
202 Uint32 format)
203 {
204 SDL_Surface *surface;
205
206 surface = SDL_CreateRGBSurfaceWithFormat(0, 0, 0, depth, format);
207 if (surface != NULL) {
208 surface->flags |= SDL_PREALLOC;
209 surface->pixels = pixels;
210 surface->w = width;
211 surface->h = height;
212 surface->pitch = pitch;
213 SDL_SetClipRect(surface, NULL);
214 }
215 return surface;
216 }
217
218 int
SDL_SetSurfacePalette(SDL_Surface * surface,SDL_Palette * palette)219 SDL_SetSurfacePalette(SDL_Surface * surface, SDL_Palette * palette)
220 {
221 if (!surface) {
222 return SDL_SetError("SDL_SetSurfacePalette() passed a NULL surface");
223 }
224 if (SDL_SetPixelFormatPalette(surface->format, palette) < 0) {
225 return -1;
226 }
227 SDL_InvalidateMap(surface->map);
228
229 return 0;
230 }
231
232 int
SDL_SetSurfaceRLE(SDL_Surface * surface,int flag)233 SDL_SetSurfaceRLE(SDL_Surface * surface, int flag)
234 {
235 int flags;
236
237 if (!surface) {
238 return -1;
239 }
240
241 flags = surface->map->info.flags;
242 if (flag) {
243 surface->map->info.flags |= SDL_COPY_RLE_DESIRED;
244 } else {
245 surface->map->info.flags &= ~SDL_COPY_RLE_DESIRED;
246 }
247 if (surface->map->info.flags != flags) {
248 SDL_InvalidateMap(surface->map);
249 }
250 return 0;
251 }
252
253 SDL_bool
SDL_HasSurfaceRLE(SDL_Surface * surface)254 SDL_HasSurfaceRLE(SDL_Surface * surface)
255 {
256 if (!surface) {
257 return SDL_FALSE;
258 }
259
260 if (!(surface->map->info.flags & SDL_COPY_RLE_DESIRED)) {
261 return SDL_FALSE;
262 }
263
264 return SDL_TRUE;
265 }
266
267 int
SDL_SetColorKey(SDL_Surface * surface,int flag,Uint32 key)268 SDL_SetColorKey(SDL_Surface * surface, int flag, Uint32 key)
269 {
270 int flags;
271
272 if (!surface) {
273 return SDL_InvalidParamError("surface");
274 }
275
276 if (surface->format->palette && key >= ((Uint32) surface->format->palette->ncolors)) {
277 return SDL_InvalidParamError("key");
278 }
279
280 if (flag & SDL_RLEACCEL) {
281 SDL_SetSurfaceRLE(surface, 1);
282 }
283
284 flags = surface->map->info.flags;
285 if (flag) {
286 surface->map->info.flags |= SDL_COPY_COLORKEY;
287 surface->map->info.colorkey = key;
288 } else {
289 surface->map->info.flags &= ~SDL_COPY_COLORKEY;
290 }
291 if (surface->map->info.flags != flags) {
292 SDL_InvalidateMap(surface->map);
293 }
294
295 return 0;
296 }
297
298 SDL_bool
SDL_HasColorKey(SDL_Surface * surface)299 SDL_HasColorKey(SDL_Surface * surface)
300 {
301 if (!surface) {
302 return SDL_FALSE;
303 }
304
305 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
306 return SDL_FALSE;
307 }
308
309 return SDL_TRUE;
310 }
311
312 int
SDL_GetColorKey(SDL_Surface * surface,Uint32 * key)313 SDL_GetColorKey(SDL_Surface * surface, Uint32 * key)
314 {
315 if (!surface) {
316 return SDL_InvalidParamError("surface");
317 }
318
319 if (!(surface->map->info.flags & SDL_COPY_COLORKEY)) {
320 return SDL_SetError("Surface doesn't have a colorkey");
321 }
322
323 if (key) {
324 *key = surface->map->info.colorkey;
325 }
326 return 0;
327 }
328
329 /* This is a fairly slow function to switch from colorkey to alpha
330 NB: it doesn't handle bpp 1 or 3, because they have no alpha channel */
331 static void
SDL_ConvertColorkeyToAlpha(SDL_Surface * surface,SDL_bool ignore_alpha)332 SDL_ConvertColorkeyToAlpha(SDL_Surface * surface, SDL_bool ignore_alpha)
333 {
334 int x, y, bpp;
335
336 if (!surface) {
337 return;
338 }
339
340 if (!(surface->map->info.flags & SDL_COPY_COLORKEY) ||
341 !surface->format->Amask) {
342 return;
343 }
344
345 bpp = surface->format->BytesPerPixel;
346
347 SDL_LockSurface(surface);
348
349 if (bpp == 2) {
350 Uint16 *row, *spot;
351 Uint16 ckey = (Uint16) surface->map->info.colorkey;
352 Uint16 mask = (Uint16) (~surface->format->Amask);
353
354 /* Ignore, or not, alpha in colorkey comparison */
355 if (ignore_alpha) {
356 ckey &= mask;
357 row = (Uint16 *) surface->pixels;
358 for (y = surface->h; y--;) {
359 spot = row;
360 for (x = surface->w; x--;) {
361 if ((*spot & mask) == ckey) {
362 *spot &= mask;
363 }
364 ++spot;
365 }
366 row += surface->pitch / 2;
367 }
368 } else {
369 row = (Uint16 *) surface->pixels;
370 for (y = surface->h; y--;) {
371 spot = row;
372 for (x = surface->w; x--;) {
373 if (*spot == ckey) {
374 *spot &= mask;
375 }
376 ++spot;
377 }
378 row += surface->pitch / 2;
379 }
380 }
381 } else if (bpp == 4) {
382 Uint32 *row, *spot;
383 Uint32 ckey = surface->map->info.colorkey;
384 Uint32 mask = ~surface->format->Amask;
385
386 /* Ignore, or not, alpha in colorkey comparison */
387 if (ignore_alpha) {
388 ckey &= mask;
389 row = (Uint32 *) surface->pixels;
390 for (y = surface->h; y--;) {
391 spot = row;
392 for (x = surface->w; x--;) {
393 if ((*spot & mask) == ckey) {
394 *spot &= mask;
395 }
396 ++spot;
397 }
398 row += surface->pitch / 4;
399 }
400 } else {
401 row = (Uint32 *) surface->pixels;
402 for (y = surface->h; y--;) {
403 spot = row;
404 for (x = surface->w; x--;) {
405 if (*spot == ckey) {
406 *spot &= mask;
407 }
408 ++spot;
409 }
410 row += surface->pitch / 4;
411 }
412 }
413 }
414
415 SDL_UnlockSurface(surface);
416
417 SDL_SetColorKey(surface, 0, 0);
418 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
419 }
420
421 int
SDL_SetSurfaceColorMod(SDL_Surface * surface,Uint8 r,Uint8 g,Uint8 b)422 SDL_SetSurfaceColorMod(SDL_Surface * surface, Uint8 r, Uint8 g, Uint8 b)
423 {
424 int flags;
425
426 if (!surface) {
427 return -1;
428 }
429
430 surface->map->info.r = r;
431 surface->map->info.g = g;
432 surface->map->info.b = b;
433
434 flags = surface->map->info.flags;
435 if (r != 0xFF || g != 0xFF || b != 0xFF) {
436 surface->map->info.flags |= SDL_COPY_MODULATE_COLOR;
437 } else {
438 surface->map->info.flags &= ~SDL_COPY_MODULATE_COLOR;
439 }
440 if (surface->map->info.flags != flags) {
441 SDL_InvalidateMap(surface->map);
442 }
443 return 0;
444 }
445
446
447 int
SDL_GetSurfaceColorMod(SDL_Surface * surface,Uint8 * r,Uint8 * g,Uint8 * b)448 SDL_GetSurfaceColorMod(SDL_Surface * surface, Uint8 * r, Uint8 * g, Uint8 * b)
449 {
450 if (!surface) {
451 return -1;
452 }
453
454 if (r) {
455 *r = surface->map->info.r;
456 }
457 if (g) {
458 *g = surface->map->info.g;
459 }
460 if (b) {
461 *b = surface->map->info.b;
462 }
463 return 0;
464 }
465
466 int
SDL_SetSurfaceAlphaMod(SDL_Surface * surface,Uint8 alpha)467 SDL_SetSurfaceAlphaMod(SDL_Surface * surface, Uint8 alpha)
468 {
469 int flags;
470
471 if (!surface) {
472 return -1;
473 }
474
475 surface->map->info.a = alpha;
476
477 flags = surface->map->info.flags;
478 if (alpha != 0xFF) {
479 surface->map->info.flags |= SDL_COPY_MODULATE_ALPHA;
480 } else {
481 surface->map->info.flags &= ~SDL_COPY_MODULATE_ALPHA;
482 }
483 if (surface->map->info.flags != flags) {
484 SDL_InvalidateMap(surface->map);
485 }
486 return 0;
487 }
488
489 int
SDL_GetSurfaceAlphaMod(SDL_Surface * surface,Uint8 * alpha)490 SDL_GetSurfaceAlphaMod(SDL_Surface * surface, Uint8 * alpha)
491 {
492 if (!surface) {
493 return -1;
494 }
495
496 if (alpha) {
497 *alpha = surface->map->info.a;
498 }
499 return 0;
500 }
501
502 int
SDL_SetSurfaceBlendMode(SDL_Surface * surface,SDL_BlendMode blendMode)503 SDL_SetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode blendMode)
504 {
505 int flags, status;
506
507 if (!surface) {
508 return -1;
509 }
510
511 status = 0;
512 flags = surface->map->info.flags;
513 surface->map->info.flags &=
514 ~(SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL);
515 switch (blendMode) {
516 case SDL_BLENDMODE_NONE:
517 break;
518 case SDL_BLENDMODE_BLEND:
519 surface->map->info.flags |= SDL_COPY_BLEND;
520 break;
521 case SDL_BLENDMODE_ADD:
522 surface->map->info.flags |= SDL_COPY_ADD;
523 break;
524 case SDL_BLENDMODE_MOD:
525 surface->map->info.flags |= SDL_COPY_MOD;
526 break;
527 case SDL_BLENDMODE_MUL:
528 surface->map->info.flags |= SDL_COPY_MUL;
529 break;
530 default:
531 status = SDL_Unsupported();
532 break;
533 }
534
535 if (surface->map->info.flags != flags) {
536 SDL_InvalidateMap(surface->map);
537 }
538
539 return status;
540 }
541
542 int
SDL_GetSurfaceBlendMode(SDL_Surface * surface,SDL_BlendMode * blendMode)543 SDL_GetSurfaceBlendMode(SDL_Surface * surface, SDL_BlendMode *blendMode)
544 {
545 if (!surface) {
546 return -1;
547 }
548
549 if (!blendMode) {
550 return 0;
551 }
552
553 switch (surface->map->
554 info.flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL)) {
555 case SDL_COPY_BLEND:
556 *blendMode = SDL_BLENDMODE_BLEND;
557 break;
558 case SDL_COPY_ADD:
559 *blendMode = SDL_BLENDMODE_ADD;
560 break;
561 case SDL_COPY_MOD:
562 *blendMode = SDL_BLENDMODE_MOD;
563 break;
564 case SDL_COPY_MUL:
565 *blendMode = SDL_BLENDMODE_MUL;
566 break;
567 default:
568 *blendMode = SDL_BLENDMODE_NONE;
569 break;
570 }
571 return 0;
572 }
573
574 SDL_bool
SDL_SetClipRect(SDL_Surface * surface,const SDL_Rect * rect)575 SDL_SetClipRect(SDL_Surface * surface, const SDL_Rect * rect)
576 {
577 SDL_Rect full_rect;
578
579 /* Don't do anything if there's no surface to act on */
580 if (!surface) {
581 return SDL_FALSE;
582 }
583
584 /* Set up the full surface rectangle */
585 full_rect.x = 0;
586 full_rect.y = 0;
587 full_rect.w = surface->w;
588 full_rect.h = surface->h;
589
590 /* Set the clipping rectangle */
591 if (!rect) {
592 surface->clip_rect = full_rect;
593 return SDL_TRUE;
594 }
595 return SDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
596 }
597
598 void
SDL_GetClipRect(SDL_Surface * surface,SDL_Rect * rect)599 SDL_GetClipRect(SDL_Surface * surface, SDL_Rect * rect)
600 {
601 if (surface && rect) {
602 *rect = surface->clip_rect;
603 }
604 }
605
606 /*
607 * Set up a blit between two surfaces -- split into three parts:
608 * The upper part, SDL_UpperBlit(), performs clipping and rectangle
609 * verification. The lower part is a pointer to a low level
610 * accelerated blitting function.
611 *
612 * These parts are separated out and each used internally by this
613 * library in the optimimum places. They are exported so that if
614 * you know exactly what you are doing, you can optimize your code
615 * by calling the one(s) you need.
616 */
617 int
SDL_LowerBlit(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)618 SDL_LowerBlit(SDL_Surface * src, SDL_Rect * srcrect,
619 SDL_Surface * dst, SDL_Rect * dstrect)
620 {
621 /* Check to make sure the blit mapping is valid */
622 if ((src->map->dst != dst) ||
623 (dst->format->palette &&
624 src->map->dst_palette_version != dst->format->palette->version) ||
625 (src->format->palette &&
626 src->map->src_palette_version != src->format->palette->version)) {
627 if (SDL_MapSurface(src, dst) < 0) {
628 return (-1);
629 }
630 /* just here for debugging */
631 /* printf */
632 /* ("src = 0x%08X src->flags = %08X src->map->info.flags = %08x\ndst = 0x%08X dst->flags = %08X dst->map->info.flags = %08X\nsrc->map->blit = 0x%08x\n", */
633 /* src, dst->flags, src->map->info.flags, dst, dst->flags, */
634 /* dst->map->info.flags, src->map->blit); */
635 }
636 return (src->map->blit(src, srcrect, dst, dstrect));
637 }
638
639
640 int
SDL_UpperBlit(SDL_Surface * src,const SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)641 SDL_UpperBlit(SDL_Surface * src, const SDL_Rect * srcrect,
642 SDL_Surface * dst, SDL_Rect * dstrect)
643 {
644 SDL_Rect fulldst;
645 int srcx, srcy, w, h;
646
647 /* Make sure the surfaces aren't locked */
648 if (!src || !dst) {
649 return SDL_SetError("SDL_UpperBlit: passed a NULL surface");
650 }
651 if (src->locked || dst->locked) {
652 return SDL_SetError("Surfaces must not be locked during blit");
653 }
654
655 /* If the destination rectangle is NULL, use the entire dest surface */
656 if (dstrect == NULL) {
657 fulldst.x = fulldst.y = 0;
658 fulldst.w = dst->w;
659 fulldst.h = dst->h;
660 dstrect = &fulldst;
661 }
662
663 /* clip the source rectangle to the source surface */
664 if (srcrect) {
665 int maxw, maxh;
666
667 srcx = srcrect->x;
668 w = srcrect->w;
669 if (srcx < 0) {
670 w += srcx;
671 dstrect->x -= srcx;
672 srcx = 0;
673 }
674 maxw = src->w - srcx;
675 if (maxw < w)
676 w = maxw;
677
678 srcy = srcrect->y;
679 h = srcrect->h;
680 if (srcy < 0) {
681 h += srcy;
682 dstrect->y -= srcy;
683 srcy = 0;
684 }
685 maxh = src->h - srcy;
686 if (maxh < h)
687 h = maxh;
688
689 } else {
690 srcx = srcy = 0;
691 w = src->w;
692 h = src->h;
693 }
694
695 /* clip the destination rectangle against the clip rectangle */
696 {
697 SDL_Rect *clip = &dst->clip_rect;
698 int dx, dy;
699
700 dx = clip->x - dstrect->x;
701 if (dx > 0) {
702 w -= dx;
703 dstrect->x += dx;
704 srcx += dx;
705 }
706 dx = dstrect->x + w - clip->x - clip->w;
707 if (dx > 0)
708 w -= dx;
709
710 dy = clip->y - dstrect->y;
711 if (dy > 0) {
712 h -= dy;
713 dstrect->y += dy;
714 srcy += dy;
715 }
716 dy = dstrect->y + h - clip->y - clip->h;
717 if (dy > 0)
718 h -= dy;
719 }
720
721 /* Switch back to a fast blit if we were previously stretching */
722 if (src->map->info.flags & SDL_COPY_NEAREST) {
723 src->map->info.flags &= ~SDL_COPY_NEAREST;
724 SDL_InvalidateMap(src->map);
725 }
726
727 if (w > 0 && h > 0) {
728 SDL_Rect sr;
729 sr.x = srcx;
730 sr.y = srcy;
731 sr.w = dstrect->w = w;
732 sr.h = dstrect->h = h;
733 return SDL_LowerBlit(src, &sr, dst, dstrect);
734 }
735 dstrect->w = dstrect->h = 0;
736 return 0;
737 }
738
739 int
SDL_UpperBlitScaled(SDL_Surface * src,const SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)740 SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
741 SDL_Surface * dst, SDL_Rect * dstrect)
742 {
743 return SDL_PrivateUpperBlitScaled(src, srcrect, dst, dstrect, SDL_ScaleModeNearest);
744 }
745
746
747 int
SDL_PrivateUpperBlitScaled(SDL_Surface * src,const SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect,SDL_ScaleMode scaleMode)748 SDL_PrivateUpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect,
749 SDL_Surface * dst, SDL_Rect * dstrect, SDL_ScaleMode scaleMode)
750 {
751 double src_x0, src_y0, src_x1, src_y1;
752 double dst_x0, dst_y0, dst_x1, dst_y1;
753 SDL_Rect final_src, final_dst;
754 double scaling_w, scaling_h;
755 int src_w, src_h;
756 int dst_w, dst_h;
757
758 /* Make sure the surfaces aren't locked */
759 if (!src || !dst) {
760 return SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface");
761 }
762 if (src->locked || dst->locked) {
763 return SDL_SetError("Surfaces must not be locked during blit");
764 }
765
766 if (NULL == srcrect) {
767 src_w = src->w;
768 src_h = src->h;
769 } else {
770 src_w = srcrect->w;
771 src_h = srcrect->h;
772 }
773
774 if (NULL == dstrect) {
775 dst_w = dst->w;
776 dst_h = dst->h;
777 } else {
778 dst_w = dstrect->w;
779 dst_h = dstrect->h;
780 }
781
782 if (dst_w == src_w && dst_h == src_h) {
783 /* No scaling, defer to regular blit */
784 return SDL_BlitSurface(src, srcrect, dst, dstrect);
785 }
786
787 scaling_w = (double)dst_w / src_w;
788 scaling_h = (double)dst_h / src_h;
789
790 if (NULL == dstrect) {
791 dst_x0 = 0;
792 dst_y0 = 0;
793 dst_x1 = dst_w;
794 dst_y1 = dst_h;
795 } else {
796 dst_x0 = dstrect->x;
797 dst_y0 = dstrect->y;
798 dst_x1 = dst_x0 + dst_w;
799 dst_y1 = dst_y0 + dst_h;
800 }
801
802 if (NULL == srcrect) {
803 src_x0 = 0;
804 src_y0 = 0;
805 src_x1 = src_w;
806 src_y1 = src_h;
807 } else {
808 src_x0 = srcrect->x;
809 src_y0 = srcrect->y;
810 src_x1 = src_x0 + src_w;
811 src_y1 = src_y0 + src_h;
812
813 /* Clip source rectangle to the source surface */
814
815 if (src_x0 < 0) {
816 dst_x0 -= src_x0 * scaling_w;
817 src_x0 = 0;
818 }
819
820 if (src_x1 > src->w) {
821 dst_x1 -= (src_x1 - src->w) * scaling_w;
822 src_x1 = src->w;
823 }
824
825 if (src_y0 < 0) {
826 dst_y0 -= src_y0 * scaling_h;
827 src_y0 = 0;
828 }
829
830 if (src_y1 > src->h) {
831 dst_y1 -= (src_y1 - src->h) * scaling_h;
832 src_y1 = src->h;
833 }
834 }
835
836 /* Clip destination rectangle to the clip rectangle */
837
838 /* Translate to clip space for easier calculations */
839 dst_x0 -= dst->clip_rect.x;
840 dst_x1 -= dst->clip_rect.x;
841 dst_y0 -= dst->clip_rect.y;
842 dst_y1 -= dst->clip_rect.y;
843
844 if (dst_x0 < 0) {
845 src_x0 -= dst_x0 / scaling_w;
846 dst_x0 = 0;
847 }
848
849 if (dst_x1 > dst->clip_rect.w) {
850 src_x1 -= (dst_x1 - dst->clip_rect.w) / scaling_w;
851 dst_x1 = dst->clip_rect.w;
852 }
853
854 if (dst_y0 < 0) {
855 src_y0 -= dst_y0 / scaling_h;
856 dst_y0 = 0;
857 }
858
859 if (dst_y1 > dst->clip_rect.h) {
860 src_y1 -= (dst_y1 - dst->clip_rect.h) / scaling_h;
861 dst_y1 = dst->clip_rect.h;
862 }
863
864 /* Translate back to surface coordinates */
865 dst_x0 += dst->clip_rect.x;
866 dst_x1 += dst->clip_rect.x;
867 dst_y0 += dst->clip_rect.y;
868 dst_y1 += dst->clip_rect.y;
869
870 final_src.x = (int)SDL_round(src_x0);
871 final_src.y = (int)SDL_round(src_y0);
872 final_src.w = (int)SDL_round(src_x1 - src_x0);
873 final_src.h = (int)SDL_round(src_y1 - src_y0);
874
875 final_dst.x = (int)SDL_round(dst_x0);
876 final_dst.y = (int)SDL_round(dst_y0);
877 final_dst.w = (int)SDL_round(dst_x1 - dst_x0);
878 final_dst.h = (int)SDL_round(dst_y1 - dst_y0);
879
880 /* Clip again */
881 {
882 SDL_Rect tmp;
883 tmp.x = 0;
884 tmp.y = 0;
885 tmp.w = src->w;
886 tmp.h = src->h;
887 SDL_IntersectRect(&tmp, &final_src, &final_src);
888 }
889
890 /* Clip again */
891 SDL_IntersectRect(&dst->clip_rect, &final_dst, &final_dst);
892
893 if (dstrect) {
894 *dstrect = final_dst;
895 }
896
897 if (final_dst.w == 0 || final_dst.h == 0 ||
898 final_src.w <= 0 || final_src.h <= 0) {
899 /* No-op. */
900 return 0;
901 }
902
903 return SDL_PrivateLowerBlitScaled(src, &final_src, dst, &final_dst, scaleMode);
904 }
905
906 /**
907 * This is a semi-private blit function and it performs low-level surface
908 * scaled blitting only.
909 */
910 int
SDL_LowerBlitScaled(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)911 SDL_LowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect,
912 SDL_Surface * dst, SDL_Rect * dstrect)
913 {
914 return SDL_PrivateLowerBlitScaled(src, srcrect, dst, dstrect, SDL_ScaleModeNearest);
915 }
916
917 int
SDL_PrivateLowerBlitScaled(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect,SDL_ScaleMode scaleMode)918 SDL_PrivateLowerBlitScaled(SDL_Surface * src, SDL_Rect * srcrect,
919 SDL_Surface * dst, SDL_Rect * dstrect, SDL_ScaleMode scaleMode)
920 {
921 static const Uint32 complex_copy_flags = (
922 SDL_COPY_MODULATE_COLOR | SDL_COPY_MODULATE_ALPHA |
923 SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL |
924 SDL_COPY_COLORKEY
925 );
926
927 if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 ||
928 dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) {
929 return SDL_SetError("Size too large for scaling");
930 }
931
932 if (!(src->map->info.flags & SDL_COPY_NEAREST)) {
933 src->map->info.flags |= SDL_COPY_NEAREST;
934 SDL_InvalidateMap(src->map);
935 }
936
937 if (scaleMode == SDL_ScaleModeNearest) {
938 if ( !(src->map->info.flags & complex_copy_flags) &&
939 src->format->format == dst->format->format &&
940 !SDL_ISPIXELFORMAT_INDEXED(src->format->format) ) {
941 return SDL_SoftStretch( src, srcrect, dst, dstrect );
942 } else {
943 return SDL_LowerBlit( src, srcrect, dst, dstrect );
944 }
945 } else {
946 if ( !(src->map->info.flags & complex_copy_flags) &&
947 src->format->format == dst->format->format &&
948 !SDL_ISPIXELFORMAT_INDEXED(src->format->format) &&
949 src->format->BytesPerPixel == 4 &&
950 src->format->format != SDL_PIXELFORMAT_ARGB2101010) {
951 /* fast path */
952 return SDL_SoftStretchLinear(src, srcrect, dst, dstrect);
953 } else {
954 /* Use intermediate surface(s) */
955 SDL_Surface *tmp1 = NULL;
956 int ret;
957 SDL_Rect srcrect2;
958 int is_complex_copy_flags = (src->map->info.flags & complex_copy_flags);
959
960 Uint32 flags;
961 Uint8 r, g, b;
962 Uint8 alpha;
963 SDL_BlendMode blendMode;
964
965 /* Save source infos */
966 flags = src->flags;
967 SDL_GetSurfaceColorMod(src, &r, &g, &b);
968 SDL_GetSurfaceAlphaMod(src, &alpha);
969 SDL_GetSurfaceBlendMode(src, &blendMode);
970 srcrect2.x = srcrect->x;
971 srcrect2.y = srcrect->y;
972 srcrect2.w = srcrect->w;
973 srcrect2.h = srcrect->h;
974
975 /* Change source format if not appropriate for scaling */
976 if (src->format->BytesPerPixel != 4 || src->format->format == SDL_PIXELFORMAT_ARGB2101010) {
977 SDL_Rect tmprect;
978 int fmt;
979 tmprect.x = 0;
980 tmprect.y = 0;
981 tmprect.w = src->w;
982 tmprect.h = src->h;
983 if (dst->format->BytesPerPixel == 4 && dst->format->format != SDL_PIXELFORMAT_ARGB2101010) {
984 fmt = dst->format->format;
985 } else {
986 fmt = SDL_PIXELFORMAT_ARGB8888;
987 }
988 tmp1 = SDL_CreateRGBSurfaceWithFormat(flags, src->w, src->h, 0, fmt);
989 SDL_LowerBlit(src, srcrect, tmp1, &tmprect);
990
991
992 srcrect2.x = 0;
993 srcrect2.y = 0;
994 SDL_SetSurfaceColorMod(tmp1, r, g, b);
995 SDL_SetSurfaceAlphaMod(tmp1, alpha);
996 SDL_SetSurfaceBlendMode(tmp1, blendMode);
997
998 src = tmp1;
999 }
1000
1001 /* Intermediate scaling */
1002 if (is_complex_copy_flags || src->format->format != dst->format->format) {
1003 SDL_Rect tmprect;
1004 SDL_Surface *tmp2 = SDL_CreateRGBSurfaceWithFormat(flags, dstrect->w, dstrect->h, 0, src->format->format);
1005 SDL_SoftStretchLinear(src, &srcrect2, tmp2, NULL);
1006
1007 SDL_SetSurfaceColorMod(tmp2, r, g, b);
1008 SDL_SetSurfaceAlphaMod(tmp2, alpha);
1009 SDL_SetSurfaceBlendMode(tmp2, blendMode);
1010
1011 tmprect.x = 0;
1012 tmprect.y = 0;
1013 tmprect.w = dstrect->w;
1014 tmprect.h = dstrect->h;
1015 ret = SDL_LowerBlit(tmp2, &tmprect, dst, dstrect);
1016 SDL_FreeSurface(tmp2);
1017 } else {
1018 ret = SDL_SoftStretchLinear(src, &srcrect2, dst, dstrect);
1019 }
1020
1021 SDL_FreeSurface(tmp1);
1022 return ret;
1023 }
1024 }
1025 }
1026
1027 /*
1028 * Lock a surface to directly access the pixels
1029 */
1030 int
SDL_LockSurface(SDL_Surface * surface)1031 SDL_LockSurface(SDL_Surface * surface)
1032 {
1033 if (!surface->locked) {
1034 #if SDL_HAVE_RLE
1035 /* Perform the lock */
1036 if (surface->flags & SDL_RLEACCEL) {
1037 SDL_UnRLESurface(surface, 1);
1038 surface->flags |= SDL_RLEACCEL; /* save accel'd state */
1039 }
1040 #endif
1041 }
1042
1043 /* Increment the surface lock count, for recursive locks */
1044 ++surface->locked;
1045
1046 /* Ready to go.. */
1047 return (0);
1048 }
1049
1050 /*
1051 * Unlock a previously locked surface
1052 */
1053 void
SDL_UnlockSurface(SDL_Surface * surface)1054 SDL_UnlockSurface(SDL_Surface * surface)
1055 {
1056 /* Only perform an unlock if we are locked */
1057 if (!surface->locked || (--surface->locked > 0)) {
1058 return;
1059 }
1060
1061 #if SDL_HAVE_RLE
1062 /* Update RLE encoded surface with new data */
1063 if ((surface->flags & SDL_RLEACCEL) == SDL_RLEACCEL) {
1064 surface->flags &= ~SDL_RLEACCEL; /* stop lying */
1065 SDL_RLESurface(surface);
1066 }
1067 #endif
1068 }
1069
1070 /*
1071 * Creates a new surface identical to the existing surface
1072 */
1073 SDL_Surface *
SDL_DuplicateSurface(SDL_Surface * surface)1074 SDL_DuplicateSurface(SDL_Surface * surface)
1075 {
1076 return SDL_ConvertSurface(surface, surface->format, surface->flags);
1077 }
1078
1079 /*
1080 * Convert a surface into the specified pixel format.
1081 */
1082 SDL_Surface *
SDL_ConvertSurface(SDL_Surface * surface,const SDL_PixelFormat * format,Uint32 flags)1083 SDL_ConvertSurface(SDL_Surface * surface, const SDL_PixelFormat * format,
1084 Uint32 flags)
1085 {
1086 SDL_Surface *convert;
1087 Uint32 copy_flags;
1088 SDL_Color copy_color;
1089 SDL_Rect bounds;
1090 int ret;
1091 SDL_bool palette_ck_transform = SDL_FALSE;
1092 int palette_ck_value = 0;
1093 SDL_bool palette_has_alpha = SDL_FALSE;
1094 Uint8 *palette_saved_alpha = NULL;
1095 int palette_saved_alpha_ncolors = 0;
1096
1097 if (!surface) {
1098 SDL_InvalidParamError("surface");
1099 return NULL;
1100 }
1101 if (!format) {
1102 SDL_InvalidParamError("format");
1103 return NULL;
1104 }
1105
1106 /* Check for empty destination palette! (results in empty image) */
1107 if (format->palette != NULL) {
1108 int i;
1109 for (i = 0; i < format->palette->ncolors; ++i) {
1110 if ((format->palette->colors[i].r != 0xFF) ||
1111 (format->palette->colors[i].g != 0xFF) ||
1112 (format->palette->colors[i].b != 0xFF))
1113 break;
1114 }
1115 if (i == format->palette->ncolors) {
1116 SDL_SetError("Empty destination palette");
1117 return (NULL);
1118 }
1119 }
1120
1121 /* Create a new surface with the desired format */
1122 convert = SDL_CreateRGBSurface(flags, surface->w, surface->h,
1123 format->BitsPerPixel, format->Rmask,
1124 format->Gmask, format->Bmask,
1125 format->Amask);
1126 if (convert == NULL) {
1127 return (NULL);
1128 }
1129
1130 /* Copy the palette if any */
1131 if (format->palette && convert->format->palette) {
1132 SDL_memcpy(convert->format->palette->colors,
1133 format->palette->colors,
1134 format->palette->ncolors * sizeof(SDL_Color));
1135 convert->format->palette->ncolors = format->palette->ncolors;
1136 }
1137
1138 /* Save the original copy flags */
1139 copy_flags = surface->map->info.flags;
1140 copy_color.r = surface->map->info.r;
1141 copy_color.g = surface->map->info.g;
1142 copy_color.b = surface->map->info.b;
1143 copy_color.a = surface->map->info.a;
1144 surface->map->info.r = 0xFF;
1145 surface->map->info.g = 0xFF;
1146 surface->map->info.b = 0xFF;
1147 surface->map->info.a = 0xFF;
1148 surface->map->info.flags = (copy_flags & (SDL_COPY_RLE_COLORKEY | SDL_COPY_RLE_ALPHAKEY));
1149 SDL_InvalidateMap(surface->map);
1150
1151 /* Copy over the image data */
1152 bounds.x = 0;
1153 bounds.y = 0;
1154 bounds.w = surface->w;
1155 bounds.h = surface->h;
1156
1157 /* Source surface has a palette with no real alpha (0 or OPAQUE).
1158 * Destination format has alpha.
1159 * -> set alpha channel to be opaque */
1160 if (surface->format->palette && format->Amask) {
1161 SDL_bool set_opaque = SDL_FALSE;
1162
1163 SDL_bool is_opaque, has_alpha_channel;
1164 SDL_DetectPalette(surface->format->palette, &is_opaque, &has_alpha_channel);
1165
1166 if (is_opaque) {
1167 if (!has_alpha_channel) {
1168 set_opaque = SDL_TRUE;
1169 }
1170 } else {
1171 palette_has_alpha = SDL_TRUE;
1172 }
1173
1174 /* Set opaque and backup palette alpha values */
1175 if (set_opaque) {
1176 int i;
1177 palette_saved_alpha_ncolors = surface->format->palette->ncolors;
1178 palette_saved_alpha = SDL_stack_alloc(Uint8, palette_saved_alpha_ncolors);
1179 for (i = 0; i < palette_saved_alpha_ncolors; i++) {
1180 palette_saved_alpha[i] = surface->format->palette->colors[i].a;
1181 surface->format->palette->colors[i].a = SDL_ALPHA_OPAQUE;
1182 }
1183 }
1184 }
1185
1186 /* Transform colorkey to alpha. for cases where source palette has duplicate values, and colorkey is one of them */
1187 if (copy_flags & SDL_COPY_COLORKEY) {
1188 if (surface->format->palette && !format->palette) {
1189 palette_ck_transform = SDL_TRUE;
1190 palette_has_alpha = SDL_TRUE;
1191 palette_ck_value = surface->format->palette->colors[surface->map->info.colorkey].a;
1192 surface->format->palette->colors[surface->map->info.colorkey].a = SDL_ALPHA_TRANSPARENT;
1193 }
1194 }
1195
1196 ret = SDL_LowerBlit(surface, &bounds, convert, &bounds);
1197
1198 /* Restore colorkey alpha value */
1199 if (palette_ck_transform) {
1200 surface->format->palette->colors[surface->map->info.colorkey].a = palette_ck_value;
1201 }
1202
1203 /* Restore palette alpha values */
1204 if (palette_saved_alpha) {
1205 int i;
1206 for (i = 0; i < palette_saved_alpha_ncolors; i++) {
1207 surface->format->palette->colors[i].a = palette_saved_alpha[i];
1208 }
1209 SDL_stack_free(palette_saved_alpha);
1210 }
1211
1212 /* Clean up the original surface, and update converted surface */
1213 convert->map->info.r = copy_color.r;
1214 convert->map->info.g = copy_color.g;
1215 convert->map->info.b = copy_color.b;
1216 convert->map->info.a = copy_color.a;
1217 convert->map->info.flags =
1218 (copy_flags &
1219 ~(SDL_COPY_COLORKEY | SDL_COPY_BLEND
1220 | SDL_COPY_RLE_DESIRED | SDL_COPY_RLE_COLORKEY |
1221 SDL_COPY_RLE_ALPHAKEY));
1222 surface->map->info.r = copy_color.r;
1223 surface->map->info.g = copy_color.g;
1224 surface->map->info.b = copy_color.b;
1225 surface->map->info.a = copy_color.a;
1226 surface->map->info.flags = copy_flags;
1227 SDL_InvalidateMap(surface->map);
1228
1229 /* SDL_LowerBlit failed, and so the conversion */
1230 if (ret < 0) {
1231 SDL_FreeSurface(convert);
1232 return NULL;
1233 }
1234
1235 if (copy_flags & SDL_COPY_COLORKEY) {
1236 SDL_bool set_colorkey_by_color = SDL_FALSE;
1237 SDL_bool convert_colorkey = SDL_TRUE;
1238
1239 if (surface->format->palette) {
1240 if (format->palette &&
1241 surface->format->palette->ncolors <= format->palette->ncolors &&
1242 (SDL_memcmp(surface->format->palette->colors, format->palette->colors,
1243 surface->format->palette->ncolors * sizeof(SDL_Color)) == 0)) {
1244 /* The palette is identical, just set the same colorkey */
1245 SDL_SetColorKey(convert, 1, surface->map->info.colorkey);
1246 } else if (!format->palette) {
1247 if (format->Amask) {
1248 /* No need to add the colorkey, transparency is in the alpha channel*/
1249 } else {
1250 /* Only set the colorkey information */
1251 set_colorkey_by_color = SDL_TRUE;
1252 convert_colorkey = SDL_FALSE;
1253 }
1254 } else {
1255 set_colorkey_by_color = SDL_TRUE;
1256 }
1257 } else {
1258 set_colorkey_by_color = SDL_TRUE;
1259 }
1260
1261 if (set_colorkey_by_color) {
1262 SDL_Surface *tmp;
1263 SDL_Surface *tmp2;
1264 int converted_colorkey = 0;
1265
1266 /* Create a dummy surface to get the colorkey converted */
1267 tmp = SDL_CreateRGBSurface(0, 1, 1,
1268 surface->format->BitsPerPixel, surface->format->Rmask,
1269 surface->format->Gmask, surface->format->Bmask,
1270 surface->format->Amask);
1271
1272 /* Share the palette, if any */
1273 if (surface->format->palette) {
1274 SDL_SetSurfacePalette(tmp, surface->format->palette);
1275 }
1276
1277 SDL_FillRect(tmp, NULL, surface->map->info.colorkey);
1278
1279 tmp->map->info.flags &= ~SDL_COPY_COLORKEY;
1280
1281 /* Convertion of the colorkey */
1282 tmp2 = SDL_ConvertSurface(tmp, format, 0);
1283
1284 /* Get the converted colorkey */
1285 SDL_memcpy(&converted_colorkey, tmp2->pixels, tmp2->format->BytesPerPixel);
1286
1287 SDL_FreeSurface(tmp);
1288 SDL_FreeSurface(tmp2);
1289
1290 /* Set the converted colorkey on the new surface */
1291 SDL_SetColorKey(convert, 1, converted_colorkey);
1292
1293 /* This is needed when converting for 3D texture upload */
1294 if (convert_colorkey) {
1295 SDL_ConvertColorkeyToAlpha(convert, SDL_TRUE);
1296 }
1297 }
1298 }
1299 SDL_SetClipRect(convert, &surface->clip_rect);
1300
1301 /* Enable alpha blending by default if the new surface has an
1302 * alpha channel or alpha modulation */
1303 if ((surface->format->Amask && format->Amask) ||
1304 (palette_has_alpha && format->Amask) ||
1305 (copy_flags & SDL_COPY_MODULATE_ALPHA)) {
1306 SDL_SetSurfaceBlendMode(convert, SDL_BLENDMODE_BLEND);
1307 }
1308 if ((copy_flags & SDL_COPY_RLE_DESIRED) || (flags & SDL_RLEACCEL)) {
1309 SDL_SetSurfaceRLE(convert, SDL_RLEACCEL);
1310 }
1311
1312 /* We're ready to go! */
1313 return (convert);
1314 }
1315
1316 SDL_Surface *
SDL_ConvertSurfaceFormat(SDL_Surface * surface,Uint32 pixel_format,Uint32 flags)1317 SDL_ConvertSurfaceFormat(SDL_Surface * surface, Uint32 pixel_format,
1318 Uint32 flags)
1319 {
1320 SDL_PixelFormat *fmt;
1321 SDL_Surface *convert = NULL;
1322
1323 fmt = SDL_AllocFormat(pixel_format);
1324 if (fmt) {
1325 convert = SDL_ConvertSurface(surface, fmt, flags);
1326 SDL_FreeFormat(fmt);
1327 }
1328 return convert;
1329 }
1330
1331 /*
1332 * Create a surface on the stack for quick blit operations
1333 */
1334 static SDL_INLINE SDL_bool
SDL_CreateSurfaceOnStack(int width,int height,Uint32 pixel_format,void * pixels,int pitch,SDL_Surface * surface,SDL_PixelFormat * format,SDL_BlitMap * blitmap)1335 SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format,
1336 void * pixels, int pitch, SDL_Surface * surface,
1337 SDL_PixelFormat * format, SDL_BlitMap * blitmap)
1338 {
1339 if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) {
1340 SDL_SetError("Indexed pixel formats not supported");
1341 return SDL_FALSE;
1342 }
1343 if (SDL_InitFormat(format, pixel_format) < 0) {
1344 return SDL_FALSE;
1345 }
1346
1347 SDL_zerop(surface);
1348 surface->flags = SDL_PREALLOC;
1349 surface->format = format;
1350 surface->pixels = pixels;
1351 surface->w = width;
1352 surface->h = height;
1353 surface->pitch = pitch;
1354 /* We don't actually need to set up the clip rect for our purposes */
1355 /* SDL_SetClipRect(surface, NULL); */
1356
1357 /* Allocate an empty mapping */
1358 SDL_zerop(blitmap);
1359 blitmap->info.r = 0xFF;
1360 blitmap->info.g = 0xFF;
1361 blitmap->info.b = 0xFF;
1362 blitmap->info.a = 0xFF;
1363 surface->map = blitmap;
1364
1365 /* The surface is ready to go */
1366 surface->refcount = 1;
1367 return SDL_TRUE;
1368 }
1369
1370 /*
1371 * Copy a block of pixels of one format to another format
1372 */
SDL_ConvertPixels(int width,int height,Uint32 src_format,const void * src,int src_pitch,Uint32 dst_format,void * dst,int dst_pitch)1373 int SDL_ConvertPixels(int width, int height,
1374 Uint32 src_format, const void * src, int src_pitch,
1375 Uint32 dst_format, void * dst, int dst_pitch)
1376 {
1377 SDL_Surface src_surface, dst_surface;
1378 SDL_PixelFormat src_fmt, dst_fmt;
1379 SDL_BlitMap src_blitmap, dst_blitmap;
1380 SDL_Rect rect;
1381 void *nonconst_src = (void *) src;
1382 int ret;
1383
1384 if (!src) {
1385 return SDL_InvalidParamError("src");
1386 }
1387 if (!src_pitch) {
1388 return SDL_InvalidParamError("src_pitch");
1389 }
1390 if (!dst) {
1391 return SDL_InvalidParamError("dst");
1392 }
1393 if (!dst_pitch) {
1394 return SDL_InvalidParamError("dst_pitch");
1395 }
1396
1397 #if SDL_HAVE_YUV
1398 if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
1399 return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
1400 } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) {
1401 return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
1402 } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
1403 return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src, src_pitch, dst_format, dst, dst_pitch);
1404 }
1405 #else
1406 if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) {
1407 SDL_SetError("SDL not built with YUV support");
1408 return -1;
1409 }
1410 #endif
1411
1412 /* Fast path for same format copy */
1413 if (src_format == dst_format) {
1414 int i;
1415 const int bpp = SDL_BYTESPERPIXEL(src_format);
1416 width *= bpp;
1417 for (i = height; i--;) {
1418 SDL_memcpy(dst, src, width);
1419 src = (const Uint8*)src + src_pitch;
1420 dst = (Uint8*)dst + dst_pitch;
1421 }
1422 return 0;
1423 }
1424
1425 if (!SDL_CreateSurfaceOnStack(width, height, src_format, nonconst_src,
1426 src_pitch,
1427 &src_surface, &src_fmt, &src_blitmap)) {
1428 return -1;
1429 }
1430 if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst, dst_pitch,
1431 &dst_surface, &dst_fmt, &dst_blitmap)) {
1432 return -1;
1433 }
1434
1435 /* Set up the rect and go! */
1436 rect.x = 0;
1437 rect.y = 0;
1438 rect.w = width;
1439 rect.h = height;
1440 ret = SDL_LowerBlit(&src_surface, &rect, &dst_surface, &rect);
1441
1442 /* Free blitmap reference, after blitting between stack'ed surfaces */
1443 SDL_InvalidateMap(src_surface.map);
1444
1445 return ret;
1446 }
1447
1448 /*
1449 * Premultiply the alpha on a block of pixels
1450 *
1451 * This is currently only implemented for SDL_PIXELFORMAT_ARGB8888
1452 *
1453 * Here are some ideas for optimization:
1454 * https://github.com/Wizermil/premultiply_alpha/tree/master/premultiply_alpha
1455 * https://developer.arm.com/documentation/101964/0201/Pre-multiplied-alpha-channel-data
1456 */
SDL_PremultiplyAlpha(int width,int height,Uint32 src_format,const void * src,int src_pitch,Uint32 dst_format,void * dst,int dst_pitch)1457 int SDL_PremultiplyAlpha(int width, int height,
1458 Uint32 src_format, const void * src, int src_pitch,
1459 Uint32 dst_format, void * dst, int dst_pitch)
1460 {
1461 int c;
1462 Uint32 srcpixel;
1463 Uint32 srcR, srcG, srcB, srcA;
1464 Uint32 dstpixel;
1465 Uint32 dstR, dstG, dstB, dstA;
1466
1467 if (!src) {
1468 return SDL_InvalidParamError("src");
1469 }
1470 if (!src_pitch) {
1471 return SDL_InvalidParamError("src_pitch");
1472 }
1473 if (!dst) {
1474 return SDL_InvalidParamError("dst");
1475 }
1476 if (!dst_pitch) {
1477 return SDL_InvalidParamError("dst_pitch");
1478 }
1479 if (src_format != SDL_PIXELFORMAT_ARGB8888) {
1480 return SDL_InvalidParamError("src_format");
1481 }
1482 if (dst_format != SDL_PIXELFORMAT_ARGB8888) {
1483 return SDL_InvalidParamError("dst_format");
1484 }
1485
1486 while (height--) {
1487 const Uint32 *src_px = (const Uint32 *)src;
1488 Uint32 *dst_px = (Uint32 *)dst;
1489 for (c = width; c; --c) {
1490 /* Component bytes extraction. */
1491 srcpixel = *src_px++;
1492 RGBA_FROM_ARGB8888(srcpixel, srcR, srcG, srcB, srcA);
1493
1494 /* Alpha pre-multiplication of each component. */
1495 dstA = srcA;
1496 dstR = (srcA * srcR) / 255;
1497 dstG = (srcA * srcG) / 255;
1498 dstB = (srcA * srcB) / 255;
1499
1500 /* ARGB8888 pixel recomposition. */
1501 ARGB8888_FROM_RGBA(dstpixel, dstR, dstG, dstB, dstA);
1502 *dst_px++ = dstpixel;
1503 }
1504 src = (const Uint8 *)src + src_pitch;
1505 dst = (Uint8 *)dst + dst_pitch;
1506 }
1507 return 0;
1508 }
1509
1510 /*
1511 * Free a surface created by the above function.
1512 */
1513 void
SDL_FreeSurface(SDL_Surface * surface)1514 SDL_FreeSurface(SDL_Surface * surface)
1515 {
1516 if (surface == NULL) {
1517 return;
1518 }
1519 if (surface->flags & SDL_DONTFREE) {
1520 return;
1521 }
1522 SDL_InvalidateMap(surface->map);
1523
1524 SDL_InvalidateAllBlitMap(surface);
1525
1526 if (--surface->refcount > 0) {
1527 return;
1528 }
1529 while (surface->locked > 0) {
1530 SDL_UnlockSurface(surface);
1531 }
1532 #if SDL_HAVE_RLE
1533 if (surface->flags & SDL_RLEACCEL) {
1534 SDL_UnRLESurface(surface, 0);
1535 }
1536 #endif
1537 if (surface->format) {
1538 SDL_SetSurfacePalette(surface, NULL);
1539 SDL_FreeFormat(surface->format);
1540 surface->format = NULL;
1541 }
1542 if (surface->flags & SDL_PREALLOC) {
1543 /* Don't free */
1544 } else if (surface->flags & SDL_SIMD_ALIGNED) {
1545 /* Free aligned */
1546 SDL_SIMDFree(surface->pixels);
1547 } else {
1548 /* Normal */
1549 SDL_free(surface->pixels);
1550 }
1551 if (surface->map) {
1552 SDL_FreeBlitMap(surface->map);
1553 }
1554 SDL_free(surface);
1555 }
1556
1557 /* vi: set ts=4 sw=4 expandtab: */
1558