Magic_Game/easy2d/renderer/D2DDeviceResources.cpp

516 lines
11 KiB
C++
Raw Normal View History

2018-11-21 17:18:59 +08:00
// Copyright (c) 2016-2018 Easy2D - 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.
2019-03-10 19:44:28 +08:00
#include "D2DDeviceResources.h"
#include "../2d/Image.h"
#include "../base/logs.h"
#include "../platform/modules.h"
2018-11-21 17:18:59 +08:00
2019-03-10 19:44:28 +08:00
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dwrite.lib")
2018-11-21 17:18:59 +08:00
namespace easy2d
{
2019-03-10 13:44:02 +08:00
D2DDeviceResources::D2DDeviceResources()
: ref_count_(0)
, dpi_(96.f)
{
CreateDeviceIndependentResources();
}
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;
}
STDMETHODIMP D2DDeviceResources::QueryInterface(
IID const& riid,
void** object)
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
if (__uuidof(IUnknown) == riid)
{
*object = this;
}
else
{
*object = nullptr;
return E_FAIL;
}
AddRef();
return S_OK;
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
void D2DDeviceResources::DiscardResources()
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
ClearImageCache();
d2d_factory_.Reset();
d2d_device_.Reset();
d2d_device_context_.Reset();
d2d_target_bitmap_.Reset();
imaging_factory_.Reset();
dwrite_factory_.Reset();
d2d_miter_stroke_style_.Reset();
d2d_bevel_stroke_style_.Reset();
d2d_round_stroke_style_.Reset();
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::CreateDeviceIndependentResources()
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
HRESULT hr = S_OK;
ComPtr<ID2D1Factory1> d2d_factory;
ComPtr<IWICImagingFactory> imaging_factory;
ComPtr<IDWriteFactory> dwrite_factory;
2018-11-21 17:18:59 +08:00
2019-01-25 14:48:37 +08:00
D2D1_FACTORY_OPTIONS options;
2019-03-10 13:44:02 +08:00
ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
#ifdef E2D_DEBUG
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
2019-03-10 19:44:28 +08:00
hr = D2D1CreateFactory(
2018-11-21 19:24:18 +08:00
D2D1_FACTORY_TYPE_SINGLE_THREADED,
2019-03-10 13:44:02 +08:00
__uuidof(ID2D1Factory1),
2019-01-25 14:48:37 +08:00
&options,
2019-03-10 13:44:02 +08:00
reinterpret_cast<void**>(&d2d_factory)
2018-11-21 17:18:59 +08:00
);
2018-11-21 19:24:18 +08:00
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
d2d_factory_ = d2d_factory;
2019-03-10 19:44:28 +08:00
hr = CoCreateInstance(
2018-11-21 17:18:59 +08:00
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
2019-03-10 13:44:02 +08:00
reinterpret_cast<void**>(&imaging_factory)
2018-11-21 19:24:18 +08:00
);
}
2018-11-21 17:18:59 +08:00
2018-11-21 19:24:18 +08:00
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
imaging_factory_ = imaging_factory;
2019-03-10 19:44:28 +08:00
hr = DWriteCreateFactory(
2018-11-21 17:18:59 +08:00
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
2019-03-10 13:44:02 +08:00
reinterpret_cast<IUnknown**>(&dwrite_factory)
2018-11-21 19:24:18 +08:00
);
}
2018-11-21 17:18:59 +08:00
2018-11-21 19:24:18 +08:00
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
dwrite_factory_ = dwrite_factory;
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(
2018-11-21 19:24:18 +08:00
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_FLAT,
D2D1_CAP_STYLE_FLAT,
D2D1_LINE_JOIN_MITER,
2.0f,
D2D1_DASH_STYLE_SOLID,
0.0f
);
2018-11-21 17:18:59 +08:00
2019-03-10 13:44:02 +08:00
hr = d2d_factory_->CreateStrokeStyle(
2018-11-21 17:18:59 +08:00
stroke_style,
nullptr,
0,
2019-03-10 13:44:02 +08:00
&d2d_miter_stroke_style
2018-11-21 19:24:18 +08:00
);
2018-11-21 17:18:59 +08:00
2018-11-21 19:24:18 +08:00
if (SUCCEEDED(hr))
{
stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL;
2019-03-10 13:44:02 +08:00
hr = d2d_factory_->CreateStrokeStyle(
2018-11-21 19:24:18 +08:00
stroke_style,
nullptr,
0,
2019-03-10 13:44:02 +08:00
&d2d_bevel_stroke_style
2018-11-21 19:24:18 +08:00
);
}
2018-11-21 17:18:59 +08:00
2018-11-21 19:24:18 +08:00
if (SUCCEEDED(hr))
{
stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND;
2019-03-10 13:44:02 +08:00
hr = d2d_factory_->CreateStrokeStyle(
2018-11-21 19:24:18 +08:00
stroke_style,
nullptr,
0,
2019-03-10 13:44:02 +08:00
&d2d_round_stroke_style
2018-11-21 19:24:18 +08:00
);
}
2018-11-21 17:18:59 +08:00
2019-03-10 13:44:02 +08:00
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;
}
}
2019-03-10 13:44:02 +08:00
return hr;
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::SetD2DDevice(ComPtr<ID2D1Device> const& device)
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
ComPtr<ID2D1DeviceContext> d2d_device_ctx;
2018-11-21 17:18:59 +08:00
2019-03-10 13:44:02 +08:00
HRESULT hr = device->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&d2d_device_ctx
2018-11-21 17:18:59 +08:00
);
if (SUCCEEDED(hr))
2019-03-10 13:44:02 +08:00
{
d2d_device_ = device;
d2d_device_context_ = d2d_device_ctx;
d2d_device_context_->SetDpi(dpi_, dpi_);
}
2018-11-21 17:18:59 +08:00
return hr;
}
2019-03-10 13:44:02 +08:00
void D2DDeviceResources::SetTargetBitmap(ComPtr<ID2D1Bitmap1> const& target)
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
d2d_target_bitmap_ = target;
if (d2d_device_context_)
d2d_device_context_->SetTarget(d2d_target_bitmap_.Get());
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::CreateBitmapFromFile(ComPtr<ID2D1Bitmap> & bitmap, String const & file_path)
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
if (!imaging_factory_ || !d2d_device_context_)
2018-11-21 17:18:59 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
size_t hash_code = std::hash<String>{}(file_path);
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
{
bitmap = bitmap_cache_[hash_code];
return S_OK;
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
ComPtr<IWICBitmapDecoder> decoder;
ComPtr<IWICBitmapFrameDecode> source;
ComPtr<IWICStream> stream;
ComPtr<IWICFormatConverter> converter;
ComPtr<ID2D1Bitmap> bitmap_tmp;
2018-11-21 17:18:59 +08:00
2018-11-21 19:24:18 +08:00
HRESULT hr = imaging_factory_->CreateDecoderFromFilename(
2018-11-21 17:18:59 +08:00
file_path.c_str(),
nullptr,
GENERIC_READ,
WICDecodeMetadataCacheOnLoad,
&decoder
);
if (SUCCEEDED(hr))
{
hr = decoder->GetFrame(0, &source);
}
if (SUCCEEDED(hr))
{
2018-11-21 19:24:18 +08:00
hr = imaging_factory_->CreateFormatConverter(&converter);
2018-11-21 17:18:59 +08:00
}
if (SUCCEEDED(hr))
{
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
hr = converter->Initialize(
source.Get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
hr = d2d_device_context_->CreateBitmapFromWicBitmap(
2018-11-21 17:18:59 +08:00
converter.Get(),
nullptr,
&bitmap_tmp
);
}
if (SUCCEEDED(hr))
2019-03-10 13:44:02 +08:00
{
2018-11-21 17:18:59 +08:00
bitmap = bitmap_tmp;
2019-03-10 13:44:02 +08:00
bitmap_cache_.insert(std::make_pair(hash_code, bitmap));
}
2018-11-21 17:18:59 +08:00
return hr;
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::CreateBitmapFromResource(ComPtr<ID2D1Bitmap> & bitmap, Resource const & res)
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
if (!imaging_factory_ || !d2d_device_context_)
2018-11-21 17:18:59 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
size_t hash_code = res.GetHashCode();
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
{
bitmap = bitmap_cache_[hash_code];
return S_OK;
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
ComPtr<IWICBitmapDecoder> decoder;
ComPtr<IWICBitmapFrameDecode> source;
ComPtr<IWICStream> stream;
ComPtr<IWICFormatConverter> converter;
ComPtr<ID2D1Bitmap> bitmap_tmp;
2018-11-21 17:18:59 +08:00
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
2019-01-21 21:24:45 +08:00
LPVOID buffer;
DWORD buffer_size;
HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL;
2018-11-21 17:18:59 +08:00
if (SUCCEEDED(hr))
{
2018-11-21 19:24:18 +08:00
hr = imaging_factory_->CreateStream(&stream);
2018-11-21 17:18:59 +08:00
}
if (SUCCEEDED(hr))
{
hr = stream->InitializeFromMemory(
2019-01-21 21:24:45 +08:00
static_cast<WICInProcPointer>(buffer),
buffer_size
2018-11-21 17:18:59 +08:00
);
}
if (SUCCEEDED(hr))
{
2018-11-21 19:24:18 +08:00
hr = imaging_factory_->CreateDecoderFromStream(
2018-11-21 17:18:59 +08:00
stream.Get(),
nullptr,
WICDecodeMetadataCacheOnLoad,
&decoder
);
}
if (SUCCEEDED(hr))
{
hr = decoder->GetFrame(0, &source);
}
if (SUCCEEDED(hr))
{
2018-11-21 19:24:18 +08:00
hr = imaging_factory_->CreateFormatConverter(&converter);
2018-11-21 17:18:59 +08:00
}
if (SUCCEEDED(hr))
{
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
hr = converter->Initialize(
source.Get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.f,
WICBitmapPaletteTypeMedianCut
);
}
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
hr = d2d_device_context_->CreateBitmapFromWicBitmap(
2018-11-21 17:18:59 +08:00
converter.Get(),
nullptr,
&bitmap_tmp
);
}
if (SUCCEEDED(hr))
{
bitmap = bitmap_tmp;
2019-03-10 13:44:02 +08:00
bitmap_cache_.insert(std::make_pair(hash_code, bitmap));
2018-11-21 17:18:59 +08:00
}
return hr;
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::CreateTextFormat(ComPtr<IDWriteTextFormat> & text_format, Font const & font, TextStyle const & text_style) const
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
if (!dwrite_factory_)
2018-11-21 17:18:59 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
ComPtr<IDWriteTextFormat> text_format_tmp;
HRESULT hr = dwrite_factory_->CreateTextFormat(
2018-11-21 17:18:59 +08:00
font.family.c_str(),
nullptr,
DWRITE_FONT_WEIGHT(font.weight),
font.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
font.size,
L"",
&text_format_tmp
);
if (SUCCEEDED(hr))
{
if (text_style.line_spacing == 0.f)
{
text_format_tmp->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0);
}
else
{
text_format_tmp->SetLineSpacing(
DWRITE_LINE_SPACING_METHOD_UNIFORM,
text_style.line_spacing,
text_style.line_spacing * 0.8f
);
}
text_format_tmp->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(text_style.alignment));
text_format_tmp->SetWordWrapping(text_style.wrap ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP);
text_format = text_format_tmp;
}
return hr;
}
2019-03-10 13:44:02 +08:00
HRESULT D2DDeviceResources::CreateTextLayout(ComPtr<IDWriteTextLayout> & text_layout, Size& layout_size, String const & text, ComPtr<IDWriteTextFormat> const& text_format, TextStyle const & text_style) const
2018-11-21 17:18:59 +08:00
{
2019-03-10 13:44:02 +08:00
if (!dwrite_factory_)
2018-11-21 17:18:59 +08:00
return E_UNEXPECTED;
text_layout = nullptr;
HRESULT hr;
2019-03-10 13:44:02 +08:00
ComPtr<IDWriteTextLayout> text_layout_tmp;
2018-11-21 17:18:59 +08:00
UINT32 length = static_cast<UINT32>(text.length());
if (text_style.wrap)
{
2019-03-10 13:44:02 +08:00
hr = dwrite_factory_->CreateTextLayout(
2018-11-21 17:18:59 +08:00
text.c_str(),
length,
text_format.Get(),
text_style.wrap_width,
0,
&text_layout_tmp
);
}
else
{
2019-03-10 13:44:02 +08:00
hr = dwrite_factory_->CreateTextLayout(
2018-11-21 17:18:59 +08:00
text.c_str(),
length,
text_format.Get(),
0,
0,
&text_layout_tmp
);
DWRITE_TEXT_METRICS metrics;
2019-02-03 22:38:39 +08:00
if (SUCCEEDED(hr))
{
hr = text_layout_tmp->GetMetrics(&metrics);
}
2018-11-21 17:18:59 +08:00
if (SUCCEEDED(hr))
{
text_layout_tmp = nullptr;
2019-03-10 13:44:02 +08:00
hr = dwrite_factory_->CreateTextLayout(
2018-11-21 17:18:59 +08:00
text.c_str(),
length,
text_format.Get(),
metrics.width,
0,
&text_layout_tmp
);
}
}
if (SUCCEEDED(hr))
{
DWRITE_TEXT_METRICS metrics;
text_layout_tmp->GetMetrics(&metrics);
if (text_style.wrap)
{
layout_size = Size(metrics.layoutWidth, metrics.height);
}
else
{
layout_size = Size(metrics.width, metrics.height);
}
DWRITE_TEXT_RANGE range = { 0, length };
if (text_style.underline)
{
text_layout_tmp->SetUnderline(true, range);
}
if (text_style.strikethrough)
{
text_layout_tmp->SetStrikethrough(true, range);
}
text_layout = text_layout_tmp;
}
return hr;
}
2019-03-10 13:44:02 +08:00
void D2DDeviceResources::ClearImageCache()
{
bitmap_cache_.clear();
}
ID2D1StrokeStyle* D2DDeviceResources::GetStrokeStyle(StrokeStyle stroke) const
2018-11-21 17:18:59 +08:00
{
switch (stroke)
{
2019-03-10 13:44:02 +08:00
case StrokeStyle::Miter: return d2d_miter_stroke_style_.Get(); break;
case StrokeStyle::Bevel: return d2d_bevel_stroke_style_.Get(); break;
case StrokeStyle::Round: return d2d_round_stroke_style_.Get(); break;
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
return nullptr;
2018-11-21 17:18:59 +08:00
}
2019-03-10 13:44:02 +08:00
}