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