Scripts and settings for profiling
[tennix.git] / graphics.c
blob45f31f7e1d05cb932839e1ac37ef58a32d8642a4
2 /**
4 * Tennix! SDL Port
5 * Copyright (C) 2003, 2007, 2008 Thomas Perl <thp@perli.net>
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>
28 #include "tennix.h"
29 #include "graphics.h"
30 #include "archive.h"
32 static Image* images;
34 static SDL_Surface *buffer;
35 static SDL_Surface *background;
37 static SDL_Rect *rect_update_cache;
38 static SDL_Rect *rect_update_old;
39 static int rect_update_cache_current;
40 static int rect_update_old_count;
42 static Uint32 fading_start = 0;
44 static char* graphics[] = {
45 "court.png",
46 "shadow.png",
47 "player-racket.png",
48 "ball.png",
49 "smallish-font.png",
50 "dkc2.png",
51 "referee.png",
52 "ctt_hard.png",
53 "ctt_clay.png",
54 "ctt_grass.png",
55 "sidebar.png",
56 "tennixlogo.png",
57 "btnplay.png",
58 "btnresume.png",
59 "btnquit.png",
60 "stadium.png",
61 "fog.png",
62 "fog2.png",
63 "night.png",
64 "talk.png"
67 void init_graphics() {
68 int i;
69 SDL_Surface* data;
70 SDL_Surface* tmp;
71 TennixArchive *tnxar;
73 rect_update_cache = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
74 rect_update_old = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
75 rect_update_cache_current = 0;
76 rect_update_old_count = 0;
78 images = (Image*)calloc( GR_COUNT, sizeof( Image));
80 tnxar = tnxar_open(ARCHIVE_FILE);
82 for( i=0; i<GR_COUNT; i++) {
83 if (tnxar_set_current_filename(tnxar, graphics[i]) != 0) {
84 tmp = IMG_Load_RW(SDL_RWFromMem(tnxar_read_current(tnxar), tnxar_size_current(tnxar)), 1);
85 } else {
86 fprintf(stderr, "Cannot find file: %s\n", graphics[i]);
87 exit(EXIT_FAILURE);
89 if( !tmp) {
90 fprintf( stderr, "Error: %s\n", SDL_GetError());
91 continue;
94 if( GRAPHICS_IS_FONT(i)) {
95 /* Convert to RGB w/ colorkey=black for opacity support */
96 SDL_SetColorKey( tmp, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB( tmp->format, 0, 0, 0));
97 data = SDL_ConvertSurface( tmp, screen->format, SDL_SRCCOLORKEY | SDL_RLEACCEL);
98 } else {
99 /* Convert to RGBA for alpha channel from PNG */
100 data = SDL_DisplayFormatAlpha( tmp);
102 SDL_FreeSurface( tmp);
104 if( !data) {
105 fprintf( stderr, "Error: %s\n", SDL_GetError());
106 continue;
108 images[i].data = data;
110 tnxar_close(tnxar);
112 buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
113 screen->format->BitsPerPixel,
114 screen->format->Rmask,
115 screen->format->Gmask,
116 screen->format->Bmask,
117 screen->format->Amask);
119 background = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
120 screen->format->BitsPerPixel,
121 screen->format->Rmask,
122 screen->format->Gmask,
123 screen->format->Bmask,
124 screen->format->Amask);
126 if( buffer == NULL) {
127 fprintf( stderr, "Cannot create buffer surface: %s\n", SDL_GetError());
131 void uninit_graphics() {
132 int i;
134 for( i=0; i<GR_COUNT; i++) {
135 SDL_FreeSurface( images[i].data);
138 if( buffer != NULL) {
139 SDL_FreeSurface( buffer);
142 if (background != NULL) {
143 SDL_FreeSurface(background);
146 free(rect_update_cache);
147 free(rect_update_old);
148 free(images);
151 int get_image_width( image_id id) {
152 return images[id].data->w;
155 int get_image_height( image_id id) {
156 return images[id].data->h;
159 int get_sprite_width( image_id id, int items) {
160 return images[id].data->w / items;
163 void show_sprite( image_id id, int pos, int items, int x_offset, int y_offset, int opacity) {
164 SDL_Surface *bitmap;
165 SDL_Rect src, dst;
167 bitmap = images[id].data;
169 if( !bitmap) return;
171 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
173 dst.w = src.w = bitmap->w/items;
174 dst.h = src.h = bitmap->h;
175 src.x = src.w*pos;
176 src.y = 0;
177 dst.x = x_offset;
178 dst.y = y_offset;
180 SDL_BlitSurface( bitmap, &src, screen, &dst);
181 update_rect2(dst);
184 void fill_image_offset( image_id id, int x, int y, int w, int h, int offset_x, int offset_y) {
185 SDL_Surface *bitmap;
186 SDL_Rect src, dst;
187 int dx = 0, dy = 0, cx, cy;
189 if( id >= GR_COUNT) return;
191 bitmap = images[id].data;
193 /* Make negative offsets positive */
194 if (offset_x < 0) {
195 offset_x = (offset_x%bitmap->w)+bitmap->w;
197 if (offset_y < 0) {
198 offset_y = (offset_y%bitmap->h)+bitmap->h;
201 src.y = offset_y % bitmap->h;
202 while( dy < h) {
203 src.h = dst.h = cy = (h-dy > bitmap->h-src.y)?(bitmap->h-src.y):(h-dy);
204 dst.y = y+dy;
206 dx = 0;
207 src.x = offset_x % bitmap->w;
208 while( dx < w) {
209 src.w = dst.w = cx = (w-dx > bitmap->w-src.x)?(bitmap->w-src.x):(w-dx);
210 dst.x = x+dx;
212 SDL_BlitSurface( bitmap, &src, screen, &dst);
213 update_rect2(dst);
215 dx += cx;
216 src.x = 0;
219 dy += cy;
220 src.y = 0;
224 void line_horiz( int y, Uint8 r, Uint8 g, Uint8 b) {
225 rectangle(0, y, screen->w, 1, r, g, b);
228 void line_vert( int x, Uint8 r, Uint8 g, Uint8 b) {
229 rectangle(x, 0, 1, screen->h, r, g, b);
232 void rectangle( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b) {
233 Uint32 color = SDL_MapRGB( screen->format, r, g, b);
234 SDL_Rect rect;
236 rect.x = x;
237 rect.y = y;
238 rect.w = w;
239 rect.h = h;
241 SDL_FillRect( screen, &rect, color);
242 update_rect(x, y, w, h);
245 void draw_button( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
246 float diff = (pressed?1.0-BUTTON_HIGHLIGHT:1.0+BUTTON_HIGHLIGHT);
247 int border = BUTTON_BORDER;
248 rectangle( x, y, w, h, r*diff, g*diff, b*diff);
249 rectangle( x+border, y+border, w-border, h-border, r/diff, g/diff, b/diff);
250 rectangle( x+border, y+border, w-2*border, h-2*border, r, g, b);
253 void draw_button_text( char* s, int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
254 int font_x, font_y;
255 pressed = pressed?1:0;
256 draw_button( x, y, w, h, r, g, b, pressed);
257 font_x = x + w/2 - font_get_string_width( GR_SMALLISH_FONT, s)/2 + pressed*BUTTON_BORDER;
258 font_y = y + h/2 - get_image_height( GR_SMALLISH_FONT)/2 + pressed*BUTTON_BORDER;
259 font_draw_string( GR_SMALLISH_FONT, s, font_x, font_y, 0, 0);
262 void clear_screen()
264 SDL_Rect rect;
266 rect.x = rect.y = 0;
267 rect.w = WIDTH;
268 rect.h = HEIGHT;
270 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
273 void store_screen()
275 SDL_BlitSurface(screen, NULL, background, NULL);
276 rect_update_old[0].x = rect_update_old[0].y = 0;
277 rect_update_old[0].w = WIDTH;
278 rect_update_old[0].h = HEIGHT;
279 rect_update_old_count = 1;
280 rect_update_cache_current = 0;
283 void reset_screen()
285 int i;
286 SDL_Rect* tmp;
287 SDL_BlitSurface(background, NULL, screen, NULL);
289 for (i=0; i<rect_update_cache_current; i++) {
290 SDL_BlitSurface(background, &rect_update_cache[i], screen, &rect_update_cache[i]);
293 /* Save rects I've just updated for redraw later */
294 tmp = rect_update_cache;
295 rect_update_cache = rect_update_old;
296 rect_update_old = tmp;
297 rect_update_old_count = rect_update_cache_current;
298 rect_update_cache_current = 0;
301 void update_rect(Sint32 x, Sint32 y, Sint32 w, Sint32 h)
303 static int inside = 0;
304 if (inside) return;
305 inside = 1;
307 #ifdef DRAW_UPDATE_RECTANGLE
308 rectangle(x, y, w, h, 50+rand()%200, 50+rand()%200 ,50+rand()%200);
309 #endif
311 if ((x >= WIDTH) | (y >= HEIGHT) | (x+w <= 0) | (y+h <= 0) | (!w) | (!h)) {
312 inside = 0;
313 return;
316 if (rect_update_cache_current == RECT_UPDATE_CACHE) {
317 fprintf(stderr, "Overflow\n");
318 rect_update_cache_current = 0;
321 SDL_Rect* u = &(rect_update_cache[rect_update_cache_current]);
323 if (x < 0/* && x+w > 0*/) {
324 w += x;
325 x = 0;
327 if (y < 0/* && y+h > 0*/) {
328 h += y;
329 y = 0;
332 if (x+w >= WIDTH) {
333 w -= (x+w-WIDTH+1);
336 if (y+h >= HEIGHT) {
337 h -= (y+h-HEIGHT+1);
340 if (w==0 || h==0) {
341 inside = 0;
342 return;
345 u->x = x;
346 u->y = y;
347 u->w = w;
348 u->h = h;
350 rect_update_cache_current++;
352 inside = 0;
355 void updatescr()
357 int ticks = SDL_GetTicks();
358 SDL_Rect *r_current = NULL, *end_current = NULL;
359 SDL_Rect *r_old = NULL;
361 static int fading_last_time = 0;
362 int fading_now = is_fading();
364 if (fading_now) {
365 SDL_SetAlpha(buffer, SDL_SRCALPHA | SDL_RLEACCEL, 255-255*(ticks-fading_start)/FADE_DURATION);
366 SDL_BlitSurface(buffer, NULL, screen, NULL);
367 SDL_UpdateRect(screen, 0, 0, 0, 0);
368 } else if (fading_last_time && !fading_now) {
369 SDL_UpdateRect(screen, 0, 0, 0, 0);
370 } else {
371 if (rect_update_old_count == rect_update_cache_current) {
372 /* Merge rects into one single rect list */
373 r_old = rect_update_old;
374 r_current = rect_update_cache;
375 end_current = rect_update_cache + rect_update_cache_current;
376 while (r_current != end_current) {
377 r_old->w = MAX(r_current->x+r_current->w, r_old->x+r_old->w);
378 r_old->h = MAX(r_current->y+r_current->h, r_old->y+r_old->h);
379 r_old->x = MIN(r_current->x, r_old->x);
380 r_old->y = MIN(r_current->y, r_old->y);
381 r_old->w -= r_old->x;
382 r_old->h -= r_old->y;
384 r_current++;
385 r_old++;
387 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
388 } else {
389 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
390 SDL_UpdateRects(screen, rect_update_cache_current, rect_update_cache);
394 reset_screen();
395 fading_last_time = fading_now;
398 void start_fade() {
399 SDL_BlitSurface( screen, NULL, buffer, NULL);
400 fading_start = SDL_GetTicks();
403 int is_fading() {
404 return SDL_GetTicks() < fading_start+FADE_DURATION;
407 int font_get_metrics( image_id id, char ch, int* xp, int* wp) {
408 SDL_Surface *bitmap;
409 int pos, x = -1, w = 0;
410 int search_pos = 0, search_x = 0;
411 Uint8 red, green, blue;
413 if( id >= GR_COUNT) return 0;
415 pos = toupper( ch) - ' '; /* ' ' = first character in font bitmap */
417 bitmap = images[id].data;
419 SDL_LockSurface( bitmap);
420 while( search_x < bitmap->w) {
421 GET_PIXEL_RGB( bitmap, search_x, 0, &red, &green, &blue);
423 /* Increase pos counter if we have a "marker" pixel (255,0,255) */
424 if( red > 250 && green < 10 && blue > 250) {
425 search_pos++;
426 if( search_pos == pos) {
427 x = search_x;
428 } else if( search_pos == pos + 1) {
429 w = search_x - x;
430 break;
434 search_x++;
436 SDL_UnlockSurface( bitmap);
438 if( wp != NULL) (*wp) = w;
439 if( xp != NULL) (*xp) = x;
441 return w;
444 int font_draw_char( image_id id, char ch, int x_offset, int y_offset) {
445 SDL_Surface *bitmap;
446 SDL_Rect src, dst;
447 int x = -1, w = 0;
449 font_get_metrics( id, ch, &x, &w);
450 if( x == -1) return w;
452 bitmap = images[id].data;
454 dst.w = src.w = w;
455 dst.h = src.h = bitmap->h - 1;
456 src.x = x;
457 src.y = 1;
458 dst.x = x_offset;
459 dst.y = y_offset;
461 SDL_BlitSurface( bitmap, &src, screen, &dst);
463 return src.w;
466 void font_draw_string_alpha( image_id id, const char* s, int x_offset, int y_offset, int start, int animation, int opacity) {
467 int y = y_offset;
468 int x = x_offset;
469 int i, additional_x = 0, additional_y = 0;
470 float xw = 0.0, xw_diff;
471 SDL_Surface *bitmap;
473 if( id > GR_COUNT) return;
475 bitmap = images[id].data;
476 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
478 if( animation & ANIMATION_BUNGEE) {
479 xw = (25.0*sinf( start/10.0));
480 xw_diff = 0.0;
481 x -= xw / 2;
484 if( animation & ANIMATION_PENDULUM) {
485 x -= (int)(20.0*sinf( start/20.0));
486 additional_x += 21;
489 for( i=0; i<strlen(s); i++) {
490 if( animation & ANIMATION_WAVE) {
491 y = y_offset + (int)(3.0*sinf( start/10.0 + x/30.0));
493 x += font_draw_char( id, s[i], x, y);
494 if( animation & ANIMATION_BUNGEE) {
495 xw_diff += xw/strlen(s);
496 if( xw_diff > 1.0 || xw_diff < -1.0) {
497 x += (int)(xw_diff);
498 if( xw_diff > 1.0) {
499 xw_diff -= 1.0;
500 } else {
501 xw_diff += 1.0;
506 if (animation & ANIMATION_WAVE) {
507 additional_y += 4;
509 if (animation & ANIMATION_BUNGEE) {
510 additional_x += 26;
512 update_rect(x_offset-additional_x, y_offset-additional_y, x-x_offset+additional_x*2, get_image_height(id)+additional_y*2);
515 int font_get_string_width( image_id id, const char* s) {
516 int w = 0, i;
518 for( i=0; i<strlen(s); i++) {
519 w += font_get_metrics( id, s[i], NULL, NULL);
522 return w;
525 void draw_line_faded( int x1, int y1, int x2, int y2, int r, int g, int b, int r2, int g2, int b2) {
526 float step, dx, dy, x = x1, y = y1;
527 int i;
528 char fade = (r!=r2 || g!=g2 || b!=b2);
530 step = (float)(abs(x2-x1)>abs(y2-y1)?abs(x2-x1):abs(y2-y1));
531 dx = (float)(x2-x1) / step;
532 dy = (float)(y2-y1) / step;
534 SDL_LockSurface( screen);
535 for( i=0; i<step; i++) {
536 x += dx;
537 y += dy;
538 if( x < 0.0 || x >= WIDTH || y < 0.0 || y >= HEIGHT) {
539 continue;
541 if( fade) {
542 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);
543 } else {
544 SET_PIXEL_RGB( screen, (int)x, (int)y, r, g, b);
547 SDL_UnlockSurface( screen);