mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
Gfx: Fix rendering issues in some cases
It seems that Direct2D does not expect the underlying pixel data of the render target to change outside of the target draw commands while the render target exists. As a result, Direct2D may in some cases overwrite changes to the pixel data. This change changes the render target to be (re)created for each batch of Direct2D drawing operations as a temporary solution. As a side effect, a DIB section is now used as the render target bitmap.
This commit is contained in:
@ -59,12 +59,10 @@ IWICImagingFactory* CanvasD2D::c_WIC = nullptr;
|
||||
|
||||
CanvasD2D::CanvasD2D() : Canvas(),
|
||||
m_Target(),
|
||||
m_InteropTarget(),
|
||||
m_Bitmap(),
|
||||
m_GdipGraphics(),
|
||||
m_GdipBitmap(),
|
||||
m_BeginDrawCalled(false),
|
||||
m_TargetBeginDrawCalled(false)
|
||||
m_TextAntiAliasing(false)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
@ -122,9 +120,7 @@ void CanvasD2D::Finalize()
|
||||
|
||||
void CanvasD2D::DiscardDeviceResources()
|
||||
{
|
||||
SafeRelease(&m_InteropTarget);
|
||||
SafeRelease(&m_Target);
|
||||
SafeRelease(&m_Bitmap);
|
||||
|
||||
delete m_GdipGraphics;
|
||||
m_GdipGraphics = nullptr;
|
||||
@ -139,6 +135,26 @@ void CanvasD2D::Resize(int w, int h)
|
||||
|
||||
DiscardDeviceResources();
|
||||
|
||||
m_Bitmap.Resize(w, h);
|
||||
|
||||
m_GdipBitmap = new Gdiplus::Bitmap(w, h, w * 4, PixelFormat32bppPARGB, m_Bitmap.GetData());
|
||||
m_GdipGraphics = new Gdiplus::Graphics(m_GdipBitmap);
|
||||
}
|
||||
|
||||
bool CanvasD2D::BeginDraw()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanvasD2D::EndDraw()
|
||||
{
|
||||
EndTargetDraw();
|
||||
}
|
||||
|
||||
bool CanvasD2D::BeginTargetDraw()
|
||||
{
|
||||
if (m_Target) return true;
|
||||
|
||||
const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat(
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_PREMULTIPLIED);
|
||||
@ -150,115 +166,57 @@ void CanvasD2D::Resize(int w, int h)
|
||||
0.0f, // Default DPI
|
||||
D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);
|
||||
|
||||
c_WIC->CreateBitmap(
|
||||
w,
|
||||
h,
|
||||
GUID_WICPixelFormat32bppPBGRA,
|
||||
WICBitmapCacheOnLoad,
|
||||
&m_Bitmap);
|
||||
|
||||
HRESULT hr = c_D2D->CreateWicBitmapRenderTarget(m_Bitmap, properties, &m_Target);
|
||||
// A new Direct2D render target must be created for each sequence of Direct2D draw operations
|
||||
// since we use GDI+ to render to the same pixel data. Without creating a new render target
|
||||
// each time, it has been found that Direct2D may overwrite the draws by GDI+ since it is
|
||||
// unaware of the changes made by GDI+. By creating a new render target and then releasing it
|
||||
// before the next GDI+ draw operations, we ensure that the pixel data result is as expected
|
||||
// Once GDI+ drawing is no longer needed, we change to recreate the render target only when the
|
||||
// bitmap size is changed.
|
||||
HRESULT hr = c_D2D->CreateWicBitmapRenderTarget(&m_Bitmap, properties, &m_Target);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = m_Target->QueryInterface(&m_InteropTarget); // Always succeeds
|
||||
SetTextAntiAliasing(m_TextAntiAliasing);
|
||||
|
||||
// Get the data pointer of the created IWICBitmap to create a Gdiplus::Bitmap
|
||||
// that shares the data. It is assumed that the data pointer will stay valid
|
||||
// and writable until the next resize.
|
||||
WICRect rect = {0, 0, w, h};
|
||||
IWICBitmapLock* lock = nullptr;
|
||||
hr = m_Bitmap->Lock(&rect, WICBitmapLockRead | WICBitmapLockWrite, &lock);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
UINT size;
|
||||
BYTE* data;
|
||||
HRESULT hr = lock->GetDataPointer(&size, &data);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_GdipBitmap = new Gdiplus::Bitmap(w, h, w * 4, PixelFormat32bppPARGB, data);
|
||||
m_GdipGraphics = new Gdiplus::Graphics(m_GdipBitmap);
|
||||
}
|
||||
|
||||
lock->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CanvasD2D::BeginDraw()
|
||||
{
|
||||
if (m_Target)
|
||||
{
|
||||
m_BeginDrawCalled = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanvasD2D::EndDraw()
|
||||
{
|
||||
m_BeginDrawCalled = false;
|
||||
EndTargetDraw();
|
||||
}
|
||||
|
||||
void CanvasD2D::BeginTargetDraw()
|
||||
{
|
||||
if (m_BeginDrawCalled && !m_TargetBeginDrawCalled)
|
||||
{
|
||||
m_TargetBeginDrawCalled = true;
|
||||
m_Target->BeginDraw();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CanvasD2D::EndTargetDraw()
|
||||
{
|
||||
if (m_TargetBeginDrawCalled)
|
||||
if (m_Target)
|
||||
{
|
||||
m_TargetBeginDrawCalled = false;
|
||||
HRESULT hr = m_Target->EndDraw();
|
||||
if (hr == D2DERR_RECREATE_TARGET)
|
||||
{
|
||||
DiscardDeviceResources();
|
||||
m_Target->EndDraw();
|
||||
|
||||
// Attempt to recreate target.
|
||||
Resize(m_W, m_H);
|
||||
}
|
||||
SafeRelease(&m_Target);
|
||||
}
|
||||
}
|
||||
|
||||
Gdiplus::Graphics& CanvasD2D::BeginGdiplusContext()
|
||||
{
|
||||
if (m_BeginDrawCalled)
|
||||
{
|
||||
EndTargetDraw();
|
||||
|
||||
// Pretend that the render target BeginDraw() has been called. This will cause draw calls
|
||||
// on the render target to fail until EndGdiplusContext() is used.
|
||||
m_TargetBeginDrawCalled = true;
|
||||
}
|
||||
|
||||
EndTargetDraw();
|
||||
return *m_GdipGraphics;
|
||||
}
|
||||
|
||||
void CanvasD2D::EndGdiplusContext()
|
||||
{
|
||||
// See BeginGdiplusContext().
|
||||
m_TargetBeginDrawCalled = false;
|
||||
}
|
||||
|
||||
HDC CanvasD2D::GetDC()
|
||||
{
|
||||
BeginTargetDraw();
|
||||
EndTargetDraw();
|
||||
|
||||
HDC dcMemory = nullptr;
|
||||
m_InteropTarget->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &dcMemory);
|
||||
HDC dcMemory = CreateCompatibleDC(nullptr);
|
||||
SelectObject(dcMemory, m_Bitmap.GetHandle());
|
||||
return dcMemory;
|
||||
}
|
||||
|
||||
void CanvasD2D::ReleaseDC(HDC dc)
|
||||
{
|
||||
// Assume that the DC was not modified.
|
||||
RECT r = {0, 0, 0, 0};
|
||||
m_InteropTarget->ReleaseDC(&r);
|
||||
DeleteDC(dc);
|
||||
}
|
||||
|
||||
bool CanvasD2D::IsTransparentPixel(int x, int y)
|
||||
@ -267,21 +225,11 @@ bool CanvasD2D::IsTransparentPixel(int x, int y)
|
||||
|
||||
bool transparent = true;
|
||||
|
||||
WICRect rect = {0, 0, m_W, m_H};
|
||||
IWICBitmapLock* lock = nullptr;
|
||||
HRESULT hr = m_Bitmap->Lock(&rect, WICBitmapLockRead, &lock);
|
||||
if (SUCCEEDED(hr))
|
||||
DWORD* data = (DWORD*)m_Bitmap.GetData();
|
||||
if (data)
|
||||
{
|
||||
UINT size;
|
||||
DWORD* data;
|
||||
hr = lock->GetDataPointer(&size, (BYTE**)&data);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
DWORD pixel = data[y * m_W + x]; // top-down DIB
|
||||
transparent = (pixel & 0xFF000000) != 0;
|
||||
}
|
||||
|
||||
lock->Release();
|
||||
DWORD pixel = data[y * m_W + x]; // Top-down DIB.
|
||||
transparent = (pixel & 0xFF000000) != 0;
|
||||
}
|
||||
|
||||
return transparent;
|
||||
@ -299,19 +247,25 @@ void CanvasD2D::SetAntiAliasing(bool enable)
|
||||
void CanvasD2D::SetTextAntiAliasing(bool enable)
|
||||
{
|
||||
// TODO: Add support for D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE?
|
||||
m_Target->SetTextAntialiasMode(
|
||||
enable ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
m_TextAntiAliasing = enable;
|
||||
|
||||
if (m_Target)
|
||||
{
|
||||
m_Target->SetTextAntialiasMode(
|
||||
m_TextAntiAliasing ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasD2D::Clear(const Gdiplus::Color& color)
|
||||
{
|
||||
BeginTargetDraw();
|
||||
if (!BeginTargetDraw()) return;
|
||||
|
||||
m_Target->Clear(ToColorF(color));
|
||||
}
|
||||
|
||||
void CanvasD2D::DrawTextW(const WCHAR* str, UINT strLen, const TextFormat& format, Gdiplus::RectF& rect, const Gdiplus::SolidBrush& brush)
|
||||
{
|
||||
BeginTargetDraw();
|
||||
if (!BeginTargetDraw()) return;
|
||||
|
||||
Gdiplus::Color color;
|
||||
brush.GetColor(&color);
|
||||
@ -391,10 +345,11 @@ bool CanvasD2D::MeasureTextLinesW(const WCHAR* str, UINT strLen, const TextForma
|
||||
|
||||
void CanvasD2D::DrawBitmap(Gdiplus::Bitmap* bitmap, const Gdiplus::Rect& dstRect, const Gdiplus::Rect& srcRect)
|
||||
{
|
||||
if (!BeginTargetDraw()) return;
|
||||
|
||||
// The D2D DrawBitmap seems to perform exactly like Gdiplus::Graphics::DrawImage since we are
|
||||
// not using a hardware accelerated render target. Nevertheless, we will use it to avoid
|
||||
// the EndDraw() call needed for GDI+ drawing.
|
||||
bool draw = false;
|
||||
WICBitmapLockGDIP* bitmapLock = new WICBitmapLockGDIP();
|
||||
Gdiplus::Status status = bitmap->LockBits(
|
||||
&srcRect, Gdiplus::ImageLockModeRead, PixelFormat32bppPARGB, bitmapLock->GetBitmapData());
|
||||
@ -406,12 +361,9 @@ void CanvasD2D::DrawBitmap(Gdiplus::Bitmap* bitmap, const Gdiplus::Rect& dstRect
|
||||
HRESULT hr = m_Target->CreateSharedBitmap(__uuidof(IWICBitmapLock), bitmapLock, &props, &d2dBitmap);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
BeginTargetDraw();
|
||||
|
||||
auto rDst = ToRectF(dstRect);
|
||||
auto rSrc = ToRectF(srcRect);
|
||||
m_Target->DrawBitmap(d2dBitmap, rDst, 1.0F, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, rSrc);
|
||||
draw = true;
|
||||
|
||||
d2dBitmap->Release();
|
||||
}
|
||||
@ -420,15 +372,12 @@ void CanvasD2D::DrawBitmap(Gdiplus::Bitmap* bitmap, const Gdiplus::Rect& dstRect
|
||||
bitmap->UnlockBits(bitmapLock->GetBitmapData());
|
||||
}
|
||||
|
||||
if (!draw)
|
||||
{
|
||||
delete bitmapLock;
|
||||
}
|
||||
bitmapLock->Release();
|
||||
}
|
||||
|
||||
void CanvasD2D::FillRectangle(Gdiplus::Rect& rect, const Gdiplus::SolidBrush& brush)
|
||||
{
|
||||
BeginTargetDraw();
|
||||
if (!BeginTargetDraw()) return;
|
||||
|
||||
Gdiplus::Color color;
|
||||
brush.GetColor(&color);
|
||||
|
Reference in New Issue
Block a user