/* * video.cpp * * Copyright (C) 2001 Matt Ownby * * This file is part of DAPHNE, a laserdisc arcade game emulator * * DAPHNE 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. * * DAPHNE 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // video.cpp // Part of the DAPHNE emulator // This code started by Matt Ownby, May 2000 #include #include #include #include // for some error messages #include // rdg2010 #include "video.h" #include "palette.h" #include "SDL_DrawText.h" #include "../io/conout.h" #include "../io/error.h" #include "../io/mpo_fileio.h" #include "../io/mpo_mem.h" #include "SDL_Console.h" #include "../game/game.h" #include "../ldp-out/ldp.h" #include "../ldp-out/ldp-vldp-gl.h" #include "../io/homedir.h" #ifdef USE_OPENGL #ifdef MAC_OSX #include #else #include #endif // This is the max width and height that any .BMP will be, so that we can fit it into // an opengl texture. If you need to display a bigger .BMP, increase this number. // NOTE : this number must be a power of 2!!! #define GL_TEX_SIZE 1024 // pointer to the buffer that we allocate for bitmap blits Uint8 *g_pVidTex = NULL; #endif using namespace std; #ifndef GP2X unsigned int g_vid_width = 640, g_vid_height = 480; // default video width and video height #ifdef DEBUG const Uint16 cg_normalwidths[] = { 320, 640, 800, 1024, 1280, 1280, 1600 }; const Uint16 cg_normalheights[]= { 240, 480, 600, 768, 960, 1024, 1200 }; #else const Uint16 cg_normalwidths[] = { 640, 800, 1024, 1280, 1280, 1600 }; const Uint16 cg_normalheights[]= { 480, 600, 768, 960, 1024, 1200 }; #endif // DEBUG #else unsigned int g_vid_width = 320, g_vid_height = 240; // default for gp2x const Uint16 cg_normalwidths[] = { 320 }; const Uint16 cg_normalheights[]= { 240 }; #endif // the dimensions that we draw (may differ from g_vid_width/height if aspect ratio is enforced) unsigned int g_draw_width = 640, g_draw_height = 480; SDL_Surface *g_led_bmps[LED_RANGE] = { 0 }; SDL_Surface *g_other_bmps[B_EMPTY] = { 0 }; SDL_Surface *g_screen = NULL; // our primary display SDL_Surface *g_screen_blitter = NULL; // the surface we blit to (we don't blit directly to g_screen because opengl doesn't like that) bool g_console_initialized = false; // 1 once console is initialized bool g_fullscreen = false; // whether we should initialize video in fullscreen mode or not bool g_fakefullscreen = false; // by RDG2010 -- whether daphne should do fullscreen window int g_scalefactor = 100; // by RDG2010 -- scales the image to this percentage value (for CRT TVs with overscan problems). int sboverlay_characterset = 1; // whether we will try to force a 4:3 aspect ratio regardless of window size // (this is probably a good idea as a default option) bool g_bForceAspectRatio = true; bool g_bUseOpenGL = false; // whether user has requested we use OpenGL // the # of degrees to rotate counter-clockwise in opengl mode float g_fRotateDegrees = 0.0; #ifdef USE_OPENGL GLuint g_texture_id = 0; // for any blits we have to do in opengl ... #endif //////////////////////////////////////////////////////////////////////////////////////////////////// // initializes the window in which we will draw our BMP's // returns true if successful, false if failure bool init_display() { bool result = false; // whether video initialization is successful or not bool abnormalscreensize = true; // assume abnormal const SDL_VideoInfo *vidinfo = NULL; Uint8 suggested_bpp = 0; Uint32 sdl_flags = 0; // the default for this depends on whether we are using HW accelerated YUV overlays or not char *hw_env = getenv("SDL_VIDEO_YUV_HWACCEL"); // if HW acceleration has been disabled, we need to use a SW surface due to some oddities with crashing and fullscreen if (hw_env && (hw_env[0] == '0')) { sdl_flags = SDL_SWSURFACE; } // else if HW acceleration hasn't been explicitely disabled ... else { sdl_flags = SDL_HWSURFACE; // Win32 notes (may not apply to linux) : // After digging around in the SDL source code, I've discovered a few things ... // When using fullscreen mode, you should always use SDL_HWSURFACE because otherwise // you can't use YUV hardware overlays due to SDL creating an extra surface without // your consent (which seems retarded to me hehe). // SDL_SWSURFACE + hw accelerated YUV overlays will work in windowed mode, but might // as well just use SDL_HWSURFACE for everything. } char s[250] = { 0 }; Uint32 x = 0; // temporary index // if we were able to initialize the video properly if ( SDL_InitSubSystem(SDL_INIT_VIDEO) >=0 ) { vidinfo = SDL_GetVideoInfo(); suggested_bpp = vidinfo->vfmt->BitsPerPixel; // if we were in 8-bit mode, try to get at least 16-bit color // because we definitely use more than 256 colors in daphne if (suggested_bpp < 16) { suggested_bpp = 32; } // By RDG2010 // Have cursor disabled always. // Useful for ALG SINGE games // If there is a game that needs to show the cursor icon of the OS // then modify. SDL_ShowCursor(SDL_DISABLE); // hide mouse in fullscreen mode if (g_fullscreen) { //SDL_ShowCursor(SDL_DISABLE); // hide mouse in fullscreen mode sdl_flags |= SDL_FULLSCREEN; } // go through each standard resolution size to see if we are using a standard resolution for (x=0; x < (sizeof(cg_normalwidths) / sizeof(Uint16)); x++) { // if we find a match, we know we're using a standard res if ((g_vid_width == cg_normalwidths[x]) && (g_vid_height == cg_normalheights[x])) { abnormalscreensize = false; } } // if we're using a non-standard resolution if (abnormalscreensize) { printline("WARNING : You have specified an abnormal screen resolution! Normal screen resolutions are:"); // print each standard resolution for (x=0; x < (sizeof(cg_normalwidths) / sizeof(Uint16)); x++) { sprintf(s,"%d x %d", cg_normalwidths[x], cg_normalheights[x]); printline(s); } newline(); } /* by RDG2010 * Adding preliminary lightgun support. * For now that means a fake fullscreen. * A borderless, no title bar window the same size as the desktop. * This is done in three steps: * 1. Get desktop resolution. * 2. Make SDL window borderless. * 3. Move window to top-left corner of the screen. * * Tested OK on both Windows and Ubuntu Linux. * TODO: Provide a better solution. * */ if (get_fakefullscreen()) { // Step 1. Get desktop resolution. // Get the current resolution of the system's desktop. const SDL_VideoInfo* info = SDL_GetVideoInfo(); // For SDL 1.3 use SDL_GetDesktopDisplayMode() instead. int desktopWidth = info->current_w; int desktopHeight = info->current_h; // Update variables here, before initializing any surfaces. g_vid_width = desktopWidth ; g_vid_height = desktopHeight; } // RDG2010 g_draw_width = g_vid_width; g_draw_height = g_vid_height; // if we're supposed to enforce the aspect ratio ... if (g_bForceAspectRatio) { double dCurAspectRatio = (double) g_vid_width / g_vid_height; const double dTARGET_ASPECT_RATIO = 4.0/3.0; // if current aspect ratio is less than 1.3333 if (dCurAspectRatio < dTARGET_ASPECT_RATIO) { g_draw_height = (g_draw_width * 3) / 4; } // else if current aspect ratio is greater than 1.3333 else if (dCurAspectRatio > dTARGET_ASPECT_RATIO) { g_draw_width = (g_draw_height * 4) / 3; } // else the aspect ratio is already correct, so don't change anything } // if we're supposed to scale the image... if (g_scalefactor < 100) { g_draw_width = g_draw_width * g_scalefactor / 100; g_draw_height = g_draw_height * g_scalefactor / 100; } #ifndef GP2X if (!g_bUseOpenGL) { #ifdef WIN32 if (g_game->verify_required_file2("singe32.bmp","pics",0xC17A933D)) { // Add icon to the SDL Window -- RDG Uint32 colorkey; SDL_Surface *image; image = SDL_LoadBMP("pics/singe32.bmp"); colorkey = SDL_MapRGB(image->format, 255, 0, 255); SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey); SDL_WM_SetIcon(image,NULL); // MUST BE CALLED BEFORE set video mode, or else it won't show on the title bar. } #endif // by RDG2010 // Step 2. Create a borderless SDL window. // If doing fullscreen window, make the window bordeless (no title bar). // This is achieved by adding the SDL_NOFRAME flag. if (get_fakefullscreen()) sdl_flags = sdl_flags | SDL_NOFRAME; g_screen = SDL_SetVideoMode(g_vid_width, g_vid_height, suggested_bpp, sdl_flags); SDL_WM_SetCaption("SINGE: Somewhat Interactive Nostalgic Game Engine =]", "Singe"); } else { #ifdef USE_OPENGL init_opengl(); glGenTextures(1, &g_texture_id); // generate texture buffer for use in this file g_pVidTex = MPO_MALLOC(GL_TEX_SIZE * GL_TEX_SIZE * 4); // 32-bit bits per pixel, width and height the same #endif } #else SDL_ShowCursor(SDL_DISABLE); // always hide mouse for gp2x g_screen = SDL_SetVideoMode(320, 240, 0, SDL_HWSURFACE); #endif /* by RDG2010 * Step 3. Move window to the top-left corner of the screen. * * The task of positioning the window is OS dependant. * When using a window the same size of the desktop, X11 will * automatically position it to the top-left corner of the screen. * In the case of Windows, it has to be done manually. * */ #ifdef WIN32 if (get_fakefullscreen()) { // Access to SDL's OS specific structure. SDL_SysWMinfo windowInfo; SDL_VERSION(&windowInfo.version); if(SDL_GetWMInfo(&windowInfo)) { // Retrieve the Windows handle to the SDL window. HWND handle = windowInfo.window; // Position Window to top-left corner of the screen. if(!SetWindowPos(handle, NULL, 0, 0, 0, 0, SWP_NOREPOSITION|SWP_NOZORDER|SWP_NOSIZE)) { printline("Error occurred with 'SetWindowPos'. Fullscreen window failed."); result = 0; } } else { // Error occurred with 'SDL_GetWMInfo' printline("Error occurred with 'SDL_GetWMInfo'. Fullscreen window failed."); result = 0; } // endif } // endif #endif // create a 32-bit surface g_screen_blitter = SDL_CreateRGBSurface(SDL_SWSURFACE, g_vid_width, g_vid_height, 32, 0xff, 0xFF00, 0xFF0000, 0xFF000000); if (g_screen && g_screen_blitter) { SDL_SetAlpha(g_screen_blitter,SDL_SRCALPHA,0); //rdg sprintf(s, "Set %dx%d at %d bpp with flags: %x", g_screen->w, g_screen->h, g_screen->format->BitsPerPixel, g_screen->flags); printline(s); // initialize SDL console in the background if (ConsoleInit("pics/ConsoleFont.bmp", g_screen_blitter, 100)==0) { AddCommand(g_cpu_break, "break"); g_console_initialized = true; result = true; } else { printerror("Error initializing SDL console =("); } // sometimes the screen initializes to white, so this attempts to prevent that vid_blank(); vid_flip(); vid_blank(); vid_flip(); } } if (result == 0) { sprintf(s, "Could not initialize video display: %s", SDL_GetError()); printerror(s); } return(result); } #ifdef USE_OPENGL bool init_opengl() { bool bResult = false; Uint32 sdl_flags = SDL_SWSURFACE | SDL_OPENGL; // fullscreen is broken if (get_fullscreen()) sdl_flags |= SDL_FULLSCREEN; SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // by RDG2010 // Step 2. Create a borderless SDL window (on the OpenGL side). // If doing fullscreen window, make the window bordeless (no title bar). // This is achieved by adding the SDL_NOFRAME flag. // Tested OK on both Windows and Ubuntu Linux. if (get_fakefullscreen()) sdl_flags = sdl_flags | SDL_NOFRAME; g_screen = SDL_SetVideoMode(g_vid_width, g_vid_height, 0, sdl_flags); // if SDL_SetVideoMode worked ... if (g_screen) { //SDL_WM_SetCaption("DAPHNE: Now in experimental OpenGL mode :)", "daphne"); SDL_WM_SetCaption("SINGE: Somewhat Interactive Nostalgic Game Engine =]", "Singe"); //glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); // since we are doing scaling, we need this ... glFrontFace(GL_CCW); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); // this should give us some optimization for free glDisable(GL_LIGHTING); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* Set the clear color. */ glClearColor( 0, 0.0, 0, 1 ); /* Setup our viewport. */ glViewport( 0, 0, g_screen->w, g_screen->h ); // ENABLE 2D MODE (ORTHO) glMatrixMode (GL_PROJECTION); glLoadIdentity(); // The idea is to have the center of the image be at 0,0 to make rotation, scaling, etc // easier. glOrtho( -g_screen->w / 2, // left g_screen->w / 2, // right -g_screen->h / 2, // bottom g_screen->h / 2, // top -10, 10); glMatrixMode (GL_MODELVIEW); glEnable(GL_TEXTURE_2D); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // handle optional rotation // if we're to one side or the other, scale the image so it will fit if ((g_fRotateDegrees == 90.0) || (g_fRotateDegrees == 270.0)) { // NOTE : this can either shrink or enlarge depending on whether the width or height is bigger GLfloat fFactor = (GLfloat) g_vid_height / g_vid_width; glScalef(fFactor, fFactor, 1.0); } // only rotate if we have a need to if (g_fRotateDegrees != 0) { glRotatef(g_fRotateDegrees, 0, 0, 1.0); } bResult = true; } // else SetVideoMode failed ... return bResult; } #endif // USE_OPENGL // shuts down video display void shutdown_display() { printline("Shutting down video display..."); if (g_console_initialized) { ConsoleShutdown(); g_console_initialized = false; } #ifdef USE_OPENGL // free texture buffer from memory MPO_FREE(g_pVidTex); #endif SDL_QuitSubSystem(SDL_INIT_VIDEO); } void vid_flip() { // if we're not using OpenGL, then just use the regular SDL Flip ... if (!g_bUseOpenGL) { SDL_Flip(g_screen); } else { SDL_GL_SwapBuffers(); } } void vid_blank() { if (!g_bUseOpenGL) { SDL_FillRect(g_screen, NULL, 0); } else { #ifdef USE_OPENGL glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); #endif } } #ifdef USE_OPENGL // converts an SDL surface to an opengl texture void vid_srf2tex(SDL_Surface *srf, GLuint uTexID) { Uint8 *ptrPixelRow = (Uint8 *) srf->pixels; Uint32 *RGBARow = (Uint32 *) g_pVidTex; Uint32 *g_puRGBAPalette = get_rgba_palette(); for (unsigned int uRow = 0; (uRow < (unsigned) srf->h) && (uRow < GL_TEX_SIZE); ++uRow) { Uint8 *ptrPixel = ptrPixelRow; Uint32 *RGBA = RGBARow; for (unsigned int uCol = 0; (uCol < (unsigned) srf->w) && (uCol < GL_TEX_SIZE); ++uCol) { Uint8 R, G, B; // if this is an 8-bit surface if (srf->format->BitsPerPixel == 8) { // retrieve precalculated RGBA value *RGBA = g_puRGBAPalette[*ptrPixel]; } // else it doesn't use a color palette else { Uint32 uVal = *((Uint32*) ptrPixel); R = ((uVal & srf->format->Rmask) >> srf->format->Rshift) << srf->format->Rloss; G = ((uVal & srf->format->Gmask) >> srf->format->Gshift) << srf->format->Gloss; B = ((uVal & srf->format->Bmask) >> srf->format->Bshift) << srf->format->Bloss; // this may not work for big endian, but we won't know until we try *RGBA = R | (G << 8) | (B << 16) | 0xFF000000; } // move to the next pixel ... ptrPixel += srf->format->BytesPerPixel; ++RGBA; } // end this line // move to the next row ptrPixelRow += srf->pitch; RGBARow += GL_TEX_SIZE; // move down one row in the texture memory } glBindTexture(GL_TEXTURE_2D, uTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // apply the texture glTexImage2D(GL_TEXTURE_2D, 0, // level is 0 GL_RGBA, GL_TEX_SIZE, GL_TEX_SIZE, 0, // border is 0 GL_RGBA, GL_UNSIGNED_BYTE, g_pVidTex); } #endif void vid_blit(SDL_Surface *srf, int x, int y) { // if (g_ldp->is_blitting_allowed()) rdg int result = 0; { if (!g_bUseOpenGL) { SDL_Rect dest; dest.x = (short) x; dest.y = (short) y; dest.w = (unsigned short) srf->w; dest.h = (unsigned short) srf->h; result = SDL_BlitSurface(srf, NULL, g_screen, &dest); } // else, OpenGL mode for blitting ... else { #ifdef USE_OPENGL // convert surface to a texture vid_srf2tex(srf, g_texture_id); // draw the textured rectangle GLfloat fWidth = (GLfloat) srf->w / GL_TEX_SIZE; GLfloat fHeight = (GLfloat) srf->h / GL_TEX_SIZE; // convert y from top-to-bottom to bottom-to-top (SDL -> openGL) y = g_vid_height - y; // adjust coordinates so they match up with glOrtho projection x -= (g_vid_width >> 1); y -= (g_vid_height >> 1); glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex3i(x, y, 0); // top left glTexCoord2f(0, fHeight); glVertex3i(x, y - srf->h, 0); // bottom left glTexCoord2f(fWidth, fHeight); glVertex3i(x + srf->w, y - srf->h, 0); // bottom right glTexCoord2f(fWidth, 0); glVertex3i(x + srf->w, y, 0); // top right glEnd(); #endif // USE_OPENGL } } // else blitting isn't allowed, so just ignore } // redraws the proper display (Scoreboard, etc) on the screen, after first clearing the screen // call this every time you want the display to return to normal void display_repaint() { vid_blank(); vid_flip(); g_game->video_force_blit(); } // loads all the .bmp's used by DAPHNE // returns true if they were all successfully loaded, or a false if they weren't bool load_bmps() { bool result = true; // assume success unless we hear otherwise int index = 0; char filename[81]; /* for (; index < LED_RANGE; index++) { sprintf(filename, "pics/led%d.bmp", index); g_led_bmps[index] = load_one_bmp(filename); // If the bit map did not successfully load if (g_led_bmps[index] == 0) { result = false; } } g_other_bmps[B_DL_PLAYER1] = load_one_bmp("pics/player1.bmp"); g_other_bmps[B_DL_PLAYER2] = load_one_bmp("pics/player2.bmp"); g_other_bmps[B_DL_LIVES] = load_one_bmp("pics/lives.bmp"); g_other_bmps[B_DL_CREDITS] = load_one_bmp("pics/credits.bmp"); */ g_other_bmps[B_DAPHNE_SAVEME] = load_one_bmp("pics/saveme.bmp"); g_other_bmps[B_GAMENOWOOK] = load_one_bmp("pics/gamenowook.bmp"); /* if (sboverlay_characterset != 2) g_other_bmps[B_OVERLAY_LEDS] = load_one_bmp("pics/overlayleds1.bmp"); else g_other_bmps[B_OVERLAY_LEDS] = load_one_bmp("pics/overlayleds2.bmp"); g_other_bmps[B_OVERLAY_LDP1450] = load_one_bmp("pics/ldp1450font.bmp"); */ // check to make sure they all loaded //for (index = 0; index < B_EMPTY; index++) for (index = 0; index < 2; index++) { if (g_other_bmps[index] == NULL) { result = false; } } return(result); } SDL_Surface *load_one_bmp(const char *filename) { SDL_Surface *result = SDL_LoadBMP(filename); if (!result) { string err = "Could not load bitmap : "; err = err + filename; printerror(err.c_str()); } return(result); } // Draw's one of our LED's to the screen // value contains the bitmap to draw (0-9 is valid) // x and y contain the coordinates on the screen // This function is called from scoreboard.cpp // 1 is returned on success, 0 on failure bool draw_led(int value, int x, int y) { vid_blit(g_led_bmps[value], x, y); return true; } // Draw overlay digits to the screen void draw_overlay_leds(unsigned int values[], int num_digits, int start_x, int y, SDL_Surface *overlay) { SDL_Rect src, dest; dest.x = start_x; dest.y = y; dest.w = OVERLAY_LED_WIDTH; dest.h = OVERLAY_LED_HEIGHT; src.y = 0; src.w = OVERLAY_LED_WIDTH; src.h = OVERLAY_LED_HEIGHT; /* Draw the digit(s) */ for(int i = 0; i < num_digits; i++) { src.x = values[i] * OVERLAY_LED_WIDTH; SDL_BlitSurface(g_other_bmps[B_OVERLAY_LEDS], &src, overlay, &dest); dest.x += OVERLAY_LED_WIDTH; } dest.x = start_x; dest.w = num_digits * OVERLAY_LED_WIDTH; SDL_UpdateRects(overlay, 1, &dest); } // Draw LDP1450 overlay characters to the screen (added by Brad O.) void draw_singleline_LDP1450(char *LDP1450_String, int start_x, int y, SDL_Surface *overlay) { SDL_Rect src, dest; int i = 0; int value = 0; int LDP1450_strlen; // char s[81]=""; dest.x = start_x; dest.y = y; dest.w = OVERLAY_LDP1450_WIDTH; dest.h = OVERLAY_LDP1450_HEIGHT; src.y = 0; src.w = OVERLAY_LDP1450_WIDTH; src.h = OVERLAY_LDP1450_WIDTH; LDP1450_strlen = strlen(LDP1450_String); if (!LDP1450_strlen) // if a blank line is sent, we must blank out the entire line { strcpy(LDP1450_String," "); LDP1450_strlen = strlen(LDP1450_String); } else { if (LDP1450_strlen <= 11) // pad end of string with spaces (in case previous line was not cleared) { for (i=LDP1450_strlen; i<=11; i++) LDP1450_String[i] = 32; LDP1450_strlen = strlen(LDP1450_String); } } for (i=0; i= 0x26 && value <= 0x39) // numbers and symbols value -= 0x25; else if (value >= 0x41 && value <= 0x5a) // alpha value -= 0x2a; else if (value == 0x13) // special LDP-1450 character (inversed space) value = 0x32; else value = 0x31; // if not a number, symbol, or alpha, recognize as a space src.x = value * OVERLAY_LDP1450_WIDTH; SDL_BlitSurface(g_other_bmps[B_OVERLAY_LDP1450], &src, overlay, &dest); dest.x += OVERLAY_LDP1450_CHARACTER_SPACING; } dest.x = start_x; dest.w = LDP1450_strlen * OVERLAY_LDP1450_CHARACTER_SPACING; // MPO : calling UpdateRects probably isn't necessary and may be harmful //SDL_UpdateRects(overlay, 1, &dest); } // used to draw non LED stuff like scoreboard text // 'which' corresponds to enumerated values bool draw_othergfx(int which, int x, int y, bool bSendToScreenBlitter) { // NOTE : this is drawn to g_screen_blitter, not to g_screen, // to be more friendly to our opengl implementation! SDL_Surface *srf = g_other_bmps[which]; SDL_Rect dest; dest.x = (short) x; dest.y = (short) y; dest.w = (unsigned short) srf->w; dest.h = (unsigned short) srf->h; // if we should blit this to the screen blitter for later use ... if (bSendToScreenBlitter) { SDL_BlitSurface(srf, NULL, g_screen_blitter, &dest); } // else blit it now else { vid_blit(g_other_bmps[which], x, y); } return true; } // de-allocates all of the .bmps that we have allocated void free_bmps() { int nuke_index = 0; // get rid of all the LED's for (; nuke_index < LED_RANGE; nuke_index++) { free_one_bmp(g_led_bmps[nuke_index]); } for (nuke_index = 0; nuke_index < B_EMPTY; nuke_index++) { // check to make sure it exists before we try to free if (g_other_bmps[nuke_index]) { free_one_bmp(g_other_bmps[nuke_index]); } } } void free_one_bmp(SDL_Surface *candidate) { SDL_FreeSurface(candidate); } ////////////////////////////////////////////////////////////////////////////////////////// SDL_Surface *get_screen() { return g_screen; } SDL_Surface *get_screen_blitter() { return g_screen_blitter; } int get_console_initialized() { return g_console_initialized; } bool get_fullscreen() { return g_fullscreen; } // sets our g_fullscreen bool (determines whether will be in fullscreen mode or not) void set_fullscreen(bool value) { g_fullscreen = value; } // by RDG2010 bool get_fakefullscreen() { return g_fakefullscreen; } void set_fakefullscreen(bool value) { g_fakefullscreen = value; } int get_scalefactor() { return g_scalefactor; } void set_scalefactor(int value) { if (value > 100 || value < 50) // Validating in case user inputs crazy values. { printline("Invalid scale value. Ignoring -scalefactor parameter."); g_scalefactor = 100; } else { g_scalefactor = value; } } void set_rotate_degrees(float fDegrees) { g_fRotateDegrees = fDegrees; } void set_sboverlay_characterset(int value) { sboverlay_characterset = value; } // returns video width Uint16 get_video_width() { return g_vid_width; } // sets g_vid_width void set_video_width(Uint16 width) { // Let the user specify whatever width s/he wants (and suffer the consequences) // We need to support arbitrary resolution to accomodate stuff like screen rotation g_vid_width = width; } // returns video height Uint16 get_video_height() { return g_vid_height; } // sets g_vid_height void set_video_height(Uint16 height) { // Let the user specify whatever height s/he wants (and suffer the consequences) // We need to support arbitrary resolution to accomodate stuff like screen rotation g_vid_height = height; } /////////////////////////////////////////////////////////////////////////////////////////// // converts the SDL_Overlay to a BMP file // ASSUMES OVERLAY IS ALREADY LOCKED!!! void take_screenshot(SDL_Overlay *yuvimage) { SDL_Color cur_color = { 0 }; bool bSaveYUV = false; #ifdef DEBUG // in debug mode, save the YUV stuff too bSaveYUV = true; #endif // DEBUG SDL_Surface *rgbimage = SDL_CreateRGBSurface(SDL_SWSURFACE, yuvimage->w, yuvimage->h, 32, 0xFF, 0xFF00, 0xFF0000, 0); if (rgbimage) { Uint32 *cur_pixel = (Uint32 *) rgbimage->pixels; // if we're in YV12 format (old) if (yuvimage->format == SDL_YV12_OVERLAY) { Uint8 *Y = yuvimage->pixels[0]; Uint8 *V = yuvimage->pixels[1]; Uint8 *U = yuvimage->pixels[2]; int y_extra = yuvimage->pitches[0] - yuvimage->w; int v_extra = yuvimage->pitches[1] - (yuvimage->w / 2); int u_extra = yuvimage->pitches[2] - (yuvimage->w / 2); // advance by 2 since the U and V channels are half-sized for (int row = 0; row < yuvimage->h; row += 2) { // advance by 2 since the U and V channels are half-sized for (int col = 0; col < yuvimage->w; col += 2) { // get a 2x2 block of pixels and store them due to YUV structure // upper left yuv2rgb(&cur_color, *Y, *U, *V); cur_pixel[0] = SDL_MapRGB(rgbimage->format, cur_color.r, cur_color.g, cur_color.b); // upper right yuv2rgb(&cur_color, *(Y+1), *U, *V); cur_pixel[1] = SDL_MapRGB(rgbimage->format, cur_color.r, cur_color.g, cur_color.b); // lower left yuv2rgb(&cur_color, *(Y + yuvimage->pitches[0]), *U, *V); cur_pixel[0 + rgbimage->w] = SDL_MapRGB(rgbimage->format, cur_color.r, cur_color.g, cur_color.b); // lower right yuv2rgb(&cur_color, *(Y + yuvimage->pitches[0] + 1), *U, *V); cur_pixel[1 + rgbimage->w] = SDL_MapRGB(rgbimage->format, cur_color.r, cur_color.g, cur_color.b); cur_pixel += 2; // move two pixels over Y += 2; // move two Y values over U++; // but just 1 U and V V++; } Y += y_extra; V += v_extra; U += u_extra; cur_pixel += rgbimage->w; // move down one row (FIXME: I think this should use pitch) // WARNING: the above assumes that the pitch is the width * 4 Y += yuvimage->pitches[0]; // move down one line } } else if (yuvimage->format == SDL_YUY2_OVERLAY) { mpo_io *io = NULL; if (bSaveYUV) { io = mpo_open("surface.YUY2", MPO_OPEN_CREATE); } Uint8 *line_ptr = yuvimage->pixels[0]; for (int row = 0; row < yuvimage->h; row ++) { // if we're saving to a file, then write the YUY2 line if (bSaveYUV) { mpo_write(line_ptr, yuvimage->w * 2, NULL, io); } Uint8 *pixel_ptr = line_ptr; // advance by 2 because we're doing 2 pixels at a time for (int col = 0; col < yuvimage->w; col += 2) { // left yuv2rgb(&cur_color, *(pixel_ptr), *(pixel_ptr+1), *(pixel_ptr+3)); cur_pixel[0] = SDL_MapRGB(rgbimage->format, cur_color.r, cur_color.g, cur_color.b); // right yuv2rgb(&cur_color, *(pixel_ptr+2), *(pixel_ptr+1), *(pixel_ptr+3)); cur_pixel[1] = SDL_MapRGB(rgbimage->format, (cur_color.r & 0xFE), (cur_color.g & 0xFE), (cur_color.b & 0xFE)); cur_pixel += 2; // move 2 pixels over pixel_ptr += 4; // move 4 bytes over } line_ptr += yuvimage->pitches[0]; cur_pixel += (rgbimage->pitch - (rgbimage->w*4)) / 4; // this look complicated, but the pitch should be the width * 4 so it's just a precaution // NOTE : if the pitch is not a multiple of 4, then we've got problems.. } if (io) { mpo_close(io); } } else { printline ("ERROR : unknown YUV format!"); } // now the RGB image has been created, time to save save_screenshot(rgbimage); SDL_FreeSurface(rgbimage); // de-allocate surface, as we no longer need it } // end if RGB image else { printline("ERROR: Could not create RGB image to take screenshot with"); } // if we're in YV12 mode and we should save the YUV stuff, then do so if (bSaveYUV && (yuvimage->format == SDL_YV12_OVERLAY)) { // NOW save the Y, U and V channels in separate files // Most people won't need or want these files, but I have use of them for testing Uint8 *Y = yuvimage->pixels[0]; Uint8 *V = yuvimage->pixels[1]; Uint8 *U = yuvimage->pixels[2]; struct mpo_io *io = mpo_open("surface.Y", MPO_OPEN_CREATE); if (io) { for (int row = 0; row < yuvimage->h; row++) { mpo_write(Y, yuvimage->w, NULL, io); Y += yuvimage->pitches[0]; } mpo_close(io); } io = mpo_open("surface.V", MPO_OPEN_CREATE); if (io) { for (int row = 0; row < (yuvimage->h / 2); row++) { mpo_write(V, yuvimage->w / 2, NULL, io); V += yuvimage->pitches[1]; } mpo_close(io); } io = mpo_open("surface.U", MPO_OPEN_CREATE); if (io) { for (int row = 0; row < (yuvimage->h / 2); row++) { mpo_write(U, yuvimage->w / 2, NULL, io); U += yuvimage->pitches[2]; } mpo_close(io); } } } #ifdef USE_OPENGL void take_screenshot_GL() { SDL_Surface *rgbimage = SDL_CreateRGBSurface(SDL_SWSURFACE, g_vid_width, g_vid_height, 32, 0xFF, 0xFF00, 0xFF0000, 0); if (rgbimage) { unsigned char *bufDst = (unsigned char *) rgbimage->pixels; unsigned int uLineLength = g_vid_width << 2; unsigned char *bufTmp = new unsigned char [uLineLength * g_vid_height]; if (bufTmp) { // grab current screen glReadPixels(0, 0, g_vid_width, g_vid_height, GL_RGBA, GL_UNSIGNED_BYTE, bufTmp); // OpenGL goes bottom to top, and the SDL Surface goes top to bottom, // so we have to flip the image. for (unsigned int uLine = 0; uLine < g_vid_height; ++uLine) { unsigned char *u8Dst = bufDst + (uLine * uLineLength); unsigned char *u8Src = bufTmp + ((g_vid_height - uLine - 1) * uLineLength); memcpy(u8Dst, u8Src, (g_vid_width << 2)); } // now the RGB image has been created, time to save save_screenshot(rgbimage); delete [] bufTmp; } else printline("ERROR: take_screenshot_GL memory allocation failed"); SDL_FreeSurface(rgbimage); // de-allocate surface, as we no longer need it } // end if RGB image else { printline("ERROR: Could not create RGB image to take screenshot with"); } } #endif // USE_OPENGL // saves an SDL surface to a .BMP file in the screenshots directory void save_screenshot(SDL_Surface *shot) { int screenshot_num = 0; char filename[81] = { 0 }; string fullpath; // search for a filename that does not exist for (;;) { screenshot_num++; sprintf(filename, "screen%d.bmp", screenshot_num); fullpath = g_homedir.get_screenshot_dir() + filename; // if file does not exist, we'll save a screenshot to that filename if (!mpo_file_exists(fullpath.c_str())) { break; } } if (SDL_SaveBMP(shot, fullpath.c_str()) == 0) { outstr("NOTE: Wrote screenshot to file "); printline(filename); } else { outstr("ERROR: Could not write screenshot to file "); printline(filename); } } // converts YUV to RGB // Use this only when you don't care about speed =] // NOTE : it is important for y, u, and v to be signed void yuv2rgb(SDL_Color *result, int y, int u, int v) { // NOTE : Visual C++ 7.0 apparently has a bug // in its floating point optimizations because this function // will return incorrect results when compiled as a Release // Possible workaround: use integer math instead of float? or don't use VC++ 7? hehe int b = (int) (1.164*(y - 16) + 2.018*(u - 128)); int g = (int) (1.164*(y - 16) - 0.813*(v - 128) - 0.391*(u - 128)); int r = (int) (1.164*(y - 16) + 1.596*(v - 128)); // clamp values (not sure if this is necessary, but we aren't worried about speed) if (b > 255) b = 255; if (b < 0) b = 0; if (g > 255) g = 255; if (g < 0) g = 0; if (r > 255) r = 255; if (r < 0) r = 0; result->r = (unsigned char) r; result->g = (unsigned char) g; result->b = (unsigned char) b; } void draw_string(const char* t, int col, int row, SDL_Surface* overlay) { SDL_Rect dest; dest.x = (short) ((col*6)); dest.y = (short) ((row*13)); dest.w = (unsigned short) (6 * strlen(t)); // width of rectangle area to draw (width of font * length of string) dest.h = 13; // height of area (height of font) SDL_FillRect(overlay, &dest, 0); // erase anything at our destination before we print new text SDLDrawText(t, overlay, FONT_SMALL, dest.x, dest.y); SDL_UpdateRects(overlay, 1, &dest); } // toggles fullscreen mode void vid_toggle_fullscreen() { // Commented out because it creates major problems with YUV overlays and causing segfaults.. // The real way to toggle fullscreen is to kill overlay, kill surface, make surface, make overlay. /* g_screen = SDL_SetVideoMode(g_screen->w, g_screen->h, g_screen->format->BitsPerPixel, g_screen->flags ^ SDL_FULLSCREEN); */ } // NOTE : put into a separate function to make autotesting easier void set_yuv_hwaccel(bool enabled) { const char *val = "0"; if (enabled) val = "1"; #ifdef WIN32 SetEnvironmentVariable("SDL_VIDEO_YUV_HWACCEL", val); string sEnv = "SDL_VIDEO_YUV_HWACCEL="; sEnv += val; putenv(sEnv.c_str()); #else setenv("SDL_VIDEO_YUV_HWACCEL", val, 1); #endif } bool get_yuv_hwaccel() { bool result = true; // it is enabled by default #ifdef WIN32 char buf[30]; ZeroMemory(buf, sizeof(buf)); DWORD res = GetEnvironmentVariable("SDL_VIDEO_YUV_HWACCEL", buf, sizeof(buf)); if (buf[0] == '0') result = false; #else char *hw_env = getenv("SDL_VIDEO_YUV_HWACCEL"); // if HW acceleration has been disabled if (hw_env && (hw_env[0] == '0')) result = false; #endif return result; } void set_force_aspect_ratio(bool bEnabled) { g_bForceAspectRatio = bEnabled; } bool get_force_aspect_ratio() { return g_bForceAspectRatio; } #ifdef USE_OPENGL void set_use_opengl(bool enabled) { g_bUseOpenGL = enabled; } bool get_use_opengl() { return g_bUseOpenGL; } #endif // USE_OPENGL