Magic_Game/core/base/render.cpp

494 lines
11 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"
#include "time.h"
2018-11-16 15:53:39 +08:00
#include "base.hpp"
2018-11-15 17:59:18 +08:00
#include "logs.h"
2018-11-21 17:18:59 +08:00
#include "Factory.h"
#include "Image.h"
2018-11-18 20:26:41 +08:00
#include "Transform.hpp"
namespace easy2d
{
2018-11-12 20:46:54 +08:00
namespace devices
{
GraphicsDevice::GraphicsDevice()
: fps_text_format_(nullptr)
, fps_text_layout_(nullptr)
, clear_color_(D2D1::ColorF(D2D1::ColorF::Black))
2018-11-18 20:26:41 +08:00
, opacity_(1.f)
2018-11-21 16:26:52 +08:00
, window_occluded_(false)
, initialized_(false)
, options_()
{
}
GraphicsDevice::~GraphicsDevice()
{
2018-11-15 17:59:18 +08:00
E2D_LOG("Destroying graphics device");
2018-11-12 20:46:54 +08:00
ClearImageCache();
}
2018-11-21 16:26:52 +08:00
void GraphicsDevice::Init(HWND hwnd, GraphicsOptions options, bool debug)
{
2018-11-21 16:26:52 +08:00
if (initialized_)
return;
2018-11-15 17:59:18 +08:00
E2D_LOG("Initing graphics device");
2018-11-21 16:26:52 +08:00
options_ = options;
CreateDeviceResources(hwnd);
2018-11-21 16:26:52 +08:00
initialized_ = true;
}
void GraphicsDevice::BeginDraw(HWND hwnd)
{
CreateDeviceResources(hwnd);
2018-11-21 17:18:59 +08:00
window_occluded_ = !!(render_target->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED);
2018-11-21 16:26:52 +08:00
if (!window_occluded_)
{
2018-11-21 17:18:59 +08:00
render_target->BeginDraw();
render_target->Clear(clear_color_);
}
}
void GraphicsDevice::EndDraw()
{
2018-11-21 16:26:52 +08:00
if (!window_occluded_)
{
2018-11-21 17:18:59 +08:00
HRESULT hr = render_target->EndDraw();
if (hr == D2DERR_RECREATE_TARGET)
{
// <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>Դ
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>ε<EFBFBD><CEB5><EFBFBD>ʱ<EFBFBD>ؽ<EFBFBD><D8BD><EFBFBD>Դ
hr = S_OK;
fps_text_format_ = nullptr;
fps_text_layout_ = nullptr;
2018-11-21 17:18:59 +08:00
text_renderer = nullptr;
solid_brush = nullptr;
render_target = nullptr;
}
ThrowIfFailed(hr);
}
}
2018-11-12 20:46:54 +08:00
void GraphicsDevice::ClearImageCache()
{
bitmap_cache_.clear();
}
HRESULT GraphicsDevice::CreateLayer(cpLayer& layer)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
layer = nullptr;
2018-11-21 17:18:59 +08:00
return render_target->CreateLayer(&layer);
}
HRESULT GraphicsDevice::CreateSolidColorBrush(cpSolidColorBrush & brush) const
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
brush = nullptr;
2018-11-21 17:18:59 +08:00
return render_target->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
&brush
);
}
HRESULT GraphicsDevice::DrawGeometry(
cpGeometry const& geometry,
2018-11-20 01:20:06 +08:00
Color const& stroke_color,
float stroke_width,
StrokeStyle stroke
)
{
2018-11-21 17:18:59 +08:00
if (!solid_brush ||
!render_target)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
solid_brush->SetColor(stroke_color);
auto stroke_style = Factory::Instance()->GetStrokeStyle(stroke);
render_target->DrawGeometry(
geometry.Get(),
2018-11-21 17:18:59 +08:00
solid_brush.Get(),
stroke_width,
2018-11-21 17:18:59 +08:00
stroke_style.Get()
);
return S_OK;
}
2018-11-20 01:20:06 +08:00
HRESULT GraphicsDevice::FillGeometry(cpGeometry const & geometry, const Color & fill_color)
{
2018-11-21 17:18:59 +08:00
if (!solid_brush ||
!render_target)
2018-11-20 01:20:06 +08:00
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
2018-11-20 01:20:06 +08:00
return S_OK;
2018-11-21 17:18:59 +08:00
solid_brush->SetColor(fill_color);
render_target->FillGeometry(
2018-11-20 01:20:06 +08:00
geometry.Get(),
2018-11-21 17:18:59 +08:00
solid_brush.Get()
2018-11-20 01:20:06 +08:00
);
return S_OK;
}
2018-11-18 20:26:41 +08:00
HRESULT GraphicsDevice::DrawImage(spImage const & image)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
2018-11-18 20:26:41 +08:00
return E_UNEXPECTED;
if (!image->GetBitmap())
return S_OK;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
render_target->DrawBitmap(
2018-11-18 20:26:41 +08:00
image->GetBitmap().Get(),
D2D1::RectF(0.f, 0.f, image->GetWidth(), image->GetHeight()),
opacity_,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
image->GetCropRect()
);
return S_OK;
}
HRESULT GraphicsDevice::DrawBitmap(
cpBitmap const& bitmap
)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-18 20:26:41 +08:00
// Do not crop bitmap
auto rect = D2D1::RectF(0.f, 0.f, bitmap->GetSize().width, bitmap->GetSize().height);
2018-11-21 17:18:59 +08:00
render_target->DrawBitmap(
2018-11-18 20:26:41 +08:00
bitmap.Get(),
rect,
opacity_,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
2018-11-18 20:26:41 +08:00
rect
);
return S_OK;
}
HRESULT GraphicsDevice::DrawTextLayout(cpTextLayout const& text_layout)
{
2018-11-21 17:18:59 +08:00
if (!text_renderer)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
return text_layout->Draw(nullptr, text_renderer.Get(), 0, 0);
}
HRESULT GraphicsDevice::PushClip(const math::Matrix & clip_matrix, const Size & clip_size)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
render_target->SetTransform(ConvertToD2DMatrix(clip_matrix));
render_target->PushAxisAlignedClip(
D2D1::RectF(0, 0, clip_size.width, clip_size.height),
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
return S_OK;
}
HRESULT GraphicsDevice::PopClip()
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
render_target->PopAxisAlignedClip();
return S_OK;
}
HRESULT GraphicsDevice::PushLayer(cpLayer const& layer, LayerProperties const& properties)
{
2018-11-21 17:18:59 +08:00
if (!render_target ||
!solid_brush)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
render_target->PushLayer(
D2D1::LayerParameters(
properties.area,
nullptr,
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
D2D1::Matrix3x2F::Identity(),
properties.opacity,
2018-11-21 17:18:59 +08:00
solid_brush.Get(),
D2D1_LAYER_OPTIONS_NONE
),
layer.Get()
);
return S_OK;
}
HRESULT GraphicsDevice::PopLayer()
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 16:26:52 +08:00
if (window_occluded_)
return S_OK;
2018-11-21 17:18:59 +08:00
render_target->PopLayer();
return S_OK;
}
2018-11-18 20:26:41 +08:00
HRESULT GraphicsDevice::GetSize(Size & size)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
2018-11-18 20:26:41 +08:00
return E_UNEXPECTED;
2018-11-21 17:18:59 +08:00
auto rtsize = render_target->GetSize();
2018-11-18 20:26:41 +08:00
size.width = rtsize.width;
size.height = rtsize.height;
return S_OK;
}
HRESULT GraphicsDevice::CreateBitmapFromFile(cpBitmap& bitmap, String const& file_path)
{
2018-11-21 17:18:59 +08:00
if (render_target == nullptr)
{
return E_UNEXPECTED;
}
2018-11-12 20:46:54 +08:00
size_t hash_code = std::hash<String>{}(file_path);
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
{
bitmap = bitmap_cache_[hash_code];
2018-11-12 20:46:54 +08:00
return S_OK;
}
2018-11-21 17:18:59 +08:00
cpBitmap bitmap_tmp;
HRESULT hr = Factory::Instance()->CreateBitmapFromFile(
bitmap,
render_target,
file_path
);
2018-11-12 20:46:54 +08:00
if (SUCCEEDED(hr))
{
bitmap_cache_.insert(std::make_pair(hash_code, bitmap));
2018-11-12 20:46:54 +08:00
}
return hr;
}
HRESULT GraphicsDevice::CreateBitmapFromResource(cpBitmap& bitmap, Resource const& res)
{
2018-11-21 17:18:59 +08:00
if (render_target == nullptr)
{
return E_UNEXPECTED;
}
2018-11-12 20:46:54 +08:00
size_t hash_code = res.GetHashCode();
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
{
bitmap = bitmap_cache_[hash_code];
2018-11-12 20:46:54 +08:00
return S_OK;
}
2018-11-21 17:18:59 +08:00
HRESULT hr = Factory::Instance()->CreateBitmapFromResource(
bitmap,
render_target,
res
);
2018-11-12 20:46:54 +08:00
if (SUCCEEDED(hr))
{
bitmap_cache_.insert(std::make_pair(hash_code, bitmap));
2018-11-12 20:46:54 +08:00
}
return hr;
}
2018-11-18 20:26:41 +08:00
HRESULT GraphicsDevice::CreateBitmapRenderTarget(cpBitmapRenderTarget & brt)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
2018-11-18 20:26:41 +08:00
return E_UNEXPECTED;
brt = nullptr;
2018-11-21 17:18:59 +08:00
return render_target->CreateCompatibleRenderTarget(&brt);
2018-11-18 20:26:41 +08:00
}
HRESULT GraphicsDevice::Resize(UINT32 width, UINT32 height)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 17:18:59 +08:00
render_target->Resize(D2D1::SizeU(width, height));
return S_OK;
}
HRESULT GraphicsDevice::SetTransform(const math::Matrix & matrix)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-21 17:18:59 +08:00
render_target->SetTransform(ConvertToD2DMatrix(matrix));
return S_OK;
}
2018-11-18 20:26:41 +08:00
HRESULT GraphicsDevice::SetOpacity(float opacity)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
return E_UNEXPECTED;
2018-11-18 20:26:41 +08:00
opacity_ = opacity;
2018-11-21 17:18:59 +08:00
solid_brush->SetOpacity(opacity);
return S_OK;
}
HRESULT GraphicsDevice::SetTextStyle(
Color const& color,
bool has_outline,
Color const& outline_color,
float outline_width,
StrokeStyle outline_stroke
)
{
2018-11-21 17:18:59 +08:00
if (!text_renderer)
return E_UNEXPECTED;
2018-11-21 17:18:59 +08:00
auto stroke_style = Factory::Instance()->GetStrokeStyle(outline_stroke);
text_renderer->SetTextStyle(
color,
has_outline,
outline_color,
outline_width,
2018-11-21 17:18:59 +08:00
stroke_style.Get()
);
return S_OK;
}
void GraphicsDevice::SetBackgroundColor(const Color& color)
{
clear_color_ = color;
}
void GraphicsDevice::CreateDeviceResources(HWND hwnd)
{
2018-11-21 17:18:59 +08:00
if (!render_target)
{
RECT rc;
::GetClientRect(hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E8B1B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD><EFBFBD>Щ<EFBFBD><D0A9>ԴӦ<D4B4><D3A6> Direct2D <20><EFBFBD><E8B1B8>ʧʱ<CAA7>ؽ<EFBFBD>
// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> Direct2D <20><>ȾĿ<C8BE><C4BF>
ThrowIfFailed(
2018-11-21 17:18:59 +08:00
Factory::Instance()->CreateHwndRenderTarget(
render_target,
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(
hwnd,
size,
2018-11-21 16:26:52 +08:00
options_.vsync ? D2D1_PRESENT_OPTIONS_NONE : D2D1_PRESENT_OPTIONS_IMMEDIATELY
2018-11-21 17:18:59 +08:00
)
)
);
2018-11-21 16:26:52 +08:00
2018-11-21 17:18:59 +08:00
render_target->SetAntialiasMode(
2018-11-21 16:26:52 +08:00
options_.antialias ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED
);
D2D1_TEXT_ANTIALIAS_MODE mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
switch (options_.text_antialias)
{
case TextAntialias::Default:
mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
break;
case TextAntialias::ClearType:
mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
break;
case TextAntialias::GrayScale:
mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
break;
case TextAntialias::None:
mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
break;
default:
break;
}
2018-11-21 17:18:59 +08:00
render_target->SetTextAntialiasMode(mode);
}
2018-11-21 17:18:59 +08:00
if (!solid_brush)
{
ThrowIfFailed(
2018-11-21 17:18:59 +08:00
render_target->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
2018-11-21 17:18:59 +08:00
&solid_brush
)
);
}
2018-11-21 17:18:59 +08:00
if (!text_renderer)
{
ThrowIfFailed(
2018-11-21 17:18:59 +08:00
Factory::Instance()->CreateTextRenderer(
text_renderer,
render_target,
solid_brush
)
);
}
}
2018-11-20 01:20:06 +08:00
}
}