// 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. #include "D2DDeviceResources.h" #include "../2d/Image.h" #include "../base/logs.h" #include "../platform/modules.h" #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") namespace easy2d { 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) { if (__uuidof(IUnknown) == riid) { *object = this; } else { *object = nullptr; return E_FAIL; } AddRef(); return S_OK; } void D2DDeviceResources::DiscardResources() { 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(); } HRESULT D2DDeviceResources::CreateDeviceIndependentResources() { HRESULT hr = S_OK; ComPtr d2d_factory; ComPtr imaging_factory; ComPtr dwrite_factory; D2D1_FACTORY_OPTIONS options; ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS)); #ifdef E2D_DEBUG options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; #endif hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), &options, reinterpret_cast(&d2d_factory) ); if (SUCCEEDED(hr)) { d2d_factory_ = d2d_factory; hr = CoCreateInstance( CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), reinterpret_cast(&imaging_factory) ); } if (SUCCEEDED(hr)) { imaging_factory_ = imaging_factory; hr = DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&dwrite_factory) ); } if (SUCCEEDED(hr)) { dwrite_factory_ = dwrite_factory; ComPtr d2d_miter_stroke_style; ComPtr d2d_bevel_stroke_style; ComPtr 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 ); hr = d2d_factory_->CreateStrokeStyle( stroke_style, nullptr, 0, &d2d_miter_stroke_style ); if (SUCCEEDED(hr)) { stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL; hr = d2d_factory_->CreateStrokeStyle( stroke_style, nullptr, 0, &d2d_bevel_stroke_style ); } if (SUCCEEDED(hr)) { stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND; hr = d2d_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(ComPtr const& device) { ComPtr d2d_device_ctx; HRESULT hr = device->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_device_ctx ); if (SUCCEEDED(hr)) { d2d_device_ = device; d2d_device_context_ = d2d_device_ctx; d2d_device_context_->SetDpi(dpi_, dpi_); } return hr; } void D2DDeviceResources::SetTargetBitmap(ComPtr const& target) { d2d_target_bitmap_ = target; if (d2d_device_context_) d2d_device_context_->SetTarget(d2d_target_bitmap_.Get()); } HRESULT D2DDeviceResources::CreateBitmapFromFile(ComPtr & bitmap, String const & file_path) { if (!imaging_factory_ || !d2d_device_context_) return E_UNEXPECTED; size_t hash_code = std::hash{}(file_path); if (bitmap_cache_.find(hash_code) != bitmap_cache_.end()) { bitmap = bitmap_cache_[hash_code]; return S_OK; } ComPtr decoder; ComPtr source; ComPtr stream; ComPtr converter; ComPtr 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)) { // 图片格式转换成 32bppPBGRA hr = converter->Initialize( source.Get(), GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeMedianCut ); } if (SUCCEEDED(hr)) { hr = d2d_device_context_->CreateBitmapFromWicBitmap( converter.Get(), nullptr, &bitmap_tmp ); } if (SUCCEEDED(hr)) { bitmap = bitmap_tmp; bitmap_cache_.insert(std::make_pair(hash_code, bitmap)); } return hr; } HRESULT D2DDeviceResources::CreateBitmapFromResource(ComPtr & bitmap, Resource const & res) { if (!imaging_factory_ || !d2d_device_context_) return E_UNEXPECTED; size_t hash_code = res.GetHashCode(); if (bitmap_cache_.find(hash_code) != bitmap_cache_.end()) { bitmap = bitmap_cache_[hash_code]; return S_OK; } ComPtr decoder; ComPtr source; ComPtr stream; ComPtr converter; ComPtr bitmap_tmp; // 加载资源 LPVOID buffer; DWORD buffer_size; HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { hr = imaging_factory_->CreateStream(&stream); } if (SUCCEEDED(hr)) { hr = stream->InitializeFromMemory( static_cast(buffer), buffer_size ); } if (SUCCEEDED(hr)) { hr = imaging_factory_->CreateDecoderFromStream( stream.Get(), nullptr, WICDecodeMetadataCacheOnLoad, &decoder ); } if (SUCCEEDED(hr)) { hr = decoder->GetFrame(0, &source); } if (SUCCEEDED(hr)) { hr = imaging_factory_->CreateFormatConverter(&converter); } if (SUCCEEDED(hr)) { // 图片格式转换成 32bppPBGRA hr = converter->Initialize( source.Get(), GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeMedianCut ); } if (SUCCEEDED(hr)) { hr = d2d_device_context_->CreateBitmapFromWicBitmap( converter.Get(), nullptr, &bitmap_tmp ); } if (SUCCEEDED(hr)) { bitmap = bitmap_tmp; bitmap_cache_.insert(std::make_pair(hash_code, bitmap)); } return hr; } HRESULT D2DDeviceResources::CreateTextFormat(ComPtr & text_format, Font const & font, TextStyle const & text_style) const { if (!dwrite_factory_) return E_UNEXPECTED; ComPtr text_format_tmp; HRESULT hr = dwrite_factory_->CreateTextFormat( 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; } HRESULT D2DDeviceResources::CreateTextLayout(ComPtr & text_layout, Size& layout_size, String const & text, ComPtr const& text_format, TextStyle const & text_style) const { if (!dwrite_factory_) return E_UNEXPECTED; text_layout = nullptr; HRESULT hr; ComPtr text_layout_tmp; UINT32 length = static_cast(text.length()); if (text_style.wrap) { hr = dwrite_factory_->CreateTextLayout( text.c_str(), length, text_format.Get(), text_style.wrap_width, 0, &text_layout_tmp ); } else { hr = dwrite_factory_->CreateTextLayout( text.c_str(), length, text_format.Get(), 0, 0, &text_layout_tmp ); DWRITE_TEXT_METRICS metrics; if (SUCCEEDED(hr)) { hr = text_layout_tmp->GetMetrics(&metrics); } if (SUCCEEDED(hr)) { text_layout_tmp = nullptr; hr = dwrite_factory_->CreateTextLayout( 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; } void D2DDeviceResources::ClearImageCache() { bitmap_cache_.clear(); } ID2D1StrokeStyle* D2DDeviceResources::GetStrokeStyle(StrokeStyle stroke) const { switch (stroke) { 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; } return nullptr; } }