// 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" #include "../../base/Logger.h" #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") namespace kiwano { struct D2DDeviceResources : public ID2DDeviceResources { public: D2DDeviceResources(); virtual ~D2DDeviceResources(); HRESULT CreateDeviceIndependentResources(); public: HRESULT CreateBitmapConverter( _Out_ ComPtr& converter, _In_opt_ ComPtr source, _In_ REFWICPixelFormatGUID format, WICBitmapDitherType dither, _In_opt_ ComPtr palette, double alpha_threshold_percent, WICBitmapPaletteType palette_translate ) override; HRESULT CreateBitmapFromConverter( _Out_ ComPtr& bitmap, _In_opt_ const D2D1_BITMAP_PROPERTIES* properties, _In_ ComPtr converter ) override; HRESULT CreateBitmapDecoderFromFile( _Out_ ComPtr& decoder, const String& file_path ) override; HRESULT CreateBitmapDecoderFromResource( _Out_ ComPtr& decoder, const Resource& resource ) override; HRESULT CreateTextFormat( _Out_ ComPtr& text_format, _In_ Font const& font ) const override; HRESULT CreateTextLayout( _Out_ ComPtr& text_layout, _In_ String const& text, _In_ ComPtr const& text_format ) const override; HRESULT SetD2DDevice( _In_ ComPtr const& device ) override; void SetTargetBitmap( _In_ ComPtr const& target ) override; void DiscardResources() override; public: unsigned long STDMETHODCALLTYPE AddRef(); unsigned long STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( IID const& riid, void** ppvObject ); protected: unsigned long ref_count_; Float32 dpi_; }; 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(); if (*device_resources) { (*device_resources)->Release(); } (*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; } STDMETHODIMP D2DDeviceResources::QueryInterface(IID const& riid, void** object) { 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() { factory_.reset(); device_.reset(); device_context_.reset(); 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 config; ZeroMemory(&config, sizeof(D2D1_FACTORY_OPTIONS)); #if defined(KGE_DEBUG) && defined(KGE_ENABLE_DX_DEBUG) config.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; #endif hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), &config, reinterpret_cast(&d2d_factory) ); if (SUCCEEDED(hr)) { 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 = factory_->CreateStrokeStyle( stroke_style, nullptr, 0, &d2d_miter_stroke_style ); if (SUCCEEDED(hr)) { stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL; hr = factory_->CreateStrokeStyle( stroke_style, nullptr, 0, &d2d_bevel_stroke_style ); } if (SUCCEEDED(hr)) { stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND; 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 const& device) { ComPtr d2d_device_ctx; HRESULT hr = device->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d_device_ctx ); if (SUCCEEDED(hr)) { device_ = device; device_context_ = d2d_device_ctx; device_context_->SetDpi(dpi_, dpi_); } return hr; } void D2DDeviceResources::SetTargetBitmap(_In_ ComPtr const& target) { target_bitmap_ = target; if (device_context_) device_context_->SetTarget(target_bitmap_.get()); } HRESULT D2DDeviceResources::CreateBitmapConverter(_Out_ ComPtr& converter, _In_opt_ ComPtr source, _In_ REFWICPixelFormatGUID format, WICBitmapDitherType dither, _In_opt_ ComPtr palette, double alpha_threshold_percent, WICBitmapPaletteType palette_translate ) { if (!imaging_factory_) return E_UNEXPECTED; ComPtr output; HRESULT hr = imaging_factory_->CreateFormatConverter(&output); if (SUCCEEDED(hr)) { hr = output->Initialize( source.get(), format, dither, palette.get(), alpha_threshold_percent, palette_translate ); } if (SUCCEEDED(hr)) { converter = output; } return hr; } HRESULT D2DDeviceResources::CreateBitmapFromConverter(_Out_ ComPtr& bitmap, _In_opt_ const D2D1_BITMAP_PROPERTIES* properties, _In_ ComPtr converter) { if (!device_context_) return E_UNEXPECTED; ComPtr output; HRESULT hr = device_context_->CreateBitmapFromWicBitmap( converter.get(), properties, &output ); if (SUCCEEDED(hr)) { bitmap = output; } return hr; } HRESULT D2DDeviceResources::CreateBitmapDecoderFromFile(_Out_ ComPtr& decoder, const String& file_path) { if (!imaging_factory_) return E_UNEXPECTED; ComPtr decoder_output; HRESULT hr = imaging_factory_->CreateDecoderFromFilename( file_path.c_str(), nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder_output ); if (SUCCEEDED(hr)) { decoder = decoder_output; } return hr; } HRESULT D2DDeviceResources::CreateBitmapDecoderFromResource(_Out_ ComPtr& decoder, const Resource& resource) { if (!imaging_factory_) return E_UNEXPECTED; Resource::Data res_data = resource.GetData(); HRESULT hr = res_data ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { ComPtr stream; hr = imaging_factory_->CreateStream(&stream); if (SUCCEEDED(hr)) { hr = stream->InitializeFromMemory( static_cast(res_data.buffer), res_data.size ); } if (SUCCEEDED(hr)) { ComPtr decoder_output; hr = imaging_factory_->CreateDecoderFromStream( stream.get(), nullptr, WICDecodeMetadataCacheOnLoad, &decoder_output ); if (SUCCEEDED(hr)) { decoder = decoder_output; } } } return hr; } HRESULT D2DDeviceResources::CreateTextFormat(_Out_ ComPtr & text_format, _In_ Font const & font) const { if (!dwrite_factory_) return E_UNEXPECTED; ComPtr output; HRESULT hr = dwrite_factory_->CreateTextFormat( font.family.c_str(), 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, L"", &output ); if (SUCCEEDED(hr)) { text_format = output; } return hr; } HRESULT D2DDeviceResources::CreateTextLayout(_Out_ ComPtr& text_layout, _In_ String const& text, _In_ ComPtr const& text_format) const { if (!dwrite_factory_) return E_UNEXPECTED; ComPtr output; HRESULT hr = dwrite_factory_->CreateTextLayout( text.c_str(), static_cast(text.length()), text_format.get(), 0, 0, &output ); if (SUCCEEDED(hr)) { text_layout = output; } return hr; } }