mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
1427 lines
35 KiB
C++
1427 lines
35 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 2006-2025 by The Odamex Team.
|
|
//
|
|
// 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.
|
|
//
|
|
// DESCRIPTION:
|
|
//
|
|
// Low-level video hardware management.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include "odamex.h"
|
|
|
|
#include <cstdlib>
|
|
#include <climits>
|
|
#include <algorithm>
|
|
|
|
#include "i_sdl.h"
|
|
|
|
#include "i_video.h"
|
|
#include "v_video.h"
|
|
|
|
#if defined(SDL20)
|
|
#include "i_video_sdl20.h"
|
|
#else
|
|
#error "no video subsystem selected"
|
|
#endif
|
|
|
|
#include "i_system.h"
|
|
#include "m_misc.h"
|
|
#include "i_input.h"
|
|
#include "m_fileio.h"
|
|
|
|
#include "w_wad.h"
|
|
#include "cmdlib.h"
|
|
|
|
// [Russell] - Just for windows, display the icon in the system menu and
|
|
// alt-tab display
|
|
#if defined(_WIN32)
|
|
#include "win32inc.h"
|
|
#include "SDL_syswm.h"
|
|
#include "resource.h"
|
|
#endif // _WIN32
|
|
|
|
// Global IVideoSubsystem instance for video startup and shutdown
|
|
static IVideoSubsystem* video_subsystem = NULL;
|
|
|
|
// Global IWindowSurface instance for the application window
|
|
static IWindowSurface* primary_surface = NULL;
|
|
|
|
// Global IWindowSurface instance for converting 8bpp data to a 32bpp surface
|
|
static IWindowSurface* converted_surface = NULL;
|
|
|
|
// Global IWindowSurface instance constructed from primary_surface.
|
|
// Used when matting is required (letter-boxing/pillar-boxing)
|
|
static IWindowSurface* matted_surface = NULL;
|
|
|
|
// Global IWindowSurface instance of size 320x200 or 640x400.
|
|
// Emulates low-resolution video modes by rendering to a small surface
|
|
// and then stretch it to the primary surface after rendering is complete.
|
|
static IWindowSurface* emulated_surface = NULL;
|
|
|
|
extern int NewWidth, NewHeight, NewBits, DisplayBits;
|
|
|
|
static int loading_icon_expire = -1;
|
|
static IWindowSurface* loading_icon_background_surface = NULL;
|
|
|
|
extern IWindowSurface* scaled_screenblocks_surface;
|
|
|
|
EXTERN_CVAR(vid_32bpp)
|
|
EXTERN_CVAR(vid_fullscreen)
|
|
EXTERN_CVAR(vid_vsync)
|
|
EXTERN_CVAR(vid_filter)
|
|
EXTERN_CVAR(vid_overscan)
|
|
EXTERN_CVAR(vid_320x200)
|
|
EXTERN_CVAR(vid_640x400)
|
|
EXTERN_CVAR(vid_autoadjust)
|
|
EXTERN_CVAR(vid_displayfps)
|
|
EXTERN_CVAR(vid_ticker)
|
|
EXTERN_CVAR(vid_widescreen)
|
|
EXTERN_CVAR(sv_allowwidescreen)
|
|
|
|
|
|
// ****************************************************************************
|
|
|
|
|
|
// ============================================================================
|
|
//
|
|
// IWindowSurface abstract base class implementation
|
|
//
|
|
// Default implementation for the IWindowSurface base class.
|
|
//
|
|
// ============================================================================
|
|
|
|
//
|
|
// IWindowSurface::IWindowSurface
|
|
//
|
|
// Initializes a new IWindowSurface given a width, height, and PixelFormat.
|
|
// If a buffer is not given, one will be allocated and marked for deallocation
|
|
// from the destructor. If the pitch is not given, the width will be used as
|
|
// the basis for the pitch.
|
|
//
|
|
IWindowSurface::IWindowSurface(uint16_t width, uint16_t height, const PixelFormat* format,
|
|
void* buffer, uint16_t pitch) :
|
|
mCanvas(NULL),
|
|
mSurfaceBuffer((uint8_t*)buffer), mOwnsSurfaceBuffer(buffer == NULL),
|
|
mPalette(V_GetDefaultPalette()->colors), mPixelFormat(*format),
|
|
mWidth(width), mHeight(height), mPitch(pitch), mLocks(0)
|
|
{
|
|
static constexpr uintptr_t alignment = 16;
|
|
|
|
// Not given a pitch? Just base pitch on the given width
|
|
if (pitch == 0)
|
|
{
|
|
// make the pitch a multiple of the alignment value
|
|
mPitch = (mWidth * mPixelFormat.getBytesPerPixel() + alignment - 1) & ~(alignment - 1);
|
|
// add a little to the pitch to prevent cache thrashing if it's 512 or 1024
|
|
if ((mPitch & 511) == 0)
|
|
mPitch += alignment;
|
|
}
|
|
|
|
mPitchInPixels = mPitch / mPixelFormat.getBytesPerPixel();
|
|
|
|
if (mOwnsSurfaceBuffer)
|
|
{
|
|
uint8_t* buffer = new uint8_t[mPitch * mHeight + alignment];
|
|
|
|
// calculate the offset from buffer to the next aligned memory address
|
|
uintptr_t offset = ((uintptr_t)(buffer + alignment) & ~(alignment - 1)) - (uintptr_t)buffer;
|
|
|
|
mSurfaceBuffer = buffer + offset;
|
|
|
|
// verify we'll have enough room to store offset immediately
|
|
// before mSurfaceBuffer's address and that offset's value is sane.
|
|
assert(offset > 0 && offset <= alignment);
|
|
|
|
// store mSurfaceBuffer's offset from buffer so that mSurfaceBuffer
|
|
// can be properly freed later.
|
|
buffer[offset - 1] = offset;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::IWindowSurface
|
|
//
|
|
// Initializes the IWindowSurface from an existing IWindowSurface, but
|
|
// with possibly different values for width and height. If width or height
|
|
// are less than that of the existing surface, the new surface will be centered
|
|
// inside the existing surface.
|
|
//
|
|
IWindowSurface::IWindowSurface(IWindowSurface* base_surface, uint16_t width, uint16_t height) :
|
|
mCanvas(NULL), mOwnsSurfaceBuffer(false),
|
|
mPixelFormat(*base_surface->getPixelFormat()),
|
|
mPitch(base_surface->getPitch()), mPitchInPixels(base_surface->getPitchInPixels()),
|
|
mLocks(0)
|
|
{
|
|
mWidth = std::min(base_surface->getWidth(), width);
|
|
mHeight = std::min(base_surface->getHeight(), height);
|
|
|
|
// adjust mSurfaceBuffer so that the new surface is centered in base_surface
|
|
uint16_t x = (base_surface->getWidth() - mWidth) / 2;
|
|
uint16_t y = (base_surface->getHeight() - mHeight) / 2;
|
|
|
|
mSurfaceBuffer = (uint8_t*)base_surface->getBuffer(x, y);
|
|
|
|
mPalette = base_surface->mPalette;
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::~IWindowSurface
|
|
//
|
|
// Frees all of the DCanvas objects that were instantiated by this surface.
|
|
//
|
|
IWindowSurface::~IWindowSurface()
|
|
{
|
|
// free all DCanvas objects allocated by this surface
|
|
for (auto& canvas : mCanvasStore)
|
|
delete canvas;
|
|
|
|
// calculate the buffer's original address when freeing mSurfaceBuffer
|
|
if (mOwnsSurfaceBuffer)
|
|
{
|
|
ptrdiff_t offset = mSurfaceBuffer[-1];
|
|
delete [] (mSurfaceBuffer - offset);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Pixel format conversion function used by IWindowSurface::blit
|
|
//
|
|
template <typename SOURCE_PIXEL_T, typename DEST_PIXEL_T>
|
|
static inline DEST_PIXEL_T ConvertPixel(SOURCE_PIXEL_T value, const argb_t* palette);
|
|
|
|
template <>
|
|
inline palindex_t ConvertPixel(palindex_t value, const argb_t* palette)
|
|
{ return value; }
|
|
|
|
template <>
|
|
inline argb_t ConvertPixel(palindex_t value, const argb_t* palette)
|
|
{ return palette[value]; }
|
|
|
|
template <>
|
|
inline palindex_t ConvertPixel(argb_t value, const argb_t* palette)
|
|
{ return 0; }
|
|
|
|
template <>
|
|
inline argb_t ConvertPixel(argb_t value, const argb_t* palette)
|
|
{ return value; }
|
|
|
|
|
|
template <typename SOURCE_PIXEL_T, typename DEST_PIXEL_T>
|
|
static void BlitLoop(DEST_PIXEL_T* dest, const SOURCE_PIXEL_T* source,
|
|
int destpitchpixels, int srcpitchpixels, int destw, int desth,
|
|
fixed_t xstep, fixed_t ystep, const argb_t* palette)
|
|
{
|
|
fixed_t yfrac = 0;
|
|
for (int y = 0; y < desth; y++)
|
|
{
|
|
if (sizeof(DEST_PIXEL_T) == sizeof(SOURCE_PIXEL_T) && xstep == FRACUNIT)
|
|
{
|
|
memcpy(dest, source, destw * sizeof(SOURCE_PIXEL_T));
|
|
}
|
|
else
|
|
{
|
|
fixed_t xfrac = 0;
|
|
for (int x = 0; x < destw; x++)
|
|
{
|
|
dest[x] = ConvertPixel<SOURCE_PIXEL_T, DEST_PIXEL_T>(source[xfrac >> FRACBITS], palette);
|
|
xfrac += xstep;
|
|
}
|
|
}
|
|
|
|
dest += destpitchpixels;
|
|
yfrac += ystep;
|
|
|
|
source += srcpitchpixels * (yfrac >> FRACBITS);
|
|
yfrac &= (FRACUNIT - 1);
|
|
}
|
|
}
|
|
|
|
template <typename SOURCE_PIXEL_T, typename DEST_PIXEL_T>
|
|
static void BlitLoopCrop(DEST_PIXEL_T* dest, const SOURCE_PIXEL_T* source,
|
|
int destpitchpixels, int srcpitchpixels,
|
|
int destw, int desth,
|
|
int off_top, int off_bottom, int off_left, int off_right,
|
|
fixed_t xstep, fixed_t ystep, const argb_t* palette)
|
|
{
|
|
fixed_t yfrac = 0;
|
|
|
|
int pixelcur = 0;
|
|
for (int y = 0; y < desth; y++)
|
|
{
|
|
// Find if we're off the top or bottom of page
|
|
if (y - off_top >= 0 && y < desth - off_bottom)
|
|
{
|
|
if (sizeof(DEST_PIXEL_T) == sizeof(SOURCE_PIXEL_T) && xstep == FRACUNIT)
|
|
{
|
|
for (int x = 0; x < destw; x++)
|
|
{
|
|
// Find if we're off the left or right of page
|
|
if (x - off_left >= 0 && x < destw - off_right)
|
|
{
|
|
dest[pixelcur] = source[x];
|
|
pixelcur++;
|
|
}
|
|
}
|
|
pixelcur = 0;
|
|
}
|
|
else
|
|
{
|
|
fixed_t xfrac = 0;
|
|
for (int x = 0; x < destw; x++)
|
|
{
|
|
// Find if we're off the left or right of page
|
|
if (x - off_left >= 0 && x < destw - off_right)
|
|
{
|
|
dest[pixelcur] = ConvertPixel<SOURCE_PIXEL_T, DEST_PIXEL_T>(source[xfrac >> FRACBITS], palette);
|
|
pixelcur++;
|
|
}
|
|
|
|
xfrac += xstep;
|
|
}
|
|
pixelcur = 0;
|
|
}
|
|
}
|
|
|
|
dest += destpitchpixels;
|
|
yfrac += ystep;
|
|
|
|
source += srcpitchpixels * (yfrac >> FRACBITS);
|
|
yfrac &= (FRACUNIT - 1);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::blitcrop
|
|
//
|
|
// Blits a surface into this surface, automatically scaling the source image
|
|
// to fit the destination dimensions. However, instead of scaling the image to fit
|
|
// the surface dimensions, it scales the image to destination dimensions and then
|
|
// crops it to the screen if it's off the side of the surface in any direction.
|
|
//
|
|
void IWindowSurface::blitcrop(const IWindowSurface* source_surface, int srcx, int srcy,
|
|
int srcw, int srch, int destx, int desty, int destw, int desth)
|
|
{
|
|
int off_left = 0;
|
|
int off_right = 0;
|
|
int off_top = 0;
|
|
int off_bottom = 0;
|
|
|
|
int bufferx = 0;
|
|
int buffery = 0;
|
|
|
|
// clamp to source surface edges
|
|
if (srcx < 0)
|
|
{
|
|
srcw += srcx;
|
|
srcx = 0;
|
|
}
|
|
|
|
if (srcy < 0)
|
|
{
|
|
srch += srcy;
|
|
srcy = 0;
|
|
}
|
|
|
|
if (srcx + srcw > source_surface->getWidth())
|
|
srcw = source_surface->getWidth() - srcx;
|
|
if (srcy + srch > source_surface->getHeight())
|
|
srch = source_surface->getHeight() - srcy;
|
|
|
|
if (srcw == 0 || srch == 0)
|
|
return;
|
|
|
|
// clamp buffer to source edges, but keep destx and desty
|
|
// for clipping purposes
|
|
|
|
if (destx < 0)
|
|
{
|
|
off_left -= destx;
|
|
bufferx = 0;
|
|
}
|
|
else
|
|
{
|
|
bufferx = destx;
|
|
}
|
|
|
|
if (desty < 0)
|
|
{
|
|
off_top -= desty;
|
|
buffery = 0;
|
|
}
|
|
else
|
|
{
|
|
buffery = desty;
|
|
}
|
|
|
|
if (destx + destw > getWidth())
|
|
{
|
|
off_right = destw + destx - getWidth();
|
|
}
|
|
if (desty + desth > getHeight())
|
|
{
|
|
off_bottom = desth + desty - getHeight();
|
|
}
|
|
|
|
if (destw == 0 || desth == 0)
|
|
return;
|
|
|
|
// Our starting position is off the screen
|
|
// And we will never recover from this
|
|
// Someone messed their x/y or desth/w calc up!
|
|
// We're done here!
|
|
|
|
// Too far right to draw anything
|
|
if (destx > getWidth())
|
|
return;
|
|
|
|
// Too far down to draw anything
|
|
if (desty > getHeight())
|
|
return;
|
|
|
|
// Box at the top will never be drawn
|
|
if (desty + desth < 0)
|
|
return;
|
|
|
|
// Box at the left will never be drawn
|
|
if (destx + destw < 0)
|
|
return;
|
|
|
|
fixed_t xstep = FixedDiv(srcw << FRACBITS, destw << FRACBITS);
|
|
fixed_t ystep = FixedDiv(srch << FRACBITS, desth << FRACBITS);
|
|
|
|
int srcbits = source_surface->getBitsPerPixel();
|
|
int destbits = getBitsPerPixel();
|
|
int srcpitchpixels = source_surface->getPitchInPixels();
|
|
int destpitchpixels = getPitchInPixels();
|
|
|
|
const argb_t* palette = source_surface->getPalette();
|
|
|
|
if (srcbits == 8 && destbits == 8)
|
|
{
|
|
const palindex_t* source =
|
|
(palindex_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
palindex_t* dest = (palindex_t*)getBuffer() + buffery * destpitchpixels + bufferx;
|
|
|
|
BlitLoopCrop(dest, source, destpitchpixels, srcpitchpixels,
|
|
destw, desth,
|
|
off_top, off_bottom, off_left, off_right,
|
|
xstep, ystep, palette);
|
|
}
|
|
else if (srcbits == 8 && destbits == 32)
|
|
{
|
|
if (palette == NULL)
|
|
return;
|
|
|
|
const palindex_t* source =
|
|
(palindex_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
argb_t* dest = (argb_t*)getBuffer() + buffery * destpitchpixels + bufferx;
|
|
|
|
BlitLoopCrop(dest, source, destpitchpixels, srcpitchpixels,
|
|
destw, desth,
|
|
off_top, off_bottom, off_left, off_right,
|
|
xstep, ystep, palette);
|
|
}
|
|
else if (srcbits == 32 && destbits == 8)
|
|
{
|
|
// we can't quickly convert from 32bpp source to 8bpp dest so don't bother
|
|
return;
|
|
}
|
|
else if (srcbits == 32 && destbits == 32)
|
|
{
|
|
const argb_t* source =
|
|
(argb_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
argb_t* dest = (argb_t*)getBuffer() + buffery * destpitchpixels + bufferx;
|
|
|
|
BlitLoopCrop(dest, source, destpitchpixels, srcpitchpixels,
|
|
destw, desth,
|
|
off_top, off_bottom, off_left, off_right,
|
|
xstep, ystep, palette);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::blit
|
|
//
|
|
// Blits a surface into this surface, automatically scaling the source image
|
|
// to fit the destination dimensions.
|
|
//
|
|
void IWindowSurface::blit(const IWindowSurface* source_surface, int srcx, int srcy, int srcw, int srch,
|
|
int destx, int desty, int destw, int desth)
|
|
{
|
|
// clamp to source surface edges
|
|
if (srcx < 0)
|
|
{
|
|
srcw += srcx;
|
|
srcx = 0;
|
|
}
|
|
|
|
if (srcy < 0)
|
|
{
|
|
srch += srcy;
|
|
srcy = 0;
|
|
}
|
|
|
|
if (srcx + srcw > source_surface->getWidth())
|
|
srcw = source_surface->getWidth() - srcx;
|
|
if (srcy + srch > source_surface->getHeight())
|
|
srch = source_surface->getHeight() - srcy;
|
|
|
|
if (srcw == 0 || srch == 0)
|
|
return;
|
|
|
|
// clamp to dest surface edges
|
|
if (destx < 0)
|
|
{
|
|
destw += destx;
|
|
destx = 0;
|
|
}
|
|
|
|
if (desty < 0)
|
|
{
|
|
desth += desty;
|
|
desty = 0;
|
|
}
|
|
|
|
if (destx + destw > getWidth())
|
|
destw = getWidth() - destx;
|
|
if (desty + desth > getHeight())
|
|
desth = getHeight() - desty;
|
|
|
|
if (destw == 0 || desth == 0)
|
|
return;
|
|
|
|
fixed_t xstep = FixedDiv(srcw << FRACBITS, destw << FRACBITS);
|
|
fixed_t ystep = FixedDiv(srch << FRACBITS, desth << FRACBITS);
|
|
|
|
int srcbits = source_surface->getBitsPerPixel();
|
|
int destbits = getBitsPerPixel();
|
|
int srcpitchpixels = source_surface->getPitchInPixels();
|
|
int destpitchpixels = getPitchInPixels();
|
|
|
|
const argb_t* palette = source_surface->getPalette();
|
|
|
|
if (srcbits == 8 && destbits == 8)
|
|
{
|
|
const palindex_t* source = (palindex_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
palindex_t* dest = (palindex_t*)getBuffer() + desty * destpitchpixels + destx;
|
|
|
|
BlitLoop(dest, source, destpitchpixels, srcpitchpixels, destw, desth, xstep, ystep, palette);
|
|
}
|
|
else if (srcbits == 8 && destbits == 32)
|
|
{
|
|
if (palette == NULL)
|
|
return;
|
|
|
|
const palindex_t* source = (palindex_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
argb_t* dest = (argb_t*)getBuffer() + desty * destpitchpixels + destx;
|
|
|
|
BlitLoop(dest, source, destpitchpixels, srcpitchpixels, destw, desth, xstep, ystep, palette);
|
|
}
|
|
else if (srcbits == 32 && destbits == 8)
|
|
{
|
|
// we can't quickly convert from 32bpp source to 8bpp dest so don't bother
|
|
return;
|
|
}
|
|
else if (srcbits == 32 && destbits == 32)
|
|
{
|
|
const argb_t* source = (argb_t*)source_surface->getBuffer() + srcy * srcpitchpixels + srcx;
|
|
argb_t* dest = (argb_t*)getBuffer() + desty * destpitchpixels + destx;
|
|
|
|
BlitLoop(dest, source, destpitchpixels, srcpitchpixels, destw, desth, xstep, ystep, palette);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::clear
|
|
//
|
|
// Clears the surface to black.
|
|
//
|
|
void IWindowSurface::clear()
|
|
{
|
|
const argb_t color(255, 0, 0, 0);
|
|
|
|
lock();
|
|
|
|
if (getBitsPerPixel() == 8)
|
|
{
|
|
const argb_t* palette_colors = V_GetDefaultPalette()->basecolors;
|
|
palindex_t color_index = V_BestColor(palette_colors, color);
|
|
palindex_t* dest = (palindex_t*)getBuffer();
|
|
|
|
for (int y = 0; y < getHeight(); y++)
|
|
{
|
|
memset(dest, color_index, getWidth());
|
|
dest += getPitchInPixels();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
argb_t* dest = (argb_t*)getBuffer();
|
|
|
|
for (int y = 0; y < getHeight(); y++)
|
|
{
|
|
for (int x = 0; x < getWidth(); x++)
|
|
dest[x] = color;
|
|
|
|
dest += getPitchInPixels();
|
|
}
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::createCanvas
|
|
//
|
|
// Generic factory function to instantiate a DCanvas object capable of drawing
|
|
// to this surface.
|
|
//
|
|
DCanvas* IWindowSurface::createCanvas()
|
|
{
|
|
DCanvas* canvas = new DCanvas(this);
|
|
mCanvasStore.push_back(canvas);
|
|
|
|
return canvas;
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::getDefaultCanvas
|
|
//
|
|
// Returns the default DCanvas object for the surface, creating it if it
|
|
// has not yet been instantiated.
|
|
//
|
|
DCanvas* IWindowSurface::getDefaultCanvas()
|
|
{
|
|
if (mCanvas == NULL)
|
|
mCanvas = createCanvas();
|
|
|
|
return mCanvas;
|
|
}
|
|
|
|
|
|
//
|
|
// IWindowSurface::releaseCanvas
|
|
//
|
|
// Manually frees a DCanvas object that was instantiated by this surface
|
|
// via the createCanvas function. Note that the destructor takes care of
|
|
// freeing all of the instantiated DCanvas objects.
|
|
//
|
|
void IWindowSurface::releaseCanvas(DCanvas* canvas)
|
|
{
|
|
if (canvas->getSurface() != this)
|
|
I_Error("IWindowSurface::releaseCanvas: releasing canvas not owned by this surface\n");
|
|
|
|
// Remove the DCanvas pointer from the surface's list of allocated canvases
|
|
DCanvasCollection::iterator it = std::find(mCanvasStore.begin(), mCanvasStore.end(), canvas);
|
|
if (it != mCanvasStore.end())
|
|
mCanvasStore.erase(it);
|
|
|
|
if (canvas == mCanvas)
|
|
mCanvas = NULL;
|
|
|
|
delete canvas;
|
|
}
|
|
|
|
|
|
// ****************************************************************************
|
|
|
|
//
|
|
// I_GetVideoModeString
|
|
//
|
|
// Returns a string with a text description of the given video mode.
|
|
//
|
|
std::string I_GetVideoModeString(const IVideoMode& mode)
|
|
{
|
|
const char window_strs[][25] = {
|
|
"window",
|
|
"full screen exclusive",
|
|
"full screen window"
|
|
};
|
|
|
|
return fmt::sprintf("%dx%d %dbpp (%s)", mode.width, mode.height, mode.bpp, window_strs[I_GetWindow()->getWindowMode()]);
|
|
}
|
|
|
|
|
|
//
|
|
// I_IsModeSupported
|
|
//
|
|
// Helper function for I_ValidateVideoMode. Returns true if there is a video
|
|
// mode availible with the desired bpp and screen mode.
|
|
//
|
|
static bool I_IsModeSupported(uint8_t bpp, EWindowMode window_mode)
|
|
{
|
|
const IVideoModeList* modelist = I_GetVideoCapabilities()->getSupportedVideoModes();
|
|
return std::any_of(modelist->cbegin(), modelist->cend(), [bpp, window_mode](const auto& mode){ return mode.bpp == bpp && mode.window_mode == window_mode; });
|
|
}
|
|
|
|
|
|
//
|
|
// I_ValidateVideoMode
|
|
//
|
|
// Transforms the given video mode into a mode that is valid for the current
|
|
// video hardware capabilities.
|
|
//
|
|
static IVideoMode I_ValidateVideoMode(const IVideoMode& mode)
|
|
{
|
|
const IVideoMode invalid_mode(0, 0, 0, WINDOW_Windowed);
|
|
IVideoMode desired_mode = mode;
|
|
|
|
desired_mode.width = clamp<uint16_t>(mode.width, 320, MAXWIDTH);
|
|
desired_mode.height = clamp<uint16_t>(mode.height, 200, MAXHEIGHT);
|
|
desired_mode.bpp = mode.bpp;
|
|
desired_mode.window_mode = mode.window_mode;
|
|
|
|
// If the user requested a windowed mode, we don't have to worry about
|
|
// the requested dimensions aligning to an actual video resolution.
|
|
if (mode.window_mode != WINDOW_Fullscreen || !vid_autoadjust)
|
|
return desired_mode;
|
|
|
|
// Ensure the display type is adhered to
|
|
if (!I_GetVideoCapabilities()->supportsFullScreen())
|
|
desired_mode.window_mode = WINDOW_Windowed;
|
|
else if (!I_GetVideoCapabilities()->supportsWindowed())
|
|
desired_mode.window_mode = WINDOW_Fullscreen;
|
|
|
|
// check if the given bit-depth is supported
|
|
if (!I_IsModeSupported(desired_mode.bpp, desired_mode.window_mode))
|
|
{
|
|
// mode is not supported -- check a different bit depth
|
|
desired_mode.bpp = desired_mode.bpp ^ (32 | 8);
|
|
if (!I_IsModeSupported(desired_mode.bpp, desired_mode.window_mode))
|
|
return invalid_mode;
|
|
}
|
|
|
|
unsigned int closest_dist = UINT_MAX;
|
|
const IVideoMode* closest_mode = NULL;
|
|
|
|
const IVideoModeList& modelist = *I_GetVideoCapabilities()->getSupportedVideoModes();
|
|
for (int iteration = 0; iteration < 2; iteration++)
|
|
{
|
|
for (const auto& mode : modelist)
|
|
{
|
|
if (mode == desired_mode) // perfect match?
|
|
return mode;
|
|
|
|
if (mode.bpp == desired_mode.bpp && mode.window_mode == desired_mode.window_mode)
|
|
{
|
|
if (iteration == 0 && (mode.width < desired_mode.width || mode.height < desired_mode.height))
|
|
continue;
|
|
|
|
unsigned int dist = (mode.width - desired_mode.width) * (mode.width - desired_mode.width)
|
|
+ (mode.height - desired_mode.height) * (mode.height - desired_mode.height);
|
|
|
|
if (dist < closest_dist)
|
|
{
|
|
closest_dist = dist;
|
|
closest_mode = &mode;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closest_mode != NULL)
|
|
return *closest_mode;
|
|
}
|
|
|
|
return invalid_mode;
|
|
}
|
|
|
|
|
|
//
|
|
// I_SetVideoMode
|
|
//
|
|
// Main function to set the video mode at the hardware level.
|
|
//
|
|
void I_SetVideoMode(const IVideoMode& requested_mode)
|
|
{
|
|
// ensure the requested mode is valid
|
|
IVideoMode validated_mode = I_ValidateVideoMode(requested_mode);
|
|
validated_mode.vsync = bool(vid_vsync.asInt());
|
|
validated_mode.stretch_mode = std::string(vid_filter);
|
|
assert(validated_mode.isValid());
|
|
|
|
IWindow* window = I_GetWindow();
|
|
|
|
window->setMode(validated_mode);
|
|
I_ForceUpdateGrab();
|
|
|
|
// [SL] 2011-11-30 - Prevent the player's view angle from moving
|
|
I_FlushInput();
|
|
|
|
// Set up the primary and emulated surfaces
|
|
primary_surface = window->getPrimarySurface();
|
|
int surface_width = primary_surface->getWidth(), surface_height = primary_surface->getHeight();
|
|
|
|
I_FreeSurface(converted_surface);
|
|
I_FreeSurface(matted_surface);
|
|
I_FreeSurface(emulated_surface);
|
|
I_FreeSurface(scaled_screenblocks_surface);
|
|
|
|
// Handle a requested 8bpp surface when the video capabilities only support 32bpp
|
|
if (requested_mode.bpp != validated_mode.bpp)
|
|
{
|
|
const PixelFormat* format = requested_mode.bpp == 8 ? I_Get8bppPixelFormat() : I_Get32bppPixelFormat();
|
|
converted_surface = new IWindowSurface(surface_width, surface_height, format);
|
|
primary_surface = converted_surface;
|
|
}
|
|
|
|
// clear window's surface to all black;
|
|
primary_surface->lock();
|
|
primary_surface->clear();
|
|
primary_surface->unlock();
|
|
|
|
// [SL] Determine the size of the matted surface.
|
|
// A matted surface will be used if pillar-boxing or letter-boxing are used, or
|
|
// if vid_320x200/vid_640x400 are being used in a wide-screen video mode, or
|
|
// if vid_overscan is used to create a matte around the entire screen.
|
|
//
|
|
// Only vid_overscan creates a matted surface if the video mode is 320x200 or 640x400.
|
|
//
|
|
|
|
if (vid_overscan < 1.0f)
|
|
{
|
|
surface_width *= vid_overscan;
|
|
surface_height *= vid_overscan;
|
|
}
|
|
|
|
if (!I_IsProtectedResolution(I_GetVideoWidth(), I_GetVideoHeight()))
|
|
{
|
|
if (vid_320x200 || vid_640x400 || (!serverside && !sv_allowwidescreen))
|
|
{
|
|
surface_width = surface_height * 4 / 3;
|
|
}
|
|
else
|
|
{
|
|
int ratios_x[] = { 4, 0, 16, 16, 21, 32 };
|
|
int ratios_y[] = { 3, 0, 10, 9, 9, 9 };
|
|
int ratio_x = ratios_x[vid_widescreen.asInt()];
|
|
int ratio_y = ratios_y[vid_widescreen.asInt()];
|
|
|
|
// Clamp "Auto" aspect ratio.
|
|
if (vid_widescreen.asInt() == 1)
|
|
{
|
|
if (3 * surface_width < 4 * surface_height)
|
|
{
|
|
ratio_x = 4;
|
|
ratio_y = 3;
|
|
}
|
|
else if (9 * surface_width > 32 * surface_height)
|
|
{
|
|
ratio_x = 32;
|
|
ratio_y = 9;
|
|
}
|
|
}
|
|
|
|
// Pillarbox or letterbox as needed.
|
|
if (ratio_x > 0 && ratio_y > 0)
|
|
{
|
|
if (surface_height * ratio_x < surface_width * ratio_y)
|
|
surface_width = surface_height * ratio_x / ratio_y;
|
|
else if (surface_height * ratio_x > surface_width * ratio_y)
|
|
surface_height = surface_width * ratio_y / ratio_x;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensure matted surface dimensions are sane and sanitized.
|
|
surface_width = clamp<uint16_t>(surface_width, 320, MAXWIDTH);
|
|
surface_height = clamp<uint16_t>(surface_height, 200, MAXHEIGHT);
|
|
|
|
// Is matting being used? Create matted_surface based on the primary_surface.
|
|
if (surface_width != primary_surface->getWidth() ||
|
|
surface_height != primary_surface->getHeight())
|
|
{
|
|
matted_surface = new IWindowSurface(primary_surface, surface_width, surface_height);
|
|
primary_surface = matted_surface;
|
|
}
|
|
|
|
// Create emulated_surface for emulating low resolution modes.
|
|
if (vid_320x200)
|
|
{
|
|
emulated_surface = new IWindowSurface(320, 200, primary_surface->getPixelFormat());
|
|
emulated_surface->clear();
|
|
}
|
|
else if (vid_640x400)
|
|
{
|
|
emulated_surface = new IWindowSurface(640, 400, primary_surface->getPixelFormat());
|
|
emulated_surface->clear();
|
|
}
|
|
|
|
screen = primary_surface->getDefaultCanvas();
|
|
|
|
assert(I_VideoInitialized());
|
|
|
|
if (window->getVideoMode() != requested_mode)
|
|
DPrintFmt("I_SetVideoMode: could not set video mode to {}. Using {} instead.\n",
|
|
I_GetVideoModeString(requested_mode),
|
|
I_GetVideoModeString(window->getVideoMode()));
|
|
else
|
|
DPrintFmt("I_SetVideoMode: set video mode to {}\n",
|
|
I_GetVideoModeString(window->getVideoMode()));
|
|
|
|
const argb_t* palette = V_GetGamePalette()->colors;
|
|
if (matted_surface)
|
|
matted_surface->setPalette(palette);
|
|
if (emulated_surface)
|
|
emulated_surface->setPalette(palette);
|
|
if (converted_surface)
|
|
converted_surface->setPalette(palette);
|
|
|
|
window->enableRefresh();
|
|
}
|
|
|
|
|
|
//
|
|
// I_VideoInitialized
|
|
//
|
|
// Returns true if the video subsystem has been initialized.
|
|
//
|
|
bool I_VideoInitialized()
|
|
{
|
|
return video_subsystem != NULL && I_GetWindow() != NULL && I_GetWindow()->getPrimarySurface() != NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_ShutdownHardware
|
|
//
|
|
// Destroys the application window and frees its memory.
|
|
//
|
|
void STACK_ARGS I_ShutdownHardware()
|
|
{
|
|
I_FreeSurface(loading_icon_background_surface);
|
|
|
|
delete video_subsystem;
|
|
video_subsystem = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_InitHardware
|
|
//
|
|
void I_InitHardware()
|
|
{
|
|
if (I_IsHeadless())
|
|
{
|
|
video_subsystem = new IDummyVideoSubsystem();
|
|
}
|
|
else
|
|
{
|
|
#if defined(SDL20)
|
|
video_subsystem = new ISDL20VideoSubsystem();
|
|
#endif
|
|
assert(video_subsystem != NULL);
|
|
|
|
const IVideoMode& native_mode = I_GetVideoCapabilities()->getNativeMode();
|
|
PrintFmt(PRINT_HIGH, "I_InitHardware: native resolution: {:s}\n", I_GetVideoModeString(native_mode));
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetVideoCapabilities
|
|
//
|
|
const IVideoCapabilities* I_GetVideoCapabilities()
|
|
{
|
|
// a valid IWindow is not needed for querying video capabilities
|
|
// so don't call I_VideoInitialized
|
|
if (video_subsystem)
|
|
return video_subsystem->getVideoCapabilities();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetWindow
|
|
//
|
|
// Returns a pointer to the application window object.
|
|
//
|
|
IWindow* I_GetWindow()
|
|
{
|
|
if (video_subsystem)
|
|
return video_subsystem->getWindow();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetVideoWidth
|
|
//
|
|
// Returns the width of the current video mode. Assumes that the video
|
|
// window has already been created.
|
|
//
|
|
int I_GetVideoWidth()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetWindow()->getWidth();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetVideoHeight
|
|
//
|
|
// Returns the height of the current video mode. Assumes that the video
|
|
// window has already been created.
|
|
//
|
|
int I_GetVideoHeight()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetWindow()->getHeight();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetVideoBitDepth
|
|
//
|
|
// Returns the bits per pixelof the current video mode. Assumes that the video
|
|
// window has already been created.
|
|
//
|
|
int I_GetVideoBitDepth()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetWindow()->getBitsPerPixel();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetPrimarySurface
|
|
//
|
|
// Returns a pointer to the application window's primary surface object.
|
|
//
|
|
IWindowSurface* I_GetPrimarySurface()
|
|
{
|
|
return primary_surface;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetPrimaryCanvas
|
|
//
|
|
// Returns a pointer to the primary surface's default canvas.
|
|
//
|
|
DCanvas* I_GetPrimaryCanvas()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetPrimarySurface()->getDefaultCanvas();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetEmulatedSurface
|
|
//
|
|
IWindowSurface* I_GetEmulatedSurface()
|
|
{
|
|
return emulated_surface;
|
|
}
|
|
|
|
|
|
//
|
|
// I_BlitEmulatedSurface
|
|
//
|
|
// Blits the emulated surface (320x200 or 640x400) to the primary surface,
|
|
// stretching it to fill the entire screen.
|
|
//
|
|
void I_BlitEmulatedSurface()
|
|
{
|
|
if (emulated_surface)
|
|
{
|
|
primary_surface->blit(emulated_surface, 0, 0,
|
|
emulated_surface->getWidth(), emulated_surface->getHeight(),
|
|
0, 0, primary_surface->getWidth(), primary_surface->getHeight());
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// I_AllocateSurface
|
|
//
|
|
// Creates a new (non-primary) surface and returns it.
|
|
//
|
|
IWindowSurface* I_AllocateSurface(int width, int height, int bpp)
|
|
{
|
|
const PixelFormat* format;
|
|
|
|
if (I_GetPrimarySurface() && bpp == I_GetPrimarySurface()->getBitsPerPixel())
|
|
format = I_GetPrimarySurface()->getPixelFormat();
|
|
else if (bpp == 8)
|
|
format = I_Get8bppPixelFormat();
|
|
else
|
|
format = I_Get32bppPixelFormat();
|
|
|
|
return new IWindowSurface(width, height, format);
|
|
}
|
|
|
|
|
|
//
|
|
// I_FreeSurface
|
|
//
|
|
void I_FreeSurface(IWindowSurface* &surface)
|
|
{
|
|
delete surface;
|
|
surface = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetSurfaceWidth
|
|
//
|
|
int I_GetSurfaceWidth()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetPrimarySurface()->getWidth();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetSurfaceHeight
|
|
//
|
|
int I_GetSurfaceHeight()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetPrimarySurface()->getHeight();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_IsProtectedReslotuion
|
|
//
|
|
// [ML] If this is 320x200 or 640x400, the resolutions
|
|
// "protected" from aspect ratio correction.
|
|
//
|
|
bool I_IsProtectedResolution(int width, int height)
|
|
{
|
|
return (width == 320 && height == 200) || (width == 640 && height == 400);
|
|
}
|
|
|
|
bool I_IsProtectedResolution(const IWindowSurface* surface)
|
|
{
|
|
return I_IsProtectedResolution(surface->getWidth(), surface->getHeight());
|
|
}
|
|
|
|
//
|
|
// I_IsWideResolution
|
|
//
|
|
bool I_IsWideResolution(int width, int height)
|
|
{
|
|
if (I_IsProtectedResolution(width, height))
|
|
return false;
|
|
|
|
// consider the mode widescreen if it's width-to-height ratio is
|
|
// closer to 16:10 than it is to 4:3
|
|
return abs(15 * width - 20 * height) > abs(15 * width - 24 * height);
|
|
}
|
|
|
|
bool I_IsWideResolution(const IWindowSurface* surface)
|
|
{
|
|
return I_IsWideResolution(surface->getWidth(), surface->getHeight());
|
|
}
|
|
|
|
|
|
//
|
|
// I_LockAllSurfaces
|
|
//
|
|
static void I_LockAllSurfaces()
|
|
{
|
|
if (emulated_surface)
|
|
emulated_surface->lock();
|
|
if (matted_surface)
|
|
matted_surface->lock();
|
|
if (converted_surface)
|
|
converted_surface->lock();
|
|
primary_surface->lock();
|
|
|
|
I_GetWindow()->lockSurface();
|
|
}
|
|
|
|
|
|
//
|
|
// I_UnlockAllSurfaces
|
|
//
|
|
static void I_UnlockAllSurfaces()
|
|
{
|
|
I_GetWindow()->unlockSurface();
|
|
|
|
primary_surface->unlock();
|
|
|
|
if (converted_surface)
|
|
converted_surface->unlock();
|
|
if (matted_surface)
|
|
matted_surface->unlock();
|
|
if (emulated_surface)
|
|
emulated_surface->unlock();
|
|
}
|
|
|
|
|
|
//
|
|
// I_DrawLoadingIcon
|
|
//
|
|
// Sets a flag to indicate that I_FinishUpdate should draw the loading (disk)
|
|
// icon in the lower right corner of the screen.
|
|
//
|
|
void I_DrawLoadingIcon()
|
|
{
|
|
loading_icon_expire = gametic;
|
|
}
|
|
|
|
|
|
//
|
|
// I_BlitLoadingIcon
|
|
//
|
|
// Takes care of the actual drawing of the loading icon.
|
|
//
|
|
static void I_BlitLoadingIcon()
|
|
{
|
|
const patch_t* diskpatch = W_CachePatch("STDISK");
|
|
IWindowSurface* surface = I_GetPrimarySurface();
|
|
|
|
surface->lock();
|
|
|
|
int bpp = surface->getBitsPerPixel();
|
|
int scale = std::min(CleanXfac, CleanYfac);
|
|
int w = diskpatch->width() * scale;
|
|
int h = diskpatch->height() * scale;
|
|
int x = surface->getWidth() - w;
|
|
int y = surface->getHeight() - h;
|
|
|
|
// offset x and y for the lower right corner of the screen
|
|
int ofsx = x + (scale * diskpatch->leftoffset());
|
|
int ofsy = y + (scale * diskpatch->topoffset());
|
|
|
|
// save the area where the icon will be drawn to an off-screen surface
|
|
// so that it can be restored after the frame is blitted
|
|
if (!loading_icon_background_surface ||
|
|
loading_icon_background_surface->getWidth() != w ||
|
|
loading_icon_background_surface->getHeight() != h ||
|
|
loading_icon_background_surface->getBitsPerPixel() != bpp)
|
|
{
|
|
I_FreeSurface(loading_icon_background_surface);
|
|
|
|
loading_icon_background_surface = I_AllocateSurface(w, h, bpp);
|
|
}
|
|
|
|
loading_icon_background_surface->lock();
|
|
|
|
loading_icon_background_surface->blit(surface, x, y, w, h, 0, 0, w, h);
|
|
surface->getDefaultCanvas()->DrawPatchStretched(diskpatch, ofsx, ofsy, w, h);
|
|
|
|
loading_icon_background_surface->unlock();
|
|
surface->unlock();
|
|
}
|
|
|
|
|
|
//
|
|
// I_RestoreLoadingIcon
|
|
//
|
|
// Restores the background area that was saved prior to blitting the
|
|
// loading icon.
|
|
//
|
|
static void I_RestoreLoadingIcon()
|
|
{
|
|
IWindowSurface* surface = I_GetPrimarySurface();
|
|
|
|
surface->lock();
|
|
loading_icon_background_surface->lock();
|
|
|
|
int w = loading_icon_background_surface->getWidth();
|
|
int h = loading_icon_background_surface->getHeight();
|
|
int x = surface->getWidth() - w;
|
|
int y = surface->getHeight() - h;
|
|
|
|
surface->blit(loading_icon_background_surface, 0, 0, w, h, x, y, w, h);
|
|
|
|
loading_icon_background_surface->unlock();
|
|
surface->unlock();
|
|
}
|
|
|
|
|
|
//
|
|
// I_BeginUpdate
|
|
//
|
|
// Called at the start of drawing a new video frame. This locks the primary
|
|
// surface for drawing.
|
|
//
|
|
void I_BeginUpdate()
|
|
{
|
|
if (I_VideoInitialized())
|
|
{
|
|
I_GetWindow()->startRefresh();
|
|
|
|
I_LockAllSurfaces();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// I_FinishUpdate
|
|
//
|
|
// Called at the end of drawing a video frame. This unlocks the primary
|
|
// surface for drawing and blits the contents to the video screen.
|
|
//
|
|
void I_FinishUpdate()
|
|
{
|
|
if (I_VideoInitialized())
|
|
{
|
|
// draws little dots on the bottom of the screen
|
|
if (vid_ticker)
|
|
V_DrawFPSTicker();
|
|
|
|
// Draws frame time and cumulative fps
|
|
if (vid_displayfps)
|
|
V_DrawFPSWidget();
|
|
|
|
// draws a disk loading icon in the lower right corner
|
|
if (gametic <= loading_icon_expire)
|
|
I_BlitLoadingIcon();
|
|
|
|
// Handle blitting our 8bpp surface to the 32bpp video window surface
|
|
if (converted_surface)
|
|
{
|
|
IWindowSurface* real_primary_surface = I_GetWindow()->getPrimarySurface();
|
|
real_primary_surface->blit(converted_surface,
|
|
0, 0, converted_surface->getWidth(), converted_surface->getHeight(),
|
|
0, 0, real_primary_surface->getWidth(), real_primary_surface->getHeight());
|
|
}
|
|
|
|
I_UnlockAllSurfaces();
|
|
|
|
I_GetWindow()->finishRefresh();
|
|
|
|
// restores the background underneath the disk loading icon in the lower right corner
|
|
if (gametic <= loading_icon_expire)
|
|
I_RestoreLoadingIcon();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// I_SetPalette
|
|
//
|
|
void I_SetPalette(const argb_t* palette)
|
|
{
|
|
if (I_VideoInitialized())
|
|
I_GetWindow()->setPalette(palette);
|
|
}
|
|
|
|
|
|
//
|
|
// I_SetWindowCaption
|
|
//
|
|
// Set the window caption.
|
|
//
|
|
void I_SetWindowCaption(const std::string& caption)
|
|
{
|
|
// [Russell] - A basic version string that will eventually get replaced
|
|
|
|
std::string title("Odamex ");
|
|
title += NiceVersion();
|
|
|
|
if (!caption.empty())
|
|
title += " - " + caption;
|
|
|
|
I_GetWindow()->setWindowTitle(title);
|
|
}
|
|
|
|
|
|
//
|
|
// I_SetWindowIcon
|
|
//
|
|
// Set the window icon (currently Windows only).
|
|
//
|
|
void I_SetWindowIcon()
|
|
{
|
|
I_GetWindow()->setWindowIcon();
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetMonitorCount
|
|
//
|
|
int I_GetMonitorCount()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return video_subsystem->getMonitorCount();
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// I_GetVideoDriverName
|
|
//
|
|
// Returns the name of the current video driver in-use.
|
|
//
|
|
std::string I_GetVideoDriverName()
|
|
{
|
|
if (I_VideoInitialized())
|
|
return I_GetWindow()->getVideoDriverName();
|
|
return std::string();
|
|
}
|
|
|
|
|
|
//
|
|
// I_Get8bppPixelFormat
|
|
//
|
|
// Returns the platform's PixelFormat for 8bpp video modes.
|
|
//
|
|
const PixelFormat* I_Get8bppPixelFormat()
|
|
{
|
|
static PixelFormat format(8, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
return &format;
|
|
}
|
|
|
|
|
|
//
|
|
// I_Get32bppPixelFormat
|
|
//
|
|
// Returns the platform's PixelFormat for 32bpp video modes.
|
|
// This changes on certain platforms depending on the current video mode
|
|
// in use (or fullscreen vs windowed).
|
|
//
|
|
const PixelFormat* I_Get32bppPixelFormat()
|
|
{
|
|
if (I_GetPrimarySurface() && I_GetPrimarySurface()->getBitsPerPixel() == 32)
|
|
return I_GetPrimarySurface()->getPixelFormat();
|
|
|
|
#ifdef __BIG_ENDIAN__
|
|
static PixelFormat format(32, 0, 0, 0, 0, 0, 8, 16, 24);
|
|
#else
|
|
static PixelFormat format(32, 0, 0, 0, 0, 24, 16, 8, 0);
|
|
#endif
|
|
|
|
return &format;
|
|
}
|
|
|
|
int I_GetAspectCorrectWidth(int surface_height, int asset_height, int asset_width)
|
|
{
|
|
float aspect_scale_ratio = (float)surface_height / (float)asset_height;
|
|
return aspect_scale_ratio * asset_width;
|
|
}
|
|
|
|
VERSION_CONTROL (i_video_cpp, "$Id$")
|