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