2019-04-11 14:40:54 +08:00
|
|
|
// Copyright (c) 2016-2018 Kiwano - Nomango
|
2020-01-21 10:09:55 +08:00
|
|
|
//
|
2019-03-31 01:37:06 +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-03-31 01:37:06 +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-03-31 01:37:06 +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-10-11 21:55:29 +08:00
|
|
|
#include <kiwano/2d/Canvas.h>
|
2019-11-13 14:33:15 +08:00
|
|
|
#include <kiwano/core/Logger.h>
|
2020-01-17 16:55:47 +08:00
|
|
|
#include <kiwano/render/Renderer.h>
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2019-04-11 14:40:54 +08:00
|
|
|
namespace kiwano
|
2019-03-31 01:37:06 +08:00
|
|
|
{
|
2020-02-06 16:54:47 +08:00
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
CanvasPtr Canvas::Create(const Size& size)
|
2020-02-06 16:54:47 +08:00
|
|
|
{
|
|
|
|
|
CanvasPtr ptr = new (std::nothrow) Canvas;
|
2020-02-08 00:17:31 +08:00
|
|
|
if (ptr)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
ptr->ctx_ = TextureRenderContext::Create();
|
|
|
|
|
ptr->stroke_brush_ = Brush::Create(Color::White);
|
|
|
|
|
ptr->fill_brush_ = Brush::Create(Color::White);
|
2020-02-09 18:41:59 +08:00
|
|
|
|
|
|
|
|
ptr->SetSize(ptr->ctx_->GetSize());
|
2020-02-08 00:17:31 +08:00
|
|
|
}
|
|
|
|
|
catch (std::exception)
|
|
|
|
|
{
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-06 16:54:47 +08:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-21 10:09:55 +08:00
|
|
|
Canvas::Canvas()
|
|
|
|
|
: cache_expired_(false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::BeginDraw()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->BeginDraw();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::EndDraw()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->EndDraw();
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::OnRender(RenderContext& ctx)
|
|
|
|
|
{
|
|
|
|
|
UpdateCache();
|
|
|
|
|
|
|
|
|
|
if (texture_cached_ && texture_cached_->IsValid())
|
|
|
|
|
{
|
|
|
|
|
PrepareToRender(ctx);
|
2020-02-09 18:41:59 +08:00
|
|
|
ctx.DrawTexture(*texture_cached_, nullptr, &GetBounds());
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::SetBrush(BrushPtr brush)
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(brush);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::SetBrushTransform(const Transform& transform)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetTransform(transform.ToMatrix());
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::SetBrushTransform(const Matrix3x2& transform)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetTransform(transform);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-16 20:14:01 +08:00
|
|
|
void Canvas::PushLayer(LayerPtr layer)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-02-16 20:14:01 +08:00
|
|
|
if (layer)
|
|
|
|
|
{
|
|
|
|
|
ctx_->PushLayer(*layer);
|
|
|
|
|
}
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
2019-03-31 01:37:06 +08:00
|
|
|
|
2020-02-10 18:03:35 +08:00
|
|
|
void Canvas::PopLayer()
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->PopLayer();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::PushClipRect(const Rect& clip_rect)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->PushClipRect(clip_rect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::PopClipRect()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->PopClipRect();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-05 19:56:22 +08:00
|
|
|
void Canvas::DrawShape(ShapePtr shape)
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
|
|
|
|
if (shape)
|
|
|
|
|
{
|
|
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawShape(*shape);
|
2020-02-08 00:17:31 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
2020-02-05 19:56:22 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawLine(const Point& begin, const Point& end)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawLine(begin, end);
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawCircle(const Point& center, float radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawEllipse(center, Vec2(radius, radius));
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawEllipse(const Point& center, const Vec2& radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawEllipse(center, radius);
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawRect(const Rect& rect)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawRectangle(rect);
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawRoundedRect(const Rect& rect, const Vec2& radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawRoundedRectangle(rect, radius);
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-05 19:56:22 +08:00
|
|
|
void Canvas::FillShape(ShapePtr shape)
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
|
|
|
|
if (shape)
|
|
|
|
|
{
|
|
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
|
|
|
|
ctx_->FillShape(*shape);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
2020-02-05 19:56:22 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::FillCircle(const Point& center, float radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
|
|
|
|
ctx_->FillEllipse(center, Vec2(radius, radius));
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::FillEllipse(const Point& center, const Vec2& radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
|
|
|
|
ctx_->FillEllipse(center, radius);
|
|
|
|
|
cache_expired_ = true;
|
2019-08-08 14:15:06 +08:00
|
|
|
}
|
2020-01-21 10:09:55 +08:00
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::FillRect(const Rect& rect)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
|
|
|
|
ctx_->FillRectangle(rect);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::FillRoundedRect(const Rect& rect, const Vec2& radius)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
|
|
|
|
ctx_->FillRoundedRectangle(rect, radius);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::DrawTexture(TexturePtr texture, const Rect* src_rect, const Rect* dest_rect)
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
if (texture)
|
|
|
|
|
{
|
|
|
|
|
ctx_->DrawTexture(*texture, src_rect, dest_rect);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawTextLayout(const String& text, const TextStyle& style, const Point& point)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
|
|
|
|
if (text.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
2020-02-16 12:53:18 +08:00
|
|
|
DrawTextLayout(TextLayout::Create(text, style), point);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::DrawTextLayout(TextLayoutPtr layout, const Point& point)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-02-16 12:53:18 +08:00
|
|
|
if (layout)
|
|
|
|
|
{
|
|
|
|
|
ctx_->DrawTextLayout(*layout, point);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::BeginPath(const Point& begin_pos)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.BeginPath(begin_pos);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::EndPath(bool closed)
|
|
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.EndPath(closed);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::AddLine(const Point& point)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.AddLine(point);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::AddLines(const Vector<Point>& points)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.AddLines(points);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::AddBezier(const Point& point1, const Point& point2, const Point& point3)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.AddBezier(point1, point2, point3);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::AddArc(const Point& point, const Size& radius, float rotation, bool clockwise, bool is_small)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-16 20:14:01 +08:00
|
|
|
shape_maker_.AddArc(point, radius, rotation, clockwise, is_small);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::StrokePath()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(stroke_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->SetCurrentStrokeStyle(stroke_style_);
|
|
|
|
|
ctx_->DrawShape(*shape_maker_.GetShape());
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::FillPath()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->SetCurrentBrush(fill_brush_);
|
2020-02-16 20:14:01 +08:00
|
|
|
ctx_->FillShape(*shape_maker_.GetShape());
|
2020-01-21 10:09:55 +08:00
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::Clear()
|
|
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->Clear();
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 12:09:50 +08:00
|
|
|
void Canvas::Clear(const Color& clear_color)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
KGE_ASSERT(ctx_);
|
2020-01-21 10:09:55 +08:00
|
|
|
ctx_->Clear(clear_color);
|
|
|
|
|
cache_expired_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 00:17:31 +08:00
|
|
|
void Canvas::ResizeAndClear(Size size)
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
ctx_ = TextureRenderContext::Create(size);
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
2020-02-08 00:17:31 +08:00
|
|
|
TexturePtr Canvas::ExportToTexture() const
|
2020-01-21 10:09:55 +08:00
|
|
|
{
|
2020-02-08 00:17:31 +08:00
|
|
|
UpdateCache();
|
|
|
|
|
return texture_cached_;
|
2020-01-21 10:09:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Canvas::UpdateCache() const
|
|
|
|
|
{
|
|
|
|
|
if (cache_expired_ && ctx_)
|
|
|
|
|
{
|
|
|
|
|
if (!texture_cached_)
|
|
|
|
|
{
|
|
|
|
|
texture_cached_ = new Texture;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctx_->GetOutput(*texture_cached_))
|
|
|
|
|
{
|
|
|
|
|
cache_expired_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace kiwano
|