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