1 /*
2    t4k_sdl.c
3 
4    Wrapper and utility functions to simplify use of the SDL libraries
5    in the Tux4Kids programs (Tux Math and Tux Typing).
6 
7    Copyright 2000, 2003, 2007, 2008, 2009, 2010.
8    Authors: David Bruce, Sam Hart, Bill Kendrick, Tim Holy,
9             Boleslaw Kulbabinski, Brendan Luchen.
10    Project email: <tuxmath-devel@lists.sourceforge.net>
11    Project website: http://tux4kids.alioth.debian.org
12 
13 t4k_sdl.c is part of the t4k_common library.
14 
15 t4k_common is free software: you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation; either version 3 of the License, or
18 (at your option) any later version.
19 
20 t4k_common is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 GNU General Public License for more details.
24 
25 You should have received a copy of the GNU General Public License
26 along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
27 
28 
29 
30 #include <math.h>
31 
32 #include "t4k_common.h"
33 #include "t4k_globals.h"
34 
35 SDL_Surface* screen = NULL;
36 
37 static ResSwitchCallback res_switch_callback = NULL;
38 static ResSwitchCallback internal_res_switch_callback = NULL;
39 
40 /* window size */
41 int win_res_x = 640;
42 int win_res_y = 480;
43 
44 /* full screen size (set in initialize_SDL() ) */
45 int fs_res_x = 0;
46 int fs_res_y = 0;
47 
48 const char* _font_name = DEFAULT_FONT_NAME;
49 
T4K_SetFontName(const char * name)50 void T4K_SetFontName(const char* name)
51 {
52   DEBUGMSG(debug_sdl, "Switching font to %s\n", name);
53   _font_name = name;
54 }
55 
T4K_AskFontName()56 const char* T4K_AskFontName()
57 {
58   return _font_name;
59 }
60 
61 /*
62 Return a pointer to the screen we're using, as an alternative to making screen
63 global. Not sure what is involved performance-wise in SDL_GetVideoSurface,
64 or if this check is even necessary -Cheez
65 */
T4K_GetScreen()66 SDL_Surface* T4K_GetScreen()
67 {
68     if (screen != SDL_GetVideoSurface() )
69     {
70       fprintf(stderr, "Video Surface changed from outside of SDL_Extras!\n");
71       screen = SDL_GetVideoSurface();
72     }
73   return screen;
74 }
75 
76 
77 /*
78  * T4K_GetResolutions() takes int pointer args for the windowed and
79  * fullscreen resolutions and fills them in with the current values.
80  * Returns 1 if successful, 0 otherwise.
81  */
82 
T4K_GetResolutions(int * win_x,int * win_y,int * full_x,int * full_y)83 int T4K_GetResolutions(int* win_x, int* win_y, int* full_x, int* full_y)
84 {
85   if(!win_x || !win_y || !full_x || !full_y)
86   {
87     fprintf(stderr, "T4K_GetResolutions() - invalid pointer arg");
88     return 0;
89   }
90 
91   *win_x = win_res_x;
92   *win_y = win_res_y;
93   *full_x = fs_res_x;
94   *full_y = fs_res_y;
95 
96   return 1;
97 }
98 
99 /* T4K_DrawButton() creates a translucent button with rounded ends
100    and draws it on the screen.
101    All colors and alpha values are supported.*/
T4K_DrawButton(SDL_Rect * target_rect,int radius,Uint8 r,Uint8 g,Uint8 b,Uint8 a)102 void T4K_DrawButton(SDL_Rect* target_rect,
103                 int radius,
104                 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
105 {
106   T4K_DrawButtonOn(screen, target_rect, radius, r, g, b, a);
107 }
108 
T4K_DrawButtonOn(SDL_Surface * target,SDL_Rect * target_rect,int radius,Uint8 r,Uint8 g,Uint8 b,Uint8 a)109 void T4K_DrawButtonOn(SDL_Surface* target,
110                 SDL_Rect* target_rect,
111                 int radius,
112                 Uint8 r, Uint8 g, Uint8 b, Uint8 a)
113 
114 {
115   SDL_Surface* tmp_surf = T4K_CreateButton(target_rect->w, target_rect->h,
116                                        radius, r, g, b, a);
117   SDL_BlitSurface(tmp_surf, NULL, target, target_rect);
118   SDL_FreeSurface(tmp_surf);
119 }
120 
121 
122 
123 /* T4K_CreateButton() creates a translucent button with rounded ends
124    All colors and alpha values are supported.*/
T4K_CreateButton(int w,int h,int radius,Uint8 r,Uint8 g,Uint8 b,Uint8 a)125 SDL_Surface* T4K_CreateButton(int w, int h, int radius,
126                           Uint8 r, Uint8 g, Uint8 b, Uint8 a)
127 {
128   /* NOTE - we use a 32-bit temp surface even if we have a 16-bit */
129   /* screen - it gets converted during blitting.                  */
130   SDL_Surface* tmp_surf = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA,
131                                           w,
132                                           h,
133                                           32,
134                                           rmask, gmask, bmask, amask);
135 
136   Uint32 color = SDL_MapRGBA(tmp_surf->format, r, g, b, a);
137   SDL_FillRect(tmp_surf, NULL, color);
138   T4K_RoundCorners(tmp_surf, radius);
139   return tmp_surf;
140 }
141 
142 
T4K_RoundCorners(SDL_Surface * s,Uint16 radius)143 void T4K_RoundCorners(SDL_Surface* s, Uint16 radius)
144 {
145   int y = 0;
146   int x_dist, y_dist;
147   Uint32* p = NULL;
148   Uint32 alpha_mask;
149   int bytes_per_pix;
150 
151   if (!s)
152     return;
153   if (SDL_LockSurface(s) == -1)
154     return;
155 
156   bytes_per_pix = s->format->BytesPerPixel;
157   if (bytes_per_pix != 4)
158     return;
159 
160   /* radius cannot be more than half of width or height: */
161   if (radius > (s->w)/2)
162     radius = (s->w)/2;
163   if (radius > (s->h)/2)
164     radius = (s->h)/2;
165 
166 
167   alpha_mask = s->format->Amask;
168 
169   /* Now round off corners: */
170   /* upper left:            */
171   for (y = 0; y < radius; y++)
172   {
173     p = (Uint32*)(s->pixels + (y * s->pitch));
174     x_dist = radius;
175     y_dist = radius - y;
176 
177     while (((x_dist * x_dist) + (y_dist * y_dist)) > (radius * radius))
178     {
179       /* (make pixel (x,y) transparent) */
180       *p = *p & ~alpha_mask;
181       p++;
182       x_dist--;
183     }
184   }
185 
186   /* upper right:            */
187   for (y = 0; y < radius; y++)
188   {
189     /* start at end of top row: */
190     p = (Uint32*)(s->pixels + ((y + 1) * s->pitch) - bytes_per_pix);
191 
192     x_dist = radius;
193     y_dist = radius - y;
194 
195     while (((x_dist * x_dist) + (y_dist * y_dist)) > (radius * radius))
196     {
197       /* (make pixel (x,y) transparent) */
198       *p = *p & ~alpha_mask;
199       p--;
200       x_dist--;
201     }
202   }
203 
204   /* bottom left:            */
205   for (y = (s->h - 1); y > (s->h - radius); y--)
206   {
207     /* start at beginning of bottom row */
208     p = (Uint32*)(s->pixels + (y * s->pitch));
209     x_dist = radius;
210     y_dist = y - (s->h - radius);
211 
212     while (((x_dist * x_dist) + (y_dist * y_dist)) > (radius * radius))
213     {
214       /* (make pixel (x,y) transparent) */
215       *p = *p & ~alpha_mask;
216       p++;
217       x_dist--;
218     }
219   }
220 
221   /* bottom right:            */
222   for (y = (s->h - 1); y > (s->h - radius); y--)
223   {
224     /* start at end of bottom row */
225     p = (Uint32*)(s->pixels + ((y + 1) * s->pitch) - bytes_per_pix);
226     x_dist = radius;
227     y_dist = y - (s->h - radius);
228 
229     while (((x_dist * x_dist) + (y_dist * y_dist)) > (radius * radius))
230     {
231       /* (make pixel (x,y) transparent) */
232       *p = *p & ~alpha_mask;
233       p--;
234       x_dist--;
235     }
236   }
237   SDL_UnlockSurface(s);
238 }
239 
240 /**********************
241  Flip:
242    input: a SDL_Surface, x, y
243    output: a copy of the SDL_Surface flipped via rules:
244 
245      if x is a nonzero value, then flip horizontally
246      if y is a nonzero value, then flip vertically
247 
248      note: you can have it flip both
249 **********************/
T4K_Flip(SDL_Surface * in,int x,int y)250 SDL_Surface* T4K_Flip( SDL_Surface *in, int x, int y ) {
251         SDL_Surface *out, *tmp;
252         SDL_Rect from_rect, to_rect;
253         Uint32        flags;
254         Uint32  colorkey=0;
255 
256         /* --- grab the settings for the incoming pixmap --- */
257 
258         SDL_LockSurface(in);
259         flags = in->flags;
260 
261         /* --- change in's flags so ignore colorkey & alpha --- */
262 
263         if (flags & SDL_SRCCOLORKEY) {
264                 in->flags &= ~SDL_SRCCOLORKEY;
265                 colorkey = in->format->colorkey;
266         }
267         if (flags & SDL_SRCALPHA) {
268                 in->flags &= ~SDL_SRCALPHA;
269         }
270 
271         SDL_UnlockSurface(in);
272 
273         /* --- create our new surface --- */
274 
275         out = SDL_CreateRGBSurface(
276                 SDL_SWSURFACE,
277                 in->w, in->h, 32, rmask, gmask, bmask, amask);
278 
279         /* --- flip horizontally if requested --- */
280 
281         if (x) {
282                 from_rect.h = to_rect.h = in->h;
283                 from_rect.w = to_rect.w = 1;
284                 from_rect.y = to_rect.y = 0;
285                 from_rect.x = 0;
286                 to_rect.x = in->w - 1;
287 
288                 do {
289                         SDL_BlitSurface(in, &from_rect, out, &to_rect);
290                         from_rect.x++;
291                         to_rect.x--;
292                 } while (to_rect.x >= 0);
293         }
294 
295         /* --- flip vertically if requested --- */
296 
297         if (y) {
298                 from_rect.h = to_rect.h = 1;
299                 from_rect.w = to_rect.w = in->w;
300                 from_rect.x = to_rect.x = 0;
301                 from_rect.y = 0;
302                 to_rect.y = in->h - 1;
303 
304                 do {
305                         SDL_BlitSurface(in, &from_rect, out, &to_rect);
306                         from_rect.y++;
307                         to_rect.y--;
308                 } while (to_rect.y >= 0);
309         }
310 
311         /* --- restore colorkey & alpha on in and setup out the same --- */
312 
313         SDL_LockSurface(in);
314 
315         if (flags & SDL_SRCCOLORKEY) {
316                 in->flags |= SDL_SRCCOLORKEY;
317                 in->format->colorkey = colorkey;
318                 tmp = SDL_DisplayFormat(out);
319                 SDL_FreeSurface(out);
320                 out = tmp;
321                 out->flags |= SDL_SRCCOLORKEY;
322                 out->format->colorkey = colorkey;
323         } else if (flags & SDL_SRCALPHA) {
324                 in->flags |= SDL_SRCALPHA;
325                 tmp = SDL_DisplayFormatAlpha(out);
326                 SDL_FreeSurface(out);
327                 out = tmp;
328         } else {
329                 tmp = SDL_DisplayFormat(out);
330                 SDL_FreeSurface(out);
331                 out = tmp;
332         }
333 
334         SDL_UnlockSurface(in);
335 
336         return out;
337 }
338 
339 /* Blend two surfaces together. The third argument is between 0.0 and
340    1.0, and represents the weight assigned to the first surface.  If
341    the pointer to the second surface is NULL, this performs fading.
342 
343    Currently this works only with RGBA images, but this is largely to
344    make the (fast) pointer arithmetic work out; it could be easily
345    generalized to other image types. */
T4K_Blend(SDL_Surface * S1,SDL_Surface * S2,float gamma)346 SDL_Surface* T4K_Blend(SDL_Surface *S1, SDL_Surface *S2, float gamma)
347 {
348   SDL_PixelFormat *fmt1, *fmt2;
349   Uint8 r1, r2, g1, g2, b1, b2, a1, a2;
350   SDL_Surface *tmpS, *ret;
351   Uint32 *cpix1, *epix1, *cpix2, *epix2;
352   float gamflip;
353 
354   if (!S1)
355     return NULL;
356 
357   fmt1 = fmt2 = NULL;
358   tmpS = ret = NULL;
359 
360   gamflip = 1.0 - gamma;
361   if (gamma < 0 || gamflip < 0)
362   {
363     perror("gamma must be between 0 and 1");
364     exit(0);
365   }
366 
367   fmt1 = S1->format;
368 
369   if (fmt1 && fmt1->BitsPerPixel != 32)
370   {
371     perror("This works only with RGBA images");
372     return S1;
373   }
374   if (S2 != NULL)
375   {
376     fmt2 = S2->format;
377     if (fmt2->BitsPerPixel != 32)
378     {
379       perror("This works only with RGBA images");
380       return S1;
381     }
382     // Check that both images have the same width dimension
383     if (S1->w != S2->w)
384     {
385       printf("S1->w %d, S2->w %d;  S1->h %d, S2->h %d\n",
386              S1->w, S2->w, S1->h, S2->h);
387       printf("Both images must have the same width dimensions\n");
388       return S1;
389     }
390   }
391 
392   tmpS = SDL_ConvertSurface(S1, fmt1, SDL_SWSURFACE);
393   if (tmpS == NULL)
394   {
395     perror("SDL_ConvertSurface() failed");
396     return S1;
397   }
398   if (-1 == SDL_LockSurface(tmpS))
399   {
400     perror("SDL_LockSurface() failed");
401     return S1;
402   }
403 
404   // We're going to go through the pixels in reverse order, to start
405   // from the bottom of each image. That way, we can blend things that
406   // are not of the same height and have them align at the bottom.
407   // So the "ending pixel" (epix) will be before the first pixel, and
408   // the current pixel (cpix) will be the last pixel.
409   epix1 = (Uint32*) tmpS->pixels - 1;
410   cpix1 = epix1 + tmpS->w * tmpS->h;
411   if (S2 != NULL
412       && (SDL_LockSurface(S2) != -1))
413   {
414     epix2 = (Uint32*) S2->pixels - 1;
415     cpix2 = epix2 + S2->w * S2->h;
416   }
417   else
418   {
419     epix2 = epix1;
420     cpix2 = cpix1;
421   }
422 
423   for (; cpix1 > epix1; cpix1--, cpix2--)
424   {
425     SDL_GetRGBA(*cpix1, fmt1, &r1, &g1, &b1, &a1);
426     a1 = gamma * a1;
427     if (S2 != NULL && cpix2 > epix2)
428     {
429       SDL_GetRGBA(*cpix2, fmt2, &r2, &g2, &b2, &a2);
430       r1 = gamma * r1 + gamflip * r2;
431       g1 = gamma * g1 + gamflip * g2;
432       b1 = gamma * b1 + gamflip * b2;
433       a1 += gamflip * a2;
434     }
435     *cpix1 = SDL_MapRGBA(fmt1,r1,g1,b1,a1);
436   }
437 
438   SDL_UnlockSurface(tmpS);
439 
440   if (S2 != NULL)
441     SDL_UnlockSurface(S2);
442 
443   ret = SDL_DisplayFormatAlpha(tmpS);
444   SDL_FreeSurface(tmpS);
445 
446   return ret;
447 }
448 
449 
450 /* free every surface in the array together with the array itself */
T4K_FreeSurfaceArray(SDL_Surface ** surfs,int length)451 void T4K_FreeSurfaceArray(SDL_Surface** surfs, int length)
452 {
453   int i;
454 
455   if(surfs == NULL)
456     return;
457 
458   for(i = 0; i < length; i++)
459     if(surfs[i] != NULL)
460       SDL_FreeSurface(surfs[i]);
461   free(surfs);
462 }
463 
T4K_inRect(SDL_Rect r,int x,int y)464 int T4K_inRect( SDL_Rect r, int x, int y) {
465         if ((x < r.x) || (y < r.y) || (x > r.x + r.w) || (y > r.y + r.h))
466                 return 0;
467         return 1;
468 }
469 
T4K_UpdateRect(SDL_Surface * surf,SDL_Rect * rect)470 void T4K_UpdateRect(SDL_Surface* surf, SDL_Rect* rect)
471 {
472   SDL_UpdateRect(surf, rect->x, rect->y, rect->w, rect->h);
473 }
474 
T4K_SetRect(SDL_Rect * rect,const float * pos)475 void T4K_SetRect(SDL_Rect* rect, const float* pos)
476 {
477   rect->x = pos[0] * screen->w;
478   rect->y = pos[1] * screen->h;
479   rect->w = pos[2] * screen->w;
480   rect->h = pos[3] * screen->h;
481 }
482 
483 /* Darkens the screen by a factor of 2^bits */
T4K_DarkenScreen(Uint8 bits)484 void T4K_DarkenScreen(Uint8 bits)
485 {
486 #if PIXEL_BITS == 32
487   Uint32* p;
488 #elif PIXEL_BITS == 16
489   Uint16* p;
490 #else
491   Uint16* p;
492   return;
493 #endif
494   Uint32 rm = screen->format->Rmask;
495   Uint32 gm = screen->format->Gmask;
496   Uint32 bm = screen->format->Bmask;
497 
498 
499   int x, y;
500 
501   /* (realistically, 1 and 2 are the only useful values) */
502   if (bits > 8)
503     return;
504 
505   p = screen->pixels;
506 
507   for (y = 0; y < screen->h; y++)
508   {
509     for (x = 0; x < screen->w; x++)
510     {
511       *p = (((*p&rm)>>bits)&rm)
512          | (((*p&gm)>>bits)&gm)
513          | (((*p&bm)>>bits)&bm);
514       p++;
515     }
516   }
517 }
518 
519 /* change window size (works only in windowed mode) */
T4K_ChangeWindowSize(int new_res_x,int new_res_y)520 void T4K_ChangeWindowSize(int new_res_x, int new_res_y)
521 {
522   SDL_Surface* oldscreen = screen;
523 
524   if(!(screen->flags & SDL_FULLSCREEN))
525   {
526     screen = SDL_SetVideoMode(new_res_x,
527                               new_res_y,
528                               PIXEL_BITS,
529                               SDL_SWSURFACE|SDL_HWPALETTE);
530 
531     if(screen == NULL)
532     {
533       fprintf(stderr,
534               "\nError: I could not change screen mode into %d x %d.\n",
535               new_res_x, new_res_y);
536       screen = oldscreen;
537     }
538     else
539     {
540       DEBUGMSG(debug_sdl, "T4K_ChangeWindowSize(): Changed window size to %d x %d\n", screen->w, screen->h);
541       oldscreen = NULL;
542       win_res_x = screen->w;
543       win_res_y = screen->h;
544       if (res_switch_callback)
545         res_switch_callback(win_res_x, win_res_y);
546       SDL_UpdateRect(screen, 0, 0, 0, 0);
547     }
548   }
549   else
550     DEBUGMSG(debug_sdl, "T4K_ChangeWindowSize() can be run only in windowed mode !");
551 }
552 
553 /* switch between fullscreen and windowed mode */
T4K_SwitchScreenMode(void)554 void T4K_SwitchScreenMode(void)
555 {
556   int window = (screen->flags & SDL_FULLSCREEN);
557   SDL_Surface* oldscreen = screen;
558 
559   screen = SDL_SetVideoMode(window ? win_res_x : fs_res_x,
560                             window ? win_res_y : fs_res_y,
561                             PIXEL_BITS,
562                             screen->flags ^ SDL_FULLSCREEN);
563 
564   if (screen == NULL)
565   {
566     fprintf(stderr,
567             "\nError: I could not switch to %s mode.\n"
568             "The Simple DirectMedia error that occured was:\n"
569             "%s\n\n",
570             window ? "windowed" : "fullscreen",
571             SDL_GetError());
572     screen = oldscreen;
573   }
574   else
575   {
576     //success, no need to free the old video surface
577     DEBUGMSG(debug_sdl, "Switched screen mode to %s\n", window ? "windowed" : "fullscreen");
578     oldscreen = NULL;
579     if (res_switch_callback)
580       res_switch_callback(screen->w, screen->h);
581     if (internal_res_switch_callback)
582       internal_res_switch_callback(screen->w, screen->h);
583 
584     SDL_UpdateRect(screen, 0, 0, 0, 0);
585   }
586 }
587 
internal_res_switch_handler(ResSwitchCallback callback)588 void internal_res_switch_handler(ResSwitchCallback callback)
589 {
590   internal_res_switch_callback = callback;
591 }
592 
T4K_OnResolutionSwitch(ResSwitchCallback callback)593 void T4K_OnResolutionSwitch (ResSwitchCallback callback)
594 {
595   res_switch_callback = callback;
596 }
597 
598 /*
599 Block application until SDL receives an appropriate event. Events can be
600 a single or OR'd combination of event masks.
601 e.g. e = T4K_WaitForEvent(SDL_KEYDOWNMASK | SDL_QUITMASK)
602 */
T4K_WaitForEvent(SDL_EventMask events)603 SDL_EventType T4K_WaitForEvent(SDL_EventMask events)
604 {
605   SDL_Event evt;
606   while (1)
607   {
608     while (SDL_PollEvent(&evt) )
609     {
610       if (SDL_EVENTMASK(evt.type) & events)
611         return evt.type;
612       else
613         SDL_Delay(50);
614     }
615   }
616 }
617 /* Swiped shamelessly from TuxPaint
618    Based on code from: http://www.codeproject.com/cs/media/imageprocessing4.asp
619    copyright 2002 Christian Graus */
620 
T4K_zoom(SDL_Surface * src,int new_w,int new_h)621 SDL_Surface* T4K_zoom(SDL_Surface* src, int new_w, int new_h)
622 {
623   SDL_Surface* s;
624 
625   /* These function pointers will point to the appropriate */
626   /* putpixel() and getpixel() variants to be used in the  */
627   /* current colorspace:                                   */
628   void (*putpixel) (SDL_Surface*, int, int, Uint32);
629   Uint32(*getpixel) (SDL_Surface*, int, int);
630 
631   float xscale, yscale;
632   int x, y;
633   int floor_x, ceil_x,
634         floor_y, ceil_y;
635   float fraction_x, fraction_y,
636         one_minus_x, one_minus_y;
637   float n1, n2;
638   Uint8 r1, g1, b1, a1;
639   Uint8 r2, g2, b2, a2;
640   Uint8 r3, g3, b3, a3;
641   Uint8 r4, g4, b4, a4;
642   Uint8 r, g, b, a;
643 
644   DEBUGMSG(debug_sdl, "Entering T4K_zoom():\n");
645 
646   /* Create surface for zoom: */
647 
648   s = SDL_CreateRGBSurface(src->flags,        /* SDL_SWSURFACE, */
649                            new_w, new_h, src->format->BitsPerPixel,
650                            src->format->Rmask,
651                            src->format->Gmask,
652                            src->format->Bmask,
653                            src->format->Amask);
654 
655   if (s == NULL)
656   {
657     fprintf(stderr, "\nError: Can't build zoom surface\n"
658             "The Simple DirectMedia Layer error that occurred was:\n"
659             "%s\n\n", SDL_GetError());
660     return NULL;
661 //    cleanup();
662 //    exit(1);
663   }
664 
665   DEBUGMSG(debug_sdl, "T4K_zoom(): orig surface %dx%d, %d bytes per pixel\n",
666             src->w, src->h, src->format->BytesPerPixel);
667   DEBUGMSG(debug_sdl, "T4K_zoom(): new surface %dx%d, %d bytes per pixel\n",
668             s->w, s->h, s->format->BytesPerPixel);
669 
670   /* Now assign function pointers to correct functions based */
671   /* on data format of original and zoomed surfaces:         */
672   getpixel = getpixels[src->format->BytesPerPixel];
673   putpixel = putpixels[s->format->BytesPerPixel];
674 
675   SDL_LockSurface(src);
676   SDL_LockSurface(s);
677 
678   xscale = (float) src->w / (float) new_w;
679   yscale = (float) src->h / (float) new_h;
680 
681   for (x = 0; x < new_w; x++)
682   {
683     for (y = 0; y < new_h; y++)
684     {
685       /* Here we calculate the new RGBA values for each pixel */
686       /* using a "weighted average" of the four pixels in the */
687       /* corresponding location in the orginal surface:       */
688 
689       /* figure out which original pixels to use in the calc: */
690       floor_x = floor((float) x * xscale);
691       ceil_x = floor_x + 1;
692       if (ceil_x >= src->w)
693         ceil_x = floor_x;
694 
695       floor_y = floor((float) y * yscale);
696       ceil_y = floor_y + 1;
697       if (ceil_y >= src->h)
698         ceil_y = floor_y;
699 
700       fraction_x = x * xscale - floor_x;
701       fraction_y = y * yscale - floor_y;
702 
703       one_minus_x = 1.0 - fraction_x;
704       one_minus_y = 1.0 - fraction_y;
705 
706       /* Grab their values:  */
707       SDL_GetRGBA(getpixel(src, floor_x, floor_y), src->format,
708                   &r1, &g1, &b1, &a1);
709       SDL_GetRGBA(getpixel(src, ceil_x,  floor_y), src->format,
710                   &r2, &g2, &b2, &a2);
711       SDL_GetRGBA(getpixel(src, floor_x, ceil_y),  src->format,
712                   &r3, &g3, &b3, &a3);
713       SDL_GetRGBA(getpixel(src, ceil_x,  ceil_y),  src->format,
714                   &r4, &g4, &b4, &a4);
715 
716       /* Create the weighted averages: */
717       n1 = (one_minus_x * r1 + fraction_x * r2);
718       n2 = (one_minus_x * r3 + fraction_x * r4);
719       r = (one_minus_y * n1 + fraction_y * n2);
720 
721       n1 = (one_minus_x * g1 + fraction_x * g2);
722       n2 = (one_minus_x * g3 + fraction_x * g4);
723       g = (one_minus_y * n1 + fraction_y * n2);
724 
725       n1 = (one_minus_x * b1 + fraction_x * b2);
726       n2 = (one_minus_x * b3 + fraction_x * b4);
727       b = (one_minus_y * n1 + fraction_y * n2);
728 
729       n1 = (one_minus_x * a1 + fraction_x * a2);
730       n2 = (one_minus_x * a3 + fraction_x * a4);
731       a = (one_minus_y * n1 + fraction_y * n2);
732 
733       /* and put them into our new surface: */
734       putpixel(s, x, y, SDL_MapRGBA(s->format, r, g, b, a));
735 
736     }
737   }
738 
739   SDL_UnlockSurface(s);
740   SDL_UnlockSurface(src);
741 
742   DEBUGMSG(debug_sdl, "Leaving T4K_zoom():\n");
743 
744   return s;
745 }
746 
747 /*************************************************/
748 /* TransWipe: Performs various wipes to new bkgs */
749 /*************************************************/
750 /*
751  * Given a wipe request type, and any variables
752  * that wipe requires, will perform a wipe from
753  * the current screen image to a new one.
754  * NOTE duration should be given in tenths-of-seconds
755  * NOTE this transition is uninterruptible!
756  */
T4K_TransWipe(const SDL_Surface * newbkg,WipeStyle type,int segments,int duration)757 int T4K_TransWipe(const SDL_Surface* newbkg, WipeStyle type, int segments, int duration)
758 {
759   int i, j, x1, x2, y1, y2;
760   int step1, step2, step3, step4;
761   int frame;
762   SDL_Rect src;
763   SDL_Rect dst;
764 
765   T4K_ResetBlitQueue();
766 
767   /* Input validation: ----------------------- */
768   if (!newbkg)
769   {
770     fprintf(stderr, "T4K_TransWipe() - 'newbkg' arg invalid!\n");
771     return 0;
772   }
773 
774   /* FIXME should support scaling here - DSB */
775   if(newbkg->w != screen->w || newbkg->h != screen->h)
776   {
777     fprintf(stderr, "T4K_TransWipe() - wrong size newbkg* arg");
778     return 0;
779   }
780 
781   /* segments is num of divisions */
782   /* duration is how many frames animation should take */
783 
784   if(segments < 1)
785     segments = 1;
786   if(duration < 1)
787     duration = 1;
788 
789   /* Pick a card, any card...            */
790   while(type == RANDOM_WIPE)
791     type = rand() % NUM_WIPES;
792 
793 
794   T4K_ResetBlitQueue();
795   frame = 0;
796 
797   DEBUGVARX(debug_sdl, type);
798 
799   switch(type)
800   {
801     case WIPE_BLINDS_VERT:
802     {
803 
804       step1 = screen->w/segments;
805       step2 = step1/duration;
806 
807       src.y = 0;
808       dst.y = 0;
809       src.h = screen->h;
810       dst.h = screen->h;
811       src.w = step2;
812       dst.w = step2;
813 
814       for(i = 0; i <= duration; i++)
815       {
816         for(j = 0; j <= segments; j++)
817         {
818           x1 = step1 * (j - 0.5) - i * step2 + 1;
819           x2 = step1 * (j - 0.5) + i * step2 + 1;
820           src.x = x1;
821           dst.x = x2;
822           SDL_BlitSurface((SDL_Surface*)newbkg, &src, screen, &src);
823           SDL_BlitSurface((SDL_Surface*)newbkg, &dst, screen, &dst);
824           T4K_AddRect(&src, &src);
825           T4K_AddRect(&dst, &dst);
826         }
827         SDL_Flip(screen);
828         SDL_Delay(10);
829       }
830 
831       src.x = 0;
832       src.y = 0;
833       src.w = screen->w;
834       src.h = screen->h;
835       SDL_BlitSurface((SDL_Surface*)newbkg, NULL, screen, &src);
836       SDL_Flip(screen);
837 
838       break;
839     }
840 
841     case WIPE_BLINDS_HORIZ:
842     {
843 
844       step1 = screen->h / segments;
845       step2 = step1 / duration;
846 
847       src.x = 0;
848       dst.x = 0;
849       src.w = screen->w;
850       dst.w = screen->w;
851       src.h = step2;
852       dst.h = step2;
853 
854       for(i = 0; i <= duration; i++)
855       {
856         for(j = 0; j <= segments; j++)
857         {
858           y1 = step1 * (j - 0.5) - i * step2 + 1;
859           y2 = step1 * (j - 0.5) + i * step2 + 1;
860           src.y = y1;
861           dst.y = y2;
862           SDL_BlitSurface((SDL_Surface*)newbkg, &src, screen, &src);
863           SDL_BlitSurface((SDL_Surface*)newbkg, &dst, screen, &dst);
864           T4K_AddRect(&src, &src);
865           T4K_AddRect(&dst, &dst);
866         }
867         SDL_Flip(screen);
868         SDL_Delay(10);
869       }
870 
871       src.x = 0;
872       src.y = 0;
873       src.w = screen->w;
874       src.h = screen->h;
875       SDL_BlitSurface((SDL_Surface*)newbkg, NULL, screen, &src);
876       SDL_Flip(screen);
877 
878       break;
879     }
880 
881     case WIPE_BLINDS_BOX:
882     {
883 
884       step1 = screen->w/segments;
885       step2 = step1/duration;
886       step3 = screen->h/segments;
887       step4 = step1/duration;
888 
889       for(i = 0; i <= duration; i++)
890       {
891         for(j = 0; j <= segments; j++)
892         {
893           x1 = step1 * (j - 0.5) - i * step2 + 1;
894           x2 = step1 * (j - 0.5) + i * step2 + 1;
895           src.x = x1;
896           dst.x = x2;
897           dst.y = 0;
898           dst.w = step2;
899           dst.h = screen->h;
900           SDL_BlitSurface((SDL_Surface*)newbkg, &src, screen, &src);
901           SDL_BlitSurface((SDL_Surface*)newbkg, &dst, screen, &dst);
902           T4K_AddRect(&src, &src);
903           T4K_AddRect(&dst, &dst);
904 
905           y1 = step3 * (j - 0.5) - i * step4 + 1;
906           y2 = step3 * (j - 0.5) + i * step4 + 1;
907           src.x = 0;
908           src.y = y1;
909           src.w = screen->w;
910           src.h = step4;
911           dst.x = 0;
912           dst.y = y2;
913           dst.w = screen->w;
914           dst.h = step4;
915           SDL_BlitSurface((SDL_Surface*)newbkg, &src, screen, &src);
916           SDL_BlitSurface((SDL_Surface*)newbkg, &dst, screen, &dst);
917           T4K_AddRect(&src, &src);
918           T4K_AddRect(&dst, &dst);
919         }
920         SDL_Flip(screen);
921         SDL_Delay(10);
922       }
923 
924       src.x = 0;
925       src.y = 0;
926       src.w = screen->w;
927       src.h = screen->h;
928       SDL_BlitSurface((SDL_Surface*)newbkg, NULL, screen, &src);
929       SDL_Flip(screen);
930 
931       break;
932     }
933     default:
934       break;
935   }
936   return 1;
937 }
938 
939 
940 
941 
942 
943 
944 /************************************************************************/
945 /*                                                                      */
946 /*        Begin blit queue support                                      */
947 /*                                                                      */
948 /* This code (modified from Sam Lantinga's "Alien" example program)     */
949 /* implements a blit queue to perform screen updates in a more          */
950 /* optimized fashion.                                                   */
951 /************************************************************************/
952 
953 //With fullscreen, we need more updates - 180 wasn't enough
954 #define MAX_UPDATES 512
955 
956 /* --- Data Structure for Dirty Blitting --- */
957 static SDL_Rect srcupdate[MAX_UPDATES];
958 static SDL_Rect dstupdate[MAX_UPDATES];
959 static int numupdates = 0; // tracks how many blits to be done
960 
961 struct blit {
962     SDL_Surface* src;
963     SDL_Rect* srcrect;
964     SDL_Rect* dstrect;
965     unsigned char type;
966 } blits[MAX_UPDATES];
967 
968 
969 
970 /***********************
971  T4K_InitBlitQueue()
972  ***********************/
T4K_InitBlitQueue(void)973 void T4K_InitBlitQueue(void)
974 {
975   int i;
976 
977   /* --- Set up the update rectangle pointers --- */
978   for (i = 0; i < MAX_UPDATES; ++i)
979   {
980     blits[i].srcrect = &srcupdate[i];
981     blits[i].dstrect = &dstupdate[i];
982   }
983   numupdates = 0;
984 }
985 
986 
987 /**************************
988 ResetBlitQueue(): just set the number
989 of pending updates to zero
990 ***************************/
T4K_ResetBlitQueue(void)991 void T4K_ResetBlitQueue(void)
992 {
993   numupdates = 0;
994 }
995 
996 
997 /******************************
998 AddRect : Don't actually blit a surface,
999     but add a rect to be updated next
1000     update
1001 *******************************/
T4K_AddRect(SDL_Rect * src,SDL_Rect * dst)1002 int T4K_AddRect(SDL_Rect* src, SDL_Rect* dst)
1003 {
1004 
1005   /*borrowed from SL's alien (and modified)*/
1006   struct blit* update;
1007 
1008   if(!src)
1009   {
1010     fprintf(stderr, "T4K_AddRect() - invalid 'src' arg!\n");
1011     return 0;
1012   }
1013 
1014   if(!dst)
1015   {
1016     fprintf(stderr, "T4K_AddRect() - invalid 'dst' arg!\n");
1017     return 0;
1018   }
1019 
1020   if(numupdates >= MAX_UPDATES)
1021   {
1022     fprintf(stderr, "Warning - MAX_UPDATES exceeded, cannot add blit to queue\n");
1023     return 0;
1024   }
1025 
1026   update = &blits[numupdates++];
1027 
1028   if(!update || !update->srcrect || !update->dstrect)
1029   {
1030     fprintf(stderr, "T4K_AddRect() - 'update' ptr invalid!\n");
1031     return 0;
1032   }
1033 
1034   update->srcrect->x = src->x;
1035   update->srcrect->y = src->y;
1036   update->srcrect->w = src->w;
1037   update->srcrect->h = src->h;
1038   update->dstrect->x = dst->x;
1039   update->dstrect->y = dst->y;
1040   update->dstrect->w = dst->w;
1041   update->dstrect->h = dst->h;
1042   update->type = 'I';
1043 
1044   return 1;
1045 }
1046 
1047 
1048 
T4K_DrawSprite(sprite * gfx,int x,int y)1049 int T4K_DrawSprite(sprite* gfx, int x, int y)
1050 {
1051   if (!gfx || !gfx->frame[gfx->cur])
1052   {
1053     fprintf(stderr, "T4K_DrawSprite() - 'gfx' arg invalid!\n");
1054     return 0;
1055   }
1056   return T4K_DrawObject(gfx->frame[gfx->cur], x, y);
1057 }
1058 
1059 
1060 
1061 /**********************
1062 DrawObject : Draw an object at the specified
1063 location. No respect to clipping!
1064 *************************/
T4K_DrawObject(SDL_Surface * surf,int x,int y)1065 int T4K_DrawObject(SDL_Surface* surf, int x, int y)
1066 {
1067   struct blit *update;
1068 
1069   if (!surf)
1070   {
1071     fprintf(stderr, "T4K_DrawObject() - invalid 'surf' arg!\n");
1072     return 0;
1073   }
1074 
1075   if(numupdates >= MAX_UPDATES)
1076   {
1077     fprintf(stderr, "Warning - MAX_UPDATES exceeded, cannot add blit to queue\n");
1078     return 0;
1079   }
1080 
1081   update = &blits[numupdates++];
1082 
1083   if(!update || !update->srcrect || !update->dstrect)
1084   {
1085     fprintf(stderr, "T4K_DrawObject() - 'update' ptr invalid!\n");
1086     return 0;
1087   }
1088 
1089   update->src = surf;
1090   update->srcrect->x = 0;
1091   update->srcrect->y = 0;
1092   update->srcrect->w = surf->w;
1093   update->srcrect->h = surf->h;
1094   update->dstrect->x = x;
1095   update->dstrect->y = y;
1096   update->dstrect->w = surf->w;
1097   update->dstrect->h = surf->h;
1098   update->type = 'D';
1099 
1100   return 1;
1101 }
1102 
1103 
1104 
1105 /************************
1106 UpdateScreen : Update the screen and increment the frame num
1107 ***************************/
T4K_UpdateScreen(int * frame)1108 void T4K_UpdateScreen(int* frame)
1109 {
1110   int i;
1111 
1112   /* -- First erase everything we need to -- */
1113   for (i = 0; i < numupdates; i++)
1114   {
1115     if (blits[i].type == 'E')
1116     {
1117 //       DEBUGCODE(debug_sdl)
1118 //       {
1119 //         fprintf(stderr, "Erasing blits[%d]\n", i);
1120 //         fprintf(stderr, "srcrect->x = %d\t srcrect->y = %d\t srcrect->w = %d\t srcrect->h = %d\n",
1121 //               blits[i].srcrect->x, blits[i].srcrect->y, blits[i].srcrect->w, blits[i].srcrect->h);
1122 //         fprintf(stderr, "dstrect->x = %d\t dstrect->y = %d\t dstrect->w = %d\t dstrect->h = %d\n",
1123 //               blits[i].dstrect->x, blits[i].dstrect->y, blits[i].dstrect->w, blits[i].dstrect->h);
1124 //       }
1125 
1126       SDL_LowerBlit(blits[i].src, blits[i].srcrect, screen, blits[i].dstrect);
1127     }
1128   }
1129 
1130 //  SNOW_erase();
1131 
1132   /* -- then draw -- */
1133   for (i = 0; i < numupdates; i++)
1134   {
1135     if (blits[i].type == 'D')
1136     {
1137 //       DEBUGCODE(debug_sdl)
1138 //       {
1139 //         fprintf(stderr, "drawing blits[%d]\n", i);
1140 //         fprintf(stderr, "srcrect->x = %d\t srcrect->y = %d\t srcrect->w = %d\t srcrect->h = %d\n",
1141 //               blits[i].srcrect->x, blits[i].srcrect->y, blits[i].srcrect->w, blits[i].srcrect->h);
1142 //         fprintf(stderr, "dstrect->x = %d\t dstrect->y = %d\t dstrect->w = %d\t dstrect->h = %d\n",
1143 //               blits[i].dstrect->x, blits[i].dstrect->y, blits[i].dstrect->w, blits[i].dstrect->h);
1144 //       }
1145 
1146       SDL_BlitSurface(blits[i].src, blits[i].srcrect, screen, blits[i].dstrect);
1147     }
1148   }
1149 
1150 //  SNOW_draw();
1151 
1152   /* -- update the screen only where we need to! -- */
1153 //  if (SNOW_on)
1154 //    SDL_UpdateRects(screen, SNOW_add( (SDL_Rect*)&dstupdate, numupdates ), SNOW_rects);
1155 //  else
1156     SDL_UpdateRects(screen, numupdates, dstupdate);
1157 
1158   numupdates = 0;
1159   *frame = *frame + 1;
1160 }
1161 
1162 
1163 /* basically puts in an order to overdraw sprite with corresponding */
1164 /* rect of bkgd img                                                 */
T4K_EraseSprite(sprite * img,SDL_Surface * curr_bkgd,int x,int y)1165 int T4K_EraseSprite(sprite* img, SDL_Surface* curr_bkgd, int x, int y)
1166 {
1167   if( !img
1168    || img->cur < 0
1169    || img->cur > MAX_SPRITE_FRAMES
1170    || !img->frame[img->cur])
1171   {
1172     fprintf(stderr, "T4K_EraseSprite() - invalid 'img' arg!\n");
1173     return 0;
1174   }
1175   return T4K_EraseObject(img->frame[img->cur], curr_bkgd, x, y);
1176 }
1177 
1178 
1179 
1180 /*************************
1181 EraseObject : Erase an object from the screen
1182 **************************/
T4K_EraseObject(SDL_Surface * surf,SDL_Surface * curr_bkgd,int x,int y)1183 int T4K_EraseObject(SDL_Surface* surf, SDL_Surface* curr_bkgd, int x, int y)
1184 {
1185   struct blit* update = NULL;
1186 
1187   if(!surf)
1188   {
1189     fprintf(stderr, "T4K_EraseObject() - invalid 'surf' arg!\n");
1190     return 0;
1191   }
1192 
1193   if(numupdates >= MAX_UPDATES)
1194   {
1195     fprintf(stderr, "Warning - MAX_UPDATES exceeded, cannot add blit to queue\n");
1196     return 0;
1197   }
1198 
1199   update = &blits[numupdates++];
1200 
1201   if(!update || !update->srcrect || !update->dstrect)
1202   {
1203     fprintf(stderr, "T4K_EraseObject() - 'update' ptr invalid!\n");
1204     return 0;
1205   }
1206 
1207   update->src = curr_bkgd;
1208 
1209   /* take dimentsions from src surface: */
1210   update->srcrect->x = x;
1211   update->srcrect->y = y;
1212   update->srcrect->w = surf->w;
1213   update->srcrect->h = surf->h;
1214 
1215   /* NOTE this is needed because the letters may go beyond the size of */
1216   /* the fish, and we only erase the fish image before we redraw the   */
1217   /* fish followed by the letter - DSB                                 */
1218   /* add margin of a few pixels on each side: */
1219   update->srcrect->x -= ERASE_MARGIN;
1220   update->srcrect->y -= ERASE_MARGIN;
1221   update->srcrect->w += (ERASE_MARGIN * 2);
1222   update->srcrect->h += (ERASE_MARGIN * 2);
1223 
1224 
1225   /* Adjust srcrect so it doesn't go past bkgd: */
1226   if (update->srcrect->x < 0)
1227   {
1228     update->srcrect->w += update->srcrect->x; //so right edge stays correct
1229     update->srcrect->x = 0;
1230   }
1231   if (update->srcrect->y < 0)
1232   {
1233     update->srcrect->h += update->srcrect->y; //so bottom edge stays correct
1234     update->srcrect->y = 0;
1235   }
1236 
1237   if (update->srcrect->x + update->srcrect->w > curr_bkgd->w)
1238     update->srcrect->w = curr_bkgd->w - update->srcrect->x;
1239   if (update->srcrect->y + update->srcrect->h > curr_bkgd->h)
1240     update->srcrect->h = curr_bkgd->h - update->srcrect->y;
1241 
1242 
1243   update->dstrect->x = update->srcrect->x;
1244   update->dstrect->y = update->srcrect->y;
1245   update->dstrect->w = update->srcrect->w;
1246   update->dstrect->h = update->srcrect->h;
1247   update->type = 'E';
1248 
1249   return 1;
1250 }
1251 
1252 //#if 0
1253 
1254 /************************************************************************/
1255 /*                                                                      */
1256 /*        Begin text drawing functions                                  */
1257 /*                                                                      */
1258 /* These functions support text drawing using either SDL_Pango          */
1259 /* or SDL_ttf. SDL_Pango is preferable but is not available on all      */
1260 /* platforms. Code outside of this file does not have to worry about    */
1261 /* which library is used to do the actual rendering.                    */
1262 /************************************************************************/
1263 
1264 #define MAX_FONT_SIZE 40
1265 #define DEFAULT_FONT_SIZE 10
1266 
1267 //NOTE to test program with SDL_ttf, do "./configure --without-sdlpango"
1268 
1269 
1270 /*-- file-scope variables and local file prototypes for SDL_Pango-based code: */
1271 #if HAVE_LIBSDL_PANGO
1272 #include "SDL_Pango.h"
1273 SDLPango_Context* context = NULL;
1274 static SDLPango_Matrix* SDL_Colour_to_SDLPango_Matrix(const SDL_Color* cl);
1275 static int Set_SDL_Pango_Font_Size(int size);
1276 
1277 /*-- file-scope variables and local file prototypes for SDL_ttf-based code: */
1278 #else
1279 #include "SDL_ttf.h"
1280 /* We cache fonts here once loaded to improve performance: */
1281 TTF_Font* font_list[MAX_FONT_SIZE + 1] = {NULL};
1282 static void free_font_list(void);
1283 static TTF_Font* get_font(int size);
1284 static TTF_Font* load_font(const char* font_name, int font_size);
1285 #endif
1286 
1287 
1288 /* "Public" functions called from other files that use either */
1289 /*SDL_Pango or SDL_ttf:                                       */
1290 
1291 
1292 /* For setup, we either initialize SDL_Pango and set its context, */
1293 /* or we initialize SDL_ttf:                                      */
T4K_Setup_SDL_Text(void)1294 int T4K_Setup_SDL_Text(void)
1295 {
1296 #if HAVE_LIBSDL_PANGO
1297 
1298   DEBUGMSG(debug_sdl, "T4K_Setup_SDL_Text() - using SDL_Pango\n");
1299 
1300   SDLPango_Init();
1301   if (!Set_SDL_Pango_Font_Size(DEFAULT_FONT_SIZE))
1302   {
1303     fprintf(stderr, "\nError: I could not set SDL_Pango context\n");
1304     return 0;
1305   }
1306   return 1;
1307 
1308 #else
1309 /* using SDL_ttf: */
1310   DEBUGMSG(debug_sdl, "T4K_Setup_SDL_Text() - using SDL_ttf\n");
1311 
1312   if (TTF_Init() < 0)
1313   {
1314     fprintf(stderr, "\nError: I could not initialize SDL_ttf\n");
1315     return 0;
1316   }
1317   return 1;
1318 #endif
1319 }
1320 
1321 
1322 
T4K_Cleanup_SDL_Text(void)1323 void T4K_Cleanup_SDL_Text(void)
1324 {
1325 #if HAVE_LIBSDL_PANGO
1326   if(context != NULL)
1327     SDLPango_FreeContext(context);
1328   context = NULL;
1329 #else
1330   free_font_list();
1331   TTF_Quit();
1332 #endif
1333 }
1334 
1335 
1336 /* T4K_BlackOutline() creates a surface containing text of the designated */
1337 /* foreground color, surrounded by a black shadow, on a transparent    */
1338 /* background.  The appearance can be tuned by adjusting the number of */
1339 /* background copies and the offset where the foreground text is       */
1340 /* finally written (see below).                                        */
T4K_BlackOutline(const char * t,int size,SDL_Color * c)1341 SDL_Surface* T4K_BlackOutline(const char* t, int size, SDL_Color* c)
1342 {
1343   SDL_Surface* out = NULL;
1344   SDL_Surface* black_letters = NULL;
1345   SDL_Surface* white_letters = NULL;
1346   SDL_Surface* bg = NULL;
1347   SDL_Rect dstrect;
1348   Uint32 color_key;
1349 
1350 /* Make sure everything is sane before we proceed: */
1351 #if HAVE_LIBSDL_PANGO
1352   if (!context)
1353   {
1354     fprintf(stderr, "T4K_BlackOutline(): invalid SDL_Pango context - returning.\n");
1355     return NULL;
1356   }
1357 #else
1358   TTF_Font* font = get_font(size);
1359   if (!font)
1360   {
1361     fprintf(stderr, "T4K_BlackOutline(): could not load needed font - returning.\n");
1362     return NULL;
1363   }
1364 #endif
1365 
1366   if (!t || !c)
1367   {
1368     fprintf(stderr, "T4K_BlackOutline(): invalid ptr parameter, returning.\n");
1369     return NULL;
1370   }
1371 
1372   if (t[0] == '\0')
1373   {
1374     fprintf(stderr, "T4K_BlackOutline(): empty string, returning\n");
1375     return NULL;
1376   }
1377 
1378   DEBUGMSG(debug_sdl, "Entering T4K_BlackOutline():\n");
1379   DEBUGMSG(debug_sdl, "BlackOutline of \"%s\"\n", t );
1380 
1381 #if HAVE_LIBSDL_PANGO
1382   Set_SDL_Pango_Font_Size(size);
1383   SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_BLACK_LETTER);
1384   SDLPango_SetText(context, t, -1);
1385   black_letters = SDLPango_CreateSurfaceDraw(context);
1386 #else
1387   black_letters = TTF_RenderUTF8_Blended(font, t, black);
1388 #endif
1389 
1390   if (!black_letters)
1391   {
1392     fprintf (stderr, "Warning - T4K_BlackOutline() could not create image for %s\n", t);
1393     return NULL;
1394   }
1395 
1396   bg = SDL_CreateRGBSurface(SDL_SWSURFACE,
1397                             (black_letters->w) + 5,
1398                             (black_letters->h) + 5,
1399                              32,
1400                              rmask, gmask, bmask, amask);
1401   /* Use color key for eventual transparency: */
1402   color_key = SDL_MapRGB(bg->format, 30, 30, 30);
1403   SDL_FillRect(bg, NULL, color_key);
1404 
1405   /* Now draw black outline/shadow 2 pixels on each side: */
1406   dstrect.w = black_letters->w;
1407   dstrect.h = black_letters->h;
1408 
1409   /* NOTE: can make the "shadow" more or less pronounced by */
1410   /* changing the parameters of these loops.                */
1411   for (dstrect.x = 1; dstrect.x < 5; dstrect.x++)
1412     for (dstrect.y = 1; dstrect.y < 5; dstrect.y++)
1413       SDL_BlitSurface(black_letters , NULL, bg, &dstrect );
1414 
1415   SDL_FreeSurface(black_letters);
1416 
1417   /* --- Put the color version of the text on top! --- */
1418 #if HAVE_LIBSDL_PANGO
1419   /* convert color arg: */
1420   SDLPango_Matrix* color_matrix = SDL_Colour_to_SDLPango_Matrix(c);
1421 
1422   if (color_matrix)
1423   {
1424     SDLPango_SetDefaultColor(context, color_matrix);
1425     free(color_matrix);
1426   }
1427   else  /* fall back to just using white if conversion fails: */
1428     SDLPango_SetDefaultColor(context, MATRIX_TRANSPARENT_BACK_WHITE_LETTER);
1429 
1430   white_letters = SDLPango_CreateSurfaceDraw(context);
1431 
1432 #else
1433   white_letters = TTF_RenderUTF8_Blended(font, t, *c);
1434 #endif
1435 
1436   if (!white_letters)
1437   {
1438     fprintf (stderr, "Warning - T4K_BlackOutline() could not create image for %s\n", t);
1439     return NULL;
1440   }
1441 
1442   dstrect.x = 1;
1443   dstrect.y = 1;
1444   SDL_BlitSurface(white_letters, NULL, bg, &dstrect);
1445   SDL_FreeSurface(white_letters);
1446 
1447   /* --- Convert to the screen format for quicker blits --- */
1448   SDL_SetColorKey(bg, SDL_SRCCOLORKEY|SDL_RLEACCEL, color_key);
1449   out = SDL_DisplayFormatAlpha(bg);
1450   SDL_FreeSurface(bg);
1451 
1452   DEBUGMSG(debug_sdl, "\nLeaving T4K_BlackOutline(): \n");
1453 
1454   return out;
1455 }
1456 
1457 
1458 /* This (fast) function just returns a non-outlined surf */
1459 /* using either SDL_Pango or SDL_ttf                     */
T4K_SimpleText(const char * t,int size,SDL_Color * col)1460 SDL_Surface* T4K_SimpleText(const char *t, int size, SDL_Color* col)
1461 {
1462   SDL_Surface* surf = NULL;
1463 
1464   if (!t)
1465     return NULL;
1466   if (!col)
1467     col = &black;
1468 
1469 #if HAVE_LIBSDL_PANGO
1470   if (!context)
1471   {
1472     fprintf(stderr, "T4K_SimpleText() - context not valid!\n");
1473     return NULL;
1474   }
1475   else
1476   {
1477     SDLPango_Matrix colormatrix =
1478     {{
1479       {col->r,  col->r,  0,  0},
1480       {col->g,  col->g,  0,  0},
1481       {col->b,  col->b,  0,  0},
1482       {0,      255,      0,  0}
1483     }};
1484     Set_SDL_Pango_Font_Size(size);
1485     SDLPango_SetDefaultColor(context, &colormatrix );
1486     SDLPango_SetText(context, t, -1);
1487     surf = SDLPango_CreateSurfaceDraw(context);
1488   }
1489 
1490 #else
1491   {
1492     TTF_Font* font = get_font(size);
1493     if (!font)
1494       return NULL;
1495     surf = TTF_RenderUTF8_Blended(font, t, *col);
1496   }
1497 #endif
1498 
1499   return surf;
1500 }
1501 
1502 /* Here we calculate an estimate of the string length that will
1503  * fit within a box 'pixel_width' pixels wide.  The letter 'x'
1504  * was chosen for this calculation based on some googling that
1505  * suggested it gave a reasonable estimate - DSB.
1506  */
T4K_CharsForWidth(int fontsize,int pixel_width)1507 int T4K_CharsForWidth(int fontsize, int pixel_width)
1508 {
1509   char buf[256];
1510   int i = 0;
1511   int done = 0;
1512   SDL_Surface* s;
1513   for(i = 0; i < 255 && !done; i++)
1514   {
1515     buf[i] = 'x';
1516     buf[i + 1] = '\0';
1517     s = T4K_SimpleText(buf, fontsize, &white);
1518     if(s && s->w > pixel_width)  //means string of (i++) 'x' exceeds width
1519       done = 1;
1520     SDL_FreeSurface(s);
1521   }
1522   return  i;
1523 }
1524 
size_text(const char * text,int font_size,int * width,int * height)1525 int size_text(const char* text, int font_size, int* width, int* height)
1526 {
1527 #if HAVE_LIBSDL_PANGO
1528   int ret = 0;
1529   SDL_Surface* temptext = T4K_SimpleText(text, font_size, &black);
1530   if (width)
1531     *width = temptext->w;
1532   if (height)
1533     *height = temptext->h;
1534   SDL_FreeSurface(temptext);
1535   return ret;
1536 #else
1537   return TTF_SizeUTF8(get_font(font_size), text, width, height);
1538 #endif
1539 }
1540 /* This (fast) function just returns a non-outlined surf */
1541 /* using SDL_Pango if available, SDL_ttf as fallback     */
T4K_SimpleTextWithOffset(const char * t,int size,SDL_Color * col,int * glyph_offset)1542 SDL_Surface* T4K_SimpleTextWithOffset(const char *t, int size, SDL_Color* col, int *glyph_offset)
1543 {
1544   SDL_Surface* surf = NULL;
1545 
1546   if (!t||!col)
1547     return NULL;
1548 
1549 #if HAVE_LIBSDL_PANGO
1550   if (!context)
1551   {
1552     fprintf(stderr, "T4K_SimpleText() - context not valid!\n");
1553     return NULL;
1554   }
1555   else
1556   {
1557     SDLPango_Matrix colormatrix =
1558     {{
1559       {col->r,  col->r,  0,  0},
1560       {col->g,  col->g,  0,  0},
1561       {col->b,  col->b,  0,  0},
1562       {0,      255,      0,  0}
1563     }};
1564     Set_SDL_Pango_Font_Size(size);
1565     SDLPango_SetDefaultColor(context, &colormatrix );
1566     SDLPango_SetText(context, t, -1);
1567     surf = SDLPango_CreateSurfaceDraw(context);
1568     *glyph_offset = 0; // fixme?
1569   }
1570 
1571 #else
1572   {
1573     TTF_Font* font = get_font(size);
1574     if (!font)
1575       return NULL;
1576     surf = TTF_RenderUTF8_Blended(font, t, *col);
1577     {
1578       int h;
1579       int hmax = 0;
1580       int len = strlen(t);
1581       int i;
1582       for (i = 0; i < len; i++)
1583       {
1584         TTF_GlyphMetrics(font, t[i], NULL, NULL, NULL, &h, NULL);
1585         if (h > hmax)
1586 	  hmax = h;
1587       }
1588       *glyph_offset = hmax - TTF_FontAscent(font);
1589     }
1590   }
1591 #endif
1592 
1593   return surf;
1594 }
1595 
1596 
1597 
1598 /*-----------------------------------------------------------*/
1599 /* Local functions, callable only within SDL_extras, divided */
1600 /* according with which text lib we are using:               */
1601 /*-----------------------------------------------------------*/
1602 
1603 
1604 
1605 #if HAVE_LIBSDL_PANGO
1606 /* Local functions when using SDL_Pango:   */
1607 
1608 
1609 /* NOTE the scaling by 3/4 a few lines down represents a conversion from      */
1610 /* the usual text dpi of 72 to the typical screen dpi of 96. It gives         */
1611 /* font sizes fairly similar to a SDL_ttf font with the same numerical value. */
Set_SDL_Pango_Font_Size(int size)1612 static int Set_SDL_Pango_Font_Size(int size)
1613 {
1614   /* static so we can "remember" values from previous time through: */
1615   static int prev_pango_font_size;
1616   static char prev_font_name[FONT_NAME_LENGTH];
1617   /* Do nothing unless we need to change size or font: */
1618   if ((size == prev_pango_font_size)
1619       &&
1620       (0 == strncmp(prev_font_name, T4K_AskFontName(), sizeof(prev_font_name))))
1621     return 1;
1622   else
1623   {
1624     char buf[64];
1625 
1626     DEBUGMSG(debug_sdl, "Setting font size to %d\n", size);
1627 
1628     if(context != NULL)
1629       SDLPango_FreeContext(context);
1630     context = NULL;
1631     snprintf(buf, sizeof(buf), "%s %d", T4K_AskFontName(), (int)((size * 3)/4));
1632     context =  SDLPango_CreateContext_GivenFontDesc(buf);
1633   }
1634 
1635   if (!context)
1636     return 0;
1637   else
1638   {
1639     prev_pango_font_size = size;
1640     strncpy(prev_font_name, T4K_AskFontName(), sizeof(prev_font_name));
1641     return 1;
1642   }
1643 }
1644 
1645 
SDL_Colour_to_SDLPango_Matrix(const SDL_Color * cl)1646 SDLPango_Matrix* SDL_Colour_to_SDLPango_Matrix(const SDL_Color *cl)
1647 {
1648   int k = 0;
1649   SDLPango_Matrix* colour = NULL;
1650 
1651   if (!cl)
1652   {
1653     fprintf(stderr, "Invalid SDL_Color* arg\n");
1654     return NULL;
1655   }
1656 
1657   colour = (SDLPango_Matrix*)malloc(sizeof(SDLPango_Matrix));
1658 
1659   for(k = 0; k < 4; k++)
1660   {
1661     (*colour).m[0][k] = (*cl).r;
1662     (*colour).m[1][k] = (*cl).g;
1663     (*colour).m[2][k] = (*cl).b;
1664   }
1665   (*colour).m[3][0] = 0;
1666   (*colour).m[3][1] = 255;
1667   (*colour).m[3][2] = 0;
1668   (*colour).m[3][3] = 0;
1669 
1670   return colour;
1671 }
1672 
1673 #else
1674 
1675 /* Local functions when using SDL_ttf: */
1676 
free_font_list(void)1677 static void free_font_list(void)
1678 {
1679   int i;
1680   for(i = 0; i < MAX_FONT_SIZE; i++)
1681   {
1682     if(font_list[i])
1683     {
1684       TTF_CloseFont(font_list[i]);
1685       font_list[i] = NULL;
1686     }
1687   }
1688 }
1689 
1690 /* FIXME - could combine this with load_font() below:         */
1691 /* Loads and caches fonts in each size as they are requested: */
1692 /* We use the font size as an array index, keeping each size  */
1693 /* font in memory once loaded until cleanup.                  */
get_font(int size)1694 static TTF_Font* get_font(int size)
1695 {
1696   static char prev_font_name[FONT_NAME_LENGTH];
1697   if (size < 0)
1698   {
1699     fprintf(stderr, "Error - requested font size %d is negative\n", size);
1700     return NULL;
1701   }
1702 
1703   if (size > MAX_FONT_SIZE)
1704   {
1705     fprintf(stderr, "Error - requested font size %d exceeds max = %d, resetting.\n",
1706             size, MAX_FONT_SIZE);
1707     size = MAX_FONT_SIZE;
1708   }
1709 
1710   /* If the font has changed, we need to wipe out the old ones: */
1711   if (0 != strncmp(prev_font_name, T4K_AskFontName(), sizeof(prev_font_name)))
1712   {
1713     free_font_list();
1714     strncpy(prev_font_name, T4K_AskFontName(), sizeof(prev_font_name));
1715   }
1716 
1717   if(font_list[size] == NULL)
1718     font_list[size] = load_font(DEFAULT_FONT_NAME, size);
1719   return font_list[size];
1720 }
1721 
1722 /* FIXME: I think we need to provide a single default font with the program data, */
1723 /* then more flexible code to try to locate or load system fonts. DSB             */
1724 /* Returns ptr to loaded font if successful, NULL otherwise. */
load_font(const char * font_name,int font_size)1725 static TTF_Font* load_font(const char* font_name, int font_size)
1726 {
1727   TTF_Font* f;
1728   char fontfile[PATH_MAX];
1729   sprintf(fontfile, "%s/fonts/%s", COMMON_DATA_PREFIX, font_name);
1730 
1731   f = TTF_OpenFont(fontfile, font_size);
1732 
1733   /* HACK - better font searching needed! */
1734   /* This should mean that font wasn't bundled into data path, which for  */
1735   /* now means we are using Debian, so grab from Debian installation loc: */
1736   if (!f)
1737   {
1738     sprintf(fontfile, "/usr/share/fonts/truetype/ttf-sil-andika/AndikaDesRevG.ttf");
1739     f = TTF_OpenFont(fontfile, font_size);
1740   }
1741 
1742 
1743   if (f)
1744   {
1745     DEBUGMSG(debug_sdl, "LoadFont(): %s loaded successfully\n\n", fontfile);
1746     return f;
1747   }
1748   else
1749   {
1750    fprintf(stderr, "LoadFont(): %s NOT loaded successfully.\n", fontfile);
1751    return NULL;
1752   }
1753 }
1754 
1755 //#endif
1756 
1757 #endif
1758