#include <thorvg.h>
#include <vector>
#include "lodepng.h"
+#include <dirent.h>
+#include <unistd.h>
+#include <limits.h>
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<unsigned char> image;
+ //Used ARGB8888 so have to move pixels now
+ vector<unsigned char> 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<uint32_t>(fw);
+ h = static_cast<uint32_t>(fh);
+ } else {
+ picture->size(w, h);
+ }
- //Proper size is not specified, Get default size.
- if (w == 0 || h == 0) {
- width = static_cast<uint32_t>(fw);
- height = static_cast<uint32_t>(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<char, 5000> 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<tvg::SwCanvas> 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<const char*> 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 : "<<pngName<<std::endl;
- return 0;
- }
+ //Terminate renderer
+ renderer.terminate();
- int help() {
- std::cout<<"Usage: \n svg2png [svgFileName] [Resolution] [bgColor]\n\nExamples: \n $ svg2png input.svg\n $ svg2png input.svg 200x200\n $ svg2png input.svg 200x200 ff00ff\n\n";
- return 1;
+ return ret;
}
private:
+ Renderer renderer;
uint32_t bgColor = 0xffffffff;
uint32_t width = 0;
uint32_t height = 0;
- std::string fileName;
- std::string pngName;
-};
+ char full[PATH_MAX];
-int main(int argc, char **argv)
-{
- App app;
- size_t w, h;
+private:
+ int help()
+ {
+ cout << "Usage:\n svg2png [svgFileName] [-r resolution] [-b bgColor]\n\nFlags:\n -r set output image resolution.\n -b set output image background color.\n\nExamples:\n $ svg2png input.svg\n $ svg2png input.svg -r 200x200\n $ svg2png input.svg -r 200x200 -b ff00ff\n $ svg2png input1.svg input2.svg -r 200x200 -b ff00ff\n $ svg2png . -r 200x200\n\n";
+ return 1;
+ }
+
+ bool svgFile(const char* path)
+ {
+ size_t length = strlen(path);
+ return length > 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);
}