diff options
author | H. Peter Anvin <hpa@zytor.com> | 2003-03-24 16:31:19 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2003-03-24 16:31:19 +0000 |
commit | 1128ad360b5cccb1b82de092505e5ca1c4dbed8d (patch) | |
tree | 89c7ebe649b47fa1b901872d646a2183df023c8d /graphics.c | |
download | grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.gz grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.tar.xz grv-1128ad360b5cccb1b82de092505e5ca1c4dbed8d.zip |
Port of "grävning" to C/SDL, started 2003-03-22
Diffstat (limited to 'graphics.c')
-rw-r--r-- | graphics.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/graphics.c b/graphics.c new file mode 100644 index 0000000..155d628 --- /dev/null +++ b/graphics.c @@ -0,0 +1,409 @@ +/* + * graphics.c + * + * Graphics function emulation + * This pretty much attempts to emulate the QuickBasic-style text screen + * in 25x40 or 25x80 mode with no scrolling... + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include "SDL.h" +#include "grv.h" +#include "graphics.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +#define TS_WIDTH 80 +#define TS_HEIGHT 25 + +#define FONT_XSIZE 8 +#define FONT_YSIZE 14 + +#define PX_WIDTH (TS_WIDTH*FONT_XSIZE) +#define PX_HEIGHT (TS_HEIGHT*FONT_YSIZE) + +typedef uint8_t font_t; +extern font_t grv_font[256][FONT_YSIZE]; + +#define NCOLORS 16 + +static uint32_t colors[NCOLORS]; + +static struct rgba { uint8_t a, r, g, b; } rgbcolors[NCOLORS] = { + {0x00,0x00,0x00,0x00}, /* black */ + {0x00,0x00,0x00,0xaa}, /* dark blue */ + {0x00,0x00,0xaa,0x00}, /* dark green */ + {0x00,0x00,0xaa,0xaa}, /* dark cyan */ + {0x00,0xaa,0x00,0x00}, /* dark red */ + {0x00,0xaa,0x00,0xaa}, /* dark purple */ + {0x00,0xaa,0x55,0x00}, /* brown */ + {0x00,0xaa,0xaa,0xaa}, /* light grey */ + {0x00,0x55,0x55,0x55}, /* dark grey */ + {0x00,0x55,0x55,0xff}, /* light blue */ + {0x00,0x55,0xff,0x55}, /* light green */ + {0x00,0x55,0xff,0xff}, /* light cyan */ + {0x00,0xff,0x55,0x55}, /* light red */ + {0x00,0xff,0x55,0xff}, /* light purple */ + {0x00,0xff,0xff,0x55}, /* yellow */ + {0x00,0xff,0xff,0xff}, /* white */ +}; + +struct screen_char { + uint8_t sym; /* Symbol code */ + uint8_t attr; /* Attribute code */ +}; + +static struct screen_char screendata[TS_HEIGHT][TS_WIDTH]; + +static struct current_screen { + int x; + int y; + uint8_t attr; + uint8_t dwidth; /* Double width characters, must be 0 or 1 */ +} current = {0, 0, 7, 1}; /* Current screen parameters */ + +static SDL_Surface *rscreen; +static volatile uint8_t blink_mask = 0; + +/* + * Set the attribute: fg, bg + * For compatiblity's sake the flashing attribute is in fg, not bg + */ +void color(int fg, int bg) +{ + current.attr = (fg & 15) | ((bg & 7) << 4) | ((fg & 16) << 3); +} + +/* + * Set the cursor position (1-based for compatiblity) + */ +void locate(int r, int c) +{ + current.y = r-1; + current.x = (c-1) << current.dwidth; +} + +/* + * Get the symbol at a specific screen coordinate + */ +int screen(int r, int c) +{ + return screendata[r-1][(c-1) << current.dwidth].sym; +} + +/* + * Get the attribute at a specific screen coordinate + */ +int screen1(int r, int c) +{ + return screendata[r-1][(c-1) << current.dwidth].attr; +} + +/* + * Prepare screen for modification + */ +static void lock_screen(void) +{ + SDL_LockSurface(rscreen); +} + +/* + * Update the on-screen structure to match the screendata[] + * for character (tx,ty), but don't refresh the rectangle just + * yet... + * + * tx,ty is zero-based and must be adjusted for doublewidth mode + */ +static void put_screen(int tx, int ty) +{ + font_t *fontp; + font_t v; + uint32_t *pixelp, fgp, bgp; + int x, y, z; + int attr; + int pxwid = current.dwidth + 1; + int bmask = blink_mask; + + attr = screendata[ty][tx].attr; + fontp = grv_font[screendata[ty][tx].sym]; + + bgp = colors[(attr >> 4) & 7]; + fgp = (bmask & attr) ? bgp : colors[attr & 15]; + + pixelp = ((uint32_t *) rscreen->pixels) + + ty*(FONT_XSIZE*FONT_YSIZE*TS_WIDTH) + + tx*FONT_XSIZE; + + for ( y = 0 ; y < FONT_YSIZE ; y++ ) { + v = fontp[y]; + for ( x = 0 ; x < FONT_XSIZE ; x++ ) { + for ( z = 0 ; z < pxwid ; z++ ) + *pixelp++ = v & ((font_t)1 << (FONT_XSIZE-1)) ? fgp : bgp; + v <<= 1; + } + pixelp += PX_WIDTH-pxwid*FONT_XSIZE; + } +} + +/* + * This routine switches the blink status, then goes around the screen + * and updates all characters which has the blink attribute set. + */ +void update_blink(void) +{ + int x, y, xs; + int gx, gy, gw, gh; + SDL_Rect rects[TS_WIDTH*TS_HEIGHT]; /* Absolute maximum needed */ + SDL_Rect *rect = rects-1; + int nrects = 0; + + blink_mask ^= 0x80; + + xs = current.dwidth+1; + + SDL_LockSurface(rscreen); + + gw = FONT_XSIZE << current.dwidth; + gh = FONT_YSIZE; + + for ( y = 0, gy = 0 ; y < TS_HEIGHT ; y++, gy += gh ) { + for ( x = 0, gx = 0 ; x < TS_WIDTH ; x += xs, gx += gw ) { + if ( screendata[y][x].attr & 0x80 ) { + put_screen(x,y); + if ( !nrects || rect->y != gy || rect->x+rect->w != gx ) { + nrects++; + rect++; + rect->x = gx; rect->y = gy; + rect->w = gw; rect->h = gh; + } else { + rect->w += gw; + } + } + } + } + + SDL_UnlockSurface(rscreen); + + if ( nrects ) + SDL_UpdateRects(rscreen, nrects, rects); +} + +/* + * Refresh rectangle and unlock screen + * Coordinates are inclusive and must be adjusted for double-pixel mode + */ +static void update_screen(int x0, int y0, int x1, int y1) +{ + SDL_UnlockSurface(rscreen); + + SDL_UpdateRect(rscreen, x0*FONT_XSIZE, y0*FONT_YSIZE, + (x1-x0+1)*FONT_XSIZE, (y1-y0+1)*FONT_YSIZE); +} + +/* + * Display a string of characters and advance cursor + */ +void print(const char *str) +{ + const unsigned char *p = (const unsigned char *)str; + unsigned char ch; + int x = current.x; + int y = current.y; + uint8_t attr = current.attr; + int x0 = x; int y0 = y; + int x1 = x; int y1 = y; + int xwid; + + if ( !*str ) + return; + + xwid = current.dwidth+1; + + lock_screen(); + + while ( (ch = *p++) ) { + screendata[y][x].sym = ch; + screendata[y][x].attr = attr; + put_screen(x,y); + x0 = min(x0,x); y0 = min(y0,y); + x1 = max(x1,x+xwid-1); y1 = max(y1,y); + x += xwid; + if ( x >= TS_WIDTH ) { + x = 0; y++; + if ( y >= TS_HEIGHT ) { + y = 0; + } + } + } + update_screen(x0,y0,x1,y1); + + current.x = x; current.y = y; +} + +/* + * Same except with printf-style formatting + */ +void gprintf(const char *fmt, ...) +{ + unsigned char buffer[4096]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + print(buffer); +} + +/* + * locate() and print() as a combined function + */ +void lprint(int r, int c, const char *str) +{ + locate(r,c); + print(str); +} + +/* + * locate() and gprintf() as a combined function + */ +void lprintf(int r, int c, const char *fmt, ...) +{ + unsigned char buffer[4096]; + va_list ap; + + locate(r,c); + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + print(buffer); +} + +/* + * Set 40/80 character mode. Unlike hardware this doesn't change the + * text currently on the screen, but funny things can happen; cls() is + * highly recommended. + */ +void width(int wid) +{ + switch(wid) { + case 40: + current.dwidth = 1; + current.x &= ~1; + break; + case 80: + current.dwidth = 0; + break; + default: + abort(); + } +} + +/* + * Clear the screen, set all characters to ' ' and set all pixels to the + * current background + */ +void cls(void) +{ + int i; + struct screen_char *scp = &screendata[0][0]; + struct screen_char empty; + uint32_t pixelval; + SDL_Rect rect; + + lock_screen(); + + empty.sym = ' '; + empty.attr = current.attr; + + pixelval = colors[(current.attr >> 4) & 7]; + + for ( i = TS_WIDTH*TS_HEIGHT ; i ; i-- ) + *scp++ = empty; + + rect.x = rect.y = 0; + rect.w = PX_WIDTH; + rect.h = PX_HEIGHT; + + SDL_FillRect(rscreen, &rect, pixelval); + + update_screen(0, 0, TS_WIDTH-1, TS_HEIGHT-1); + + current.x = current.y = 0; +} + + +/* + * Save the screen as well as "current" in a malloc'd buffer + */ +struct saved_screen +{ + struct current_screen current; + struct screen_char screendata[TS_HEIGHT][TS_WIDTH]; +}; + +struct saved_screen *save_screen(void) +{ + struct saved_screen *save = malloc(sizeof(struct saved_screen)); + + if ( !save ) + return NULL; + + memcpy(&save->current, ¤t, sizeof(current)); + memcpy(&save->screendata, &screendata, sizeof(screendata)); + + return save; +} + +void restore_screen(struct saved_screen *save) +{ + int x, y, xs; + + memcpy(¤t, &save->current, sizeof(current)); + memcpy(&screendata, &save->screendata, sizeof(screendata)); + + xs = current.dwidth+1; + + lock_screen(); + + for ( y = 0 ; y < TS_HEIGHT ; y++ ) { + for ( x = 0 ; x < TS_WIDTH ; x += xs ) { + put_screen(x,y); + } + } + + update_screen(0, 0, TS_WIDTH-1, TS_HEIGHT-1); +} + +/* + * Initialize SDL and the data structures + */ +int screen_init(void) +{ + int i; + + if ( SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO) ) + return -1; + + if ( !(rscreen = SDL_SetVideoMode(PX_WIDTH, PX_HEIGHT, 32, SDL_SWSURFACE)) ) { + SDL_Quit(); + return -1; + } + + atexit(SDL_Quit); + + /* Convert colors to preferred machine representation */ + for ( i = 0 ; i < NCOLORS ; i++ ) { + colors[i] = SDL_MapRGB(rscreen->format, + rgbcolors[i].r, rgbcolors[i].g, rgbcolors[i].b); + } + + cls(); + + SDL_AddTimer(400, post_periodic, (void *)event_blink); + + return 0; +} |