diff options
author | jaseg <git@jaseg.de> | 2021-01-29 23:20:16 +0100 |
---|---|---|
committer | jaseg <git@jaseg.de> | 2021-01-29 23:20:16 +0100 |
commit | 52dcceb87f5847dc235f5b5965f57881f327143c (patch) | |
tree | f4979228c2b1c85a9628ff892305c8fbdc4aaccb | |
parent | a34efc058a18021143b75e30efdfd8dd0e72df10 (diff) | |
download | gerbolyze-52dcceb87f5847dc235f5b5965f57881f327143c.tar.gz gerbolyze-52dcceb87f5847dc235f5b5965f57881f327143c.tar.bz2 gerbolyze-52dcceb87f5847dc235f5b5965f57881f327143c.zip |
Add support for preserveAspectRatio for both SVG and bitmap input
-rw-r--r-- | src/main.cpp | 17 | ||||
-rw-r--r-- | src/vec_core.cpp | 96 | ||||
-rw-r--r-- | src/vec_core.h | 1 |
3 files changed, 102 insertions, 12 deletions
diff --git a/src/main.cpp b/src/main.cpp index 931ab74..915b71f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -64,6 +64,9 @@ int main(int argc, char **argv) { {"size", {"-s", "--size"}, "Bitmap mode only: Physical size of output image in mm. Format: 12.34x56.78", 1}, + {"preserve_aspect_ratio", {"-a", "--preserve-aspect-ratio"}, + "Bitmap mode only: Preserve aspect ratio of image. Allowed values are meet, slice. Can also parse full SVG preserveAspectRatio syntax.", + 1}, {"skip_usvg", {"--no-usvg"}, "Do not preprocess input using usvg (do not use unless you know *exactly* what you're doing)", 0}, @@ -252,7 +255,19 @@ int main(int argc, char **argv) { << width << " " << height << "\" " << "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl; - svg << "<image width=\"" << width << "\" height=\"" << height << "\" x=\"0\" y=\"0\" xlink:href=\"data:image/png;base64,"; + string par_attr = "none"; + if (args["preserve_aspect_ratio"]) { + string aspect_ratio = args["preserve_aspect_ratio"].as<string>(); + if (aspect_ratio == "meet") { + par_attr = "xMidYMid meet"; + } else if (aspect_ratio == "slice") { + par_attr = "xMidYMid slice"; + } else { + par_attr = aspect_ratio; + } + } + svg << "<image width=\"" << width << "\" height=\"" << height << "\" x=\"0\" y=\"0\" preserveAspectRatio=\"" + << par_attr << "\" xlink:href=\"data:image/png;base64,"; /* c++ has the best hacks */ std::ostringstream sstr; diff --git a/src/vec_core.cpp b/src/vec_core.cpp index 81a2a96..9d2909f 100644 --- a/src/vec_core.cpp +++ b/src/vec_core.cpp @@ -21,6 +21,7 @@ #include <iostream> #include <algorithm> #include <vector> +#include <regex> #include <opencv2/opencv.hpp> #include "svg_import_util.h" #include "vec_core.h" @@ -36,7 +37,7 @@ ImageVectorizer *gerbolyze::makeVectorizer(const std::string &name) { else if (name == "hex-grid") return new VoronoiVectorizer(HEXGRID, /* relax */ false); else if (name == "square-grid") - return new VoronoiVectorizer(SQUAREGRID, /* relax */ true); + return new VoronoiVectorizer(SQUAREGRID, /* relax */ false); else if (name == "binary-contours") return new OpenCVContoursVectorizer(); else if (name == "dev-null") @@ -189,6 +190,15 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ apply_cairo_transform_from_svg(cr, node.attribute("transform").value()); cairo_translate(cr, x, y); + double orig_rows = img.rows; + double orig_cols = img.cols; + double scale_x = (double)width / orig_cols; + double scale_y = (double)height / orig_rows; + double off_x = 0; + double off_y = 0; + handle_aspect_ratio(node.attribute("preserveAspectRatio").value(), + scale_x, scale_y, off_x, off_y, orig_cols, orig_rows); + /* Adjust minimum feature size given in mm and translate into px document units in our local coordinate system. */ double f_x = min_feature_size_px, f_y = 0; cairo_device_to_user_distance(cr, &f_x, &f_y); @@ -202,7 +212,7 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ grayscale interpolation. Larger values -> better grayscale resolution, larger cells. */ double center_distance = min_feature_size_px * 2.0 * (1.0 / (1.0-grayscale_overhead)); - vector<d2p> *grid_centers = get_sampler(m_grid_type)(width, height, center_distance); + vector<d2p> *grid_centers = get_sampler(m_grid_type)(scale_x * orig_cols, scale_y*orig_rows, center_distance); //vector<d2p> *grid_centers = sample_poisson_disc(width, height, min_feature_size_px * 2.0 * 2.0); //vector<d2p> *grid_centers = sample_hexgrid(width, height, center_distance); //vector<d2p> *grid_centers = sample_squaregrid(width, height, center_distance); @@ -232,7 +242,9 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ /* Calculate voronoi diagram for the grid generated above. */ jcv_diagram diagram; memset(&diagram, 0, sizeof(jcv_diagram)); - jcv_rect rect {{0.0, 0.0}, {width, height}}; + cerr << "adjusted scale " << scale_x << " " << scale_y << endl; + cerr << "voronoi clip rect " << (scale_x * orig_cols) << " " << (scale_y * orig_rows) << endl; + jcv_rect rect {{0.0, 0.0}, {scale_x * orig_cols, scale_y * orig_rows}}; jcv_point *pts = reinterpret_cast<jcv_point *>(grid_centers->data()); /* hackety hack */ jcv_diagram_generate(grid_centers->size(), pts, &rect, 0, &diagram); /* Relax points, i.e. wiggle them around a little bit to equalize differences between cell sizes a little bit. */ @@ -255,8 +267,8 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ const jcv_point center = sites[i].p; double pxd = (double)blurred.at<unsigned char>( - (int)round(center.y / height * blurred.rows), - (int)round(center.x / width * blurred.cols)) / 255.0; + (int)round(center.y / (scale_y * orig_rows / blurred.rows)), + (int)round(center.x / (scale_x * orig_cols / blurred.cols))) / 255.0; /* 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. */ @@ -321,8 +333,8 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ * "step". */ double x = e->pos[0].x; double y = e->pos[0].y; - x = center.x + (x - center.x) * fill_factor; - y = center.y + (y - center.y) * fill_factor; + x = off_x + center.x + (x - center.x) * fill_factor; + y = off_y + center.y + (y - center.y) * fill_factor; cairo_user_to_device(cr, &x, &y); cell_path.push_back({ (ClipperLib::cInt)round(x * clipper_scale), (ClipperLib::cInt)round(y * clipper_scale) }); @@ -331,8 +343,8 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ /* Emit endpoint of current edge */ double x = e->pos[1].x; double y = e->pos[1].y; - x = center.x + (x - center.x) * fill_factor; - y = center.y + (y - center.y) * fill_factor; + x = off_x + center.x + (x - center.x) * fill_factor; + y = off_y + center.y + (y - center.y) * fill_factor; cairo_user_to_device(cr, &x, &y); cell_path.push_back({ (ClipperLib::cInt)round(x * clipper_scale), (ClipperLib::cInt)round(y * clipper_scale) }); @@ -379,6 +391,61 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_ cairo_restore(cr); } +void gerbolyze::handle_aspect_ratio(string spec, double &scale_x, double &scale_y, double &off_x, double &off_y, double cols, double rows) { + + if (spec.empty()) { + spec = "xMidYMid meet"; + } + + auto idx = spec.find(" "); + string par_align = spec; + string par_meet = "meet"; + if (idx != string::npos) { + par_align = spec.substr(0, idx); + par_meet = spec.substr(idx+1); + } + + if (par_align != "none") { + double scale = scale_x; + if (par_meet == "slice") { + scale = std::max(scale_x, scale_y); + } else { + scale = std::min(scale_x, scale_y); + } + + std::regex reg("x(Min|Mid|Max)Y(Min|Mid|Max)"); + std::smatch match; + + cerr << "data: " <<" "<< scale_x << "/" << scale_y << ": " << scale << endl; + off_x = (scale_x - scale) * cols; + off_y = (scale_y - scale) * rows; + cerr << rows <<","<<cols<<" " << off_x << "," << off_y << endl; + if (std::regex_match(par_align, match, reg)) { + assert (match.size() == 3); + if (match[1].str() == "Min") { + off_x = 0; + } else if (match[1].str() == "Mid") { + off_x *= 0.5; + } + + if (match[2].str() == "Min") { + off_y = 0; + } else if (match[2].str() == "Mid") { + off_y *= 0.5; + } + + } else { + cerr << "Invalid preserveAspectRatio meetOrSlice value \"" << par_align << "\"" << endl; + off_x *= 0.5; + off_y *= 0.5; + } + + scale_x = scale_y = scale; + } + cerr << "res: "<< off_x << "," << off_y << endl; +} + + void gerbolyze::OpenCVContoursVectorizer::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) { double x, y, width, height; parse_img_meta(node, x, y, width, height); @@ -391,6 +458,13 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(cairo_t *cr, const pug apply_cairo_transform_from_svg(cr, node.attribute("transform").value()); cairo_translate(cr, x, y); + double scale_x = (double)width / (double)img.cols; + double scale_y = (double)height / (double)img.rows; + double off_x = 0; + double off_y = 0; + handle_aspect_ratio(node.attribute("preserveAspectRatio").value(), + scale_x, scale_y, off_x, off_y, img.cols, img.rows); + draw_bg_rect(cr, width, height, clip_path, sink, viewport_matrix); vector<vector<cv::Point>> contours; @@ -415,8 +489,8 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(cairo_t *cr, const pug ClipperLib::Path out; for (const auto &p : contours[i]) { - double x = (double)p.x / (double)img.cols * (double)width; - double y = (double)p.y / (double)img.rows * (double)height; + double x = off_x + (double)p.x * scale_x; + double y = off_y + (double)p.y * scale_y; cairo_user_to_device(cr, &x, &y); out.push_back({ (ClipperLib::cInt)round(x * clipper_scale), (ClipperLib::cInt)round(y * clipper_scale) }); } diff --git a/src/vec_core.h b/src/vec_core.h index adaa241..8267fd1 100644 --- a/src/vec_core.h +++ b/src/vec_core.h @@ -53,5 +53,6 @@ namespace gerbolyze { 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); + void handle_aspect_ratio(std::string spec, double &scale_x, double &scale_y, double &off_x, double &off_y, double cols, double rows); } |