| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | // Copyright (c) 2016-2019 Kiwano - Nomango
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | // 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:
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | // The above copyright notice and this permission notice shall be included in
 | 
					
						
							|  |  |  | // all copies or substantial portions of the Software.
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | // 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.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-28 22:04:08 +08:00
										 |  |  | #include <kiwano/core/Logger.h>
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | #include <kiwano/render/RenderContext.h>
 | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace kiwano | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // RenderContext
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RenderContext::RenderContext() | 
					
						
							|  |  |  |     : collecting_status_(false) | 
					
						
							|  |  |  |     , fast_global_transform_(true) | 
					
						
							|  |  |  |     , brush_opacity_(1.0f) | 
					
						
							|  |  |  |     , antialias_(true) | 
					
						
							|  |  |  |     , text_antialias_(TextAntialiasMode::GrayScale) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     status_.primitives = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HRESULT RenderContext::CreateDeviceResources(ComPtr<ID2D1Factory> factory, ComPtr<ID2D1RenderTarget> ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!factory || !ctx) | 
					
						
							|  |  |  |         return E_INVALIDARG; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_ = ctx; | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     text_renderer_.reset(); | 
					
						
							|  |  |  |     current_brush_.reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     HRESULT hr = ITextRenderer::Create(&text_renderer_, render_target_.get()); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         SetAntialiasMode(antialias_); | 
					
						
							|  |  |  |         SetTextAntialiasMode(text_antialias_); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Resize(reinterpret_cast<const Size&>(GetRenderTarget()->GetSize())); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     // DrawingStateBlock
 | 
					
						
							|  |  |  |     if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         hr = factory->CreateDrawingStateBlock(&drawing_state_); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     return hr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DiscardDeviceResources() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     text_renderer_.reset(); | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_.reset(); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     current_brush_.reset(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool RenderContext::IsValid() const | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     return render_target_ != nullptr; | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::BeginDraw() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (collecting_status_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         status_.start      = Time::Now(); | 
					
						
							|  |  |  |         status_.primitives = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     if (render_target_) | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->BeginDraw(); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::EndDraw() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     win32::ThrowIfFailed(render_target_->EndDraw()); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (collecting_status_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         status_.duration = Time::Now() - status_.start; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawGeometry(Geometry const& geometry, float stroke_width, const StrokeStyle& stroke) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (geometry.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->DrawGeometry(geometry.GetGeometry().get(), current_brush_->GetBrush().get(), stroke_width, | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                   stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         IncreasePrimitivesCount(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::FillGeometry(Geometry const& geometry) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (geometry.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->FillGeometry(geometry.GetGeometry().get(), current_brush_->GetBrush().get()); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         IncreasePrimitivesCount(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawLine(Point const& point1, Point const& point2, float stroke_width, const StrokeStyle& stroke) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->DrawLine(DX::ConvertToPoint2F(point1), DX::ConvertToPoint2F(point2), current_brush_->GetBrush().get(), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                           stroke_width, stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawRectangle(Rect const& rect, float stroke_width, const StrokeStyle& stroke) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->DrawRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get(), stroke_width, | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::FillRectangle(Rect const& rect) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->FillRectangle(DX::ConvertToRectF(rect), current_brush_->GetBrush().get()); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawRoundedRectangle(Rect const& rect, Vec2 const& radius, float stroke_width, | 
					
						
							|  |  |  |                                          const StrokeStyle& stroke) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->DrawRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                       current_brush_->GetBrush().get(), stroke_width, stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::FillRoundedRectangle(Rect const& rect, Vec2 const& radius) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->FillRoundedRectangle(D2D1::RoundedRect(DX::ConvertToRectF(rect), radius.x, radius.y), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                       current_brush_->GetBrush().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawEllipse(Point const& center, Vec2 const& radius, float stroke_width, const StrokeStyle& stroke) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->DrawEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                              current_brush_->GetBrush().get(), stroke_width, stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::FillEllipse(Point const& center, Vec2 const& radius) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     KGE_ASSERT(current_brush_ && "The brush used for rendering has not been set!"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->FillEllipse(D2D1::Ellipse(DX::ConvertToPoint2F(center), radius.x, radius.y), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                              current_brush_->GetBrush().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IncreasePrimitivesCount(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawTexture(Texture const& texture, Rect const& src_rect, Rect const& dest_rect) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DrawTexture(texture, &src_rect, &dest_rect); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawTexture(Texture const& texture, const Rect* src_rect, const Rect* dest_rect) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (texture.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto mode = (texture.GetBitmapInterpolationMode() == InterpolationMode::Linear) | 
					
						
							|  |  |  |                         ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR | 
					
						
							|  |  |  |                         : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->DrawBitmap(texture.GetBitmap().get(), dest_rect ? &DX::ConvertToRectF(*dest_rect) : nullptr, | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                 brush_opacity_, mode, src_rect ? &DX::ConvertToRectF(*src_rect) : nullptr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         IncreasePrimitivesCount(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::DrawTextLayout(TextLayout const& layout, Point const& offset) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     KGE_ASSERT(text_renderer_ && "Text renderer has not been initialized!"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (layout.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ComPtr<ID2D1Brush> fill_brush; | 
					
						
							|  |  |  |         ComPtr<ID2D1Brush> outline_brush; | 
					
						
							|  |  |  |         const TextStyle&   style = layout.GetStyle(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (style.fill_brush) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fill_brush = style.fill_brush->GetBrush(); | 
					
						
							|  |  |  |             fill_brush->SetOpacity(brush_opacity_); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (style.outline_brush) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             outline_brush = style.outline_brush->GetBrush(); | 
					
						
							|  |  |  |             outline_brush->SetOpacity(brush_opacity_); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         HRESULT hr = text_renderer_->DrawTextLayout(layout.GetTextLayout().get(), offset.x, offset.y, fill_brush.get(), | 
					
						
							|  |  |  |                                                     outline_brush.get(), style.outline_width, | 
					
						
							|  |  |  |                                                     style.outline_stroke.GetStrokeStyle().get()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             IncreasePrimitivesCount(text_renderer_->GetLastPrimitivesCount()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             KGE_ERROR(L"Failed to draw text layout with HRESULT of %08X", hr); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::CreateTexture(Texture& texture, math::Vec2T<uint32_t> size, D2D1_PIXEL_FORMAT format) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ComPtr<ID2D1Bitmap> saved_bitmap; | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     HRESULT hr = render_target_->CreateBitmap(D2D1::SizeU(size.x, size.y), D2D1::BitmapProperties(format), &saved_bitmap); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         texture.SetBitmap(saved_bitmap); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         win32::ThrowIfFailed(hr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::PushClipRect(Rect const& clip_rect) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							|  |  |  |     render_target_->PushAxisAlignedClip(DX::ConvertToRectF(clip_rect), | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |                                      antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::PopClipRect() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							|  |  |  |     render_target_->PopAxisAlignedClip(); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::PushLayer(LayerArea& layer) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     if (!layer.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ComPtr<ID2D1Layer> output; | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         HRESULT            hr = render_target_->CreateLayer(&output); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             layer.SetLayer(output); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             win32::ThrowIfFailed(hr); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (layer.IsValid()) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->PushLayer( | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |             D2D1::LayerParameters(DX::ConvertToRectF(layer.GetAreaRect()), layer.GetMaskGeometry().GetGeometry().get(), | 
					
						
							|  |  |  |                                   antialias_ ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED, | 
					
						
							|  |  |  |                                   DX::ConvertToMatrix3x2F(layer.GetMaskTransform()), layer.GetOpacity(), nullptr, | 
					
						
							|  |  |  |                                   D2D1_LAYER_OPTIONS_NONE), | 
					
						
							|  |  |  |             layer.GetLayer().get()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::PopLayer() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							|  |  |  |     render_target_->PopLayer(); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::Clear() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							|  |  |  |     render_target_->Clear(); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::Clear(Color const& clear_color) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							|  |  |  |     render_target_->Clear(DX::ConvertToColorF(clear_color)); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::SetTransform(const Matrix3x2& matrix) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (fast_global_transform_) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->SetTransform(DX::ConvertToMatrix3x2F(&matrix)); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         Matrix3x2 result = matrix * global_transform_; | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |         render_target_->SetTransform(DX::ConvertToMatrix3x2F(&result)); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::SetGlobalTransform(const Matrix3x2* matrix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (matrix) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         global_transform_      = *matrix; | 
					
						
							|  |  |  |         fast_global_transform_ = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         fast_global_transform_ = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::SetAntialiasMode(bool enabled) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->SetAntialiasMode(enabled ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  |     antialias_ = enabled; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::SetTextAntialiasMode(TextAntialiasMode mode) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     D2D1_TEXT_ANTIALIAS_MODE antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; | 
					
						
							|  |  |  |     switch (mode) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     case TextAntialiasMode::Default: | 
					
						
							|  |  |  |         antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case TextAntialiasMode::ClearType: | 
					
						
							|  |  |  |         antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case TextAntialiasMode::GrayScale: | 
					
						
							|  |  |  |         antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case TextAntialiasMode::None: | 
					
						
							|  |  |  |         antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     text_antialias_ = mode; | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     render_target_->SetTextAntialiasMode(antialias_mode); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool RenderContext::CheckVisibility(Rect const& bounds, Matrix3x2 const& transform) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  |     KGE_ASSERT(render_target_ && "Render target has not been initialized!"); | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (fast_global_transform_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return visible_size_.Intersects(transform.Transform(bounds)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return visible_size_.Intersects(Matrix3x2(transform * global_transform_).Transform(bounds)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | void RenderContext::Resize(Size const& size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     visible_size_ = Rect(Point(), size); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::SetCollectingStatus(bool enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     collecting_status_ = enable; | 
					
						
							| 
									
										
										
										
											2019-08-16 00:50:54 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::IncreasePrimitivesCount(uint32_t increase) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (collecting_status_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         status_.primitives += increase; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-03 14:15:30 +08:00
										 |  |  | void RenderContext::SaveDrawingState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     KGE_ASSERT(IsValid()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (drawing_state_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         render_target_->SaveDrawingState(drawing_state_.get()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RenderContext::RestoreDrawingState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     KGE_ASSERT(IsValid()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (drawing_state_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         render_target_->RestoreDrawingState(drawing_state_.get()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-21 10:09:55 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // TextureRenderContext
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TextureRenderContext::TextureRenderContext() {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool TextureRenderContext::GetOutput(Texture& texture) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     HRESULT hr = E_FAIL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (bitmap_rt_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ComPtr<ID2D1Bitmap> bitmap; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         hr = bitmap_rt_->GetBitmap(&bitmap); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (SUCCEEDED(hr)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             texture.SetBitmap(bitmap); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return SUCCEEDED(hr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace kiwano
 |