517 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			517 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 "Factory.h"
 | ||
| #include "logs.h"
 | ||
| #include "modules.h"
 | ||
| #include "Transform.hpp"
 | ||
| 
 | ||
| namespace easy2d
 | ||
| {
 | ||
| 	FactoryImpl::FactoryImpl()
 | ||
| 	{
 | ||
| 	}
 | ||
| 
 | ||
| 	FactoryImpl::~FactoryImpl()
 | ||
| 	{
 | ||
| 		E2D_LOG("Destroying device independent resources");
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::Init(bool debug)
 | ||
| 	{
 | ||
| 		E2D_LOG("Creating device independent resources");
 | ||
| 
 | ||
| 		D2D1_FACTORY_OPTIONS fact_options;
 | ||
| 		fact_options.debugLevel = debug ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE;
 | ||
| 		HRESULT hr = modules::DirectX().D2D1CreateFactory(
 | ||
| 			D2D1_FACTORY_TYPE_SINGLE_THREADED,
 | ||
| 			__uuidof(ID2D1Factory),
 | ||
| 			&fact_options,
 | ||
| 			reinterpret_cast<void**>(&factory_)
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			CoCreateInstance(
 | ||
| 				CLSID_WICImagingFactory,
 | ||
| 				nullptr,
 | ||
| 				CLSCTX_INPROC_SERVER,
 | ||
| 				__uuidof(IWICImagingFactory),
 | ||
| 				reinterpret_cast<void**>(&imaging_factory_)
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			modules::DirectX().DWriteCreateFactory(
 | ||
| 				DWRITE_FACTORY_TYPE_SHARED,
 | ||
| 				__uuidof(IDWriteFactory),
 | ||
| 				reinterpret_cast<IUnknown**>(&write_factory_)
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			auto 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,
 | ||
| 				&miter_stroke_style_
 | ||
| 			);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL;
 | ||
| 				hr = factory_->CreateStrokeStyle(
 | ||
| 					stroke_style,
 | ||
| 					nullptr,
 | ||
| 					0,
 | ||
| 					&bevel_stroke_style_
 | ||
| 				);
 | ||
| 			}
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND;
 | ||
| 				hr = factory_->CreateStrokeStyle(
 | ||
| 					stroke_style,
 | ||
| 					nullptr,
 | ||
| 					0,
 | ||
| 					&round_stroke_style_
 | ||
| 				);
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateHwndRenderTarget(CpHwndRenderTarget & hwnd_render_target, D2D1_RENDER_TARGET_PROPERTIES const & properties, D2D1_HWND_RENDER_TARGET_PROPERTIES const & hwnd_rt_properties) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpHwndRenderTarget hwnd_render_target_tmp;
 | ||
| 		HRESULT hr = factory_->CreateHwndRenderTarget(
 | ||
| 			properties,
 | ||
| 			hwnd_rt_properties,
 | ||
| 			&hwnd_render_target_tmp
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			hwnd_render_target = hwnd_render_target_tmp;
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateTextRenderer(
 | ||
| 		CpTextRenderer& text_renderer,
 | ||
| 		CpRenderTarget const& render_target,
 | ||
| 		CpSolidColorBrush const& brush
 | ||
| 	)
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpTextRenderer text_renderer_tmp;
 | ||
| 		HRESULT hr = ITextRenderer::Create(
 | ||
| 			&text_renderer_tmp,
 | ||
| 			factory_.Get(),
 | ||
| 			render_target.Get(),
 | ||
| 			brush.Get()
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			text_renderer = text_renderer_tmp;
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateBitmapFromFile(CpBitmap & bitmap, CpRenderTarget const & rt, std::wstring const & file_path)
 | ||
| 	{
 | ||
| 		if (imaging_factory_ == nullptr)
 | ||
| 		{
 | ||
| 			return E_UNEXPECTED;
 | ||
| 		}
 | ||
| 
 | ||
| 		using namespace intrusive;
 | ||
| 
 | ||
| 		SmartPointer<IWICBitmapDecoder>		decoder;
 | ||
| 		SmartPointer<IWICBitmapFrameDecode>	source;
 | ||
| 		SmartPointer<IWICStream>			stream;
 | ||
| 		SmartPointer<IWICFormatConverter>	converter;
 | ||
| 		SmartPointer<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 = rt->CreateBitmapFromWicBitmap(
 | ||
| 				converter.Get(),
 | ||
| 				nullptr,
 | ||
| 				&bitmap_tmp
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			bitmap = bitmap_tmp;
 | ||
| 
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateBitmapFromResource(CpBitmap & bitmap, CpRenderTarget const & rt, Resource const & res)
 | ||
| 	{
 | ||
| 		if (imaging_factory_ == nullptr)
 | ||
| 		{
 | ||
| 			return E_UNEXPECTED;
 | ||
| 		}
 | ||
| 
 | ||
| 		using namespace intrusive;
 | ||
| 
 | ||
| 		SmartPointer<IWICBitmapDecoder>		decoder;
 | ||
| 		SmartPointer<IWICBitmapFrameDecode>	source;
 | ||
| 		SmartPointer<IWICStream>			stream;
 | ||
| 		SmartPointer<IWICFormatConverter>	converter;
 | ||
| 		SmartPointer<ID2D1Bitmap>			bitmap_tmp;
 | ||
| 		
 | ||
| 		// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
 | ||
| 		ResourceData buffer;
 | ||
| 		HRESULT hr = res.Load(&buffer) ? S_OK : E_FAIL;
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = imaging_factory_->CreateStream(&stream);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = stream->InitializeFromMemory(
 | ||
| 				static_cast<WICInProcPointer>(buffer.buffer),
 | ||
| 				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 = rt->CreateBitmapFromWicBitmap(
 | ||
| 				converter.Get(),
 | ||
| 				nullptr,
 | ||
| 				&bitmap_tmp
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			bitmap = bitmap_tmp;
 | ||
| 		}
 | ||
| 
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateRectangleGeometry(CpRectangleGeometry & geo, Rect const& rect) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpRectangleGeometry rectangle;
 | ||
| 		HRESULT hr = factory_->CreateRectangleGeometry(
 | ||
| 			rect,
 | ||
| 			&rectangle
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			geo = rectangle;
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateRoundedRectangleGeometry(CpRoundedRectangleGeometry & geo, Rect const & rect, float radius_x, float radius_y) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpRoundedRectangleGeometry rounded_rect;
 | ||
| 		HRESULT hr = factory_->CreateRoundedRectangleGeometry(
 | ||
| 			D2D1::RoundedRect(
 | ||
| 				rect,
 | ||
| 				radius_x,
 | ||
| 				radius_y
 | ||
| 			),
 | ||
| 			&rounded_rect
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			geo = rounded_rect;
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateEllipseGeometry(CpEllipseGeometry & geo, Point const & center, float radius_x, float radius_y) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpEllipseGeometry ellipse;
 | ||
| 		HRESULT hr = factory_->CreateEllipseGeometry(
 | ||
| 			D2D1::Ellipse(
 | ||
| 				center,
 | ||
| 				radius_x,
 | ||
| 				radius_y
 | ||
| 			),
 | ||
| 			&ellipse
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 			geo = ellipse;
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateTransformedGeometry(
 | ||
| 		CpTransformedGeometry& transformed,
 | ||
| 		Matrix const& matrix,
 | ||
| 		CpGeometry const& geo
 | ||
| 	) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpTransformedGeometry transformed_tmp;
 | ||
| 		HRESULT hr = factory_->CreateTransformedGeometry(
 | ||
| 			geo.Get(),
 | ||
| 			matrix,
 | ||
| 			&transformed_tmp
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			transformed = transformed_tmp;
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreatePathGeometry(CpPathGeometry & geometry) const
 | ||
| 	{
 | ||
| 		if (!factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		return factory_->CreatePathGeometry(&geometry);
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT FactoryImpl::CreateTextFormat(CpTextFormat & text_format, Font const & font, TextStyle const & text_style) const
 | ||
| 	{
 | ||
| 		if (!write_factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		CpTextFormat text_format_tmp;
 | ||
| 		HRESULT hr = write_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 FactoryImpl::CreateTextLayout(CpTextLayout & text_layout, Size& layout_size, std::wstring const & text, CpTextFormat const& text_format, TextStyle const & text_style) const
 | ||
| 	{
 | ||
| 		if (!write_factory_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		text_layout = nullptr;
 | ||
| 
 | ||
| 		HRESULT hr;
 | ||
| 		CpTextLayout text_layout_tmp;
 | ||
| 		UINT32 length = static_cast<UINT32>(text.length());
 | ||
| 
 | ||
| 		if (text_style.wrap)
 | ||
| 		{
 | ||
| 			hr = write_factory_->CreateTextLayout(
 | ||
| 				text.c_str(),
 | ||
| 				length,
 | ||
| 				text_format.Get(),
 | ||
| 				text_style.wrap_width,
 | ||
| 				0,
 | ||
| 				&text_layout_tmp
 | ||
| 			);
 | ||
| 		}
 | ||
| 		else
 | ||
| 		{
 | ||
| 			hr = write_factory_->CreateTextLayout(
 | ||
| 				text.c_str(),
 | ||
| 				length,
 | ||
| 				text_format.Get(),
 | ||
| 				0,
 | ||
| 				0,
 | ||
| 				&text_layout_tmp
 | ||
| 			);
 | ||
| 
 | ||
| 			DWRITE_TEXT_METRICS metrics;
 | ||
| 			text_layout_tmp->GetMetrics(&metrics);
 | ||
| 
 | ||
| 			if (SUCCEEDED(hr))
 | ||
| 			{
 | ||
| 				text_layout_tmp = nullptr;
 | ||
| 				hr = write_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;
 | ||
| 	}
 | ||
| 
 | ||
| 	CpStrokeStyle const& FactoryImpl::GetStrokeStyle(StrokeStyle stroke) const
 | ||
| 	{
 | ||
| 		switch (stroke)
 | ||
| 		{
 | ||
| 		case StrokeStyle::Miter:
 | ||
| 			return miter_stroke_style_;
 | ||
| 			break;
 | ||
| 		case StrokeStyle::Bevel:
 | ||
| 			return bevel_stroke_style_;
 | ||
| 			break;
 | ||
| 		case StrokeStyle::Round:
 | ||
| 			return round_stroke_style_;
 | ||
| 			break;
 | ||
| 		}
 | ||
| 		return miter_stroke_style_;
 | ||
| 	}
 | ||
| 
 | ||
| }
 |