//Copyright Paul Reiche, Fred Ford. 1992-2002 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "pure.h" #include "libs/graphics/bbox.h" #include "scalers.h" #include "libs/log.h" #if SDL_MAJOR_VERSION == 1 static SDL_Surface *SDL_Video = NULL; static SDL_Surface *fade_color_surface = NULL; static SDL_Surface *fade_temp = NULL; static SDL_Surface *scaled_display = NULL; static TFB_ScaleFunc scaler = NULL; static Uint32 fade_color; static void TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); static void TFB_Pure_Scaled_Postprocess (void); static void TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount); static void TFB_Pure_Unscaled_Postprocess (void); static void TFB_Pure_UploadTransitionScreen (void); static void TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect); static void TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect); static TFB_GRAPHICS_BACKEND pure_scaled_backend = { TFB_Pure_Scaled_Preprocess, TFB_Pure_Scaled_Postprocess, TFB_Pure_UploadTransitionScreen, TFB_Pure_ScreenLayer, TFB_Pure_ColorLayer }; static TFB_GRAPHICS_BACKEND pure_unscaled_backend = { TFB_Pure_Unscaled_Preprocess, TFB_Pure_Unscaled_Postprocess, TFB_Pure_UploadTransitionScreen, TFB_Pure_ScreenLayer, TFB_Pure_ColorLayer }; // We cannot rely on SDL_DisplayFormatAlpha() anymore. It can return // formats that we do not expect (SDL v1.2.14 on Mac OSX). Mac likes // ARGB surfaces, but SDL_DisplayFormatAlpha thinks that only RGBA are fast. // This is a generic replacement that gives what we want. static void CalcAlphaFormat (const SDL_PixelFormat* video, SDL_PixelFormat* ours) { int valid = 0; // We use 32-bit surfaces internally ours->BitsPerPixel = 32; // Try to get as close to the video format as possible if (video->BitsPerPixel == 15 || video->BitsPerPixel == 16) { // At least match the channel order ours->Rshift = video->Rshift / 5 * 8; ours->Gshift = video->Gshift / 5 * 8; ours->Bshift = video->Bshift / 5 * 8; valid = 1; } else if (video->BitsPerPixel == 24 || video->BitsPerPixel == 32) { // We can only use channels aligned on byte boundary if (video->Rshift % 8 == 0 && video->Gshift % 8 == 0 && video->Bshift % 8 == 0) { // Match RGB in video ours->Rshift = video->Rshift; ours->Gshift = video->Gshift; ours->Bshift = video->Bshift; valid = 1; } } if (valid) { // For alpha, use the unoccupied byte ours->Ashift = 48 - (ours->Rshift + ours->Gshift + ours->Bshift); // Set channels according to byte positions ours->Rmask = 0xff << ours->Rshift; ours->Gmask = 0xff << ours->Gshift; ours->Bmask = 0xff << ours->Bshift; ours->Amask = 0xff << ours->Ashift; return; } // Fallback case. It does not matter what we set, but SDL likes // Alpha to be the highest. ours->Rmask = 0x000000ff; ours->Gmask = 0x0000ff00; ours->Bmask = 0x00ff0000; ours->Amask = 0xff000000; } int TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen) { int i, videomode_flags; SDL_PixelFormat conv_fmt; GraphicsDriver = driver; // must use SDL_SWSURFACE, HWSURFACE doesn't work properly // with fades/scaling if (width == 320 && height == 240) { videomode_flags = SDL_SWSURFACE; ScreenWidthActual = 320; ScreenHeightActual = 240; graphics_backend = &pure_unscaled_backend; } else { videomode_flags = SDL_SWSURFACE; ScreenWidthActual = 640; ScreenHeightActual = 480; graphics_backend = &pure_scaled_backend; if (width != 640 || height != 480) log_add (log_Error, "Screen resolution of %dx%d not supported " "under pure SDL, using 640x480", width, height); } videomode_flags |= SDL_ANYFORMAT; if (flags & TFB_GFXFLAGS_FULLSCREEN) videomode_flags |= SDL_FULLSCREEN; /* We'll ask for a 32bpp frame, but it doesn't really matter, because we've set SDL_ANYFORMAT */ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual, 16, videomode_flags); if (SDL_Video == NULL) { log_add (log_Error, "Couldn't set %ix%i video mode: %s", ScreenWidthActual, ScreenHeightActual, SDL_GetError ()); return -1; } else { const SDL_Surface *video = SDL_GetVideoSurface (); const SDL_PixelFormat* fmt = video->format; ScreenColorDepth = fmt->BitsPerPixel; log_add (log_Info, "Set the resolution to: %ix%ix%i", video->w, video->h, ScreenColorDepth); log_add (log_Info, " Video: R %08x, G %08x, B %08x, A %08x", fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask); if (togglefullscreen) { // NOTE: We cannot change the format_conv_surf now because we // have already loaded lots of graphics and changing it now // will only lead to chaos. // Just check if channel order has changed significantly CalcAlphaFormat (fmt, &conv_fmt); fmt = format_conv_surf->format; if (conv_fmt.Rmask != fmt->Rmask || conv_fmt.Bmask != fmt->Bmask) log_add (log_Warning, "Warning: pixel format has changed " "significantly. Rendering will be slow."); return 0; } } // Create a 32bpp surface in a compatible format which will supply // the format information to all other surfaces used in the game if (format_conv_surf) { SDL_FreeSurface (format_conv_surf); format_conv_surf = NULL; } CalcAlphaFormat (SDL_Video->format, &conv_fmt); format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, conv_fmt.BitsPerPixel, conv_fmt.Rmask, conv_fmt.Gmask, conv_fmt.Bmask, conv_fmt.Amask); if (!format_conv_surf) { log_add (log_Error, "Couldn't create format_conv_surf: %s", SDL_GetError()); return -1; } else { const SDL_PixelFormat* fmt = format_conv_surf->format; log_add (log_Info, " Internal: R %08x, G %08x, B %08x, A %08x", fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask); } for (i = 0; i < TFB_GFX_NUMSCREENS; i++) { if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf, ScreenWidth, ScreenHeight)) return -1; } SDL_Screen = SDL_Screens[0]; TransitionScreen = SDL_Screens[2]; if (0 != SDL1_ReInit_Screen (&fade_color_surface, format_conv_surf, ScreenWidth, ScreenHeight)) return -1; fade_color = SDL_MapRGB (fade_color_surface->format, 0, 0, 0); SDL_FillRect (fade_color_surface, NULL, fade_color); if (0 != SDL1_ReInit_Screen (&fade_temp, format_conv_surf, ScreenWidth, ScreenHeight)) return -1; if (ScreenWidthActual > ScreenWidth || ScreenHeightActual > ScreenHeight) { if (0 != SDL1_ReInit_Screen (&scaled_display, format_conv_surf, ScreenWidthActual, ScreenHeightActual)) return -1; scaler = Scale_PrepPlatform (flags, SDL_Screen->format); } else { // no need to scale scaler = NULL; } return 0; } int TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height) { char VideoName[256]; log_add (log_Info, "Initializing Pure-SDL graphics."); SDL_VideoDriverName (VideoName, sizeof (VideoName)); log_add (log_Info, "SDL driver used: %s", VideoName); (void) renderer; // The "renderer" argument is ignored by SDL1. To control how SDL1 // gets its pixmap, set the environment variable SDL_VIDEODRIVER. // For Linux: x11 (default), dga, fbcon, directfb, svgalib, // ggi, aalib // For Windows: directx (default), windib log_add (log_Info, "SDL initialized."); log_add (log_Info, "Initializing Screen."); ScreenWidth = 320; ScreenHeight = 240; if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0)) { log_add (log_Fatal, "Could not initialize video: " "no fallback at start of program!"); exit (EXIT_FAILURE); } // Initialize scalers (let them precompute whatever) Scale_Init (); return 0; } void TFB_Pure_UninitGraphics (void) { UnInit_Screen (&scaled_display); UnInit_Screen (&fade_color_surface); UnInit_Screen (&fade_temp); } static void ScanLines (SDL_Surface *dst, SDL_Rect *r) { const int rw = r->w * 2; const int rh = r->h * 2; SDL_PixelFormat *fmt = dst->format; const int pitch = dst->pitch; const int len = pitch / fmt->BytesPerPixel; int ddst; Uint32 *p = (Uint32 *) dst->pixels; int x, y; p += len * (r->y * 2) + (r->x * 2); ddst = len + len - rw; for (y = rh; y; y -= 2, p += ddst) { for (x = rw; x; --x, ++p) { // we ignore the lower bits as the difference // of 1 in 255 is negligible *p = ((*p >> 1) & 0x7f7f7f7f) + ((*p >> 2) & 0x3f3f3f3f); } } } static SDL_Surface *backbuffer = NULL, *scalebuffer = NULL; static SDL_Rect updated; static void TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) { if (force_full_redraw != TFB_REDRAW_NO) { updated.x = updated.y = 0; updated.w = ScreenWidth; updated.h = ScreenHeight; } else { updated.x = TFB_BBox.region.corner.x; updated.y = TFB_BBox.region.corner.y; updated.w = TFB_BBox.region.extent.width; updated.h = TFB_BBox.region.extent.height; } if (transition_amount == 255 && fade_amount == 255) backbuffer = SDL_Screens[TFB_SCREEN_MAIN]; else backbuffer = fade_temp; // we can scale directly onto SDL_Video if video is compatible if (SDL_Video->format->BitsPerPixel == SDL_Screen->format->BitsPerPixel && SDL_Video->format->Rmask == SDL_Screen->format->Rmask && SDL_Video->format->Bmask == SDL_Screen->format->Bmask) scalebuffer = SDL_Video; else scalebuffer = scaled_display; } static void TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount) { if (force_full_redraw != TFB_REDRAW_NO) { updated.x = updated.y = 0; updated.w = ScreenWidth; updated.h = ScreenHeight; } else { updated.x = TFB_BBox.region.corner.x; updated.y = TFB_BBox.region.corner.y; updated.w = TFB_BBox.region.extent.width; updated.h = TFB_BBox.region.extent.height; } backbuffer = SDL_Video; (void)transition_amount; (void)fade_amount; } static void TFB_Pure_Scaled_Postprocess (void) { SDL_LockSurface (scalebuffer); SDL_LockSurface (backbuffer); if (scaler) scaler (backbuffer, scalebuffer, &updated); if (GfxFlags & TFB_GFXFLAGS_SCANLINES) ScanLines (scalebuffer, &updated); SDL_UnlockSurface (backbuffer); SDL_UnlockSurface (scalebuffer); updated.x *= 2; updated.y *= 2; updated.w *= 2; updated.h *= 2; if (scalebuffer != SDL_Video) SDL_BlitSurface (scalebuffer, &updated, SDL_Video, &updated); SDL_UpdateRects (SDL_Video, 1, &updated); } static void TFB_Pure_Unscaled_Postprocess (void) { SDL_UpdateRect (SDL_Video, updated.x, updated.y, updated.w, updated.h); } static void TFB_Pure_UploadTransitionScreen (void) { /* This is a no-op in SDL1 Pure mode */ } static void TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect) { if (SDL_Screens[screen] == backbuffer) return; SDL_SetAlpha (SDL_Screens[screen], SDL_SRCALPHA, a); SDL_BlitSurface (SDL_Screens[screen], rect, backbuffer, rect); } static void TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect) { Uint32 col = SDL_MapRGB (fade_color_surface->format, r, g, b); if (col != fade_color) { fade_color = col; SDL_FillRect (fade_color_surface, NULL, fade_color); } SDL_SetAlpha (fade_color_surface, SDL_SRCALPHA, a); SDL_BlitSurface (fade_color_surface, rect, backbuffer, rect); } void Scale_PerfTest (void) { TimeCount TimeStart, TimeIn; TimeCount Now = 0; SDL_Rect updated = {0, 0, ScreenWidth, ScreenHeight}; int i; if (!scaler) { log_add (log_Error, "No scaler configured! " "Run with larger resolution, please"); return; } if (!scaled_display) { log_add (log_Error, "Run scaler performance tests " "in Pure mode, please"); return; } SDL_LockSurface (SDL_Screen); SDL_LockSurface (scaled_display); TimeStart = TimeIn = SDL_GetTicks (); for (i = 1; i < 1001; ++i) // run for 1000 frames { scaler (SDL_Screen, scaled_display, &updated); if (GfxFlags & TFB_GFXFLAGS_SCANLINES) ScanLines (scaled_display, &updated); if (i % 100 == 0) { Now = SDL_GetTicks (); log_add (log_Debug, "%03d(%04u) ", 100*1000 / (Now - TimeIn), Now - TimeIn); TimeIn = Now; } } log_add (log_Debug, "Full frames scaled: %d; over %u ms; %d fps\n", (i - 1), Now - TimeStart, i * 1000 / (Now - TimeStart)); SDL_UnlockSurface (scaled_display); SDL_UnlockSurface (SDL_Screen); } #endif