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