Magic_Game/src/kiwano/renderer/win32/D2DDeviceResources.cpp

576 lines
12 KiB
C++
Raw Normal View History

2019-04-11 14:40:54 +08:00
// Copyright (c) 2016-2018 Kiwano - Nomango
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "D2DDeviceResources.h"
2019-08-18 10:23:54 +08:00
#include "../../base/Logger.h"
#include "../../utils/FileUtil.h"
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
2019-04-11 14:40:54 +08:00
namespace kiwano
{
2019-07-30 00:41:06 +08:00
struct D2DDeviceResources
: public ID2DDeviceResources
{
public:
D2DDeviceResources();
virtual ~D2DDeviceResources();
HRESULT CreateDeviceIndependentResources();
public:
HRESULT CreateBitmapFromFile(
_Out_ ComPtr<ID2D1Bitmap>& bitmap,
_In_ String const& file_path
) override;
HRESULT CreateBitmapFromResource(
_Out_ ComPtr<ID2D1Bitmap>& bitmap,
_In_ Resource const& res
) override;
HRESULT CreateTextFormat(
_Out_ ComPtr<IDWriteTextFormat>& text_format,
2019-08-15 11:22:51 +08:00
_In_ Font const& font
2019-07-30 00:41:06 +08:00
) const override;
HRESULT CreateTextLayout(
_Out_ ComPtr<IDWriteTextLayout>& text_layout,
_In_ String const& text,
2019-08-15 11:22:51 +08:00
_In_ TextStyle const& text_style,
_In_ ComPtr<IDWriteTextFormat> const& text_format
2019-07-30 00:41:06 +08:00
) const override;
HRESULT SetD2DDevice(
_In_ ComPtr<ID2D1Device> const& device
) override;
void SetTargetBitmap(
_In_ ComPtr<ID2D1Bitmap1> const& target
) override;
void DiscardResources() override;
2019-07-30 00:41:06 +08:00
public:
unsigned long STDMETHODCALLTYPE AddRef();
unsigned long STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE QueryInterface(
IID const& riid,
void** ppvObject
);
protected:
unsigned long ref_count_;
2019-08-18 22:49:44 +08:00
Float32 dpi_;
2019-07-30 00:41:06 +08:00
};
HRESULT ID2DDeviceResources::Create(ID2DDeviceResources** device_resources)
{
HRESULT hr = E_FAIL;
if (device_resources)
{
D2DDeviceResources* res = new (std::nothrow) D2DDeviceResources;
if (res)
{
hr = res->CreateDeviceIndependentResources();
}
if (SUCCEEDED(hr))
{
res->AddRef();
2019-08-18 10:23:54 +08:00
if (*device_resources)
{
(*device_resources)->Release();
}
2019-07-30 00:41:06 +08:00
(*device_resources) = res;
}
else
{
delete res;
res = nullptr;
}
}
return hr;
}
D2DDeviceResources::D2DDeviceResources()
: ref_count_(0)
, dpi_(96.f)
{
}
D2DDeviceResources::~D2DDeviceResources()
{
DiscardResources();
}
STDMETHODIMP_(unsigned long) D2DDeviceResources::AddRef()
{
return InterlockedIncrement(&ref_count_);
}
STDMETHODIMP_(unsigned long) D2DDeviceResources::Release()
{
unsigned long newCount = InterlockedDecrement(&ref_count_);
if (newCount == 0)
{
delete this;
return 0;
}
return newCount;
}
2019-07-30 00:41:06 +08:00
STDMETHODIMP D2DDeviceResources::QueryInterface(IID const& riid, void** object)
{
2019-07-30 00:41:06 +08:00
if (__uuidof(ID2DDeviceResources) == riid)
{
*object = this;
}
else if (__uuidof(IUnknown) == riid)
{
*object = this;
}
else
{
*object = nullptr;
return E_FAIL;
}
AddRef();
return S_OK;
}
void D2DDeviceResources::DiscardResources()
{
2019-08-13 21:16:38 +08:00
factory_.reset();
device_.reset();
device_context_.reset();
target_bitmap_.reset();
2019-08-13 21:16:38 +08:00
imaging_factory_.reset();
dwrite_factory_.reset();
2019-08-13 21:16:38 +08:00
d2d_miter_stroke_style_.reset();
d2d_bevel_stroke_style_.reset();
d2d_round_stroke_style_.reset();
}
HRESULT D2DDeviceResources::CreateDeviceIndependentResources()
{
HRESULT hr = S_OK;
2019-08-18 10:23:54 +08:00
ComPtr<ID2D1Factory1> d2d_factory;
ComPtr<IWICImagingFactory> imaging_factory;
ComPtr<IDWriteFactory> dwrite_factory;
D2D1_FACTORY_OPTIONS options;
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
2019-08-21 16:54:51 +08:00
#if defined(KGE_DEBUG) && defined(KGE_ENABLE_DX_DEBUG)
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory1),
&options,
reinterpret_cast<void**>(&d2d_factory)
);
if (SUCCEEDED(hr))
{
2019-07-30 00:41:06 +08:00
factory_ = d2d_factory;
hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
reinterpret_cast<void**>(&imaging_factory)
);
}
if (SUCCEEDED(hr))
{
imaging_factory_ = imaging_factory;
hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&dwrite_factory)
);
}
if (SUCCEEDED(hr))
{
dwrite_factory_ = dwrite_factory;
2019-08-18 10:23:54 +08:00
ComPtr<ID2D1StrokeStyle> d2d_miter_stroke_style;
ComPtr<ID2D1StrokeStyle> d2d_bevel_stroke_style;
ComPtr<ID2D1StrokeStyle> d2d_round_stroke_style;
D2D1_STROKE_STYLE_PROPERTIES stroke_style = D2D1::StrokeStyleProperties(
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_FLAT,
D2D1_LINE_JOIN_MITER,
2.0f,
D2D1_DASH_STYLE_SOLID,
0.0f
);
2019-07-30 00:41:06 +08:00
hr = factory_->CreateStrokeStyle(
stroke_style,
nullptr,
0,
&d2d_miter_stroke_style
);
if (SUCCEEDED(hr))
{
stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL;
2019-07-30 00:41:06 +08:00
hr = factory_->CreateStrokeStyle(
stroke_style,
nullptr,
0,
&d2d_bevel_stroke_style
);
}
if (SUCCEEDED(hr))
{
stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND;
2019-07-30 00:41:06 +08:00
hr = factory_->CreateStrokeStyle(
stroke_style,
nullptr,
0,
&d2d_round_stroke_style
);
}
if (SUCCEEDED(hr))
{
d2d_miter_stroke_style_ = d2d_miter_stroke_style;
d2d_bevel_stroke_style_ = d2d_bevel_stroke_style;
d2d_round_stroke_style_ = d2d_round_stroke_style;
}
}
return hr;
}
HRESULT D2DDeviceResources::SetD2DDevice(_In_ ComPtr<ID2D1Device> const& device)
{
ComPtr<ID2D1DeviceContext> d2d_device_ctx;
HRESULT hr = device->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&d2d_device_ctx
);
if (SUCCEEDED(hr))
{
2019-07-30 00:41:06 +08:00
device_ = device;
device_context_ = d2d_device_ctx;
device_context_->SetDpi(dpi_, dpi_);
}
return hr;
}
void D2DDeviceResources::SetTargetBitmap(_In_ ComPtr<ID2D1Bitmap1> const& target)
{
2019-07-30 00:41:06 +08:00
target_bitmap_ = target;
if (device_context_)
2019-08-13 21:16:38 +08:00
device_context_->SetTarget(target_bitmap_.get());
}
HRESULT D2DDeviceResources::CreateBitmapFromFile(_Out_ ComPtr<ID2D1Bitmap> & bitmap, _In_ String const & file_path)
{
2019-07-30 00:41:06 +08:00
if (!imaging_factory_ || !device_context_)
return E_UNEXPECTED;
if (!FileUtil::ExistsFile(file_path))
{
2019-08-21 16:33:41 +08:00
KGE_WARNING_LOG(L"Texture file '%s' not found!", file_path.c_str());
return E_FAIL;
}
ComPtr<IWICBitmapDecoder> decoder;
ComPtr<IWICBitmapFrameDecode> source;
ComPtr<IWICStream> stream;
ComPtr<IWICFormatConverter> converter;
ComPtr<ID2D1Bitmap> bitmap_tmp;
HRESULT hr = imaging_factory_->CreateDecoderFromFilename(
file_path.c_str(),
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&decoder
);
if (SUCCEEDED(hr))
{
hr = decoder->GetFrame(0, &source);
}
if (SUCCEEDED(hr))
{
hr = imaging_factory_->CreateFormatConverter(&converter);
}
if (SUCCEEDED(hr))
{
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
hr = converter->Initialize(
2019-08-13 21:16:38 +08:00
source.get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
if (SUCCEEDED(hr))
{
2019-07-30 00:41:06 +08:00
hr = device_context_->CreateBitmapFromWicBitmap(
2019-08-13 21:16:38 +08:00
converter.get(),
nullptr,
&bitmap_tmp
);
}
if (SUCCEEDED(hr))
{
bitmap = bitmap_tmp;
}
return hr;
}
HRESULT D2DDeviceResources::CreateBitmapFromResource(_Out_ ComPtr<ID2D1Bitmap> & bitmap, _In_ Resource const & res)
{
2019-07-30 00:41:06 +08:00
if (!imaging_factory_ || !device_context_)
return E_UNEXPECTED;
ComPtr<IWICBitmapDecoder> decoder;
ComPtr<IWICBitmapFrameDecode> source;
ComPtr<IWICStream> stream;
ComPtr<IWICFormatConverter> converter;
ComPtr<ID2D1Bitmap> bitmap_tmp;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
2019-08-18 17:49:13 +08:00
Resource::Data res_data = res.GetData();
HRESULT hr = res_data ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
hr = imaging_factory_->CreateStream(&stream);
}
if (SUCCEEDED(hr))
{
hr = stream->InitializeFromMemory(
2019-08-18 17:49:13 +08:00
static_cast<WICInProcPointer>(res_data.buffer),
res_data.size
);
}
if (SUCCEEDED(hr))
{
hr = imaging_factory_->CreateDecoderFromStream(
2019-08-13 21:16:38 +08:00
stream.get(),
nullptr,
WICDecodeMetadataCacheOnLoad,
&decoder
);
}
if (SUCCEEDED(hr))
{
hr = decoder->GetFrame(0, &source);
}
if (SUCCEEDED(hr))
{
hr = imaging_factory_->CreateFormatConverter(&converter);
}
if (SUCCEEDED(hr))
{
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
hr = converter->Initialize(
2019-08-13 21:16:38 +08:00
source.get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
if (SUCCEEDED(hr))
{
2019-07-30 00:41:06 +08:00
hr = device_context_->CreateBitmapFromWicBitmap(
2019-08-13 21:16:38 +08:00
converter.get(),
nullptr,
&bitmap_tmp
);
}
if (SUCCEEDED(hr))
{
bitmap = bitmap_tmp;
}
return hr;
}
2019-08-15 11:22:51 +08:00
HRESULT D2DDeviceResources::CreateTextFormat(_Out_ ComPtr<IDWriteTextFormat> & text_format, _In_ Font const & font) const
{
if (!dwrite_factory_)
return E_UNEXPECTED;
2019-08-15 11:22:51 +08:00
ComPtr<IDWriteTextFormat> output;
HRESULT hr = dwrite_factory_->CreateTextFormat(
font.family.c_str(),
2019-08-18 10:23:54 +08:00
font.collection.GetFontCollection().get(),
DWRITE_FONT_WEIGHT(font.weight),
font.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
font.size,
2019-08-21 12:47:19 +08:00
L"",
2019-08-15 11:22:51 +08:00
&output
);
if (SUCCEEDED(hr))
{
2019-08-15 11:22:51 +08:00
text_format = output;
}
return hr;
}
2019-08-15 11:22:51 +08:00
HRESULT D2DDeviceResources::CreateTextLayout(_Out_ ComPtr<IDWriteTextLayout> & text_layout, _In_ String const & text,
_In_ TextStyle const & text_style, _In_ ComPtr<IDWriteTextFormat> const& text_format) const
{
if (!dwrite_factory_)
return E_UNEXPECTED;
HRESULT hr;
2019-08-15 11:22:51 +08:00
ComPtr<IDWriteTextLayout> output;
2019-08-18 22:49:44 +08:00
UInt32 length = static_cast<UInt32>(text.length());
2019-08-15 11:22:51 +08:00
if (text_style.wrap_width > 0)
{
hr = dwrite_factory_->CreateTextLayout(
text.c_str(),
length,
2019-08-13 21:16:38 +08:00
text_format.get(),
text_style.wrap_width,
0,
2019-08-15 11:22:51 +08:00
&output
);
}
else
{
hr = dwrite_factory_->CreateTextLayout(
text.c_str(),
length,
2019-08-13 21:16:38 +08:00
text_format.get(),
0,
0,
2019-08-15 11:22:51 +08:00
&output
);
2019-08-15 11:22:51 +08:00
}
2019-08-15 11:22:51 +08:00
if (SUCCEEDED(hr))
{
if (text_style.line_spacing == 0.f)
{
2019-08-15 11:22:51 +08:00
hr = output->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0);
}
2019-08-15 11:22:51 +08:00
else
{
2019-08-15 11:22:51 +08:00
hr = output->SetLineSpacing(
DWRITE_LINE_SPACING_METHOD_UNIFORM,
text_style.line_spacing,
text_style.line_spacing * 0.8f
);
}
}
if (SUCCEEDED(hr))
{
2019-08-15 11:22:51 +08:00
hr = output->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(text_style.alignment));
}
if (SUCCEEDED(hr))
{
hr = output->SetWordWrapping((text_style.wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP);
}
2019-08-15 11:22:51 +08:00
if (SUCCEEDED(hr))
{
if (text_style.underline)
{
2019-08-15 11:22:51 +08:00
hr = output->SetUnderline(true, { 0, length });
}
2019-08-15 11:22:51 +08:00
}
2019-08-15 11:22:51 +08:00
if (SUCCEEDED(hr))
{
if (text_style.strikethrough)
{
2019-08-15 11:22:51 +08:00
output->SetStrikethrough(true, { 0, length });
}
2019-08-15 11:22:51 +08:00
}
if (SUCCEEDED(hr))
{
// Fix the layout width when the text does not wrap
if (!(text_style.wrap_width > 0))
{
2019-08-15 11:22:51 +08:00
DWRITE_TEXT_METRICS metrics;
hr = output->GetMetrics(&metrics);
if (SUCCEEDED(hr))
{
hr = output->SetMaxWidth(metrics.width);
}
}
2019-08-15 11:22:51 +08:00
}
if (SUCCEEDED(hr))
{
text_layout = output;
}
return hr;
}
}