Permalink
Browse files

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.
  • Loading branch information...
1 parent 7f51273 commit 5f03d7b235871e704d55a69d9715aeaac3883501 @poiru poiru committed Mar 26, 2013
View
@@ -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,126 +135,88 @@ void CanvasD2D::Resize(int w, int h)
DiscardDeviceResources();
- const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat(
- DXGI_FORMAT_B8G8R8A8_UNORM,
- D2D1_ALPHA_MODE_PREMULTIPLIED);
-
- const D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties(
- D2D1_RENDER_TARGET_TYPE_DEFAULT,
- format,
- 0.0f, // Default DPI
- 0.0f, // Default DPI
- D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);
+ m_Bitmap.Resize(w, h);
- c_WIC->CreateBitmap(
- w,
- h,
- GUID_WICPixelFormat32bppPBGRA,
- WICBitmapCacheOnLoad,
- &m_Bitmap);
-
- HRESULT hr = c_D2D->CreateWicBitmapRenderTarget(m_Bitmap, properties, &m_Target);
- if (SUCCEEDED(hr))
- {
- hr = m_Target->QueryInterface(&m_InteropTarget); // Always succeeds
-
- // 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();
- }
- }
+ m_GdipBitmap = new Gdiplus::Bitmap(w, h, w * 4, PixelFormat32bppPARGB, m_Bitmap.GetData());
+ m_GdipGraphics = new Gdiplus::Graphics(m_GdipBitmap);
}
bool CanvasD2D::BeginDraw()
{
- if (m_Target)
- {
- m_BeginDrawCalled = true;
- }
-
return true;
}
void CanvasD2D::EndDraw()
{
- m_BeginDrawCalled = false;
EndTargetDraw();
}
-void CanvasD2D::BeginTargetDraw()
+bool CanvasD2D::BeginTargetDraw()
{
- if (m_BeginDrawCalled && !m_TargetBeginDrawCalled)
+ if (m_Target) return true;
+
+ const D2D1_PIXEL_FORMAT format = D2D1::PixelFormat(
+ DXGI_FORMAT_B8G8R8A8_UNORM,
+ D2D1_ALPHA_MODE_PREMULTIPLIED);
+
+ const D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties(
+ D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ format,
+ 0.0f, // Default DPI
+ 0.0f, // Default DPI
+ D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);
+
+ // 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))
{
- m_TargetBeginDrawCalled = true;
+ SetTextAntiAliasing(m_TextAntiAliasing);
+
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);
View
@@ -21,6 +21,7 @@
#include "Canvas.h"
#include "TextFormatD2D.h"
+#include "WICBitmapDIB.h"
#include <string>
#include <GdiPlus.h>
#include <d2d1.h>
@@ -75,24 +76,17 @@ class CanvasD2D : public Canvas
void DiscardDeviceResources();
- void BeginTargetDraw();
+ bool BeginTargetDraw();
void EndTargetDraw();
ID2D1RenderTarget* m_Target;
- ID2D1GdiInteropRenderTarget* m_InteropTarget;
- IWICBitmap* m_Bitmap;
+ WICBitmapDIB m_Bitmap;
// GDI+ objects that share the pixel data of m_Bitmap.
Gdiplus::Graphics* m_GdipGraphics;
Gdiplus::Bitmap* m_GdipBitmap;
- // If true, the BeginDraw() function of this class has been called and the matching EndDraw()
- // has not been called yet.
- bool m_BeginDrawCalled;
-
- // If true, the BeginDraw() function of the render target has been called and the matching
- // EndDraw() has not been called yet.
- bool m_TargetBeginDrawCalled;
+ bool m_TextAntiAliasing;
static UINT c_Instances;
static ID2D1Factory* c_D2D;
Oops, something went wrong.

0 comments on commit 5f03d7b

Please sign in to comment.