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