From 743cffbd327478b25a20eaf75741fdb3039fc8c2 Mon Sep 17 00:00:00 2001 From: Michal Maciola <71131832+mmaciola@users.noreply.github.com> Date: Wed, 18 Aug 2021 13:29:10 +0200 Subject: [PATCH] svg2png: enhance the feature. Svg2png fully redesigned. Introduced whole directory parsing. Created flags interface. Usage: svg2png [svgFileName] [-r resolution] [-b bgColor] Flags: -r set output image resolution. -b set output image background color. Examples: $ svg2png input.svg $ svg2png input.svg -r 200x200 $ svg2png input.svg -r 200x200 -b ff00ff $ svg2png input1.svg input2.svg -r 200x200 -b ff00ff --- src/bin/svg2png/svg2png.cpp | 367 ++++++++++++++++++++++++++++++-------------- 1 file changed, 254 insertions(+), 113 deletions(-) diff --git a/src/bin/svg2png/svg2png.cpp b/src/bin/svg2png/svg2png.cpp index 3e0f56e..c56e39e 100644 --- a/src/bin/svg2png/svg2png.cpp +++ b/src/bin/svg2png/svg2png.cpp @@ -25,180 +25,321 @@ #include #include #include "lodepng.h" +#include +#include +#include using namespace std; - struct PngBuilder { - void build(const std::string &fileName , const uint32_t width, const uint32_t height, uint32_t *buffer) + void build(const string& fileName, const uint32_t width, const uint32_t height, uint32_t* buffer) { - std::vector image; + //Used ARGB8888 so have to move pixels now + vector image; image.resize(width * height * 4); for (unsigned y = 0; y < height; y++) { for (unsigned x = 0; x < width; x++) { - uint32_t n = buffer[ y * width + x ]; + uint32_t n = buffer[y * width + x]; image[4 * width * y + 4 * x + 0] = (n >> 16) & 0xff; image[4 * width * y + 4 * x + 1] = (n >> 8) & 0xff; image[4 * width * y + 4 * x + 2] = n & 0xff; image[4 * width * y + 4 * x + 3] = (n >> 24) & 0xff; } } + unsigned error = lodepng::encode(fileName, image, width, height); //if there's an error, display it - if (error) std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl; + if (error) cout << "encoder error " << error << ": " << lodepng_error_text(error) << endl; } }; - -struct App +struct Renderer { - void tvgRender(int w, int h) +public: + int render(const char* path, int w, int h, const string& dst, uint32_t bgColor) { - tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw; - - //Threads Count - auto threads = std::thread::hardware_concurrency(); - - //Initialize ThorVG Engine - if (tvg::Initializer::init(tvgEngine, threads) == tvg::Result::Success) { - //Create a Canvas - auto canvas = tvg::SwCanvas::gen(); + //Canvas + if (!canvas) createCanvas(); + if (!canvas) { + cout << "Error: Canvas failure" << endl; + return 1; + } - //Create a Picture - auto picture = tvg::Picture::gen(); - if (picture->load(fileName) != tvg::Result::Success) return; + //Picture + auto picture = tvg::Picture::gen(); + if (picture->load(path) != tvg::Result::Success) { + cout << "Error: Couldn't load image " << path << endl; + return 1; + } + if (w == 0 || h == 0) { float fw, fh; picture->size(&fw, &fh); + w = static_cast(fw); + h = static_cast(fh); + } else { + picture->size(w, h); + } - //Proper size is not specified, Get default size. - if (w == 0 || h == 0) { - width = static_cast(fw); - height = static_cast(fh); - //Otherwise, transform size to keep aspect ratio - } else { - width = w; - height = h; - picture->size(w, h); - } + //Buffer + createBuffer(w, h); + if (!buffer) { + cout << "Error: Buffer failure" << endl; + return 1; + } - //Setup the canvas - auto buffer = (uint32_t*)malloc(sizeof(uint32_t) * width * height); - canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888); + if (canvas->target(buffer, w, w, h, tvg::SwCanvas::ARGB8888) != tvg::Result::Success) { + cout << "Error: Canvas target failure" << endl; + return 1; + } - //Background color? - if (bgColor != 0xffffffff) { - uint8_t bgColorR = (uint8_t) ((bgColor & 0xff0000) >> 16); - uint8_t bgColorG = (uint8_t) ((bgColor & 0x00ff00) >> 8); - uint8_t bgColorB = (uint8_t) ((bgColor & 0x0000ff)); + //Background color if needed + if (bgColor != 0xffffffff) { + uint8_t r = (uint8_t)((bgColor & 0xff0000) >> 16); + uint8_t g = (uint8_t)((bgColor & 0x00ff00) >> 8); + uint8_t b = (uint8_t)((bgColor & 0x0000ff)); - auto shape = tvg::Shape::gen(); - shape->appendRect(0, 0, width, height, 0, 0); - shape->fill(bgColorR, bgColorG, bgColorB, 255); + auto shape = tvg::Shape::gen(); + shape->appendRect(0, 0, w, h, 0, 0); + shape->fill(r, g, b, 255); - if (canvas->push(move(shape)) != tvg::Result::Success) return; - } + if (canvas->push(move(shape)) != tvg::Result::Success) return 1; + } - //Drawing - canvas->push(move(picture)); - canvas->draw(); - canvas->sync(); + //Drawing + canvas->push(move(picture)); + canvas->draw(); + canvas->sync(); - //Build Png - PngBuilder builder; - builder.build(pngName.data(), width, height, buffer); + //Build Png + PngBuilder builder; + builder.build(dst, w, h, buffer); - //Terminate ThorVG Engine - tvg::Initializer::term(tvg::CanvasEngine::Sw); + cout << "Generated PNG file: " << dst << endl; - free(buffer); + canvas->clear(true); - } else { - cout << "engine is not supported" << endl; - } + return 0; } - int setup(int argc, char **argv, size_t *w, size_t *h) + void terminate() { - char *path{nullptr}; - - *w = *h = 0; - - if (argc > 1) path = argv[1]; - if (argc > 2) { - char tmp[20]; - char *x = strstr(argv[2], "x"); - if (x) { - snprintf(tmp, x - argv[2] + 1, "%s", argv[2]); - *w = atoi(tmp); - snprintf(tmp, sizeof(tmp), "%s", x + 1); - *h = atoi(tmp); - } - } - if (argc > 3) bgColor = strtol(argv[3], NULL, 16); + //Terminate ThorVG Engine + tvg::Initializer::term(tvg::CanvasEngine::Sw); + free(buffer); + } - if (!path) return help(); +private: + void createCanvas() + { + //Canvas Engine + tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw; - std::array memory; + //Threads Count + auto threads = thread::hardware_concurrency(); -#ifdef _WIN32 - path = _fullpath(memory.data(), path, memory.size()); -#else - path = realpath(path, memory.data()); -#endif - if (!path) return help(); + //Initialize ThorVG Engine + if (tvg::Initializer::init(tvgEngine, threads) != tvg::Result::Success) { + cout << "Error: Engine is not supported" << endl; + } - fileName = std::string(path); + //Create a Canvas + canvas = tvg::SwCanvas::gen(); + } - if (!svgFile()) return help(); + void createBuffer(int w, int h) + { + uint32_t size = w * h; + //Reuse old buffer if size is enough + if (buffer && bufferSize >= size) return; - pngName = basename(fileName); - pngName.append(".png"); - return 0; + //Alloc or realloc buffer + buffer = (uint32_t*) realloc(buffer, sizeof(uint32_t) * size); + bufferSize = size; } private: - std::string basename(const std::string &str) - { - return str.substr(str.find_last_of("/\\") + 1); - } + unique_ptr canvas = nullptr; + uint32_t* buffer = nullptr; + uint32_t bufferSize = 0; +}; - bool svgFile() { - std::string extn = ".svg"; - if (fileName.size() <= extn.size() || fileName.substr(fileName.size() - extn.size()) != extn) - return false; +struct App +{ +public: + int setup(int argc, char** argv) + { + vector paths; + + for (int i = 1; i < argc; i++) { + const char* p = argv[i]; + if (*p == '-') { + //flags + const char* p_arg = (i + 1 < argc) ? argv[i] : nullptr; + if (p[1] == 'r') { + //image resolution + if (!p_arg) { + cout << "Error: Missing resolution attribute. Expected eg. -r 200x200." << endl; + return 1; + } + + const char* x = strchr(p_arg, 'x'); + if (x) { + width = atoi(p_arg); + height = atoi(x + 1); + } + if (!x || width <= 0 || height <= 0) { + cout << "Error: Resolution (" << p_arg << ") is corrupted. Expected eg. -r 200x200." << endl; + return 1; + } + + } else if (p[1] == 'b') { + //image background color + if (!p_arg) { + cout << "Error: Missing background color attribute. Expected eg. -b #fa7410." << endl; + return 1; + } + + if (*p_arg == '#') ++p_arg; + bgColor = (uint32_t) strtol(p, NULL, 16); + + } else { + cout << "Warning: Unknown flag (" << p << ")." << endl; + } + } else { + //arguments + paths.push_back(p); + } + } - return true; - } + int ret = 0; + if (paths.empty()) { + //no attributes - print help + return help(); + + } else { + for (auto path : paths) { + auto real_path = realFile(path); + if (real_path) { + DIR* dir = opendir(real_path); + if (dir) { + //load from directory + cout << "Trying load from directory \"" << real_path << "\"." << endl; + if ((ret = handleDirectory(real_path, dir))) break; + + } else if (svgFile(path)) { + //load single file + if ((ret = renderFile(real_path))) break; + } else { + //not a directory and not .svg file + cout << "Warning: File \"" << path << "\" is not a proper svg file." << endl; + } + + } else { + cout << "Warning: File \"" << path << "\" is invalid." << endl; + } + } + } - int result() { - std::cout<<"Generated PNG file : "< 4 && (strcmp(&path[length - 4], ".svg") == 0); + } + + const char* realFile(const char* path) + { + //real path +#ifdef _WIN32 + path = fullpath(full, path, PATH_MAX); +#else + path = realpath(path, full); +#endif + return path; + } - if (app.setup(argc, argv, &w, &h)) return 1; + int renderFile(const char* path) + { + if (!path) return 1; - app.tvgRender(w, h); + //destination png file + const char* dot = strrchr(path, '.'); + if (!dot) return 1; + string dst(path, dot - path); + dst += ".png"; - return 0; + return renderer.render(path, width, height, dst, bgColor); + } + + int handleDirectory(const string& path, DIR* dir) + { + //open directory + if (!dir) { + dir = opendir(path.c_str()); + if (!dir) { + cout << "Couldn't open directory \"" << path.c_str() << "\"." << endl; + return 1; + } + } + + //list directory + int ret = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (*entry->d_name == '.' || *entry->d_name == '$') continue; + if (entry->d_type == DT_DIR) { + string subpath = string(path); +#ifdef _WIN32 + subpath += '\\'; +#else + subpath += '/'; +#endif + subpath += entry->d_name; + ret = handleDirectory(subpath, nullptr); + if (ret) break; + + } else { + if (!svgFile(entry->d_name)) continue; + string fullpath = string(path); +#ifdef _WIN32 + fullpath += '\\'; +#else + fullpath += '/'; +#endif + fullpath += entry->d_name; + ret = renderFile(fullpath.c_str()); + if (ret) break; + } + } + closedir(dir); + return ret; + } +}; + +int main(int argc, char** argv) +{ + App app; + return app.setup(argc, argv); } -- 2.7.4