Magic_Game/src/kiwano/renderer/RenderTarget.cpp

719 lines
14 KiB
C++
Raw Normal View History

2019-08-16 00:50:54 +08:00
// Copyright (c) 2016-2019 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 "RenderTarget.h"
#include "../base/Logger.h"
namespace kiwano
{
//
// RenderTarget
//
RenderTarget::RenderTarget()
: opacity_(1.f)
, collecting_status_(false)
, antialias_(true)
2019-08-21 12:47:19 +08:00
, fast_global_transform_(true)
2019-08-16 00:50:54 +08:00
, text_antialias_(TextAntialias::GrayScale)
{
status_.primitives = 0;
}
2019-08-20 19:32:36 +08:00
HRESULT RenderTarget::CreateDeviceResources(ComPtr<ID2D1RenderTarget> rt, ComPtr<ID2DDeviceResources> dev_res)
2019-08-16 00:50:54 +08:00
{
HRESULT hr = E_FAIL;
if (rt && dev_res)
{
render_target_ = rt;
2019-08-20 19:32:36 +08:00
device_resources_ = dev_res;
2019-08-16 00:50:54 +08:00
hr = S_OK;
}
if (SUCCEEDED(hr))
{
text_renderer_.reset();
hr = ITextRenderer::Create(
&text_renderer_,
render_target_.get()
);
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_.reset();
2019-08-16 00:50:54 +08:00
hr = render_target_->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
D2D1::BrushProperties(),
2019-08-20 19:32:36 +08:00
&default_brush_
2019-08-16 00:50:54 +08:00
);
}
return hr;
}
2019-08-20 19:32:36 +08:00
void RenderTarget::DiscardDeviceResources()
{
text_renderer_.reset();
render_target_.reset();
default_brush_.reset();
current_brush_.reset();
device_resources_.reset();
}
2019-08-16 00:50:54 +08:00
bool RenderTarget::IsValid() const
{
2019-08-20 19:32:36 +08:00
return render_target_ && device_resources_;
2019-08-16 00:50:54 +08:00
}
void RenderTarget::BeginDraw()
{
HRESULT hr = E_FAIL;
if (collecting_status_)
{
status_.start = Time::Now();
status_.primitives = 0;
}
if (render_target_)
{
render_target_->BeginDraw();
hr = S_OK;
}
ThrowIfFailed(hr);
}
void RenderTarget::EndDraw()
{
ThrowIfFailed(render_target_->EndDraw());
if (collecting_status_)
{
status_.duration = Time::Now() - status_.start;
}
}
void RenderTarget::DrawGeometry(
Geometry const& geometry,
Color const& stroke_color,
2019-08-18 22:49:44 +08:00
Float32 stroke_width,
2019-08-16 00:50:54 +08:00
StrokeStyle stroke
) const
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr) && geometry.GetGeometry())
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-08-16 00:50:54 +08:00
render_target_->DrawGeometry(
geometry.GetGeometry().get(),
2019-08-20 19:32:36 +08:00
default_brush_.get(),
2019-08-16 00:50:54 +08:00
stroke_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(stroke)
2019-08-16 00:50:54 +08:00
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
void RenderTarget::FillGeometry(Geometry const& geometry, Color const& fill_color) const
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr) && geometry.GetGeometry())
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(fill_color));
2019-08-16 00:50:54 +08:00
render_target_->FillGeometry(
geometry.GetGeometry().get(),
2019-08-20 19:32:36 +08:00
default_brush_.get()
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
2019-08-18 22:49:44 +08:00
void RenderTarget::DrawLine(Point const& point1, Point const& point2, Color const& stroke_color, Float32 stroke_width, StrokeStyle stroke) const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-08-16 00:50:54 +08:00
render_target_->DrawLine(
DX::ConvertToPoint2F(point1),
DX::ConvertToPoint2F(point2),
2019-08-20 19:32:36 +08:00
default_brush_.get(),
2019-08-16 00:50:54 +08:00
stroke_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(stroke)
2019-08-16 00:50:54 +08:00
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
2019-08-18 22:49:44 +08:00
void RenderTarget::DrawRectangle(Rect const& rect, Color const& stroke_color, Float32 stroke_width, StrokeStyle stroke) const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-08-16 00:50:54 +08:00
render_target_->DrawRectangle(
DX::ConvertToRectF(rect),
2019-08-20 19:32:36 +08:00
default_brush_.get(),
2019-08-16 00:50:54 +08:00
stroke_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(stroke)
2019-08-16 00:50:54 +08:00
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
void RenderTarget::FillRectangle(Rect const& rect, Color const& fill_color) const
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(fill_color));
2019-08-16 00:50:54 +08:00
render_target_->FillRectangle(
DX::ConvertToRectF(rect),
2019-08-20 19:32:36 +08:00
default_brush_.get()
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
2019-08-18 22:49:44 +08:00
void RenderTarget::DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, Color const& stroke_color, Float32 stroke_width, StrokeStyle stroke) const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-08-16 00:50:54 +08:00
render_target_->DrawRoundedRectangle(
D2D1::RoundedRect(
DX::ConvertToRectF(rect),
radius.x,
radius.y
),
2019-08-20 19:32:36 +08:00
default_brush_.get(),
2019-08-16 00:50:54 +08:00
stroke_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(stroke)
2019-08-16 00:50:54 +08:00
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
void RenderTarget::FillRoundedRectangle(Rect const& rect, Vec2 const& radius, Color const& fill_color) const
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(fill_color));
2019-08-16 00:50:54 +08:00
render_target_->FillRoundedRectangle(
D2D1::RoundedRect(
DX::ConvertToRectF(rect),
radius.x,
radius.y
),
2019-08-20 19:32:36 +08:00
default_brush_.get()
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
2019-08-18 22:49:44 +08:00
void RenderTarget::DrawEllipse(Point const& center, Vec2 const& radius, Color const& stroke_color, Float32 stroke_width, StrokeStyle stroke) const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(stroke_color));
2019-08-16 00:50:54 +08:00
render_target_->DrawEllipse(
D2D1::Ellipse(
DX::ConvertToPoint2F(center),
radius.x,
radius.y
),
2019-08-20 19:32:36 +08:00
default_brush_.get(),
2019-08-16 00:50:54 +08:00
stroke_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(stroke)
2019-08-16 00:50:54 +08:00
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
void RenderTarget::FillEllipse(Point const& center, Vec2 const& radius, Color const& fill_color) const
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_ || !render_target_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-20 19:32:36 +08:00
default_brush_->SetColor(DX::ConvertToColorF(fill_color));
2019-08-16 00:50:54 +08:00
render_target_->FillEllipse(
D2D1::Ellipse(
DX::ConvertToPoint2F(center),
radius.x,
radius.y
),
2019-08-20 19:32:36 +08:00
default_brush_.get()
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
void RenderTarget::DrawImage(Image const& image, Rect const& src_rect, Rect const& dest_rect) const
{
DrawImage(image, &src_rect, &dest_rect);
}
void RenderTarget::DrawImage(Image const& image, const Rect* src_rect, const Rect* dest_rect) const
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr) && image.IsValid())
{
render_target_->DrawBitmap(
image.GetBitmap().get(),
dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr,
opacity_,
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr
);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
void RenderTarget::DrawTextLayout(TextLayout const& layout, Point const& offset) const
{
HRESULT hr = S_OK;
if (!text_renderer_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
text_renderer_->SetTextStyle(
opacity_,
DX::ConvertToColorF(layout.GetTextStyle().color),
layout.GetTextStyle().outline,
DX::ConvertToColorF(layout.GetTextStyle().outline_color),
layout.GetTextStyle().outline_width,
2019-08-20 19:32:36 +08:00
device_resources_->GetStrokeStyle(layout.GetTextStyle().outline_stroke)
2019-08-16 00:50:54 +08:00
);
}
if (SUCCEEDED(hr))
{
hr = layout.GetTextLayout()->Draw(nullptr, text_renderer_.get(), offset.x, offset.y);
IncreasePrimitivesCount();
}
ThrowIfFailed(hr);
}
2019-08-16 10:12:34 +08:00
void RenderTarget::CreateLayer(LayerArea& layer) const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-16 10:12:34 +08:00
ComPtr<ID2D1Layer> output;
hr = render_target_->CreateLayer(&output);
2019-08-16 00:50:54 +08:00
2019-08-16 10:12:34 +08:00
if (SUCCEEDED(hr))
{
layer.SetLayer(output);
}
2019-08-16 00:50:54 +08:00
}
ThrowIfFailed(hr);
}
void RenderTarget::PushClipRect(Rect const& clip_rect)
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
render_target_->PushAxisAlignedClip(
DX::ConvertToRectF(clip_rect),
2019-08-16 10:12:34 +08:00
antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
void RenderTarget::PopClipRect()
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
render_target_->PopAxisAlignedClip();
}
ThrowIfFailed(hr);
}
2019-08-20 19:32:36 +08:00
void RenderTarget::PushLayer(LayerArea& layer)
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!render_target_ || !default_brush_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
2019-08-20 19:32:36 +08:00
if (!layer.IsValid())
{
CreateLayer(layer);
}
2019-08-16 10:12:34 +08:00
if (SUCCEEDED(hr) && layer.IsValid())
2019-08-16 00:50:54 +08:00
{
render_target_->PushLayer(
D2D1::LayerParameters(
2019-08-16 10:12:34 +08:00
DX::ConvertToRectF(layer.GetAreaRect()),
layer.GetMaskGeometry().GetGeometry().get(),
antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED,
2019-08-20 19:32:36 +08:00
DX::ConvertToMatrix3x2F(layer.GetMaskTransform()),
2019-08-16 10:12:34 +08:00
layer.GetOpacity(),
2019-08-16 00:50:54 +08:00
nullptr,
D2D1_LAYER_OPTIONS_NONE
),
2019-08-16 10:12:34 +08:00
layer.GetLayer().get()
2019-08-16 00:50:54 +08:00
);
}
ThrowIfFailed(hr);
}
void RenderTarget::PopLayer()
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
render_target_->PopLayer();
}
ThrowIfFailed(hr);
}
void RenderTarget::Clear()
{
HRESULT hr = E_FAIL;
if (render_target_)
{
render_target_->Clear();
hr = S_OK;
}
ThrowIfFailed(hr);
}
void RenderTarget::Clear(Color const& clear_color)
{
HRESULT hr = E_FAIL;
if (render_target_)
{
render_target_->Clear(DX::ConvertToColorF(clear_color));
hr = S_OK;
}
ThrowIfFailed(hr);
}
2019-08-18 22:49:44 +08:00
Float32 RenderTarget::GetOpacity() const
2019-08-16 00:50:54 +08:00
{
return opacity_;
}
2019-08-20 23:51:12 +08:00
Matrix3x2 RenderTarget::GetGlobalTransform() const
{
2019-08-21 12:47:19 +08:00
return global_transform_;
2019-08-20 23:51:12 +08:00
}
2019-08-16 00:50:54 +08:00
void RenderTarget::SetTransform(const Matrix3x2& matrix)
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
2019-08-21 12:47:19 +08:00
if (fast_global_transform_)
{
render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix));
}
else
{
Matrix3x2 result = matrix * global_transform_;
render_target_->SetTransform(DX::ConvertToMatrix3x2F(&result));
}
2019-08-16 00:50:54 +08:00
}
ThrowIfFailed(hr);
}
2019-08-20 23:51:12 +08:00
void RenderTarget::SetGlobalTransform(const Matrix3x2& matrix)
{
2019-08-21 12:47:19 +08:00
SetGlobalTransform(&matrix);
}
void RenderTarget::SetGlobalTransform(const Matrix3x2* matrix)
{
if (matrix)
{
global_transform_ = *matrix;
fast_global_transform_ = false;
}
else
{
fast_global_transform_ = true;
}
2019-08-20 23:51:12 +08:00
}
2019-08-18 22:49:44 +08:00
void RenderTarget::SetOpacity(Float32 opacity)
2019-08-16 00:50:54 +08:00
{
HRESULT hr = S_OK;
2019-08-20 19:32:36 +08:00
if (!default_brush_)
2019-08-16 00:50:54 +08:00
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
if (opacity_ != opacity)
{
opacity_ = opacity;
2019-08-20 19:32:36 +08:00
default_brush_->SetOpacity(opacity);
2019-08-16 00:50:54 +08:00
}
}
ThrowIfFailed(hr);
}
void RenderTarget::SetAntialiasMode(bool enabled)
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
render_target_->SetAntialiasMode(
enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED
);
antialias_ = enabled;
}
ThrowIfFailed(hr);
}
void RenderTarget::SetTextAntialiasMode(TextAntialias mode)
{
HRESULT hr = S_OK;
if (!render_target_)
{
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr))
{
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;
}
render_target_->SetTextAntialiasMode(antialias_mode);
}
ThrowIfFailed(hr);
}
2019-08-20 21:15:15 +08:00
bool RenderTarget::CheckVisibility(Rect const& bounds, Matrix3x2 const& transform)
2019-08-20 19:32:36 +08:00
{
2019-08-21 12:47:19 +08:00
Rect visible_size = { Point{}, reinterpret_cast<const Size&>(render_target_->GetSize()) };
if (fast_global_transform_)
{
return visible_size.Intersects(transform.Transform(bounds));
}
return visible_size.Intersects(Matrix3x2(transform * global_transform_).Transform(bounds));
2019-08-20 19:32:36 +08:00
}
2019-08-16 00:50:54 +08:00
void RenderTarget::SetCollectingStatus(bool collecting)
{
collecting_status_ = collecting;
}
void RenderTarget::IncreasePrimitivesCount() const
{
if (collecting_status_)
{
++status_.primitives;
}
}
//
// ImageRenderTarget
//
ImageRenderTarget::ImageRenderTarget()
{
}
2019-08-20 19:32:36 +08:00
Image ImageRenderTarget::GetOutput() const
2019-08-16 00:50:54 +08:00
{
HRESULT hr = E_FAIL;
if (render_target_)
{
2019-08-20 19:32:36 +08:00
ComPtr<ID2D1BitmapRenderTarget> bitmap_rt;
2019-08-16 00:50:54 +08:00
hr = render_target_->QueryInterface<ID2D1BitmapRenderTarget>(&bitmap_rt);
2019-08-20 19:32:36 +08:00
if (SUCCEEDED(hr))
{
ComPtr<ID2D1Bitmap> bitmap;
hr = bitmap_rt->GetBitmap(&bitmap);
2019-08-16 00:50:54 +08:00
2019-08-20 19:32:36 +08:00
if (SUCCEEDED(hr))
{
return Image(bitmap);
}
}
2019-08-16 00:50:54 +08:00
}
ThrowIfFailed(hr);
2019-08-20 19:32:36 +08:00
return Image();
2019-08-16 00:50:54 +08:00
}
}