diff --git a/appveyor.yml b/appveyor.yml index dc89cd0d..073bdfc0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,8 +5,8 @@ skip_tags: true # fetch repository as zip archive shallow_clone: true -# pull_requests: -# do_not_increment_build_number: true +pull_requests: + do_not_increment_build_number: true # Do not build feature branch with open Pull Requests # skip_branch_with_pr: true @@ -47,6 +47,18 @@ only_commits: - scripts/**/*.ps1 - appveyor.yml +for: +- + branches: + except: + - master + # only_commits: + # message: /\[build\]/ + environment: + matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + VS_PLATFORM_TOOLSET: v142 + configuration: - Debug - Release @@ -67,19 +79,8 @@ build: project: projects/Kiwano.sln verbosity: minimal -for: -- - branches: - except: - - master - only_commits: - message: /\[build\]/ -- - branches: - only: - - master - after_build: - - ps: .\scripts\appveyor\wait_for_other_jobs.ps1 +after_build: +- ps: .\scripts\appveyor\wait_for_other_jobs.ps1 artifacts: - path: projects/output/**/*.lib @@ -119,6 +120,6 @@ notifications: - provider: Email to: - 569629550@qq.com - on_build_success: false + on_build_success: true on_build_failure: true on_build_status_changed: false \ No newline at end of file diff --git a/projects/kiwano.vcxproj b/projects/kiwano.vcxproj index 2931f641..39a9021c 100644 --- a/projects/kiwano.vcxproj +++ b/projects/kiwano.vcxproj @@ -11,7 +11,6 @@ - @@ -28,7 +27,6 @@ - @@ -49,7 +47,7 @@ - + @@ -59,7 +57,7 @@ - + @@ -67,17 +65,24 @@ - - - - + + + - + - + + + + + + + + + @@ -96,7 +101,6 @@ - @@ -113,7 +117,7 @@ - + @@ -122,15 +126,22 @@ - - - + + + + - + + + + + + + @@ -193,7 +204,7 @@ Level3 Disabled true - None + EditAndContinue true false @@ -210,7 +221,7 @@ true false true - None + EditAndContinue true false diff --git a/projects/kiwano.vcxproj.filters b/projects/kiwano.vcxproj.filters index 1be66a3b..8995881e 100644 --- a/projects/kiwano.vcxproj.filters +++ b/projects/kiwano.vcxproj.filters @@ -37,6 +37,9 @@ {86e2d0f2-a9d0-4456-b6a5-d480228bbf82} + + {30333461-e9bc-4709-84bd-ce6e0e1a3079} + @@ -48,9 +51,6 @@ 2d - - 2d - 2d @@ -99,21 +99,6 @@ base - - renderer - - - renderer - - - renderer - - - renderer - - - renderer - math @@ -143,9 +128,6 @@ - - math - math @@ -155,9 +137,6 @@ base - - base - utils @@ -177,9 +156,6 @@ utils - - renderer - base @@ -201,12 +177,6 @@ 2d - - renderer - - - renderer - 2d\action @@ -225,9 +195,6 @@ 2d\action - - renderer - utils @@ -258,9 +225,6 @@ core - - base - core @@ -270,9 +234,6 @@ 2d - - renderer - 2d\action @@ -285,9 +246,66 @@ base + + math + + + base + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + renderer + + renderer + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer + + + renderer + @@ -299,9 +317,6 @@ 2d - - 2d - 2d @@ -326,18 +341,6 @@ base - - renderer - - - renderer - - - renderer - - - renderer - platform @@ -350,9 +353,6 @@ base - - base - utils @@ -389,12 +389,6 @@ 2d - - renderer - - - renderer - 2d\action @@ -410,9 +404,6 @@ 2d\action - - renderer - utils @@ -425,9 +416,6 @@ 2d - - renderer - 2d\action @@ -440,8 +428,56 @@ base + + base + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + + + renderer + renderer + + renderer + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer\win32 + + + renderer + + + renderer + \ No newline at end of file diff --git a/scripts/appveyor/wait_for_other_jobs.ps1 b/scripts/appveyor/wait_for_other_jobs.ps1 index 4a2aa2df..422b65da 100644 --- a/scripts/appveyor/wait_for_other_jobs.ps1 +++ b/scripts/appveyor/wait_for_other_jobs.ps1 @@ -1,5 +1,8 @@ . .\scripts\appveyor\appveyor_get_build.ps1 +# Ignore commits without APPVEYOR_API_TOKEN envrionment variable +if (-not ($env:APPVEYOR_API_TOKEN)) { return } + # Only deploy when commit message contains "[deploy]" if (!(Get-AppVeyorBuild).build.message.Contains('[deploy]')) { return } diff --git a/src/kiwano-audio/src/Player.cpp b/src/kiwano-audio/src/Player.cpp index f460a2c5..a7a862bf 100644 --- a/src/kiwano-audio/src/Player.cpp +++ b/src/kiwano-audio/src/Player.cpp @@ -34,13 +34,33 @@ namespace kiwano ClearCache(); } - bool Player::Load(Resource const& res) + size_t Player::Load(String const& file_path) { - size_t hash_code = res.GetHashCode(); + size_t hash_code = file_path.hash(); if (sound_cache_.end() != sound_cache_.find(hash_code)) return true; - SoundPtr sound = new (std::nothrow) Sound(); + SoundPtr sound = new (std::nothrow) Sound; + + if (sound) + { + if (sound->Load(file_path)) + { + sound->SetVolume(volume_); + sound_cache_.insert(std::make_pair(hash_code, sound)); + return true; + } + } + return false; + } + + size_t Player::Load(Resource const& res) + { + size_t hash_code = res.GetId(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return true; + + SoundPtr sound = new (std::nothrow) Sound; if (sound) { @@ -54,42 +74,39 @@ namespace kiwano return false; } - void Player::Play(Resource const& res, int loop_count) + void Player::Play(size_t id, int loop_count) { - if (Load(res)) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Play(loop_count); - } + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Play(loop_count); } - void Player::Pause(Resource const& res) + void Player::Pause(size_t id) { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Pause(); + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Pause(); } - void Player::Resume(Resource const& res) + void Player::Resume(size_t id) { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Resume(); + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Resume(); } - void Player::Stop(Resource const& res) + void Player::Stop(size_t id) { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Stop(); + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + iter->second->Stop(); } - bool Player::IsPlaying(Resource const& res) + bool Player::IsPlaying(size_t id) { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - return sound_cache_[hash_code]->IsPlaying(); + auto iter = sound_cache_.find(id); + if (sound_cache_.end() != iter) + return iter->second->IsPlaying(); return false; } diff --git a/src/kiwano-audio/src/Player.h b/src/kiwano-audio/src/Player.h index 290c7de7..8ad90766 100644 --- a/src/kiwano-audio/src/Player.h +++ b/src/kiwano-audio/src/Player.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include "Sound.h" namespace kiwano @@ -31,44 +31,47 @@ namespace kiwano // 音乐播放器 class KGE_API Player - : protected Object + : protected ObjectBase { - using MusicMap = Map; - public: Player(); ~Player(); - // 加载音乐资源 - bool Load( + // 加载本地音频文件, 返回该资源标识符 + size_t Load( + String const& file_path + ); + + // 加载音乐资源, 返回该资源标识符 + size_t Load( Resource const& res /* 音乐资源 */ ); // 播放音乐 void Play( - Resource const& res, /* 音乐资源 */ + size_t id, /* 标识符 */ int loop_count = 0 /* 播放循环次数 (-1 为循环播放) */ ); // 暂停音乐 void Pause( - Resource const& res /* 音乐资源 */ + size_t id /* 标识符 */ ); // 继续播放音乐 void Resume( - Resource const& res /* 音乐资源 */ + size_t id /* 标识符 */ ); // 停止音乐 void Stop( - Resource const& res /* 音乐资源 */ + size_t id /* 标识符 */ ); // 获取音乐播放状态 bool IsPlaying( - Resource const& res /* 音乐资源 */ + size_t id /* 标识符 */ ); // 获取音量 @@ -92,8 +95,10 @@ namespace kiwano void ClearCache(); protected: - float volume_; - MusicMap sound_cache_; + float volume_; + + using MusicMap = Map; + MusicMap sound_cache_; }; } } diff --git a/src/kiwano-audio/src/Sound.cpp b/src/kiwano-audio/src/Sound.cpp index ddf88d6f..cf2c9c2e 100644 --- a/src/kiwano-audio/src/Sound.cpp +++ b/src/kiwano-audio/src/Sound.cpp @@ -49,6 +49,46 @@ namespace kiwano Close(); } + bool Sound::Load(String const& file_path) + { + if (opened_) + { + Close(); + } + +#if defined(KGE_DEBUG) + if (!FileUtil::ExistsFile(file_path)) + { + KGE_WARNING_LOG(L"Media file '%s' not found", file_path.c_str()); + return false; + } +#endif + + Transcoder transcoder; + HRESULT hr = transcoder.LoadMediaFile(file_path, &wave_data_, &size_); + + if (FAILED(hr)) + { + KGE_ERROR_LOG(L"Load media file failed with HRESULT of %08X", hr); + return false; + } + + hr = Audio::GetInstance()->CreateVoice(&voice_, transcoder.GetWaveFormatEx()); + if (FAILED(hr)) + { + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + KGE_ERROR_LOG(L"Create source voice failed with HRESULT of %08X", hr); + return false; + } + + opened_ = true; + return true; + } + bool Sound::Load(Resource const& res) { if (opened_) @@ -56,28 +96,12 @@ namespace kiwano Close(); } - HRESULT hr = S_OK; Transcoder transcoder; - - if (res.IsFileType()) - { -#if defined(KGE_DEBUG) - if (!FileUtil::ExistsFile(res.GetFileName())) - { - KGE_WARNING_LOG(L"Media file '%s' not found", res.GetFileName().c_str()); - return false; - } -#endif - hr = transcoder.LoadMediaFile(res.GetFileName(), &wave_data_, &size_); - } - else - { - hr = transcoder.LoadMediaResource(res, &wave_data_, &size_); - } + HRESULT hr = transcoder.LoadMediaResource(res, &wave_data_, &size_); if (FAILED(hr)) { - KGE_ERROR_LOG(L"Load media file failed with HRESULT of %08X", hr); + KGE_ERROR_LOG(L"Load media resource failed with HRESULT of %08X", hr); return false; } diff --git a/src/kiwano-audio/src/Sound.h b/src/kiwano-audio/src/Sound.h index fa505676..d39f457f 100644 --- a/src/kiwano-audio/src/Sound.h +++ b/src/kiwano-audio/src/Sound.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include #include @@ -32,17 +32,26 @@ namespace kiwano // 音乐对象 class KGE_API Sound - : public Object + : public ObjectBase { public: Sound(); + Sound( + String const& file_path /* 本地音频文件 */ + ); + Sound( Resource const& res /* 音乐资源 */ ); virtual ~Sound(); + // 打开本地音频文件 + bool Load( + String const& file_path + ); + // 打开音乐资源 bool Load( Resource const& res /* 音乐资源 */ diff --git a/src/kiwano-audio/src/Transcoder.cpp b/src/kiwano-audio/src/Transcoder.cpp index 4d2d7baf..0e38305e 100644 --- a/src/kiwano-audio/src/Transcoder.cpp +++ b/src/kiwano-audio/src/Transcoder.cpp @@ -24,9 +24,9 @@ #include #include -#include #include #include +#include #include #include "audio-modules.h" #include "Transcoder.h" @@ -83,13 +83,12 @@ namespace kiwano ComPtr byte_stream; ComPtr reader; - LPVOID buffer; - DWORD buffer_size; - if (!res.Load(buffer, buffer_size)) { return E_FAIL; } + Resource::Data data = res.GetData(); + if (!data) { return E_FAIL; } stream = kiwano::modules::Shlwapi::Get().SHCreateMemStream( - static_cast(buffer), - static_cast(buffer_size) + static_cast(data.buffer), + static_cast(data.size) ); if (stream == nullptr) diff --git a/src/kiwano-network/src/HttpRequest.hpp b/src/kiwano-network/src/HttpRequest.hpp index 79335b4f..5f147e57 100644 --- a/src/kiwano-network/src/HttpRequest.hpp +++ b/src/kiwano-network/src/HttpRequest.hpp @@ -21,7 +21,7 @@ #pragma once #include #include -#include +#include namespace kiwano { @@ -30,7 +30,7 @@ namespace kiwano typedef Function ResponseCallback; class KGE_API HttpRequest - : public Object + : public ObjectBase { public: enum class Type diff --git a/src/kiwano-network/src/HttpResponse.hpp b/src/kiwano-network/src/HttpResponse.hpp index 094361b4..c5d35ffe 100644 --- a/src/kiwano-network/src/HttpResponse.hpp +++ b/src/kiwano-network/src/HttpResponse.hpp @@ -21,14 +21,14 @@ #pragma once #include #include -#include +#include namespace kiwano { namespace network { class KGE_API HttpResponse - : public Object + : public ObjectBase { public: inline HttpResponse(HttpRequestPtr request) diff --git a/src/kiwano/2d/Actor.cpp b/src/kiwano/2d/Actor.cpp index 2db554b4..8ef488f5 100644 --- a/src/kiwano/2d/Actor.cpp +++ b/src/kiwano/2d/Actor.cpp @@ -71,7 +71,7 @@ namespace kiwano OnUpdate(dt); } - if (!children_.is_empty()) + if (!children_.item_empty()) { ActorPtr next; for (auto child = children_.first_item(); child; child = next) @@ -89,7 +89,7 @@ namespace kiwano UpdateTransform(); - if (children_.is_empty()) + if (children_.item_empty()) { OnRender(renderer); } @@ -131,7 +131,7 @@ namespace kiwano auto renderer = Renderer::GetInstance(); renderer->SetTransform(transform_matrix_); renderer->FillRectangle(bounds, Color(Color::Red, .4f)); - renderer->DrawRectangle(bounds, Color(Color::Red, .8f), 4.f); + renderer->DrawRectangle(bounds, Color(Color::Red, .8f), 2.f); } for (auto child = children_.first_item(); child; child = child->next_item()) @@ -407,7 +407,7 @@ namespace kiwano { if (!IsName(name)) { - Object::SetName(name); + ObjectBase::SetName(name); hash_name_ = std::hash{}(name); } } @@ -609,7 +609,7 @@ namespace kiwano { KGE_ASSERT(child && "Actor::RemoveChild failed, NULL pointer exception"); - if (children_.is_empty()) + if (children_.item_empty()) return; if (child) @@ -622,7 +622,7 @@ namespace kiwano void Actor::RemoveChildren(String const& child_name) { - if (children_.is_empty()) + if (children_.item_empty()) { return; } diff --git a/src/kiwano/2d/Actor.h b/src/kiwano/2d/Actor.h index 55f8f105..e8764689 100644 --- a/src/kiwano/2d/Actor.h +++ b/src/kiwano/2d/Actor.h @@ -32,7 +32,7 @@ namespace kiwano // 角色 class KGE_API Actor - : public Object + : public ObjectBase , public TimerManager , public ActionManager , public EventDispatcher diff --git a/src/kiwano/2d/Canvas.cpp b/src/kiwano/2d/Canvas.cpp index c9f01d1b..cef8a6fb 100644 --- a/src/kiwano/2d/Canvas.cpp +++ b/src/kiwano/2d/Canvas.cpp @@ -27,35 +27,11 @@ namespace kiwano Canvas::Canvas() : cache_expired_(false) , stroke_width_(1.0f) + , fill_color_(0, 0, 0) + , stroke_color_(Color::White) + , stroke_style_(StrokeStyle::Miter) { - auto ctx = Renderer::GetInstance()->GetD2DDeviceResources()->GetDeviceContext(); - - ThrowIfFailed( - ctx->CreateCompatibleRenderTarget(&render_target_) - ); - - ThrowIfFailed( - render_target_->CreateSolidColorBrush( - D2D1::ColorF(0, 0, 0, 0), - D2D1::BrushProperties(), - &fill_brush_) - ); - - ThrowIfFailed( - render_target_->CreateSolidColorBrush( - D2D1::ColorF(Color::White), - D2D1::BrushProperties(), - &stroke_brush_) - ); - - ThrowIfFailed( - ITextRenderer::Create( - &text_renderer_, - render_target_.get() - ) - ); - - SetTextStyle(Font{}, TextStyle{}); + Renderer::GetInstance()->CreateImageRenderTarget(rt_); } Canvas::Canvas(float width, float height) @@ -75,45 +51,36 @@ namespace kiwano void Canvas::BeginDraw() { - render_target_->BeginDraw(); + rt_.BeginDraw(); } void Canvas::EndDraw() { - ThrowIfFailed( - render_target_->EndDraw() - ); + rt_.EndDraw(); cache_expired_ = true; } void Canvas::OnRender(Renderer* renderer) { - if (cache_expired_) - { - GetBitmap(); - } + UpdateCache(); - if (bitmap_cached_) + if (image_cached_.IsValid()) { PrepareRender(renderer); - Rect bitmap_rect(0.f, 0.f, bitmap_cached_->GetSize().width, bitmap_cached_->GetSize().height); - renderer->DrawBitmap( - bitmap_cached_, - bitmap_rect, - bitmap_rect - ); + Rect bitmap_rect(0.f, 0.f, image_cached_.GetWidth(), image_cached_.GetHeight()); + renderer->DrawImage(image_cached_, bitmap_rect, bitmap_rect); } } void Canvas::SetStrokeColor(Color const& color) { - stroke_brush_->SetColor(DX::ConvertToColorF(color)); + stroke_color_ = color; } void Canvas::SetFillColor(Color const& color) { - fill_brush_->SetColor(DX::ConvertToColorF(color)); + fill_color_ = color; } void Canvas::SetStrokeWidth(float width) @@ -121,39 +88,34 @@ namespace kiwano stroke_width_ = std::max(width, 0.f); } - void Canvas::SetOutlineJoinStyle(StrokeStyle outline_join) + void Canvas::SetStrokeStyle(StrokeStyle stroke_style) { - outline_join_style_ = Renderer::GetInstance()->GetD2DDeviceResources()->GetStrokeStyle(outline_join); + stroke_style_ = stroke_style; } - void Canvas::SetTextStyle(Font const& font, TextStyle const & text_style) + void Canvas::SetTextFont(Font const& font) { text_font_ = font; + } + + void Canvas::SetTextStyle(TextStyle const & text_style) + { text_style_ = text_style; + } - text_renderer_->SetTextStyle( - 1.f, - DX::ConvertToColorF(text_style_.color), - text_style_.outline, - DX::ConvertToColorF(text_style_.outline_color), - text_style_.outline_width, - Renderer::GetInstance()->GetD2DDeviceResources()->GetStrokeStyle(text_style_.outline_stroke) - ); - - // clear text format - text_format_ = nullptr; + void Canvas::SetBrushOpacity(float opacity) + { + rt_.SetOpacity(opacity); } Color Canvas::GetStrokeColor() const { - auto color_f = stroke_brush_->GetColor(); - return Color{ color_f.r, color_f.g, color_f.b, color_f.a }; + return stroke_color_; } Color Canvas::GetFillColor() const { - auto color_f = fill_brush_->GetColor(); - return Color{ color_f.r, color_f.g, color_f.b, color_f.a }; + return fill_color_; } float Canvas::GetStrokeWidth() const @@ -161,324 +123,219 @@ namespace kiwano return stroke_width_; } + float Canvas::GetBrushOpacity() const + { + return rt_.GetOpacity(); + } + + void Canvas::SetBrushTransform(Transform const& transform) + { + Matrix3x2 matrix = Matrix3x2::SRT(transform.position, transform.scale, transform.rotation); + if (!transform.skew.IsOrigin()) + { + matrix = Matrix3x2::Skewing(transform.skew) * matrix; + } + + rt_.SetTransform(matrix); + } + void Canvas::SetBrushTransform(Matrix3x2 const & transform) { - render_target_->SetTransform(DX::ConvertToMatrix3x2F(transform)); + rt_.SetTransform(transform); } - void Canvas::DrawLine(const Point & begin, const Point & end) + void Canvas::DrawLine(Point const& begin, Point const& end) { - render_target_->DrawLine( - D2D1::Point2F(begin.x, begin.y), - D2D1::Point2F(end.x, end.y), - stroke_brush_.get(), + rt_.DrawLine( + begin, + end, + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } - void Canvas::DrawCircle(const Point & center, float radius) + void Canvas::DrawCircle(Point const& center, float radius) { - render_target_->DrawEllipse( - D2D1::Ellipse( - D2D1::Point2F( - center.x, - center.y - ), - radius, - radius - ), - stroke_brush_.get(), + rt_.DrawEllipse( + center, + Vec2(radius, radius), + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } - void Canvas::DrawEllipse(const Point & center, float radius_x, float radius_y) + void Canvas::DrawEllipse(Point const& center, Vec2 const& radius) { - render_target_->DrawEllipse( - D2D1::Ellipse( - D2D1::Point2F( - center.x, - center.y - ), - radius_x, - radius_y - ), - stroke_brush_.get(), + rt_.DrawEllipse( + center, + radius, + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } - void Canvas::DrawRect(const Rect & rect) + void Canvas::DrawRect(Rect const& rect) { - render_target_->DrawRectangle( - D2D1::RectF( - rect.origin.x, - rect.origin.y, - rect.origin.x + rect.size.x, - rect.origin.y + rect.size.y - ), - stroke_brush_.get(), + rt_.DrawRectangle( + rect, + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } - void Canvas::DrawRoundedRect(const Rect & rect, float radius_x, float radius_y) + void Canvas::DrawRoundedRect(Rect const& rect, Vec2 const& radius) { - render_target_->DrawRoundedRectangle( - D2D1::RoundedRect( - D2D1::RectF( - rect.origin.x, - rect.origin.y, - rect.origin.x + rect.size.x, - rect.origin.y + rect.size.y - ), - radius_x, - radius_y - ), - stroke_brush_.get(), + rt_.DrawRoundedRectangle( + rect, + radius, + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } - void Canvas::DrawImage(ImagePtr image, float opacity) + void Canvas::FillCircle(Point const& center, float radius) { - if (image && image->GetBitmap()) + rt_.FillEllipse( + center, + Vec2(radius, radius), + fill_color_ + ); + cache_expired_ = true; + } + + void Canvas::FillEllipse(Point const& center, Vec2 const& radius) + { + rt_.FillEllipse( + center, + radius, + fill_color_ + ); + cache_expired_ = true; + } + + void Canvas::FillRect(Rect const& rect) + { + rt_.FillRectangle( + rect, + fill_color_ + ); + cache_expired_ = true; + } + + void Canvas::FillRoundedRect(Rect const& rect, Vec2 const& radius) + { + rt_.FillRoundedRectangle( + rect, + radius, + fill_color_ + ); + cache_expired_ = true; + } + + void Canvas::DrawImage(Image const& image, const Rect* src_rect, const Rect* dest_rect) + { + if (image.IsValid()) { - render_target_->DrawBitmap( - image->GetBitmap().get(), - D2D1::RectF(0, 0, image->GetWidth(), image->GetHeight()), - opacity, - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, - D2D1::RectF(0, 0, image->GetWidth(), image->GetHeight()) - ); + rt_.DrawImage(image, src_rect, dest_rect); cache_expired_ = true; } } - void Canvas::DrawText(String const & text, Point const & point) + void Canvas::DrawText(String const& text, Point const& point) { if (text.empty()) return; - if (!text_format_) - { - ThrowIfFailed( - Renderer::GetInstance()->GetD2DDeviceResources()->CreateTextFormat( - text_format_, - text_font_, - text_style_ - ) - ); - } + TextLayout layout(text, text_font_, text_style_); - ComPtr text_layout; - Size layout_size; - ThrowIfFailed( - Renderer::GetInstance()->GetD2DDeviceResources()->CreateTextLayout( - text_layout, - layout_size, - text, - text_format_, - text_style_ - ) - ); - - ThrowIfFailed( - text_layout->Draw(nullptr, text_renderer_.get(), point.x, point.y) - ); - } - - void Canvas::FillCircle(const Point & center, float radius) - { - render_target_->FillEllipse( - D2D1::Ellipse( - D2D1::Point2F( - center.x, - center.y - ), - radius, - radius - ), - fill_brush_.get() - ); - cache_expired_ = true; - } - - void Canvas::FillEllipse(const Point & center, float radius_x, float radius_y) - { - render_target_->FillEllipse( - D2D1::Ellipse( - D2D1::Point2F( - center.x, - center.y - ), - radius_x, - radius_y - ), - fill_brush_.get() - ); - cache_expired_ = true; - } - - void Canvas::FillRect(const Rect & rect) - { - render_target_->FillRectangle( - D2D1::RectF( - rect.origin.x, - rect.origin.y, - rect.origin.x + rect.size.x, - rect.origin.y + rect.size.y - ), - fill_brush_.get() - ); - cache_expired_ = true; - } - - void Canvas::FillRoundedRect(const Rect & rect, float radius_x, float radius_y) - { - render_target_->FillRoundedRectangle( - D2D1::RoundedRect( - D2D1::RectF( - rect.origin.x, - rect.origin.y, - rect.origin.x + rect.size.x, - rect.origin.y + rect.size.y - ), - radius_x, - radius_y - ), - fill_brush_.get() - ); - cache_expired_ = true; + rt_.DrawTextLayout(layout, point); } void Canvas::BeginPath(Point const& begin_pos) { - current_geometry_ = nullptr; - - ThrowIfFailed( - Renderer::GetInstance()->GetD2DDeviceResources()->GetFactory()->CreatePathGeometry(¤t_geometry_) - ); - - ThrowIfFailed( - current_geometry_->Open(¤t_sink_) - ); - - current_sink_->BeginFigure(DX::ConvertToPoint2F(begin_pos), D2D1_FIGURE_BEGIN_FILLED); + geo_sink_.BeginPath(begin_pos); } void Canvas::EndPath(bool closed) { - if (current_sink_) - { - current_sink_->EndFigure(closed ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN); - ThrowIfFailed( - current_sink_->Close() - ); - current_sink_ = nullptr; - } + geo_sink_.EndPath(closed); } void Canvas::AddLine(Point const & point) { - if (current_sink_) - current_sink_->AddLine(DX::ConvertToPoint2F(point)); + geo_sink_.AddLine(point); } void Canvas::AddLines(Vector const& points) { - if (current_sink_ && !points.empty()) - { - current_sink_->AddLines( - reinterpret_cast(&points[0]), - static_cast(points.size()) - ); - } + geo_sink_.AddLines(points); } void Canvas::AddBezier(Point const & point1, Point const & point2, Point const & point3) { - if (current_sink_) - { - current_sink_->AddBezier( - D2D1::BezierSegment( - DX::ConvertToPoint2F(point1), - DX::ConvertToPoint2F(point2), - DX::ConvertToPoint2F(point3) - ) - ); - } + geo_sink_.AddBezier(point1, point2, point3); } void Canvas::AddArc(Point const & point, Point const & radius, float rotation, bool clockwise, bool is_small) { - if (current_sink_) - { - current_sink_->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 - ) - ); - } + geo_sink_.AddArc(point, radius, rotation, clockwise, is_small); } void Canvas::StrokePath() { - render_target_->DrawGeometry( - current_geometry_.get(), - stroke_brush_.get(), + rt_.DrawGeometry( + geo_sink_.GetGeometry(), + stroke_color_, stroke_width_, - outline_join_style_.get() + stroke_style_ ); cache_expired_ = true; } void Canvas::FillPath() { - render_target_->FillGeometry( - current_geometry_.get(), - fill_brush_.get() + rt_.FillGeometry( + geo_sink_.GetGeometry(), + fill_color_ ); cache_expired_ = true; } void Canvas::Clear() { - render_target_->Clear(); + rt_.Clear(); cache_expired_ = true; } - ImagePtr Canvas::ExportToImage() const + void Canvas::Clear(Color const& clear_color) { - ImagePtr image = new Image(GetBitmap()); - return image; + rt_.Clear(clear_color); + cache_expired_ = true; } - ComPtr const& kiwano::Canvas::GetBitmap() const + Image Canvas::ExportToImage() const + { + UpdateCache(); + return image_cached_; + } + + void Canvas::UpdateCache() const { if (cache_expired_) { - bitmap_cached_ = nullptr; - ThrowIfFailed( - render_target_->GetBitmap(&bitmap_cached_) - ); + rt_.GetOutput(image_cached_); cache_expired_ = false; } - return bitmap_cached_; } } diff --git a/src/kiwano/2d/Canvas.h b/src/kiwano/2d/Canvas.h index add2a2c1..6508ebd0 100644 --- a/src/kiwano/2d/Canvas.h +++ b/src/kiwano/2d/Canvas.h @@ -20,10 +20,7 @@ #pragma once #include "Actor.h" -#include "Font.hpp" -#include "TextStyle.hpp" -#include "../renderer/Image.h" -#include "../renderer/TextRenderer.h" +#include "../renderer/RenderTarget.h" #ifdef DrawText # undef DrawText @@ -57,39 +54,61 @@ namespace kiwano // 画直线 void DrawLine( - const Point& begin, - const Point& end + Point const& begin, + Point const& end ); // 画圆形边框 void DrawCircle( - const Point& center, + Point const& center, float radius ); // 画椭圆形边框 void DrawEllipse( - const Point& center, - float radius_x, - float radius_y + Point const& center, + Vec2 const& radius ); // 画矩形边框 void DrawRect( - const Rect& rect + Rect const& rect ); // 画圆角矩形边框 void DrawRoundedRect( - const Rect& rect, - float radius_x, - float radius_y + Rect const& rect, + Vec2 const& radius + ); + + // 填充圆形 + void FillCircle( + Point const& center, + float radius + ); + + // 填充椭圆形 + void FillEllipse( + Point const& center, + Vec2 const& radius + ); + + // 填充矩形 + void FillRect( + Rect const& rect + ); + + // 填充圆角矩形 + void FillRoundedRect( + Rect const& rect, + Vec2 const& radius ); // 画图片 void DrawImage( - ImagePtr image, - float opacity = 1.f + Image const& image, + const Rect* src_rect = nullptr, + const Rect* dest_rect = nullptr ); // 画文字 @@ -98,31 +117,6 @@ namespace kiwano Point const& point /* 文字位置 */ ); - // 填充圆形 - void FillCircle( - const Point& center, - float radius - ); - - // 填充椭圆形 - void FillEllipse( - const Point& center, - float radius_x, - float radius_y - ); - - // 填充矩形 - void FillRect( - const Rect& rect - ); - - // 填充圆角矩形 - void FillRoundedRect( - const Rect& rect, - float radius_x, - float radius_y - ); - // 开始绘制路径 void BeginPath( Point const& begin_pos /* 路径起始点 */ @@ -168,14 +162,19 @@ namespace kiwano // 清空画布 void Clear(); + // 清空画布 + void Clear( + Color const& clear_color + ); + // 设置填充颜色 void SetFillColor( - const Color& color + Color const& color ); // 设置线条颜色 void SetStrokeColor( - const Color& color + Color const& color ); // 设置线条宽度 @@ -183,17 +182,26 @@ namespace kiwano float width ); - // 设置线条相交样式 - void SetOutlineJoinStyle( - StrokeStyle outline_join + // 设置线条样式 + void SetStrokeStyle( + StrokeStyle stroke_style + ); + + // 设置文字字体 + void SetTextFont( + Font const& font ); // 设置文字画刷样式 void SetTextStyle( - Font const& font, TextStyle const& text_style ); + // 设置画笔透明度 + void SetBrushOpacity( + float opacity + ); + // 获取填充颜色 Color GetFillColor() const; @@ -203,34 +211,38 @@ namespace kiwano // 获取线条宽度 float GetStrokeWidth() const; - // 变换画笔 + // 获取画笔透明度 + float GetBrushOpacity() const; + + // 画笔二维变换 + void SetBrushTransform( + Transform const& transform + ); + + // 画笔二维变换 void SetBrushTransform( Matrix3x2 const& transform ); // 导出为图片 - ImagePtr ExportToImage() const; + Image ExportToImage() const; void OnRender(Renderer* renderer) override; protected: - ComPtr const& GetBitmap() const; + void UpdateCache() const; protected: - float stroke_width_; - Font text_font_; - TextStyle text_style_; + float stroke_width_; + Color fill_color_; + Color stroke_color_; + Font text_font_; + TextStyle text_style_; + StrokeStyle stroke_style_; + GeometrySink geo_sink_; + ImageRenderTarget rt_; - ComPtr current_geometry_; - ComPtr current_sink_; - ComPtr outline_join_style_; - ComPtr fill_brush_; - ComPtr stroke_brush_; - ComPtr text_format_; - ComPtr text_renderer_; - ComPtr render_target_; - - mutable bool cache_expired_; - mutable ComPtr bitmap_cached_; + mutable bool cache_expired_; + mutable Image image_cached_; }; } diff --git a/src/kiwano/2d/DebugActor.cpp b/src/kiwano/2d/DebugActor.cpp index e7429567..a2242f93 100644 --- a/src/kiwano/2d/DebugActor.cpp +++ b/src/kiwano/2d/DebugActor.cpp @@ -46,7 +46,6 @@ namespace kiwano debug_text_->SetFont(font); TextStyle style; - style.wrap = false; style.line_spacing = 20.f; debug_text_->SetStyle(style); @@ -83,9 +82,9 @@ namespace kiwano ss << "Fps: " << frame_time_.size() << std::endl; #if defined(KGE_DEBUG) - if (Object::IsTracingLeaks()) + if (ObjectBase::IsTracingLeaks()) { - ss << "Objects: " << Object::__GetTracingObjects().size() << std::endl; + ss << "Objects: " << ObjectBase::__GetTracingObjects().size() << std::endl; } #endif @@ -98,7 +97,7 @@ namespace kiwano ss << "Memory: " << pmc.PrivateUsage / 1024 << "kb"; debug_text_->SetText(ss.str()); - SetSize(Size{ 20 + debug_text_->GetLayoutSize().x, 20 + debug_text_->GetLayoutSize().y }); + SetSize(Size{ 20 + debug_text_->GetSize().x, 20 + debug_text_->GetSize().y }); } } diff --git a/src/kiwano/2d/Frame.cpp b/src/kiwano/2d/Frame.cpp index 97781255..ff99edde 100644 --- a/src/kiwano/2d/Frame.cpp +++ b/src/kiwano/2d/Frame.cpp @@ -27,20 +27,25 @@ namespace kiwano { } + Frame::Frame(String const& file_path) + { + Load(file_path); + } + Frame::Frame(Resource const& res) { Load(res); } - Frame::Frame(ImagePtr image) - : image_(image) + Frame::Frame(Image const& image) { + SetImage(image); } - bool Frame::Load(Resource const& res) + bool Frame::Load(String const& file_path) { - ImagePtr image = ImageCache::GetInstance()->AddImage(res); - if (image && image->IsValid()) + Image image = ImageCache::GetInstance()->AddOrGetImage(file_path); + if (image.IsValid()) { SetImage(image); return true; @@ -48,11 +53,22 @@ namespace kiwano return false; } - void Frame::Crop(Rect const& crop_rect) + bool Frame::Load(Resource const& res) { - if (image_) + Image image = ImageCache::GetInstance()->AddOrGetImage(res); + if (image.IsValid()) { - auto bitmap_size = image_->GetSize(); + SetImage(image); + return true; + } + return false; + } + + void Frame::SetCropRect(Rect const& crop_rect) + { + if (image_.IsValid()) + { + auto bitmap_size = image_.GetSize(); crop_rect_.origin.x = std::min(std::max(crop_rect.origin.x, 0.f), bitmap_size.x); crop_rect_.origin.y = std::min(std::max(crop_rect.origin.y, 0.f), bitmap_size.y); crop_rect_.size.x = std::min(std::max(crop_rect.size.x, 0.f), bitmap_size.x - crop_rect.origin.x); @@ -60,14 +76,14 @@ namespace kiwano } } - void Frame::SetImage(ImagePtr image) + void Frame::SetImage(Image const& image) { image_ = image; - if (image_) + if (image_.IsValid()) { crop_rect_.origin.x = crop_rect_.origin.y = 0; - crop_rect_.size.x = image_->GetWidth(); - crop_rect_.size.y = image_->GetHeight(); + crop_rect_.size.x = image_.GetWidth(); + crop_rect_.size.y = image_.GetHeight(); } } } diff --git a/src/kiwano/2d/Frame.h b/src/kiwano/2d/Frame.h index 360cfd63..9f13d4f0 100644 --- a/src/kiwano/2d/Frame.h +++ b/src/kiwano/2d/Frame.h @@ -25,17 +25,25 @@ namespace kiwano { // 帧图像 class KGE_API Frame - : public Object + : public ObjectBase { public: Frame(); + explicit Frame( + String const& file_path + ); + explicit Frame( Resource const& res ); explicit Frame( - ImagePtr image + Image const& image + ); + + bool Load( + String const& file_path ); bool Load( @@ -43,10 +51,15 @@ namespace kiwano ); // 裁剪矩形 - void Crop( + void SetCropRect( Rect const& crop_rect /* 裁剪矩形 */ ); + // 设置位图 + void SetImage( + Image const& image + ); + // 获取宽度 float GetWidth() const { return crop_rect_.size.x; } @@ -63,13 +76,10 @@ namespace kiwano inline Rect const& GetCropRect() const { return crop_rect_; } // 获取位图 - inline ImagePtr GetImage() const { return image_; } - - // 设置位图 - void SetImage(ImagePtr image); + inline Image const& GetImage() const { return image_; } protected: - ImagePtr image_; + Image image_; Rect crop_rect_; }; } diff --git a/src/kiwano/2d/FrameSequence.h b/src/kiwano/2d/FrameSequence.h index 863fa38e..09c0ccde 100644 --- a/src/kiwano/2d/FrameSequence.h +++ b/src/kiwano/2d/FrameSequence.h @@ -25,7 +25,7 @@ namespace kiwano { // 序列帧 class KGE_API FrameSequence - : public Object + : public ObjectBase { public: FrameSequence(); diff --git a/src/kiwano/2d/GifSprite.cpp b/src/kiwano/2d/GifSprite.cpp index bccf256c..08c9c471 100644 --- a/src/kiwano/2d/GifSprite.cpp +++ b/src/kiwano/2d/GifSprite.cpp @@ -20,7 +20,8 @@ #include "GifSprite.h" #include "../base/Logger.h" -#include "../platform/modules.h" +#include "../renderer/ImageCache.h" +#include "../renderer/Renderer.h" namespace kiwano { @@ -29,53 +30,59 @@ namespace kiwano , next_index_(0) , total_loop_count_(1) , loop_count_(0) + , disposal_type_(DisposalType::Unknown) { } + GifSprite::GifSprite(String const& file_path) + { + Load(file_path); + } + GifSprite::GifSprite(Resource const& res) : GifSprite() { Load(res); } - GifSprite::GifSprite(GifImagePtr image) + GifSprite::GifSprite(GifImage image) { Load(image); } - bool GifSprite::Load(Resource const& res) + bool GifSprite::Load(String const& file_path) { - GifImagePtr image = new (std::nothrow) GifImage; - if (image->Load(res)) - { - return Load(image); - } - return false; + GifImage image = ImageCache::GetInstance()->AddOrGetGifImage(file_path); + return Load(image); } - bool GifSprite::Load(GifImagePtr image) + bool GifSprite::Load(Resource const& res) { - if (image && image_ != image) + GifImage image = ImageCache::GetInstance()->AddOrGetGifImage(res); + return Load(image); + } + + bool GifSprite::Load(GifImage image) + { + if (image.IsValid()) { image_ = image; next_index_ = 0; loop_count_ = 0; + disposal_type_ = DisposalType::None; SetSize( - static_cast(image_->GetWidthInPixels()), - static_cast(image_->GetHeightInPixels()) + static_cast(image_.GetWidthInPixels()), + static_cast(image_.GetHeightInPixels()) ); - if (!frame_rt_) + if (!frame_rt_.IsValid()) { - auto ctx = Renderer::GetInstance()->GetD2DDeviceResources()->GetDeviceContext(); - ThrowIfFailed( - ctx->CreateCompatibleRenderTarget(&frame_rt_) - ); + Renderer::GetInstance()->CreateImageRenderTarget(frame_rt_); } - if (image_->GetFramesCount() > 0) + if (image_.GetFramesCount() > 0) { ComposeNextFrame(); } @@ -84,11 +91,21 @@ namespace kiwano return false; } + void GifSprite::OnRender(Renderer* renderer) + { + if (frame_.IsValid() && renderer->CheckVisibility(size_, transform_matrix_)) + { + PrepareRender(renderer); + + renderer->DrawImage(frame_); + } + } + void GifSprite::Update(Duration dt) { Actor::Update(dt); - if (image_ && animating_) + if (image_.IsValid() && animating_) { frame_elapsed_ += dt; if (frame_delay_ <= frame_elapsed_) @@ -100,84 +117,94 @@ namespace kiwano } } - void GifSprite::OnRender(Renderer* renderer) - { - if (frame_to_render_ && renderer->CheckVisibility(size_, transform_matrix_)) - { - PrepareRender(renderer); - - Rect bounds = GetBounds(); - renderer->DrawBitmap(frame_to_render_, bounds, bounds); - } - } - void GifSprite::RestartAnimation() { animating_ = true; next_index_ = 0; loop_count_ = 0; - image_->SetDisposalType(GifImage::DisposalType::None); + disposal_type_ = DisposalType::None; } void GifSprite::ComposeNextFrame() { - if (frame_rt_) + if (frame_rt_.IsValid()) { - // 找到延迟大于 0 的帧 (0 延迟帧是不可见的中间帧) - HRESULT hr = E_FAIL; do { - hr = image_->DisposeCurrentFrame(frame_rt_); - if (SUCCEEDED(hr)) - { - hr = OverlayNextFrame(); - } - - if (SUCCEEDED(hr)) - { - frame_delay_.SetMilliseconds(static_cast(image_->GetFrameDelay())); - } - } while (SUCCEEDED(hr) && frame_delay_.IsZero() && !IsLastFrame()); + DisposeCurrentFrame(); + OverlayNextFrame(); + } while (frame_delay_.IsZero() && !IsLastFrame()); - animating_ = (SUCCEEDED(hr) && !EndOfAnimation() && image_->GetFramesCount() > 1); + animating_ = (!EndOfAnimation() && image_.GetFramesCount() > 1); } } - HRESULT GifSprite::OverlayNextFrame() + void GifSprite::DisposeCurrentFrame() { - HRESULT hr = image_->GetRawFrame(next_index_); + switch (disposal_type_) + { + case DisposalType::Unknown: + case DisposalType::None: + break; + + case DisposalType::Background: + { + ClearCurrentFrameArea(); + break; + } + + case DisposalType::Previous: + { + RestoreSavedFrame(); + break; + } + + default: + ThrowIfFailed(E_FAIL); + } + } + + void GifSprite::OverlayNextFrame() + { + Image raw_image; + + HRESULT hr = image_.GetRawFrame(next_index_, raw_image, frame_rect_, frame_delay_, disposal_type_); + if (SUCCEEDED(hr)) { - if (image_->GetDisposalType() == GifImage::DisposalType::Previous) + if (disposal_type_ == DisposalType::Previous) { - hr = image_->SaveComposedFrame(frame_rt_); + SaveComposedFrame(); } } if (SUCCEEDED(hr)) { - frame_rt_->BeginDraw(); + frame_rt_.BeginDraw(); if (next_index_ == 0) { // 重新绘制背景 - frame_rt_->Clear(image_->GetBackgroundColor()); + frame_rt_.Clear(image_.GetBackgroundColor()); loop_count_++; } - frame_rt_->DrawBitmap(image_->GetRawFrame().get(), image_->GetFramePosition()); - hr = frame_rt_->EndDraw(); + frame_rt_.DrawImage(raw_image, nullptr, &frame_rect_); + frame_rt_.EndDraw(); } if (SUCCEEDED(hr)) { - frame_to_render_ = nullptr; - hr = frame_rt_->GetBitmap(&frame_to_render_); - } + Image frame_to_render; + frame_rt_.GetOutput(frame_to_render); - if (SUCCEEDED(hr)) - { - next_index_ = (++next_index_) % image_->GetFramesCount(); + hr = frame_to_render.IsValid() ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + frame_ = frame_to_render; + next_index_ = (++next_index_) % image_.GetFramesCount(); + } } if (IsLastFrame() && loop_cb_) @@ -189,7 +216,71 @@ namespace kiwano { done_cb_(); } - return hr; + + ThrowIfFailed(hr); + } + + void GifSprite::SaveComposedFrame() + { + Image frame_to_be_saved; + frame_rt_.GetOutput(frame_to_be_saved); + + HRESULT hr = frame_to_be_saved.IsValid() ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + if (!saved_frame_.IsValid()) + { + auto size = frame_to_be_saved.GetSizeInPixels(); + auto prop = D2D1::BitmapProperties(frame_to_be_saved.GetPixelFormat()); + + ComPtr saved_bitmap; + hr = frame_rt_.GetRenderTarget()->CreateBitmap(D2D1::SizeU(size.x, size.y), prop, &saved_bitmap); + + if (SUCCEEDED(hr)) + { + saved_frame_.SetBitmap(saved_bitmap); + } + } + } + + if (SUCCEEDED(hr)) + { + saved_frame_.CopyFrom(frame_to_be_saved); + } + + ThrowIfFailed(hr); + } + + void GifSprite::RestoreSavedFrame() + { + HRESULT hr = saved_frame_.IsValid() ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + Image frame_to_copy_to; + frame_rt_.GetOutput(frame_to_copy_to); + + hr = frame_to_copy_to.IsValid() ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + frame_to_copy_to.CopyFrom(saved_frame_); + } + } + + ThrowIfFailed(hr); + } + + void GifSprite::ClearCurrentFrameArea() + { + frame_rt_.BeginDraw(); + + frame_rt_.PushClipRect(frame_rect_); + frame_rt_.Clear(image_.GetBackgroundColor()); + frame_rt_.PopClipRect(); + + return frame_rt_.EndDraw(); } } diff --git a/src/kiwano/2d/GifSprite.h b/src/kiwano/2d/GifSprite.h index 0c730c7d..6fec6a03 100644 --- a/src/kiwano/2d/GifSprite.h +++ b/src/kiwano/2d/GifSprite.h @@ -21,26 +21,36 @@ #pragma once #include "Actor.h" #include "../base/Resource.h" -#include "../renderer/Renderer.h" +#include "../renderer/RenderTarget.h" #include "../renderer/GifImage.h" namespace kiwano { + // GIF 精灵 class KGE_API GifSprite : public Actor { public: - using LoopDoneCallback = Function; - using DoneCallback = Function; + using DisposalType = GifImage::DisposalType; + using LoopDoneCallback = Function; + using DoneCallback = Function; GifSprite(); + GifSprite( + String const& file_path + ); + GifSprite( Resource const& res ); GifSprite( - GifImagePtr image + GifImage image + ); + + bool Load( + String const& file_path ); bool Load( @@ -48,7 +58,7 @@ namespace kiwano ); bool Load( - GifImagePtr image + GifImage image ); // 设置 GIF 动画循环次数 @@ -72,27 +82,36 @@ namespace kiwano protected: void Update(Duration dt) override; - void ComposeNextFrame(); - - HRESULT OverlayNextFrame(); - inline bool IsLastFrame() const { return (next_index_ == 0); } inline bool EndOfAnimation() const { return IsLastFrame() && loop_count_ == total_loop_count_ + 1; } - protected: - bool animating_; - int total_loop_count_; - int loop_count_; - unsigned int next_index_; - Duration frame_delay_; - Duration frame_elapsed_; + void ComposeNextFrame(); + void DisposeCurrentFrame(); + + void OverlayNextFrame(); + + void SaveComposedFrame(); + + void RestoreSavedFrame(); + + void ClearCurrentFrameArea(); + + protected: + bool animating_; + int total_loop_count_; + int loop_count_; + UINT next_index_; + Duration frame_delay_; + Duration frame_elapsed_; + DisposalType disposal_type_; LoopDoneCallback loop_cb_; DoneCallback done_cb_; - - GifImagePtr image_; - ComPtr frame_to_render_; - ComPtr frame_rt_; + GifImage image_; + Image frame_; + Rect frame_rect_; + Image saved_frame_; + ImageRenderTarget frame_rt_; }; } diff --git a/src/kiwano/2d/ShapeActor.cpp b/src/kiwano/2d/ShapeActor.cpp index 3cab94c1..cd6963b3 100644 --- a/src/kiwano/2d/ShapeActor.cpp +++ b/src/kiwano/2d/ShapeActor.cpp @@ -28,7 +28,7 @@ namespace kiwano : fill_color_(Color::White) , stroke_color_(Color(Color::Black, 0)) , stroke_width_(1.f) - , outline_join_(StrokeStyle::Miter) + , stroke_style_(StrokeStyle::Miter) { } @@ -73,9 +73,9 @@ namespace kiwano stroke_width_ = std::max(width, 0.f); } - void ShapeActor::SetOutlineJoinStyle(StrokeStyle outline_join) + void ShapeActor::SetStrokeStyle(StrokeStyle stroke_style) { - outline_join_ = outline_join; + stroke_style_ = stroke_style; } void ShapeActor::OnRender(Renderer* renderer) @@ -93,7 +93,7 @@ namespace kiwano geo_, stroke_color_, stroke_width_, - outline_join_ + stroke_style_ ); } } diff --git a/src/kiwano/2d/ShapeActor.h b/src/kiwano/2d/ShapeActor.h index 940a34df..7ba2a3c4 100644 --- a/src/kiwano/2d/ShapeActor.h +++ b/src/kiwano/2d/ShapeActor.h @@ -46,8 +46,8 @@ namespace kiwano // 获取线条宽度 float GetStrokeWidth() const { return stroke_width_; } - // 获取线条相交样式 - StrokeStyle SetOutlineJoinStyle() const { return outline_join_; } + // 获取线条样式 + StrokeStyle SetStrokeStyle() const { return stroke_style_; } // 获取边界 Rect GetBounds() const override; @@ -70,9 +70,9 @@ namespace kiwano float width ); - // 设置线条相交样式 - void SetOutlineJoinStyle( - StrokeStyle outline_join + // 设置线条样式 + void SetStrokeStyle( + StrokeStyle stroke_style ); // 设置形状 @@ -87,7 +87,7 @@ namespace kiwano Color fill_color_; Color stroke_color_; float stroke_width_; - StrokeStyle outline_join_; + StrokeStyle stroke_style_; Geometry geo_; }; diff --git a/src/kiwano/2d/Sprite.cpp b/src/kiwano/2d/Sprite.cpp index ac4ffcba..688faf90 100644 --- a/src/kiwano/2d/Sprite.cpp +++ b/src/kiwano/2d/Sprite.cpp @@ -24,21 +24,29 @@ namespace kiwano { Sprite::Sprite() - : frame_(nullptr) { } + Sprite::Sprite(String const& file_path) + { + Load(file_path); + } + + Sprite::Sprite(String const& file_path, Rect const& crop_rect) + { + Load(file_path); + SetCropRect(crop_rect); + } + Sprite::Sprite(Resource const& res) - : frame_(nullptr) { Load(res); } Sprite::Sprite(Resource const& res, const Rect& crop_rect) - : frame_(nullptr) { Load(res); - Crop(crop_rect); + SetCropRect(crop_rect); } Sprite::Sprite(FramePtr frame) @@ -51,6 +59,17 @@ namespace kiwano { } + bool Sprite::Load(String const& file_path) + { + FramePtr frame = new (std::nothrow) Frame; + if (frame->Load(file_path)) + { + SetFrame(frame); + return true; + } + return false; + } + bool Sprite::Load(Resource const& res) { FramePtr frame = new (std::nothrow) Frame; @@ -62,11 +81,11 @@ namespace kiwano return false; } - void Sprite::Crop(const Rect& crop_rect) + void Sprite::SetCropRect(const Rect& crop_rect) { if (frame_) { - frame_->Crop(crop_rect); + frame_->SetCropRect(crop_rect); SetSize(frame_->GetWidth(), frame_->GetHeight()); } } @@ -89,7 +108,7 @@ namespace kiwano { PrepareRender(renderer); - renderer->DrawBitmap(frame_->GetImage()->GetBitmap(), frame_->GetCropRect(), GetBounds()); + renderer->DrawImage(frame_->GetImage(), &frame_->GetCropRect(), nullptr); } } } diff --git a/src/kiwano/2d/Sprite.h b/src/kiwano/2d/Sprite.h index 01940df6..8138f5e2 100644 --- a/src/kiwano/2d/Sprite.h +++ b/src/kiwano/2d/Sprite.h @@ -32,10 +32,19 @@ namespace kiwano Sprite(); explicit Sprite( - Resource const& res + String const& file_path + ); + + Sprite( + String const& file_path, + Rect const& crop_rect /* 裁剪矩形 */ ); explicit Sprite( + Resource const& res + ); + + Sprite( Resource const& res, Rect const& crop_rect /* 裁剪矩形 */ ); @@ -46,13 +55,18 @@ namespace kiwano virtual ~Sprite(); + // 加载图像 + bool Load( + String const& file_path + ); + // 加载图像资源 bool Load( Resource const& res ); // 裁剪矩形 - void Crop( + void SetCropRect( const Rect& crop_rect ); diff --git a/src/kiwano/2d/Text.cpp b/src/kiwano/2d/Text.cpp index 84fae7f8..dc460d0b 100644 --- a/src/kiwano/2d/Text.cpp +++ b/src/kiwano/2d/Text.cpp @@ -44,6 +44,7 @@ namespace kiwano : font_(text_default_font) , style_(text_default_style) , layout_dirty_(false) + , format_dirty_(false) { } @@ -74,96 +75,6 @@ namespace kiwano { } - String const& Text::GetText() const - { - return text_; - } - - const Font& Text::GetFont() const - { - return font_; - } - - const TextStyle& Text::GetStyle() const - { - return style_; - } - - String const& Text::GetFontFamily() const - { - return font_.family; - } - - float Text::GetFontSize() const - { - return font_.size; - } - - unsigned int Text::GetFontWeight() const - { - return font_.weight; - } - - const Color& Text::GetColor() const - { - return style_.color; - } - - const Color& Text::GetOutlineColor() const - { - return style_.outline_color; - } - - float Text::GetOutlineWidth() const - { - return style_.outline_width; - } - - StrokeStyle Text::GetOutlineStroke() const - { - return style_.outline_stroke; - } - - int Text::GetLineCount() - { - UpdateLayout(); - if (text_layout_) - { - DWRITE_TEXT_METRICS metrics; - if (SUCCEEDED(text_layout_->GetMetrics(&metrics))) - { - return static_cast(metrics.lineCount); - } - } - return 0; - } - - Size const& Text::GetLayoutSize() const - { - UpdateLayout(); - return layout_size_; - } - - bool Text::IsItalic() const - { - return font_.italic; - } - - bool Text::HasStrikethrough() const - { - return style_.strikethrough; - } - - bool Text::HasUnderline() const - { - return style_.underline; - } - - bool Text::HasOutline() const - { - return style_.outline; - } - void Text::SetText(String const& text) { text_ = text; @@ -179,7 +90,7 @@ namespace kiwano void Text::SetFont(const Font & font) { font_ = font; - layout_dirty_ = true; + format_dirty_ = true; } void Text::SetFontFamily(String const& family) @@ -187,7 +98,7 @@ namespace kiwano if (font_.family != family) { font_.family = family; - layout_dirty_ = true; + format_dirty_ = true; } } @@ -196,7 +107,7 @@ namespace kiwano if (font_.size != size) { font_.size = size; - layout_dirty_ = true; + format_dirty_ = true; } } @@ -205,7 +116,7 @@ namespace kiwano if (font_.weight != weight) { font_.weight = weight; - layout_dirty_ = true; + format_dirty_ = true; } } @@ -219,16 +130,7 @@ namespace kiwano if (font_.italic != val) { font_.italic = val; - layout_dirty_ = true; - } - } - - void Text::SetWrapEnabled(bool wrap) - { - if (style_.wrap != wrap) - { - style_.wrap = wrap; - layout_dirty_ = true; + format_dirty_ = true; } } @@ -236,12 +138,12 @@ namespace kiwano { if (style_.wrap_width != wrap_width) { - style_.wrap_width = std::max(wrap_width, 0.f); + style_.wrap_width = wrap_width; layout_dirty_ = true; } } - void Text::SetLineacingPtr(float line_spacing) + void Text::SetLineSpacing(float line_spacing) { if (style_.line_spacing != line_spacing) { @@ -301,51 +203,26 @@ namespace kiwano { UpdateLayout(); - if (text_layout_ && renderer->CheckVisibility(layout_size_, transform_matrix_)) + if (text_layout_ && renderer->CheckVisibility(size_, transform_matrix_)) { PrepareRender(renderer); - renderer->SetTextStyle( - GetDisplayedOpacity(), - style_.color, - style_.outline, - style_.outline_color, - style_.outline_width, - style_.outline_stroke - ); - renderer->DrawTextLayout(text_layout_); + renderer->DrawTextLayout(text_layout_); } } - void Text::UpdateLayout() const + void Text::UpdateLayout() { - if (!layout_dirty_) - return; + if (format_dirty_) + { + format_dirty_ = false; + text_layout_.Update(font_); + } - layout_dirty_ = false; - text_format_ = nullptr; - text_layout_ = nullptr; - - if (text_.empty()) - return; - - auto renderer = Renderer::GetInstance(); - - ThrowIfFailed( - renderer->GetD2DDeviceResources()->CreateTextFormat( - text_format_, - font_, - style_ - ) - ); - - ThrowIfFailed( - renderer->GetD2DDeviceResources()->CreateTextLayout( - text_layout_, - layout_size_, - text_, - text_format_, - style_ - ) - ); + if (layout_dirty_) + { + layout_dirty_ = false; + text_layout_.Update(text_, style_); + SetSize(text_layout_.GetLayoutSize()); + } } } diff --git a/src/kiwano/2d/Text.h b/src/kiwano/2d/Text.h index d2c07ce3..1877b711 100644 --- a/src/kiwano/2d/Text.h +++ b/src/kiwano/2d/Text.h @@ -20,9 +20,7 @@ #pragma once #include "Actor.h" -#include "Font.hpp" -#include "TextStyle.hpp" -#include +#include "../renderer/TextLayout.h" namespace kiwano { @@ -56,52 +54,16 @@ namespace kiwano virtual ~Text(); // 获取文本 - String const& GetText() const; + inline String const& GetText() const { return text_; } // 获取字体 - const Font& GetFont() const; + inline Font GetFont() const { return font_; } // 获取文本样式 - const TextStyle& GetStyle() const; + inline TextStyle GetStyle() const { return style_; } - // 获取字体族 - String const& GetFontFamily() const; - - // 获取当前字号 - float GetFontSize() const; - - // 获取当前字体粗细值 - unsigned int GetFontWeight() const; - - // 获取文字颜色 - const Color& GetColor() const; - - // 获取描边颜色 - const Color& GetOutlineColor() const; - - // 获取描边线宽 - float GetOutlineWidth() const; - - // 获取描边线相交样式 - StrokeStyle GetOutlineStroke() const; - - // 获取文本显示行数 - int GetLineCount(); - - // 获取文字布局大小 - Size const& GetLayoutSize() const; - - // 是否是斜体 - bool IsItalic() const; - - // 是否显示删除线 - bool HasStrikethrough() const; - - // 是否显示下划线 - bool HasUnderline() const; - - // 是否显示描边 - bool HasOutline() const; + // 获取文本布局 + inline TextLayout GetLayout() const { return text_layout_; } // 设置文本 void SetText( @@ -143,18 +105,13 @@ namespace kiwano bool val ); - // 打开或关闭文本自动换行(默认为关闭) - void SetWrapEnabled( - bool wrap - ); - // 设置文本自动换行的宽度(默认为 0) void SetWrapWidth( float wrap_width ); // 设置行间距(默认为 0) - void SetLineacingPtr( + void SetLineSpacing( float line_spacing ); @@ -206,16 +163,14 @@ namespace kiwano void OnRender(Renderer* renderer) override; protected: - void UpdateLayout() const; + void UpdateLayout(); protected: + bool format_dirty_; + bool layout_dirty_; + TextLayout text_layout_; String text_; Font font_; TextStyle style_; - - mutable bool layout_dirty_; - mutable Size layout_size_; - mutable ComPtr text_format_; - mutable ComPtr text_layout_; }; } diff --git a/src/kiwano/2d/TextStyle.hpp b/src/kiwano/2d/TextStyle.hpp index 0cc3c51b..1e5a299d 100644 --- a/src/kiwano/2d/TextStyle.hpp +++ b/src/kiwano/2d/TextStyle.hpp @@ -37,7 +37,6 @@ namespace kiwano public: Color color; // 颜色 TextAlign alignment; // 对齐方式 - bool wrap; // 打开自动换行 float wrap_width; // 自动换行宽度 float line_spacing; // 行间距 bool underline; // 下划线 @@ -51,7 +50,6 @@ namespace kiwano TextStyle() : color(Color::White) , alignment(TextAlign::Left) - , wrap(false) , wrap_width(0.f) , line_spacing(0.f) , underline(false) @@ -65,7 +63,6 @@ namespace kiwano TextStyle( Color color, TextAlign alignment = TextAlign::Left, - bool wrap = false, float wrap_width = 0.f, float line_spacing = 0.f, bool underline = false, @@ -77,7 +74,6 @@ namespace kiwano ) : color(color) , alignment(alignment) - , wrap(wrap) , wrap_width(wrap_width) , line_spacing(line_spacing) , underline(underline) @@ -88,4 +84,4 @@ namespace kiwano , outline_stroke(outline_stroke) {} }; -} \ No newline at end of file +} diff --git a/src/kiwano/2d/Transition.cpp b/src/kiwano/2d/Transition.cpp index af2876c5..8fe41b9c 100644 --- a/src/kiwano/2d/Transition.cpp +++ b/src/kiwano/2d/Transition.cpp @@ -39,10 +39,8 @@ namespace kiwano , window_size_() , out_scene_(nullptr) , in_scene_(nullptr) - , out_layer_(nullptr) - , in_layer_(nullptr) - , out_layer_prop_() - , in_layer_prop_() + , out_layer_() + , in_layer_() { } @@ -74,7 +72,8 @@ namespace kiwano } window_size_ = Renderer::GetInstance()->GetOutputSize(); - out_layer_prop_ = in_layer_prop_ = LayerProperties{ Rect(Point(), window_size_),1.f }; + out_layer_.SetAreaRect(Rect{ Point(), window_size_ }); + in_layer_.SetAreaRect(Rect{ Point(), window_size_ }); } void Transition::Update(Duration dt) @@ -99,30 +98,26 @@ namespace kiwano { if (out_scene_) { - renderer->PushClip( - out_scene_->GetTransformMatrix(), - window_size_ - ); - renderer->PushLayer(out_layer_, out_layer_prop_); + renderer->SetTransform(out_scene_->GetTransformMatrix()); + renderer->PushClipRect(Rect{ Point{}, window_size_ }); + renderer->PushLayer(out_layer_); out_scene_->Render(renderer); renderer->PopLayer(); - renderer->PopClip(); + renderer->PopClipRect(); } if (in_scene_) { - renderer->PushClip( - in_scene_->GetTransformMatrix(), - window_size_ - ); - renderer->PushLayer(in_layer_, in_layer_prop_); + renderer->SetTransform(in_scene_->GetTransformMatrix()); + renderer->PushClipRect(Rect{ Point{}, window_size_ }); + renderer->PushLayer(in_layer_); in_scene_->Render(renderer); renderer->PopLayer(); - renderer->PopClip(); + renderer->PopClipRect(); } } @@ -145,7 +140,7 @@ namespace kiwano { Transition::Init(prev, next); - in_layer_prop_.opacity = 0; + in_layer_.SetOpacity(0.f); } void BoxTransition::Update(Duration dt) @@ -154,22 +149,26 @@ namespace kiwano if (process_ < .5f) { - out_layer_prop_.area = Rect( - window_size_.x * process_, - window_size_.y * process_, - window_size_.x * (1 - process_ * 2), - window_size_.y * (1 - process_ * 2) + out_layer_.SetAreaRect( + Rect( + window_size_.x * process_, + window_size_.y * process_, + window_size_.x * (1 - process_ * 2), + window_size_.y * (1 - process_ * 2) + ) ); } else { - out_layer_prop_.opacity = 0; - in_layer_prop_.opacity = 1; - in_layer_prop_.area = Rect( - window_size_.x * (1 - process_), - window_size_.y * (1 - process_), - window_size_.x * (2 * process_ - 1), - window_size_.y * (2 * process_ - 1) + out_layer_.SetOpacity(0.f); + in_layer_.SetOpacity(1.f); + in_layer_.SetAreaRect( + Rect( + window_size_.x * (1 - process_), + window_size_.y * (1 - process_), + window_size_.x * (2 * process_ - 1), + window_size_.y * (2 * process_ - 1) + ) ); } } @@ -187,16 +186,16 @@ namespace kiwano { Transition::Init(prev, next); - out_layer_prop_.opacity = 1; - in_layer_prop_.opacity = 0; + out_layer_.SetOpacity(1.f); + in_layer_.SetOpacity(0.f); } void EmergeTransition::Update(Duration dt) { Transition::Update(dt); - out_layer_prop_.opacity = 1 - process_; - in_layer_prop_.opacity = process_; + out_layer_.SetOpacity(1 - process_); + in_layer_.SetOpacity(process_); } //------------------------------------------------------- @@ -212,8 +211,8 @@ namespace kiwano { Transition::Init(prev, next); - out_layer_prop_.opacity = 1; - in_layer_prop_.opacity = 0; + out_layer_.SetOpacity(1.f); + in_layer_.SetOpacity(0.f); } void FadeTransition::Update(Duration dt) @@ -222,13 +221,13 @@ namespace kiwano if (process_ < 0.5) { - out_layer_prop_.opacity = 1 - process_ * 2; - in_layer_prop_.opacity = 0; + out_layer_.SetOpacity(1 - process_ * 2); + in_layer_.SetOpacity(0.f); } else { - out_layer_prop_.opacity = 0; - in_layer_prop_.opacity = (process_ - 0.5f) * 2; + out_layer_.SetOpacity(0.f); + in_layer_.SetOpacity((process_ - 0.5f) * 2); } } @@ -340,7 +339,7 @@ namespace kiwano in_scene_->SetAnchor(0.5f, 0.5f); } - in_layer_prop_.opacity = 0; + in_layer_.SetOpacity(0.f); } void RotationTransition::Update(Duration dt) @@ -361,8 +360,8 @@ namespace kiwano { if (in_scene_) { - out_layer_prop_.opacity = 0; - in_layer_prop_.opacity = 1; + out_layer_.SetOpacity(0.f); + in_layer_.SetOpacity(1.f); auto transform = in_scene_->GetTransform(); transform.scale = Point{ (process_ - .5f) * 2, (process_ - .5f) * 2 }; diff --git a/src/kiwano/2d/Transition.h b/src/kiwano/2d/Transition.h index 63cd9642..a70b89cd 100644 --- a/src/kiwano/2d/Transition.h +++ b/src/kiwano/2d/Transition.h @@ -20,7 +20,7 @@ #pragma once #include "include-forwards.h" -#include +#include "../renderer/LayerArea.h" namespace kiwano { @@ -29,7 +29,7 @@ namespace kiwano // 舞台过渡 class KGE_API Transition - : public Object + : public ObjectBase { friend class Director; @@ -57,17 +57,15 @@ namespace kiwano virtual void Reset() { }; protected: - bool done_; - float process_; - Duration duration_; - Duration delta_; - Size window_size_; - StagePtr out_scene_; - StagePtr in_scene_; - ComPtr out_layer_; - ComPtr in_layer_; - LayerProperties out_layer_prop_; - LayerProperties in_layer_prop_; + bool done_; + float process_; + Duration duration_; + Duration delta_; + Size window_size_; + StagePtr out_scene_; + StagePtr in_scene_; + LayerArea out_layer_; + LayerArea in_layer_; }; diff --git a/src/kiwano/2d/action/Action.h b/src/kiwano/2d/action/Action.h index 427bb215..ce7eb0c2 100644 --- a/src/kiwano/2d/action/Action.h +++ b/src/kiwano/2d/action/Action.h @@ -28,7 +28,7 @@ namespace kiwano class ActionManager; class KGE_API Action - : public Object + : public ObjectBase , protected intrusive_list_item { friend class ActionManager; diff --git a/src/kiwano/2d/action/ActionGroup.cpp b/src/kiwano/2d/action/ActionGroup.cpp index fd1a646a..0cd36d40 100644 --- a/src/kiwano/2d/action/ActionGroup.cpp +++ b/src/kiwano/2d/action/ActionGroup.cpp @@ -45,7 +45,7 @@ namespace kiwano void ActionGroup::Init(ActorPtr target) { - if (actions_.is_empty()) + if (actions_.item_empty()) { Done(); return; @@ -135,7 +135,7 @@ namespace kiwano ActionPtr ActionGroup::Reverse() const { auto group = new (std::nothrow) ActionGroup(); - if (group && !actions_.is_empty()) + if (group && !actions_.item_empty()) { for (auto action = actions_.last_item(); action; action = action->prev_item()) { diff --git a/src/kiwano/2d/action/ActionManager.cpp b/src/kiwano/2d/action/ActionManager.cpp index 3428bd8a..2a0c5ac5 100644 --- a/src/kiwano/2d/action/ActionManager.cpp +++ b/src/kiwano/2d/action/ActionManager.cpp @@ -26,7 +26,7 @@ namespace kiwano { void ActionManager::UpdateActions(ActorPtr target, Duration dt) { - if (actions_.is_empty() || !target) + if (actions_.item_empty() || !target) return; ActionPtr next; @@ -55,7 +55,7 @@ namespace kiwano ActionPtr ActionManager::GetAction(String const & name) { - if (actions_.is_empty()) + if (actions_.item_empty()) return nullptr; for (auto action = actions_.first_item().get(); action; action = action->next_item().get()) @@ -66,7 +66,7 @@ namespace kiwano void ActionManager::ResumeAllActions() { - if (actions_.is_empty()) + if (actions_.item_empty()) return; for (auto action = actions_.first_item().get(); action; action = action->next_item().get()) @@ -77,7 +77,7 @@ namespace kiwano void ActionManager::PauseAllActions() { - if (actions_.is_empty()) + if (actions_.item_empty()) return; for (auto action = actions_.first_item().get(); action; action = action->next_item().get()) @@ -88,7 +88,7 @@ namespace kiwano void ActionManager::StopAllActions() { - if (actions_.is_empty()) + if (actions_.item_empty()) return; for (auto action = actions_.first_item().get(); action; action = action->next_item().get()) diff --git a/src/kiwano/2d/include-forwards.h b/src/kiwano/2d/include-forwards.h index 98f832e4..edbbf0ae 100644 --- a/src/kiwano/2d/include-forwards.h +++ b/src/kiwano/2d/include-forwards.h @@ -19,15 +19,14 @@ // THE SOFTWARE. #pragma once -#include "Color.h" #include "../core/core.h" #include "../base/time.h" #include "../base/RefCounter.hpp" #include "../base/SmartPtr.hpp" -#include "../base/ComPtr.hpp" -#include "../base/Object.h" -#include "../math/helper.h" +#include "../base/ObjectBase.h" #include "../base/types.h" +#include "../math/math.h" +#include "../renderer/Color.h" namespace kiwano { diff --git a/src/kiwano/base/AsyncTask.h b/src/kiwano/base/AsyncTask.h index f1d31efa..15551475 100644 --- a/src/kiwano/base/AsyncTask.h +++ b/src/kiwano/base/AsyncTask.h @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include "Object.h" +#include "ObjectBase.h" #include #include @@ -31,7 +31,7 @@ namespace kiwano typedef Function AsyncTaskCallback; class AsyncTask - : public Object + : public ObjectBase { public: AsyncTask(); diff --git a/src/kiwano/base/Director.cpp b/src/kiwano/base/Director.cpp index 89ee9fe4..39d5d36e 100644 --- a/src/kiwano/base/Director.cpp +++ b/src/kiwano/base/Director.cpp @@ -146,7 +146,9 @@ namespace kiwano if (render_border_enabled_) { if (curr_scene_) + { curr_scene_->RenderBorder(); + } } } diff --git a/src/kiwano/base/EventDispatcher.cpp b/src/kiwano/base/EventDispatcher.cpp index 8dc93e26..d46fbb3b 100644 --- a/src/kiwano/base/EventDispatcher.cpp +++ b/src/kiwano/base/EventDispatcher.cpp @@ -25,7 +25,7 @@ namespace kiwano { void EventDispatcher::Dispatch(Event& evt) { - if (listeners_.is_empty()) + if (listeners_.item_empty()) return; EventListenerPtr next; diff --git a/src/kiwano/base/EventListener.h b/src/kiwano/base/EventListener.h index 40ed7aa0..f07e83f3 100644 --- a/src/kiwano/base/EventListener.h +++ b/src/kiwano/base/EventListener.h @@ -21,7 +21,7 @@ #pragma once #include "../core/core.h" #include "../base/SmartPtr.hpp" -#include "Object.h" +#include "ObjectBase.h" #include "Event.hpp" namespace kiwano @@ -34,7 +34,7 @@ namespace kiwano // 事件监听器 class KGE_API EventListener - : public Object + : public ObjectBase , protected intrusive_list_item { friend class EventDispatcher; diff --git a/src/kiwano/base/Input.h b/src/kiwano/base/Input.h index e23b3796..2cbc24bc 100644 --- a/src/kiwano/base/Input.h +++ b/src/kiwano/base/Input.h @@ -21,7 +21,7 @@ #pragma once #include "../macros.h" #include "../core/core.h" -#include "../math/helper.h" +#include "../math/math.h" #include "keys.hpp" #include "Component.h" diff --git a/src/kiwano/base/Object.cpp b/src/kiwano/base/ObjectBase.cpp similarity index 78% rename from src/kiwano/base/Object.cpp rename to src/kiwano/base/ObjectBase.cpp index 888e9108..b3020125 100644 --- a/src/kiwano/base/Object.cpp +++ b/src/kiwano/base/ObjectBase.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "Object.h" +#include "ObjectBase.h" #include "Logger.h" #include @@ -27,12 +27,12 @@ namespace kiwano namespace { bool tracing_leaks = false; - Vector tracing_objects; + Vector tracing_objects; } - unsigned int Object::last_object_id = 0; + unsigned int ObjectBase::last_object_id = 0; - Object::Object() + ObjectBase::ObjectBase() : tracing_leak_(false) , user_data_(nullptr) , name_(nullptr) @@ -40,34 +40,34 @@ namespace kiwano { #ifdef KGE_DEBUG - Object::__AddObjectToTracingList(this); + ObjectBase::__AddObjectToTracingList(this); #endif } - Object::~Object() + ObjectBase::~ObjectBase() { if (name_) delete name_; #ifdef KGE_DEBUG - Object::__RemoveObjectFromTracingList(this); + ObjectBase::__RemoveObjectFromTracingList(this); #endif } - void * Object::GetUserData() const + void * ObjectBase::GetUserData() const { return user_data_; } - void Object::SetUserData(void * data) + void ObjectBase::SetUserData(void * data) { user_data_ = data; } - void Object::SetName(String const & name) + void ObjectBase::SetName(String const & name) { if (IsName(name)) return; @@ -88,29 +88,29 @@ namespace kiwano *name_ = name; } - String Object::DumpObject() + String ObjectBase::DumpObject() { String name = kiwano::string_to_wide(typeid(*this).name()); return String::format(L"{ class=\"%s\" id=%d refcount=%d name=\"%s\" }", name.c_str(), GetObjectID(), GetRefCount(), GetName().c_str()); } - bool Object::IsTracingLeaks() + bool ObjectBase::IsTracingLeaks() { return tracing_leaks; } - void Object::StartTracingLeaks() + void ObjectBase::StartTracingLeaks() { tracing_leaks = true; } - void Object::StopTracingLeaks() + void ObjectBase::StopTracingLeaks() { tracing_leaks = false; } - void Object::DumpTracingObjects() + void ObjectBase::DumpTracingObjects() { KGE_LOG(L"-------------------------- All Objects --------------------------"); for (const auto object : tracing_objects) @@ -120,12 +120,12 @@ namespace kiwano KGE_LOG(L"------------------------- Total size: %d -------------------------", tracing_objects.size()); } - Vector& kiwano::Object::__GetTracingObjects() + Vector& kiwano::ObjectBase::__GetTracingObjects() { return tracing_objects; } - void Object::__AddObjectToTracingList(Object * obj) + void ObjectBase::__AddObjectToTracingList(ObjectBase * obj) { #ifdef KGE_DEBUG @@ -138,7 +138,7 @@ namespace kiwano #endif } - void Object::__RemoveObjectFromTracingList(Object * obj) + void ObjectBase::__RemoveObjectFromTracingList(ObjectBase * obj) { #ifdef KGE_DEBUG diff --git a/src/kiwano/base/Object.h b/src/kiwano/base/ObjectBase.h similarity index 88% rename from src/kiwano/base/Object.h rename to src/kiwano/base/ObjectBase.h index 435d0cf0..e527fbf3 100644 --- a/src/kiwano/base/Object.h +++ b/src/kiwano/base/ObjectBase.h @@ -26,15 +26,15 @@ namespace kiwano { - KGE_DECLARE_SMART_PTR(Object); + KGE_DECLARE_SMART_PTR(ObjectBase); - class KGE_API Object + class KGE_API ObjectBase : public RefCounter { public: - Object(); + ObjectBase(); - virtual ~Object(); + virtual ~ObjectBase(); void* GetUserData() const; @@ -60,11 +60,11 @@ namespace kiwano static void DumpTracingObjects(); public: - static Vector& __GetTracingObjects(); + static Vector& __GetTracingObjects(); - static void __AddObjectToTracingList(Object*); + static void __AddObjectToTracingList(ObjectBase*); - static void __RemoveObjectFromTracingList(Object*); + static void __RemoveObjectFromTracingList(ObjectBase*); private: bool tracing_leak_; diff --git a/src/kiwano/base/Resource.cpp b/src/kiwano/base/Resource.cpp index ee6c84e7..1ea7ee67 100644 --- a/src/kiwano/base/Resource.cpp +++ b/src/kiwano/base/Resource.cpp @@ -21,116 +21,63 @@ #include "Resource.h" #include "../base/Logger.h" +#include namespace kiwano { - Resource::Resource(LPCWSTR file_name) - : type_(Type::File) - , bin_name_(nullptr) - , bin_type_(nullptr) + Resource::Resource() + : id_(0) + , type_(nullptr) { - if (file_name) - file_name_ = new (std::nothrow) String(file_name); + } - Resource::Resource(String const& file_name) - : type_(Type::File) - , bin_name_(nullptr) - , bin_type_(nullptr) - { - if (!file_name.empty()) - file_name_ = new (std::nothrow) String(file_name); - } - - Resource::Resource(LPCWSTR name, LPCWSTR type) - : type_(Type::Binary) - , bin_name_(name) - , bin_type_(type) + Resource::Resource(UINT id, LPCWSTR type) + : id_(id) + , type_(type) { } - Resource::Resource(Resource const & rhs) + Resource::Data Resource::GetData() const { - operator=(rhs); - } - - Resource::~Resource() - { - if (IsFileType() && file_name_) - delete file_name_; - } - - size_t Resource::GetHashCode() const - { - if (type_ == Type::File) - return GetFileName().hash(); - return std::hash{}(bin_name_); - } - - Resource & Resource::operator=(Resource const & rhs) - { - if (&rhs != this) + do { - if (IsFileType() && file_name_) + if (data_.buffer && data_.size) { - delete file_name_; - file_name_ = nullptr; + break; } - type_ = rhs.type_; - if (IsFileType()) + HRSRC res_info = FindResourceW(nullptr, MAKEINTRESOURCE(id_), type_); + if (res_info == nullptr) { - if (rhs.file_name_) - { - file_name_ = new (std::nothrow) String(*rhs.file_name_); - } + KGE_ERROR_LOG(L"FindResource failed"); + break; } - else + + HGLOBAL res_data = LoadResource(nullptr, res_info); + if (res_data == nullptr) { - bin_name_ = rhs.bin_name_; - bin_type_ = rhs.bin_type_; + KGE_ERROR_LOG(L"LoadResource failed"); + break; } - } - return *this; - } - bool Resource::Load(LPVOID& buffer, DWORD& buffer_size) const - { - if (type_ != Type::Binary) - { - KGE_ERROR_LOG(L"Only binary resource can be loaded"); - return false; - } + DWORD size = SizeofResource(nullptr, res_info); + if (size == 0) + { + KGE_ERROR_LOG(L"SizeofResource failed"); + break; + } - HGLOBAL res_data; - HRSRC res_info; + LPVOID buffer = LockResource(res_data); + if (buffer == nullptr) + { + KGE_ERROR_LOG(L"LockResource failed"); + break; + } - res_info = FindResourceW(nullptr, bin_name_, bin_type_); - if (res_info == nullptr) - { - KGE_ERROR_LOG(L"FindResource failed"); - return false; - } + data_.buffer = static_cast(buffer); + data_.size = static_cast(size); + } while (0); - res_data = LoadResource(nullptr, res_info); - if (res_data == nullptr) - { - KGE_ERROR_LOG(L"LoadResource failed"); - return false; - } - - buffer_size = SizeofResource(nullptr, res_info); - if (buffer_size == 0) - { - KGE_ERROR_LOG(L"SizeofResource failed"); - return false; - } - - buffer = LockResource(res_data); - if (buffer == nullptr) - { - KGE_ERROR_LOG(L"LockResource failed"); - return false; - } - return true; + return data_; } } diff --git a/src/kiwano/base/Resource.h b/src/kiwano/base/Resource.h index 4c99c34a..32f990d3 100644 --- a/src/kiwano/base/Resource.h +++ b/src/kiwano/base/Resource.h @@ -26,63 +26,43 @@ namespace kiwano { // 资源 // - // 资源可以是文件类型,也可以是保存在 exe 中的二进制资源 + // 资源是保存在 exe 中的二进制数据 // 例如, 一份音频资源的类型为 L"WAVE", 名称标识符为 IDR_WAVE_1, - // 那么可以这样指定该资源: Resource res(MAKEINTRESOURCE(IDR_WAVE_1), L"WAVE"); + // 那么可以这样指定该资源: Resource(IDR_WAVE_1, L"WAVE"); // // 了解资源的更多信息: https://docs.microsoft.com/en-us/windows/desktop/menurc/resources // class KGE_API Resource { - enum class Type { File, Binary }; - public: - Resource( - LPCWSTR file_name /* 文件路径 */ - ); + // 二进制数据 + struct Data + { + void* buffer; + UINT32 size; + + inline Data() : buffer(nullptr), size(0) {} + + inline operator bool() const { return buffer && size; } + }; + + Resource(); Resource( - String const& file_name /* 文件路径 */ + UINT id, /* 资源名称 */ + LPCWSTR type /* 资源类型 */ ); - Resource( - LPCWSTR name, /* 资源名称 */ - LPCWSTR type /* 资源类型 */ - ); + // 获取二进制数据 + Resource::Data GetData() const; - Resource( - Resource const& rhs - ); + inline UINT GetId() const { return id_; } - virtual ~Resource(); - - inline bool IsFileType() const { return type_ == Type::File; } - - inline String GetFileName() const { if (file_name_) return *file_name_; return String(); } - - bool Load( - LPVOID& buffer, - DWORD& buffer_size - ) const; - - size_t GetHashCode() const; - - Resource& operator= (Resource const& rhs); + inline LPCWSTR GetType() const { return type_; } private: - Type type_; - union - { - struct - { - String* file_name_; - }; - - struct - { - LPCWSTR bin_name_; - LPCWSTR bin_type_; - }; - }; + UINT id_; + LPCWSTR type_; + mutable Resource::Data data_; }; } diff --git a/src/kiwano/base/Timer.h b/src/kiwano/base/Timer.h index a3ba1a2f..e82211dd 100644 --- a/src/kiwano/base/Timer.h +++ b/src/kiwano/base/Timer.h @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include "Object.h" +#include "ObjectBase.h" #include "time.h" #include @@ -31,7 +31,7 @@ namespace kiwano // 定时任务 class KGE_API Timer - : public Object + : public ObjectBase , protected intrusive_list_item { friend class TimerManager; diff --git a/src/kiwano/base/TimerManager.cpp b/src/kiwano/base/TimerManager.cpp index 1476e157..0380baf8 100644 --- a/src/kiwano/base/TimerManager.cpp +++ b/src/kiwano/base/TimerManager.cpp @@ -25,7 +25,7 @@ namespace kiwano { void TimerManager::UpdateTimers(Duration dt) { - if (timers_.is_empty()) + if (timers_.item_empty()) return; TimerPtr next; @@ -54,7 +54,7 @@ namespace kiwano void TimerManager::StopTimers(String const& name) { - if (timers_.is_empty()) + if (timers_.item_empty()) return; for (auto timer = timers_.first_item().get(); timer; timer = timer->next_item().get()) @@ -68,7 +68,7 @@ namespace kiwano void TimerManager::StartTimers(String const& name) { - if (timers_.is_empty()) + if (timers_.item_empty()) return; for (auto timer = timers_.first_item().get(); timer; timer = timer->next_item().get()) @@ -82,7 +82,7 @@ namespace kiwano void TimerManager::RemoveTimers(String const& name) { - if (timers_.is_empty()) + if (timers_.item_empty()) return; TimerPtr next; @@ -98,7 +98,7 @@ namespace kiwano void TimerManager::StopAllTimers() { - if (timers_.is_empty()) + if (timers_.item_empty()) return; for (auto timer = timers_.first_item().get(); timer; timer = timer->next_item().get()) @@ -109,7 +109,7 @@ namespace kiwano void TimerManager::StartAllTimers() { - if (timers_.is_empty()) + if (timers_.item_empty()) return; for (auto timer = timers_.first_item().get(); timer; timer = timer->next_item().get()) diff --git a/src/kiwano/base/Window.cpp b/src/kiwano/base/Window.cpp index bc2a5f80..00afa86c 100644 --- a/src/kiwano/base/Window.cpp +++ b/src/kiwano/base/Window.cpp @@ -66,7 +66,7 @@ namespace kiwano } } - HRESULT Window::Create(String const& title, int width, int height, LPCWSTR icon, bool fullscreen, WNDPROC proc) + void Window::Init(String const& title, int width, int height, LPCWSTR icon, bool fullscreen, WNDPROC proc) { HINSTANCE hinst = GetModuleHandleW(nullptr); WNDCLASSEX wcex = { 0 }; @@ -157,16 +157,17 @@ namespace kiwano if (handle_ == nullptr) { ::UnregisterClass(KGE_WND_CLASS_NAME, hinst); - return HRESULT_FROM_WIN32(GetLastError()); + ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); } + else + { + RECT rc; + GetClientRect(handle_, &rc); + width_ = rc.right - rc.left; + height_ = rc.bottom - rc.top; - RECT rc; - GetClientRect(handle_, &rc); - width_ = rc.right - rc.left; - height_ = rc.bottom - rc.top; - - SetMouseCursor(MouseCursor::Arrow); - return S_OK; + SetMouseCursor(MouseCursor::Arrow); + } } void Window::Prepare() diff --git a/src/kiwano/base/Window.h b/src/kiwano/base/Window.h index bd1dc631..19cf280a 100644 --- a/src/kiwano/base/Window.h +++ b/src/kiwano/base/Window.h @@ -21,7 +21,7 @@ #pragma once #include "../macros.h" #include "../core/core.h" -#include "../math/helper.h" +#include "../math/math.h" #include "types.h" namespace kiwano @@ -60,7 +60,7 @@ namespace kiwano void SetMouseCursor(MouseCursor cursor); public: - HRESULT Create( + void Init( String const& title, int width, int height, diff --git a/src/kiwano/base/types.h b/src/kiwano/base/types.h index 28e139e0..b3045ffb 100644 --- a/src/kiwano/base/types.h +++ b/src/kiwano/base/types.h @@ -23,7 +23,7 @@ namespace kiwano { - // 画笔样式 + // 线条样式 enum class StrokeStyle : int { Miter = 0, /* 斜切 */ @@ -61,11 +61,4 @@ namespace kiwano GrayScale, // 灰度抗锯齿 None // 不启用抗锯齿 }; - - // 图层属性 - struct LayerProperties - { - Rect area; - float opacity; - }; -} \ No newline at end of file +} diff --git a/src/kiwano/base/window.cpp b/src/kiwano/base/window.cpp deleted file mode 100644 index bc2a5f80..00000000 --- a/src/kiwano/base/window.cpp +++ /dev/null @@ -1,418 +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. - -#include "Window.h" -#include "Logger.h" - -#define WINDOW_STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX -#define WINDOW_FULLSCREEN_STYLE WS_CLIPCHILDREN | WS_POPUP -#define KGE_WND_CLASS_NAME L"KiwanoAppWnd" - -namespace kiwano -{ - namespace - { - MONITORINFOEX GetMoniterInfoEx(HWND hwnd); - - void AdjustWindow(UINT width, UINT height, DWORD style, UINT* win_width, UINT* win_height); - - void ChangeFullScreenResolution(int width, int height, WCHAR* device_name); - - void RestoreResolution(WCHAR* device_name); - } - - Window::Window() - : handle_(nullptr) - , width_(0) - , height_(0) - , device_name_(nullptr) - , is_fullscreen_(false) - , mouse_cursor_(MouseCursor(-1)) - { - } - - Window::~Window() - { - if (is_fullscreen_) - RestoreResolution(device_name_); - - if (device_name_) - { - delete[] device_name_; - device_name_ = nullptr; - } - - if (handle_) - { - ::DestroyWindow(handle_); - handle_ = nullptr; - } - } - - HRESULT Window::Create(String const& title, int width, int height, LPCWSTR icon, bool fullscreen, WNDPROC proc) - { - HINSTANCE hinst = GetModuleHandleW(nullptr); - WNDCLASSEX wcex = { 0 }; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.lpszClassName = KGE_WND_CLASS_NAME; - wcex.style = CS_HREDRAW | CS_VREDRAW /* | CS_DBLCLKS */; - wcex.lpfnWndProc = proc; - wcex.hIcon = nullptr; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = sizeof(LONG_PTR); - wcex.hInstance = hinst; - wcex.hbrBackground = nullptr; - wcex.lpszMenuName = nullptr; - wcex.hCursor = nullptr; - - if (icon) - { - wcex.hIcon = (HICON)::LoadImageW(hinst, icon, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE); - } - - ::RegisterClassExW(&wcex); - - // Get the nearest monitor to this window - HMONITOR monitor = ::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY); - - // Get the target monitor info - MONITORINFOEX monitor_info_ex; - memset(&monitor_info_ex, 0, sizeof(MONITORINFOEX)); - monitor_info_ex.cbSize = sizeof(MONITORINFOEX); - ::GetMonitorInfoW(monitor, &monitor_info_ex); - - // Save the device name - int len = lstrlenW(monitor_info_ex.szDevice); - device_name_ = new WCHAR[len + 1]; - lstrcpyW(device_name_, monitor_info_ex.szDevice); - - int left = -1; - int top = -1; - - is_fullscreen_ = fullscreen; - - if (is_fullscreen_) - { - top = monitor_info_ex.rcMonitor.top; - left = monitor_info_ex.rcMonitor.left; - - if (width > monitor_info_ex.rcWork.right - left) - width = monitor_info_ex.rcWork.right - left; - - if (height > monitor_info_ex.rcWork.bottom - top) - height = monitor_info_ex.rcWork.bottom - top; - } - else - { - UINT screenw = monitor_info_ex.rcWork.right - monitor_info_ex.rcWork.left; - UINT screenh = monitor_info_ex.rcWork.bottom - monitor_info_ex.rcWork.top; - - UINT win_width, win_height; - AdjustWindow( - width, - height, - GetWindowStyle(), - &win_width, - &win_height - ); - - left = monitor_info_ex.rcWork.left + (screenw - win_width) / 2; - top = monitor_info_ex.rcWork.top + (screenh - win_height) / 2; - width = win_width; - height = win_height; - } - - handle_ = ::CreateWindowExW( - is_fullscreen_ ? WS_EX_TOPMOST : 0, - KGE_WND_CLASS_NAME, - title.c_str(), - GetWindowStyle(), - left, - top, - width, - height, - nullptr, - nullptr, - hinst, - nullptr - ); - - if (handle_ == nullptr) - { - ::UnregisterClass(KGE_WND_CLASS_NAME, hinst); - return HRESULT_FROM_WIN32(GetLastError()); - } - - RECT rc; - GetClientRect(handle_, &rc); - width_ = rc.right - rc.left; - height_ = rc.bottom - rc.top; - - SetMouseCursor(MouseCursor::Arrow); - return S_OK; - } - - void Window::Prepare() - { - ::ShowWindow(handle_, SW_SHOWNORMAL); - ::UpdateWindow(handle_); - - if (is_fullscreen_) - { - ChangeFullScreenResolution(width_, height_, device_name_); - } - } - - String Window::GetTitle() const - { - if (handle_) - { - wchar_t title[256]; - ::GetWindowTextW(handle_, title, 256); - return title; - } - return String(); - } - - void Window::SetTitle(String const& title) - { - if (handle_) - ::SetWindowTextW(handle_, title.c_str()); - } - - Size Window::GetSize() const - { - return Size{ - static_cast(width_), - static_cast(height_) - }; - } - - float Window::GetWidth() const - { - return static_cast(width_); - } - - float Window::GetHeight() const - { - return static_cast(height_); - } - - void Window::SetIcon(LPCWSTR icon_resource) - { - if (handle_) - { - HINSTANCE hinstance = GetModuleHandle(nullptr); - HICON icon = (HICON)::LoadImage( - hinstance, - icon_resource, - IMAGE_ICON, - 0, - 0, - LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_DEFAULTSIZE - ); - - ::SendMessage(handle_, WM_SETICON, ICON_BIG, (LPARAM)icon); - ::SendMessage(handle_, WM_SETICON, ICON_SMALL, (LPARAM)icon); - } - } - - void Window::Resize(int width, int height) - { - if (handle_ && !is_fullscreen_) - { - RECT rc = { 0, 0, int(width), int(height) }; - ::AdjustWindowRect(&rc, GetWindowStyle(), false); - - width = rc.right - rc.left; - height = rc.bottom - rc.top; - ::SetWindowPos(handle_, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); - } - } - - void Window::SetFullscreen(bool fullscreen, int width, int height) - { - if (is_fullscreen_ != fullscreen || width != width_ || height != height_) - { - is_fullscreen_ = fullscreen; - - if (is_fullscreen_) - { - // move window to (0, 0) before display switch - ::SetWindowPos(handle_, HWND_TOPMOST, 0, 0, width_, height_, SWP_NOACTIVATE); - - ChangeFullScreenResolution(width, height, device_name_); - - MONITORINFOEX info = GetMoniterInfoEx(handle_); - - ::SetWindowLongPtr(handle_, GWL_STYLE, GetWindowStyle()); - ::SetWindowPos(handle_, HWND_TOPMOST, info.rcMonitor.top, info.rcMonitor.left, width, height, SWP_NOACTIVATE); - - width_ = width; - height_ = height; - } - else - { - RestoreResolution(device_name_); - - MONITORINFOEX info = GetMoniterInfoEx(handle_); - - UINT screenw = info.rcWork.right - info.rcWork.left; - UINT screenh = info.rcWork.bottom - info.rcWork.top; - - UINT win_width, win_height; - AdjustWindow(width, height, GetWindowStyle(), &win_width, &win_height); - - int left = screenw > win_width ? ((screenw - win_width) / 2) : 0; - int top = screenh > win_height ? ((screenh - win_height) / 2) : 0; - - ::SetWindowLongPtr(handle_, GWL_STYLE, GetWindowStyle()); - ::SetWindowPos(handle_, HWND_NOTOPMOST, left, top, win_width, win_height, SWP_DRAWFRAME | SWP_FRAMECHANGED); - - UpdateWindowRect(); - } - - ::ShowWindow(handle_, SW_SHOWNORMAL); - } - } - - void Window::SetMouseCursor(MouseCursor cursor) - { - if (mouse_cursor_ != cursor) - { - mouse_cursor_ = cursor; - - LPTSTR win32_cursor = IDC_ARROW; - switch (cursor) - { - case MouseCursor::Arrow: win32_cursor = IDC_ARROW; break; - case MouseCursor::TextInput: win32_cursor = IDC_IBEAM; break; - case MouseCursor::SizeAll: win32_cursor = IDC_SIZEALL; break; - case MouseCursor::SizeWE: win32_cursor = IDC_SIZEWE; break; - case MouseCursor::SizeNS: win32_cursor = IDC_SIZENS; break; - case MouseCursor::SizeNESW: win32_cursor = IDC_SIZENESW; break; - case MouseCursor::SizeNWSE: win32_cursor = IDC_SIZENWSE; break; - case MouseCursor::Hand: win32_cursor = IDC_HAND; break; - } - ::SetCursor(::LoadCursorW(nullptr, win32_cursor)); - } - } - - HWND Window::GetHandle() const - { - return handle_; - } - - DWORD Window::GetWindowStyle() const - { - return is_fullscreen_ ? (WINDOW_FULLSCREEN_STYLE) : (WINDOW_STYLE); - } - - void Window::UpdateWindowRect() - { - if (!handle_) - return; - - RECT rc; - ::GetClientRect(handle_, &rc); - - width_ = rc.right - rc.left; - height_ = rc.bottom - rc.top; - } - - void Window::SetActive(bool actived) - { - if (!handle_) - return; - - if (is_fullscreen_) - { - if (actived) - { - ChangeFullScreenResolution(width_, height_, device_name_); - - MONITORINFOEX info = GetMoniterInfoEx(handle_); - ::SetWindowPos(handle_, HWND_TOPMOST, info.rcMonitor.top, info.rcMonitor.left, width_, height_, SWP_NOACTIVATE); - } - else - { - RestoreResolution(device_name_); - - ::SetWindowPos(handle_, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - ::ShowWindow(handle_, SW_MINIMIZE); - } - } - } - - namespace - { - MONITORINFOEX GetMoniterInfoEx(HWND hwnd) - { - HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - MONITORINFOEX monitor_info; - - memset(&monitor_info, 0, sizeof(MONITORINFOEX)); - monitor_info.cbSize = sizeof(MONITORINFOEX); - ::GetMonitorInfoW(monitor, &monitor_info); - - return monitor_info; - } - - void AdjustWindow(UINT width, UINT height, DWORD style, UINT* win_width, UINT* win_height) - { - RECT rc; - ::SetRect(&rc, 0, 0, (int)width, (int)height); - ::AdjustWindowRect(&rc, style, false); - - *win_width = rc.right - rc.left; - *win_height = rc.bottom - rc.top; - - MONITORINFOEX info = GetMoniterInfoEx(NULL); - - UINT screenw = info.rcWork.right - info.rcWork.left; - UINT screenh = info.rcWork.bottom - info.rcWork.top; - - if (*win_width > screenw) - *win_width = screenw; - if (*win_height > screenh) - *win_height = screenh; - } - - void ChangeFullScreenResolution(int width, int height, WCHAR* device_name) - { - DEVMODE mode; - - memset(&mode, 0, sizeof(mode)); - mode.dmSize = sizeof(DEVMODE); - mode.dmBitsPerPel = ::GetDeviceCaps(::GetDC(0), BITSPIXEL);; - mode.dmPelsWidth = width; - mode.dmPelsHeight = height; - mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - - if (::ChangeDisplaySettingsExW(device_name, &mode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) - KGE_ERROR_LOG(L"ChangeDisplaySettings failed"); - } - - void RestoreResolution(WCHAR* device_name) - { - ::ChangeDisplaySettingsExW(device_name, NULL, NULL, 0, NULL); - } - } -} diff --git a/src/kiwano/core/intrusive_list.hpp b/src/kiwano/core/intrusive_list.hpp index e8f56a28..76e0053d 100644 --- a/src/kiwano/core/intrusive_list.hpp +++ b/src/kiwano/core/intrusive_list.hpp @@ -82,7 +82,7 @@ public: T& last_item() { return last_; } - bool is_empty() const { return !first_; } + bool item_empty() const { return !first_; } void push_back_item(T const& child) { diff --git a/src/kiwano/kiwano.h b/src/kiwano/kiwano.h index 92597682..5ddf7f3d 100644 --- a/src/kiwano/kiwano.h +++ b/src/kiwano/kiwano.h @@ -56,23 +56,31 @@ // -// base +// renderer // -#include "renderer/Renderer.h" +#include "renderer/Color.h" +#include "renderer/Font.h" #include "renderer/Image.h" #include "renderer/GifImage.h" +#include "renderer/TextLayout.h" +#include "renderer/Geometry.h" +#include "renderer/LayerArea.h" +#include "renderer/ImageCache.h" +#include "renderer/Renderer.h" + + +// +// base +// #include "base/time.h" #include "base/Window.h" #include "base/input.h" #include "base/Director.h" #include "base/Logger.h" - #include "base/SmartPtr.hpp" -#include "base/ComPtr.hpp" - -#include "base/Object.h" +#include "base/ObjectBase.h" #include "base/Event.hpp" #include "base/EventListener.h" #include "base/EventDispatcher.h" @@ -81,8 +89,12 @@ #include "base/AsyncTask.h" #include "base/Resource.h" -#include "2d/Font.hpp" -#include "2d/Color.h" + +// +// 2d +// + + #include "2d/Transform.hpp" #include "2d/TextStyle.hpp" @@ -110,9 +122,15 @@ #include "2d/ShapeActor.h" #include "2d/DebugActor.h" + +// +// platform +// + #include "platform/modules.h" #include "platform/Application.h" + // // utils // diff --git a/src/kiwano/macros.h b/src/kiwano/macros.h index 25260d3b..a9fa0c46 100644 --- a/src/kiwano/macros.h +++ b/src/kiwano/macros.h @@ -87,7 +87,7 @@ #ifndef KGE_ASSERT # ifdef KGE_DEBUG -# define KGE_ASSERT(EXPR) assert(EXPR) +# define KGE_ASSERT(EXPR) do { (void)((!!(EXPR)) || (_wassert(_CRT_WIDE(#EXPR), _CRT_WIDE(__FUNCTION__), (unsigned)(__LINE__)), 0)); } while(0) # else # define KGE_ASSERT __noop # endif diff --git a/src/kiwano/math/Rect.hpp b/src/kiwano/math/Rect.hpp index 7c53ef82..c45480ae 100644 --- a/src/kiwano/math/Rect.hpp +++ b/src/kiwano/math/Rect.hpp @@ -99,7 +99,7 @@ namespace kiwano inline value_type GetBottom() const { return origin.y + size.y; } - inline bool is_empty() const { return origin.IsOrigin() && size.IsOrigin(); } + inline bool IsEmpty() const { return origin.IsOrigin() && size.IsOrigin(); } inline bool ContainsPoint(const Vec2T& point) const { diff --git a/src/kiwano/math/helper.h b/src/kiwano/math/math.h similarity index 100% rename from src/kiwano/math/helper.h rename to src/kiwano/math/math.h diff --git a/src/kiwano/platform/Application.cpp b/src/kiwano/platform/Application.cpp index c47fa142..31f2997c 100644 --- a/src/kiwano/platform/Application.cpp +++ b/src/kiwano/platform/Application.cpp @@ -24,6 +24,7 @@ #include "../base/Logger.h" #include "../base/input.h" #include "../base/Director.h" +#include "../renderer/ImageCache.h" #include "../renderer/Renderer.h" #include "../utils/ResourceCache.h" #include // GET_X_LPARAM, GET_Y_LPARAM @@ -79,15 +80,13 @@ namespace kiwano void Application::Init(const Options& options) { - ThrowIfFailed( - Window::GetInstance()->Create( - options.title, - options.width, - options.height, - options.icon, - options.fullscreen, - Application::WndProc - ) + Window::GetInstance()->Init( + options.title, + options.width, + options.height, + options.icon, + options.fullscreen, + Application::WndProc ); Renderer::GetInstance()->SetClearColor(options.clear_color); @@ -121,10 +120,9 @@ namespace kiwano void Application::Run() { - HWND hwnd = Window::GetInstance()->GetHandle(); + KGE_ASSERT(inited_ && "Calling Application::Run before Application::Init"); - if (!inited_) - throw std::exception("Calling Application::Run before Application::Init"); + HWND hwnd = Window::GetInstance()->GetHandle(); if (hwnd) { @@ -147,8 +145,10 @@ namespace kiwano void Application::Destroy() { - // Clear all stages + // Clear all resources Director::GetInstance()->ClearStages(); + ResourceCache::GetInstance()->Clear(); + ImageCache::GetInstance()->Clear(); if (inited_) { @@ -164,6 +164,7 @@ namespace kiwano // Destroy all instances Director::DestroyInstance(); ResourceCache::DestroyInstance(); + ImageCache::DestroyInstance(); Input::DestroyInstance(); Renderer::DestroyInstance(); Window::DestroyInstance(); diff --git a/src/kiwano/platform/Application.h b/src/kiwano/platform/Application.h index 595bbbb3..bb8804ee 100644 --- a/src/kiwano/platform/Application.h +++ b/src/kiwano/platform/Application.h @@ -23,7 +23,7 @@ #include "../base/time.h" #include "../base/Component.h" #include "../base/Event.hpp" -#include "../2d/Color.h" +#include "../renderer/Color.h" namespace kiwano { diff --git a/src/kiwano/2d/Color.cpp b/src/kiwano/renderer/Color.cpp similarity index 100% rename from src/kiwano/2d/Color.cpp rename to src/kiwano/renderer/Color.cpp diff --git a/src/kiwano/2d/Color.h b/src/kiwano/renderer/Color.h similarity index 100% rename from src/kiwano/2d/Color.h rename to src/kiwano/renderer/Color.h diff --git a/src/kiwano/renderer/D2DDeviceResources.h b/src/kiwano/renderer/D2DDeviceResources.h deleted file mode 100644 index f112b3ef..00000000 --- a/src/kiwano/renderer/D2DDeviceResources.h +++ /dev/null @@ -1,92 +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 "helper.hpp" -#include "../base/Resource.h" -#include "../2d/Font.hpp" -#include "../2d/TextStyle.hpp" -#include -#include -#include - -namespace kiwano -{ - MIDL_INTERFACE("5706684a-bf6d-4b03-b627-094758a33032") - KGE_API ID2DDeviceResources - : public IUnknown - { - public: - static HRESULT Create(ID2DDeviceResources** device_resources); - - virtual HRESULT CreateBitmapFromFile( - _Out_ ComPtr& bitmap, - _In_ String const& file_path - ) = 0; - - virtual HRESULT CreateBitmapFromResource( - _Out_ ComPtr& bitmap, - _In_ Resource const& res - ) = 0; - - virtual HRESULT CreateTextFormat( - _Out_ ComPtr& text_format, - _In_ Font const& font, - _In_ TextStyle const& text_style - ) const = 0; - - virtual HRESULT CreateTextLayout( - _Out_ ComPtr& text_layout, - _Out_ Size& layout_size, - _In_ String const& text, - _In_ ComPtr const& text_format, - _In_ TextStyle const& text_style - ) const = 0; - - virtual ID2D1StrokeStyle* GetStrokeStyle(StrokeStyle stroke) const = 0; - - virtual HRESULT SetD2DDevice( - _In_ ComPtr const& device - ) = 0; - - virtual void SetTargetBitmap( - _In_ ComPtr const& target - ) = 0; - - virtual void DiscardResources() = 0; - - inline ID2D1Factory1* GetFactory() const { KGE_ASSERT(factory_); return factory_.get(); } - inline IWICImagingFactory* GetWICImagingFactory() const { KGE_ASSERT(imaging_factory_); return imaging_factory_.get(); } - inline IDWriteFactory* GetDWriteFactory() const { KGE_ASSERT(dwrite_factory_); return dwrite_factory_.get(); } - inline ID2D1Device* GetDevice() const { KGE_ASSERT(device_); return device_.get(); } - inline ID2D1DeviceContext* GetDeviceContext() const { KGE_ASSERT(device_context_); return device_context_.get(); } - inline ID2D1Bitmap1* GetTargetBitmap() const { 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/renderer/Font.cpp b/src/kiwano/renderer/Font.cpp new file mode 100644 index 00000000..1c0016c3 --- /dev/null +++ b/src/kiwano/renderer/Font.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Font.h" + +namespace kiwano +{ + Font::Font(const String& family, float size, unsigned int weight, bool italic, FontCollection collection) + : family(family) + , size(size) + , weight(weight) + , italic(italic) + , collection(collection) + { + } + +} diff --git a/src/kiwano/2d/Font.hpp b/src/kiwano/renderer/Font.h similarity index 77% rename from src/kiwano/2d/Font.hpp rename to src/kiwano/renderer/Font.h index 091679a3..f389df68 100644 --- a/src/kiwano/2d/Font.hpp +++ b/src/kiwano/renderer/Font.h @@ -19,22 +19,22 @@ // THE SOFTWARE. #pragma once -#include +#include "FontCollection.h" namespace kiwano { // 字体粗细值 enum FontWeight : unsigned int { - Thin = 100, - ExtraLight = 200, - Light = 300, - Normal = 400, - Medium = 500, - Bold = 700, - ExtraBold = 800, - Black = 900, - ExtraBlack = 950 + Thin = 100, + ExtraLight = 200, + Light = 300, + Normal = 400, + Medium = 500, + Bold = 700, + ExtraBold = 800, + Black = 900, + ExtraBlack = 950 }; // 字体 @@ -45,19 +45,15 @@ namespace kiwano float size; // 字号 unsigned int weight; // 粗细值 bool italic; // 是否斜体 + FontCollection collection; // 字体集 public: Font( - const String& family = L"", - float size = 18, - unsigned int weight = FontWeight::Normal, - bool italic = false - ) - : family(family) - , size(size) - , weight(weight) - , italic(italic) - { - } + const String& family = L"", + float size = 18, + unsigned int weight = FontWeight::Normal, + bool italic = false, + FontCollection collection = FontCollection() + ); }; } diff --git a/src/kiwano/renderer/FontCollection.cpp b/src/kiwano/renderer/FontCollection.cpp new file mode 100644 index 00000000..87661691 --- /dev/null +++ b/src/kiwano/renderer/FontCollection.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "FontCollection.h" +#include "Renderer.h" + +namespace kiwano +{ + FontCollection::FontCollection() + { + } + + FontCollection::FontCollection(String const& file) + { + Load(file); + } + + FontCollection::FontCollection(Vector const& files) + { + Load(files); + } + + FontCollection::FontCollection(Resource const& res) + { + Load(res); + } + + FontCollection::FontCollection(Vector const& res_arr) + { + Load(res_arr); + } + + bool FontCollection::Load(String const& file) + { + Renderer::GetInstance()->CreateFontCollection(*this, { file }); + return false; + } + + bool FontCollection::Load(Vector const& files) + { + Renderer::GetInstance()->CreateFontCollection(*this, files); + return false; + } + + bool FontCollection::Load(Resource const& res) + { + Renderer::GetInstance()->CreateFontCollection(*this, { res }); + return false; + } + + bool FontCollection::Load(Vector const& res_arr) + { + Renderer::GetInstance()->CreateFontCollection(*this, res_arr); + return false; + } +} diff --git a/src/kiwano/base/window.h b/src/kiwano/renderer/FontCollection.h similarity index 52% rename from src/kiwano/base/window.h rename to src/kiwano/renderer/FontCollection.h index bd1dc631..e4297181 100644 --- a/src/kiwano/base/window.h +++ b/src/kiwano/renderer/FontCollection.h @@ -19,77 +19,44 @@ // THE SOFTWARE. #pragma once -#include "../macros.h" -#include "../core/core.h" -#include "../math/helper.h" -#include "types.h" +#include "../base/Resource.h" +#include "win32/ComPtr.hpp" +#include namespace kiwano { - class KGE_API Window - : public Singleton + // 字体集 + class FontCollection { - KGE_DECLARE_SINGLETON(Window); + public: + FontCollection(); + + FontCollection(String const& file); + + FontCollection(Vector const& files); + + FontCollection(Resource const& res); + + FontCollection(Vector const& res_arr); + + // 从本地文件加载字体 + bool Load(String const& file); + + // 从多个本地文件加载字体 + bool Load(Vector const& files); + + // 从资源加载字体 + bool Load(Resource const& res); + + // 从多个资源加载字体 + bool Load(Vector const& res_arr); public: - // 获取标题 - String GetTitle() const; + inline ComPtr GetFontCollection() const { return collection_; } - // 获取窗口大小 - Size GetSize() const; - - // 获取窗口宽度 - float GetWidth() const; - - // 获取窗口高度 - float GetHeight() const; - - // 设置标题 - void SetTitle(String const& title); - - // 设置窗口图标 - void SetIcon(LPCWSTR icon_resource); - - // 重设窗口大小 - void Resize(int width, int height); - - // 设置全屏模式 - void SetFullscreen(bool fullscreen, int width, int height); - - // 设置鼠标指针 - void SetMouseCursor(MouseCursor cursor); - - public: - HRESULT Create( - String const& title, - int width, - int height, - LPCWSTR icon, - bool fullscreen, - WNDPROC proc - ); - - void Prepare(); - - HWND GetHandle() const; - - DWORD GetWindowStyle() const; - - void UpdateWindowRect(); - - void SetActive(bool actived); + inline void SetFontCollection(ComPtr collection) { collection_ = collection; } protected: - Window(); - - ~Window(); - - private: - HWND handle_; - bool is_fullscreen_; - int width_; - int height_; - WCHAR* device_name_; - MouseCursor mouse_cursor_; + ComPtr collection_; }; } diff --git a/src/kiwano/renderer/Geometry.h b/src/kiwano/renderer/Geometry.h index 8a4f5417..be611d73 100644 --- a/src/kiwano/renderer/Geometry.h +++ b/src/kiwano/renderer/Geometry.h @@ -19,8 +19,7 @@ // THE SOFTWARE. #pragma once -#include "../base/SmartPtr.hpp" -#include "D2DDeviceResources.h" +#include "win32/D2DDeviceResources.h" namespace kiwano { diff --git a/src/kiwano/renderer/GifImage.cpp b/src/kiwano/renderer/GifImage.cpp index d2546c51..a057a663 100644 --- a/src/kiwano/renderer/GifImage.cpp +++ b/src/kiwano/renderer/GifImage.cpp @@ -19,258 +19,65 @@ // THE SOFTWARE. #include "GifImage.h" +#include "Renderer.h" #include "../base/Logger.h" -#include "../utils/FileUtil.h" namespace kiwano { GifImage::GifImage() : frames_count_(0) - , disposal_type_(DisposalType::Unknown) , width_in_pixels_(0) , height_in_pixels_(0) - , frame_delay_(0) - , frame_position_{} , bg_color_{} { } + GifImage::GifImage(String const& file_path) + { + Load(file_path); + } + GifImage::GifImage(Resource const& res) : GifImage() { Load(res); } - bool GifImage::Load(Resource const& res) + bool GifImage::Load(String const& file_path) { - HRESULT hr = S_OK; + Renderer::GetInstance()->CreateGifImage(*this, file_path); - frames_count_ = 0; - disposal_type_ = DisposalType::None; - - saved_frame_.reset(); - decoder_.reset(); - - auto factory = Renderer::GetInstance()->GetD2DDeviceResources()->GetWICImagingFactory(); - - if (res.IsFileType()) + if (IsValid()) { -#ifdef KGE_DEBUG - if (!FileUtil::ExistsFile(res.GetFileName().c_str())) + if (FAILED(GetGlobalMetadata())) { - KGE_WARNING_LOG(L"Gif file '%s' not found!", res.GetFileName().c_str()); + SetDecoder(nullptr); return false; } -#endif - - hr = factory->CreateDecoderFromFilename( - res.GetFileName().c_str(), - nullptr, - GENERIC_READ, - WICDecodeMetadataCacheOnLoad, - &decoder_); + return true; } - else - { - LPVOID buffer; - DWORD buffer_size; - HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL; - - ComPtr stream; - - if (SUCCEEDED(hr)) - { - hr = factory->CreateStream(&stream); - } - - if (SUCCEEDED(hr)) - { - hr = stream->InitializeFromMemory( - static_cast(buffer), - buffer_size - ); - } - - if (SUCCEEDED(hr)) - { - hr = factory->CreateDecoderFromStream( - stream.get(), - nullptr, - WICDecodeMetadataCacheOnLoad, - &decoder_ - ); - } - } - - if (SUCCEEDED(hr)) - { - hr = GetGlobalMetadata(); - } - - return SUCCEEDED(hr); + return false; } - HRESULT GifImage::GetRawFrame(UINT frame_index) + bool GifImage::Load(Resource const& res) { - ComPtr converter; - ComPtr wic_frame; - ComPtr metadata_reader; + Renderer::GetInstance()->CreateGifImage(*this, res); - PROPVARIANT prop_val; - PropVariantInit(&prop_val); - - // Retrieve the current frame - HRESULT hr = decoder_->GetFrame(frame_index, &wic_frame); - if (SUCCEEDED(hr)) + if (IsValid()) { - // Format convert to 32bppPBGRA which D2D expects - auto factory = Renderer::GetInstance()->GetD2DDeviceResources()->GetWICImagingFactory(); - hr = factory->CreateFormatConverter(&converter); - } - - if (SUCCEEDED(hr)) - { - hr = converter->Initialize( - wic_frame.get(), - GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, - nullptr, - 0.f, - WICBitmapPaletteTypeCustom); - } - - if (SUCCEEDED(hr)) - { - auto ctx = Renderer::GetInstance()->GetD2DDeviceResources()->GetDeviceContext(); - - // Create a D2DBitmap from IWICBitmapSource - raw_frame_.reset(); - hr = ctx->CreateBitmapFromWicBitmap( - converter.get(), - nullptr, - &raw_frame_); - } - - if (SUCCEEDED(hr)) - { - // Get Metadata Query Reader from the frame - hr = wic_frame->GetMetadataQueryReader(&metadata_reader); - } - - // Get the Metadata for the current frame - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Left", &prop_val); - if (SUCCEEDED(hr)) + if (FAILED(GetGlobalMetadata())) { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame_position_.left = static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); + SetDecoder(nullptr); + return false; } + return true; } + return false; + } - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Top", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame_position_.top = static_cast(prop_val.uiVal); - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Width", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame_position_.right = static_cast(prop_val.uiVal) - + frame_position_.left; - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName(L"/imgdesc/Height", &prop_val); - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame_position_.bottom = static_cast(prop_val.uiVal) - + frame_position_.top; - } - PropVariantClear(&prop_val); - } - } - - if (SUCCEEDED(hr)) - { - frame_delay_ = 0; - - hr = metadata_reader->GetMetadataByName( - L"/grctlext/Delay", - &prop_val); - - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - hr = UIntMult(prop_val.uiVal, 10, &frame_delay_); - } - PropVariantClear(&prop_val); - } - else - { - frame_delay_ = 0; - } - - if (SUCCEEDED(hr)) - { - // 插入一个强制延迟 - if (frame_delay_ < 90) - { - frame_delay_ = 90; - } - } - } - - if (SUCCEEDED(hr)) - { - hr = metadata_reader->GetMetadataByName( - L"/grctlext/Disposal", - &prop_val); - - if (SUCCEEDED(hr)) - { - hr = (prop_val.vt == VT_UI1) ? S_OK : E_FAIL; - if (SUCCEEDED(hr)) - { - disposal_type_ = DisposalType(prop_val.bVal); - } - } - else - { - // 获取 DisposalType 失败,可能图片是只有一帧的图片 - disposal_type_ = DisposalType::Unknown; - } - } - - ::PropVariantClear(&prop_val); - return hr; + bool GifImage::IsValid() const + { + return decoder_ != nullptr; } HRESULT GifImage::GetGlobalMetadata() @@ -284,7 +91,12 @@ namespace kiwano ComPtr metadata_reader; // 获取帧数量 - HRESULT hr = decoder_->GetFrameCount(&frames_count_); + HRESULT hr = decoder_ ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + hr = decoder_->GetFrameCount(&frames_count_); + } if (SUCCEEDED(hr)) { @@ -297,7 +109,7 @@ namespace kiwano if (FAILED(GetBackgroundColor(metadata_reader.get()))) { // 如果未能获得颜色,则默认为透明 - bg_color_ = D2D1::ColorF(0, 0.f); + bg_color_ = Color(0, 0.f); } } @@ -305,9 +117,7 @@ namespace kiwano if (SUCCEEDED(hr)) { // 获取宽度 - hr = metadata_reader->GetMetadataByName( - L"/logscrdesc/Width", - &prop_val); + hr = metadata_reader->GetMetadataByName(L"/logscrdesc/Width", &prop_val); if (SUCCEEDED(hr)) { @@ -323,9 +133,7 @@ namespace kiwano if (SUCCEEDED(hr)) { // 获取高度 - hr = metadata_reader->GetMetadataByName( - L"/logscrdesc/Height", - &prop_val); + hr = metadata_reader->GetMetadataByName(L"/logscrdesc/Height", &prop_val); if (SUCCEEDED(hr)) { @@ -341,9 +149,7 @@ namespace kiwano if (SUCCEEDED(hr)) { // 获得像素纵横比 - hr = metadata_reader->GetMetadataByName( - L"/logscrdesc/PixelAspectRatio", - &prop_val); + hr = metadata_reader->GetMetadataByName(L"/logscrdesc/PixelAspectRatio", &prop_val); if (SUCCEEDED(hr)) { @@ -383,101 +189,17 @@ namespace kiwano return hr; } - HRESULT GifImage::DisposeCurrentFrame(ComPtr frame_rt) + HRESULT GifImage::GetBackgroundColor(ComPtr metadata_reader) { - HRESULT hr = S_OK; - - switch (disposal_type_) - { - case DisposalType::Unknown: - case DisposalType::None: - break; - case DisposalType::Background: - // 用背景颜色清除当前原始帧覆盖的区域 - hr = ClearCurrentFrameArea(frame_rt); - break; - case DisposalType::Previous: - // 恢复先前构图的帧 - hr = RestoreSavedFrame(frame_rt); - break; - default: - hr = E_FAIL; - } - return hr; - } - - HRESULT GifImage::SaveComposedFrame(ComPtr frame_rt) - { - HRESULT hr = S_OK; - - ComPtr frame_to_be_saved; - - hr = frame_rt->GetBitmap(&frame_to_be_saved); - if (SUCCEEDED(hr)) - { - if (saved_frame_ == nullptr) - { - auto size = frame_to_be_saved->GetPixelSize(); - auto prop = D2D1::BitmapProperties(frame_to_be_saved->GetPixelFormat()); - - hr = frame_rt->CreateBitmap(size, prop, &saved_frame_); - } - } - - if (SUCCEEDED(hr)) - { - hr = saved_frame_->CopyFromBitmap(nullptr, frame_to_be_saved.get(), nullptr); - } - return hr; - } - - HRESULT GifImage::RestoreSavedFrame(ComPtr frame_rt) - { - HRESULT hr = S_OK; - - ComPtr frame_to_copy_to; - - hr = saved_frame_ ? S_OK : E_FAIL; - - if (SUCCEEDED(hr)) - { - hr = frame_rt->GetBitmap(&frame_to_copy_to); - } - - if (SUCCEEDED(hr)) - { - hr = frame_to_copy_to->CopyFromBitmap(nullptr, saved_frame_.get(), nullptr); - } - - return hr; - } - - HRESULT GifImage::ClearCurrentFrameArea(ComPtr frame_rt) - { - frame_rt->BeginDraw(); - - frame_rt->PushAxisAlignedClip(&frame_position_, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - frame_rt->Clear(bg_color_); - frame_rt->PopAxisAlignedClip(); - - return frame_rt->EndDraw(); - } - - HRESULT GifImage::GetBackgroundColor(IWICMetadataQueryReader* metadata_reader) - { - DWORD bgcolor = 0; BYTE bg_index = 0; WICColor bgcolors[256]; UINT colors_copied = 0; + ComPtr wic_palette; PROPVARIANT prop_val; PropVariantInit(&prop_val); - ComPtr wic_palette; - - HRESULT hr = metadata_reader->GetMetadataByName( - L"/logscrdesc/GlobalColorTableFlag", - &prop_val); + HRESULT hr = metadata_reader->GetMetadataByName(L"/logscrdesc/GlobalColorTableFlag", &prop_val); if (SUCCEEDED(hr)) { @@ -487,9 +209,7 @@ namespace kiwano if (SUCCEEDED(hr)) { - hr = metadata_reader->GetMetadataByName( - L"/logscrdesc/BackgroundColorIndex", - &prop_val); + hr = metadata_reader->GetMetadataByName(L"/logscrdesc/BackgroundColorIndex", &prop_val); if (SUCCEEDED(hr)) { @@ -523,19 +243,175 @@ namespace kiwano if (SUCCEEDED(hr)) { - // 检查下标 hr = (bg_index >= colors_copied) ? E_FAIL : S_OK; } if (SUCCEEDED(hr)) { - bgcolor = bgcolors[bg_index]; - // 转换为 ARGB 格式 - float alpha = (bgcolor >> 24) / 255.f; - bg_color_ = D2D1::ColorF(bgcolor, alpha); + float alpha = (bgcolors[bg_index] >> 24) / 255.f; + bg_color_ = Color(bgcolors[bg_index], alpha); } return hr; } + HRESULT GifImage::GetRawFrame(UINT frame_index, Image& raw_frame, Rect& frame_rect, Duration& delay, DisposalType& disposal_type) + { + ComPtr converter; + ComPtr wic_frame; + ComPtr metadata_reader; + + PROPVARIANT prop_val; + PropVariantInit(&prop_val); + + // Retrieve the current frame + HRESULT hr = decoder_->GetFrame(frame_index, &wic_frame); + if (SUCCEEDED(hr)) + { + // Format convert to 32bppPBGRA which D2D expects + auto factory = Renderer::GetInstance()->GetD2DDeviceResources()->GetWICImagingFactory(); + hr = factory->CreateFormatConverter(&converter); + } + + if (SUCCEEDED(hr)) + { + hr = converter->Initialize( + wic_frame.get(), + GUID_WICPixelFormat32bppPBGRA, + WICBitmapDitherTypeNone, + nullptr, + 0.f, + WICBitmapPaletteTypeCustom); + } + + if (SUCCEEDED(hr)) + { + auto ctx = Renderer::GetInstance()->GetD2DDeviceResources()->GetDeviceContext(); + + // Create a D2DBitmap from IWICBitmapSource + ComPtr raw_bitmap; + hr = ctx->CreateBitmapFromWicBitmap( + converter.get(), + nullptr, + &raw_bitmap + ); + + if (SUCCEEDED(hr)) + { + raw_frame.SetBitmap(raw_bitmap); + } + } + + if (SUCCEEDED(hr)) + { + // Get Metadata Query Reader from the frame + hr = wic_frame->GetMetadataQueryReader(&metadata_reader); + } + + // Get the Metadata for the current frame + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Left", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame_rect.origin.x = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Top", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame_rect.origin.y = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Width", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame_rect.size.x = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/imgdesc/Height", &prop_val); + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + if (SUCCEEDED(hr)) + { + frame_rect.size.y = static_cast(prop_val.uiVal); + } + PropVariantClear(&prop_val); + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/grctlext/Delay", &prop_val); + + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); + + if (SUCCEEDED(hr)) + { + UINT udelay = 0; + hr = UIntMult(prop_val.uiVal, 10, &udelay); + if (SUCCEEDED(hr)) + { + delay.SetMilliseconds(static_cast(udelay)); + } + } + PropVariantClear(&prop_val); + } + else + { + delay = 0; + } + } + + if (SUCCEEDED(hr)) + { + hr = metadata_reader->GetMetadataByName(L"/grctlext/Disposal", &prop_val); + + if (SUCCEEDED(hr)) + { + hr = (prop_val.vt == VT_UI1) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + disposal_type = DisposalType(prop_val.bVal); + } + ::PropVariantClear(&prop_val); + } + else + { + // 获取 DisposalType 失败,可能图片是只有一帧的图片 + disposal_type = DisposalType::Unknown; + } + } + + ::PropVariantClear(&prop_val); + return hr; + } + } diff --git a/src/kiwano/renderer/GifImage.h b/src/kiwano/renderer/GifImage.h index 556a2b82..30538924 100644 --- a/src/kiwano/renderer/GifImage.h +++ b/src/kiwano/renderer/GifImage.h @@ -19,34 +19,33 @@ // THE SOFTWARE. #pragma once -#include "../base/Resource.h" -#include "Renderer.h" +#include "Image.h" namespace kiwano { // GIF 图像 - KGE_DECLARE_SMART_PTR(GifImage); class KGE_API GifImage - : public Object { public: GifImage(); - GifImage( - Resource const& res - ); + GifImage(String const& file_path); - bool Load( - Resource const& res - ); + GifImage(Resource const& res); - inline unsigned int GetWidthInPixels() const { return width_in_pixels_; } + bool Load(String const& file_path); - inline unsigned int GetHeightInPixels() const { return height_in_pixels_; } + bool Load(Resource const& res); - inline unsigned int GetFrameDelay() const { return frame_delay_; } + bool IsValid() const; - inline unsigned int GetFramesCount() const { return frames_count_; } + inline UINT GetWidthInPixels() const { return width_in_pixels_; } + + inline UINT GetHeightInPixels() const { return height_in_pixels_; } + + inline UINT GetFramesCount() const { return frames_count_; } + + inline Color GetBackgroundColor() const { return bg_color_; } public: enum class DisposalType @@ -57,37 +56,31 @@ namespace kiwano Previous }; - inline DisposalType GetDisposalType() const { return disposal_type_; } + HRESULT GetRawFrame( + UINT frame_index, + Image& raw_frame, + Rect& frame_rect, + Duration& delay, + DisposalType& disposal_type + ); - inline D2D1_COLOR_F GetBackgroundColor() const { return bg_color_; } + inline ComPtr GetDecoder() const { return decoder_; } - inline D2D1_RECT_F const& GetFramePosition() const { return frame_position_; } - - inline ComPtr GetRawFrame() const { return raw_frame_; } - - inline void SetDisposalType(DisposalType type) { disposal_type_ = type; } - - public: - HRESULT GetRawFrame(UINT frame_index); - HRESULT GetGlobalMetadata(); - HRESULT GetBackgroundColor(IWICMetadataQueryReader* metadata_reader); - - HRESULT DisposeCurrentFrame(ComPtr frame_rt); - HRESULT SaveComposedFrame(ComPtr frame_rt); - HRESULT RestoreSavedFrame(ComPtr frame_rt); - HRESULT ClearCurrentFrameArea(ComPtr frame_rt); + inline void SetDecoder(ComPtr decoder) { decoder_ = decoder; } protected: - ComPtr raw_frame_; - ComPtr saved_frame_; - ComPtr decoder_; + HRESULT GetGlobalMetadata(); - unsigned int frames_count_; - unsigned int frame_delay_; - unsigned int width_in_pixels_; - unsigned int height_in_pixels_; - DisposalType disposal_type_; - D2D1_RECT_F frame_position_; - D2D1_COLOR_F bg_color_; + HRESULT GetBackgroundColor( + ComPtr metadata_reader + ); + + protected: + UINT frames_count_; + UINT width_in_pixels_; + UINT height_in_pixels_; + Color bg_color_; + + ComPtr decoder_; }; } diff --git a/src/kiwano/renderer/Image.cpp b/src/kiwano/renderer/Image.cpp index d01a8d91..ef001ad6 100644 --- a/src/kiwano/renderer/Image.cpp +++ b/src/kiwano/renderer/Image.cpp @@ -19,18 +19,26 @@ // THE SOFTWARE. #include "Image.h" +#include "Renderer.h" #include "../base/Logger.h" -#include "../platform/modules.h" namespace kiwano { Image::Image() - : bitmap_(nullptr) { } + Image::Image(String const& file_path) + { + Load(file_path); + } + + Image::Image(Resource const& res) + { + Load(res); + } + Image::Image(ComPtr const & bitmap) - : Image() { SetBitmap(bitmap); } @@ -39,9 +47,21 @@ namespace kiwano { } + bool Image::Load(String const& file_path) + { + Renderer::GetInstance()->CreateImage(*this, file_path); + return IsValid(); + } + + bool Image::Load(Resource const& res) + { + Renderer::GetInstance()->CreateImage(*this, res); + return IsValid(); + } + bool Image::IsValid() const { - return !!bitmap_; + return bitmap_ != nullptr; } float Image::GetWidth() const @@ -100,6 +120,43 @@ namespace kiwano return math::Vec2T{}; } + void Image::CopyFrom(Image const& copy_from) + { + if (IsValid() && copy_from.IsValid()) + { + HRESULT hr = bitmap_->CopyFromBitmap(nullptr, copy_from.GetBitmap().get(), nullptr); + + ThrowIfFailed(hr); + } + } + + void Image::CopyFrom(Image const& copy_from, Rect const& src_rect, Point const& dest_point) + { + if (IsValid() && copy_from.IsValid()) + { + HRESULT hr = bitmap_->CopyFromBitmap( + &D2D1::Point2U(UINT(dest_point.x), UINT(dest_point.y)), + copy_from.GetBitmap().get(), + &D2D1::RectU( + UINT(src_rect.GetLeft()), + UINT(src_rect.GetTop()), + UINT(src_rect.GetRight()), + UINT(src_rect.GetBottom())) + ); + + ThrowIfFailed(hr); + } + } + + D2D1_PIXEL_FORMAT Image::GetPixelFormat() const + { + if (bitmap_) + { + return bitmap_->GetPixelFormat(); + } + return D2D1_PIXEL_FORMAT(); + } + ComPtr Image::GetBitmap() const { return bitmap_; diff --git a/src/kiwano/renderer/Image.h b/src/kiwano/renderer/Image.h index 295d1e72..ec3aa193 100644 --- a/src/kiwano/renderer/Image.h +++ b/src/kiwano/renderer/Image.h @@ -19,25 +19,40 @@ // THE SOFTWARE. #pragma once -#include "../base/Resource.h" -#include "D2DDeviceResources.h" // ID2D1Bitmap +#include "win32/D2DDeviceResources.h" namespace kiwano { // 图像 - KGE_DECLARE_SMART_PTR(Image); class KGE_API Image - : public Object { public: Image(); + explicit Image( + String const& file_path + ); + + explicit Image( + Resource const& res + ); + explicit Image( ComPtr const& bitmap ); virtual ~Image(); + // 加载本地文件 + bool Load( + String const& file_path + ); + + // 加载资源 + bool Load( + Resource const& res + ); + // 资源是否有效 bool IsValid() const; @@ -59,12 +74,22 @@ namespace kiwano // 获取位图像素大小 math::Vec2T GetSizeInPixels() const; + // 拷贝位图内存 + void CopyFrom(Image const& copy_from); + + // 拷贝位图内存 + void CopyFrom(Image const& copy_from, Rect const& src_rect, Point const& dest_point); + + public: // 获取源位图 ComPtr GetBitmap() const; // 设置源位图 void SetBitmap(ComPtr bitmap); + // 获取像素格式 + D2D1_PIXEL_FORMAT GetPixelFormat() const; + protected: ComPtr bitmap_; }; diff --git a/src/kiwano/renderer/ImageCache.cpp b/src/kiwano/renderer/ImageCache.cpp index 342f2268..3437c6c4 100644 --- a/src/kiwano/renderer/ImageCache.cpp +++ b/src/kiwano/renderer/ImageCache.cpp @@ -19,10 +19,37 @@ // THE SOFTWARE. #include "ImageCache.h" +#include "Renderer.h" #include "../base/Logger.h" namespace kiwano { + template + _Ty CreateOrGetCache(_CacheTy& cache, _PathTy const& path, size_t hash) + { + auto iter = cache.find(hash); + if (iter != cache.end()) + { + return iter->second; + } + + _Ty image; + if (image.Load(path)) + { + cache.insert(std::make_pair(hash, image)); + } + return image; + } + + template + void RemoveCache(_CacheTy& cache, size_t hash) + { + auto iter = cache.find(hash); + if (iter != cache.end()) + { + cache.erase(iter); + } + } ImageCache::ImageCache() { @@ -32,53 +59,50 @@ namespace kiwano { } - ImagePtr ImageCache::AddImage(Resource const& res) + Image ImageCache::AddOrGetImage(String const& file_path) { - size_t hash_code = res.GetHashCode(); - auto iter = image_cache_.find(hash_code); - if (iter != image_cache_.end()) - { - return iter->second; - } + return CreateOrGetCache(image_cache_, file_path, file_path.hash()); + } - HRESULT hr = S_OK; - ComPtr bitmap; + Image ImageCache::AddOrGetImage(Resource const& res) + { + return CreateOrGetCache(image_cache_, res, res.GetId()); + } - if (res.IsFileType()) - { - hr = Renderer::GetInstance()->GetD2DDeviceResources()->CreateBitmapFromFile(bitmap, res.GetFileName()); - } - else - { - hr = Renderer::GetInstance()->GetD2DDeviceResources()->CreateBitmapFromResource(bitmap, res); - } + GifImage ImageCache::AddOrGetGifImage(String const& file_path) + { + return CreateOrGetCache(gif_image_cache_, file_path, file_path.hash()); + } - if (SUCCEEDED(hr)) - { - ImagePtr ptr = new Image(bitmap); - image_cache_.insert(std::make_pair(hash_code, ptr)); - return ptr; - } - else - { - KGE_ERROR_LOG(L"Load image file failed with HRESULT of %08X", hr); - } - return nullptr; + GifImage ImageCache::AddOrGetGifImage(Resource const& res) + { + return CreateOrGetCache(gif_image_cache_, res, res.GetId()); + } + + void ImageCache::RemoveImage(String const& file_path) + { + RemoveCache(image_cache_, file_path.hash()); } void ImageCache::RemoveImage(Resource const& res) { - size_t hash_code = res.GetHashCode(); - auto iter = image_cache_.find(hash_code); - if (iter != image_cache_.end()) - { - image_cache_.erase(iter); - } + RemoveCache(image_cache_, res.GetId()); + } + + void ImageCache::RemoveGifImage(String const& file_path) + { + RemoveCache(gif_image_cache_, file_path.hash()); + } + + void ImageCache::RemoveGifImage(Resource const& res) + { + RemoveCache(gif_image_cache_, res.GetId()); } void ImageCache::Clear() { image_cache_.clear(); + gif_image_cache_.clear(); } } diff --git a/src/kiwano/renderer/ImageCache.h b/src/kiwano/renderer/ImageCache.h index 2d8365df..25076f12 100644 --- a/src/kiwano/renderer/ImageCache.h +++ b/src/kiwano/renderer/ImageCache.h @@ -19,8 +19,8 @@ // THE SOFTWARE. #pragma once -#include "../core/singleton.hpp" -#include "Renderer.h" +#include "Image.h" +#include "GifImage.h" namespace kiwano { @@ -30,9 +30,15 @@ namespace kiwano KGE_DECLARE_SINGLETON(ImageCache); public: - ImagePtr AddImage(Resource const& res); + Image AddOrGetImage(String const& file_path); + Image AddOrGetImage(Resource const& res); + GifImage AddOrGetGifImage(String const& file_path); + GifImage AddOrGetGifImage(Resource const& res); + void RemoveImage(String const& file_path); void RemoveImage(Resource const& res); + void RemoveGifImage(String const& file_path); + void RemoveGifImage(Resource const& res); void Clear(); @@ -42,7 +48,10 @@ namespace kiwano virtual ~ImageCache(); protected: - using ImageMap = UnorderedMap; + using ImageMap = UnorderedMap; ImageMap image_cache_; + + using GifImageMap = UnorderedMap; + GifImageMap gif_image_cache_; }; } diff --git a/src/kiwano/renderer/LayerArea.cpp b/src/kiwano/renderer/LayerArea.cpp new file mode 100644 index 00000000..a81ddddd --- /dev/null +++ b/src/kiwano/renderer/LayerArea.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2016-2019 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "LayerArea.h" + +namespace kiwano +{ + LayerArea::LayerArea() + : opacity_(1.f) + { + } + + bool LayerArea::IsValid() const + { + return layer_ != nullptr; + } + + Size LayerArea::GetSize() const + { + if (layer_) + { + return reinterpret_cast(layer_->GetSize()); + } + return Size(); + } + +} diff --git a/src/kiwano/renderer/LayerArea.h b/src/kiwano/renderer/LayerArea.h new file mode 100644 index 00000000..59122a2c --- /dev/null +++ b/src/kiwano/renderer/LayerArea.h @@ -0,0 +1,59 @@ +// Copyright (c) 2016-2019 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "Geometry.h" + +namespace kiwano +{ + // 图层 + class KGE_API LayerArea + { + public: + LayerArea(); + + bool IsValid() const; + + Size GetSize() const; + + inline Rect const& GetAreaRect() const { return area_; } + + inline void SetAreaRect(Rect const& area) { area_ = area; } + + inline float GetOpacity() const { return opacity_; } + + inline void SetOpacity(float opacity) { opacity_ = opacity; } + + inline Geometry const& GetMaskGeometry() const { return mask_; } + + inline void SetMaskGeometry(Geometry const& mask) { mask_ = mask; } + + public: + inline ComPtr GetLayer() const { return layer_; } + + inline void SetLayer(ComPtr layer) { layer_ = layer; } + + protected: + Rect area_; + float opacity_; + Geometry mask_; + ComPtr layer_; + }; +} diff --git a/src/kiwano/renderer/RenderTarget.cpp b/src/kiwano/renderer/RenderTarget.cpp new file mode 100644 index 00000000..1403d109 --- /dev/null +++ b/src/kiwano/renderer/RenderTarget.cpp @@ -0,0 +1,660 @@ +// Copyright (c) 2016-2019 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "RenderTarget.h" +#include "../base/Logger.h" + +namespace kiwano +{ + // + // RenderTarget + // + + RenderTarget::RenderTarget() + : opacity_(1.f) + , collecting_status_(false) + , antialias_(true) + , text_antialias_(TextAntialias::GrayScale) + { + status_.primitives = 0; + } + + HRESULT RenderTarget::InitDeviceResources(ComPtr rt, ComPtr dev_res) + { + HRESULT hr = E_FAIL; + + if (rt && dev_res) + { + render_target_ = rt; + d2d_res_ = dev_res; + hr = S_OK; + } + + if (SUCCEEDED(hr)) + { + text_renderer_.reset(); + hr = ITextRenderer::Create( + &text_renderer_, + render_target_.get() + ); + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_.reset(); + hr = render_target_->CreateSolidColorBrush( + D2D1::ColorF(D2D1::ColorF::White), + D2D1::BrushProperties(), + &solid_color_brush_ + ); + } + + return hr; + } + + bool RenderTarget::IsValid() const + { + return render_target_ && d2d_res_; + } + + void RenderTarget::BeginDraw() + { + HRESULT hr = E_FAIL; + + if (collecting_status_) + { + status_.start = Time::Now(); + status_.primitives = 0; + } + + if (render_target_) + { + render_target_->BeginDraw(); + hr = S_OK; + } + + ThrowIfFailed(hr); + } + + void RenderTarget::EndDraw() + { + ThrowIfFailed(render_target_->EndDraw()); + + if (collecting_status_) + { + status_.duration = Time::Now() - status_.start; + } + } + + void RenderTarget::DrawGeometry( + Geometry const& geometry, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke + ) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr) && geometry.GetGeometry()) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); + + render_target_->DrawGeometry( + geometry.GetGeometry().get(), + solid_color_brush_.get(), + stroke_width, + d2d_res_->GetStrokeStyle(stroke) + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::FillGeometry(Geometry const& geometry, Color const& fill_color) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr) && geometry.GetGeometry()) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); + render_target_->FillGeometry( + geometry.GetGeometry().get(), + solid_color_brush_.get() + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawLine(Point const& point1, Point const& point2, Color const& stroke_color, float stroke_width, StrokeStyle stroke) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); + + render_target_->DrawLine( + DX::ConvertToPoint2F(point1), + DX::ConvertToPoint2F(point2), + solid_color_brush_.get(), + stroke_width, + d2d_res_->GetStrokeStyle(stroke) + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawRectangle(Rect const& rect, Color const& stroke_color, float stroke_width, StrokeStyle stroke) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); + + render_target_->DrawRectangle( + DX::ConvertToRectF(rect), + solid_color_brush_.get(), + stroke_width, + d2d_res_->GetStrokeStyle(stroke) + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::FillRectangle(Rect const& rect, Color const& fill_color) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); + render_target_->FillRectangle( + DX::ConvertToRectF(rect), + solid_color_brush_.get() + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, Color const& stroke_color, float stroke_width, StrokeStyle stroke) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); + + render_target_->DrawRoundedRectangle( + D2D1::RoundedRect( + DX::ConvertToRectF(rect), + radius.x, + radius.y + ), + solid_color_brush_.get(), + stroke_width, + d2d_res_->GetStrokeStyle(stroke) + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::FillRoundedRectangle(Rect const& rect, Vec2 const& radius, Color const& fill_color) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); + render_target_->FillRoundedRectangle( + D2D1::RoundedRect( + DX::ConvertToRectF(rect), + radius.x, + radius.y + ), + solid_color_brush_.get() + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawEllipse(Point const& center, Vec2 const& radius, Color const& stroke_color, float stroke_width, StrokeStyle stroke) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); + + render_target_->DrawEllipse( + D2D1::Ellipse( + DX::ConvertToPoint2F(center), + radius.x, + radius.y + ), + solid_color_brush_.get(), + stroke_width, + d2d_res_->GetStrokeStyle(stroke) + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::FillEllipse(Point const& center, Vec2 const& radius, Color const& fill_color) const + { + HRESULT hr = S_OK; + if (!solid_color_brush_ || !render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); + render_target_->FillEllipse( + D2D1::Ellipse( + DX::ConvertToPoint2F(center), + radius.x, + radius.y + ), + solid_color_brush_.get() + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawImage(Image const& image, Rect const& src_rect, Rect const& dest_rect) const + { + DrawImage(image, &src_rect, &dest_rect); + } + + void RenderTarget::DrawImage(Image const& image, const Rect* src_rect, const Rect* dest_rect) const + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr) && image.IsValid()) + { + render_target_->DrawBitmap( + image.GetBitmap().get(), + dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, + opacity_, + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, + src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr + ); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::DrawTextLayout(TextLayout const& layout, Point const& offset) const + { + HRESULT hr = S_OK; + if (!text_renderer_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + text_renderer_->SetTextStyle( + opacity_, + DX::ConvertToColorF(layout.GetTextStyle().color), + layout.GetTextStyle().outline, + DX::ConvertToColorF(layout.GetTextStyle().outline_color), + layout.GetTextStyle().outline_width, + d2d_res_->GetStrokeStyle(layout.GetTextStyle().outline_stroke) + ); + } + + if (SUCCEEDED(hr)) + { + hr = layout.GetTextLayout()->Draw(nullptr, text_renderer_.get(), offset.x, offset.y); + + IncreasePrimitivesCount(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::CreateLayer(LayerArea& layer) const + { + HRESULT hr = S_OK; + + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + ComPtr output; + hr = render_target_->CreateLayer(&output); + + if (SUCCEEDED(hr)) + { + layer.SetLayer(output); + } + } + + ThrowIfFailed(hr); + } + + void RenderTarget::PushClipRect(Rect const& clip_rect) + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->PushAxisAlignedClip( + DX::ConvertToRectF(clip_rect), + antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::PopClipRect() + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->PopAxisAlignedClip(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::PushLayer(LayerArea const& layer) + { + HRESULT hr = S_OK; + if (!render_target_ || !solid_color_brush_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr) && layer.IsValid()) + { + render_target_->PushLayer( + D2D1::LayerParameters( + DX::ConvertToRectF(layer.GetAreaRect()), + layer.GetMaskGeometry().GetGeometry().get(), + antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED, + D2D1::Matrix3x2F::Identity(), + layer.GetOpacity(), + nullptr, + D2D1_LAYER_OPTIONS_NONE + ), + layer.GetLayer().get() + ); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::PopLayer() + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->PopLayer(); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::Clear() + { + HRESULT hr = E_FAIL; + + if (render_target_) + { + render_target_->Clear(); + hr = S_OK; + } + + ThrowIfFailed(hr); + } + + void RenderTarget::Clear(Color const& clear_color) + { + HRESULT hr = E_FAIL; + + if (render_target_) + { + render_target_->Clear(DX::ConvertToColorF(clear_color)); + hr = S_OK; + } + + ThrowIfFailed(hr); + } + + float RenderTarget::GetOpacity() const + { + return opacity_; + } + + void RenderTarget::SetTransform(const Matrix3x2& matrix) + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::SetOpacity(float opacity) + { + HRESULT hr = S_OK; + if (!solid_color_brush_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + if (opacity_ != opacity) + { + opacity_ = opacity; + solid_color_brush_->SetOpacity(opacity); + } + } + + ThrowIfFailed(hr); + } + + void RenderTarget::SetAntialiasMode(bool enabled) + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->SetAntialiasMode( + enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED + ); + antialias_ = enabled; + } + + ThrowIfFailed(hr); + } + + void RenderTarget::SetTextAntialiasMode(TextAntialias mode) + { + HRESULT hr = S_OK; + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + text_antialias_ = mode; + D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + switch (text_antialias_) + { + case TextAntialias::Default: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; + break; + case TextAntialias::ClearType: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + break; + case TextAntialias::GrayScale: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + break; + case TextAntialias::None: + antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; + break; + default: + break; + } + render_target_->SetTextAntialiasMode(antialias_mode); + } + + ThrowIfFailed(hr); + } + + void RenderTarget::SetCollectingStatus(bool collecting) + { + collecting_status_ = collecting; + } + + void RenderTarget::IncreasePrimitivesCount() const + { + if (collecting_status_) + { + ++status_.primitives; + } + } + + + // + // ImageRenderTarget + // + + ImageRenderTarget::ImageRenderTarget() + { + } + + void ImageRenderTarget::GetOutput(Image& output) const + { + HRESULT hr = E_FAIL; + + ComPtr bitmap_rt; + if (render_target_) + { + hr = render_target_->QueryInterface(&bitmap_rt); + } + + ComPtr bitmap; + if (SUCCEEDED(hr)) + { + hr = bitmap_rt->GetBitmap(&bitmap); + } + + if (SUCCEEDED(hr)) + { + output.SetBitmap(bitmap); + } + + ThrowIfFailed(hr); + } + +} diff --git a/src/kiwano/renderer/RenderTarget.h b/src/kiwano/renderer/RenderTarget.h new file mode 100644 index 00000000..308f8778 --- /dev/null +++ b/src/kiwano/renderer/RenderTarget.h @@ -0,0 +1,207 @@ +// Copyright (c) 2016-2019 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "../base/time.h" +#include "Image.h" +#include "Geometry.h" +#include "TextLayout.h" +#include "LayerArea.h" +#include "win32/TextRenderer.h" + +namespace kiwano +{ + // 渲染目标 + class KGE_API RenderTarget + : public noncopyable + { + public: + void BeginDraw(); + + void EndDraw(); + + void CreateLayer( + LayerArea& layer + ) const; + + void DrawGeometry( + Geometry const& geometry, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke = StrokeStyle::Miter + ) const; + + void FillGeometry( + Geometry const& geometry, + Color const& fill_color + ) const; + + void DrawLine( + Point const& point1, + Point const& point2, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke = StrokeStyle::Miter + ) const; + + void DrawRectangle( + Rect const& rect, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke = StrokeStyle::Miter + ) const; + + void FillRectangle( + Rect const& rect, + Color const& fill_color + ) const; + + void DrawRoundedRectangle( + Rect const& rect, + Vec2 const& radius, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke = StrokeStyle::Miter + ) const; + + void FillRoundedRectangle( + Rect const& rect, + Vec2 const& radius, + Color const& fill_color + ) const; + + void DrawEllipse( + Point const& center, + Vec2 const& radius, + Color const& stroke_color, + float stroke_width, + StrokeStyle stroke = StrokeStyle::Miter + ) const; + + void FillEllipse( + Point const& center, + Vec2 const& radius, + Color const& fill_color + ) const; + + void DrawImage( + Image const& image, + Rect const& src_rect, + Rect const& dest_rect + ) const; + + void DrawImage( + Image const& image, + const Rect* src_rect = nullptr, + const Rect* dest_rect = nullptr + ) const; + + void DrawTextLayout( + TextLayout const& layout, + Point const& offset = Point{} + ) const; + + void PushClipRect( + Rect const& clip_rect + ); + + void PopClipRect(); + + void PushLayer( + LayerArea const& layer + ); + + void PopLayer(); + + void Clear(); + + void Clear( + Color const& clear_color + ); + + float GetOpacity() const; + + void SetOpacity( + float opacity + ); + + void SetTransform( + const Matrix3x2& matrix + ); + + // 设置抗锯齿模式 + void SetAntialiasMode( + bool enabled + ); + + // 设置文字抗锯齿模式 + void SetTextAntialiasMode( + TextAntialias mode + ); + + public: + struct Status + { + int primitives; + Time start; + Duration duration; + }; + + void SetCollectingStatus(bool collecting); + + void IncreasePrimitivesCount() const; + + inline Status const& GetStatus() const { return status_; } + + inline ComPtr GetRenderTarget() const { return render_target_; } + + public: + RenderTarget(); + + HRESULT InitDeviceResources( + ComPtr rt, + ComPtr dev_res + ); + + bool IsValid() const; + + protected: + float opacity_; + bool antialias_; + mutable bool collecting_status_; + mutable Status status_; + TextAntialias text_antialias_; + ComPtr render_target_; + ComPtr text_renderer_; + ComPtr solid_color_brush_; + ComPtr d2d_res_; + }; + + + // 位图渲染目标 + class KGE_API ImageRenderTarget + : public RenderTarget + { + public: + ImageRenderTarget(); + + void GetOutput(Image& output) const; + }; +} diff --git a/src/kiwano/renderer/Renderer.cpp b/src/kiwano/renderer/Renderer.cpp index 77361827..74bb8f18 100644 --- a/src/kiwano/renderer/Renderer.cpp +++ b/src/kiwano/renderer/Renderer.cpp @@ -21,19 +21,15 @@ #include "Renderer.h" #include "../base/Logger.h" #include "../base/Window.h" +#include "../utils/FileUtil.h" namespace kiwano { Renderer::Renderer() : hwnd_(nullptr) - , antialias_(true) , vsync_(true) - , text_antialias_(TextAntialias::GrayScale) , clear_color_(Color::Black) - , opacity_(1.f) - , collecting_status_(false) { - status_.primitives = 0; } Renderer::~Renderer() @@ -45,54 +41,97 @@ namespace kiwano KGE_LOG(L"Creating device resources"); hwnd_ = Window::GetInstance()->GetHandle(); + output_size_ = Window::GetInstance()->GetSize(); - ThrowIfFailed(hwnd_ ? S_OK : E_FAIL); - d2d_res_ = nullptr; d3d_res_ = nullptr; drawing_state_block_ = nullptr; - ThrowIfFailed( - ID2DDeviceResources::Create( - &d2d_res_ - ) - ); + HRESULT hr = hwnd_ ? S_OK : E_FAIL; - ThrowIfFailed( + // Direct2D device resources + if (SUCCEEDED(hr)) + { + hr = ID2DDeviceResources::Create(&d2d_res_); + } + + // Direct3D device resources + if (SUCCEEDED(hr)) + { #if defined(KGE_USE_DIRECTX10) - ID3D10DeviceResources::Create( + hr = ID3D10DeviceResources::Create( &d3d_res_, d2d_res_.get(), hwnd_ - ) + ); #else - ID3D11DeviceResources::Create( + hr = ID3D11DeviceResources::Create( &d3d_res_, d2d_res_.get(), hwnd_ - ) + ); #endif - ); + } - device_context_ = d2d_res_->GetDeviceContext(); - - ThrowIfFailed( - d2d_res_->GetFactory()->CreateDrawingStateBlock( + // DrawingStateBlock + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetFactory()->CreateDrawingStateBlock( &drawing_state_block_ - ) - ); + ); + } - ThrowIfFailed( - CreateDeviceResources() - ); + // Other device resources + if (SUCCEEDED(hr)) + { + hr = CreateDeviceResources(); + } - output_size_ = Window::GetInstance()->GetSize(); + // FontFileLoader and FontCollectionLoader + if (SUCCEEDED(hr)) + { + hr = IFontCollectionLoader::Create(&font_collection_loader_); + } + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader(font_collection_loader_.get()); + } + + // ResourceFontFileLoader and ResourceFontCollectionLoader + if (SUCCEEDED(hr)) + { + hr = IResourceFontFileLoader::Create(&res_font_file_loader_); + } + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontFileLoader(res_font_file_loader_.get()); + } + + if (SUCCEEDED(hr)) + { + hr = IResourceFontCollectionLoader::Create(&res_font_collection_loader_, res_font_file_loader_.get()); + } + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetDWriteFactory()->RegisterFontCollectionLoader(res_font_collection_loader_.get()); + } + + ThrowIfFailed(hr); } void Renderer::DestroyComponent() { KGE_LOG(L"Destroying device resources"); + d2d_res_->GetDWriteFactory()->UnregisterFontFileLoader(res_font_file_loader_.get()); + res_font_file_loader_.reset(); + + d2d_res_->GetDWriteFactory()->UnregisterFontCollectionLoader(res_font_collection_loader_.get()); + res_font_collection_loader_.reset(); + drawing_state_block_.reset(); solid_color_brush_.reset(); d2d_res_.reset(); @@ -101,16 +140,55 @@ namespace kiwano void Renderer::BeforeRender() { - ThrowIfFailed( - BeginDraw() - ); + HRESULT hr = S_OK; + + if (!IsValid()) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->SaveDrawingState(drawing_state_block_.get()); + BeginDraw(); + } + + if (SUCCEEDED(hr)) + { + hr = d3d_res_->ClearRenderTarget(clear_color_); + } + + ThrowIfFailed(hr); } void Renderer::AfterRender() { - ThrowIfFailed( - EndDraw() - ); + HRESULT hr = S_OK; + + if (!IsValid()) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + EndDraw(); + + render_target_->RestoreDrawingState(drawing_state_block_.get()); + } + + if (SUCCEEDED(hr)) + { + hr = d3d_res_->Present(vsync_); + } + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) + { + // 如果 Direct3D 设备在执行过程中消失,将丢弃当前的设备相关资源 + hr = HandleDeviceLost(); + } + + ThrowIfFailed(hr); } void Renderer::HandleMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) @@ -130,24 +208,11 @@ namespace kiwano HRESULT Renderer::CreateDeviceResources() { - HRESULT hr = S_OK; - - solid_color_brush_.reset(); - hr = device_context_->CreateSolidColorBrush( - D2D1::ColorF(D2D1::ColorF::White), - D2D1::BrushProperties(), - &solid_color_brush_ + HRESULT hr = InitDeviceResources( + d2d_res_->GetDeviceContext(), + d2d_res_ ); - if (SUCCEEDED(hr)) - { - text_renderer_.reset(); - hr = ITextRenderer::Create( - &text_renderer_, - device_context_.get() - ); - } - if (SUCCEEDED(hr)) { SetAntialiasMode(antialias_); @@ -167,79 +232,273 @@ namespace kiwano return hr; } - HRESULT Renderer::BeginDraw() - { - if (!device_context_) - return E_UNEXPECTED; - - if (collecting_status_) - { - status_.start = Time::Now(); - status_.primitives = 0; - } - - device_context_->SaveDrawingState(drawing_state_block_.get()); - - device_context_->BeginDraw(); - - HRESULT hr = d3d_res_->ClearRenderTarget(clear_color_); - - return hr; - } - - HRESULT Renderer::EndDraw() - { - if (!device_context_) - return E_UNEXPECTED; - - HRESULT hr = device_context_->EndDraw(); - - device_context_->RestoreDrawingState(drawing_state_block_.get()); - - if (SUCCEEDED(hr)) - { - hr = d3d_res_->Present(vsync_); - } - - if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) - { - // 如果 Direct3D 设备在执行过程中消失,将丢弃当前的设备相关资源 - hr = HandleDeviceLost(); - } - - if (collecting_status_) - { - status_.duration = Time::Now() - status_.start; - } - return hr; - } - - void Renderer::IncreasePrimitivesCount() - { - if (collecting_status_) - { - ++status_.primitives; - } - } - - void Renderer::CreateLayer(ComPtr& layer) + void Renderer::CreateImage(Image& image, String const& file_path) { HRESULT hr = S_OK; - ComPtr new_layer; - - if (!device_context_) + if (!d2d_res_) { hr = E_UNEXPECTED; } if (SUCCEEDED(hr)) { - hr = device_context_->CreateLayer(&new_layer); + ComPtr bitmap; + hr = d2d_res_->CreateBitmapFromFile(bitmap, file_path); + + if (SUCCEEDED(hr)) + { + image.SetBitmap(bitmap); + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load image failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateImage(Image& image, Resource const& res) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; } if (SUCCEEDED(hr)) { - layer = new_layer; + ComPtr bitmap; + hr = d2d_res_->CreateBitmapFromResource(bitmap, res); + + if (SUCCEEDED(hr)) + { + image.SetBitmap(bitmap); + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load image failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateGifImage(GifImage& image, String const& file_path) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (!FileUtil::ExistsFile(file_path)) + { + KGE_WARNING_LOG(L"Gif image file '%s' not found!", file_path.c_str()); + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + ComPtr decoder; + hr = d2d_res_->GetWICImagingFactory()->CreateDecoderFromFilename( + file_path.c_str(), + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnLoad, + &decoder + ); + + if (SUCCEEDED(hr)) + { + image.SetDecoder(decoder); + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load GIF image failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateGifImage(GifImage& image, Resource const& res) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + Resource::Data res_data = res.GetData(); + + hr = res_data ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + ComPtr stream; + hr = d2d_res_->GetWICImagingFactory()->CreateStream(&stream); + + if (SUCCEEDED(hr)) + { + hr = stream->InitializeFromMemory( + static_cast(res_data.buffer), + res_data.size + ); + } + + if (SUCCEEDED(hr)) + { + ComPtr decoder; + hr = d2d_res_->GetWICImagingFactory()->CreateDecoderFromStream( + stream.get(), + nullptr, + WICDecodeMetadataCacheOnLoad, + &decoder + ); + + if (SUCCEEDED(hr)) + { + image.SetDecoder(decoder); + } + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load GIF image failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateFontCollection(FontCollection& collection, Vector const& file_paths) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + for (const auto& file_path : file_paths) + { + if (!FileUtil::ExistsFile(file_path)) + { + KGE_WARNING_LOG(L"Font file '%s' not found!", file_path.c_str()); + hr = E_FAIL; + } + } + } + + if (SUCCEEDED(hr)) + { + LPVOID collection_key = nullptr; + UINT32 collection_key_size = 0; + + hr = font_collection_loader_->AddFilePaths(file_paths, &collection_key, &collection_key_size); + + if (SUCCEEDED(hr)) + { + ComPtr font_collection; + hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection( + font_collection_loader_.get(), + collection_key, + collection_key_size, + &font_collection + ); + + if (SUCCEEDED(hr)) + { + collection.SetFontCollection(font_collection); + } + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load font failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateFontCollection(FontCollection& collection, Vector const& res_arr) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + LPVOID collection_key = nullptr; + UINT32 collection_key_size = 0; + + hr = res_font_collection_loader_->AddResources(res_arr, &collection_key, &collection_key_size); + + if (SUCCEEDED(hr)) + { + ComPtr font_collection; + hr = d2d_res_->GetDWriteFactory()->CreateCustomFontCollection( + res_font_collection_loader_.get(), + collection_key, + collection_key_size, + &font_collection + ); + + if (SUCCEEDED(hr)) + { + collection.SetFontCollection(font_collection); + } + } + } + + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load font failed with HRESULT of %08X!", hr); + } + } + + void Renderer::CreateTextFormat(TextFormat& format, Font const& font) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->CreateTextFormat(output, font); + } + + if (SUCCEEDED(hr)) + { + format.SetTextFormat(output); + } + + ThrowIfFailed(hr); + } + + void Renderer::CreateTextLayout(TextLayout& layout, String const& text, TextStyle const& style, TextFormat const& format) + { + HRESULT hr = S_OK; + if (!d2d_res_) + { + hr = E_UNEXPECTED; + } + + ComPtr output; + if (SUCCEEDED(hr)) + { + hr = d2d_res_->CreateTextLayout( + output, + text, + style, + format.GetTextFormat() + ); + } + + if (SUCCEEDED(hr)) + { + layout.SetTextLayout(output); } ThrowIfFailed(hr); @@ -248,7 +507,7 @@ namespace kiwano void Renderer::CreateLineGeometry(Geometry& geo, Point const& begin_pos, Point const& end_pos) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -284,7 +543,7 @@ namespace kiwano void Renderer::CreateRectGeometry(Geometry& geo, Rect const& rect) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -306,7 +565,7 @@ namespace kiwano void Renderer::CreateRoundedRectGeometry(Geometry& geo, Rect const& rect, Vec2 const& radius) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -334,7 +593,7 @@ namespace kiwano void Renderer::CreateEllipseGeometry(Geometry& geo, Point const& center, Vec2 const& radius) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -362,7 +621,7 @@ namespace kiwano void Renderer::CreatePathGeometrySink(GeometrySink& sink) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -381,138 +640,23 @@ namespace kiwano ThrowIfFailed(hr); } - void Renderer::DrawGeometry( - Geometry const& geometry, - Color const& stroke_color, - float stroke_width, - StrokeStyle stroke - ) + void Renderer::CreateImageRenderTarget(ImageRenderTarget& render_target) { HRESULT hr = S_OK; - if (!solid_color_brush_ || !device_context_) + if (!d2d_res_) { hr = E_UNEXPECTED; } - if (SUCCEEDED(hr) && geometry.GetGeometry()) + ComPtr output; + if (SUCCEEDED(hr)) { - solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); - - device_context_->DrawGeometry( - geometry.GetGeometry().get(), - solid_color_brush_.get(), - stroke_width, - d2d_res_->GetStrokeStyle(stroke) - ); - - IncreasePrimitivesCount(); - } - - ThrowIfFailed(hr); - } - - void Renderer::FillGeometry(Geometry const & geometry, Color const& fill_color) - { - HRESULT hr = S_OK; - if (!solid_color_brush_ || !device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr) && geometry.GetGeometry()) - { - solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); - device_context_->FillGeometry( - geometry.GetGeometry().get(), - solid_color_brush_.get() - ); - } - - ThrowIfFailed(hr); - } - - void Renderer::DrawRectangle(Rect const& rect, Color const& stroke_color, float stroke_width, StrokeStyle stroke) - { - HRESULT hr = S_OK; - if (!solid_color_brush_ || !device_context_) - { - hr = E_UNEXPECTED; + hr = d2d_res_->GetDeviceContext()->CreateCompatibleRenderTarget(&output); } if (SUCCEEDED(hr)) { - solid_color_brush_->SetColor(DX::ConvertToColorF(stroke_color)); - - device_context_->DrawRectangle( - DX::ConvertToRectF(rect), - solid_color_brush_.get(), - stroke_width, - d2d_res_->GetStrokeStyle(stroke) - ); - - IncreasePrimitivesCount(); - } - - ThrowIfFailed(hr); - } - - void Renderer::FillRectangle(Rect const& rect, Color const& fill_color) - { - HRESULT hr = S_OK; - if (!solid_color_brush_ || !device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - solid_color_brush_->SetColor(DX::ConvertToColorF(fill_color)); - device_context_->FillRectangle( - DX::ConvertToRectF(rect), - solid_color_brush_.get() - ); - } - - ThrowIfFailed(hr); - } - - void Renderer::DrawBitmap(ComPtr const & bitmap, Rect const& src_rect, Rect const& dest_rect) - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr) && bitmap) - { - device_context_->DrawBitmap( - bitmap.get(), - DX::ConvertToRectF(dest_rect), - opacity_, - D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, - DX::ConvertToRectF(src_rect) - ); - - IncreasePrimitivesCount(); - } - - ThrowIfFailed(hr); - } - - void Renderer::DrawTextLayout(ComPtr const& text_layout) - { - HRESULT hr = S_OK; - if (!text_renderer_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - hr = text_layout->Draw(nullptr, text_renderer_.get(), 0, 0); - - IncreasePrimitivesCount(); + hr = render_target.InitDeviceResources(output, d2d_res_); } ThrowIfFailed(hr); @@ -523,85 +667,6 @@ namespace kiwano vsync_ = enabled; } - void Renderer::PushClip(const Matrix3x2 & clip_matrix, const Size & clip_size) - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->SetTransform(DX::ConvertToMatrix3x2F(clip_matrix)); - device_context_->PushAxisAlignedClip( - D2D1::RectF(0, 0, clip_size.x, clip_size.y), - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE - ); - } - - ThrowIfFailed(hr); - } - - void Renderer::PopClip() - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->PopAxisAlignedClip(); - } - - ThrowIfFailed(hr); - } - - void Renderer::PushLayer(ComPtr const& layer, LayerProperties const& properties) - { - HRESULT hr = S_OK; - if (!device_context_ || !solid_color_brush_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->PushLayer( - D2D1::LayerParameters( - DX::ConvertToRectF(properties.area), - nullptr, - D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - D2D1::Matrix3x2F::Identity(), - properties.opacity, - nullptr, - D2D1_LAYER_OPTIONS_NONE - ), - layer.get() - ); - } - - ThrowIfFailed(hr); - } - - void Renderer::PopLayer() - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->PopLayer(); - } - - ThrowIfFailed(hr); - } - void Renderer::Resize(UINT width, UINT height) { HRESULT hr = S_OK; @@ -620,136 +685,11 @@ namespace kiwano ThrowIfFailed(hr); } - void Renderer::SetCollectingStatus(bool collecting) - { - collecting_status_ = collecting; - } - - void Renderer::SetClearColor(const Color & color) + void Renderer::SetClearColor(const Color& color) { clear_color_ = color; } - void Renderer::SetTransform(const Matrix3x2 & matrix) - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); - } - - ThrowIfFailed(hr); - } - - void Renderer::SetOpacity(float opacity) - { - HRESULT hr = S_OK; - if (!solid_color_brush_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - if (opacity_ != opacity) - { - opacity_ = opacity; - solid_color_brush_->SetOpacity(opacity); - } - } - - ThrowIfFailed(hr); - } - - void Renderer::SetTextStyle( - float opacity, - Color const& color, - bool has_outline, - Color const& outline_color, - float outline_width, - StrokeStyle outline_stroke - ) - { - HRESULT hr = S_OK; - if (!text_renderer_ || !d3d_res_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - text_renderer_->SetTextStyle( - opacity, - DX::ConvertToColorF(color), - has_outline, - DX::ConvertToColorF(outline_color), - outline_width, - d2d_res_->GetStrokeStyle(outline_stroke) - ); - } - - ThrowIfFailed(hr); - } - - void Renderer::SetAntialiasMode(bool enabled) - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - device_context_->SetAntialiasMode( - enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED - ); - antialias_ = enabled; - } - - ThrowIfFailed(hr); - } - - void Renderer::SetTextAntialiasMode(TextAntialias mode) - { - HRESULT hr = S_OK; - if (!device_context_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - text_antialias_ = mode; - D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - switch (text_antialias_) - { - case TextAntialias::Default: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; - break; - case TextAntialias::ClearType: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - break; - case TextAntialias::GrayScale: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; - break; - case TextAntialias::None: - antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; - break; - default: - break; - } - device_context_->SetTextAntialiasMode(antialias_mode); - } - - ThrowIfFailed(hr); - } - bool Renderer::CheckVisibility(Size const& content_size, Matrix3x2 const& transform) { return Rect{ Point{}, output_size_ }.Intersects( diff --git a/src/kiwano/renderer/Renderer.h b/src/kiwano/renderer/Renderer.h index a365a6cb..a0fc0d39 100644 --- a/src/kiwano/renderer/Renderer.h +++ b/src/kiwano/renderer/Renderer.h @@ -19,21 +19,15 @@ // THE SOFTWARE. #pragma once -#include "../base/time.h" #include "../base/Component.h" -#include "../base/Resource.h" -#include "../2d/include-forwards.h" -#include "../2d/Font.hpp" -#include "../2d/TextStyle.hpp" -#include "helper.hpp" -#include "Image.h" -#include "Geometry.h" -#include "TextRenderer.h" +#include "win32/FontCollectionLoader.h" +#include "RenderTarget.h" +#include "GifImage.h" #if defined(KGE_USE_DIRECTX10) # include "D3D10DeviceResources.h" #else -# include "D3D11DeviceResources.h" +# include "win32/D3D11DeviceResources.h" #endif namespace kiwano @@ -48,12 +42,62 @@ namespace kiwano class KGE_API Renderer : public Singleton , public Component + , public RenderTarget { KGE_DECLARE_SINGLETON(Renderer); public: - void CreateLayer( - ComPtr& layer + // 设置清屏颜色 + void SetClearColor( + Color const& clear_color + ); + + // 开启或关闭垂直同步 + void SetVSyncEnabled( + bool enabled + ); + + public: + void CreateImage( + Image& image, + String const& file_path + ); + + void CreateImage( + Image& image, + Resource const& res + ); + + void CreateGifImage( + GifImage& image, + String const& file_path + ); + + void CreateGifImage( + GifImage& image, + Resource const& res + ); + + void CreateFontCollection( + FontCollection& collection, + Vector const& file_paths + ); + + void CreateFontCollection( + FontCollection& collection, + Vector const& res_arr + ); + + void CreateTextFormat( + TextFormat& format, + Font const& font + ); + + void CreateTextLayout( + TextLayout& layout, + String const& text, + TextStyle const& style, + TextFormat const& format ); void CreateLineGeometry( @@ -83,92 +127,10 @@ namespace kiwano GeometrySink& sink ); - void DrawGeometry( - Geometry const& geometry, - const Color& stroke_color, - float stroke_width, - StrokeStyle stroke = StrokeStyle::Miter + void CreateImageRenderTarget( + ImageRenderTarget& render_target ); - void FillGeometry( - Geometry const& geometry, - Color const& fill_color - ); - - void DrawRectangle( - Rect const& rect, - Color const& stroke_color, - float stroke_width, - StrokeStyle stroke = StrokeStyle::Miter - ); - - void FillRectangle( - Rect const& rect, - Color const& fill_color - ); - - void DrawBitmap( - ComPtr const& bitmap, - Rect const& src_rect, - Rect const& dest_rect - ); - - void DrawTextLayout( - ComPtr const& text_layout - ); - - // 设置清屏颜色 - void SetClearColor( - Color const& clear_color - ); - - // 设置抗锯齿模式 - void SetAntialiasMode( - bool enabled - ); - - // 设置文字抗锯齿模式 - void SetTextAntialiasMode( - TextAntialias mode - ); - - // 开启或关闭垂直同步 - void SetVSyncEnabled( - bool enabled - ); - - // 设置画笔透明度 - void SetOpacity( - float opacity - ); - - void SetTransform( - const Matrix3x2& matrix - ); - - void SetTextStyle( - float opacity, - const Color& color, - bool has_outline, - const Color& outline_color, - float outline_width, - StrokeStyle outline_stroke - ); - - void PushClip( - const Matrix3x2& clip_matrix, - const Size& clip_size - ); - - void PopClip(); - - void PushLayer( - ComPtr const& layer, - LayerProperties const& properties - ); - - void PopLayer(); - void Resize( UINT width, UINT height @@ -190,20 +152,9 @@ namespace kiwano void HandleMessage(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) override; - void SetCollectingStatus(bool collecting); - public: - struct Status - { - Time start; - Duration duration; - int primitives; - }; - inline HWND GetTargetWindow() const { return hwnd_; } - inline Status const& GetStatus() const { return status_; } - inline Size const& GetOutputSize() const { return output_size_; } inline ID2DDeviceResources* GetD2DDeviceResources() const { KGE_ASSERT(d2d_res_); return d2d_res_.get(); } @@ -223,28 +174,18 @@ namespace kiwano HRESULT HandleDeviceLost(); - HRESULT BeginDraw(); - - HRESULT EndDraw(); - - void IncreasePrimitivesCount(); - private: - bool vsync_; - bool antialias_; - bool collecting_status_; - float opacity_; - HWND hwnd_; - Size output_size_; - Color clear_color_; - TextAntialias text_antialias_; - Status status_; + bool vsync_; + HWND hwnd_; + Size output_size_; + Color clear_color_; ComPtr d2d_res_; ComPtr d3d_res_; - ComPtr device_context_; ComPtr drawing_state_block_; - ComPtr text_renderer_; - ComPtr solid_color_brush_; + + ComPtr font_collection_loader_; + ComPtr res_font_file_loader_; + ComPtr res_font_collection_loader_; }; } diff --git a/src/kiwano/renderer/TextLayout.cpp b/src/kiwano/renderer/TextLayout.cpp new file mode 100644 index 00000000..a18247d6 --- /dev/null +++ b/src/kiwano/renderer/TextLayout.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "TextLayout.h" +#include "Renderer.h" +#include "../base/Logger.h" + +namespace kiwano +{ + // + // TextFormat + // + + TextFormat::TextFormat() + { + } + + TextFormat::TextFormat(Font const& font) + { + Update(font); + } + + void TextFormat::Update(Font const& font) + { + Renderer::GetInstance()->CreateTextFormat(*this, font); + } + + + // + // TextLayout + // + + TextLayout::TextLayout() + { + } + + TextLayout::TextLayout(String const& text, Font const& font, TextStyle const& style) + { + Update(font); + Update(text, style); + } + + void TextLayout::Update(Font const& font) + { + text_format_.Update(font); + } + + void TextLayout::Update(String const& text, TextStyle const& style) + { + style_ = style; + + if (text.empty()) + { + text_format_.SetTextFormat(nullptr); + SetTextLayout(nullptr); + return; + } + + Renderer::GetInstance()->CreateTextLayout( + *this, + text, + style, + text_format_ + ); + } + + UINT32 TextLayout::GetLineCount() + { + if (text_layout_) + { + DWRITE_TEXT_METRICS metrics; + if (SUCCEEDED(text_layout_->GetMetrics(&metrics))) + { + return metrics.lineCount; + } + } + return 0; + } + + Size TextLayout::GetLayoutSize() const + { + if (text_layout_) + { + DWRITE_TEXT_METRICS metrics; + if (SUCCEEDED(text_layout_->GetMetrics(&metrics))) + { + return (metrics.layoutWidth > 0) ? Size(metrics.layoutWidth, metrics.height) : Size(metrics.width, metrics.height); + } + } + return Size(); + } + +} diff --git a/src/kiwano/renderer/TextLayout.h b/src/kiwano/renderer/TextLayout.h new file mode 100644 index 00000000..d6b23a92 --- /dev/null +++ b/src/kiwano/renderer/TextLayout.h @@ -0,0 +1,79 @@ +// 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 "Font.h" +#include "../2d/TextStyle.hpp" + +namespace kiwano +{ + // 文本格式化 + class KGE_API TextFormat + { + public: + TextFormat(); + + TextFormat(Font const& font); + + void Update(Font const& font); + + public: + inline ComPtr GetTextFormat() const { return text_format_; } + + inline void SetTextFormat(ComPtr format) { text_format_ = format; } + + protected: + ComPtr text_format_; + }; + + + // 文本布局 + class KGE_API TextLayout + { + public: + TextLayout(); + + TextLayout(String const& text, Font const& font, TextStyle const& style); + + void Update(Font const& font); + + void Update(String const& text, TextStyle const& style); + + UINT32 GetLineCount(); + + Size GetLayoutSize() const; + + inline TextStyle const& GetTextStyle() const { return style_; } + + public: + inline TextFormat GetTextFormat() const { return text_format_; } + + inline ComPtr GetTextLayout() const { return text_layout_; } + + inline void SetTextLayout(ComPtr layout) { text_layout_ = layout; } + + inline operator bool() const { return static_cast(text_layout_); } + + protected: + TextStyle style_; + TextFormat text_format_; + ComPtr text_layout_; + }; +} diff --git a/src/kiwano/base/ComPtr.hpp b/src/kiwano/renderer/win32/ComPtr.hpp similarity index 97% rename from src/kiwano/base/ComPtr.hpp rename to src/kiwano/renderer/win32/ComPtr.hpp index cf525bf3..10812639 100644 --- a/src/kiwano/base/ComPtr.hpp +++ b/src/kiwano/renderer/win32/ComPtr.hpp @@ -19,7 +19,7 @@ // THE SOFTWARE. #pragma once -#include "../core/intrusive_ptr.hpp" +#include "../../core/intrusive_ptr.hpp" #include #include diff --git a/src/kiwano/renderer/D2DDeviceResources.cpp b/src/kiwano/renderer/win32/D2DDeviceResources.cpp similarity index 81% rename from src/kiwano/renderer/D2DDeviceResources.cpp rename to src/kiwano/renderer/win32/D2DDeviceResources.cpp index e930829a..9e5459f5 100644 --- a/src/kiwano/renderer/D2DDeviceResources.cpp +++ b/src/kiwano/renderer/win32/D2DDeviceResources.cpp @@ -19,9 +19,8 @@ // THE SOFTWARE. #include "D2DDeviceResources.h" -#include "ImageCache.h" -#include "../base/Logger.h" -#include "../utils/FileUtil.h" +#include "../../base/Logger.h" +#include "../../utils/FileUtil.h" #pragma comment(lib, "d2d1.lib") #pragma comment(lib, "dwrite.lib") @@ -51,16 +50,14 @@ namespace kiwano HRESULT CreateTextFormat( _Out_ ComPtr& text_format, - _In_ Font const& font, - _In_ TextStyle const& text_style + _In_ Font const& font ) const override; HRESULT CreateTextLayout( _Out_ ComPtr& text_layout, - _Out_ Size& layout_size, _In_ String const& text, - _In_ ComPtr const& text_format, - _In_ TextStyle const& text_style + _In_ TextStyle const& text_style, + _In_ ComPtr const& text_format ) const override; HRESULT SetD2DDevice( @@ -110,7 +107,10 @@ namespace kiwano { res->AddRef(); - DX::SafeRelease(*device_resources); + if (*device_resources) + { + (*device_resources)->Release(); + } (*device_resources) = res; } else @@ -174,8 +174,6 @@ namespace kiwano void D2DDeviceResources::DiscardResources() { - ImageCache::GetInstance()->Clear(); - factory_.reset(); device_.reset(); device_context_.reset(); @@ -193,9 +191,9 @@ namespace kiwano { HRESULT hr = S_OK; - ComPtr d2d_factory; - ComPtr imaging_factory; - ComPtr dwrite_factory; + ComPtr d2d_factory; + ComPtr imaging_factory; + ComPtr dwrite_factory; D2D1_FACTORY_OPTIONS options; ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS)); @@ -238,9 +236,9 @@ namespace kiwano { dwrite_factory_ = dwrite_factory; - ComPtr d2d_miter_stroke_style; - ComPtr d2d_bevel_stroke_style; - ComPtr d2d_round_stroke_style; + ComPtr d2d_miter_stroke_style; + ComPtr d2d_bevel_stroke_style; + ComPtr d2d_round_stroke_style; D2D1_STROKE_STYLE_PROPERTIES stroke_style = D2D1::StrokeStyleProperties( D2D1_CAP_STYLE_FLAT, @@ -394,9 +392,8 @@ namespace kiwano ComPtr bitmap_tmp; // 加载资源 - LPVOID buffer; - DWORD buffer_size; - HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL; + Resource::Data res_data = res.GetData(); + HRESULT hr = res_data ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { @@ -406,8 +403,8 @@ namespace kiwano if (SUCCEEDED(hr)) { hr = stream->InitializeFromMemory( - static_cast(buffer), - buffer_size + static_cast(res_data.buffer), + res_data.size ); } @@ -460,59 +457,41 @@ namespace kiwano return hr; } - HRESULT D2DDeviceResources::CreateTextFormat(_Out_ ComPtr & text_format, - _In_ Font const & font, _In_ TextStyle const & text_style) const + HRESULT D2DDeviceResources::CreateTextFormat(_Out_ ComPtr & text_format, _In_ Font const & font) const { if (!dwrite_factory_) return E_UNEXPECTED; - ComPtr text_format_tmp; + ComPtr output; HRESULT hr = dwrite_factory_->CreateTextFormat( font.family.c_str(), - nullptr, + font.collection.GetFontCollection().get(), DWRITE_FONT_WEIGHT(font.weight), font.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, font.size, - L"", - &text_format_tmp + L"en-us", + &output ); if (SUCCEEDED(hr)) { - if (text_style.line_spacing == 0.f) - { - text_format_tmp->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0); - } - else - { - text_format_tmp->SetLineSpacing( - DWRITE_LINE_SPACING_METHOD_UNIFORM, - text_style.line_spacing, - text_style.line_spacing * 0.8f - ); - } - text_format_tmp->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(text_style.alignment)); - text_format_tmp->SetWordWrapping(text_style.wrap ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP); - text_format = text_format_tmp; + text_format = output; } return hr; } - HRESULT D2DDeviceResources::CreateTextLayout(_Out_ ComPtr & text_layout, - _Out_ Size& layout_size, _In_ String const & text, _In_ ComPtr const& text_format, - _In_ TextStyle const & text_style) const + HRESULT D2DDeviceResources::CreateTextLayout(_Out_ ComPtr & text_layout, _In_ String const & text, + _In_ TextStyle const & text_style, _In_ ComPtr const& text_format) const { if (!dwrite_factory_) return E_UNEXPECTED; - text_layout = nullptr; - HRESULT hr; - ComPtr text_layout_tmp; + ComPtr output; UINT32 length = static_cast(text.length()); - if (text_style.wrap) + if (text_style.wrap_width > 0) { hr = dwrite_factory_->CreateTextLayout( text.c_str(), @@ -520,7 +499,7 @@ namespace kiwano text_format.get(), text_style.wrap_width, 0, - &text_layout_tmp + &output ); } else @@ -531,53 +510,70 @@ namespace kiwano text_format.get(), 0, 0, - &text_layout_tmp + &output ); + } - DWRITE_TEXT_METRICS metrics; - if (SUCCEEDED(hr)) + if (SUCCEEDED(hr)) + { + if (text_style.line_spacing == 0.f) { - hr = text_layout_tmp->GetMetrics(&metrics); + hr = output->SetLineSpacing(DWRITE_LINE_SPACING_METHOD_DEFAULT, 0, 0); } - - if (SUCCEEDED(hr)) + else { - text_layout_tmp = nullptr; - hr = dwrite_factory_->CreateTextLayout( - text.c_str(), - length, - text_format.get(), - metrics.width, - 0, - &text_layout_tmp + hr = output->SetLineSpacing( + DWRITE_LINE_SPACING_METHOD_UNIFORM, + text_style.line_spacing, + text_style.line_spacing * 0.8f ); } } if (SUCCEEDED(hr)) { - DWRITE_TEXT_METRICS metrics; - text_layout_tmp->GetMetrics(&metrics); + hr = output->SetTextAlignment(DWRITE_TEXT_ALIGNMENT(text_style.alignment)); + } - if (text_style.wrap) - { - layout_size = Size(metrics.layoutWidth, metrics.height); - } - else - { - layout_size = Size(metrics.width, metrics.height); - } + if (SUCCEEDED(hr)) + { + hr = output->SetWordWrapping((text_style.wrap_width > 0) ? DWRITE_WORD_WRAPPING_WRAP : DWRITE_WORD_WRAPPING_NO_WRAP); + } - DWRITE_TEXT_RANGE range = { 0, length }; + if (SUCCEEDED(hr)) + { if (text_style.underline) { - text_layout_tmp->SetUnderline(true, range); + hr = output->SetUnderline(true, { 0, length }); } + } + + if (SUCCEEDED(hr)) + { if (text_style.strikethrough) { - text_layout_tmp->SetStrikethrough(true, range); + output->SetStrikethrough(true, { 0, length }); } - text_layout = text_layout_tmp; + } + + if (SUCCEEDED(hr)) + { + // Fix the layout width when the text does not wrap + if (!(text_style.wrap_width > 0)) + { + DWRITE_TEXT_METRICS metrics; + hr = output->GetMetrics(&metrics); + + if (SUCCEEDED(hr)) + { + hr = output->SetMaxWidth(metrics.width); + } + } + } + + if (SUCCEEDED(hr)) + { + text_layout = output; } return hr; } diff --git a/src/kiwano/renderer/helper.hpp b/src/kiwano/renderer/win32/D2DDeviceResources.h similarity index 62% rename from src/kiwano/renderer/helper.hpp rename to src/kiwano/renderer/win32/D2DDeviceResources.h index 3c781b1b..c87bbfb6 100644 --- a/src/kiwano/renderer/helper.hpp +++ b/src/kiwano/renderer/win32/D2DDeviceResources.h @@ -19,10 +19,14 @@ // THE SOFTWARE. #pragma once -#include "../base/ComPtr.hpp" -#include "../math/helper.h" -#include "../2d/Color.h" +#include "../Font.h" +#include "../Color.h" +#include "../../math/math.h" +#include "../../base/Resource.h" +#include "../../2d/TextStyle.hpp" +#include #include +#include namespace kiwano { @@ -38,6 +42,16 @@ namespace kiwano } } + template + inline T* SafeAcquire(T* ptr) + { + if (ptr != nullptr) + { + ptr->AddRef(); + } + return ptr; + } + // // Point2F // @@ -150,3 +164,65 @@ namespace kiwano } } } + +namespace kiwano +{ + MIDL_INTERFACE("5706684a-bf6d-4b03-b627-094758a33032") + KGE_API ID2DDeviceResources + : public IUnknown + { + public: + static HRESULT Create(ID2DDeviceResources** device_resources); + + virtual HRESULT CreateBitmapFromFile( + _Out_ ComPtr& bitmap, + _In_ String const& file_path + ) = 0; + + virtual HRESULT CreateBitmapFromResource( + _Out_ ComPtr& bitmap, + _In_ Resource const& res + ) = 0; + + virtual HRESULT CreateTextFormat( + _Out_ ComPtr& text_format, + _In_ Font const& font + ) const = 0; + + virtual HRESULT CreateTextLayout( + _Out_ ComPtr& text_layout, + _In_ String const& text, + _In_ TextStyle const& text_style, + _In_ ComPtr const& text_format + ) const = 0; + + virtual ID2D1StrokeStyle* GetStrokeStyle(StrokeStyle stroke) const = 0; + + virtual HRESULT SetD2DDevice( + _In_ ComPtr const& device + ) = 0; + + virtual void SetTargetBitmap( + _In_ ComPtr const& target + ) = 0; + + virtual void DiscardResources() = 0; + + inline ID2D1Factory1* GetFactory() const { KGE_ASSERT(factory_); return factory_.get(); } + inline IWICImagingFactory* GetWICImagingFactory() const { KGE_ASSERT(imaging_factory_); return imaging_factory_.get(); } + inline IDWriteFactory* GetDWriteFactory() const { KGE_ASSERT(dwrite_factory_); return dwrite_factory_.get(); } + inline ID2D1Device* GetDevice() const { KGE_ASSERT(device_); return device_.get(); } + inline ID2D1DeviceContext* GetDeviceContext() const { KGE_ASSERT(device_context_); return device_context_.get(); } + inline ID2D1Bitmap1* GetTargetBitmap() const { 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/renderer/D3D10DeviceResources.cpp b/src/kiwano/renderer/win32/D3D10DeviceResources.cpp similarity index 99% rename from src/kiwano/renderer/D3D10DeviceResources.cpp rename to src/kiwano/renderer/win32/D3D10DeviceResources.cpp index db3b3839..ef5374d6 100644 --- a/src/kiwano/renderer/D3D10DeviceResources.cpp +++ b/src/kiwano/renderer/win32/D3D10DeviceResources.cpp @@ -20,7 +20,7 @@ #include "D3D10DeviceResources.h" -#include "../base/Logger.h" +#include "../../base/Logger.h" #pragma comment(lib, "d3d10_1.lib") @@ -163,7 +163,10 @@ namespace kiwano { res->AddRef(); - DX::SafeRelease(*device_resources); + if (*device_resources) + { + (*device_resources)->Release(); + } (*device_resources) = res; } else diff --git a/src/kiwano/renderer/D3D10DeviceResources.h b/src/kiwano/renderer/win32/D3D10DeviceResources.h similarity index 98% rename from src/kiwano/renderer/D3D10DeviceResources.h rename to src/kiwano/renderer/win32/D3D10DeviceResources.h index 2e01c72f..ad926305 100644 --- a/src/kiwano/renderer/D3D10DeviceResources.h +++ b/src/kiwano/renderer/win32/D3D10DeviceResources.h @@ -20,7 +20,7 @@ #pragma once -#include "../macros.h" +#include "../../macros.h" #include "D2DDeviceResources.h" #include "D3DDeviceResourcesBase.h" diff --git a/src/kiwano/renderer/D3D11DeviceResources.cpp b/src/kiwano/renderer/win32/D3D11DeviceResources.cpp similarity index 99% rename from src/kiwano/renderer/D3D11DeviceResources.cpp rename to src/kiwano/renderer/win32/D3D11DeviceResources.cpp index 07f052dc..3eeadb82 100644 --- a/src/kiwano/renderer/D3D11DeviceResources.cpp +++ b/src/kiwano/renderer/win32/D3D11DeviceResources.cpp @@ -20,7 +20,7 @@ #include "D3D11DeviceResources.h" -#include "../base/Logger.h" +#include "../../base/Logger.h" #include // IsWindows10OrGreater #pragma comment(lib, "d3d11.lib") @@ -140,7 +140,10 @@ namespace kiwano { res->AddRef(); - DX::SafeRelease(*device_resources); + if (*device_resources) + { + (*device_resources)->Release(); + } (*device_resources) = res; } else diff --git a/src/kiwano/renderer/D3D11DeviceResources.h b/src/kiwano/renderer/win32/D3D11DeviceResources.h similarity index 98% rename from src/kiwano/renderer/D3D11DeviceResources.h rename to src/kiwano/renderer/win32/D3D11DeviceResources.h index acf41a8d..dc26fd30 100644 --- a/src/kiwano/renderer/D3D11DeviceResources.h +++ b/src/kiwano/renderer/win32/D3D11DeviceResources.h @@ -20,7 +20,7 @@ #pragma once -#include "../macros.h" +#include "../../macros.h" #include "D2DDeviceResources.h" #include "D3DDeviceResourcesBase.h" diff --git a/src/kiwano/renderer/D3DDeviceResourcesBase.h b/src/kiwano/renderer/win32/D3DDeviceResourcesBase.h similarity index 92% rename from src/kiwano/renderer/D3DDeviceResourcesBase.h rename to src/kiwano/renderer/win32/D3DDeviceResourcesBase.h index d742c41f..ca4631ff 100644 --- a/src/kiwano/renderer/D3DDeviceResourcesBase.h +++ b/src/kiwano/renderer/win32/D3DDeviceResourcesBase.h @@ -19,16 +19,12 @@ // THE SOFTWARE. #pragma once -#include "../macros.h" -#include "../math/helper.h" -#include "../2d/Color.h" #include namespace kiwano { - MIDL_INTERFACE("fb99fa64-d9cf-4e0e-9c75-90514797b01d") - KGE_API ID3DDeviceResourcesBase : public IUnknown + ID3DDeviceResourcesBase : public IUnknown { public: virtual HRESULT Present(bool vsync) = 0; diff --git a/src/kiwano/renderer/win32/FontCollectionLoader.cpp b/src/kiwano/renderer/win32/FontCollectionLoader.cpp new file mode 100644 index 00000000..304cd72d --- /dev/null +++ b/src/kiwano/renderer/win32/FontCollectionLoader.cpp @@ -0,0 +1,985 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// _Out_ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "FontCollectionLoader.h" + +namespace kiwano +{ + //////////////////////////////////////////////////////////////////////////////////////// + // + // FontCollectionLoader + // + //////////////////////////////////////////////////////////////////////////////////////// + + class FontCollectionLoader + : public IFontCollectionLoader + { + public: + FontCollectionLoader() + : refCount_(0) + { + } + + STDMETHOD(AddFilePaths)( + Vector const& filePaths, + _Out_ LPVOID* pCollectionKey, + _Out_ UINT32* pCollectionKeySize + ); + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontCollectionLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey( + IDWriteFactory* pFactory, + void const* collectionKey, + UINT32 collectionKeySize, + _Out_ IDWriteFontFileEnumerator** fontFileEnumerator + ); + + private: + ULONG refCount_; + + typedef Vector FileCollection; + Vector filePaths_; + Vector collectionKeys_; + }; + + HRESULT IFontCollectionLoader::Create(_Out_ IFontCollectionLoader** ppCollectionLoader) + { + HRESULT hr = S_OK; + + if (!ppCollectionLoader) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + FontCollectionLoader* pCollectionLoader = new (std::nothrow) FontCollectionLoader; + hr = pCollectionLoader ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*ppCollectionLoader); + (*ppCollectionLoader) = DX::SafeAcquire(pCollectionLoader); + } + } + return hr; + } + + STDMETHODIMP FontCollectionLoader::AddFilePaths( + Vector const& filePaths, + _Out_ LPVOID* pCollectionKey, + _Out_ UINT32* pCollectionKeySize + ) + { + if (!pCollectionKey || !pCollectionKeySize) + { + return E_INVALIDARG; + } + + try + { + UINT32 collectionKey = filePaths_.size(); + collectionKeys_.push_back(collectionKey); + filePaths_.push_back(filePaths); + + *pCollectionKey = reinterpret_cast(&collectionKeys_.back()); + *pCollectionKeySize = sizeof(collectionKey); + } + catch (std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (...) + { + return E_FAIL; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE FontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE FontCollectionLoader::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE FontCollectionLoader::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE FontCollectionLoader::CreateEnumeratorFromKey( + IDWriteFactory* pFactory, + void const* collectionKey, + UINT32 collectionKeySize, + _Out_ IDWriteFontFileEnumerator** fontFileEnumerator + ) + { + HRESULT hr = S_OK; + + if (collectionKey == NULL || collectionKeySize % sizeof(UINT32) != 0) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + IFontFileEnumerator* pEnumerator = NULL; + hr = IFontFileEnumerator::Create(&pEnumerator, pFactory); + + if (SUCCEEDED(hr)) + { + const UINT32 fileIndex = *static_cast(collectionKey); + hr = pEnumerator->SetFilePaths(filePaths_[fileIndex]); + } + + if (SUCCEEDED(hr)) + { + *fontFileEnumerator = DX::SafeAcquire(pEnumerator); + } + } + return hr; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // FontFileEnumerator + // + //////////////////////////////////////////////////////////////////////////////////////// + + class FontFileEnumerator + : public IFontFileEnumerator + { + public: + FontFileEnumerator(); + + STDMETHOD(Initialize)( + IDWriteFactory* pFactory + ); + + STDMETHOD(SetFilePaths)( + Vector const& filePaths + ); + + ~FontFileEnumerator() + { + DX::SafeRelease(currentFile_); + DX::SafeRelease(pFactory_); + } + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, _Out_ void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileEnumerator methods + virtual HRESULT STDMETHODCALLTYPE MoveNext(_Out_ BOOL* hasCurrentFile); + virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(_Out_ IDWriteFontFile** fontFile); + + private: + ULONG refCount_; + + IDWriteFactory* pFactory_; + IDWriteFontFile* currentFile_; + Vector filePaths_; + size_t nextIndex_; + }; + + HRESULT IFontFileEnumerator::Create(_Out_ IFontFileEnumerator** ppEnumerator, IDWriteFactory* pFactory) + { + HRESULT hr = S_OK; + + if (!ppEnumerator) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + FontFileEnumerator* pEnumerator = new (std::nothrow) FontFileEnumerator; + hr = pEnumerator ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + hr = pEnumerator->Initialize(pFactory); + } + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*ppEnumerator); + (*ppEnumerator) = DX::SafeAcquire(pEnumerator); + } + } + return hr; + } + + FontFileEnumerator::FontFileEnumerator() + : refCount_(0) + , pFactory_(NULL) + , currentFile_(NULL) + , nextIndex_(0) + { + } + + STDMETHODIMP FontFileEnumerator::Initialize( + IDWriteFactory* pFactory + ) + { + if (pFactory) + { + pFactory_ = DX::SafeAcquire(pFactory); + return S_OK; + } + return E_INVALIDARG; + } + + STDMETHODIMP FontFileEnumerator::SetFilePaths( + Vector const& filePaths + ) + { + try + { + filePaths_.assign(filePaths); + } + catch (std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (...) + { + return E_FAIL; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE FontFileEnumerator::QueryInterface(REFIID iid, _Out_ void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE FontFileEnumerator::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE FontFileEnumerator::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE FontFileEnumerator::MoveNext(_Out_ BOOL* hasCurrentFile) + { + HRESULT hr = S_OK; + + *hasCurrentFile = FALSE; + DX::SafeRelease(currentFile_); + + if (nextIndex_ < filePaths_.size()) + { + hr = pFactory_->CreateFontFileReference( + filePaths_[nextIndex_].c_str(), + NULL, + ¤tFile_ + ); + + if (SUCCEEDED(hr)) + { + *hasCurrentFile = TRUE; + + ++nextIndex_; + } + } + + return hr; + } + + HRESULT STDMETHODCALLTYPE FontFileEnumerator::GetCurrentFontFile(_Out_ IDWriteFontFile** fontFile) + { + *fontFile = DX::SafeAcquire(currentFile_); + + return (currentFile_ != NULL) ? S_OK : E_FAIL; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // ResourceFontCollectionLoader + // + //////////////////////////////////////////////////////////////////////////////////////// + + class ResourceFontCollectionLoader + : public IResourceFontCollectionLoader + { + public: + ResourceFontCollectionLoader(IDWriteFontFileLoader* pFileLoader) + : refCount_(0) + , pFileLoader_(pFileLoader) + { + } + + STDMETHOD(AddResources)( + Vector const& resources, + _Out_ LPVOID* pCollectionKey, + _Out_ UINT32* pCollectionKeySize + ); + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontCollectionLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey( + IDWriteFactory* pFactory, + void const* collectionKey, + UINT32 collectionKeySize, + _Out_ IDWriteFontFileEnumerator** fontFileEnumerator + ); + + private: + ULONG refCount_; + IDWriteFontFileLoader* pFileLoader_; + + typedef Vector ResourceCollection; + Vector resources_; + Vector collectionKeys_; + }; + + HRESULT IResourceFontCollectionLoader::Create(_Out_ IResourceFontCollectionLoader** ppCollectionLoader, IDWriteFontFileLoader* pFileLoader) + { + HRESULT hr = S_OK; + + if (!ppCollectionLoader) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + ResourceFontCollectionLoader* pCollectionLoader = new (std::nothrow) ResourceFontCollectionLoader(pFileLoader); + hr = pCollectionLoader ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*ppCollectionLoader); + (*ppCollectionLoader) = DX::SafeAcquire(pCollectionLoader); + } + } + return hr; + } + + STDMETHODIMP ResourceFontCollectionLoader::AddResources( + Vector const& resources, + _Out_ LPVOID* pCollectionKey, + _Out_ UINT32* pCollectionKeySize + ) + { + if (!pCollectionKey || !pCollectionKeySize) + { + return E_INVALIDARG; + } + + try + { + UINT32 collectionKey = resources_.size(); + collectionKeys_.push_back(collectionKey); + resources_.push_back(resources); + + *pCollectionKey = reinterpret_cast(&collectionKeys_.back()); + *pCollectionKeySize = sizeof(collectionKey); + } + catch (std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (...) + { + return E_FAIL; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ResourceFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE ResourceFontCollectionLoader::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE ResourceFontCollectionLoader::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE ResourceFontCollectionLoader::CreateEnumeratorFromKey( + IDWriteFactory* pFactory, + void const* collectionKey, + UINT32 collectionKeySize, + _Out_ IDWriteFontFileEnumerator** fontFileEnumerator + ) + { + HRESULT hr = S_OK; + + if (collectionKey == NULL || collectionKeySize % sizeof(Resource*) != 0) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + IResourceFontFileEnumerator* pEnumerator = NULL; + hr = IResourceFontFileEnumerator::Create(&pEnumerator, pFactory, pFileLoader_); + + if (SUCCEEDED(hr)) + { + const UINT32 resourceIndex = *static_cast(collectionKey); + + hr = pEnumerator->SetResources(resources_[resourceIndex]); + } + + if (SUCCEEDED(hr)) + { + *fontFileEnumerator = DX::SafeAcquire(pEnumerator); + } + } + return hr; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // ResourceFontFileLoader + // + //////////////////////////////////////////////////////////////////////////////////////// + + class ResourceFontFileLoader + : public IResourceFontFileLoader + { + public: + ResourceFontFileLoader() + : refCount_(0) + { + } + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey( + void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + _Out_ IDWriteFontFileStream** fontFileStream + ); + + private: + ULONG refCount_; + }; + + HRESULT IResourceFontFileLoader::Create(_Out_ IResourceFontFileLoader** ppFileLoader) + { + HRESULT hr = S_OK; + + if (!ppFileLoader) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + ResourceFontFileLoader* pFileLoader = new (std::nothrow) ResourceFontFileLoader; + hr = pFileLoader ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + (*ppFileLoader) = DX::SafeAcquire(pFileLoader); + } + } + return hr; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE ResourceFontFileLoader::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE ResourceFontFileLoader::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileLoader::CreateStreamFromKey( + void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + _Out_ IDWriteFontFileStream** fontFileStream + ) + { + HRESULT hr = S_OK; + + // Make sure the key is the right size. + if (fontFileReferenceKeySize != sizeof(Resource)) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + // Create the pFileStream object. + IResourceFontFileStream* pFileStream = NULL; + Resource resource = *static_cast(fontFileReferenceKey); + + hr = IResourceFontFileStream::Create(&pFileStream, resource); + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*fontFileStream); + *fontFileStream = DX::SafeAcquire(pFileStream); + } + } + return hr; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // ResourceFontFileEnumerator + // + //////////////////////////////////////////////////////////////////////////////////////// + + class ResourceFontFileEnumerator + : public IResourceFontFileEnumerator + { + public: + ResourceFontFileEnumerator(); + + STDMETHOD(Initialize)( + IDWriteFactory* pFactory, + IDWriteFontFileLoader* pLoader + ); + + STDMETHOD(SetResources)( + Vector const& resources + ); + + ~ResourceFontFileEnumerator() + { + DX::SafeRelease(currentFile_); + DX::SafeRelease(pFactory_); + } + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, _Out_ void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileEnumerator methods + virtual HRESULT STDMETHODCALLTYPE MoveNext(_Out_ BOOL* hasCurrentFile); + virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(_Out_ IDWriteFontFile** fontFile); + + private: + ULONG refCount_; + + IDWriteFactory* pFactory_; + IDWriteFontFile* currentFile_; + IDWriteFontFileLoader* pLoader_; + Vector resources_; + size_t nextIndex_; + }; + + HRESULT IResourceFontFileEnumerator::Create(_Out_ IResourceFontFileEnumerator** ppEnumerator, IDWriteFactory* pFactory, IDWriteFontFileLoader* pFileLoader) + { + HRESULT hr = S_OK; + + if (!ppEnumerator) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + ResourceFontFileEnumerator* pEnumerator = new (std::nothrow) ResourceFontFileEnumerator; + hr = pEnumerator ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + hr = pEnumerator->Initialize(pFactory, pFileLoader); + } + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*ppEnumerator); + (*ppEnumerator) = DX::SafeAcquire(pEnumerator); + } + } + return hr; + } + + ResourceFontFileEnumerator::ResourceFontFileEnumerator() + : refCount_(0) + , pFactory_(NULL) + , currentFile_(NULL) + , nextIndex_(0) + , pLoader_(NULL) + { + } + + STDMETHODIMP ResourceFontFileEnumerator::Initialize( + IDWriteFactory* pFactory, + IDWriteFontFileLoader* pLoader + ) + { + if (pFactory && pLoader) + { + pFactory_ = DX::SafeAcquire(pFactory); + pLoader_ = DX::SafeAcquire(pLoader); + return S_OK; + } + return E_INVALIDARG; + } + + STDMETHODIMP ResourceFontFileEnumerator::SetResources( + Vector const& resources + ) + { + try + { + resources_.assign(resources); + } + catch (std::bad_alloc&) + { + return E_OUTOFMEMORY; + } + catch (...) + { + return E_FAIL; + } + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileEnumerator::QueryInterface(REFIID iid, _Out_ void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE ResourceFontFileEnumerator::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE ResourceFontFileEnumerator::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileEnumerator::MoveNext(_Out_ BOOL* hasCurrentFile) + { + HRESULT hr = S_OK; + + *hasCurrentFile = FALSE; + DX::SafeRelease(currentFile_); + + if (nextIndex_ < resources_.size()) + { + hr = pFactory_->CreateCustomFontFileReference( + &resources_[nextIndex_], + sizeof(Resource), + pLoader_, + ¤tFile_ + ); + + if (SUCCEEDED(hr)) + { + *hasCurrentFile = TRUE; + + ++nextIndex_; + } + } + + return hr; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileEnumerator::GetCurrentFontFile(_Out_ IDWriteFontFile** fontFile) + { + *fontFile = DX::SafeAcquire(currentFile_); + + return (currentFile_ != NULL) ? S_OK : E_FAIL; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // ResourceFontFileStream + // + //////////////////////////////////////////////////////////////////////////////////////// + + class ResourceFontFileStream + : public IResourceFontFileStream + { + public: + ResourceFontFileStream(); + + STDMETHOD(Initialize)( + Resource const resources + ); + + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileStream methods + virtual HRESULT STDMETHODCALLTYPE ReadFileFragment( + void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + _Out_ void** fragmentContext + ); + + virtual void STDMETHODCALLTYPE ReleaseFileFragment( + void* fragmentContext + ); + + virtual HRESULT STDMETHODCALLTYPE GetFileSize( + _Out_ UINT64* fileSize + ); + + virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime( + _Out_ UINT64* lastWriteTime + ); + + bool IsInitialized() + { + return resourcePtr_ != NULL; + } + + private: + ULONG refCount_; + LPVOID resourcePtr_; + DWORD resourceSize_; + }; + + HRESULT IResourceFontFileStream::Create(_Out_ IResourceFontFileStream** ppStream, const Resource resource) + { + HRESULT hr = S_OK; + + if (!ppStream) + { + hr = E_POINTER; + } + + if (SUCCEEDED(hr)) + { + ResourceFontFileStream* pFileStream = new (std::nothrow) ResourceFontFileStream; + hr = pFileStream ? S_OK : E_OUTOFMEMORY; + + if (SUCCEEDED(hr)) + { + hr = pFileStream->Initialize(resource); + } + + if (SUCCEEDED(hr)) + { + DX::SafeRelease(*ppStream); + (*ppStream) = DX::SafeAcquire(pFileStream); + } + } + return hr; + } + + ResourceFontFileStream::ResourceFontFileStream() + : refCount_(0) + , resourcePtr_(NULL) + , resourceSize_(0) + { + } + + STDMETHODIMP ResourceFontFileStream::Initialize( + const Resource resource + ) + { + Resource::Data data = resource.GetData(); + HRESULT hr = data ? S_OK : E_FAIL; + + if (SUCCEEDED(hr)) + { + resourcePtr_ = data.buffer; + resourceSize_ = data.size; + } + return hr; + } + + // IUnknown methods + HRESULT STDMETHODCALLTYPE ResourceFontFileStream::QueryInterface(REFIID iid, void** ppvObject) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) + { + *ppvObject = this; + AddRef(); + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE ResourceFontFileStream::AddRef() + { + return InterlockedIncrement(&refCount_); + } + + ULONG STDMETHODCALLTYPE ResourceFontFileStream::Release() + { + ULONG newCount = InterlockedDecrement(&refCount_); + if (newCount == 0) + delete this; + + return newCount; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileStream::ReadFileFragment( + void const** fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + _Out_ void** fragmentContext + ) + { + // The pLoader is responsible for doing a bounds check. + if (fileOffset <= resourceSize_ && + fragmentSize <= resourceSize_ - fileOffset) + { + *fragmentStart = static_cast(resourcePtr_) + static_cast(fileOffset); + *fragmentContext = NULL; + return S_OK; + } + else + { + *fragmentStart = NULL; + *fragmentContext = NULL; + return E_FAIL; + } + } + + void STDMETHODCALLTYPE ResourceFontFileStream::ReleaseFileFragment( + void* fragmentContext + ) + { + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileStream::GetFileSize( + _Out_ UINT64* fileSize + ) + { + *fileSize = resourceSize_; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ResourceFontFileStream::GetLastWriteTime( + _Out_ UINT64* lastWriteTime + ) + { + *lastWriteTime = 0; + return E_NOTIMPL; + } + +} diff --git a/src/kiwano/renderer/win32/FontCollectionLoader.h b/src/kiwano/renderer/win32/FontCollectionLoader.h new file mode 100644 index 00000000..c5e01c20 --- /dev/null +++ b/src/kiwano/renderer/win32/FontCollectionLoader.h @@ -0,0 +1,109 @@ +// 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", WITH_Out_ 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 "D2DDeviceResources.h" + +namespace kiwano +{ + interface DWRITE_DECLARE_INTERFACE("7EC7A55A-1964-4098-83E0-EFA7C12C6EF7") + IFontCollectionLoader : public IDWriteFontCollectionLoader + { + public: + static HRESULT Create( + _Out_ IFontCollectionLoader** ppCollectionLoader + ); + + STDMETHOD(AddFilePaths)( + Vector const& filePaths, + _Out_ LPVOID * pCollectionKey, + _Out_ UINT32 * pCollectionKeySize + ) PURE; + }; + + + interface DWRITE_DECLARE_INTERFACE("0A1A3F2A-85F2-41BB-80FD-EC01271740C4") + IFontFileEnumerator : public IDWriteFontFileEnumerator + { + public: + static HRESULT Create( + _Out_ IFontFileEnumerator** ppEnumerator, + IDWriteFactory* pFactory + ); + + STDMETHOD(SetFilePaths)( + Vector const& filePaths + ) PURE; + }; + + + interface DWRITE_DECLARE_INTERFACE("F2C411F0-2FB0-4D0E-8C73-D2B8F30137A4") + IResourceFontCollectionLoader : public IDWriteFontCollectionLoader + { + public: + static HRESULT Create( + _Out_ IResourceFontCollectionLoader** ppCollectionLoader, + IDWriteFontFileLoader * pFileLoader + ); + + STDMETHOD(AddResources)( + Vector const& resources, + _Out_ LPVOID * pCollectionKey, + _Out_ UINT32 * pCollectionKeySize + ) PURE; + }; + + + interface DWRITE_DECLARE_INTERFACE("08D21408-6FC1-4E36-A4EB-4DA16BE3399E") + IResourceFontFileLoader : public IDWriteFontFileLoader + { + public: + static HRESULT Create( + _Out_ IResourceFontFileLoader** ppFileLoader + ); + }; + + + interface DWRITE_DECLARE_INTERFACE("0AD0EC74-7503-46E8-8899-520175ECCB4A") + IResourceFontFileEnumerator : public IDWriteFontFileEnumerator + { + public: + static HRESULT Create( + _Out_ IResourceFontFileEnumerator** ppEnumerator, + IDWriteFactory* pFactory, + IDWriteFontFileLoader* pFileLoader + ); + + STDMETHOD(SetResources)( + Vector const& resources + ) PURE; + }; + + + interface DWRITE_DECLARE_INTERFACE("A6267450-27F3-4948-995F-FF8345A72F88") + IResourceFontFileStream : public IDWriteFontFileStream + { + public: + static HRESULT Create( + _Out_ IResourceFontFileStream** ppStream, + const Resource resource + ); + }; +} diff --git a/src/kiwano/renderer/TextRenderer.cpp b/src/kiwano/renderer/win32/TextRenderer.cpp similarity index 99% rename from src/kiwano/renderer/TextRenderer.cpp rename to src/kiwano/renderer/win32/TextRenderer.cpp index a0694a21..9c4e8aca 100644 --- a/src/kiwano/renderer/TextRenderer.cpp +++ b/src/kiwano/renderer/win32/TextRenderer.cpp @@ -98,7 +98,7 @@ namespace kiwano unsigned long STDMETHODCALLTYPE AddRef(); unsigned long STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface( - IID const& riid, + REFIID riid, void** ppvObject ); @@ -454,24 +454,6 @@ namespace kiwano return E_NOTIMPL; } - STDMETHODIMP_(unsigned long) TextRenderer::AddRef() - { - return InterlockedIncrement(&cRefCount_); - } - - STDMETHODIMP_(unsigned long) TextRenderer::Release() - { - unsigned long newCount = InterlockedDecrement(&cRefCount_); - - if (newCount == 0) - { - delete this; - return 0; - } - - return newCount; - } - STDMETHODIMP TextRenderer::IsPixelSnappingDisabled( __maybenull void* clientDrawingContext, __out BOOL* isDisabled) @@ -506,8 +488,26 @@ namespace kiwano return S_OK; } + STDMETHODIMP_(unsigned long) TextRenderer::AddRef() + { + return InterlockedIncrement(&cRefCount_); + } + + STDMETHODIMP_(unsigned long) TextRenderer::Release() + { + unsigned long newCount = InterlockedDecrement(&cRefCount_); + + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + STDMETHODIMP TextRenderer::QueryInterface( - IID const& riid, + REFIID riid, void** ppvObject) { if (__uuidof(ITextRenderer) == riid) diff --git a/src/kiwano/renderer/TextRenderer.h b/src/kiwano/renderer/win32/TextRenderer.h similarity index 94% rename from src/kiwano/renderer/TextRenderer.h rename to src/kiwano/renderer/win32/TextRenderer.h index ea5e47de..604a232e 100644 --- a/src/kiwano/renderer/TextRenderer.h +++ b/src/kiwano/renderer/win32/TextRenderer.h @@ -19,13 +19,12 @@ // THE SOFTWARE. #pragma once -#include "helper.hpp" -#include +#include "D2DDeviceResources.h" namespace kiwano { - interface DWRITE_DECLARE_INTERFACE("b293e798-9916-4096-a3c1-e5d4039dfa64") ITextRenderer - : public IDWriteTextRenderer + interface DWRITE_DECLARE_INTERFACE("b293e798-9916-4096-a3c1-e5d4039dfa64") + ITextRenderer : public IDWriteTextRenderer { public: static KGE_API HRESULT Create( diff --git a/src/kiwano/utils/FileUtil.cpp b/src/kiwano/utils/FileUtil.cpp index 25d3974c..519346e5 100644 --- a/src/kiwano/utils/FileUtil.cpp +++ b/src/kiwano/utils/FileUtil.cpp @@ -46,12 +46,11 @@ namespace kiwano if (file_handle == INVALID_HANDLE_VALUE) return false; - LPVOID buffer; - DWORD buffer_size; - if (res.Load(buffer, buffer_size)) + Resource::Data data = res.GetData(); + if (data) { DWORD written_bytes = 0; - ::WriteFile(file_handle, buffer, buffer_size, &written_bytes, NULL); + ::WriteFile(file_handle, data.buffer, data.size, &written_bytes, NULL); ::CloseHandle(file_handle); return true; @@ -79,4 +78,4 @@ namespace kiwano return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } -} \ No newline at end of file +} diff --git a/src/kiwano/utils/ResourceCache.cpp b/src/kiwano/utils/ResourceCache.cpp index b4c111c8..9340e3d1 100644 --- a/src/kiwano/utils/ResourceCache.cpp +++ b/src/kiwano/utils/ResourceCache.cpp @@ -173,12 +173,12 @@ namespace kiwano return false; } - bool ResourceCache::AddFrame(String const& id, Resource const& res) + bool ResourceCache::AddFrame(String const& id, String const& file_path) { FramePtr ptr = new (std::nothrow) Frame; if (ptr) { - if (ptr->Load(res)) + if (ptr->Load(file_path)) { return AddFrame(id, ptr); } @@ -196,43 +196,20 @@ namespace kiwano return false; } - bool ResourceCache::AddGifImage(String const& id, Resource const& res) + size_t ResourceCache::AddFrameSequence(String const& id, Vector const& files) { - GifImagePtr ptr = new (std::nothrow) GifImage; - if (ptr) - { - if (ptr->Load(res)) - { - return AddGifImage(id, ptr); - } - } - return false; - } - - bool ResourceCache::AddGifImage(String const& id, GifImagePtr image) - { - if (image) - { - cache_.insert(std::make_pair(id, image)); - return true; - } - return false; - } - - size_t ResourceCache::AddFrameSequence(String const& id, Vector const& images) - { - if (images.empty()) + if (files.empty()) return 0; Vector image_arr; - image_arr.reserve(images.size()); + image_arr.reserve(files.size()); - for (const auto& image : images) + for (const auto& file : files) { FramePtr ptr = new (std::nothrow) Frame; if (ptr) { - if (ptr->Load(image)) + if (ptr->Load(file)) { image_arr.push_back(ptr); } @@ -247,22 +224,13 @@ namespace kiwano return 0; } - size_t ResourceCache::AddFrameSequence(String const& id, Vector const& images) - { - if (images.empty()) - return 0; - - FrameSequencePtr frames = new (std::nothrow) FrameSequence(images); - return AddFrameSequence(id, frames); - } - - size_t ResourceCache::AddFrameSequence(String const & id, Resource const & image, int cols, int rows) + size_t ResourceCache::AddFrameSequence(String const & id, String const& file_path, int cols, int rows) { if (cols <= 0 || rows <= 0) return 0; FramePtr raw = new (std::nothrow) Frame; - if (!raw || !raw->Load(image)) + if (!raw || !raw->Load(file_path)) return false; float raw_width = raw->GetWidth(); @@ -280,7 +248,7 @@ namespace kiwano FramePtr ptr = new (std::nothrow) Frame(raw->GetImage()); if (ptr) { - ptr->Crop(Rect{ j * width, i * height, width, height }); + ptr->SetCropRect(Rect{ j * width, i * height, width, height }); image_arr.push_back(ptr); } } @@ -290,29 +258,6 @@ namespace kiwano return AddFrameSequence(id, frames); } - size_t ResourceCache::AddFrameSequence(String const & id, Resource const & image, Vector const & crop_rects) - { - FramePtr raw = new (std::nothrow) Frame; - if (!raw || !raw->Load(image)) - return 0; - - Vector image_arr; - image_arr.reserve(crop_rects.size()); - - for (const auto& rect : crop_rects) - { - FramePtr ptr = new (std::nothrow) Frame(raw->GetImage()); - if (ptr) - { - ptr->Crop(rect); - image_arr.push_back(ptr); - } - } - - FrameSequencePtr frames = new (std::nothrow) FrameSequence(image_arr); - return AddFrameSequence(id, frames); - } - size_t ResourceCache::AddFrameSequence(String const & id, FrameSequencePtr frames) { if (frames) @@ -323,7 +268,7 @@ namespace kiwano return 0; } - bool ResourceCache::AddObj(String const& id, ObjectPtr obj) + bool ResourceCache::AddObjectBase(String const& id, ObjectBasePtr obj) { if (obj) { @@ -338,11 +283,6 @@ namespace kiwano return Get(id); } - GifImagePtr ResourceCache::GetGifImage(String const& id) const - { - return Get(id); - } - FrameSequencePtr ResourceCache::GetFrameSequence(String const & id) const { return Get(id); @@ -376,23 +316,17 @@ namespace kiwano if (file) { - // Gif image - if (type && (*type) == L"gif") - { - return loader->AddGifImage(*id, Resource(gdata->path + (*file))); - } - if (!(*file).empty()) { if (rows || cols) { // Frame slices - return !!loader->AddFrameSequence(*id, Resource(gdata->path + (*file)), std::max(cols, 1), std::max(rows, 1)); + return !!loader->AddFrameSequence(*id, gdata->path + (*file), std::max(cols, 1), std::max(rows, 1)); } else { // Simple image - return loader->AddFrame(*id, Resource(gdata->path + (*file))); + return loader->AddFrame(*id, gdata->path + (*file)); } } } @@ -410,7 +344,8 @@ namespace kiwano frames.push_back(frame); } } - return !!loader->AddFrameSequence(*id, frames); + FrameSequencePtr frame_seq = new FrameSequence(frames); + return !!loader->AddFrameSequence(*id, frame_seq); } return false; } diff --git a/src/kiwano/utils/ResourceCache.h b/src/kiwano/utils/ResourceCache.h index 8a7da7b8..76559d32 100644 --- a/src/kiwano/utils/ResourceCache.h +++ b/src/kiwano/utils/ResourceCache.h @@ -46,44 +46,28 @@ namespace kiwano // 从 XML 文档对象加载资源信息 bool LoadFromXml(tinyxml2::XMLDocument* doc); - // 添加图片 - bool AddFrame(String const& id, Resource const& res); + // 添加帧图像 + bool AddFrame(String const& id, String const& file_path); - // 添加图片 + // 添加帧图像 bool AddFrame(String const& id, FramePtr frame); - // 添加 GIF 图片 - bool AddGifImage(String const& id, Resource const& res); - - // 添加 GIF 图片 - bool AddGifImage(String const& id, GifImagePtr image); - // 添加序列帧 - size_t AddFrameSequence(String const& id, Vector const& frames); - - // 添加序列帧 - size_t AddFrameSequence(String const& id, Vector const& frames); + size_t AddFrameSequence(String const& id, Vector const& files); // 添加序列帧 // 按行列数裁剪图片 - size_t AddFrameSequence(String const& id, Resource const& frame, int cols, int rows = 1); - - // 添加序列帧 - // 按指定裁剪矩形裁剪图片 - size_t AddFrameSequence(String const& id, Resource const& frame, Vector const& crop_rects); + size_t AddFrameSequence(String const& id, String const& file_path, int cols, int rows = 1); // 添加序列帧 size_t AddFrameSequence(String const& id, FrameSequencePtr frames); // 添加对象 - bool AddObj(String const& id, ObjectPtr obj); + bool AddObjectBase(String const& id, ObjectBasePtr obj); - // 获取图片资源 + // 获取帧图像 FramePtr GetFrame(String const& id) const; - // 获取 GIF 图片资源 - GifImagePtr GetGifImage(String const& id) const; - // 获取序列帧 FrameSequencePtr GetFrameSequence(String const& id) const; @@ -108,6 +92,6 @@ namespace kiwano virtual ~ResourceCache(); protected: - UnorderedMap cache_; + UnorderedMap cache_; }; }