432 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			432 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C++
		
	
	
	
| // 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 "render.h"
 | ||
| #include "../2d/Image.h"
 | ||
| #include "../base/logs.h"
 | ||
| #include "../platform/Application.h"
 | ||
| 
 | ||
| namespace kiwano
 | ||
| {
 | ||
| 	Renderer::Renderer()
 | ||
| 		: hwnd_(nullptr)
 | ||
| 		, antialias_(true)
 | ||
| 		, vsync_(true)
 | ||
| 		, text_antialias_(TextAntialias::ClearType)
 | ||
| 		, clear_color_(Color::Black)
 | ||
| 		, opacity_(1.f)
 | ||
| 		, collecting_status_(false)
 | ||
| 	{
 | ||
| 		status_.primitives = 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	Renderer::~Renderer()
 | ||
| 	{
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::SetupComponent(Application* app)
 | ||
| 	{
 | ||
| 		KGE_LOG(L"Creating device resources");
 | ||
| 
 | ||
| 		hwnd_ = app->GetWindow()->GetHandle();
 | ||
| 
 | ||
| 		ThrowIfFailed(hwnd_ ? S_OK : E_FAIL);
 | ||
| 		
 | ||
| 		device_resources_ = nullptr;
 | ||
| 		drawing_state_block_ = nullptr;
 | ||
| 
 | ||
| 		ThrowIfFailed(
 | ||
| 			DeviceResources::Create(
 | ||
| 				&device_resources_,
 | ||
| 				hwnd_
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		factory_ = device_resources_->GetD2DFactory();
 | ||
| 		device_context_ = device_resources_->GetD2DDeviceContext();
 | ||
| 
 | ||
| 		ThrowIfFailed(
 | ||
| 			factory_->CreateDrawingStateBlock(
 | ||
| 				&drawing_state_block_
 | ||
| 			)
 | ||
| 		);
 | ||
| 
 | ||
| 		ThrowIfFailed(
 | ||
| 			CreateDeviceResources()
 | ||
| 		);
 | ||
| 
 | ||
| 		output_size_ = app->GetWindow()->GetSize();
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::DestroyComponent()
 | ||
| 	{
 | ||
| 		KGE_LOG(L"Destroying device resources");
 | ||
| 
 | ||
| 		drawing_state_block_.Reset();
 | ||
| 		text_renderer_.Reset();
 | ||
| 		solid_color_brush_.Reset();
 | ||
| 		device_context_.Reset();
 | ||
| 		factory_.Reset();
 | ||
| 		device_resources_.Reset();
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::CreateDeviceResources()
 | ||
| 	{
 | ||
| 		HRESULT hr = S_OK;
 | ||
| 
 | ||
| 		hr = device_context_->CreateSolidColorBrush(
 | ||
| 			D2D1::ColorF(D2D1::ColorF::White),
 | ||
| 			&solid_color_brush_
 | ||
| 		);
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = ITextRenderer::Create(
 | ||
| 				&text_renderer_,
 | ||
| 				device_context_.Get()
 | ||
| 			);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			SetAntialiasMode(antialias_);
 | ||
| 			SetTextAntialiasMode(text_antialias_);
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::HandleDeviceLost()
 | ||
| 	{
 | ||
| 		HRESULT hr = device_resources_->HandleDeviceLost();
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = CreateDeviceResources();
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::BeginDraw()
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 		{
 | ||
| 			status_.start = Time::Now();
 | ||
| 			status_.primitives = 0;
 | ||
| 		}
 | ||
| 
 | ||
| 		device_context_->SaveDrawingState(drawing_state_block_.Get());
 | ||
| 
 | ||
| 		device_context_->BeginDraw();
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::EndDraw()
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		HRESULT hr = device_context_->EndDraw();
 | ||
| 
 | ||
| 		device_context_->RestoreDrawingState(drawing_state_block_.Get());
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = device_resources_->Present(vsync_);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (SUCCEEDED(hr))
 | ||
| 		{
 | ||
| 			hr = device_resources_->ClearRenderTarget(clear_color_);
 | ||
| 		}
 | ||
| 
 | ||
| 		if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
 | ||
| 		{
 | ||
| 			// <20><><EFBFBD><EFBFBD> Direct3D <20>豸<EFBFBD><E8B1B8>ִ<EFBFBD>й<EFBFBD><D0B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD>豸<EFBFBD><E8B1B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
 | ||
| 			hr = HandleDeviceLost();
 | ||
| 		}
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 		{
 | ||
| 			status_.duration = Time::Now() - status_.start;
 | ||
| 		}
 | ||
| 		return hr;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::CreateLayer(ComPtr<ID2D1Layer>& layer)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		layer = nullptr;
 | ||
| 		return device_context_->CreateLayer(&layer);
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::DrawGeometry(
 | ||
| 		ComPtr<ID2D1Geometry> const& geometry,
 | ||
| 		Color const& stroke_color,
 | ||
| 		float stroke_width,
 | ||
| 		StrokeStyle stroke
 | ||
| 	)
 | ||
| 	{
 | ||
| 		if (!solid_color_brush_ || !device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color));
 | ||
| 
 | ||
| 		device_context_->DrawGeometry(
 | ||
| 			geometry.Get(),
 | ||
| 			solid_color_brush_.Get(),
 | ||
| 			stroke_width,
 | ||
| 			device_resources_->GetStrokeStyle(stroke)
 | ||
| 		);
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 			++status_.primitives;
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::FillGeometry(ComPtr<ID2D1Geometry> const & geometry, Color const& fill_color)
 | ||
| 	{
 | ||
| 		if (!solid_color_brush_ || !device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color));
 | ||
| 		device_context_->FillGeometry(
 | ||
| 			geometry.Get(),
 | ||
| 			solid_color_brush_.Get()
 | ||
| 		);
 | ||
| 
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::DrawImage(ImagePtr image, Rect const& dest_rect)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		if (!image->GetBitmap())
 | ||
| 			return S_OK;
 | ||
| 
 | ||
| 		device_context_->DrawBitmap(
 | ||
| 			image->GetBitmap().Get(),
 | ||
| 			DX::ConvertToRectF(dest_rect),
 | ||
| 			opacity_,
 | ||
| 			D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
 | ||
| 			DX::ConvertToRectF(image->GetCropRect())
 | ||
| 		);
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 			++status_.primitives;
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::DrawBitmap(ComPtr<ID2D1Bitmap> const & bitmap, Rect const& src_rect, Rect const& dest_rect)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		if (!bitmap)
 | ||
| 			return S_OK;
 | ||
| 
 | ||
| 		// Do not crop bitmap 
 | ||
| 		device_context_->DrawBitmap(
 | ||
| 			bitmap.Get(),
 | ||
| 			DX::ConvertToRectF(dest_rect),
 | ||
| 			opacity_,
 | ||
| 			D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
 | ||
| 			DX::ConvertToRectF(src_rect)
 | ||
| 		);
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 		++status_.primitives;
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::DrawTextLayout(ComPtr<IDWriteTextLayout> const& text_layout)
 | ||
| 	{
 | ||
| 		if (!text_renderer_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		if (collecting_status_)
 | ||
| 			++status_.primitives;
 | ||
| 		return text_layout->Draw(nullptr, text_renderer_.Get(), 0, 0);
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::SetVSyncEnabled(bool enabled)
 | ||
| 	{
 | ||
| 		vsync_ = enabled;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::PushClip(const Matrix & clip_matrix, const Size & clip_size)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->SetTransform(DX::ConvertToMatrix3x2F(clip_matrix));
 | ||
| 		device_context_->PushAxisAlignedClip(
 | ||
| 			D2D1::RectF(0, 0, clip_size.x, clip_size.y),
 | ||
| 			D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
 | ||
| 		);
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::PopClip()
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->PopAxisAlignedClip();
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::PushLayer(ComPtr<ID2D1Layer> const& layer, LayerProperties const& properties)
 | ||
| 	{
 | ||
| 		if (!device_context_ || !solid_color_brush_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->PushLayer(
 | ||
| 			D2D1::LayerParameters(
 | ||
| 				DX::ConvertToRectF(properties.area),
 | ||
| 				nullptr,
 | ||
| 				D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
 | ||
| 				D2D1::Matrix3x2F::Identity(),
 | ||
| 				properties.opacity,
 | ||
| 				solid_color_brush_.Get(),
 | ||
| 				D2D1_LAYER_OPTIONS_NONE
 | ||
| 			),
 | ||
| 			layer.Get()
 | ||
| 		);
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::PopLayer()
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->PopLayer();
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::Resize(UINT width, UINT height)
 | ||
| 	{
 | ||
| 		output_size_.x = static_cast<float>(width);
 | ||
| 		output_size_.y = static_cast<float>(height);
 | ||
| 		if (device_resources_)
 | ||
| 		{
 | ||
| 			return device_resources_->SetLogicalSize(output_size_);
 | ||
| 		}
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::SetCollectingStatus(bool collecting)
 | ||
| 	{
 | ||
| 		collecting_status_ = collecting;
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::SetClearColor(const Color & color)
 | ||
| 	{
 | ||
| 		clear_color_ = color;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::SetTransform(const Matrix & matrix)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->SetTransform(DX::ConvertToMatrix3x2F(matrix));
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	void Renderer::SetOpacity(float opacity)
 | ||
| 	{
 | ||
| 		if (opacity_ != opacity)
 | ||
| 		{
 | ||
| 			opacity_ = opacity;
 | ||
| 			solid_color_brush_->SetOpacity(opacity);
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::SetTextStyle(
 | ||
| 		Color const& color,
 | ||
| 		bool has_outline,
 | ||
| 		Color const& outline_color,
 | ||
| 		float outline_width,
 | ||
| 		StrokeStyle outline_stroke
 | ||
| 	)
 | ||
| 	{
 | ||
| 		if (!text_renderer_ || !device_resources_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		text_renderer_->SetTextStyle(
 | ||
| 			DX::ConvertToColorF(color),
 | ||
| 			has_outline,
 | ||
| 			DX::ConvertToColorF(outline_color),
 | ||
| 			outline_width,
 | ||
| 			device_resources_->GetStrokeStyle(outline_stroke)
 | ||
| 		);
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::SetAntialiasMode(bool enabled)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		device_context_->SetAntialiasMode(
 | ||
| 			enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED
 | ||
| 		);
 | ||
| 		antialias_ = enabled;
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| 	HRESULT Renderer::SetTextAntialiasMode(TextAntialias mode)
 | ||
| 	{
 | ||
| 		if (!device_context_)
 | ||
| 			return E_UNEXPECTED;
 | ||
| 
 | ||
| 		text_antialias_ = mode;
 | ||
| 		D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
 | ||
| 		switch (text_antialias_)
 | ||
| 		{
 | ||
| 		case TextAntialias::Default:
 | ||
| 			antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
 | ||
| 			break;
 | ||
| 		case TextAntialias::ClearType:
 | ||
| 			antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
 | ||
| 			break;
 | ||
| 		case TextAntialias::GrayScale:
 | ||
| 			antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
 | ||
| 			break;
 | ||
| 		case TextAntialias::None:
 | ||
| 			antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
 | ||
| 			break;
 | ||
| 		default:
 | ||
| 			break;
 | ||
| 		}
 | ||
| 		device_context_->SetTextAntialiasMode(antialias_mode);
 | ||
| 		return S_OK;
 | ||
| 	}
 | ||
| 
 | ||
| }
 |