Tennix 0.6.0 and documentation updates
[tennix.git] / graphics.c
blobdbb1fa1c52c6e913022c3108305ff5ae1ff795d2
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"
31 static Image* images;
33 static SDL_Surface *buffer;
34 static SDL_Surface *background;
36 static SDL_Rect *rect_update_cache;
37 static SDL_Rect *rect_update_old;
38 static int rect_update_cache_current;
39 static int rect_update_old_count;
41 static Uint32 fading_start = 0;
43 #include "data/graphics_data.c"
44 static const ResourceData graphics[] = {
45 RESOURCE(court),
46 RESOURCE(shadow),
47 RESOURCE(player_racket),
48 RESOURCE(ground),
49 RESOURCE(ball),
50 RESOURCE(menu),
51 RESOURCE(smallish_font),
52 RESOURCE(dkc2),
53 RESOURCE(referee),
54 RESOURCE(ctt_hard),
55 RESOURCE(ctt_clay),
56 RESOURCE(ctt_grass)
59 void init_graphics() {
60 int i;
61 SDL_Surface* data;
62 SDL_Surface* tmp;
64 #ifndef MACOSX
65 tmp = IMG_Load_RW( SDL_RWFromConstMem( icon, sizeof(icon)), 1);
66 if( tmp != NULL) {
67 SDL_WM_SetIcon( tmp, NULL);
68 SDL_FreeSurface( tmp);
70 #endif
72 rect_update_cache = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
73 rect_update_old = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
74 rect_update_cache_current = 0;
75 rect_update_old_count = 0;
77 images = (Image*)calloc( GR_COUNT, sizeof( Image));
79 for( i=0; i<GR_COUNT; i++) {
80 tmp = IMG_Load_RW( SDL_RWFromConstMem( graphics[i].data, graphics[i].size), 1);
81 if( !tmp) {
82 fprintf( stderr, "Error: %s\n", SDL_GetError());
83 continue;
86 if( GRAPHICS_IS_FONT(i)) {
87 /* Convert to RGB w/ colorkey=black for opacity support */
88 SDL_SetColorKey( tmp, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB( tmp->format, 0, 0, 0));
89 data = SDL_ConvertSurface( tmp, screen->format, SDL_SRCCOLORKEY | SDL_RLEACCEL);
90 } else {
91 /* Convert to RGBA for alpha channel from PNG */
92 data = SDL_DisplayFormatAlpha( tmp);
94 SDL_FreeSurface( tmp);
96 if( !data) {
97 fprintf( stderr, "Error: %s\n", SDL_GetError());
98 continue;
100 images[i].data = data;
103 buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
104 screen->format->BitsPerPixel,
105 screen->format->Rmask,
106 screen->format->Gmask,
107 screen->format->Bmask,
108 screen->format->Amask);
110 background = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
111 screen->format->BitsPerPixel,
112 screen->format->Rmask,
113 screen->format->Gmask,
114 screen->format->Bmask,
115 screen->format->Amask);
117 if( buffer == NULL) {
118 fprintf( stderr, "Cannot create buffer surface: %s\n", SDL_GetError());
122 void uninit_graphics() {
123 int i;
125 for( i=0; i<GR_COUNT; i++) {
126 SDL_FreeSurface( images[i].data);
129 if( buffer != NULL) {
130 SDL_FreeSurface( buffer);
133 if (background != NULL) {
134 SDL_FreeSurface(background);
137 free(rect_update_cache);
138 free(rect_update_old);
139 free(images);
142 int get_image_width( image_id id) {
143 return images[id].data->w;
146 int get_image_height( image_id id) {
147 return images[id].data->h;
150 int get_sprite_width( image_id id, int items) {
151 return images[id].data->w / items;
154 void show_sprite( image_id id, int pos, int items, int x_offset, int y_offset, int opacity) {
155 SDL_Surface *bitmap;
156 SDL_Rect src, dst;
158 bitmap = images[id].data;
160 if( !bitmap) return;
162 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
164 dst.w = src.w = bitmap->w/items;
165 dst.h = src.h = bitmap->h;
166 src.x = src.w*pos;
167 src.y = 0;
168 dst.x = x_offset;
169 dst.y = y_offset;
171 SDL_BlitSurface( bitmap, &src, screen, &dst);
172 update_rect2(dst);
175 void fill_image( image_id id, int x, int y, int w, int h) {
176 SDL_Surface *bitmap;
177 SDL_Rect src, dst;
178 int dx = 0, dy = 0, cx, cy;
180 if( id >= GR_COUNT) return;
182 bitmap = images[id].data;
183 src.x = src.y = 0;
185 while( dy < h) {
186 src.h = dst.h = cy = (h-dy > bitmap->h)?(bitmap->h):(h-dy);
187 dst.y = y+dy;
189 dx = 0;
190 while( dx < w) {
191 src.w = dst.w = cx = (w-dx > bitmap->w)?(bitmap->w):(w-dx);
192 dst.x = x+dx;
194 SDL_BlitSurface( bitmap, &src, screen, &dst);
195 update_rect2(dst);
197 dx += cx;
200 dy += cy;
204 void line_horiz( int y, Uint8 r, Uint8 g, Uint8 b) {
205 rectangle(0, y, screen->w, 1, r, g, b);
208 void line_vert( int x, Uint8 r, Uint8 g, Uint8 b) {
209 rectangle(x, 0, 1, screen->h, r, g, b);
212 void rectangle( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b) {
213 Uint32 color = SDL_MapRGB( screen->format, r, g, b);
214 SDL_Rect rect;
216 rect.x = x;
217 rect.y = y;
218 rect.w = w;
219 rect.h = h;
221 SDL_FillRect( screen, &rect, color);
222 update_rect(x, y, w, h);
225 void draw_button( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
226 float diff = (pressed?1.0-BUTTON_HIGHLIGHT:1.0+BUTTON_HIGHLIGHT);
227 int border = BUTTON_BORDER;
228 rectangle( x, y, w, h, r*diff, g*diff, b*diff);
229 rectangle( x+border, y+border, w-border, h-border, r/diff, g/diff, b/diff);
230 rectangle( x+border, y+border, w-2*border, h-2*border, r, g, b);
233 void draw_button_text( char* s, int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
234 int font_x, font_y;
235 pressed = pressed?1:0;
236 draw_button( x, y, w, h, r, g, b, pressed);
237 font_x = x + w/2 - font_get_string_width( GR_SMALLISH_FONT, s)/2 + pressed*BUTTON_BORDER;
238 font_y = y + h/2 - get_image_height( GR_SMALLISH_FONT)/2 + pressed*BUTTON_BORDER;
239 font_draw_string( GR_SMALLISH_FONT, s, font_x, font_y, 0, 0);
242 void show_image( image_id id, int x_offset, int y_offset, int opacity) {
243 show_sprite( id, 0, 1, x_offset, y_offset, opacity);
246 void clear_screen()
248 SDL_Rect rect;
250 rect.x = rect.y = 0;
251 rect.w = WIDTH;
252 rect.h = HEIGHT;
254 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
255 store_screen();
258 void store_screen()
260 SDL_BlitSurface(screen, NULL, background, NULL);
261 rect_update_old[0].x = rect_update_old[0].y = 0;
262 rect_update_old[0].w = WIDTH;
263 rect_update_old[0].h = HEIGHT;
264 rect_update_old_count = 1;
265 rect_update_cache_current = 0;
268 void reset_screen()
270 int i;
271 SDL_Rect* tmp;
272 SDL_BlitSurface(background, NULL, screen, NULL);
274 for (i=0; i<rect_update_cache_current; i++) {
275 SDL_BlitSurface(background, &rect_update_cache[i], screen, &rect_update_cache[i]);
278 /* Save rects I've just updated for redraw later */
279 tmp = rect_update_cache;
280 rect_update_cache = rect_update_old;
281 rect_update_old = tmp;
282 rect_update_old_count = rect_update_cache_current;
283 rect_update_cache_current = 0;
286 void update_rect(Sint32 x, Sint32 y, Sint32 w, Sint32 h)
288 //rectangle(x, y, w, h, 50+rand()%200, 50+rand()%200 ,50+rand()%200);
290 if (x >= WIDTH || y >= HEIGHT || x+w <= 0 || y+h <= 0) {
291 return;
294 if (rect_update_cache_current == RECT_UPDATE_CACHE) {
295 fprintf(stderr, "Overflow\n");
296 rect_update_cache_current = 0;
299 SDL_Rect* u = &(rect_update_cache[rect_update_cache_current]);
301 if (x < 0 && x+w > 0) {
302 w += x;
303 x = 0;
305 if (y < 0 && y+h > 0) {
306 h += y;
307 y = 0;
310 if (x+w >= WIDTH) {
311 w -= (x+w-WIDTH+1);
314 if (y+h >= HEIGHT) {
315 h -= (y+h-HEIGHT+1);
318 if (w==0 || h==0) {
319 return;
322 u->x = x;
323 u->y = y;
324 u->w = w;
325 u->h = h;
327 rect_update_cache_current++;
330 void updatescr()
332 int ticks = SDL_GetTicks();
334 static int fading_last_time = 0;
335 int fading_now = is_fading();
337 if (fading_now) {
338 SDL_SetAlpha(buffer, SDL_SRCALPHA | SDL_RLEACCEL, 255-255*(ticks-fading_start)/FADE_DURATION);
339 SDL_BlitSurface(buffer, NULL, screen, NULL);
340 SDL_UpdateRect(screen, 0, 0, 0, 0);
341 } else if (fading_last_time && !fading_now) {
342 SDL_UpdateRect(screen, 0, 0, 0, 0);
343 } else {
344 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
345 SDL_UpdateRects(screen, rect_update_cache_current, rect_update_cache);
348 reset_screen();
349 fading_last_time = fading_now;
352 void start_fade() {
353 SDL_BlitSurface( screen, NULL, buffer, NULL);
354 fading_start = SDL_GetTicks();
357 int is_fading() {
358 return SDL_GetTicks() < fading_start+FADE_DURATION;
361 int font_get_metrics( image_id id, char ch, int* xp, int* wp) {
362 SDL_Surface *bitmap;
363 int pos, x = -1, w = 0;
364 int search_pos = 0, search_x = 0;
365 Uint8 red, green, blue;
367 if( id >= GR_COUNT) return 0;
369 pos = toupper( ch) - ' '; /* ' ' = first character in font bitmap */
371 bitmap = images[id].data;
373 SDL_LockSurface( bitmap);
374 while( search_x < bitmap->w) {
375 GET_PIXEL_RGB( bitmap, search_x, 0, &red, &green, &blue);
377 /* Increase pos counter if we have a "marker" pixel (255,0,255) */
378 if( red > 250 && green < 10 && blue > 250) {
379 search_pos++;
380 if( search_pos == pos) {
381 x = search_x;
382 } else if( search_pos == pos + 1) {
383 w = search_x - x;
384 break;
388 search_x++;
390 SDL_UnlockSurface( bitmap);
392 if( wp != NULL) (*wp) = w;
393 if( xp != NULL) (*xp) = x;
395 return w;
398 int font_draw_char( image_id id, char ch, int x_offset, int y_offset) {
399 SDL_Surface *bitmap;
400 SDL_Rect src, dst;
401 int x = -1, w = 0;
403 font_get_metrics( id, ch, &x, &w);
404 if( x == -1) return w;
406 bitmap = images[id].data;
408 dst.w = src.w = w;
409 dst.h = src.h = bitmap->h - 1;
410 src.x = x;
411 src.y = 1;
412 dst.x = x_offset;
413 dst.y = y_offset;
415 SDL_BlitSurface( bitmap, &src, screen, &dst);
417 return src.w;
420 void font_draw_string_alpha( image_id id, const char* s, int x_offset, int y_offset, int start, int animation, int opacity) {
421 int y = y_offset;
422 int x = x_offset;
423 int i, additional_x = 0, additional_y = 0;
424 float xw = 0.0, xw_diff;
425 SDL_Surface *bitmap;
427 if( id > GR_COUNT) return;
429 bitmap = images[id].data;
430 SDL_SetAlpha( bitmap, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
432 if( animation & ANIMATION_BUNGEE) {
433 xw = (25.0*sinf( start/10.0));
434 xw_diff = 0.0;
435 x -= xw / 2;
438 if( animation & ANIMATION_PENDULUM) {
439 x -= (int)(20.0*sinf( start/20.0));
440 additional_x += 21;
443 for( i=0; i<strlen(s); i++) {
444 if( animation & ANIMATION_WAVE) {
445 y = y_offset + (int)(3.0*sinf( start/10.0 + x/30.0));
447 x += font_draw_char( id, s[i], x, y);
448 if( animation & ANIMATION_BUNGEE) {
449 xw_diff += xw/strlen(s);
450 if( xw_diff > 1.0 || xw_diff < -1.0) {
451 x += (int)(xw_diff);
452 if( xw_diff > 1.0) {
453 xw_diff -= 1.0;
454 } else {
455 xw_diff += 1.0;
460 if (animation & ANIMATION_WAVE) {
461 additional_y += 4;
463 if (animation & ANIMATION_BUNGEE) {
464 additional_x += 26;
466 update_rect(x_offset-additional_x, y_offset-additional_y, x-x_offset+additional_x*2, get_image_height(id)+additional_y*2);
469 int font_get_string_width( image_id id, const char* s) {
470 int w = 0, i;
472 for( i=0; i<strlen(s); i++) {
473 w += font_get_metrics( id, s[i], NULL, NULL);
476 return w;
479 void draw_line_faded( int x1, int y1, int x2, int y2, int r, int g, int b, int r2, int g2, int b2) {
480 float step, dx, dy, x = x1, y = y1;
481 int i;
482 char fade = (r!=r2 || g!=g2 || b!=b2);
484 step = (float)(abs(x2-x1)>abs(y2-y1)?abs(x2-x1):abs(y2-y1));
485 dx = (float)(x2-x1) / step;
486 dy = (float)(y2-y1) / step;
488 SDL_LockSurface( screen);
489 for( i=0; i<step; i++) {
490 x += dx;
491 y += dy;
492 if( x < 0.0 || x >= WIDTH || y < 0.0 || y >= HEIGHT) {
493 continue;
495 if( fade) {
496 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);
497 } else {
498 SET_PIXEL_RGB( screen, (int)x, (int)y, r, g, b);
501 SDL_UnlockSurface( screen);