1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "LRSDL_config.h"
23 
24 #include "LRSDL_video.h"
25 #include "SDL_sysvideo.h"
26 #include "SDL_blit.h"
27 #include "SDL_pixels_c.h"
28 
29 /* Public routines */
30 /*
31  * Create an empty RGB surface of the appropriate depth
32  */
LRSDL_CreateRGBSurface(Uint32 flags,int width,int height,int depth,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)33 SDL_Surface * LRSDL_CreateRGBSurface (
34       Uint32 flags,
35       int width, int height, int depth,
36       Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
37 {
38    SDL_Surface *surface;
39 
40    /* Make sure the size requested doesn't overflow our datatypes */
41    /* Next time I write a library like SDL, I'll use int for size. :) */
42    if ( width >= 16384 || height >= 65536 )
43    {
44       LRSDL_SetError("Width or height is too large");
45       return NULL;
46    }
47 
48    flags &= ~SDL_HWSURFACE;
49 
50    /* Allocate the surface */
51    surface = (SDL_Surface *)SDL_malloc(sizeof(*surface));
52    if (!surface)
53    {
54       LRSDL_OutOfMemory();
55       return NULL;
56    }
57 
58    surface->flags  = SDL_SWSURFACE;
59    surface->format = LRSDL_AllocFormat(depth, Rmask, Gmask, Bmask, Amask);
60 
61    if (!surface->format)
62    {
63       SDL_free(surface);
64       return NULL;
65    }
66 
67    if (Amask)
68       surface->flags |= SDL_SRCALPHA;
69    surface->w         = width;
70    surface->h         = height;
71    surface->pitch     = LRSDL_CalculatePitch(surface);
72    surface->pixels    = NULL;
73    surface->offset    = 0;
74    surface->hwdata    = NULL;
75    surface->locked    = 0;
76    surface->map       = NULL;
77    surface->unused1   = 0;
78    LRSDL_SetClipRect(surface, NULL);
79    LRSDL_FormatChanged(surface);
80 
81    /* Get the pixels */
82    if ( ((flags&SDL_HWSURFACE) == SDL_SWSURFACE))
83    {
84       if ( surface->w && surface->h )
85       {
86          surface->pixels = SDL_malloc(surface->h*surface->pitch);
87 
88          if (!surface->pixels)
89          {
90             LRSDL_OutOfMemory();
91             goto error;
92          }
93 
94          /* This is important for bitmaps */
95          SDL_memset(surface->pixels, 0, surface->h*surface->pitch);
96       }
97    }
98 
99    /* Allocate an empty mapping */
100    surface->map = LRSDL_AllocBlitMap();
101    if (!surface->map)
102       goto error;
103 
104    /* The surface is ready to go */
105    surface->refcount = 1;
106    return surface;
107 
108 error:
109    if (surface)
110       LRSDL_FreeSurface(surface);
111 
112    return NULL;
113 }
114 
115 /*
116  * Create an RGB surface from an existing memory buffer
117  */
LRSDL_CreateRGBSurfaceFrom(void * pixels,int width,int height,int depth,int pitch,Uint32 Rmask,Uint32 Gmask,Uint32 Bmask,Uint32 Amask)118 SDL_Surface * LRSDL_CreateRGBSurfaceFrom (void *pixels,
119       int width, int height, int depth, int pitch,
120       Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
121 {
122    SDL_Surface *surface = LRSDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0, depth,
123          Rmask, Gmask, Bmask, Amask);
124 
125    if (!surface)
126       return NULL;
127 
128    surface->flags |= SDL_PREALLOC;
129    surface->pixels = pixels;
130    surface->w      = width;
131    surface->h      = height;
132    surface->pitch  = pitch;
133 
134    LRSDL_SetClipRect(surface, NULL);
135 
136    return surface;
137 }
138 
139 /*
140  * Set the color key in a blittable surface
141  */
LRSDL_SetColorKey(SDL_Surface * surface,Uint32 flag,Uint32 key)142 int LRSDL_SetColorKey (SDL_Surface *surface, Uint32 flag, Uint32 key)
143 {
144    /* Sanity check the flag as it gets passed in */
145    if ( flag & SDL_SRCCOLORKEY )
146    {
147       if ( flag & (SDL_RLEACCEL|SDL_RLEACCELOK) )
148          flag = (SDL_SRCCOLORKEY | SDL_RLEACCELOK);
149       else
150          flag = SDL_SRCCOLORKEY;
151    }
152    else
153       flag = 0;
154 
155    /* Optimize away operations that don't change anything */
156    if ( (flag == (surface->flags & (SDL_SRCCOLORKEY|SDL_RLEACCELOK))) &&
157          (key == surface->format->colorkey) )
158       return 0;
159 
160    if ( flag )
161    {
162       surface->flags            |= SDL_SRCCOLORKEY;
163       surface->format->colorkey  = key;
164 
165       if ( flag & SDL_RLEACCELOK )
166          surface->flags |= SDL_RLEACCELOK;
167       else
168          surface->flags &= ~SDL_RLEACCELOK;
169    }
170    else
171    {
172       surface->flags            &= ~(SDL_SRCCOLORKEY|SDL_RLEACCELOK);
173       surface->format->colorkey  = 0;
174    }
175    LRSDL_InvalidateMap(surface->map);
176    return(0);
177 }
178 /* This function sets the alpha channel of a surface */
LRSDL_SetAlpha(SDL_Surface * surface,Uint32 flag,Uint8 value)179 int LRSDL_SetAlpha (SDL_Surface *surface, Uint32 flag, Uint8 value)
180 {
181    uint32_t oldflags = surface->flags;
182    uint32_t oldalpha = surface->format->alpha;
183 
184    /* Sanity check the flag as it gets passed in */
185    if ( flag & SDL_SRCALPHA ) {
186       if ( flag & (SDL_RLEACCEL|SDL_RLEACCELOK) )
187          flag = (SDL_SRCALPHA | SDL_RLEACCELOK);
188       else
189          flag = SDL_SRCALPHA;
190    }
191    else
192       flag = 0;
193 
194    /* Optimize away operations that don't change anything */
195    if ( (flag == (surface->flags & (SDL_SRCALPHA|SDL_RLEACCELOK))) &&
196          (!flag || value == oldalpha) )
197       return 0;
198 
199    if ( flag )
200    {
201       surface->flags         |= SDL_SRCALPHA;
202       surface->format->alpha  = value;
203 
204       if ( flag & SDL_RLEACCELOK )
205          surface->flags |= SDL_RLEACCELOK;
206       else
207          surface->flags &= ~SDL_RLEACCELOK;
208    }
209    else
210    {
211       surface->flags         &= ~SDL_SRCALPHA;
212       surface->format->alpha  = SDL_ALPHA_OPAQUE;
213    }
214    /*
215     * The representation for software surfaces is independent of
216     * per-surface alpha, so no need to invalidate the blit mapping
217     * if just the alpha value was changed. (If either is 255, we still
218     * need to invalidate.)
219     */
220    if(oldflags != surface->flags || (((oldalpha + 1) ^ (value + 1)) & 0x100))
221       LRSDL_InvalidateMap(surface->map);
222 
223    return 0;
224 }
225 
LRSDL_SetAlphaChannel(SDL_Surface * surface,Uint8 value)226 int LRSDL_SetAlphaChannel(SDL_Surface *surface, Uint8 value)
227 {
228    int row;
229    int offset;
230 
231    if ( (surface->format->Amask != 0xFF000000) &&
232          (surface->format->Amask != 0x000000FF) )
233    {
234       LRSDL_SetError("Unsupported surface alpha mask format");
235       return -1;
236    }
237 
238 #ifdef MSB_FIRST
239    offset = ( surface->format->Amask == 0xFF000000 ) ? 0 : 3;
240 #else
241    offset = ( surface->format->Amask == 0xFF000000 ) ? 3 : 0;
242 #endif
243    row    = surface->h;
244 
245    while (row--)
246    {
247       int      col = surface->w;
248       uint8_t *buf = (uint8_t*)surface->pixels + row * surface->pitch + offset;
249 
250       while(col--)
251       {
252          *buf = value;
253          buf += 4;
254       }
255    }
256    return 0;
257 }
258 
259 /*
260  * A function to calculate the intersection of two rectangles:
261  * return true if the rectangles intersect, false otherwise
262  */
263    static __inline__
LRSDL_IntersectRect(const SDL_Rect * A,const SDL_Rect * B,SDL_Rect * intersection)264 LRSDL_bool LRSDL_IntersectRect(const SDL_Rect *A,
265       const SDL_Rect *B, SDL_Rect *intersection)
266 {
267    /* Horizontal intersection */
268    int Amin = A->x;
269    int Amax = Amin + A->w;
270    int Bmin = B->x;
271    int Bmax = Bmin + B->w;
272 
273    if(Bmin > Amin)
274       Amin = Bmin;
275 
276    intersection->x = Amin;
277 
278    if(Bmax < Amax)
279       Amax = Bmax;
280    intersection->w = Amax - Amin > 0 ? Amax - Amin : 0;
281 
282    /* Vertical intersection */
283    Amin = A->y;
284    Amax = Amin + A->h;
285    Bmin = B->y;
286    Bmax = Bmin + B->h;
287    if(Bmin > Amin)
288       Amin = Bmin;
289    intersection->y = Amin;
290    if(Bmax < Amax)
291       Amax = Bmax;
292    intersection->h = Amax - Amin > 0 ? Amax - Amin : 0;
293 
294    return (LRSDL_bool)(intersection->w && intersection->h);
295 }
296 
297 /*
298  * Set the clipping rectangle for a blittable surface
299  */
LRSDL_SetClipRect(SDL_Surface * surface,const SDL_Rect * rect)300 LRSDL_bool LRSDL_SetClipRect(SDL_Surface *surface, const SDL_Rect *rect)
301 {
302    SDL_Rect full_rect;
303 
304    /* Don't do anything if there's no surface to act on */
305    if ( ! surface )
306       return LRSDL_FALSE;
307 
308    /* Set up the full surface rectangle */
309    full_rect.x = 0;
310    full_rect.y = 0;
311    full_rect.w = surface->w;
312    full_rect.h = surface->h;
313 
314    /* Set the clipping rectangle */
315    if (!rect)
316    {
317       surface->clip_rect = full_rect;
318       return (LRSDL_bool)1;
319    }
320 
321    return LRSDL_IntersectRect(rect, &full_rect, &surface->clip_rect);
322 }
323 
324 /*
325  * Set up a blit between two surfaces -- split into three parts:
326  * The upper part, SDL_UpperBlit(), performs clipping and rectangle
327  * verification.  The lower part is a pointer to a low level
328  * accelerated blitting function.
329  *
330  * These parts are separated out and each used internally by this
331  * library in the optimimum places.  They are exported so that if
332  * you know exactly what you are doing, you can optimize your code
333  * by calling the one(s) you need.
334  */
LRSDL_LowerBlit(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)335 int LRSDL_LowerBlit (SDL_Surface *src, SDL_Rect *srcrect,
336       SDL_Surface *dst, SDL_Rect *dstrect)
337 {
338    /* Check to make sure the blit mapping is valid */
339    if ( (src->map->dst != dst) ||
340          (src->map->dst->format_version != src->map->format_version) )
341    {
342       if ( LRSDL_MapSurface(src, dst) < 0 )
343          return -1;
344    }
345 
346    return(src->map->sw_blit(src, srcrect, dst, dstrect));
347 }
348 
349 
LRSDL_UpperBlit(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)350 int LRSDL_UpperBlit (SDL_Surface *src, SDL_Rect *srcrect,
351       SDL_Surface *dst, SDL_Rect *dstrect)
352 {
353    SDL_Rect sr;
354    SDL_Rect fulldst;
355    int srcx = 0;
356    int srcy = 0;
357    int w    = src->w;
358    int h    = src->h;
359 
360    /* If the destination rectangle is NULL, use the entire dest surface */
361    if (!dstrect)
362    {
363       fulldst.x = fulldst.y = 0;
364       dstrect   = &fulldst;
365    }
366 
367    /* clip the source rectangle to the source surface */
368    if(srcrect)
369    {
370       int maxw, maxh;
371       srcx = srcrect->x;
372       w    = srcrect->w;
373 
374       if(srcx < 0)
375       {
376          w += srcx;
377          dstrect->x -= srcx;
378          srcx = 0;
379       }
380 
381       maxw = src->w - srcx;
382 
383       if(maxw < w)
384          w = maxw;
385 
386       srcy = srcrect->y;
387       h    = srcrect->h;
388 
389       if(srcy < 0)
390       {
391          h += srcy;
392          dstrect->y -= srcy;
393          srcy = 0;
394       }
395       maxh = src->h - srcy;
396       if(maxh < h)
397          h = maxh;
398 
399    }
400 
401    /* clip the destination rectangle against the clip rectangle */
402    {
403       int dy;
404       SDL_Rect *clip = &dst->clip_rect;
405       int         dx = clip->x - dstrect->x;
406 
407       if(dx > 0)
408       {
409          w -= dx;
410          dstrect->x += dx;
411          srcx += dx;
412       }
413 
414       dx = dstrect->x + w - clip->x - clip->w;
415 
416       if(dx > 0)
417          w -= dx;
418 
419       dy = clip->y - dstrect->y;
420 
421       if(dy > 0)
422       {
423          h -= dy;
424          dstrect->y += dy;
425          srcy += dy;
426       }
427 
428       dy = dstrect->y + h - clip->y - clip->h;
429 
430       if(dy > 0)
431          h -= dy;
432    }
433 
434    if(w <=  0 || h <= 0)
435    {
436       dstrect->w = 0;
437       dstrect->h = 0;
438       return 0;
439    }
440 
441    sr.x = srcx;
442    sr.y = srcy;
443    sr.w = dstrect->w = w;
444    sr.h = dstrect->h = h;
445    return LRSDL_LowerBlit(src, &sr, dst, dstrect);
446 }
447 
448 /*
449  * This function performs a fast fill of the given rectangle with 'color'
450  */
LRSDL_FillRect(SDL_Surface * dst,SDL_Rect * dstrect,uint32_t color)451 int LRSDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, uint32_t color)
452 {
453    int x, y;
454    uint8_t *row;
455 
456    if ( dstrect )
457    {
458       /* Perform clipping */
459       if ( !LRSDL_IntersectRect(dstrect, &dst->clip_rect, dstrect) )
460          return(0);
461    }
462    else  /* If 'dstrect' == NULL, then fill the whole surface */
463       dstrect = &dst->clip_rect;
464 
465    row = (uint8_t*)dst->pixels+dstrect->y*dst->pitch+
466       dstrect->x*dst->format->BytesPerPixel;
467 
468    if ( dst->format->palette || (color == 0) )
469    {
470       x = dstrect->w*dst->format->BytesPerPixel;
471       if ( !color && !((uintptr_t)row&3) && !(x&3) && !(dst->pitch&3) )
472       {
473          int n = x >> 2;
474          for ( y=dstrect->h; y; --y )
475          {
476             SDL_memset4(row, 0, n);
477             row += dst->pitch;
478          }
479       }
480       else
481       {
482          for(y = dstrect->h; y; y--)
483          {
484             SDL_memset(row, color, x);
485             row += dst->pitch;
486          }
487       }
488    }
489    else
490    {
491       switch (dst->format->BytesPerPixel)
492       {
493          case 2:
494             for ( y=dstrect->h; y; --y )
495             {
496                uint16_t *pixels = (uint16_t*)row;
497                uint16_t c       = (uint16_t)color;
498                uint32_t cc      = (uint32_t)c << 16 | c;
499                int n            = dstrect->w;
500 
501                if((uintptr_t)pixels & 3)
502                {
503                   *pixels++ = c;
504                   n--;
505                }
506                if(n >> 1)
507                   SDL_memset4(pixels, cc, n >> 1);
508                if(n & 1)
509                   pixels[n - 1] = c;
510                row += dst->pitch;
511             }
512             break;
513 
514          case 3:
515 #ifdef MSB_FIRST
516             color <<= 8;
517 #endif
518             for ( y=dstrect->h; y; --y )
519             {
520                uint8_t *pixels = row;
521 
522                for ( x=dstrect->w; x; --x )
523                {
524                   memcpy(pixels, &color, 3);
525                   pixels += 3;
526                }
527                row += dst->pitch;
528             }
529             break;
530 
531          case 4:
532             for(y = dstrect->h; y; --y)
533             {
534                SDL_memset4(row, color, dstrect->w);
535                row += dst->pitch;
536             }
537             break;
538       }
539    }
540 
541    return 0;
542 }
543 
544 /*
545  * Lock a surface to directly access the pixels
546  */
LRSDL_LockSurface(SDL_Surface * surface)547 int LRSDL_LockSurface (SDL_Surface *surface)
548 {
549    return 0;
550 }
551 /*
552  * Unlock a previously locked surface
553  */
LRSDL_UnlockSurface(SDL_Surface * surface)554 void LRSDL_UnlockSurface (SDL_Surface *surface)
555 {
556    surface->pixels = (uint8_t*)surface->pixels - surface->offset;
557 }
558 
559 /*
560  * Convert a surface into the specified pixel format.
561  */
LRSDL_ConvertSurface(SDL_Surface * surface,SDL_PixelFormat * format,Uint32 flags)562 SDL_Surface * LRSDL_ConvertSurface (SDL_Surface *surface,
563 					SDL_PixelFormat *format, Uint32 flags)
564 {
565    SDL_Surface *convert;
566    uint32_t surface_flags;
567    uint32_t colorkey = 0;
568    uint8_t alpha = 0;
569    SDL_Rect bounds;
570 
571    /* Check for empty destination palette! (results in empty image) */
572    if ( format->palette != NULL )
573    {
574       int i;
575       for ( i=0; i<format->palette->ncolors; ++i ) {
576          if ( (format->palette->colors[i].r != 0) ||
577                (format->palette->colors[i].g != 0) ||
578                (format->palette->colors[i].b != 0) )
579             break;
580       }
581       if ( i == format->palette->ncolors ) {
582          LRSDL_SetError("Empty destination palette");
583          return(NULL);
584       }
585    }
586 
587    flags &= ~SDL_HWSURFACE;
588 
589    /* Create a new surface with the desired format */
590    convert = LRSDL_CreateRGBSurface(flags,
591          surface->w, surface->h, format->BitsPerPixel,
592          format->Rmask, format->Gmask, format->Bmask, format->Amask);
593 
594    if ( convert == NULL )
595       return(NULL);
596 
597    /* Copy the palette if any */
598    if ( format->palette && convert->format->palette ) {
599       SDL_memcpy(convert->format->palette->colors,
600             format->palette->colors,
601             format->palette->ncolors*sizeof(SDL_Color));
602       convert->format->palette->ncolors = format->palette->ncolors;
603    }
604 
605    /* Save the original surface color key and alpha */
606    surface_flags = surface->flags;
607    if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
608       /* Convert colourkeyed surfaces to RGBA if requested */
609       if((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY
610             && format->Amask)
611          surface_flags &= ~SDL_SRCCOLORKEY;
612       else
613       {
614          colorkey = surface->format->colorkey;
615          LRSDL_SetColorKey(surface, 0, 0);
616       }
617    }
618    if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
619       /* Copy over the alpha channel to RGBA if requested */
620       if ( format->Amask )
621          surface->flags &= ~SDL_SRCALPHA;
622       else
623       {
624          alpha = surface->format->alpha;
625          LRSDL_SetAlpha(surface, 0, 0);
626       }
627    }
628 
629    /* Copy over the image data */
630    bounds.x = 0;
631    bounds.y = 0;
632    bounds.w = surface->w;
633    bounds.h = surface->h;
634    LRSDL_LowerBlit(surface, &bounds, convert, &bounds);
635 
636    /* Clean up the original surface, and update converted surface */
637    if ( convert != NULL )
638       LRSDL_SetClipRect(convert, &surface->clip_rect);
639 
640    if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY )
641    {
642       uint32_t cflags = surface_flags&(SDL_SRCCOLORKEY|SDL_RLEACCELOK);
643       if (convert)
644       {
645          uint8_t keyR, keyG, keyB;
646 
647          LRSDL_GetRGB(colorkey,surface->format,&keyR,&keyG,&keyB);
648          LRSDL_SetColorKey(convert, cflags|(flags&SDL_RLEACCELOK),
649                LRSDL_MapRGB(convert->format, keyR, keyG, keyB));
650       }
651       LRSDL_SetColorKey(surface, cflags, colorkey);
652    }
653 
654    if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA )
655    {
656       uint32_t aflags = surface_flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
657       if ( convert != NULL )
658          LRSDL_SetAlpha(convert, aflags|(flags&SDL_RLEACCELOK),
659                alpha);
660 
661       if ( format->Amask )
662          surface->flags |= SDL_SRCALPHA;
663       else
664          LRSDL_SetAlpha(surface, aflags, alpha);
665    }
666 
667    /* We're ready to go! */
668    return convert;
669 }
670 
671 /*
672  * Free a surface created by the above function.
673  */
LRSDL_FreeSurface(SDL_Surface * surface)674 void LRSDL_FreeSurface (SDL_Surface *surface)
675 {
676    /* Free anything that's not NULL, and not the screen surface */
677    if (!surface)
678       return;
679 
680    if ( --surface->refcount > 0 )
681       return;
682 
683    if (surface->format)
684    {
685       LRSDL_FreeFormat(surface->format);
686       surface->format = NULL;
687    }
688 
689    if (surface->map)
690    {
691       LRSDL_FreeBlitMap(surface->map);
692       surface->map = NULL;
693    }
694 
695    if ( surface->pixels &&
696          ((surface->flags & SDL_PREALLOC) != SDL_PREALLOC) )
697       SDL_free(surface->pixels);
698    SDL_free(surface);
699 }
700