diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index f544ab77..d4eadbbe 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -62,6 +62,7 @@ + @@ -71,7 +72,10 @@ + + + @@ -140,7 +144,10 @@ + + + diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index 007fb85b..06a3e54e 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -288,6 +288,18 @@ render + + render\DirectX + + + render\DirectX + + + render\DirectX + + + platform\win32 + @@ -488,5 +500,14 @@ render + + render\DirectX + + + render\DirectX + + + render\DirectX + \ No newline at end of file diff --git a/src/kiwano-imgui/ImGuiModule.cpp b/src/kiwano-imgui/ImGuiModule.cpp index 360e0320..a3129855 100644 --- a/src/kiwano-imgui/ImGuiModule.cpp +++ b/src/kiwano-imgui/ImGuiModule.cpp @@ -58,7 +58,7 @@ void ImGuiModule::SetupComponent() io.KeyMap[ImGuiKey_Y] = (int)KeyCode::Y; io.KeyMap[ImGuiKey_Z] = (int)KeyCode::Z; - ImGui_Impl_Init(Renderer::GetInstance()); + ImGui_Impl_Init(); } void ImGuiModule::DestroyComponent() diff --git a/src/kiwano-imgui/imgui_impl.h b/src/kiwano-imgui/imgui_impl.h index 0402bcd5..31275492 100644 --- a/src/kiwano-imgui/imgui_impl.h +++ b/src/kiwano-imgui/imgui_impl.h @@ -7,20 +7,25 @@ #if !defined(KGE_USE_DIRECTX10) #include +#include -inline bool ImGui_Impl_Init(::kiwano::Renderer& renderer) +inline bool ImGui_Impl_Init() { + ::kiwano::RendererImpl& renderer = ::kiwano::RendererImpl::GetInstance(); return ImGui_ImplDX11_Init(renderer.GetD3DDeviceResources()->GetDevice(), renderer.GetD3DDeviceResources()->GetDeviceContext()); } + inline void ImGui_Impl_Shutdown() { ImGui_ImplDX11_Shutdown(); } + inline void ImGui_Impl_NewFrame() { ImGui_ImplDX11_NewFrame(); } + inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) { ImGui_ImplDX11_RenderDrawData(draw_data); @@ -30,6 +35,7 @@ inline void ImGui_Impl_InvalidateDeviceObjects() { ImGui_ImplDX11_InvalidateDeviceObjects(); } + inline bool ImGui_Impl_CreateDeviceObjects() { return ImGui_ImplDX11_CreateDeviceObjects(); @@ -39,18 +45,22 @@ inline bool ImGui_Impl_CreateDeviceObjects() #include -inline bool ImGui_Impl_Init(::kiwano::Renderer& renderer) +inline bool ImGui_Impl_Init() { + ::kiwano::RendererImpl& renderer = ::kiwano::RendererImpl::GetInstance(); return ImGui_ImplDX10_Init(renderer.GetD3DDeviceResources()->GetDevice()); } + inline void ImGui_Impl_Shutdown() { ImGui_ImplDX10_Shutdown(); } + inline void ImGui_Impl_NewFrame() { ImGui_ImplDX10_NewFrame(); } + inline void ImGui_Impl_RenderDrawData(ImDrawData* draw_data) { ImGui_ImplDX10_RenderDrawData(draw_data); @@ -60,6 +70,7 @@ inline void ImGui_Impl_InvalidateDeviceObjects() { ImGui_ImplDX10_InvalidateDeviceObjects(); } + inline bool ImGui_Impl_CreateDeviceObjects() { return ImGui_ImplDX10_CreateDeviceObjects(); diff --git a/src/kiwano/2d/Canvas.cpp b/src/kiwano/2d/Canvas.cpp index eb7715a6..305c2e4d 100644 --- a/src/kiwano/2d/Canvas.cpp +++ b/src/kiwano/2d/Canvas.cpp @@ -35,6 +35,8 @@ CanvasPtr Canvas::Create(Size const& size) ptr->ctx_ = TextureRenderContext::Create(); ptr->stroke_brush_ = Brush::Create(Color::White); ptr->fill_brush_ = Brush::Create(Color::White); + + ptr->SetSize(ptr->ctx_->GetSize()); } catch (std::exception) { @@ -71,9 +73,7 @@ void Canvas::OnRender(RenderContext& ctx) if (texture_cached_ && texture_cached_->IsValid()) { PrepareToRender(ctx); - - Rect bitmap_rect(0.f, 0.f, texture_cached_->GetWidth(), texture_cached_->GetHeight()); - ctx.DrawTexture(*texture_cached_, bitmap_rect, bitmap_rect); + ctx.DrawTexture(*texture_cached_, nullptr, &GetBounds()); } } diff --git a/src/kiwano/platform/win32/WindowImpl.cpp b/src/kiwano/platform/win32/WindowImpl.cpp index 1f8c1fd7..104bfe9c 100644 --- a/src/kiwano/platform/win32/WindowImpl.cpp +++ b/src/kiwano/platform/win32/WindowImpl.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include +#include #if defined(KGE_WIN32) @@ -38,8 +38,19 @@ namespace kiwano { -namespace win32 + + +Window& Window::GetInstance() { + return WindowImpl::GetInstance(); +} + +WindowImpl& WindowImpl::GetInstance() +{ + static WindowImpl instance; + return instance; +} + namespace { MONITORINFOEX GetMoniterInfoEx(HWND hwnd) @@ -96,51 +107,6 @@ void RestoreResolution(WCHAR* device_name) } } // namespace -class KGE_API WindowImpl : public kiwano::Window -{ -public: - WindowImpl(); - - ~WindowImpl(); - - void Create(String const& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, - bool fullscreen) override; - - WindowHandle GetHandle() const override; - - void SetTitle(String const& title) override; - - void SetIcon(uint32_t icon_resource) override; - - void Resize(uint32_t width, uint32_t height) override; - - void SetFullscreen(bool fullscreen) override; - - void SetCursor(CursorType cursor) override; - - void Destroy() override; - -private: - void PumpEvents() override; - - DWORD GetStyle() const; - - void UpdateCursor(); - - void SetActive(bool actived); - - static LRESULT CALLBACK WndProc(HWND, UINT32, WPARAM, LPARAM); - -private: - bool resizable_; - bool is_fullscreen_; - wchar_t* device_name_; - WindowHandle handle_; - CursorType mouse_cursor_; - - std::array key_map_; -}; - WindowImpl::WindowImpl() : handle_(nullptr) , device_name_(nullptr) @@ -678,18 +644,6 @@ LRESULT CALLBACK WindowImpl::WndProc(HWND hwnd, UINT32 msg, WPARAM wparam, LPARA return ::DefWindowProcW(hwnd, msg, wparam, lparam); } -} // namespace win32 -} // namespace kiwano - -namespace kiwano -{ - -Window& Window::GetInstance() -{ - static win32::WindowImpl instance; - return instance; -} - } // namespace kiwano #endif diff --git a/src/kiwano/platform/win32/WindowImpl.h b/src/kiwano/platform/win32/WindowImpl.h new file mode 100644 index 00000000..fc46a1dd --- /dev/null +++ b/src/kiwano/platform/win32/WindowImpl.h @@ -0,0 +1,79 @@ +// Copyright (c) 2016-2020 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. + +#pragma once +#include +#include + +#if defined(KGE_WIN32) + +namespace kiwano +{ + +class KGE_API WindowImpl : public Window +{ +public: + static WindowImpl& GetInstance(); + + void Create(String const& title, uint32_t width, uint32_t height, uint32_t icon, bool resizable, + bool fullscreen) override; + + WindowHandle GetHandle() const override; + + void SetTitle(String const& title) override; + + void SetIcon(uint32_t icon_resource) override; + + void Resize(uint32_t width, uint32_t height) override; + + void SetFullscreen(bool fullscreen) override; + + void SetCursor(CursorType cursor) override; + + void Destroy() override; + +private: + WindowImpl(); + + ~WindowImpl(); + + void PumpEvents() override; + + DWORD GetStyle() const; + + void UpdateCursor(); + + void SetActive(bool actived); + + static LRESULT CALLBACK WndProc(HWND, UINT32, WPARAM, LPARAM); + +private: + bool resizable_; + bool is_fullscreen_; + wchar_t* device_name_; + WindowHandle handle_; + CursorType mouse_cursor_; + + std::array key_map_; +}; + +} // namespace kiwano + +#endif diff --git a/src/kiwano/render/Brush.cpp b/src/kiwano/render/Brush.cpp index f02d53d5..f5e6250f 100644 --- a/src/kiwano/render/Brush.cpp +++ b/src/kiwano/render/Brush.cpp @@ -86,8 +86,7 @@ BrushPtr Brush::Create(RadialGradientStyle const& style) } Brush::Brush() - : opacity_(1.f) - , type_(Type::Unknown) + : type_(Type::Unknown) { } @@ -96,20 +95,6 @@ bool Brush::IsValid() const return raw_ != nullptr; } -float Brush::GetOpacity() const -{ - return opacity_; -} - -void Brush::SetOpacity(float opacity) -{ - opacity_ = opacity; - if (raw_) - { - raw_->SetOpacity(opacity); - } -} - void Brush::SetColor(Color const& color) { Renderer::GetInstance().CreateBrush(*this, color); diff --git a/src/kiwano/render/Brush.h b/src/kiwano/render/Brush.h index 01268952..2d9c977e 100644 --- a/src/kiwano/render/Brush.h +++ b/src/kiwano/render/Brush.h @@ -24,8 +24,6 @@ namespace kiwano { -class RenderContext; -class Renderer; KGE_DECLARE_SMART_PTR(Brush); @@ -89,9 +87,6 @@ struct RadialGradientStyle */ class KGE_API Brush : public virtual ObjectBase { - friend class RenderContext; - friend class Renderer; - public: /// \~chinese /// @brief 创建纯色画刷 @@ -142,23 +137,15 @@ public: Type GetType() const; private: - /// \~chinese - /// @brief 获取透明度 - float GetOpacity() const; - - /// \~chinese - /// @brief 设置透明度 - void SetOpacity(float opacity); - -private: - Type type_; - float opacity_; + Type type_; #if defined(KGE_WIN32) +public: void SetBrush(ComPtr brush, Type type); ComPtr GetBrush() const; +private: ComPtr raw_; #endif }; @@ -175,10 +162,6 @@ inline void Brush::SetBrush(ComPtr brush, Type type) { type_ = type; raw_ = brush; - if (raw_) - { - raw_->SetOpacity(opacity_); - } } inline ComPtr Brush::GetBrush() const diff --git a/src/kiwano/render/DirectX/RenderContextImpl.cpp b/src/kiwano/render/DirectX/RenderContextImpl.cpp new file mode 100644 index 00000000..b3d4ffe3 --- /dev/null +++ b/src/kiwano/render/DirectX/RenderContextImpl.cpp @@ -0,0 +1,489 @@ +// 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 +#include + +namespace kiwano +{ + +RenderContextImpl::RenderContextImpl() +{} + +RenderContextImpl::~RenderContextImpl() +{ + DiscardDeviceResources(); +} + +HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, ComPtr ctx) +{ + if (!factory || !ctx) + return E_INVALIDARG; + + render_target_ = ctx; + text_renderer_.reset(); + current_brush_.reset(); + + HRESULT hr = ITextRenderer::Create(&text_renderer_, render_target_.get()); + + if (SUCCEEDED(hr)) + { + SetAntialiasMode(antialias_); + SetTextAntialiasMode(text_antialias_); + + Resize(reinterpret_cast(ctx->GetSize())); + } + + // DrawingStateBlock + if (SUCCEEDED(hr)) + { + hr = factory->CreateDrawingStateBlock(&drawing_state_); + } + + return hr; +} + +void RenderContextImpl::DiscardDeviceResources() +{ + text_renderer_.reset(); + render_target_.reset(); + current_brush_.reset(); +} + +bool RenderContextImpl::IsValid() const +{ + return render_target_ != nullptr; +} + +void RenderContextImpl::BeginDraw() +{ + SaveDrawingState(); + + RenderContext::BeginDraw(); + + if (render_target_) + { + render_target_->BeginDraw(); + } +} + +void RenderContextImpl::EndDraw() +{ + win32::ThrowIfFailed(render_target_->EndDraw()); + + RenderContext::EndDraw(); + + RestoreDrawingState(); +} + +void RenderContextImpl::DrawTexture(Texture const& texture, const Rect* src_rect, const Rect* dest_rect) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + if (texture.IsValid()) + { + auto mode = (texture.GetBitmapInterpolationMode() == InterpolationMode::Linear) + ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR + : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + + render_target_->DrawBitmap(texture.GetBitmap().get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, + brush_opacity_, mode, src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr); + + IncreasePrimitivesCount(); + } +} + +void RenderContextImpl::DrawTextLayout(TextLayout const& layout, Point const& offset) +{ + KGE_ASSERT(text_renderer_ && "Text renderer has not been initialized!"); + + if (layout.IsValid()) + { + ComPtr fill_brush; + ComPtr outline_brush; + const TextStyle& style = layout.GetStyle(); + + if (style.fill_brush) + { + fill_brush = style.fill_brush->GetBrush(); + fill_brush->SetOpacity(brush_opacity_); + } + + if (style.outline_brush) + { + outline_brush = style.outline_brush->GetBrush(); + outline_brush->SetOpacity(brush_opacity_); + } + + HRESULT hr = S_OK; + + if (style.outline_stroke) + { + hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().get(), offset.x, offset.y, fill_brush.get(), + outline_brush.get(), style.outline_width, + style.outline_stroke->GetStrokeStyle().get()); + } + else + { + hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().get(), offset.x, offset.y, fill_brush.get(), + outline_brush.get(), style.outline_width, nullptr); + } + + if (SUCCEEDED(hr)) + { + IncreasePrimitivesCount(text_renderer_->GetLastPrimitivesCount()); + } + else + { + KGE_ERROR(L"Failed to draw text layout with HRESULT of %08X", hr); + } + } +} + +void RenderContextImpl::DrawShape(Shape const& shape, StrokeStylePtr stroke, float stroke_width) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (shape.IsValid()) + { + if (stroke) + { + render_target_->DrawGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get(), stroke_width, + stroke->GetStrokeStyle().get()); + } + else + { + render_target_->DrawGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get(), stroke_width, + nullptr); + } + + IncreasePrimitivesCount(); + } +} + +void RenderContextImpl::DrawLine(Point const& point1, Point const& point2, StrokeStylePtr stroke, float stroke_width) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (stroke) + { + render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), + current_brush_->GetBrush().get(), stroke_width, stroke->GetStrokeStyle().get()); + } + else + { + render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), + current_brush_->GetBrush().get(), stroke_width, nullptr); + } + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::DrawRectangle(Rect const& rect, StrokeStylePtr stroke, float stroke_width) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (stroke) + { + render_target_->DrawRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get(), stroke_width, + stroke->GetStrokeStyle().get()); + } + else + { + render_target_->DrawRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get(), stroke_width, + nullptr); + } + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, StrokeStylePtr stroke, + float stroke_width) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (stroke) + { + render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), + current_brush_->GetBrush().get(), stroke_width, + stroke->GetStrokeStyle().get()); + } + else + { + render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), + current_brush_->GetBrush().get(), stroke_width, nullptr); + } + IncreasePrimitivesCount(); +} + +void RenderContextImpl::DrawEllipse(Point const& center, Vec2 const& radius, StrokeStylePtr stroke, float stroke_width) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (stroke) + { + render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), + current_brush_->GetBrush().get(), stroke_width, stroke->GetStrokeStyle().get()); + } + else + { + render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), + current_brush_->GetBrush().get(), stroke_width, nullptr); + } + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::FillShape(Shape const& shape) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + if (shape.IsValid()) + { + render_target_->FillGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get()); + + IncreasePrimitivesCount(); + } +} + +void RenderContextImpl::FillRectangle(Rect const& rect) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + render_target_->FillRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get()); + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::FillRoundedRectangle(Rect const& rect, Vec2 const& radius) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + render_target_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), + current_brush_->GetBrush().get()); + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::FillEllipse(Point const& center, Vec2 const& radius) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); + + render_target_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), + current_brush_->GetBrush().get()); + + IncreasePrimitivesCount(); +} + +void RenderContextImpl::CreateTexture(Texture& texture, math::Vec2T size) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + ComPtr saved_bitmap; + + HRESULT hr = render_target_->CreateBitmap(D2D1::SizeU(size.x, size.y), D2D1::BitmapProperties(), &saved_bitmap); + + if (SUCCEEDED(hr)) + { + texture.SetBitmap(saved_bitmap); + } + + win32::ThrowIfFailed(hr); +} + +void RenderContextImpl::PushClipRect(Rect const& clip_rect) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + render_target_->PushAxisAlignedClip(DX::ConvertToRectF(clip_rect), + antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); +} + +void RenderContextImpl::PopClipRect() +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + render_target_->PopAxisAlignedClip(); +} + +void RenderContextImpl::PushLayer(LayerArea& layer) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + if (!layer.IsValid()) + { + ComPtr output; + HRESULT hr = render_target_->CreateLayer(&output); + + if (SUCCEEDED(hr)) + { + layer.SetLayer(output); + } + else + { + win32::ThrowIfFailed(hr); + } + } + + if (layer.IsValid()) + { + ComPtr mask; + if (layer.GetMaskShape()) + mask = layer.GetMaskShape()->GetGeometry(); + + render_target_->PushLayer( + D2D1::LayerParameters(DX::ConvertToRectF(layer.GetAreaRect()), mask.get(), + antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED, + DX::ConvertToMatrix3x2F(layer.GetMaskTransform()), layer.GetOpacity(), nullptr, + D2D1_LAYER_OPTIONS_NONE), + layer.GetLayer().get()); + } +} + +void RenderContextImpl::PopLayer() +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + render_target_->PopLayer(); +} + +void RenderContextImpl::Clear() +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + render_target_->Clear(); +} + +void RenderContextImpl::Clear(Color const& clear_color) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + render_target_->Clear(DX::ConvertToColorF(clear_color)); +} + +Size RenderContextImpl::GetSize() const +{ + if (render_target_) + { + return reinterpret_cast(render_target_->GetSize()); + } + return Size(); +} + +void RenderContextImpl::SetCurrentBrush(BrushPtr brush) +{ + RenderContext::SetCurrentBrush(brush); + + if (current_brush_ && current_brush_->IsValid()) + { + current_brush_->GetBrush()->SetOpacity(brush_opacity_); + } +} + +void RenderContextImpl::SetTransform(const Matrix3x2& matrix) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + if (fast_global_transform_) + { + render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); + } + else + { + Matrix3x2 result = matrix * global_transform_; + render_target_->SetTransform(DX::ConvertToMatrix3x2F(&result)); + } +} + +void RenderContextImpl::SetAntialiasMode(bool enabled) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + render_target_->SetAntialiasMode(enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); + antialias_ = enabled; +} + +void RenderContextImpl::SetTextAntialiasMode(TextAntialiasMode mode) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + switch (mode) + { + case TextAntialiasMode::Default: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + break; + case TextAntialiasMode::ClearType: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + case TextAntialiasMode::GrayScale: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case TextAntialiasMode::None: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + default: + break; + } + + text_antialias_ = mode; + render_target_->SetTextAntialiasMode(antialias_mode); +} + +bool RenderContextImpl::CheckVisibility(Rect const& bounds, Matrix3x2 const& transform) +{ + KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + + if (fast_global_transform_) + { + return visible_size_.Intersects(transform.Transform(bounds)); + } + return visible_size_.Intersects(Matrix3x2(transform * global_transform_).Transform(bounds)); +} + +void RenderContextImpl::Resize(Size const& size) +{ + visible_size_ = Rect(Point(), size); +} + +void RenderContextImpl::SaveDrawingState() +{ + KGE_ASSERT(IsValid()); + + if (drawing_state_) + { + render_target_->SaveDrawingState(drawing_state_.get()); + } +} + +void RenderContextImpl::RestoreDrawingState() +{ + KGE_ASSERT(IsValid()); + + if (drawing_state_) + { + render_target_->RestoreDrawingState(drawing_state_.get()); + } +} + +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/RenderContextImpl.h b/src/kiwano/render/DirectX/RenderContextImpl.h new file mode 100644 index 00000000..92d6a95d --- /dev/null +++ b/src/kiwano/render/DirectX/RenderContextImpl.h @@ -0,0 +1,114 @@ +// 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. + +#pragma once +#include + +namespace kiwano +{ + +class RendererImpl; + +KGE_DECLARE_SMART_PTR(RenderContextImpl); + +class KGE_API RenderContextImpl : public virtual RenderContext +{ + friend class RendererImpl; + +public: + bool IsValid() const override; + + void BeginDraw() override; + + void EndDraw() override; + + void DrawTexture(Texture const& texture, const Rect* src_rect = nullptr, const Rect* dest_rect = nullptr) override; + + void DrawTextLayout(TextLayout const& layout, Point const& offset = Point()) override; + + void DrawShape(Shape const& shape, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f) override; + + void DrawLine(Point const& point1, Point const& point2, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) override; + + void DrawRectangle(Rect const& rect, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f) override; + + void DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) override; + + void DrawEllipse(Point const& center, Vec2 const& radius, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) override; + + void FillShape(Shape const& shape) override; + + void FillRectangle(Rect const& rect) override; + + void FillRoundedRectangle(Rect const& rect, Vec2 const& radius) override; + + void FillEllipse(Point const& center, Vec2 const& radius) override; + + void CreateTexture(Texture& texture, math::Vec2T size) override; + + void PushClipRect(Rect const& clip_rect) override; + + void PopClipRect() override; + + void PushLayer(LayerArea& layer) override; + + void PopLayer() override; + + void Clear() override; + + void Clear(Color const& clear_color) override; + + Size GetSize() const override; + + void SetCurrentBrush(BrushPtr brush) override; + + void SetTransform(const Matrix3x2& matrix) override; + + void SetAntialiasMode(bool enabled) override; + + void SetTextAntialiasMode(TextAntialiasMode mode) override; + + bool CheckVisibility(Rect const& bounds, Matrix3x2 const& transform) override; + + void Resize(Size const& size) override; + +protected: + RenderContextImpl(); + + virtual ~RenderContextImpl(); + + HRESULT CreateDeviceResources(ComPtr factory, ComPtr ctx); + + void DiscardDeviceResources(); + + void SaveDrawingState(); + + void RestoreDrawingState(); + +protected: + ComPtr text_renderer_; + ComPtr render_target_; + ComPtr drawing_state_; +}; + +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/RendererImpl.cpp b/src/kiwano/render/DirectX/RendererImpl.cpp new file mode 100644 index 00000000..0f9ff4d2 --- /dev/null +++ b/src/kiwano/render/DirectX/RendererImpl.cpp @@ -0,0 +1,954 @@ +// 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 +#include +#include +#include +#include +#include + +namespace kiwano +{ + +Renderer& Renderer::GetInstance() +{ + return RendererImpl::GetInstance(); +} + +RendererImpl& RendererImpl::GetInstance() +{ + static RendererImpl instance; + return instance; +} + +RendererImpl::RendererImpl() +{ + render_ctx_ = new RenderContextImpl; +} + +void RendererImpl::SetupComponent() +{ + KGE_SYS_LOG(L"Creating device resources"); + + win32::ThrowIfFailed(::CoInitialize(nullptr)); + + target_window_ = Window::GetInstance().GetHandle(); + output_size_ = Window::GetInstance().GetSize(); + + d2d_res_ = nullptr; + d3d_res_ = nullptr; + + HRESULT hr = target_window_ ? S_OK : E_FAIL; + + // Direct3D device resources + if (SUCCEEDED(hr)) + { + hr = ID3DDeviceResources::Create(&d3d_res_, target_window_); + + // Direct2D device resources + if (SUCCEEDED(hr)) + { + hr = ID2DDeviceResources::Create(&d2d_res_, d3d_res_->GetDXGIDevice(), d3d_res_->GetDXGISwapChain()); + + // Other device resources + if (SUCCEEDED(hr)) + { + hr = render_ctx_->CreateDeviceResources(d2d_res_->GetFactory(), d2d_res_->GetDeviceContext()); + } + + // FontFileLoader and FontCollectionLoader + if (SUCCEEDED(hr)) + { + hr = IFontCollectionLoader::Create(&font_collection_loader_); + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader(font_collection_loader_.get()); + } + } + + // ResourceFontFileLoader and ResourceFontCollectionLoader + if (SUCCEEDED(hr)) + { + hr = IResourceFontFileLoader::Create(&res_font_file_loader_); + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontFileLoader(res_font_file_loader_.get()); + } + + if (SUCCEEDED(hr)) + { + hr = IResourceFontCollectionLoader::Create(&res_font_collection_loader_, + res_font_file_loader_.get()); + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader( + res_font_collection_loader_.get()); + } + } + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::DestroyComponent() +{ + KGE_SYS_LOG(L"Destroying device resources"); + + d2d_res_->GetDWriteFactory()->UnregisterFontFileLoader(res_font_file_loader_.get()); + res_font_file_loader_.reset(); + + d2d_res_->GetDWriteFactory()->UnregisterFontCollectionLoader(res_font_collection_loader_.get()); + res_font_collection_loader_.reset(); + + render_ctx_.reset(); + d2d_res_.reset(); + d3d_res_.reset(); + + ::CoUninitialize(); +} + +void RendererImpl::BeginDraw() +{ + KGE_ASSERT(render_ctx_); + + render_ctx_->BeginDraw(); +} + +void RendererImpl::EndDraw() +{ + KGE_ASSERT(render_ctx_); + + render_ctx_->EndDraw(); +} + +void RendererImpl::Clear() +{ + KGE_ASSERT(d3d_res_); + + HRESULT hr = d3d_res_->ClearRenderTarget(clear_color_); + win32::ThrowIfFailed(hr); +} + +void RendererImpl::Present() +{ + KGE_ASSERT(d3d_res_); + + HRESULT hr = d3d_res_->Present(vsync_); + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { + // 如果 Direct3D 设备在执行过程中消失,将丢弃当前的设备相关资源 + hr = HandleDeviceLost(); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::HandleEvent(Event* evt) +{ + if (evt->IsType()) + { + auto window_evt = dynamic_cast(evt); + Resize(window_evt->width, window_evt->height); + } +} + +HRESULT RendererImpl::HandleDeviceLost() +{ + KGE_ASSERT(d3d_res_ && d2d_res_ && render_ctx_); + + HRESULT hr = d3d_res_->HandleDeviceLost(); + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->HandleDeviceLost(d3d_res_->GetDXGIDevice(), d3d_res_->GetDXGISwapChain()); + } + + if (SUCCEEDED(hr)) + { + hr = render_ctx_->CreateDeviceResources(d2d_res_->GetFactory(), d2d_res_->GetDeviceContext()); + } + return hr; +} + +void RendererImpl::CreateTexture(Texture& texture, String const& file_path) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (!FileSystem::GetInstance().IsFileExists(file_path)) + { + KGE_WARN(L"Texture file '%s' not found!", file_path.c_str()); + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); + + ComPtr decoder; + hr = d2d_res_->CreateBitmapDecoderFromFile(decoder, full_path); + + if (SUCCEEDED(hr)) + { + ComPtr source; + hr = decoder->GetFrame(0, &source); + + if (SUCCEEDED(hr)) + { + ComPtr converter; + hr = d2d_res_->CreateBitmapConverter(converter, source, GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, nullptr, 0.f, + WICBitmapPaletteTypeMedianCut); + + if (SUCCEEDED(hr)) + { + ComPtr bitmap; + hr = d2d_res_->CreateBitmapFromConverter(bitmap, nullptr, converter); + + if (SUCCEEDED(hr)) + { + texture.SetBitmap(bitmap); + } + } + } + } + } + + if (FAILED(hr)) + { + KGE_WARN(L"Load texture failed with HRESULT of %08X!", hr); + } +} + +void RendererImpl::CreateTexture(Texture& texture, Resource const& resource) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr decoder; + hr = d2d_res_->CreateBitmapDecoderFromResource(decoder, resource); + + if (SUCCEEDED(hr)) + { + ComPtr source; + hr = decoder->GetFrame(0, &source); + + if (SUCCEEDED(hr)) + { + ComPtr converter; + hr = d2d_res_->CreateBitmapConverter(converter, source, GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, nullptr, 0.f, + WICBitmapPaletteTypeMedianCut); + + if (SUCCEEDED(hr)) + { + ComPtr bitmap; + hr = d2d_res_->CreateBitmapFromConverter(bitmap, nullptr, converter); + + if (SUCCEEDED(hr)) + { + texture.SetBitmap(bitmap); + } + } + } + } + } + + if (FAILED(hr)) + { + KGE_WARN(L"Load texture failed with HRESULT of %08X!", hr); + } +} + +void RendererImpl::CreateGifImage(GifImage& gif, String const& file_path) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (!FileSystem::GetInstance().IsFileExists(file_path)) + { + KGE_WARN(L"Gif texture file '%s' not found!", file_path.c_str()); + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); + + ComPtr decoder; + hr = d2d_res_->CreateBitmapDecoderFromFile(decoder, full_path); + + if (SUCCEEDED(hr)) + { + gif.SetDecoder(decoder); + } + } + + if (FAILED(hr)) + { + KGE_WARN(L"Load GIF texture failed with HRESULT of %08X!", hr); + } +} + +void RendererImpl::CreateGifImage(GifImage& gif, Resource const& resource) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr decoder; + hr = d2d_res_->CreateBitmapDecoderFromResource(decoder, resource); + + if (SUCCEEDED(hr)) + { + gif.SetDecoder(decoder); + } + } + + if (FAILED(hr)) + { + KGE_WARN(L"Load GIF texture failed with HRESULT of %08X!", hr); + } +} + +void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, GifImage const& gif, size_t frame_index) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (gif.GetDecoder() == nullptr) + { + hr = E_INVALIDARG; + } + + if (SUCCEEDED(hr)) + { + ComPtr wic_frame; + + HRESULT hr = gif.GetDecoder()->GetFrame(UINT(frame_index), &wic_frame); + + if (SUCCEEDED(hr)) + { + ComPtr converter; + d2d_res_->CreateBitmapConverter(converter, wic_frame, GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeCustom); + + if (SUCCEEDED(hr)) + { + ComPtr raw_bitmap; + hr = d2d_res_->CreateBitmapFromConverter(raw_bitmap, nullptr, converter); + + if (SUCCEEDED(hr)) + { + frame.texture = new Texture; + frame.texture->SetBitmap(raw_bitmap); + } + } + } + + if (SUCCEEDED(hr)) + { + PROPVARIANT prop_val; + PropVariantInit(&prop_val); + + // Get Metadata Query Reader from the frame + ComPtr metadata_reader; + hr = wic_frame->GetMetadataQueryReader(&metadata_reader); + + // Get the Metadata for the current frame + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Left", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame.rect.left_top.x = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Top", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame.rect.left_top.y = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Width", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame.rect.right_bottom.x = frame.rect.left_top.x + static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Height", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame.rect.right_bottom.y = frame.rect.left_top.y + static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/grctlext/Delay", &prop_val); + + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + + if (SUCCEEDED(hr)) + { + uint32_t udelay = 0; + + hr = UIntMult(prop_val.uiVal, 10, &udelay); + if (SUCCEEDED(hr)) + { + frame.delay.SetMilliseconds(static_cast(udelay)); + } + } + PropVariantClear(&prop_val); + } + else + { + frame.delay = 0; + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/grctlext/Disposal", &prop_val); + + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI1) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + frame.disposal_type = GifImage::DisposalType(prop_val.bVal); + } + ::PropVariantClear(&prop_val); + } + else + { + frame.disposal_type = GifImage::DisposalType::Unknown; + } + } + + ::PropVariantClear(&prop_val); + } + } + + if (FAILED(hr)) + { + KGE_WARN(L"Load GIF frame failed with HRESULT of %08X!", hr); + } +} + +void RendererImpl::CreateFontCollection(Font& font, String const& file_path) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + if (!FileSystem::GetInstance().IsFileExists(file_path)) + { + KGE_WARN(L"Font file '%s' not found!", file_path.c_str()); + hr = E_FAIL; + } + } + + if (SUCCEEDED(hr)) + { + LPVOID key = nullptr; + uint32_t key_size = 0; + String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); + + hr = font_collection_loader_->AddFilePaths({ full_path }, &key, &key_size); + + if (SUCCEEDED(hr)) + { + ComPtr font_collection; + hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection(font_collection_loader_.get(), key, key_size, + &font_collection); + + if (SUCCEEDED(hr)) + { + font.SetCollection(font_collection); + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateFontCollection(Font& font, Resource const& res) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + LPVOID key = nullptr; + uint32_t key_size = 0; + + hr = res_font_collection_loader_->AddResources(Vector{ res }, &key, &key_size); + + if (SUCCEEDED(hr)) + { + ComPtr font_collection; + hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection(res_font_collection_loader_.get(), key, + key_size, &font_collection); + + if (SUCCEEDED(hr)) + { + font.SetCollection(font_collection); + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateTextFormat(TextLayout& layout) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + const TextStyle& style = layout.GetStyle(); + + hr = d2d_res_->CreateTextFormat(output, style.font_family, style.font ? style.font->GetCollection() : nullptr, + DWRITE_FONT_WEIGHT(style.font_weight), + style.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, style.font_size); + } + + if (SUCCEEDED(hr)) + { + layout.SetTextFormat(output); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateTextLayout(TextLayout& layout) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->CreateTextLayout(output, layout.GetText(), layout.GetTextFormat()); + } + + if (SUCCEEDED(hr)) + { + layout.SetTextLayout(output); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr path_geo; + ComPtr path_sink; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreatePathGeometry(&path_geo); + } + + if (SUCCEEDED(hr)) + { + hr = path_geo->Open(&path_sink); + } + + if (SUCCEEDED(hr)) + { + path_sink->BeginFigure(DX::ConvertToPoint2F(begin_pos), D2D1_FIGURE_BEGIN_FILLED); + path_sink->AddLine(DX::ConvertToPoint2F(end_pos)); + path_sink->EndFigure(D2D1_FIGURE_END_OPEN); + hr = path_sink->Close(); + } + + if (SUCCEEDED(hr)) + { + shape.SetGeometry(path_geo); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateRectShape(Shape& shape, Rect const& rect) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreateRectangleGeometry(DX::ConvertToRectF(rect), &output); + } + + if (SUCCEEDED(hr)) + { + shape.SetGeometry(output); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateRoundedRectShape(Shape& shape, Rect const& rect, Vec2 const& radius) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreateRoundedRectangleGeometry( + D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), &output); + } + + if (SUCCEEDED(hr)) + { + shape.SetGeometry(output); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateEllipseShape(Shape& shape, Point const& center, Vec2 const& radius) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreateEllipseGeometry( + D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), &output); + } + + if (SUCCEEDED(hr)) + { + shape.SetGeometry(output); + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateShapeSink(ShapeSink& sink) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreatePathGeometry(&output); + } + + if (SUCCEEDED(hr)) + { + sink.SetPathGeometry(output); + } + + win32::ThrowIfFailed(hr); +} + +TextureRenderContextPtr RendererImpl::CreateTextureRenderContext(const Size* desired_size) +{ + TextureRenderContextImplPtr ptr = new TextureRenderContextImpl; + + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr bitmap_rt; + + if (desired_size) + { + hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(DX::ConvertToSizeF(*desired_size), + &bitmap_rt); + } + else + { + hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(&bitmap_rt); + } + + if (SUCCEEDED(hr)) + { + hr = ptr->CreateDeviceResources(d2d_res_->GetFactory(), bitmap_rt); + } + + if (SUCCEEDED(hr)) + { + ptr->bitmap_rt_ = bitmap_rt; + } + } + + if (SUCCEEDED(hr)) + return ptr; + + return nullptr; +} + +void RendererImpl::CreateBrush(Brush& brush, Color const& color) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr solid_brush; + + if (brush.GetType() == Brush::Type::SolidColor && brush.GetBrush()) + { + hr = brush.GetBrush()->QueryInterface(&solid_brush); + if (SUCCEEDED(hr)) + { + solid_brush->SetColor(DX::ConvertToColorF(color)); + } + } + else + { + hr = d2d_res_->GetDeviceContext()->CreateSolidColorBrush(DX::ConvertToColorF(color), &solid_brush); + + if (SUCCEEDED(hr)) + { + brush.SetBrush(solid_brush, Brush::Type::SolidColor); + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateBrush(Brush& brush, LinearGradientStyle const& style) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr collection; + hr = d2d_res_->GetDeviceContext()->CreateGradientStopCollection( + reinterpret_cast(&style.stops[0]), UINT32(style.stops.size()), D2D1_GAMMA_2_2, + D2D1_EXTEND_MODE(style.extend_mode), &collection); + + if (SUCCEEDED(hr)) + { + ComPtr output; + hr = d2d_res_->GetDeviceContext()->CreateLinearGradientBrush( + D2D1::LinearGradientBrushProperties(DX::ConvertToPoint2F(style.begin), DX::ConvertToPoint2F(style.end)), + collection.get(), &output); + + if (SUCCEEDED(hr)) + { + brush.SetBrush(output, Brush::Type::LinearGradient); + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateBrush(Brush& brush, RadialGradientStyle const& style) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr collection; + hr = d2d_res_->GetDeviceContext()->CreateGradientStopCollection( + reinterpret_cast(&style.stops[0]), UINT32(style.stops.size()), D2D1_GAMMA_2_2, + D2D1_EXTEND_MODE(style.extend_mode), &collection); + + if (SUCCEEDED(hr)) + { + ComPtr output; + hr = d2d_res_->GetDeviceContext()->CreateRadialGradientBrush( + D2D1::RadialGradientBrushProperties(DX::ConvertToPoint2F(style.center), + DX::ConvertToPoint2F(style.offset), style.radius.x, style.radius.y), + collection.get(), &output); + + if (SUCCEEDED(hr)) + { + brush.SetBrush(output, Brush::Type::RadialGradient); + } + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::CreateStrokeStyle(StrokeStyle& stroke_style, CapStyle cap, LineJoinStyle line_join, + const float* dash_array, size_t dash_size, float dash_offset) +{ + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + D2D1_STROKE_STYLE_PROPERTIES style = + D2D1::StrokeStyleProperties(D2D1_CAP_STYLE(cap), D2D1_CAP_STYLE(cap), D2D1_CAP_STYLE(cap), + D2D1_LINE_JOIN(line_join), 10.0f, D2D1_DASH_STYLE_CUSTOM, dash_offset); + + ComPtr output; + hr = d2d_res_->GetFactory()->CreateStrokeStyle(style, dash_array, dash_size, &output); + + if (SUCCEEDED(hr)) + { + stroke_style.SetStrokeStyle(output); + } + } + + win32::ThrowIfFailed(hr); +} + +void RendererImpl::Resize(uint32_t width, uint32_t height) +{ + HRESULT hr = S_OK; + + if (!d3d_res_) + hr = E_UNEXPECTED; + + if (SUCCEEDED(hr)) + { + output_size_.x = static_cast(width); + output_size_.y = static_cast(height); + + hr = d3d_res_->SetLogicalSize(output_size_); + } + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->SetLogicalSize(output_size_); + } + + if (SUCCEEDED(hr)) + { + render_ctx_->Resize(output_size_); + } + + win32::ThrowIfFailed(hr); +} + +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/RendererImpl.h b/src/kiwano/render/DirectX/RendererImpl.h new file mode 100644 index 00000000..0b8ecccd --- /dev/null +++ b/src/kiwano/render/DirectX/RendererImpl.h @@ -0,0 +1,147 @@ +// 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. + +#pragma once +#include +#include +#include + +#if defined(KGE_USE_DIRECTX10) +#include +#else +#include +#endif + +namespace kiwano +{ + +#if defined(KGE_USE_DIRECTX10) +typedef ID3D10DeviceResources ID3DDeviceResources; +#else +typedef ID3D11DeviceResources ID3DDeviceResources; +#endif + +class KGE_API RendererImpl + : public Renderer +{ +public: + static RendererImpl& GetInstance(); + + void CreateTexture(Texture& texture, String const& file_path); + + void CreateTexture(Texture& texture, Resource const& resource); + + void CreateGifImage(GifImage& gif, String const& file_path); + + void CreateGifImage(GifImage& gif, Resource const& resource); + + void CreateGifImageFrame(GifImage::Frame& frame, GifImage const& gif, size_t frame_index); + + void CreateFontCollection(Font& font, String const& file_path); + + void CreateFontCollection(Font& font, Resource const& res); + + void CreateTextFormat(TextLayout& layout); + + void CreateTextLayout(TextLayout& layout); + + void CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos); + + void CreateRectShape(Shape& shape, Rect const& rect); + + void CreateRoundedRectShape(Shape& shape, Rect const& rect, Vec2 const& radius); + + void CreateEllipseShape(Shape& shape, Point const& center, Vec2 const& radius); + + void CreateShapeSink(ShapeSink& sink); + + void CreateBrush(Brush& brush, Color const& color); + + void CreateBrush(Brush& brush, LinearGradientStyle const& style); + + void CreateBrush(Brush& brush, RadialGradientStyle const& style); + + void CreateStrokeStyle(StrokeStyle& stroke_style, CapStyle cap, LineJoinStyle line_join, const float* dash_array, + size_t dash_size, float dash_offset); + + TextureRenderContextPtr CreateTextureRenderContext(const Size* desired_size = nullptr); + +public: + void BeginDraw(); + + void EndDraw(); + + void Clear(); + + void Present(); + + RenderContext& GetContext(); + + /// \~chinese + /// @brief 获取Direct2D设备资源 + ID2DDeviceResources* GetD2DDeviceResources(); + + /// \~chinese + /// @brief 获取Direct3D设备资源 + ID3DDeviceResources* GetD3DDeviceResources(); + +public: + void SetupComponent() override; + + void DestroyComponent() override; + + void HandleEvent(Event* evt) override; + +private: + RendererImpl(); + + HRESULT HandleDeviceLost(); + + void Resize(uint32_t width, uint32_t height); + +private: + RenderContextImplPtr render_ctx_; + ComPtr d2d_res_; + ComPtr d3d_res_; + ComPtr font_collection_loader_; + ComPtr res_font_file_loader_; + ComPtr res_font_collection_loader_; +}; + +/** @} */ + +inline RenderContext& RendererImpl::GetContext() +{ + return *render_ctx_; +} + +inline ID2DDeviceResources* RendererImpl::GetD2DDeviceResources() +{ + KGE_ASSERT(d2d_res_); + return d2d_res_.get(); +} + +inline ID3DDeviceResources* RendererImpl::GetD3DDeviceResources() +{ + KGE_ASSERT(d3d_res_); + return d3d_res_.get(); +} + +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextureRenderContextImpl.cpp b/src/kiwano/render/DirectX/TextureRenderContextImpl.cpp new file mode 100644 index 00000000..de2c24e1 --- /dev/null +++ b/src/kiwano/render/DirectX/TextureRenderContextImpl.cpp @@ -0,0 +1,46 @@ +// 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 +#include +#include + +namespace kiwano +{ + +bool TextureRenderContextImpl::GetOutput(Texture& texture) +{ + HRESULT hr = E_FAIL; + + if (bitmap_rt_) + { + ComPtr bitmap; + + hr = bitmap_rt_->GetBitmap(&bitmap); + + if (SUCCEEDED(hr)) + { + texture.SetBitmap(bitmap); + } + } + return SUCCEEDED(hr); +} + +} // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextureRenderContextImpl.h b/src/kiwano/render/DirectX/TextureRenderContextImpl.h new file mode 100644 index 00000000..b8274f13 --- /dev/null +++ b/src/kiwano/render/DirectX/TextureRenderContextImpl.h @@ -0,0 +1,104 @@ +// 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. + +#pragma once +#include +#include + +namespace kiwano +{ + +class RendererImpl; + +KGE_DECLARE_SMART_PTR(TextureRenderContextImpl); + +KGE_SUPPRESS_WARNING_PUSH +KGE_SUPPRESS_WARNING(4250) // inherits via domainance + +class KGE_API TextureRenderContextImpl + : public RenderContextImpl + , public TextureRenderContext +{ + friend class RendererImpl; + +public: + bool GetOutput(Texture& texture) override; + + using RenderContextImpl::IsValid; + + using RenderContextImpl::BeginDraw; + + using RenderContextImpl::EndDraw; + + using RenderContextImpl::DrawTexture; + + using RenderContextImpl::DrawTextLayout; + + using RenderContextImpl::DrawShape; + + using RenderContextImpl::DrawLine; + + using RenderContextImpl::DrawRectangle; + + using RenderContextImpl::DrawRoundedRectangle; + + using RenderContextImpl::DrawEllipse; + + using RenderContextImpl::FillShape; + + using RenderContextImpl::FillRectangle; + + using RenderContextImpl::FillRoundedRectangle; + + using RenderContextImpl::FillEllipse; + + using RenderContextImpl::CreateTexture; + + using RenderContextImpl::PushClipRect; + + using RenderContextImpl::PopClipRect; + + using RenderContextImpl::PushLayer; + + using RenderContextImpl::PopLayer; + + using RenderContextImpl::Clear; + + using RenderContextImpl::GetSize; + + using RenderContextImpl::SetCurrentBrush; + + using RenderContextImpl::SetTransform; + + using RenderContextImpl::SetAntialiasMode; + + using RenderContextImpl::SetTextAntialiasMode; + + using RenderContextImpl::CheckVisibility; + + using RenderContextImpl::Resize; + +private: + ComPtr bitmap_rt_; +}; + +KGE_SUPPRESS_WARNING_POP + +} // namespace kiwano diff --git a/src/kiwano/render/Font.h b/src/kiwano/render/Font.h index 9ba98b91..e4d2e5b5 100644 --- a/src/kiwano/render/Font.h +++ b/src/kiwano/render/Font.h @@ -63,11 +63,12 @@ public: bool Load(Resource const& resource); #if defined(KGE_WIN32) -private: +public: ComPtr GetCollection() const; void SetCollection(ComPtr collection); +private: ComPtr collection_; #endif }; diff --git a/src/kiwano/render/GifImage.h b/src/kiwano/render/GifImage.h index c791eb1e..bd8e082c 100644 --- a/src/kiwano/render/GifImage.h +++ b/src/kiwano/render/GifImage.h @@ -24,8 +24,6 @@ namespace kiwano { -class Renderer; - KGE_DECLARE_SMART_PTR(GifImage); /** @@ -39,8 +37,6 @@ KGE_DECLARE_SMART_PTR(GifImage); */ class KGE_API GifImage : public virtual ObjectBase { - friend class Renderer; - public: /// \~chinese /// @brief 创建GIF图片 @@ -113,10 +109,12 @@ private: uint32_t height_in_pixels_; #if defined(KGE_WIN32) +public: ComPtr GetDecoder() const; void SetDecoder(ComPtr decoder); +private: ComPtr decoder_; #endif }; diff --git a/src/kiwano/render/LayerArea.h b/src/kiwano/render/LayerArea.h index ad10ed0a..e88db1f3 100644 --- a/src/kiwano/render/LayerArea.h +++ b/src/kiwano/render/LayerArea.h @@ -23,7 +23,6 @@ namespace kiwano { -class RenderContext; /** * \addtogroup Render @@ -36,8 +35,6 @@ class RenderContext; */ class KGE_API LayerArea { - friend class RenderContext; - public: LayerArea(); @@ -84,10 +81,12 @@ private: Matrix3x2 mask_transform_; #if defined(KGE_WIN32) +public: ComPtr GetLayer() const; void SetLayer(ComPtr layer); +private: ComPtr layer_; #endif }; diff --git a/src/kiwano/render/RenderContext.cpp b/src/kiwano/render/RenderContext.cpp index e2376083..3cdc98f6 100644 --- a/src/kiwano/render/RenderContext.cpp +++ b/src/kiwano/render/RenderContext.cpp @@ -18,7 +18,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include #include namespace kiwano @@ -31,47 +30,6 @@ RenderContext::RenderContext() , antialias_(true) , text_antialias_(TextAntialiasMode::GrayScale) { - status_.primitives = 0; -} - -HRESULT RenderContext::CreateDeviceResources(ComPtr factory, ComPtr ctx) -{ - if (!factory || !ctx) - return E_INVALIDARG; - - render_target_ = ctx; - text_renderer_.reset(); - current_brush_.reset(); - - HRESULT hr = ITextRenderer::Create(&text_renderer_, render_target_.get()); - - if (SUCCEEDED(hr)) - { - SetAntialiasMode(antialias_); - SetTextAntialiasMode(text_antialias_); - - Resize(reinterpret_cast(GetRenderTarget()->GetSize())); - } - - // DrawingStateBlock - if (SUCCEEDED(hr)) - { - hr = factory->CreateDrawingStateBlock(&drawing_state_); - } - - return hr; -} - -void RenderContext::DiscardDeviceResources() -{ - text_renderer_.reset(); - render_target_.reset(); - current_brush_.reset(); -} - -bool RenderContext::IsValid() const -{ - return render_target_ != nullptr; } void RenderContext::BeginDraw() @@ -81,340 +39,16 @@ void RenderContext::BeginDraw() status_.start = Time::Now(); status_.primitives = 0; } - - if (render_target_) - { - render_target_->BeginDraw(); - } } void RenderContext::EndDraw() { - win32::ThrowIfFailed(render_target_->EndDraw()); - if (collecting_status_) { status_.duration = Time::Now() - status_.start; } } -void RenderContext::DrawTexture(Texture const& texture, Rect const& src_rect, Rect const& dest_rect) -{ - DrawTexture(texture, &src_rect, &dest_rect); -} - -void RenderContext::DrawTexture(Texture const& texture, const Rect* src_rect, const Rect* dest_rect) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - if (texture.IsValid()) - { - auto mode = (texture.GetBitmapInterpolationMode() == InterpolationMode::Linear) - ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR - : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; - - render_target_->DrawBitmap(texture.GetBitmap().get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, - brush_opacity_, mode, src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr); - - IncreasePrimitivesCount(); - } -} - -void RenderContext::DrawTextLayout(TextLayout const& layout, Point const& offset) -{ - KGE_ASSERT(text_renderer_ && "Text renderer has not been initialized!"); - - if (layout.IsValid()) - { - ComPtr fill_brush; - ComPtr outline_brush; - const TextStyle& style = layout.GetStyle(); - - if (style.fill_brush) - { - fill_brush = style.fill_brush->GetBrush(); - fill_brush->SetOpacity(brush_opacity_); - } - - if (style.outline_brush) - { - outline_brush = style.outline_brush->GetBrush(); - outline_brush->SetOpacity(brush_opacity_); - } - - HRESULT hr = S_OK; - - if (style.outline_stroke) - { - hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().get(), offset.x, offset.y, fill_brush.get(), - outline_brush.get(), style.outline_width, - style.outline_stroke->GetStrokeStyle().get()); - } - else - { - hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().get(), offset.x, offset.y, fill_brush.get(), - outline_brush.get(), style.outline_width, nullptr); - } - - if (SUCCEEDED(hr)) - { - IncreasePrimitivesCount(text_renderer_->GetLastPrimitivesCount()); - } - else - { - KGE_ERROR(L"Failed to draw text layout with HRESULT of %08X", hr); - } - } -} - -void RenderContext::DrawShape(Shape const& shape, StrokeStylePtr stroke, float stroke_width) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (shape.IsValid()) - { - if (stroke) - { - render_target_->DrawGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get(), stroke_width, - stroke->GetStrokeStyle().get()); - } - else - { - render_target_->DrawGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get(), stroke_width, - nullptr); - } - - IncreasePrimitivesCount(); - } -} - -void RenderContext::DrawLine(Point const& point1, Point const& point2, StrokeStylePtr stroke, float stroke_width) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (stroke) - { - render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), - current_brush_->GetBrush().get(), stroke_width, stroke->GetStrokeStyle().get()); - } - else - { - render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), - current_brush_->GetBrush().get(), stroke_width, nullptr); - } - - IncreasePrimitivesCount(); -} - -void RenderContext::DrawRectangle(Rect const& rect, StrokeStylePtr stroke, float stroke_width) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (stroke) - { - render_target_->DrawRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get(), stroke_width, - stroke->GetStrokeStyle().get()); - } - else - { - render_target_->DrawRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get(), stroke_width, - nullptr); - } - - IncreasePrimitivesCount(); -} - -void RenderContext::DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, StrokeStylePtr stroke, - float stroke_width) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (stroke) - { - render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), - current_brush_->GetBrush().get(), stroke_width, - stroke->GetStrokeStyle().get()); - } - else - { - render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), - current_brush_->GetBrush().get(), stroke_width, nullptr); - } - IncreasePrimitivesCount(); -} - -void RenderContext::DrawEllipse(Point const& center, Vec2 const& radius, StrokeStylePtr stroke, float stroke_width) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (stroke) - { - render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), - current_brush_->GetBrush().get(), stroke_width, stroke->GetStrokeStyle().get()); - } - else - { - render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), - current_brush_->GetBrush().get(), stroke_width, nullptr); - } - - IncreasePrimitivesCount(); -} - -void RenderContext::FillShape(Shape const& shape) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - if (shape.IsValid()) - { - render_target_->FillGeometry(shape.GetGeometry().get(), current_brush_->GetBrush().get()); - - IncreasePrimitivesCount(); - } -} - -void RenderContext::FillRectangle(Rect const& rect) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - render_target_->FillRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get()); - - IncreasePrimitivesCount(); -} - -void RenderContext::FillRoundedRectangle(Rect const& rect, Vec2 const& radius) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - render_target_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), - current_brush_->GetBrush().get()); - - IncreasePrimitivesCount(); -} - -void RenderContext::FillEllipse(Point const& center, Vec2 const& radius) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - - render_target_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), - current_brush_->GetBrush().get()); - - IncreasePrimitivesCount(); -} - -void RenderContext::CreateTexture(Texture& texture, math::Vec2T size) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - ComPtr saved_bitmap; - - HRESULT hr = render_target_->CreateBitmap(D2D1::SizeU(size.x, size.y), D2D1::BitmapProperties(), &saved_bitmap); - - if (SUCCEEDED(hr)) - { - texture.SetBitmap(saved_bitmap); - } - - win32::ThrowIfFailed(hr); -} - -void RenderContext::PushClipRect(Rect const& clip_rect) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->PushAxisAlignedClip(DX::ConvertToRectF(clip_rect), - antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); -} - -void RenderContext::PopClipRect() -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->PopAxisAlignedClip(); -} - -void RenderContext::PushLayer(LayerArea& layer) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - if (!layer.IsValid()) - { - ComPtr output; - HRESULT hr = render_target_->CreateLayer(&output); - - if (SUCCEEDED(hr)) - { - layer.SetLayer(output); - } - else - { - win32::ThrowIfFailed(hr); - } - } - - if (layer.IsValid()) - { - ComPtr mask; - if (layer.GetMaskShape()) - mask = layer.GetMaskShape()->GetGeometry(); - - render_target_->PushLayer( - D2D1::LayerParameters(DX::ConvertToRectF(layer.GetAreaRect()), mask.get(), - antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED, - DX::ConvertToMatrix3x2F(layer.GetMaskTransform()), layer.GetOpacity(), nullptr, - D2D1_LAYER_OPTIONS_NONE), - layer.GetLayer().get()); - } -} - -void RenderContext::PopLayer() -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->PopLayer(); -} - -void RenderContext::Clear() -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->Clear(); -} - -void RenderContext::Clear(Color const& clear_color) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->Clear(DX::ConvertToColorF(clear_color)); -} - -Size RenderContext::GetSize() const -{ - if (render_target_) - { - return reinterpret_cast(render_target_->GetSize()); - } - return Size(); -} - -void RenderContext::SetTransform(const Matrix3x2& matrix) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - if (fast_global_transform_) - { - render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); - } - else - { - Matrix3x2 result = matrix * global_transform_; - render_target_->SetTransform(DX::ConvertToMatrix3x2F(&result)); - } -} - void RenderContext::SetGlobalTransform(const Matrix3x2* matrix) { if (matrix) @@ -428,57 +62,6 @@ void RenderContext::SetGlobalTransform(const Matrix3x2* matrix) } } -void RenderContext::SetAntialiasMode(bool enabled) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - render_target_->SetAntialiasMode(enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); - antialias_ = enabled; -} - -void RenderContext::SetTextAntialiasMode(TextAntialiasMode mode) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - switch (mode) - { - case TextAntialiasMode::Default: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; - break; - case TextAntialiasMode::ClearType: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - break; - case TextAntialiasMode::GrayScale: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - break; - case TextAntialiasMode::None: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; - break; - default: - break; - } - - text_antialias_ = mode; - render_target_->SetTextAntialiasMode(antialias_mode); -} - -bool RenderContext::CheckVisibility(Rect const& bounds, Matrix3x2 const& transform) -{ - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - - if (fast_global_transform_) - { - return visible_size_.Intersects(transform.Transform(bounds)); - } - return visible_size_.Intersects(Matrix3x2(transform * global_transform_).Transform(bounds)); -} - -void RenderContext::Resize(Size const& size) -{ - visible_size_ = Rect(Point(), size); -} - void RenderContext::SetCollectingStatus(bool enable) { collecting_status_ = enable; @@ -492,24 +75,34 @@ void RenderContext::IncreasePrimitivesCount(uint32_t increase) const } } -void RenderContext::SaveDrawingState() +float RenderContext::GetBrushOpacity() const { - KGE_ASSERT(IsValid()); - - if (drawing_state_) - { - render_target_->SaveDrawingState(drawing_state_.get()); - } + return brush_opacity_; } -void RenderContext::RestoreDrawingState() +BrushPtr RenderContext::GetCurrentBrush() const { - KGE_ASSERT(IsValid()); + return current_brush_; +} - if (drawing_state_) - { - render_target_->RestoreDrawingState(drawing_state_.get()); - } +const Matrix3x2& RenderContext::GetGlobalTransform() const +{ + return global_transform_; +} + +void RenderContext::SetBrushOpacity(float opacity) +{ + brush_opacity_ = opacity; +} + +void RenderContext::SetGlobalTransform(const Matrix3x2& matrix) +{ + SetGlobalTransform(&matrix); +} + +void RenderContext::SetCurrentBrush(BrushPtr brush) +{ + current_brush_ = brush; } } // namespace kiwano diff --git a/src/kiwano/render/RenderContext.h b/src/kiwano/render/RenderContext.h index 6f45f207..0c8a853e 100644 --- a/src/kiwano/render/RenderContext.h +++ b/src/kiwano/render/RenderContext.h @@ -60,42 +60,36 @@ class KGE_API RenderContext : public virtual ObjectBase public: /// \~chinese /// @brief 是否有效 - bool IsValid() const; + virtual bool IsValid() const = 0; /// \~chinese /// @brief 开始渲染 - void BeginDraw(); + virtual void BeginDraw(); /// \~chinese /// @brief 结束渲染 - void EndDraw(); + virtual void EndDraw(); /// \~chinese /// @brief 绘制纹理 /// @param texture 纹理 /// @param src_rect 源纹理裁剪矩形 /// @param dest_rect 绘制的目标区域 - void DrawTexture(Texture const& texture, Rect const& src_rect, Rect const& dest_rect); - - /// \~chinese - /// @brief 绘制纹理 - /// @param texture 纹理 - /// @param src_rect 源纹理裁剪矩形 - /// @param dest_rect 绘制的目标区域 - void DrawTexture(Texture const& texture, const Rect* src_rect = nullptr, const Rect* dest_rect = nullptr); + virtual void DrawTexture(Texture const& texture, const Rect* src_rect = nullptr, + const Rect* dest_rect = nullptr) = 0; /// \~chinese /// @brief 绘制文本布局 /// @param layout 文本布局 /// @param offset 偏移量 - void DrawTextLayout(TextLayout const& layout, Point const& offset = Point()); + virtual void DrawTextLayout(TextLayout const& layout, Point const& offset = Point()) = 0; /// \~chinese /// @brief 绘制形状轮廓 /// @param shape 形状 /// @param stroke 线条样式 /// @param stroke_width 线条宽度 - void DrawShape(Shape const& shape, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f); + virtual void DrawShape(Shape const& shape, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f) = 0; /// \~chinese /// @brief 绘制线段 @@ -103,14 +97,15 @@ public: /// @param point2 线段终点 /// @param stroke 线条样式 /// @param stroke_width 线条宽度 - void DrawLine(Point const& point1, Point const& point2, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f); + virtual void DrawLine(Point const& point1, Point const& point2, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) = 0; /// \~chinese /// @brief 绘制矩形边框 /// @param rect 矩形 /// @param stroke 线条样式 /// @param stroke_width 线条宽度 - void DrawRectangle(Rect const& rect, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f); + virtual void DrawRectangle(Rect const& rect, StrokeStylePtr stroke = nullptr, float stroke_width = 1.0f) = 0; /// \~chinese /// @brief 绘制圆角矩形边框 @@ -118,8 +113,8 @@ public: /// @param radius 圆角半径 /// @param stroke 线条样式 /// @param stroke_width 线条宽度 - void DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, StrokeStylePtr stroke = nullptr, - float stroke_width = 1.0f); + virtual void DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) = 0; /// \~chinese /// @brief 绘制椭圆边框 @@ -127,115 +122,115 @@ public: /// @param radius 椭圆半径 /// @param stroke 线条样式 /// @param stroke_width 线条宽度 - void DrawEllipse(Point const& center, Vec2 const& radius, StrokeStylePtr stroke = nullptr, - float stroke_width = 1.0f); + virtual void DrawEllipse(Point const& center, Vec2 const& radius, StrokeStylePtr stroke = nullptr, + float stroke_width = 1.0f) = 0; /// \~chinese /// @brief 填充形状 /// @param shape 形状 - void FillShape(Shape const& shape); + virtual void FillShape(Shape const& shape) = 0; /// \~chinese /// @brief 填充矩形 /// @param rect 矩形 - void FillRectangle(Rect const& rect); + virtual void FillRectangle(Rect const& rect) = 0; /// \~chinese /// @brief 填充圆角矩形 /// @param rect 矩形 /// @param radius 圆角半径 - void FillRoundedRectangle(Rect const& rect, Vec2 const& radius); + virtual void FillRoundedRectangle(Rect const& rect, Vec2 const& radius) = 0; /// \~chinese /// @brief 填充椭圆 /// @param center 圆心 /// @param radius 椭圆半径 - void FillEllipse(Point const& center, Vec2 const& radius); + virtual void FillEllipse(Point const& center, Vec2 const& radius) = 0; /// \~chinese /// @brief 创建纹理 /// @param texture 纹理 /// @param size 纹理像素大小 - void CreateTexture(Texture& texture, math::Vec2T size); + virtual void CreateTexture(Texture& texture, math::Vec2T size) = 0; /// \~chinese /// @brief 设置绘制的裁剪区域 /// @param clip_rect 裁剪矩形 - void PushClipRect(Rect const& clip_rect); + virtual void PushClipRect(Rect const& clip_rect) = 0; /// \~chinese /// @brief 取消上一次设置的绘制裁剪区域 - void PopClipRect(); + virtual void PopClipRect() = 0; /// \~chinese /// @brief 设置图层区域 /// @param layer 图层区域 - void PushLayer(LayerArea& layer); + virtual void PushLayer(LayerArea& layer) = 0; /// \~chinese /// @brief 取消上一次设置的图层区域 - void PopLayer(); + virtual void PopLayer() = 0; /// \~chinese /// @brief 清空渲染内容 - void Clear(); + virtual void Clear() = 0; /// \~chinese /// @brief 使用纯色清空渲染内容 /// @param clear_color 清屏颜色 - void Clear(Color const& clear_color); + virtual void Clear(Color const& clear_color) = 0; /// \~chinese /// @brief 获取渲染区域大小 - Size GetSize() const; + virtual Size GetSize() const = 0; /// \~chinese /// @brief 获取画刷透明度 - float GetBrushOpacity() const; + virtual float GetBrushOpacity() const; /// \~chinese /// @brief 获取当前画刷 - BrushPtr GetCurrentBrush() const; + virtual BrushPtr GetCurrentBrush() const; /// \~chinese /// @brief 获取全局二维变换 - Matrix3x2 GetGlobalTransform() const; + virtual const Matrix3x2& GetGlobalTransform() const; /// \~chinese /// @brief 设置画刷透明度 - void SetBrushOpacity(float opacity); + virtual void SetBrushOpacity(float opacity); /// \~chinese /// @brief 设置当前画刷 - void SetCurrentBrush(BrushPtr brush); - - /// \~chinese - /// @brief 设置上下文的二维变换 - void SetTransform(const Matrix3x2& matrix); - - /// \~chinese - /// @brief 设置全局二维变换 - void SetGlobalTransform(const Matrix3x2& matrix); - - /// \~chinese - /// @brief 设置全局二维变换 - void SetGlobalTransform(const Matrix3x2* matrix); + virtual void SetCurrentBrush(BrushPtr brush); /// \~chinese /// @brief 设置抗锯齿模式 - void SetAntialiasMode(bool enabled); + virtual void SetAntialiasMode(bool enabled) = 0; /// \~chinese /// @brief 设置文字抗锯齿模式 - void SetTextAntialiasMode(TextAntialiasMode mode); + virtual void SetTextAntialiasMode(TextAntialiasMode mode) = 0; /// \~chinese /// @brief 检查边界是否在视区内 - bool CheckVisibility(Rect const& bounds, Matrix3x2 const& transform); + virtual bool CheckVisibility(Rect const& bounds, Matrix3x2 const& transform) = 0; /// \~chinese /// @brief 重设渲染上下文大小 - void Resize(Size const& size); + virtual void Resize(Size const& size) = 0; + + /// \~chinese + /// @brief 设置上下文的二维变换 + virtual void SetTransform(const Matrix3x2& matrix) = 0; + + /// \~chinese + /// @brief 设置全局二维变换 + virtual void SetGlobalTransform(const Matrix3x2& matrix); + + /// \~chinese + /// @brief 设置全局二维变换 + virtual void SetGlobalTransform(const Matrix3x2* matrix); public: /// \~chinese @@ -260,32 +255,11 @@ public: protected: RenderContext(); - ComPtr GetRenderTarget() const; - - ComPtr GetTextRenderer() const; - -private: - /// \~chinese - /// @brief 创建设备依赖资源 - HRESULT CreateDeviceResources(ComPtr factory, ComPtr ctx); - - /// \~chinese - /// @brief 销毁设备依赖资源 - void DiscardDeviceResources(); - /// \~chinese /// @brief 增加渲染图元数量 void IncreasePrimitivesCount(uint32_t increase = 1) const; - /// \~chinese - /// @brief 保存绘制状态 - void SaveDrawingState(); - - /// \~chinese - /// @brief 恢复绘制状态 - void RestoreDrawingState(); - -private: +protected: bool antialias_; bool fast_global_transform_; mutable bool collecting_status_; @@ -295,10 +269,6 @@ private: Rect visible_size_; Matrix3x2 global_transform_; mutable Status status_; - - ComPtr text_renderer_; - ComPtr render_target_; - ComPtr drawing_state_; }; /** @} */ @@ -313,50 +283,4 @@ inline RenderContext::Status const& RenderContext::GetStatus() const return status_; } -inline ComPtr RenderContext::GetRenderTarget() const -{ - KGE_ASSERT(render_target_); - return render_target_; -} - -inline ComPtr RenderContext::GetTextRenderer() const -{ - KGE_ASSERT(text_renderer_); - return text_renderer_; -} - -inline float RenderContext::GetBrushOpacity() const -{ - return brush_opacity_; -} - -inline BrushPtr RenderContext::GetCurrentBrush() const -{ - return current_brush_; -} - -inline Matrix3x2 RenderContext::GetGlobalTransform() const -{ - return global_transform_; -} - -inline void RenderContext::SetBrushOpacity(float opacity) -{ - brush_opacity_ = opacity; -} - -inline void RenderContext::SetGlobalTransform(const Matrix3x2& matrix) -{ - SetGlobalTransform(&matrix); -} - -inline void RenderContext::SetCurrentBrush(BrushPtr brush) -{ - current_brush_ = brush; - if (current_brush_) - { - current_brush_->SetOpacity(brush_opacity_); - } -} - } // namespace kiwano diff --git a/src/kiwano/render/Renderer.cpp b/src/kiwano/render/Renderer.cpp index 6850dbb5..36956eac 100644 --- a/src/kiwano/render/Renderer.cpp +++ b/src/kiwano/render/Renderer.cpp @@ -18,10 +18,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include -#include -#include -#include #include namespace kiwano @@ -34,934 +30,4 @@ Renderer::Renderer() { } -Renderer::~Renderer() {} - -void Renderer::SetupComponent() -{ - KGE_SYS_LOG(L"Creating device resources"); - - win32::ThrowIfFailed(::CoInitialize(nullptr)); - - target_window_ = Window::GetInstance().GetHandle(); - output_size_ = Window::GetInstance().GetSize(); - - d2d_res_ = nullptr; - d3d_res_ = nullptr; - - HRESULT hr = target_window_ ? S_OK : E_FAIL; - - // Direct3D device resources - if (SUCCEEDED(hr)) - { - hr = ID3DDeviceResources::Create(&d3d_res_, target_window_); - - // Direct2D device resources - if (SUCCEEDED(hr)) - { - hr = ID2DDeviceResources::Create(&d2d_res_, d3d_res_->GetDXGIDevice(), d3d_res_->GetDXGISwapChain()); - - // Other device resources - if (SUCCEEDED(hr)) - { - hr = render_ctx_.CreateDeviceResources(d2d_res_->GetFactory(), d2d_res_->GetDeviceContext()); - } - - // FontFileLoader and FontCollectionLoader - if (SUCCEEDED(hr)) - { - hr = IFontCollectionLoader::Create(&font_collection_loader_); - - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader(font_collection_loader_.get()); - } - } - - // ResourceFontFileLoader and ResourceFontCollectionLoader - if (SUCCEEDED(hr)) - { - hr = IResourceFontFileLoader::Create(&res_font_file_loader_); - - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetDWriteFactory()->RegisterFontFileLoader(res_font_file_loader_.get()); - } - - if (SUCCEEDED(hr)) - { - hr = IResourceFontCollectionLoader::Create(&res_font_collection_loader_, - res_font_file_loader_.get()); - - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader( - res_font_collection_loader_.get()); - } - } - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::DestroyComponent() -{ - KGE_SYS_LOG(L"Destroying device resources"); - - render_ctx_.DiscardDeviceResources(); - - d2d_res_->GetDWriteFactory()->UnregisterFontFileLoader(res_font_file_loader_.get()); - res_font_file_loader_.reset(); - - d2d_res_->GetDWriteFactory()->UnregisterFontCollectionLoader(res_font_collection_loader_.get()); - res_font_collection_loader_.reset(); - - d2d_res_.reset(); - d3d_res_.reset(); - - ::CoUninitialize(); -} - -void Renderer::BeginDraw() -{ - KGE_ASSERT(render_ctx_.IsValid()); - - render_ctx_.SaveDrawingState(); - render_ctx_.BeginDraw(); -} - -void Renderer::EndDraw() -{ - KGE_ASSERT(render_ctx_.IsValid()); - - render_ctx_.EndDraw(); - render_ctx_.RestoreDrawingState(); -} - -void Renderer::Clear() -{ - KGE_ASSERT(d3d_res_); - - HRESULT hr = d3d_res_->ClearRenderTarget(clear_color_); - win32::ThrowIfFailed(hr); -} - -void Renderer::Present() -{ - KGE_ASSERT(d3d_res_); - - HRESULT hr = d3d_res_->Present(vsync_); - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) - { - // 如果 Direct3D 设备在执行过程中消失,将丢弃当前的设备相关资源 - hr = HandleDeviceLost(); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::HandleEvent(Event* evt) -{ - if (evt->IsType()) - { - auto window_evt = dynamic_cast(evt); - ResizeTarget(window_evt->width, window_evt->height); - } -} - -HRESULT Renderer::HandleDeviceLost() -{ - KGE_ASSERT(d3d_res_ && d2d_res_ && render_ctx_.IsValid()); - - HRESULT hr = d3d_res_->HandleDeviceLost(); - - if (SUCCEEDED(hr)) - { - hr = d2d_res_->HandleDeviceLost(d3d_res_->GetDXGIDevice(), d3d_res_->GetDXGISwapChain()); - } - - if (SUCCEEDED(hr)) - { - hr = render_ctx_.CreateDeviceResources(d2d_res_->GetFactory(), d2d_res_->GetDeviceContext()); - } - return hr; -} - -void Renderer::CreateTexture(Texture& texture, String const& file_path) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (!FileSystem::GetInstance().IsFileExists(file_path)) - { - KGE_WARN(L"Texture file '%s' not found!", file_path.c_str()); - hr = E_FAIL; - } - - if (SUCCEEDED(hr)) - { - String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); - - ComPtr decoder; - hr = d2d_res_->CreateBitmapDecoderFromFile(decoder, full_path); - - if (SUCCEEDED(hr)) - { - ComPtr source; - hr = decoder->GetFrame(0, &source); - - if (SUCCEEDED(hr)) - { - ComPtr converter; - hr = d2d_res_->CreateBitmapConverter(converter, source, GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, nullptr, 0.f, - WICBitmapPaletteTypeMedianCut); - - if (SUCCEEDED(hr)) - { - ComPtr bitmap; - hr = d2d_res_->CreateBitmapFromConverter(bitmap, nullptr, converter); - - if (SUCCEEDED(hr)) - { - texture.SetBitmap(bitmap); - } - } - } - } - } - - if (FAILED(hr)) - { - KGE_WARN(L"Load texture failed with HRESULT of %08X!", hr); - } -} - -void Renderer::CreateTexture(Texture& texture, Resource const& resource) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr decoder; - hr = d2d_res_->CreateBitmapDecoderFromResource(decoder, resource); - - if (SUCCEEDED(hr)) - { - ComPtr source; - hr = decoder->GetFrame(0, &source); - - if (SUCCEEDED(hr)) - { - ComPtr converter; - hr = d2d_res_->CreateBitmapConverter(converter, source, GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, nullptr, 0.f, - WICBitmapPaletteTypeMedianCut); - - if (SUCCEEDED(hr)) - { - ComPtr bitmap; - hr = d2d_res_->CreateBitmapFromConverter(bitmap, nullptr, converter); - - if (SUCCEEDED(hr)) - { - texture.SetBitmap(bitmap); - } - } - } - } - } - - if (FAILED(hr)) - { - KGE_WARN(L"Load texture failed with HRESULT of %08X!", hr); - } -} - -void Renderer::CreateGifImage(GifImage& gif, String const& file_path) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (!FileSystem::GetInstance().IsFileExists(file_path)) - { - KGE_WARN(L"Gif texture file '%s' not found!", file_path.c_str()); - hr = E_FAIL; - } - - if (SUCCEEDED(hr)) - { - String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); - - ComPtr decoder; - hr = d2d_res_->CreateBitmapDecoderFromFile(decoder, full_path); - - if (SUCCEEDED(hr)) - { - gif.SetDecoder(decoder); - } - } - - if (FAILED(hr)) - { - KGE_WARN(L"Load GIF texture failed with HRESULT of %08X!", hr); - } -} - -void Renderer::CreateGifImage(GifImage& gif, Resource const& resource) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr decoder; - hr = d2d_res_->CreateBitmapDecoderFromResource(decoder, resource); - - if (SUCCEEDED(hr)) - { - gif.SetDecoder(decoder); - } - } - - if (FAILED(hr)) - { - KGE_WARN(L"Load GIF texture failed with HRESULT of %08X!", hr); - } -} - -void Renderer::CreateGifImageFrame(GifImage::Frame& frame, GifImage const& gif, size_t frame_index) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (gif.GetDecoder() == nullptr) - { - hr = E_INVALIDARG; - } - - if (SUCCEEDED(hr)) - { - ComPtr wic_frame; - - HRESULT hr = gif.GetDecoder()->GetFrame(UINT(frame_index), &wic_frame); - - if (SUCCEEDED(hr)) - { - ComPtr converter; - d2d_res_->CreateBitmapConverter(converter, wic_frame, GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, nullptr, 0.f, WICBitmapPaletteTypeCustom); - - if (SUCCEEDED(hr)) - { - ComPtr raw_bitmap; - hr = d2d_res_->CreateBitmapFromConverter(raw_bitmap, nullptr, converter); - - if (SUCCEEDED(hr)) - { - frame.texture = new Texture; - frame.texture->SetBitmap(raw_bitmap); - } - } - } - - if (SUCCEEDED(hr)) - { - PROPVARIANT prop_val; - PropVariantInit(&prop_val); - - // Get Metadata Query Reader from the frame - ComPtr metadata_reader; - hr = wic_frame->GetMetadataQueryReader(&metadata_reader); - - // Get the Metadata for the current frame - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Left", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame.rect.left_top.x = static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Top", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame.rect.left_top.y = static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Width", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame.rect.right_bottom.x = frame.rect.left_top.x + static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Height", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame.rect.right_bottom.y = frame.rect.left_top.y + static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/grctlext/Delay", &prop_val); - - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - - if (SUCCEEDED(hr)) - { - uint32_t udelay = 0; - - hr = UIntMult(prop_val.uiVal, 10, &udelay); - if (SUCCEEDED(hr)) - { - frame.delay.SetMilliseconds(static_cast(udelay)); - } - } - PropVariantClear(&prop_val); - } - else - { - frame.delay = 0; - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/grctlext/Disposal", &prop_val); - - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI1) ? S_OK : E_FAIL; - if (SUCCEEDED(hr)) - { - frame.disposal_type = GifImage::DisposalType(prop_val.bVal); - } - ::PropVariantClear(&prop_val); - } - else - { - frame.disposal_type = GifImage::DisposalType::Unknown; - } - } - - ::PropVariantClear(&prop_val); - } - } - - if (FAILED(hr)) - { - KGE_WARN(L"Load GIF frame failed with HRESULT of %08X!", hr); - } -} - -void Renderer::CreateFontCollection(Font& font, String const& file_path) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - if (!FileSystem::GetInstance().IsFileExists(file_path)) - { - KGE_WARN(L"Font file '%s' not found!", file_path.c_str()); - hr = E_FAIL; - } - } - - if (SUCCEEDED(hr)) - { - LPVOID key = nullptr; - uint32_t key_size = 0; - String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); - - hr = font_collection_loader_->AddFilePaths({ full_path }, &key, &key_size); - - if (SUCCEEDED(hr)) - { - ComPtr font_collection; - hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection(font_collection_loader_.get(), key, key_size, - &font_collection); - - if (SUCCEEDED(hr)) - { - font.SetCollection(font_collection); - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateFontCollection(Font& font, Resource const& res) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - LPVOID key = nullptr; - uint32_t key_size = 0; - - hr = res_font_collection_loader_->AddResources(Vector{ res }, &key, &key_size); - - if (SUCCEEDED(hr)) - { - ComPtr font_collection; - hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection(res_font_collection_loader_.get(), key, - key_size, &font_collection); - - if (SUCCEEDED(hr)) - { - font.SetCollection(font_collection); - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateTextFormat(TextLayout& layout) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - const TextStyle& style = layout.GetStyle(); - - hr = d2d_res_->CreateTextFormat(output, style.font_family, style.font ? style.font->GetCollection() : nullptr, - DWRITE_FONT_WEIGHT(style.font_weight), - style.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, style.font_size); - } - - if (SUCCEEDED(hr)) - { - layout.SetTextFormat(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateTextLayout(TextLayout& layout) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->CreateTextLayout(output, layout.GetText(), layout.GetTextFormat()); - } - - if (SUCCEEDED(hr)) - { - layout.SetTextLayout(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr path_geo; - ComPtr path_sink; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetFactory()->CreatePathGeometry(&path_geo); - } - - if (SUCCEEDED(hr)) - { - hr = path_geo->Open(&path_sink); - } - - if (SUCCEEDED(hr)) - { - path_sink->BeginFigure(DX::ConvertToPoint2F(begin_pos), D2D1_FIGURE_BEGIN_FILLED); - path_sink->AddLine(DX::ConvertToPoint2F(end_pos)); - path_sink->EndFigure(D2D1_FIGURE_END_OPEN); - hr = path_sink->Close(); - } - - if (SUCCEEDED(hr)) - { - shape.SetGeometry(path_geo); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateRectShape(Shape& shape, Rect const& rect) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetFactory()->CreateRectangleGeometry(DX::ConvertToRectF(rect), &output); - } - - if (SUCCEEDED(hr)) - { - shape.SetGeometry(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateRoundedRectShape(Shape& shape, Rect const& rect, Vec2 const& radius) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetFactory()->CreateRoundedRectangleGeometry( - D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), &output); - } - - if (SUCCEEDED(hr)) - { - shape.SetGeometry(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateEllipseShape(Shape& shape, Point const& center, Vec2 const& radius) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetFactory()->CreateEllipseGeometry( - D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), &output); - } - - if (SUCCEEDED(hr)) - { - shape.SetGeometry(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateShapeSink(ShapeSink& sink) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - ComPtr output; - if (SUCCEEDED(hr)) - { - hr = d2d_res_->GetFactory()->CreatePathGeometry(&output); - } - - if (SUCCEEDED(hr)) - { - sink.SetPathGeometry(output); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateTextureRenderContext(TextureRenderContext& render_context, const Size* desired_size) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr bitmap_rt; - - if (desired_size) - { - hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(DX::ConvertToSizeF(*desired_size), - &bitmap_rt); - } - else - { - hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(&bitmap_rt); - } - - if (SUCCEEDED(hr)) - { - hr = render_context.CreateDeviceResources(d2d_res_->GetFactory(), bitmap_rt); - } - - if (SUCCEEDED(hr)) - { - render_context.SetBitmapRenderTarget(bitmap_rt); - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateBrush(Brush& brush, Color const& color) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr solid_brush; - - if (brush.GetType() == Brush::Type::SolidColor && brush.GetBrush()) - { - hr = brush.GetBrush()->QueryInterface(&solid_brush); - if (SUCCEEDED(hr)) - { - solid_brush->SetColor(DX::ConvertToColorF(color)); - } - } - else - { - hr = d2d_res_->GetDeviceContext()->CreateSolidColorBrush(DX::ConvertToColorF(color), &solid_brush); - - if (SUCCEEDED(hr)) - { - brush.SetBrush(solid_brush, Brush::Type::SolidColor); - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateBrush(Brush& brush, LinearGradientStyle const& style) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr collection; - hr = d2d_res_->GetDeviceContext()->CreateGradientStopCollection( - reinterpret_cast(&style.stops[0]), UINT32(style.stops.size()), D2D1_GAMMA_2_2, - D2D1_EXTEND_MODE(style.extend_mode), &collection); - - if (SUCCEEDED(hr)) - { - ComPtr output; - hr = d2d_res_->GetDeviceContext()->CreateLinearGradientBrush( - D2D1::LinearGradientBrushProperties(DX::ConvertToPoint2F(style.begin), DX::ConvertToPoint2F(style.end)), - collection.get(), &output); - - if (SUCCEEDED(hr)) - { - brush.SetBrush(output, Brush::Type::LinearGradient); - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateBrush(Brush& brush, RadialGradientStyle const& style) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - ComPtr collection; - hr = d2d_res_->GetDeviceContext()->CreateGradientStopCollection( - reinterpret_cast(&style.stops[0]), UINT32(style.stops.size()), D2D1_GAMMA_2_2, - D2D1_EXTEND_MODE(style.extend_mode), &collection); - - if (SUCCEEDED(hr)) - { - ComPtr output; - hr = d2d_res_->GetDeviceContext()->CreateRadialGradientBrush( - D2D1::RadialGradientBrushProperties(DX::ConvertToPoint2F(style.center), - DX::ConvertToPoint2F(style.offset), style.radius.x, style.radius.y), - collection.get(), &output); - - if (SUCCEEDED(hr)) - { - brush.SetBrush(output, Brush::Type::RadialGradient); - } - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::CreateStrokeStyle(StrokeStyle& stroke_style, CapStyle cap, LineJoinStyle line_join, - const float* dash_array, size_t dash_size, float dash_offset) -{ - HRESULT hr = S_OK; - if (!d2d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - D2D1_STROKE_STYLE_PROPERTIES style = - D2D1::StrokeStyleProperties(D2D1_CAP_STYLE(cap), D2D1_CAP_STYLE(cap), D2D1_CAP_STYLE(cap), - D2D1_LINE_JOIN(line_join), 10.0f, D2D1_DASH_STYLE_CUSTOM, dash_offset); - - ComPtr output; - hr = d2d_res_->GetFactory()->CreateStrokeStyle(style, dash_array, dash_size, &output); - - if (SUCCEEDED(hr)) - { - stroke_style.SetStrokeStyle(output); - } - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::SetDpi(float dpi) -{ - KGE_ASSERT(d3d_res_ && d2d_res_); - - HRESULT hr = d3d_res_->SetDpi(dpi); - if (SUCCEEDED(hr)) - { - hr = d2d_res_->SetDpi(dpi); - } - - win32::ThrowIfFailed(hr); -} - -void Renderer::SetVSyncEnabled(bool enabled) -{ - vsync_ = enabled; -} - -void Renderer::SetClearColor(const Color& color) -{ - clear_color_ = color; -} - -void Renderer::ResizeTarget(uint32_t width, uint32_t height) -{ - HRESULT hr = S_OK; - - if (!d3d_res_) - hr = E_UNEXPECTED; - - if (SUCCEEDED(hr)) - { - output_size_.x = static_cast(width); - output_size_.y = static_cast(height); - - hr = d3d_res_->SetLogicalSize(output_size_); - } - - if (SUCCEEDED(hr)) - { - hr = d2d_res_->SetLogicalSize(output_size_); - } - - if (SUCCEEDED(hr)) - { - render_ctx_.Resize(output_size_); - } - - win32::ThrowIfFailed(hr); -} - } // namespace kiwano diff --git a/src/kiwano/render/Renderer.h b/src/kiwano/render/Renderer.h index f5e9e756..c820aa7d 100644 --- a/src/kiwano/render/Renderer.h +++ b/src/kiwano/render/Renderer.h @@ -26,23 +26,10 @@ #include #include #include -#include - -#if defined(KGE_USE_DIRECTX10) -#include -#else -#include -#endif namespace kiwano { -#if defined(KGE_USE_DIRECTX10) -typedef ID3D10DeviceResources ID3DDeviceResources; -#else -typedef ID3D11DeviceResources ID3DDeviceResources; -#endif - /** * \~chinese * \defgroup Render 渲染引擎 @@ -57,137 +44,127 @@ typedef ID3D11DeviceResources ID3DDeviceResources; * \~chinese * @brief 渲染器 */ -class KGE_API Renderer - : public Singleton - , public EventComponent +class KGE_API Renderer : public EventComponent { - friend Singleton; - public: + /// \~chinese + /// @brief 获取实例 + static Renderer& GetInstance(); + /// \~chinese /// @brief 获取清屏颜色 - Color const& GetClearColor() const; + virtual Color GetClearColor() const; /// \~chinese /// @brief 设置清屏颜色 - void SetClearColor(Color const& clear_color); + virtual void SetClearColor(Color const& clear_color); /// \~chinese /// @brief 开启或关闭垂直同步 - void SetVSyncEnabled(bool enabled); - - /// \~chinese - /// @brief 设置DPI - void SetDpi(float dpi); + virtual void SetVSyncEnabled(bool enabled); /// \~chinese /// @brief 创建纹理 /// @param[out] texture 纹理 /// @param[in] file_path 图片路径 - void CreateTexture(Texture& texture, String const& file_path); + virtual void CreateTexture(Texture& texture, String const& file_path) = 0; /// \~chinese /// @brief 创建纹理 /// @param[out] texture 纹理 /// @param[in] resource 图片资源 - void CreateTexture(Texture& texture, Resource const& resource); + virtual void CreateTexture(Texture& texture, Resource const& resource) = 0; /// \~chinese /// @brief 创建GIF图像 /// @param[out] gif GIF图像 /// @param[in] file_path 图片路径 - void CreateGifImage(GifImage& gif, String const& file_path); + virtual void CreateGifImage(GifImage& gif, String const& file_path) = 0; /// \~chinese /// @brief 创建GIF图像 /// @param[out] gif GIF图像 /// @param[in] resource 图片资源 - void CreateGifImage(GifImage& gif, Resource const& resource); + virtual void CreateGifImage(GifImage& gif, Resource const& resource) = 0; /// \~chinese /// @brief 创建GIF图像帧 /// @param[out] frame GIF图像帧 /// @param[in] gif GIF图像 /// @param[in] frame_index 帧下标 - void CreateGifImageFrame(GifImage::Frame& frame, GifImage const& gif, size_t frame_index); + virtual void CreateGifImageFrame(GifImage::Frame& frame, GifImage const& gif, size_t frame_index) = 0; /// \~chinese /// @brief 创建字体集 /// @param[out] font 字体 /// @param[in] file_paths 字体文件路径 - void CreateFontCollection(Font& font, String const& file_path); + virtual void CreateFontCollection(Font& font, String const& file_path) = 0; /// \~chinese /// @brief 创建字体集 /// @param[out] font 字体 /// @param[in] res_arr 字体资源 - void CreateFontCollection(Font& font, Resource const& res); + virtual void CreateFontCollection(Font& font, Resource const& res) = 0; /// \~chinese /// @brief 创建文字格式 /// @param[out] layout 字体布局 - void CreateTextFormat(TextLayout& layout); + virtual void CreateTextFormat(TextLayout& layout) = 0; /// \~chinese /// @brief 创建文字布局 /// @param[out] layout 字体布局 - void CreateTextLayout(TextLayout& layout); + virtual void CreateTextLayout(TextLayout& layout) = 0; /// \~chinese /// @brief 创建线段形状 /// @param[out] geo 形状 /// @param[in] begin_pos 线段起点 /// @param[in] end_pos 线段终点 - void CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos); + virtual void CreateLineShape(Shape& shape, Point const& begin_pos, Point const& end_pos) = 0; /// \~chinese /// @brief 创建矩形形状 /// @param[out] geo 形状 /// @param[in] rect 矩形大小 - void CreateRectShape(Shape& shape, Rect const& rect); + virtual void CreateRectShape(Shape& shape, Rect const& rect) = 0; /// \~chinese /// @brief 创建圆角矩形形状 /// @param[out] geo 形状 /// @param[in] rect 矩形大小 /// @param[in] radius 圆角半径 - void CreateRoundedRectShape(Shape& shape, Rect const& rect, Vec2 const& radius); + virtual void CreateRoundedRectShape(Shape& shape, Rect const& rect, Vec2 const& radius) = 0; /// \~chinese /// @brief 创建椭圆形状 /// @param[out] geo 形状 /// @param[in] center 椭圆圆心 /// @param[in] radius 椭圆半径 - void CreateEllipseShape(Shape& shape, Point const& center, Vec2 const& radius); + virtual void CreateEllipseShape(Shape& shape, Point const& center, Vec2 const& radius) = 0; /// \~chinese /// @brief 创建几何图形生成器 /// @param[out] sink 形状生成器 - void CreateShapeSink(ShapeSink& sink); - - /// \~chinese - /// @brief 创建纹理渲染上下文 - /// @param[out] render_context 渲染上下文 - /// @param[in] desired_size 期望的输出大小 - void CreateTextureRenderContext(TextureRenderContext& render_context, const Size* desired_size = nullptr); + virtual void CreateShapeSink(ShapeSink& sink) = 0; /// \~chinese /// @brief 创建纯色画刷 /// @param[out] brush 画刷 /// @param[in] color 颜色 - void CreateBrush(Brush& brush, Color const& color); + virtual void CreateBrush(Brush& brush, Color const& color) = 0; /// \~chinese /// @brief 创建线性渐变画刷 /// @param[out] brush 画刷 /// @param[in] style 线性渐变样式 - void CreateBrush(Brush& brush, LinearGradientStyle const& style); + virtual void CreateBrush(Brush& brush, LinearGradientStyle const& style) = 0; /// \~chinese /// @brief 创建径向渐变画刷 /// @param[out] brush 画刷 /// @param[in] style 径向渐变样式 - void CreateBrush(Brush& brush, RadialGradientStyle const& style); + virtual void CreateBrush(Brush& brush, RadialGradientStyle const& style) = 0; /// \~chinese /// @brief 创建线条样式 @@ -197,74 +174,52 @@ public: /// @param[in] dash_array 虚线长度与间隙数组 /// @param[in] dash_size 虚线数组大小 /// @param[in] dash_offset 虚线偏移量 - void CreateStrokeStyle(StrokeStyle& stroke_style, CapStyle cap, LineJoinStyle line_join, const float* dash_array, - size_t dash_size, float dash_offset); + virtual void CreateStrokeStyle(StrokeStyle& stroke_style, CapStyle cap, LineJoinStyle line_join, + const float* dash_array, size_t dash_size, float dash_offset) = 0; + + /// \~chinese + /// @brief 创建纹理渲染上下文 + /// @param[in] desired_size 期望的输出大小 + /// @return 纹理渲染上下文 + virtual TextureRenderContextPtr CreateTextureRenderContext(const Size* desired_size = nullptr) = 0; public: /// \~chinese /// @brief 开始渲染 - void BeginDraw(); + virtual void BeginDraw() = 0; /// \~chinese /// @brief 结束渲染 - void EndDraw(); + virtual void EndDraw() = 0; /// \~chinese /// @brief 清除绘制内容 - void Clear(); + virtual void Clear() = 0; /// \~chinese /// @brief 将绘制内容呈现至窗口 - void Present(); + virtual void Present() = 0; /// \~chinese /// @brief 获取渲染上下文 - RenderContext& GetContext(); + virtual RenderContext& GetContext() = 0; /// \~chinese /// @brief 获取目标窗口 - WindowHandle GetTargetWindow() const; + virtual WindowHandle GetTargetWindow() const; /// \~chinese /// @brief 获取渲染输出大小 - Size const& GetOutputSize() const; + virtual Size GetOutputSize() const; - /// \~chinese - /// @brief 获取Direct2D设备资源 - ID2DDeviceResources* GetD2DDeviceResources(); - - /// \~chinese - /// @brief 获取Direct3D设备资源 - ID3DDeviceResources* GetD3DDeviceResources(); - -public: - void SetupComponent() override; - - void DestroyComponent() override; - - void HandleEvent(Event* evt) override; - -private: +protected: Renderer(); - ~Renderer(); - - HRESULT HandleDeviceLost(); - - void ResizeTarget(uint32_t width, uint32_t height); - -private: - bool vsync_; - WindowHandle target_window_; - Color clear_color_; - Size output_size_; - RenderContext render_ctx_; - - ComPtr d2d_res_; - ComPtr d3d_res_; - ComPtr font_collection_loader_; - ComPtr res_font_file_loader_; - ComPtr res_font_collection_loader_; +protected: + bool vsync_; + WindowHandle target_window_; + Color clear_color_; + Size output_size_; }; /** @} */ @@ -274,30 +229,24 @@ inline WindowHandle Renderer::GetTargetWindow() const return target_window_; } -inline Size const& Renderer::GetOutputSize() const +inline Size Renderer::GetOutputSize() const { return output_size_; } -inline Color const& Renderer::GetClearColor() const +inline Color Renderer::GetClearColor() const { return clear_color_; } -inline RenderContext& Renderer::GetContext() +inline void Renderer::SetVSyncEnabled(bool enabled) { - return render_ctx_; + vsync_ = enabled; } -inline ID2DDeviceResources* Renderer::GetD2DDeviceResources() +inline void Renderer::SetClearColor(const Color& color) { - KGE_ASSERT(d2d_res_); - return d2d_res_.get(); + clear_color_ = color; } -inline ID3DDeviceResources* Renderer::GetD3DDeviceResources() -{ - KGE_ASSERT(d3d_res_); - return d3d_res_.get(); -} } // namespace kiwano diff --git a/src/kiwano/render/Shape.h b/src/kiwano/render/Shape.h index 85930f9a..768bfba8 100644 --- a/src/kiwano/render/Shape.h +++ b/src/kiwano/render/Shape.h @@ -24,8 +24,6 @@ namespace kiwano { -class RenderContext; -class Renderer; class ShapeSink; KGE_DECLARE_SMART_PTR(Shape); @@ -41,8 +39,6 @@ KGE_DECLARE_SMART_PTR(Shape); */ class KGE_API Shape : public virtual ObjectBase { - friend class RenderContext; - friend class Renderer; friend class ShapeSink; public: @@ -116,7 +112,7 @@ public: void Clear(); #if defined(KGE_WIN32) -private: +public: ComPtr GetGeometry() const; void SetGeometry(ComPtr shape); diff --git a/src/kiwano/render/ShapeSink.h b/src/kiwano/render/ShapeSink.h index 1cccf39c..280b09cc 100644 --- a/src/kiwano/render/ShapeSink.h +++ b/src/kiwano/render/ShapeSink.h @@ -23,8 +23,6 @@ namespace kiwano { -class RenderContext; -class Renderer; /** * \addtogroup Render @@ -45,8 +43,6 @@ enum class CombineMode /// @brief 形状生成器 class KGE_API ShapeSink : protected Noncopyable { - friend class Renderer; - public: ShapeSink(); @@ -137,7 +133,7 @@ private: ShapePtr shape_; #if defined(KGE_WIN32) -private: +public: ComPtr GetPathGeometry() const; void SetPathGeometry(ComPtr path); @@ -146,6 +142,7 @@ private: void SetGeometrySink(ComPtr sink); +private: ComPtr sink_; ComPtr path_geo_; #endif diff --git a/src/kiwano/render/StrokeStyle.h b/src/kiwano/render/StrokeStyle.h index ec104f95..f4da804c 100644 --- a/src/kiwano/render/StrokeStyle.h +++ b/src/kiwano/render/StrokeStyle.h @@ -24,8 +24,6 @@ namespace kiwano { -class RenderContext; -class Renderer; KGE_DECLARE_SMART_PTR(StrokeStyle); @@ -71,9 +69,6 @@ enum class DashStyle /// @brief 线条样式 class StrokeStyle : public virtual ObjectBase { - friend class RenderContext; - friend class Renderer; - public: /// \~chinese /// @brief 创建线条样式 @@ -115,7 +110,7 @@ public: bool IsValid() const; #if defined(KGE_WIN32) -private: +public: ComPtr GetStrokeStyle() const; void SetStrokeStyle(ComPtr style); diff --git a/src/kiwano/render/TextLayout.h b/src/kiwano/render/TextLayout.h index 1f01f241..0652b98d 100644 --- a/src/kiwano/render/TextLayout.h +++ b/src/kiwano/render/TextLayout.h @@ -24,8 +24,6 @@ namespace kiwano { -class RenderContext; -class Renderer; /** * \addtogroup Render @@ -36,9 +34,6 @@ class Renderer; /// @brief 文本布局 class KGE_API TextLayout { - friend class RenderContext; - friend class Renderer; - public: /// \~chinese /// @brief 构造空的文本布局 @@ -165,8 +160,13 @@ public: void SetDirtyFlag(uint8_t flag); -#if defined(KGE_WIN32) private: + uint8_t dirty_flag_; + String text_; + TextStyle style_; + +#if defined(KGE_WIN32) +public: ComPtr GetTextFormat() const; void SetTextFormat(ComPtr format); @@ -175,15 +175,10 @@ private: void SetTextLayout(ComPtr layout); +private: ComPtr text_format_; ComPtr text_layout_; #endif - -private: - uint8_t dirty_flag_; - - String text_; - TextStyle style_; }; /** @} */ diff --git a/src/kiwano/render/Texture.cpp b/src/kiwano/render/Texture.cpp index 4c9248fa..219f7dc6 100644 --- a/src/kiwano/render/Texture.cpp +++ b/src/kiwano/render/Texture.cpp @@ -181,15 +181,6 @@ InterpolationMode Texture::GetDefaultInterpolationMode() } #if defined(KGE_WIN32) -D2D1_PIXEL_FORMAT Texture::GetPixelFormat() const -{ - if (bitmap_) - { - return bitmap_->GetPixelFormat(); - } - return D2D1_PIXEL_FORMAT(); -} - ComPtr Texture::GetBitmap() const { return bitmap_; diff --git a/src/kiwano/render/Texture.h b/src/kiwano/render/Texture.h index 715a32df..fada3dce 100644 --- a/src/kiwano/render/Texture.h +++ b/src/kiwano/render/Texture.h @@ -24,9 +24,6 @@ namespace kiwano { -class RenderContext; -class TextureRenderContext; -class Renderer; KGE_DECLARE_SMART_PTR(Texture); @@ -52,10 +49,6 @@ enum class InterpolationMode */ class KGE_API Texture : public virtual ObjectBase { - friend class RenderContext; - friend class TextureRenderContext; - friend class Renderer; - public: /// \~chinese /// @brief 从本地文件创建纹理 @@ -133,8 +126,13 @@ public: /// @brief 获取默认的像素插值方式 static InterpolationMode GetDefaultInterpolationMode(); -#if defined(KGE_WIN32) private: + InterpolationMode interpolation_mode_; + + static InterpolationMode default_interpolation_mode_; + +#if defined(KGE_WIN32) +public: /// \~chinese /// @brief 获取源位图 ComPtr GetBitmap() const; @@ -143,17 +141,9 @@ private: /// @brief 设置源位图 void SetBitmap(ComPtr bitmap); - /// \~chinese - /// @brief 获取像素格式 - D2D1_PIXEL_FORMAT GetPixelFormat() const; - +private: ComPtr bitmap_; #endif - -private: - InterpolationMode interpolation_mode_; - - static InterpolationMode default_interpolation_mode_; }; /** @} */ diff --git a/src/kiwano/render/TextureRenderContext.cpp b/src/kiwano/render/TextureRenderContext.cpp index f2995b8c..19e6bc62 100644 --- a/src/kiwano/render/TextureRenderContext.cpp +++ b/src/kiwano/render/TextureRenderContext.cpp @@ -27,56 +27,30 @@ namespace kiwano TextureRenderContextPtr TextureRenderContext::Create() { - TextureRenderContextPtr ptr = new TextureRenderContext; - if (ptr) + TextureRenderContextPtr ptr; + try { - try - { - Renderer::GetInstance().CreateTextureRenderContext(*ptr); - } - catch (std::exception) - { - return nullptr; - } + ptr = Renderer::GetInstance().CreateTextureRenderContext(nullptr); + } + catch (std::exception&) + { + return nullptr; } return ptr; } TextureRenderContextPtr TextureRenderContext::Create(Size const& desired_size) { - TextureRenderContextPtr ptr = new TextureRenderContext; - if (ptr) + TextureRenderContextPtr ptr; + try { - try - { - Renderer::GetInstance().CreateTextureRenderContext(*ptr, &desired_size); - } - catch (std::exception) - { - return nullptr; - } + ptr = Renderer::GetInstance().CreateTextureRenderContext(&desired_size); + } + catch (std::exception&) + { + return nullptr; } return ptr; } -TextureRenderContext::TextureRenderContext() {} - -bool TextureRenderContext::GetOutput(Texture& texture) -{ - HRESULT hr = E_FAIL; - - if (bitmap_rt_) - { - ComPtr bitmap; - - hr = bitmap_rt_->GetBitmap(&bitmap); - - if (SUCCEEDED(hr)) - { - texture.SetBitmap(bitmap); - } - } - return SUCCEEDED(hr); -} - } // namespace kiwano diff --git a/src/kiwano/render/TextureRenderContext.h b/src/kiwano/render/TextureRenderContext.h index ed412152..27746a06 100644 --- a/src/kiwano/render/TextureRenderContext.h +++ b/src/kiwano/render/TextureRenderContext.h @@ -23,8 +23,6 @@ namespace kiwano { -class Renderer; - KGE_DECLARE_SMART_PTR(TextureRenderContext); /** @@ -35,10 +33,8 @@ KGE_DECLARE_SMART_PTR(TextureRenderContext); /// \~chinese /// @brief 纹理渲染上下文 /// @details 纹理渲染上下文将渲染输出到一个纹理对象中 -class KGE_API TextureRenderContext : public RenderContext +class KGE_API TextureRenderContext : public virtual RenderContext { - friend class Renderer; - public: /// \~chinese /// @brief 创建纹理渲染上下文 @@ -49,41 +45,13 @@ public: /// @param size 期望的输出大小 static TextureRenderContextPtr Create(Size const& desired_size); - /// \~chinese - /// @brief 是否有效 - bool IsValid() const; - /// \~chinese /// @brief 获取渲染输出 /// @param[out] texture 纹理输出 /// @return 操作是否成功 - bool GetOutput(Texture& texture); - -private: - TextureRenderContext(); - - ComPtr GetBitmapRenderTarget() const; - - void SetBitmapRenderTarget(ComPtr ctx); - -private: - ComPtr bitmap_rt_; + virtual bool GetOutput(Texture& texture) = 0; }; /** @} */ -inline bool TextureRenderContext::IsValid() const -{ - return bitmap_rt_ != nullptr; -} - -inline ComPtr TextureRenderContext::GetBitmapRenderTarget() const -{ - return bitmap_rt_; -} - -inline void TextureRenderContext::SetBitmapRenderTarget(ComPtr ctx) -{ - bitmap_rt_ = ctx; -} } // namespace kiwano