20081227
[gdash.git] / src / sdlgfx.c
blobcdf739ea69f0216327e66216bfdd3937e94dbf59
1 /*
2 * Copyright (c) 2007, 2008 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 <glib.h>
18 #include "colors.h"
19 #include "gameplay.h"
20 #include "cave.h"
21 #include "settings.h"
22 #include "sdlgfx.h"
23 #include "util.h"
24 #include "c64_gfx.h" /* char c64_gfx[] with (almost) original graphics */
25 #include "title.h"
26 #include "c64_font.h"
27 #include "gfxutil.h"
29 #include "c64_png_colors.h"
31 #define GD_NUM_OF_CHARS 128
33 /* these can't be larger than 127, or they mess up with utf8 coding */
34 #define UNKNOWN_CHAR_INDEX 31
35 #define BACKSLASH_CHAR_INDEX 64
36 #define TILDE_CHAR_INDEX 95
37 #define UNDERSCORE_CHAR_INDEX 100
38 #define OPEN_BRACKET_CHAR_INDEX 27
39 #define CLOSE_BRACKET_CHAR_INDEX 29
40 #define CHECKED_BOX_CHAR_INDEX 121
41 #define CHECK_MARK_INDEX 122
42 #define BALL_CHAR_INDEX 104
44 int gd_scale=1; /* a graphics scale things which sets EVERYTHING. it is set with gd_sdl_init, and cannot be modified later. */
45 int gd_scale_type=GD_SCALING_ORIGINAL;
47 static SDL_Surface *cells[2*NUM_OF_CELLS];
48 static const guchar *font;
49 static GHashTable *font_w, *font_n;
50 static GdColor color0, color1, color2, color3, color4, color5; /* currently used cell colors */
51 static guint8 *c64_custom_gfx=NULL;
52 static gboolean using_png_gfx;
54 /* these masks are always for RGB and RGBA ordering in memory.
55 that is what gdk-pixbuf uses, and also what sdl uses.
56 no BGR, no ABGR.
58 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
59 static guint32 rmask = 0xff000000;
60 static guint32 gmask = 0x00ff0000;
61 static guint32 bmask = 0x0000ff00;
62 static guint32 amask = 0x000000ff;
64 /* for non-alpha channel gdk pixbuf includes */
65 static guint32 rmask_24 = 0xff0000;
66 static guint32 gmask_24 = 0x00ff00;
67 static guint32 bmask_24 = 0x0000ff;
68 #else
69 static guint32 rmask = 0x000000ff;
70 static guint32 gmask = 0x0000ff00;
71 static guint32 bmask = 0x00ff0000;
72 static guint32 amask = 0xff000000;
74 /* for non-alpha channel gdk pixbuf includes */
75 static guint32 rmask_24 = 0x0000ff;
76 static guint32 gmask_24 = 0x00ff00;
77 static guint32 bmask_24 = 0xff0000;
78 #endif
81 /* screen area */
82 SDL_Surface *gd_screen=NULL;
83 static SDL_Surface *dark_background=NULL;
84 static GList *backup_screens=NULL, *dark_screens=NULL;
85 int play_area_w=320;
86 int play_area_h=180;
87 int statusbar_height=20;
88 int statusbar_y1=1;
89 int statusbar_y2=10;
90 int statusbar_mid=(20-8)/2;
91 static int scroll_x, scroll_y;
94 /* quit, global variable which is set to true if the application should quit */
95 gboolean gd_quit=FALSE;
97 guint8 *gd_keystate;
98 SDL_Joystick *gd_joy;
100 static int cell_size=16;
108 /* return name of key. names taken from sdl documentation */
110 w3m -dump file:///usr/share/doc/libsdl1.2-dev/docs/html/sdlkey.html |grep -v "──" |
111 cut -c 1-19,38- |sed "s/│$//;s/ *$//g;s/│/case /;s/ *│/: return \"/;s/$/\";/"|tee ~/keys.txt
113 const char *
114 gd_key_name(guint keysym)
116 static char keyname[30];
117 switch(keysym) {
118 case SDLK_BACKSPACE: return "BACKSPACE";
119 case SDLK_TAB: return "TAB";
120 case SDLK_CLEAR: return "CLEAR";
121 case SDLK_RETURN: return "RETURN";
122 case SDLK_PAUSE: return "PAUSE";
123 case SDLK_ESCAPE: return "ESCAPE";
124 case SDLK_SPACE: return "SPACE";
125 case SDLK_EXCLAIM: return "EXCLAIM";
126 case SDLK_QUOTEDBL: return "QUOTEDBL";
127 case SDLK_HASH: return "HASH";
128 case SDLK_DOLLAR: return "DOLLAR";
129 case SDLK_AMPERSAND: return "AMPERSAND";
130 case SDLK_QUOTE: return "QUOTE";
131 case SDLK_LEFTPAREN: return "LEFT PARENTHESIS";
132 case SDLK_RIGHTPAREN: return "RIGHT PARENTHESIS";
133 case SDLK_ASTERISK: return "ASTERISK";
134 case SDLK_PLUS: return "PLUS SIGN";
135 case SDLK_COMMA: return "COMMA";
136 case SDLK_MINUS: return "MINUS SIGN";
137 case SDLK_PERIOD: return "PERIOD";
138 case SDLK_SLASH: return "FORWARD SLASH";
139 case SDLK_0: return "0";
140 case SDLK_1: return "1";
141 case SDLK_2: return "2";
142 case SDLK_3: return "3";
143 case SDLK_4: return "4";
144 case SDLK_5: return "5";
145 case SDLK_6: return "6";
146 case SDLK_7: return "7";
147 case SDLK_8: return "8";
148 case SDLK_9: return "9";
149 case SDLK_COLON: return "COLON";
150 case SDLK_SEMICOLON: return "SEMICOLON";
151 case SDLK_LESS: return "LESS-THAN SIGN";
152 case SDLK_EQUALS: return "EQUALS SIGN";
153 case SDLK_GREATER: return "GREATER-THAN SIGN";
154 case SDLK_QUESTION: return "QUESTION MARK";
155 case SDLK_AT: return "AT";
156 case SDLK_LEFTBRACKET: return "LEFT BRACKET";
157 case SDLK_BACKSLASH: return "BACKSLASH";
158 case SDLK_RIGHTBRACKET: return "RIGHT BRACKET";
159 case SDLK_CARET: return "CARET";
160 case SDLK_UNDERSCORE: return "UNDERSCORE";
161 case SDLK_BACKQUOTE: return "GRAVE";
162 case SDLK_a: return "A";
163 case SDLK_b: return "B";
164 case SDLK_c: return "C";
165 case SDLK_d: return "D";
166 case SDLK_e: return "E";
167 case SDLK_f: return "F";
168 case SDLK_g: return "G";
169 case SDLK_h: return "H";
170 case SDLK_i: return "I";
171 case SDLK_j: return "J";
172 case SDLK_k: return "K";
173 case SDLK_l: return "L";
174 case SDLK_m: return "M";
175 case SDLK_n: return "N";
176 case SDLK_o: return "O";
177 case SDLK_p: return "P";
178 case SDLK_q: return "Q";
179 case SDLK_r: return "R";
180 case SDLK_s: return "S";
181 case SDLK_t: return "T";
182 case SDLK_u: return "U";
183 case SDLK_v: return "V";
184 case SDLK_w: return "W";
185 case SDLK_x: return "X";
186 case SDLK_y: return "Y";
187 case SDLK_z: return "Z";
188 case SDLK_DELETE: return "DELETE";
189 case SDLK_KP0: return "KEYPAD 0";
190 case SDLK_KP1: return "KEYPAD 1";
191 case SDLK_KP2: return "KEYPAD 2";
192 case SDLK_KP3: return "KEYPAD 3";
193 case SDLK_KP4: return "KEYPAD 4";
194 case SDLK_KP5: return "KEYPAD 5";
195 case SDLK_KP6: return "KEYPAD 6";
196 case SDLK_KP7: return "KEYPAD 7";
197 case SDLK_KP8: return "KEYPAD 8";
198 case SDLK_KP9: return "KEYPAD 9";
199 case SDLK_KP_PERIOD: return "KEYPAD PERIOD";
200 case SDLK_KP_DIVIDE: return "KEYPAD DIVIDE";
201 case SDLK_KP_MULTIPLY: return "KEYPAD MULTIPLY";
202 case SDLK_KP_MINUS: return "KEYPAD MINUS";
203 case SDLK_KP_PLUS: return "KEYPAD PLUS";
204 case SDLK_KP_ENTER: return "KEYPAD ENTER";
205 case SDLK_KP_EQUALS: return "KEYPAD EQUALS";
206 case SDLK_UP: return "UP ARROW";
207 case SDLK_DOWN: return "DOWN ARROW";
208 case SDLK_RIGHT: return "RIGHT ARROW";
209 case SDLK_LEFT: return "LEFT ARROW";
210 case SDLK_INSERT: return "INSERT";
211 case SDLK_HOME: return "HOME";
212 case SDLK_END: return "END";
213 case SDLK_PAGEUP: return "PAGE UP";
214 case SDLK_PAGEDOWN: return "PAGE DOWN";
215 case SDLK_F1: return "F1";
216 case SDLK_F2: return "F2";
217 case SDLK_F3: return "F3";
218 case SDLK_F4: return "F4";
219 case SDLK_F5: return "F5";
220 case SDLK_F6: return "F6";
221 case SDLK_F7: return "F7";
222 case SDLK_F8: return "F8";
223 case SDLK_F9: return "F9";
224 case SDLK_F10: return "F10";
225 case SDLK_F11: return "F11";
226 case SDLK_F12: return "F12";
227 case SDLK_F13: return "F13";
228 case SDLK_F14: return "F14";
229 case SDLK_F15: return "F15";
230 case SDLK_NUMLOCK: return "NUMLOCK";
231 case SDLK_CAPSLOCK: return "CAPSLOCK";
232 case SDLK_SCROLLOCK: return "SCROLLOCK";
233 case SDLK_RSHIFT: return "RIGHT SHIFT";
234 case SDLK_LSHIFT: return "LEFT SHIFT";
235 case SDLK_RCTRL: return "RIGHT CTRL";
236 case SDLK_LCTRL: return "LEFT CTRL";
237 case SDLK_RALT: return "RIGHT ALT";
238 case SDLK_LALT: return "LEFT ALT";
239 case SDLK_RMETA: return "RIGHT META";
240 case SDLK_LMETA: return "LEFT META";
241 case SDLK_LSUPER: return "LEFT WINDOWS KEY";
242 case SDLK_RSUPER: return "RIGHT WINDOWS KEY";
243 case SDLK_MODE: return "MODE SHIFT";
244 case SDLK_HELP: return "HELP";
245 case SDLK_PRINT: return "PRINT-SCREEN";
246 case SDLK_SYSREQ: return "SYSRQ";
247 case SDLK_BREAK: return "BREAK";
248 case SDLK_MENU: return "MENU";
249 case SDLK_POWER: return "POWER";
250 case SDLK_EURO: return "EURO";
251 default:
252 sprintf(keyname, "KEY %04X", keysym);
253 return g_intern_string(keyname); /* abuse? :) */
259 /* read a gdk-pixbuf source, and return an sdl surface. */
260 static SDL_Surface *
261 surface_from_gdk_pixbuf_data(guint32 *data)
263 SDL_Surface *surface;
265 g_assert(GUINT32_FROM_BE(data[0])==0x47646b50); /* gdk-pixbuf magic number */
266 if (GUINT32_FROM_BE(data[2])==0x1010002) /* 32-bit rgba */
267 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);
268 else
269 if (GUINT32_FROM_BE(data[2])==0x1010001) /* 24-bit rgb */
270 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);
271 else
272 /* unknown pixel format */
273 g_assert_not_reached();
274 g_assert(surface!=NULL);
276 return surface;
281 /* This is taken from the SDL_gfx project. */
283 SDL_rotozoom.c - rotozoomer for 32bit or 8bit surfaces
284 LGPL (c) A. Schiffler
285 32bit Zoomer with optional anti-aliasing by bilinear interpolation.
286 Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
289 typedef struct tColorRGBA {
290 guint8 r;
291 guint8 g;
292 guint8 b;
293 guint8 a;
294 } tColorRGBA;
296 static void zoomSurfaceRGBA(SDL_Surface *src, SDL_Surface *dst, gboolean smooth)
298 int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep;
299 tColorRGBA *c00, *c01, *c10, *c11;
300 tColorRGBA *sp, *csp, *dp;
301 int dgap;
303 g_assert(src->format->BytesPerPixel==4);
304 g_assert(dst->format->BytesPerPixel==4);
306 /* Variable setup */
307 if (smooth) {
308 /* For interpolation: assume source dimension is one pixel */
309 /* smaller to avoid overflow on right and bottom edge. */
310 sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
311 sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
312 } else {
313 sx = (int) (65536.0 * (float) src->w / (float) dst->w);
314 sy = (int) (65536.0 * (float) src->h / (float) dst->h);
317 /* Allocate memory for row increments */
318 sax=g_new(gint32, dst->w+1);
319 say=g_new(gint32, dst->h+1);
321 /* Precalculate row increments */
322 SDL_LockSurface(src);
323 SDL_LockSurface(dst);
324 sp = csp = (tColorRGBA *) src->pixels;
325 dp = (tColorRGBA *) dst->pixels;
327 csx = 0;
328 csax = sax;
329 for (x = 0; x <= dst->w; x++) {
330 *csax = csx;
331 csax++;
332 csx &= 0xffff;
333 csx += sx;
335 csy = 0;
336 csay = say;
337 for (y = 0; y <= dst->h; y++) {
338 *csay = csy;
339 csay++;
340 csy &= 0xffff;
341 csy += sy;
344 dgap = dst->pitch - dst->w * 4;
347 * Switch between interpolating and non-interpolating code
349 if (smooth) {
350 /* Interpolating Zoom */
352 /* Scan destination */
353 csay = say;
354 for (y = 0; y < dst->h; y++) {
355 /* Setup color source pointers */
356 c00 = csp;
357 c01 = csp;
358 c01++;
359 c10 = (tColorRGBA *) ((guint8 *) csp + src->pitch);
360 c11 = c10;
361 c11++;
362 csax = sax;
363 for (x = 0; x < dst->w; x++) {
364 /* Interpolate colors */
365 ex = (*csax & 0xffff);
366 ey = (*csay & 0xffff);
367 t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
368 t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
369 dp->r = (((t2 - t1) * ey) >> 16) + t1;
370 t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
371 t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
372 dp->g = (((t2 - t1) * ey) >> 16) + t1;
373 t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
374 t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
375 dp->b = (((t2 - t1) * ey) >> 16) + t1;
376 t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
377 t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
378 dp->a = (((t2 - t1) * ey) >> 16) + t1;
380 /* Advance source pointers */
381 csax++;
382 sstep = (*csax >> 16);
383 c00 += sstep;
384 c01 += sstep;
385 c10 += sstep;
386 c11 += sstep;
387 /* Advance destination pointer */
388 dp++;
390 /* Advance source pointer */
391 csay++;
392 csp = (tColorRGBA *) ((guint8 *) csp + (*csay >> 16) * src->pitch);
393 /* Advance destination pointers */
394 dp = (tColorRGBA *) ((guint8 *) dp + dgap);
396 } else {
397 /* Non-Interpolating Zoom */
398 csay = say;
399 for (y = 0; y < dst->h; y++) {
400 sp = csp;
401 csax = sax;
402 for (x = 0; x < dst->w; x++) {
403 /* Draw */
404 *dp = *sp;
405 /* Advance source pointers */
406 csax++;
407 sstep = (*csax >> 16);
408 sp += sstep;
409 /* Advance destination pointer */
410 dp++;
412 /* Advance source pointer */
413 csay++;
414 sstep = (*csay >> 16) * src->pitch;
415 csp = (tColorRGBA *) ((guint8 *) csp + sstep);
417 /* Advance destination pointers */
418 dp = (tColorRGBA *) ((guint8 *) dp + dgap);
423 SDL_UnlockSurface(src);
424 SDL_UnlockSurface(dst);
426 /* Remove temp arrays */
427 g_free(sax);
428 g_free(say);
432 /* wrapper */
433 static void
434 scale2x(SDL_Surface *src, SDL_Surface *dst)
436 g_assert(dst->w == src->w*2);
437 g_assert(dst->h == src->h*2);
438 g_assert(src->format->BytesPerPixel==4);
439 g_assert(dst->format->BytesPerPixel==4);
441 SDL_LockSurface(src);
442 SDL_LockSurface(dst);
443 gd_scale2x(src->pixels, src->w, src->h, src->pitch, dst->pixels, dst->pitch);
444 SDL_UnlockSurface(src);
445 SDL_UnlockSurface(dst);
448 static void
449 scale3x(SDL_Surface *src, SDL_Surface *dst)
451 g_assert(dst->w == src->w*3);
452 g_assert(dst->h == src->h*3);
453 g_assert(src->format->BytesPerPixel==4);
454 g_assert(dst->format->BytesPerPixel==4);
456 SDL_LockSurface(src);
457 SDL_LockSurface(dst);
458 gd_scale3x(src->pixels, src->w, src->h, src->pitch, dst->pixels, dst->pitch);
459 SDL_UnlockSurface(src);
460 SDL_UnlockSurface(dst);
467 /* nearest neighbor scaling for 2x and 3x. */
468 /* nearest pixel 2x scaling. */
469 static void
470 scale_2x_nearest(SDL_Surface *src, SDL_Surface *dst)
472 guint32 E;
473 int y, x;
474 guint8 *srcpix=src->pixels;
475 guint8 *dstpix=dst->pixels;
476 int width=src->w;
477 int height=src->h;
478 int srcpitch=src->pitch;
479 int dstpitch=dst->pitch;
481 g_assert(dst->w == src->w*2);
482 g_assert(dst->h == src->h*2);
483 g_assert(src->format->BytesPerPixel==4);
484 g_assert(dst->format->BytesPerPixel==4);
486 SDL_LockSurface(src);
487 SDL_LockSurface(dst);
488 for (y=0; y<height; ++y) {
489 for (x=0; x<width; ++x) {
490 E = *(guint32*)(srcpix + (y*srcpitch) + (4*x));
492 *(guint32*)(dstpix + y*2*dstpitch + x*2*4) = E;
493 *(guint32*)(dstpix + y*2*dstpitch + (x*2+1)*4) = E;
494 *(guint32*)(dstpix + (y*2+1)*dstpitch + x*2*4) = E;
495 *(guint32*)(dstpix + (y*2+1)*dstpitch + (x*2+1)*4) = E;
498 SDL_UnlockSurface(src);
499 SDL_UnlockSurface(dst);
503 /* nearest pixel 3x scaling. the rotozoomer is not correct at the bottom of the image. */
504 static void
505 scale_3x_nearest(SDL_Surface *src, SDL_Surface *dst)
507 guint32 E;
508 int y, x;
509 guint8 *srcpix=src->pixels;
510 guint8 *dstpix=dst->pixels;
511 int width=src->w;
512 int height=src->h;
513 int srcpitch=src->pitch;
514 int dstpitch=dst->pitch;
516 g_assert(dst->w == src->w*3);
517 g_assert(dst->h == src->h*3);
518 g_assert(src->format->BytesPerPixel==4);
519 g_assert(dst->format->BytesPerPixel==4);
521 SDL_LockSurface(src);
522 SDL_LockSurface(dst);
523 for (y=0; y<height; ++y) {
524 int ny=y*3; /* new coordinate */
526 for (x=0; x<width; ++ x) {
527 int nx=x*3; /* new coordinate */
529 E = *(guint32*)(srcpix + (y*srcpitch + 4*x));
531 *(guint32*)(dstpix + ny*dstpitch + nx*4) = E;
532 *(guint32*)(dstpix + ny*dstpitch + (nx+1)*4) = E;
533 *(guint32*)(dstpix + ny*dstpitch + (nx+2)*4) = E;
534 *(guint32*)(dstpix + (ny+1)*dstpitch + nx*4) = E;
535 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+1)*4) = E;
536 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+2)*4) = E;
537 *(guint32*)(dstpix + (ny+2)*dstpitch + nx*4) = E;
538 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+1)*4) = E;
539 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+2)*4) = E;
542 SDL_UnlockSurface(src);
543 SDL_UnlockSurface(dst);
548 /* scales a pixbuf with the appropriate scaling type. */
549 SDL_Surface *
550 surface_scale(SDL_Surface *orig)
552 SDL_Surface *dest, *dest2x;
554 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);
556 switch (gd_scale_type) {
557 case GD_SCALING_ORIGINAL:
558 SDL_BlitSurface(orig, NULL, dest, NULL);
559 break;
561 case GD_SCALING_2X:
562 scale_2x_nearest(orig, dest);
563 break;
565 case GD_SCALING_2X_BILINEAR:
566 zoomSurfaceRGBA(orig, dest, TRUE);
567 break;
569 case GD_SCALING_2X_SCALE2X:
570 scale2x(orig, dest);
571 break;
573 case GD_SCALING_3X:
574 scale_3x_nearest(orig, dest);
575 break;
577 case GD_SCALING_3X_BILINEAR:
578 zoomSurfaceRGBA(orig, dest, TRUE);
579 break;
581 case GD_SCALING_3X_SCALE3X:
582 scale3x(orig, dest);
583 break;
585 case GD_SCALING_4X:
586 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);
587 scale_2x_nearest(orig, dest2x);
588 scale_2x_nearest(dest2x, dest);
589 SDL_FreeSurface(dest2x);
590 break;
592 case GD_SCALING_4X_BILINEAR:
593 zoomSurfaceRGBA(orig, dest, TRUE);
594 break;
596 case GD_SCALING_4X_SCALE4X:
597 /* scale2x applied twice. */
598 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);
599 scale2x(orig, dest2x);
600 scale2x(dest2x, dest);
601 SDL_FreeSurface(dest2x);
602 break;
604 /* not a valid case, but to avoid compiler warning */
605 case GD_SCALING_MAX:
606 g_assert_not_reached();
607 break;
610 return dest;
614 /* wrapper */
615 static void
616 pal_emu_surface(SDL_Surface *image)
618 SDL_LockSurface(image);
619 gd_pal_emu(image->pixels, image->w, image->h, image->pitch, image->format->Rshift, image->format->Gshift, image->format->Bshift, image->format->Ashift);
620 SDL_UnlockSurface(image);
624 /* create new surface, which is SDL_DisplayFormatted. */
625 /* it is also scaled by gd_scale. */
626 static SDL_Surface *
627 displayformat(SDL_Surface *orig)
629 SDL_Surface *scaled, *displayformat;
631 /* at this point we must already be working with 32bit surfaces */
632 g_assert(orig->format->BytesPerPixel==4);
634 // pal_emu(orig);
635 scaled=surface_scale(orig);
636 if (gd_sdl_pal_emulation)
637 pal_emu_surface(scaled);
638 displayformat=SDL_DisplayFormat(scaled);
639 SDL_FreeSurface(scaled);
641 return displayformat;
644 /* needed to alpha-copy an src image over dst.
645 the resulting image of sdl_blitsurface is not exact?!
646 can't explain why. */
647 static void
648 copy_alpha(SDL_Surface *src, SDL_Surface *dst)
650 int x, y;
652 g_assert(src->format->BytesPerPixel==4);
653 g_assert(dst->format->BytesPerPixel==4);
654 g_assert(src->w == dst->w);
655 g_assert(src->h == dst->h);
657 SDL_LockSurface(src);
658 SDL_LockSurface(dst);
659 for (y=0; y<src->h; y++) {
660 guint32 *srcpix=(guint32 *)((guint8*)src->pixels + src->pitch*y);
661 guint32 *dstpix=(guint32 *)((guint8*)dst->pixels + dst->pitch*y);
662 for (x=0; x<src->w; x++) {
663 if ((srcpix[x]&src->format->Amask)>>src->format->Ashift) /* if alpha is nonzero */
664 dstpix[x]=srcpix[x]; /* copy pixel as it is */
667 SDL_UnlockSurface(src);
668 SDL_UnlockSurface(dst);
671 /* create and return an array of surfaces, which contain the title animation.
672 the array is one pointer larger than all frames; the last pointer is a null.
673 up to the caller to free.
675 SDL_Surface **
676 gd_get_title_animation()
678 SDL_Surface *screen;
679 SDL_Surface *tile;
680 SDL_Surface *bigone;
681 SDL_Surface *frame;
682 SDL_Surface **animation;
683 int x, y, i;
685 screen=surface_from_gdk_pixbuf_data((guint32 *) gdash_screen);
686 tile=surface_from_gdk_pixbuf_data((guint32 *) gdash_tile);
688 /* do not allow more than 40 frames of animation */
689 g_assert(tile->h<40);
690 animation=g_new0(SDL_Surface *, tile->h+1);
692 /* create a big image, which is one tile larger than the title image size */
693 bigone=SDL_CreateRGBSurface(0, screen->w, screen->h+tile->h, 32, 0, 0, 0, 0);
694 /* and fill it with the tile. */
695 for (y=0; y<screen->h+tile->h; y+=tile->h)
696 for (x=0; x<screen->w; x+=tile->h) {
697 SDL_Rect dest;
699 dest.x=x;
700 dest.y=y;
701 SDL_BlitSurface(tile, 0, bigone, &dest);
704 frame=SDL_CreateRGBSurface(0, screen->w, screen->h, 32, rmask, gmask, bmask, amask); /* must be same *mask so copy_alpha works correctly */
705 for (i=0; i<tile->h; i++) {
706 SDL_Rect src;
708 /* copy part of the big tiled image */
709 src.x=0;
710 src.y=i;
711 src.w=screen->w;
712 src.h=screen->h;
713 SDL_BlitSurface(bigone, &src, frame, 0);
714 /* and composite it with the title image */
715 copy_alpha(screen, frame);
717 animation[i]=displayformat(frame);
719 SDL_FreeSurface(frame);
720 SDL_FreeSurface(bigone);
721 SDL_FreeSurface(screen);
722 SDL_FreeSurface(tile);
724 return animation;
727 static void
728 rendered_font_free(gpointer font)
730 SDL_Surface **p;
732 /* null-terminated list of pointers to sdl_surfaces */
733 for(p=font; p!=NULL; p++)
734 SDL_FreeSurface(*p);
735 g_free(font);
739 gboolean
740 gd_sdl_init(GdScalingType scaling_type)
742 SDL_Surface *icon;
744 /* set the cell scale option. */
745 gd_scale_type=scaling_type;
746 gd_scale=gd_scaling_scale[gd_scale_type];
747 play_area_w*=gd_scale;
748 play_area_h*=gd_scale;
749 statusbar_y1*=gd_scale;
750 statusbar_y2*=gd_scale;
751 statusbar_height*=gd_scale;
752 statusbar_mid*=gd_scale;
754 SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|SDL_INIT_JOYSTICK);
755 SDL_EnableKeyRepeat(250, 100);
756 gd_screen=SDL_SetVideoMode(play_area_w, play_area_h+statusbar_height, 32, SDL_ANYFORMAT | (gd_sdl_fullscreen?SDL_FULLSCREEN:0));
757 SDL_ShowCursor(SDL_DISABLE);
758 if (!gd_screen)
759 return FALSE;
761 /* keyboard, joystick */
762 gd_keystate=SDL_GetKeyState(NULL);
763 if (SDL_NumJoysticks()>0)
764 gd_joy=SDL_JoystickOpen(0);
766 /* icon */
767 icon=surface_from_gdk_pixbuf_data((guint32 *) gdash_32);
768 SDL_WM_SetIcon(icon, NULL);
769 SDL_WM_SetCaption("GDash", NULL);
770 SDL_FreeSurface(icon);
772 font_n=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rendered_font_free);
773 font_w=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rendered_font_free);
775 return TRUE;
781 static inline gboolean
782 gd_joy_up()
784 return gd_joy!=NULL && SDL_JoystickGetAxis(gd_joy, 1)<-3200;
787 static inline gboolean
788 gd_joy_down()
790 return gd_joy!=NULL && SDL_JoystickGetAxis(gd_joy, 1)>3200;
793 static inline gboolean
794 gd_joy_left()
796 return gd_joy!=NULL && SDL_JoystickGetAxis(gd_joy, 0)<-3200;
799 static inline gboolean
800 gd_joy_right()
802 return gd_joy!=NULL && SDL_JoystickGetAxis(gd_joy, 0)>3200;
805 static inline gboolean
806 gd_joy_fire()
808 return gd_joy!=NULL && (SDL_JoystickGetButton(gd_joy, 0) || SDL_JoystickGetButton(gd_joy, 1));
814 gboolean
815 gd_up()
817 return gd_keystate[gd_sdl_key_up] || gd_joy_up();
820 gboolean
821 gd_down()
823 return gd_keystate[gd_sdl_key_down] || gd_joy_down();
826 gboolean
827 gd_left()
829 return gd_keystate[gd_sdl_key_left] || gd_joy_left();
832 gboolean
833 gd_right()
835 return gd_keystate[gd_sdl_key_right] || gd_joy_right();
838 gboolean
839 gd_fire()
841 return gd_keystate[gd_sdl_key_fire_1] || gd_keystate[gd_sdl_key_fire_2] || gd_joy_fire();
847 /* like the above one, but used in the main menu */
848 gboolean
849 gd_space_or_enter_or_fire()
851 /* set these to zero, so each keypress is reported only once */
852 return gd_joy_fire() || gd_keystate[SDLK_SPACE] || gd_keystate[SDLK_RETURN];
861 width: width of playfield.
862 visible: visible part. (remember: player_x-x1!)
864 center: the coordinates to scroll to.
865 exact: scroll exactly
866 start: start scrolling
867 to: scroll to, if started
868 current
870 desired: the function stores its data here
871 speed: the function stores its data here
873 gboolean
874 cave_scroll(int width, int visible, int center, gboolean exact, int start, int to, int *current, int *desired, int *speed, int divisor)
876 int i;
877 gboolean changed;
879 changed=FALSE;
881 /* HORIZONTAL */
882 /* hystheresis function.
883 * when scrolling left, always go a bit less left than player being at the middle.
884 * when scrolling right, always go a bit less to the right. */
885 if (width<visible) {
886 *speed=0;
887 *desired=0;
888 if (*current!=0) {
889 *current=0;
890 changed=TRUE;
893 return changed;
896 if (exact)
897 *desired=center;
898 else {
899 if (*current+start<center)
900 *desired=center-to;
901 if (*current-start>center)
902 *desired=center+to;
904 *desired=CLAMP(*desired, 0, width-visible);
906 /* adaptive scrolling speed.
907 * gets faster with distance.
908 * minimum speed is 1, to allow scrolling precisely to the desired positions (important at borders).
910 if (*speed<ABS(*desired-*current)/divisor+1)
911 (*speed)++;
912 if (*speed>ABS(*desired-*current)/divisor+1)
913 (*speed)--;
914 if (*current < *desired) {
915 for (i=0; i < *speed; i++)
916 if (*current < *desired)
917 (*current)++;
918 changed=TRUE;
920 if (*current > *desired) {
921 for (i=0; i < *speed; i++)
922 if (*current > *desired)
923 (*current)--;
924 changed=TRUE;
927 return changed;
936 /* remove pixmaps from x server */
937 static void
938 free_cells()
940 int i;
942 /* if cells already loaded, unref them */
943 for (i=0; i<G_N_ELEMENTS(cells); i++)
944 if (cells[i]) {
945 SDL_FreeSurface(cells[i]);
946 cells[i]=NULL;
951 static void
952 loadcells(SDL_Surface *image)
954 int i;
955 int pixbuf_cell_size;
956 SDL_Surface *rect;
957 SDL_Surface *cut;
959 /* if we have display-formatted pixmaps, remove them */
960 free_cells();
962 /* 8 (NUM_OF_CELLS_X) cells in a row, so divide by it and we get the size of a cell in pixels */
963 pixbuf_cell_size=image->w/NUM_OF_CELLS_X;
964 cell_size=pixbuf_cell_size*gd_scale;
966 rect=SDL_CreateRGBSurface(0, pixbuf_cell_size, pixbuf_cell_size, 32, 0, 0, 0, 0); /* no amask, as we set overall alpha! */
967 SDL_FillRect(rect, NULL, SDL_MapRGB(rect->format, (gd_flash_color>>16)&0xff, (gd_flash_color>>8)&0xff, gd_flash_color&0xff));
968 SDL_SetAlpha(rect, SDL_SRCALPHA, 128); /* 50% alpha; nice choice. also sdl is rendering faster for the special value alpha=128 */
970 cut=SDL_CreateRGBSurface(0, pixbuf_cell_size, pixbuf_cell_size, 32, rmask, gmask, bmask, amask);
972 /* make individual cells */
973 for (i=0; i<NUM_OF_CELLS_X*NUM_OF_CELLS_Y; i++) {
974 SDL_Rect from;
976 from.x=(i%NUM_OF_CELLS_X)*pixbuf_cell_size;
977 from.y=(i/NUM_OF_CELLS_X)*pixbuf_cell_size;
978 from.w=pixbuf_cell_size;
979 from.h=pixbuf_cell_size;
981 SDL_BlitSurface(image, &from, cut, NULL);
983 cells[i]=displayformat(cut);
984 SDL_BlitSurface(rect, NULL, cut, NULL); /* create yellowish image */
985 cells[NUM_OF_CELLS+i]=displayformat(cut);
987 SDL_FreeSurface(cut);
988 SDL_FreeSurface(rect);
991 /* sets one of the colors in the indexed palette of an sdl surface to a GdColor. */
992 static void
993 setpalette(SDL_Surface *image, int index, GdColor col)
995 SDL_Color c;
996 c.r=gd_color_get_r(col);
997 c.g=gd_color_get_g(col);
998 c.b=gd_color_get_b(col);
1000 SDL_SetPalette(image, SDL_LOGPAL|SDL_PHYSPAL, &c, index, 1);
1003 static void
1004 loadcells_c64(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
1006 const guint8 *gfx; /* currently used graphics, will point to c64_gfx or c64_custom_gfx */
1007 SDL_Surface *image;
1009 gfx=c64_custom_gfx?c64_custom_gfx:c64_gfx;
1011 /* create a 8-bit palette and set its colors */
1012 /* gfx[0] is the pixel width and height of one cell. */
1013 /* from gfx[1], we have the color data, one byte/pixel. so this is an indexed color image: 8bit/pixel. */
1014 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);
1015 /* sdl supports paletted images, so this is very easy: */
1016 setpalette(image, 0, 0);
1017 setpalette(image, 1, c0);
1018 setpalette(image, 2, c1);
1019 setpalette(image, 3, c2);
1020 setpalette(image, 4, c3);
1021 setpalette(image, 5, c4);
1022 setpalette(image, 6, c5);
1023 setpalette(image, 7, 0);
1024 setpalette(image, 8, 0);
1026 /* from here, same as any other image */
1027 loadcells(image);
1028 SDL_FreeSurface(image);
1031 /* takes a c64_gfx.png-coded 32-bit pixbuf, and creates a paletted pixbuf in our internal format. */
1032 guchar *
1033 c64_gfx_data_from_pixbuf(SDL_Surface *image)
1035 int x, y;
1036 guint8 *data;
1037 int out;
1039 g_assert(image->format->BytesPerPixel==4);
1041 data=g_new(guint8, image->w*image->h+1);
1042 out=0;
1043 data[out++]=image->w/NUM_OF_CELLS_X;
1045 SDL_LockSurface(image);
1046 for (y=0; y<image->h; y++) {
1047 guint32 *p=(guint32 *)((char *)image->pixels+y*image->pitch);
1049 for (x=0; x<image->w; x++) {
1050 int r, g, b, a;
1052 r=(p[x]&image->format->Rmask) >> image->format->Rshift << image->format->Rloss;
1053 g=(p[x]&image->format->Gmask) >> image->format->Gshift << image->format->Gloss;
1054 b=(p[x]&image->format->Bmask) >> image->format->Bshift << image->format->Bloss;
1055 a=(p[x]&image->format->Amask) >> image->format->Ashift << image->format->Aloss;
1057 data[out++]=c64_png_colors(r, g, b, a);
1060 SDL_UnlockSurface(image);
1062 return data;
1066 /* returns true, if the given pixbuf seems to be a c64 imported image. */
1067 static gboolean
1068 check_if_pixbuf_c64_png(SDL_Surface *image)
1070 const guchar *p;
1071 int x, y;
1072 gboolean c64_png;
1074 g_assert(image->format->BytesPerPixel==4);
1076 SDL_LockSurface(image);
1077 c64_png=TRUE;
1078 for (y=0; y<image->h; y++) {
1079 p=((guchar *)image->pixels) + y * image->pitch;
1080 for (x=0; x<image->w*image->format->BytesPerPixel; x++)
1081 if (p[x]!=0 && p[x]!=255)
1082 c64_png=FALSE;
1084 SDL_UnlockSurface(image);
1086 return c64_png;
1089 /* check if given surface is ok to be a gdash theme. */
1090 gboolean
1091 gd_is_surface_ok_for_theme (SDL_Surface *surface)
1093 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)) {
1094 g_warning("image should contain %d cells in a row and %d in a column!", NUM_OF_CELLS_X, NUM_OF_CELLS_Y);
1095 return FALSE; /* Image should contain 16 cells in a row and 32 in a column! */
1097 /* we do not check for an alpha channel, as in the gtk version. we do not need it here. */
1098 /* the gtk version needs it because of the editor. */
1100 return TRUE; /* passed checks */
1104 /* load theme from bmp file. */
1105 /* return true if successful. */
1106 gboolean
1107 gd_loadcells_file(const char *filename)
1109 SDL_Surface *surface, *converted;
1111 /* load cell graphics */
1112 /* load from file */
1113 surface=SDL_LoadBMP(filename);
1115 if (!surface) {
1116 g_warning("%s: unable to load image", filename);
1117 return FALSE;
1120 /* do some checks. if those fail, the error is already reported by the function */
1121 if (!gd_is_surface_ok_for_theme(surface)) {
1122 SDL_FreeSurface(surface);
1123 return FALSE;
1126 /* convert to 32bit rgba */
1127 converted=SDL_CreateRGBSurface(0, surface->w, surface->h, 32, rmask, bmask, gmask, amask);
1128 SDL_BlitSurface(surface, NULL, converted, NULL);
1129 SDL_FreeSurface(surface);
1131 if (check_if_pixbuf_c64_png(converted)) {
1132 /* c64 pixbuf with a small number of colors which can be changed */
1133 g_free(c64_custom_gfx);
1134 c64_custom_gfx=c64_gfx_data_from_pixbuf(converted);
1135 using_png_gfx=FALSE;
1136 } else {
1137 /* normal, "truecolor" pixbuf */
1138 g_free(c64_custom_gfx);
1139 c64_custom_gfx=NULL;
1140 using_png_gfx=TRUE;
1141 loadcells(converted);
1143 SDL_FreeSurface(converted);
1144 color0=GD_COLOR_INVALID; /* this is an invalid gdash color; so redraw is forced */
1146 return TRUE;
1151 /* load the theme specified in gd_sdl_theme. */
1152 /* if successful, ok. */
1153 /* if fails, or no theme specified, load the builtin, and forget gd_sdl_theme */
1154 void
1155 gd_load_theme()
1157 if (gd_sdl_theme) {
1158 if (!gd_loadcells_file(gd_sdl_theme)) {
1159 /* if failing to load the bmp file specified in the config file: forget the setting now, and load the builtin. */
1160 g_free(gd_sdl_theme);
1161 gd_sdl_theme=NULL;
1164 if (!gd_sdl_theme)
1165 gd_loadcells_default(); /* if no theme specified, or loading the file failed, simply load the builtin. */
1170 /* C64 FONT HANDLING */
1172 /* creates a guint32 value, which can be raw-written to a sdl_surface memory area. */
1173 /* takes the pixel format of the surface into consideration. */
1174 static guint32
1175 rgba_pixel_from_gdcolor(SDL_PixelFormat *format, GdColor col, guint8 a)
1177 return SDL_MapRGBA(format, gd_color_get_r(col), gd_color_get_g(col), gd_color_get_b(col), a);
1181 /* FIXME fixed font size: 8x8 */
1182 /* renders the fonts for a specific color. */
1183 /* the wide font draws black pixels, the narrow (normal) font does not! */
1184 static void
1185 renderfont_color(GdColor color)
1187 int j, x, y;
1188 guint32 col, black;
1189 SDL_Surface *image, *image_n;
1190 SDL_Surface **fn, **fw;
1192 /* check that we already got an rgb color */
1193 g_assert(gd_color_is_rgb(color));
1195 /* if already rendered, return now */
1196 if (g_hash_table_lookup(font_w, GUINT_TO_POINTER(color)))
1197 return;
1199 fn=g_new0(SDL_Surface *, GD_NUM_OF_CHARS+1);
1200 fw=g_new0(SDL_Surface *, GD_NUM_OF_CHARS+1);
1202 /* colors data into memory */
1203 image=SDL_CreateRGBSurface(0, 16, 8, 32, rmask, gmask, bmask, amask);
1204 image_n=SDL_CreateRGBSurface(0, 8, 8, 32, rmask, gmask, bmask, amask);
1206 col=rgba_pixel_from_gdcolor(image->format, color, 0xff);
1207 black=rgba_pixel_from_gdcolor(image->format, 0, 0xff);
1209 /* for all characters */
1210 /* render character in "image", then do a displayformat(image) to generate the resulting one */
1211 for (j=0; j<GD_NUM_OF_CHARS; j++) {
1212 int x1, y1;
1214 y1=(j/16)*8; /* 16 characters in a row */
1215 x1=(j%16)*8;
1217 SDL_LockSurface(image);
1218 SDL_LockSurface(image_n);
1219 for (y=0; y<8; y++) {
1220 guint32 *p=(guint32*) ((char *)image->pixels + y*image->pitch);
1221 guint32 *p_n=(guint32*) ((char *)image_n->pixels + y*image_n->pitch);
1223 for (x=0; x<8; x++) {
1224 /* the font array is encoded the same way as a c64-colored pixbuf. see c64_gfx_data...() */
1225 if (font[(y1+y)*128+x1+x]!=1) {
1226 p[0]=col; /* wide */
1227 p[1]=col;
1228 p_n[0]=col; /* normal */
1229 } else {
1230 p[0]=black; /* wide */
1231 p[1]=black;
1232 p_n[0]=black; /* normal */
1234 p+=2;
1235 p_n++;
1238 SDL_UnlockSurface(image);
1239 SDL_UnlockSurface(image_n);
1240 fw[j]=displayformat(image);
1241 fn[j]=displayformat(image_n);
1242 /* small font does not draw black background */
1243 SDL_SetColorKey(fn[j], SDL_SRCCOLORKEY, SDL_MapRGB(fn[j]->format, 0, 0, 0));
1245 SDL_FreeSurface(image);
1246 SDL_FreeSurface(image_n);
1248 g_hash_table_insert(font_w, GINT_TO_POINTER(color), fw);
1249 g_hash_table_insert(font_n, GINT_TO_POINTER(color), fn);
1252 /* XXX these functions are not really implemented */
1253 static void
1254 loadfont_buffer()
1256 /* forget all previously rendered chars */
1257 g_hash_table_remove_all(font_w);
1258 g_hash_table_remove_all(font_n);
1262 /* loads a font from a file */
1263 void
1264 gd_loadfont_file(const char *filename)
1266 g_assert_not_reached();
1269 /* loads inlined c64 font */
1270 void
1271 gd_loadfont_default()
1273 font=c64_font+1; /* first byte in c64_gfx[] is the "cell size", we ignore that */
1274 loadfont_buffer();
1280 int gd_font_height()
1282 return 8*gd_scale;
1285 int gd_font_width()
1287 return 8*gd_scale;
1290 int gd_line_height()
1292 return gd_font_height()+2;
1295 /* clears one line on the screen. takes dark screen or totally black screen into consideration. */
1296 void
1297 gd_clear_line(SDL_Surface *screen, int y)
1299 SDL_Rect rect;
1301 rect.x=0;
1302 rect.y=y;
1303 rect.w=screen->w;
1304 rect.h=gd_font_height();
1305 if (dark_screens!=NULL && dark_screens->data!=NULL)
1306 SDL_BlitSurface(dark_screens->data, &rect, screen, &rect);
1307 else
1308 SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 0, 0, 0)); /* fill rectangle with black */
1313 /* function which draws characters on the screen. used internally. */
1314 /* x=-1 -> center horizontally */
1315 static int
1316 gd_blittext_font(SDL_Surface *screen, SDL_Surface **font, int x1, int y, const char *text)
1318 const char *p=text;
1319 SDL_Rect destrect;
1320 int i;
1321 int x;
1322 gunichar c;
1324 if (x1==-1)
1325 x1=screen->w/2 - (font[0]->w*g_utf8_strlen(text, -1))/2;
1327 x=x1;
1328 c=g_utf8_get_char(p);
1329 while (c!=0) {
1330 if (c=='\n') {
1331 /* if it is an enter */
1332 if (x==x1)
1333 y+=font[0]->h/2;
1334 else
1335 y+=font[0]->h;
1336 x=x1;
1337 } else {
1338 /* it is a normal character */
1339 if (c==GD_CHECKED_BOX_CHAR)
1340 i=CHECKED_BOX_CHAR_INDEX;
1341 else
1342 if (c==GD_BALL_CHAR)
1343 i=BALL_CHAR_INDEX;
1344 else
1345 if (c==GD_CHECK_MARK_CHAR)
1346 i=CHECK_MARK_INDEX;
1347 else
1348 if (c==GD_PLAYER_CHAR || c==GD_DIAMOND_CHAR || c==GD_UNCHECKED_BOX_CHAR) /* special, by gdash */
1349 i=c;
1350 else
1351 if (c==GD_KEY_CHAR)
1352 i=92;
1353 else
1354 if (c=='@')
1355 i=0;
1356 else
1357 if (c>=' ' && c<='Z') /* from space to Z, petscii=ascii */
1358 i=c;
1359 else
1360 if (c>='a' && c<='z')
1361 i=c-'a'+1;
1362 else
1363 if (c=='\\')
1364 i=BACKSLASH_CHAR_INDEX;
1365 else
1366 if (c=='_')
1367 i=UNDERSCORE_CHAR_INDEX;
1368 else
1369 if (c=='~')
1370 i=TILDE_CHAR_INDEX;
1371 else
1372 if (c=='[')
1373 i=OPEN_BRACKET_CHAR_INDEX;
1374 else
1375 if (c==']')
1376 i=CLOSE_BRACKET_CHAR_INDEX;
1377 else
1378 i=UNKNOWN_CHAR_INDEX;
1380 destrect.x=x;
1381 destrect.y=y;
1382 SDL_BlitSurface(font[i], NULL, screen, &destrect);
1384 x+=font[i]->w;
1387 p=g_utf8_next_char(p); /* next character */
1388 c=g_utf8_get_char(p);
1391 return x;
1394 /* write something to the screen, with wide characters. */
1395 /* x=-1 -> center horizontally */
1397 gd_blittext(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1399 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1400 renderfont_color(color);
1401 return gd_blittext_font(screen, g_hash_table_lookup(font_w, GUINT_TO_POINTER(color)), x, y, text);
1404 /* write something to the screen, with normal characters. */
1405 /* x=-1 -> center horizontally */
1407 gd_blittext_n(SDL_Surface *screen, int x, int y, GdColor color, const char *text)
1409 color=gd_color_get_rgb(color); /* convert to rgb, so they are stored that way in the hash table */
1410 renderfont_color(color);
1411 return gd_blittext_font(screen, g_hash_table_lookup(font_n, GUINT_TO_POINTER(color)), x, y, text);
1414 /* write something to the screen, with wide characters. */
1415 /* x=-1 -> center horizontally */
1417 gd_blittext_printf(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1419 va_list args;
1420 char *text;
1422 va_start(args, format);
1423 text=g_strdup_vprintf(format, args);
1424 x=gd_blittext(screen, x, y, color, text);
1425 g_free(text);
1426 va_end(args);
1428 return x;
1431 /* write something to the screen, with normal characters. */
1432 /* x=-1 -> center horizontally */
1434 gd_blittext_printf_n(SDL_Surface *screen, int x, int y, GdColor color, const char *format, ...)
1436 va_list args;
1437 char *text;
1439 va_start(args, format);
1440 text=g_strdup_vprintf(format, args);
1441 x=gd_blittext_n(screen, x, y, color, text);
1442 g_free(text);
1443 va_end(args);
1445 return x;
1450 void
1451 gd_select_pixbuf_colors(GdColor c0, GdColor c1, GdColor c2, GdColor c3, GdColor c4, GdColor c5)
1453 /* if non-c64 gfx, nothing to do */
1454 if (using_png_gfx)
1455 return;
1457 /* convert to rgb value */
1458 c0=gd_color_get_rgb(c0);
1459 c1=gd_color_get_rgb(c1);
1460 c2=gd_color_get_rgb(c2);
1461 c3=gd_color_get_rgb(c3);
1462 c4=gd_color_get_rgb(c4);
1463 c5=gd_color_get_rgb(c5);
1465 /* and compare rgb values! */
1466 if (c0!=color0 || c1!=color1 || c2!=color2 || c3!=color3 || c4!=color4 || c5!=color5) {
1467 /* if not the same colors as requested before */
1468 color0=c0;
1469 color1=c1;
1470 color2=c2;
1471 color3=c3;
1472 color4=c4;
1473 color5=c5;
1475 loadcells_c64(c0, c1, c2, c3, c4, c5);
1479 /* selects built-in graphics */
1480 void
1481 gd_loadcells_default()
1483 g_free(c64_custom_gfx);
1484 c64_custom_gfx=NULL;
1485 using_png_gfx=FALSE;
1486 /* just to set some default */
1487 color0=GD_COLOR_INVALID; /* this is an invalid gdash color; so redraw is forced */
1490 /* the dark gray background */
1491 void
1492 gd_create_dark_background()
1494 SDL_Surface *tile, *dark_tile, *dark_screen_tiled;
1495 int x, y;
1497 g_assert(gd_screen!=NULL);
1499 if (dark_background)
1500 SDL_FreeSurface(dark_background);
1502 tile=surface_from_gdk_pixbuf_data((guint32 *) gdash_tile);
1503 /* 24bpp, as it has no alpha channel */
1504 dark_tile=SDL_CreateRGBSurface(0, tile->w, tile->h, 24, 0, 0, 0, 0);
1505 SDL_BlitSurface(tile, NULL, dark_tile, NULL);
1506 SDL_FreeSurface(tile);
1507 SDL_SetAlpha(dark_tile, SDL_SRCALPHA, 256/6); /* 1/6 opacity */
1509 /* create the image, and fill it with the tile. */
1510 /* the image is screen size / gd_scale, so we prefer the original screen size here */
1511 /* and only do the scaling later! */
1512 dark_screen_tiled=SDL_CreateRGBSurface(0, gd_screen->w/gd_scale, gd_screen->h/gd_scale, 32, rmask, gmask, bmask, amask);
1513 for (y=0; y<dark_screen_tiled->h; y+=dark_tile->h)
1514 for (x=0; x<dark_screen_tiled->w; x+=dark_tile->w) {
1515 SDL_Rect rect;
1517 rect.x=x;
1518 rect.y=y;
1520 SDL_BlitSurface(dark_tile, NULL, dark_screen_tiled, &rect);
1522 SDL_FreeSurface(dark_tile);
1523 dark_background=displayformat(dark_screen_tiled);
1524 SDL_FreeSurface(dark_screen_tiled);
1530 * screen backup and restore functions. these are used by menus, help screens
1531 * and the like. backups and restores can be nested.
1534 /* backups the current screen contents, and darkens it. */
1535 /* later, gd_restore_screen can be called. */
1536 void
1537 gd_backup_and_dark_screen()
1539 SDL_Surface *backup_screen, *dark_screen;
1541 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
1542 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
1544 dark_screen=SDL_DisplayFormat(dark_background);
1546 SDL_BlitSurface(dark_screen, NULL, gd_screen, NULL);
1548 backup_screens=g_list_prepend(backup_screens, backup_screen);
1549 dark_screens=g_list_prepend(dark_screens, dark_screen);
1552 /* backups the current screen contents, and clears it. */
1553 /* later, gd_restore_screen can be called. */
1554 void
1555 gd_backup_and_black_screen()
1557 SDL_Surface *backup_screen;
1559 backup_screen=SDL_CreateRGBSurface(0, gd_screen->w, gd_screen->h, 32, 0, 0, 0, 0);
1560 SDL_BlitSurface(gd_screen, 0, backup_screen, 0);
1562 backup_screens=g_list_prepend(backup_screens, backup_screen);
1563 dark_screens=g_list_prepend(dark_screens, NULL);
1566 /* restores a backed up screen. */
1567 void
1568 gd_restore_screen()
1570 SDL_Surface *backup_screen, *dark_screen;
1572 /* check if lists are non-empty */
1573 g_assert(backup_screens!=NULL);
1574 g_assert(dark_screens!=NULL);
1576 backup_screen=backup_screens->data;
1577 backup_screens=g_list_delete_link(backup_screens, backup_screens); /* remove first */
1578 dark_screen=dark_screens->data;
1579 dark_screens=g_list_delete_link(dark_screens, dark_screens); /* remove first */
1581 SDL_BlitSurface(backup_screen, 0, gd_screen, 0);
1582 SDL_Flip(gd_screen);
1583 SDL_FreeSurface(backup_screen);
1584 if (dark_screen)
1585 SDL_FreeSurface(dark_screen);
1590 /* process pending events, so presses and releases are applied */
1591 /* also check for quit event */
1592 void
1593 gd_process_pending_events()
1595 SDL_Event event;
1597 /* process pending events, so presses and releases are applied */
1598 while (SDL_PollEvent(&event)) {
1599 /* meanwhile, if we receive a quit event, we set the global variable. */
1600 if (event.type==SDL_QUIT)
1601 gd_quit=TRUE;
1607 /* this function waits until SPACE, ENTER and ESCAPE are released */
1608 void
1609 gd_wait_for_key_releases()
1611 /* wait until the user releases return and escape */
1612 while (gd_keystate[SDLK_RETURN]!=0 || gd_keystate[SDLK_ESCAPE]!=0 || gd_keystate[SDLK_SPACE]!=0 || gd_keystate[SDLK_n]!=0 || gd_fire()) {
1613 SDL_Event event;
1615 /* process pending events, so presses and releases are applied */
1616 while (SDL_PollEvent(&event)) {
1617 /* meanwhile, if we receive a quit event, we set the global variable. */
1618 if (event.type==SDL_QUIT)
1619 gd_quit=TRUE;
1622 SDL_Delay(50); /* do not eat cpu */
1634 /* just set current viewport to upper left. */
1635 void
1636 gd_scroll_to_origin()
1638 scroll_x=0;
1639 scroll_y=0;
1643 /* SCROLLING
1645 * scrolls to the player during game play.
1646 * called by drawcave
1647 * returns true, if player is not visible-ie it is out of the visible size in the drawing area.
1649 gboolean
1650 gd_scroll(const Cave *cave, gboolean exact_scroll)
1652 static int scroll_desired_x=0, scroll_desired_y=0;
1653 static int scroll_speed_x=0, scroll_speed_y=0;
1654 gboolean out_of_window;
1655 int player_x, player_y, visible_x, visible_y;
1656 gboolean changed;
1657 int scroll_divisor;
1659 player_x=cave->player_x-cave->x1; /* cell coordinates of player */
1660 player_y=cave->player_y-cave->y1;
1661 visible_x=(cave->x2-cave->x1+1)*cell_size; /* pixel size of visible part of the cave (may be smaller in intermissions) */
1662 visible_y=(cave->y2-cave->y1+1)*cell_size;
1664 changed=FALSE;
1665 scroll_divisor=12; /* some sort of scrolling speed */
1666 if (gd_fine_scroll)
1667 scroll_divisor*=2; /* as fine scrolling is 50hz, whereas normal is 25hz only */
1668 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_x, scroll_divisor))
1669 changed=TRUE;
1670 if (cave_scroll(visible_y, play_area_h, player_y*cell_size+cell_size/2-play_area_h/2, exact_scroll, play_area_h/4, play_area_h/8, &scroll_y, &scroll_desired_y, &scroll_speed_y, scroll_divisor))
1671 changed=TRUE;
1673 /* if scrolling, we should update entire gd_screen. */
1674 if (changed) {
1675 int x, y;
1677 for (y=0; y<gd_gameplay.cave->h; y++)
1678 for (x=0; x<gd_gameplay.cave->w; x++)
1679 gd_gameplay.gfx_buffer[y][x] |= GD_REDRAW;
1682 /* check if active player is visible at the moment. */
1683 out_of_window=FALSE;
1684 /* check if active player is outside drawing area. if yes, we should wait for scrolling */
1685 if ((player_x*cell_size)<scroll_x || (player_x*cell_size+cell_size-1)>scroll_x+play_area_w)
1686 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1687 if (cave->player_x>=cave->x1 && cave->player_x<=cave->x2)
1688 out_of_window=TRUE;
1689 if ((player_y*cell_size)<scroll_y || (player_y*cell_size+cell_size-1)>scroll_y+play_area_h)
1690 /* but only do the wait, if the player SHOULD BE visible, ie. he is inside the defined visible area of the cave */
1691 if (cave->player_y>=cave->y1 && cave->player_y<=cave->y2)
1692 out_of_window=TRUE;
1694 /* 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. */
1695 if (cave->player_state==GD_PL_NOT_YET)
1696 return FALSE;
1697 return out_of_window;
1704 gd_drawcave(SDL_Surface *dest, const Cave *cave, int **gfx_buffer)
1706 int x, y, xd, yd;
1707 SDL_Rect cliprect;
1708 int scroll_y_aligned;
1710 /* on-screen clipping rectangle */
1711 cliprect.x=0;
1712 cliprect.y=statusbar_height;
1713 cliprect.w=play_area_w;
1714 cliprect.h=play_area_h;
1715 SDL_SetClipRect(dest, &cliprect);
1717 /* for the paused parameter, we set FALSE here, as the sdl version does not color the playfield when paused */
1718 if (gd_sdl_pal_emulation && gd_even_line_pal_emu_vertical_scroll)
1719 /* make it even (dividable by two) */
1720 scroll_y_aligned=scroll_y/2*2;
1721 else
1722 scroll_y_aligned=scroll_y;
1723 /* here we draw all cells to be redrawn. we do not take scrolling area into consideration - sdl will do the clipping. */
1724 for (y=cave->y1, yd=0; y<=cave->y2; y++, yd++) {
1725 for (x=cave->x1, xd=0; x<=cave->x2; x++, xd++) {
1726 if (gd_gameplay.gfx_buffer[y][x] & GD_REDRAW) { /* if it needs to be redrawn */
1727 SDL_Rect offset;
1729 offset.y=y*cell_size+statusbar_height-scroll_y_aligned; /* sdl_blitsurface destroys offset, so we have to set y here, too. (ie. in every iteration) */
1730 offset.x=x*cell_size-scroll_x;
1732 gd_gameplay.gfx_buffer[y][x]=gd_gameplay.gfx_buffer[y][x] & ~GD_REDRAW; /* now we have drawn it */
1734 SDL_BlitSurface(cells[gfx_buffer[y][x]], NULL, dest, &offset);
1739 /* restore clipping to whole screen */
1740 SDL_SetClipRect(dest, NULL);
1741 return 0;