diff --git a/libs/vecr/include/psemek/vecr/add.hpp b/libs/vecr/include/psemek/vecr/add.hpp index 86bdc8cd..bc0e7ee8 100644 --- a/libs/vecr/include/psemek/vecr/add.hpp +++ b/libs/vecr/include/psemek/vecr/add.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -14,4 +15,6 @@ namespace psemek::vecr sdf_sample sdf(add const & s, geom::point const & p); + geom::box bbox(add const & s); + } diff --git a/libs/vecr/include/psemek/vecr/any.hpp b/libs/vecr/include/psemek/vecr/any.hpp index 9d7a8d46..0adf9473 100644 --- a/libs/vecr/include/psemek/vecr/any.hpp +++ b/libs/vecr/include/psemek/vecr/any.hpp @@ -2,12 +2,52 @@ #include #include +#include #include +#include namespace psemek::vecr { + namespace detail + { + + struct any_base + { + virtual sdf_sample sdf_func(geom::point const & p) const = 0; + virtual geom::box bbox_func() const = 0; + + virtual ~any_base() {} + }; + + template + struct any_impl + : any_base + { + any_impl(Shape && shape) + : shape(std::move(shape)) + {} + + any_impl(Shape const & shape) + : shape(shape) + {} + + Shape shape; + + sdf_sample sdf_func(geom::point const & p) const override + { + return sdf(shape, p); + } + + geom::box bbox_func() const override + { + return bbox(shape); + } + }; + + } + struct any { any() = default; @@ -26,26 +66,30 @@ namespace psemek::vecr template any & operator = (Shape && shape) { - sdf_ = [shape = std::forward(shape)](geom::point const & p){ - return sdf(shape, p); - }; + impl_ = std::make_shared>>(std::move(shape)); return *this; } explicit operator bool() const { - return static_cast(sdf_); + return static_cast(impl_); } friend sdf_sample sdf(any const & s, geom::point const & p); + friend geom::box bbox(any const & s); private: - std::function const &)> sdf_; + std::shared_ptr impl_; }; inline sdf_sample sdf(any const & s, geom::point const & p) { - return s.sdf_ ? s.sdf_(p) : sdf_sample{}; + return s.impl_ ? s.impl_->sdf_func(p) : sdf_sample{}; + } + + inline geom::box bbox(any const & s) + { + return s.impl_ ? s.impl_->bbox_func() : geom::box{}; } } diff --git a/libs/vecr/include/psemek/vecr/border.hpp b/libs/vecr/include/psemek/vecr/border.hpp index 10d6ed4e..9ae71110 100644 --- a/libs/vecr/include/psemek/vecr/border.hpp +++ b/libs/vecr/include/psemek/vecr/border.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -24,4 +25,10 @@ namespace psemek::vecr return result; } + template + geom::box bbox(border const & s) + { + return bbox(s.shape); + } + } diff --git a/libs/vecr/include/psemek/vecr/circle.hpp b/libs/vecr/include/psemek/vecr/circle.hpp index a3578089..f3c750a7 100644 --- a/libs/vecr/include/psemek/vecr/circle.hpp +++ b/libs/vecr/include/psemek/vecr/circle.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -14,4 +15,6 @@ namespace psemek::vecr sdf_sample sdf(circle const & s, geom::point const & p); + geom::box bbox(circle const & s); + } diff --git a/libs/vecr/include/psemek/vecr/exact.hpp b/libs/vecr/include/psemek/vecr/exact.hpp index b525431a..bb5c0bad 100644 --- a/libs/vecr/include/psemek/vecr/exact.hpp +++ b/libs/vecr/include/psemek/vecr/exact.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -55,4 +56,10 @@ namespace psemek::vecr return result; } + template + geom::box bbox(exact const & s) + { + return bbox(s.shape); + } + } diff --git a/libs/vecr/include/psemek/vecr/fill.hpp b/libs/vecr/include/psemek/vecr/fill.hpp index 4862b59e..87809ba9 100644 --- a/libs/vecr/include/psemek/vecr/fill.hpp +++ b/libs/vecr/include/psemek/vecr/fill.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace psemek::vecr { @@ -12,4 +13,6 @@ namespace psemek::vecr sdf_sample sdf(fill const & s, geom::point const & p); + geom::box bbox(fill const & s); + } diff --git a/libs/vecr/include/psemek/vecr/grow.hpp b/libs/vecr/include/psemek/vecr/grow.hpp index 5dd9f0eb..c2a139a4 100644 --- a/libs/vecr/include/psemek/vecr/grow.hpp +++ b/libs/vecr/include/psemek/vecr/grow.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -21,4 +22,10 @@ namespace psemek::vecr return result; } + template + geom::box bbox(grow const & s) + { + return geom::expand(bbox(s.shape), s.distance); + } + } diff --git a/libs/vecr/include/psemek/vecr/halfspace.hpp b/libs/vecr/include/psemek/vecr/halfspace.hpp index b7a6a3e1..1a1fb5b2 100644 --- a/libs/vecr/include/psemek/vecr/halfspace.hpp +++ b/libs/vecr/include/psemek/vecr/halfspace.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -17,4 +18,10 @@ namespace psemek::vecr return {geom::dot(p - s.origin, s.normal), s.normal}; } + inline geom::box bbox(halfspace const &) + { + return geom::box::full(); + } + + } diff --git a/libs/vecr/include/psemek/vecr/intersect.hpp b/libs/vecr/include/psemek/vecr/intersect.hpp index 3ab40888..445e3635 100644 --- a/libs/vecr/include/psemek/vecr/intersect.hpp +++ b/libs/vecr/include/psemek/vecr/intersect.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -18,4 +19,6 @@ namespace psemek::vecr sdf_sample sdf(intersect const & s, geom::point const & p); + geom::box bbox(intersect const & s); + } diff --git a/libs/vecr/include/psemek/vecr/invert.hpp b/libs/vecr/include/psemek/vecr/invert.hpp index e2998f39..ec7b773c 100644 --- a/libs/vecr/include/psemek/vecr/invert.hpp +++ b/libs/vecr/include/psemek/vecr/invert.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -21,4 +22,10 @@ namespace psemek::vecr return result; } + template + geom::box bbox(invert const &) + { + return geom::box::full(); + } + } diff --git a/libs/vecr/include/psemek/vecr/mirror.hpp b/libs/vecr/include/psemek/vecr/mirror.hpp index 41f8cac0..57f114ac 100644 --- a/libs/vecr/include/psemek/vecr/mirror.hpp +++ b/libs/vecr/include/psemek/vecr/mirror.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace psemek::vecr { @@ -34,4 +35,24 @@ namespace psemek::vecr return result; } + template + geom::box bbox(mirror const & s) + { + geom::box result; + + auto sbox = bbox(s.shape); + + for (int x = 0; x <= 1; ++x) + { + for (int y = 0; y <= 1; ++y) + { + auto p = sbox.corner(x, y); + p = p - (2.f * geom::dot(s.axis, p - s.origin)) * s.axis; + result |= p; + } + } + + return result; + } + } diff --git a/libs/vecr/include/psemek/vecr/path.hpp b/libs/vecr/include/psemek/vecr/path.hpp index 1b596dda..eb09370e 100644 --- a/libs/vecr/include/psemek/vecr/path.hpp +++ b/libs/vecr/include/psemek/vecr/path.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -15,4 +16,6 @@ namespace psemek::vecr sdf_sample sdf(path const & s, geom::point const & p, bool closed = false); + geom::box bbox(path const & s); + } diff --git a/libs/vecr/include/psemek/vecr/renderer.hpp b/libs/vecr/include/psemek/vecr/renderer.hpp index cfb3fd31..7b865211 100644 --- a/libs/vecr/include/psemek/vecr/renderer.hpp +++ b/libs/vecr/include/psemek/vecr/renderer.hpp @@ -24,6 +24,7 @@ namespace psemek::vecr geom::vector size() const; std::size_t samples() const; gfx::pixmap_rgba const & result() const; + gfx::pixmap_rgba release(); void clear(gfx::color_rgba const & color = {0, 0, 0, 0}); diff --git a/libs/vecr/include/psemek/vecr/subtract.hpp b/libs/vecr/include/psemek/vecr/subtract.hpp index 28b6f937..c3833b5d 100644 --- a/libs/vecr/include/psemek/vecr/subtract.hpp +++ b/libs/vecr/include/psemek/vecr/subtract.hpp @@ -20,4 +20,10 @@ namespace psemek::vecr return sdf(intersect{{s.shape1, invert{s.shape2}}, s.smooth}, p); } + template + geom::box bbox(subtract const & s) + { + return bbox(s.shape1); + } + } diff --git a/libs/vecr/include/psemek/vecr/tile.hpp b/libs/vecr/include/psemek/vecr/tile.hpp index 47bfa058..4c089814 100644 --- a/libs/vecr/include/psemek/vecr/tile.hpp +++ b/libs/vecr/include/psemek/vecr/tile.hpp @@ -25,4 +25,10 @@ namespace psemek::vecr return sdf(s.shape, q); } + template + geom::box bbox(tile const &) + { + return geom::box::full(); + } + } diff --git a/libs/vecr/include/psemek/vecr/transform.hpp b/libs/vecr/include/psemek/vecr/transform.hpp index 09cf5536..aa6f322d 100644 --- a/libs/vecr/include/psemek/vecr/transform.hpp +++ b/libs/vecr/include/psemek/vecr/transform.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace psemek::vecr { @@ -45,6 +46,12 @@ namespace psemek::vecr return sdf(s.shape, p - s.delta); } + template + geom::box bbox(translate const & s) + { + return bbox(s.shape) + s.delta; + } + template sdf_sample sdf(rotate const & s, geom::point const & p) { @@ -53,6 +60,20 @@ namespace psemek::vecr return result; } + template + geom::box bbox(rotate const & s) + { + auto sbox = bbox(s.shape); + + geom::box result; + + for (int y = 0; y <= 1; ++y) + for (int x = 0; x <= 1; ++x) + result |= s.origin + geom::rotate(sbox.corner(x, y) - s.origin, s.angle); + + return result; + } + template sdf_sample sdf(scale const & s, geom::point const & p) { @@ -65,6 +86,20 @@ namespace psemek::vecr return result; } + template + geom::box bbox(scale const & s) + { + auto sbox = bbox(s.shape); + + geom::box result; + + for (int y = 0; y <= 1; ++y) + for (int x = 0; x <= 1; ++x) + result |= s.origin + (sbox.corner(x, y) - s.origin) * s.factor; + + return result; + } + template sdf_sample sdf(transform const & s, geom::point const & p) { @@ -89,5 +124,18 @@ namespace psemek::vecr return result; } + template + geom::box bbox(transform const & s) + { + auto sbox = bbox(s.shape); + + geom::box result; + + for (int y = 0; y <= 1; ++y) + for (int x = 0; x <= 1; ++x) + result |= geom::as_point(s.matrix * geom::homogeneous(sbox.corner(x, y))); + + return result; + } } diff --git a/libs/vecr/source/add.cpp b/libs/vecr/source/add.cpp index 23a508c5..c38db064 100644 --- a/libs/vecr/source/add.cpp +++ b/libs/vecr/source/add.cpp @@ -38,4 +38,12 @@ namespace psemek::vecr return result; } + geom::box bbox(add const & s) + { + geom::box result; + for (auto const & ss : s.shapes) + result |= bbox(ss); + return result; + } + } diff --git a/libs/vecr/source/circle.cpp b/libs/vecr/source/circle.cpp index 7527c18d..82b7f197 100644 --- a/libs/vecr/source/circle.cpp +++ b/libs/vecr/source/circle.cpp @@ -12,4 +12,9 @@ namespace psemek::vecr return {l - s.radius, {0.f, 0.f}}; } + geom::box bbox(circle const & s) + { + return geom::expand(geom::box::singleton(s.center), s.radius); + } + } diff --git a/libs/vecr/source/fill.cpp b/libs/vecr/source/fill.cpp index b099d64c..f9049fe8 100644 --- a/libs/vecr/source/fill.cpp +++ b/libs/vecr/source/fill.cpp @@ -29,4 +29,9 @@ namespace psemek::vecr return result; } + geom::box bbox(fill const & s) + { + return bbox(s.border); + } + } diff --git a/libs/vecr/source/intersect.cpp b/libs/vecr/source/intersect.cpp index 63ae27a6..685d5698 100644 --- a/libs/vecr/source/intersect.cpp +++ b/libs/vecr/source/intersect.cpp @@ -39,4 +39,12 @@ namespace psemek::vecr return result; } + geom::box bbox(intersect const & s) + { + auto result = geom::box::full(); + for (auto const & ss : s.shapes) + result &= bbox(ss); + return result; + } + } diff --git a/libs/vecr/source/path.cpp b/libs/vecr/source/path.cpp index d0e23baa..dd558eed 100644 --- a/libs/vecr/source/path.cpp +++ b/libs/vecr/source/path.cpp @@ -27,4 +27,14 @@ namespace psemek::vecr return result; } + geom::box bbox(path const & s) + { + geom::box result; + + for (auto const & p : s.points) + result |= p; + + return result; + } + } diff --git a/libs/vecr/source/renderer.cpp b/libs/vecr/source/renderer.cpp index 20acb9f7..6311625f 100644 --- a/libs/vecr/source/renderer.cpp +++ b/libs/vecr/source/renderer.cpp @@ -28,6 +28,13 @@ namespace psemek::vecr return result_; } + gfx::pixmap_rgba renderer::release() + { + resolve(); + canvas_.clear(); + return std::move(result_); + } + void renderer::clear(gfx::color_rgba const & color) { canvas_.fill(color); @@ -39,22 +46,32 @@ namespace psemek::vecr { float const aa = primitive.blur / 2.f; - for (auto const & idx : canvas_.indices()) + auto const box = geom::expand(bbox(primitive.mask), aa); + + int xmin = std::max(0, std::floor(box[0].min) * samples_); + int xmax = std::min(canvas_.width() - 1, std::ceil(box[0].max) * samples_); + int ymin = std::max(0, std::floor(box[1].min) * samples_); + int ymax = std::min(canvas_.height() - 1, std::ceil(box[1].max) * samples_); + + for (int y = ymin; y < ymax; ++y) { - geom::point const center{(idx[0] + 0.5f) / samples_, (idx[1] + 0.5f) / samples_}; + for (int x = xmin; x < xmax; ++x) + { + geom::point const center{(x + 0.5f) / samples_, (y + 0.5f) / samples_}; - auto const sample = sdf(primitive.mask, center); + auto const sample = sdf(primitive.mask, center); - if (sample.value > aa) continue; + if (sample.value > aa) continue; - float blur = 1.f; - if (sample.value > - aa) - blur = (aa - sample.value) / (2.f * aa); + float blur = 1.f; + if (sample.value > - aa) + blur = (aa - sample.value) / (2.f * aa); - auto color = colorize(primitive.colorizer, center, sample); - color[3] *= blur; + auto color = colorize(primitive.colorizer, center, sample); + color[3] *= blur; - canvas_(idx) = gfx::to_coloru8(primitive.blend(gfx::to_colorf(canvas_(idx)), color)); + canvas_(x, y) = gfx::to_coloru8(primitive.blend(gfx::to_colorf(canvas_(x, y)), color)); + } } need_resolve_ = true;