Magic_Game/src/core/render.cpp

442 lines
9.4 KiB
C++
Raw Normal View History

// 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 "render.h"
2018-11-15 17:59:18 +08:00
#include "logs.h"
#include "Image.h"
namespace easy2d
{
2019-03-10 13:44:02 +08:00
Renderer::Renderer()
2019-03-11 17:24:11 +08:00
: hwnd_(nullptr)
, antialias_(true)
2019-03-11 11:11:39 +08:00
, vsync_(true)
2019-03-10 13:44:02 +08:00
, text_antialias_(TextAntialias::ClearType)
2018-11-22 19:31:44 +08:00
, clear_color_(D2D1::ColorF(D2D1::ColorF::Black))
, opacity_(1.f)
2019-03-11 17:24:11 +08:00
, collecting_data_(false)
{
2019-03-10 13:44:02 +08:00
status_.primitives = 0;
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
Renderer::~Renderer()
2018-11-22 19:31:44 +08:00
{
}
2019-03-11 17:24:11 +08:00
void Renderer::Setup()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
E2D_LOG(L"Creating device resources");
2019-03-11 17:24:11 +08:00
HRESULT hr = hwnd_ ? S_OK : E_FAIL;
2019-03-10 13:44:02 +08:00
2019-03-11 17:24:11 +08:00
if (SUCCEEDED(hr))
2019-03-10 13:44:02 +08:00
{
device_resources_ = nullptr;
hr = DeviceResources::Create(
&device_resources_,
2019-03-11 17:24:11 +08:00
hwnd_
2019-03-10 13:44:02 +08:00
);
}
if (SUCCEEDED(hr))
{
factory_ = device_resources_->GetD2DFactory();
device_context_ = device_resources_->GetD2DDeviceContext();
}
2018-11-15 17:59:18 +08:00
2019-03-10 13:44:02 +08:00
if (SUCCEEDED(hr))
{
drawing_state_block_ = nullptr;
hr = factory_->CreateDrawingStateBlock(
&drawing_state_block_
);
}
2019-03-10 13:44:02 +08:00
if (SUCCEEDED(hr))
{
hr = CreateDeviceResources();
}
2019-03-11 17:24:11 +08:00
ThrowIfFailed(hr);
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
void Renderer::Destroy()
{
2019-03-10 13:44:02 +08:00
E2D_LOG(L"Destroying device resources");
2019-03-10 13:44:02 +08:00
drawing_state_block_.Reset();
text_renderer_.Reset();
2019-03-10 13:44:02 +08:00
solid_color_brush_.Reset();
2019-03-11 17:24:11 +08:00
device_context_.Reset();
factory_.Reset();
device_resources_.Reset();
}
void Renderer::SetTargetWindow(HWND hwnd)
{
hwnd_ = hwnd;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::CreateDeviceResources()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
HRESULT hr = S_OK;
hr = device_context_->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
&solid_color_brush_
);
2018-11-22 19:31:44 +08:00
2019-03-10 13:44:02 +08:00
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
hr = ITextRenderer::Create(
&text_renderer_,
device_context_.Get()
);
}
2018-11-22 19:31:44 +08:00
if (SUCCEEDED(hr))
{
2019-03-10 13:44:02 +08:00
SetAntialiasMode(antialias_);
SetTextAntialiasMode(text_antialias_);
}
2018-11-22 19:31:44 +08:00
return hr;
}
2018-11-12 20:46:54 +08:00
2019-03-10 13:44:02 +08:00
HRESULT Renderer::HandleDeviceLost()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
HRESULT hr = device_resources_->HandleDeviceLost();
2018-11-25 19:29:32 +08:00
2019-03-10 13:44:02 +08:00
if (SUCCEEDED(hr))
2018-11-25 19:29:32 +08:00
{
2019-03-10 13:44:02 +08:00
hr = CreateDeviceResources();
2018-11-25 19:29:32 +08:00
}
2018-11-22 19:31:44 +08:00
return hr;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::BeginDraw()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
return E_UNEXPECTED;
2019-03-11 17:24:11 +08:00
if (collecting_data_)
2019-03-10 13:44:02 +08:00
{
status_.start = time::Now();
status_.primitives = 0;
}
2019-03-10 13:44:02 +08:00
device_context_->SaveDrawingState(drawing_state_block_.Get());
device_context_->BeginDraw();
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::EndDraw()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
HRESULT hr = device_context_->EndDraw();
device_context_->RestoreDrawingState(drawing_state_block_.Get());
if (SUCCEEDED(hr))
{
// The first argument instructs DXGI to block until VSync.
2019-03-11 11:11:39 +08:00
hr = device_resources_->GetDXGISwapChain()->Present(vsync_ ? 1 : 0, 0);
auto main_rt_view = device_resources_->GetD3DRenderTargetView();
device_resources_->GetD3DDeviceContext()->OMSetRenderTargets(
1,
&main_rt_view,
device_resources_->GetD3DDepthStencilView()
);
device_resources_->GetD3DDeviceContext()->ClearRenderTargetView(
main_rt_view,
reinterpret_cast<float*>(&clear_color_)
);
2019-03-10 13:44:02 +08:00
}
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();
}
2019-03-11 17:24:11 +08:00
if (collecting_data_)
2019-03-10 13:44:02 +08:00
{
status_.duration = time::Now() - status_.start;
}
return hr;
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::CreateLayer(ComPtr<ID2D1Layer>& layer)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
layer = nullptr;
return device_context_->CreateLayer(&layer);
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::DrawGeometry(
ComPtr<ID2D1Geometry> const& geometry,
2018-11-22 19:31:44 +08:00
Color const& stroke_color,
float stroke_width,
StrokeStyle stroke
)
{
2019-03-10 13:44:02 +08:00
if (!solid_color_brush_ || !device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-03-10 13:44:02 +08:00
device_context_->DrawGeometry(
2018-11-22 19:31:44 +08:00
geometry.Get(),
2019-03-10 13:44:02 +08:00
solid_color_brush_.Get(),
2018-11-22 19:31:44 +08:00
stroke_width,
2019-03-10 13:44:02 +08:00
device_resources_->GetStrokeStyle(stroke)
2018-11-22 19:31:44 +08:00
);
2019-03-11 17:24:11 +08:00
if (collecting_data_)
++status_.primitives;
2018-11-22 19:31:44 +08:00
return S_OK;
}
2018-11-20 01:20:06 +08:00
2019-03-10 13:44:02 +08:00
HRESULT Renderer::FillGeometry(ComPtr<ID2D1Geometry> const & geometry, Color const& fill_color)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!solid_color_brush_ || !device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2018-11-20 01:20:06 +08:00
2019-03-10 13:44:02 +08:00
solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color));
device_context_->FillGeometry(
2018-11-22 19:31:44 +08:00
geometry.Get(),
2019-03-10 13:44:02 +08:00
solid_color_brush_.Get()
2018-11-22 19:31:44 +08:00
);
2018-11-22 19:31:44 +08:00
return S_OK;
}
2018-11-18 20:26:41 +08:00
2019-03-10 13:44:02 +08:00
HRESULT Renderer::DrawImage(ImagePtr const & image, Rect const& dest_rect)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2018-11-18 20:26:41 +08:00
2018-11-22 19:31:44 +08:00
if (!image->GetBitmap())
return S_OK;
2019-03-10 13:44:02 +08:00
device_context_->DrawBitmap(
2018-11-22 19:31:44 +08:00
image->GetBitmap().Get(),
2019-03-10 13:44:02 +08:00
DX::ConvertToRectF(dest_rect),
2018-11-22 19:31:44 +08:00
opacity_,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
2019-03-10 13:44:02 +08:00
DX::ConvertToRectF(image->GetCropRect())
2018-11-22 19:31:44 +08:00
);
2019-03-11 17:24:11 +08:00
if (collecting_data_)
++status_.primitives;
2018-11-22 19:31:44 +08:00
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::DrawBitmap(ComPtr<ID2D1Bitmap> const & bitmap)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
if (!bitmap)
return S_OK;
2018-11-22 19:31:44 +08:00
// Do not crop bitmap
2019-03-10 13:44:02 +08:00
D2D_RECT_F rect = D2D1::RectF(0.f, 0.f, bitmap->GetSize().width, bitmap->GetSize().height);
device_context_->DrawBitmap(
2018-11-22 19:31:44 +08:00
bitmap.Get(),
rect,
opacity_,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
rect
);
2019-03-11 17:24:11 +08:00
if (collecting_data_)
2019-03-10 13:44:02 +08:00
++status_.primitives;
2018-11-22 19:31:44 +08:00
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::DrawTextLayout(ComPtr<IDWriteTextLayout> const& text_layout)
2018-11-22 19:31:44 +08:00
{
if (!text_renderer_)
return E_UNEXPECTED;
2019-03-11 17:24:11 +08:00
if (collecting_data_)
++status_.primitives;
2018-11-22 19:31:44 +08:00
return text_layout->Draw(nullptr, text_renderer_.Get(), 0, 0);
}
2019-03-11 11:11:39 +08:00
void Renderer::SetVSyncEnabled(bool enabled)
{
vsync_ = enabled;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::PushClip(const Matrix & clip_matrix, const Size & clip_size)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
device_context_->SetTransform(DX::ConvertToMatrix3x2F(clip_matrix));
device_context_->PushAxisAlignedClip(
2018-11-25 15:00:15 +08:00
D2D1::RectF(0, 0, clip_size.x, clip_size.y),
2018-11-22 19:31:44 +08:00
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::PopClip()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
device_context_->PopAxisAlignedClip();
2018-11-22 19:31:44 +08:00
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::PushLayer(ComPtr<ID2D1Layer> const& layer, LayerProperties const& properties)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_ || !solid_color_brush_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
device_context_->PushLayer(
2018-11-22 19:31:44 +08:00
D2D1::LayerParameters(
2019-03-10 13:44:02 +08:00
DX::ConvertToRectF(properties.area),
2018-11-22 19:31:44 +08:00
nullptr,
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
D2D1::Matrix3x2F::Identity(),
properties.opacity,
2019-03-10 13:44:02 +08:00
solid_color_brush_.Get(),
2018-11-22 19:31:44 +08:00
D2D1_LAYER_OPTIONS_NONE
),
layer.Get()
);
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::PopLayer()
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
device_context_->PopLayer();
2018-11-22 19:31:44 +08:00
return S_OK;
}
2019-03-11 17:24:11 +08:00
void Renderer::StartCollectData()
2019-03-11 11:11:39 +08:00
{
2019-03-11 17:24:11 +08:00
collecting_data_ = true;
2019-03-11 11:11:39 +08:00
}
2019-03-11 17:24:11 +08:00
void Renderer::StopCollectData()
2019-03-11 11:11:39 +08:00
{
2019-03-11 17:24:11 +08:00
collecting_data_ = false;
2019-03-11 11:11:39 +08:00
}
2019-03-10 13:44:02 +08:00
void Renderer::SetClearColor(const Color & color)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
clear_color_ = DX::ConvertToColorF(color);
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::SetTransform(const Matrix & matrix)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2018-11-12 20:46:54 +08:00
2019-03-10 13:44:02 +08:00
device_context_->SetTransform(DX::ConvertToMatrix3x2F(matrix));
return S_OK;
2018-11-22 19:31:44 +08:00
}
2019-03-10 13:44:02 +08:00
void Renderer::SetOpacity(float opacity)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (opacity_ != opacity)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
opacity_ = opacity;
solid_color_brush_->SetOpacity(opacity);
2018-11-22 19:31:44 +08:00
}
}
2018-11-12 20:46:54 +08:00
2019-03-10 13:44:02 +08:00
HRESULT Renderer::SetTextStyle(
2018-11-22 19:31:44 +08:00
Color const& color,
bool has_outline,
Color const& outline_color,
float outline_width,
StrokeStyle outline_stroke
)
{
2019-03-10 13:44:02 +08:00
if (!text_renderer_ || !device_resources_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
text_renderer_->SetTextStyle(
2019-03-10 13:44:02 +08:00
DX::ConvertToColorF(color),
2018-11-22 19:31:44 +08:00
has_outline,
2019-03-10 13:44:02 +08:00
DX::ConvertToColorF(outline_color),
2018-11-22 19:31:44 +08:00
outline_width,
2019-03-10 13:44:02 +08:00
device_resources_->GetStrokeStyle(outline_stroke)
2018-11-22 19:31:44 +08:00
);
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::SetAntialiasMode(bool enabled)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2019-03-10 13:44:02 +08:00
device_context_->SetAntialiasMode(
2018-11-22 19:31:44 +08:00
enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED
);
antialias_ = enabled;
return S_OK;
}
2019-03-10 13:44:02 +08:00
HRESULT Renderer::SetTextAntialiasMode(TextAntialias mode)
2018-11-22 19:31:44 +08:00
{
2019-03-10 13:44:02 +08:00
if (!device_context_)
2018-11-22 19:31:44 +08:00
return E_UNEXPECTED;
2018-11-22 19:31:44 +08:00
text_antialias_ = mode;
D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
switch (text_antialias_)
{
2018-11-22 19:31:44 +08:00
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;
}
2019-03-10 13:44:02 +08:00
device_context_->SetTextAntialiasMode(antialias_mode);
2018-11-22 19:31:44 +08:00
return S_OK;
}
}