OSDN Git Service

c45f0f8b28a99548971a169541ec4d13870ecfdb
[nazghul-jp/nazghul-jp.git] / src / screen.c
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
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
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
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
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
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
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
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
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
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
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
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
368 void screenErase(SDL_Rect * rect)
369 {
370         SDL_FillRect(Screen, rect, Black);
371 }
372
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
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
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
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
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. */
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
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
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
587 int screenWidth(void)
588 {
589         return Screen->w;
590 }
591
592 int screenHeight(void)
593 {
594         return Screen->h;
595 }
596
597 SDL_PixelFormat *screenFormat(void)
598 {
599         return Screen->format;
600 }
601
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
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
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
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
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
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
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
895 void screenHighlight(SDL_Rect *area)
896 {
897         screenHighlightColored(area, White);
898 }
899
900 int screenLock(void)
901 {
902         return SDL_LockSurface(Screen);
903 }
904
905 void screenUnlock(void)
906 {
907         SDL_UnlockSurface(Screen);
908 }
909
910 /* assumes the pixel value is gotten from screenMapRGB() i.e. safe! */
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
924 Uint32 screenMapRGB(Uint8 red, Uint8 grn, Uint8 blu)
925 {
926         return SDL_MapRGB(Screen->format, red, grn, blu);
927 }
928
929 void screenZoomOut(int factor)
930 {
931         if (factor)
932                 Zoom *= factor;
933 }
934
935 void screenZoomIn(int factor)
936 {
937         if (factor)
938                 Zoom /= factor;
939 }
940
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 }