20100212
[gdash.git] / src / sdlgfx.c
blob08834b31f8592bad95ac031e0131c8fd5005689a
1 /*
2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include <SDL.h>
17 #include <SDL_image.h>
18 #include <glib.h>
19 #include "colors.h"
20 #include "cave.h"
21 #include "gameplay.h"
22 #include "settings.h"
23 #include "sdlgfx.h"
24 #include "util.h"
25 #include "c64_gfx.h" /* char c64_gfx[] with (almost) original graphics */
26 #include "title.h"
27 #include "c64_font.h"
28 #include "gfxutil.h"
29 #include "caveset.h" /* UGLY */
31 #include "c64_png_colors.h"
33 #define NUM_OF_CHARS 128
35 int gd_scale=1; /* a graphics scale things which sets EVERYTHING. it is set with gd_sdl_init, and cannot be modified later. */
36 int gd_scale_type=GD_SCALING_ORIGINAL;
38 static SDL_Surface *cells[2*NUM_OF_CELLS]; /* CANNOT BE NUM_OF_CELLS_X*NUM_OF_CELLS_Y, as the cave rendering routine in cave.c uses this. */
39 static const guchar *font;
40 static GHashTable *font_w, *font_n;
41 static GList *font_color_recently_used;
42 static GdColor color0, color1, color2, color3, color4, color5; /* currently used cell colors */
43 static guint8 *c64_custom_gfx=NULL;
44 static gboolean using_png_gfx;
46 /* these masks are always for RGB and RGBA ordering in memory.
47 that is what gdk-pixbuf uses, and also what sdl uses.
48 no BGR, no ABGR.
50 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
51 static guint32 rmask = 0xff000000;
52 static guint32 gmask = 0x00ff0000;
53 static guint32 bmask = 0x0000ff00;
54 static guint32 amask = 0x000000ff;
55 #else
56 static guint32 rmask = 0x000000ff;
57 static guint32 gmask = 0x0000ff00;
58 static guint32 bmask = 0x00ff0000;
59 static guint32 amask = 0xff000000;
60 #endif
63 /* screen area */
64 SDL_Surface *gd_screen=NULL;
65 static SDL_Surface *dark_background=NULL;
66 static GList *backup_screens=NULL, *dark_screens=NULL;
67 static int play_area_w=320;
68 static int play_area_h=180;
69 int gd_statusbar_height=20;
70 int gd_statusbar_y1=1;
71 int gd_statusbar_y2=10;
72 int gd_statusbar_mid=(20-8)/2; /* (height-fontheight)/2 */
73 static int scroll_x, scroll_y;
76 /* quit, global variable which is set to true if the application should quit */
77 gboolean gd_quit=FALSE;
79 guint8 *gd_keystate;
80 static SDL_Joystick *joystick_1;
82 static int cell_size=16;
90 /* return name of key. names taken from sdl documentation */
92 w3m -dump file:///usr/share/doc/libsdl1.2-dev/docs/html/sdlkey.html |grep -v "──" |
93 cut -c 1-19,38- |sed "s/│$//;s/ *$//g;s/│/case /;s/ *│/: return \"/;s/$/\";/"|tee ~/keys.txt
95 const char *
96 gd_key_name(guint keysym)
98 static char keyname[30];
99 switch(keysym) {
100 case SDLK_BACKSPACE: return "BACKSPACE";
101 case SDLK_TAB: return "TAB";
102 case SDLK_CLEAR: return "CLEAR";
103 case SDLK_RETURN: return "RETURN";
104 case SDLK_PAUSE: return "PAUSE";
105 case SDLK_ESCAPE: return "ESCAPE";
106 case SDLK_SPACE: return "SPACE";
107 case SDLK_EXCLAIM: return "EXCLAIM";
108 case SDLK_QUOTEDBL: return "QUOTEDBL";
109 case SDLK_HASH: return "HASH";
110 case SDLK_DOLLAR: return "DOLLAR";
111 case SDLK_AMPERSAND: return "AMPERSAND";
112 case SDLK_QUOTE: return "QUOTE";
113 case SDLK_LEFTPAREN: return "LEFT PARENTHESIS";
114 case SDLK_RIGHTPAREN: return "RIGHT PARENTHESIS";
115 case SDLK_ASTERISK: return "ASTERISK";
116 case SDLK_PLUS: return "PLUS SIGN";
117 case SDLK_COMMA: return "COMMA";
118 case SDLK_MINUS: return "MINUS SIGN";
119 case SDLK_PERIOD: return "PERIOD";
120 case SDLK_SLASH: return "FORWARD SLASH";
121 case SDLK_0: return "0";
122 case SDLK_1: return "1";
123 case SDLK_2: return "2";
124 case SDLK_3: return "3";
125 case SDLK_4: return "4";
126 case SDLK_5: return "5";
127 case SDLK_6: return "6";
128 case SDLK_7: return "7";
129 case SDLK_8: return "8";
130 case SDLK_9: return "9";
131 case SDLK_COLON: return "COLON";
132 case SDLK_SEMICOLON: return "SEMICOLON";
133 case SDLK_LESS: return "LESS-THAN SIGN";
134 case SDLK_EQUALS: return "EQUALS SIGN";
135 case SDLK_GREATER: return "GREATER-THAN SIGN";
136 case SDLK_QUESTION: return "QUESTION MARK";
137 case SDLK_AT: return "AT";
138 case SDLK_LEFTBRACKET: return "LEFT BRACKET";
139 case SDLK_BACKSLASH: return "BACKSLASH";
140 case SDLK_RIGHTBRACKET: return "RIGHT BRACKET";
141 case SDLK_CARET: return "CARET";
142 case SDLK_UNDERSCORE: return "UNDERSCORE";
143 case SDLK_BACKQUOTE: return "GRAVE";
144 case SDLK_a: return "A";
145 case SDLK_b: return "B";
146 case SDLK_c: return "C";
147 case SDLK_d: return "D";
148 case SDLK_e: return "E";
149 case SDLK_f: return "F";
150 case SDLK_g: return "G";
151 case SDLK_h: return "H";
152 case SDLK_i: return "I";
153 case SDLK_j: return "J";
154 case SDLK_k: return "K";
155 case SDLK_l: return "L";
156 case SDLK_m: return "M";
157 case SDLK_n: return "N";
158 case SDLK_o: return "O";
159 case SDLK_p: return "P";
160 case SDLK_q: return "Q";
161 case SDLK_r: return "R";
162 case SDLK_s: return "S";
163 case SDLK_t: return "T";
164 case SDLK_u: return "U";
165 case SDLK_v: return "V";
166 case SDLK_w: return "W";
167 case SDLK_x: return "X";
168 case SDLK_y: return "Y";
169 case SDLK_z: return "Z";
170 case SDLK_DELETE: return "DELETE";
171 case SDLK_KP0: return "KEYPAD 0";
172 case SDLK_KP1: return "KEYPAD 1";
173 case SDLK_KP2: return "KEYPAD 2";
174 case SDLK_KP3: return "KEYPAD 3";
175 case SDLK_KP4: return "KEYPAD 4";
176 case SDLK_KP5: return "KEYPAD 5";
177 case SDLK_KP6: return "KEYPAD 6";
178 case SDLK_KP7: return "KEYPAD 7";
179 case SDLK_KP8: return "KEYPAD 8";
180 case SDLK_KP9: return "KEYPAD 9";
181 case SDLK_KP_PERIOD: return "KEYPAD PERIOD";
182 case SDLK_KP_DIVIDE: return "KEYPAD DIVIDE";
183 case SDLK_KP_MULTIPLY: return "KEYPAD MULTIPLY";
184 case SDLK_KP_MINUS: return "KEYPAD MINUS";
185 case SDLK_KP_PLUS: return "KEYPAD PLUS";
186 case SDLK_KP_ENTER: return "KEYPAD ENTER";
187 case SDLK_KP_EQUALS: return "KEYPAD EQUALS";
188 case SDLK_UP: return "UP ARROW";
189 case SDLK_DOWN: return "DOWN ARROW";
190 case SDLK_RIGHT: return "RIGHT ARROW";
191 case SDLK_LEFT: return "LEFT ARROW";
192 case SDLK_INSERT: return "INSERT";
193 case SDLK_HOME: return "HOME";
194 case SDLK_END: return "END";
195 case SDLK_PAGEUP: return "PAGE UP";
196 case SDLK_PAGEDOWN: return "PAGE DOWN";
197 case SDLK_F1: return "F1";
198 case SDLK_F2: return "F2";
199 case SDLK_F3: return "F3";
200 case SDLK_F4: return "F4";
201 case SDLK_F5: return "F5";
202 case SDLK_F6: return "F6";
203 case SDLK_F7: return "F7";
204 case SDLK_F8: return "F8";
205 case SDLK_F9: return "F9";
206 case SDLK_F10: return "F10";
207 case SDLK_F11: return "F11";
208 case SDLK_F12: return "F12";
209 case SDLK_F13: return "F13";
210 case SDLK_F14: return "F14";
211 case SDLK_F15: return "F15";
212 case SDLK_NUMLOCK: return "NUMLOCK";
213 case SDLK_CAPSLOCK: return "CAPSLOCK";
214 case SDLK_SCROLLOCK: return "SCROLLOCK";
215 case SDLK_RSHIFT: return "RIGHT SHIFT";
216 case SDLK_LSHIFT: return "LEFT SHIFT";
217 case SDLK_RCTRL: return "RIGHT CTRL";
218 case SDLK_LCTRL: return "LEFT CTRL";
219 case SDLK_RALT: return "RIGHT ALT";
220 case SDLK_LALT: return "LEFT ALT";
221 case SDLK_RMETA: return "RIGHT META";
222 case SDLK_LMETA: return "LEFT META";
223 case SDLK_LSUPER: return "LEFT WINDOWS KEY";
224 case SDLK_RSUPER: return "RIGHT WINDOWS KEY";
225 case SDLK_MODE: return "MODE SHIFT";
226 case SDLK_HELP: return "HELP";
227 case SDLK_PRINT: return "PRINT-SCREEN";
228 case SDLK_SYSREQ: return "SYSRQ";
229 case SDLK_BREAK: return "BREAK";
230 case SDLK_MENU: return "MENU";
231 case SDLK_POWER: return "POWER";
232 case SDLK_EURO: return "EURO";
233 default:
234 sprintf(keyname, "KEY %04X", keysym);
235 return g_intern_string(keyname); /* abuse? :) */
241 #if 0
242 /* read a gdk-pixbuf source, and return an sdl surface. */
243 /* these masks are always for RGB and RGBA ordering in memory.
244 that is what gdk-pixbuf uses, and also what sdl uses.
245 no BGR, no ABGR.
247 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
248 static guint32 rmask = 0xff000000;
249 static guint32 gmask = 0x00ff0000;
250 static guint32 bmask = 0x0000ff00;
251 static guint32 amask = 0x000000ff;
253 /* for non-alpha channel gdk pixbuf includes */
254 static guint32 rmask_24 = 0xff0000;
255 static guint32 gmask_24 = 0x00ff00;
256 static guint32 bmask_24 = 0x0000ff;
257 #else
258 static guint32 rmask = 0x000000ff;
259 static guint32 gmask = 0x0000ff00;
260 static guint32 bmask = 0x00ff0000;
261 static guint32 amask = 0xff000000;
263 /* for non-alpha channel gdk pixbuf includes */
264 static guint32 rmask_24 = 0x0000ff;
265 static guint32 gmask_24 = 0x00ff00;
266 static guint32 bmask_24 = 0xff0000;
267 #endif
268 static SDL_Surface *
269 surface_from_gdk_pixbuf_data(guint32 *data)
271 SDL_Surface *surface;
273 g_assert(GUINT32_FROM_BE(data[0])==0x47646b50); /* gdk-pixbuf magic number */
274 if (GUINT32_FROM_BE(data[2])==0x1010002) /* 32-bit rgba */
275 surface=SDL_CreateRGBSurfaceFrom(data+6, GUINT32_FROM_BE(data[4]), GUINT32_FROM_BE(data[5]), 32, GUINT32_FROM_BE(data[3]), rmask, gmask, bmask, amask);
276 else
277 if (GUINT32_FROM_BE(data[2])==0x1010001) /* 24-bit rgb */
278 surface=SDL_CreateRGBSurfaceFrom(data+6, GUINT32_FROM_BE(data[4]), GUINT32_FROM_BE(data[5]), 24, GUINT32_FROM_BE(data[3]), rmask_24, gmask_24, bmask_24, 0);
279 else
280 /* unknown pixel format */
281 g_assert_not_reached();
282 g_assert(surface!=NULL);
284 return surface;
286 #endif
288 static SDL_Surface *
289 surface_from_raw_data(const guint8 *data, int length)
291 SDL_RWops *rwop;
292 SDL_Surface *surface;
294 rwop=SDL_RWFromConstMem(data, length);
295 surface=IMG_Load_RW(rwop, 1); /* 1 = automatically closes rwop */
296 return surface;
299 static SDL_Surface *
300 surface_from_base64(const char *base64)
302 guchar *data;
303 gsize length;
304 SDL_Surface *surface;
306 data=g_base64_decode(base64, &length);
307 surface=surface_from_raw_data(data, length);
308 g_free(data);
310 return surface;
313 /* This is taken from the SDL_gfx project. */
315 SDL_rotozoom.c - rotozoomer for 32bit or 8bit surfaces
316 LGPL (c) A. Schiffler
317 32bit Zoomer with optional anti-aliasing by bilinear interpolation.
318 Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
321 static void
322 zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst, gboolean smooth)
324 typedef struct tColorRGBA {
325 guint8 r;
326 guint8 g;
327 guint8 b;
328 guint8 a;
329 } tColorRGBA;
331 int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep;
332 tColorRGBA *c00, *c01, *c10, *c11;
333 tColorRGBA *sp, *csp, *dp;
334 int dgap;
336 g_assert(src->format->BytesPerPixel==4);
337 g_assert(dst->format->BytesPerPixel==4);
339 /* Variable setup */
340 if (smooth) {
341 /* For interpolation: assume source dimension is one pixel */
342 /* smaller to avoid overflow on right and bottom edge. */
343 sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
344 sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
345 } else {
346 sx = (int) (65536.0 * (float) src->w / (float) dst->w);
347 sy = (int) (65536.0 * (float) src->h / (float) dst->h);
350 /* Allocate memory for row increments */
351 sax=g_new(gint32, dst->w+1);
352 say=g_new(gint32, dst->h+1);
354 /* Precalculate row increments */
355 if (SDL_MUSTLOCK(src))
356 SDL_LockSurface(src);
357 if (SDL_MUSTLOCK(dst))
358 SDL_LockSurface(dst);
359 sp = csp = (tColorRGBA *) src->pixels;
360 dp = (tColorRGBA *) dst->pixels;
362 csx = 0;
363 csax = sax;
364 for (x = 0; x <= dst->w; x++) {
365 *csax = csx;
366 csax++;
367 csx &= 0xffff;
368 csx += sx;
370 csy = 0;
371 csay = say;
372 for (y = 0; y <= dst->h; y++) {
373 *csay = csy;
374 csay++;
375 csy &= 0xffff;
376 csy += sy;
379 dgap = dst->pitch - dst->w * 4;
382 * Switch between interpolating and non-interpolating code
384 if (smooth) {
385 /* Interpolating Zoom */
387 /* Scan destination */
388 csay = say;
389 for (y = 0; y < dst->h; y++) {
390 /* Setup color source pointers */
391 c00 = csp;
392 c01 = csp;
393 c01++;
394 c10 = (tColorRGBA *) ((guint8 *) csp + src->pitch);
395 c11 = c10;
396 c11++;
397 csax = sax;
398 for (x = 0; x < dst->w; x++) {
399 /* Interpolate colors */
400 ex = (*csax & 0xffff);
401 ey = (*csay & 0xffff);
402 t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
403 t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
404 dp->r = (((t2 - t1) * ey) >> 16) + t1;
405 t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
406 t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
407 dp->g = (((t2 - t1) * ey) >> 16) + t1;
408 t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
409 t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
410 dp->b = (((t2 - t1) * ey) >> 16) + t1;
411 t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
412 t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
413 dp->a = (((t2 - t1) * ey) >> 16) + t1;
415 /* Advance source pointers */
416 csax++;
417 sstep = (*csax >> 16);
418 c00 += sstep;
419 c01 += sstep;
420 c10 += sstep;
421 c11 += sstep;
422 /* Advance destination pointer */
423 dp++;
425 /* Advance source pointer */
426 csay++;
427 csp = (tColorRGBA *) ((guint8 *) csp + (*csay >> 16) * src->pitch);
428 /* Advance destination pointers */
429 dp = (tColorRGBA *) ((guint8 *) dp + dgap);
431 } else {
432 /* Non-Interpolating Zoom */
433 csay = say;
434 for (y = 0; y < dst->h; y++) {
435 sp = csp;
436 csax = sax;
437 for (x = 0; x < dst->w; x++) {
438 /* Draw */
439 *dp = *sp;
440 /* Advance source pointers */
441 csax++;
442 sstep = (*csax >> 16);
443 sp += sstep;
444 /* Advance destination pointer */
445 dp++;
447 /* Advance source pointer */
448 csay++;
449 sstep = (*csay >> 16) * src->pitch;
450 csp = (tColorRGBA *) ((guint8 *) csp + sstep);
452 /* Advance destination pointers */
453 dp = (tColorRGBA *) ((guint8 *) dp + dgap);
458 if (SDL_MUSTLOCK(src))
459 SDL_UnlockSurface(src);
460 if (SDL_MUSTLOCK(dst))
461 SDL_UnlockSurface(dst);
463 /* Remove temp arrays */
464 g_free(sax);
465 g_free(say);
469 /* wrapper */
470 static void
471 scale2x(SDL_Surface *src, SDL_Surface *dst)
473 g_assert(dst->w == src->w*2);
474 g_assert(dst->h == src->h*2);
475 g_assert(src->format->BytesPerPixel==4);
476 g_assert(dst->format->BytesPerPixel==4);
478 if (SDL_MUSTLOCK(src))
479 SDL_LockSurface(src);
480 if (SDL_MUSTLOCK(dst))
481 SDL_LockSurface(dst);
482 gd_scale2x_raw(src->pixels, src->w, src->h, src->pitch, dst->pixels, dst->pitch);
483 if (SDL_MUSTLOCK(src))
484 SDL_UnlockSurface(src);
485 if (SDL_MUSTLOCK(dst))
486 SDL_UnlockSurface(dst);
489 static void
490 scale3x(SDL_Surface *src, SDL_Surface *dst)
492 g_assert(dst->w == src->w*3);
493 g_assert(dst->h == src->h*3);
494 g_assert(src->format->BytesPerPixel==4);
495 g_assert(dst->format->BytesPerPixel==4);
497 if (SDL_MUSTLOCK(src))
498 SDL_LockSurface(src);
499 if (SDL_MUSTLOCK(dst))
500 SDL_LockSurface(dst);
501 gd_scale3x_raw(src->pixels, src->w, src->h, src->pitch, dst->pixels, dst->pitch);
502 if (SDL_MUSTLOCK(src))
503 SDL_UnlockSurface(src);
504 if (SDL_MUSTLOCK(dst))
505 SDL_UnlockSurface(dst);
512 /* nearest neighbor scaling for 2x and 3x. */
513 /* nearest pixel 2x scaling. */
514 static void
515 scale_2x_nearest(SDL_Surface *src, SDL_Surface *dst)
517 guint32 E;
518 int y, x;
519 guint8 *srcpix=src->pixels;
520 guint8 *dstpix=dst->pixels;
521 int width=src->w;
522 int height=src->h;
523 int srcpitch=src->pitch;
524 int dstpitch=dst->pitch;
526 g_assert(dst->w == src->w*2);
527 g_assert(dst->h == src->h*2);
528 g_assert(src->format->BytesPerPixel==4);
529 g_assert(dst->format->BytesPerPixel==4);
531 if (SDL_MUSTLOCK(src))
532 SDL_LockSurface(src);
533 if (SDL_MUSTLOCK(dst))
534 SDL_LockSurface(dst);
535 for (y=0; y<height; ++y) {
536 for (x=0; x<width; ++x) {
537 E = *(guint32*)(srcpix + (y*srcpitch) + (4*x));
539 *(guint32*)(dstpix + y*2*dstpitch + x*2*4) = E;
540 *(guint32*)(dstpix + y*2*dstpitch + (x*2+1)*4) = E;
541 *(guint32*)(dstpix + (y*2+1)*dstpitch + x*2*4) = E;
542 *(guint32*)(dstpix + (y*2+1)*dstpitch + (x*2+1)*4) = E;
545 if (SDL_MUSTLOCK(src))
546 SDL_UnlockSurface(src);
547 if (SDL_MUSTLOCK(dst))
548 SDL_UnlockSurface(dst);
552 /* nearest pixel 3x scaling. the rotozoomer is not correct at the bottom of the image. */
553 static void
554 scale_3x_nearest(SDL_Surface *src, SDL_Surface *dst)
556 guint32 E;
557 int y, x;
558 guint8 *srcpix=src->pixels;
559 guint8 *dstpix=dst->pixels;
560 int width=src->w;
561 int height=src->h;
562 int srcpitch=src->pitch;
563 int dstpitch=dst->pitch;
565 g_assert(dst->w == src->w*3);
566 g_assert(dst->h == src->h*3);
567 g_assert(src->format->BytesPerPixel==4);
568 g_assert(dst->format->BytesPerPixel==4);
570 if (SDL_MUSTLOCK(src))
571 SDL_LockSurface(src);
572 if (SDL_MUSTLOCK(dst))
573 SDL_LockSurface(dst);
574 for (y=0; y<height; ++y) {
575 int ny=y*3; /* new coordinate */
577 for (x=0; x<width; ++ x) {
578 int nx=x*3; /* new coordinate */
580 E = *(guint32*)(srcpix + (y*srcpitch + 4*x));
582 *(guint32*)(dstpix + ny*dstpitch + nx*4) = E;
583 *(guint32*)(dstpix + ny*dstpitch + (nx+1)*4) = E;
584 *(guint32*)(dstpix + ny*dstpitch + (nx+2)*4) = E;
585 *(guint32*)(dstpix + (ny+1)*dstpitch + nx*4) = E;
586 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+1)*4) = E;
587 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+2)*4) = E;
588 *(guint32*)(dstpix + (ny+2)*dstpitch + nx*4) = E;
589 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+1)*4) = E;
590 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+2)*4) = E;
593 if (SDL_MUSTLOCK(src))
594 SDL_UnlockSurface(src);
595 if (SDL_MUSTLOCK(dst))
596 SDL_UnlockSurface(dst);
601 /* scales a pixbuf with the appropriate scaling type. */
602 static SDL_Surface *
603 surface_scale(SDL_Surface *orig)
605 SDL_Surface *dest, *dest2x;
607 /* special case: no scaling. */
608 /* return a new pixbuf, but its pixels are at the same place as the original. (so the pixbuf struct can be freed on its own) */
609 if (gd_scale_type==GD_SCALING_ORIGINAL) {
610 g_assert(gd_scale==1); /* just to be sure */
612 return SDL_CreateRGBSurfaceFrom(orig->pixels, orig->w, orig->h, orig->format->BitsPerPixel, orig->pitch, orig->format->Rmask, orig->format->Gmask, orig->format->Bmask, orig->format->Amask);
615 dest=SDL_CreateRGBSurface(orig->flags, orig->w*gd_scale, orig->h*gd_scale, orig->format->BitsPerPixel, orig->format->Rmask, orig->format->Gmask, orig->format->Bmask, orig->format->Amask);
617 switch (gd_scale_type) {
618 case GD_SCALING_ORIGINAL:
619 /* handled above */
620 g_assert_not_reached();
621 break;
623 case GD_SCALING_2X:
624 scale_2x_nearest(orig, dest);
625 break;
627 case GD_SCALING_2X_BILINEAR:
628 zoomSurfaceRGBA(orig, dest, TRUE);
629 break;
631 case GD_SCALING_2X_SCALE2X:
632 scale2x(orig, dest);
633 break;
635 case GD_SCALING_3X:
636 scale_3x_nearest(orig, dest);
637 break;
639 case GD_SCALING_3X_BILINEAR:
640 zoomSurfaceRGBA(orig, dest, TRUE);
641 break;
643 case GD_SCALING_3X_SCALE3X:
644 scale3x(orig, dest);
645 break;
647 case GD_SCALING_4X:
648 dest2x=SDL_CreateRGBSurface(orig->flags, orig->w*2, orig->h*2, orig->format->BitsPerPixel, orig->format->Rmask, orig->format->Gmask, orig->format->Bmask, orig->format->Amask);
649 scale_2x_nearest(orig, dest2x);
650 scale_2x_nearest(dest2x, dest);
651 SDL_FreeSurface(dest2x);
652 break;
654 case GD_SCALING_4X_BILINEAR:
655 zoomSurfaceRGBA(orig, dest, TRUE);
656 break;
658 case GD_SCALING_4X_SCALE4X:
659 /* scale2x applied twice. */
660 dest2x=SDL_CreateRGBSurface(orig->flags, orig->w*2, orig->h*2, orig->format->BitsPerPixel, orig->format->Rmask, orig->format->Gmask, orig->format->Bmask, orig->format->Amask);
661 scale2x(orig, dest2x);
662 scale2x(dest2x, dest);
663 SDL_FreeSurface(dest2x);
664 break;
666 /* not a valid case, but to avoid compiler warning */
667 case GD_SCALING_MAX:
668 g_assert_not_reached();
669 break;
672 return dest;
676 /* wrapper */
677 static void
678 pal_emu_surface(SDL_Surface *image)
680 if (SDL_MUSTLOCK(image))
681 SDL_LockSurface(image);
682 gd_pal_emulate_raw(image->pixels, image->w, image->h, image->pitch, image->format->Rshift, image->format->Gshift, image->format->Bshift, image->format->Ashift);
683 if (SDL_MUSTLOCK(image))
684 SDL_UnlockSurface(image);
688 /* create new surface, which is SDL_DisplayFormatted. */
689 /* it is also scaled by gd_scale. */
690 static SDL_Surface *
691 displayformat(SDL_Surface *orig)
693 SDL_Surface *scaled, *displayformat;
695 /* at this point we must already be working with 32bit surfaces */
696 g_assert(orig->format->BytesPerPixel==4);
698 scaled=surface_scale(orig);
699 if (gd_sdl_pal_emulation)
700 pal_emu_surface(scaled);
701 displayformat=SDL_DisplayFormat(scaled);
702 SDL_FreeSurface(scaled);
704 return displayformat;
707 static SDL_Surface *
708 displayformatalpha(SDL_Surface *orig)
710 SDL_Surface *scaled, *displayformat;
712 /* at this point we must already be working with 32bit surfaces */
713 g_assert(orig->format->BytesPerPixel==4);
715 scaled=surface_scale(orig);
716 if (gd_sdl_pal_emulation)
717 pal_emu_surface(scaled);
718 displayformat=SDL_DisplayFormatAlpha(scaled);
719 SDL_FreeSurface(scaled);
721 return displayformat;
724 #if 0
725 /* needed to alpha-copy an src image over dst.
726 the resulting image of sdl_blitsurface is not exact?!
727 can't explain why. XXX. */
728 static void
729 copy_alpha(SDL_Surface *src, SDL_Surface *dst)
731 int x, y;
733 g_assert(src->format->BytesPerPixel==4);
734 g_assert(dst->format->BytesPerPixel==4);
735 g_assert(src->w == dst->w);
736 g_assert(src->h == dst->h);
738 SDL_LockSurface(src);
739 SDL_LockSurface(dst);
740 for (y=0; y<src->h; y++) {
741 guint32 *srcpix=(guint32 *)((guint8*)src->pixels + src->pitch*y);
742 guint32 *dstpix=(guint32 *)((guint8*)dst->pixels + dst->pitch*y);
743 for (x=0; x<src->w; x++) {
744 if ((srcpix[x]&src->format->Amask)>>src->format->Ashift) /* if alpha is nonzero */
745 dstpix[x]=srcpix[x]; /* copy pixel as it is */
748 SDL_UnlockSurface(src);
749 SDL_UnlockSurface(dst);
751 #endif
753 /* create and return an array of surfaces, which contain the title animation.
754 the array is one pointer larger than all frames; the last pointer is a null.
755 up to the caller to free.
757 SDL_Surface **
758 gd_get_title_animation(gboolean one_frame_only)
760 SDL_Surface *screen;
761 SDL_Surface *tile;
762 SDL_Surface *bigone;
763 SDL_Surface *frame;
764 SDL_Surface **animation;
765 int x, y, i;
766 int framenum;
768 screen=NULL;
769 tile=NULL;
770 if (gd_caveset_data->title_screen->len!=0) {
771 /* user defined title screen */
772 screen=surface_from_base64(gd_caveset_data->title_screen->str);
773 if (!screen) {
774 g_warning("Caveset is storing an invalid title screen image.");
775 g_string_assign(gd_caveset_data->title_screen, "");
776 } else {
777 /* if we loaded the screen, now try to load the tile. */
778 /* only if the screen has an alpha channel. otherwise it would not make any sense */
779 if (screen->format->Amask!=0 && gd_caveset_data->title_screen_scroll->len!=0) {
780 tile=surface_from_base64(gd_caveset_data->title_screen_scroll->str);
782 if (!tile) {
783 g_warning("Caveset is storing an invalid title screen background image.");
784 g_string_assign(gd_caveset_data->title_screen_scroll, "");
791 /* if no special title image or unable to load that one, load the built-in */
792 if (!screen) {
793 /* the screen */
794 screen=surface_from_raw_data(gdash_screen, sizeof(gdash_screen));
795 /* the tile to be put under the screen */
796 tile=surface_from_raw_data(gdash_tile, sizeof(gdash_tile));
797 g_assert(screen!=NULL);
798 g_assert(tile!=NULL);
801 /* if no tile, let it be black. */
802 /* the sdl version does the same. */
803 if (!tile) {
804 /* one-row pixbuf, so no animation. */
805 tile=SDL_CreateRGBSurface(0, screen->w, 1, 32, 0, 0, 0, 0);
806 SDL_FillRect(tile, NULL, SDL_MapRGB(tile->format, 0, 0, 0));
809 /* do not allow more than 40 frames of animation */
810 g_assert(tile->h<40);
811 if (one_frame_only)
812 framenum=1;
813 else
814 framenum=tile->h;
815 animation=g_new0(SDL_Surface *, framenum+1);
817 /* create a big image, which is one tile larger than the title image size */
818 bigone=SDL_CreateRGBSurface(0, screen->w, screen->h+tile->h, 32, 0, 0, 0, 0);
819 /* and fill it with the tile. */
820 for (y=0; y<screen->h+tile->h; y+=tile->h)
821 for (x=0; x<screen->w; x+=tile->h) {
822 SDL_Rect dest;
824 dest.x=x;
825 dest.y=y;
826 SDL_BlitSurface(tile, 0, bigone, &dest);
829 frame=SDL_CreateRGBSurface(0, screen->w, screen->h, 32, rmask, gmask, bmask, amask); /* must be same *mask so copy_alpha works correctly */
830 for (i=0; i<framenum; i++) {
831 SDL_Rect src;
833 /* copy part of the big tiled image */
834 src.x=0;
835 src.y=i;
836 src.w=screen->w;
837 src.h=screen->h;
838 SDL_BlitSurface(bigone, &src, frame, 0);
839 /* and composite it with the title image */
840 // copy_alpha(screen, frame);
841 SDL_BlitSurface(screen, NULL, frame, NULL);
842 animation[i]=displayformat(frame);
844 SDL_FreeSurface(frame);
845 SDL_FreeSurface(bigone);
846 SDL_FreeSurface(screen);
847 SDL_FreeSurface(tile);
849 return animation;
852 static void
853 rendered_font_free(gpointer font)
855 SDL_Surface **p;
857 /* null-terminated list of pointers to sdl_surfaces */
858 for(p=font; p!=NULL; p++)
859 SDL_FreeSurface(*p);
860 g_free(font);
864 gboolean
865 gd_sdl_init(GdScalingType scaling_type)
867 SDL_Surface *icon;
869 /* set the cell scale option. */
870 gd_scale_type=scaling_type;
871 gd_scale=gd_scaling_scale[gd_scale_type];
872 play_area_w*=gd_scale;
873 play_area_h*=gd_scale;
874 gd_statusbar_y1*=gd_scale;
875 gd_statusbar_y2*=gd_scale;
876 gd_statusbar_height*=gd_scale;
877 gd_statusbar_mid*=gd_scale;
879 SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_JOYSTICK);
880 SDL_EnableKeyRepeat(250, 100);
881 gd_screen=SDL_SetVideoMode(play_area_w, play_area_h+gd_statusbar_height, 32, SDL_ANYFORMAT | SDL_ASYNCBLIT | (gd_sdl_fullscreen?SDL_FULLSCREEN:0));
882 /* do not show mouse cursor */
883 SDL_ShowCursor(SDL_DISABLE);
884 /* warp mouse pointer so cursor cannot be seen, if the above call did nothing for some reason */
885 SDL_WarpMouse(gd_screen->w-1, gd_screen->h-1);
886 if (!gd_screen)
887 return FALSE;
889 /* keyboard, joystick */
890 gd_keystate=SDL_GetKeyState(NULL);
891 if (SDL_NumJoysticks()>0)
892 joystick_1=SDL_JoystickOpen(0);
894 /* icon */
895 icon=surface_from_raw_data(gdash_32, sizeof(gdash_32));
896 SDL_WM_SetIcon(icon, NULL);
897 SDL_WM_SetCaption("GDash", NULL);
898 SDL_FreeSurface(icon);
900 font_n=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rendered_font_free);
901 font_w=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rendered_font_free);
902 font_color_recently_used=NULL;
904 return TRUE;
910 static inline gboolean
911 get_joy_up()
913 return joystick_1!=NULL && SDL_JoystickGetAxis(joystick_1, 1)<-3200;
916 static inline gboolean
917 get_joy_down()
919 return joystick_1!=NULL && SDL_JoystickGetAxis(joystick_1, 1)>3200;
922 static inline gboolean
923 get_joy_left()
925 return joystick_1!=NULL && SDL_JoystickGetAxis(joystick_1, 0)<-3200;
928 static inline gboolean
929 get_joy_right()
931 return joystick_1!=NULL && SDL_JoystickGetAxis(joystick_1, 0)>3200;
934 static inline gboolean
935 get_joy_fire()
937 return joystick_1!=NULL && (SDL_JoystickGetButton(joystick_1, 0) || SDL_JoystickGetButton(joystick_1, 1));
943 gboolean
944 gd_up()
946 return gd_keystate[gd_sdl_key_up] || get_joy_up();
949 gboolean
950 gd_down()
952 return gd_keystate[gd_sdl_key_down] || get_joy_down();
955 gboolean
956 gd_left()
958 return gd_keystate[gd_sdl_key_left] || get_joy_left();
961 gboolean
962 gd_right()
964 return gd_keystate[gd_sdl_key_right] || get_joy_right();
967 gboolean
968 gd_fire()
970 return gd_keystate[gd_sdl_key_fire_1] || gd_keystate[gd_sdl_key_fire_2] || get_joy_fire();
976 /* like the above one, but used in the main menu */
977 gboolean
978 gd_space_or_enter_or_fire()
980 /* set these to zero, so each keypress is reported only once */
981 return get_joy_fire() || gd_keystate[SDLK_SPACE] || gd_keystate[SDLK_RETURN];
985 /* process pending events, so presses and releases are applied */
986 /* also check for quit event */
987 void
988 gd_process_pending_events()
990 SDL_Event event;
992 /* process pending events, so presses and releases are applied */
993 while (SDL_PollEvent(&event)) {
994 /* meanwhile, if we receive a quit event, we set the global variable. */
995 if (event.type==SDL_QUIT)
996 gd_quit=TRUE;
1002 /* this function waits until SPACE, ENTER and ESCAPE are released */
1003 void
1004 gd_wait_for_key_releases()
1006 /* wait until the user releases return and escape */
1007 while (gd_keystate[SDLK_RETURN]!=0 || gd_keystate[SDLK_ESCAPE]!=0 || gd_keystate[SDLK_SPACE]!=0 || gd_keystate[SDLK_n]!=0 || gd_fire()) {
1008 SDL_Event event;
1010 /* process pending events, so presses and releases are applied */
1011 while (SDL_PollEvent(&event)) {
1012 /* meanwhile, if we receive a quit event, we set the global variable. */
1013 if (event.type==SDL_QUIT)
1014 gd_quit=TRUE;
1017 SDL_Delay(50); /* do not eat cpu */
1031 /* remove pixmaps from x server */
1032 static void
1033 free_cells()
1035 int i;
1037 /* if cells already loaded, unref them */
1038 for (i=0; i<G_N_ELEMENTS(cells); i++)
1039 if (cells[i]) {
1040 SDL_FreeSurface(cells[i]);
1041 cells[i]=NULL;
1046 static void
1047 loadcells_from_surface(SDL_Surface *image)
1049 int i;
1050 int pixbuf_cell_size;
1051 SDL_Surface *rect;
1052 SDL_Surface *cut;
1054 /* if we have display-formatted pixmaps, remove them */
1055 free_cells();
1057 /* 8 (NUM_OF_CELLS_X) cells in a row, so divide by it and we get the size of a cell in pixels */
1058 pixbuf_cell_size=image->w/NUM_OF_CELLS_X;
1059 cell_size=pixbuf_cell_size*gd_scale;
1061 rect=SDL_CreateRGBSurface(0, pixbuf_cell_size, pixbuf_cell_size, 32, 0, 0, 0, 0); /* no amask, as we set overall alpha! */
1062 SDL_FillRect(rect, NULL, SDL_MapRGB(rect->format, (gd_flash_color>>16)&0xff, (gd_flash_color>>8)&0xff, gd_flash_color&0xff));
1063 SDL_SetAlpha(rect, SDL_SRCALPHA, 128); /* 50% alpha; nice choice. also sdl is rendering faster for the special value alpha=128 */
1065 cut=SDL_CreateRGBSurface(0, pixbuf_cell_size, pixbuf_cell_size, 32, rmask, gmask, bmask, amask);
1067 /* make individual cells */
1068 for (i=0; i<NUM_OF_CELLS_X*NUM_OF_CELLS_Y; i++) {
1069 SDL_Rect from;
1071 from.x=(i%NUM_OF_CELLS_X)*pixbuf_cell_size;
1072 from.y=(i/NUM_OF_CELLS_X)*pixbuf_cell_size;
1073 from.w=pixbuf_cell_size;
1074 from.h=pixbuf_cell_size;
1076 SDL_BlitSurface(image, &from, cut, NULL);
1078 cells[i]=displayformat(cut);
1079 SDL_BlitSurface(rect, NULL, cut, NULL); /* create yellowish image */
1080 cells[NUM_OF_CELLS+i]=displayformat(cut);
1082 SDL_FreeSurface(cut);
1083 SDL_FreeSurface(rect);
1086 /* sets one of the colors in the indexed palette of an sdl surface to a GdColor. */
1087 static void
1088 surface_setpalette(SDL_Surface *image, int index, GdColor col)
1090 SDL_Color c;
1091 c.r=gd_color_get_r(col);
1092 c.g=gd_color_get_g(col);
1093 c.b=gd_color_get_b(col);
1095 SDL_SetPalette(image, SDL_LOGPAL|SDL_PHYSPAL, &c, index, 1);
1098 static void
1099 loadcells_from_c64_data(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
1101 const guint8 *gfx; /* currently used graphics, will point to c64_gfx or c64_custom_gfx */
1102 SDL_Surface *image;
1104 gfx=c64_custom_gfx?c64_custom_gfx:c64_gfx;
1106 /* create a 8-bit palette and set its colors */
1107 /* gfx[0] is the pixel width and height of one cell. */
1108 /* from gfx[1], we have the color data, one byte/pixel. so this is an indexed color image: 8bit/pixel. */
1109 image=SDL_CreateRGBSurfaceFrom((void *)(gfx+1), NUM_OF_CELLS_X*(int)gfx[0], NUM_OF_CELLS_Y*(int)gfx[0], 8, NUM_OF_CELLS_X*(int)gfx[0], 0, 0, 0, 0);
1110 /* sdl supports paletted images, so this is very easy: */
1111 surface_setpalette(image, 0, 0);
1112 surface_setpalette(image, 1, c0);
1113 surface_setpalette(image, 2, c1);
1114 surface_setpalette(image, 3, c2);
1115 surface_setpalette(image, 4, c3);
1116 surface_setpalette(image, 5, c4);
1117 surface_setpalette(image, 6, c5);
1118 surface_setpalette(image, 7, 0);
1119 surface_setpalette(image, 8, 0);
1121 /* from here, same as any other image */
1122 loadcells_from_surface(image);
1123 SDL_FreeSurface(image);
1126 /* takes a c64_gfx.png-coded 32-bit pixbuf, and creates a paletted pixbuf in our internal format. */
1127 static guchar *
1128 c64_gfx_data_from_pixbuf(SDL_Surface *image)
1130 int x, y;
1131 guint8 *data;
1132 int out;
1134 g_assert(image->format->BytesPerPixel==4);
1136 data=g_new(guint8, image->w*image->h+1);
1137 out=0;
1138 data[out++]=image->w/NUM_OF_CELLS_X;
1140 SDL_LockSurface(image);
1141 for (y=0; y<image->h; y++) {
1142 guint32 *p=(guint32 *)((char *)image->pixels+y*image->pitch);
1144 for (x=0; x<image->w; x++) {
1145 int r, g, b;
1147 r=(p[x]&image->format->Rmask) >> image->format->Rshift << image->format->Rloss;
1148 g=(p[x]&image->format->Gmask) >> image->format->Gshift << image->format->Gloss;
1149 b=(p[x]&image->format->Bmask) >> image->format->Bshift << image->format->Bloss;
1150 /* should be:
1151 a=(p[x]&image->format->Amask) >> image->format->Ashift << image->format->Aloss;
1152 but we do not use the alpha channel in sdash, so we just use 255 (max alpha)
1154 data[out++]=c64_png_colors(r, g, b, 255);
1157 SDL_UnlockSurface(image);
1159 return data;
1163 /* returns true, if the given pixbuf seems to be a c64 imported image. */
1164 static gboolean
1165 check_if_pixbuf_c64_png(SDL_Surface *image)
1167 const guchar *p;
1168 int x, y;
1169 gboolean c64_png;
1171 g_assert(image->format->BytesPerPixel==4);
1173 SDL_LockSurface(image);
1174 c64_png=TRUE;
1175 for (y=0; y<image->h; y++) {
1176 p=((guchar *)image->pixels) + y * image->pitch;
1177 for (x=0; x<image->w*image->format->BytesPerPixel; x++)
1178 if (p[x]!=0 && p[x]!=255)
1179 c64_png=FALSE;
1181 SDL_UnlockSurface(image);
1183 return c64_png;
1186 /* check if given surface is ok to be a gdash theme. */
1187 gboolean
1188 gd_is_surface_ok_for_theme(SDL_Surface *surface)
1190 if ((surface->w % NUM_OF_CELLS_X != 0) || (surface->h % NUM_OF_CELLS_Y != 0) || (surface->w / NUM_OF_CELLS_X != surface->h / NUM_OF_CELLS_Y)) {
1191 g_warning("image should contain %d cells in a row and %d in a column!", NUM_OF_CELLS_X, NUM_OF_CELLS_Y);
1192 return FALSE; /* Image should contain 16 cells in a row and 32 in a column! */
1194 /* we do not check for an alpha channel, as in the gtk version. we do not need it here. */
1195 /* the gtk version needs it because of the editor. */
1197 return TRUE; /* passed checks */
1201 /* load theme from image file. */
1202 /* return true if successful. */
1203 gboolean
1204 gd_loadcells_file(const char *filename)
1206 SDL_Surface *surface, *converted;
1208 /* load cell graphics */
1209 /* load from file */
1210 surface=IMG_Load(filename);
1212 if (!surface) {
1213 g_warning("%s: unable to load image", filename);
1214 return FALSE;
1217 /* do some checks. if those fail, the error is already reported by the function */
1218 if (!gd_is_surface_ok_for_theme(surface)) {
1219 SDL_FreeSurface(surface);
1220 return FALSE;
1223 /* convert to 32bit rgba */
1224 converted=SDL_CreateRGBSurface(0, surface->w, surface->h, 32, rmask, gmask, bmask, amask); /* 32bits per pixel, but we dont need an alpha channel */
1225 SDL_SetAlpha(surface, 0, 0); /* do not use the alpha blending for the copy */
1226 SDL_BlitSurface(surface, NULL, converted, NULL);
1227 SDL_FreeSurface(surface);
1229 if (check_if_pixbuf_c64_png(converted)) {
1230 /* c64 pixbuf with a small number of colors which can be changed */
1231 g_free(c64_custom_gfx);
1232 c64_custom_gfx=c64_gfx_data_from_pixbuf(converted);
1233 using_png_gfx=FALSE;
1234 } else {
1235 /* normal, "truecolor" pixbuf */
1236 g_free(c64_custom_gfx);
1237 c64_custom_gfx=NULL;
1238 using_png_gfx=TRUE;
1239 loadcells_from_surface(converted);
1241 SDL_FreeSurface(converted);
1242 color0=GD_COLOR_INVALID; /* this is an invalid gdash color; so redraw is forced */
1244 return TRUE;
1249 /* load the theme specified in gd_sdl_theme. */
1250 /* if successful, ok. */
1251 /* if fails, or no theme specified, load the builtin, and forget gd_sdl_theme */
1252 void
1253 gd_load_theme()
1255 if (gd_sdl_theme) {
1256 if (!gd_loadcells_file(gd_sdl_theme)) {
1257 /* if failing to load the bmp file specified in the config file: forget the setting now, and load the builtin. */
1258 g_free(gd_sdl_theme);
1259 gd_sdl_theme=NULL;
1262 if (!gd_sdl_theme)
1263 gd_loadcells_default(); /* if no theme specified, or loading the file failed, simply load the builtin. */
1267 /* selects built-in graphics */
1268 void
1269 gd_loadcells_default()
1271 g_free(c64_custom_gfx);
1272 c64_custom_gfx=NULL;
1273 using_png_gfx=FALSE;
1274 /* just to set some default */
1275 color0=GD_COLOR_INVALID; /* this is an invalid gdash color; so redraw is forced */
1277 /* size of array (in bytes), -1 which is the cell size */
1278 g_assert(sizeof(c64_gfx)-1 == NUM_OF_CELLS_X*NUM_OF_CELLS_Y*c64_gfx[0]*c64_gfx[0]);
1284 /* C64 FONT HANDLING */
1286 /* creates a guint32 value, which can be raw-written to a sdl_surface memory area. */
1287 /* takes the pixel format of the surface into consideration. */
1288 static guint32
1289 rgba_pixel_from_gdcolor(SDL_PixelFormat *format, GdColor col, guint8 a)
1291 return SDL_MapRGBA(format, gd_color_get_r(col), gd_color_get_g(col), gd_color_get_b(col), a);
1295 /* FIXME fixed font size: 8x8 */
1296 /* renders the fonts for a specific color. */
1297 /* the wide font draws black pixels, the narrow (normal) font does not! */
1298 static void
1299 renderfont_color(GdColor color)
1301 int j, x, y;
1302 guint32 col, transparent;
1303 SDL_Surface *image, *image_n;
1304 SDL_Surface **fn, **fw;
1306 /* check that we already got an rgb color */
1307 g_assert(gd_color_is_rgb(color));
1309 /* remove from list and add to the front */
1310 font_color_recently_used=g_list_remove(font_color_recently_used, GUINT_TO_POINTER(color));
1311 font_color_recently_used=g_list_prepend(font_color_recently_used, GUINT_TO_POINTER(color));
1313 /* if already rendered, return now */
1314 if (g_hash_table_lookup(font_w, GUINT_TO_POINTER(color)))
1315 return;
1317 /* -------- RENDERING A NEW FONT -------- */
1318 /* if storing too many fonts, free the oldest one. */
1319 if (g_list_length(font_color_recently_used)>32) {
1320 GList *last;
1322 /* if list too big, remove least recently used */
1323 last=g_list_last(font_color_recently_used);
1324 g_hash_table_remove(font_w, last->data);
1325 g_hash_table_remove(font_n, last->data);
1326 font_color_recently_used=g_list_delete_link(font_color_recently_used, last);
1329 fn=g_new0(SDL_Surface *, NUM_OF_CHARS+1);
1330 fw=g_new0(SDL_Surface *, NUM_OF_CHARS+1);
1332 /* colors data into memory */
1333 image=SDL_CreateRGBSurface(SDL_SRCALPHA, 16, 8, 32, rmask, gmask, bmask, amask);
1334 image_n=SDL_CreateRGBSurface(SDL_SRCALPHA, 8, 8, 32, rmask, gmask, bmask, amask);
1336 col=rgba_pixel_from_gdcolor(image->format, color, SDL_ALPHA_OPAQUE);
1337 transparent=rgba_pixel_from_gdcolor(image->format, 0, SDL_ALPHA_TRANSPARENT); /* color does not matter as totally transparent */
1339 /* for all characters */
1340 /* render character in "image", then do a displayformat(image) to generate the resulting one */
1341 for (j=0; j<NUM_OF_CHARS; j++) {
1342 int x1, y1;
1344 y1=(j/16)*8; /* 16 characters in a row */
1345 x1=(j%16)*8;
1347 if (SDL_MUSTLOCK(image))
1348 SDL_LockSurface(image);
1349 if (SDL_MUSTLOCK(image_n))
1350 SDL_LockSurface(image_n);
1351 for (y=0; y<8; y++) {
1352 guint32 *p=(guint32*) ((char *)image->pixels + y*image->pitch);
1353 guint32 *p_n=(guint32*) ((char *)image_n->pixels + y*image_n->pitch);
1355 for (x=0; x<8; x++) {
1356 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
1357 if (font[(y1+y)*128+x1+x]!=1) {
1358 p[0]=col; /* wide */
1359 p[1]=col;
1360 p_n[0]=col; /* normal */
1361 } else {
1362 p[0]=transparent; /* wide */
1363 p[1]=transparent;
1364 p_n[0]=transparent; /* normal */
1366 p+=2;
1367 p_n++;
1370 if (SDL_MUSTLOCK(image))
1371 SDL_UnlockSurface(image);
1372 if (SDL_MUSTLOCK(image_n))
1373 SDL_UnlockSurface(image_n);
1374 fw[j]=displayformatalpha(image); /* alpha channel is used! */
1375 fn[j]=displayformatalpha(image_n);
1377 SDL_FreeSurface(image);
1378 SDL_FreeSurface(image_n);
1380 /* add newly rendered font to hash table. to the recently used list, it is already added. */
1381 g_hash_table_insert(font_w, GINT_TO_POINTER(color), fw);
1382 g_hash_table_insert(font_n, GINT_TO_POINTER(color), fn);
1385 /* XXX these functions are not really implemented */
1386 static void
1387 loadfont_buffer()
1389 /* forget all previously rendered chars */
1390 g_hash_table_remove_all(font_w);
1391 g_hash_table_remove_all(font_n);
1392 g_list_free(font_color_recently_used);
1393 font_color_recently_used=NULL;
1397 /* loads a font from a file */
1398 void
1399 gd_loadfont_file(const char *filename)
1401 /* not yet implemented */
1402 g_assert_not_reached();
1405 /* loads inlined c64 font */
1406 void
1407 gd_loadfont_default()
1409 font=c64_font+1; /* first byte in c64_gfx[] is the "cell size", we ignore that */
1410 loadfont_buffer();
1416 int gd_font_height()
1418 return 8*gd_scale;
1421 int gd_font_width()
1423 return 8*gd_scale;
1426 int gd_line_height()
1428 return gd_font_height()+2;
1431 /* clears one line on the screen. takes dark screen or totally black screen into consideration. */
1432 void
1433 gd_clear_line(SDL_Surface *screen, int y)
1435 SDL_Rect rect;
1437 rect.x=0;
1438 rect.y=y;
1439 rect.w=screen->w;
1440 rect.h=gd_font_height();
1441 if (dark_screens!=NULL && dark_screens->data!=NULL)
1442 SDL_BlitSurface(dark_screens->data, &rect, screen, &rect);
1443 else
1444 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0)); /* fill rectangle with black */
1450 /* function which draws characters on the screen. used internally. */
1451 /* x=-1 -> center horizontally */
1452 static int
1453 blittext_font(SDL_Surface *screen, SDL_Surface **font, int x1, int y, const char *text)
1455 const char *p=text;
1456 SDL_Rect destrect;
1457 int i;
1458 int x;
1459 gunichar c;
1461 if (!text) return x1; /* do not crash if null */
1463 if (x1==-1)
1464 x1=screen->w/2 - (font[0]->w*g_utf8_strlen(text, -1))/2;
1466 x=x1;
1467 c=g_utf8_get_char(p);
1468 while (c!=0) {
1469 /* decompose accented character, ie. e' -> e + '. in the c64 font, we don't have accents */
1470 gsize m;
1471 gunichar *decomp=g_unicode_canonical_decomposition(c, &m);
1472 if (m>0)
1473 c=decomp[0];
1474 g_free(decomp);
1475 /* some unicode hack */
1476 switch (c) {
1477 case 0x2018: c='\''; break; /* left single quotation mark */
1478 case 0x2019: c='\''; break; /* right single quotation mark */
1479 case 0x201A: c=','; break; /* low single comma quotation mark */
1480 case 0x201B: c='\''; break; /* high-reversed-9 quotation mark */
1481 case 0x201C: c='\"'; break; /* left double quotation mark */
1482 case 0x201D: c='\"'; break; /* right double quotation mark */
1483 case 0x201E: c='\"'; break; /* low double quotation mark */
1484 case 0x201F: c='\"'; break; /* double reversed comma quotation mark */
1485 case 0x2032: c='\''; break; /* prime */
1486 case 0x2033: c='\"'; break; /* double prime */
1487 case 0x2034: c='\"'; break; /* triple prime */
1488 case 0x2035: c='\''; break; /* reversed prime */
1489 case 0x2036: c='\"'; break; /* reversed double prime */
1490 case 0x2037: c='\"'; break; /* reversed triple prime */
1491 case 0x2039: c='<'; break; /* single left-pointing angle quotation mark */
1492 case 0x203A: c='>'; break; /* single right-pointing angle quotation mark */
1494 if (c=='\n') {
1495 /* if it is an enter */
1496 if (x==x1)
1497 y+=font[0]->h/2;
1498 else
1499 y+=font[0]->h;
1500 x=x1;
1501 } else {
1502 if (c<NUM_OF_CHARS)
1503 i=c;
1504 else
1505 i=127; /* unknown char */
1507 destrect.x=x;
1508 destrect.y=y;
1509 SDL_BlitSurface(font[i], NULL, screen, &destrect);
1511 x+=font[i]->w;
1514 p=g_utf8_next_char(p); /* next character */
1515 c=g_utf8_get_char(p);
1517 return x;
1520 /* write something to the screen, with wide characters. */
1521 /* x=-1 -> center horizontally */
1523 gd_blittext(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1525 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1526 renderfont_color(color);
1527 return blittext_font(screen, g_hash_table_lookup(font_w, GUINT_TO_POINTER(color)), x, y, text);
1530 /* write something to the screen, with normal characters. */
1531 /* x=-1 -> center horizontally */
1533 gd_blittext_n(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1535 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1536 renderfont_color(color);
1537 return blittext_font(screen, g_hash_table_lookup(font_n, GUINT_TO_POINTER(color)), x, y, text);
1540 void
1541 gd_blitlines_n(SDL_Surface *screen, int x, int y, GdColor color, char **text, int maxlines, int first)
1543 int n;
1544 int all_lines=g_strv_length((gchar **) text);
1546 for (n=0; n<maxlines && first+n<all_lines; n++)
1547 gd_blittext_n(screen, x, y+n*gd_line_height(), color, text[first+n]);
1551 /* write something to the screen, with wide characters. */
1552 /* x=-1 -> center horizontally */
1554 gd_blittext_printf(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1556 va_list args;
1557 char *text;
1559 va_start(args, format);
1560 text=g_strdup_vprintf(format, args);
1561 x=gd_blittext(screen, x, y, color, text);
1562 g_free(text);
1563 va_end(args);
1565 return x;
1568 /* write something to the screen, with normal characters. */
1569 /* x=-1 -> center horizontally */
1571 gd_blittext_printf_n(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1573 va_list args;
1574 char *text;
1576 va_start(args, format);
1577 text=g_strdup_vprintf(format, args);
1578 x=gd_blittext_n(screen, x, y, color, text);
1579 g_free(text);
1580 va_end(args);
1582 return x;
1587 void
1588 gd_select_pixbuf_colors(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
1590 /* if non-c64 gfx, nothing to do */
1591 if (using_png_gfx)
1592 return;
1594 /* convert to rgb value */
1595 c0=gd_color_get_rgb(c0);
1596 c1=gd_color_get_rgb(c1);
1597 c2=gd_color_get_rgb(c2);
1598 c3=gd_color_get_rgb(c3);
1599 c4=gd_color_get_rgb(c4);
1600 c5=gd_color_get_rgb(c5);
1602 /* and compare rgb values! */
1603 if (c0!=color0 || c1!=color1 || c2!=color2 || c3!=color3 || c4!=color4 || c5!=color5) {
1604 /* if not the same colors as requested before */
1605 color0=c0;
1606 color1=c1;
1607 color2=c2;
1608 color3=c3;
1609 color4=c4;
1610 color5=c5;
1612 loadcells_from_c64_data(c0, c1, c2, c3, c4, c5);
1625 logical_size: logical pixel size of playfield, usually larger than the screen.
1626 physical_size: visible part. (remember: player_x-x1!)
1628 center: the coordinates to scroll to.
1629 exact: scroll exactly
1630 start: start scrolling if difference is larger than
1631 to: scroll to, if started, until difference is smaller than
1632 current
1634 desired: the function stores its data here
1635 speed: the function stores its data here
1637 cell_size: size of one cell. used to determine if the play field is only a slightly larger than the screen, in that case no scrolling is desirable
1639 static gboolean
1640 cave_scroll(int logical_size, int physical_size, int center, gboolean exact, int start, int to, int *current, int *desired, int speed, int cell_size)
1642 int i;
1643 gboolean changed;
1644 int max;
1646 max=logical_size-physical_size;
1647 if (max<0)
1648 max=0;
1650 changed=FALSE;
1652 /* if cave size smaller than the screen, no scrolling req'd */
1653 if (logical_size<physical_size) {
1654 *desired=0;
1655 if (*current!=0) {
1656 *current=0;
1657 changed=TRUE;
1660 return changed;
1663 /* if cave size is only a slightly larger than the screen, also no scrolling */
1664 if (logical_size<=physical_size+cell_size) {
1665 *desired=max/2; /* scroll to the middle of the cell */
1666 } else {
1667 /* hystheresis function.
1668 * when scrolling left, always go a bit less left than player being at the middle.
1669 * when scrolling right, always go a bit less to the right. */
1670 if (exact)
1671 *desired=center;
1672 else {
1673 if (*current+start<center)
1674 *desired=center-to;
1675 if (*current-start>center)
1676 *desired=center+to;
1679 *desired=gd_clamp(*desired, 0, max);
1681 if (*current<*desired) {
1682 for (i=0; i<speed; i++)
1683 if (*current<*desired)
1684 (*current)++;
1685 changed=TRUE;
1687 if (*current > *desired) {
1688 for (i=0; i<speed; i++)
1689 if (*current>*desired)
1690 (*current)--;
1691 changed=TRUE;
1694 return changed;
1698 /* just set current viewport to upper left. */
1699 void
1700 gd_scroll_to_origin()
1702 scroll_x=0;
1703 scroll_y=0;
1707 /* SCROLLING
1709 * scrolls to the player during game play.
1710 * called by drawcave
1711 * returns true, if player is not visible-ie it is out of the visible size in the drawing area.
1713 gboolean
1714 gd_scroll(GdGame *game, gboolean exact_scroll)
1716 static int scroll_desired_x=0, scroll_desired_y=0;
1717 gboolean out_of_window;
1718 int player_x, player_y, visible_x, visible_y;
1719 gboolean changed;
1720 int scroll_divisor;
1721 /* max scrolling speed depends on the speed of the cave. */
1722 /* game moves cell_size_game* 1s/cave time pixels in a second. */
1723 /* scrolling moves scroll speed * 1s/scroll_time in a second. */
1724 /* these should be almost equal; scrolling speed a little slower. */
1725 /* that way, the player might reach the border with a small probability, */
1726 /* but the scrolling will not "oscillate", ie. turn on for little intervals as it has */
1727 /* caught up with the desired position. smaller is better. */
1728 int scroll_speed=cell_size*(gd_fine_scroll?20:40)/game->cave->speed;
1730 player_x=game->cave->player_x-game->cave->x1; /* cell coordinates of player */
1731 player_y=game->cave->player_y-game->cave->y1;
1732 visible_x=(game->cave->x2-game->cave->x1+1)*cell_size; /* pixel size of visible part of the cave (may be smaller in intermissions) */
1733 visible_y=(game->cave->y2-game->cave->y1+1)*cell_size;
1735 /* cell_size contains the scaled size, but we need the original. */
1736 changed=FALSE;
1737 /* some sort of scrolling speed. with larger cells, the divisor must be smaller, so the scrolling faster. */
1738 scroll_divisor=256/cell_size;
1739 if (gd_fine_scroll)
1740 scroll_divisor*=2; /* as fine scrolling is 50hz, whereas normal is 25hz only */
1742 if (cave_scroll(visible_x, play_area_w, player_x*cell_size+cell_size/2-play_area_w/2, exact_scroll, play_area_w/4, play_area_w/8, &scroll_x, &scroll_desired_x, scroll_speed, cell_size))
1743 changed=TRUE;
1744 if (cave_scroll(visible_y, play_area_h, player_y*cell_size+cell_size/2-play_area_h/2, exact_scroll, play_area_h/5, play_area_h/10, &scroll_y, &scroll_desired_y, scroll_speed, cell_size))
1745 changed=TRUE;
1747 /* if scrolling, we should update entire gd_screen. */
1748 if (changed) {
1749 int x, y;
1751 for (y=0; y<game->cave->h; y++)
1752 for (x=0; x<game->cave->w; x++)
1753 game->gfx_buffer[y][x] |= GD_REDRAW;
1756 /* check if active player is visible at the moment. */
1757 out_of_window=FALSE;
1758 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
1759 if ((player_x*cell_size)<scroll_x || (player_x*cell_size+cell_size-1)>scroll_x+play_area_w)
1760 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1761 if (game->cave->player_x>=game->cave->x1 && game->cave->player_x<=game->cave->x2)
1762 out_of_window=TRUE;
1763 if ((player_y*cell_size)<scroll_y || (player_y*cell_size+cell_size-1)>scroll_y+play_area_h)
1764 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1765 if (game->cave->player_y>=game->cave->y1 && game->cave->player_y<=game->cave->y2)
1766 out_of_window=TRUE;
1768 /* if not yet born, we treat as visible. so cave will run. the user is unable to control an unborn player, so this is the right behaviour. */
1769 if (game->cave->player_state==GD_PL_NOT_YET)
1770 return FALSE;
1771 return out_of_window;
1778 gd_drawcave(SDL_Surface *dest, GdGame *game)
1780 int x, y, xd, yd;
1781 SDL_Rect cliprect;
1782 int scroll_y_aligned;
1784 /* on-screen clipping rectangle */
1785 cliprect.x=0;
1786 cliprect.y=gd_statusbar_height;
1787 cliprect.w=play_area_w;
1788 cliprect.h=play_area_h;
1789 SDL_SetClipRect(dest, &cliprect);
1791 if (gd_sdl_pal_emulation && gd_even_line_pal_emu_vertical_scroll)
1792 scroll_y_aligned=scroll_y/2*2; /* make it even (dividable by two) */
1793 else
1794 scroll_y_aligned=scroll_y;
1796 /* here we draw all cells to be redrawn. we do not take scrolling area into consideration - sdl will do the clipping. */
1797 for (y=game->cave->y1, yd=0; y<=game->cave->y2; y++, yd++) {
1798 for (x=game->cave->x1, xd=0; x<=game->cave->x2; x++, xd++) {
1799 if (game->gfx_buffer[y][x] & GD_REDRAW) { /* if it needs to be redrawn */
1800 SDL_Rect offset;
1802 offset.y=y*cell_size+gd_statusbar_height-scroll_y_aligned; /* sdl_blitsurface destroys offset, so we have to set y here, too. (ie. in every iteration) */
1803 offset.x=x*cell_size-scroll_x;
1805 game->gfx_buffer[y][x]=game->gfx_buffer[y][x] & ~GD_REDRAW; /* now we have drawn it */
1807 SDL_BlitSurface(cells[game->gfx_buffer[y][x]], NULL, dest, &offset);
1812 /* restore clipping to whole screen */
1813 SDL_SetClipRect(dest, NULL);
1814 return 0;
1817 void
1818 gd_play_game_select_status_bar_colors(GdStatusBarColors *cols, const GdCave *cave)
1820 GdColor (*color_indexer) (int i);
1821 int c64_col;
1823 /* first, count the number of c64 colors the cave uses. */
1824 /* if it uses mostly c64 colors, we will use c64 colors for the status bar. */
1825 /* otherwise we will use gdash colors. */
1826 /* note that the atari original status bar color setting only uses the game colors. */
1827 c64_col=0;
1828 if (gd_color_is_c64(cave->color0)) c64_col++;
1829 if (gd_color_is_c64(cave->color1)) c64_col++;
1830 if (gd_color_is_c64(cave->color2)) c64_col++;
1831 if (gd_color_is_c64(cave->color3)) c64_col++;
1832 if (gd_color_is_c64(cave->color4)) c64_col++;
1833 if (gd_color_is_c64(cave->color5)) c64_col++;
1834 if (c64_col>4)
1835 color_indexer=gd_c64_color;
1836 else
1837 color_indexer=gd_gdash_color;
1839 switch (gd_status_bar_type) {
1840 case GD_STATUS_BAR_ORIGINAL:
1841 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1842 cols->diamond_needed=cols->diamond_collected=color_indexer(GD_COLOR_INDEX_YELLOW);
1843 cols->diamond_value=cols->score=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1844 break;
1845 case GD_STATUS_BAR_1STB:
1846 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1847 cols->diamond_needed=cols->diamond_collected=cols->score=color_indexer(GD_COLOR_INDEX_YELLOW);
1848 cols->diamond_value=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1849 break;
1850 case GD_STATUS_BAR_CRLI:
1851 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1852 cols->diamond_needed=color_indexer(GD_COLOR_INDEX_RED);
1853 cols->diamond_collected=color_indexer(GD_COLOR_INDEX_GREEN);
1854 cols->diamond_value=color_indexer(GD_COLOR_INDEX_CYAN);
1855 cols->score=color_indexer(GD_COLOR_INDEX_YELLOW);
1856 cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1857 break;
1858 case GD_STATUS_BAR_FINAL:
1859 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1860 cols->diamond_needed=color_indexer(GD_COLOR_INDEX_RED);
1861 cols->diamond_collected=color_indexer(GD_COLOR_INDEX_GREEN);
1862 cols->diamond_value=cols->score=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1863 break;
1864 case GD_STATUS_BAR_ATARI_ORIGINAL:
1865 cols->background=cave->color0;
1866 cols->diamond_needed=cols->diamond_collected=cave->color2;
1867 cols->diamond_value=cols->score=cols->default_color=cave->color3;
1868 break;
1869 default:
1870 g_assert_not_reached();
1875 void
1876 gd_clear_header(GdColor c)
1878 SDL_Rect r;
1880 r.x=0;
1881 r.y=0;
1882 r.w=gd_screen->w;
1883 r.h=gd_statusbar_height;
1884 SDL_FillRect(gd_screen, &r, SDL_MapRGB(gd_screen->format, gd_color_get_r(c), gd_color_get_g(c), gd_color_get_b(c)));
1887 void
1888 gd_showheader_uncover(const GdGame *game, const GdStatusBarColors *cols, gboolean show_replay_sign)
1890 int cavename_y;
1891 int len;
1892 char *str;
1893 gboolean first_line;
1895 gd_clear_header(cols->background);
1896 first_line=FALSE; /* will be set to TRUE, if we draw in the next few code lines. so the y coordinate of the second status line can be decided. */
1898 /* if playing a replay, tell the user! */
1899 if (game->type==GD_GAMETYPE_REPLAY && show_replay_sign) {
1900 gd_blittext(gd_screen, -1, gd_statusbar_y1, GD_GDASH_YELLOW, "PLAYING REPLAY");
1901 first_line=TRUE;
1903 else {
1904 /* also inform about intermission, but not if playing a replay. also the replay saver should not show it! f */
1905 if (game->cave->intermission && !game->type==GD_GAMETYPE_REPLAY) {
1906 gd_blittext(gd_screen, -1, gd_statusbar_y1, cols->default_color, "ONE LIFE EXTRA");
1907 first_line=TRUE;
1909 else
1910 /* if not an intermission, we may show the name of the game (caveset) */
1911 if (gd_show_name_of_game) { /* if showing the name of the cave... */
1912 len=g_utf8_strlen(gd_caveset_data->name, -1);
1913 if (gd_screen->w/gd_font_width()/2>=len) /* if have place for double-width font */
1914 gd_blittext(gd_screen, -1, gd_statusbar_y1, cols->default_color, gd_caveset_data->name);
1915 else
1916 gd_blittext_n(gd_screen, -1, gd_statusbar_y1, cols->default_color, gd_caveset_data->name);
1917 first_line=TRUE;
1921 cavename_y=first_line?gd_statusbar_y2:gd_statusbar_mid;
1922 /* "xy players, cave ab/3" */
1923 if (game->type==GD_GAMETYPE_NORMAL)
1924 str=g_strdup_printf("%d%c, %s/%d", game->player_lives, GD_PLAYER_CHAR, game->cave->name, game->cave->rendered);
1925 else
1926 /* if not a normal game, do not show number of remaining lives */
1927 str=g_strdup_printf("%s/%d", game->cave->name, game->cave->rendered);
1928 len=g_utf8_strlen(str, -1);
1929 if (gd_screen->w/gd_font_width()/2>=len) /* if have place for double-width font */
1930 gd_blittext(gd_screen, -1, cavename_y, cols->default_color, str);
1931 else
1932 gd_blittext_n(gd_screen, -1, cavename_y, cols->default_color, str);
1933 g_free(str);
1936 void
1937 gd_showheader_game(const GdGame *game, int timeout_since, const GdStatusBarColors *cols, gboolean show_replay_sign)
1939 int x, y;
1940 gboolean first_line;
1941 first_line=FALSE; /* will be set to TRUE, if we draw in the next few code lines. so the y coordinate of the second status line can be decided. */
1943 gd_clear_header(cols->background);
1945 /* if playing a replay, tell the user! */
1946 if (game->type==GD_GAMETYPE_REPLAY && show_replay_sign) {
1947 gd_blittext(gd_screen, -1, gd_statusbar_y1, GD_GDASH_YELLOW, "PLAYING REPLAY");
1948 first_line=TRUE;
1951 /* y position of status bar */
1952 y=first_line?gd_statusbar_y2:gd_statusbar_mid;
1954 if (game->cave->player_state==GD_PL_TIMEOUT && timeout_since/50%4==0) {
1955 gd_clear_header(cols->background);
1956 gd_blittext(gd_screen, -1, y, GD_GDASH_WHITE, "OUT OF TIME");
1957 return;
1959 if (game->cave->player_state==GD_PL_NOT_YET) {
1960 /* ... if the player is not yet born, we should rather show the uncover bar. */
1961 gd_showheader_uncover(game, cols, show_replay_sign);
1962 return;
1965 if (gd_keystate[SDLK_LSHIFT] || gd_keystate[SDLK_RSHIFT]) {
1966 /* ALTERNATIVE STATUS BAR BY PRESSING SHIFT */
1967 x=10*gd_scale;
1969 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c%02d", GD_PLAYER_CHAR, MIN(game->player_lives, 99)); /* max 99 in %2d */
1970 x+=14*gd_scale;
1971 /* color numbers are not the same as key numbers! c3->k1, c2->k2, c1->k3 */
1972 /* this is how it was implemented in crdr7. */
1973 x=gd_blittext_printf(gd_screen, x, y, game->cave->color3, "%c%1d", GD_KEY_CHAR, MIN(game->cave->key1, 9)); /* max 9 in %1d */
1974 x+=10*gd_scale;
1975 x=gd_blittext_printf(gd_screen, x, y, game->cave->color2, "%c%1d", GD_KEY_CHAR, MIN(game->cave->key2, 9));
1976 x+=10*gd_scale;
1977 x=gd_blittext_printf(gd_screen, x, y, game->cave->color1, "%c%1d", GD_KEY_CHAR, MIN(game->cave->key3, 9));
1978 x+=12*gd_scale;
1979 if (game->cave->gravity_will_change>0) {
1980 int gravity_char;
1982 switch(game->cave->gravity_next_direction) {
1983 case MV_DOWN: gravity_char=GD_DOWN_CHAR; break;
1984 case MV_LEFT: gravity_char=GD_LEFT_CHAR; break;
1985 case MV_RIGHT: gravity_char=GD_RIGHT_CHAR; break;
1986 case MV_UP: gravity_char=GD_UP_CHAR; break;
1987 default: gravity_char='?'; break;
1989 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c%02d", gravity_char, MIN(gd_cave_time_show(game->cave, game->cave->gravity_will_change), 99));
1990 } else {
1991 int gravity_char;
1993 switch(game->cave->gravity) {
1994 case MV_DOWN: gravity_char=GD_DOWN_CHAR; break;
1995 case MV_LEFT: gravity_char=GD_LEFT_CHAR; break;
1996 case MV_RIGHT: gravity_char=GD_RIGHT_CHAR; break;
1997 case MV_UP: gravity_char=GD_UP_CHAR; break;
1998 default: gravity_char='?'; break;
2000 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c%02d", gravity_char, 0);
2002 x+=10*gd_scale;
2003 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_collected, "%c%02d", GD_SKELETON_CHAR, MIN(game->cave->skeletons_collected, 99));
2004 } else {
2005 /* NORMAL STATUS BAR */
2006 x=1*gd_scale;
2007 int time_secs;
2009 /* cave time is rounded _UP_ to seconds. so at the exact moment when it changes from
2010 2sec remaining to 1sec remaining, the player has exactly one second. when it changes
2011 to zero, it is the exact moment of timeout. */
2012 time_secs=gd_cave_time_show(game->cave, game->cave->time);
2014 if (gd_keystate[SDLK_f]) {
2015 /* fast forward mode - show "FAST" */
2016 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%cFAST%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
2017 } else {
2018 /* normal speed mode - show diamonds NEEDED <> VALUE */
2019 /* or if collected enough diamonds, <><><> VALUE */
2020 if (game->cave->diamonds_needed>game->cave->diamonds_collected) {
2021 if (game->cave->diamonds_needed>0)
2022 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_needed, "%03d", game->cave->diamonds_needed);
2023 else
2024 /* did not already count diamonds needed */
2025 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_needed, "%c%c%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
2027 else
2028 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, " %c%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
2029 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
2030 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_value, "%02d", game->cave->diamond_value);
2032 x+=10*gd_scale;
2033 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_collected, "%03d", game->cave->diamonds_collected);
2034 x+=11*gd_scale;
2035 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%03d", time_secs);
2036 x+=10*gd_scale;
2037 x=gd_blittext_printf(gd_screen, x, y, cols->score, "%06d", game->player_score);
2055 /* the dark gray background */
2056 /* to be called at application startup */
2057 void
2058 gd_create_dark_background()
2060 SDL_Surface *tile, *dark_tile, *dark_screen_tiled;
2061 int x, y;
2063 g_assert(gd_screen!=NULL);
2065 if (dark_background)
2066 SDL_FreeSurface(dark_background);
2068 tile=surface_from_raw_data(gdash_tile, sizeof(gdash_tile));
2069 /* 24bpp, as it has no alpha channel */
2070 dark_tile=SDL_CreateRGBSurface(0, tile->w, tile->h, 24, 0, 0, 0, 0);
2071 SDL_BlitSurface(tile, NULL, dark_tile, NULL);
2072 SDL_FreeSurface(tile);
2073 SDL_SetAlpha(dark_tile, SDL_SRCALPHA, 256/6); /* 1/6 opacity */
2075 /* create the image, and fill it with the tile. */
2076 /* the image is screen size / gd_scale, so we prefer the original screen size here */
2077 /* and only do the scaling later! */
2078 dark_screen_tiled=SDL_CreateRGBSurface(0, gd_screen->w/gd_scale, gd_screen->h/gd_scale, 32, rmask, gmask, bmask, amask);
2079 for (y=0; y<dark_screen_tiled->h; y+=dark_tile->h)
2080 for (x=0; x<dark_screen_tiled->w; x+=dark_tile->w) {
2081 SDL_Rect rect;
2083 rect.x=x;
2084 rect.y=y;
2086 SDL_BlitSurface(dark_tile, NULL, dark_screen_tiled, &rect);
2088 SDL_FreeSurface(dark_tile);
2089 dark_background=displayformat(dark_screen_tiled);
2090 SDL_FreeSurface(dark_screen_tiled);
2093 void
2094 gd_dark_screen()
2096 SDL_BlitSurface(dark_background, NULL, gd_screen, NULL);
2101 * screen backup and restore functions. these are used by menus, help screens
2102 * and the like. backups and restores can be nested.
2105 /* backups the current screen contents, and darkens it. */
2106 /* later, gd_restore_screen can be called. */
2107 void
2108 gd_backup_and_dark_screen()
2110 SDL_Surface *backup_screen, *dark_screen;
2112 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
2113 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
2115 dark_screen=SDL_DisplayFormat(dark_background);
2117 SDL_BlitSurface(dark_screen, NULL, gd_screen, NULL);
2119 backup_screens=g_list_prepend(backup_screens, backup_screen);
2120 dark_screens=g_list_prepend(dark_screens, dark_screen);
2123 /* backups the current screen contents, and clears it. */
2124 /* later, gd_restore_screen can be called. */
2125 void
2126 gd_backup_and_black_screen()
2128 SDL_Surface *backup_screen;
2130 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
2131 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
2133 backup_screens=g_list_prepend(backup_screens, backup_screen);
2134 dark_screens=g_list_prepend(dark_screens, NULL);
2137 /* restores a backed up screen. */
2138 void
2139 gd_restore_screen()
2141 SDL_Surface *backup_screen, *dark_screen;
2143 /* check if lists are non-empty */
2144 g_assert(backup_screens!=NULL);
2145 g_assert(dark_screens!=NULL);
2147 backup_screen=backup_screens->data;
2148 backup_screens=g_list_delete_link(backup_screens, backup_screens); /* remove first */
2149 dark_screen=dark_screens->data;
2150 dark_screens=g_list_delete_link(dark_screens, dark_screens); /* remove first */
2152 SDL_BlitSurface(backup_screen, 0, gd_screen, 0);
2153 SDL_Flip(gd_screen);
2154 SDL_FreeSurface(backup_screen);
2155 if (dark_screen)
2156 SDL_FreeSurface(dark_screen);