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:
Birunthan Mohanathas 2013-03-26 21:29:05 +02:00
parent 7f51273950
commit 5f03d7b235
8 changed files with 436 additions and 120 deletions

View File

@ -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;
}
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,23 +225,13 @@ 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
DWORD pixel = data[y * m_W + x]; // Top-down DIB.
transparent = (pixel & 0xFF000000) != 0;
}
lock->Release();
}
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_TextAntiAliasing = enable;
if (m_Target)
{
m_Target->SetTextAntialiasMode(
enable ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
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 File

@ -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 @@ private:
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;

134
Common/Gfx/WICBitmapDIB.cpp Normal file
View File

@ -0,0 +1,134 @@
/*
Copyright (C) 2013 Birunthan Mohanathas
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "WICBitmapDIB.h"
#include "WICBitmapLockDIB.h"
#include <cassert>
namespace Gfx {
WICBitmapDIB::WICBitmapDIB() :
m_DIBSectionBuffer(),
m_DIBSectionBufferPixels(),
m_W(0),
m_H(0)
{
}
WICBitmapDIB::~WICBitmapDIB()
{
if (m_DIBSectionBuffer)
{
DeleteObject(m_DIBSectionBuffer);
m_DIBSectionBufferPixels = nullptr;
}
}
void WICBitmapDIB::Resize(UINT w, UINT h)
{
if (m_DIBSectionBuffer)
{
DeleteObject(m_DIBSectionBuffer);
m_DIBSectionBufferPixels = nullptr;
}
m_W = w;
m_H = h;
BITMAPV4HEADER bh = {sizeof(BITMAPV4HEADER)};
bh.bV4Width = (LONG)m_W;
bh.bV4Height = -(LONG)m_H; // Top-down DIB
bh.bV4Planes = 1;
bh.bV4BitCount = 32;
bh.bV4V4Compression = BI_BITFIELDS;
bh.bV4RedMask = 0x00FF0000;
bh.bV4GreenMask = 0x0000FF00;
bh.bV4BlueMask = 0x000000FF;
bh.bV4AlphaMask = 0xFF000000;
m_DIBSectionBuffer = CreateDIBSection(
nullptr,
(BITMAPINFO*)&bh,
DIB_RGB_COLORS,
(void**)&m_DIBSectionBufferPixels,
nullptr,
0);
assert(m_DIBSectionBufferPixels);
}
IFACEMETHODIMP WICBitmapDIB::QueryInterface(REFIID riid, void** ppvObject)
{
return E_NOTIMPL;
}
IFACEMETHODIMP_(ULONG) WICBitmapDIB::AddRef()
{
return 0;
}
IFACEMETHODIMP_(ULONG) WICBitmapDIB::Release()
{
return 0;
}
IFACEMETHODIMP WICBitmapDIB::GetSize(UINT* puiWidth, UINT* puiHeight)
{
if (puiWidth) *puiWidth = m_W;
if (puiHeight) *puiHeight = m_H;
return S_OK;
}
IFACEMETHODIMP WICBitmapDIB::GetPixelFormat(WICPixelFormatGUID* pPixelFormat)
{
*pPixelFormat = GUID_WICPixelFormat32bppPBGRA;
return S_OK;
}
IFACEMETHODIMP WICBitmapDIB::GetResolution(double* pDpiX, double* pDpiY)
{
return E_NOTIMPL;
}
IFACEMETHODIMP WICBitmapDIB::CopyPalette(IWICPalette* pIPalette)
{
return E_NOTIMPL;
}
IFACEMETHODIMP WICBitmapDIB::CopyPixels(const WICRect* prc, UINT cbStride, UINT cbBufferSize, BYTE* pbBuffer)
{
return E_NOTIMPL;
}
IFACEMETHODIMP WICBitmapDIB::Lock(const WICRect* prcLock, DWORD flags, IWICBitmapLock** ppILock)
{
if (ppILock) *ppILock = (IWICBitmapLock*)new WICBitmapLockDIB(this, prcLock);
return S_OK;
}
IFACEMETHODIMP WICBitmapDIB::SetPalette(IWICPalette* pIPalette)
{
return E_NOTIMPL;
}
IFACEMETHODIMP WICBitmapDIB::SetResolution(double dpiX, double dpiY)
{
return E_NOTIMPL;
}
} // namespace Gfx

73
Common/Gfx/WICBitmapDIB.h Normal file
View File

@ -0,0 +1,73 @@
/*
Copyright (C) 2013 Birunthan Mohanathas
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef RM_GFX_WICBITMAPDIB_H_
#define RM_GFX_WICBITMAPDIB_H_
#include <Windows.h>
#include <GdiPlus.h>
#include <wincodec.h>
namespace Gfx {
// Allows the use of a DIB section (HBITMAP) in Direct2D as a WIC bitmap. It is assumed that this
// class is used only with 32bpp PARGB bitmaps and using a sigle thread.
//
// This class does not follow the COM reference count model. RTTI is used instead. This class
// implements only the bare essentials in order to use a DIB section as a Direct2D render target.
class WICBitmapDIB : public IWICBitmap
{
public:
WICBitmapDIB();
~WICBitmapDIB();
void Resize(UINT w, UINT h);
HBITMAP GetHandle() const { return m_DIBSectionBuffer; }
BYTE* GetData() const { return (BYTE*)m_DIBSectionBufferPixels; }
// IUnknown
IFACEMETHOD(QueryInterface)(REFIID riid, void** ppvObject);
IFACEMETHOD_(ULONG, AddRef)();
IFACEMETHOD_(ULONG, Release)();
// IWICBitmapSource
IFACEMETHOD(GetSize)(UINT* puiWidth, UINT* puiHeight);
IFACEMETHOD(GetPixelFormat)(WICPixelFormatGUID* pPixelFormat);
IFACEMETHOD(GetResolution)(double* pDpiX, double* pDpiY);
IFACEMETHOD(CopyPalette)(IWICPalette* pIPalette);
IFACEMETHOD(CopyPixels)(const WICRect* prc, UINT cbStride, UINT cbBufferSize, BYTE* pbBuffer);
// IWICBitmap
IFACEMETHOD(Lock)(const WICRect* prcLock, DWORD flags, IWICBitmapLock** ppILock);
IFACEMETHOD(SetPalette)(IWICPalette* pIPalette);
IFACEMETHOD(SetResolution)(double dpiX, double dpiY);
private:
friend class WICBitmapLockDIB;
HBITMAP m_DIBSectionBuffer;
LPDWORD m_DIBSectionBufferPixels;
UINT m_W;
UINT m_H;
};
} // namespace Gfx
#endif

View File

@ -0,0 +1,91 @@
/*
Copyright (C) 2013 Birunthan Mohanathas
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "WICBitmapLockDIB.h"
#include <cassert>
#include "../../Library/Litestep.h"
namespace Gfx {
WICBitmapLockDIB::WICBitmapLockDIB(WICBitmapDIB* bitmap, const WICRect* lockRect) :
m_Bitmap(bitmap),
m_Rect(lockRect),
m_RefCount(1)
{
}
WICBitmapLockDIB::~WICBitmapLockDIB()
{
}
IFACEMETHODIMP WICBitmapLockDIB::QueryInterface(REFIID riid, void** ppvObject)
{
return E_NOTIMPL;
}
IFACEMETHODIMP_(ULONG) WICBitmapLockDIB::AddRef()
{
++m_RefCount;
return m_RefCount;
}
IFACEMETHODIMP_(ULONG) WICBitmapLockDIB::Release()
{
--m_RefCount;
if (m_RefCount == 0)
{
delete this;
return 0;
}
return m_RefCount;
}
IFACEMETHODIMP WICBitmapLockDIB::GetSize(UINT* puiWidth, UINT* puiHeight)
{
return m_Bitmap->GetSize(puiWidth, puiHeight);
}
IFACEMETHODIMP WICBitmapLockDIB::GetStride(UINT* pcbStride)
{
UINT width = 0;
m_Bitmap->GetSize(&width, nullptr);
if (pcbStride) *pcbStride = (width * 32 + 31) / 32 * 4;
return S_OK;
}
IFACEMETHODIMP WICBitmapLockDIB::GetDataPointer(UINT* pcbBufferSize, BYTE** ppbData)
{
UINT stride = 0;
GetStride(&stride);
if (pcbBufferSize) *pcbBufferSize = stride * m_Rect->Height;
if (ppbData) *ppbData = (BYTE*)&m_Bitmap->m_DIBSectionBufferPixels[m_Rect->Y * m_Rect->Width + m_Rect->X];
return S_OK;
}
IFACEMETHODIMP WICBitmapLockDIB::GetPixelFormat(WICPixelFormatGUID* pPixelFormat)
{
return m_Bitmap->GetPixelFormat(pPixelFormat);
}
} // namespace Gfx

View File

@ -0,0 +1,59 @@
/*
Copyright (C) 2013 Birunthan Mohanathas
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef RM_GFX_WICBITMAPLOCKDIB_H_
#define RM_GFX_WICBITMAPLOCKDIB_H_
#include <Windows.h>
#include <GdiPlus.h>
#include <wincodec.h>
#include "WICBitmapDIB.h"
namespace Gfx {
// Implements the IWICBitmapLock interface for use with WICBitmapDIB. It is assumed that this
// class is used only with 32bpp PARGB bitmaps and using a sigle thread.
class WICBitmapLockDIB : public IWICBitmapLock
{
public:
WICBitmapLockDIB(WICBitmapDIB* bitmap, const WICRect* lockRect);
virtual ~WICBitmapLockDIB();
void Resize(UINT w, UINT h);
// IUnknown
IFACEMETHOD(QueryInterface)(REFIID riid, void** ppvObject);
IFACEMETHOD_(ULONG, AddRef)();
IFACEMETHOD_(ULONG, Release)();
// IWICBitmapLock
IFACEMETHOD(GetSize)(UINT* puiWidth, UINT* puiHeight);
IFACEMETHOD(GetStride)(UINT* pcbStride);
IFACEMETHOD(GetDataPointer)(UINT* pcbBufferSize, BYTE** ppbData);
IFACEMETHOD(GetPixelFormat)(WICPixelFormatGUID* pPixelFormat);
private:
WICBitmapDIB* m_Bitmap;
const WICRect* m_Rect;
UINT m_RefCount;
};
} // namespace Gfx
#endif

View File

@ -71,6 +71,8 @@
<ClCompile Include="..\Common\Gfx\TextFormat.cpp" />
<ClCompile Include="..\Common\Gfx\TextFormatD2D.cpp" />
<ClCompile Include="..\Common\Gfx\TextFormatGDIP.cpp" />
<ClCompile Include="..\Common\Gfx\WICBitmapDIB.cpp" />
<ClCompile Include="..\Common\Gfx\WICBitmapLockDIB.cpp" />
<ClCompile Include="..\Common\Gfx\WICBitmapLockGDIP.cpp" />
<ClCompile Include="..\Common\MenuTemplate.cpp" />
<ClCompile Include="..\Common\Platform.cpp" />
@ -288,6 +290,8 @@
<ClInclude Include="..\Common\Gfx\TextFormat.h" />
<ClInclude Include="..\Common\Gfx\TextFormatD2D.h" />
<ClInclude Include="..\Common\Gfx\TextFormatGDIP.h" />
<ClInclude Include="..\Common\Gfx\WICBitmapDIB.h" />
<ClInclude Include="..\Common\Gfx\WICBitmapLockDIB.h" />
<ClInclude Include="..\Common\Gfx\WICBitmapLockGDIP.h" />
<ClInclude Include="..\Common\MenuTemplate.h" />
<ClInclude Include="..\Common\Platform.h" />

View File

@ -363,6 +363,12 @@
<ClCompile Include="..\Common\Gfx\CanvasD2D.cpp">
<Filter>Common\Gfx</Filter>
</ClCompile>
<ClCompile Include="..\Common\Gfx\WICBitmapDIB.cpp">
<Filter>Common\Gfx</Filter>
</ClCompile>
<ClCompile Include="..\Common\Gfx\WICBitmapLockDIB.cpp">
<Filter>Common\Gfx</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ConfigParser.h">
@ -629,6 +635,12 @@
<ClInclude Include="..\Common\Gfx\CanvasD2D.h">
<Filter>Common\Gfx</Filter>
</ClInclude>
<ClInclude Include="..\Common\Gfx\WICBitmapDIB.h">
<Filter>Common\Gfx</Filter>
</ClInclude>
<ClInclude Include="..\Common\Gfx\WICBitmapLockDIB.h">
<Filter>Common\Gfx</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Library.rc">