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