/* * ============================================================================ * Title: Graphics Interface Routines * Author: J. Zbiciak, J. Tanner * $Id: gfx.c,v 1.18 2001/02/03 02:34:21 im14u2c Exp $ * ============================================================================ * GFX_INIT -- Initializes a gfx_t object. * GFX_TICK -- Services a gfx_t tick. * GFX_VID_ENABLE -- Alert gfx that video has been enabled or blanked * GFX_SET_BORD -- Set the border / offset parameters for the display * ============================================================================ * GFX_T -- Graphics subsystem object. * GFX_PVT_T -- Private internal state to gfx_t structure. * GFX_STIC_PALETTE -- The STIC palette. * ============================================================================ * The graphics subsystem provides an abstraction layer between the * emulator and the graphics library being used. Theoretically, this * should allow easy porting to other graphics libraries. * * TODO: * -- Make use of dirty rectangle updating for speed. * ============================================================================ */ static const char rcs_id[]="$Id: gfx.c,v 1.18 2001/02/03 02:34:21 im14u2c Exp $"; #include "sdl.h" #include "config.h" #include "periph/periph.h" #include "gfx.h" #include "file/file.h" #include "mvi/mvi.h" #include "gif/gif_enc.h" /* * ============================================================================ * GFX_PVT_T -- Private internal state to gfx_t structure. * ============================================================================ */ typedef struct gfx_pvt_t { SDL_Surface *scr; /* Screen surface. */ SDL_Color pal_on [32]; /* Palette when video is enabled. */ SDL_Color pal_off[32]; /* Palette when video is blanked. */ int vid_enable; /* Video enable flag. */ int ofs_x, ofs_y; /* X/Y offsets for centering img. */ int bpp; /* Actual color depth. */ int flags; /* Flags for current display surf. */ int des_x, des_y, des_bpp; /* Remember desired dims. */ int movie_init; /* Is movie structure initialized? */ mvi_t *movie; /* Pointer to mvi_t to reduce deps */ } gfx_pvt_t; /* * ============================================================================ * GFX_STIC_PALETTE -- The STIC palette. * ============================================================================ */ LOCAL uint_8 gfx_stic_palette[32][3] = { /* -------------------------------------------------------------------- */ /* I generated these colors by directly eyeballing my television */ /* while it was next to my computer monitor. I then tweaked each */ /* color until it was pretty close to my TV. Bear in mind that */ /* NTSC (said to mean "Never The Same Color") is highly susceptible */ /* to Tint/Brightness/Contrast settings, so your mileage may vary */ /* with this particular pallete setting. */ /* -------------------------------------------------------------------- */ { 0x00, 0x00, 0x00 }, { 0x00, 0x2D, 0xFF }, { 0xFF, 0x3D, 0x10 }, { 0xC9, 0xCF, 0xAB }, { 0x38, 0x6B, 0x3F }, { 0x00, 0xA7, 0x56 }, { 0xFA, 0xEA, 0x50 }, { 0xFF, 0xFC, 0xFF }, { 0xBD, 0xAC, 0xC8 }, { 0x24, 0xB8, 0xFF }, { 0xFF, 0xB4, 0x1F }, { 0x54, 0x6E, 0x00 }, { 0xFF, 0x4E, 0x57 }, { 0xA4, 0x96, 0xFF }, { 0x75, 0xCC, 0x80 }, { 0xB5, 0x1A, 0x58 }, /* -------------------------------------------------------------------- */ /* This pink color is used for drawing rectangles around sprites. */ /* It's a temporary hack. */ /* -------------------------------------------------------------------- */ { 0xFF, 0x80, 0x80 }, /* -------------------------------------------------------------------- */ /* Grey shades used for misc tasks (not currently used). */ /* -------------------------------------------------------------------- */ { 0x11, 0x11, 0x11 }, { 0x22, 0x22, 0x22 }, { 0x33, 0x33, 0x33 }, { 0x44, 0x44, 0x44 }, { 0x55, 0x55, 0x55 }, { 0x66, 0x66, 0x66 }, { 0x77, 0x77, 0x77 }, { 0x88, 0x88, 0x88 }, { 0x99, 0x99, 0x99 }, { 0xAA, 0xAA, 0xAA }, { 0xBB, 0xBB, 0xBB }, { 0xCC, 0xCC, 0xCC }, { 0xDD, 0xDD, 0xDD }, { 0xEE, 0xEE, 0xEE }, { 0xFF, 0xFF, 0xFF }, }; LOCAL uint_32 gfx_stic_palette16[256]; LOCAL void gfx_gen_16bpp(SDL_Surface *scr, SDL_Color pal[32], int s, int e); /* 01234567890123 ** ### #### ### ** # # # # ** ### ### # ** # # # # ** # # #### ### */ LOCAL char* gfx_rec_bmp[5] = { "### #### ###", "# # # # ", "### ### # ", "# # # # ", "# # #### ###" }; /* ======================================================================== */ /* GFX_SDL_ABORT -- Abort due to SDL errors. */ /* ======================================================================== */ LOCAL void gfx_sdl_abort(void) { fprintf(stderr, "gfx/SDL Error:%s\n", SDL_GetError()); exit(1); } /* ======================================================================== */ /* GFX_SETUP_SDL_SURFACE: Do all the dirty SDL dirty work for setting up */ /* the display. This gets called during init, or */ /* when toggling between full-screen and windowed */ /* ======================================================================== */ int gfx_setup_sdl_surface ( gfx_t *gfx, int flags ) { int i; int actual_x, actual_y; int desire_x = gfx->pvt->des_x, desire_y = gfx->pvt->des_y; int desire_bpp = gfx->pvt->des_bpp; uint_32 sdl_flags = 0; SDL_Surface *scr; /* -------------------------------------------------------------------- */ /* Set up the SDL video flags from our flags. */ /* -------------------------------------------------------------------- */ if (desire_bpp != 8) flags &= ~GFX_HWPAL; /* ignore if not 8 bpp */ sdl_flags = flags & GFX_SWSURF ? SDL_SWSURFACE : SDL_HWSURFACE; sdl_flags |= flags & GFX_DBLBUF ? SDL_DOUBLEBUF : 0; sdl_flags |= flags & GFX_ASYNCB ? SDL_ASYNCBLIT : 0; sdl_flags |= flags & GFX_HWPAL ? SDL_HWPALETTE : 0; sdl_flags |= flags & GFX_FULLSC ? SDL_FULLSCREEN : 0; /* -------------------------------------------------------------------- */ /* Try to allocate a screen surface at the desired size, etc. */ /* -------------------------------------------------------------------- */ /* NOTE: This eventually should do better things about finding */ /* resolutions / color depths that we like, etc. For now just be */ /* braindead, even if it means SDL will run our video in "emulation." */ /* -------------------------------------------------------------------- */ jzp_printf("gfx: Searching for video modes near %dx%dx%d with:\n" "gfx: %s surf, %s buf, %s blit, %s pal, %s\n", desire_x, desire_y, desire_bpp, flags & GFX_SWSURF ? "Software" : "Hardware", flags & GFX_DBLBUF ? "Double" : "Single", flags & GFX_ASYNCB ? "Async" : "Sync", flags & GFX_HWPAL ? "Hardware" : desire_bpp!=8 ? "No" : "Software", flags & GFX_FULLSC ? "Full screen" : "Windowed"); jzp_flush(); /* -------------------------------------------------------------------- */ /* JJT: First, the program must check that the video hardware */ /* actually supports the requested resolution. For instance, some */ /* Macs cannot handle 320x200 fullscreen. */ /* */ /* While SDL can try to emulate a low resolution, this feature is */ /* currently broken on SDL for Mac OS X. This program must handle */ /* such emulation itself. */ /* */ /* For now, the program assumes if it can get a mode with the proper */ /* resolution, that mode will suport 8 bits-per-pixel. */ /* Play this on a EGA machine at your own risk. ;-) */ /* -------------------------------------------------------------------- */ #ifndef GP2X { SDL_Rect **available_modes; available_modes = SDL_ListModes(NULL, sdl_flags); /* No available mode! */ if (available_modes == NULL) gfx_sdl_abort(); else /* All modes are available for a windowed display. */ if (available_modes == (SDL_Rect **)-1) { actual_x = desire_x; actual_y = desire_y; } else /* ListModes returns a list sorted largest to smallest. */ /* Find the smallest mode >= the size requested. */ { i = 0; while (available_modes[i] && available_modes[i]->w >= desire_x && available_modes[i]->h >= desire_y ) { i++; } i--; /* No suitable mode available. */ if (i == -1) gfx_sdl_abort(); actual_x = available_modes[i]->w; actual_y = available_modes[i]->h; } } #else actual_x = 320; actual_y = 240; #endif scr = SDL_SetVideoMode(actual_x, actual_y, desire_bpp, sdl_flags); if (scr) gfx->pvt->scr = scr; else return -1; gfx->pvt->ofs_x = (actual_x - desire_x) >> 1; gfx->pvt->ofs_y = (actual_y - desire_y) >> 1; gfx->pvt->bpp = gfx->pvt->scr->format->BitsPerPixel; gfx->pvt->flags = flags; sdl_flags = gfx->pvt->scr->flags; jzp_printf("gfx: Selected: %dx%dx%d with:\n" "gfx: %s surf, %s buf, %s blit, %s pal, %s\n", actual_x, actual_y, gfx->pvt->bpp, sdl_flags & SDL_HWSURFACE ? "Hardware" : "Software", sdl_flags & SDL_DOUBLEBUF ? "Double" : "Single", sdl_flags & SDL_ASYNCBLIT ? "Async" : "Sync", sdl_flags & SDL_HWPALETTE ? "Hardware" : "Software/No", sdl_flags & SDL_FULLSCREEN ? "Full screen" : "Windowed"); /* -------------------------------------------------------------------- */ /* TEMPORARY: Verify that the surface's format is as we expect. This */ /* is just a temporary bit of paranoia to ensure that scr->pixels */ /* is in the format I _think_ it's in. */ /* -------------------------------------------------------------------- */ if ((desire_bpp == 8 && (gfx->pvt->scr->format->BitsPerPixel != 8 || gfx->pvt->scr->format->BytesPerPixel != 1)) || (desire_bpp ==16 && (gfx->pvt->scr->format->BitsPerPixel !=16 || gfx->pvt->scr->format->BytesPerPixel != 2))) { fprintf(stderr,"gfx panic: BitsPerPixel = %d, BytesPerPixel = %d\n", gfx->pvt->scr->format->BitsPerPixel, gfx->pvt->scr->format->BytesPerPixel); return -1; } /* -------------------------------------------------------------------- */ /* New surface will may need palette initialization. */ /* -------------------------------------------------------------------- */ if (gfx->pvt->bpp == 8) SDL_SetColors(gfx->pvt->scr, gfx->pvt->vid_enable ? gfx->pvt->pal_on : gfx->pvt->pal_off, 0, 32); return 0; } /* ======================================================================== */ /* GFX_INIT -- Initializes a gfx_t object. */ /* ======================================================================== */ int gfx_init(gfx_t *gfx, int desire_x, int desire_y, int desire_bpp, int flags) { periph_tick_t gfx_tick = NULL; int i; /* -------------------------------------------------------------------- */ /* Sanity checks and cleanups. */ /* -------------------------------------------------------------------- */ assert(gfx); memset((void*)gfx, 0, sizeof(gfx_t)); /* -------------------------------------------------------------------- */ /* Select the appropriate tick function based on our display res. */ /* For now, only support 320x200x8bpp or 640x480x8bpp. */ /* -------------------------------------------------------------------- */ if (desire_x == 320 && desire_y == 200 && desire_bpp == 8) gfx_tick = gfx_tick_320x200; if (desire_x == 640 && desire_y == 480 && desire_bpp == 8) gfx_tick = gfx_tick_640x480; if (desire_x == 320 && desire_y == 240 && desire_bpp == 16) gfx_tick = gfx_tick_320x240_16; if (gfx_tick == NULL) { fprintf(stderr, "gfx panic: Currently, jzintv only " "supports 320x200x8bpp, 640x480x8bpp \n" " and 640x480x16bpp modes \n"); return -1; } /* -------------------------------------------------------------------- */ /* Allocate memory for the gfx_t. */ /* -------------------------------------------------------------------- */ gfx->vid = calloc(160, 200); gfx->pvt = calloc(1, sizeof(gfx_pvt_t)); if (!gfx->vid && !gfx->pvt) { fprintf(stderr, "gfx panic: Could not allocate memory.\n"); return -1; } /* -------------------------------------------------------------------- */ /* Set up our color palette. We start with video blanked. */ /* -------------------------------------------------------------------- */ for (i = 0; i < 16; i++) { gfx->pvt->pal_on [i].r = gfx_stic_palette[i][0]; gfx->pvt->pal_on [i].g = gfx_stic_palette[i][1]; gfx->pvt->pal_on [i].b = gfx_stic_palette[i][2]; gfx->pvt->pal_off[i].r = gfx_stic_palette[i][0] >> 1; gfx->pvt->pal_off[i].g = gfx_stic_palette[i][1] >> 1; gfx->pvt->pal_off[i].b = gfx_stic_palette[i][2] >> 1; } for (i = 16; i < 32; i++) { gfx->pvt->pal_on [i].r = gfx_stic_palette[i][0]; gfx->pvt->pal_on [i].g = gfx_stic_palette[i][1]; gfx->pvt->pal_on [i].b = gfx_stic_palette[i][2]; gfx->pvt->pal_off[i].r = gfx_stic_palette[i][0]; gfx->pvt->pal_off[i].g = gfx_stic_palette[i][1]; gfx->pvt->pal_off[i].b = gfx_stic_palette[i][2]; } gfx->pvt->vid_enable = 0; /* -------------------------------------------------------------------- */ /* Set up initial graphics mode. */ /* -------------------------------------------------------------------- */ gfx->pvt->des_x = desire_x; gfx->pvt->des_y = desire_y; gfx->pvt->des_bpp = desire_bpp; if (gfx_setup_sdl_surface(gfx, flags) < 0) gfx_sdl_abort(); /* -------------------------------------------------------------------- */ /* Ok, see if we succeeded in setting our initial video mode, and do */ /* some minor tidying. */ /* -------------------------------------------------------------------- */ if (!gfx->pvt->scr || SDL_Flip(gfx->pvt->scr) == -1) gfx_sdl_abort(); if (gfx->pvt->bpp == 16) gfx_gen_16bpp(gfx->pvt->scr, gfx->pvt->pal_off, 0, 32); /* -------------------------------------------------------------------- */ /* Hide the mouse. */ /* -------------------------------------------------------------------- */ SDL_ShowCursor(0); /* -------------------------------------------------------------------- */ /* Set up the gfx_t's internal structures. */ /* -------------------------------------------------------------------- */ gfx->periph.read = NULL; gfx->periph.write = NULL; gfx->periph.peek = NULL; gfx->periph.poke = NULL; gfx->periph.tick = gfx_tick; gfx->periph.min_tick = 14934; gfx->periph.max_tick = 14934; gfx->periph.addr_base = 0; gfx->periph.addr_mask = 0; return 0; } /* ======================================================================== */ /* GFX_TOGGLE_WINDOWED -- Try to toggle windowed vs. full-screen. */ /* ======================================================================== */ void gfx_toggle_windowed(gfx_t *gfx) { jzp_printf("\n"); gfx->toggle = 0; if (gfx_setup_sdl_surface(gfx, gfx->pvt->flags ^ GFX_FULLSC) < 0) gfx_setup_sdl_surface(gfx, gfx->pvt->flags); else { plat_delay(1000); /* Let monitor come up to speed w/ new res. */ gfx->b_dirty = 2; } } /* ======================================================================== */ /* GFX_SET_TITLE -- Sets the window title */ /* ======================================================================== */ int gfx_set_title(gfx_t *gfx, const char *title) { (void)gfx; SDL_WM_SetCaption(title, title); return 0; } /* ======================================================================== */ /* GFX_TICK_640X480 -- Services a gfx_t tick (in 640x480 mode) */ /* ======================================================================== */ uint_32 gfx_tick_640x480(periph_p gfx_periph, uint_32 len) { gfx_t *gfx = (gfx_t*) gfx_periph; uint_32 *scr0, *scr1, pix, *tmp; uint_8 *vid; int j; int i; int r_step; #ifdef BENCHMARK_GFX double start, end; start = get_time(); #endif gfx->tot_frames++; /* -------------------------------------------------------------------- */ /* Update a movie if one's active, or user requested toggle in movie */ /* state. We do this prior to dropping frames so that movies always */ /* have a consistent frame rate. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & (GFX_MOVIE | GFX_MVTOG)) gfx_movieupd(gfx); /* -------------------------------------------------------------------- */ /* Toggle full-screen/windowed if req'd. */ /* -------------------------------------------------------------------- */ if (gfx->toggle) gfx_toggle_windowed(gfx); /* -------------------------------------------------------------------- */ /* Drop a frame if we need to. */ /* -------------------------------------------------------------------- */ if (gfx->drop_frame) { gfx->drop_frame--; if (gfx->dirty) gfx->dropped_frames++; return len; } /* -------------------------------------------------------------------- */ /* Don't bother if display isn't dirty or if we're iconified. */ /* -------------------------------------------------------------------- */ if (!gfx->scrshot && (!gfx->dirty || gfx->hidden)) { return len; } /* -------------------------------------------------------------------- */ /* DEBUG: Report blocks of dropped frames. */ /* -------------------------------------------------------------------- */ if (gfx->dropped_frames) { #if 0 jzp_printf("Dropped %d frames.\n", gfx->dropped_frames); jzp_flush(); #endif gfx->tot_dropped_frames += gfx->dropped_frames; gfx->dropped_frames = 0; } /* -------------------------------------------------------------------- */ /* Draw the frame to the screen surface. */ /* -------------------------------------------------------------------- */ if (gfx->b_dirty > 0) { static SDL_Rect top = { 0, 0, 640, 40 }, bot = { 0, 440, 640, 40 } ; top.x = gfx->pvt->ofs_x; top.y = gfx->pvt->ofs_y; bot.x = gfx->pvt->ofs_x; bot.y = gfx->pvt->ofs_y + 440; gfx->b_dirty--; SDL_FillRect(gfx->pvt->scr, &top, gfx->b_color); SDL_FillRect(gfx->pvt->scr, &bot, gfx->b_color); } if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_LockSurface(gfx->pvt->scr); scr0 = gfx->pvt->ofs_x/sizeof(uint_32) + (uint_32 *) gfx->pvt->scr->pixels; vid = (uint_8 *) gfx->vid; r_step = (2*gfx->pvt->scr->pitch - 640) / sizeof(uint_32); if (gfx->pvt->scr->pitch % sizeof(uint_32)) { if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_UnlockSurface(gfx->pvt->scr); jzp_printf("error: can't blit to pitch %% 4 != 0\n"); exit(1); } scr0 += (gfx->pvt->ofs_y + 40) * (gfx->pvt->scr->pitch) / sizeof(uint_32); scr1 = scr0 + (gfx->pvt->scr->pitch) / sizeof(uint_32); tmp = scr0; for (j = 0; j < 200; j++) { for (i = 0; i < 160; i++) { pix = *vid++ * 0x01010101; *scr0++ = *scr1++ = pix; } scr0 += r_step; scr1 += r_step; } /* Put the word "REC" in the lower right if a movie's on */ if ((gfx->scrshot & GFX_MOVIE) && (gfx->pvt->movie->fr & 64) == 0) { uint_8 *scr; scr = (uint_8*)tmp; scr += 430*gfx->pvt->scr->pitch + 620; for (j = 0; j < 5; j++) { for (i = 0; gfx_rec_bmp[j][i] != 0; i++) { if (gfx_rec_bmp[j][i] != ' ') scr[i] = 16; } scr += gfx->pvt->scr->pitch; } } if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_UnlockSurface(gfx->pvt->scr); if (!((gfx->scrshot & GFX_MOVIE) && (gfx->pvt->movie->fr & 64) == 0)) { static SDL_Rect norec = { 0, 0, 20, 5 }; norec.x = gfx->pvt->ofs_x + 620; norec.y = gfx->pvt->ofs_y + 470; SDL_FillRect(gfx->pvt->scr, &norec, gfx->b_color); } /* -------------------------------------------------------------------- */ /* Actually update the display. */ /* -------------------------------------------------------------------- */ //SDL_UpdateRect(gfx->pvt->scr, 0, 0, 0, 0); SDL_Flip(gfx->pvt->scr); /* -------------------------------------------------------------------- */ /* Update the palette if there's been a change in blanking state. */ /* -------------------------------------------------------------------- */ if (gfx->pvt->vid_enable & 2) { gfx->pvt->vid_enable &= 1; gfx->pvt->vid_enable ^= 1; SDL_SetColors(gfx->pvt->scr, gfx->pvt->vid_enable ? gfx->pvt->pal_on : gfx->pvt->pal_off, 0, 16); } gfx->dirty = 0; /* -------------------------------------------------------------------- */ /* If a screen-shot was requested, go write out a PPM file of the */ /* screen right now. Screen-shot PPMs are always 320x200. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & GFX_SHOT) { gfx_scrshot(gfx->vid); gfx->scrshot &= ~GFX_SHOT; } #ifdef BENCHMARK_GFX end = get_time(); jzp_printf("gfx_tick = %8.3fms\n", (end-start)*1000.); #endif return len; } /* ======================================================================== */ /* GFX_TICK_320X200 -- Services a gfx_t tick (in 320x200 mode) */ /* ======================================================================== */ uint_32 gfx_tick_320x200(periph_p gfx_periph, uint_32 len) { gfx_t *gfx = (gfx_t*) gfx_periph; uint_8 *vid, *scr, pix, *tmp; int x, y; #ifdef BENCHMARK_GFX double start, end; start = get_time(); #endif gfx->tot_frames++; /* -------------------------------------------------------------------- */ /* Update a movie file if the recorder's active. We do this before */ /* we drop a frame, so that the movie holds a consistent 60fps. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & (GFX_MVTOG|GFX_MOVIE)) /* Movie, or toggle req'd */ gfx_movieupd(gfx); /* -------------------------------------------------------------------- */ /* Toggle full-screen/windowed if req'd. */ /* -------------------------------------------------------------------- */ if (gfx->toggle) gfx_toggle_windowed(gfx); /* -------------------------------------------------------------------- */ /* Drop a frame if we need to. */ /* -------------------------------------------------------------------- */ if (gfx->drop_frame) { gfx->drop_frame--; if (gfx->dirty) gfx->dropped_frames++; return len; } /* -------------------------------------------------------------------- */ /* Don't bother if display isn't dirty or if we're iconified. */ /* -------------------------------------------------------------------- */ if (!gfx->dirty || gfx->hidden) { return len; } /* -------------------------------------------------------------------- */ /* DEBUG: Report blocks of dropped frames. */ /* -------------------------------------------------------------------- */ if (gfx->dropped_frames) { #if 0 jzp_printf("Dropped %d frames.\n", gfx->dropped_frames); jzp_flush(); #endif gfx->tot_dropped_frames += gfx->dropped_frames; gfx->dropped_frames = 0; } /* -------------------------------------------------------------------- */ /* Draw the frame to the screen surface. */ /* -------------------------------------------------------------------- */ if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_LockSurface(gfx->pvt->scr); scr = gfx->pvt->ofs_x + gfx->pvt->scr->pitch * gfx->pvt->ofs_y + (uint_8 *) gfx->pvt->scr->pixels; tmp = scr; vid = (uint_8 *) gfx->vid; for (y = 0; y < 200; y++) { for (x = 0; x < 160; x++) { pix = *vid++; scr[0] = scr[1] = pix; scr += 2; } scr += gfx->pvt->scr->pitch - 320; } /* Put the word "REC" in the lower right if a movie's on */ if (gfx->scrshot & GFX_MOVIE) { if ((gfx->pvt->movie->fr & 64) == 0) { scr = tmp; scr += 194*gfx->pvt->scr->pitch + 300; for (y = 0; y < 5; y++) { for (x = 0; gfx_rec_bmp[y][x] != 0; x++) { if (gfx_rec_bmp[y][x] != ' ') scr[x] = 16; } scr += gfx->pvt->scr->pitch; } } } if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_UnlockSurface(gfx->pvt->scr); /* -------------------------------------------------------------------- */ /* Actually update the display. */ /* -------------------------------------------------------------------- */ //SDL_UpdateRect(gfx->pvt->scr, 0, 0, 0, 0); SDL_Flip(gfx->pvt->scr); #if 0 { static double now, then = 0; now = get_time(); if (then > 0.0) jzp_printf("flip after %8.0fus\n", (now - then) * 1e6); then = now; } #endif /* -------------------------------------------------------------------- */ /* Update the palette if there's been a change in blanking state. */ /* -------------------------------------------------------------------- */ if (gfx->pvt->vid_enable & 2) { gfx->pvt->vid_enable &= 1; gfx->pvt->vid_enable ^= 1; SDL_SetColors(gfx->pvt->scr, gfx->pvt->vid_enable ? gfx->pvt->pal_on : gfx->pvt->pal_off, 0, 16); } gfx->dirty = 0; /* -------------------------------------------------------------------- */ /* If a screen-shot was requested, go write out a PPM file of the */ /* screen right now. Screen-shot PPMs are always 320x200. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & GFX_SHOT) { gfx_scrshot(gfx->vid); gfx->scrshot &= ~1; } #ifdef BENCHMARK_GFX end = get_time(); jzp_printf("gfx_tick = %8.3fms\n", (end-start)*1000.); #endif return len; } /* ======================================================================== */ /* GFX_TICK_320X240X16 -- Services a gfx_t tick (in 320x240 16bpp mode) */ /* ======================================================================== */ uint_32 gfx_tick_320x240_16(periph_p gfx_periph, uint_32 len) { gfx_t *gfx = (gfx_t*) gfx_periph; uint_8 *vid; uint_32 *scr, *tmp; int x, y; #ifdef BENCHMARK_GFX double start, end; start = get_time(); #endif gfx->tot_frames++; /* -------------------------------------------------------------------- */ /* Update a movie file if the recorder's active. We do this before */ /* we drop a frame, so that the movie holds a consistent 60fps. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & (GFX_MVTOG|GFX_MOVIE)) /* Movie, or toggle req'd */ gfx_movieupd(gfx); /* -------------------------------------------------------------------- */ /* Toggle full-screen/windowed if req'd. */ /* -------------------------------------------------------------------- */ if (gfx->toggle) gfx_toggle_windowed(gfx); /* -------------------------------------------------------------------- */ /* Drop a frame if we need to. */ /* -------------------------------------------------------------------- */ if (gfx->drop_frame) { gfx->drop_frame--; if (gfx->dirty) gfx->dropped_frames++; return len; } /* -------------------------------------------------------------------- */ /* Don't bother if display isn't dirty or if we're iconified. */ /* -------------------------------------------------------------------- */ if (!gfx->dirty || gfx->hidden) { return len; } /* -------------------------------------------------------------------- */ /* DEBUG: Report blocks of dropped frames. */ /* -------------------------------------------------------------------- */ if (gfx->dropped_frames) { #if 0 jzp_printf("Dropped %d frames.\n", gfx->dropped_frames); jzp_flush(); #endif gfx->tot_dropped_frames += gfx->dropped_frames; gfx->dropped_frames = 0; } /* -------------------------------------------------------------------- */ /* Draw the frame to the screen surface. */ /* -------------------------------------------------------------------- */ if (gfx->b_dirty > 0) { static SDL_Rect top = { 0, 0, 320, 20 }, bot = { 0, 220, 320, 20 } ; uint_32 bord; top.x = gfx->pvt->ofs_x; top.y = gfx->pvt->ofs_y; bot.x = gfx->pvt->ofs_x; bot.y = gfx->pvt->ofs_y + 220; gfx->b_dirty--; bord = SDL_MapRGB(gfx->pvt->scr->format, gfx_stic_palette[gfx->b_color][0], gfx_stic_palette[gfx->b_color][1], gfx_stic_palette[gfx->b_color][2]); SDL_FillRect(gfx->pvt->scr, &top, bord); SDL_FillRect(gfx->pvt->scr, &bot, bord); } if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_LockSurface(gfx->pvt->scr); scr = gfx->pvt->ofs_x + (uint_32*)(gfx->pvt->scr->pitch * (gfx->pvt->ofs_y + 20) + (uint_8*)gfx->pvt->scr->pixels); tmp = scr; vid = (uint_8 *) gfx->vid; for (y = 0; y < 200; y++) { for (x = 0; x < 160; x += 4) { *scr++ = gfx_stic_palette16[*vid++]; *scr++ = gfx_stic_palette16[*vid++]; *scr++ = gfx_stic_palette16[*vid++]; *scr++ = gfx_stic_palette16[*vid++]; } scr = (uint_32*)((uint_8*)scr + gfx->pvt->scr->pitch) - 160; } /* Put the word "REC" in the lower right if a movie's on */ if (gfx->scrshot & GFX_MOVIE) { if ((gfx->pvt->movie->fr & 64) == 0) { scr = (uint_32*)((uint_8*)tmp + 194*gfx->pvt->scr->pitch) + 300; for (y = 0; y < 5; y++) { for (x = 0; gfx_rec_bmp[y][x] != 0; x++) { if (gfx_rec_bmp[y][x] != ' ') scr[x] = gfx_stic_palette16[16]; } scr = (uint_32*)((uint_8*)scr + gfx->pvt->scr->pitch); } } } if (SDL_MUSTLOCK(gfx->pvt->scr)) SDL_UnlockSurface(gfx->pvt->scr); /* -------------------------------------------------------------------- */ /* Actually update the display. */ /* -------------------------------------------------------------------- */ //SDL_UpdateRect(gfx->pvt->scr, 0, 0, 0, 0); SDL_Flip(gfx->pvt->scr); #if 0 { static double now, then = 0; now = get_time(); if (then > 0.0) jzp_printf("flip after %8.0fus\n", (now - then) * 1e6); then = now; } #endif /* -------------------------------------------------------------------- */ /* Update the palette if there's been a change in blanking state. */ /* -------------------------------------------------------------------- */ if (gfx->pvt->vid_enable & 2) { gfx->pvt->vid_enable &= 1; gfx->pvt->vid_enable ^= 1; gfx_gen_16bpp(gfx->pvt->scr, gfx->pvt->vid_enable ? gfx->pvt->pal_on : gfx->pvt->pal_off, 0, 16); } gfx->dirty = 0; /* -------------------------------------------------------------------- */ /* If a screen-shot was requested, go write out a PPM file of the */ /* screen right now. Screen-shot PPMs are always 320x200. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & GFX_SHOT) { gfx_scrshot(gfx->vid); gfx->scrshot &= ~1; } #ifdef BENCHMARK_GFX end = get_time(); jzp_printf("gfx_tick = %8.3fms\n", (end-start)*1000.); #endif return len; } /* ======================================================================== */ /* GFX_GEN_16BPP -- Convert the RGB palette into a 16bpp palette */ /* ======================================================================== */ LOCAL void gfx_gen_16bpp(SDL_Surface *scr, SDL_Color pal[32], int s, int e) { int i; uint_32 t; for (i = s; i < e; i++) { t = SDL_MapRGB(scr->format, pal[i].r, pal[i].g, pal[i].b) & 0xFFFF; gfx_stic_palette16[i] = (t << 16) | t; } } /* ======================================================================== */ /* GFX_VID_ENABLE -- Alert gfx that video has been enabled or blanked */ /* ======================================================================== */ void gfx_vid_enable(gfx_t *gfx, int enabled) { /* -------------------------------------------------------------------- */ /* Force 'enabled' to be 0 or 1. */ /* -------------------------------------------------------------------- */ enabled = enabled != 0; /* -------------------------------------------------------------------- */ /* If enabled state changed, schedule a palette update. */ /* -------------------------------------------------------------------- */ if ((gfx->pvt->vid_enable ^ enabled) & 1) { gfx->pvt->vid_enable |= 2; gfx->dirty = 1; } } /* ======================================================================== */ /* GFX_SET_BORD -- Set the border color for the display */ /* ======================================================================== */ void gfx_set_bord ( gfx_t *gfx, /* Graphics object. */ int b_color ) { int dirty = 0; /* -------------------------------------------------------------------- */ /* Set up the display parameters. */ /* -------------------------------------------------------------------- */ if (gfx->b_color != b_color) { gfx->b_color = b_color; dirty = 3; } /* -------------------------------------------------------------------- */ /* If we're using the normal STIC blanking behavior, set our "off" */ /* colors to the currently selected border color. The alternate mode */ /* (which is useful for debugging) sets the blanked colors to be */ /* dimmed versions of the normal palette. */ /* -------------------------------------------------------------------- */ if (gfx->debug_blank == 0) { int i; for (i = 0; i < 16; i++) gfx->pvt->pal_off[i] = gfx->pvt->pal_on[b_color]; } if (dirty) { gfx->dirty = 1; } if (dirty & 2) { gfx->b_dirty = 2; } } /* ======================================================================== */ /* GFX_SCRSHOT -- Write a 320x200 screen shot to a GIF file. */ /* ======================================================================== */ LOCAL uint_8 scrshot_buf[320*200]; void gfx_scrshot(uint_8 *scr) { static int last = -1; FILE * f; char f_name[32]; int num = last, i, len; /* -------------------------------------------------------------------- */ /* Search for an unused screen-shot file name. */ /* -------------------------------------------------------------------- */ do { num = (num + 1) % 10000; sprintf(f_name, "shot%.4d.gif", num); if (!file_exists(f_name)) break; } while (num != last); /* -------------------------------------------------------------------- */ /* Warn the user if we wrapped all 10000 screen shots... */ /* -------------------------------------------------------------------- */ if (num == last) { num = (num + 1) % 10000; sprintf(f_name, "shot%.4d.gif", num); fprintf(stderr, "Warning: Overwriting %s...\n", f_name); } /* -------------------------------------------------------------------- */ /* Update our 'last' pointer and open the file and dump the PPM. */ /* -------------------------------------------------------------------- */ last = num; f = fopen(f_name, "wb"); if (!f) { fprintf(stderr, "Error: Could not open '%s' for screen dump.\n", f_name); return; } /* -------------------------------------------------------------------- */ /* Do the screen dump. Write it as a nice GIF. We need to pixel */ /* double the image ahead of time. */ /* -------------------------------------------------------------------- */ for (i = 0; i < 200*160; i++) scrshot_buf[i*2 + 0] = scrshot_buf[i*2 + 1] = scr[i]; len = gif_write(f, scrshot_buf, 320, 200, gfx_stic_palette, 16); if (len > 0) { jzp_printf("\nWrote screen shot to '%s', %d bytes\n", f_name, len); } else { jzp_printf("\nError writing screen shot to '%s'\n", f_name); } jzp_flush(); fclose(f); return; } /* ======================================================================== */ /* GFX_MOVIEUPD -- Start/Stop/Update a movie in progress */ /* ======================================================================== */ void gfx_movieupd(gfx_t *gfx) { gfx_pvt_t *pvt = gfx->pvt; /* -------------------------------------------------------------------- */ /* Toggle current movie state if user requested. */ /* -------------------------------------------------------------------- */ if (gfx->scrshot & GFX_MVTOG) { static int last = -1; int num = last; char f_name[32]; /* ---------------------------------------------------------------- */ /* Whatever happens, clear the toggle. */ /* ---------------------------------------------------------------- */ gfx->scrshot &= ~GFX_MVTOG; /* ---------------------------------------------------------------- */ /* Make sure movie subsystem initialized. We only init this if */ /* someone tries to take a movie. */ /* ---------------------------------------------------------------- */ if (!pvt->movie_init) { if (!pvt->movie) pvt->movie = calloc(sizeof(mvi_t), 1); if (!pvt->movie) { fprintf(stderr, "No memory for movie structure\n"); return; } mvi_init(pvt->movie, 160, 200); pvt->movie_init = 1; } /* ---------------------------------------------------------------- */ /* If a movie's open, close it. */ /* ---------------------------------------------------------------- */ if ((gfx->scrshot & GFX_MOVIE) != 0) { if (pvt->movie->f) { fclose(pvt->movie->f); jzp_printf("\nDone writing movie:\n" " Total frames: %10d\n" " Total size: %10d\n" " Bytes/frame: %10d\n" " Dupe frames: %10d\n" " Dupe rows: %10d\n" " Compression ratio: %8.2f:1\n", pvt->movie->fr, pvt->movie->tot_bytes, pvt->movie->tot_bytes / pvt->movie->fr, pvt->movie->rpt_frames, pvt->movie->rpt_rows, (16032.*pvt->movie->fr) / pvt->movie->tot_bytes); jzp_flush(); } gfx->scrshot &= ~GFX_MOVIE; pvt->movie->f = NULL; pvt->movie->fr = 0; return; } /* ---------------------------------------------------------------- */ /* Otherwise, open a new movie. */ /* Search for an unused movie file name. */ /* ---------------------------------------------------------------- */ do { num = (num + 1) % 10000; sprintf(f_name, "mvi_%.4d.imv", num); if (!file_exists(f_name)) break; } while (num != last); /* ---------------------------------------------------------------- */ /* Warn the user if we wrapped all 10000 movie slots... */ /* ---------------------------------------------------------------- */ if (num == last) { num = (num + 1) % 10000; sprintf(f_name, "mvi_%.4d.imv", num); fprintf(stderr, "Warning: Overwriting %s...\n", f_name); } /* ---------------------------------------------------------------- */ /* Update our 'last' pointer, and start the movie. */ /* ---------------------------------------------------------------- */ last = num; pvt->movie->f = fopen(f_name, "wb"); if (!pvt->movie->f) { fprintf(stderr, "Error: Could not open '%s' for movie.\n", f_name); return; } jzp_printf("\nStarted movie file '%s'\n", f_name); jzp_flush(); /* ---------------------------------------------------------------- */ /* Success: Turn on the movie. */ /* ---------------------------------------------------------------- */ gfx->scrshot |= GFX_MOVIE; pvt->movie->fr = 0; } if ((gfx->scrshot & GFX_RESET) == 0) mvi_wr_frame(pvt->movie, gfx->vid, gfx->bbox); } /* ======================================================================== */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ======================================================================== */ /* Copyright (c) 1998-2006, Joseph Zbiciak, John Tanner */ /* ======================================================================== */