mirror of https://github.com/bsnes-emu/bsnes.git
Update to v103r18 release.
byuu says: Changelog: - tomoko: improved handling of changing audio devices on the audio settings panel - ruby/audio/wasapi: added device enumeration and selection support¹ - ruby/audio/wasapi: release property store handle from audio device - ruby/audio/wasapi: fix exclusive mode buffer filling - ruby/video/glx2: ported to new API -- tested and confirmed working great² - ruby/video/sdl: fixed initialization -- tested and confirmed working on FreeBSD now³ - ruby/video/xv: ported to new API -- tested and mostly working great, sans fullscreen mode⁴ Errata: - accidentally changed "Driver Settings" label to "Driver" on the audio settings tab because I deleted the line and forgot the "Settings" part - need to use "return initialize();" from setDevice() in the WASAPI driver, instead of "return true;", so device selection is currently not functioning in this WIP for said driver ¹: for now, this will likely end up selecting the first available endpoint device, which is probably wrong. I need to come up with a system to expose good 'default values' when selecting new audio drivers, or changing audio device settings. ²: glx2 is a fallback driver for system with only OpenGL 2.0 and no OpenGL 3.2 drivers, such as FreeBSD 10.1 with AMD graphics cards. ³: although I really should track down why InputManager::poll() is crashing the emulator when Video::ready() returns false ... ⁴: really bizarrely, when entering fullscreen mode, it looks like the image was a triangle strip, and the bottom right triange is missing, and the top left triangle skews the entire image into it. I'm suspecting this is a Radeon driver bug when trying to create a 2560x1600 X-Video surface. The glitch persists when exiting fullscreen, too. If anyone can test the X-Video driver on their Linux/BSD system, it'd be appreciated. If it's just my video card, I'll ignore it. If not, hopefully someone can find the cause of the issue :|
This commit is contained in:
parent
0b4e7fb5a5
commit
284e4c043e
|
@ -12,7 +12,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "103.17";
|
||||
static const string Version = "103.18";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -26,11 +26,11 @@ else ifeq ($(platform),macosx)
|
|||
ruby += audio.openal
|
||||
ruby += #input.quartz input.carbon
|
||||
else ifeq ($(platform),linux)
|
||||
ruby += video.xshm video.sdl #video.glx video.xv
|
||||
ruby += video.glx2 video.xv video.xshm video.sdl #video.glx
|
||||
ruby += audio.oss audio.openal #audio.alsa audio.pulseaudio audio.pulseaudiosimple audio.ao
|
||||
ruby += input.sdl input.xlib #input.udev
|
||||
else ifeq ($(platform),bsd)
|
||||
ruby += video.xshm #video.glx video.xv
|
||||
ruby += video.glx2 video.xv video.xshm video.sdl #video.glx
|
||||
ruby += audio.oss audio.openal
|
||||
ruby += input.sdl input.xlib
|
||||
endif
|
||||
|
|
|
@ -4,10 +4,19 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
|
||||
layout.setMargin(5);
|
||||
|
||||
driverLabel.setFont(Font().setBold()).setText("Driver Settings");
|
||||
driverLabel.setFont(Font().setBold()).setText("Driver");
|
||||
|
||||
deviceLabel.setText("Device:");
|
||||
deviceList.onChange([&] { updateDriver(); updateDriverLists(); });
|
||||
deviceList.onChange([&] { updateDevice(); updateDriver(); });
|
||||
|
||||
//the device list never changes once a driver is activated;
|
||||
//however, the available frequencies and latencies may change when the active device is changed
|
||||
for(auto& device : audio->information().devices) {
|
||||
deviceList.append(ComboButtonItem().setText(device));
|
||||
if(device == settings["Audio/Device"].text()) {
|
||||
deviceList.item(deviceList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
|
||||
frequencyLabel.setText("Frequency:");
|
||||
frequencyList.onChange([&] { updateDriver(); });
|
||||
|
@ -30,14 +39,29 @@ AudioSettings::AudioSettings(TabFrame* parent) : TabFrameItem(parent) {
|
|||
|
||||
reverbEnable.setText("Reverb").setChecked(settings["Audio/Reverb/Enable"].boolean()).onToggle([&] { updateEffects(); });
|
||||
|
||||
updateDriverLists();
|
||||
updateDevice();
|
||||
updateDriver(true);
|
||||
updateEffects(true);
|
||||
}
|
||||
|
||||
//when changing audio drivers, device/frequency/latency values may no longer be valid for new driver
|
||||
//updateDriverLists() will try to select a match if one is found
|
||||
//otherwise, this function will force now-invalid settings to the first setting in each list
|
||||
auto AudioSettings::updateDevice() -> void {
|
||||
frequencyList.reset();
|
||||
for(auto& frequency : audio->information().frequencies) {
|
||||
frequencyList.append(ComboButtonItem().setText(frequency));
|
||||
if(frequency == settings["Audio/Frequency"].real()) {
|
||||
frequencyList.item(frequencyList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
|
||||
latencyList.reset();
|
||||
for(auto& latency : audio->information().latencies) {
|
||||
latencyList.append(ComboButtonItem().setText(latency));
|
||||
if(latency == settings["Audio/Latency"].natural()) {
|
||||
latencyList.item(latencyList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto AudioSettings::updateDriver(bool initializing) -> void {
|
||||
settings["Audio/Device"].setValue(deviceList.selected().text());
|
||||
settings["Audio/Frequency"].setValue(frequencyList.selected().text());
|
||||
|
@ -58,33 +82,3 @@ auto AudioSettings::updateEffects(bool initializing) -> void {
|
|||
|
||||
if(!initializing) program->updateAudioEffects();
|
||||
}
|
||||
|
||||
//called during initialization, and after changing audio device
|
||||
//each audio device may have separately supported frequencies and/or latencies
|
||||
auto AudioSettings::updateDriverLists() -> void {
|
||||
auto information = audio->information();
|
||||
|
||||
deviceList.reset();
|
||||
for(auto& device : information.devices) {
|
||||
deviceList.append(ComboButtonItem().setText(device));
|
||||
if(device == settings["Audio/Device"].text()) {
|
||||
deviceList.item(deviceList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
|
||||
frequencyList.reset();
|
||||
for(auto& frequency : information.frequencies) {
|
||||
frequencyList.append(ComboButtonItem().setText(frequency));
|
||||
if(frequency == settings["Audio/Frequency"].real()) {
|
||||
frequencyList.item(frequencyList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
|
||||
latencyList.reset();
|
||||
for(auto& latency : information.latencies) {
|
||||
latencyList.append(ComboButtonItem().setText(latency));
|
||||
if(latency == settings["Audio/Latency"].natural()) {
|
||||
latencyList.item(latencyList.itemCount() - 1).setSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,9 +63,9 @@ struct AudioSettings : TabFrameItem {
|
|||
HorizontalSlider balanceSlider{&balanceLayout, Size{~0, 0}};
|
||||
CheckLabel reverbEnable{&layout, Size{~0, 0}};
|
||||
|
||||
auto updateDevice() -> void;
|
||||
auto updateDriver(bool initializing = false) -> void;
|
||||
auto updateEffects(bool initializing = false) -> void;
|
||||
auto updateDriverLists() -> void;
|
||||
};
|
||||
|
||||
struct InputSettings : TabFrameItem {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <audiopolicy.h>
|
||||
#include <devicetopology.h>
|
||||
#include <endpointvolume.h>
|
||||
#include <functiondiscoverykeys_devpkey.h>
|
||||
|
||||
struct AudioWASAPI : Audio {
|
||||
AudioWASAPI() { initialize(); }
|
||||
|
@ -13,14 +14,15 @@ struct AudioWASAPI : Audio {
|
|||
|
||||
auto information() -> Information {
|
||||
Information information;
|
||||
information.devices = {"Default"};
|
||||
for(auto& device : _devices) information.devices.append(device);
|
||||
information.channels = {2};
|
||||
information.frequencies = {(double)_frequency};
|
||||
information.latencies = {20, 40, 60, 80, 100};
|
||||
information.latencies = {0, 20, 40, 60, 80, 100};
|
||||
return information;
|
||||
}
|
||||
|
||||
auto exclusive() -> bool { return _exclusive; }
|
||||
auto device() -> string { return _device; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto channels() -> uint { return _channels; }
|
||||
auto frequency() -> double { return (double)_frequency; }
|
||||
|
@ -32,6 +34,12 @@ struct AudioWASAPI : Audio {
|
|||
return initialize();
|
||||
}
|
||||
|
||||
auto setDevice(string device) -> bool {
|
||||
if(_device == device) return true;
|
||||
_device = device;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
|
@ -77,31 +85,65 @@ private:
|
|||
terminate();
|
||||
|
||||
if(CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&_enumerator) != S_OK) return false;
|
||||
if(_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &_audioDevice) != S_OK) return false;
|
||||
|
||||
//enumerate all audio endpoint devices, and select the first to match the device() name
|
||||
IMMDeviceCollection* deviceCollection = nullptr;
|
||||
if(_enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection) != S_OK) return false;
|
||||
uint deviceCount = 0;
|
||||
if(deviceCollection->GetCount(&deviceCount) != S_OK) return false;
|
||||
for(uint deviceIndex : range(deviceCount)) {
|
||||
IMMDevice* device = nullptr;
|
||||
if(deviceCollection->Item(deviceIndex, &device) != S_OK) return false;
|
||||
IPropertyStore* propertyStore = nullptr;
|
||||
device->OpenPropertyStore(STGM_READ, &propertyStore);
|
||||
PROPVARIANT propVariant;
|
||||
propertyStore->GetValue(PKEY_Device_FriendlyName, &propVariant);
|
||||
_devices.append((const char*)utf8_t(propVariant.pwszVal));
|
||||
propertyStore->Release();
|
||||
if(!_audioDevice && _devices.right() == _device) {
|
||||
_audioDevice = device;
|
||||
} else {
|
||||
device->Release();
|
||||
}
|
||||
}
|
||||
deviceCollection->Release();
|
||||
|
||||
//if no match is found, choose the default audio endpoint for the device()
|
||||
if(!_audioDevice) {
|
||||
if(_enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &_audioDevice) != S_OK) return false;
|
||||
}
|
||||
|
||||
if(_audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&_audioClient) != S_OK) return false;
|
||||
|
||||
WAVEFORMATEXTENSIBLE waveFormat = {};
|
||||
if(_exclusive) {
|
||||
if(_audioDevice->OpenPropertyStore(STGM_READ, &_propertyStore) != S_OK) return false;
|
||||
if(_propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &_propVariant) != S_OK) return false;
|
||||
_waveFormat = (WAVEFORMATEX*)_propVariant.blob.pBlobData;
|
||||
IPropertyStore* propertyStore = nullptr;
|
||||
if(_audioDevice->OpenPropertyStore(STGM_READ, &propertyStore) != S_OK) return false;
|
||||
PROPVARIANT propVariant;
|
||||
if(propertyStore->GetValue(PKEY_AudioEngine_DeviceFormat, &propVariant) != S_OK) return false;
|
||||
waveFormat = *(WAVEFORMATEXTENSIBLE*)propVariant.blob.pBlobData;
|
||||
propertyStore->Release();
|
||||
if(_audioClient->GetDevicePeriod(nullptr, &_devicePeriod) != S_OK) return false;
|
||||
auto latency = max(_devicePeriod, (REFERENCE_TIME)_latency * 10'000); //1ms to 100ns units
|
||||
auto result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, _waveFormat, nullptr);
|
||||
auto result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, &waveFormat.Format, nullptr);
|
||||
if(result == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
|
||||
if(_audioClient->GetBufferSize(&_bufferSize) != S_OK) return false;
|
||||
_audioClient->Release();
|
||||
latency = (REFERENCE_TIME)(10'000 * 1'000 * _bufferSize / _waveFormat->nSamplesPerSec);
|
||||
latency = (REFERENCE_TIME)(10'000 * 1'000 * _bufferSize / waveFormat.Format.nSamplesPerSec);
|
||||
if(_audioDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)&_audioClient) != S_OK) return false;
|
||||
result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, _waveFormat, nullptr);
|
||||
result = _audioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, latency, &waveFormat.Format, nullptr);
|
||||
}
|
||||
if(result != S_OK) return false;
|
||||
DWORD taskIndex = 0;
|
||||
_taskHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskIndex);
|
||||
} else {
|
||||
if(_audioClient->GetMixFormat(&_waveFormat) != S_OK) return false;
|
||||
WAVEFORMATEX* waveFormatEx = nullptr;
|
||||
if(_audioClient->GetMixFormat(&waveFormatEx) != S_OK) return false;
|
||||
waveFormat = *(WAVEFORMATEXTENSIBLE*)waveFormatEx;
|
||||
CoTaskMemFree(waveFormatEx);
|
||||
if(_audioClient->GetDevicePeriod(&_devicePeriod, nullptr)) return false;
|
||||
auto latency = max(_devicePeriod, (REFERENCE_TIME)_latency * 10'000); //1ms to 100ns units
|
||||
if(_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, 0, _waveFormat, nullptr) != S_OK) return false;
|
||||
if(_audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, latency, 0, &waveFormat.Format, nullptr) != S_OK) return false;
|
||||
}
|
||||
|
||||
_eventHandle = CreateEvent(nullptr, false, false, nullptr);
|
||||
|
@ -109,19 +151,19 @@ private:
|
|||
if(_audioClient->GetService(IID_IAudioRenderClient, (void**)&_renderClient) != S_OK) return false;
|
||||
if(_audioClient->GetBufferSize(&_bufferSize) != S_OK) return false;
|
||||
|
||||
_channels = _waveFormat->nChannels;
|
||||
_frequency = _waveFormat->nSamplesPerSec;
|
||||
_mode = ((WAVEFORMATEXTENSIBLE*)_waveFormat)->SubFormat.Data1;
|
||||
_precision = _waveFormat->wBitsPerSample;
|
||||
_channels = waveFormat.Format.nChannels;
|
||||
_frequency = waveFormat.Format.nSamplesPerSec;
|
||||
_mode = waveFormat.SubFormat.Data1;
|
||||
_precision = waveFormat.Format.wBitsPerSample;
|
||||
|
||||
clear();
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
_devices.reset();
|
||||
if(_audioClient) _audioClient->Stop();
|
||||
if(_renderClient) _renderClient->Release(), _renderClient = nullptr;
|
||||
if(_waveFormat) CoTaskMemFree(_waveFormat), _waveFormat = nullptr;
|
||||
if(_audioClient) _audioClient->Release(), _audioClient = nullptr;
|
||||
if(_audioDevice) _audioDevice->Release(), _audioDevice = nullptr;
|
||||
if(_eventHandle) CloseHandle(_eventHandle), _eventHandle = nullptr;
|
||||
|
@ -129,9 +171,12 @@ private:
|
|||
}
|
||||
|
||||
auto write() -> void {
|
||||
uint32_t padding = 0;
|
||||
_audioClient->GetCurrentPadding(&padding);
|
||||
uint32_t available = _bufferSize - padding;
|
||||
uint32_t available = _bufferSize;
|
||||
if(!_exclusive) {
|
||||
uint32_t padding = 0;
|
||||
_audioClient->GetCurrentPadding(&padding);
|
||||
available = _bufferSize - padding;
|
||||
}
|
||||
|
||||
uint8_t* buffer = nullptr;
|
||||
if(_renderClient->GetBuffer(available, &buffer) == S_OK) {
|
||||
|
@ -171,6 +216,7 @@ private:
|
|||
|
||||
bool _ready = false;
|
||||
bool _exclusive = false;
|
||||
string _device;
|
||||
bool _blocking = true;
|
||||
uint _channels = 2;
|
||||
uint _frequency = 48000;
|
||||
|
@ -187,12 +233,10 @@ private:
|
|||
} _queue;
|
||||
|
||||
IMMDeviceEnumerator* _enumerator = nullptr;
|
||||
vector<string> _devices;
|
||||
IMMDevice* _audioDevice = nullptr;
|
||||
IPropertyStore* _propertyStore = nullptr;
|
||||
IAudioClient* _audioClient = nullptr;
|
||||
IAudioRenderClient* _renderClient = nullptr;
|
||||
WAVEFORMATEX* _waveFormat = nullptr;
|
||||
PROPVARIANT _propVariant;
|
||||
HANDLE _eventHandle = nullptr;
|
||||
HANDLE _taskHandle = nullptr;
|
||||
REFERENCE_TIME _devicePeriod = 0;
|
||||
|
|
|
@ -311,6 +311,7 @@ private:
|
|||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
_ready = false;
|
||||
if(_vertexBuffer) { _vertexBuffer->Release(); _vertexBuffer = nullptr; }
|
||||
if(_surface) { _surface->Release(); _surface = nullptr; }
|
||||
if(_texture) { _texture->Release(); _texture = nullptr; }
|
||||
|
|
|
@ -3,94 +3,82 @@
|
|||
//note: this is a fallback driver for use when OpenGL 3.2 is not available.
|
||||
//see glx.cpp for comments on how this driver operates (they are very similar.)
|
||||
|
||||
#if defined(DISPLAY_XORG)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#endif
|
||||
#elif defined(DISPLAY_QUARTZ)
|
||||
#include <OpenGL/gl3.h>
|
||||
#elif defined(DISPLAY_WINDOWS)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#endif
|
||||
#else
|
||||
#error "ruby::OpenGL2: unsupported platform"
|
||||
#endif
|
||||
|
||||
struct VideoGLX2 : Video {
|
||||
~VideoGLX2() { term(); }
|
||||
VideoGLX2() { initialize(); }
|
||||
~VideoGLX2() { terminate(); }
|
||||
|
||||
auto (*glXSwapInterval)(int) -> int = nullptr;
|
||||
Display* display = nullptr;
|
||||
int screen = 0;
|
||||
Window xwindow = 0;
|
||||
Colormap colormap = 0;
|
||||
GLXContext glxcontext = nullptr;
|
||||
GLXWindow glxwindow = 0;
|
||||
auto ready() -> bool { return _ready; }
|
||||
|
||||
struct {
|
||||
Window handle = 0;
|
||||
bool synchronize = false;
|
||||
uint filter = Video::FilterLinear;
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
auto smooth() -> bool { return _smooth; }
|
||||
|
||||
uint width = 256;
|
||||
uint height = 256;
|
||||
|
||||
bool isDoubleBuffered = false;
|
||||
bool isDirect = false;
|
||||
} settings;
|
||||
|
||||
auto cap(const string& name) -> bool {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) return true;
|
||||
if(name == Video::Filter) return true;
|
||||
return false;
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto get(const string& name) -> any {
|
||||
if(name == Video::Handle) return (uintptr)settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
if(name == Video::Filter) return settings.filter;
|
||||
return {};
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
if(_ready && glXSwapInterval) glXSwapInterval(_blocking);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto set(const string& name, const any& value) -> bool {
|
||||
if(name == Video::Handle && value.is<uintptr>()) {
|
||||
settings.handle = value.get<uintptr>();
|
||||
return true;
|
||||
}
|
||||
auto setSmooth(bool smooth) -> bool {
|
||||
if(_smooth == smooth) return true;
|
||||
_smooth = smooth;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize && value.is<bool>()) {
|
||||
if(settings.synchronize != value.get<bool>()) {
|
||||
settings.synchronize = value.get<bool>();
|
||||
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(name == Video::Filter && value.is<uint>()) {
|
||||
settings.filter = value.get<uint>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
auto clear() -> void {
|
||||
memory::fill(_glBuffer, _glWidth * _glHeight * sizeof(uint32_t));
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFlush();
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
if(width != settings.width || height != settings.height) resize(width, height);
|
||||
pitch = glwidth * sizeof(uint32_t);
|
||||
return data = glbuffer;
|
||||
if(width != _width || height != _height) resize(width, height);
|
||||
pitch = _glWidth * sizeof(uint32_t);
|
||||
return data = _glBuffer;
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
memory::fill(glbuffer, glwidth * glheight * sizeof(uint32_t));
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glFlush();
|
||||
if(settings.isDoubleBuffered) glXSwapBuffers(display, glxwindow);
|
||||
}
|
||||
|
||||
auto refresh() -> void {
|
||||
auto output() -> void {
|
||||
XWindowAttributes parent, child;
|
||||
XGetWindowAttributes(display, settings.handle, &parent);
|
||||
XGetWindowAttributes(display, xwindow, &child);
|
||||
XGetWindowAttributes(_display, (Window)_context, &parent);
|
||||
XGetWindowAttributes(_display, _window, &child);
|
||||
if(child.width != parent.width || child.height != parent.height) {
|
||||
XResizeWindow(display, xwindow, parent.width, parent.height);
|
||||
XResizeWindow(_display, _window, parent.width, parent.height);
|
||||
}
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, settings.filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, settings.filter ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _smooth ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _smooth ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
@ -99,12 +87,11 @@ struct VideoGLX2 : Video {
|
|||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, glwidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, settings.width, settings.height,
|
||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, glbuffer);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, _glWidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, _glBuffer);
|
||||
|
||||
double w = (double)settings.width / (double)glwidth;
|
||||
double h = (double)settings.height / (double)glheight;
|
||||
double w = (double)_width / (double)_glWidth;
|
||||
double h = (double)_height / (double)_glHeight;
|
||||
int u = parent.width;
|
||||
int v = parent.height;
|
||||
|
||||
|
@ -116,19 +103,23 @@ struct VideoGLX2 : Video {
|
|||
glEnd();
|
||||
glFlush();
|
||||
|
||||
if(settings.isDoubleBuffered) glXSwapBuffers(display, glxwindow);
|
||||
if(_isDoubleBuffered) glXSwapBuffers(_display, _glXWindow);
|
||||
}
|
||||
|
||||
auto init() -> bool {
|
||||
display = XOpenDisplay(0);
|
||||
screen = DefaultScreen(display);
|
||||
private:
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!_context) return false;
|
||||
|
||||
_display = XOpenDisplay(0);
|
||||
_screen = DefaultScreen(_display);
|
||||
|
||||
int versionMajor = 0, versionMinor = 0;
|
||||
glXQueryVersion(display, &versionMajor, &versionMinor);
|
||||
glXQueryVersion(_display, &versionMajor, &versionMinor);
|
||||
if(versionMajor < 1 || (versionMajor == 1 && versionMinor < 2)) return false;
|
||||
|
||||
XWindowAttributes windowAttributes;
|
||||
XGetWindowAttributes(display, settings.handle, &windowAttributes);
|
||||
XGetWindowAttributes(_display, (Window)_context, &windowAttributes);
|
||||
|
||||
int attributeList[] = {
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
|
@ -141,37 +132,37 @@ struct VideoGLX2 : Video {
|
|||
};
|
||||
|
||||
int fbCount = 0;
|
||||
auto fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount);
|
||||
auto fbConfig = glXChooseFBConfig(_display, _screen, attributeList, &fbCount);
|
||||
if(fbCount == 0) return false;
|
||||
|
||||
auto vi = glXGetVisualFromFBConfig(display, fbConfig[0]);
|
||||
colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
|
||||
auto vi = glXGetVisualFromFBConfig(_display, fbConfig[0]);
|
||||
_colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = colormap;
|
||||
attributes.colormap = _colormap;
|
||||
attributes.border_pixel = 0;
|
||||
xwindow = XCreateWindow(display, settings.handle, 0, 0, windowAttributes.width, windowAttributes.height,
|
||||
_window = XCreateWindow(_display, (Window)_context, 0, 0, windowAttributes.width, windowAttributes.height,
|
||||
0, vi->depth, InputOutput, vi->visual, CWColormap | CWBorderPixel, &attributes);
|
||||
XSetWindowBackground(display, xwindow, 0);
|
||||
XMapWindow(display, xwindow);
|
||||
XFlush(display);
|
||||
XSetWindowBackground(_display, _window, 0);
|
||||
XMapWindow(_display, _window);
|
||||
XFlush(_display);
|
||||
|
||||
while(XPending(display)) {
|
||||
while(XPending(_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(display, &event);
|
||||
XNextEvent(_display, &event);
|
||||
}
|
||||
|
||||
glxcontext = glXCreateContext(display, vi, 0, GL_TRUE);
|
||||
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
|
||||
_glXContext = glXCreateContext(_display, vi, 0, GL_TRUE);
|
||||
glXMakeCurrent(_display, _glXWindow = _window, _glXContext);
|
||||
|
||||
if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
|
||||
if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
|
||||
|
||||
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
|
||||
if(glXSwapInterval) glXSwapInterval(_blocking);
|
||||
|
||||
int value = 0;
|
||||
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value);
|
||||
settings.isDoubleBuffered = value;
|
||||
settings.isDirect = glXIsDirect(display, glxcontext);
|
||||
glXGetConfig(_display, vi, GLX_DOUBLEBUFFER, &value);
|
||||
_isDoubleBuffered = value;
|
||||
_isDirect = glXIsDirect(_display, _glXContext);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -183,62 +174,83 @@ struct VideoGLX2 : Video {
|
|||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
resize(256, 256);
|
||||
return true;
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto term() -> void {
|
||||
if(gltexture) {
|
||||
glDeleteTextures(1, &gltexture);
|
||||
gltexture = 0;
|
||||
auto terminate() -> void {
|
||||
_ready = false;
|
||||
|
||||
if(_glTexture) {
|
||||
glDeleteTextures(1, &_glTexture);
|
||||
_glTexture = 0;
|
||||
}
|
||||
|
||||
if(glbuffer) {
|
||||
delete[] glbuffer;
|
||||
glbuffer = 0;
|
||||
if(_glBuffer) {
|
||||
delete[] _glBuffer;
|
||||
_glBuffer = nullptr;
|
||||
}
|
||||
|
||||
glwidth = 0;
|
||||
glheight = 0;
|
||||
_glWidth = 0;
|
||||
_glHeight = 0;
|
||||
|
||||
if(glxcontext) {
|
||||
glXDestroyContext(display, glxcontext);
|
||||
glxcontext = nullptr;
|
||||
if(_glXContext) {
|
||||
glXDestroyContext(_display, _glXContext);
|
||||
_glXContext = nullptr;
|
||||
}
|
||||
|
||||
if(xwindow) {
|
||||
XUnmapWindow(display, xwindow);
|
||||
xwindow = 0;
|
||||
if(_window) {
|
||||
XUnmapWindow(_display, _window);
|
||||
_window = 0;
|
||||
}
|
||||
|
||||
if(colormap) {
|
||||
XFreeColormap(display, colormap);
|
||||
colormap = 0;
|
||||
if(_colormap) {
|
||||
XFreeColormap(_display, _colormap);
|
||||
_colormap = 0;
|
||||
}
|
||||
|
||||
if(display) {
|
||||
XCloseDisplay(display);
|
||||
display = nullptr;
|
||||
if(_display) {
|
||||
XCloseDisplay(_display);
|
||||
_display = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
GLuint gltexture = 0;
|
||||
uint32_t* glbuffer = nullptr;
|
||||
uint glwidth = 0;
|
||||
uint glheight = 0;
|
||||
|
||||
auto resize(uint width, uint height) -> void {
|
||||
settings.width = width;
|
||||
settings.height = height;
|
||||
_width = width;
|
||||
_height = height;
|
||||
|
||||
if(gltexture == 0) glGenTextures(1, &gltexture);
|
||||
glwidth = max(glwidth, width);
|
||||
glheight = max(glheight, height);
|
||||
if(glbuffer) delete[] glbuffer;
|
||||
glbuffer = new uint32_t[glwidth * glheight]();
|
||||
if(_glTexture == 0) glGenTextures(1, &_glTexture);
|
||||
_glWidth = max(_glWidth, width);
|
||||
_glHeight = max(_glHeight, height);
|
||||
delete[] _glBuffer;
|
||||
_glBuffer = new uint32_t[_glWidth * _glHeight]();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, gltexture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, glwidth);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, glbuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, _glTexture);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, _glWidth);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _glWidth, _glHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, _glBuffer);
|
||||
}
|
||||
|
||||
auto (*glXSwapInterval)(int) -> int = nullptr;
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _blocking = false;
|
||||
bool _smooth = true;
|
||||
|
||||
Display* _display = nullptr;
|
||||
int _screen = 0;
|
||||
Window _window = 0;
|
||||
Colormap _colormap = 0;
|
||||
GLXContext _glXContext = nullptr;
|
||||
GLXWindow _glXWindow = 0;
|
||||
|
||||
bool _isDoubleBuffered = false;
|
||||
bool _isDirect = false;
|
||||
|
||||
uint _width = 256;
|
||||
uint _height = 256;
|
||||
|
||||
GLuint _glTexture = 0;
|
||||
uint32_t* _glBuffer = nullptr;
|
||||
uint _glWidth = 0;
|
||||
uint _glHeight = 0;
|
||||
};
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#if defined(DISPLAY_XORG)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name))
|
||||
#endif
|
||||
#elif defined(DISPLAY_QUARTZ)
|
||||
#include <OpenGL/gl3.h>
|
||||
#elif defined(DISPLAY_WINDOWS)
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#ifndef glGetProcAddress
|
||||
#define glGetProcAddress(name) wglGetProcAddress(name)
|
||||
#endif
|
||||
#else
|
||||
#error "ruby::OpenGL: unsupported platform"
|
||||
#error "ruby::OpenGL3: unsupported platform"
|
||||
#endif
|
||||
|
||||
#include "bind.hpp"
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
//note: this driver works under Linux, but crashes with SIGSEGV under FreeBSD
|
||||
//exact reason is unknown; but I suspect it's a bug in FreeBSD's SDL 1.2 package
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/Xv.h>
|
||||
|
@ -97,10 +94,11 @@ private:
|
|||
_bufferHeight = 0;
|
||||
resize(_width = 256, _height = 256);
|
||||
|
||||
return true;
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto terminate() -> void {
|
||||
_ready = false;
|
||||
if(_buffer) SDL_FreeSurface(_buffer), _buffer = nullptr;
|
||||
if(_screen) SDL_QuitSubSystem(SDL_INIT_VIDEO), _screen = nullptr;
|
||||
if(_display) XCloseDisplay(_display), _display = nullptr;
|
||||
|
|
|
@ -4,153 +4,78 @@
|
|||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
|
||||
extern "C" auto XvShmCreateImage(Display*, XvPortID, signed, char*, signed, signed, XShmSegmentInfo*) -> XvImage*;
|
||||
extern "C" auto XvShmCreateImage(Display*, XvPortID, int, char*, int, int, XShmSegmentInfo*) -> XvImage*;
|
||||
|
||||
struct VideoXv : Video {
|
||||
~VideoXv() { term(); }
|
||||
VideoXv() { initialize(); }
|
||||
~VideoXv() { terminate(); }
|
||||
|
||||
uint32_t* buffer = nullptr;
|
||||
uint8_t* ytable = nullptr;
|
||||
uint8_t* utable = nullptr;
|
||||
uint8_t* vtable = nullptr;
|
||||
auto ready() -> bool { return _ready; }
|
||||
|
||||
enum XvFormat : unsigned {
|
||||
XvFormatRGB32,
|
||||
XvFormatRGB24,
|
||||
XvFormatRGB16,
|
||||
XvFormatRGB15,
|
||||
XvFormatYUY2,
|
||||
XvFormatUYVY,
|
||||
XvFormatUnknown,
|
||||
};
|
||||
auto context() -> uintptr { return _context; }
|
||||
auto blocking() -> bool { return _blocking; }
|
||||
|
||||
struct {
|
||||
Display* display = nullptr;
|
||||
GC gc = 0;
|
||||
Window window = 0;
|
||||
Colormap colormap = 0;
|
||||
XShmSegmentInfo shminfo;
|
||||
|
||||
signed port = -1;
|
||||
signed depth = 0;
|
||||
signed visualid = 0;
|
||||
|
||||
XvImage* image = nullptr;
|
||||
XvFormat format = XvFormatUnknown;
|
||||
uint32_t fourcc = 0;
|
||||
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
} device;
|
||||
|
||||
struct {
|
||||
Window handle = 0;
|
||||
bool synchronize = false;
|
||||
|
||||
unsigned width = 0;
|
||||
unsigned height = 0;
|
||||
} settings;
|
||||
|
||||
auto cap(const string& name) -> bool {
|
||||
if(name == Video::Handle) return true;
|
||||
if(name == Video::Synchronize) {
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
bool result = XInternAtom(display, "XV_SYNC_TO_VBLANK", true) != None;
|
||||
XCloseDisplay(display);
|
||||
return result;
|
||||
}
|
||||
return false;
|
||||
auto setContext(uintptr context) -> bool {
|
||||
if(_context == context) return true;
|
||||
_context = context;
|
||||
return initialize();
|
||||
}
|
||||
|
||||
auto get(const string& name) -> any {
|
||||
if(name == Video::Handle) return settings.handle;
|
||||
if(name == Video::Synchronize) return settings.synchronize;
|
||||
return {};
|
||||
auto setBlocking(bool blocking) -> bool {
|
||||
if(_blocking == blocking) return true;
|
||||
_blocking = blocking;
|
||||
|
||||
bool result = false;
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
|
||||
if(atom != None && _port >= 0) {
|
||||
XvSetPortAttribute(display, _port, atom, _blocking);
|
||||
result = true;
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
return result;
|
||||
}
|
||||
|
||||
auto set(const string& name, const any& value) -> bool {
|
||||
if(name == Video::Handle && value.is<uintptr_t>()) {
|
||||
settings.handle = value.get<uintptr_t>();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(name == Video::Synchronize && value.is<bool>()) {
|
||||
bool result = false;
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
Atom atom = XInternAtom(display, "XV_SYNC_TO_VBLANK", true);
|
||||
if(atom != None && device.port >= 0) {
|
||||
settings.synchronize = value.get<bool>();
|
||||
XvSetPortAttribute(display, device.port, atom, settings.synchronize);
|
||||
result = true;
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
auto clear() -> void {
|
||||
memory::fill(_buffer, _bufferWidth * _bufferHeight * sizeof(uint32_t));
|
||||
//clear twice in case video is double buffered ...
|
||||
output();
|
||||
output();
|
||||
}
|
||||
|
||||
auto resize(unsigned width, unsigned height) -> void {
|
||||
if(device.width >= width && device.height >= height) return;
|
||||
device.width = max(width, device.width);
|
||||
device.height = max(height, device.height);
|
||||
|
||||
XShmDetach(device.display, &device.shminfo);
|
||||
shmdt(device.shminfo.shmaddr);
|
||||
shmctl(device.shminfo.shmid, IPC_RMID, nullptr);
|
||||
XFree(device.image);
|
||||
delete[] buffer;
|
||||
|
||||
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo);
|
||||
|
||||
device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777);
|
||||
device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0);
|
||||
device.shminfo.readOnly = false;
|
||||
XShmAttach(device.display, &device.shminfo);
|
||||
|
||||
buffer = new uint32_t[device.width * device.height];
|
||||
}
|
||||
|
||||
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
|
||||
if(width != settings.width || height != settings.height) {
|
||||
resize(settings.width = width, settings.height = height);
|
||||
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
||||
if(width != _width || height != _height) {
|
||||
resize(_width = width, _height = height);
|
||||
}
|
||||
|
||||
pitch = device.width * 4;
|
||||
return data = buffer;
|
||||
pitch = _bufferWidth * 4;
|
||||
return data = _buffer;
|
||||
}
|
||||
|
||||
auto unlock() -> void {
|
||||
}
|
||||
|
||||
auto clear() -> void {
|
||||
memory::fill(buffer, device.width * device.height * sizeof(uint32_t));
|
||||
//clear twice in case video is double buffered ...
|
||||
refresh();
|
||||
refresh();
|
||||
}
|
||||
|
||||
auto refresh() -> void {
|
||||
unsigned width = settings.width;
|
||||
unsigned height = settings.height;
|
||||
auto output() -> void {
|
||||
uint width = _width;
|
||||
uint height = _height;
|
||||
|
||||
XWindowAttributes target;
|
||||
XGetWindowAttributes(device.display, device.window, &target);
|
||||
XGetWindowAttributes(_display, _window, &target);
|
||||
|
||||
//we must ensure that the child window is the same size as the parent window.
|
||||
//unfortunately, we cannot hook the parent window resize event notification,
|
||||
//as we did not create the parent window, nor have any knowledge of the toolkit used.
|
||||
//therefore, query each window size and resize as needed.
|
||||
XWindowAttributes parent;
|
||||
XGetWindowAttributes(device.display, settings.handle, &parent);
|
||||
XGetWindowAttributes(_display, (Window)_context, &parent);
|
||||
if(target.width != parent.width || target.height != parent.height) {
|
||||
XResizeWindow(device.display, device.window, parent.width, parent.height);
|
||||
XResizeWindow(_display, _window, parent.width, parent.height);
|
||||
}
|
||||
|
||||
//update target width and height attributes
|
||||
XGetWindowAttributes(device.display, device.window, &target);
|
||||
XGetWindowAttributes(_display, _window, &target);
|
||||
|
||||
switch(device.format) {
|
||||
switch(_format) {
|
||||
case XvFormatRGB32: renderRGB32(width, height); break;
|
||||
case XvFormatRGB24: renderRGB24(width, height); break;
|
||||
case XvFormatRGB16: renderRGB16(width, height); break;
|
||||
|
@ -159,39 +84,42 @@ struct VideoXv : Video {
|
|||
case XvFormatUYVY: renderUYVY (width, height); break;
|
||||
}
|
||||
|
||||
XvShmPutImage(device.display, device.port, device.window, device.gc, device.image,
|
||||
XvShmPutImage(_display, _port, _window, _gc, _image,
|
||||
0, 0, width, height,
|
||||
0, 0, target.width, target.height,
|
||||
true);
|
||||
}
|
||||
|
||||
auto init() -> bool {
|
||||
device.display = XOpenDisplay(nullptr);
|
||||
auto initialize() -> bool {
|
||||
terminate();
|
||||
if(!_context) return false;
|
||||
|
||||
if(!XShmQueryExtension(device.display)) {
|
||||
fprintf(stderr, "VideoXv: XShm extension not found.\n");
|
||||
_display = XOpenDisplay(nullptr);
|
||||
|
||||
if(!XShmQueryExtension(_display)) {
|
||||
print("VideoXv: XShm extension not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
//find an appropriate Xv port
|
||||
device.port = -1;
|
||||
XvAdaptorInfo* adaptor_info;
|
||||
unsigned adaptor_count;
|
||||
XvQueryAdaptors(device.display, DefaultRootWindow(device.display), &adaptor_count, &adaptor_info);
|
||||
for(unsigned i = 0; i < adaptor_count; i++) {
|
||||
_port = -1;
|
||||
XvAdaptorInfo* adaptorInfo = nullptr;
|
||||
uint adaptorCount = 0;
|
||||
XvQueryAdaptors(_display, DefaultRootWindow(_display), &adaptorCount, &adaptorInfo);
|
||||
for(uint n : range(adaptorCount)) {
|
||||
//find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks
|
||||
if(adaptor_info[i].num_formats < 1) continue;
|
||||
if(!(adaptor_info[i].type & XvInputMask)) continue;
|
||||
if(!(adaptor_info[i].type & XvImageMask)) continue;
|
||||
if(adaptorInfo[n].num_formats < 1) continue;
|
||||
if(!(adaptorInfo[n].type & XvInputMask)) continue;
|
||||
if(!(adaptorInfo[n].type & XvImageMask)) continue;
|
||||
|
||||
device.port = adaptor_info[i].base_id;
|
||||
device.depth = adaptor_info[i].formats->depth;
|
||||
device.visualid = adaptor_info[i].formats->visual_id;
|
||||
_port = adaptorInfo[n].base_id;
|
||||
_depth = adaptorInfo[n].formats->depth;
|
||||
_visualID = adaptorInfo[n].formats->visual_id;
|
||||
break;
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptor_info);
|
||||
if(device.port < 0) {
|
||||
fprintf(stderr, "VideoXv: failed to find valid XvPort.\n");
|
||||
XvFreeAdaptorInfo(adaptorInfo);
|
||||
if(_port < 0) {
|
||||
print("VideoXv: failed to find valid XvPort.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -199,272 +127,297 @@ struct VideoXv : Video {
|
|||
//this is so that even if parent window visual depth doesn't match Xv visual
|
||||
//(common with composited windows), Xv can still render to child window.
|
||||
XWindowAttributes window_attributes;
|
||||
XGetWindowAttributes(device.display, settings.handle, &window_attributes);
|
||||
XGetWindowAttributes(_display, (Window)_context, &window_attributes);
|
||||
|
||||
XVisualInfo visualtemplate;
|
||||
visualtemplate.visualid = device.visualid;
|
||||
visualtemplate.screen = DefaultScreen(device.display);
|
||||
visualtemplate.depth = device.depth;
|
||||
visualtemplate.visual = 0;
|
||||
signed visualmatches = 0;
|
||||
XVisualInfo* visualinfo = XGetVisualInfo(device.display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
|
||||
if(visualmatches < 1 || !visualinfo->visual) {
|
||||
if(visualinfo) XFree(visualinfo);
|
||||
fprintf(stderr, "VideoXv: unable to find Xv-compatible visual.\n");
|
||||
XVisualInfo visualTemplate;
|
||||
visualTemplate.visualid = _visualID;
|
||||
visualTemplate.screen = DefaultScreen(_display);
|
||||
visualTemplate.depth = _depth;
|
||||
visualTemplate.visual = 0;
|
||||
int visualMatches = 0;
|
||||
XVisualInfo* visualInfo = XGetVisualInfo(_display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualTemplate, &visualMatches);
|
||||
if(visualMatches < 1 || !visualInfo->visual) {
|
||||
if(visualInfo) XFree(visualInfo);
|
||||
print("VideoXv: unable to find Xv-compatible visual.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
device.colormap = XCreateColormap(device.display, settings.handle, visualinfo->visual, AllocNone);
|
||||
_colormap = XCreateColormap(_display, (Window)_context, visualInfo->visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = device.colormap;
|
||||
attributes.colormap = _colormap;
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask;
|
||||
device.window = XCreateWindow(device.display, /* parent = */ settings.handle,
|
||||
_window = XCreateWindow(_display, /* parent = */ (Window)_context,
|
||||
/* x = */ 0, /* y = */ 0, window_attributes.width, window_attributes.height,
|
||||
/* border_width = */ 0, device.depth, InputOutput, visualinfo->visual,
|
||||
/* border_width = */ 0, _depth, InputOutput, visualInfo->visual,
|
||||
CWColormap | CWBorderPixel | CWEventMask, &attributes);
|
||||
XFree(visualinfo);
|
||||
XSetWindowBackground(device.display, device.window, /* color = */ 0);
|
||||
XMapWindow(device.display, device.window);
|
||||
XFree(visualInfo);
|
||||
XSetWindowBackground(_display, _window, /* color = */ 0);
|
||||
XMapWindow(_display, _window);
|
||||
|
||||
device.gc = XCreateGC(device.display, device.window, 0, 0);
|
||||
_gc = XCreateGC(_display, _window, 0, 0);
|
||||
|
||||
//set colorkey to auto paint, so that Xv video output is always visible
|
||||
Atom atom = XInternAtom(device.display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if(atom != None) XvSetPortAttribute(device.display, device.port, atom, 1);
|
||||
Atom atom = XInternAtom(_display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if(atom != None) XvSetPortAttribute(_display, _port, atom, 1);
|
||||
|
||||
//find optimal rendering format
|
||||
device.format = XvFormatUnknown;
|
||||
signed format_count;
|
||||
XvImageFormatValues* format = XvListImageFormats(device.display, device.port, &format_count);
|
||||
_format = XvFormatUnknown;
|
||||
int formatCount = 0;
|
||||
XvImageFormatValues* format = XvListImageFormats(_display, _port, &formatCount);
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 32) {
|
||||
device.format = XvFormatRGB32;
|
||||
device.fourcc = format[i].id;
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel == 32) {
|
||||
_format = XvFormatRGB32;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel == 24) {
|
||||
device.format = XvFormatRGB24;
|
||||
device.fourcc = format[i].id;
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel == 24) {
|
||||
_format = XvFormatRGB24;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0xf800) {
|
||||
device.format = XvFormatRGB16;
|
||||
device.fourcc = format[i].id;
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0xf800) {
|
||||
_format = XvFormatRGB16;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvRGB && format[i].bits_per_pixel <= 16 && format[i].red_mask == 0x7c00) {
|
||||
device.format = XvFormatRGB15;
|
||||
device.fourcc = format[i].id;
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvRGB && format[n].bits_per_pixel <= 16 && format[n].red_mask == 0x7c00) {
|
||||
_format = XvFormatRGB15;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
|
||||
if(format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
|
||||
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V'
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
|
||||
if(format[n].component_order[0] == 'Y' && format[n].component_order[1] == 'U'
|
||||
&& format[n].component_order[2] == 'Y' && format[n].component_order[3] == 'V'
|
||||
) {
|
||||
device.format = XvFormatYUY2;
|
||||
device.fourcc = format[i].id;
|
||||
_format = XvFormatYUY2;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(device.format == XvFormatUnknown) for(signed i = 0; i < format_count; i++) {
|
||||
if(format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked) {
|
||||
if(format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
|
||||
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y'
|
||||
if(_format == XvFormatUnknown) for(auto n : range(formatCount)) {
|
||||
if(format[n].type == XvYUV && format[n].bits_per_pixel == 16 && format[n].format == XvPacked) {
|
||||
if(format[n].component_order[0] == 'U' && format[n].component_order[1] == 'Y'
|
||||
&& format[n].component_order[2] == 'V' && format[n].component_order[3] == 'Y'
|
||||
) {
|
||||
device.format = XvFormatUYVY;
|
||||
device.fourcc = format[i].id;
|
||||
_format = XvFormatUYVY;
|
||||
_fourCC = format[n].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(format);
|
||||
if(device.format == XvFormatUnknown) {
|
||||
fprintf(stderr, "VideoXv: unable to find a supported image format.\n");
|
||||
if(_format == XvFormatUnknown) {
|
||||
print("VideoXv: unable to find a supported image format.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
device.width = 256;
|
||||
device.height = 256;
|
||||
_bufferWidth = 256;
|
||||
_bufferHeight = 256;
|
||||
|
||||
device.image = XvShmCreateImage(device.display, device.port, device.fourcc, 0, device.width, device.height, &device.shminfo);
|
||||
if(!device.image) {
|
||||
fprintf(stderr, "VideoXv: XShmCreateImage failed.\n");
|
||||
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
|
||||
if(!_image) {
|
||||
print("VideoXv: XShmCreateImage failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
device.shminfo.shmid = shmget(IPC_PRIVATE, device.image->data_size, IPC_CREAT | 0777);
|
||||
device.shminfo.shmaddr = device.image->data = (char*)shmat(device.shminfo.shmid, 0, 0);
|
||||
device.shminfo.readOnly = false;
|
||||
if(!XShmAttach(device.display, &device.shminfo)) {
|
||||
fprintf(stderr, "VideoXv: XShmAttach failed.\n");
|
||||
_shmInfo.shmid = shmget(IPC_PRIVATE, _image->data_size, IPC_CREAT | 0777);
|
||||
_shmInfo.shmaddr = _image->data = (char*)shmat(_shmInfo.shmid, 0, 0);
|
||||
_shmInfo.readOnly = false;
|
||||
if(!XShmAttach(_display, &_shmInfo)) {
|
||||
print("VideoXv: XShmAttach failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = new uint32_t[device.width * device.height];
|
||||
settings.width = 256;
|
||||
settings.height = 256;
|
||||
initTables();
|
||||
_buffer = new uint32_t[_bufferWidth * _bufferHeight];
|
||||
_width = 256;
|
||||
_height = 256;
|
||||
initializeTables();
|
||||
clear();
|
||||
return true;
|
||||
return _ready = true;
|
||||
}
|
||||
|
||||
auto term() -> void {
|
||||
XShmDetach(device.display, &device.shminfo);
|
||||
shmdt(device.shminfo.shmaddr);
|
||||
shmctl(device.shminfo.shmid, IPC_RMID, nullptr);
|
||||
XFree(device.image);
|
||||
auto terminate() -> void {
|
||||
_ready = false;
|
||||
|
||||
if(device.window) {
|
||||
XUnmapWindow(device.display, device.window);
|
||||
device.window = 0;
|
||||
if(_image) {
|
||||
XShmDetach(_display, &_shmInfo);
|
||||
shmdt(_shmInfo.shmaddr);
|
||||
shmctl(_shmInfo.shmid, IPC_RMID, nullptr);
|
||||
XFree(_image);
|
||||
_image = nullptr;
|
||||
}
|
||||
|
||||
if(device.colormap) {
|
||||
XFreeColormap(device.display, device.colormap);
|
||||
device.colormap = 0;
|
||||
if(_window) {
|
||||
XUnmapWindow(_display, _window);
|
||||
_window = 0;
|
||||
}
|
||||
|
||||
if(device.display) {
|
||||
XCloseDisplay(device.display);
|
||||
device.display = nullptr;
|
||||
if(_colormap) {
|
||||
XFreeColormap(_display, _colormap);
|
||||
_colormap = 0;
|
||||
}
|
||||
|
||||
if(buffer) { delete[] buffer; buffer = nullptr; }
|
||||
if(ytable) { delete[] ytable; ytable = nullptr; }
|
||||
if(utable) { delete[] utable; utable = nullptr; }
|
||||
if(vtable) { delete[] vtable; vtable = nullptr; }
|
||||
if(_display) {
|
||||
XCloseDisplay(_display);
|
||||
_display = nullptr;
|
||||
}
|
||||
|
||||
delete[] _buffer, _buffer = nullptr;
|
||||
delete[] _ytable, _ytable = nullptr;
|
||||
delete[] _utable, _utable = nullptr;
|
||||
delete[] _vtable, _vtable = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
auto renderRGB32(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint32_t* output = (uint32_t*)device.image->data;
|
||||
auto resize(uint width, uint height) -> void {
|
||||
if(_bufferWidth >= width && _bufferHeight >= height) return;
|
||||
_bufferWidth = max(width, _bufferWidth);
|
||||
_bufferHeight = max(height, _bufferHeight);
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
memcpy(output, input, width * 4);
|
||||
input += device.width;
|
||||
output += device.width;
|
||||
XShmDetach(_display, &_shmInfo);
|
||||
shmdt(_shmInfo.shmaddr);
|
||||
shmctl(_shmInfo.shmid, IPC_RMID, nullptr);
|
||||
XFree(_image);
|
||||
|
||||
_image = XvShmCreateImage(_display, _port, _fourCC, 0, _bufferWidth, _bufferHeight, &_shmInfo);
|
||||
|
||||
_shmInfo.shmid = shmget(IPC_PRIVATE, _image->data_size, IPC_CREAT | 0777);
|
||||
_shmInfo.shmaddr = _image->data = (char*)shmat(_shmInfo.shmid, 0, 0);
|
||||
_shmInfo.readOnly = false;
|
||||
XShmAttach(_display, &_shmInfo);
|
||||
|
||||
delete[] _buffer;
|
||||
_buffer = new uint32_t[_bufferWidth * _bufferHeight];
|
||||
}
|
||||
|
||||
auto renderRGB32(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint32_t* output = (uint32_t*)_image->data;
|
||||
|
||||
for(uint y : range(height)) {
|
||||
memory::copy(output, input, width * 4);
|
||||
input += _bufferWidth;
|
||||
output += _bufferWidth;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderRGB24(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint8_t* output = (uint8_t*)device.image->data;
|
||||
auto renderRGB24(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint8_t* output = (uint8_t*)_image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(uint y : range(height)) {
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = p;
|
||||
*output++ = p >> 8;
|
||||
*output++ = p >> 16;
|
||||
}
|
||||
|
||||
input += (device.width - width);
|
||||
output += (device.width - width) * 3;
|
||||
input += (_bufferWidth - width);
|
||||
output += (_bufferWidth - width) * 3;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderRGB16(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint16_t* output = (uint16_t*)device.image->data;
|
||||
auto renderRGB16(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(uint y : range(height)) {
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x001f); //RGB32->RGB16
|
||||
}
|
||||
|
||||
input += device.width - width;
|
||||
output += device.width - width;
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderRGB15(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint16_t* output = (uint16_t*)device.image->data;
|
||||
auto renderRGB15(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
for(uint y : range(height)) {
|
||||
for(uint x : range(width)) {
|
||||
uint32_t p = *input++;
|
||||
*output++ = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x001f); //RGB32->RGB15
|
||||
}
|
||||
|
||||
input += device.width - width;
|
||||
output += device.width - width;
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderYUY2(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint16_t* output = (uint16_t*)device.image->data;
|
||||
auto renderYUY2(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width >> 1; x++) {
|
||||
for(uint y : range(height)) {
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint32_t p0 = *input++;
|
||||
uint32_t p1 = *input++;
|
||||
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f); //RGB32->RGB16
|
||||
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f); //RGB32->RGB16
|
||||
|
||||
uint8_t u = (utable[p0] + utable[p1]) >> 1;
|
||||
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
|
||||
uint8_t u = (_utable[p0] + _utable[p1]) >> 1;
|
||||
uint8_t v = (_vtable[p0] + _vtable[p1]) >> 1;
|
||||
|
||||
*output++ = (u << 8) | ytable[p0];
|
||||
*output++ = (v << 8) | ytable[p1];
|
||||
*output++ = (u << 8) | _ytable[p0];
|
||||
*output++ = (v << 8) | _ytable[p1];
|
||||
}
|
||||
|
||||
input += device.width - width;
|
||||
output += device.width - width;
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
auto renderUYVY(unsigned width, unsigned height) -> void {
|
||||
uint32_t* input = (uint32_t*)buffer;
|
||||
uint16_t* output = (uint16_t*)device.image->data;
|
||||
auto renderUYVY(uint width, uint height) -> void {
|
||||
uint32_t* input = (uint32_t*)_buffer;
|
||||
uint16_t* output = (uint16_t*)_image->data;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width >> 1; x++) {
|
||||
for(uint y : range(height)) {
|
||||
for(uint x : range(width >> 1)) {
|
||||
uint32_t p0 = *input++;
|
||||
uint32_t p1 = *input++;
|
||||
p0 = ((p0 >> 8) & 0xf800) + ((p0 >> 5) & 0x07e0) + ((p0 >> 3) & 0x001f);
|
||||
p1 = ((p1 >> 8) & 0xf800) + ((p1 >> 5) & 0x07e0) + ((p1 >> 3) & 0x001f);
|
||||
|
||||
uint8_t u = (utable[p0] + utable[p1]) >> 1;
|
||||
uint8_t v = (vtable[p0] + vtable[p1]) >> 1;
|
||||
uint8_t u = (_utable[p0] + _utable[p1]) >> 1;
|
||||
uint8_t v = (_vtable[p0] + _vtable[p1]) >> 1;
|
||||
|
||||
*output++ = (ytable[p0] << 8) | u;
|
||||
*output++ = (ytable[p1] << 8) | v;
|
||||
*output++ = (_ytable[p0] << 8) | u;
|
||||
*output++ = (_ytable[p1] << 8) | v;
|
||||
}
|
||||
|
||||
input += device.width - width;
|
||||
output += device.width - width;
|
||||
input += _bufferWidth - width;
|
||||
output += _bufferWidth - width;
|
||||
}
|
||||
}
|
||||
|
||||
auto initTables() -> void {
|
||||
ytable = new uint8_t[65536];
|
||||
utable = new uint8_t[65536];
|
||||
vtable = new uint8_t[65536];
|
||||
auto initializeTables() -> void {
|
||||
_ytable = new uint8_t[65536];
|
||||
_utable = new uint8_t[65536];
|
||||
_vtable = new uint8_t[65536];
|
||||
|
||||
for(unsigned i = 0; i < 65536; i++) {
|
||||
for(uint n : range(65536)) {
|
||||
//extract RGB565 color data from i
|
||||
uint8_t r = (i >> 11) & 31, g = (i >> 5) & 63, b = (i) & 31;
|
||||
uint8_t r = (n >> 11) & 31, g = (n >> 5) & 63, b = (n) & 31;
|
||||
r = (r << 3) | (r >> 2); //R5->R8
|
||||
g = (g << 2) | (g >> 4); //G6->G8
|
||||
b = (b << 3) | (b >> 2); //B5->B8
|
||||
|
@ -481,9 +434,48 @@ private:
|
|||
//int u = int( (double(b) - y) / (2.0 - 2.0 * lb) + 128.0 );
|
||||
//int v = int( (double(r) - y) / (2.0 - 2.0 * lr) + 128.0 );
|
||||
|
||||
ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
|
||||
utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
|
||||
vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
|
||||
_ytable[n] = y < 0 ? 0 : y > 255 ? 255 : y;
|
||||
_utable[n] = u < 0 ? 0 : u > 255 ? 255 : u;
|
||||
_vtable[n] = v < 0 ? 0 : v > 255 ? 255 : v;
|
||||
}
|
||||
}
|
||||
|
||||
bool _ready = false;
|
||||
uintptr _context = 0;
|
||||
bool _blocking = false;
|
||||
|
||||
uint _width = 0;
|
||||
uint _height = 0;
|
||||
|
||||
uint32_t* _buffer = nullptr;
|
||||
uint _bufferWidth = 0;
|
||||
uint _bufferHeight = 0;
|
||||
|
||||
uint8_t* _ytable = nullptr;
|
||||
uint8_t* _utable = nullptr;
|
||||
uint8_t* _vtable = nullptr;
|
||||
|
||||
enum XvFormat : uint {
|
||||
XvFormatRGB32,
|
||||
XvFormatRGB24,
|
||||
XvFormatRGB16,
|
||||
XvFormatRGB15,
|
||||
XvFormatYUY2,
|
||||
XvFormatUYVY,
|
||||
XvFormatUnknown,
|
||||
};
|
||||
|
||||
Display* _display = nullptr;
|
||||
GC _gc = 0;
|
||||
Window _window = 0;
|
||||
Colormap _colormap = 0;
|
||||
XShmSegmentInfo _shmInfo;
|
||||
|
||||
int _port = -1;
|
||||
int _depth = 0;
|
||||
int _visualID = 0;
|
||||
|
||||
XvImage* _image = nullptr;
|
||||
XvFormat _format = XvFormatUnknown;
|
||||
uint32_t _fourCC = 0;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue