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