From ac305fbbbaa5c7b7c708f800e9265a8c13a22a41 Mon Sep 17 00:00:00 2001 From: narzoul Date: Mon, 28 Mar 2016 23:32:54 +0200 Subject: [PATCH] DIB based palette converter --- DDrawCompat/CompatPaletteConverter.cpp | 216 ++++++++++++++++++++++++ DDrawCompat/CompatPaletteConverter.h | 17 ++ DDrawCompat/DDrawCompat.vcxproj | 2 + DDrawCompat/DDrawCompat.vcxproj.filters | 6 + DDrawCompat/RealPrimarySurface.cpp | 88 +++------- 5 files changed, 261 insertions(+), 68 deletions(-) create mode 100644 DDrawCompat/CompatPaletteConverter.cpp create mode 100644 DDrawCompat/CompatPaletteConverter.h diff --git a/DDrawCompat/CompatPaletteConverter.cpp b/DDrawCompat/CompatPaletteConverter.cpp new file mode 100644 index 0000000..e30cd8e --- /dev/null +++ b/DDrawCompat/CompatPaletteConverter.cpp @@ -0,0 +1,216 @@ +#include + +#include "CompatDirectDraw.h" +#include "CompatDirectDrawPalette.h" +#include "CompatDirectDrawSurface.h" +#include "CompatPaletteConverter.h" +#include "CompatPrimarySurface.h" +#include "DDrawRepository.h" +#include "DDrawTypes.h" +#include "Hook.h" +#include "RealPrimarySurface.h" + +namespace +{ + CRITICAL_SECTION g_criticalSection = {}; + HDC g_dc = nullptr; + RGBQUAD g_halftonePalette[256] = {}; + HGDIOBJ g_oldBitmap = nullptr; + IDirectDrawSurface7* g_surface = nullptr; + + void convertPaletteEntriesToRgbQuad(RGBQUAD (&entries)[256]) + { + for (int i = 0; i < 256; ++i) + { + entries[i].rgbReserved = 0; + std::swap(entries[i].rgbRed, entries[i].rgbBlue); + } + } + + HBITMAP createDibSection(void*& bits) + { + struct PalettizedBitmapInfo + { + BITMAPINFOHEADER header; + PALETTEENTRY colors[256]; + }; + + PalettizedBitmapInfo bmi = {}; + bmi.header.biSize = sizeof(bmi.header); + bmi.header.biWidth = RealPrimarySurface::s_surfaceDesc.dwWidth; + bmi.header.biHeight = -static_cast(RealPrimarySurface::s_surfaceDesc.dwHeight); + bmi.header.biPlanes = 1; + bmi.header.biBitCount = 8; + bmi.header.biCompression = BI_RGB; + bmi.header.biClrUsed = 256; + + return CreateDIBSection(nullptr, reinterpret_cast(&bmi), + DIB_RGB_COLORS, &bits, nullptr, 0); + } + + IDirectDrawSurface7* createSurface(void* bits) + { + IDirectDraw7* dd = DDrawRepository::getDirectDraw(); + if (!dd) + { + return nullptr; + } + + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | + DDSD_PITCH | DDSD_LPSURFACE; + desc.dwWidth = RealPrimarySurface::s_surfaceDesc.dwWidth; + desc.dwHeight = RealPrimarySurface::s_surfaceDesc.dwHeight; + desc.ddpfPixelFormat = CompatPrimarySurface::displayMode.pixelFormat; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + desc.lPitch = (RealPrimarySurface::s_surfaceDesc.dwWidth + 3) & ~3; + desc.lpSurface = bits; + + IDirectDrawSurface7* surface = nullptr; + CompatDirectDraw::s_origVtable.CreateSurface(dd, &desc, &surface, nullptr); + return surface; + } + + void initHalftonePalette() + { + HDC dc = GetDC(nullptr); + HPALETTE palette = CreateHalftonePalette(dc); + + GetPaletteEntries(palette, 0, 256, reinterpret_cast(g_halftonePalette)); + convertPaletteEntriesToRgbQuad(g_halftonePalette); + + DeleteObject(palette); + ReleaseDC(nullptr, dc); + } +} + +namespace CompatPaletteConverter +{ + bool init() + { + if (CompatPrimarySurface::displayMode.pixelFormat.dwRGBBitCount > 8 && + RealPrimarySurface::s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount > 8) + { + return true; + } + + static bool isFirstInit = true; + if (isFirstInit) + { + InitializeCriticalSection(&g_criticalSection); + initHalftonePalette(); + isFirstInit = false; + } + + void* bits = nullptr; + HBITMAP dib = createDibSection(bits); + if (!dib) + { + Compat::Log() << "Failed to create the palette converter DIB section"; + return false; + } + + IDirectDrawSurface7* surface = createSurface(bits); + if (!surface) + { + Compat::Log() << "Failed to create the palette converter surface"; + DeleteObject(dib); + return false; + } + + HDC dc = CALL_ORIG_FUNC(CreateCompatibleDC)(nullptr); + if (!dc) + { + Compat::Log() << "Failed to create the palette converter DC"; + CompatDirectDrawSurface::s_origVtable.Release(surface); + DeleteObject(dib); + return false; + } + + EnterCriticalSection(&g_criticalSection); + g_oldBitmap = SelectObject(dc, dib); + g_dc = dc; + g_surface = surface; + LeaveCriticalSection(&g_criticalSection); + return true; + } + + HDC lockDc() + { + EnterCriticalSection(&g_criticalSection); + return g_dc; + } + + IDirectDrawSurface7* lockSurface() + { + EnterCriticalSection(&g_criticalSection); + return g_surface; + } + + void release() + { + EnterCriticalSection(&g_criticalSection); + + if (!g_surface) + { + LeaveCriticalSection(&g_criticalSection); + return; + } + + CompatDirectDrawSurface::s_origVtable.Release(g_surface); + g_surface = nullptr; + + DeleteObject(SelectObject(g_dc, g_oldBitmap)); + DeleteDC(g_dc); + g_dc = nullptr; + + LeaveCriticalSection(&g_criticalSection); + } + + void setClipper(IDirectDrawClipper* clipper) + { + EnterCriticalSection(&g_criticalSection); + if (g_surface) + { + HRESULT result = CompatDirectDrawSurface::s_origVtable.SetClipper( + g_surface, clipper); + if (FAILED(result)) + { + LOG_ONCE("Failed to set a clipper on the palette converter surface: " << result); + } + } + LeaveCriticalSection(&g_criticalSection); + } + + void setPalette(IDirectDrawPalette* palette) + { + EnterCriticalSection(&g_criticalSection); + if (g_dc) + { + if (palette) + { + RGBQUAD entries[256] = {}; + CompatDirectDrawPalette::s_origVtable.GetEntries( + palette, 0, 0, 256, reinterpret_cast(entries)); + convertPaletteEntriesToRgbQuad(entries); + SetDIBColorTable(g_dc, 0, 256, entries); + } + else + { + SetDIBColorTable(g_dc, 0, 256, g_halftonePalette); + } + } + LeaveCriticalSection(&g_criticalSection); + } + + void unlockDc() + { + LeaveCriticalSection(&g_criticalSection); + } + + void unlockSurface() + { + LeaveCriticalSection(&g_criticalSection); + } +}; diff --git a/DDrawCompat/CompatPaletteConverter.h b/DDrawCompat/CompatPaletteConverter.h new file mode 100644 index 0000000..b58ea0d --- /dev/null +++ b/DDrawCompat/CompatPaletteConverter.h @@ -0,0 +1,17 @@ +#pragma once + +#define CINTERFACE + +#include + +namespace CompatPaletteConverter +{ + bool init(); + HDC lockDc(); + IDirectDrawSurface7* lockSurface(); + void release(); + void setClipper(IDirectDrawClipper* clipper); + void setPalette(IDirectDrawPalette* palette); + void unlockDc(); + void unlockSurface(); +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index a64cdc2..9a07513 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -155,6 +155,7 @@ + @@ -187,6 +188,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 52422f7..8dd014d 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -102,6 +102,9 @@ Header Files + + Header Files + @@ -176,6 +179,9 @@ Source Files + + Source Files + diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp index 039d429..2ea60fd 100644 --- a/DDrawCompat/RealPrimarySurface.cpp +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -3,6 +3,7 @@ #include "CompatDirectDraw.h" #include "CompatDirectDrawSurface.h" #include "CompatGdi.h" +#include "CompatPaletteConverter.h" #include "CompatPrimarySurface.h" #include "Config.h" #include "DDrawProcs.h" @@ -18,7 +19,6 @@ namespace IDirectDrawSurface7* g_frontBuffer = nullptr; IDirectDrawSurface7* g_backBuffer = nullptr; - IDirectDrawSurface7* g_paletteConverterSurface = nullptr; IReleaseNotifier g_releaseNotifier(onRelease); HANDLE g_updateThread = nullptr; @@ -46,22 +46,23 @@ namespace clipper->lpVtbl->Release(clipper); } - if (g_paletteConverterSurface) + if (CompatPrimarySurface::pixelFormat.dwRGBBitCount <= 8) { - origVtable.Blt(g_paletteConverterSurface, nullptr, CompatPrimarySurface::surface, nullptr, + IDirectDrawSurface7* converterSurface = CompatPaletteConverter::lockSurface(); + HDC converterDc = CompatPaletteConverter::lockDc(); + + origVtable.Blt(converterSurface, nullptr, CompatPrimarySurface::surface, nullptr, DDBLT_WAIT, nullptr); HDC destDc = nullptr; origVtable.GetDC(dest, &destDc); - HDC converterDc = nullptr; - origVtable.GetDC(g_paletteConverterSurface, &converterDc); - result = TRUE == CALL_ORIG_FUNC(BitBlt)(destDc, 0, 0, RealPrimarySurface::s_surfaceDesc.dwWidth, RealPrimarySurface::s_surfaceDesc.dwHeight, converterDc, 0, 0, SRCCOPY); - - origVtable.ReleaseDC(g_paletteConverterSurface, converterDc); origVtable.ReleaseDC(dest, destDc); + + CompatPaletteConverter::unlockDc(); + CompatPaletteConverter::unlockSurface(); } else { @@ -73,36 +74,6 @@ namespace return result; } - template - HRESULT createPaletteConverterSurface(DirectDraw& dd) - { - if (CompatPrimarySurface::displayMode.pixelFormat.dwRGBBitCount > 8 && - RealPrimarySurface::s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount > 8) - { - return DD_OK; - } - - typename Types::TSurfaceDesc desc = {}; - desc.dwSize = sizeof(desc); - desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; - desc.dwWidth = RealPrimarySurface::s_surfaceDesc.dwWidth; - desc.dwHeight = RealPrimarySurface::s_surfaceDesc.dwHeight; - desc.ddpfPixelFormat = CompatPrimarySurface::displayMode.pixelFormat; - desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - - typedef typename Types::TCreatedSurface TCreatedSurface; - TCreatedSurface* surface = nullptr; - HRESULT result = CompatDirectDraw::s_origVtable.CreateSurface(&dd, &desc, &surface, nullptr); - if (SUCCEEDED(result)) - { - CompatDirectDrawSurface::s_origVtable.QueryInterface( - surface, IID_IDirectDrawSurface7, reinterpret_cast(&g_paletteConverterSurface)); - CompatDirectDrawSurface::s_origVtable.Release(surface); - } - - return result; - } - DWORD getTimeElapsedInMs(const LARGE_INTEGER& time) { LARGE_INTEGER currentTime = {}; @@ -117,11 +88,7 @@ namespace g_frontBuffer = nullptr; g_backBuffer = nullptr; g_isFullScreen = false; - if (g_paletteConverterSurface) - { - g_paletteConverterSurface->lpVtbl->Release(g_paletteConverterSurface); - g_paletteConverterSurface = nullptr; - } + CompatPaletteConverter::release(); ZeroMemory(&RealPrimarySurface::s_surfaceDesc, sizeof(RealPrimarySurface::s_surfaceDesc)); @@ -220,13 +187,11 @@ HRESULT RealPrimarySurface::create(DirectDraw& dd) s_surfaceDesc.dwSize = sizeof(s_surfaceDesc); g_frontBuffer->lpVtbl->GetSurfaceDesc(g_frontBuffer, &s_surfaceDesc); - result = createPaletteConverterSurface(dd); - if (FAILED(result)) + if (!CompatPaletteConverter::init()) { - Compat::Log() << "Failed to create palette converter surface"; g_frontBuffer->lpVtbl->Release(g_frontBuffer); g_frontBuffer = nullptr; - return result; + return DDERR_GENERIC; } if (isFlippable) @@ -322,15 +287,7 @@ HRESULT RealPrimarySurface::restore() void RealPrimarySurface::setClipper(LPDIRECTDRAWCLIPPER clipper) { - if (g_paletteConverterSurface) - { - HRESULT result = CompatDirectDrawSurface::s_origVtable.SetClipper( - g_paletteConverterSurface, clipper); - if (FAILED(result)) - { - LOG_ONCE("Failed to set the clipper on the converter surface: " << result); - } - } + CompatPaletteConverter::setClipper(clipper); HRESULT result = CompatDirectDrawSurface::s_origVtable.SetClipper( g_frontBuffer, clipper); @@ -342,20 +299,10 @@ void RealPrimarySurface::setClipper(LPDIRECTDRAWCLIPPER clipper) void RealPrimarySurface::setPalette(LPDIRECTDRAWPALETTE palette) { - auto& origVtable = CompatDirectDrawSurface::s_origVtable; - - if (g_paletteConverterSurface && CompatPrimarySurface::pixelFormat.dwRGBBitCount <= 8) - { - HRESULT result = origVtable.SetPalette(g_paletteConverterSurface, palette); - if (FAILED(result)) - { - LOG_ONCE("Failed to set the palette on the converter surface: " << result); - } - } - if (s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8) { - HRESULT result = origVtable.SetPalette(g_frontBuffer, palette); + HRESULT result = CompatDirectDrawSurface::s_origVtable.SetPalette( + g_frontBuffer, palette); if (FAILED(result) && DDERR_NOPALETTEATTACHED != result) { LOG_ONCE("Failed to set the palette on the real primary surface: " << result); @@ -373,6 +320,11 @@ void RealPrimarySurface::update() void RealPrimarySurface::updatePalette() { + if (CompatPrimarySurface::pixelFormat.dwRGBBitCount <= 8) + { + CompatPaletteConverter::setPalette(CompatPrimarySurface::palette); + } + CompatGdi::updatePalette(); updateNow(); }