From 95c1c3c9e5d22b3ae154273886383901af447550 Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Sat, 23 Aug 2025 13:28:01 +0300 Subject: [PATCH] Use SkPath instead of SkVertices to draw brush strokes --- .../com.infinipaint.infinipaint.desktop | 2 +- src/DrawComponents/DrawBrushStroke.cpp | 91 +++++++++++-------- src/DrawComponents/DrawBrushStroke.hpp | 6 +- 3 files changed, 54 insertions(+), 45 deletions(-) diff --git a/linuxinstall/com.infinipaint.infinipaint.desktop b/linuxinstall/com.infinipaint.infinipaint.desktop index 4e0ca99..5dbbeae 100644 --- a/linuxinstall/com.infinipaint.infinipaint.desktop +++ b/linuxinstall/com.infinipaint.infinipaint.desktop @@ -5,7 +5,7 @@ Type=Application Name=InfiniPaint Comment=Infinite canvas program Categories=Graphics;VectorGraphics; -Keywords=whiteboard;notes;drawing;infinite;canvas;graphics;zoom +Keywords=whiteboard;notes;drawing;draw;paint;painting;infinite;canvas;graphics;zoom Icon=com.infinipaint.infinipaint Exec=infinipaint %F diff --git a/src/DrawComponents/DrawBrushStroke.cpp b/src/DrawComponents/DrawBrushStroke.cpp index ec1d557..d9b6a60 100644 --- a/src/DrawComponents/DrawBrushStroke.cpp +++ b/src/DrawComponents/DrawBrushStroke.cpp @@ -40,8 +40,7 @@ std::shared_ptr DrawBrushStroke::deep_copy() const { a->d = d; a->coords = coords; // Just copy pointers over, brush strokes cant be edited so they can share the same memory - a->vertices = vertices; - a->verticesMipMap = verticesMipMap; + a->brushPath = brushPath; a->bounds = bounds; return a; @@ -63,7 +62,7 @@ Vector2f two_dim_vec_slerp(const Vector2f& v1, const Vector2f& v2) { } void DrawBrushStroke::draw(SkCanvas* canvas, const DrawData& drawData) { - if(drawSetupData.shouldDraw && vertices) { + if(drawSetupData.shouldDraw && brushPath) { //bool deepPrecheck = false; //for(auto& aabb : precheckAABBLevels) { // if(SCollision::collide(drawData.cam.viewingAreaGenerousCollider, aabb)) { @@ -75,16 +74,12 @@ void DrawBrushStroke::draw(SkCanvas* canvas, const DrawData& drawData) { // return; SkPaint paint; - if(d->color.w() == 1.0) // Fully opaque, dont bother with blending - canvas->save(); - else - canvas->saveLayerAlphaf(nullptr, d->color.w()); - paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), 1.0f}); + //paint.setStroke(true); + //paint.setStrokeWidth(0.0f); + canvas->save(); + paint.setColor4f(SkColor4f{d->color.x(), d->color.y(), d->color.z(), d->color.w()}); canvas_do_calculated_transform(canvas); - if(drawSetupData.mipmapLevel == 0) - canvas->drawVertices(vertices, SkBlendMode::kClear, paint); - else - canvas->drawVertices(verticesMipMap[drawSetupData.mipmapLevel - 1], SkBlendMode::kClear, paint); + canvas->drawPath(*brushPath, paint); //SkPaint p2(SkColor4f{1, 0, 0, 1}); //p2.setStroke(true); //SkPaint p3(SkColor4f{0, 1, 0, 1}); @@ -100,25 +95,14 @@ void DrawBrushStroke::draw(SkCanvas* canvas, const DrawData& drawData) { void DrawBrushStroke::initialize_draw_data(DrawingProgram& drawP) { if(!d->points.empty()) { - std::vector vertexData; std::vector points = smooth_points(0, d->points.size() - 1, DEFAULT_SMOOTHNESS); auto triangleFunc = [&](Vector2f a, Vector2f b, Vector2f c) { - vertexData.emplace_back(a.x(), a.y()); - vertexData.emplace_back(b.x(), b.y()); - vertexData.emplace_back(c.x(), c.y()); return false; }; - create_triangles(triangleFunc, points, 0); - vertices_to_draw_data(vertices, vertexData); - vertexData.clear(); + brushPath = std::make_shared(); - create_triangles(triangleFunc, points, 4); - vertices_to_draw_data(verticesMipMap[0], vertexData); - vertexData.clear(); - - create_triangles(triangleFunc, points, 30); - vertices_to_draw_data(verticesMipMap[1], vertexData); + create_triangles(triangleFunc, points, 0, brushPath); } create_collider(); } @@ -211,19 +195,26 @@ std::vector DrawBrushStroke::smooth_points_avg(size_t begi return toRet; } -void DrawBrushStroke::create_triangles(const std::function& passTriangleFunc, const std::vector& points, unsigned skipVertexCount) { +void DrawBrushStroke::create_triangles(const std::function& passTriangleFunc, const std::vector& points, unsigned skipVertexCount, std::shared_ptr bPath) { const int ARC_SMOOTHNESS = 10; const int CIRCLE_SMOOTHNESS = 20; std::vector& pointsN = d->points; + std::vector topPoints; + std::vector bottomPoints; if(pointsN.size() == 1 && d->hasRoundCaps) { std::vector circlePoints = gen_circle_points(pointsN.front().pos, pointsN.front().width / 2.0f, CIRCLE_SMOOTHNESS); - for(size_t i = 0; i < circlePoints.size(); i++) + for(size_t i = 0; i < circlePoints.size(); i++) { if(passTriangleFunc(pointsN.front().pos, circlePoints[i], circlePoints[(i + 1) % circlePoints.size()])) return; + topPoints.emplace_back(convert_vec2(circlePoints[i])); + } } - if(pointsN.size() < 2) + if(pointsN.size() < 2) { + if(bPath && !topPoints.empty()) + bPath->addPoly(topPoints.data(), topPoints.size(), false); return; + } std::vector wedgeIndices = get_wedge_indices(points); for(size_t i = 0; i < wedgeIndices.size() - 1; i++) { @@ -238,7 +229,9 @@ void DrawBrushStroke::create_triangles(const std::function(vertArray[0])); vertArray[1] = points[pointsBegin].pos - perpPrev * points[pointsBegin].width * 0.5f; + bottomPoints.emplace_back(convert_vec2(vertArray[1])); newIndex = 2; if(d->hasRoundCaps && pointsBegin == 0) { @@ -248,8 +241,11 @@ void DrawBrushStroke::create_triangles(const std::function arcPoints = arc_vec(points[0].pos, arcDirStart, arcDirEnd, arcDirCenter, points[0].width * 0.5f, ARC_SMOOTHNESS); arcPoints.emplace(arcPoints.begin(), convert_vec2(vertArray[0])); arcPoints.emplace_back(convert_vec2(vertArray[1])); - for(size_t i = 0; i < arcPoints.size() - 1; i++) + for(size_t i = 0; i < arcPoints.size() - 1; i++) { if(passTriangleFunc(points[0].pos, arcPoints[i], arcPoints[i + 1])) return; + bottomPoints.emplace_back(convert_vec2(arcPoints[i])); + } + bottomPoints.emplace_back(convert_vec2(arcPoints.back())); } for(size_t j = pointsBegin + 1; j < pointsEnd; j++) { @@ -272,10 +268,12 @@ void DrawBrushStroke::create_triangles(const std::function(vertArray[newIndex])); newIndex = (newIndex + 1) % 3; if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; vertArray[newIndex] = points[j].pos - perp * 0.5f * points[j].width; + bottomPoints.emplace_back(convert_vec2(vertArray[newIndex])); newIndex = (newIndex + 1) % 3; if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; @@ -288,10 +286,12 @@ void DrawBrushStroke::create_triangles(const std::function(vertArray[newIndex])); newIndex = (newIndex + 1) % 3; if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; vertArray[newIndex] = points[pointsEnd].pos - perp * points[pointsEnd].width * 0.5; + bottomPoints.emplace_back(convert_vec2(vertArray[newIndex])); newIndex = (newIndex + 1) % 3; if(passTriangleFunc(vertArray[0], vertArray[1], vertArray[2])) return; @@ -311,6 +311,7 @@ void DrawBrushStroke::create_triangles(const std::function(topPoints.back()); float arcRadius; @@ -330,8 +331,17 @@ void DrawBrushStroke::create_triangles(const std::function(arcPoints[i])); + else + bottomPoints.emplace_back(convert_vec2(arcPoints[i])); + } + if(isTopWedge) + topPoints.emplace_back(convert_vec2(arcPoints.back())); + else + bottomPoints.emplace_back(convert_vec2(arcPoints.back())); } else if(d->hasRoundCaps) { // It's the last point, cap it off Vector2f arcDirStart = (convert_vec2(vertArray[(newIndex + 1) % 3]) - points.back().pos).normalized(); @@ -340,10 +350,18 @@ void DrawBrushStroke::create_triangles(const std::function arcPoints = arc_vec(points.back().pos, arcDirStart, arcDirEnd, arcDirCenter, points.back().width * 0.5f, ARC_SMOOTHNESS); arcPoints.emplace(arcPoints.begin(), convert_vec2(vertArray[(newIndex + 1) % 3])); arcPoints.emplace_back(convert_vec2(vertArray[(newIndex + 2) % 3])); - for(size_t i = 0; i < arcPoints.size() - 1; i++) + for(size_t i = 0; i < arcPoints.size() - 1; i++) { if(passTriangleFunc(points.back().pos, arcPoints[i], arcPoints[i + 1])) return; + topPoints.emplace_back(convert_vec2(arcPoints[i])); + } + topPoints.emplace_back(convert_vec2(arcPoints.back())); } } + + if(bPath && !topPoints.empty()) { + topPoints.insert(topPoints.begin(), bottomPoints.rbegin(), bottomPoints.rend()); + bPath->addPoly(topPoints.data(), topPoints.size(), true); + } } void DrawBrushStroke::create_collider() { @@ -355,7 +373,7 @@ void DrawBrushStroke::create_collider() { create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { strokeObjects.triangle.emplace_back(a, b, c); return false; - }, points, 0); + }, points, 0, nullptr); strokeObjects.recalculate_bounds(); SCollision::BVHContainer collisionTree; @@ -376,13 +394,6 @@ void DrawBrushStroke::add_precheck_aabb_level(size_t level, const std::vector& vertexDataPtr, const std::vector& vertexData) { - if((d->points.size() == 1 && d->hasRoundCaps) || d->points.size() >= 2) - vertexDataPtr = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, vertexData.size(), vertexData.data(), nullptr, nullptr); - else - vertexDataPtr = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 0, nullptr, nullptr, nullptr); -} - void DrawBrushStroke::update(DrawingProgram& drawP) { } @@ -405,7 +416,7 @@ bool DrawBrushStroke::collides_within_coords(const SCollision::ColliderCollectio create_triangles([&](Vector2f a, Vector2f b, Vector2f c) { toRet = SCollision::collide(checkAgainst, SCollision::Triangle{a, b, c}); return toRet; - }, points, 0); + }, points, 0, nullptr); return toRet; } diff --git a/src/DrawComponents/DrawBrushStroke.hpp b/src/DrawComponents/DrawBrushStroke.hpp index 0cbcd42..8e50a2d 100644 --- a/src/DrawComponents/DrawBrushStroke.hpp +++ b/src/DrawComponents/DrawBrushStroke.hpp @@ -46,15 +46,13 @@ class DrawBrushStroke : public DrawComponent { virtual SCollision::AABB get_obj_coord_bounds() const override; - sk_sp vertices; - std::array, 3> verticesMipMap; + std::shared_ptr brushPath; SCollision::AABB bounds; private: void add_precheck_aabb_level(size_t level, const std::vector>& levelArray); std::vector> precheckAABBLevels; - void create_triangles(const std::function& passTriangleFunc, const std::vector& points, unsigned skipVertexCount); - void vertices_to_draw_data(sk_sp& vertexDataPtr, const std::vector& vertexData); + void create_triangles(const std::function& passTriangleFunc, const std::vector& points, unsigned skipVertexCount, std::shared_ptr bPath); std::vector get_wedge_indices(const std::vector& points); std::vector smooth_points(size_t beginIndex, size_t endIndex, unsigned numOfDivisions); std::vector smooth_points_avg(size_t beginIndex, size_t endIndex, unsigned numOfDivisions);