From 89925763cd7225c0e258cd49f53199f354b08a8e Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sat, 7 Feb 2026 18:49:56 +0300 Subject: [PATCH] Calculate accurate brush stroke data in parallel before drawing --- .../BrushStrokeCanvasComponent.cpp | 43 +++++++++++-------- .../BrushStrokeCanvasComponent.hpp | 6 ++- src/CanvasComponents/CanvasComponent.cpp | 10 ++++- src/CanvasComponents/CanvasComponent.hpp | 6 ++- .../CanvasComponentContainer.cpp | 26 +++++++---- .../CanvasComponentContainer.hpp | 15 ++++--- .../EllipseCanvasComponent.cpp | 2 +- .../EllipseCanvasComponent.hpp | 2 +- src/CanvasComponents/ImageCanvasComponent.cpp | 2 +- src/CanvasComponents/ImageCanvasComponent.hpp | 2 +- .../RectangleCanvasComponent.cpp | 2 +- .../RectangleCanvasComponent.hpp | 2 +- .../TextBoxCanvasComponent.cpp | 2 +- .../TextBoxCanvasComponent.hpp | 2 +- src/DrawingProgram/DrawingProgramCache.cpp | 14 +++--- 15 files changed, 86 insertions(+), 50 deletions(-) diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp index f2d920d..cb28110 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.cpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.cpp @@ -8,6 +8,7 @@ #include "../TimePoint.hpp" #include #include +#include #include "../DrawCollision.hpp" #include "CanvasComponentContainer.hpp" #include "Helpers/SCollision.hpp" @@ -49,7 +50,7 @@ std::optional BrushStrokeCanvasComponent::get_stroke_color() const { return d.color; } -void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { +void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const { SkPaint paint; paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()}); @@ -73,10 +74,10 @@ void BrushStrokeCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData //draw_collider(canvas, drawData, strokeObjects, paint2); } -bool BrushStrokeCanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords) const { +std::shared_ptr BrushStrokeCanvasComponent::get_predraw_data_accurate(const DrawData& drawData, const CoordSpaceHelper& coords) const { std::vector points = smooth_points(0, d.points->size() - 1, DEFAULT_SMOOTHNESS); - std::vector pathsToDraw; + auto pathsToDraw = std::make_shared>(); auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) { SCollision::Triangle worldTri = { @@ -111,7 +112,7 @@ bool BrushStrokeCanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& SkPathBuilder pathBuilder; std::vector triSkPoints = {convert_vec2(c.to_space(tri[0])), convert_vec2(c.to_space(tri[1])), convert_vec2(c.to_space(tri[2]))}; pathBuilder.addPolygon({triSkPoints.data(), triSkPoints.size()}, true); - pathsToDraw.emplace_back(pathBuilder.detach()); + pathsToDraw->emplace_back(pathBuilder.detach()); } } return false; @@ -119,19 +120,27 @@ bool BrushStrokeCanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& create_triangles(triangleFunc, points, 0, nullptr); - if(d.color.w() == 1.0f) { - SkPaint paint; - paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()}); - for(const SkPath& p : pathsToDraw) - canvas->drawPath(p, paint); - } - else { - canvas->saveLayerAlphaf(nullptr, d.color.w()); - SkPaint paint; - paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), 1.0f}); - for(const SkPath& p : pathsToDraw) - canvas->drawPath(p, paint); - canvas->restore(); + return pathsToDraw; +} + +bool BrushStrokeCanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords, const std::shared_ptr& predrawData) const { + auto pathsToDraw = std::static_pointer_cast>(predrawData); + + if(!pathsToDraw->empty()) { + if(d.color.w() == 1.0f) { + SkPaint paint; + paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), d.color.w()}); + for(const SkPath& p : *pathsToDraw) + canvas->drawPath(p, paint); + } + else { + canvas->saveLayerAlphaf(nullptr, d.color.w()); + SkPaint paint; + paint.setColor4f(SkColor4f{d.color.x(), d.color.y(), d.color.z(), 1.0f}); + for(const SkPath& p : *pathsToDraw) + canvas->drawPath(p, paint); + canvas->restore(); + } } return true; diff --git a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp index 4c9dbb9..968dc8b 100644 --- a/src/CanvasComponents/BrushStrokeCanvasComponent.hpp +++ b/src/CanvasComponents/BrushStrokeCanvasComponent.hpp @@ -34,8 +34,10 @@ class BrushStrokeCanvasComponent : public CanvasComponent { virtual void set_data_from(const CanvasComponent& other) override; private: - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; - virtual bool accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords) const override; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; + virtual bool accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords, const std::shared_ptr& predrawData) const override; + virtual std::shared_ptr get_predraw_data_accurate(const DrawData& drawData, const CoordSpaceHelper& coords) const override; + virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; void create_collider(); diff --git a/src/CanvasComponents/CanvasComponent.cpp b/src/CanvasComponents/CanvasComponent.cpp index bc27df4..0d54b94 100644 --- a/src/CanvasComponents/CanvasComponent.cpp +++ b/src/CanvasComponents/CanvasComponent.cpp @@ -37,8 +37,16 @@ std::optional CanvasComponent::get_stroke_color() const { return std::nullopt; } -bool CanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords) const { +bool CanvasComponent::accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords, const std::shared_ptr& predrawData) const { return false; } +std::shared_ptr CanvasComponent::get_predraw_data(const DrawData& drawData) const { + return nullptr; +} + +std::shared_ptr CanvasComponent::get_predraw_data_accurate(const DrawData& drawData, const CoordSpaceHelper& coords) const { + return nullptr; +} + CanvasComponent::~CanvasComponent() {} diff --git a/src/CanvasComponents/CanvasComponent.hpp b/src/CanvasComponents/CanvasComponent.hpp index 4d82041..6507761 100644 --- a/src/CanvasComponents/CanvasComponent.hpp +++ b/src/CanvasComponents/CanvasComponent.hpp @@ -30,9 +30,11 @@ class CanvasComponent { friend class CanvasComponentContainer; friend class CanvasComponentAllocator; - virtual bool accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords) const; + virtual bool accurate_draw(SkCanvas* canvas, const DrawData& drawData, const CoordSpaceHelper& coords, const std::shared_ptr& predrawData) const; + virtual std::shared_ptr get_predraw_data(const DrawData& drawData) const; + virtual std::shared_ptr get_predraw_data_accurate(const DrawData& drawData, const CoordSpaceHelper& coords) const; - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const = 0; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const = 0; virtual void initialize_draw_data(DrawingProgram& drawP) = 0; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const = 0; diff --git a/src/CanvasComponents/CanvasComponentContainer.cpp b/src/CanvasComponents/CanvasComponentContainer.cpp index 535eab4..90ac6e9 100644 --- a/src/CanvasComponents/CanvasComponentContainer.cpp +++ b/src/CanvasComponents/CanvasComponentContainer.cpp @@ -89,14 +89,14 @@ void CanvasComponentContainer::load_file(cereal::PortableBinaryInputArchive& a, void CanvasComponentContainer::draw(SkCanvas* canvas, const DrawData& drawData) const { if(should_draw(drawData)) - draw_with_transform(canvas, drawData, calculate_draw_transform(drawData)); + draw_with_predraw_data(canvas, drawData, calculate_predraw_data(drawData)); } -void CanvasComponentContainer::draw_with_transform(SkCanvas* canvas, const DrawData& drawData, const TransformDrawData& transformDrawData) const { - if(transformDrawData.scale < COMP_MAX_BEFORE_STOP_SCALING || !get_comp().accurate_draw(canvas, drawData, coords)) { +void CanvasComponentContainer::draw_with_predraw_data(SkCanvas* canvas, const DrawData& drawData, const PreDrawData& preDrawData) const { + if(preDrawData.transformData.scale < COMP_MAX_BEFORE_STOP_SCALING || !get_comp().accurate_draw(canvas, drawData, coords, preDrawData.extraData)) { canvas->save(); - canvas_do_transform(canvas, transformDrawData); - get_comp().draw(canvas, drawData); + canvas_do_transform(canvas, preDrawData.transformData); + get_comp().draw(canvas, drawData, preDrawData.extraData); canvas->restore(); } } @@ -151,7 +151,7 @@ bool CanvasComponentContainer::collides_with(const CoordSpaceHelper& camCoords, } } -void CanvasComponentContainer::canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const { +void CanvasComponentContainer::canvas_do_transform(SkCanvas* canvas, const TransformData& transformData) const { canvas->scale(transformData.scale, transformData.scale); canvas->rotate(transformData.rotation); canvas->translate(transformData.translation.x(), transformData.translation.y()); @@ -161,8 +161,18 @@ bool CanvasComponentContainer::should_draw(const DrawData& drawData) const { return (!drawData.clampDrawBetween || (drawData.clampDrawMinimum < coords.inverseScale)) && SCollision::collide(worldAABB.value(), drawData.cam.viewingAreaGenerousCollider); } -CanvasComponentContainer::TransformDrawData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const { - TransformDrawData toRet; +CanvasComponentContainer::PreDrawData CanvasComponentContainer::calculate_predraw_data(const DrawData& drawData) const { + PreDrawData toRet; + toRet.transformData = calculate_draw_transform(drawData); + if(toRet.transformData.scale < COMP_MAX_BEFORE_STOP_SCALING) + toRet.extraData = get_comp().get_predraw_data(drawData); + else + toRet.extraData = get_comp().get_predraw_data_accurate(drawData, coords); + return toRet; +} + +CanvasComponentContainer::TransformData CanvasComponentContainer::calculate_draw_transform(const DrawData& drawData) const { + TransformData toRet; toRet.translation = -coords.to_space(drawData.cam.c.pos); toRet.rotation = (coords.rotation - drawData.cam.c.rotation) * 180.0 / std::numbers::pi; toRet.scale = std::min(static_cast(coords.inverseScale / drawData.cam.c.inverseScale), COMP_MAX_BEFORE_STOP_SCALING); diff --git a/src/CanvasComponents/CanvasComponentContainer.hpp b/src/CanvasComponents/CanvasComponentContainer.hpp index 53a8ac0..c3e528c 100644 --- a/src/CanvasComponents/CanvasComponentContainer.hpp +++ b/src/CanvasComponents/CanvasComponentContainer.hpp @@ -34,12 +34,16 @@ class CanvasComponentContainer { void scale_up(const WorldScalar& scaleUpAmount); }; - struct TransformDrawData { + struct TransformData { Vector2f translation; float rotation; float scale; }; - std::optional transformHolder; // Can be used to store transforms when calculating transforms in parallel + struct PreDrawData { + TransformData transformData; + std::shared_ptr extraData; + }; + std::optional preDrawDataHolder; // Can be used to store transforms when calculating transforms in parallel (and other predraw data) CanvasComponentContainer(); CanvasComponentContainer(NetworkingObjects::NetObjManager& objMan, CanvasComponentType type); @@ -53,8 +57,9 @@ class CanvasComponentContainer { CanvasComponent& get_comp() const; SCollision::AABB get_world_bounds() const; void draw(SkCanvas* canvas, const DrawData& drawData) const; - void draw_with_transform(SkCanvas* canvas, const DrawData& drawData, const TransformDrawData& transformDrawData) const; - TransformDrawData calculate_draw_transform(const DrawData& drawData) const; + void draw_with_predraw_data(SkCanvas* canvas, const DrawData& drawData, const PreDrawData& preDrawData) const; + PreDrawData calculate_predraw_data(const DrawData& drawData) const; + TransformData calculate_draw_transform(const DrawData& drawData) const; void commit_update(DrawingProgram& drawP); void commit_transform_dont_invalidate_cache(); // Must be thread safe void commit_transform(DrawingProgram& drawP); @@ -78,7 +83,7 @@ class CanvasComponentContainer { unsigned get_mipmap_level(const DrawData& drawData) const; CanvasComponent* allocate_comp(CanvasComponentType type); - void canvas_do_transform(SkCanvas* canvas, const TransformDrawData& transformData) const; + void canvas_do_transform(SkCanvas* canvas, const TransformData& transformData) const; void calculate_world_bounds(); std::optional> worldAABB; diff --git a/src/CanvasComponents/EllipseCanvasComponent.cpp b/src/CanvasComponents/EllipseCanvasComponent.cpp index 409d731..33ad21b 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.cpp +++ b/src/CanvasComponents/EllipseCanvasComponent.cpp @@ -33,7 +33,7 @@ std::optional EllipseCanvasComponent::get_stroke_color() const { return d.strokeColor; } -void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { +void EllipseCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const { SkPaint p; if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) { p.setStyle(SkPaint::kFill_Style); diff --git a/src/CanvasComponents/EllipseCanvasComponent.hpp b/src/CanvasComponents/EllipseCanvasComponent.hpp index a9dca0c..ddf1564 100644 --- a/src/CanvasComponents/EllipseCanvasComponent.hpp +++ b/src/CanvasComponents/EllipseCanvasComponent.hpp @@ -29,7 +29,7 @@ class EllipseCanvasComponent : public CanvasComponent { private: void create_draw_data(); void create_collider(); - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; virtual SCollision::AABB get_obj_coord_bounds() const override; diff --git a/src/CanvasComponents/ImageCanvasComponent.cpp b/src/CanvasComponents/ImageCanvasComponent.cpp index 37c1c8e..dcffa8d 100644 --- a/src/CanvasComponents/ImageCanvasComponent.cpp +++ b/src/CanvasComponents/ImageCanvasComponent.cpp @@ -86,7 +86,7 @@ void ImageCanvasComponent::get_used_resources(std::unordered_set& predrawData) const { ResourceDisplay* display = drawData.rMan->get_display_data(d.imageID); if(d.editing) { canvas->saveLayerAlphaf(nullptr, 1.0f); diff --git a/src/CanvasComponents/ImageCanvasComponent.hpp b/src/CanvasComponents/ImageCanvasComponent.hpp index a76d129..6710b01 100644 --- a/src/CanvasComponents/ImageCanvasComponent.hpp +++ b/src/CanvasComponents/ImageCanvasComponent.hpp @@ -35,7 +35,7 @@ class ImageCanvasComponent : public CanvasComponent { bool contains_actual_image(); private: - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; void create_collider(); diff --git a/src/CanvasComponents/RectangleCanvasComponent.cpp b/src/CanvasComponents/RectangleCanvasComponent.cpp index a58449a..12d12e2 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.cpp +++ b/src/CanvasComponents/RectangleCanvasComponent.cpp @@ -41,7 +41,7 @@ std::unique_ptr RectangleCanvasComponent::get_data_copy() const return toRet; } -void RectangleCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { +void RectangleCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const { if(d.fillStrokeMode == 0 || d.fillStrokeMode == 2) { SkPaint p; p.setStyle(SkPaint::kFill_Style); diff --git a/src/CanvasComponents/RectangleCanvasComponent.hpp b/src/CanvasComponents/RectangleCanvasComponent.hpp index be8ccdf..e4f115d 100644 --- a/src/CanvasComponents/RectangleCanvasComponent.hpp +++ b/src/CanvasComponents/RectangleCanvasComponent.hpp @@ -29,7 +29,7 @@ class RectangleCanvasComponent : public CanvasComponent { } d; private: - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; void create_draw_data(); diff --git a/src/CanvasComponents/TextBoxCanvasComponent.cpp b/src/CanvasComponents/TextBoxCanvasComponent.cpp index e910aeb..6f395f5 100644 --- a/src/CanvasComponents/TextBoxCanvasComponent.cpp +++ b/src/CanvasComponents/TextBoxCanvasComponent.cpp @@ -79,7 +79,7 @@ void TextBoxCanvasComponent::init_text_box(DrawingProgram& drawP) { textBox->set_width(d.p2.x() - d.p1.x() - TEXTBOX_PADDING * 2.0f); } -void TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const { +void TextBoxCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const { SkRect clipR = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y()); if(d.editing) { SkPaint p; diff --git a/src/CanvasComponents/TextBoxCanvasComponent.hpp b/src/CanvasComponents/TextBoxCanvasComponent.hpp index 72e7fea..1bfd941 100644 --- a/src/CanvasComponents/TextBoxCanvasComponent.hpp +++ b/src/CanvasComponents/TextBoxCanvasComponent.hpp @@ -30,7 +30,7 @@ class TextBoxCanvasComponent : public CanvasComponent { private: friend class TextBoxEditTool; - virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override; + virtual void draw(SkCanvas* canvas, const DrawData& drawData, const std::shared_ptr& predrawData) const override; virtual void initialize_draw_data(DrawingProgram& drawP) override; virtual bool collides_within_coords(const SCollision::ColliderCollection& checkAgainst) const override; void create_collider(); diff --git a/src/DrawingProgram/DrawingProgramCache.cpp b/src/DrawingProgram/DrawingProgramCache.cpp index cdc1b4c..addf8d3 100644 --- a/src/DrawingProgram/DrawingProgramCache.cpp +++ b/src/DrawingProgram/DrawingProgramCache.cpp @@ -449,32 +449,32 @@ void DrawingProgramCache::recursive_draw_layer_item_to_canvas(const DrawingProgr parallel_loop_container(nodesToDraw, [&](auto& node) { std::for_each(node->components.begin(), node->components.end(), [&](auto& c) { if(c->obj->parentLayer == &layerListItem && (!drawBounds.has_value() || SCollision::collide(drawBounds.value(), c->obj->get_world_bounds())) && c->obj->should_draw(drawData)) - c->obj->transformHolder = c->obj->calculate_draw_transform(drawData); + c->obj->preDrawDataHolder = c->obj->calculate_predraw_data(drawData); else - c->obj->transformHolder = std::nullopt; + c->obj->preDrawDataHolder = std::nullopt; }); }); parallel_loop_container(unsortedComponents, [&](auto& c) { if(c->obj->parentLayer == &layerListItem && (!drawBounds.has_value() || SCollision::collide(drawBounds.value(), c->obj->get_world_bounds())) && c->obj->should_draw(drawData)) - c->obj->transformHolder = c->obj->calculate_draw_transform(drawData); + c->obj->preDrawDataHolder = c->obj->calculate_predraw_data(drawData); else - c->obj->transformHolder = std::nullopt; + c->obj->preDrawDataHolder = std::nullopt; }); for(auto& node : nodesToDraw) { for(auto& c : node->components) { - if(c->obj->transformHolder.has_value()) + if(c->obj->preDrawDataHolder.has_value()) compsToDraw.emplace_back(c); } } for(auto& c : unsortedComponents) { - if(c->obj->transformHolder.has_value()) + if(c->obj->preDrawDataHolder.has_value()) compsToDraw.emplace_back(c); } std::sort(compsToDraw.begin(), compsToDraw.end(), [](auto& a, auto& b) { return a->pos < b->pos; }); for(auto& c : compsToDraw) - c->obj->draw_with_transform(canvas, drawData, c->obj->transformHolder.value()); + c->obj->draw_with_predraw_data(canvas, drawData, c->obj->preDrawDataHolder.value()); } canvas->restore(); }