1 /*
2     pygame - Python Game Library
3     Copyright (C) 2000-2001  Pete Shinners
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 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     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19     Pete Shinners
20     pete@shinners.org
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <fc_config.h>
25 #endif
26 
27 /* SDL */
28 #include <SDL/SDL.h>
29 
30 /* utility.h */
31 #include "shared.h"
32 
33 int pygame_AlphaBlit (SDL_Surface *src, SDL_Rect *srcrect,
34                    SDL_Surface *dst, SDL_Rect *dstrect);
35 
36 /* The structure passed to the low level blit functions */
37 typedef struct {
38         Uint8 *s_pixels;
39         int s_width;
40         int s_height;
41         int s_skip;
42         Uint8 *d_pixels;
43         int d_width;
44         int d_height;
45         int d_skip;
46         void *aux_data;
47         SDL_PixelFormat *src;
48         Uint8 *table;
49         SDL_PixelFormat *dst;
50 } SDL_BlitInfo;
51 static void alphablit_alpha(SDL_BlitInfo *info);
52 static void alphablit_colorkey(SDL_BlitInfo *info);
53 static void alphablit_solid(SDL_BlitInfo *info);
54 static int SoftBlitAlpha(SDL_Surface *src, SDL_Rect *srcrect,
55                         SDL_Surface *dst, SDL_Rect *dstrect);
56 extern int SDL_RLESurface(SDL_Surface *surface);
57 extern void SDL_UnRLESurface(SDL_Surface *surface, int recode);
58 
59 
60 /**********************************************************************
61   Blit from source rectangle to destination rectangle with alpha.
62   If dstrect is NULL, full dst surface is used.
63   We assume the "dst" has pixel alpha.
64 ***********************************************************************/
pygame_AlphaBlit(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)65 int pygame_AlphaBlit (SDL_Surface *src, SDL_Rect *srcrect,
66                    SDL_Surface *dst, SDL_Rect *dstrect)
67 {
68         SDL_Rect fulldst;
69         int srcx, srcy, w, h;
70 
71         /* Make sure the surfaces aren't locked */
72         if ( ! src || ! dst ) {
73                 SDL_SetError("SDL_UpperBlit: passed a NULL surface");
74                 return(-1);
75         }
76         if ( src->locked || dst->locked ) {
77                 SDL_SetError("Surfaces must not be locked during blit");
78                 return(-1);
79         }
80 
81         /* If the destination rectangle is NULL, use the entire dest surface */
82         if ( dstrect == NULL ) {
83                 fulldst.x = fulldst.y = 0;
84                 dstrect = &fulldst;
85         }
86 
87         /* clip the source rectangle to the source surface */
88         if(srcrect) {
89                 int maxw, maxh;
90 
91                 srcx = srcrect->x;
92                 w = srcrect->w;
93                 if(srcx < 0) {
94                         w += srcx;
95                         dstrect->x -= srcx;
96                         srcx = 0;
97                 }
98                 maxw = src->w - srcx;
99                 if(maxw < w)
100                         w = maxw;
101 
102                 srcy = srcrect->y;
103                 h = srcrect->h;
104                 if(srcy < 0) {
105                         h += srcy;
106                         dstrect->y -= srcy;
107                         srcy = 0;
108                 }
109                 maxh = src->h - srcy;
110                 if(maxh < h)
111                         h = maxh;
112 
113         } else {
114                 srcx = srcy = 0;
115                 w = src->w;
116                 h = src->h;
117         }
118 
119         /* clip the destination rectangle against the clip rectangle */
120         {
121                 SDL_Rect *clip = &dst->clip_rect;
122                 int dx, dy;
123 
124                 dx = clip->x - dstrect->x;
125                 if(dx > 0) {
126                         w -= dx;
127                         dstrect->x += dx;
128                         srcx += dx;
129                 }
130                 dx = dstrect->x + w - clip->x - clip->w;
131                 if(dx > 0)
132                         w -= dx;
133 
134                 dy = clip->y - dstrect->y;
135                 if(dy > 0) {
136                         h -= dy;
137                         dstrect->y += dy;
138                         srcy += dy;
139                 }
140                 dy = dstrect->y + h - clip->y - clip->h;
141                 if(dy > 0)
142                         h -= dy;
143         }
144 
145         if(w > 0 && h > 0) {
146                 SDL_Rect sr;
147                 sr.x = srcx;
148                 sr.y = srcy;
149                 sr.w = dstrect->w = w;
150                 sr.h = dstrect->h = h;
151                 return SoftBlitAlpha(src, &sr, dst, dstrect);
152         }
153         dstrect->w = dstrect->h = 0;
154         return 0;
155 }
156 
157 /**********************************************************************
158   Blit using alpha from src to dest. Rectangles are assumed to be
159   already clipped.
160 ***********************************************************************/
SoftBlitAlpha(SDL_Surface * src,SDL_Rect * srcrect,SDL_Surface * dst,SDL_Rect * dstrect)161 static int SoftBlitAlpha(SDL_Surface *src, SDL_Rect *srcrect,
162                         SDL_Surface *dst, SDL_Rect *dstrect)
163 {
164         int okay;
165         int src_locked;
166         int dst_locked;
167 
168     /* Everything is okay at the beginning...  */
169         okay = 1;
170 
171         /* Lock the destination if it's in hardware */
172         dst_locked = 0;
173         if ( SDL_MUSTLOCK(dst) ) {
174                 if ( SDL_LockSurface(dst) < 0 )
175                         okay = 0;
176                 else
177                         dst_locked = 1;
178         }
179         /* Lock the source if it's in hardware */
180         src_locked = 0;
181         if ( SDL_MUSTLOCK(src) ) {
182                 if ( SDL_LockSurface(src) < 0 )
183                         okay = 0;
184                 else
185                         src_locked = 1;
186         }
187 
188         /* Set up source and destination buffer pointers, and BLIT! */
189         if ( okay  && srcrect->w && srcrect->h ) {
190                 SDL_BlitInfo info;
191 
192                 /* Set up the blit information */
193                 info.s_pixels = (Uint8 *)src->pixels + src->offset +
194                                 (Uint16)srcrect->y*src->pitch +
195                                 (Uint16)srcrect->x*src->format->BytesPerPixel;
196                 info.s_width = srcrect->w;
197                 info.s_height = srcrect->h;
198                 info.s_skip=src->pitch-info.s_width*src->format->BytesPerPixel;
199                 info.d_pixels = (Uint8 *)dst->pixels + dst->offset +
200                                 (Uint16)dstrect->y*dst->pitch +
201                                 (Uint16)dstrect->x*dst->format->BytesPerPixel;
202                 info.d_width = dstrect->w;
203                 info.d_height = dstrect->h;
204                 info.d_skip=dst->pitch-info.d_width*dst->format->BytesPerPixel;
205                 info.src = src->format;
206                 info.dst = dst->format;
207 
208                 if(src->flags&SDL_SRCALPHA && src->format->Amask)
209                     alphablit_alpha(&info);
210                 else if(src->flags & SDL_SRCCOLORKEY)
211                     alphablit_colorkey(&info);
212                 else
213                     alphablit_solid(&info);
214         }
215 
216         /* We need to unlock the surfaces if they're locked */
217         if ( dst_locked )
218                 SDL_UnlockSurface(dst);
219         if ( src_locked )
220                 SDL_UnlockSurface(src);
221         /* Blit is done! */
222         return(okay ? 0 : -1);
223 }
224 
225 
226 #define GET_PIXEL(buf, bpp, fmt, pixel)                    \
227 do {                                                                       \
228         switch (bpp) {                                                           \
229                 case 1:                                                           \
230                         pixel = *((Uint8 *)(buf));                           \
231                 break;                                                       \
232                 case 2:                                                           \
233                         pixel = *((Uint16 *)(buf));                           \
234                 break;                                                           \
235                 case 4:                                                           \
236                         pixel = *((Uint32 *)(buf));                           \
237                 break;                                                           \
238                 default:        {/* case 3: FIXME: broken code (no alpha) */                   \
239                         Uint8 *b = (Uint8 *)buf;                           \
240                         if(SDL_BYTEORDER == SDL_LIL_ENDIAN) {                   \
241                                 pixel = b[0] + (b[1] << 8) + (b[2] << 16); \
242                         } else {                                           \
243                                 pixel = (b[0] << 16) + (b[1] << 8) + b[2]; \
244                         }                                                   \
245                 }                                                           \
246                 break;                                                           \
247         }                                                                   \
248 } while(0)
249 
250 
251 #define DISEMBLE_RGB(buf, bpp, fmt, pixel, R, G, B)               \
252 do {                                                              \
253   Uint8 *b = (Uint8 *)buf;                                        \
254   if (bpp == 1) {                                                 \
255     pixel = *((Uint8 *)(buf));                                    \
256     R = fmt->palette->colors[pixel].r;                            \
257     G = fmt->palette->colors[pixel].g;                            \
258     B = fmt->palette->colors[pixel].b;                            \
259   } else {                                                        \
260     switch (bpp) {                                                \
261     case 2:                                                       \
262       if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {                      \
263         pixel = b[0] + (b[1] << 8);                               \
264       } else {                                                    \
265         pixel = (b[0] << 8) + b[1];                               \
266       }                                                           \
267       break;                                                      \
268     case 4:                                                       \
269       if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {                      \
270         pixel = b[0] + (b[1] << 8) + (b[2] << 16) + (b[3] << 24); \
271       } else {                                                    \
272         pixel = (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; \
273       }                                                           \
274       break;                                                      \
275     default:                                                      \
276       { /* case 3: FIXME: broken code (no alpha) */               \
277         if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {                    \
278           pixel = b[0] + (b[1] << 8) + (b[2] << 16);              \
279         } else {                                                  \
280           pixel = (b[0] << 16) + (b[1] << 8) + b[2];              \
281         }                                                         \
282       }                                                           \
283       break;                                                      \
284     }                                                             \
285     R = ((pixel & fmt->Rmask) >> fmt->Rshift) << fmt->Rloss;      \
286     G = ((pixel & fmt->Gmask) >> fmt->Gshift) << fmt->Gloss;      \
287     B = ((pixel & fmt->Bmask) >> fmt->Bshift) << fmt->Bloss;      \
288   }                                                               \
289 } while(FALSE);
290 
291 #define DISEMBLE_RGBA(buf, bpp, fmt, pixel, R, G, B, A)           \
292 do {                                                              \
293   DISEMBLE_RGB(buf, bpp, fmt, pixel, R, G, B);                    \
294   if (bpp == 1) {                                                 \
295     A = 255;                                                      \
296   } else {                                                        \
297     A = ((pixel & fmt->Amask) >> fmt->Ashift) << fmt->Aloss;      \
298   }                                                               \
299 } while(FALSE);
300 
301 #define PIXEL_FROM_RGBA(pixel, fmt, r, g, b, a)                   \
302 {                                                                 \
303   pixel = ((r >> fmt->Rloss) << fmt->Rshift) |                    \
304           ((g >> fmt->Gloss) << fmt->Gshift) |                    \
305           ((b >> fmt->Bloss) << fmt->Bshift) |                    \
306           ((a << fmt->Aloss) << fmt->Ashift);                     \
307 }
308 
309 #define ASSEMBLE_RGBA(buf, bpp, fmt, r, g, b, a)                  \
310 {                                                                 \
311   switch (bpp) {                                                  \
312   case 2:                                                         \
313   {                                                               \
314     Uint16 pxl;                                                   \
315     PIXEL_FROM_RGBA(pxl, fmt, r, g, b, a);                        \
316     if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {                        \
317       buf[0] = (pxl & 0x00ff);                                    \
318       buf[1] = (pxl & 0xff00) >> 8;                               \
319     } else {                                                      \
320       buf[0] = (pxl & 0xff00) >> 8;                               \
321       buf[1] = (pxl & 0x00ff);                                    \
322     }                                                             \
323   }                                                               \
324   break;                                                          \
325   case 4:                                                         \
326   {                                                               \
327     Uint32 pxl;                                                   \
328     PIXEL_FROM_RGBA(pxl, fmt, r, g, b, a);                        \
329     if (SDL_BYTEORDER == SDL_LIL_ENDIAN) {                        \
330       buf[0] = (pxl & 0x000000ff);                                \
331       buf[1] = (pxl & 0x0000ff00) >> 8;                           \
332       buf[2] = (pxl & 0x00ff0000) >> 16;                          \
333       buf[3] = (pxl & 0xff000000) >> 24;                          \
334     } else {                                                      \
335       buf[3] = (pxl & 0x000000ff);                                \
336       buf[2] = (pxl & 0x0000ff00) >> 8;                           \
337       buf[1] = (pxl & 0x00ff0000) >> 16;                          \
338       buf[0] = (pxl & 0xff000000) >> 24;                          \
339     }                                                             \
340   }                                                               \
341   break;                                                          \
342   }                                                               \
343 }
344 
345 #if 0
346 #define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA)  \
347 do {                                            \
348         dR = (((sR-dR)*(sA))>>8)+dR;                \
349         dG = (((sG-dG)*(sA))>>8)+dG;                \
350         dB = (((sB-dB)*(sA))>>8)+dB;                \
351         dA = sA+dA - ((sA*dA)/255);                \
352 } while(0)
353 #else /* 0 */
354 #define ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA)  \
355 do {   if(dA){\
356         dR = ( ((255-sA)*dR ) + ((sR*sA)) ) >> 8;                \
357         dG = ( ((255-sA)*dG ) + ((sG*sA)) ) >> 8;                \
358         dB = ( ((255-sA)*dB ) + ((sB*sA)) ) >> 8;                \
359         dA = sA+dA - ((sA*dA)/255);                \
360     }else{\
361         dR = sR;                \
362         dG = sG;                \
363         dB = sB;                \
364         dA = sA;               \
365     }\
366 } while(0)
367 #endif /* !0 */
368 
369 #if 0
370 /* a sad tale of many other blending techniques that didn't fly */
371     if(0&&dA){\
372         dR = (((255-sA)*(dR<<8)/dA)) + (sR>>8) ) >> 8;                \
373         dG = (((255-sA)*(dG<<8)/dA)) + (sG>>8) ) >> 8;                \
374         dB = (((255-sA)*(dB<<8)/dA)) + (sB>>8) ) >> 8;                \
375         dA = sA+dA - ((sA*dA)>>8);               \
376     }else{\
377         dR = 255;                \
378         dG = 0;                \
379         dB = 255;                \
380         dA = 255;               \
381     }\
382 
383 
384         int temp; \
385         temp = (((sR-dR)*(sA))>>8)+dR; dR = (((sR-temp)*(255-dA))>>8)+temp; \
386         temp = (((sG-dG)*(sA))>>8)+dG; dG = (((sG-temp)*(255-dA))>>8)+temp; \
387         temp = (((sB-dB)*(sA))>>8)+dB; dB = (((sB-temp)*(255-dA))>>8)+temp; \
388 
389         temp = (((sR-dR)*(sA))>>8)+dR; dR = (((temp-sR)*dA)>>8)+sR; \
390         temp = (((sG-dG)*(sA))>>8)+dG; dG = (((temp-sG)*dA)>>8)+sG; \
391         temp = (((sB-dB)*(sA))>>8)+dB; dB = (((temp-sB)*dA)>>8)+sB; \
392 
393         dR = (((dR - sR) * (255-sA) * dA) >> 16) + (sR*sA)>>8);
394         dG = (((dG - sG) * (255-sA) * dA) >> 16) + (sG*sA)>>8);
395         dB = (((dB - sB) * (255-sA) * dA) >> 16) + (sB*sA)>>8);
396 #endif /* 0 */
397 
398 
399 /**********************************************************************
400   Basic alpha blitting
401 ***********************************************************************/
alphablit_alpha(SDL_BlitInfo * info)402 static void alphablit_alpha(SDL_BlitInfo *info)
403 {
404         int n;
405         int width = info->d_width;
406         int height = info->d_height;
407         Uint8 *src = info->s_pixels;
408         int srcskip = info->s_skip;
409         Uint8 *dst = info->d_pixels;
410         int dstskip = info->d_skip;
411         SDL_PixelFormat *srcfmt = info->src;
412         SDL_PixelFormat *dstfmt = info->dst;
413         int srcbpp = srcfmt->BytesPerPixel;
414         int dstbpp = dstfmt->BytesPerPixel;
415         int dR, dG, dB, dA, sR, sG, sB, sA;
416 
417         while ( height-- )
418         {
419             for(n=width; n>0; --n)
420             {
421                 Uint32 pixel;
422                 DISEMBLE_RGBA(src, srcbpp, srcfmt, pixel, sR, sG, sB, sA);
423                 DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
424                 ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA);
425                 ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
426                 src += srcbpp;
427                 dst += dstbpp;
428             }
429             src += srcskip;
430             dst += dstskip;
431         }
432 }
433 
434 /**********************************************************************
435   Blit using colorkey
436 ***********************************************************************/
alphablit_colorkey(SDL_BlitInfo * info)437 static void alphablit_colorkey(SDL_BlitInfo *info)
438 {
439         int n;
440         int width = info->d_width;
441         int height = info->d_height;
442         Uint8 *src = info->s_pixels;
443         int srcskip = info->s_skip;
444         Uint8 *dst = info->d_pixels;
445         int dstskip = info->d_skip;
446         SDL_PixelFormat *srcfmt = info->src;
447         SDL_PixelFormat *dstfmt = info->dst;
448         int srcbpp = srcfmt->BytesPerPixel;
449         int dstbpp = dstfmt->BytesPerPixel;
450         int dR, dG, dB, dA, sR, sG, sB, sA;
451         int alpha = srcfmt->alpha;
452         Uint32 colorkey = srcfmt->colorkey;
453 
454         while ( height-- )
455         {
456             for(n=width; n>0; --n)
457             {
458                 Uint32 pixel;
459                 DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
460                 DISEMBLE_RGBA(src, srcbpp, srcfmt, pixel, sR, sG, sB, sA);
461                 sA = (pixel == colorkey) ? 0 : alpha;
462                 ALPHA_BLEND(sR, sG, sB, sA, dR, dG, dB, dA);
463                 ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
464                 src += srcbpp;
465                 dst += dstbpp;
466             }
467             src += srcskip;
468             dst += dstskip;
469         }
470 }
471 
472 /**********************************************************************
473   Blit solid surface
474 ***********************************************************************/
alphablit_solid(SDL_BlitInfo * info)475 static void alphablit_solid(SDL_BlitInfo *info)
476 {
477         int n;
478         int width = info->d_width;
479         int height = info->d_height;
480         Uint8 *src = info->s_pixels;
481         int srcskip = info->s_skip;
482         Uint8 *dst = info->d_pixels;
483         int dstskip = info->d_skip;
484         SDL_PixelFormat *srcfmt = info->src;
485         SDL_PixelFormat *dstfmt = info->dst;
486         int srcbpp = srcfmt->BytesPerPixel;
487         int dstbpp = dstfmt->BytesPerPixel;
488         int dR, dG, dB, dA, sR, sG, sB;
489         int alpha = srcfmt->alpha;
490 
491         while ( height-- )
492         {
493             for(n=width; n>0; --n)
494             {
495                 int pixel;
496                 DISEMBLE_RGBA(dst, dstbpp, dstfmt, pixel, dR, dG, dB, dA);
497                 DISEMBLE_RGB(src, srcbpp, srcfmt, pixel, sR, sG, sB);
498                 ALPHA_BLEND(sR, sG, sB, alpha, dR, dG, dB, dA);
499                 ASSEMBLE_RGBA(dst, dstbpp, dstfmt, dR, dG, dB, dA);
500                 src += srcbpp;
501                 dst += dstbpp;
502             }
503             src += srcskip;
504             dst += dstskip;
505         }
506 }
507