26e44dfec675352282f00538104abb318aa69141
[tennix.git] / graphics.cc
blob26e44dfec675352282f00538104abb318aa69141
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>
31 #include <assert.h>
33 #include "tennix.h"
34 #include "graphics.h"
35 #include "archive.hh"
36 #include "sound.h"
38 #include "SDL_rotozoom.h"
40 static Image* images;
42 static Font* fonts;
43 FreeList* font_freelist = NULL;
45 static FontDescription font_desc[] = {
46 /**
47 * This is the list of fonts to be loaded. Be sure to
48 * use NULL as filename when using the same .ttf file
49 * the second time in a row - this saves memory in the
50 * loader function.
51 **/
52 { "dustismo.ttf", 15, TTF_STYLE_NORMAL },
53 { NULL, 20, TTF_STYLE_NORMAL },
54 { "itwasntme.ttf", 22, TTF_STYLE_NORMAL },
55 { NULL, 28, TTF_STYLE_NORMAL }
58 static SDL_Surface *buffer;
59 static SDL_Surface *background;
61 static SDL_Rect *rect_update_cache;
62 static SDL_Rect *rect_update_old;
63 static int rect_update_cache_current;
64 static int rect_update_old_count;
66 Uint32 fading_start = 0;
68 static const char* graphics[] = {
69 "court.png",
70 "shadow.png",
71 "player-racket.png",
72 "ball.png",
73 "referee.png",
74 "ctt_hard.png",
75 "ctt_clay.png",
76 "ctt_grass.png",
77 "sidebar.png",
78 "tennixlogo.png",
79 "btnplay.png",
80 "btnresume.png",
81 "btnquit.png",
82 "stadium.png",
83 "fog.png",
84 "fog2.png",
85 "night.png",
86 "talk.png",
87 "cursor.png",
88 "worldmap.png",
89 "input_gamepad.png",
90 "input_keyboard_arrows.png",
91 "input_keyboard_ol.png",
92 "input_keyboard_ws.png",
93 "input_maemo_dpad.png",
94 "input_mouse.png",
95 "input_touchscreen.png",
96 "input_ai.png",
97 "back.png",
98 "forward.png",
99 "loc_margaret_court_arena.png",
100 "loc_stade_roland_garros.png",
101 "loc_court_no_1.png",
102 "loc_arthur_ashe_stadium.png",
103 #ifdef NONFREE_LOCATIONS
104 "loc_training_camp.png",
105 "loc_austrian_open.png",
106 "loc_olympic_green_tennis.png"
107 #endif
110 void init_graphics() {
111 int i;
112 char *d;
113 SDL_Surface* data;
114 SDL_Surface* tmp;
115 TennixArchive* tnxar;
116 struct SDL_RWops* rw;
117 const char* font_data = NULL;
119 if (TTF_Init() == -1) {
120 fprintf(stderr, "Cannot init TTF: %s\n", TTF_GetError());
121 exit(EXIT_FAILURE);
124 rect_update_cache = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
125 rect_update_old = (SDL_Rect*)calloc(RECT_UPDATE_CACHE, sizeof(SDL_Rect));
126 rect_update_cache_current = 0;
127 rect_update_old_count = 0;
129 images = (Image*)calloc( GR_COUNT, sizeof( Image));
130 fonts = (Font*)calloc(FONT_COUNT, sizeof(Font));
132 buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
133 screen->format->BitsPerPixel,
134 screen->format->Rmask,
135 screen->format->Gmask,
136 screen->format->Bmask,
137 screen->format->Amask);
139 background = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT,
140 screen->format->BitsPerPixel,
141 screen->format->Rmask,
142 screen->format->Gmask,
143 screen->format->Bmask,
144 screen->format->Amask);
146 if( buffer == NULL) {
147 fprintf( stderr, "Cannot create buffer surface: %s\n", SDL_GetError());
150 tnxar = new TennixArchive(ARCHIVE_FILE);
151 #if 0
152 if (tnxar == NULL) {
153 /* not found in cwd - try installed... */
154 tnxar = tnxar_open(ARCHIVE_FILE_INSTALLED);
155 assert(tnxar != NULL);
157 #endif
159 font_freelist = freelist_create();
161 /* Load fonts from resource file */
162 for (i=0; i<FONT_COUNT; i++) {
163 if (font_desc[i].filename != NULL) {
164 assert(tnxar->setItemFilename(font_desc[i].filename) != 0);
165 font_data = tnxar->getItemBytes();
166 freelist_append(font_freelist, font_data);
168 assert(font_data != NULL);
169 rw = SDL_RWFromMem((void*)font_data, tnxar->getItemSize());
170 fonts[i].data = TTF_OpenFontRW(rw, 1, font_desc[i].size);
171 if (fonts[i].data == NULL) {
172 fprintf(stderr, "TTF error: %s\n", TTF_GetError());
173 assert(fonts[i].data != NULL);
175 TTF_SetFontStyle(fonts[i].data, font_desc[i].style);
178 draw_button(40, (HEIGHT-40)/2, WIDTH-80, 40, 100, 100, 100, 1);
179 store_screen();
180 updatescr();
182 for( i=0; i<GR_COUNT; i++) {
183 if (tnxar->setItemFilename(graphics[i]) != 0 ) {
184 d = tnxar->getItemBytes();
185 tmp = IMG_Load_RW(SDL_RWFromMem(d, tnxar->getItemSize()), 1);
186 free(d);
187 } else {
188 fprintf(stderr, "Cannot find file: %s\n", graphics[i]);
189 exit(EXIT_FAILURE);
191 if( !tmp) {
192 fprintf( stderr, "Error: %s\n", SDL_GetError());
193 continue;
196 /* Convert to RGBA in the display format */
197 data = SDL_DisplayFormatAlpha(tmp);
198 SDL_FreeSurface(tmp);
200 if( !data) {
201 fprintf( stderr, "Error: %s\n", SDL_GetError());
202 continue;
204 images[i].data = data;
206 draw_button(40, (HEIGHT-40)/2, (WIDTH-80)*i/(GR_COUNT+SOUND_MAX), 40, 100, 250, 100, 0);
207 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);
208 updatescr();
211 delete tnxar;
214 void uninit_graphics() {
215 int i;
217 for( i=0; i<GR_COUNT; i++) {
218 SDL_FreeSurface( images[i].data);
221 if( buffer != NULL) {
222 SDL_FreeSurface( buffer);
225 if (background != NULL) {
226 SDL_FreeSurface(background);
229 free(rect_update_cache);
230 free(rect_update_old);
231 free(images);
233 for (i=0; i<FONT_COUNT; i++) {
234 TTF_CloseFont(fonts[i].data);
235 fonts[i].data = NULL;
237 free(fonts);
239 freelist_free_all(font_freelist);
241 TTF_Quit();
245 FreeList* freelist_create()
247 FreeList* list = (FreeList*)malloc(sizeof(FreeList));
248 assert(list != NULL);
250 list->head = NULL;
252 return list;
255 void freelist_append(FreeList* list, const char* data)
257 FreeListItem* new_item = (FreeListItem*)malloc(sizeof(FreeListItem));
259 assert(list != NULL);
260 assert(data != NULL);
262 new_item->data = data;
264 /* Insert item at the beginning of list */
265 new_item->next = list->head;
266 list->head = new_item;
269 void freelist_free_all(FreeList* list)
271 FreeListItem* next;
273 assert(list != NULL);
275 while (list->head != NULL) {
276 /* Remove one item from the head of the list */
277 next = list->head->next;
278 free((void*)list->head->data);
279 free(list->head);
280 list->head = next;
283 free(list);
287 int get_image_width( image_id id) {
288 return images[id].data->w;
291 int get_image_height( image_id id) {
292 return images[id].data->h;
295 int get_sprite_width( image_id id, int items) {
296 return images[id].data->w / items;
299 void show_sprite( image_id id, int pos, int items, int x_offset,
300 int y_offset, int opacity)
302 assert(id < GR_COUNT);
303 SDL_SetAlpha(images[id].data, SDL_SRCALPHA | SDL_RLEACCEL, opacity);
304 blit_surface(images[id].data, x_offset, y_offset, pos, items);
307 void show_image_rotozoom(image_id id, int x, int y, float rotate, float zoom)
309 SDL_Surface* tmp;
310 SDL_Rect src, dst;
312 assert(id < GR_COUNT);
314 tmp = rotozoomSurface(images[id].data, rotate, zoom, SMOOTHING_OFF);
316 dst.w = src.w = tmp->w;
317 dst.h = src.h = tmp->h;
318 src.x = 0;
319 src.y = 0;
320 dst.x = x - src.w/2;
321 dst.y = y - src.h/2;
323 SDL_BlitSurface(tmp, &src, screen, &dst);
324 update_rect2(dst);
325 SDL_FreeSurface(tmp);
328 void fill_image_offset( image_id id, int x, int y, int w, int h, int offset_x, int offset_y) {
329 SDL_Surface *bitmap;
330 SDL_Rect src, dst;
331 int dx = 0, dy = 0, cx, cy;
333 if( id >= GR_COUNT) return;
335 bitmap = images[id].data;
337 /* Make negative offsets positive */
338 if (offset_x < 0) {
339 offset_x = (offset_x%bitmap->w)+bitmap->w;
341 if (offset_y < 0) {
342 offset_y = (offset_y%bitmap->h)+bitmap->h;
345 src.y = offset_y % bitmap->h;
346 while( dy < h) {
347 src.h = dst.h = cy = (h-dy > bitmap->h-src.y)?(bitmap->h-src.y):(h-dy);
348 dst.y = y+dy;
350 dx = 0;
351 src.x = offset_x % bitmap->w;
352 while( dx < w) {
353 src.w = dst.w = cx = (w-dx > bitmap->w-src.x)?(bitmap->w-src.x):(w-dx);
354 dst.x = x+dx;
356 SDL_BlitSurface( bitmap, &src, screen, &dst);
357 update_rect2(dst);
359 dx += cx;
360 src.x = 0;
363 dy += cy;
364 src.y = 0;
368 void line_horiz( int y, Uint8 r, Uint8 g, Uint8 b) {
369 rectangle(0, y, screen->w, 1, r, g, b);
372 void line_vert( int x, Uint8 r, Uint8 g, Uint8 b) {
373 rectangle(x, 0, 1, screen->h, r, g, b);
376 void rectangle( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b) {
377 Uint32 color;
378 SDL_Rect rect;
380 if (w <= 0 || h <= 0 || x < 0 || y < 0 || x+w > WIDTH || y+h > HEIGHT) {
381 return;
384 color = SDL_MapRGB(screen->format, r, g, b);
386 rect.x = x;
387 rect.y = y;
388 rect.w = w;
389 rect.h = h;
391 SDL_FillRect( screen, &rect, color);
392 update_rect(x, y, w, h);
395 void rectangle_alpha(int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, Uint8 opacity)
397 Uint32 color;
398 SDL_Surface *buf;
399 SDL_Rect rect, rect2;
401 if (w <= 0 || h <= 0 || x < 0 || y < 0 || x+w > WIDTH || y+h > HEIGHT) {
402 return;
405 color = SDL_MapRGB(screen->format, r, g, b);
407 buf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
408 screen->format->BitsPerPixel,
409 screen->format->Rmask,
410 screen->format->Gmask,
411 screen->format->Bmask,
412 screen->format->Amask);
414 rect.x = rect.y = 0;
415 rect2.x = x;
416 rect2.y = y;
417 rect.w = rect2.w = w;
418 rect.h = rect2.h = h;
420 SDL_FillRect(buf, &rect, color);
421 SDL_SetAlpha(buf, SDL_RLEACCEL | SDL_SRCALPHA, opacity);
422 SDL_BlitSurface(buf, &rect, screen, &rect2);
423 update_rect(x, y, w, h);
425 SDL_FreeSurface(buf);
428 void draw_button( int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
429 float diff = (pressed?1.0-BUTTON_HIGHLIGHT:1.0+BUTTON_HIGHLIGHT);
430 rectangle(x, y, w, h, MIN(r*diff, 255), MIN(g*diff, 255), MIN(b*diff, 255));
431 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));
432 rectangle(x+BUTTON_BORDER, y+BUTTON_BORDER, w-2*BUTTON_BORDER, h-2*BUTTON_BORDER, r, g, b);
435 void draw_button_text(const char* s, int x, int y, int w, int h, Uint8 r, Uint8 g, Uint8 b, char pressed) {
436 int font_x, font_y;
437 float factor = 0.3;
438 pressed = pressed?1:0;
439 draw_button( x, y, w, h, r, g, b, pressed);
440 font_x = x + w/2 - font_get_string_width(FONT_SMALL, s)/2 + pressed*BUTTON_BORDER;
441 font_y = y + h/2 - font_get_height(FONT_SMALL)/2 + pressed*BUTTON_BORDER;
442 font_draw_string_color(FONT_SMALL, s, font_x, font_y, (Uint8)(r*factor), (Uint8)(g*factor), (Uint8)(b*factor));
445 void draw_button_object(MenuButton* b, int mx, int my)
447 if (b->image_id == GR_COUNT) {
448 draw_button_text(b->text, b->x, b->y, b->w, b->h, b->r, b->g, b->b, M_POS_BUTTON(*b, mx, my));
449 } else {
450 show_image_rotozoom(b->image_id, b->x+b->w/2, b->y+b->h/2, 0.0, 1.0+0.2*M_POS_BUTTON(*b, mx, my));
454 void clear_screen()
456 SDL_Rect rect;
458 rect.x = rect.y = 0;
459 rect.w = WIDTH;
460 rect.h = HEIGHT;
462 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0));
465 void store_screen()
467 SDL_BlitSurface(screen, NULL, background, NULL);
468 rect_update_old[0].x = rect_update_old[0].y = 0;
469 rect_update_old[0].w = WIDTH;
470 rect_update_old[0].h = HEIGHT;
471 rect_update_old_count = 1;
472 rect_update_cache_current = 0;
475 void reset_screen()
477 int i;
478 SDL_Rect* tmp;
479 SDL_BlitSurface(background, NULL, screen, NULL);
481 for (i=0; i<rect_update_cache_current; i++) {
482 SDL_BlitSurface(background, &rect_update_cache[i], screen, &rect_update_cache[i]);
485 /* Save rects I've just updated for redraw later */
486 tmp = rect_update_cache;
487 rect_update_cache = rect_update_old;
488 rect_update_old = tmp;
489 rect_update_old_count = rect_update_cache_current;
490 rect_update_cache_current = 0;
493 void update_rect(Sint32 x, Sint32 y, Sint32 w, Sint32 h)
495 SDL_Rect* u = NULL;
496 static int inside = 0;
497 if (inside) return;
498 inside = 1;
500 #ifdef DRAW_UPDATE_RECTANGLE
501 rectangle(x, y, w, h, 50+rand()%200, 50+rand()%200 ,50+rand()%200);
502 #endif
504 if ((x >= WIDTH) | (y >= HEIGHT) | (x+w <= 0) | (y+h <= 0) | (!w) | (!h)) {
505 inside = 0;
506 return;
509 if (rect_update_cache_current == RECT_UPDATE_CACHE) {
510 fprintf(stderr, "Overflow\n");
511 rect_update_cache_current = 0;
514 u = &(rect_update_cache[rect_update_cache_current]);
516 if (x < 0/* && x+w > 0*/) {
517 w += x;
518 x = 0;
520 if (y < 0/* && y+h > 0*/) {
521 h += y;
522 y = 0;
525 if (x+w >= WIDTH) {
526 w -= (x+w-WIDTH+1);
529 if (y+h >= HEIGHT) {
530 h -= (y+h-HEIGHT+1);
533 if (w==0 || h==0) {
534 inside = 0;
535 return;
538 u->x = x;
539 u->y = y;
540 u->w = w;
541 u->h = h;
543 rect_update_cache_current++;
545 inside = 0;
548 void updatescr()
550 Uint32 ticks = SDL_GetTicks();
551 SDL_Rect *r_current = NULL, *end_current = NULL;
552 SDL_Rect *r_old = NULL;
554 static int fading_last_time = 0;
555 unsigned char fading_now = ticks < fading_start+FADE_DURATION;
557 if (fading_now) {
558 SDL_SetAlpha(buffer, SDL_SRCALPHA | SDL_RLEACCEL, 255-255*(ticks-fading_start)/FADE_DURATION);
559 SDL_BlitSurface(buffer, NULL, screen, NULL);
560 SDL_UpdateRect(screen, 0, 0, 0, 0);
561 } else if (fading_last_time && !fading_now) {
562 SDL_UpdateRect(screen, 0, 0, 0, 0);
563 } else {
564 if (rect_update_old_count == rect_update_cache_current) {
565 /* Merge rects into one single rect list */
566 r_old = rect_update_old;
567 r_current = rect_update_cache;
568 end_current = rect_update_cache + rect_update_cache_current;
569 while (r_current != end_current) {
570 r_old->w = MAX(r_current->x+r_current->w, r_old->x+r_old->w);
571 r_old->h = MAX(r_current->y+r_current->h, r_old->y+r_old->h);
572 r_old->x = MIN(r_current->x, r_old->x);
573 r_old->y = MIN(r_current->y, r_old->y);
574 r_old->w -= r_old->x;
575 r_old->h -= r_old->y;
577 r_current++;
578 r_old++;
580 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
581 } else {
582 SDL_UpdateRects(screen, rect_update_old_count, rect_update_old);
583 SDL_UpdateRects(screen, rect_update_cache_current, rect_update_cache);
587 reset_screen();
588 fading_last_time = fading_now;
591 void start_fade() {
592 SDL_BlitSurface( screen, NULL, buffer, NULL);
593 fading_start = SDL_GetTicks();
596 SDL_Surface* font_render_surface(font_id id, const char* text, Uint8 r,
597 Uint8 g, Uint8 b)
599 SDL_Surface *result;
600 SDL_Color color;
602 color.r = r;
603 color.g = g;
604 color.b = b;
606 assert(id < FONT_COUNT);
608 result = TTF_RenderText_Blended(fonts[id].data, text, color);
610 assert(result != NULL);
612 return result;
615 void blit_surface(SDL_Surface* surface, int x, int y, int pos, int count)
617 SDL_Rect src, dst;
619 dst.w = src.w = surface->w/count;
620 dst.h = src.h = surface->h;
621 src.x = src.w*pos;
622 src.y = 0;
623 dst.x = x;
624 dst.y = y;
626 SDL_BlitSurface(surface, &src, screen, &dst);
627 update_rect2(dst);
630 SDL_Surface* get_surface(image_id id)
632 assert(id < GR_COUNT);
633 return images[id].data;
636 void font_draw_string_color(font_id id, const char* s, int x_offset, int y_offset, Uint8 r, Uint8 g, Uint8 b) {
637 SDL_Surface *text;
639 text = font_render_surface(id, s, r, g, b);
640 blit_surface_simple(text, x_offset, y_offset);
641 SDL_FreeSurface(text);
644 int font_get_string_width(font_id id, const char* s) {
645 int w, h;
647 assert(id < FONT_COUNT);
649 if (TTF_SizeText(fonts[id].data, s, &w, &h) != 0) {
650 return 0;
653 return w;
656 int font_get_height(font_id id) {
657 assert(id < FONT_COUNT);
658 return TTF_FontHeight(fonts[id].data);
661 void draw_line_faded( int x1, int y1, int x2, int y2, int r, int g, int b, int r2, int g2, int b2) {
662 float step, dx, dy, x = x1, y = y1;
663 int i;
664 char fade = (r!=r2 || g!=g2 || b!=b2);
666 step = (float)(abs(x2-x1)>abs(y2-y1)?abs(x2-x1):abs(y2-y1));
667 dx = (float)(x2-x1) / step;
668 dy = (float)(y2-y1) / step;
670 SDL_LockSurface( screen);
671 for( i=0; i<step; i++) {
672 x += dx;
673 y += dy;
674 if( x < 0.0 || x >= WIDTH || y < 0.0 || y >= HEIGHT) {
675 continue;
677 if( fade) {
678 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);
679 } else {
680 SET_PIXEL_RGB( screen, (int)x, (int)y, r, g, b);
683 SDL_UnlockSurface( screen);