00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "gvplugin_skillgui_cairo.h"
00024
00025 #include <utils/system/argparser.h>
00026 #include <cstdlib>
00027 #include <cstring>
00028 #include <cstdio>
00029 #include <cmath>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <unistd.h>
00033 #include <dirent.h>
00034 #include <fnmatch.h>
00035
00036 using namespace fawkes;
00037
00038
00039
00040 class SkillGuiBatchRenderer
00041 : public SkillGuiCairoRenderInstructor
00042 {
00043 public:
00044
00045
00046
00047
00048 SkillGuiBatchRenderer(int argc, char **argv)
00049 : argp(argc, argv, "hi:o:f:wps:")
00050 {
00051 if (! (argp.has_arg("i") && argp.has_arg("o") && argp.has_arg("f"))
00052 || argp.has_arg("h")) {
00053 usage();
00054 exit(-1);
00055 }
00056
00057 format = argp.arg("f");
00058 write_to_png = false;
00059 bbw = bbh = 0;
00060 white_bg = argp.has_arg("w");
00061 postproc_required = false;
00062 do_postproc = argp.has_arg("p");
00063 maxwidth = maxheight = 0;
00064 scale = 1.0;
00065
00066 if ( (format != "pdf") && (format != "svg") && (format != "png") ) {
00067 printf("Unknown format '%s'\n\n", format.c_str());
00068 usage();
00069 exit(-2);
00070 }
00071
00072 if ( do_postproc && (format != "png") ) {
00073 printf("Post-processing only available for PNG output format.\n");
00074 exit(-7);
00075 }
00076
00077 if (argp.has_arg("s")) {
00078 char *endptr;
00079 scale = strtod(argp.arg("s"), &endptr);
00080 if ( *endptr != 0 ) {
00081 printf("Invalid scale value '%s', could not convert to number (failed at '%s').\n",
00082 argp.arg("s"), endptr);
00083 exit(-8);
00084 }
00085 }
00086
00087 indir = argp.arg("i");
00088 outdir = argp.arg("o");
00089
00090 struct stat statbuf_in, statbuf_out;
00091 if (stat(indir.c_str(), &statbuf_in) != 0) {
00092 perror("Unable to stat input directory");
00093 exit(-3);
00094 }
00095 if (stat(outdir.c_str(), &statbuf_out) != 0) {
00096 perror("Unable to stat output directory");
00097 exit(-4);
00098 }
00099 if (! S_ISDIR(statbuf_in.st_mode) || ! S_ISDIR(statbuf_out.st_mode)) {
00100 printf("Input or output directory is not a directory.\n\n");
00101 exit(-5);
00102 }
00103
00104 char outdir_real[PATH_MAX];
00105 if (realpath(outdir.c_str(), outdir_real)) {
00106 outdir = outdir_real;
00107 }
00108
00109 directory = opendir(indir.c_str());
00110 if (! directory) {
00111 printf("Could not open input directory\n");
00112 exit(-6);
00113 }
00114
00115 gvc = gvContext();
00116 gvplugin_skillgui_cairo_setup(gvc, this);
00117 }
00118
00119
00120 ~SkillGuiBatchRenderer()
00121 {
00122 gvFreeContext(gvc);
00123 closedir(directory);
00124 }
00125
00126
00127 void usage()
00128 {
00129 printf("\nUsage: %s -i <dir> -o <dir> -f <format> [-w] [-s scale]\n"
00130 " -i dir Input directory containing dot graphs\n"
00131 " -o dir Output directory for generated graphs\n"
00132 " -f format Output format, one of pdf, svg, or png\n"
00133 " -w White background\n"
00134 " -p Postprocess frames to same size (PNG only)\n"
00135 " -s scale Scale factor to apply during rendering\n"
00136 "\n",
00137 argp.program_name());
00138 }
00139
00140 virtual Cairo::RefPtr<Cairo::Context> get_cairo()
00141 {
00142 if (! cairo) {
00143 this->bbw = bbw;
00144 this->bbh = bbh;
00145 if (format == "pdf") {
00146 surface = Cairo::PdfSurface::create(outfile, bbw * scale, bbh * scale);
00147 printf("Creating PDF context of size %f x %f\n", bbw * scale, bbh * scale);
00148 } else if (format == "svg") {
00149 surface = Cairo::SvgSurface::create(outfile, bbw * scale, bbh * scale);
00150 } else if (format == "png") {
00151 surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
00152 (int)ceilf(bbw * scale),
00153 (int)ceilf(bbh * scale));
00154 write_to_png = true;
00155 }
00156 cairo = Cairo::Context::create(surface);
00157 if (white_bg) {
00158 cairo->set_source_rgb(1, 1, 1);
00159 cairo->paint();
00160 }
00161 }
00162 return cairo;
00163 }
00164
00165 virtual bool scale_override() { return true; }
00166
00167 virtual void get_dimensions(double &width, double &height)
00168 {
00169 width = bbw * scale;
00170 height = bbh * scale;
00171 }
00172
00173 virtual double get_scale() { return scale; }
00174 virtual void set_scale(double scale) {};
00175 virtual void set_translation(double tx, double ty) {};
00176
00177 virtual void get_translation(double &tx, double &ty)
00178 {
00179
00180 tx = pad_x * scale;
00181 ty = (bbh - pad_y) * scale;
00182 }
00183
00184 virtual void set_bb(double bbw, double bbh)
00185 {
00186 this->bbw = bbw;
00187 this->bbh = bbh;
00188
00189 if ( bbw * scale > maxwidth ) {
00190 postproc_required = (maxwidth != 0);
00191 maxwidth = bbw * scale;
00192 }
00193 if ( bbh * scale > maxheight * scale ) {
00194 postproc_required = (maxheight != 0);
00195 maxheight = bbh * scale;
00196 }
00197 }
00198
00199 virtual void set_pad(double pad_x, double pad_y)
00200 {
00201 this->pad_x = pad_x;
00202 this->pad_y = pad_y;
00203 }
00204
00205
00206 virtual void get_pad(double &pad_x, double &pad_y)
00207 {
00208 pad_x = 0;
00209 pad_y = 0;
00210 }
00211
00212
00213 void render()
00214 {
00215
00216 FILE *f = fopen(infile.c_str(), "r");
00217 Agraph_t *g = agread(f);
00218 if (g) {
00219 gvLayout(gvc, g, (char *)"dot");
00220 gvRender(gvc, g, (char *)"skillguicairo", NULL);
00221 gvFreeLayout(gvc, g);
00222 agclose(g);
00223 }
00224 fclose(f);
00225
00226 if (write_to_png) {
00227 surface->write_to_png(outfile);
00228 }
00229
00230 cairo.clear();
00231 surface.clear();
00232 }
00233
00234
00235 void run()
00236 {
00237 struct dirent *d;
00238
00239 while ((d = readdir(directory)) != NULL) {
00240 if (fnmatch("*.dot", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
00241 char infile_real[PATH_MAX];
00242 infile = indir + "/" + d->d_name;
00243 if (realpath(infile.c_str(), infile_real)) {
00244 infile = infile_real;
00245 }
00246 char *basefile = strdup(infile.c_str());
00247 std::string basen = basename(basefile);
00248 free(basefile);
00249 outfile = outdir + "/" + basen.substr(0, basen.length() - 3) + format;
00250 printf("Converting %s to %s\n", infile.c_str(), outfile.c_str());
00251 render();
00252 } else {
00253 printf("%s does not match pattern\n", d->d_name);
00254 }
00255 }
00256
00257 if (do_postproc && postproc_required) {
00258 postprocess();
00259 }
00260 }
00261
00262
00263
00264
00265
00266
00267 static cairo_status_t write_func(void *closure,
00268 const unsigned char *data, unsigned int length)
00269 {
00270 FILE *f = (FILE *)closure;
00271 if (fwrite(data, length, 1, f)) {
00272 return CAIRO_STATUS_SUCCESS;
00273 } else {
00274 return CAIRO_STATUS_WRITE_ERROR;
00275 }
00276 }
00277
00278
00279 void postprocess()
00280 {
00281 printf("Post-processing PNG files, resizing to %fx%f\n", maxwidth, maxheight);
00282 struct dirent *d;
00283 DIR *output_dir = opendir(outdir.c_str());
00284 while ((d = readdir(output_dir)) != NULL) {
00285 if (fnmatch("*.png", d->d_name, FNM_PATHNAME | FNM_PERIOD) == 0) {
00286 infile = outdir + "/" + d->d_name;
00287 Cairo::RefPtr<Cairo::ImageSurface> imgs = Cairo::ImageSurface::create_from_png(infile);
00288 if ( (imgs->get_height() != maxheight) || (imgs->get_width() != maxwidth)) {
00289
00290 char *tmpout = strdup((outdir + "/tmpXXXXXX").c_str());
00291 FILE *f = fdopen(mkstemp(tmpout), "w");
00292 outfile = tmpout;
00293 free(tmpout);
00294
00295 Cairo::RefPtr<Cairo::ImageSurface> outs = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
00296 (int)ceilf(maxwidth),
00297 (int)ceilf(maxheight));
00298 double tx = (maxwidth - imgs->get_width()) / 2.0;
00299 double ty = (maxheight - imgs->get_height()) / 2.0;
00300 printf("Re-creating %s for post-processing, "
00301 "resizing from %ix%i, tx=%f, ty=%f\n", infile.c_str(),
00302 imgs->get_width(), imgs->get_height(), tx, ty);
00303 Cairo::RefPtr<Cairo::Context> cc = Cairo::Context::create(outs);
00304 if (white_bg) {
00305 cc->set_source_rgb(1, 1, 1);
00306 cc->paint();
00307 }
00308 cc->set_source(imgs, tx, ty);
00309 cc->paint();
00310 outs->write_to_png(&SkillGuiBatchRenderer::write_func, f);
00311 imgs.clear();
00312 cc.clear();
00313 outs.clear();
00314 fclose(f);
00315 rename(outfile.c_str(), infile.c_str());
00316 }
00317 }
00318 }
00319 closedir(output_dir);
00320 }
00321
00322 private:
00323 GVC_t *gvc;
00324 ArgumentParser argp;
00325 std::string format;
00326 Cairo::RefPtr<Cairo::Surface> surface;
00327 Cairo::RefPtr<Cairo::Context> cairo;
00328 bool write_to_png;
00329 bool white_bg;
00330 double bbw, bbh;
00331 double pad_x, pad_y;
00332 std::string infile;
00333 std::string outfile;
00334 std::string indir;
00335 std::string outdir;
00336 DIR *directory;
00337 double maxwidth, maxheight;
00338 bool postproc_required;
00339 bool do_postproc;
00340 double scale;
00341 };
00342
00343
00344
00345 int
00346 main(int argc, char **argv)
00347 {
00348 SkillGuiBatchRenderer renderer(argc, argv);
00349 renderer.run();
00350 return 0;
00351 }