diff --git a/SpoutDX/source/SpoutCopy.cpp b/SpoutDX/source/SpoutCopy.cpp index 750e3cf..b3558ce 100644 --- a/SpoutDX/source/SpoutCopy.cpp +++ b/SpoutDX/source/SpoutCopy.cpp @@ -47,6 +47,7 @@ 07.09.20 - experimental SSE RGBA to RGB not working 19.09.20 - Removed experimental SSE RGBA to RGB function 23.09.20 - CheckSSE - initialize CPUInfo + 06.10.20 - Modifications to rgba2rgb and rgba2rgbResample for SpoutCam mirror and swap red/blue */ #include "SpoutCopy.h" @@ -588,7 +589,7 @@ void spoutCopy::bgr2bgra(const void *bgr_source, void *bgra_dest, unsigned int w void spoutCopy::rgba2rgb(const void *rgba_source, void *rgb_dest, unsigned int width, unsigned int height, - unsigned int rgba_pitch, bool bInvert) const + unsigned int rgba_pitch, bool bInvert, bool bMirror, bool bSwapRB) const { // RGB dest does not have padding const unsigned long rgbsize = width * height * 3; @@ -606,14 +607,30 @@ void spoutCopy::rgba2rgb(const void *rgba_source, void *rgb_dest, rgb -= rgbpitch; // beginning of the last rgb line } + // Swap red and blue option + int ir = 0; int ig = 1; int ib = 2; + if(bSwapRB) { + ir = 2; ib = 0; + } + + unsigned int z = 0; for (unsigned int y = 0; y < height; y++) { for (unsigned int x = 0; x < width; x++) { - *(rgb + 0) = *(rgba + 0); // red - *(rgb + 1) = *(rgba + 1); // grn - *(rgb + 2) = *(rgba + 2); // blu - rgb += 3; + if (bMirror) { + z = (width - x - 1)*3; + *(rgb + z + ir) = *(rgba + 0); // red + *(rgb + z + ig) = *(rgba + 1); // grn + *(rgb + z + ib) = *(rgba + 2); // blu + } + else { + z = x * 3; + *(rgb + z + ir) = *(rgba + 0); // red + *(rgb + z + ig) = *(rgba + 1); // grn + *(rgb + z + ib) = *(rgba + 2); // blu + } rgba += 4; } + rgb += width * 3; rgba += rgba_padding; if (bInvert) @@ -828,13 +845,20 @@ void spoutCopy::rgba2rgbaResample(const void* source, void* dest, void spoutCopy::rgba2rgbResample(const void* source, void* dest, unsigned int sourceWidth, unsigned int sourceHeight, unsigned int sourcePitch, - unsigned int destWidth, unsigned int destHeight, bool bInvert) const + unsigned int destWidth, unsigned int destHeight, bool bInvert, bool bMirror, bool bSwapRB) const { unsigned char *srcBuffer = (unsigned char *)source; // bgra source unsigned char *dstBuffer = (unsigned char *)dest; // bgr dest float x_ratio = (float)sourceWidth / (float)destWidth; float y_ratio = (float)sourceHeight / (float)destHeight; + + // Swap red and blue option + int ir = 0; int ig = 1; int ib = 2; + if (bSwapRB) { + ir = 2; ib = 0; + } + float px, py; unsigned int i, j; unsigned int pixel, nearestMatch; @@ -842,14 +866,25 @@ void spoutCopy::rgba2rgbResample(const void* source, void* dest, for (j = 0; j < destWidth; j++) { px = floor((float)j*x_ratio); py = floor((float)i*y_ratio); - if (bInvert) - pixel = (destHeight - i - 1)*destWidth * 3 + j * 3; // flip vertically - else - pixel = i * destWidth * 3 + j * 3; + + if (bMirror) { + if (bInvert) + pixel = (destHeight - i - 1)*destWidth * 3 + (destWidth - j - 1) * 3; // flip vertically + else + pixel = i * destWidth * 3 + (destWidth - j - 1) * 3; + } + else { + if (bInvert) + pixel = (destHeight - i - 1)*destWidth * 3 + j * 3; // flip vertically + else + pixel = i * destWidth * 3 + j * 3; + } + nearestMatch = (unsigned int)(py*sourcePitch + px * 4); - dstBuffer[pixel + 0] = srcBuffer[nearestMatch + 0]; - dstBuffer[pixel + 1] = srcBuffer[nearestMatch + 1]; - dstBuffer[pixel + 2] = srcBuffer[nearestMatch + 2]; + + dstBuffer[pixel + ir] = srcBuffer[nearestMatch + 0]; + dstBuffer[pixel + ig] = srcBuffer[nearestMatch + 1]; + dstBuffer[pixel + ib] = srcBuffer[nearestMatch + 2]; } } } diff --git a/SpoutDX/source/SpoutCopy.h b/SpoutDX/source/SpoutCopy.h index 6989cd7..8944ee0 100644 --- a/SpoutDX/source/SpoutCopy.h +++ b/SpoutDX/source/SpoutCopy.h @@ -99,11 +99,12 @@ class SPOUT_DLLEXP spoutCopy { // Copy RGBA to RGB allowing for source line pitch void rgba2rgb (const void* rgba_source, void *rgb_dest, unsigned int width, unsigned int height, - unsigned int sourcePitch, bool bInvert = false) const; + unsigned int sourcePitch, bool bInvert = false, bool bMirror = false, bool bSwapRB = false) const; // Copy RGBA to RGB allowing for source and destination pitch - void rgba2rgbResample(const void* source,void* dest, + void rgba2rgbResample(const void* source, void* dest, unsigned int sourceWidth, unsigned int sourceHeight, unsigned int sourcePitch, - unsigned int destWidth, unsigned int destHeight, bool bInvert = false) const; + unsigned int destWidth, unsigned int destHeight, + bool bInvert = false, bool bMirror = false, bool bSwapRB = false) const; // Copy RGBA to BGR allowing for source line pitch void rgba2bgr(const void* rgba_source, void *rgb_dest, unsigned int width, unsigned int height, unsigned int sourcePitch, bool bInvert = false) const; diff --git a/SpoutDX/source/SpoutDX.cpp b/SpoutDX/source/SpoutDX.cpp index 975fa51..edb76e2 100644 --- a/SpoutDX/source/SpoutDX.cpp +++ b/SpoutDX/source/SpoutDX.cpp @@ -77,6 +77,8 @@ // - Replace ReadRGBAimage, ReadRGBimage, ReadRGBApixels, ReadRGBpixels // with ReadPixelData and rgb flag // 06.10.20 - Allow for DX9 shared textures by creating receiving texture with compatible format +// - Mirror and swap red/blue options for SpoutCam via class flags m_bMirror, and m_bSwapRB +// Modifications to SpoutCopy rgba2rgb and rgba2rgbResample // // ==================================================================================== /* @@ -128,6 +130,8 @@ spoutDX::spoutDX() m_bSpoutPanelOpened = false; m_bSpoutPanelActive = false; m_bClassDevice = false; + m_bMirror = false; + m_bSwapRB = false; ZeroMemory(&m_SenderInfo, sizeof(SharedTextureInfo)); ZeroMemory(&m_ShExecInfo, sizeof(m_ShExecInfo)); @@ -544,7 +548,8 @@ bool spoutDX::ReceiveTexture(ID3D11Texture2D** ppTexture) // Receive from a sender via DX11 staging textures to an rgba or rgb buffer of variable size // A new shared texture pointer (m_pSharedTexture) is retrieved if the sender changed -bool spoutDX::ReceiveImage(unsigned char * pixels, unsigned int width, unsigned int height, bool bRGB, bool bInvert) +bool spoutDX::ReceiveImage(unsigned char * pixels, + unsigned int width, unsigned int height, bool bRGB, bool bInvert) { // Make sure DirectX is initialized if (!OpenDirectX11()) @@ -960,7 +965,13 @@ bool spoutDX::ReadPixelData(ID3D11Texture2D* pStagingTexture, unsigned char* pix // Copy the staging texture pixels to the user buffer if (!bRGB) { // RGBA buffer - spoutcopy.rgba2rgba(mappedSubResource.pData, pixels, width, height, mappedSubResource.RowPitch, bInvert); + // TODO : test rgba-rgba resample + if (width != m_Width || height != m_Height) { + spoutcopy.rgba2rgbaResample(mappedSubResource.pData, pixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); + } + else { + spoutcopy.rgba2rgba(mappedSubResource.pData, pixels, width, height, mappedSubResource.RowPitch, bInvert); + } } else if (m_dwFormat == 28) { // RGB buffer @@ -973,12 +984,13 @@ bool spoutDX::ReadPixelData(ID3D11Texture2D* pStagingTexture, unsigned char* pix } } else { + // Used for SpoutCam to receive RGB images if (width != m_Width || height != m_Height) { - spoutcopy.rgba2rgbResample(mappedSubResource.pData, pixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert); + spoutcopy.rgba2rgbResample(mappedSubResource.pData, pixels, m_Width, m_Height, mappedSubResource.RowPitch, width, height, bInvert, m_bMirror, m_bSwapRB); } else { // Approx 5 msec at 1920x1080 - spoutcopy.rgba2rgb(mappedSubResource.pData, pixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert); + spoutcopy.rgba2rgb(mappedSubResource.pData, pixels, m_Width, m_Height, mappedSubResource.RowPitch, bInvert, m_bMirror, m_bSwapRB); } } diff --git a/SpoutDX/source/SpoutDX.h b/SpoutDX/source/SpoutDX.h index a4b23e4..3508205 100644 --- a/SpoutDX/source/SpoutDX.h +++ b/SpoutDX/source/SpoutDX.h @@ -155,6 +155,10 @@ class SPOUT_DLLEXP spoutDX { spoutDirectX spoutdx; spoutCopy spoutcopy; + // Options used for SpoutCam + bool m_bMirror; // Mirror image + bool m_bSwapRB; // RGB <> BGR + protected : ID3D11Device* m_pd3dDevice; diff --git a/release/win32/SpoutCam32.ax b/release/win32/SpoutCam32.ax index b778c12..a69d4f3 100644 Binary files a/release/win32/SpoutCam32.ax and b/release/win32/SpoutCam32.ax differ diff --git a/release/x64/SpoutCam64.ax b/release/x64/SpoutCam64.ax index c7aefc8..caea975 100644 Binary files a/release/x64/SpoutCam64.ax and b/release/x64/SpoutCam64.ax differ diff --git a/source/cam.cpp b/source/cam.cpp index 17549f4..d74feee 100644 --- a/source/cam.cpp +++ b/source/cam.cpp @@ -238,6 +238,8 @@ 06.10.20 Add "public IAMDroppedFrames" to the declaration of CVCamStream in "cam.h" to prevent problems due to missing property dialog. Thanks to Valentin Schmidt. Allow for receiving from DX9 senders in SpoutDX + Add options for image mirror, flip and swap (RGB <> BGR) + Requires SpoutCamSettings - Version 2.005 or greater Verson 2.017 */ @@ -514,6 +516,35 @@ CVCamStream::CVCamStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName) : // (Memoryshare is not supported by DirectX) bMemoryMode = receiver.GetMemoryShareMode(); + // + // Options in SpoutCamSettings + // + + DWORD dwMode = 0; + // Mirror image + ReadDwordFromRegistry(HKEY_CURRENT_USER, "Software\\Leading Edge\\SpoutCam", "mirror", &dwMode); + if(dwMode > 0) + receiver.m_bMirror = true; + else + receiver.m_bMirror = false; + + // RGB <> BGR + ReadDwordFromRegistry(HKEY_CURRENT_USER, "Software\\Leading Edge\\SpoutCam", "swap", &dwMode); + if (dwMode > 0) + receiver.m_bSwapRB = true; + else + receiver.m_bSwapRB = false; + + // Flip image + // Default is flipped due to upside down Windows bitmap + // If set false, the result comes out inverted + // bInvert = false; + ReadDwordFromRegistry(HKEY_CURRENT_USER, "Software\\Leading Edge\\SpoutCam", "flip", &dwMode); + if (dwMode > 0) + bInvert = false; + else + bInvert = true; + m_Fps = dwFps; m_Resolution = dwResolution; @@ -727,12 +758,6 @@ HRESULT CVCamStream::FillBuffer(IMediaSample *pms) if(width == 0 || height == 0) return NOERROR; - // LJ DEBUG - // DX9 mode not supported for SpoutCam DX11 version - // LJ DEBUG - // if (receiver.GetDX9()) - // return NOERROR; - // Don't do anything if disconnected because it will already have connected // previously and something has changed. It can only disconnect after it has connected. if(!bDisconnected) { @@ -767,7 +792,8 @@ HRESULT CVCamStream::FillBuffer(IMediaSample *pms) // Get bgr pixels from the sender bgra shared texture // ReceiveImage handles sender detection, connection and copy of pixels - if (receiver.ReceiveImage(pData, g_Width, g_Height, true, true)) { // rgb = true (not rgba), invert = true + if (receiver.ReceiveImage(pData, g_Width, g_Height, true, bInvert)) { + // rgb(not rgba) = true, invert = true // If IsUpdated() returns true, the sender has changed if (receiver.IsUpdated()) { if (strcmp(g_SenderName, receiver.GetSenderName()) != 0) {