diff --git a/DDrawCompat/D3dDdi/Resource.cpp b/DDrawCompat/D3dDdi/Resource.cpp index ed84a9d..873cc45 100644 --- a/DDrawCompat/D3dDdi/Resource.cpp +++ b/DDrawCompat/D3dDdi/Resource.cpp @@ -1317,18 +1317,46 @@ namespace D3dDdi continue; } + D3DDDIFORMAT format = D3DDDIFMT_X8R8G8B8; + COLORREF colorKey = 0; + BYTE alpha = 0; + DWORD flags = 0; + if (layeredWindow.dc) + { + colorKey = layeredWindow.colorKey; + alpha = layeredWindow.alpha; + if (CLR_INVALID != colorKey) + { + flags |= ULW_COLORKEY; + } + if (255 != alpha) + { + flags |= ULW_ALPHA; + } + + if (layeredWindow.alphaFormat & AC_SRC_ALPHA) + { + format = D3DDDIFMT_A8R8G8B8; + } + } + else + { + GetLayeredWindowAttributes(layeredWindow.hwnd, &colorKey, &alpha, &flags); + } + RECT srcRect = { 0, 0, visibleRect.right - visibleRect.left + 1, visibleRect.bottom - visibleRect.top + 1 }; auto& windowSurface = repo.getTempSysMemSurface(srcRect.right, srcRect.bottom); - auto& texture = repo.getTempTexture(srcRect.right, srcRect.bottom, D3DDDIFMT_A8R8G8B8); + auto& texture = repo.getTempTexture(srcRect.right, srcRect.bottom, format); if (!windowSurface.resource || !texture.resource) { continue; } - HDC srcDc = GetWindowDC(layeredWindow.hwnd); + HDC srcDc = layeredWindow.dc ? layeredWindow.dc : GetWindowDC(layeredWindow.hwnd); HDC dstDc = nullptr; POINT srcOrig = { visibleRect.left - layeredWindow.rect.left, visibleRect.top - layeredWindow.rect.top }; windowSurface.surface->GetDC(windowSurface.surface, &dstDc); + CALL_ORIG_FUNC(BitBlt)(dstDc, 0, 0, srcRect.right - 1, srcRect.bottom - 1, srcDc, srcOrig.x, srcOrig.y, SRCCOPY); CALL_ORIG_FUNC(BitBlt)(dstDc, srcRect.right - 1, 0, 1, srcRect.bottom - 1, @@ -1337,16 +1365,16 @@ namespace D3dDdi srcDc, srcOrig.x, srcOrig.y + srcRect.bottom - 2, SRCCOPY); CALL_ORIG_FUNC(BitBlt)(dstDc, srcRect.right - 1, srcRect.bottom - 1, 1, 1, srcDc, srcOrig.x + srcRect.right - 2, srcOrig.y + srcRect.bottom - 2, SRCCOPY); + windowSurface.surface->ReleaseDC(windowSurface.surface, dstDc); - ReleaseDC(layeredWindow.hwnd, srcDc); + if (!layeredWindow.dc) + { + ReleaseDC(layeredWindow.hwnd, srcDc); + } copySubResourceRegion(*texture.resource, 0, srcRect, *windowSurface.resource, 0, srcRect); windowSurface.resource->notifyLock(0); - COLORREF colorKey = 0; - BYTE alpha = 0; - DWORD flags = 0; - GetLayeredWindowAttributes(layeredWindow.hwnd, &colorKey, &alpha, &flags); const ShaderBlitter::ColorKeyInfo ck = { colorKey, (flags & ULW_COLORKEY) ? D3DDDIFMT_A8R8G8B8 : D3DDDIFMT_UNKNOWN }; @@ -1359,9 +1387,17 @@ namespace D3dDdi srcRect.right--; srcRect.bottom--; - blitter.textureBlt(dst, dstSubResourceIndex, visibleRect, *texture.resource, 0, srcRect, - D3DTEXF_LINEAR | D3DTEXF_SRGB, ck, (flags & ULW_ALPHA) ? &alpha : nullptr, - layeredWindow.region); + if (layeredWindow.dc) + { + blitter.alphaBlendBlt(dst, dstSubResourceIndex, visibleRect, *texture.resource, 0, srcRect, + ck, (flags & ULW_ALPHA) ? alpha : 255, layeredWindow.region); + } + else + { + blitter.textureBlt(dst, dstSubResourceIndex, visibleRect, *texture.resource, 0, srcRect, + D3DTEXF_LINEAR | D3DTEXF_SRGB, ck, (flags & ULW_ALPHA) ? &alpha : nullptr, + layeredWindow.region); + } } } diff --git a/DDrawCompat/D3dDdi/ShaderBlitter.cpp b/DDrawCompat/D3dDdi/ShaderBlitter.cpp index 2101b77..c3a7e8e 100644 --- a/DDrawCompat/D3dDdi/ShaderBlitter.cpp +++ b/DDrawCompat/D3dDdi/ShaderBlitter.cpp @@ -3,10 +3,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -29,7 +31,8 @@ namespace { const UINT BLT_SRCALPHA = 1; - const UINT BLT_COLORKEYTEST = 2; + const UINT BLT_PREMULTIPLIED = 2; + const UINT BLT_COLORKEYTEST = 4; const UINT CF_HORIZONTAL = 1; const UINT CF_GAMMARAMP = 2; @@ -41,6 +44,11 @@ namespace std::array convertToShaderConst(D3dDdi::ShaderBlitter::ColorKeyInfo colorKeyInfo) { + if (D3DDDIFMT_UNKNOWN == colorKeyInfo.format) + { + return {}; + } + const auto& fi = D3dDdi::getFormatInfo(colorKeyInfo.format); return { { { @@ -78,8 +86,16 @@ namespace namespace D3dDdi { + std::ostream& operator<<(std::ostream& os, ShaderBlitter::ColorKeyInfo ck) + { + return Compat::LogStruct(os) + << Compat::hex(ck.colorKey) + << ck.format; + } + ShaderBlitter::ShaderBlitter(Device& device) : m_device(device) + , m_psAlphaBlend(createPixelShader(g_psAlphaBlend)) , m_psBilinear(createPixelShader(g_psBilinear)) , m_psColorKey(createPixelShader(g_psColorKey)) , m_psColorKeyBlend(createPixelShader(g_psColorKeyBlend)) @@ -106,6 +122,22 @@ namespace D3dDdi } } + void ShaderBlitter::alphaBlendBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect, + const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect, + ColorKeyInfo srcColorKey, BYTE alpha, const Gdi::Region& srcRgn) + { + LOG_FUNC("ShaderBlitter::alphaBlendBlt", static_cast(dstResource), dstSubResourceIndex, dstRect, + static_cast(srcResource), srcSubResourceIndex, srcRect, + srcColorKey, static_cast(alpha), static_cast(srcRgn)); + + auto ck = convertToShaderConst(srcColorKey); + ck[0][3] = alpha / 255.0f; + DeviceState::TempPixelShaderConst psConst(m_device.getState(), { 30, 2 }, ck.data()); + blt(dstResource, dstSubResourceIndex, dstRect, + srcResource, srcSubResourceIndex, srcRect, + m_psAlphaBlend.get(), D3DTEXF_LINEAR, BLT_SRCALPHA | BLT_PREMULTIPLIED, nullptr, srcRgn); + } + void ShaderBlitter::bicubicBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect, const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect, UINT blurPercent) { @@ -156,7 +188,7 @@ namespace D3dDdi HANDLE pixelShader, UINT filter, UINT flags, const BYTE* alpha, const Gdi::Region& srcRgn) { LOG_FUNC("ShaderBlitter::blt", static_cast(dstResource), dstSubResourceIndex, dstRect, - static_cast(srcResource), srcSubResourceIndex, srcRect, pixelShader, filter, + static_cast(srcResource), srcSubResourceIndex, srcRect, pixelShader, Compat::hex(filter), Compat::hex(flags), alpha, static_cast(srcRgn)); if (!m_vertexShaderDecl || !pixelShader) @@ -214,9 +246,10 @@ namespace D3dDdi } else if (flags & BLT_SRCALPHA) { + const UINT D3DBLEND_ONE = 2; const UINT D3DBLEND_SRCALPHA = 5; const UINT D3DBLEND_INVSRCALPHA = 6; - state.setTempRenderState({ D3DDDIRS_SRCBLEND, D3DBLEND_SRCALPHA }); + state.setTempRenderState({ D3DDDIRS_SRCBLEND, (flags & BLT_PREMULTIPLIED) ? D3DBLEND_ONE : D3DBLEND_SRCALPHA }); state.setTempRenderState({ D3DDDIRS_DESTBLEND, D3DBLEND_INVSRCALPHA }); } diff --git a/DDrawCompat/D3dDdi/ShaderBlitter.h b/DDrawCompat/D3dDdi/ShaderBlitter.h index 023b20f..30b6955 100644 --- a/DDrawCompat/D3dDdi/ShaderBlitter.h +++ b/DDrawCompat/D3dDdi/ShaderBlitter.h @@ -31,6 +31,9 @@ namespace D3dDdi ShaderBlitter& operator=(const ShaderBlitter&) = delete; ShaderBlitter& operator=(ShaderBlitter&&) = delete; + void alphaBlendBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect, + const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect, + ColorKeyInfo srcColorKey, BYTE alpha, const Gdi::Region& srcRgn); void bilinearBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect, const Resource& srcResource, UINT srcSubResourceIndex, const RECT& srcRect, UINT blurPercent); void bicubicBlt(const Resource& dstResource, UINT dstSubResourceIndex, const RECT& dstRect, @@ -117,6 +120,7 @@ namespace D3dDdi void setTextureCoords(UINT stage, const RECT& rect, UINT width, UINT height); Device& m_device; + std::unique_ptr m_psAlphaBlend; std::unique_ptr m_psBilinear; std::unique_ptr m_psColorKey; std::unique_ptr m_psColorKeyBlend; @@ -134,4 +138,6 @@ namespace D3dDdi ConvolutionParams m_convolutionParams; std::array m_vertices; }; + + std::ostream& operator<<(std::ostream& os, ShaderBlitter::ColorKeyInfo ck); } diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index ae98ffc..ac1e00b 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -478,6 +478,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index d811100..6da85da 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -1169,6 +1169,9 @@ Shaders + + Shaders + diff --git a/DDrawCompat/Gdi/PresentationWindow.cpp b/DDrawCompat/Gdi/PresentationWindow.cpp index 3b6a211..392d250 100644 --- a/DDrawCompat/Gdi/PresentationWindow.cpp +++ b/DDrawCompat/Gdi/PresentationWindow.cpp @@ -40,6 +40,10 @@ namespace Gdi if (presentationWindow) { CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA); + if (owner) + { + AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(owner, nullptr), FALSE); + } } }); return LOG_RESULT(presentationWindow); diff --git a/DDrawCompat/Gdi/WinProc.cpp b/DDrawCompat/Gdi/WinProc.cpp index 8624de6..e086305 100644 --- a/DDrawCompat/Gdi/WinProc.cpp +++ b/DDrawCompat/Gdi/WinProc.cpp @@ -755,6 +755,36 @@ namespace } } + BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize, + HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags) + { + LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags); + BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)( + hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags); + if (result) + { + Gdi::Window::updateLayeredWindowInfo(hWnd, hdcSrc, pptSrc, + (dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID, + ((dwFlags & ULW_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255, + pblend ? pblend->AlphaFormat : 0); + } + return LOG_RESULT(result); + } + + BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo) + { + LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo); + BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo); + if (result && pULWInfo) + { + Gdi::Window::updateLayeredWindowInfo(hwnd, pULWInfo->hdcSrc, pULWInfo->pptSrc, + (pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID, + ((pULWInfo->dwFlags & ULW_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255, + pULWInfo->pblend ? pULWInfo->pblend->AlphaFormat : 0); + } + return LOG_RESULT(result); + } + void CALLBACK winEventProc( HWINEVENTHOOK /*hWinEventHook*/, DWORD event, @@ -880,6 +910,8 @@ namespace Gdi HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA); HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW); HOOK_FUNCTION(user32, SetWindowPos, setWindowPos); + HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow); + HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect); g_dwmSetIconicThumbnail = reinterpret_cast( GetProcAddress(GetModuleHandle("dwmapi"), "DwmSetIconicThumbnail")); diff --git a/DDrawCompat/Gdi/Window.cpp b/DDrawCompat/Gdi/Window.cpp index f6be18c..9399f64 100644 --- a/DDrawCompat/Gdi/Window.cpp +++ b/DDrawCompat/Gdi/Window.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,74 @@ namespace { + class LayeredWindowContent + { + public: + ~LayeredWindowContent() + { + destroy(); + } + + BYTE getAlpha() const { return m_alpha; } + BYTE getAlphaFormat() const { return m_alphaFormat; } + COLORREF getColorKey() const { return m_colorKey; } + HDC getDc() const { return m_dc; } + + void set(HWND hwnd, HDC hdcSrc, const POINT* pptSrc, COLORREF colorKey, BYTE alpha, BYTE alphaFormat) + { + if (hdcSrc) + { + RECT wr = {}; + GetWindowRect(hwnd, &wr); + const SIZE size = Rect::getSize(wr); + + if (size != m_size) + { + destroy(); + m_size = size; + m_dc = CreateCompatibleDC(nullptr); + m_origBmp = SelectObject(m_dc, CALL_ORIG_FUNC(CreateBitmap)(size.cx, size.cy, 1, 32, nullptr)); + } + + CALL_ORIG_FUNC(BitBlt)(m_dc, 0, 0, size.cx, size.cy, + hdcSrc, pptSrc ? pptSrc->x : 0, pptSrc ? pptSrc->y : 0, SRCCOPY); + + if (alphaFormat & AC_SRC_ALPHA) + { + BITMAP bm = {}; + GetObject(GetCurrentObject(hdcSrc, OBJ_BITMAP), sizeof(bm), &bm); + if (32 != bm.bmBitsPixel) + { + alphaFormat &= ~AC_SRC_ALPHA; + } + } + } + + m_colorKey = colorKey; + m_alpha = alpha; + m_alphaFormat = alphaFormat; + } + + private: + void destroy() + { + if (m_dc) + { + DeleteObject(SelectObject(m_dc, m_origBmp)); + DeleteDC(m_dc); + m_dc = nullptr; + m_origBmp = nullptr; + } + } + + HDC m_dc = nullptr; + HGDIOBJ m_origBmp = nullptr; + SIZE m_size = {}; + COLORREF m_colorKey = CLR_INVALID; + BYTE m_alpha = 255; + BYTE m_alphaFormat = 0; + }; + struct UpdateWindowContext { Gdi::Region obscuredRegion; @@ -39,6 +108,7 @@ namespace RECT clientRect; Gdi::Region visibleRegion; Gdi::Region invalidatedRegion; + LayeredWindowContent layeredWindowContent; bool isMenu; bool isLayered; bool isDpiAware; @@ -90,57 +160,6 @@ namespace return true; } - void presentLayeredWindow(CompatWeakPtr dst, - HWND hwnd, RECT wr, const RECT& monitorRect, HDC& dstDc, Gdi::Region* rgn = nullptr, bool isMenu = false) - { - if (!dst) - { - throw true; - } - - if (!dstDc) - { - dst->GetDC(dst, &dstDc); - if (!dstDc) - { - throw false; - } - } - - OffsetRect(&wr, -monitorRect.left, -monitorRect.top); - if (rgn) - { - rgn->offset(-monitorRect.left, -monitorRect.top); - } - - HDC windowDc = GetWindowDC(hwnd); - if (rgn) - { - SelectClipRgn(dstDc, *rgn); - } - - COLORREF colorKey = 0; - BYTE alpha = 255; - DWORD flags = ULW_ALPHA; - if (isMenu || CALL_ORIG_FUNC(GetLayeredWindowAttributes)(hwnd, &colorKey, &alpha, &flags)) - { - if (flags & LWA_COLORKEY) - { - CALL_ORIG_FUNC(TransparentBlt)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, - windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey); - } - else - { - BLENDFUNCTION blend = {}; - blend.SourceConstantAlpha = alpha; - CALL_ORIG_FUNC(AlphaBlend)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, - windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend); - } - } - - CALL_ORIG_FUNC(ReleaseDC)(hwnd, windowDc); - } - void updatePosition(Window& window, const RECT& oldWindowRect, const RECT& oldClientRect, const Gdi::Region& oldVisibleRegion, Gdi::Region& invalidatedRegion) { @@ -351,7 +370,14 @@ namespace Gdi auto& window = **it; if (window.isLayered && !window.visibleRegion.isEmpty()) { - layeredWindows.push_back({ window.hwnd, window.windowRect, window.visibleRegion }); + layeredWindows.push_back({ + window.hwnd, + window.windowRect, + window.visibleRegion, + window.layeredWindowContent.getDc(), + window.layeredWindowContent.getColorKey(), + window.layeredWindowContent.getAlpha(), + window.layeredWindowContent.getAlphaFormat() }); } } return layeredWindows; @@ -640,6 +666,21 @@ namespace Gdi } } + void updateLayeredWindowInfo(HWND hwnd, HDC hdcSrc, const POINT* pptSrc, + COLORREF colorKey, BYTE alpha, BYTE alphaFormat) + { + D3dDdi::ScopedCriticalSection lock; + auto it = g_windows.find(hwnd); + if (it != g_windows.end()) + { + it->second.layeredWindowContent.set(hwnd, hdcSrc, pptSrc, colorKey, alpha, alphaFormat); + if (DDraw::RealPrimarySurface::isFullscreen()) + { + DDraw::RealPrimarySurface::scheduleOverlayUpdate(); + } + } + } + void updatePresentationWindowPos(HWND presentationWindow, HWND owner) { if (IsIconic(owner)) diff --git a/DDrawCompat/Gdi/Window.h b/DDrawCompat/Gdi/Window.h index 2cf8ddb..3149352 100644 --- a/DDrawCompat/Gdi/Window.h +++ b/DDrawCompat/Gdi/Window.h @@ -15,6 +15,10 @@ namespace Gdi HWND hwnd; RECT rect; Gdi::Region region; + HDC dc; + COLORREF colorKey; + BYTE alpha; + BYTE alphaFormat; }; HWND getPresentationWindow(HWND hwnd); @@ -31,6 +35,8 @@ namespace Gdi void present(Gdi::Region excludeRegion); void setDpiAwareness(HWND hwnd, bool dpiAware); void updateAll(); + void updateLayeredWindowInfo(HWND hwnd, HDC hdcSrc, const POINT* pptSrc, + COLORREF colorKey, BYTE alpha, BYTE alphaFormat); void updatePresentationWindowPos(HWND presentationWindow, HWND owner); } } diff --git a/DDrawCompat/Shaders/AlphaBlend.hlsl b/DDrawCompat/Shaders/AlphaBlend.hlsl new file mode 100644 index 0000000..1ff8501 --- /dev/null +++ b/DDrawCompat/Shaders/AlphaBlend.hlsl @@ -0,0 +1,10 @@ +sampler2D s_texture : register(s0); +float4 g_colorKey : register(c30); +float4 g_threshold : register(c31); + +float4 main(float2 texCoord : TEXCOORD0) : COLOR0 +{ + float4 color = tex2D(s_texture, texCoord); + const float4 diff = abs(color - g_colorKey); + return all(diff.rgb < g_threshold.rgb) ? 0 : (g_colorKey.a * color); +} diff --git a/DDrawCompat/Win32/Log.cpp b/DDrawCompat/Win32/Log.cpp index b32742e..82622f7 100644 --- a/DDrawCompat/Win32/Log.cpp +++ b/DDrawCompat/Win32/Log.cpp @@ -134,6 +134,15 @@ std::ostream& operator<<(std::ostream& os, const BITMAPINFOHEADER& bmih) << bmih.biClrImportant; } +std::ostream& operator<<(std::ostream& os, const BLENDFUNCTION& bf) +{ + return Compat::LogStruct(os) + << static_cast(bf.BlendOp) + << static_cast(bf.BlendFlags) + << static_cast(bf.SourceConstantAlpha) + << static_cast(bf.AlphaFormat); +} + std::ostream& operator<<(std::ostream& os, const COMPAREITEMSTRUCT& cis) { return Compat::LogStruct(os) diff --git a/DDrawCompat/Win32/Log.h b/DDrawCompat/Win32/Log.h index 9a1cd00..bdd0c20 100644 --- a/DDrawCompat/Win32/Log.h +++ b/DDrawCompat/Win32/Log.h @@ -7,6 +7,7 @@ std::ostream& operator<<(std::ostream& os, const BITMAP& bm); std::ostream& operator<<(std::ostream& os, const BITMAPINFO& bmi); std::ostream& operator<<(std::ostream& os, const BITMAPINFOHEADER& bmih); +std::ostream& operator<<(std::ostream& os, const BLENDFUNCTION& bf); std::ostream& operator<<(std::ostream& os, const COMPAREITEMSTRUCT& cis); std::ostream& operator<<(std::ostream& os, const COPYDATASTRUCT& cds); std::ostream& operator<<(std::ostream& os, const CREATESTRUCTA& cs);