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