1 //
2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 // Suite 330, Boston, MA 02111-1307 USA
18 //
19 // Gordon McNutt
20 // gmcnutt@users.sourceforge.net
21 //
22 #include "screen.h"
23 #include "common.h"
24 #include "ascii.h"
25 #include "sprite.h"
26 #include "status.h"
27 #include "foogod.h"
28 #include "cfg.h"
29 #include "images.h"
30 #include "nazghul.h"	// for FullScreenMode
31 
32 #include <png.h>
33 #include <unistd.h>
34 #include <stdarg.h>
35 #include <assert.h>
36 
37 #include <SDL_image.h>
38 
39 #define N_SHADERS 3
40 #define MAX_SHADER (N_SHADERS - 1)
41 #define SHADER_W STAT_W
42 #define SHADER_H STAT_H_MAX
43 #define HIGHLIGHT_THICKNESS 2
44 
45 /* Frame image indices */
46 #define FRAME_ULC  0
47 #define FRAME_TD   1
48 #define FRAME_URC  2
49 #define FRAME_ENDT 3
50 #define FRAME_TR   4
51 #define FRAME_TX   5
52 #define FRAME_TL   6
53 #define FRAME_VERT 7
54 #define FRAME_LLC  8
55 #define FRAME_TU   9
56 #define FRAME_LRC  10
57 #define FRAME_ENDD 11
58 #define FRAME_ENDL 12
59 #define FRAME_HORZ 13
60 #define FRAME_ENDR 14
61 #define FRAME_DOT  15
62 #define FRAME_NUM_SPRITES 16
63 
64 /* Enable this to dump surfaces and video info */
65 #ifndef SCREEN_DEBUG
66 #define SCREEN_DEBUG 0
67 #endif
68 
69 static SDL_Surface *Screen;
70 static SDL_Surface *Shaders[N_SHADERS];
71 static SDL_Surface *Highlight;
72 static struct sprite *FrameSprites[FRAME_NUM_SPRITES];
73 static int Zoom;
74 static char screen_buf[128];
75 
76 Uint32 Black;
77 Uint32 Blue;
78 Uint32 White;
79 Uint32 Green;
80 Uint32 Red;
81 Uint32 Yellow;
82 Uint32 Cyan;
83 Uint32 Magenta;
84 Uint32 Gray;
85 
86 Uint32 TextRed;
87 Uint32 TextGreen;
88 Uint32 TextBlue;
89 Uint32 TextYellow;
90 Uint32 TextCyan;
91 Uint32 TextMagenta;
92 
93 
94 SDL_Color fontWhite = { 0xff, 0xff, 0xff, 0x00 };
95 SDL_Color fontBlack = { 0, 0, 0, 0 };
96 
97 static void scaled_blit(SDL_Surface * source, SDL_Rect * from,
98 			SDL_Surface * dest, SDL_Rect * to);
99 
screenInitColors(void)100 void screenInitColors(void)
101 {
102 
103 	Black   = SDL_MapRGB(Screen->format, 0x00, 0x00, 0x00);
104 	White   = SDL_MapRGB(Screen->format, 0xff, 0xff, 0xff);
105 	Red     = SDL_MapRGB(Screen->format, 0xff, 0x00, 0x00);
106 	TextRed = SDL_MapRGB(Screen->format, 0xff, 0x99, 0x99);
107 	Green   = SDL_MapRGB(Screen->format, 0x00, 0xff, 0x00);
108 	TextGreen = SDL_MapRGB(Screen->format, 0x99, 0xff, 0x99);
109 	Blue    = SDL_MapRGB(Screen->format, 0x00, 0x00, 0xff);
110 	TextBlue = SDL_MapRGB(Screen->format, 0x99, 0x99, 0xff);
111 	Yellow  = SDL_MapRGB(Screen->format, 0xff, 0xff, 0x00);
112 	TextYellow = SDL_MapRGB(Screen->format, 0xff, 0xff, 0x99);
113 	Cyan    = SDL_MapRGB(Screen->format, 0x00, 0xff, 0xff);
114 	TextCyan = SDL_MapRGB(Screen->format, 0x99, 0xff, 0xff);
115 	Magenta = SDL_MapRGB(Screen->format, 0xff, 0xff, 0x00);
116 	TextMagenta = SDL_MapRGB(Screen->format, 0xff, 0x99, 0xff);
117 	Gray    = SDL_MapRGB(Screen->format, 0x80, 0x80, 0x80);
118 }
119 
dump_SDL_PixelFormat(SDL_PixelFormat * fmt)120 void dump_SDL_PixelFormat(SDL_PixelFormat *fmt)
121 {
122         printf("Pixel Format:\n");
123         printf("     palette: %p\n", fmt->palette);
124         printf("BitsPerPixel: %d\n", fmt->BitsPerPixel);
125         printf("       Rmask: 0x%x\n", fmt->Rmask);
126         printf("       Gmask: 0x%x\n", fmt->Gmask);
127         printf("       Bmask: 0x%x\n", fmt->Bmask);
128         printf("       Amask: 0x%x\n", fmt->Amask);
129         printf("      Rshift: %d\n", fmt->Rshift);
130         printf("      Gshift: %d\n", fmt->Gshift);
131         printf("      Bshift: %d\n", fmt->Bshift);
132         printf("      Ashift: %d\n", fmt->Ashift);
133         printf("       Rloss: %d\n", fmt->Rloss);
134         printf("       Gloss: %d\n", fmt->Gloss);
135         printf("       Bloss: %d\n", fmt->Bloss);
136         printf("       Aloss: %d\n", fmt->Aloss);
137         printf("    colorkey: 0x%x\n", fmt->colorkey);
138         printf("       alpha: 0x%x\n", fmt->alpha);
139 }
140 
dump_SDL_VideoInfo(const SDL_VideoInfo * fmt)141 void dump_SDL_VideoInfo(const SDL_VideoInfo *fmt)
142 {
143         printf("Video Info:\n");
144         printf(" hw_available: %c\n", fmt->hw_available ? 'y' : 'n');
145         printf(" wm_available: %c\n", fmt->wm_available ? 'y' : 'n');
146         printf("      blit_hw: %c\n", fmt->blit_hw ? 'y' : 'n');
147         printf("   blit_hw_CC: %c\n", fmt->blit_hw_CC ? 'y' : 'n');
148         printf("    blit_hw_A: %c\n", fmt->blit_hw_A ? 'y' : 'n');
149         printf("      blit_sw: %c\n", fmt->blit_sw ? 'y' : 'n');
150         printf("   blit_sw_CC: %c\n", fmt->blit_sw_CC ? 'y' : 'n');
151         printf("    blit_sw_A: %c\n", fmt->blit_sw_A ? 'y' : 'n');
152         printf("    blit_fill: %c\n", fmt->blit_fill ? 'y' : 'n');
153         printf("    video_mem: %d\n", fmt->video_mem);
154         dump_SDL_PixelFormat(fmt->vfmt);
155 }
156 
dump_SDL_Surface(SDL_Surface * surf)157 void dump_SDL_Surface(SDL_Surface *surf)
158 {
159         printf("Surface Info:\n");
160         printf("     flags:\n");
161         if (surf->flags & SDL_SWSURFACE)
162                 printf("  SDL_SWSURFACE\n");
163         if (surf->flags & SDL_HWSURFACE)
164                 printf("  SDL_HWSURFACE\n");
165         if (surf->flags & SDL_ASYNCBLIT)
166                 printf("  SDL_ASYNCBLIT\n");
167         if (surf->flags & SDL_ANYFORMAT)
168                 printf("  SDL_ANYFORMAT\n");
169         if (surf->flags & SDL_HWPALETTE)
170                 printf("  SDL_HWPALETTE\n");
171         if (surf->flags & SDL_DOUBLEBUF)
172                 printf("  SDL_DOUBLEBUF\n");
173         if (surf->flags & SDL_FULLSCREEN)
174                 printf("  SDL_FULLSCREEN\n");
175         if (surf->flags & SDL_OPENGL)
176                 printf("  SDL_OPENGL\n");
177         if (surf->flags & SDL_OPENGLBLIT)
178                 printf("  SDL_OPENGLBLIT\n");
179         if (surf->flags & SDL_RESIZABLE)
180                 printf("  SDL_RESIZABLE\n");
181         if (surf->flags & SDL_HWACCEL)
182                 printf("  SDL_HWACCEL\n");
183         if (surf->flags & SDL_SRCCOLORKEY)
184                 printf("  SDL_SRCCOLORKEY\n");
185         if (surf->flags & SDL_RLEACCEL)
186                 printf("  SDL_RLEACCEL\n");
187         if (surf->flags & SDL_SRCALPHA)
188                 printf("  SDL_SRCALPHA\n");
189         if (surf->flags & SDL_PREALLOC)
190                 printf("  SDL_PREALLOC\n");
191         printf("         w: %d\n", surf->w);
192         printf("         h: %d\n", surf->h);
193         printf("     pitch: %d\n", surf->pitch);
194         printf("    pixels: %p\n", surf->pixels);
195         printf(" clip_rect: [%d %d %d %d]\n",
196                surf->clip_rect.x,
197                surf->clip_rect.y,
198                surf->clip_rect.w,
199                surf->clip_rect.h);
200         printf("  refcount: %d\n", surf->refcount);
201         dump_SDL_PixelFormat(surf->format);
202 
203 }
204 
screenInitScreen(void)205 void screenInitScreen(void)
206 {
207 	Uint32 flags = SDL_ANYFORMAT;
208 	const SDL_VideoInfo *fmt;
209 
210         const int SCREEN_BPP = 0;	/* use display BPP */
211 
212 	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
213 		perror_sdl("SDL_Init");
214 		exit(-1);
215 	}
216 	atexit(SDL_Quit);
217 
218 	fmt = SDL_GetVideoInfo();
219 	if (!fmt) {
220 		perror_sdl("SDL_GetVideoInfo");
221 		exit(-1);
222 	}
223 
224         if (SCREEN_DEBUG) {
225                 dump_SDL_VideoInfo(fmt);
226         }
227 
228 	if (fmt->blit_hw_CC && fmt->blit_fill) {
229 		flags |= SDL_HWSURFACE;
230 		flags |= SDL_DOUBLEBUF;
231 	}
232 	if (FullScreenMode) {
233 		flags |= SDL_FULLSCREEN;
234 	}
235 
236 	Screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, SCREEN_BPP, flags);
237 	if (!Screen) {
238 		perror_sdl("SDL_SetVideoMode");
239 		exit(-1);
240 	}
241 
242         if (SCREEN_DEBUG) {
243                 printf("Video initialized to...\n");
244                 dump_SDL_Surface(Screen);
245         }
246 
247 	SDL_WM_SetCaption(APPLICATION_NAME, APPLICATION_NAME);
248 }
249 
screen_fade_surface(SDL_Surface * surf,int fade_level)250 void screen_fade_surface(SDL_Surface * surf, int fade_level)
251 {
252 	int x, y;
253 	Uint8 *pix;
254 	Uint8 trans;
255 	int base;
256 	int toggle;
257 
258 	assert(surf->format->BitsPerPixel == 8);
259 
260 	if (fade_level == 0)
261 		return;
262 
263 	pix = (Uint8 *) surf->pixels;
264 	trans = (Uint8) surf->format->colorkey;
265 
266 	for (y = 0; y < surf->h; y++) {
267 		base = y * surf->pitch;
268 		toggle = y % 2;
269 		for (x = 0; x < surf->w; x++) {
270 			int i = base + x;
271 			if (toggle) {
272 				if (pix[i] != trans) {
273 					toggle = 0;
274 					pix[i] = trans;
275 				}
276 			} else if (pix[i] != trans) {
277 				toggle = 1;
278 			}
279 		}		// for (x)
280 	}			// for (y)
281 
282 	assert(surf->format->BitsPerPixel == 8);
283 
284 	screen_fade_surface(surf, fade_level - 1);
285 }
286 
create_shader(int fade_level)287 static SDL_Surface *create_shader(int fade_level)
288 {
289 	SDL_Surface *shader;
290 
291 	shader = screenCreateSurface(SHADER_W, SHADER_H);
292 	if (shader == NULL)
293 		return NULL;
294 
295 	SDL_FillRect(shader, NULL, SDL_MapRGBA(shader->format, 0, 0, 0, 0));
296 
297 	if (shader->format->palette != NULL) {
298 		SDL_LockSurface(shader);
299 		screen_fade_surface(shader, fade_level);
300 		SDL_UnlockSurface(shader);
301 	}
302 
303 	return shader;
304 }
305 
screenInitShader(void)306 void screenInitShader(void)
307 {
308 	int n, i;
309 
310 	n = (Screen->format->BitsPerPixel == 8) ? N_SHADERS : 1;
311 
312 	for (i = 0; i < n; i++) {
313 		Shaders[i] = create_shader(i);
314 	}
315 }
316 
screenInitHighlight(void)317 void screenInitHighlight(void)
318 {
319 	Highlight = screenCreateSurface(SHADER_W, SHADER_H);
320         assert(Highlight != NULL);
321 
322 	SDL_FillRect(Highlight, NULL, SDL_MapRGBA(Highlight->format, 255, 255, 255, 0));
323 
324 	if (Highlight->format->palette != NULL) {
325 		SDL_LockSurface(Highlight);
326 		screen_fade_surface(Highlight, 4);
327 		SDL_UnlockSurface(Highlight);
328 	}
329 }
330 
screenInitFrame(void)331 static void screenInitFrame(void)
332 {
333         int i;
334         char *fname = cfg_get("frame-image-filename");
335         struct images *ss_frame = 0;
336 
337         if (!fname) {
338                 warn("No frame image filename!");
339                 return;
340         }
341 
342         memset(FrameSprites, 0, sizeof(FrameSprites));
343 
344         ss_frame = images_new(0, 16, 16, 4, 4, 0, 0, fname);
345         assert(ss_frame);
346 
347         for (i = 0; i < FRAME_NUM_SPRITES; i++) {
348                 FrameSprites[i] = sprite_new(0, 1, i, 0, 0, ss_frame);
349                 assert(FrameSprites[i]);
350         }
351 }
352 
screenInit(void)353 int screenInit(void)
354 {
355 	screenInitScreen();
356 	screenInitColors();
357 	screenInitShader();
358         screenInitHighlight();
359         screenInitFrame();
360 	Zoom = 1;
361 
362 	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
363 			    SDL_DEFAULT_REPEAT_INTERVAL);
364 
365         return 0;
366 }
367 
screenErase(SDL_Rect * rect)368 void screenErase(SDL_Rect * rect)
369 {
370 	SDL_FillRect(Screen, rect, Black);
371 }
372 
screenFill(SDL_Rect * rect,Uint32 color)373 void screenFill(SDL_Rect * rect, Uint32 color)
374 {
375 	SDL_Rect tmp = *rect;
376 	rect->w /= Zoom;
377 	rect->h /= Zoom;
378 	SDL_FillRect(Screen, rect, color);
379 }
380 
screenUpdate(SDL_Rect * rect)381 void screenUpdate(SDL_Rect * rect)
382 {
383 	if (rect) {
384 		SDL_UpdateRect(Screen, rect->x, rect->y, rect->w, rect->h);
385 	} else {
386 		SDL_Flip(Screen);
387 	}
388 }
389 
390 /* bpp-independent macro to test if a pixel is magenta */
391 #define isTransparent(ff,pp) \
392         (((ff)->Rmask&(pp))==(ff)->Rmask \
393           && ((ff)->Gmask&(pp))==0 \
394           && ((ff)->Bmask&(pp))==(ff)->Bmask)
395 
scaled_blit_32bpp(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to,int spitch,int dpitch)396 static void scaled_blit_32bpp(SDL_Surface * source, SDL_Rect * from,
397 			      SDL_Surface * dest, SDL_Rect * to,
398 			      int spitch, int dpitch)
399 {
400 	int dx, dy, di, sx, sy, si;
401 	Uint32 *d, *s;
402 
403 	d = (Uint32 *) dest->pixels;
404 	s = (Uint32 *) source->pixels;
405 
406 	for (dy = 0; dy < to->h; dy++) {
407 		sy = dy * Zoom;
408 		for (dx = 0; dx < to->w; dx++) {
409 			sx = dx * Zoom;
410 			di = (dy + to->y) * dpitch + (dx + to->x);
411 			si = (sy + from->y) * spitch + (sx + from->x);
412                         if (!isTransparent(dest->format, s[si]))
413                                 d[di] = s[si];
414 		}		// for (dx)
415 	}			// for (dy)
416 }
417 
scaled_blit_16bpp(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to,int spitch,int dpitch)418 static void scaled_blit_16bpp(SDL_Surface * source, SDL_Rect * from,
419 			      SDL_Surface * dest, SDL_Rect * to,
420 			      int spitch, int dpitch)
421 {
422 	int dx, dy, di, sx, sy, si;
423 	Uint16 *d, *s;
424 
425 	d = (Uint16 *) dest->pixels;
426 	s = (Uint16 *) source->pixels;
427 
428 	for (dy = 0; dy < to->h; dy++) {
429 		sy = dy * Zoom;
430 		for (dx = 0; dx < to->w; dx++) {
431 			sx = dx * Zoom;
432 			di = (dy + to->y) * dpitch + (dx + to->x);
433 			si = (sy + from->y) * spitch + (sx + from->x);
434                         if (! isTransparent(dest->format, s[si]))
435                                 d[di] = s[si];
436 		}		// for (dx)
437 	}			// for (dy)
438 }
439 
scaled_blit_8bpp(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to,int spitch,int dpitch)440 static void scaled_blit_8bpp(SDL_Surface * source, SDL_Rect * from,
441 			     SDL_Surface * dest, SDL_Rect * to,
442 			     int spitch, int dpitch)
443 {
444 	int dx, dy, di, sx, sy, si;
445 	Uint8 *d, *s;
446 
447 	d = (Uint8 *) dest->pixels;
448 	s = (Uint8 *) source->pixels;
449 
450 	for (dy = 0; dy < to->h; dy++) {
451 		sy = dy * Zoom;
452 		for (dx = 0; dx < to->w; dx++) {
453 			sx = dx * Zoom;
454 			di = (dy + to->y) * dpitch + (dx + to->x);
455 			si = (sy + from->y) * spitch + (sx + from->x);
456 			d[di] = s[si];
457 		}		// for (dx)
458 	}			// for (dy)
459 }
460 
461 /* scale_then_blit_normal -- cheesy hack to support scaled blitting of
462  * incompatible surface types. This blits the source to a temporary compatible
463  * surface using the scaled_blit function, then blits the tmp surface to the
464  * screen with Zoom=1. Inefficient but functional. */
scale_then_blit_normal(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to)465 static void scale_then_blit_normal(SDL_Surface * source, SDL_Rect * from,
466                                    SDL_Surface * dest, SDL_Rect * to)
467 {
468         SDL_Surface *tmp = 0;
469         SDL_Rect rect;
470         int o_zoom = Zoom;
471 
472         /* Create a temporary surface for the scaled blit which has the same
473          * format as the source. */
474 	tmp = SDL_CreateRGBSurface(source->flags,
475 				   from->w / Zoom, from->h / Zoom,
476 				   source->format->BitsPerPixel,
477 				   source->format->Rmask,
478 				   source->format->Gmask,
479 				   source->format->Bmask,
480 				   source->format->Amask);
481         if (!tmp) {
482 		perror_sdl("SDL_CreateRGBSurface");
483 		return;
484         }
485 
486         /* Setup a rect for the tmp surface. */
487         rect.x = 0;
488         rect.y = 0;
489         rect.w = to->w;
490         rect.h = to->h;
491 
492         /* Do a scaled_blit from the source to the temporary surface. */
493         scaled_blit(source, from, tmp, &rect);
494 
495         /* Do a normal blit from the tmp surface to the final dest, temporarily
496          * setting Zoom factor to 1 to prevent another call into
497          * scaled_blit(). */
498         o_zoom = Zoom;
499         Zoom = 1;
500         screenBlit(tmp, &rect, to);
501         Zoom = o_zoom;
502 
503         /* Free the tmp surface. */
504 	SDL_FreeSurface(tmp);
505 }
506 
scaled_blit(SDL_Surface * source,SDL_Rect * from,SDL_Surface * dest,SDL_Rect * to)507 static void scaled_blit(SDL_Surface * source, SDL_Rect * from,
508 			SDL_Surface * dest, SDL_Rect * to)
509 {
510 	int dpitch, spitch;
511 
512 	assert(Zoom > 0);
513 
514 	/* This is not a general-purpose blitting routine. If the source and
515          * destination surfaces don't have the same format then use a hack to
516          * workaround it. */
517 	if (source->format->BitsPerPixel != dest->format->BitsPerPixel
518             || source->format->Amask != dest->format->Amask
519                 ) {
520                 scale_then_blit_normal(source, from, dest, to);
521                 return;
522         }
523 
524 	to->w /= Zoom;
525 	to->h /= Zoom;
526 
527 	dpitch = dest->pitch / dest->format->BytesPerPixel;
528 	spitch = source->pitch / source->format->BytesPerPixel;
529 
530 	if (SDL_LockSurface(dest) < 0)
531 		return;
532 
533 	switch (dest->format->BitsPerPixel) {
534 	case 32:
535 		scaled_blit_32bpp(source, from, dest, to, spitch, dpitch);
536 		break;
537 	case 16:
538 		scaled_blit_16bpp(source, from, dest, to, spitch, dpitch);
539 		break;
540 	case 8:
541 		scaled_blit_8bpp(source, from, dest, to, spitch, dpitch);
542 		break;
543 	default:
544 		assert(0);
545 		break;
546 	}
547 
548 	SDL_UnlockSurface(dest);
549 }
550 
screenBlit(SDL_Surface * source,SDL_Rect * from,SDL_Rect * to)551 void screenBlit(SDL_Surface * source, SDL_Rect * from, SDL_Rect * to)
552 {
553 	/* Clipping is really only needed for wave sprites right now. If the
554 	 * following proves to be too expensive on slow machines... */
555 	if (to) {
556 		SDL_Rect _to = *to;
557 		SDL_SetClipRect(Screen, &_to);
558 		if (Zoom > 1) {
559 
560                         // Clients are allowed to pass a NULL from rect,
561                         // indicating they want to blit the whole source
562                         // area. But the scaled blits require a non-NULL
563                         // from rect.
564                         SDL_Rect _from;
565                         if (from == NULL) {
566                                 _from.x = 0;
567                                 _from.y = 0;
568                                 _from.w = source->w;
569                                 _from.h = source->h;
570                                 from = &_from;
571                         }
572 
573 			scaled_blit(source, from, Screen, &_to);
574                 } else {
575 			if (SDL_BlitSurface(source, from, Screen, &_to) < 0)
576 				perror_sdl("SDL_BlitSurface");
577 		}
578 		SDL_SetClipRect(Screen, 0);
579 	} else {
580 		SDL_SetClipRect(Screen, to);
581 		if (SDL_BlitSurface(source, from, Screen, NULL) < 0)
582 			perror_sdl("SDL_BlitSurface");
583 		SDL_SetClipRect(Screen, 0);
584 	}
585 }
586 
screenWidth(void)587 int screenWidth(void)
588 {
589 	return Screen->w;
590 }
591 
screenHeight(void)592 int screenHeight(void)
593 {
594 	return Screen->h;
595 }
596 
screenFormat(void)597 SDL_PixelFormat *screenFormat(void)
598 {
599 	return Screen->format;
600 }
601 
screenFlash(SDL_Rect * rect,int mdelay,Uint32 color)602 void screenFlash(SDL_Rect * rect, int mdelay, Uint32 color)
603 {
604 	screenFill(rect, color);
605 	screenUpdate(rect);
606 	//usleep(mdelay * 1000);
607         SDL_Delay(mdelay);
608 }
609 
screenPrint(SDL_Rect * rect,int flags,const char * fmt,...)610 void screenPrint(SDL_Rect * rect, int flags, const char *fmt, ...)
611 {
612 	va_list args;
613 	int i;
614 	int x = rect->x;
615 	int y = rect->y;
616 	int alen, slen, stop;
617 
618         /* Print the string to a buffer. */
619 	va_start(args, fmt);
620 	vsnprintf(screen_buf, sizeof(screen_buf), fmt, args);
621 	va_end(args);
622 
623 	slen = strlen(screen_buf);
624         alen = asciiStrlen(screen_buf);
625         stop = rect->x + (rect->w * ASCII_W);
626 
627 	/* If painting on the border then first fill the line with the border
628          * image. */
629 	if (flags & SP_ONBORDER) {
630 		for (x = rect->x; x < rect->x + rect->w; x += BORDER_W)
631 			sprite_paint(FrameSprites[FRAME_HORZ], 0, x, rect->y);
632 	}
633 
634         /* Calculate offset for center and right-justified cases */
635 	if (flags & SP_CENTERED) {
636 		int w = alen * ASCII_W;
637 		if (w > rect->w) {
638 			w = rect->w;
639 		}
640 		x = (rect->w - w) / 2 + rect->x;
641 	} else if (flags & SP_RIGHTJUSTIFIED) {
642 		int w = alen * ASCII_W;
643 		if (w > rect->w) {
644 			w = rect->w;
645 		}
646 		x = (rect->w - w) + rect->x;
647 	}
648 
649 	/* If painting on the border, then paint the right stub
650          * to the left of the text. */
651 	if (flags & SP_ONBORDER) {
652 		sprite_paint(FrameSprites[FRAME_ENDR], 0, x - BORDER_W, rect->y);
653         }
654 
655         /* Paint the characters until we run out or hit the end of the
656          * region. */
657 	for (i = 0; i < slen && x < stop; i++) {
658 
659                 if (asciiPaint(screen_buf[i], x, y, Screen)) {
660 
661                         /* Move right. */
662                         x += ASCII_W;
663                 }
664 	}
665 
666 	/* If painting on the border, then paint the left stub
667          * to the right of the text. */
668 	if (flags & SP_ONBORDER) {
669 		sprite_paint(FrameSprites[FRAME_ENDL], 0, x, rect->y);
670         }
671 }
672 
screen_repaint_frame(void)673 void screen_repaint_frame(void)
674 {
675 	int i;
676 
677 	// First draw the top and bottom horizontal bars. Leave gaps for the
678 	// sky and wind windows. Originally I went ahead and painted over them
679 	// here, relying on their update routines to black out their
680 	// backgrounds. But when I started using the tall/short mode for the
681 	// status window I found that this was no longer good enough. The
682 	// backgrounds of these windows tended to flash when switching mode.
683 
684 	// Draw the top bar from the top left corner to the sky window.
685 	for (i = 0; i < SKY_X - BORDER_W; i += BORDER_W)
686 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, 0);
687 
688 	// Draw the top bar from the sky window to the left edge of the status
689 	// window's title.
690 	for (i = SKY_X + SKY_W + BORDER_W; i < STAT_X; i += BORDER_W)
691 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, 0);
692 
693 	// Draw the bottom of the map from the left edge to the wind window.
694 	for (i = 0; i < (int) (WIND_X - BORDER_W); i += BORDER_W)
695 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, MAP_X + MAP_H);
696 
697 	// Draw the bottom of the map from the wind window to the left edge of
698 	// the console window.
699 	for (i = WIND_X + WIND_W + BORDER_W; i < CONS_X - BORDER_W;
700 	     i += BORDER_W)
701 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, MAP_X + MAP_H);
702 
703 	// Draw the bar across the bottom of the screen.
704 	for (i = 0; i < SCREEN_W; i += BORDER_W)
705 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, SCREEN_H - BORDER_H);
706 
707 	// Next draw the bottom of the status and food/gold window.
708 	for (i = (MAP_X + MAP_W); i < SCREEN_W; i += BORDER_W) {
709 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i, STAT_Y + status_get_h());
710 		sprite_paint(FrameSprites[FRAME_HORZ], 0, i,
711 			    foogod_get_y() + FOOGOD_H);
712 	}
713 
714 	// Next rough in all the vertical lines.
715 	for (i = 0; i < SCREEN_H; i += BORDER_H) {
716 		sprite_paint(FrameSprites[FRAME_VERT], 0, 0, i);
717 		sprite_paint(FrameSprites[FRAME_VERT], 0, MAP_X + MAP_W, i);
718 		sprite_paint(FrameSprites[FRAME_VERT], 0, SCREEN_W - BORDER_W, i);
719 	}
720 
721 	// Now paint the four corner pieces
722 	sprite_paint(FrameSprites[FRAME_ULC], 0, 0, 0);
723 	sprite_paint(FrameSprites[FRAME_URC], 0, SCREEN_W - BORDER_W, 0);
724 	sprite_paint(FrameSprites[FRAME_LLC], 0, 0, SCREEN_H - BORDER_H);
725 	sprite_paint(FrameSprites[FRAME_LRC], 0, SCREEN_W - BORDER_W,
726 		    SCREEN_H - BORDER_H);
727 
728 	// Then all the right-facing tee-joints
729 	sprite_paint(FrameSprites[FRAME_TR], 0, 0, MAP_Y + MAP_H);
730 	sprite_paint(FrameSprites[FRAME_TR], 0, MAP_X + MAP_W,
731 		    STAT_Y + status_get_h());
732 	sprite_paint(FrameSprites[FRAME_TR], 0, MAP_X + MAP_W,
733 		    foogod_get_y() + FOOGOD_H);
734 
735 	// Then all the left-facing tee-joints
736 	sprite_paint(FrameSprites[FRAME_TL], 0, MAP_X + MAP_W, MAP_Y + MAP_H);
737 	sprite_paint(FrameSprites[FRAME_TL], 0, SCREEN_W - BORDER_W,
738 		    STAT_Y + status_get_h());
739 	sprite_paint(FrameSprites[FRAME_TL], 0, SCREEN_W - BORDER_W,
740 		    foogod_get_y() + FOOGOD_H);
741 
742 	// Then the downward and upward-facing tee-joints
743 	sprite_paint(FrameSprites[FRAME_TD], 0, MAP_X + MAP_W, 0);
744 	sprite_paint(FrameSprites[FRAME_TU], 0, MAP_X + MAP_W, SCREEN_H - BORDER_H);
745 
746 	// And then the stubs around the sky section
747 	sprite_paint(FrameSprites[FRAME_ENDR], 0, SKY_X - BORDER_W, 0);
748 	sprite_paint(FrameSprites[FRAME_ENDL], 0, SKY_X + SKY_W, 0);
749 
750 	// And finally stubs around the wind section
751 	sprite_paint(FrameSprites[FRAME_ENDR], 0, WIND_X - BORDER_W, MAP_X + MAP_H);
752 	sprite_paint(FrameSprites[FRAME_ENDL], 0, WIND_X + WIND_W, MAP_X + MAP_H);
753 
754         // And some stubs around the status title section
755 	sprite_paint(FrameSprites[FRAME_ENDR], 0, STAT_X, 0);
756 	sprite_paint(FrameSprites[FRAME_ENDL], 0, STAT_X + STAT_W - BORDER_W,   0);
757 
758 	screenUpdate(0);
759 
760 }
761 
screenCreateSurface(int w,int h)762 SDL_Surface *screenCreateSurface(int w, int h)
763 {
764 	SDL_Surface *surf = NULL, *tmp;
765 
766 	tmp = SDL_CreateRGBSurface(Screen->flags,
767 				   w, h,
768 				   Screen->format->BitsPerPixel,
769 				   Screen->format->Rmask,
770 				   Screen->format->Gmask,
771 				   Screen->format->Bmask,
772 				   Screen->format->Amask);
773 
774 	// surf->format->palette = Screen->format->palette;
775 
776 	if (tmp == NULL) {
777 		perror_sdl("SDL_CreateRGBSurface");
778 		return NULL;
779 	}
780 
781 	surf = SDL_DisplayFormat(tmp);
782 	SDL_FreeSurface(tmp);
783 
784 	if (surf == NULL) {
785 		perror_sdl("SDL_DisplayFormat");
786 		return NULL;
787 	}
788 
789 	if (surf->format->palette) {
790 		SDL_SetColorKey(surf, SDL_SRCCOLORKEY,
791 				SDL_MapRGB(surf->format, 0xFF, 0x00, 0xFF));
792 	}
793 
794 	return surf;
795 }
796 
screenCopy(SDL_Rect * from,SDL_Rect * to,SDL_Surface * dest)797 void screenCopy(SDL_Rect * from, SDL_Rect * to, SDL_Surface * dest)
798 {
799 	if (SDL_BlitSurface(Screen, from, dest, to) < 0)
800 		perror_sdl("SDL_BlitSurface");
801 
802         assert(from);
803         assert(dest);
804 
805         SDL_Rect _from = *from;
806 
807         if (Zoom > 1) {
808                 // Clients are allowed to pass a NULL 'to' rect,
809                 // indicating they want to blit the whole dest
810                 // area. But the scaled blits require a non-NULL
811                 // 'to' rect.
812                 SDL_Rect _to;
813                 if (to == NULL) {
814                         _to.x = 0;
815                         _to.y = 0;
816                         _to.w = dest->w;
817                         _to.h = dest->h;
818                         to = &_to;
819                 }
820                 scaled_blit(Screen, &_from, dest, to);
821         } else {
822                 if (SDL_BlitSurface(Screen, &_from, dest, to) < 0)
823                         perror_sdl("SDL_BlitSurface");
824         }
825 }
826 
screenShade(SDL_Rect * area,unsigned char amount)827 void screenShade(SDL_Rect * area, unsigned char amount)
828 {
829 	SDL_Surface *shade;
830 
831 	assert(area->w <= SHADER_W);
832 	assert(area->h <= SHADER_H);
833 
834 	if (amount == 0)
835 		return;
836 
837 	if (Screen->format->BitsPerPixel == 8) {
838 		shade = Shaders[MAX_SHADER - (amount * MAX_SHADER) / 255];
839 	} else {
840 		shade = Shaders[0];
841 		SDL_SetAlpha(shade, SDL_SRCALPHA, amount);
842 	}
843 	screenBlit(shade, NULL, area);
844 }
845 
screenHighlightColored(SDL_Rect * area,Uint32 color)846 void screenHighlightColored(SDL_Rect * area, Uint32 color)
847 {
848         SDL_Rect edge;
849 
850         // ---------------------------------------------------------------------
851         // Top edge
852         // ---------------------------------------------------------------------
853 
854         edge.x = area->x;
855         edge.y = area->y;
856         edge.w = area->w;
857         edge.h = HIGHLIGHT_THICKNESS;
858 
859         screenFill(&edge, color);
860 
861         // ---------------------------------------------------------------------
862         // Bottom edge
863         // ---------------------------------------------------------------------
864 
865         edge.x = area->x;
866         edge.y = area->y + (area->h/Zoom) - HIGHLIGHT_THICKNESS;
867         edge.w = area->w;
868         edge.h = HIGHLIGHT_THICKNESS;
869 
870         screenFill(&edge, color);
871 
872         // ---------------------------------------------------------------------
873         // Left edge
874         // ---------------------------------------------------------------------
875 
876         edge.x = area->x;
877         edge.y = area->y;
878         edge.w = HIGHLIGHT_THICKNESS;
879         edge.h = area->h;
880 
881         screenFill(&edge, color);
882 
883         // ---------------------------------------------------------------------
884         // Right edge
885         // ---------------------------------------------------------------------
886 
887         edge.x = area->x + (area->w/Zoom) - HIGHLIGHT_THICKNESS;
888         edge.y = area->y;
889         edge.w = HIGHLIGHT_THICKNESS;
890         edge.h = area->h;
891 
892         screenFill(&edge, color);
893 }
894 
screenHighlight(SDL_Rect * area)895 void screenHighlight(SDL_Rect *area)
896 {
897         screenHighlightColored(area, White);
898 }
899 
screenLock(void)900 int screenLock(void)
901 {
902 	return SDL_LockSurface(Screen);
903 }
904 
screenUnlock(void)905 void screenUnlock(void)
906 {
907 	SDL_UnlockSurface(Screen);
908 }
909 
910 /* assumes the pixel value is gotten from screenMapRGB() i.e. safe! */
screenSetPixel(int x,int y,Uint32 color)911 void screenSetPixel(int x, int y, Uint32 color)
912 {
913 	Uint8 *pix = (Uint8*)(Screen->pixels);
914 	pix += y * Screen->pitch + x * Screen->format->BytesPerPixel;
915 
916 	switch (Screen->format->BytesPerPixel) {
917 	case 4: *(Uint32*)pix = (Uint32)color; break;
918 	case 2: *(Uint16*)pix = (Uint16)color; break;
919 	case 1: *(Uint8 *)pix = (Uint8)color;  break;
920 	default: assert(0); break;
921 	}
922 }
923 
screenMapRGB(Uint8 red,Uint8 grn,Uint8 blu)924 Uint32 screenMapRGB(Uint8 red, Uint8 grn, Uint8 blu)
925 {
926 	return SDL_MapRGB(Screen->format, red, grn, blu);
927 }
928 
screenZoomOut(int factor)929 void screenZoomOut(int factor)
930 {
931 	if (factor)
932 		Zoom *= factor;
933 }
934 
screenZoomIn(int factor)935 void screenZoomIn(int factor)
936 {
937 	if (factor)
938 		Zoom /= factor;
939 }
940 
screenCapture(char * fname,SDL_Rect * rect)941 void screenCapture(char *fname, SDL_Rect *rect)
942 {
943         png_structp png_ptr = 0;
944         png_infop info_ptr = 0;
945         Uint32 *spix = 0;
946         Uint8 *row_pointer = 0;
947         int si, spitch;
948 
949         /* Open the destination file. */
950         FILE *fp = fopen(fname, "wb");
951         if (!fp) {
952                 return;
953         }
954 
955         /* Setup PNG for writing. */
956         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
957                                           (png_voidp)0,
958                                           0,
959                                           0);
960         if (!png_ptr) {
961                 goto done;
962                 return;
963         }
964 
965         info_ptr = png_create_info_struct(png_ptr);
966         if (!info_ptr) {
967                 goto done;
968         }
969 
970         if (setjmp(png_jmpbuf(png_ptr))) {
971                 warn("screenCapture: PNG error!\n");
972                 goto done;
973         }
974 
975         png_init_io(png_ptr, fp);
976 
977         /* Setup the image header. */
978         png_set_IHDR(png_ptr, info_ptr,
979                      MAP_W,
980                      MAP_H,
981                      8,
982                      PNG_COLOR_TYPE_RGB,
983                      PNG_INTERLACE_NONE,
984                      PNG_COMPRESSION_TYPE_DEFAULT,
985                      PNG_FILTER_TYPE_DEFAULT);
986 
987         /* Write the header. */
988         png_write_info(png_ptr, info_ptr);
989 
990 	/* TODO: if Screen is not in correct format, convert it to
991 	 *       a suitable temp surface (maybe a row at the time?).
992 	 */
993         assert(Screen->format->BytesPerPixel==4);
994         /* Grab the screen pixels. */
995         spix = (Uint32*)Screen->pixels;
996         spitch = Screen->pitch / Screen->format->BytesPerPixel;
997 
998         /* Allocate the row buffer. I copy pixels to an intermediate row buffer
999          * so that I can handle different pixel formats (eg, RGBA vs ARGB,
1000          * etc). */
1001         row_pointer = (Uint8*)malloc(rect->w * 3);
1002         assert(row_pointer);
1003 
1004         for (int y = 0; y < rect->h; y++) {
1005 
1006                 /* Copy the SDL pixels into the intermediate buffer. PNG
1007                  * expects pixels in RGB order. */
1008                 Uint8 *dpix = row_pointer;
1009                 for (int x = 0; x < rect->w; x++) {
1010                         si = (y + rect->y) * spitch + (x + rect->x);
1011                         *dpix++ = ((spix[si] & Screen->format->Rmask)
1012                                    >> Screen->format->Rshift);
1013                         *dpix++ = ((spix[si] & Screen->format->Gmask)
1014                                    >> Screen->format->Gshift);
1015                         *dpix++ = ((spix[si] & Screen->format->Bmask)
1016                                    >> Screen->format->Bshift);
1017                 }
1018 
1019                 /* Write the row to PNG. */
1020                 png_write_row(png_ptr, row_pointer);
1021         }
1022 
1023         png_write_end(png_ptr, 0);
1024 
1025  done:
1026         if (row_pointer) {
1027                 free(row_pointer);
1028         }
1029 
1030         if (png_ptr) {
1031                 png_destroy_write_struct(&png_ptr, &info_ptr);
1032         }
1033 
1034         fclose(fp);
1035 
1036 }
1037