#include #include namespace psemek::vecr { void renderer::reset(math::vector const & size, std::size_t samples, gfx::color_rgba const & color) { samples_ = samples; canvas_.assign({size[0] * samples, size[1] * samples}, color); result_.assign({size[0], size[1]}, color); need_resolve_ = false; } void renderer::reset(gfx::pixmap_rgba image, std::size_t samples) { result_ = std::move(image); canvas_.resize({result_.width() * samples, result_.height() * samples}); for (auto idx : canvas_.indices()) canvas_(idx) = result_({idx[0] / samples, idx[1] / samples}); need_resolve_ = false; } math::vector renderer::size() const { return {result_.width(), result_.height()}; } std::size_t renderer::samples() const { return samples_; } gfx::pixmap_rgba const & renderer::result() const { resolve(); return result_; } gfx::pixmap_rgba renderer::release() { resolve(); canvas_.clear(); return std::move(result_); } gfx::pixmap_rgba const & renderer::canvas() const { return canvas_; } gfx::pixmap_rgba renderer::release_canvas() { result_.clear(); need_resolve_ = false; return std::move(canvas_); } void renderer::clear(gfx::color_rgba const & color) { canvas_.fill(color); result_.fill(color); need_resolve_ = false; } void renderer::set_pixel(math::vector const & coords, gfx::color_rgba const & color) { for (std::size_t ty = 0; ty < samples_; ++ty) { for (std::size_t tx = 0; tx < samples_; ++tx) { canvas_(coords[0] * samples_ + tx, coords[1] * samples_ + ty) = color; } } } void renderer::draw(primitive const & primitive) { float const aa = primitive.blur / 2.f; auto const box = math::expand(bbox(primitive.mask), aa); int xmin = std::floor(std::max(0.f, box[0].min * samples_)); int xmax = std::floor(std::min(canvas_.width() - 1.f, box[0].max * samples_)); int ymin = std::floor(std::max(0.f, box[1].min * samples_)); int ymax = std::floor(std::min(canvas_.height() - 1.f, box[1].max * samples_)); for (int y = ymin; y <= ymax; ++y) { for (int x = xmin; x <= xmax; ++x) { math::point const center{(x + 0.5f) / samples_, (y + 0.5f) / samples_}; auto const filter_sample = sdf(primitive.filter, center); if (filter_sample.value > aa) continue; auto const sample = sdf(primitive.mask, center); if (sample.value > aa) continue; 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; color[3] *= primitive.alpha; canvas_(x, y) = gfx::to_coloru8(primitive.blend(gfx::to_colorf(canvas_(x, y)), color)); } } need_resolve_ = true; } void renderer::resolve() const { if (!need_resolve_) return; for (auto const & idx : result_.indices()) { gfx::color_4f sum{0.f, 0.f, 0.f, 0.f}; for (std::size_t y = 0; y < samples_; ++y) { for (std::size_t x = 0; x < samples_; ++x) { sum += gfx::premult(gfx::to_colorf(canvas_({idx[0] * samples_ + x, idx[1] * samples_ + y}))); } } result_(idx) = gfx::to_coloru8(gfx::unpremult(sum / (1.f * samples_ * samples_))); } need_resolve_ = false; } }