bsnes/ruby/video/directdraw.cpp

168 lines
5.1 KiB
C++

#include <ddraw.h>
#undef interface
struct VideoDirectDraw : Video {
VideoDirectDraw() { initialize(); }
~VideoDirectDraw() { terminate(); }
auto ready() -> bool { return _ready; }
auto context() -> uintptr { return _context; }
auto blocking() -> bool { return _blocking; }
auto setContext(uintptr context) -> bool {
if(_context == context) return true;
_context = context;
return initialize();
}
auto setBlocking(bool blocking) -> bool {
if(_blocking == blocking) return true;
_blocking = blocking;
return true;
}
auto clear() -> void {
if(!ready()) return;
DDBLTFX fx = {};
fx.dwSize = sizeof(DDBLTFX);
fx.dwFillColor = 0x00000000;
_screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
_raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
}
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!ready()) return false;
if(width != _width || height != _height) resize(_width = width, _height = height);
DDSURFACEDESC2 description = {};
description.dwSize = sizeof(DDSURFACEDESC2);
if(_raster->Lock(0, &description, DDLOCK_WAIT, 0) != DD_OK) {
_raster->Restore();
if(_raster->Lock(0, &description, DDLOCK_WAIT, 0) != DD_OK) return false;
}
pitch = description.lPitch;
return data = (uint32_t*)description.lpSurface;
}
auto unlock() -> void {
if(!ready()) return;
_raster->Unlock(0);
}
auto output() -> void {
if(!ready()) return;
if(_blocking) while(true) {
BOOL vblank;
_interface->GetVerticalBlankStatus(&vblank);
if(vblank) break;
}
RECT source;
SetRect(&source, 0, 0, _width, _height);
POINT point = {0, 0};
ClientToScreen((HWND)_context, &point);
RECT target;
GetClientRect((HWND)_context, &target);
OffsetRect(&target, point.x, point.y);
if(_screen->Blt(&target, _raster, &source, DDBLT_WAIT, 0) == DDERR_SURFACELOST) {
_screen->Restore();
_raster->Restore();
}
}
private:
auto initialize() -> bool {
terminate();
if(!_context) return false;
LPDIRECTDRAW interface = nullptr;
DirectDrawCreate(0, &interface, 0);
interface->QueryInterface(IID_IDirectDraw7, (void**)&_interface);
interface->Release();
_interface->SetCooperativeLevel((HWND)_context, DDSCL_NORMAL);
DDSURFACEDESC2 description = {};
description.dwSize = sizeof(DDSURFACEDESC2);
description.dwFlags = DDSD_CAPS;
description.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
_interface->CreateSurface(&description, &_screen, 0);
_interface->CreateClipper(0, &_clipper, 0);
_clipper->SetHWnd(0, (HWND)_context);
_screen->SetClipper(_clipper);
_raster = nullptr;
_surfaceWidth = 0;
_surfaceHeight = 0;
resize(_width = 256, _height = 256);
return _ready = true;
}
auto terminate() -> void {
_ready = false;
if(_clipper) { _clipper->Release(); _clipper = nullptr; }
if(_raster) { _raster->Release(); _raster = nullptr; }
if(_screen) { _screen->Release(); _screen = nullptr; }
if(_interface) { _interface->Release(); _interface = nullptr; }
}
auto resize(uint width, uint height) -> void {
if(_surfaceWidth >= width && _surfaceHeight >= height) return;
_surfaceWidth = max(width, _surfaceWidth);
_surfaceHeight = max(height, _surfaceHeight);
if(_raster) _raster->Release();
DDSURFACEDESC2 description = {};
description.dwSize = sizeof(DDSURFACEDESC2);
_screen->GetSurfaceDesc(&description);
int depth = description.ddpfPixelFormat.dwRGBBitCount;
if(depth == 32) goto tryNativeSurface;
memory::fill(&description, sizeof(DDSURFACEDESC2));
description.dwSize = sizeof(DDSURFACEDESC2);
description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
description.dwWidth = _surfaceWidth;
description.dwHeight = _surfaceHeight;
description.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
description.ddpfPixelFormat.dwFlags = DDPF_RGB;
description.ddpfPixelFormat.dwRGBBitCount = 32;
description.ddpfPixelFormat.dwRBitMask = 0xff0000;
description.ddpfPixelFormat.dwGBitMask = 0x00ff00;
description.ddpfPixelFormat.dwBBitMask = 0x0000ff;
if(_interface->CreateSurface(&description, &_raster, 0) == DD_OK) return clear();
tryNativeSurface:
memory::fill(&description, sizeof(DDSURFACEDESC2));
description.dwSize = sizeof(DDSURFACEDESC2);
description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY
description.dwWidth = _surfaceWidth;
description.dwHeight = _surfaceHeight;
if(_interface->CreateSurface(&description, &_raster, 0) == DD_OK) return clear();
}
bool _ready = false;
uintptr _context = 0;
bool _blocking = false;
uint _width = 0;
uint _height = 0;
LPDIRECTDRAW7 _interface = nullptr;
LPDIRECTDRAWSURFACE7 _screen = nullptr;
LPDIRECTDRAWSURFACE7 _raster = nullptr;
LPDIRECTDRAWCLIPPER _clipper = nullptr;
uint _surfaceWidth = 0;
uint _surfaceHeight = 0;
};