Add location support (all four grand slam locations)
[tennix.git] / graphics.c
blobf27aee0f083c190343cfaf067fa2e288d8b2e5bd
2 /**
4 * Tennix! SDL Port
5 * Copyright (C) 2003, 2007, 2008, 2009 Thomas Perl <thp@thpinfo.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
22 **/
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include <string.h>
32 #include "tennix.h"
33 #include "graphics.h"
34 #include "archive.h"
35 #include "sound.h"
37 static Image* images;
39 static SDL_Surface *buffer;
40 static SDL_Surface *background;
42 static SDL_Rect *rect_update_cache;
43 static SDL_Rect *rect_update_old;
44 static int rect_update_cache_current;
45 static int rect_update_old_count;
47 Uint32 fading_start = 0;
49 static const char* graphics[] = {
50 "court.png",
51 "shadow.png",
52 "player-racket.png",
53 "ball.png",
54 "smallish-font.png",
55 "dkc2.png",
56 "referee.png",
57 "ctt_hard.png",
58 "ctt_clay.png",
59 "ctt_grass.png",
60 "sidebar.png",
61 "tennixlogo.png",
62 "btnplay.png",
63 "btnresume.png",
64 "btnquit.png",
65 "stadium.png",
66 "fog.png",
67 "fog2.png",
68 "night.png",
69 "talk.png",
70 "cursor.png",
71 "loc_margaret_court_arena.png",
72 "loc_stade_roland_garros.png",
73 "loc_court_no_1.png",
74 "loc_arthur_ashe_stadium.png"
77 void init_graphics() {
78 int i;
79 char *d;
80 SDL_Surface* data;
81 SDL_Surface* tmp;
82 TennixArchive *tnxar;
84 rect_update_cache = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
85 rect_update_old = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
86 rect_update_cache_current = 0;
87 rect_update_old_count = 0;
89 images = (Image*)calloc( GR_COUNT, sizeof( Image));
91 buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
92 screen->format->BitsPerPixel,
93 screen->format->Rmask,
94 screen->format->Gmask,
95 screen->format->Bmask,
96 screen->format->Amask);
98 background = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
99 screen->format->BitsPerPixel,
100 screen->format->Rmask,
101 screen->format->Gmask,
102 screen->format->Bmask,
103 screen->format->Amask);
105 if( buffer == NULL) {
106 fprintf( stderr, "Cannot create buffer surface: %s\n", SDL_GetError());
109 tnxar = tnxar_open(ARCHIVE_FILE);
110 if (tnxar == NULL) {
111 /* not found in cwd - try installed... */
112 tnxar = tnxar_open(ARCHIVE_FILE_INSTALLED);
113 assert(tnxar != NULL);
116 draw_button(40, (HEIGHT-40)/2, WIDTH-80, 40, 100, 100, 100, 1);
117 store_screen();
118 updatescr();
120 for( i=0; i<GR_COUNT; i++) {
121 if (tnxar_set_current_filename(tnxar, graphics[i]) != 0) {
122 d = tnxar_read_current(tnxar);
123 tmp = IMG_Load_RW(SDL_RWFromMem(d, tnxar_size_current(tnxar)), 0);
124 free(d);
125 } else {
126 fprintf(stderr, "Cannot find file: %s\n", graphics[i]);
127 exit(EXIT_FAILURE);
129 if( !tmp) {
130 fprintf( stderr, "Error: %s\n", SDL_GetError());
131 continue;
134 if( GRAPHICS_IS_FONT(i)) {
135 /* Convert to RGB w/ colorkey=black for opacity support */
136 SDL_SetColorKey( tmp, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB( tmp->format, 0, 0, 0));
137 data = SDL_ConvertSurface( tmp, screen->format, SDL_SRCCOLORKEY | SDL_RLEACCEL);
138 } else {
139 /* Convert to RGBA for alpha channel from PNG */
140 data = SDL_DisplayFormatAlpha( tmp);
142 SDL_FreeSurface( tmp);
144 if( !data) {
145 fprintf( stderr, "Error: %s\n", SDL_GetError());
146 continue;
148 images[i].data = data;
150 draw_button(40, (HEIGHT-40)/2, (WIDTH-80)*i/(GR_COUNT+SOUND_MAX), 40, 100, 250, 100, 0);
151 rectangle(40+BUTTON_BORDER*2, (HEIGHT-40)/2+20+BUTTON_BORDER, (WIDTH-80)*i/(GR_COUNT+SOUND_MAX)-BUTTON_BORDER*2, 10, 170, 250, 170);
152 updatescr();
154 tnxar_close(tnxar);
157 void uninit_graphics() {
158 int i;
160 for( i=0; i<GR_COUNT; i++) {
161 SDL_FreeSurface( images[i].data);
164 if( buffer != NULL) {
165 SDL_FreeSurface( buffer);
168 if (background != NULL) {
169 SDL_FreeSurface(background);
172 free(rect_update_cache);
173 free(rect_update_old);
174 free(images);
177 int get_image_width( image_id id) {
178 return images[id].data->w;
181 int get_image_height( image_id id) {
182 return images[id].data->h;
185 int get_sprite_width( image_id id, int items) {
186 return images[id].data->w / items;
189 void show_sprite( image_id id, int pos, int items, int x_offset, int y_offset, int opacity) {
190 SDL_Surface *bitmap;
191 SDL_Rect src, dst;
193 bitmap = images[id].data;
195 if( !bitmap) return;
197 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
199 dst.w = src.w = bitmap->w/items;
200 dst.h = src.h = bitmap->h;
201 src.x = src.w*pos;
202 src.y = 0;
203 dst.x = x_offset;
204 dst.y = y_offset;
206 SDL_BlitSurface( bitmap, &src, screen, &dst);
207 update_rect2(dst);
210 void fill_image_offset( image_id id, int x, int y, int w, int h, int offset_x, int offset_y) {
211 SDL_Surface *bitmap;
212 SDL_Rect src, dst;
213 int dx = 0, dy = 0, cx, cy;
215 if( id >= GR_COUNT) return;
217 bitmap = images[id].data;
219 /* Make negative offsets positive */
220 if (offset_x < 0) {
221 offset_x = (offset_x%bitmap->w)+bitmap->w;
223 if (offset_y < 0) {
224 offset_y = (offset_y%bitmap->h)+bitmap->h;
227 src.y = offset_y % bitmap->h;
228 while( dy < h) {
229 src.h = dst.h = cy = (h-dy > bitmap->h-src.y)?(bitmap->h-src.y):(h-dy);
230 dst.y = y+dy;
232 dx = 0;
233 src.x = offset_x % bitmap->w;
234 while( dx < w) {
235 src.w = dst.w = cx = (w-dx > bitmap->w-src.x)?(bitmap->w-src.x):(w-dx);
236 dst.x = x+dx;
238 SDL_BlitSurface( bitmap, &src, screen, &dst);
239 update_rect2(dst);
241 dx += cx;
242 src.x = 0;
245 dy += cy;
246 src.y = 0;
250 void line_horiz( int y, Uint8 r, Uint8 g, Uint8 b) {
251 rectangle(0, y, screen->w, 1, r, g, b);
254 void line_vert( int x, Uint8 r, Uint8 g, Uint8 b) {
255 rectangle(x, 0, 1, screen->h, r, g, b);
258 void rectangle( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b) {
259 Uint32 color = SDL_MapRGB( screen->format, r, g, b);
260 SDL_Rect rect;
262 rect.x = x;
263 rect.y = y;
264 rect.w = w;
265 rect.h = h;
267 SDL_FillRect( screen, &rect, color);
268 update_rect(x, y, w, h);
271 void draw_button( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
272 float diff = (pressed?1.0-BUTTON_HIGHLIGHT:1.0+BUTTON_HIGHLIGHT);
273 rectangle(x, y, w, h, MIN(r*diff, 255), MIN(g*diff, 255), MIN(b*diff, 255));
274 rectangle(x+BUTTON_BORDER, y+BUTTON_BORDER, w-BUTTON_BORDER, h-BUTTON_BORDER, MIN(r/diff, 255), MIN(g/diff, 255), MIN(b/diff, 255));
275 rectangle(x+BUTTON_BORDER, y+BUTTON_BORDER, w-2*BUTTON_BORDER, h-2*BUTTON_BORDER, r, g, b);
278 void draw_button_text(const char* s, int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
279 int font_x, font_y;
280 pressed = pressed?1:0;
281 draw_button( x, y, w, h, r, g, b, pressed);
282 font_x = x + w/2 - font_get_string_width( GR_SMALLISH_FONT, s)/2 + pressed*BUTTON_BORDER;
283 font_y = y + h/2 - get_image_height( GR_SMALLISH_FONT)/2 + pressed*BUTTON_BORDER;
284 font_draw_string( GR_SMALLISH_FONT, s, font_x, font_y, 0, 0);
287 void clear_screen()
289 SDL_Rect rect;
291 rect.x = rect.y = 0;
292 rect.w = WIDTH;
293 rect.h = HEIGHT;
295 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
298 void store_screen()
300 SDL_BlitSurface(screen, NULL, background, NULL);
301 rect_update_old[0].x = rect_update_old[0].y = 0;
302 rect_update_old[0].w = WIDTH;
303 rect_update_old[0].h = HEIGHT;
304 rect_update_old_count = 1;
305 rect_update_cache_current = 0;
308 void reset_screen()
310 int i;
311 SDL_Rect* tmp;
312 SDL_BlitSurface(background, NULL, screen, NULL);
314 for (i=0; i<rect_update_cache_current; i++) {
315 SDL_BlitSurface(background, &rect_update_cache[i], screen, &rect_update_cache[i]);
318 /* Save rects I've just updated for redraw later */
319 tmp = rect_update_cache;
320 rect_update_cache = rect_update_old;
321 rect_update_old = tmp;
322 rect_update_old_count = rect_update_cache_current;
323 rect_update_cache_current = 0;
326 void update_rect(Sint32 x, Sint32 y, Sint32 w, Sint32 h)
328 SDL_Rect* u = NULL;
329 static int inside = 0;
330 if (inside) return;
331 inside = 1;
333 #ifdef DRAW_UPDATE_RECTANGLE
334 rectangle(x, y, w, h, 50+rand()%200, 50+rand()%200 ,50+rand()%200);
335 #endif
337 if ((x >= WIDTH) | (y >= HEIGHT) | (x+w <= 0) | (y+h <= 0) | (!w) | (!h)) {
338 inside = 0;
339 return;
342 if (rect_update_cache_current == RECT_UPDATE_CACHE) {
343 fprintf(stderr, "Overflow\n");
344 rect_update_cache_current = 0;
347 u = &(rect_update_cache[rect_update_cache_current]);
349 if (x < 0/* && x+w > 0*/) {
350 w += x;
351 x = 0;
353 if (y < 0/* && y+h > 0*/) {
354 h += y;
355 y = 0;
358 if (x+w >= WIDTH) {
359 w -= (x+w-WIDTH+1);
362 if (y+h >= HEIGHT) {
363 h -= (y+h-HEIGHT+1);
366 if (w==0 || h==0) {
367 inside = 0;
368 return;
371 u->x = x;
372 u->y = y;
373 u->w = w;
374 u->h = h;
376 rect_update_cache_current++;
378 inside = 0;
381 void updatescr()
383 Uint32 ticks = SDL_GetTicks();
384 SDL_Rect *r_current = NULL, *end_current = NULL;
385 SDL_Rect *r_old = NULL;
387 static int fading_last_time = 0;
388 unsigned char fading_now = ticks < fading_start+FADE_DURATION;
390 if (fading_now) {
391 SDL_SetAlpha(buffer, SDL_SRCALPHA | SDL_RLEACCEL, 255-255*(ticks-fading_start)/FADE_DURATION);
392 SDL_BlitSurface(buffer, NULL, screen, NULL);
393 SDL_UpdateRect(screen, 0, 0, 0, 0);
394 } else if (fading_last_time && !fading_now) {
395 SDL_UpdateRect(screen, 0, 0, 0, 0);
396 } else {
397 if (rect_update_old_count == rect_update_cache_current) {
398 /* Merge rects into one single rect list */
399 r_old = rect_update_old;
400 r_current = rect_update_cache;
401 end_current = rect_update_cache + rect_update_cache_current;
402 while (r_current != end_current) {
403 r_old->w = MAX(r_current->x+r_current->w, r_old->x+r_old->w);
404 r_old->h = MAX(r_current->y+r_current->h, r_old->y+r_old->h);
405 r_old->x = MIN(r_current->x, r_old->x);
406 r_old->y = MIN(r_current->y, r_old->y);
407 r_old->w -= r_old->x;
408 r_old->h -= r_old->y;
410 r_current++;
411 r_old++;
413 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
414 } else {
415 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
416 SDL_UpdateRects(screen, rect_update_cache_current, rect_update_cache);
420 reset_screen();
421 fading_last_time = fading_now;
424 void start_fade() {
425 SDL_BlitSurface( screen, NULL, buffer, NULL);
426 fading_start = SDL_GetTicks();
429 int font_get_metrics(image_id id, unsigned char ch, int* xp, int* wp) {
430 /* Caching of x and width values for faster calculation */
431 static int xcache[0xFF][GRAPHICS_FONT_COUNT],
432 wcache[0xFF][GRAPHICS_FONT_COUNT];
433 static unsigned char cacheflag[0xFF][GRAPHICS_FONT_COUNT];
435 SDL_Surface *bitmap;
436 int pos, x = -1, w = 0;
437 int search_pos = 0, search_x = 0;
438 unsigned char font_index = id-GRAPHICS_FONT_FIRST;
439 Uint8 red, green, blue;
441 if(!GRAPHICS_IS_FONT(id)) return 0;
443 if (cacheflag[ch][font_index]) {
444 /* saved! */
445 if (xp != NULL) *xp = xcache[ch][font_index];
446 if (wp != NULL) *wp = wcache[ch][font_index];
447 return wcache[ch][font_index];
450 pos = toupper( ch) - ' '; /* ' ' = first character in font bitmap */
452 bitmap = images[id].data;
454 SDL_LockSurface( bitmap);
455 while( search_x < bitmap->w) {
456 GET_PIXEL_RGB( bitmap, search_x, 0, &red, &green, &blue);
458 /* Increase pos counter if we have a "marker" pixel (255,0,255) */
459 if( red > 250 && green < 10 && blue > 250) {
460 search_pos++;
461 if( search_pos == pos) {
462 x = search_x;
463 } else if( search_pos == pos + 1) {
464 w = search_x - x;
465 break;
469 search_x++;
471 SDL_UnlockSurface( bitmap);
473 if( wp != NULL) (*wp) = w;
474 if( xp != NULL) (*xp) = x;
476 cacheflag[ch][font_index]++;
477 xcache[ch][font_index] = x;
478 wcache[ch][font_index] = w;
480 return w;
483 int font_draw_char( image_id id, char ch, int x_offset, int y_offset) {
484 SDL_Surface *bitmap;
485 SDL_Rect src, dst;
486 int x = -1, w = 0;
488 font_get_metrics( id, ch, &x, &w);
489 if( x == -1) return w;
491 bitmap = images[id].data;
493 dst.w = src.w = w;
494 dst.h = src.h = bitmap->h - 1;
495 src.x = x;
496 src.y = 1;
497 dst.x = x_offset;
498 dst.y = y_offset;
500 SDL_BlitSurface( bitmap, &src, screen, &dst);
502 return src.w;
505 void font_draw_string_alpha( image_id id, const char* s, int x_offset, int y_offset, int start, int animation, int opacity) {
506 int y = y_offset;
507 int x = x_offset;
508 int additional_x = 0, additional_y = 0;
509 float xw = 0.0, xw_diff = 0.0;
510 unsigned int i;
511 SDL_Surface *bitmap;
513 if( id > GR_COUNT) return;
515 bitmap = images[id].data;
516 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
518 if( animation & ANIMATION_BUNGEE) {
519 xw = (25.0*sinf( start/10.0));
520 x -= xw / 2;
523 if( animation & ANIMATION_PENDULUM) {
524 x -= (int)(20.0*sinf( start/20.0));
525 additional_x += 21;
528 for( i=0; i<strlen(s); i++) {
529 if( animation & ANIMATION_WAVE) {
530 y = y_offset + (int)(3.0*sinf( start/10.0 + x/30.0));
532 x += font_draw_char( id, s[i], x, y);
533 if( animation & ANIMATION_BUNGEE) {
534 xw_diff += xw/strlen(s);
535 if( xw_diff > 1.0 || xw_diff < -1.0) {
536 x += (int)(xw_diff);
537 if( xw_diff > 1.0) {
538 xw_diff -= 1.0;
539 } else {
540 xw_diff += 1.0;
545 if (animation & ANIMATION_WAVE) {
546 additional_y += 4;
548 if (animation & ANIMATION_BUNGEE) {
549 additional_x += 26;
551 update_rect(x_offset-additional_x, y_offset-additional_y, x-x_offset+additional_x*2, get_image_height(id)+additional_y*2);
554 int font_get_string_width( image_id id, const char* s) {
555 int w = 0;
556 unsigned int i;
558 for( i=0; i<strlen(s); i++) {
559 w += font_get_metrics( id, s[i], NULL, NULL);
562 return w;
565 void draw_line_faded( int x1, int y1, int x2, int y2, int r, int g, int b, int r2, int g2, int b2) {
566 float step, dx, dy, x = x1, y = y1;
567 int i;
568 char fade = (r!=r2 || g!=g2 || b!=b2);
570 step = (float)(abs(x2-x1)>abs(y2-y1)?abs(x2-x1):abs(y2-y1));
571 dx = (float)(x2-x1) / step;
572 dy = (float)(y2-y1) / step;
574 SDL_LockSurface( screen);
575 for( i=0; i<step; i++) {
576 x += dx;
577 y += dy;
578 if( x < 0.0 || x >= WIDTH || y < 0.0 || y >= HEIGHT) {
579 continue;
581 if( fade) {
582 SET_PIXEL_RGB( screen, (int)x, (int)y, (r*(step-i)+r2*i)/step, (g*(step-i)+g2*i)/step, (b*(step-i)+b2*i)/step);
583 } else {
584 SET_PIXEL_RGB( screen, (int)x, (int)y, r, g, b);
587 SDL_UnlockSurface( screen);