20090122
[gdash.git] / src / sdlgfx.c
blob0ed72188839808ae9744c269b2ccb4014a095185
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 | (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 (x1==-1)
1462 x1=screen->w/2 - (font[0]->w*g_utf8_strlen(text, -1))/2;
1464 x=x1;
1465 c=g_utf8_get_char(p);
1466 while (c!=0) {
1467 if (c=='\n') {
1468 /* if it is an enter */
1469 if (x==x1)
1470 y+=font[0]->h/2;
1471 else
1472 y+=font[0]->h;
1473 x=x1;
1474 } else {
1475 if (c<NUM_OF_CHARS)
1476 i=c;
1477 else
1478 i=127; /* unknown char */
1480 destrect.x=x;
1481 destrect.y=y;
1482 SDL_BlitSurface(font[i], NULL, screen, &destrect);
1484 x+=font[i]->w;
1487 p=g_utf8_next_char(p); /* next character */
1488 c=g_utf8_get_char(p);
1491 return x;
1494 /* write something to the screen, with wide characters. */
1495 /* x=-1 -> center horizontally */
1497 gd_blittext(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1499 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1500 renderfont_color(color);
1501 return blittext_font(screen, g_hash_table_lookup(font_w, GUINT_TO_POINTER(color)), x, y, text);
1504 /* write something to the screen, with normal characters. */
1505 /* x=-1 -> center horizontally */
1507 gd_blittext_n(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1509 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1510 renderfont_color(color);
1511 return blittext_font(screen, g_hash_table_lookup(font_n, GUINT_TO_POINTER(color)), x, y, text);
1514 /* write something to the screen, with wide characters. */
1515 /* x=-1 -> center horizontally */
1517 gd_blittext_printf(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1519 va_list args;
1520 char *text;
1522 va_start(args, format);
1523 text=g_strdup_vprintf(format, args);
1524 x=gd_blittext(screen, x, y, color, text);
1525 g_free(text);
1526 va_end(args);
1528 return x;
1531 /* write something to the screen, with normal characters. */
1532 /* x=-1 -> center horizontally */
1534 gd_blittext_printf_n(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1536 va_list args;
1537 char *text;
1539 va_start(args, format);
1540 text=g_strdup_vprintf(format, args);
1541 x=gd_blittext_n(screen, x, y, color, text);
1542 g_free(text);
1543 va_end(args);
1545 return x;
1550 void
1551 gd_select_pixbuf_colors(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
1553 /* if non-c64 gfx, nothing to do */
1554 if (using_png_gfx)
1555 return;
1557 /* convert to rgb value */
1558 c0=gd_color_get_rgb(c0);
1559 c1=gd_color_get_rgb(c1);
1560 c2=gd_color_get_rgb(c2);
1561 c3=gd_color_get_rgb(c3);
1562 c4=gd_color_get_rgb(c4);
1563 c5=gd_color_get_rgb(c5);
1565 /* and compare rgb values! */
1566 if (c0!=color0 || c1!=color1 || c2!=color2 || c3!=color3 || c4!=color4 || c5!=color5) {
1567 /* if not the same colors as requested before */
1568 color0=c0;
1569 color1=c1;
1570 color2=c2;
1571 color3=c3;
1572 color4=c4;
1573 color5=c5;
1575 loadcells_from_c64_data(c0, c1, c2, c3, c4, c5);
1588 logical_size: logical pixel size of playfield, usually larger than the screen.
1589 physical_size: visible part. (remember: player_x-x1!)
1591 center: the coordinates to scroll to.
1592 exact: scroll exactly
1593 start: start scrolling if difference is larger than
1594 to: scroll to, if started, until difference is smaller than
1595 current
1597 desired: the function stores its data here
1598 speed: the function stores its data here
1600 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
1602 static gboolean
1603 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)
1605 int i;
1606 gboolean changed;
1607 int max;
1609 max=logical_size-physical_size;
1610 if (max<0)
1611 max=0;
1613 changed=FALSE;
1615 /* if cave size smaller than the screen, no scrolling req'd */
1616 if (logical_size<physical_size) {
1617 *desired=0;
1618 if (*current!=0) {
1619 *current=0;
1620 changed=TRUE;
1623 return changed;
1626 /* if cave size is only a slightly larger than the screen, also no scrolling */
1627 if (logical_size<=physical_size+cell_size) {
1628 *desired=max/2; /* scroll to the middle of the cell */
1629 } else {
1630 /* hystheresis function.
1631 * when scrolling left, always go a bit less left than player being at the middle.
1632 * when scrolling right, always go a bit less to the right. */
1633 if (exact)
1634 *desired=center;
1635 else {
1636 if (*current+start<center)
1637 *desired=center-to;
1638 if (*current-start>center)
1639 *desired=center+to;
1642 *desired=gd_clamp(*desired, 0, max);
1644 if (*current<*desired) {
1645 for (i=0; i<speed; i++)
1646 if (*current<*desired)
1647 (*current)++;
1648 changed=TRUE;
1650 if (*current > *desired) {
1651 for (i=0; i<speed; i++)
1652 if (*current>*desired)
1653 (*current)--;
1654 changed=TRUE;
1657 return changed;
1661 /* just set current viewport to upper left. */
1662 void
1663 gd_scroll_to_origin()
1665 scroll_x=0;
1666 scroll_y=0;
1670 /* SCROLLING
1672 * scrolls to the player during game play.
1673 * called by drawcave
1674 * returns true, if player is not visible-ie it is out of the visible size in the drawing area.
1676 gboolean
1677 gd_scroll(GdGame *game, gboolean exact_scroll)
1679 static int scroll_desired_x=0, scroll_desired_y=0;
1680 gboolean out_of_window;
1681 int player_x, player_y, visible_x, visible_y;
1682 gboolean changed;
1683 int scroll_divisor;
1684 /* max scrolling speed depends on the speed of the cave. */
1685 /* game moves cell_size_game* 1s/cave time pixels in a second. */
1686 /* scrolling moves scroll speed * 1s/scroll_time in a second. */
1687 /* these should be almost equal; scrolling speed a little slower. */
1688 /* that way, the player might reach the border with a small probability, */
1689 /* but the scrolling will not "oscillate", ie. turn on for little intervals as it has */
1690 /* caught up with the desired position. smaller is better. */
1691 int scroll_speed=cell_size*(gd_fine_scroll?20:40)/game->cave->speed;
1693 player_x=game->cave->player_x-game->cave->x1; /* cell coordinates of player */
1694 player_y=game->cave->player_y-game->cave->y1;
1695 visible_x=(game->cave->x2-game->cave->x1+1)*cell_size; /* pixel size of visible part of the cave (may be smaller in intermissions) */
1696 visible_y=(game->cave->y2-game->cave->y1+1)*cell_size;
1698 /* cell_size contains the scaled size, but we need the original. */
1699 changed=FALSE;
1700 /* some sort of scrolling speed. with larger cells, the divisor must be smaller, so the scrolling faster. */
1701 scroll_divisor=256/cell_size;
1702 if (gd_fine_scroll)
1703 scroll_divisor*=2; /* as fine scrolling is 50hz, whereas normal is 25hz only */
1705 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))
1706 changed=TRUE;
1707 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))
1708 changed=TRUE;
1710 /* if scrolling, we should update entire gd_screen. */
1711 if (changed) {
1712 int x, y;
1714 for (y=0; y<game->cave->h; y++)
1715 for (x=0; x<game->cave->w; x++)
1716 game->gfx_buffer[y][x] |= GD_REDRAW;
1719 /* check if active player is visible at the moment. */
1720 out_of_window=FALSE;
1721 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
1722 if ((player_x*cell_size)<scroll_x || (player_x*cell_size+cell_size-1)>scroll_x+play_area_w)
1723 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1724 if (game->cave->player_x>=game->cave->x1 && game->cave->player_x<=game->cave->x2)
1725 out_of_window=TRUE;
1726 if ((player_y*cell_size)<scroll_y || (player_y*cell_size+cell_size-1)>scroll_y+play_area_h)
1727 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1728 if (game->cave->player_y>=game->cave->y1 && game->cave->player_y<=game->cave->y2)
1729 out_of_window=TRUE;
1731 /* 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. */
1732 if (game->cave->player_state==GD_PL_NOT_YET)
1733 return FALSE;
1734 return out_of_window;
1741 gd_drawcave(SDL_Surface *dest, GdGame *game)
1743 int x, y, xd, yd;
1744 SDL_Rect cliprect;
1745 int scroll_y_aligned;
1747 /* on-screen clipping rectangle */
1748 cliprect.x=0;
1749 cliprect.y=gd_statusbar_height;
1750 cliprect.w=play_area_w;
1751 cliprect.h=play_area_h;
1752 SDL_SetClipRect(dest, &cliprect);
1754 if (gd_sdl_pal_emulation && gd_even_line_pal_emu_vertical_scroll)
1755 scroll_y_aligned=scroll_y/2*2; /* make it even (dividable by two) */
1756 else
1757 scroll_y_aligned=scroll_y;
1759 /* here we draw all cells to be redrawn. we do not take scrolling area into consideration - sdl will do the clipping. */
1760 for (y=game->cave->y1, yd=0; y<=game->cave->y2; y++, yd++) {
1761 for (x=game->cave->x1, xd=0; x<=game->cave->x2; x++, xd++) {
1762 if (game->gfx_buffer[y][x] & GD_REDRAW) { /* if it needs to be redrawn */
1763 SDL_Rect offset;
1765 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) */
1766 offset.x=x*cell_size-scroll_x;
1768 game->gfx_buffer[y][x]=game->gfx_buffer[y][x] & ~GD_REDRAW; /* now we have drawn it */
1770 SDL_BlitSurface(cells[game->gfx_buffer[y][x]], NULL, dest, &offset);
1775 /* restore clipping to whole screen */
1776 SDL_SetClipRect(dest, NULL);
1777 return 0;
1780 void
1781 gd_play_game_select_status_bar_colors(GdStatusBarColors *cols, const GdCave *cave)
1783 GdColor (*color_indexer) (int i);
1784 int c64_col;
1786 /* first, count the number of c64 colors the cave uses. */
1787 /* if it uses mostly c64 colors, we will use c64 colors for the status bar. */
1788 /* otherwise we will use gdash colors. */
1789 /* note that the atari original status bar color setting only uses the game colors. */
1790 c64_col=0;
1791 if (gd_color_is_c64(cave->color0)) c64_col++;
1792 if (gd_color_is_c64(cave->color1)) c64_col++;
1793 if (gd_color_is_c64(cave->color2)) c64_col++;
1794 if (gd_color_is_c64(cave->color3)) c64_col++;
1795 if (gd_color_is_c64(cave->color4)) c64_col++;
1796 if (gd_color_is_c64(cave->color5)) c64_col++;
1797 if (c64_col>4)
1798 color_indexer=gd_c64_color;
1799 else
1800 color_indexer=gd_gdash_color;
1802 switch (gd_status_bar_type) {
1803 case GD_STATUS_BAR_ORIGINAL:
1804 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1805 cols->diamond_needed=cols->diamond_collected=color_indexer(GD_COLOR_INDEX_YELLOW);
1806 cols->diamond_value=cols->score=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1807 break;
1808 case GD_STATUS_BAR_1STB:
1809 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1810 cols->diamond_needed=cols->diamond_collected=cols->score=color_indexer(GD_COLOR_INDEX_YELLOW);
1811 cols->diamond_value=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1812 break;
1813 case GD_STATUS_BAR_CRLI:
1814 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1815 cols->diamond_needed=color_indexer(GD_COLOR_INDEX_RED);
1816 cols->diamond_collected=color_indexer(GD_COLOR_INDEX_GREEN);
1817 cols->diamond_value=color_indexer(GD_COLOR_INDEX_CYAN);
1818 cols->score=color_indexer(GD_COLOR_INDEX_YELLOW);
1819 cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1820 break;
1821 case GD_STATUS_BAR_FINAL:
1822 cols->background=color_indexer(GD_COLOR_INDEX_BLACK);
1823 cols->diamond_needed=color_indexer(GD_COLOR_INDEX_RED);
1824 cols->diamond_collected=color_indexer(GD_COLOR_INDEX_GREEN);
1825 cols->diamond_value=cols->score=cols->default_color=color_indexer(GD_COLOR_INDEX_WHITE);
1826 break;
1827 case GD_STATUS_BAR_ATARI_ORIGINAL:
1828 cols->background=cave->color0;
1829 cols->diamond_needed=cols->diamond_collected=cave->color2;
1830 cols->diamond_value=cols->score=cols->default_color=cave->color3;
1831 break;
1832 default:
1833 g_assert_not_reached();
1838 void
1839 gd_clear_header(GdColor c)
1841 SDL_Rect r;
1843 r.x=0;
1844 r.y=0;
1845 r.w=gd_screen->w;
1846 r.h=gd_statusbar_height;
1847 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)));
1850 void
1851 gd_showheader_uncover(const GdGame *game, const GdStatusBarColors *cols, gboolean show_replay_sign)
1853 int cavename_y;
1854 int len;
1855 char *str;
1856 gboolean first_line;
1858 gd_clear_header(cols->background);
1859 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. */
1861 /* if playing a replay, tell the user! */
1862 if (game->type==GD_GAMETYPE_REPLAY && show_replay_sign) {
1863 gd_blittext(gd_screen, -1, gd_statusbar_y1, GD_GDASH_YELLOW, "PLAYING REPLAY");
1864 first_line=TRUE;
1866 else {
1867 /* also inform about intermission, but not if playing a replay. also the replay saver should not show it! f */
1868 if (game->cave->intermission && !game->type==GD_GAMETYPE_REPLAY) {
1869 gd_blittext(gd_screen, -1, gd_statusbar_y1, cols->default_color, "ONE LIFE EXTRA");
1870 first_line=TRUE;
1872 else
1873 /* if not an intermission, we may show the name of the game (caveset) */
1874 if (gd_show_name_of_game) { /* if showing the name of the cave... */
1875 len=g_utf8_strlen(gd_caveset_data->name, -1);
1876 if (gd_screen->w/gd_font_width()/2>=len) /* if have place for double-width font */
1877 gd_blittext(gd_screen, -1, gd_statusbar_y1, cols->default_color, gd_caveset_data->name);
1878 else
1879 gd_blittext_n(gd_screen, -1, gd_statusbar_y1, cols->default_color, gd_caveset_data->name);
1880 first_line=TRUE;
1884 cavename_y=first_line?gd_statusbar_y2:gd_statusbar_mid;
1885 /* "xy players, cave ab/3" */
1886 if (game->type==GD_GAMETYPE_NORMAL)
1887 str=g_strdup_printf("%d%c, %s/%d", game->player_lives, GD_PLAYER_CHAR, game->cave->name, game->cave->rendered);
1888 else
1889 /* if not a normal game, do not show number of remaining lives */
1890 str=g_strdup_printf("%s/%d", game->cave->name, game->cave->rendered);
1891 len=g_utf8_strlen(str, -1);
1892 if (gd_screen->w/gd_font_width()/2>=len) /* if have place for double-width font */
1893 gd_blittext(gd_screen, -1, cavename_y, cols->default_color, str);
1894 else
1895 gd_blittext_n(gd_screen, -1, cavename_y, cols->default_color, str);
1896 g_free(str);
1899 void
1900 gd_showheader_game(const GdGame *game, int timeout_since, const GdStatusBarColors *cols, gboolean show_replay_sign)
1902 int x, y;
1903 gboolean first_line;
1904 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. */
1906 gd_clear_header(cols->background);
1908 /* if playing a replay, tell the user! */
1909 if (game->type==GD_GAMETYPE_REPLAY && show_replay_sign) {
1910 gd_blittext(gd_screen, -1, gd_statusbar_y1, GD_GDASH_YELLOW, "PLAYING REPLAY");
1911 first_line=TRUE;
1914 /* y position of status bar */
1915 y=first_line?gd_statusbar_y2:gd_statusbar_mid;
1917 if (game->cave->player_state==GD_PL_TIMEOUT && timeout_since/50%4==0) {
1918 gd_clear_header(cols->background);
1919 gd_blittext(gd_screen, -1, y, GD_GDASH_WHITE, "OUT OF TIME");
1920 return;
1922 if (game->cave->player_state==GD_PL_NOT_YET) {
1923 /* ... if the player is not yet born, we should rather show the uncover bar. */
1924 gd_showheader_uncover(game, cols, show_replay_sign);
1925 return;
1928 if (gd_keystate[SDLK_LSHIFT] || gd_keystate[SDLK_RSHIFT]) {
1929 /* ALTERNATIVE STATUS BAR BY PRESSING SHIFT */
1930 x=10*gd_scale;
1932 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 */
1933 x+=14*gd_scale;
1934 /* color numbers are not the same as key numbers! c3->k1, c2->k2, c1->k3 */
1935 /* this is how it was implemented in crdr7. */
1936 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 */
1937 x+=10*gd_scale;
1938 x=gd_blittext_printf(gd_screen, x, y, game->cave->color2, "%c%1d", GD_KEY_CHAR, MIN(game->cave->key2, 9));
1939 x+=10*gd_scale;
1940 x=gd_blittext_printf(gd_screen, x, y, game->cave->color1, "%c%1d", GD_KEY_CHAR, MIN(game->cave->key3, 9));
1941 x+=12*gd_scale;
1942 if (game->cave->gravity_will_change>0) {
1943 int gravity_char;
1945 switch(game->cave->gravity_next_direction) {
1946 case MV_DOWN: gravity_char=GD_DOWN_CHAR; break;
1947 case MV_LEFT: gravity_char=GD_LEFT_CHAR; break;
1948 case MV_RIGHT: gravity_char=GD_RIGHT_CHAR; break;
1949 case MV_UP: gravity_char=GD_UP_CHAR; break;
1950 default: gravity_char='?'; break;
1952 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));
1953 } else {
1954 int gravity_char;
1956 switch(game->cave->gravity) {
1957 case MV_DOWN: gravity_char=GD_DOWN_CHAR; break;
1958 case MV_LEFT: gravity_char=GD_LEFT_CHAR; break;
1959 case MV_RIGHT: gravity_char=GD_RIGHT_CHAR; break;
1960 case MV_UP: gravity_char=GD_UP_CHAR; break;
1961 default: gravity_char='?'; break;
1963 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c%02d", gravity_char, 0);
1965 x+=10*gd_scale;
1966 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_collected, "%c%02d", GD_SKELETON_CHAR, MIN(game->cave->skeletons_collected, 99));
1967 } else {
1968 /* NORMAL STATUS BAR */
1969 x=1*gd_scale;
1970 int time_secs;
1972 /* cave time is rounded _UP_ to seconds. so at the exact moment when it changes from
1973 2sec remaining to 1sec remaining, the player has exactly one second. when it changes
1974 to zero, it is the exact moment of timeout. */
1975 time_secs=gd_cave_time_show(game->cave, game->cave->time);
1977 if (gd_keystate[SDLK_f]) {
1978 /* fast forward mode - show "FAST" */
1979 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%cFAST%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
1980 } else {
1981 /* normal speed mode - show diamonds NEEDED <> VALUE */
1982 /* or if collected enough diamonds, <><><> VALUE */
1983 if (game->cave->diamonds_needed>game->cave->diamonds_collected) {
1984 if (game->cave->diamonds_needed>0)
1985 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_needed, "%03d", game->cave->diamonds_needed);
1986 else
1987 /* did not already count diamonds needed */
1988 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_needed, "%c%c%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
1990 else
1991 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, " %c%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
1992 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%c", GD_DIAMOND_CHAR, GD_DIAMOND_CHAR);
1993 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_value, "%02d", game->cave->diamond_value);
1995 x+=10*gd_scale;
1996 x=gd_blittext_printf(gd_screen, x, y, cols->diamond_collected, "%03d", game->cave->diamonds_collected);
1997 x+=11*gd_scale;
1998 x=gd_blittext_printf(gd_screen, x, y, cols->default_color, "%03d", time_secs);
1999 x+=10*gd_scale;
2000 x=gd_blittext_printf(gd_screen, x, y, cols->score, "%06d", game->player_score);
2018 /* the dark gray background */
2019 /* to be called at application startup */
2020 void
2021 gd_create_dark_background()
2023 SDL_Surface *tile, *dark_tile, *dark_screen_tiled;
2024 int x, y;
2026 g_assert(gd_screen!=NULL);
2028 if (dark_background)
2029 SDL_FreeSurface(dark_background);
2031 tile=surface_from_raw_data(gdash_tile, sizeof(gdash_tile));
2032 /* 24bpp, as it has no alpha channel */
2033 dark_tile=SDL_CreateRGBSurface(0, tile->w, tile->h, 24, 0, 0, 0, 0);
2034 SDL_BlitSurface(tile, NULL, dark_tile, NULL);
2035 SDL_FreeSurface(tile);
2036 SDL_SetAlpha(dark_tile, SDL_SRCALPHA, 256/6); /* 1/6 opacity */
2038 /* create the image, and fill it with the tile. */
2039 /* the image is screen size / gd_scale, so we prefer the original screen size here */
2040 /* and only do the scaling later! */
2041 dark_screen_tiled=SDL_CreateRGBSurface(0, gd_screen->w/gd_scale, gd_screen->h/gd_scale, 32, rmask, gmask, bmask, amask);
2042 for (y=0; y<dark_screen_tiled->h; y+=dark_tile->h)
2043 for (x=0; x<dark_screen_tiled->w; x+=dark_tile->w) {
2044 SDL_Rect rect;
2046 rect.x=x;
2047 rect.y=y;
2049 SDL_BlitSurface(dark_tile, NULL, dark_screen_tiled, &rect);
2051 SDL_FreeSurface(dark_tile);
2052 dark_background=displayformat(dark_screen_tiled);
2053 SDL_FreeSurface(dark_screen_tiled);
2056 void
2057 gd_dark_screen()
2059 SDL_BlitSurface(dark_background, NULL, gd_screen, NULL);
2064 * screen backup and restore functions. these are used by menus, help screens
2065 * and the like. backups and restores can be nested.
2068 /* backups the current screen contents, and darkens it. */
2069 /* later, gd_restore_screen can be called. */
2070 void
2071 gd_backup_and_dark_screen()
2073 SDL_Surface *backup_screen, *dark_screen;
2075 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
2076 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
2078 dark_screen=SDL_DisplayFormat(dark_background);
2080 SDL_BlitSurface(dark_screen, NULL, gd_screen, NULL);
2082 backup_screens=g_list_prepend(backup_screens, backup_screen);
2083 dark_screens=g_list_prepend(dark_screens, dark_screen);
2086 /* backups the current screen contents, and clears it. */
2087 /* later, gd_restore_screen can be called. */
2088 void
2089 gd_backup_and_black_screen()
2091 SDL_Surface *backup_screen;
2093 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
2094 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
2096 backup_screens=g_list_prepend(backup_screens, backup_screen);
2097 dark_screens=g_list_prepend(dark_screens, NULL);
2100 /* restores a backed up screen. */
2101 void
2102 gd_restore_screen()
2104 SDL_Surface *backup_screen, *dark_screen;
2106 /* check if lists are non-empty */
2107 g_assert(backup_screens!=NULL);
2108 g_assert(dark_screens!=NULL);
2110 backup_screen=backup_screens->data;
2111 backup_screens=g_list_delete_link(backup_screens, backup_screens); /* remove first */
2112 dark_screen=dark_screens->data;
2113 dark_screens=g_list_delete_link(dark_screens, dark_screens); /* remove first */
2115 SDL_BlitSurface(backup_screen, 0, gd_screen, 0);
2116 SDL_Flip(gd_screen);
2117 SDL_FreeSurface(backup_screen);
2118 if (dark_screen)
2119 SDL_FreeSurface(dark_screen);