From 0597721cba58a50a7b942c5a28a5c056846dbc3a Mon Sep 17 00:00:00 2001 From: Toni Wilen Date: Sat, 8 Oct 2016 17:01:41 +0300 Subject: [PATCH] genlock video file and capture device support. --- cfgfile.cpp | 8 +- include/options.h | 1 + include/videograb.h | 5 + od-win32/win32_videograb.cpp | 299 +++++++++++++++++++++++++++++++++++ specialmonitors.cpp | 208 +++++++++++++++++++----- 5 files changed, 476 insertions(+), 45 deletions(-) create mode 100644 include/videograb.h create mode 100644 od-win32/win32_videograb.cpp diff --git a/cfgfile.cpp b/cfgfile.cpp index 492ae2ab..d73dda6f 100644 --- a/cfgfile.cpp +++ b/cfgfile.cpp @@ -210,7 +210,7 @@ static const TCHAR *cdmodes[] = { _T("disabled"), _T(""), _T("image"), _T("ioctl static const TCHAR *cdconmodes[] = { _T(""), _T("uae"), _T("ide"), _T("scsi"), _T("cdtv"), _T("cd32"), 0 }; static const TCHAR *specialmonitors[] = { _T("none"), _T("autodetect"), _T("a2024"), _T("graffiti"), _T("ham_e"), _T("ham_e_plus"), _T("videodac18"), _T("avideo12"), _T("avideo24"), _T("firecracker24"), _T("dctv"), _T("opalvision"), _T("colorburst"), 0 }; -static const TCHAR *genlockmodes[] = { _T("none"), _T("noise"), _T("testcard"), NULL }; +static const TCHAR *genlockmodes[] = { _T("none"), _T("noise"), _T("testcard"), _T("image"), _T("video"), _T("stream"), NULL }; static const TCHAR *ppc_implementations[] = { _T("auto"), _T("dummy"), @@ -1833,9 +1833,10 @@ void cfgfile_save_options (struct zfile *f, struct uae_prefs *p, int type) cfgfile_write_bool (f, _T("immediate_blits"), p->immediate_blits); cfgfile_dwrite_str (f, _T("waiting_blits"), waitblits[p->waiting_blits]); cfgfile_write_bool (f, _T("ntsc"), p->ntscmode); - cfgfile_write_bool (f, _T("genlock"), p->genlock); + cfgfile_write_bool(f, _T("genlock"), p->genlock); cfgfile_dwrite_str(f, _T("genlockmode"), genlockmodes[p->genlock_image]); cfgfile_dwrite_str(f, _T("genlock_image"), p->genlock_image_file); + cfgfile_dwrite_str(f, _T("genlock_video"), p->genlock_video_file); cfgfile_dwrite(f, _T("genlock_mix"), _T("%d"), p->genlock_mix); cfgfile_dwrite_str(f, _T("monitoremu"), specialmonitors[p->monitoremu]); @@ -4763,6 +4764,7 @@ static int cfgfile_parse_hardware (struct uae_prefs *p, const TCHAR *option, TCH || cfgfile_path (option, value, _T("rtc_file"), p->rtcfile, sizeof p->rtcfile / sizeof (TCHAR), &p->path_rom) || cfgfile_path(option, value, _T("picassoiv_rom_file"), p->picassoivromfile, sizeof p->picassoivromfile / sizeof(TCHAR), &p->path_rom) || cfgfile_string(option, value, _T("genlock_image"), p->genlock_image_file, sizeof p->genlock_image_file / sizeof(TCHAR)) + || cfgfile_string(option, value, _T("genlock_video"), p->genlock_video_file, sizeof p->genlock_video_file / sizeof(TCHAR)) || cfgfile_string(option, value, _T ("pci_devices"), p->pci_devices, sizeof p->pci_devices / sizeof(TCHAR)) || cfgfile_string (option, value, _T("ghostscript_parameters"), p->ghostscript_parameters, sizeof p->ghostscript_parameters / sizeof (TCHAR))) return 1; @@ -5256,7 +5258,7 @@ void cfgfile_compatibility_romtype(struct uae_prefs *p) addbcromtype(p, ROMTYPE_NE2KPCMCIA, p->ne2000pcmcianame[0] != 0, NULL, 0); addbcromtype(p, ROMTYPE_NE2KPCI, p->ne2000pciname[0] != 0, NULL, 0); - static int restricted_net[] = { ROMTYPE_A2065, ROMTYPE_NE2KPCMCIA, ROMTYPE_NE2KPCI, ROMTYPE_NE2KISA, 0 }; + static int restricted_net[] = { ROMTYPE_A2065, ROMTYPE_NE2KPCMCIA, ROMTYPE_NE2KPCI, ROMTYPE_NE2KISA, ROMTYPE_ARIADNE2, 0 }; static int restricted_x86[] = { ROMTYPE_A1060, ROMTYPE_A2088, ROMTYPE_A2088T, ROMTYPE_A2286, ROMTYPE_A2386, 0 }; static int restricted_pci[] = { ROMTYPE_GREX, ROMTYPE_MEDIATOR, ROMTYPE_PROMETHEUS, 0 }; romtype_restricted(p, restricted_net); diff --git a/include/options.h b/include/options.h index b8b6a11c..5993141e 100644 --- a/include/options.h +++ b/include/options.h @@ -510,6 +510,7 @@ struct uae_prefs { int genlock_image; int genlock_mix; TCHAR genlock_image_file[MAX_DPATH]; + TCHAR genlock_video_file[MAX_DPATH]; int monitoremu; double chipset_refreshrate; struct chipset_refresh cr[MAX_CHIPSET_REFRESH + 2]; diff --git a/include/videograb.h b/include/videograb.h new file mode 100644 index 00000000..f38e52ff --- /dev/null +++ b/include/videograb.h @@ -0,0 +1,5 @@ + +bool initvideograb(const TCHAR *filename); +void uninitvideograb(void); +bool getvideograb(long **buffer, int *width, int *height); +void pausevideograb(bool pause); \ No newline at end of file diff --git a/od-win32/win32_videograb.cpp b/od-win32/win32_videograb.cpp new file mode 100644 index 00000000..fd660003 --- /dev/null +++ b/od-win32/win32_videograb.cpp @@ -0,0 +1,299 @@ + +/* UAE Win32 Video frame grabber support + * Toni Wilen 2016 + */ + +#include "sysconfig.h" + +#include +#include + +#include "sysdeps.h" +#include "options.h" + +#include +#include +#include + +#include "videograb.h" + +#pragma comment(lib,"Strmiids.lib") + +// following have been removed from newer SDKs + +static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; +static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; +static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +interface ISampleGrabberCB : public IUnknown +{ + virtual STDMETHODIMP SampleCB(double SampleTime, IMediaSample *pSample) = 0; + virtual STDMETHODIMP BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) = 0; +}; +interface ISampleGrabber : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE SetOneShot(BOOL OneShot) = 0; + virtual HRESULT STDMETHODCALLTYPE SetMediaType(const AM_MEDIA_TYPE *pType) = 0; + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(AM_MEDIA_TYPE *pType) = 0; + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples(BOOL BufferThem) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(long *pBufferSize, long *pBuffer) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(IMediaSample **ppSample) = 0; + virtual HRESULT STDMETHODCALLTYPE SetCallback(ISampleGrabberCB *pCallback, long WhichMethodToCallback) = 0; +}; + +// based on code from: http://forum.devmaster.net/t/generating-textures-from-video-frames/8259 + +static CComPtr graphBuilder; +static CComPtr filterGraph; +static CComPtr sampleGrabber; +static CComPtr mediaControl; +static bool videoInitialized; +static long *frameBuffer; +static long bufferSize; +static int videoWidth, videoHeight; + +void uninitvideograb(void) +{ + write_log(_T("uninitvideograb\n")); + + videoInitialized = false; + + sampleGrabber.Release(); + if (mediaControl) { + mediaControl->Stop(); + } + mediaControl.Release(); + filterGraph.Release(); + graphBuilder.Release(); + + delete[] frameBuffer; + frameBuffer = NULL; +} + +static void FindPin(IBaseFilter* baseFilter, PIN_DIRECTION direction, int pinNumber, IPin** destPin) +{ + CComPtr enumPins; + + *destPin = NULL; + + if (SUCCEEDED(baseFilter->EnumPins(&enumPins))) { + ULONG numFound; + IPin* tmpPin; + + while (SUCCEEDED(enumPins->Next(1, &tmpPin, &numFound))) { + PIN_DIRECTION pinDirection; + + tmpPin->QueryDirection(&pinDirection); + if (pinDirection == direction) { + if (pinNumber == 0) { + // Return the pin's interface + *destPin = tmpPin; + break; + } + pinNumber--; + } + tmpPin->Release(); + } + } +} + +static bool ConnectPins(IBaseFilter* outputFilter, unsigned int outputNum, IBaseFilter* inputFilter, unsigned int inputNum) +{ + CComPtr inputPin; + CComPtr outputPin; + + if (!outputFilter || !inputFilter) { + write_log(_T("ConnectPins OUT=%d IN=%d\n"), outputFilter != 0, inputFilter != 0); + return false; + } + + FindPin(outputFilter, PINDIR_OUTPUT, outputNum, &outputPin); + FindPin(inputFilter, PINDIR_INPUT, inputNum, &inputPin); + + if (inputPin && outputPin) { + HRESULT hr = filterGraph->Connect(outputPin, inputPin); + if (SUCCEEDED(hr)) + return true; + write_log(_T("ConnectPins Connect %08x\n"), hr); + } else { + write_log(_T("ConnectPins OUTPIN=%d INPIN=%d\n"), outputPin != 0, inputPin != 0); + + } + return false; +} + +bool initvideograb(const TCHAR *filename) +{ + HRESULT hr; + + uninitvideograb(); + + write_log(_T("initvideograb '%s'\n"), filename ? filename : _T("")); + + graphBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder2); + filterGraph.CoCreateInstance(CLSID_FilterGraph); + graphBuilder->SetFiltergraph(filterGraph); + CComPtr sourceFilter; + + if (filename != NULL && filename[0]) { + // This takes the absolute filename path and + // Loads the appropriate file reader and splitter + // Depending in the file type. + hr = filterGraph->AddSourceFilter(filename, L"Video Source", &sourceFilter); + if (FAILED(hr)) { + write_log(_T("AddSourceFilter failed %08x\n"), hr); + uninitvideograb(); + return false; + } + } else { + // capture device mode + IMoniker *pMoniker; + CComPtr pCreateDevEnum; + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, IID_ICreateDevEnum, (void**)&pCreateDevEnum); + if (FAILED(hr)) { + write_log(_T("CLSID_SystemDeviceEnum IID_ICreateDevEnum failed %08x\n"), hr); + uninitvideograb(); + return false; + } + CComPtr pEmum; + hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEmum, 0); + if (FAILED(hr)) { + write_log(_T("CreateClassEnumerator CLSID_VideoInputDeviceCategory failed %08x\n"), hr); + uninitvideograb(); + return false; + } + if (hr == S_FALSE) { + write_log(_T("initvideograb CreateDevEnum: didn't find any capture devices.\n")); + uninitvideograb(); + return false; + } + pEmum->Reset(); + ULONG cFetched = 0; + //Take the first capture device found + hr = pEmum->Next(1, &pMoniker, &cFetched); + if (FAILED(hr)) { + write_log(_T("initvideograb Next: didn't find any capture devices.\n")); + uninitvideograb(); + return false; + } + pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&sourceFilter); + pMoniker->Release(); + filterGraph->AddFilter(sourceFilter, L"Video Capture"); + } + + // Create the Sample Grabber which we will use + // To take each frame for texture generation + CComPtr grabberFilter; + grabberFilter.CoCreateInstance(CLSID_SampleGrabber); + grabberFilter->QueryInterface(IID_ISampleGrabber, reinterpret_cast(&sampleGrabber)); + + filterGraph->AddFilter(grabberFilter, L"Sample Grabber"); + + // We have to set the 24-bit RGB desire here + // So that the proper conversion filters + // Are added automatically. + AM_MEDIA_TYPE desiredType; + memset(&desiredType, 0, sizeof(desiredType)); + desiredType.majortype = MEDIATYPE_Video; + desiredType.subtype = MEDIASUBTYPE_RGB24; + desiredType.formattype = FORMAT_VideoInfo; + + sampleGrabber->SetMediaType(&desiredType); + sampleGrabber->SetBufferSamples(TRUE); + + // Use pin connection methods instead of + // ICaptureGraphBuilder::RenderStream because of + // the SampleGrabber setting we're using. + if (!ConnectPins(sourceFilter, 0, grabberFilter, 0)) { + uninitvideograb(); + return false; + } + + // A Null Renderer does not display the video + // But it allows the Sample Grabber to run + // And it will keep proper playback timing + // Unless specified otherwise. + CComPtr nullRenderer; + nullRenderer.CoCreateInstance(CLSID_NullRenderer); + + filterGraph->AddFilter(nullRenderer, L"Null Renderer"); + + if (!ConnectPins(grabberFilter, 0, nullRenderer, 0)) { + uninitvideograb(); + return false; + } + + // Just a little trick so that we don't have to know + // The video resolution when calling this method. + bool mediaConnected = false; + AM_MEDIA_TYPE connectedType; + if (SUCCEEDED(sampleGrabber->GetConnectedMediaType(&connectedType))) { + if (connectedType.formattype == FORMAT_VideoInfo) { + VIDEOINFOHEADER* infoHeader = (VIDEOINFOHEADER*)connectedType.pbFormat; + videoWidth = infoHeader->bmiHeader.biWidth; + videoHeight = infoHeader->bmiHeader.biHeight; + mediaConnected = true; + } + CoTaskMemFree(connectedType.pbFormat); + } + + if (!mediaConnected) { + uninitvideograb(); + return false; + } + + hr = filterGraph->QueryInterface(IID_IMediaControl, (void**)&mediaControl); + if (FAILED(hr)) { + uninitvideograb(); + return false; + } + if (SUCCEEDED(mediaControl->Run())) { + videoInitialized = true; + return true; + } else { + uninitvideograb(); + return false; + } +} + +void pausevideograb(bool pause) +{ + if (!videoInitialized) + return; + if (pause) { + mediaControl->Pause(); + } else { + mediaControl->Run(); + } +} + +bool getvideograb(long **buffer, int *width, int *height) +{ + HRESULT hr; + + if (!videoInitialized) + return false; + + // Only need to do this once + if (!frameBuffer) { + // The Sample Grabber requires an arbitrary buffer + // That we only know at runtime. + // (width * height * 3) bytes will not work. + hr = sampleGrabber->GetCurrentBuffer(&bufferSize, NULL); + if (FAILED(hr)) { + write_log(_T("getvideograb get size %08x\n"), hr); + return false; + } + frameBuffer = new long[bufferSize]; + } + + hr = sampleGrabber->GetCurrentBuffer(&bufferSize, (long*)frameBuffer); + if (SUCCEEDED(hr)) { + *buffer = frameBuffer; + *width = videoWidth; + *height = videoHeight; + return true; + } + write_log(_T("getvideograb get buffer %08x\n"), hr); + return false; +} diff --git a/specialmonitors.cpp b/specialmonitors.cpp index cba2db91..2744d091 100755 --- a/specialmonitors.cpp +++ b/specialmonitors.cpp @@ -12,6 +12,10 @@ #include "specialmonitors.h" #include "debug.h" #include "zfile.h" +#include "videograb.h" + +#define VIDEOGRAB 1 + static int opal_debug = 1; @@ -2104,10 +2108,13 @@ static bool a2024(struct vidbuffer *src, struct vidbuffer *dst) return true; } -static uae_u8 *genlock_image; +static uae_u8 *genlock_image_data; +static bool genlock_video; static int genlock_image_width, genlock_image_height, genlock_image_pitch; +static TCHAR genlock_video_file[MAX_DPATH], genlock_image_file[MAX_DPATH]; static uae_u8 noise_buffer[1024]; static uae_u32 noise_seed, noise_add, noise_index; +static bool genlock_error; static uae_u32 quickrand(void) { @@ -2152,7 +2159,7 @@ static void __cdecl readcallback(png_structp png_ptr, png_bytep out, png_size_t cb->size -= count; } -static void load_genlock_image(void) +static bool load_genlock_image(void) { extern unsigned char test_card_png[]; extern unsigned int test_card_png_len; @@ -2166,18 +2173,19 @@ static void load_genlock_image(void) struct png_cb cb; png_bytepp row_pp; png_size_t cols; + bool ok = false; - xfree(genlock_image); - genlock_image = NULL; + xfree(genlock_image_data); + genlock_image_data = NULL; - if (currprefs.genlock_image == 3) { + if (currprefs.genlock_image_file[0] && currprefs.genlock_image == 3) { int size; uae_u8 *bb = zfile_load_file(currprefs.genlock_image_file, &size); - if (bb) { - file_size = size; - b = bb; - bfree = bb; - } + if (!bb) + goto end; + file_size = size; + b = bb; + bfree = bb; } if (!png_check_sig(b, 8)) @@ -2219,10 +2227,10 @@ static void load_genlock_image(void) row_pp = new png_bytep[height]; - genlock_image = xcalloc(uae_u8, width * height * 4); + genlock_image_data = xcalloc(uae_u8, width * height * 4); for (int i = 0; i < height; i++) { - row_pp[i] = (png_bytep) &genlock_image[i * genlock_image_pitch]; + row_pp[i] = (png_bytep) &genlock_image_data[i * genlock_image_pitch]; } png_read_image(png_ptr, row_pp); @@ -2231,45 +2239,144 @@ static void load_genlock_image(void) png_destroy_read_struct(&png_ptr, &info_ptr, 0); delete[] row_pp; + + ok = true; end: xfree(bfree); + + return ok; } static bool do_genlock(struct vidbuffer *src, struct vidbuffer *dst, bool doublelines, int oddlines) { int y, x, vdbl, hdbl; int ystart, yend, isntsc; - int gl_vdbl_l, gl_vdbl_r; + int gl_vdbl_l, gl_vdbl_r, gl_vdbl; int gl_hdbl_l, gl_hdbl_r, gl_hdbl; int gl_hcenter, gl_vcenter; int mix1 = 0, mix2 = 0; + int genlock_image_pixbytes = 4; + int genlock_image_red_index = 0; + int genlock_image_green_index = 1; + int genlock_image_blue_index = 2; + bool genlock_image_upsidedown = false; + + uae_u8 *genlock_image = NULL; + isntsc = (beamcon0 & 0x20) ? 0 : 1; if (!(currprefs.chipset_mask & CSMASK_ECS_AGNUS)) isntsc = currprefs.ntscmode ? 1 : 0; - if (!genlock_image && currprefs.genlock_image == 2) { - load_genlock_image(); +#if VIDEOGRAB + if (currprefs.genlock_image == 5) { + if ((!genlock_video && !genlock_error) || _tcsicmp(_T(":CAPTURE:"), genlock_video_file)) { + _tcscpy(genlock_video_file, _T(":CAPTURE:")); + genlock_video = initvideograb(NULL); + if (!genlock_video) { + genlock_error = true; + } + } else { + genlock_error = true; + } + int vidw = 0, vidh = 0; + long *vidbuf = NULL; + if (genlock_video && getvideograb(&vidbuf, &vidw, &vidh)) { + genlock_image = (uae_u8*)vidbuf; + genlock_image_width = vidw; + genlock_image_height = vidh; + genlock_image_pixbytes = 3; + genlock_image_pitch = genlock_image_width * genlock_image_pixbytes; + genlock_image_red_index = 2; + genlock_image_green_index = 1; + genlock_image_blue_index = 0; + genlock_image_upsidedown = true; + genlock_error = false; + } + else { + genlock_error = true; + } + } else if (currprefs.genlock_image == 4) { + if (currprefs.genlock_video_file[0]) { + if ((!genlock_video && !genlock_error) || _tcsicmp(currprefs.genlock_video_file, genlock_video_file)) { + _tcscpy(genlock_video_file, currprefs.genlock_video_file); + genlock_video = initvideograb(currprefs.genlock_video_file); + if (!genlock_video) { + genlock_error = true; + } + } + } else { + genlock_error = true; + } + int vidw = 0, vidh = 0; + long *vidbuf = NULL; + if (genlock_video && getvideograb(&vidbuf, &vidw, &vidh)) { + genlock_image = (uae_u8*)vidbuf; + genlock_image_width = vidw; + genlock_image_height = vidh; + genlock_image_pixbytes = 3; + genlock_image_pitch = genlock_image_width * genlock_image_pixbytes; + genlock_image_red_index = 2; + genlock_image_green_index = 1; + genlock_image_blue_index = 0; + genlock_image_upsidedown = true; + genlock_error = false; + } else { + genlock_error = true; + } } - if (genlock_image && currprefs.genlock_image != 2) { - xfree(genlock_image); - genlock_image = NULL; +#endif + if (currprefs.genlock_image == 1) { + genlock_error = false; + } else if (currprefs.genlock_image == 2) { + genlock_error = false; + if (!genlock_image_data) { + load_genlock_image(); + } + genlock_image = genlock_image_data; + } else if (currprefs.genlock_image == 3) { + if (!currprefs.genlock_image_file[0]) { + genlock_error = true; + } else { + if ((!genlock_image_data && !genlock_error) || _tcsicmp(genlock_image_file, currprefs.genlock_image_file)) { + _tcscpy(genlock_image_file, currprefs.genlock_image_file); + genlock_error = load_genlock_image() == 0; + } + } + genlock_image = genlock_image_data; + } + + if (genlock_image_data && currprefs.genlock_image != 2 && currprefs.genlock_image != 3) { + xfree(genlock_image_data); + genlock_image_data = NULL; + } +#if VIDEOGRAB + if (genlock_video && currprefs.genlock_image != 4 && currprefs.genlock_image != 5) { + uninitvideograb(); + genlock_video = false; + } +#endif + if (currprefs.genlock_image != 4 && currprefs.genlock_image != 5) { + genlock_video_file[0] = 0; + } + if (currprefs.genlock_image != 3) { + genlock_image_file[0] = 0; } if (gfxvidinfo.xchange == 1) - hdbl = 0; + hdbl = 0; // shres else if (gfxvidinfo.xchange == 2) - hdbl = 1; + hdbl = 1; // hires else - hdbl = 2; + hdbl = 2; // lores gl_hdbl_l = gl_hdbl_r = 0; if (genlock_image_width < 600) { - gl_hdbl = 0; - } else if (genlock_image_width < 1000) { + gl_hdbl = 2; + } else if (genlock_image_width < 1300) { gl_hdbl = 1; } else { - gl_hdbl = 2; + gl_hdbl = 0; } if (hdbl >= gl_hdbl) { gl_hdbl_l = hdbl - gl_hdbl; @@ -2278,18 +2385,28 @@ static bool do_genlock(struct vidbuffer *src, struct vidbuffer *dst, bool double } if (gfxvidinfo.ychange == 1) - vdbl = 0; + vdbl = 0; // double else - vdbl = 1; + vdbl = 1; // single gl_vdbl_l = gl_vdbl_r = 0; + if (genlock_image_height < 400) { + gl_vdbl = 1; + } else { + gl_vdbl = 0; + } + if (vdbl >= gl_vdbl) { + gl_vdbl_l = vdbl - gl_vdbl; + } else { + gl_vdbl_r = gl_vdbl - vdbl; + } - gl_hcenter = (genlock_image_width - ((src->inwidth << hdbl) >> gl_hdbl)) / 2; + gl_hcenter = (((genlock_image_width << gl_hdbl_r) >> gl_hdbl_l) - src->inwidth) / 2; ystart = isntsc ? VBLANK_ENDLINE_NTSC : VBLANK_ENDLINE_PAL; yend = isntsc ? MAXVPOS_NTSC : MAXVPOS_PAL; - gl_vcenter = (((genlock_image_height << gl_vdbl_l) >> gl_vdbl_r) - (((yend - ystart) * 2))) / 2; + gl_vcenter = (((genlock_image_height << gl_vdbl_r) >> gl_vdbl_l) - (((yend - ystart) * 2))) / 2; init_noise(); @@ -2309,7 +2426,9 @@ static bool do_genlock(struct vidbuffer *src, struct vidbuffer *dst, bool double uae_u8 *line = src->bufmem + yoff * src->rowbytes; uae_u8 *dstline = dst->bufmem + (((y * 2 + oddlines) - dst->yoffset) >> vdbl) * dst->rowbytes; uae_u8 *line_genlock = row_map_genlock[yoff]; - int gy = ((((y * 2 + oddlines) - dst->yoffset) << gl_vdbl_l) >> gl_vdbl_r) + gl_vcenter; + int gy = ((((y * 2 + oddlines) - dst->yoffset + gl_vcenter) >> gl_vdbl_r) << gl_vdbl_l); + if (genlock_image_upsidedown) + gy = (genlock_image_height - 1) - gy; uae_u8 *image_genlock = genlock_image + gy * genlock_image_pitch; r = g = b = 0; noise_add = (quickrand() & 15) | 1; @@ -2321,13 +2440,17 @@ static bool do_genlock(struct vidbuffer *src, struct vidbuffer *dst, bool double uae_u8 *d2 = d + dst->rowbytes; if (is_transparent(*s_genlock)) { - if (genlock_image) { - int gx = (((x + gl_hcenter) << gl_hdbl_l) >> gl_hdbl_r); + if (genlock_error) { + r = 0x00; + g = 0x00; + b = 0xdd; + } else if (genlock_image) { + int gx = (((x + gl_hcenter) >> gl_hdbl_r) << gl_hdbl_l); if (gx >= 0 && gx < genlock_image_width && gy >= 0 && gy < genlock_image_height) { - uae_u8 *s_genlock_image = image_genlock + gx * 4; - r = s_genlock_image[0]; - g = s_genlock_image[1]; - b = s_genlock_image[2]; + uae_u8 *s_genlock_image = image_genlock + gx * genlock_image_pixbytes; + r = s_genlock_image[genlock_image_red_index]; + g = s_genlock_image[genlock_image_green_index]; + b = s_genlock_image[genlock_image_blue_index]; } else { r = g = b = 0; } @@ -3063,12 +3186,12 @@ static bool opalvision(struct vidbuffer *src, struct vidbuffer *dst, bool double } if (c[18] || c[19] || c[11]) write_log(_T("UNIMPLEMENTED BITS!\n")); - opal->dual_play = c[7]; - opal->latched = c[10]; - opal->wren = c[4]; - opal->colcopro = c[5]; + opal->dual_play = c[7] != 0; + opal->latched = c[10] != 0; + opal->wren = c[4] != 0; + opal->colcopro = c[5] != 0; opal->bank_field = c[8]; - opal->auto_field = c[9]; + opal->auto_field = c[9] != 0; opal->active_banks[0] = c[12]; opal->active_banks[1] = c[13]; opal->active_banks[2] = c[14]; @@ -3091,8 +3214,8 @@ static bool opalvision(struct vidbuffer *src, struct vidbuffer *dst, bool double } if (c[15]) write_log(_T("UNIMPLEMENTED BITS!\n")); - opal->wren = c[4]; - opal->colcopro = c[5]; + opal->wren = c[4] != 0; + opal->colcopro = c[5] != 0; opal->bank_field = c[11]; opal->auto_field = false; opal->active_banks[0] = c[8]; @@ -3200,6 +3323,7 @@ void specialmonitor_reset(void) { if (!currprefs.monitoremu) return; + uninitvideograb(); specialmonitor_store_fmode(-1, -1, 0); fc24_reset(); } -- 2.47.3