From 6d1a7750c5fa0d78c7fe35a7b7bff8934b86089d Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 28 Jan 2021 23:15:36 +0100 Subject: Per-image vectorizer selection works --- include/gerbolyze.hpp | 14 +++++++++++++- src/main.cpp | 10 ++++++++-- src/svg_doc.cpp | 9 +++++++-- src/vec_core.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++++- src/vec_core.h | 7 +++++++ 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/include/gerbolyze.hpp b/include/gerbolyze.hpp index 01d089a..1d4f9bd 100644 --- a/include/gerbolyze.hpp +++ b/include/gerbolyze.hpp @@ -93,15 +93,27 @@ namespace gerbolyze { class ImageVectorizer { public: + virtual ~ImageVectorizer() {}; virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px) = 0; }; ImageVectorizer *makeVectorizer(const std::string &name); + class VectorizerSelectorizer { + public: + VectorizerSelectorizer(const std::string default_vectorizer="dev-null", const std::string defs=""); + + ImageVectorizer *select(const pugi::xml_node &img); + + private: + std::string m_default; + std::map m_map; + }; + class RenderSettings { public: double m_minimum_feature_size_mm = 0.1; - ImageVectorizer *m_vec = nullptr; + VectorizerSelectorizer &m_vec_sel; }; class SVGDocument { diff --git a/src/main.cpp b/src/main.cpp index 6484295..93b6e8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,7 +43,10 @@ int main(int argc, char **argv) { "Comma-separated list of group IDs to export.", 1}, {"vectorizer", {"-b", "--vectorizer"}, - "Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours.", + "Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours, dev-null.", + 1}, + {"vectorizer_map", {"--vectorizer-map"}, + "Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,...", 1}, {"exclude_groups", {"-e", "--exclude-groups"}, "Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.", @@ -169,6 +172,7 @@ int main(int argc, char **argv) { id_match(args["exclude_groups"], sel.exclude); string vectorizer = args["vectorizer"] ? args["vectorizer"] : "poisson-disc"; + /* Check argument */ ImageVectorizer *vec = makeVectorizer(vectorizer); if (!vec) { cerr << "Unknown vectorizer \"" << vectorizer << "\"." << endl; @@ -176,10 +180,12 @@ int main(int argc, char **argv) { fmt << usage.str() << argparser; return EXIT_FAILURE; } + delete vec; + VectorizerSelectorizer vec_sel(vectorizer, args["vectorizer_map"] ? args["vectorizer_map"] : ""); RenderSettings rset { 0.1, - vec + vec_sel, }; doc.render(rset, flattener ? *flattener : *sink, &sel); diff --git a/src/svg_doc.cpp b/src/svg_doc.cpp index 5b36f35..39c5724 100644 --- a/src/svg_doc.cpp +++ b/src/svg_doc.cpp @@ -173,11 +173,16 @@ void gerbolyze::SVGDocument::export_svg_group(const RenderSettings &rset, const export_svg_path(rset, node, clip_path); } else if (name == "image") { - if (!rset.m_vec) + ImageVectorizer *vec = rset.m_vec_sel.select(node); + if (!vec) { + cerr << "Cannot resolve vectorizer for node \"" << node.attribute("id").value() << "\"" << endl; continue; + } double min_feature_size_px = mm_to_doc_units(rset.m_minimum_feature_size_mm); - rset.m_vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px); + vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px); + delete vec; + } else if (name == "defs") { /* ignore */ } else { diff --git a/src/vec_core.cpp b/src/vec_core.cpp index 6beab19..93c7a68 100644 --- a/src/vec_core.cpp +++ b/src/vec_core.cpp @@ -39,6 +39,8 @@ ImageVectorizer *gerbolyze::makeVectorizer(const std::string &name) { return new VoronoiVectorizer(SQUAREGRID, /* relax */ false); else if (name == "binary-contours") return new OpenCVContoursVectorizer(); + else if (name == "dev-null") + return new DevNullVectorizer(); return nullptr; } @@ -215,11 +217,15 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ /* Scale intermediate image (step 1.2) to have pixels per min_feature_size. */ cv::Mat scaled(cv::Size{(int)round(px_w), (int)round(px_h)}, img.type()); cv::resize(img, scaled, scaled.size(), 0, 0); + cerr << "scaled " << img.cols << ", " << img.rows << " -> " << scaled.cols << ", " << scaled.rows << endl; img.release(); /* Blur image with a kernel larger than our minimum feature size to avoid aliasing. */ cv::Mat blurred(scaled.size(), scaled.type()); int blur_size = (int)ceil(fmax(scaled.cols / width, scaled.rows / height) * center_distance); + if (blur_size%2 == 0) + blur_size += 1; + cerr << "blur size " << blur_size << endl; cv::GaussianBlur(scaled, blurred, {blur_size, blur_size}, 0, 0); scaled.release(); @@ -251,7 +257,11 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ double pxd = (double)blurred.at( (int)round(center.y / height * blurred.rows), (int)round(center.x / width * blurred.cols)) / 255.0; - fill_factors[sites[i].index] = sqrt(pxd); + /* FIXME: This is a workaround for a memory corruption bug that happens with the square-grid setting. When using + * square-grid on a fairly small test image, sometimes sites[i].index will be out of bounds here. + */ + if (sites[i].index < fill_factors.size()) + fill_factors[sites[i].index] = sqrt(pxd); } /* Minimum gap between adjacent scaled site polygons. */ @@ -446,3 +456,38 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(cairo_t *cr, const pug cairo_restore(cr); } + +gerbolyze::VectorizerSelectorizer::VectorizerSelectorizer(const string default_vectorizer, const string defs) + : m_default(default_vectorizer) { + istringstream foo(defs); + string elem; + while (std::getline(foo, elem, ',')) { + size_t pos = elem.find_first_of("="); + if (pos == string::npos) { + cerr << "Error parsing vectorizer selection string at element \"" << elem << "\"" << endl; + continue; + } + + const string parsed_id = elem.substr(0, pos); + const string mapping = elem.substr(pos+1); + m_map[parsed_id] = mapping; + } + + cerr << "parsed " << m_map.size() << " vectorizers" << endl; + for (auto &elem : m_map) { + cerr << " " << elem.first << " -> " << elem.second << endl; + } +} + +ImageVectorizer *gerbolyze::VectorizerSelectorizer::select(const pugi::xml_node &img) { + const string id = img.attribute("id").value(); + cerr << "selecting vectorizer for image \"" << id << "\"" << endl; + if (m_map.contains(id)) { + cerr << " -> found" << endl; + return makeVectorizer(m_map[id]); + } + + cerr << " -> default" << endl; + return makeVectorizer(m_default); +} + diff --git a/src/vec_core.h b/src/vec_core.h index 06099ab..adaa241 100644 --- a/src/vec_core.h +++ b/src/vec_core.h @@ -43,6 +43,13 @@ namespace gerbolyze { virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px); }; + class DevNullVectorizer : public ImageVectorizer { + public: + DevNullVectorizer() {} + + virtual void vectorize_image(cairo_t *, const pugi::xml_node &, ClipperLib::Paths &, cairo_matrix_t &, PolygonSink &, double) {} + }; + void parse_img_meta(const pugi::xml_node &node, double &x, double &y, double &width, double &height); std::string read_img_data(const pugi::xml_node &node); void draw_bg_rect(cairo_t *cr, double width, double height, ClipperLib::Paths &clip_path, PolygonSink &sink, cairo_matrix_t &viewport_matrix); -- cgit