From 7257ebf12b90030d4dceb440f6eabbf0ff9774df Mon Sep 17 00:00:00 2001 From: Nomango <569629550@qq.com> Date: Fri, 16 Aug 2019 00:50:54 +0800 Subject: [PATCH] Add RenderTarget --- projects/kiwano.vcxproj | 2 + projects/kiwano.vcxproj.filters | 6 + src/kiwano/2d/Canvas.cpp | 422 ++++++----------- src/kiwano/2d/Canvas.h | 140 +++--- src/kiwano/2d/Frame.cpp | 20 +- src/kiwano/2d/Frame.h | 10 +- src/kiwano/2d/GifSprite.cpp | 188 +++++--- src/kiwano/2d/GifSprite.h | 47 +- src/kiwano/2d/ShapeActor.cpp | 8 +- src/kiwano/2d/ShapeActor.h | 12 +- src/kiwano/2d/Sprite.cpp | 8 +- src/kiwano/2d/Sprite.h | 2 +- src/kiwano/2d/Text.cpp | 2 +- src/kiwano/2d/Transition.cpp | 16 +- src/kiwano/base/types.h | 4 +- src/kiwano/kiwano.h | 25 +- src/kiwano/renderer/GifImage.cpp | 513 ++++++++------------- src/kiwano/renderer/GifImage.h | 69 ++- src/kiwano/renderer/Image.cpp | 53 ++- src/kiwano/renderer/Image.h | 21 +- src/kiwano/renderer/ImageCache.cpp | 60 ++- src/kiwano/renderer/ImageCache.h | 14 +- src/kiwano/renderer/RenderTarget.cpp | 660 +++++++++++++++++++++++++++ src/kiwano/renderer/RenderTarget.h | 207 +++++++++ src/kiwano/renderer/Renderer.cpp | 581 ++++++----------------- src/kiwano/renderer/Renderer.h | 125 +---- src/kiwano/utils/ResourceCache.cpp | 4 +- 27 files changed, 1814 insertions(+), 1405 deletions(-) create mode 100644 src/kiwano/renderer/RenderTarget.cpp create mode 100644 src/kiwano/renderer/RenderTarget.h diff --git a/projects/kiwano.vcxproj b/projects/kiwano.vcxproj index f7b0ef14..c9958130 100644 --- a/projects/kiwano.vcxproj +++ b/projects/kiwano.vcxproj @@ -77,6 +77,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/projects/kiwano.vcxproj.filters b/projects/kiwano.vcxproj.filters index ebebec18..b3b2d69d 100644 --- a/projects/kiwano.vcxproj.filters +++ b/projects/kiwano.vcxproj.filters @@ -291,6 +291,9 @@ renderer + + renderer + @@ -449,5 +452,8 @@ renderer + + renderer + \ No newline at end of file diff --git a/src/kiwano/2d/Canvas.cpp b/src/kiwano/2d/Canvas.cpp index 61f8950a..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,321 +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_ - ) - ); - } + TextLayout layout(text, text_font_, text_style_); - ComPtr text_layout; - ThrowIfFailed( - Renderer::GetInstance()->GetD2DDeviceResources()->CreateTextLayout( - text_layout, - text, - text_style_, - text_format_ - ) - ); - - 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/Frame.cpp b/src/kiwano/2d/Frame.cpp index 97781255..9fbd506e 100644 --- a/src/kiwano/2d/Frame.cpp +++ b/src/kiwano/2d/Frame.cpp @@ -32,15 +32,15 @@ namespace kiwano Load(res); } - Frame::Frame(ImagePtr image) + Frame::Frame(Image const& image) : image_(image) { } bool Frame::Load(Resource const& res) { - ImagePtr image = ImageCache::GetInstance()->AddImage(res); - if (image && image->IsValid()) + Image image = ImageCache::GetInstance()->AddImage(res); + if (image.IsValid()) { SetImage(image); return true; @@ -48,11 +48,11 @@ namespace kiwano return false; } - void Frame::Crop(Rect const& crop_rect) + void Frame::SetCropRect(Rect const& crop_rect) { - if (image_) + if (image_.IsValid()) { - auto bitmap_size = image_->GetSize(); + 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 +60,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..93569ee9 100644 --- a/src/kiwano/2d/Frame.h +++ b/src/kiwano/2d/Frame.h @@ -35,7 +35,7 @@ namespace kiwano ); explicit Frame( - ImagePtr image + Image const& image ); bool Load( @@ -43,7 +43,7 @@ namespace kiwano ); // 裁剪矩形 - void Crop( + void SetCropRect( Rect const& crop_rect /* 裁剪矩形 */ ); @@ -63,13 +63,13 @@ namespace kiwano inline Rect const& GetCropRect() const { return crop_rect_; } // 获取位图 - inline ImagePtr GetImage() const { return image_; } + inline Image const& GetImage() const { return image_; } // 设置位图 - void SetImage(ImagePtr image); + void SetImage(Image const& image); protected: - ImagePtr image_; + Image image_; Rect crop_rect_; }; } diff --git a/src/kiwano/2d/GifSprite.cpp b/src/kiwano/2d/GifSprite.cpp index bccf256c..e331fcf8 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,6 +30,7 @@ namespace kiwano , next_index_(0) , total_loop_count_(1) , loop_count_(0) + , disposal_type_(DisposalType::Unknown) { } @@ -45,12 +47,8 @@ namespace kiwano bool GifSprite::Load(Resource const& res) { - GifImagePtr image = new (std::nothrow) GifImage; - if (image->Load(res)) - { - return Load(image); - } - return false; + GifImagePtr image = ImageCache::GetInstance()->AddGifImage(res); + return Load(image); } bool GifSprite::Load(GifImagePtr image) @@ -61,18 +59,16 @@ namespace kiwano next_index_ = 0; loop_count_ = 0; + disposal_type_ = DisposalType::None; SetSize( 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) @@ -84,6 +80,16 @@ 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); @@ -100,84 +106,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 +205,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..4e1f2a41 100644 --- a/src/kiwano/2d/GifSprite.h +++ b/src/kiwano/2d/GifSprite.h @@ -21,17 +21,19 @@ #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(); @@ -72,27 +74,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_; + GifImagePtr 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..64331909 100644 --- a/src/kiwano/2d/Sprite.cpp +++ b/src/kiwano/2d/Sprite.cpp @@ -38,7 +38,7 @@ namespace kiwano : frame_(nullptr) { Load(res); - Crop(crop_rect); + SetCropRect(crop_rect); } Sprite::Sprite(FramePtr frame) @@ -62,11 +62,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 +89,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..8cd03aad 100644 --- a/src/kiwano/2d/Sprite.h +++ b/src/kiwano/2d/Sprite.h @@ -52,7 +52,7 @@ namespace kiwano ); // 裁剪矩形 - void Crop( + void SetCropRect( const Rect& crop_rect ); diff --git a/src/kiwano/2d/Text.cpp b/src/kiwano/2d/Text.cpp index 1ea74555..dc460d0b 100644 --- a/src/kiwano/2d/Text.cpp +++ b/src/kiwano/2d/Text.cpp @@ -206,7 +206,7 @@ namespace kiwano if (text_layout_ && renderer->CheckVisibility(size_, transform_matrix_)) { PrepareRender(renderer); - renderer->DrawTextLayout(text_layout_); + renderer->DrawTextLayout(text_layout_); } } diff --git a/src/kiwano/2d/Transition.cpp b/src/kiwano/2d/Transition.cpp index af2876c5..d5353c05 100644 --- a/src/kiwano/2d/Transition.cpp +++ b/src/kiwano/2d/Transition.cpp @@ -99,30 +99,26 @@ namespace kiwano { if (out_scene_) { - renderer->PushClip( - out_scene_->GetTransformMatrix(), - window_size_ - ); + renderer->SetTransform(out_scene_->GetTransformMatrix()); + renderer->PushClipRect(Rect{ Point{}, window_size_ }); renderer->PushLayer(out_layer_, out_layer_prop_); out_scene_->Render(renderer); renderer->PopLayer(); - renderer->PopClip(); + renderer->PopClipRect(); } if (in_scene_) { - renderer->PushClip( - in_scene_->GetTransformMatrix(), - window_size_ - ); + renderer->SetTransform(in_scene_->GetTransformMatrix()); + renderer->PushClipRect(Rect{ Point{}, window_size_ }); renderer->PushLayer(in_layer_, in_layer_prop_); in_scene_->Render(renderer); renderer->PopLayer(); - renderer->PopClip(); + renderer->PopClipRect(); } } diff --git a/src/kiwano/base/types.h b/src/kiwano/base/types.h index 28e139e0..54531e14 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, /* 斜切 */ @@ -68,4 +68,4 @@ namespace kiwano Rect area; float opacity; }; -} \ No newline at end of file +} diff --git a/src/kiwano/kiwano.h b/src/kiwano/kiwano.h index 92597682..32e44077 100644 --- a/src/kiwano/kiwano.h +++ b/src/kiwano/kiwano.h @@ -56,12 +56,22 @@ // -// base +// renderer // -#include "renderer/Renderer.h" #include "renderer/Image.h" #include "renderer/GifImage.h" +#include "renderer/TextLayout.h" +#include "renderer/TextRenderer.h" +#include "renderer/Geometry.h" +#include "renderer/ImageCache.h" +#include "renderer/RenderTarget.h" +#include "renderer/Renderer.h" + + +// +// base +// #include "base/time.h" #include "base/Window.h" @@ -81,6 +91,11 @@ #include "base/AsyncTask.h" #include "base/Resource.h" + +// +// 2d +// + #include "2d/Font.hpp" #include "2d/Color.h" #include "2d/Transform.hpp" @@ -110,9 +125,15 @@ #include "2d/ShapeActor.h" #include "2d/DebugActor.h" + +// +// platform +// + #include "platform/modules.h" #include "platform/Application.h" + // // utils // diff --git a/src/kiwano/renderer/GifImage.cpp b/src/kiwano/renderer/GifImage.cpp index d2546c51..49c54acd 100644 --- a/src/kiwano/renderer/GifImage.cpp +++ b/src/kiwano/renderer/GifImage.cpp @@ -19,18 +19,15 @@ // 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_{} { } @@ -43,234 +40,23 @@ namespace kiwano bool GifImage::Load(Resource const& res) { - HRESULT hr = S_OK; + Renderer::GetInstance()->CreateGifImage(*this, res); - 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::IsValid() const { - 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 - 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)) - { - hr = (prop_val.vt == VT_UI2 ? S_OK : E_FAIL); - if (SUCCEEDED(hr)) - { - frame_position_.left = 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_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; + return decoder_ != nullptr; } HRESULT GifImage::GetGlobalMetadata() @@ -284,7 +70,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 +88,7 @@ namespace kiwano if (FAILED(GetBackgroundColor(metadata_reader.get()))) { // 如果未能获得颜色,则默认为透明 - bg_color_ = D2D1::ColorF(0, 0.f); + bg_color_ = Color(0, 0.f); } } @@ -305,9 +96,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 +112,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 +128,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 +168,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 +188,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 +222,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..b30fd113 100644 --- a/src/kiwano/renderer/GifImage.h +++ b/src/kiwano/renderer/GifImage.h @@ -19,8 +19,7 @@ // THE SOFTWARE. #pragma once -#include "../base/Resource.h" -#include "Renderer.h" +#include "Image.h" namespace kiwano { @@ -32,21 +31,19 @@ namespace kiwano public: GifImage(); - GifImage( - Resource const& res - ); + GifImage(Resource const& res); - bool Load( - Resource const& res - ); + bool Load(Resource const& res); - inline unsigned int GetWidthInPixels() const { return width_in_pixels_; } + bool IsValid() const; - inline unsigned int GetHeightInPixels() const { return height_in_pixels_; } + inline UINT GetWidthInPixels() const { return width_in_pixels_; } - inline unsigned int GetFrameDelay() const { return frame_delay_; } + inline UINT GetHeightInPixels() const { return height_in_pixels_; } - inline unsigned int GetFramesCount() const { return frames_count_; } + inline UINT GetFramesCount() const { return frames_count_; } + + inline Color GetBackgroundColor() const { return bg_color_; } public: enum class DisposalType @@ -57,37 +54,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..5fbd80ae 100644 --- a/src/kiwano/renderer/Image.cpp +++ b/src/kiwano/renderer/Image.cpp @@ -19,16 +19,20 @@ // THE SOFTWARE. #include "Image.h" +#include "Renderer.h" #include "../base/Logger.h" -#include "../platform/modules.h" namespace kiwano { Image::Image() - : bitmap_(nullptr) { } + Image::Image(Resource const& res) + { + Load(res); + } + Image::Image(ComPtr const & bitmap) : Image() { @@ -39,9 +43,15 @@ namespace kiwano { } + 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 +110,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..86d4a71c 100644 --- a/src/kiwano/renderer/Image.h +++ b/src/kiwano/renderer/Image.h @@ -25,19 +25,26 @@ namespace kiwano { // 图像 - KGE_DECLARE_SMART_PTR(Image); class KGE_API Image - : public Object { public: Image(); + explicit Image( + Resource const& res + ); + explicit Image( ComPtr const& bitmap ); virtual ~Image(); + // 加载资源 + bool Load( + Resource const& res + ); + // 资源是否有效 bool IsValid() const; @@ -59,12 +66,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..e950d1bc 100644 --- a/src/kiwano/renderer/ImageCache.cpp +++ b/src/kiwano/renderer/ImageCache.cpp @@ -19,6 +19,7 @@ // THE SOFTWARE. #include "ImageCache.h" +#include "Renderer.h" #include "../base/Logger.h" namespace kiwano @@ -32,43 +33,28 @@ namespace kiwano { } - ImagePtr ImageCache::AddImage(Resource const& res) + Image ImageCache::AddImage(Resource const& res) { size_t hash_code = res.GetHashCode(); + auto iter = image_cache_.find(hash_code); if (iter != image_cache_.end()) { return iter->second; } - HRESULT hr = S_OK; - ComPtr bitmap; - - if (res.IsFileType()) + Image image; + if (image.Load(res)) { - hr = Renderer::GetInstance()->GetD2DDeviceResources()->CreateBitmapFromFile(bitmap, res.GetFileName()); + image_cache_.insert(std::make_pair(hash_code, image)); } - else - { - hr = Renderer::GetInstance()->GetD2DDeviceResources()->CreateBitmapFromResource(bitmap, res); - } - - 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; + return image; } void ImageCache::RemoveImage(Resource const& res) { size_t hash_code = res.GetHashCode(); + auto iter = image_cache_.find(hash_code); if (iter != image_cache_.end()) { @@ -76,9 +62,39 @@ namespace kiwano } } + GifImagePtr ImageCache::AddGifImage(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + + auto iter = gif_image_cache_.find(hash_code); + if (iter != gif_image_cache_.end()) + { + return iter->second; + } + + GifImagePtr ptr = new GifImage; + if (ptr->Load(res)) + { + gif_image_cache_.insert(std::make_pair(hash_code, ptr)); + } + return ptr; + } + + void ImageCache::RemoveGifImage(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + + auto iter = gif_image_cache_.find(hash_code); + if (iter != gif_image_cache_.end()) + { + gif_image_cache_.erase(iter); + } + } + 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..0534704e 100644 --- a/src/kiwano/renderer/ImageCache.h +++ b/src/kiwano/renderer/ImageCache.h @@ -20,7 +20,8 @@ #pragma once #include "../core/singleton.hpp" -#include "Renderer.h" +#include "Image.h" +#include "GifImage.h" namespace kiwano { @@ -30,10 +31,14 @@ namespace kiwano KGE_DECLARE_SINGLETON(ImageCache); public: - ImagePtr AddImage(Resource const& res); + Image AddImage(Resource const& res); void RemoveImage(Resource const& res); + GifImagePtr AddGifImage(Resource const& res); + + void RemoveGifImage(Resource const& res); + void Clear(); protected: @@ -42,7 +47,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/RenderTarget.cpp b/src/kiwano/renderer/RenderTarget.cpp new file mode 100644 index 00000000..5dcb8b35 --- /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(ComPtr& layer) const + { + HRESULT hr = S_OK; + ComPtr new_layer; + + if (!render_target_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + hr = render_target_->CreateLayer(&new_layer); + } + + if (SUCCEEDED(hr)) + { + layer = new_layer; + } + + 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), + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE + ); + } + + 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(ComPtr const& layer, LayerProperties const& properties) + { + HRESULT hr = S_OK; + if (!render_target_ || !solid_color_brush_) + { + hr = E_UNEXPECTED; + } + + if (SUCCEEDED(hr)) + { + render_target_->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 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..5619bd30 --- /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 "D2DDeviceResources.h" +#include "Image.h" +#include "Geometry.h" +#include "TextLayout.h" +#include "TextRenderer.h" + +namespace kiwano +{ + // 渲染目标 + class KGE_API RenderTarget + : public noncopyable + { + public: + void BeginDraw(); + + void EndDraw(); + + void CreateLayer( + ComPtr& 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( + ComPtr const& layer, + LayerProperties const& properties + ); + + 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 60c365dd..1688c879 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() @@ -74,8 +70,6 @@ namespace kiwano #endif ); - device_context_ = d2d_res_->GetDeviceContext(); - ThrowIfFailed( d2d_res_->GetFactory()->CreateDrawingStateBlock( &drawing_state_block_ @@ -101,16 +95,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 +163,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,82 +187,104 @@ 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, Resource const& res) { HRESULT hr = S_OK; - ComPtr new_layer; - - if (!device_context_) + if (!d2d_res_) { hr = E_UNEXPECTED; } - if (SUCCEEDED(hr)) + ComPtr bitmap; + if (res.IsFileType()) { - hr = device_context_->CreateLayer(&new_layer); + hr = d2d_res_->CreateBitmapFromFile(bitmap, res.GetFileName()); + } + else + { + hr = d2d_res_->CreateBitmapFromResource(bitmap, res); } if (SUCCEEDED(hr)) { - layer = new_layer; + image.SetBitmap(bitmap); } - ThrowIfFailed(hr); + if (FAILED(hr)) + { + KGE_WARNING_LOG(L"Load 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; + } + + ComPtr decoder; + if (res.IsFileType()) + { + if (!FileUtil::ExistsFile(res.GetFileName().c_str())) + { + KGE_WARNING_LOG(L"Gif image file '%s' not found!", res.GetFileName().c_str()); + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetWICImagingFactory()->CreateDecoderFromFilename( + res.GetFileName().c_str(), + nullptr, + GENERIC_READ, + WICDecodeMetadataCacheOnLoad, + &decoder + ); + } + } + else + { + LPVOID buffer; + DWORD buffer_size; + HRESULT hr = res.Load(buffer, buffer_size) ? S_OK : E_FAIL; + + ComPtr stream; + + if (SUCCEEDED(hr)) + { + hr = d2d_res_->GetWICImagingFactory()->CreateStream(&stream); + } + + if (SUCCEEDED(hr)) + { + hr = stream->InitializeFromMemory( + static_cast(buffer), + buffer_size + ); + } + + if (SUCCEEDED(hr)) + { + 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::CreateTextFormat(TextFormat& format, Font const& font) @@ -297,7 +339,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; } @@ -333,7 +375,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; } @@ -355,7 +397,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; } @@ -383,7 +425,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; } @@ -411,7 +453,7 @@ namespace kiwano void Renderer::CreatePathGeometrySink(GeometrySink& sink) { HRESULT hr = S_OK; - if (!device_context_ || !d2d_res_) + if (!d2d_res_) { hr = E_UNEXPECTED; } @@ -430,150 +472,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(TextLayout const& layout) - { - HRESULT hr = S_OK; - if (!text_renderer_) - { - hr = E_UNEXPECTED; - } - - if (SUCCEEDED(hr)) - { - SetTextStyle( - opacity_, - layout.GetTextStyle().color, - layout.GetTextStyle().outline, - layout.GetTextStyle().outline_color, - layout.GetTextStyle().outline_width, - layout.GetTextStyle().outline_stroke - ); - } - - if (SUCCEEDED(hr)) - { - hr = layout.GetTextLayout()->Draw(nullptr, text_renderer_.get(), 0, 0); - - IncreasePrimitivesCount(); + hr = render_target.InitDeviceResources(output, d2d_res_); } ThrowIfFailed(hr); @@ -584,85 +499,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; @@ -681,136 +517,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_) - { - 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 aa4b88f0..16d8081e 100644 --- a/src/kiwano/renderer/Renderer.h +++ b/src/kiwano/renderer/Renderer.h @@ -24,10 +24,8 @@ #include "../base/Resource.h" #include "../2d/include-forwards.h" #include "helper.hpp" -#include "Image.h" -#include "Geometry.h" -#include "TextLayout.h" -#include "TextRenderer.h" +#include "RenderTarget.h" +#include "GifImage.h" #if defined(KGE_USE_DIRECTX10) # include "D3D10DeviceResources.h" @@ -47,6 +45,7 @@ namespace kiwano class KGE_API Renderer : public Singleton , public Component + , public RenderTarget { KGE_DECLARE_SINGLETON(Renderer); @@ -56,24 +55,20 @@ namespace kiwano Color const& clear_color ); - // 设置抗锯齿模式 - void SetAntialiasMode( - bool enabled - ); - - // 设置文字抗锯齿模式 - void SetTextAntialiasMode( - TextAntialias mode - ); - // 开启或关闭垂直同步 void SetVSyncEnabled( bool enabled ); public: - void CreateLayer( - ComPtr& layer + void CreateImage( + Image& image, + Resource const& res + ); + + void CreateGifImage( + GifImage& image, + Resource const& res ); void CreateTextFormat( @@ -115,71 +110,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( - TextLayout const& layout - ); - - 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 @@ -201,20 +135,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(); } @@ -234,28 +157,14 @@ 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_; }; } diff --git a/src/kiwano/utils/ResourceCache.cpp b/src/kiwano/utils/ResourceCache.cpp index b4c111c8..7d86fac6 100644 --- a/src/kiwano/utils/ResourceCache.cpp +++ b/src/kiwano/utils/ResourceCache.cpp @@ -280,7 +280,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); } } @@ -304,7 +304,7 @@ namespace kiwano FramePtr ptr = new (std::nothrow) Frame(raw->GetImage()); if (ptr) { - ptr->Crop(rect); + ptr->SetCropRect(rect); image_arr.push_back(ptr); } }