From dc5ec25c001790b731285f8419965afe9987ce33 Mon Sep 17 00:00:00 2001 From: Nomango Date: Tue, 19 Sep 2023 20:25:06 +0800 Subject: [PATCH 01/14] feat: update to direct2d 1.3 --- projects/kiwano/kiwano.vcxproj | 1 - projects/kiwano/kiwano.vcxproj.filters | 15 +- src/kiwano/2d/Canvas.cpp | 26 +-- src/kiwano/2d/Canvas.h | 17 +- src/kiwano/2d/GifSprite.cpp | 4 +- src/kiwano/2d/TextActor.cpp | 3 +- src/kiwano/CMakeLists.txt | 5 +- src/kiwano/render/Brush.cpp | 6 +- src/kiwano/render/Brush.h | 2 +- .../render/DirectX/D2DDeviceResources.cpp | 16 +- .../render/DirectX/D2DDeviceResources.h | 9 - .../render/DirectX/FontCollectionLoader.cpp | 17 +- .../render/DirectX/FontCollectionLoader.h | 6 + src/kiwano/render/DirectX/NativePtr.h | 79 ------- .../render/DirectX/RenderContextImpl.cpp | 216 ++++++++++-------- src/kiwano/render/DirectX/RenderContextImpl.h | 17 +- src/kiwano/render/DirectX/RendererImpl.cpp | 148 ++++++------ src/kiwano/render/DirectX/RendererImpl.h | 4 +- .../render/DirectX/TextDrawingEffect.cpp | 10 +- src/kiwano/render/DirectX/TextDrawingEffect.h | 10 +- src/kiwano/render/DirectX/TextRenderer.cpp | 63 ++--- src/kiwano/render/DirectX/TextRenderer.h | 13 +- src/kiwano/render/DirectX/helper.h | 25 +- src/kiwano/render/Font.h | 2 +- src/kiwano/render/GifImage.cpp | 3 +- src/kiwano/render/NativeObject.cpp | 5 +- src/kiwano/render/NativeObject.h | 48 +++- src/kiwano/render/RenderContext.cpp | 9 +- src/kiwano/render/RenderContext.h | 23 +- src/kiwano/render/Renderer.h | 2 +- src/kiwano/render/Shape.cpp | 16 +- src/kiwano/render/ShapeMaker.cpp | 30 ++- src/kiwano/render/TextLayout.cpp | 20 +- src/kiwano/render/Texture.cpp | 12 +- src/kiwano/render/Texture.h | 1 - 35 files changed, 427 insertions(+), 456 deletions(-) delete mode 100644 src/kiwano/render/DirectX/NativePtr.h diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index a3910d00..7e76543c 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -96,7 +96,6 @@ - diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index 8853f034..399185c7 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -222,12 +222,6 @@ core - - render - - - render\DirectX - render @@ -411,6 +405,9 @@ render\DirectX + + render + @@ -608,9 +605,6 @@ render - - render - core @@ -677,6 +671,9 @@ render\DirectX + + render + diff --git a/src/kiwano/2d/Canvas.cpp b/src/kiwano/2d/Canvas.cpp index d857ce3a..28bcc535 100644 --- a/src/kiwano/2d/Canvas.cpp +++ b/src/kiwano/2d/Canvas.cpp @@ -25,10 +25,9 @@ namespace kiwano { Canvas::Canvas() { - RecreateContext(nullptr); } -Canvas::Canvas(const Size& size) +Canvas::Canvas(const PixelSize& size) { ResizeAndClear(size); } @@ -39,24 +38,10 @@ CanvasRenderContextPtr Canvas::GetContext2D() const return ctx; } -void Canvas::ResizeAndClear(Size size) -{ - RecreateContext(&size); -} - -void Canvas::RecreateContext(Size* size) +void Canvas::ResizeAndClear(const PixelSize& size) { texture_cached_ = MakePtr(); - - if (size) - { - render_ctx_ = RenderContext::Create(*texture_cached_, *size); - } - else - { - render_ctx_ = RenderContext::Create(*texture_cached_); - } - + render_ctx_ = RenderContext::Create(texture_cached_, size); if (render_ctx_) { SetSize(render_ctx_->GetSize()); @@ -67,11 +52,6 @@ void Canvas::RecreateContext(Size* size) } } -TexturePtr Canvas::ExportToTexture() const -{ - return texture_cached_; -} - void Canvas::OnRender(RenderContext& ctx) { if (texture_cached_) diff --git a/src/kiwano/2d/Canvas.h b/src/kiwano/2d/Canvas.h index 0cfccb59..cee5df73 100644 --- a/src/kiwano/2d/Canvas.h +++ b/src/kiwano/2d/Canvas.h @@ -43,12 +43,15 @@ KGE_DECLARE_SMART_PTR(CanvasRenderContext); class KGE_API Canvas : public Actor { public: + /// \~chinese + /// @brief 创建画布 + /// @warning 必须调用 ResizeAndClear 以初始化画布 Canvas(); /// \~chinese /// @brief 创建画布 /// @param size 画布大小 - Canvas(const Size& size); + Canvas(const PixelSize& size); /// \~chinese /// @brief 获取2D绘图上下文 @@ -57,17 +60,14 @@ public: /// \~chinese /// @brief 清空画布大小并重设画布大小 /// @warning 该函数会导致原绘图上下文失效 - void ResizeAndClear(Size size); + void ResizeAndClear(const PixelSize& size); /// \~chinese /// @brief 导出纹理 - TexturePtr ExportToTexture() const; + TexturePtr GetTexture() const; void OnRender(RenderContext& ctx) override; -private: - void RecreateContext(Size* size); - private: TexturePtr texture_cached_; RenderContextPtr render_ctx_; @@ -285,6 +285,11 @@ private: /** @} */ +inline TexturePtr Canvas::GetTexture() const +{ + return texture_cached_; +} + inline void CanvasRenderContext::BeginDraw() { KGE_ASSERT(ctx_); diff --git a/src/kiwano/2d/GifSprite.cpp b/src/kiwano/2d/GifSprite.cpp index ee18cf5a..dcb8fa52 100644 --- a/src/kiwano/2d/GifSprite.cpp +++ b/src/kiwano/2d/GifSprite.cpp @@ -20,6 +20,7 @@ #include #include +#include namespace kiwano { @@ -76,9 +77,8 @@ bool GifSprite::Load(GifImagePtr gif) frame_to_render_.Reset(); frame_rt_.Reset(); - Size frame_size = Size(float(gif_->GetWidthInPixels()), float(gif_->GetHeightInPixels())); frame_to_render_ = MakePtr(); - frame_rt_ = RenderContext::Create(*frame_to_render_, frame_size); + frame_rt_ = RenderContext::Create(frame_to_render_, gif_->GetSizeInPixels()); if (frame_rt_) { diff --git a/src/kiwano/2d/TextActor.cpp b/src/kiwano/2d/TextActor.cpp index 25c732ed..d6ad7056 100644 --- a/src/kiwano/2d/TextActor.cpp +++ b/src/kiwano/2d/TextActor.cpp @@ -289,7 +289,8 @@ void TextActor::UpdateCachedTexture() const auto expectedSize = layout_->GetSize() + cached_texture_offset * 2; if (!render_ctx_) { - render_ctx_ = RenderContext::Create(*texture_cached_, expectedSize); + const auto pixelSize = PixelSize((uint32_t)math::Ceil(expectedSize.x), (uint32_t)math::Ceil(expectedSize.y)); + render_ctx_ = RenderContext::Create(texture_cached_, pixelSize); } else if (render_ctx_->GetSize() != expectedSize) { diff --git a/src/kiwano/CMakeLists.txt b/src/kiwano/CMakeLists.txt index cbb8d953..7b94a9fa 100644 --- a/src/kiwano/CMakeLists.txt +++ b/src/kiwano/CMakeLists.txt @@ -100,6 +100,8 @@ set(SOURCE_FILES math/Transform.hpp math/Vec2.hpp platform/win32/ComPtr.hpp + platform/win32/ComObject.cpp + platform/win32/ComObject.h platform/win32/libraries.cpp platform/win32/libraries.h platform/win32/WindowImpl.cpp @@ -113,6 +115,8 @@ set(SOURCE_FILES platform/Runner.h platform/Window.cpp platform/Window.h + platform/NativeObject.cpp + platform/NativeObject.h render/DirectX/D2DDeviceResources.cpp render/DirectX/D2DDeviceResources.h render/DirectX/D3D10DeviceResources.cpp @@ -123,7 +127,6 @@ set(SOURCE_FILES render/DirectX/FontCollectionLoader.cpp render/DirectX/FontCollectionLoader.h render/DirectX/helper.h - render/DirectX/NativePtr.h render/DirectX/RenderContextImpl.cpp render/DirectX/RenderContextImpl.h render/DirectX/RendererImpl.cpp diff --git a/src/kiwano/render/Brush.cpp b/src/kiwano/render/Brush.cpp index 9b09d60c..ab25d187 100644 --- a/src/kiwano/render/Brush.cpp +++ b/src/kiwano/render/Brush.cpp @@ -22,10 +22,6 @@ #include #include -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - namespace kiwano { GradientStop::GradientStop() @@ -124,7 +120,7 @@ void Brush::SetTransform(const Transform& transform) void Brush::SetTransform(const Matrix3x2& transform) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) diff --git a/src/kiwano/render/Brush.h b/src/kiwano/render/Brush.h index c4545b2a..e54da60f 100644 --- a/src/kiwano/render/Brush.h +++ b/src/kiwano/render/Brush.h @@ -19,8 +19,8 @@ // THE SOFTWARE. #pragma once -#include #include +#include #include namespace kiwano diff --git a/src/kiwano/render/DirectX/D2DDeviceResources.cpp b/src/kiwano/render/DirectX/D2DDeviceResources.cpp index 6cd2e507..5b45256e 100644 --- a/src/kiwano/render/DirectX/D2DDeviceResources.cpp +++ b/src/kiwano/render/DirectX/D2DDeviceResources.cpp @@ -92,8 +92,6 @@ public: void DiscardResources() override; - void SetTargetBitmap(_In_ ComPtr target) override; - void ResetTextRenderingParams(_In_ HMONITOR monitor) override; public: @@ -213,7 +211,6 @@ void D2DDeviceResources::DiscardResources() } } - target_bitmap_.Reset(); device_context_.Reset(); device_.Reset(); @@ -315,7 +312,7 @@ HRESULT D2DDeviceResources::CreateDeviceResources(_In_ ComPtr dxgi_ { ComPtr device_ctx; - hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &device_ctx); + hr = device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, &device_ctx); if (SUCCEEDED(hr)) { @@ -350,7 +347,7 @@ HRESULT D2DDeviceResources::CreateWindowSizeDependentResources() if (SUCCEEDED(hr)) { - SetTargetBitmap(target); + device_context_->SetTarget(target.Get()); } } return hr; @@ -384,15 +381,6 @@ HRESULT D2DDeviceResources::HandleDeviceLost(_In_ ComPtr dxgi_devic return hr; } -void D2DDeviceResources::SetTargetBitmap(ComPtr target) -{ - target_bitmap_ = target; - if (device_context_) - { - device_context_->SetTarget(target_bitmap_.Get()); - } -} - void D2DDeviceResources::ResetTextRenderingParams(HMONITOR monitor) { if (!dwrite_factory_ || device_context_) diff --git a/src/kiwano/render/DirectX/D2DDeviceResources.h b/src/kiwano/render/DirectX/D2DDeviceResources.h index 7c00daec..d59cb1cd 100644 --- a/src/kiwano/render/DirectX/D2DDeviceResources.h +++ b/src/kiwano/render/DirectX/D2DDeviceResources.h @@ -78,8 +78,6 @@ public: virtual void DiscardResources() = 0; - virtual void SetTargetBitmap(_In_ ComPtr target) = 0; - virtual void ResetTextRenderingParams(_In_ HMONITOR monitor) = 0; inline ID2D1Factory1* GetFactory() @@ -112,17 +110,10 @@ public: return device_context_.Get(); } - inline ID2D1Bitmap1* GetTargetBitmap() - { - KGE_ASSERT(target_bitmap_); - return target_bitmap_.Get(); - } - protected: ComPtr factory_; ComPtr device_; ComPtr device_context_; - ComPtr target_bitmap_; ComPtr imaging_factory_; ComPtr dwrite_factory_; diff --git a/src/kiwano/render/DirectX/FontCollectionLoader.cpp b/src/kiwano/render/DirectX/FontCollectionLoader.cpp index 826b35d2..8b9deba2 100644 --- a/src/kiwano/render/DirectX/FontCollectionLoader.cpp +++ b/src/kiwano/render/DirectX/FontCollectionLoader.cpp @@ -22,6 +22,10 @@ namespace kiwano { +namespace graphics +{ +namespace directx +{ //////////////////////////////////////////////////////////////////////////////////////// // // FontCollectionLoader @@ -41,7 +45,7 @@ public: // IDWriteFontCollectionLoader methods virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(IDWriteFactory* pFactory, void const* collectionKey, - uint32_t collectionKeySize, + uint32_t collectionKeySize, _Out_ IDWriteFontFileEnumerator** fontFileEnumerator); // IUnknown methods @@ -348,7 +352,7 @@ public: // IDWriteFontCollectionLoader methods virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(IDWriteFactory* pFactory, void const* collectionKey, - uint32_t collectionKeySize, + uint32_t collectionKeySize, _Out_ IDWriteFontFileEnumerator** fontFileEnumerator); // IUnknown methods @@ -389,8 +393,7 @@ HRESULT IResourceFontCollectionLoader::Create(_Out_ IResourceFontCollectionLoade return hr; } -STDMETHODIMP ResourceFontCollectionLoader::AddResources(const Vector& data, - _Out_ LPVOID* pCollectionKey, +STDMETHODIMP ResourceFontCollectionLoader::AddResources(const Vector& data, _Out_ LPVOID* pCollectionKey, _Out_ uint32_t* pCollectionKeySize) { if (!pCollectionKey || !pCollectionKeySize) @@ -492,8 +495,8 @@ public: } // IDWriteFontFileLoader methods - virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const* fontFileReferenceKey, - uint32_t fontFileReferenceKeySize, + virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const* fontFileReferenceKey, + uint32_t fontFileReferenceKeySize, _Out_ IDWriteFontFileStream** fontFileStream); // IUnknown methods @@ -891,4 +894,6 @@ ULONG STDMETHODCALLTYPE ResourceFontFileStream::Release() return newCount; } +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/FontCollectionLoader.h b/src/kiwano/render/DirectX/FontCollectionLoader.h index bd23c445..a7d7816f 100644 --- a/src/kiwano/render/DirectX/FontCollectionLoader.h +++ b/src/kiwano/render/DirectX/FontCollectionLoader.h @@ -25,6 +25,10 @@ namespace kiwano { +namespace graphics +{ +namespace directx +{ interface DWRITE_DECLARE_INTERFACE("7EC7A55A-1964-4098-83E0-EFA7C12C6EF7") IFontCollectionLoader : public IDWriteFontCollectionLoader { @@ -78,4 +82,6 @@ public: static HRESULT Create(_Out_ IResourceFontFileStream** ppStream, const BinaryData& data); }; +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/NativePtr.h b/src/kiwano/render/DirectX/NativePtr.h deleted file mode 100644 index 2e132d68..00000000 --- a/src/kiwano/render/DirectX/NativePtr.h +++ /dev/null @@ -1,79 +0,0 @@ -// 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 - -namespace kiwano -{ - -class KGE_API NativePtr -{ -public: - template ::value, int>::type> - static inline ComPtr<_Ty> Get(const NativeObject* object) - { - if (object) - { - ComPtr ptr = object->GetNativePointer(); - if (ptr) - { - ComPtr<_Ty> native; - if (SUCCEEDED(ptr->QueryInterface<_Ty>(&native))) - return native; - } - } - return nullptr; - } - - template - static inline ComPtr<_Ty> Get(const NativeObject& object) - { - return NativePtr::Get<_Ty>(&object); - } - - template - static inline ComPtr<_Ty> Get(NativeObjectPtr object) - { - return NativePtr::Get<_Ty>(object.Get()); - } - - static inline void Set(NativeObject* object, ComPtr com_ptr) - { - if (object) - { - object->ResetNativePointer(com_ptr.Get()); - } - } - - static inline void Set(NativeObject& object, ComPtr com_ptr) - { - NativePtr::Set(&object, com_ptr); - } - - static inline void Set(NativeObjectPtr object, ComPtr com_ptr) - { - NativePtr::Set(object.Get(), com_ptr); - } -}; - -} // namespace kiwano diff --git a/src/kiwano/render/DirectX/RenderContextImpl.cpp b/src/kiwano/render/DirectX/RenderContextImpl.cpp index d22990b0..28d91082 100644 --- a/src/kiwano/render/DirectX/RenderContextImpl.cpp +++ b/src/kiwano/render/DirectX/RenderContextImpl.cpp @@ -19,13 +19,15 @@ // THE SOFTWARE. #include -#include #include #include namespace kiwano { - +namespace graphics +{ +namespace directx +{ RenderContextImpl::RenderContextImpl() {} RenderContextImpl::~RenderContextImpl() @@ -33,23 +35,23 @@ RenderContextImpl::~RenderContextImpl() DiscardDeviceResources(); } -HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, ComPtr render_target) +HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, ComPtr ctx) { - if (!factory || !render_target) + if (!factory || !ctx) return E_INVALIDARG; - render_target_ = render_target; + render_ctx_ = ctx; text_renderer_.Reset(); current_brush_.Reset(); - HRESULT hr = ITextRenderer::Create(&text_renderer_, render_target_.Get()); + HRESULT hr = ITextRenderer::Create(&text_renderer_, render_ctx_.Get()); if (SUCCEEDED(hr)) { SetAntialiasMode(antialias_); SetTextAntialiasMode(text_antialias_); - Resize(reinterpret_cast(render_target->GetSize())); + Resize(reinterpret_cast(render_ctx_->GetSize())); } // DrawingStateBlock @@ -60,7 +62,7 @@ HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, C if (SUCCEEDED(hr)) { - NativePtr::Set(this, render_target); + NativeObject::Set(this, ctx); } return hr; } @@ -68,28 +70,42 @@ HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, C void RenderContextImpl::DiscardDeviceResources() { text_renderer_.Reset(); - render_target_.Reset(); + render_ctx_.Reset(); current_brush_.Reset(); ResetNativePointer(); } +TexturePtr RenderContextImpl::GetTarget() const +{ + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + + ComPtr target; + render_ctx_->GetTarget(&target); + if (target) + { + TexturePtr ptr = MakePtr(); + NativeObject::Set(*ptr, target.Get()); + } + return nullptr; +} + void RenderContextImpl::BeginDraw() { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); SaveDrawingState(); RenderContext::BeginDraw(); - render_target_->BeginDraw(); + render_ctx_->BeginDraw(); } void RenderContextImpl::EndDraw() { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); - HRESULT hr = render_target_->EndDraw(); + HRESULT hr = render_ctx_->EndDraw(); KGE_THROW_IF_FAILED(hr, "ID2D1RenderTarget EndDraw failed"); RenderContext::EndDraw(); @@ -97,9 +113,25 @@ void RenderContextImpl::EndDraw() RestoreDrawingState(); } +void RenderContextImpl::CreateTexture(Texture& texture, const PixelSize& size) +{ + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + + ComPtr saved_bitmap; + + HRESULT hr = render_ctx_->CreateBitmap(D2D1::SizeU(size.x, size.y), D2D1::BitmapProperties(), &saved_bitmap); + + if (SUCCEEDED(hr)) + { + NativeObject::Set(texture, saved_bitmap); + } + + KGE_THROW_IF_FAILED(hr, "Create texture failed"); +} + void RenderContextImpl::DrawTexture(const Texture& texture, const Rect* src_rect, const Rect* dest_rect) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); if (texture.IsValid()) { @@ -113,8 +145,8 @@ void RenderContextImpl::DrawTexture(const Texture& texture, const Rect* src_rect mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; } - auto bitmap = NativePtr::Get(texture); - render_target_->DrawBitmap(bitmap.Get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, brush_opacity_, + auto bitmap = NativeObject::Get(texture); + render_ctx_->DrawBitmap(bitmap.Get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, brush_opacity_, mode, src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr); IncreasePrimitivesCount(); @@ -127,10 +159,10 @@ void RenderContextImpl::DrawTextLayout(const TextLayout& layout, const Point& of if (layout.IsValid()) { - auto native = NativePtr::Get(layout); - auto fill_brush = NativePtr::Get(current_brush_); - auto outline_brush = NativePtr::Get(current_outline_brush); - auto outline_stroke = NativePtr::Get(current_stroke_); + auto native = NativeObject::Get(layout); + auto fill_brush = NativeObject::Get(current_brush_); + auto outline_brush = NativeObject::Get(current_outline_brush); + auto outline_stroke = NativeObject::Get(current_stroke_); float outline_width = 1.0f; if (fill_brush) @@ -164,17 +196,17 @@ void RenderContextImpl::DrawTextLayout(const TextLayout& layout, const Point& of void RenderContextImpl::DrawShape(const Shape& shape) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); if (shape.IsValid()) { - auto geometry = NativePtr::Get(shape); - auto brush = NativePtr::Get(current_brush_); - auto stroke_style = NativePtr::Get(current_stroke_); + auto geometry = NativeObject::Get(shape); + auto brush = NativeObject::Get(current_brush_); + auto stroke_style = NativeObject::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; - render_target_->DrawGeometry(geometry.Get(), brush.Get(), stroke_width, stroke_style.Get()); + render_ctx_->DrawGeometry(geometry.Get(), brush.Get(), stroke_width, stroke_style.Get()); IncreasePrimitivesCount(); } @@ -182,14 +214,14 @@ void RenderContextImpl::DrawShape(const Shape& shape) void RenderContextImpl::DrawLine(const Point& point1, const Point& point2) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - auto stroke_style = NativePtr::Get(current_stroke_); + auto brush = NativeObject::Get(current_brush_); + auto stroke_style = NativeObject::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; - render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), brush.Get(), stroke_width, + render_ctx_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), brush.Get(), stroke_width, stroke_style.Get()); IncreasePrimitivesCount(); @@ -197,28 +229,28 @@ void RenderContextImpl::DrawLine(const Point& point1, const Point& point2) void RenderContextImpl::DrawRectangle(const Rect& rect) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - auto stroke_style = NativePtr::Get(current_stroke_); + auto brush = NativeObject::Get(current_brush_); + auto stroke_style = NativeObject::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; - render_target_->DrawRectangle(DX::ConvertToRectF(rect), brush.Get(), stroke_width, stroke_style.Get()); + render_ctx_->DrawRectangle(DX::ConvertToRectF(rect), brush.Get(), stroke_width, stroke_style.Get()); IncreasePrimitivesCount(); } void RenderContextImpl::DrawRoundedRectangle(const Rect& rect, const Vec2& radius) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - auto stroke_style = NativePtr::Get(current_stroke_); + auto brush = NativeObject::Get(current_brush_); + auto stroke_style = NativeObject::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; - render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get(), + render_ctx_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get(), stroke_width, stroke_style.Get()); IncreasePrimitivesCount(); @@ -226,14 +258,14 @@ void RenderContextImpl::DrawRoundedRectangle(const Rect& rect, const Vec2& radiu void RenderContextImpl::DrawEllipse(const Point& center, const Vec2& radius) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - auto stroke_style = NativePtr::Get(current_stroke_); + auto brush = NativeObject::Get(current_brush_); + auto stroke_style = NativeObject::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; - render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.Get(), + render_ctx_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.Get(), stroke_width, stroke_style.Get()); IncreasePrimitivesCount(); @@ -241,14 +273,14 @@ void RenderContextImpl::DrawEllipse(const Point& center, const Vec2& radius) void RenderContextImpl::FillShape(const Shape& shape) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); if (shape.IsValid()) { - auto brush = NativePtr::Get(current_brush_); - auto geometry = NativePtr::Get(shape); - render_target_->FillGeometry(geometry.Get(), brush.Get()); + auto brush = NativeObject::Get(current_brush_); + auto geometry = NativeObject::Get(shape); + render_ctx_->FillGeometry(geometry.Get(), brush.Get()); IncreasePrimitivesCount(); } @@ -256,56 +288,40 @@ void RenderContextImpl::FillShape(const Shape& shape) void RenderContextImpl::FillRectangle(const Rect& rect) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - render_target_->FillRectangle(DX::ConvertToRectF(rect), brush.Get()); + auto brush = NativeObject::Get(current_brush_); + render_ctx_->FillRectangle(DX::ConvertToRectF(rect), brush.Get()); IncreasePrimitivesCount(); } void RenderContextImpl::FillRoundedRectangle(const Rect& rect, const Vec2& radius) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - render_target_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get()); + auto brush = NativeObject::Get(current_brush_); + render_ctx_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get()); IncreasePrimitivesCount(); } void RenderContextImpl::FillEllipse(const Point& center, const Vec2& radius) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativePtr::Get(current_brush_); - render_target_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.Get()); + auto brush = NativeObject::Get(current_brush_); + render_ctx_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.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)) - { - NativePtr::Set(texture, saved_bitmap); - } - - KGE_THROW_IF_FAILED(hr, "Create texture failed"); -} - void RenderContextImpl::PushClipRect(const Rect& clip_rect) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); D2D1_ANTIALIAS_MODE mode; if (antialias_) @@ -316,29 +332,29 @@ void RenderContextImpl::PushClipRect(const Rect& clip_rect) { mode = D2D1_ANTIALIAS_MODE_ALIASED; } - render_target_->PushAxisAlignedClip(DX::ConvertToRectF(clip_rect), mode); + render_ctx_->PushAxisAlignedClip(DX::ConvertToRectF(clip_rect), mode); } void RenderContextImpl::PopClipRect() { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->PopAxisAlignedClip(); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + render_ctx_->PopAxisAlignedClip(); } void RenderContextImpl::PushLayer(Layer& layer) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); - auto native = NativePtr::Get(layer); - auto mask = NativePtr::Get(layer.GetMaskShape()); + auto native = NativeObject::Get(layer); + auto mask = NativeObject::Get(layer.GetMaskShape()); if (!native) { - HRESULT hr = render_target_->CreateLayer(&native); + HRESULT hr = render_ctx_->CreateLayer(&native); if (SUCCEEDED(hr)) { - NativePtr::Set(layer, native); + NativeObject::Set(layer, native); } KGE_THROW_IF_FAILED(hr, "Create ID2D1Layer failed"); } @@ -348,32 +364,32 @@ void RenderContextImpl::PushLayer(Layer& layer) DX::ConvertToMatrix3x2F(layer.GetMaskTransform()), layer.GetOpacity(), nullptr, D2D1_LAYER_OPTIONS_NONE); - render_target_->PushLayer(params, native.Get()); + render_ctx_->PushLayer(params, native.Get()); } void RenderContextImpl::PopLayer() { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->PopLayer(); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + render_ctx_->PopLayer(); } void RenderContextImpl::Clear() { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->Clear(); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + render_ctx_->Clear(); } void RenderContextImpl::Clear(const Color& clear_color) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); - render_target_->Clear(DX::ConvertToColorF(clear_color)); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + render_ctx_->Clear(DX::ConvertToColorF(clear_color)); } Size RenderContextImpl::GetSize() const { - if (render_target_) + if (render_ctx_) { - return reinterpret_cast(render_target_->GetSize()); + return reinterpret_cast(render_ctx_->GetSize()); } return Size(); } @@ -384,7 +400,7 @@ void RenderContextImpl::SetCurrentBrush(BrushPtr brush) if (current_brush_ && current_brush_->IsValid()) { - NativePtr::Get(current_brush_)->SetOpacity(brush_opacity_); + NativeObject::Get(current_brush_)->SetOpacity(brush_opacity_); } } @@ -400,30 +416,30 @@ void RenderContextImpl::SetCurrentStrokeStyle(StrokeStylePtr stroke_style) void RenderContextImpl::SetTransform(const Matrix3x2& matrix) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); if (fast_global_transform_) { - render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); + render_ctx_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); } else { Matrix3x2 result = matrix * global_transform_; - render_target_->SetTransform(DX::ConvertToMatrix3x2F(&result)); + render_ctx_->SetTransform(DX::ConvertToMatrix3x2F(&result)); } } void RenderContextImpl::SetAntialiasMode(bool enabled) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); - render_target_->SetAntialiasMode(enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); + render_ctx_->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!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; switch (mode) @@ -445,12 +461,12 @@ void RenderContextImpl::SetTextAntialiasMode(TextAntialiasMode mode) } text_antialias_ = mode; - render_target_->SetTextAntialiasMode(antialias_mode); + render_ctx_->SetTextAntialiasMode(antialias_mode); } bool RenderContextImpl::CheckVisibility(const Rect& bounds, const Matrix3x2& transform) { - KGE_ASSERT(render_target_ && "Render target has not been initialized!"); + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); if (fast_global_transform_) { @@ -470,7 +486,7 @@ void RenderContextImpl::SaveDrawingState() if (drawing_state_) { - render_target_->SaveDrawingState(drawing_state_.Get()); + render_ctx_->SaveDrawingState(drawing_state_.Get()); } } @@ -480,8 +496,10 @@ void RenderContextImpl::RestoreDrawingState() if (drawing_state_) { - render_target_->RestoreDrawingState(drawing_state_.Get()); + render_ctx_->RestoreDrawingState(drawing_state_.Get()); } } +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/RenderContextImpl.h b/src/kiwano/render/DirectX/RenderContextImpl.h index cc5da0b6..d09b73a1 100644 --- a/src/kiwano/render/DirectX/RenderContextImpl.h +++ b/src/kiwano/render/DirectX/RenderContextImpl.h @@ -24,7 +24,10 @@ namespace kiwano { - +namespace graphics +{ +namespace directx +{ KGE_DECLARE_SMART_PTR(RenderContextImpl); class KGE_API RenderContextImpl : public RenderContext @@ -34,12 +37,14 @@ public: virtual ~RenderContextImpl(); - HRESULT CreateDeviceResources(ComPtr factory, ComPtr render_target); + HRESULT CreateDeviceResources(ComPtr factory, ComPtr ctx); void BeginDraw() override; void EndDraw() override; + void CreateTexture(Texture& texture, const PixelSize& size) override; + void DrawTexture(const Texture& texture, const Rect* src_rect, const Rect* dest_rect) override; void DrawTextLayout(const TextLayout& layout, const Point& offset, BrushPtr outline_brush) override; @@ -62,8 +67,6 @@ public: void FillEllipse(const Point& center, const Vec2& radius) override; - void CreateTexture(Texture& texture, math::Vec2T size) override; - void PushClipRect(const Rect& clip_rect) override; void PopClipRect() override; @@ -92,6 +95,8 @@ public: void Resize(const Size& size) override; + TexturePtr GetTarget() const override; + private: void DiscardDeviceResources(); @@ -101,8 +106,10 @@ private: private: ComPtr text_renderer_; - ComPtr render_target_; + ComPtr render_ctx_; ComPtr drawing_state_; }; +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/RendererImpl.cpp b/src/kiwano/render/DirectX/RendererImpl.cpp index 179aefa6..c68cd75d 100644 --- a/src/kiwano/render/DirectX/RendererImpl.cpp +++ b/src/kiwano/render/DirectX/RendererImpl.cpp @@ -20,11 +20,11 @@ #include #include +#include #include #include #include #include -#include #define KGE_SET_STATUS_IF_FAILED(ERRCODE, OBJ, MESSAGE) \ if (FAILED(ERRCODE)) \ @@ -37,24 +37,24 @@ namespace kiwano using namespace kiwano::graphics::directx; -inline const GUID& ConvertPixelFormat(PixelFormat format, UINT& stride) +inline DXGI_FORMAT ConvertPixelFormat(PixelFormat format, UINT32& pitch) { switch (format) { - case PixelFormat::Bpp32RGB: - stride = 3; - return GUID_WICPixelFormat32bppRGB; + //case PixelFormat::Bpp32RGB: + // pitch = 4; + // return DXGI_FORMAT_R8G8B8X8_UNORM; case PixelFormat::Bpp32RGBA: - stride = 4; - return GUID_WICPixelFormat32bppRGBA; + pitch = 4; + return DXGI_FORMAT_R8G8B8A8_UNORM; case PixelFormat::Bpp32BGR: - stride = 3; - return GUID_WICPixelFormat32bppBGR; + pitch = 4; + return DXGI_FORMAT_B8G8R8X8_UNORM; case PixelFormat::Bpp32BGRA: - stride = 4; - return GUID_WICPixelFormat32bppBGRA; + pitch = 4; + return DXGI_FORMAT_B8G8R8A8_UNORM; default: - return GUID_WICPixelFormatDontCare; + return DXGI_FORMAT_UNKNOWN; } } @@ -258,7 +258,7 @@ void RendererImpl::CreateTexture(Texture& texture, const String& file_path) if (SUCCEEDED(hr)) { - NativePtr::Set(texture, bitmap); + NativeObject::Set(texture, bitmap); texture.SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); texture.SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -307,7 +307,7 @@ void RendererImpl::CreateTexture(Texture& texture, const BinaryData& data) if (SUCCEEDED(hr)) { - NativePtr::Set(texture, bitmap); + NativeObject::Set(texture, bitmap); texture.SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); texture.SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -335,34 +335,19 @@ void RendererImpl::CreateTexture(Texture& texture, const PixelSize& size, const if (SUCCEEDED(hr)) { - UINT stride = 0; - const auto& wicFormat = ConvertPixelFormat(format, stride); - - ComPtr source; - hr = d2d_res_->CreateBitmapSourceFromMemory(source, UINT(size.x), UINT(size.y), UINT(size.x) * stride, - UINT(data.size), reinterpret_cast(data.buffer), - wicFormat); + ComPtr output; + UINT32 pitch = 0; + const auto dxgi_format = ConvertPixelFormat(format, pitch); + hr = d2d_res_->GetDeviceContext()->CreateBitmap( + DX::ConvertToSizeU(size), data.buffer, UINT(size.x) * pitch, + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(dxgi_format)), &output); if (SUCCEEDED(hr)) { - ComPtr converter; - hr = d2d_res_->CreateBitmapConverter(converter, source, GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, nullptr, 0.f, - WICBitmapPaletteTypeMedianCut); + NativeObject::Set(texture, output); - if (SUCCEEDED(hr)) - { - ComPtr bitmap; - hr = d2d_res_->CreateBitmapFromConverter(bitmap, nullptr, converter); - - if (SUCCEEDED(hr)) - { - NativePtr::Set(texture, bitmap); - - texture.SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); - texture.SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); - } - } + texture.SetSize({ output->GetSize().width, output->GetSize().height }); + texture.SetSizeInPixels({ output->GetPixelSize().width, output->GetPixelSize().height }); } } } @@ -395,7 +380,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const String& file_path) if (SUCCEEDED(hr)) { - NativePtr::Set(gif, decoder); + NativeObject::Set(gif, decoder); } } @@ -421,7 +406,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const BinaryData& data) if (SUCCEEDED(hr)) { - NativePtr::Set(gif, decoder); + NativeObject::Set(gif, decoder); } } } @@ -437,7 +422,7 @@ void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, const GifImage& g hr = E_UNEXPECTED; } - auto decoder = NativePtr::Get(gif); + auto decoder = NativeObject::Get(gif); if (!decoder) { @@ -464,7 +449,7 @@ void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, const GifImage& g if (SUCCEEDED(hr)) { frame.texture = MakePtr(); - NativePtr::Set(frame.texture, bitmap); + NativeObject::Set(frame.texture, bitmap); frame.texture->SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); frame.texture->SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -618,7 +603,7 @@ void RendererImpl::CreateFontCollection(Font& font, Vector& family_names if (SUCCEEDED(hr)) { d2d_res_->GetFontFamilyNames(family_names, font_collection); // ignore the result - NativePtr::Set(font, font_collection); + NativeObject::Set(font, font_collection); } } @@ -641,7 +626,7 @@ void RendererImpl::CreateFontCollection(Font& font, Vector& family_names if (SUCCEEDED(hr)) { d2d_res_->GetFontFamilyNames(family_names, font_collection); // ignore the result - NativePtr::Set(font, font_collection); + NativeObject::Set(font, font_collection); } } @@ -675,7 +660,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c auto font_weight = DWRITE_FONT_WEIGHT(font->GetWeight()); auto font_style = DWRITE_FONT_STYLE(font->GetPosture()); auto font_stretch = DWRITE_FONT_STRETCH(font->GetStretch()); - auto collection = NativePtr::Get(font); + auto collection = NativeObject::Get(font); WideString font_family; @@ -698,7 +683,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c if (SUCCEEDED(hr)) { - NativePtr::Set(layout, output); + NativeObject::Set(layout, output); layout.SetDirtyFlag(TextLayout::DirtyFlag::Dirty); } } @@ -735,7 +720,7 @@ void RendererImpl::CreateLineShape(Shape& shape, const Point& begin_pos, const P if (SUCCEEDED(hr)) { - NativePtr::Set(shape, path_geo); + NativeObject::Set(shape, path_geo); } } } @@ -759,7 +744,7 @@ void RendererImpl::CreateRectShape(Shape& shape, const Rect& rect) if (SUCCEEDED(hr)) { - NativePtr::Set(shape, output); + NativeObject::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1RectangleGeometry failed"); @@ -782,7 +767,7 @@ void RendererImpl::CreateRoundedRectShape(Shape& shape, const Rect& rect, const if (SUCCEEDED(hr)) { - NativePtr::Set(shape, output); + NativeObject::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1RoundedRectangleGeometry failed"); @@ -805,7 +790,7 @@ void RendererImpl::CreateEllipseShape(Shape& shape, const Point& center, const V if (SUCCEEDED(hr)) { - NativePtr::Set(shape, output); + NativeObject::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1EllipseGeometry failed"); @@ -828,7 +813,7 @@ void RendererImpl::CreateShapeSink(ShapeMaker& maker) if (SUCCEEDED(hr)) { ShapePtr shape = MakePtr(); - NativePtr::Set(shape, geometry); + NativeObject::Set(shape, geometry); maker.SetShape(shape); } @@ -850,7 +835,7 @@ void RendererImpl::CreateBrush(Brush& brush, const Color& color) if (brush.GetType() == Brush::Type::SolidColor && brush.IsValid()) { - hr = NativePtr::Get(brush)->QueryInterface(&solid_brush); + hr = NativeObject::Get(brush)->QueryInterface(&solid_brush); if (SUCCEEDED(hr)) { solid_brush->SetColor(DX::ConvertToColorF(color)); @@ -862,7 +847,7 @@ void RendererImpl::CreateBrush(Brush& brush, const Color& color) if (SUCCEEDED(hr)) { - NativePtr::Set(brush, solid_brush); + NativeObject::Set(brush, solid_brush); } } } @@ -894,7 +879,7 @@ void RendererImpl::CreateBrush(Brush& brush, const LinearGradientStyle& style) if (SUCCEEDED(hr)) { - NativePtr::Set(brush, output); + NativeObject::Set(brush, output); } } } @@ -927,7 +912,7 @@ void RendererImpl::CreateBrush(Brush& brush, const RadialGradientStyle& style) if (SUCCEEDED(hr)) { - NativePtr::Set(brush, output); + NativeObject::Set(brush, output); } } } @@ -945,7 +930,7 @@ void RendererImpl::CreateBrush(Brush& brush, TexturePtr texture) if (SUCCEEDED(hr)) { - auto bitmap = NativePtr::Get(texture); + auto bitmap = NativeObject::Get(texture); if (SUCCEEDED(hr)) { @@ -954,7 +939,7 @@ void RendererImpl::CreateBrush(Brush& brush, TexturePtr texture) if (SUCCEEDED(hr)) { - NativePtr::Set(brush, output); + NativeObject::Set(brush, output); } } } @@ -994,55 +979,60 @@ void RendererImpl::CreateStrokeStyle(StrokeStyle& stroke_style) if (SUCCEEDED(hr)) { - NativePtr::Set(stroke_style, output); + NativeObject::Set(stroke_style, output); } } KGE_SET_STATUS_IF_FAILED(hr, stroke_style, "Create ID2D1StrokeStyle failed"); } -RenderContextPtr RendererImpl::CreateTextureRenderContext(Texture& texture, const Size* desired_size) +RenderContextPtr RendererImpl::CreateTextureRenderContext(TexturePtr texture, const PixelSize& desired_size) { - RenderContextImplPtr ptr = MakePtr(); - HRESULT hr = S_OK; if (!d2d_res_) { hr = E_UNEXPECTED; } + else if (texture == nullptr) + { + hr = E_INVALIDARG; + } if (SUCCEEDED(hr)) { - ComPtr bitmap_rt; + RenderContextImplPtr ptr = MakePtr(); - if (desired_size) + ComPtr render_ctx; + hr = d2d_res_->GetDevice()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, + &render_ctx); + + if (SUCCEEDED(hr)) { - hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(DX::ConvertToSizeF(*desired_size), - &bitmap_rt); - } - else - { - hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(&bitmap_rt); + hr = ptr->CreateDeviceResources(d2d_res_->GetFactory(), render_ctx); } if (SUCCEEDED(hr)) { - hr = ptr->CreateDeviceResources(d2d_res_->GetFactory(), bitmap_rt); - } + ComPtr output; + hr = render_ctx->CreateBitmap( + DX::ConvertToSizeU(desired_size), nullptr, 0, + D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, + D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)), + &output); - if (SUCCEEDED(hr)) - { - ComPtr output; - hr = bitmap_rt->GetBitmap(&output); if (SUCCEEDED(hr)) { - NativePtr::Set(texture, output); + render_ctx->SetTarget(output.Get()); + NativeObject::Set(texture, output); + + texture->SetSize({ output->GetSize().width, output->GetSize().height }); + texture->SetSizeInPixels({ output->GetPixelSize().width, output->GetPixelSize().height }); + return ptr; } } } - if (SUCCEEDED(hr)) - return ptr; + KGE_THROW_IF_FAILED(hr, "Create render context failed"); return nullptr; } @@ -1056,7 +1046,7 @@ void RendererImpl::Resize(uint32_t width, uint32_t height) if (SUCCEEDED(hr)) { // Clear resources - d2d_res_->SetTargetBitmap(nullptr); + d2d_res_->GetDeviceContext()->SetTarget(nullptr); } if (SUCCEEDED(hr)) diff --git a/src/kiwano/render/DirectX/RendererImpl.h b/src/kiwano/render/DirectX/RendererImpl.h index b57bd811..a0eae5ca 100644 --- a/src/kiwano/render/DirectX/RendererImpl.h +++ b/src/kiwano/render/DirectX/RendererImpl.h @@ -36,7 +36,7 @@ public: void CreateTexture(Texture& texture, const BinaryData& data) override; - void CreateTexture(Texture& texture, const PixelSize& size, const BinaryData& data, PixelFormat format); + void CreateTexture(Texture& texture, const PixelSize& size, const BinaryData& data, PixelFormat format) override; void CreateGifImage(GifImage& gif, const String& file_path) override; @@ -70,7 +70,7 @@ public: void CreateStrokeStyle(StrokeStyle& stroke_style) override; - RenderContextPtr CreateTextureRenderContext(Texture& texture, const Size* desired_size = nullptr) override; + RenderContextPtr CreateTextureRenderContext(TexturePtr texture, const PixelSize& desired_size) override; public: void Clear() override; diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.cpp b/src/kiwano/render/DirectX/TextDrawingEffect.cpp index d3d891fd..13b4bd48 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.cpp +++ b/src/kiwano/render/DirectX/TextDrawingEffect.cpp @@ -23,6 +23,10 @@ namespace kiwano { +namespace graphics +{ +namespace directx +{ class TextDrawingEffect : public ITextDrawingEffect { public: @@ -39,8 +43,8 @@ public: virtual ULONG STDMETHODCALLTYPE Release(); private: - unsigned long cRefCount_; - ComPtr pFactory_; + unsigned long cRefCount_; + ComPtr pFactory_; // Outline geometry cache Map, ComPtr> outlineCache_; @@ -194,4 +198,6 @@ STDMETHODIMP TextDrawingEffect::QueryInterface(REFIID riid, void** ppvObject) return S_OK; } +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.h b/src/kiwano/render/DirectX/TextDrawingEffect.h index 81fb1217..05f6280c 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.h +++ b/src/kiwano/render/DirectX/TextDrawingEffect.h @@ -24,8 +24,12 @@ namespace kiwano { - -interface DWRITE_DECLARE_INTERFACE("7431F439-6E54-4707-A0DC-1AA035D6AFB8") ITextDrawingEffect : public IUnknown +namespace graphics +{ +namespace directx +{ +interface DWRITE_DECLARE_INTERFACE("7431F439-6E54-4707-A0DC-1AA035D6AFB8") ITextDrawingEffect + : public IUnknown { public: static HRESULT Create(_Out_ ITextDrawingEffect** ppTextDrawingEffect, _In_ ID2D1Factory* pFactory); @@ -33,4 +37,6 @@ public: STDMETHOD(CreateOutlineGeomerty) (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY) PURE; }; +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextRenderer.cpp b/src/kiwano/render/DirectX/TextRenderer.cpp index 8329f965..718ef9ed 100644 --- a/src/kiwano/render/DirectX/TextRenderer.cpp +++ b/src/kiwano/render/DirectX/TextRenderer.cpp @@ -23,6 +23,10 @@ namespace kiwano { +namespace graphics +{ +namespace directx +{ class TextRenderer : public ITextRenderer { public: @@ -30,7 +34,7 @@ public: ~TextRenderer(); - STDMETHOD(CreateDeviceResources)(_In_ ID2D1RenderTarget* pRT); + STDMETHOD(CreateDeviceResources)(_In_ ID2D1DeviceContext* pContext); STDMETHOD(DrawTextLayout) (_In_ IDWriteTextLayout* pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush, @@ -67,17 +71,17 @@ public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); private: - unsigned long cRefCount_; - uint32_t cPrimitivesCount_; - float fDefaultOutlineWidth_; - ComPtr pFactory_; - ComPtr pRT_; - ComPtr pDefaultFillBrush_; - ComPtr pDefaultOutlineBrush_; - ComPtr pDefaultStrokeStyle_; + unsigned long cRefCount_; + uint32_t cPrimitivesCount_; + float fDefaultOutlineWidth_; + ComPtr pFactory_; + ComPtr pContext_; + ComPtr pDefaultFillBrush_; + ComPtr pDefaultOutlineBrush_; + ComPtr pDefaultStrokeStyle_; }; -HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1RenderTarget* pRT) +HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1DeviceContext* pContext) { HRESULT hr = E_FAIL; @@ -86,7 +90,7 @@ HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1Re TextRenderer* pTextRenderer = new (std::nothrow) TextRenderer; if (pTextRenderer) { - hr = pTextRenderer->CreateDeviceResources(pRT); + hr = pTextRenderer->CreateDeviceResources(pContext); if (SUCCEEDED(hr)) { @@ -115,17 +119,17 @@ TextRenderer::TextRenderer() TextRenderer::~TextRenderer() {} -STDMETHODIMP TextRenderer::CreateDeviceResources(_In_ ID2D1RenderTarget* pRT) +STDMETHODIMP TextRenderer::CreateDeviceResources(_In_ ID2D1DeviceContext* pContext) { HRESULT hr = E_FAIL; pFactory_.Reset(); - pRT_.Reset(); + pContext_.Reset(); - if (pRT) + if (pContext) { - pRT_ = pRT; - pRT_->GetFactory(&pFactory_); + pContext_ = pContext; + pContext_->GetFactory(&pFactory_); hr = S_OK; } return hr; @@ -152,7 +156,7 @@ STDMETHODIMP TextRenderer::DrawTextLayout(_In_ IDWriteTextLayout* pTextLayout, f STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, float baselineOriginX, float baselineOriginY, DWRITE_MEASURING_MODE measuringMode, - __in DWRITE_GLYPH_RUN const* glyphRun, + __in DWRITE_GLYPH_RUN const* glyphRun, __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, IUnknown* clientDrawingEffect) { @@ -176,8 +180,8 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, if (SUCCEEDED(hr)) { - pRT_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); + pContext_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); ++cPrimitivesCount_; } @@ -187,7 +191,8 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, { if (SUCCEEDED(hr)) { - pRT_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, pDefaultFillBrush_.Get()); + pContext_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, + pDefaultFillBrush_.Get()); ++cPrimitivesCount_; } @@ -236,15 +241,15 @@ STDMETHODIMP TextRenderer::DrawUnderline(__maybenull void* clientDrawingContext, if (SUCCEEDED(hr) && pDefaultOutlineBrush_) { - pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); + pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); ++cPrimitivesCount_; } if (SUCCEEDED(hr) && pCurrentFillBrush) { - pRT_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); + pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); ++cPrimitivesCount_; } @@ -292,15 +297,15 @@ STDMETHODIMP TextRenderer::DrawStrikethrough(__maybenull void* clientDrawingCont if (SUCCEEDED(hr) && pDefaultOutlineBrush_) { - pRT_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); + pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); ++cPrimitivesCount_; } if (SUCCEEDED(hr) && pCurrentFillBrush) { - pRT_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); + pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); ++cPrimitivesCount_; } @@ -333,7 +338,7 @@ STDMETHODIMP TextRenderer::GetCurrentTransform(__maybenull void* clientDrawingCo { KGE_NOT_USED(clientDrawingContext); - pRT_->GetTransform(reinterpret_cast(transform)); + pContext_->GetTransform(reinterpret_cast(transform)); return S_OK; } @@ -343,7 +348,7 @@ STDMETHODIMP TextRenderer::GetPixelsPerDip(__maybenull void* clientDrawingContex float x, yUnused; - pRT_->GetDpi(&x, &yUnused); + pContext_->GetDpi(&x, &yUnused); *pixelsPerDip = x / 96; return S_OK; @@ -400,4 +405,6 @@ STDMETHODIMP TextRenderer::QueryInterface(REFIID riid, void** ppvObject) return S_OK; } +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/TextRenderer.h b/src/kiwano/render/DirectX/TextRenderer.h index 15c47347..63b8e8b4 100644 --- a/src/kiwano/render/DirectX/TextRenderer.h +++ b/src/kiwano/render/DirectX/TextRenderer.h @@ -23,16 +23,23 @@ namespace kiwano { -interface DWRITE_DECLARE_INTERFACE("b293e798-9916-4096-a3c1-e5d4039dfa64") ITextRenderer : public IDWriteTextRenderer +namespace graphics +{ +namespace directx +{ +interface DWRITE_DECLARE_INTERFACE("b293e798-9916-4096-a3c1-e5d4039dfa64") ITextRenderer + : public IDWriteTextRenderer { public: - static KGE_API HRESULT Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1RenderTarget* pRT); + static KGE_API HRESULT Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1DeviceContext* pContext); STDMETHOD(DrawTextLayout) - (_In_ IDWriteTextLayout * pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush, + (_In_ IDWriteTextLayout* pTextLayout, float fOriginX, float fOriginY, _In_opt_ ID2D1Brush* pDefaultFillBrush, _In_opt_ ID2D1Brush* pDefaultOutlineBrush, float fDefaultOutlineWidth, _In_opt_ ID2D1StrokeStyle* pDefaultStrokeStyle) PURE; STDMETHOD_(uint32_t, GetLastPrimitivesCount)() PURE; }; +} // namespace directx +} // namespace graphics } // namespace kiwano diff --git a/src/kiwano/render/DirectX/helper.h b/src/kiwano/render/DirectX/helper.h index 51ef440e..44244d4e 100644 --- a/src/kiwano/render/DirectX/helper.h +++ b/src/kiwano/render/DirectX/helper.h @@ -23,8 +23,7 @@ #include #include #include -#include -#include +#include namespace kiwano { @@ -76,7 +75,7 @@ inline D2D1_POINT_2F* ConvertToPoint2F(Vec2* vec2) } // -// SizeF +// Size // inline const D2D1_SIZE_F& ConvertToSizeF(const Vec2& vec2) @@ -99,6 +98,26 @@ inline D2D1_SIZE_F* ConvertToSizeF(Vec2* vec2) return reinterpret_cast(vec2); } +inline const D2D1_SIZE_U& ConvertToSizeU(const math::Vec2T& vec2) +{ + return reinterpret_cast(vec2); +} + +inline D2D1_SIZE_U& ConvertToSizeU(math::Vec2T& vec2) +{ + return reinterpret_cast(vec2); +} + +inline const D2D1_SIZE_U* ConvertToSizeU(const math::Vec2T* vec2) +{ + return reinterpret_cast(vec2); +} + +inline D2D1_SIZE_U* ConvertToSizeU(math::Vec2T* vec2) +{ + return reinterpret_cast(vec2); +} + // // RectF // diff --git a/src/kiwano/render/Font.h b/src/kiwano/render/Font.h index 2ab760bd..11081c24 100644 --- a/src/kiwano/render/Font.h +++ b/src/kiwano/render/Font.h @@ -19,8 +19,8 @@ // THE SOFTWARE. #pragma once -#include #include +#include namespace kiwano { diff --git a/src/kiwano/render/GifImage.cpp b/src/kiwano/render/GifImage.cpp index d3af8a95..055bb8b7 100644 --- a/src/kiwano/render/GifImage.cpp +++ b/src/kiwano/render/GifImage.cpp @@ -116,14 +116,13 @@ GifImage::Frame GifImage::GetFrame(uint32_t index) } // namespace kiwano #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include namespace kiwano { bool GifImage::GetGlobalMetadata() { - ComPtr decoder = NativePtr::Get(this); + ComPtr decoder = NativeObject::Get(this); HRESULT hr = decoder ? S_OK : E_FAIL; diff --git a/src/kiwano/render/NativeObject.cpp b/src/kiwano/render/NativeObject.cpp index fa43030d..7c3f01a6 100644 --- a/src/kiwano/render/NativeObject.cpp +++ b/src/kiwano/render/NativeObject.cpp @@ -17,10 +17,7 @@ void NativeObjectBase::ResetNativePointer(void* native_pointer) native_pointer_ = native_pointer; } -// -// NativeObject for Windows -// -#if defined(KGE_PLATFORM_WINDOWS) +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX NativeObject::~NativeObject() { diff --git a/src/kiwano/render/NativeObject.h b/src/kiwano/render/NativeObject.h index b5a1086d..eebeac0a 100644 --- a/src/kiwano/render/NativeObject.h +++ b/src/kiwano/render/NativeObject.h @@ -57,7 +57,7 @@ protected: void* native_pointer_; }; -#if defined(KGE_PLATFORM_WINDOWS) +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX class KGE_API NativeObject : public NativeObjectBase { @@ -65,6 +65,52 @@ public: virtual ~NativeObject(); void ResetNativePointer(void* native_pointer = nullptr) override; + + template ::value, int>::type> + static inline ComPtr<_Ty> Get(const NativeObject* object) + { + if (object) + { + ComPtr ptr = object->GetNativePointer(); + if (ptr) + { + ComPtr<_Ty> native; + if (SUCCEEDED(ptr->QueryInterface<_Ty>(&native))) + return native; + } + } + return nullptr; + } + + template + static inline ComPtr<_Ty> Get(const NativeObject& object) + { + return NativeObject::Get<_Ty>(&object); + } + + template + static inline ComPtr<_Ty> Get(NativeObjectPtr object) + { + return NativeObject::Get<_Ty>(object.Get()); + } + + static inline void Set(NativeObject* object, ComPtr com_ptr) + { + if (object) + { + object->ResetNativePointer(com_ptr.Get()); + } + } + + static inline void Set(NativeObject& object, ComPtr com_ptr) + { + NativeObject::Set(&object, com_ptr); + } + + static inline void Set(NativeObjectPtr object, ComPtr com_ptr) + { + NativeObject::Set(object.Get(), com_ptr); + } }; #else diff --git a/src/kiwano/render/RenderContext.cpp b/src/kiwano/render/RenderContext.cpp index a305c6c3..4342945d 100644 --- a/src/kiwano/render/RenderContext.cpp +++ b/src/kiwano/render/RenderContext.cpp @@ -24,14 +24,9 @@ namespace kiwano { -RenderContextPtr RenderContext::Create(Texture& texture) +RenderContextPtr RenderContext::Create(TexturePtr texture, const PixelSize& size) { - return Renderer::GetInstance().CreateTextureRenderContext(texture, nullptr); -} - -RenderContextPtr RenderContext::Create(Texture& texture, const Size& size) -{ - return Renderer::GetInstance().CreateTextureRenderContext(texture, &size); + return Renderer::GetInstance().CreateTextureRenderContext(texture, size); } RenderContext::RenderContext() diff --git a/src/kiwano/render/RenderContext.h b/src/kiwano/render/RenderContext.h index 29a047ee..046ed54c 100644 --- a/src/kiwano/render/RenderContext.h +++ b/src/kiwano/render/RenderContext.h @@ -52,16 +52,11 @@ enum class TextAntialiasMode class KGE_API RenderContext : public NativeObject { public: - /// \~chinese - /// @brief 创建纹理渲染上下文,将绘制结果输出到纹理中 - /// @param texture 保存绘制结果的纹理 - static RenderContextPtr Create(Texture& texture); - /// \~chinese /// @brief 创建纹理渲染上下文,将绘制结果输出到纹理中 /// @param texture 保存绘制结果的纹理 /// @param size 渲染输出大小 - static RenderContextPtr Create(Texture& texture, const Size& size); + static RenderContextPtr Create(TexturePtr texture, const PixelSize& size); /// \~chinese /// @brief 开始渲染 @@ -71,6 +66,12 @@ public: /// @brief 结束渲染 virtual void EndDraw(); + /// \~chinese + /// @brief 创建空纹理 + /// @param[out] texture 输出纹理 + /// @param[in] size 纹理像素大小 + virtual void CreateTexture(Texture& texture, const PixelSize& size) = 0; + /// \~chinese /// @brief 绘制纹理 /// @param texture 纹理 @@ -148,12 +149,6 @@ public: /// @param radius 椭圆半径 virtual void FillEllipse(const Point& center, const Vec2& radius) = 0; - /// \~chinese - /// @brief 创建纹理 - /// @param texture 纹理 - /// @param size 纹理像素大小 - virtual void CreateTexture(Texture& texture, math::Vec2T size) = 0; - /// \~chinese /// @brief 设置绘制的裁剪区域 /// @param clip_rect 裁剪矩形 @@ -237,6 +232,10 @@ public: /// @brief 设置全局二维变换 virtual void SetGlobalTransform(const Matrix3x2* matrix); + /// \~chinese + /// @brief 获取渲染目标 + virtual TexturePtr GetTarget() const = 0; + public: /// \~chinese /// @brief 渲染上下文状态 diff --git a/src/kiwano/render/Renderer.h b/src/kiwano/render/Renderer.h index 25d87311..20436994 100644 --- a/src/kiwano/render/Renderer.h +++ b/src/kiwano/render/Renderer.h @@ -210,7 +210,7 @@ public: /// @param[in,out] texture 渲染输出的纹理 /// @param[in] desired_size 期望的输出大小 /// @return 纹理渲染上下文 - virtual RenderContextPtr CreateTextureRenderContext(Texture& texture, const Size* desired_size = nullptr) = 0; + virtual RenderContextPtr CreateTextureRenderContext(TexturePtr texture, const PixelSize& desired_size) = 0; public: /// \~chinese diff --git a/src/kiwano/render/Shape.cpp b/src/kiwano/render/Shape.cpp index c20d7dbf..8833b27b 100644 --- a/src/kiwano/render/Shape.cpp +++ b/src/kiwano/render/Shape.cpp @@ -22,10 +22,6 @@ #include #include -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - namespace kiwano { @@ -40,7 +36,7 @@ Rect Shape::GetBoundingBox() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX Rect bounds; - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (geometry) { // no matter it failed or not @@ -56,7 +52,7 @@ Rect Shape::GetBoundingBox(const Matrix3x2& transform) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX Rect bounds; - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (geometry) { // no matter it failed or not @@ -72,7 +68,7 @@ float Shape::GetLength() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX float length = 0.f; - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (geometry) { // no matter it failed or not @@ -87,7 +83,7 @@ float Shape::GetLength() const bool Shape::ComputePointAtLength(float length, Point& point, Vec2& tangent) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (geometry) { HRESULT hr = geometry->ComputePointAtLength(length, D2D1::Matrix3x2F::Identity(), DX::ConvertToPoint2F(&point), @@ -104,7 +100,7 @@ bool Shape::ComputePointAtLength(float length, Point& point, Vec2& tangent) cons float Shape::ComputeArea() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (geometry) { float area = 0.f; @@ -120,7 +116,7 @@ float Shape::ComputeArea() const bool Shape::ContainsPoint(const Point& point, const Matrix3x2* transform) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativePtr::Get(this); + auto geometry = NativeObject::Get(this); if (!geometry) return false; diff --git a/src/kiwano/render/ShapeMaker.cpp b/src/kiwano/render/ShapeMaker.cpp index 819e0564..48299ee4 100644 --- a/src/kiwano/render/ShapeMaker.cpp +++ b/src/kiwano/render/ShapeMaker.cpp @@ -21,10 +21,6 @@ #include #include -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - namespace kiwano { @@ -59,7 +55,7 @@ void ShapeMaker::BeginPath(const Point& begin_pos) } #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->BeginFigure(DX::ConvertToPoint2F(begin_pos), D2D1_FIGURE_BEGIN_FILLED); #else // not supported @@ -71,7 +67,7 @@ void ShapeMaker::EndPath(bool closed) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->EndFigure(closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); #else // not supported @@ -85,7 +81,7 @@ void ShapeMaker::AddLine(const Point& point) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->AddLine(DX::ConvertToPoint2F(point)); #else // not supported @@ -97,7 +93,7 @@ void ShapeMaker::AddLines(const Vector& points) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->AddLines(reinterpret_cast(&points[0]), static_cast(points.size())); #else // not supported @@ -109,7 +105,7 @@ void kiwano::ShapeMaker::AddLines(const Point* points, size_t count) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->AddLines(reinterpret_cast(points), UINT32(count)); #else // not supported @@ -121,7 +117,7 @@ void ShapeMaker::AddBezier(const Point& point1, const Point& point2, const Point KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->AddBezier( D2D1::BezierSegment(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), DX::ConvertToPoint2F(point3))); #else @@ -134,7 +130,7 @@ void ShapeMaker::AddArc(const Point& point, const Size& radius, float rotation, KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); native->AddArc(D2D1::ArcSegment(DX::ConvertToPoint2F(point), DX::ConvertToSizeF(radius), rotation, clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, is_small ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE)); @@ -151,9 +147,9 @@ ShapePtr ShapeMaker::Combine(ShapePtr shape_a, ShapePtr shape_b, CombineMode mod #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (shape_a && shape_b) { - auto geo_a = NativePtr::Get(shape_a); - auto geo_b = NativePtr::Get(shape_b); - auto native = NativePtr::Get(maker); + auto geo_a = NativeObject::Get(shape_a); + auto geo_b = NativeObject::Get(shape_b); + auto native = NativeObject::Get(maker); HRESULT hr = geo_a->CombineWithGeometry(geo_b.Get(), D2D1_COMBINE_MODE(mode), DX::ConvertToMatrix3x2F(matrix), native.Get()); @@ -176,7 +172,7 @@ void ShapeMaker::OpenStream() Renderer::GetInstance().CreateShapeSink(*this); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativePtr::Get(shape_); + auto geometry = NativeObject::Get(shape_); if (geometry) { ComPtr native; @@ -184,7 +180,7 @@ void ShapeMaker::OpenStream() HRESULT hr = geometry->Open(&native); if (SUCCEEDED(hr)) { - NativePtr::Set(this, native); + NativeObject::Set(this, native); } KGE_THROW_IF_FAILED(hr, "ID2D1PathGeometry::Open failed"); } @@ -199,7 +195,7 @@ void ShapeMaker::CloseStream() return; #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); HRESULT hr = native->Close(); KGE_THROW_IF_FAILED(hr, "ID2D1PathGeometry::Close failed"); diff --git a/src/kiwano/render/TextLayout.cpp b/src/kiwano/render/TextLayout.cpp index e8ae17bc..da600a64 100644 --- a/src/kiwano/render/TextLayout.cpp +++ b/src/kiwano/render/TextLayout.cpp @@ -21,10 +21,6 @@ #include #include -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - namespace kiwano { @@ -84,7 +80,7 @@ void TextLayout::SetFont(FontPtr font) return; #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -93,7 +89,7 @@ void TextLayout::SetFont(FontPtr font) // reset font collection { - auto collection = NativePtr::Get(font); + auto collection = NativeObject::Get(font); hr = native->SetFontCollection(collection.Get(), { 0, content_length_ }); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontCollection failed"); @@ -148,7 +144,7 @@ void TextLayout::SetFont(FontPtr font) void TextLayout::SetUnderline(bool enable) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -166,7 +162,7 @@ void TextLayout::SetUnderline(bool enable) void TextLayout::SetStrikethrough(bool enable) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -184,7 +180,7 @@ void TextLayout::SetStrikethrough(bool enable) void TextLayout::SetAlignment(TextAlign align) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -219,7 +215,7 @@ void TextLayout::SetAlignment(TextAlign align) void TextLayout::SetWrapWidth(float wrap_width) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -249,7 +245,7 @@ void TextLayout::SetWrapWidth(float wrap_width) void TextLayout::SetLineSpacing(float line_spacing) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); KGE_ASSERT(native); if (native) @@ -284,7 +280,7 @@ bool TextLayout::UpdateIfDirty() line_count_ = 0; size_ = Size(); - auto native = NativePtr::Get(this); + auto native = NativeObject::Get(this); if (content_length_ == 0 || !native) return true; diff --git a/src/kiwano/render/Texture.cpp b/src/kiwano/render/Texture.cpp index 5d70714e..e82f9797 100644 --- a/src/kiwano/render/Texture.cpp +++ b/src/kiwano/render/Texture.cpp @@ -23,10 +23,6 @@ #include #include // std::hash -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - namespace kiwano { @@ -108,8 +104,8 @@ void Texture::CopyFrom(TexturePtr copy_from) #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (IsValid() && copy_from) { - auto native = NativePtr::Get(this); - auto native_to_copy = NativePtr::Get(copy_from); + auto native = NativeObject::Get(this); + auto native_to_copy = NativeObject::Get(copy_from); HRESULT hr = native->CopyFromBitmap(nullptr, native_to_copy.Get(), nullptr); @@ -125,8 +121,8 @@ void Texture::CopyFrom(TexturePtr copy_from, const Rect& src_rect, const Point& #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (IsValid() && copy_from) { - auto native = NativePtr::Get(this); - auto native_to_copy = NativePtr::Get(copy_from); + auto native = NativeObject::Get(this); + auto native_to_copy = NativeObject::Get(copy_from); HRESULT hr = native->CopyFromBitmap(&D2D1::Point2U(uint32_t(dest_point.x), uint32_t(dest_point.y)), native_to_copy.Get(), diff --git a/src/kiwano/render/Texture.h b/src/kiwano/render/Texture.h index b420ef5b..3a9a73be 100644 --- a/src/kiwano/render/Texture.h +++ b/src/kiwano/render/Texture.h @@ -54,7 +54,6 @@ typedef math::Vec2T PixelSize; */ enum class PixelFormat { - Bpp32RGB, Bpp32RGBA, Bpp32BGR, Bpp32BGRA, From 185a256223b9354427498227f591d9cefb7db23b Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 16:00:41 +0800 Subject: [PATCH 02/14] chore: remove .vscode --- .gitignore | 1 + .vscode/c_cpp_properties.json | 34 ---------------------------------- .vscode/settings.json | 4 ---- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index b0f0dad1..f2fe03d6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ Release/ .vs .idea ._Kiwano.sln +.vscode # vs2010 ipch/ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 1eb88ef5..00000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "configurations": [ - { - "name": "Mac", - "includePath": [ - "${workspaceFolder}/src", - "${workspaceFolder}/src/3rd-party" - ], - "defines": [ - "TARGET_OS_MAC" - ], - "macFrameworkPath": [ - "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" - ], - "compilerPath": "/usr/bin/clang", - "cStandard": "c11", - "cppStandard": "c++14", - "intelliSenseMode": "clang-x64" - }, - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/src", - "${workspaceFolder}/src/3rd-party" - ], - "defines": [], - "compilerPath": "/usr/bin/gcc", - "cStandard": "c11", - "cppStandard": "c++14", - "intelliSenseMode": "clang-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index cfc8e1f0..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "files.autoGuessEncoding": true, - "files.encoding": "gb2312" -} \ No newline at end of file From 41f726b489105ff34cc9daaa67e8a51e2355292d Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 16:10:55 +0800 Subject: [PATCH 03/14] feat(canvas): add DrawActor --- src/kiwano/2d/Canvas.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/kiwano/2d/Canvas.h b/src/kiwano/2d/Canvas.h index cee5df73..c37c0664 100644 --- a/src/kiwano/2d/Canvas.h +++ b/src/kiwano/2d/Canvas.h @@ -86,6 +86,11 @@ public: /// @brief 结束渲染 void EndDraw(); + /// \~chinese + /// @brief 画角色 + /// @param actor 角色 + void DrawActor(ActorPtr actor); + /// \~chinese /// @brief 画形状轮廓 /// @param shape 形状 @@ -302,6 +307,15 @@ inline void CanvasRenderContext::EndDraw() ctx_->EndDraw(); } +inline void CanvasRenderContext::DrawActor(ActorPtr actor) +{ + KGE_ASSERT(ctx_); + if (actor) + { + actor->OnRender(*ctx_); + } +} + inline void CanvasRenderContext::DrawShape(ShapePtr shape) { KGE_ASSERT(ctx_); From be23fedc84a6d75035bd2ee8a243a76b48806d4d Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 16:21:41 +0800 Subject: [PATCH 04/14] fix: physic debug drawer --- src/kiwano-physics/PhysicWorld.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kiwano-physics/PhysicWorld.cpp b/src/kiwano-physics/PhysicWorld.cpp index fc459269..5b6b4cca 100644 --- a/src/kiwano-physics/PhysicWorld.cpp +++ b/src/kiwano-physics/PhysicWorld.cpp @@ -31,7 +31,7 @@ class PhysicWorld::DebugDrawer : public b2Draw public: DebugDrawer(const Size& size) { - canvas_ = MakePtr(size); + canvas_ = MakePtr(PixelSize(uint32_t(size.x), uint32_t(size.y))); ctx_ = canvas_->GetContext2D(); b2Draw::SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit | b2Draw::e_jointBit | b2Draw::e_centerOfMassBit); From 494a4d05f902b4af4ad01edc2c96da122f594cd2 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 16:22:01 +0800 Subject: [PATCH 05/14] feat: add render context blend mode --- src/kiwano/render/DirectX/RenderContextImpl.cpp | 6 ++++++ src/kiwano/render/DirectX/RenderContextImpl.h | 2 ++ src/kiwano/render/RenderContext.h | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/kiwano/render/DirectX/RenderContextImpl.cpp b/src/kiwano/render/DirectX/RenderContextImpl.cpp index 28d91082..56164665 100644 --- a/src/kiwano/render/DirectX/RenderContextImpl.cpp +++ b/src/kiwano/render/DirectX/RenderContextImpl.cpp @@ -429,6 +429,12 @@ void RenderContextImpl::SetTransform(const Matrix3x2& matrix) } } +void RenderContextImpl::SetBlendMode(BlendMode blend) +{ + KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); + render_ctx_->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND(blend)); +} + void RenderContextImpl::SetAntialiasMode(bool enabled) { KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); diff --git a/src/kiwano/render/DirectX/RenderContextImpl.h b/src/kiwano/render/DirectX/RenderContextImpl.h index d09b73a1..566c3f25 100644 --- a/src/kiwano/render/DirectX/RenderContextImpl.h +++ b/src/kiwano/render/DirectX/RenderContextImpl.h @@ -87,6 +87,8 @@ public: void SetTransform(const Matrix3x2& matrix) override; + void SetBlendMode(BlendMode blend) override; + void SetAntialiasMode(bool enabled) override; void SetTextAntialiasMode(TextAntialiasMode mode) override; diff --git a/src/kiwano/render/RenderContext.h b/src/kiwano/render/RenderContext.h index 046ed54c..3f4e1a8f 100644 --- a/src/kiwano/render/RenderContext.h +++ b/src/kiwano/render/RenderContext.h @@ -46,6 +46,17 @@ enum class TextAntialiasMode None ///< 不启用抗锯齿 }; +/// \~chinese +/// @brief 混合模式 +enum class BlendMode +{ + SourceOver = 0, + Copy = 1, + Min = 2, + Add = 3, + Max = 4, +}; + /// \~chinese /// @brief 渲染上下文 /// @details 渲染上下文将完成基础图元的绘制,并将绘制结果输出到特定的平面中 @@ -204,6 +215,10 @@ public: /// @brief 设置当前使用的线条样式 virtual void SetCurrentStrokeStyle(StrokeStylePtr stroke); + /// \~chinese + /// @brief 设置混合模式 + virtual void SetBlendMode(BlendMode blend) = 0; + /// \~chinese /// @brief 设置抗锯齿模式 virtual void SetAntialiasMode(bool enabled) = 0; From a7c4e9402bae04295d5c3406f7c11af7c26423b0 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 16:44:26 +0800 Subject: [PATCH 06/14] pref: separate text outline rendering --- .../render/DirectX/TextDrawingEffect.cpp | 121 +++++++++++- src/kiwano/render/DirectX/TextDrawingEffect.h | 7 + src/kiwano/render/DirectX/TextRenderer.cpp | 172 ++++++++---------- 3 files changed, 205 insertions(+), 95 deletions(-) diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.cpp b/src/kiwano/render/DirectX/TextDrawingEffect.cpp index 13b4bd48..b74faf3f 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.cpp +++ b/src/kiwano/render/DirectX/TextDrawingEffect.cpp @@ -37,6 +37,13 @@ public: STDMETHOD(CreateOutlineGeomerty) (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY); + STDMETHOD(CreateStrikethroughGeomerty) + (_Out_ ID2D1Geometry** ppStrikethroughGeo, _In_ DWRITE_STRIKETHROUGH const* strikethrough, float fOriginX, + float fOriginY); + + STDMETHOD(CreateUnderlineGeomerty) + (_Out_ ID2D1Geometry** ppUnderlineGeo, _In_ DWRITE_UNDERLINE const* underline, float fOriginX, float fOriginY); + // IUnknown methods virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); virtual ULONG STDMETHODCALLTYPE AddRef(); @@ -46,8 +53,9 @@ private: unsigned long cRefCount_; ComPtr pFactory_; - // Outline geometry cache - Map, ComPtr> outlineCache_; + Map, ComPtr> outlineCache_; + Map, ComPtr> strikethroughCache_; + Map, ComPtr> underlineCache_; }; HRESULT ITextDrawingEffect::Create(_Out_ ITextDrawingEffect** ppTextDrawingEffect, _In_ ID2D1Factory* pFactory) @@ -160,6 +168,115 @@ STDMETHODIMP TextDrawingEffect::CreateOutlineGeomerty(_Out_ ID2D1Geometry** return hr; } +STDMETHODIMP TextDrawingEffect::CreateStrikethroughGeomerty(_Out_ ID2D1Geometry** ppStrikethroughGeo, + _In_ DWRITE_STRIKETHROUGH const* strikethrough, + float fOriginX, float fOriginY) +{ + auto cache = strikethroughCache_.find(std::make_tuple(strikethrough, fOriginX, fOriginY)); + if (cache != strikethroughCache_.end()) + { + auto& pStrikethroughGeo = cache->second; + if (pStrikethroughGeo) + { + // Use cached geometry + pStrikethroughGeo->AddRef(); + DX::SafeRelease(*ppStrikethroughGeo); + (*ppStrikethroughGeo) = pStrikethroughGeo.Get(); + return S_OK; + } + } + + HRESULT hr = S_OK; + + ComPtr pStrikethroughGeo; + if (SUCCEEDED(hr)) + { + ComPtr pRectangleGeometry; + + D2D1_RECT_F rect = D2D1::RectF(0, strikethrough->offset, strikethrough->width, + strikethrough->offset + strikethrough->thickness); + hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + + if (SUCCEEDED(hr)) + { + const auto matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, fOriginX, fOriginY); + + ComPtr pTransformedGeometry; + hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + + if (SUCCEEDED(hr)) + { + pStrikethroughGeo = pTransformedGeometry; + } + } + } + + if (SUCCEEDED(hr)) + { + pStrikethroughGeo->AddRef(); + DX::SafeRelease(*ppStrikethroughGeo); + (*ppStrikethroughGeo) = pStrikethroughGeo.Get(); + + strikethroughCache_.insert( + std::make_pair(std::make_tuple(strikethrough, fOriginX, fOriginY), pStrikethroughGeo)); + } + return hr; +} + +STDMETHODIMP TextDrawingEffect::CreateUnderlineGeomerty(_Out_ ID2D1Geometry** ppUnderlineGeo, + _In_ DWRITE_UNDERLINE const* underline, float fOriginX, + float fOriginY) +{ + auto cache = underlineCache_.find(std::make_tuple(underline, fOriginX, fOriginY)); + if (cache != underlineCache_.end()) + { + auto& pUnderlineGeo = cache->second; + if (pUnderlineGeo) + { + // Use cached geometry + pUnderlineGeo->AddRef(); + DX::SafeRelease(*ppUnderlineGeo); + (*ppUnderlineGeo) = pUnderlineGeo.Get(); + return S_OK; + } + } + + HRESULT hr = S_OK; + + ComPtr pUnderlineGeo; + if (SUCCEEDED(hr)) + { + ComPtr pRectangleGeometry; + + D2D1_RECT_F rect = + D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness); + hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + + if (SUCCEEDED(hr)) + { + const auto matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, fOriginX, fOriginY); + + ComPtr pTransformedGeometry; + hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + + if (SUCCEEDED(hr)) + { + pUnderlineGeo = pTransformedGeometry; + } + } + } + + if (SUCCEEDED(hr)) + { + pUnderlineGeo->AddRef(); + DX::SafeRelease(*ppUnderlineGeo); + (*ppUnderlineGeo) = pUnderlineGeo.Get(); + + underlineCache_.insert(std::make_pair(std::make_tuple(underline, fOriginX, fOriginY), pUnderlineGeo)); + } + return hr; +} + STDMETHODIMP_(unsigned long) TextDrawingEffect::AddRef() { return InterlockedIncrement(&cRefCount_); diff --git a/src/kiwano/render/DirectX/TextDrawingEffect.h b/src/kiwano/render/DirectX/TextDrawingEffect.h index 05f6280c..5d5de754 100644 --- a/src/kiwano/render/DirectX/TextDrawingEffect.h +++ b/src/kiwano/render/DirectX/TextDrawingEffect.h @@ -36,6 +36,13 @@ public: STDMETHOD(CreateOutlineGeomerty) (_Out_ ID2D1Geometry** ppOutlineGeo, _In_ DWRITE_GLYPH_RUN const* glyphRun, float fOriginX, float fOriginY) PURE; + + STDMETHOD(CreateStrikethroughGeomerty) + (_Out_ ID2D1Geometry** ppStrikethroughGeo, _In_ DWRITE_STRIKETHROUGH const* strikethrough, float fOriginX, + float fOriginY) PURE; + + STDMETHOD(CreateUnderlineGeomerty) + (_Out_ ID2D1Geometry** ppUnderlineGeo, _In_ DWRITE_UNDERLINE const* underline, float fOriginX, float fOriginY) PURE; }; } // namespace directx } // namespace graphics diff --git a/src/kiwano/render/DirectX/TextRenderer.cpp b/src/kiwano/render/DirectX/TextRenderer.cpp index 718ef9ed..de2f22f2 100644 --- a/src/kiwano/render/DirectX/TextRenderer.cpp +++ b/src/kiwano/render/DirectX/TextRenderer.cpp @@ -71,6 +71,7 @@ public: HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); private: + bool bOutlineRendering_; unsigned long cRefCount_; uint32_t cPrimitivesCount_; float fDefaultOutlineWidth_; @@ -111,7 +112,8 @@ HRESULT ITextRenderer::Create(_Out_ ITextRenderer** ppTextRenderer, _In_ ID2D1De } TextRenderer::TextRenderer() - : cRefCount_(0) + : bOutlineRendering_(false) + , cRefCount_(0) , cPrimitivesCount_(0) , fDefaultOutlineWidth_(1) { @@ -150,8 +152,16 @@ STDMETHODIMP TextRenderer::DrawTextLayout(_In_ IDWriteTextLayout* pTextLayout, f pDefaultOutlineBrush_ = pDefaultOutlineBrush; fDefaultOutlineWidth_ = fDefaultOutlineWidth; pDefaultStrokeStyle_ = pDefaultStrokeStyle; + bOutlineRendering_ = pDefaultOutlineBrush_ != nullptr; - return pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + HRESULT hr = pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + if (SUCCEEDED(hr) && bOutlineRendering_) + { + bOutlineRendering_ = false; + + hr = pTextLayout->Draw(nullptr, this, fOriginX, fOriginY); + } + return hr; } STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, float baselineOriginX, @@ -164,37 +174,41 @@ STDMETHODIMP TextRenderer::DrawGlyphRun(__maybenull void* clientDrawingContext, KGE_NOT_USED(measuringMode); KGE_NOT_USED(glyphRunDescription); - ComPtr pTextDrawingEffect; - - HRESULT hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - - if (pDefaultOutlineBrush_) + HRESULT hr = S_OK; + if (!bOutlineRendering_) { - ComPtr pOutlineGeometry; - - if (SUCCEEDED(hr)) + if (pDefaultFillBrush_) { - hr = pTextDrawingEffect->CreateOutlineGeomerty(&pOutlineGeometry, glyphRun, baselineOriginX, - baselineOriginY); - } + if (SUCCEEDED(hr)) + { + pContext_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, + pDefaultFillBrush_.Get()); - if (SUCCEEDED(hr)) - { - pContext_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; + ++cPrimitivesCount_; + } } } - - if (pDefaultFillBrush_) + else { - if (SUCCEEDED(hr)) + if (pDefaultOutlineBrush_) { - pContext_->DrawGlyphRun(D2D1::Point2F(baselineOriginX, baselineOriginY), glyphRun, - pDefaultFillBrush_.Get()); + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - ++cPrimitivesCount_; + ComPtr pOutlineGeometry; + if (SUCCEEDED(hr)) + { + hr = pTextDrawingEffect->CreateOutlineGeomerty(&pOutlineGeometry, glyphRun, baselineOriginX, + baselineOriginY); + } + + if (SUCCEEDED(hr)) + { + pContext_->DrawGeometry(pOutlineGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, + pDefaultStrokeStyle_.Get()); + + ++cPrimitivesCount_; + } } } return hr; @@ -208,51 +222,37 @@ STDMETHODIMP TextRenderer::DrawUnderline(__maybenull void* clientDrawingContext, HRESULT hr = S_OK; - ComPtr pRectangleGeometry; - ComPtr pTransformedGeometry; - ComPtr pCurrentFillBrush; + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - if (clientDrawingEffect) + ComPtr pUnderlineGeometry; + if (SUCCEEDED(hr)) { - hr = clientDrawingEffect->QueryInterface(&pCurrentFillBrush); - } - else - { - pCurrentFillBrush = pDefaultFillBrush_; + hr = pTextDrawingEffect->CreateUnderlineGeomerty(&pUnderlineGeometry, underline, baselineOriginX, + baselineOriginY); } - if (pCurrentFillBrush || pDefaultOutlineBrush_) + if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) + if (!bOutlineRendering_) { - D2D1_RECT_F rect = - D2D1::RectF(0, underline->offset, underline->width, underline->offset + underline->thickness); - - hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + if (pDefaultFillBrush_) + { + pContext_->FillGeometry(pUnderlineGeometry.Get(), pDefaultFillBrush_.Get()); + ++cPrimitivesCount_; + } } - - if (SUCCEEDED(hr)) + else { - D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); + if (pDefaultOutlineBrush_) + { + pContext_->DrawGeometry(pUnderlineGeometry.Get(), pDefaultOutlineBrush_.Get(), + fDefaultOutlineWidth_, pDefaultStrokeStyle_.Get()); - hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + ++cPrimitivesCount_; + } } } - - if (SUCCEEDED(hr) && pDefaultOutlineBrush_) - { - pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; - } - - if (SUCCEEDED(hr) && pCurrentFillBrush) - { - pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); - - ++cPrimitivesCount_; - } return hr; } @@ -264,51 +264,37 @@ STDMETHODIMP TextRenderer::DrawStrikethrough(__maybenull void* clientDrawingCont HRESULT hr = S_OK; - ComPtr pRectangleGeometry; - ComPtr pTransformedGeometry; - ComPtr pCurrentFillBrush; + ComPtr pTextDrawingEffect; + hr = clientDrawingEffect->QueryInterface(&pTextDrawingEffect); - if (clientDrawingEffect) + ComPtr pStrikethroughGeometry; + if (SUCCEEDED(hr)) { - hr = clientDrawingEffect->QueryInterface(&pCurrentFillBrush); - } - else - { - pCurrentFillBrush = pDefaultFillBrush_; + hr = pTextDrawingEffect->CreateStrikethroughGeomerty(&pStrikethroughGeometry, strikethrough, baselineOriginX, + baselineOriginY); } - if (pCurrentFillBrush || pDefaultOutlineBrush_) + if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) + if (!bOutlineRendering_) { - D2D1_RECT_F rect = D2D1::RectF(0, strikethrough->offset, strikethrough->width, - strikethrough->offset + strikethrough->thickness); - - hr = pFactory_->CreateRectangleGeometry(&rect, &pRectangleGeometry); + if (pDefaultFillBrush_) + { + pContext_->FillGeometry(pStrikethroughGeometry.Get(), pDefaultFillBrush_.Get()); + ++cPrimitivesCount_; + } } - - if (SUCCEEDED(hr)) + else { - D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); + if (pDefaultOutlineBrush_) + { + pContext_->DrawGeometry(pStrikethroughGeometry.Get(), pDefaultOutlineBrush_.Get(), + fDefaultOutlineWidth_, pDefaultStrokeStyle_.Get()); - hr = pFactory_->CreateTransformedGeometry(pRectangleGeometry.Get(), &matrix, &pTransformedGeometry); + ++cPrimitivesCount_; + } } } - - if (SUCCEEDED(hr) && pDefaultOutlineBrush_) - { - pContext_->DrawGeometry(pTransformedGeometry.Get(), pDefaultOutlineBrush_.Get(), fDefaultOutlineWidth_, - pDefaultStrokeStyle_.Get()); - - ++cPrimitivesCount_; - } - - if (SUCCEEDED(hr) && pCurrentFillBrush) - { - pContext_->FillGeometry(pTransformedGeometry.Get(), pCurrentFillBrush.Get()); - - ++cPrimitivesCount_; - } return hr; } From 52cbf0d1a6b6ff0c7712d4290bd914757566cd01 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 17:11:51 +0800 Subject: [PATCH 07/14] feat(sound): sound player release function --- src/kiwano-audio/SoundPlayer.cpp | 5 +++++ src/kiwano-audio/SoundPlayer.h | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp index db48a810..3cd35d3d 100644 --- a/src/kiwano-audio/SoundPlayer.cpp +++ b/src/kiwano-audio/SoundPlayer.cpp @@ -155,6 +155,11 @@ void SoundPlayer::StopAll() } } +void SoundPlayer::ReleaseSound(size_t id) +{ + sound_cache_.erase(id); +} + void SoundPlayer::ClearCache() { sound_cache_.clear(); diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h index 6ed8d8f6..1f9acf79 100644 --- a/src/kiwano-audio/SoundPlayer.h +++ b/src/kiwano-audio/SoundPlayer.h @@ -40,6 +40,8 @@ KGE_DECLARE_SMART_PTR(SoundPlayer); class KGE_API SoundPlayer : public ObjectBase { public: + using SoundMap = Map; + SoundPlayer(); ~SoundPlayer(); @@ -120,6 +122,15 @@ public: /// @brief 停止所有音频 void StopAll(); + /// \~chinese + /// @brief 释放音乐对象缓存 + /// @param id 音频标识符 + void ReleaseSound(size_t id); + + /// \~chinese + /// @brief 获取缓存 + const SoundMap& GetCache() const; + /// \~chinese /// @brief 清除缓存 void ClearCache(); @@ -127,10 +138,15 @@ public: private: float volume_; - using SoundMap = Map; SoundMap sound_cache_; }; /** @} */ + +inline const SoundPlayer::SoundMap& SoundPlayer::GetCache() const +{ + return sound_cache_; +} + } // namespace audio } // namespace kiwano From 03c24091c73352980499750645d394464b699984 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 21:43:06 +0800 Subject: [PATCH 08/14] pref: refactoring native object --- projects/kiwano/kiwano.vcxproj | 3 +- projects/kiwano/kiwano.vcxproj.filters | 7 +- src/kiwano-audio/Sound.cpp | 10 +- src/kiwano-audio/Sound.h | 1 - src/kiwano/platform/NativeObject.hpp | 89 +++++++++++ src/kiwano/platform/win32/ComPtr.hpp | 73 ++++++++- src/kiwano/render/Brush.cpp | 6 +- src/kiwano/render/Brush.h | 2 +- .../render/DirectX/FontCollectionLoader.h | 1 - .../render/DirectX/RenderContextImpl.cpp | 58 ++++---- src/kiwano/render/DirectX/RendererImpl.cpp | 50 +++---- src/kiwano/render/DirectX/helper.h | 1 + src/kiwano/render/Font.cpp | 2 +- src/kiwano/render/Font.h | 2 +- src/kiwano/render/GifImage.cpp | 7 +- src/kiwano/render/NativeObject.cpp | 44 ------ src/kiwano/render/NativeObject.h | 139 ------------------ src/kiwano/render/Shape.cpp | 18 ++- src/kiwano/render/Shape.h | 2 +- src/kiwano/render/ShapeMaker.cpp | 34 +++-- src/kiwano/render/StrokeStyle.cpp | 1 - src/kiwano/render/StrokeStyle.h | 2 +- src/kiwano/render/TextLayout.cpp | 27 ++-- src/kiwano/render/TextLayout.h | 7 +- src/kiwano/render/Texture.cpp | 12 +- src/kiwano/render/Texture.h | 2 +- 26 files changed, 295 insertions(+), 305 deletions(-) create mode 100644 src/kiwano/platform/NativeObject.hpp delete mode 100644 src/kiwano/render/NativeObject.cpp delete mode 100644 src/kiwano/render/NativeObject.h diff --git a/projects/kiwano/kiwano.vcxproj b/projects/kiwano/kiwano.vcxproj index 7e76543c..15c310dc 100644 --- a/projects/kiwano/kiwano.vcxproj +++ b/projects/kiwano/kiwano.vcxproj @@ -82,6 +82,7 @@ + @@ -100,7 +101,6 @@ - @@ -192,7 +192,6 @@ - diff --git a/projects/kiwano/kiwano.vcxproj.filters b/projects/kiwano/kiwano.vcxproj.filters index 399185c7..8171b4f8 100644 --- a/projects/kiwano/kiwano.vcxproj.filters +++ b/projects/kiwano/kiwano.vcxproj.filters @@ -405,8 +405,8 @@ render\DirectX - - render + + platform @@ -671,9 +671,6 @@ render\DirectX - - render - diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index f04421c4..e340c9f3 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -208,12 +208,12 @@ bool Sound::IsPlaying() const if (!voice_) return false; + if (!playing_) + return false; + XAUDIO2_VOICE_STATE state; voice_->GetState(&state); - uint32_t buffers_queued = state.BuffersQueued; - - if (buffers_queued && playing_) - return true; + return !!state.BuffersQueued; } return false; } @@ -231,7 +231,7 @@ void Sound::SetVolume(float volume) { KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - volume = std::min(std::max(volume, -224.f), 224.f); + volume = std::min(std::max(volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL); voice_->SetVolume(volume); } } // namespace audio diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index ebd2ce84..657cfa0c 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -22,7 +22,6 @@ #include #include #include -#include #include namespace kiwano diff --git a/src/kiwano/platform/NativeObject.hpp b/src/kiwano/platform/NativeObject.hpp new file mode 100644 index 00000000..b8f3259b --- /dev/null +++ b/src/kiwano/platform/NativeObject.hpp @@ -0,0 +1,89 @@ +// 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 + +namespace kiwano +{ + +KGE_DECLARE_SMART_PTR(NativeObject); + +/** + * \~chinese + * @brief 含有本地指针的对象 + */ +class KGE_API NativeObject : public ObjectBase +{ +public: + NativeObject() = default; + + const Any& GetNative() const; + + template + _Ty GetNative() const; + + template + _Ty* GetNativePtr() const; + + void SetNative(const Any& native); + + void ResetNative(); + +protected: + Any native_; +}; + +inline const Any& NativeObject::GetNative() const +{ + return native_; +} + +template +inline _Ty NativeObject::GetNative() const +{ + if (native_.HasValue()) + { + return native_.Cast<_Ty>(); + } + return nullptr; +} + +template +inline _Ty* NativeObject::GetNativePtr() const +{ + if (native_.HasValue()) + { + return native_.Cast<_Ty*>(); + } + return nullptr; +} + +inline void NativeObject::SetNative(const Any& native) +{ + native_ = native; +} + +inline void NativeObject::ResetNative() +{ + native_.Clear(); +} + +} // namespace kiwano diff --git a/src/kiwano/platform/win32/ComPtr.hpp b/src/kiwano/platform/win32/ComPtr.hpp index 9fe892ee..37d480f1 100644 --- a/src/kiwano/platform/win32/ComPtr.hpp +++ b/src/kiwano/platform/win32/ComPtr.hpp @@ -22,11 +22,12 @@ #include #include #include +#include #include namespace kiwano { -struct ComPtrPolicy +struct ComRefPolicy { inline void Retain(IUnknown* ptr) { @@ -43,6 +44,74 @@ struct ComPtrPolicy // ComPtr<> is a smart pointer for COM template ::value, int>::type> -using ComPtr = RefBasePtr<_Ty, ComPtrPolicy>; +using ComPtr = RefBasePtr<_Ty, ComRefPolicy>; + +struct ComPolicy +{ + template ::value, int>::type> + static inline ComPtr<_Ty> Get(const NativeObject* object) + { + if (object) + { + const auto& native = object->GetNative(); + if (native.HasValue()) + { + auto ptr = native.Cast>(); + if (ptr) + { + ComPtr<_Ty> native; + if (SUCCEEDED(ptr->QueryInterface<_Ty>(&native))) + return native; + } + } + } + return nullptr; + } + + template ::value, int>::type> + static inline ComPtr<_Ty> Get(const NativeObject& object) + { + return ComPolicy::Get<_Ty>(&object); + } + + template ::value, int>::type> + static inline ComPtr<_Ty> Get(NativeObjectPtr object) + { + return ComPolicy::Get<_Ty>(object.Get()); + } + + static inline void Set(NativeObject* object, ComPtr com_ptr) + { + if (object) + { + object->SetNative(Any{ com_ptr }); + } + } + + static inline void Set(NativeObject* object, IUnknown* com_ptr) + { + ComPolicy::Set(object, ComPtr(com_ptr)); + } + + static inline void Set(NativeObject& object, IUnknown* com_ptr) + { + ComPolicy::Set(&object, com_ptr); + } + + static inline void Set(NativeObjectPtr object, IUnknown* com_ptr) + { + ComPolicy::Set(object.Get(), com_ptr); + } + + static inline void Set(NativeObject& object, ComPtr com_ptr) + { + ComPolicy::Set(&object, com_ptr.Get()); + } + + static inline void Set(NativeObjectPtr object, ComPtr com_ptr) + { + ComPolicy::Set(object.Get(), com_ptr.Get()); + } +}; } // namespace kiwano diff --git a/src/kiwano/render/Brush.cpp b/src/kiwano/render/Brush.cpp index ab25d187..2b8545de 100644 --- a/src/kiwano/render/Brush.cpp +++ b/src/kiwano/render/Brush.cpp @@ -22,6 +22,10 @@ #include #include +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include +#endif + namespace kiwano { GradientStop::GradientStop() @@ -120,7 +124,7 @@ void Brush::SetTransform(const Transform& transform) void Brush::SetTransform(const Matrix3x2& transform) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) diff --git a/src/kiwano/render/Brush.h b/src/kiwano/render/Brush.h index e54da60f..51da5e08 100644 --- a/src/kiwano/render/Brush.h +++ b/src/kiwano/render/Brush.h @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include +#include #include #include diff --git a/src/kiwano/render/DirectX/FontCollectionLoader.h b/src/kiwano/render/DirectX/FontCollectionLoader.h index a7d7816f..3a4f0576 100644 --- a/src/kiwano/render/DirectX/FontCollectionLoader.h +++ b/src/kiwano/render/DirectX/FontCollectionLoader.h @@ -21,7 +21,6 @@ #pragma once #include #include -#include namespace kiwano { diff --git a/src/kiwano/render/DirectX/RenderContextImpl.cpp b/src/kiwano/render/DirectX/RenderContextImpl.cpp index 56164665..838e04db 100644 --- a/src/kiwano/render/DirectX/RenderContextImpl.cpp +++ b/src/kiwano/render/DirectX/RenderContextImpl.cpp @@ -62,7 +62,7 @@ HRESULT RenderContextImpl::CreateDeviceResources(ComPtr factory, C if (SUCCEEDED(hr)) { - NativeObject::Set(this, ctx); + ComPolicy::Set(this, ctx); } return hr; } @@ -73,7 +73,7 @@ void RenderContextImpl::DiscardDeviceResources() render_ctx_.Reset(); current_brush_.Reset(); - ResetNativePointer(); + ComPolicy::Set(this, nullptr); } TexturePtr RenderContextImpl::GetTarget() const @@ -85,7 +85,7 @@ TexturePtr RenderContextImpl::GetTarget() const if (target) { TexturePtr ptr = MakePtr(); - NativeObject::Set(*ptr, target.Get()); + ComPolicy::Set(*ptr, target.Get()); } return nullptr; } @@ -123,7 +123,7 @@ void RenderContextImpl::CreateTexture(Texture& texture, const PixelSize& size) if (SUCCEEDED(hr)) { - NativeObject::Set(texture, saved_bitmap); + ComPolicy::Set(texture, saved_bitmap); } KGE_THROW_IF_FAILED(hr, "Create texture failed"); @@ -145,7 +145,7 @@ void RenderContextImpl::DrawTexture(const Texture& texture, const Rect* src_rect mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; } - auto bitmap = NativeObject::Get(texture); + auto bitmap = ComPolicy::Get(texture); render_ctx_->DrawBitmap(bitmap.Get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, brush_opacity_, mode, src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr); @@ -159,10 +159,10 @@ void RenderContextImpl::DrawTextLayout(const TextLayout& layout, const Point& of if (layout.IsValid()) { - auto native = NativeObject::Get(layout); - auto fill_brush = NativeObject::Get(current_brush_); - auto outline_brush = NativeObject::Get(current_outline_brush); - auto outline_stroke = NativeObject::Get(current_stroke_); + auto native = ComPolicy::Get(layout); + auto fill_brush = ComPolicy::Get(current_brush_); + auto outline_brush = ComPolicy::Get(current_outline_brush); + auto outline_stroke = ComPolicy::Get(current_stroke_); float outline_width = 1.0f; if (fill_brush) @@ -201,9 +201,9 @@ void RenderContextImpl::DrawShape(const Shape& shape) if (shape.IsValid()) { - auto geometry = NativeObject::Get(shape); - auto brush = NativeObject::Get(current_brush_); - auto stroke_style = NativeObject::Get(current_stroke_); + auto geometry = ComPolicy::Get(shape); + auto brush = ComPolicy::Get(current_brush_); + auto stroke_style = ComPolicy::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; render_ctx_->DrawGeometry(geometry.Get(), brush.Get(), stroke_width, stroke_style.Get()); @@ -217,8 +217,8 @@ void RenderContextImpl::DrawLine(const Point& point1, const Point& point2) KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); - auto stroke_style = NativeObject::Get(current_stroke_); + auto brush = ComPolicy::Get(current_brush_); + auto stroke_style = ComPolicy::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; render_ctx_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), brush.Get(), stroke_width, @@ -232,8 +232,8 @@ void RenderContextImpl::DrawRectangle(const Rect& rect) KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); - auto stroke_style = NativeObject::Get(current_stroke_); + auto brush = ComPolicy::Get(current_brush_); + auto stroke_style = ComPolicy::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; render_ctx_->DrawRectangle(DX::ConvertToRectF(rect), brush.Get(), stroke_width, stroke_style.Get()); @@ -246,8 +246,8 @@ void RenderContextImpl::DrawRoundedRectangle(const Rect& rect, const Vec2& radiu KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); - auto stroke_style = NativeObject::Get(current_stroke_); + auto brush = ComPolicy::Get(current_brush_); + auto stroke_style = ComPolicy::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; render_ctx_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get(), @@ -261,8 +261,8 @@ void RenderContextImpl::DrawEllipse(const Point& center, const Vec2& radius) KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); - auto stroke_style = NativeObject::Get(current_stroke_); + auto brush = ComPolicy::Get(current_brush_); + auto stroke_style = ComPolicy::Get(current_stroke_); float stroke_width = current_stroke_ ? current_stroke_->GetWidth() : 1.0f; render_ctx_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.Get(), @@ -278,8 +278,8 @@ void RenderContextImpl::FillShape(const Shape& shape) if (shape.IsValid()) { - auto brush = NativeObject::Get(current_brush_); - auto geometry = NativeObject::Get(shape); + auto brush = ComPolicy::Get(current_brush_); + auto geometry = ComPolicy::Get(shape); render_ctx_->FillGeometry(geometry.Get(), brush.Get()); IncreasePrimitivesCount(); @@ -291,7 +291,7 @@ void RenderContextImpl::FillRectangle(const Rect& rect) KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); + auto brush = ComPolicy::Get(current_brush_); render_ctx_->FillRectangle(DX::ConvertToRectF(rect), brush.Get()); IncreasePrimitivesCount(); @@ -302,7 +302,7 @@ void RenderContextImpl::FillRoundedRectangle(const Rect& rect, const Vec2& radiu KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); + auto brush = ComPolicy::Get(current_brush_); render_ctx_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), brush.Get()); IncreasePrimitivesCount(); @@ -313,7 +313,7 @@ void RenderContextImpl::FillEllipse(const Point& center, const Vec2& radius) KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); - auto brush = NativeObject::Get(current_brush_); + auto brush = ComPolicy::Get(current_brush_); render_ctx_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), brush.Get()); IncreasePrimitivesCount(); @@ -345,8 +345,8 @@ void RenderContextImpl::PushLayer(Layer& layer) { KGE_ASSERT(render_ctx_ && "Render target has not been initialized!"); - auto native = NativeObject::Get(layer); - auto mask = NativeObject::Get(layer.GetMaskShape()); + auto native = ComPolicy::Get(layer); + auto mask = ComPolicy::Get(layer.GetMaskShape()); if (!native) { @@ -354,7 +354,7 @@ void RenderContextImpl::PushLayer(Layer& layer) if (SUCCEEDED(hr)) { - NativeObject::Set(layer, native); + ComPolicy::Set(layer, native); } KGE_THROW_IF_FAILED(hr, "Create ID2D1Layer failed"); } @@ -400,7 +400,7 @@ void RenderContextImpl::SetCurrentBrush(BrushPtr brush) if (current_brush_ && current_brush_->IsValid()) { - NativeObject::Get(current_brush_)->SetOpacity(brush_opacity_); + ComPolicy::Get(current_brush_)->SetOpacity(brush_opacity_); } } diff --git a/src/kiwano/render/DirectX/RendererImpl.cpp b/src/kiwano/render/DirectX/RendererImpl.cpp index c68cd75d..92e6000e 100644 --- a/src/kiwano/render/DirectX/RendererImpl.cpp +++ b/src/kiwano/render/DirectX/RendererImpl.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include @@ -258,7 +258,7 @@ void RendererImpl::CreateTexture(Texture& texture, const String& file_path) if (SUCCEEDED(hr)) { - NativeObject::Set(texture, bitmap); + ComPolicy::Set(texture, bitmap); texture.SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); texture.SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -307,7 +307,7 @@ void RendererImpl::CreateTexture(Texture& texture, const BinaryData& data) if (SUCCEEDED(hr)) { - NativeObject::Set(texture, bitmap); + ComPolicy::Set(texture, bitmap); texture.SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); texture.SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -344,7 +344,7 @@ void RendererImpl::CreateTexture(Texture& texture, const PixelSize& size, const D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(dxgi_format)), &output); if (SUCCEEDED(hr)) { - NativeObject::Set(texture, output); + ComPolicy::Set(texture, output); texture.SetSize({ output->GetSize().width, output->GetSize().height }); texture.SetSizeInPixels({ output->GetPixelSize().width, output->GetPixelSize().height }); @@ -380,7 +380,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const String& file_path) if (SUCCEEDED(hr)) { - NativeObject::Set(gif, decoder); + ComPolicy::Set(gif, decoder); } } @@ -406,7 +406,7 @@ void RendererImpl::CreateGifImage(GifImage& gif, const BinaryData& data) if (SUCCEEDED(hr)) { - NativeObject::Set(gif, decoder); + ComPolicy::Set(gif, decoder); } } } @@ -422,7 +422,7 @@ void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, const GifImage& g hr = E_UNEXPECTED; } - auto decoder = NativeObject::Get(gif); + auto decoder = ComPolicy::Get(gif); if (!decoder) { @@ -449,7 +449,7 @@ void RendererImpl::CreateGifImageFrame(GifImage::Frame& frame, const GifImage& g if (SUCCEEDED(hr)) { frame.texture = MakePtr(); - NativeObject::Set(frame.texture, bitmap); + ComPolicy::Set(frame.texture, bitmap); frame.texture->SetSize({ bitmap->GetSize().width, bitmap->GetSize().height }); frame.texture->SetSizeInPixels({ bitmap->GetPixelSize().width, bitmap->GetPixelSize().height }); @@ -603,7 +603,7 @@ void RendererImpl::CreateFontCollection(Font& font, Vector& family_names if (SUCCEEDED(hr)) { d2d_res_->GetFontFamilyNames(family_names, font_collection); // ignore the result - NativeObject::Set(font, font_collection); + ComPolicy::Set(font, font_collection); } } @@ -626,7 +626,7 @@ void RendererImpl::CreateFontCollection(Font& font, Vector& family_names if (SUCCEEDED(hr)) { d2d_res_->GetFontFamilyNames(family_names, font_collection); // ignore the result - NativeObject::Set(font, font_collection); + ComPolicy::Set(font, font_collection); } } @@ -660,7 +660,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c auto font_weight = DWRITE_FONT_WEIGHT(font->GetWeight()); auto font_style = DWRITE_FONT_STYLE(font->GetPosture()); auto font_stretch = DWRITE_FONT_STRETCH(font->GetStretch()); - auto collection = NativeObject::Get(font); + auto collection = ComPolicy::Get(font); WideString font_family; @@ -683,7 +683,7 @@ void RendererImpl::CreateTextLayout(TextLayout& layout, const String& content, c if (SUCCEEDED(hr)) { - NativeObject::Set(layout, output); + ComPolicy::Set(layout, output); layout.SetDirtyFlag(TextLayout::DirtyFlag::Dirty); } } @@ -720,7 +720,7 @@ void RendererImpl::CreateLineShape(Shape& shape, const Point& begin_pos, const P if (SUCCEEDED(hr)) { - NativeObject::Set(shape, path_geo); + ComPolicy::Set(shape, path_geo); } } } @@ -744,7 +744,7 @@ void RendererImpl::CreateRectShape(Shape& shape, const Rect& rect) if (SUCCEEDED(hr)) { - NativeObject::Set(shape, output); + ComPolicy::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1RectangleGeometry failed"); @@ -767,7 +767,7 @@ void RendererImpl::CreateRoundedRectShape(Shape& shape, const Rect& rect, const if (SUCCEEDED(hr)) { - NativeObject::Set(shape, output); + ComPolicy::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1RoundedRectangleGeometry failed"); @@ -790,7 +790,7 @@ void RendererImpl::CreateEllipseShape(Shape& shape, const Point& center, const V if (SUCCEEDED(hr)) { - NativeObject::Set(shape, output); + ComPolicy::Set(shape, output); } KGE_SET_STATUS_IF_FAILED(hr, shape, "Create ID2D1EllipseGeometry failed"); @@ -813,7 +813,7 @@ void RendererImpl::CreateShapeSink(ShapeMaker& maker) if (SUCCEEDED(hr)) { ShapePtr shape = MakePtr(); - NativeObject::Set(shape, geometry); + ComPolicy::Set(shape, geometry); maker.SetShape(shape); } @@ -835,7 +835,7 @@ void RendererImpl::CreateBrush(Brush& brush, const Color& color) if (brush.GetType() == Brush::Type::SolidColor && brush.IsValid()) { - hr = NativeObject::Get(brush)->QueryInterface(&solid_brush); + hr = ComPolicy::Get(brush)->QueryInterface(&solid_brush); if (SUCCEEDED(hr)) { solid_brush->SetColor(DX::ConvertToColorF(color)); @@ -847,7 +847,7 @@ void RendererImpl::CreateBrush(Brush& brush, const Color& color) if (SUCCEEDED(hr)) { - NativeObject::Set(brush, solid_brush); + ComPolicy::Set(brush, solid_brush); } } } @@ -879,7 +879,7 @@ void RendererImpl::CreateBrush(Brush& brush, const LinearGradientStyle& style) if (SUCCEEDED(hr)) { - NativeObject::Set(brush, output); + ComPolicy::Set(brush, output); } } } @@ -912,7 +912,7 @@ void RendererImpl::CreateBrush(Brush& brush, const RadialGradientStyle& style) if (SUCCEEDED(hr)) { - NativeObject::Set(brush, output); + ComPolicy::Set(brush, output); } } } @@ -930,7 +930,7 @@ void RendererImpl::CreateBrush(Brush& brush, TexturePtr texture) if (SUCCEEDED(hr)) { - auto bitmap = NativeObject::Get(texture); + auto bitmap = ComPolicy::Get(texture); if (SUCCEEDED(hr)) { @@ -939,7 +939,7 @@ void RendererImpl::CreateBrush(Brush& brush, TexturePtr texture) if (SUCCEEDED(hr)) { - NativeObject::Set(brush, output); + ComPolicy::Set(brush, output); } } } @@ -979,7 +979,7 @@ void RendererImpl::CreateStrokeStyle(StrokeStyle& stroke_style) if (SUCCEEDED(hr)) { - NativeObject::Set(stroke_style, output); + ComPolicy::Set(stroke_style, output); } } @@ -1023,7 +1023,7 @@ RenderContextPtr RendererImpl::CreateTextureRenderContext(TexturePtr texture, co if (SUCCEEDED(hr)) { render_ctx->SetTarget(output.Get()); - NativeObject::Set(texture, output); + ComPolicy::Set(texture, output); texture->SetSize({ output->GetSize().width, output->GetSize().height }); texture->SetSizeInPixels({ output->GetPixelSize().width, output->GetPixelSize().height }); diff --git a/src/kiwano/render/DirectX/helper.h b/src/kiwano/render/DirectX/helper.h index 44244d4e..87f8b774 100644 --- a/src/kiwano/render/DirectX/helper.h +++ b/src/kiwano/render/DirectX/helper.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace kiwano { diff --git a/src/kiwano/render/Font.cpp b/src/kiwano/render/Font.cpp index dacfdfbf..4e490274 100644 --- a/src/kiwano/render/Font.cpp +++ b/src/kiwano/render/Font.cpp @@ -105,7 +105,7 @@ Font::Font(const String& family_name, float size, uint32_t weight, FontPosture p FontPtr found = FontCache::GetInstance().GetFontByFamily(family_name); if (found) { - this->ResetNativePointer(found->GetNativePointer()); + this->SetNative(found->GetNative()); } } diff --git a/src/kiwano/render/Font.h b/src/kiwano/render/Font.h index 11081c24..87ffc3b1 100644 --- a/src/kiwano/render/Font.h +++ b/src/kiwano/render/Font.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include namespace kiwano { diff --git a/src/kiwano/render/GifImage.cpp b/src/kiwano/render/GifImage.cpp index 055bb8b7..5d7117f7 100644 --- a/src/kiwano/render/GifImage.cpp +++ b/src/kiwano/render/GifImage.cpp @@ -84,7 +84,7 @@ bool GifImage::Load(const String& file_path) return true; // Clear data - ResetNativePointer(); + ResetNative(); Fail("GifImage::Load failed"); } return false; @@ -100,7 +100,7 @@ bool GifImage::Load(const Resource& res) return true; // Clear data - ResetNativePointer(); + ResetNative(); Fail("GifImage::Load failed"); } return false; @@ -116,13 +116,14 @@ GifImage::Frame GifImage::GetFrame(uint32_t index) } // namespace kiwano #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include namespace kiwano { bool GifImage::GetGlobalMetadata() { - ComPtr decoder = NativeObject::Get(this); + ComPtr decoder = ComPolicy::Get(this); HRESULT hr = decoder ? S_OK : E_FAIL; diff --git a/src/kiwano/render/NativeObject.cpp b/src/kiwano/render/NativeObject.cpp deleted file mode 100644 index 7c3f01a6..00000000 --- a/src/kiwano/render/NativeObject.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "NativeObject.h" - -namespace kiwano -{ -NativeObjectBase::NativeObjectBase() - : native_pointer_(nullptr) -{ -} - -bool NativeObjectBase::IsValid() const -{ - return native_pointer_ != nullptr; -} - -void NativeObjectBase::ResetNativePointer(void* native_pointer) -{ - native_pointer_ = native_pointer; -} - -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - -NativeObject::~NativeObject() -{ - ResetNativePointer(); -} - -void NativeObject::ResetNativePointer(void* native_pointer) -{ - if (native_pointer_) - { - static_cast(native_pointer_)->Release(); - native_pointer_ = nullptr; - } - - if (native_pointer) - { - native_pointer_ = native_pointer; - static_cast(native_pointer_)->AddRef(); - } -} - -#endif - -} diff --git a/src/kiwano/render/NativeObject.h b/src/kiwano/render/NativeObject.h deleted file mode 100644 index eebeac0a..00000000 --- a/src/kiwano/render/NativeObject.h +++ /dev/null @@ -1,139 +0,0 @@ -// 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 - -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX -#include -#endif - -namespace kiwano -{ - -KGE_DECLARE_SMART_PTR(NativeObject); - -/** - * \addtogroup Render - * @{ - */ - -/** - * \~chinese - * @brief 含有本地指针的对象 - */ -class KGE_API NativeObjectBase : public ObjectBase -{ -public: - NativeObjectBase(); - - bool IsValid() const override; - - void* GetNativePointer() const; - - template - _NativeTy* GetNativePointer() const; - - virtual void ResetNativePointer(void* native_pointer = nullptr); - -protected: - void* native_pointer_; -}; - -#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - -class KGE_API NativeObject : public NativeObjectBase -{ -public: - virtual ~NativeObject(); - - void ResetNativePointer(void* native_pointer = nullptr) override; - - template ::value, int>::type> - static inline ComPtr<_Ty> Get(const NativeObject* object) - { - if (object) - { - ComPtr ptr = object->GetNativePointer(); - if (ptr) - { - ComPtr<_Ty> native; - if (SUCCEEDED(ptr->QueryInterface<_Ty>(&native))) - return native; - } - } - return nullptr; - } - - template - static inline ComPtr<_Ty> Get(const NativeObject& object) - { - return NativeObject::Get<_Ty>(&object); - } - - template - static inline ComPtr<_Ty> Get(NativeObjectPtr object) - { - return NativeObject::Get<_Ty>(object.Get()); - } - - static inline void Set(NativeObject* object, ComPtr com_ptr) - { - if (object) - { - object->ResetNativePointer(com_ptr.Get()); - } - } - - static inline void Set(NativeObject& object, ComPtr com_ptr) - { - NativeObject::Set(&object, com_ptr); - } - - static inline void Set(NativeObjectPtr object, ComPtr com_ptr) - { - NativeObject::Set(object.Get(), com_ptr); - } -}; - -#else - -typedef NativeObjectBase NativeObject; - -#endif - -/** @} */ - -inline void* NativeObjectBase::GetNativePointer() const -{ - return native_pointer_; -} - -template -inline _NativeTy* NativeObjectBase::GetNativePointer() const -{ - if (native_pointer_ != nullptr) - { - return static_cast<_NativeTy*>(native_pointer_); - } - return nullptr; -} - -} // namespace kiwano diff --git a/src/kiwano/render/Shape.cpp b/src/kiwano/render/Shape.cpp index 8833b27b..7b19fc68 100644 --- a/src/kiwano/render/Shape.cpp +++ b/src/kiwano/render/Shape.cpp @@ -22,6 +22,10 @@ #include #include +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include +#endif + namespace kiwano { @@ -29,14 +33,14 @@ Shape::Shape() {} void Shape::Clear() { - ResetNativePointer(); + ResetNative(); } Rect Shape::GetBoundingBox() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX Rect bounds; - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (geometry) { // no matter it failed or not @@ -52,7 +56,7 @@ Rect Shape::GetBoundingBox(const Matrix3x2& transform) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX Rect bounds; - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (geometry) { // no matter it failed or not @@ -68,7 +72,7 @@ float Shape::GetLength() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX float length = 0.f; - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (geometry) { // no matter it failed or not @@ -83,7 +87,7 @@ float Shape::GetLength() const bool Shape::ComputePointAtLength(float length, Point& point, Vec2& tangent) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (geometry) { HRESULT hr = geometry->ComputePointAtLength(length, D2D1::Matrix3x2F::Identity(), DX::ConvertToPoint2F(&point), @@ -100,7 +104,7 @@ bool Shape::ComputePointAtLength(float length, Point& point, Vec2& tangent) cons float Shape::ComputeArea() const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (geometry) { float area = 0.f; @@ -116,7 +120,7 @@ float Shape::ComputeArea() const bool Shape::ContainsPoint(const Point& point, const Matrix3x2* transform) const { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativeObject::Get(this); + auto geometry = ComPolicy::Get(this); if (!geometry) return false; diff --git a/src/kiwano/render/Shape.h b/src/kiwano/render/Shape.h index ce9f58fc..652ad572 100644 --- a/src/kiwano/render/Shape.h +++ b/src/kiwano/render/Shape.h @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include +#include namespace kiwano { diff --git a/src/kiwano/render/ShapeMaker.cpp b/src/kiwano/render/ShapeMaker.cpp index 48299ee4..d077188a 100644 --- a/src/kiwano/render/ShapeMaker.cpp +++ b/src/kiwano/render/ShapeMaker.cpp @@ -21,6 +21,10 @@ #include #include +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include +#endif + namespace kiwano { @@ -34,7 +38,7 @@ ShapeMaker::~ShapeMaker() void ShapeMaker::Clear() { CloseStream(); - ResetNativePointer(); + ResetNative(); } ShapePtr ShapeMaker::GetShape() @@ -55,7 +59,7 @@ void ShapeMaker::BeginPath(const Point& begin_pos) } #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->BeginFigure(DX::ConvertToPoint2F(begin_pos), D2D1_FIGURE_BEGIN_FILLED); #else // not supported @@ -67,7 +71,7 @@ void ShapeMaker::EndPath(bool closed) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->EndFigure(closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); #else // not supported @@ -81,7 +85,7 @@ void ShapeMaker::AddLine(const Point& point) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->AddLine(DX::ConvertToPoint2F(point)); #else // not supported @@ -93,7 +97,7 @@ void ShapeMaker::AddLines(const Vector& points) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->AddLines(reinterpret_cast(&points[0]), static_cast(points.size())); #else // not supported @@ -105,7 +109,7 @@ void kiwano::ShapeMaker::AddLines(const Point* points, size_t count) KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->AddLines(reinterpret_cast(points), UINT32(count)); #else // not supported @@ -117,7 +121,7 @@ void ShapeMaker::AddBezier(const Point& point1, const Point& point2, const Point KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->AddBezier( D2D1::BezierSegment(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), DX::ConvertToPoint2F(point3))); #else @@ -130,7 +134,7 @@ void ShapeMaker::AddArc(const Point& point, const Size& radius, float rotation, KGE_ASSERT(IsStreamOpened()); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); native->AddArc(D2D1::ArcSegment(DX::ConvertToPoint2F(point), DX::ConvertToSizeF(radius), rotation, clockwise ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, is_small ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE)); @@ -147,9 +151,9 @@ ShapePtr ShapeMaker::Combine(ShapePtr shape_a, ShapePtr shape_b, CombineMode mod #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (shape_a && shape_b) { - auto geo_a = NativeObject::Get(shape_a); - auto geo_b = NativeObject::Get(shape_b); - auto native = NativeObject::Get(maker); + auto geo_a = ComPolicy::Get(shape_a); + auto geo_b = ComPolicy::Get(shape_b); + auto native = ComPolicy::Get(maker); HRESULT hr = geo_a->CombineWithGeometry(geo_b.Get(), D2D1_COMBINE_MODE(mode), DX::ConvertToMatrix3x2F(matrix), native.Get()); @@ -172,7 +176,7 @@ void ShapeMaker::OpenStream() Renderer::GetInstance().CreateShapeSink(*this); #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto geometry = NativeObject::Get(shape_); + auto geometry = ComPolicy::Get(shape_); if (geometry) { ComPtr native; @@ -180,7 +184,7 @@ void ShapeMaker::OpenStream() HRESULT hr = geometry->Open(&native); if (SUCCEEDED(hr)) { - NativeObject::Set(this, native); + ComPolicy::Set(this, native); } KGE_THROW_IF_FAILED(hr, "ID2D1PathGeometry::Open failed"); } @@ -195,12 +199,12 @@ void ShapeMaker::CloseStream() return; #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); HRESULT hr = native->Close(); KGE_THROW_IF_FAILED(hr, "ID2D1PathGeometry::Close failed"); - ResetNativePointer(); + ResetNative(); #else return; // not supported #endif diff --git a/src/kiwano/render/StrokeStyle.cpp b/src/kiwano/render/StrokeStyle.cpp index 3b8625a0..c8967390 100644 --- a/src/kiwano/render/StrokeStyle.cpp +++ b/src/kiwano/render/StrokeStyle.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 diff --git a/src/kiwano/render/StrokeStyle.h b/src/kiwano/render/StrokeStyle.h index f5fb8800..3ca46be5 100644 --- a/src/kiwano/render/StrokeStyle.h +++ b/src/kiwano/render/StrokeStyle.h @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include +#include namespace kiwano { diff --git a/src/kiwano/render/TextLayout.cpp b/src/kiwano/render/TextLayout.cpp index da600a64..e192144e 100644 --- a/src/kiwano/render/TextLayout.cpp +++ b/src/kiwano/render/TextLayout.cpp @@ -21,6 +21,10 @@ #include #include +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include +#endif + namespace kiwano { @@ -37,8 +41,13 @@ TextLayout::TextLayout(const String& content, const TextStyle& style) Reset(content, style); } -void TextLayout::Reset(const String& content, const TextStyle& style) +void TextLayout::Clear() { + ResetNative(); +} + +void TextLayout::Reset(const String& content, const TextStyle& style) + { content_length_ = (uint32_t)content.length(); if (content_length_) { @@ -80,7 +89,7 @@ void TextLayout::SetFont(FontPtr font) return; #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get((const NativeObject*)(this)); KGE_ASSERT(native); if (native) @@ -89,7 +98,7 @@ void TextLayout::SetFont(FontPtr font) // reset font collection { - auto collection = NativeObject::Get(font); + auto collection = ComPolicy::Get(font); hr = native->SetFontCollection(collection.Get(), { 0, content_length_ }); KGE_THROW_IF_FAILED(hr, "IDWriteTextLayout::SetFontCollection failed"); @@ -144,7 +153,7 @@ void TextLayout::SetFont(FontPtr font) void TextLayout::SetUnderline(bool enable) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) @@ -162,7 +171,7 @@ void TextLayout::SetUnderline(bool enable) void TextLayout::SetStrikethrough(bool enable) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) @@ -180,7 +189,7 @@ void TextLayout::SetStrikethrough(bool enable) void TextLayout::SetAlignment(TextAlign align) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) @@ -215,7 +224,7 @@ void TextLayout::SetAlignment(TextAlign align) void TextLayout::SetWrapWidth(float wrap_width) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) @@ -245,7 +254,7 @@ void TextLayout::SetWrapWidth(float wrap_width) void TextLayout::SetLineSpacing(float line_spacing) { #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); KGE_ASSERT(native); if (native) @@ -280,7 +289,7 @@ bool TextLayout::UpdateIfDirty() line_count_ = 0; size_ = Size(); - auto native = NativeObject::Get(this); + auto native = ComPolicy::Get(this); if (content_length_ == 0 || !native) return true; diff --git a/src/kiwano/render/TextLayout.h b/src/kiwano/render/TextLayout.h index 6df086db..c257649e 100644 --- a/src/kiwano/render/TextLayout.h +++ b/src/kiwano/render/TextLayout.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include namespace kiwano @@ -135,11 +135,6 @@ inline bool TextLayout::IsDirty() const return dirty_flag_ != DirtyFlag::Clean; } -inline void TextLayout::Clear() -{ - ResetNativePointer(); -} - inline uint32_t TextLayout::GetContentLength() const { return content_length_; diff --git a/src/kiwano/render/Texture.cpp b/src/kiwano/render/Texture.cpp index e82f9797..b6b5dbce 100644 --- a/src/kiwano/render/Texture.cpp +++ b/src/kiwano/render/Texture.cpp @@ -23,6 +23,10 @@ #include #include // std::hash +#if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX +#include +#endif + namespace kiwano { @@ -104,8 +108,8 @@ void Texture::CopyFrom(TexturePtr copy_from) #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (IsValid() && copy_from) { - auto native = NativeObject::Get(this); - auto native_to_copy = NativeObject::Get(copy_from); + auto native = ComPolicy::Get(this); + auto native_to_copy = ComPolicy::Get(copy_from); HRESULT hr = native->CopyFromBitmap(nullptr, native_to_copy.Get(), nullptr); @@ -121,8 +125,8 @@ void Texture::CopyFrom(TexturePtr copy_from, const Rect& src_rect, const Point& #if KGE_RENDER_ENGINE == KGE_RENDER_ENGINE_DIRECTX if (IsValid() && copy_from) { - auto native = NativeObject::Get(this); - auto native_to_copy = NativeObject::Get(copy_from); + auto native = ComPolicy::Get(this); + auto native_to_copy = ComPolicy::Get(copy_from); HRESULT hr = native->CopyFromBitmap(&D2D1::Point2U(uint32_t(dest_point.x), uint32_t(dest_point.y)), native_to_copy.Get(), diff --git a/src/kiwano/render/Texture.h b/src/kiwano/render/Texture.h index 3a9a73be..9c24ee96 100644 --- a/src/kiwano/render/Texture.h +++ b/src/kiwano/render/Texture.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include namespace kiwano { From c061c8c802cbbc356925f45cc0ab00c86e4fb6f1 Mon Sep 17 00:00:00 2001 From: Nomango Date: Sun, 24 Sep 2023 22:17:04 +0800 Subject: [PATCH 09/14] pref: add sound transcoder cache --- src/kiwano-audio/AudioModule.cpp | 49 +++++++-- src/kiwano-audio/AudioModule.h | 12 ++- src/kiwano-audio/Sound.cpp | 166 ++++++++++++++++++------------- src/kiwano-audio/Sound.h | 38 +++---- src/kiwano-audio/Transcoder.h | 59 ++++++++++- 5 files changed, 219 insertions(+), 105 deletions(-) diff --git a/src/kiwano-audio/AudioModule.cpp b/src/kiwano-audio/AudioModule.cpp index 4717c6b0..12b80712 100644 --- a/src/kiwano-audio/AudioModule.cpp +++ b/src/kiwano-audio/AudioModule.cpp @@ -22,11 +22,13 @@ #include #include #include +#include namespace kiwano { namespace audio { + AudioModule::AudioModule() : x_audio2_(nullptr) , mastering_voice_(nullptr) @@ -58,6 +60,8 @@ void AudioModule::DestroyModule() { KGE_DEBUG_LOGF("Destroying audio resources"); + TranscoderCache::GetInstance().Clear(); + if (mastering_voice_) { mastering_voice_->DestroyVoice(); @@ -73,12 +77,45 @@ void AudioModule::DestroyModule() dlls::MediaFoundation::Get().MFShutdown(); } -bool AudioModule::CreateSound(Sound& sound, const Transcoder::Buffer& buffer) +TranscoderPtr AudioModule::CreateTranscoder(const String& file_path) +{ + if (!FileSystem::GetInstance().IsFileExists(file_path)) + { + KGE_WARNF("Media file '%s' not found", file_path.c_str()); + return nullptr; + } + + String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); + + auto ptr = MakePtr(); + HRESULT hr = ptr->LoadMediaFile(full_path); + if (FAILED(hr)) + { + KGE_ERRORF("Load media file failed with HRESULT of %08X", hr); + return nullptr; + } + return ptr; +} + +TranscoderPtr AudioModule::CreateTranscoder(const Resource& res) +{ + auto ptr = MakePtr(); + HRESULT hr = ptr->LoadMediaResource(res); + if (FAILED(hr)) + { + KGE_ERRORF("Load media resource failed with HRESULT of %08X", hr); + return nullptr; + } + return ptr; +} + +bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder) { KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!"); HRESULT hr = S_OK; + auto buffer = transcoder->GetBuffer(); if (buffer.format == nullptr) hr = E_INVALIDARG; @@ -90,14 +127,8 @@ bool AudioModule::CreateSound(Sound& sound, const Transcoder::Buffer& buffer) if (SUCCEEDED(hr)) { - IXAudio2SourceVoice* old = sound.GetXAudio2Voice(); - if (old) - { - old->DestroyVoice(); - old = nullptr; - } - - sound.SetXAudio2Voice(voice); + sound.Close(); + sound.SetNative(voice); } } diff --git a/src/kiwano-audio/AudioModule.h b/src/kiwano-audio/AudioModule.h index 0e51eb72..05045137 100644 --- a/src/kiwano-audio/AudioModule.h +++ b/src/kiwano-audio/AudioModule.h @@ -60,8 +60,16 @@ public: void Close(); /// \~chinese - /// @brief 从解码器数据缓冲中创建音频对象 - bool CreateSound(Sound& sound, const Transcoder::Buffer& buffer); + /// @brief 创建音频解码器 + TranscoderPtr CreateTranscoder(const String& file_path); + + /// \~chinese + /// @brief 创建音频解码器 + TranscoderPtr CreateTranscoder(const Resource& res); + + /// \~chinese + /// @brief 创建音频 + bool CreateSound(Sound& sound, TranscoderPtr transcoder); public: void SetupModule() override; diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index e340c9f3..1ba3b031 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -21,12 +21,50 @@ #include #include #include -#include namespace kiwano { namespace audio { + +SoundPtr Sound::Preload(const String& file_path) +{ + auto ptr = MakePtr(); + + size_t hash_code = std::hash{}(file_path); + if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code)) + { + if (ptr->Load(transcoder)) + return ptr; + return nullptr; + } + + if (ptr && ptr->Load(file_path)) + { + TranscoderCache::GetInstance().Add(hash_code, ptr->coder_); + } + return ptr; +} + +SoundPtr Sound::Preload(const Resource& res) +{ + auto ptr = MakePtr(); + + size_t hash_code = res.GetId(); + if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code)) + { + if (ptr->Load(transcoder)) + return ptr; + return nullptr; + } + + if (ptr && ptr->Load(res)) + { + TranscoderCache::GetInstance().Add(hash_code, ptr->coder_); + } + return ptr; +} + Sound::Sound(const String& file_path) : Sound() { @@ -42,7 +80,6 @@ Sound::Sound(const Resource& res) Sound::Sound() : opened_(false) , playing_(false) - , voice_(nullptr) { } @@ -53,34 +90,17 @@ Sound::~Sound() bool Sound::Load(const String& file_path) { - if (!FileSystem::GetInstance().IsFileExists(file_path)) - { - KGE_WARNF("Media file '%s' not found", file_path.c_str()); - return false; - } - if (opened_) { Close(); } - String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path); - - HRESULT hr = transcoder_.LoadMediaFile(full_path); - if (FAILED(hr)) + TranscoderPtr transcoder = AudioModule::GetInstance().CreateTranscoder(file_path); + if (!transcoder) { - KGE_ERRORF("Load media file failed with HRESULT of %08X", hr); return false; } - - if (!AudioModule::GetInstance().CreateSound(*this, transcoder_.GetBuffer())) - { - Close(); - return false; - } - - opened_ = true; - return true; + return Load(transcoder); } bool Sound::Load(const Resource& res) @@ -90,26 +110,28 @@ bool Sound::Load(const Resource& res) Close(); } - HRESULT hr = transcoder_.LoadMediaResource(res); - if (FAILED(hr)) + TranscoderPtr transcoder = AudioModule::GetInstance().CreateTranscoder(res); + if (!transcoder) { - KGE_ERRORF("Load media resource failed with HRESULT of %08X", hr); return false; } - - if (!AudioModule::GetInstance().CreateSound(*this, transcoder_.GetBuffer())) - { - Close(); - return false; - } - - opened_ = true; - return true; + return Load(transcoder); } -bool Sound::IsValid() const +bool Sound::Load(TranscoderPtr transcoder) { - return voice_ != nullptr; + if (opened_) + { + Close(); + } + if (!AudioModule::GetInstance().CreateSound(*this, transcoder)) + { + return false; + } + + coder_ = transcoder; + opened_ = true; + return true; } void Sound::Play(int loop_count) @@ -120,29 +142,30 @@ void Sound::Play(int loop_count) return; } - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); // if sound stream is not empty, stop() will clear it XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); + voice->GetState(&state); if (state.BuffersQueued) Stop(); // clamp loop count loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); - auto wave_buffer = transcoder_.GetBuffer(); + auto buffer = coder_->GetBuffer(); - XAUDIO2_BUFFER buffer = { 0 }; - buffer.pAudioData = wave_buffer.data; - buffer.Flags = XAUDIO2_END_OF_STREAM; - buffer.AudioBytes = wave_buffer.size; - buffer.LoopCount = static_cast(loop_count); + XAUDIO2_BUFFER xaudio2_buffer = { 0 }; + xaudio2_buffer.pAudioData = buffer.data; + xaudio2_buffer.Flags = XAUDIO2_END_OF_STREAM; + xaudio2_buffer.AudioBytes = buffer.size; + xaudio2_buffer.LoopCount = static_cast(loop_count); - HRESULT hr = voice_->SubmitSourceBuffer(&buffer); + HRESULT hr = voice->SubmitSourceBuffer(&xaudio2_buffer); if (SUCCEEDED(hr)) { - hr = voice_->Start(); + hr = voice->Start(); } if (FAILED(hr)) @@ -155,31 +178,34 @@ void Sound::Play(int loop_count) void Sound::Pause() { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - if (SUCCEEDED(voice_->Stop())) + if (SUCCEEDED(voice->Stop())) playing_ = false; } void Sound::Resume() { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - if (SUCCEEDED(voice_->Start())) + if (SUCCEEDED(voice->Start())) playing_ = true; } void Sound::Stop() { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - HRESULT hr = voice_->Stop(); + HRESULT hr = voice->Stop(); if (SUCCEEDED(hr)) - hr = voice_->ExitLoop(); + hr = voice->ExitLoop(); if (SUCCEEDED(hr)) - hr = voice_->FlushSourceBuffers(); + hr = voice->FlushSourceBuffers(); if (SUCCEEDED(hr)) playing_ = false; @@ -187,16 +213,15 @@ void Sound::Stop() void Sound::Close() { - if (voice_) + auto voice = GetNativePtr(); + if (voice) { - voice_->Stop(); - voice_->FlushSourceBuffers(); - voice_->DestroyVoice(); - voice_ = nullptr; + voice->Stop(); + voice->FlushSourceBuffers(); + voice->DestroyVoice(); } - transcoder_.ClearBuffer(); - + coder_ = nullptr; opened_ = false; playing_ = false; } @@ -205,14 +230,15 @@ bool Sound::IsPlaying() const { if (opened_) { - if (!voice_) - return false; - if (!playing_) return false; + auto voice = GetNativePtr(); + if (!voice) + return false; + XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); + voice->GetState(&state); return !!state.BuffersQueued; } return false; @@ -220,19 +246,21 @@ bool Sound::IsPlaying() const float Sound::GetVolume() const { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); float volume = 0.0f; - voice_->GetVolume(&volume); + voice->GetVolume(&volume); return volume; } void Sound::SetVolume(float volume) { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + auto voice = GetNativePtr(); + KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); volume = std::min(std::max(volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL); - voice_->SetVolume(volume); + voice->SetVolume(volume); } } // namespace audio } // namespace kiwano diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index 657cfa0c..2ed34ca6 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -21,7 +21,7 @@ #pragma once #include #include -#include +#include #include namespace kiwano @@ -41,11 +41,19 @@ KGE_DECLARE_SMART_PTR(Sound); * \~chinese * @brief 音频对象 */ -class KGE_API Sound : public ObjectBase +class KGE_API Sound : public NativeObject { friend class AudioModule; public: + /// \~chinese + /// @brief 预加载音频 + static SoundPtr Preload(const String& file_path); + + /// \~chinese + /// @brief 预加载音频资源 + static SoundPtr Preload(const Resource& res); + /// \~chinese /// @brief 创建音频对象 /// @param res 本地音频文件路径 @@ -71,8 +79,9 @@ public: bool Load(const Resource& res); /// \~chinese - /// @brief 是否有效 - bool IsValid() const; + /// @brief 打开音频资源 + /// @param res 音频资源 + bool Load(TranscoderPtr transcoder); /// \~chinese /// @brief 播放 @@ -109,27 +118,12 @@ public: void SetVolume(float volume); private: - IXAudio2SourceVoice* GetXAudio2Voice() const; - - void SetXAudio2Voice(IXAudio2SourceVoice* voice); - -private: - bool opened_; - bool playing_; - Transcoder transcoder_; - IXAudio2SourceVoice* voice_; + bool opened_; + bool playing_; + TranscoderPtr coder_; }; /** @} */ -inline IXAudio2SourceVoice* Sound::GetXAudio2Voice() const -{ - return voice_; -} - -inline void Sound::SetXAudio2Voice(IXAudio2SourceVoice* voice) -{ - voice_ = voice; -} } // namespace audio } // namespace kiwano diff --git a/src/kiwano-audio/Transcoder.h b/src/kiwano-audio/Transcoder.h index bd49b3ba..6bc6bc2c 100644 --- a/src/kiwano-audio/Transcoder.h +++ b/src/kiwano-audio/Transcoder.h @@ -20,6 +20,7 @@ #pragma once #include +#include #include #include #include @@ -28,7 +29,9 @@ namespace kiwano { namespace audio { -class Sound; +class AudioModule; + +KGE_DECLARE_SMART_PTR(Transcoder); /** * \addtogroup Audio @@ -39,9 +42,9 @@ class Sound; * \~chinese * @brief 音频解码器 */ -class KGE_API Transcoder +class KGE_API Transcoder : public ObjectBase { - friend class Sound; + friend class AudioModule; public: /** @@ -86,6 +89,56 @@ private: WAVEFORMATEX* wave_format_; }; + +class KGE_API TranscoderCache final : public Singleton +{ + friend Singleton; + +public: + /// \~chinese + /// @brief 添加缓存 + inline void Add(size_t key, TranscoderPtr v) + { + cache_.insert(std::make_pair(key, v)); + } + + /// \~chinese + /// @brief 获取缓存 + inline TranscoderPtr Get(size_t key) const + { + if (cache_.count(key)) + { + return cache_.at(key); + } + return nullptr; + } + + /// \~chinese + /// @brief 移除缓存 + inline void Remove(size_t key) + { + cache_.erase(key); + } + + /// \~chinese + /// @brief 清空缓存 + inline void Clear() + { + cache_.clear(); + } + + ~TranscoderCache() + { + Clear(); + } + +private: + TranscoderCache() = default; + +private: + UnorderedMap cache_; +}; + /** @} */ } // namespace audio } // namespace kiwano From 88c758b47c662525278093e37896309f7826c574 Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 00:15:18 +0800 Subject: [PATCH 10/14] feat: add sound callback --- src/kiwano-audio/AudioModule.cpp | 45 ++++++- src/kiwano-audio/Sound.cpp | 203 +++++++++++++++++++++++++++++-- src/kiwano-audio/Sound.h | 116 +++++++++++++++--- 3 files changed, 330 insertions(+), 34 deletions(-) diff --git a/src/kiwano-audio/AudioModule.cpp b/src/kiwano-audio/AudioModule.cpp index 12b80712..b1a08a5d 100644 --- a/src/kiwano-audio/AudioModule.cpp +++ b/src/kiwano-audio/AudioModule.cpp @@ -29,6 +29,42 @@ namespace kiwano namespace audio { +class VoiceCallback : public IXAudio2VoiceCallback +{ +public: + SoundCallback* cb; + + VoiceCallback(SoundCallback* cb) + : cb(cb) + { + } + + ~VoiceCallback() {} + + STDMETHOD_(void, OnBufferStart(void* pBufferContext)) + { + cb->OnStart(nullptr); + } + + STDMETHOD_(void, OnLoopEnd(void* pBufferContext)) + { + cb->OnLoopEnd(nullptr); + } + + STDMETHOD_(void, OnBufferEnd(void* pBufferContext)) + { + cb->OnEnd(nullptr); + } + + STDMETHOD_(void, OnStreamEnd()) {} + + STDMETHOD_(void, OnVoiceProcessingPassEnd()) {} + + STDMETHOD_(void, OnVoiceProcessingPassStart(UINT32 SamplesRequired)) {} + + STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) {} +}; + AudioModule::AudioModule() : x_audio2_(nullptr) , mastering_voice_(nullptr) @@ -121,10 +157,12 @@ bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder) if (SUCCEEDED(hr)) { + auto chain = sound.GetCallbackChain(); + chain->SetNative(VoiceCallback{ chain.Get() }); + auto callback = const_cast(chain->GetNative().CastPtr()); + IXAudio2SourceVoice* voice = nullptr; - - hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO); - + hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback); if (SUCCEEDED(hr)) { sound.Close(); @@ -143,7 +181,6 @@ bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder) void AudioModule::Open() { KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!"); - if (x_audio2_) x_audio2_->StartEngine(); } diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index 1ba3b031..c1ca9d97 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -27,9 +27,13 @@ namespace kiwano namespace audio { -SoundPtr Sound::Preload(const String& file_path) +SoundPtr Sound::Preload(const String& file_path, std::initializer_list callbacks) { auto ptr = MakePtr(); + for (auto& cb : callbacks) + { + ptr->AddCallback(cb); + } size_t hash_code = std::hash{}(file_path); if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code)) @@ -46,9 +50,13 @@ SoundPtr Sound::Preload(const String& file_path) return ptr; } -SoundPtr Sound::Preload(const Resource& res) +SoundPtr Sound::Preload(const Resource& res, std::initializer_list callbacks) { auto ptr = MakePtr(); + for (auto& cb : callbacks) + { + ptr->AddCallback(cb); + } size_t hash_code = res.GetId(); if (TranscoderPtr transcoder = TranscoderCache::GetInstance().Get(hash_code)) @@ -80,6 +88,7 @@ Sound::Sound(const Resource& res) Sound::Sound() : opened_(false) , playing_(false) + , volume_(1.f) { } @@ -129,6 +138,11 @@ bool Sound::Load(TranscoderPtr transcoder) return false; } + // reset volume + const float old_volume = volume_; + volume_ = 0.f; + SetVolume(old_volume); + coder_ = transcoder; opened_ = true; return true; @@ -181,8 +195,14 @@ void Sound::Pause() auto voice = GetNativePtr(); KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - if (SUCCEEDED(voice->Stop())) + HRESULT hr = voice->Stop(); + if (SUCCEEDED(hr)) playing_ = false; + + if (FAILED(hr)) + { + KGE_ERRORF("Pause voice failed with HRESULT of %08X", hr); + } } void Sound::Resume() @@ -190,8 +210,14 @@ void Sound::Resume() auto voice = GetNativePtr(); KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - if (SUCCEEDED(voice->Start())) + HRESULT hr = voice->Start(); + if (SUCCEEDED(hr)) playing_ = true; + + if (FAILED(hr)) + { + KGE_ERRORF("Start voice failed with HRESULT of %08X", hr); + } } void Sound::Stop() @@ -209,6 +235,11 @@ void Sound::Stop() if (SUCCEEDED(hr)) playing_ = false; + + if (FAILED(hr)) + { + KGE_ERRORF("Stop voice failed with HRESULT of %08X", hr); + } } void Sound::Close() @@ -246,21 +277,169 @@ bool Sound::IsPlaying() const float Sound::GetVolume() const { - auto voice = GetNativePtr(); - KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - - float volume = 0.0f; - voice->GetVolume(&volume); - return volume; + return volume_; } void Sound::SetVolume(float volume) { + if (volume_ == volume) + { + return; + } + volume_ = volume; + + float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_); + auto voice = GetNativePtr(); KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL"); - volume = std::min(std::max(volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL); - voice->SetVolume(volume); + actual_volume = std::min(std::max(actual_volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL); + voice->SetVolume(actual_volume); } + +SoundCallbackPtr Sound::GetCallbackChain() +{ + class SoundCallbackChain : public SoundCallback + { + public: + Sound* sound; + + void OnStart(Sound*) override + { + for (auto& cb : sound->GetCallbacks()) + { + if (cb) + { + cb->OnStart(sound); + } + } + } + + void OnLoopEnd(Sound*) override + { + for (auto& cb : sound->GetCallbacks()) + { + if (cb) + { + cb->OnLoopEnd(sound); + } + } + } + + void OnEnd(Sound*) override + { + for (auto& cb : sound->GetCallbacks()) + { + if (cb) + { + cb->OnEnd(sound); + } + } + } + + float OnVolumeChanged(Sound*, float volume) override + { + float actual_volume = volume; + for (auto& cb : sound->GetCallbacks()) + { + if (cb) + { + actual_volume = cb->OnVolumeChanged(sound, volume); + } + } + return actual_volume; + } + }; + + if (!callback_chain_) + { + auto chain = MakePtr(); + chain->sound = this; + callback_chain_ = chain; + } + return callback_chain_; +} + +SoundCallbackPtr SoundCallback::OnStart(const Function& cb) +{ + class SoundCallbackFunc : public SoundCallback + { + public: + Function cb; + + void OnStart(Sound* sound) override + { + if (cb) + { + cb(sound); + } + } + }; + auto ptr = MakePtr(); + ptr->cb = cb; + return ptr; +} + +SoundCallbackPtr SoundCallback::OnLoopEnd(const Function& cb) +{ + class SoundCallbackFunc : public SoundCallback + { + public: + Function cb; + + void OnLoopEnd(Sound* sound) override + { + if (cb) + { + cb(sound); + } + } + }; + auto ptr = MakePtr(); + ptr->cb = cb; + return ptr; +} + +SoundCallbackPtr SoundCallback::OnEnd(const Function& cb) +{ + class SoundCallbackFunc : public SoundCallback + { + public: + Function cb; + + void OnEnd(Sound* sound) override + { + if (cb) + { + cb(sound); + } + } + }; + auto ptr = MakePtr(); + ptr->cb = cb; + return ptr; +} + +SoundCallbackPtr SoundCallback::OnVolumeChanged(const Function& cb) +{ + class SoundCallbackFunc : public SoundCallback + { + public: + Function cb; + + float OnVolumeChanged(Sound* sound, float volume) override + { + if (cb) + { + return cb(sound, volume); + } + return volume; + } + }; + auto ptr = MakePtr(); + ptr->cb = cb; + return ptr; +} + } // namespace audio } // namespace kiwano diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index 2ed34ca6..9304434d 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -29,8 +29,10 @@ namespace kiwano namespace audio { class AudioModule; +class SoundPlayer; KGE_DECLARE_SMART_PTR(Sound); +KGE_DECLARE_SMART_PTR(SoundCallback); /** * \addtogroup Audio @@ -39,20 +41,64 @@ KGE_DECLARE_SMART_PTR(Sound); /** * \~chinese - * @brief 音频对象 + * @brief 音频回调 + */ +class KGE_API SoundCallback : public NativeObject +{ +public: + /// \~chinese + /// @brief 创建一个回调,在音频开始播放时执行 + static SoundCallbackPtr OnStart(const Function& cb); + + /// \~chinese + /// @brief 创建一个回调,在音频循环结束时执行 + static SoundCallbackPtr OnLoopEnd(const Function& cb); + + /// \~chinese + /// @brief 创建一个回调,在音频结束时执行 + static SoundCallbackPtr OnEnd(const Function& cb); + + /// \~chinese + /// @brief 创建一个回调,在音频修改音量时执行 + static SoundCallbackPtr OnVolumeChanged(const Function& cb); + + /// \~chinese + /// @brief 在音频开始播放时执行 + virtual inline void OnStart(Sound* sound) {} + + /// \~chinese + /// @brief 在音频循环结束时执行 + virtual inline void OnLoopEnd(Sound* sound) {} + + /// \~chinese + /// @brief 在音频结束时执行 + virtual inline void OnEnd(Sound* sound) {} + + /// \~chinese + /// @brief 在音频修改音量时执行 + virtual inline float OnVolumeChanged(Sound* sound, float volume) + { + return volume; + } +}; + +/** + * \~chinese + * @brief 音频 */ class KGE_API Sound : public NativeObject { friend class AudioModule; + friend class SoundPlayer; public: /// \~chinese /// @brief 预加载音频 - static SoundPtr Preload(const String& file_path); + static SoundPtr Preload(const String& file_path, std::initializer_list callbacks = {}); /// \~chinese /// @brief 预加载音频资源 - static SoundPtr Preload(const Resource& res); + static SoundPtr Preload(const Resource& res, std::initializer_list callbacks = {}); /// \~chinese /// @brief 创建音频对象 @@ -68,21 +114,6 @@ public: virtual ~Sound(); - /// \~chinese - /// @brief 打开本地音频文件 - /// @param res 本地音频文件路径 - bool Load(const String& file_path); - - /// \~chinese - /// @brief 打开音频资源 - /// @param res 音频资源 - bool Load(const Resource& res); - - /// \~chinese - /// @brief 打开音频资源 - /// @param res 音频资源 - bool Load(TranscoderPtr transcoder); - /// \~chinese /// @brief 播放 /// @param loop_count 播放循环次数,设置 -1 为循环播放 @@ -117,13 +148,62 @@ public: /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 void SetVolume(float volume); + /// \~chinese + /// @brief 添加回调 + void AddCallback(SoundCallbackPtr callback); + + /// \~chinese + /// @brief 获取所有回调 + List& GetCallbacks(); + + /// \~chinese + /// @brief 获取所有回调 + const List& GetCallbacks() const; + +protected: + /// \~chinese + /// @brief 打开本地音频文件 + /// @param res 本地音频文件路径 + bool Load(const String& file_path); + + /// \~chinese + /// @brief 打开音频资源 + /// @param res 音频资源 + bool Load(const Resource& res); + + /// \~chinese + /// @brief 打开音频资源 + /// @param res 音频资源 + bool Load(TranscoderPtr transcoder); + + SoundCallbackPtr GetCallbackChain(); + private: bool opened_; bool playing_; + float volume_; TranscoderPtr coder_; + + SoundCallbackPtr callback_chain_; + List callbacks_; }; /** @} */ +inline List& kiwano::audio::Sound::GetCallbacks() +{ + return callbacks_; +} + +inline const List& kiwano::audio::Sound::GetCallbacks() const +{ + return callbacks_; +} + +inline void kiwano::audio::Sound::AddCallback(SoundCallbackPtr callback) +{ + callbacks_.push_back(callback); +} + } // namespace audio } // namespace kiwano From 1e650283213e68879108bc07370ba9eedcd7fba8 Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 00:57:02 +0800 Subject: [PATCH 11/14] chore: fix typo --- src/kiwano-network/HttpModule.cpp | 2 +- src/kiwano/platform/Application.cpp | 2 +- src/kiwano/platform/Application.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/kiwano-network/HttpModule.cpp b/src/kiwano-network/HttpModule.cpp index 83f16e69..29a8c0cc 100644 --- a/src/kiwano-network/HttpModule.cpp +++ b/src/kiwano-network/HttpModule.cpp @@ -260,7 +260,7 @@ void HttpModule::NetworkThread() response_queue_.push(response); response_mutex_.unlock(); - Application::GetInstance().PreformInMainThread(Closure(this, &HttpModule::DispatchResponseCallback)); + Application::GetInstance().PerformInMainThread(Closure(this, &HttpModule::DispatchResponseCallback)); } } diff --git a/src/kiwano/platform/Application.cpp b/src/kiwano/platform/Application.cpp index fe666dbc..c85f0c07 100644 --- a/src/kiwano/platform/Application.cpp +++ b/src/kiwano/platform/Application.cpp @@ -235,7 +235,7 @@ void Application::Render() renderer.Present(); } -void Application::PreformInMainThread(Function func) +void Application::PerformInMainThread(Function func) { std::lock_guard lock(perform_mutex_); functions_to_perform_.push(func); diff --git a/src/kiwano/platform/Application.h b/src/kiwano/platform/Application.h index 40d969cf..346c7cde 100644 --- a/src/kiwano/platform/Application.h +++ b/src/kiwano/platform/Application.h @@ -143,7 +143,7 @@ public: * @details 提供在其他线程调用 Kiwano 函数的能力 * @param func 需要执行的函数 */ - void PreformInMainThread(Function func); + void PerformInMainThread(Function func); /** * \~chinese From 7d969eddd83fb21e7ce54544f56850ea087aa7f9 Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 00:59:27 +0800 Subject: [PATCH 12/14] feat: update sound player --- src/kiwano-audio/AudioModule.cpp | 5 +- src/kiwano-audio/Sound.cpp | 32 ++++++- src/kiwano-audio/Sound.h | 2 + src/kiwano-audio/SoundPlayer.cpp | 153 +++++++++++++------------------ src/kiwano-audio/SoundPlayer.h | 107 ++++++++------------- 5 files changed, 138 insertions(+), 161 deletions(-) diff --git a/src/kiwano-audio/AudioModule.cpp b/src/kiwano-audio/AudioModule.cpp index b1a08a5d..cfc3c961 100644 --- a/src/kiwano-audio/AudioModule.cpp +++ b/src/kiwano-audio/AudioModule.cpp @@ -62,7 +62,10 @@ public: STDMETHOD_(void, OnVoiceProcessingPassStart(UINT32 SamplesRequired)) {} - STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) {} + STDMETHOD_(void, OnVoiceError(void* pBufferContext, HRESULT Error)) + { + KGE_ERRORF("Voice error with HRESULT of %08X", Error); + } }; AudioModule::AudioModule() diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp index c1ca9d97..ba9f2275 100644 --- a/src/kiwano-audio/Sound.cpp +++ b/src/kiwano-audio/Sound.cpp @@ -139,9 +139,7 @@ bool Sound::Load(TranscoderPtr transcoder) } // reset volume - const float old_volume = volume_; - volume_ = 0.f; - SetVolume(old_volume); + ResetVolume(); coder_ = transcoder; opened_ = true; @@ -297,6 +295,14 @@ void Sound::SetVolume(float volume) voice->SetVolume(actual_volume); } +void Sound::ResetVolume() +{ + const float old_volume = volume_; + + volume_ += 1.f; + SetVolume(old_volume); +} + SoundCallbackPtr Sound::GetCallbackChain() { class SoundCallbackChain : public SoundCallback @@ -313,6 +319,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnStart(sound); } } + RemoveUsedCallbacks(); } void OnLoopEnd(Sound*) override @@ -324,6 +331,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnLoopEnd(sound); } } + RemoveUsedCallbacks(); } void OnEnd(Sound*) override @@ -335,6 +343,7 @@ SoundCallbackPtr Sound::GetCallbackChain() cb->OnEnd(sound); } } + RemoveUsedCallbacks(); } float OnVolumeChanged(Sound*, float volume) override @@ -349,6 +358,23 @@ SoundCallbackPtr Sound::GetCallbackChain() } return actual_volume; } + + void RemoveUsedCallbacks() + { + auto& cbs = sound->GetCallbacks(); + auto iter = cbs.begin(); + while (iter != cbs.end()) + { + if (*iter == nullptr) + { + iter = cbs.erase(iter); + } + else + { + iter++; + } + } + } }; if (!callback_chain_) diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h index 9304434d..d8580d26 100644 --- a/src/kiwano-audio/Sound.h +++ b/src/kiwano-audio/Sound.h @@ -178,6 +178,8 @@ protected: SoundCallbackPtr GetCallbackChain(); + void ResetVolume(); + private: bool opened_; bool playing_; diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp index 3cd35d3d..b223744f 100644 --- a/src/kiwano-audio/SoundPlayer.cpp +++ b/src/kiwano-audio/SoundPlayer.cpp @@ -19,6 +19,7 @@ // THE SOFTWARE. #include +#include namespace kiwano { @@ -32,81 +33,31 @@ SoundPlayer::SoundPlayer() SoundPlayer::~SoundPlayer() { - ClearCache(); + StopAll(); } -size_t SoundPlayer::GetId(const String& file_path) const +void SoundPlayer::Play(SoundPtr sound, int loop_count) { - return std::hash()(file_path); -} - -size_t SoundPlayer::GetId(const Resource& res) const -{ - return static_cast(res.GetId()); -} - -size_t SoundPlayer::Load(const String& file_path) -{ - size_t id = GetId(file_path); - if (sound_cache_.end() != sound_cache_.find(id)) - return id; - - SoundPtr sound = MakePtr(); - if (sound && sound->Load(file_path)) + if (sound) { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(id, sound)); - return id; - } - return 0; -} - -size_t SoundPlayer::Load(const Resource& res) -{ - size_t id = GetId(res); - if (sound_cache_.end() != sound_cache_.find(id)) - return id; - - SoundPtr sound = MakePtr(); - - if (sound && sound->Load(res)) - { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(id, sound)); - return id; - } - return 0; -} - -void SoundPlayer::Play(size_t id, int loop_count) -{ - if (auto sound = GetSound(id)) + SetCallback(sound.Get()); sound->Play(loop_count); + sound_list_.push_back(sound); + } } -void SoundPlayer::Pause(size_t id) +SoundPtr SoundPlayer::Play(const String& file_path, int loop_count, std::initializer_list callbacks) { - if (auto sound = GetSound(id)) - sound->Pause(); + SoundPtr sound = Sound::Preload(file_path, callbacks); + Play(sound, loop_count); + return sound; } -void SoundPlayer::Resume(size_t id) +SoundPtr SoundPlayer::Play(const Resource& res, int loop_count, std::initializer_list callbacks) { - if (auto sound = GetSound(id)) - sound->Resume(); -} - -void SoundPlayer::Stop(size_t id) -{ - if (auto sound = GetSound(id)) - sound->Stop(); -} - -bool SoundPlayer::IsPlaying(size_t id) -{ - if (auto sound = GetSound(id)) - return sound->IsPlaying(); - return false; + SoundPtr sound = Sound::Preload(res, callbacks); + Play(sound, loop_count); + return sound; } float SoundPlayer::GetVolume() const @@ -116,53 +67,81 @@ float SoundPlayer::GetVolume() const void SoundPlayer::SetVolume(float volume) { - volume_ = std::min(std::max(volume, -224.f), 224.f); - for (auto& pair : sound_cache_) - { - pair.second->SetVolume(volume_); - } -} - -SoundPtr SoundPlayer::GetSound(size_t id) const -{ - auto iter = sound_cache_.find(id); - if (iter != sound_cache_.end()) - return iter->second; - return SoundPtr(); + volume_ = volume; } void SoundPlayer::PauseAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Pause(); + sound->Pause(); } } void SoundPlayer::ResumeAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Resume(); + sound->Resume(); } } void SoundPlayer::StopAll() { - for (auto& pair : sound_cache_) + for (auto& sound : sound_list_) { - pair.second->Stop(); + sound->Stop(); } } -void SoundPlayer::ReleaseSound(size_t id) +void SoundPlayer::OnEnd(Sound* sound) { - sound_cache_.erase(id); + // remove callback + RemoveCallback(sound); + + // remove sound after stopped + auto iter = std::find(sound_list_.begin(), sound_list_.end(), sound); + if (iter != sound_list_.end()) + { + trash_.push_back(*iter); + sound_list_.erase(iter); + } + + // clear trash in main thread + Application::GetInstance().PerformInMainThread(std::bind(&SoundPlayer::ClearTrash, this)); } -void SoundPlayer::ClearCache() +float SoundPlayer::OnVolumeChanged(Sound* sound, float volume) { - sound_cache_.clear(); + return volume * volume_; } + +void SoundPlayer::SetCallback(Sound* sound) +{ + // add callback if not exists + auto& cbs = sound->GetCallbacks(); + auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + if (iter == cbs.end()) + { + sound->AddCallback(this); + } + sound->ResetVolume(); +} + +void SoundPlayer::RemoveCallback(Sound* sound) +{ + auto& cbs = sound->GetCallbacks(); + auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + if (iter != cbs.end()) + { + *iter = nullptr; // will be removed by sound + } +} + +void SoundPlayer::ClearTrash() +{ + trash_.clear(); +} + } // namespace audio } // namespace kiwano diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h index 1f9acf79..66e53aa7 100644 --- a/src/kiwano-audio/SoundPlayer.h +++ b/src/kiwano-audio/SoundPlayer.h @@ -20,7 +20,6 @@ #pragma once #include -#include namespace kiwano { @@ -37,78 +36,34 @@ KGE_DECLARE_SMART_PTR(SoundPlayer); * \~chinese * @brief 音频播放器 */ -class KGE_API SoundPlayer : public ObjectBase +class KGE_API SoundPlayer : public SoundCallback { public: - using SoundMap = Map; + using SoundList = List; SoundPlayer(); ~SoundPlayer(); /// \~chinese - /// @brief 加载本地音频文件 - /// @param file_path 本地音频文件路径 - /// @return 音频标识符 - size_t Load(const String& file_path); - - /// \~chinese - /// @brief 加载音频资源 - /// @param res 音频资源 - /// @return 音频标识符 - size_t Load(const Resource& res); + /// @brief 播放音频 + /// @param sound 音频 + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + void Play(SoundPtr sound, int loop_count = 0); /// \~chinese /// @brief 播放音频 - /// @param id 音频标识符 - /// @param loop_count 播放循环次数,设置 -1 为循环播放 - void Play(size_t id, int loop_count = 0); - - /// \~chinese - /// @brief 暂停音频 - /// @param id 音频标识符 - void Pause(size_t id); - - /// \~chinese - /// @brief 继续播放音频 - /// @param id 音频标识符 - void Resume(size_t id); - - /// \~chinese - /// @brief 停止音频 - /// @param id 音频标识符 - void Stop(size_t id); - - /// \~chinese - /// @brief 获取音频播放状态 - /// @param id 音频标识符 - bool IsPlaying(size_t id); - - /// \~chinese - /// @brief 获取音量 - float GetVolume() const; - - /// \~chinese - /// @brief 设置音量 - /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 - void SetVolume(float volume); - - /// \~chinese - /// @brief 获取本地音频文件id /// @param file_path 本地音频文件路径 - /// @return 音频标识符 - size_t GetId(const String& file_path) const; + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + /// @param callbacks 注册回调 + SoundPtr Play(const String& file_path, int loop_count = 0, std::initializer_list callbacks = {}); /// \~chinese - /// @brief 获取音频资源id + /// @brief 播放音频 /// @param res 音频资源 - /// @return 音频标识符 - size_t GetId(const Resource& res) const; - - /// \~chinese - /// @brief 获取音乐对象 - /// @param id 音频标识符 - SoundPtr GetSound(size_t id) const; + /// @param loop_count 播放循环次数,设置 -1 为循环播放 + /// @param callbacks 注册回调 + SoundPtr Play(const Resource& res, int loop_count = 0, std::initializer_list callbacks = {}); /// \~chinese /// @brief 暂停所有音频 @@ -123,29 +78,41 @@ public: void StopAll(); /// \~chinese - /// @brief 释放音乐对象缓存 - /// @param id 音频标识符 - void ReleaseSound(size_t id); + /// @brief 获取正在播放的音频列表 + const SoundList& GetPlayingList() const; /// \~chinese - /// @brief 获取缓存 - const SoundMap& GetCache() const; + /// @brief 获取音量 + float GetVolume() const; /// \~chinese - /// @brief 清除缓存 - void ClearCache(); + /// @brief 设置音量 + /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 + void SetVolume(float volume); -private: - float volume_; +public: + void OnEnd(Sound* sound) override; - SoundMap sound_cache_; + float OnVolumeChanged(Sound* sound, float volume) override; + +protected: + void SetCallback(Sound* sound); + + void RemoveCallback(Sound* sound); + + void ClearTrash(); + +protected: + float volume_; + SoundList sound_list_; + SoundList trash_; }; /** @} */ -inline const SoundPlayer::SoundMap& SoundPlayer::GetCache() const +inline const SoundPlayer::SoundList& SoundPlayer::GetPlayingList() const { - return sound_cache_; + return sound_list_; } } // namespace audio From 48753fb39e690289adbbe067f5eabe2ed15196bd Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 01:02:53 +0800 Subject: [PATCH 13/14] chore: use direct2d 1.1 --- src/kiwano/render/DirectX/helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kiwano/render/DirectX/helper.h b/src/kiwano/render/DirectX/helper.h index 87f8b774..0aeb02a4 100644 --- a/src/kiwano/render/DirectX/helper.h +++ b/src/kiwano/render/DirectX/helper.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include namespace kiwano From e571bf963ea71ca3e6edf2dbb1f4c53f9595d855 Mon Sep 17 00:00:00 2001 From: Nomango Date: Mon, 25 Sep 2023 01:24:24 +0800 Subject: [PATCH 14/14] fix: memory leak --- src/kiwano-audio/SoundPlayer.cpp | 28 ++++++++++++++++++++++++---- src/kiwano-audio/SoundPlayer.h | 18 +++++++++--------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp index b223744f..737dbaeb 100644 --- a/src/kiwano-audio/SoundPlayer.cpp +++ b/src/kiwano-audio/SoundPlayer.cpp @@ -29,11 +29,31 @@ namespace audio SoundPlayer::SoundPlayer() : volume_(1.f) { + class SoundCallbackFunc : public SoundCallback + { + public: + Function cb_on_end; + Function cb_on_volume_changed; + + void OnEnd(Sound* sound) override + { + cb_on_end(sound); + } + + float OnVolumeChanged(Sound* sound, float volume) override + { + return cb_on_volume_changed(sound, volume); + } + }; + auto cb = MakePtr(); + cb->cb_on_end = std::bind(&SoundPlayer::OnEnd, this, std::placeholders::_1); + cb->cb_on_volume_changed = + std::bind(&SoundPlayer::OnVolumeChanged, this, std::placeholders::_1, std::placeholders::_2); + callback_ = cb; } SoundPlayer::~SoundPlayer() { - StopAll(); } void SoundPlayer::Play(SoundPtr sound, int loop_count) @@ -120,10 +140,10 @@ void SoundPlayer::SetCallback(Sound* sound) { // add callback if not exists auto& cbs = sound->GetCallbacks(); - auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + auto iter = std::find(cbs.begin(), cbs.end(), callback_); if (iter == cbs.end()) { - sound->AddCallback(this); + sound->AddCallback(callback_); } sound->ResetVolume(); } @@ -131,7 +151,7 @@ void SoundPlayer::SetCallback(Sound* sound) void SoundPlayer::RemoveCallback(Sound* sound) { auto& cbs = sound->GetCallbacks(); - auto iter = std::find_if(cbs.begin(), cbs.end(), [this](const SoundCallbackPtr& ptr) { return ptr.Get() == this; }); + auto iter = std::find(cbs.begin(), cbs.end(), callback_); if (iter != cbs.end()) { *iter = nullptr; // will be removed by sound diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h index 66e53aa7..baca0034 100644 --- a/src/kiwano-audio/SoundPlayer.h +++ b/src/kiwano-audio/SoundPlayer.h @@ -36,7 +36,7 @@ KGE_DECLARE_SMART_PTR(SoundPlayer); * \~chinese * @brief 音频播放器 */ -class KGE_API SoundPlayer : public SoundCallback +class KGE_API SoundPlayer : public ObjectBase { public: using SoundList = List; @@ -90,12 +90,11 @@ public: /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量 void SetVolume(float volume); -public: - void OnEnd(Sound* sound) override; - - float OnVolumeChanged(Sound* sound, float volume) override; - protected: + void OnEnd(Sound* sound); + + float OnVolumeChanged(Sound* sound, float volume); + void SetCallback(Sound* sound); void RemoveCallback(Sound* sound); @@ -103,9 +102,10 @@ protected: void ClearTrash(); protected: - float volume_; - SoundList sound_list_; - SoundList trash_; + float volume_; + SoundList sound_list_; + SoundList trash_; + SoundCallbackPtr callback_; }; /** @} */