From 52dcceb87f5847dc235f5b5965f57881f327143c Mon Sep 17 00:00:00 2001 From: jaseg Date: Fri, 29 Jan 2021 23:20:16 +0100 Subject: Add support for preserveAspectRatio for both SVG and bitmap input --- src/main.cpp | 17 +++++++++- src/vec_core.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++++++------- 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 << "(); + if (aspect_ratio == "meet") { + par_attr = "xMidYMid meet"; + } else if (aspect_ratio == "slice") { + par_attr = "xMidYMid slice"; + } else { + par_attr = aspect_ratio; + } + } + svg << " #include #include +#include #include #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 *grid_centers = get_sampler(m_grid_type)(width, height, center_distance); + vector *grid_centers = get_sampler(m_grid_type)(scale_x * orig_cols, scale_y*orig_rows, center_distance); //vector *grid_centers = sample_poisson_disc(width, height, min_feature_size_px * 2.0 * 2.0); //vector *grid_centers = sample_hexgrid(width, height, center_distance); //vector *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(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( - (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 <<","<> 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); } -- cgit