516 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			516 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
// 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<ID2D1Factory1>			d2d_factory;
 | 
						||
		ComPtr<IWICImagingFactory>		imaging_factory;
 | 
						||
		ComPtr<IDWriteFactory>			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<void**>(&d2d_factory)
 | 
						||
		);
 | 
						||
 | 
						||
		if (SUCCEEDED(hr))
 | 
						||
		{
 | 
						||
			d2d_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;
 | 
						||
 | 
						||
			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
 | 
						||
			);
 | 
						||
 | 
						||
			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<ID2D1Device> const& device)
 | 
						||
	{
 | 
						||
		ComPtr<ID2D1DeviceContext> 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<ID2D1Bitmap1> const& target)
 | 
						||
	{
 | 
						||
		d2d_target_bitmap_ = target;
 | 
						||
		if (d2d_device_context_)
 | 
						||
			d2d_device_context_->SetTarget(d2d_target_bitmap_.Get());
 | 
						||
	}
 | 
						||
 | 
						||
	HRESULT D2DDeviceResources::CreateBitmapFromFile(ComPtr<ID2D1Bitmap> & bitmap, String const & file_path)
 | 
						||
	{
 | 
						||
		if (!imaging_factory_ || !d2d_device_context_)
 | 
						||
			return E_UNEXPECTED;
 | 
						||
 | 
						||
		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;
 | 
						||
		}
 | 
						||
 | 
						||
		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(
 | 
						||
				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<ID2D1Bitmap> & 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<IWICBitmapDecoder>		decoder;
 | 
						||
		ComPtr<IWICBitmapFrameDecode>	source;
 | 
						||
		ComPtr<IWICStream>				stream;
 | 
						||
		ComPtr<IWICFormatConverter>		converter;
 | 
						||
		ComPtr<ID2D1Bitmap>				bitmap_tmp;
 | 
						||
 | 
						||
		// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
 | 
						||
		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<WICInProcPointer>(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))
 | 
						||
		{
 | 
						||
			// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 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<IDWriteTextFormat> & text_format, Font const & font, TextStyle const & text_style) const
 | 
						||
	{
 | 
						||
		if (!dwrite_factory_)
 | 
						||
			return E_UNEXPECTED;
 | 
						||
 | 
						||
		ComPtr<IDWriteTextFormat> 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<IDWriteTextLayout> & text_layout, Size& layout_size, String const & text, ComPtr<IDWriteTextFormat> const& text_format, TextStyle const & text_style) const
 | 
						||
	{
 | 
						||
		if (!dwrite_factory_)
 | 
						||
			return E_UNEXPECTED;
 | 
						||
 | 
						||
		text_layout = nullptr;
 | 
						||
 | 
						||
		HRESULT hr;
 | 
						||
		ComPtr<IDWriteTextLayout> text_layout_tmp;
 | 
						||
		UINT32 length = static_cast<UINT32>(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;
 | 
						||
	}
 | 
						||
 | 
						||
} |