1 /*
2  # This file is part of the Astrometry.net suite.
3  # Licensed under a 3-clause BSD style license - see LICENSE
4  */
5 
6 // Avoid *nasty* problem when 'bool' gets redefined (by ppm.h) to be 4 bytes!
7 #include "an-bool.h"
8 
9 #include <math.h>
10 #include <string.h>
11 #include <stdint.h>
12 #include <assert.h>
13 
14 #include <cairo.h>
15 #include <cairo-pdf.h>
16 
17 #include "os-features.h"
18 #include "plotstuff.h"
19 #include "plotfill.h"
20 #include "plotxy.h"
21 #include "plotimage.h"
22 #include "plotannotations.h"
23 #include "plotgrid.h"
24 #include "plotoutline.h"
25 #include "plotindex.h"
26 #include "plotradec.h"
27 #include "plothealpix.h"
28 #include "plotmatch.h"
29 
30 #include "sip_qfits.h"
31 #include "sip-utils.h"
32 #include "sip.h"
33 #include "cairoutils.h"
34 #include "starutil.h"
35 #include "ioutils.h"
36 #include "log.h"
37 #include "errors.h"
38 #include "anwcs.h"
39 
40 
41 enum cmdtype {
42     CIRCLE,
43     TEXT,
44     LINE,
45     RECTANGLE,
46     ARROW,
47     MARKER,
48     POLYGON,
49 };
50 typedef enum cmdtype cmdtype;
51 
52 struct cairocmd {
53     cmdtype type;
54     int layer;
55     double x, y;
56     float rgba[4];
57     // CIRCLE
58     double radius;
59     // TEXT
60     char* text;
61     // LINE / RECTANGLE / ARROW
62     double x2, y2;
63     // MARKER
64     int marker;
65     double markersize;
66     // POLYGON
67     dl* xy;
68     anbool fill;
69 };
70 typedef struct cairocmd cairocmd_t;
71 
72 static void get_text_position(plot_args_t* pargs, cairo_t* cairo,
73                               const char* txt, double* px, double* py);
74 
plotstuff_new()75 plot_args_t* plotstuff_new() {
76     plot_args_t* pargs = calloc(1, sizeof(plot_args_t));
77     plotstuff_init(pargs);
78     return pargs;
79 }
80 
plotstuff_clear(plot_args_t * pargs)81 void plotstuff_clear(plot_args_t* pargs) {
82     cairo_operator_t op;
83     assert(pargs->cairo);
84     op = cairo_get_operator(pargs->cairo);
85     cairo_set_operator(pargs->cairo, CAIRO_OPERATOR_CLEAR);
86     cairo_paint(pargs->cairo);
87     cairo_set_operator(pargs->cairo, op);
88 }
89 
plotstuff_move_to(plot_args_t * pargs,double x,double y)90 void plotstuff_move_to(plot_args_t* pargs, double x, double y) {
91     if (pargs->move_to)
92         pargs->move_to(pargs, x, y, pargs->move_to_baton);
93     else {
94         assert(pargs->cairo);
95         cairo_move_to(pargs->cairo, x, y);
96     }
97 }
98 
plotstuff_line_to(plot_args_t * pargs,double x,double y)99 void plotstuff_line_to(plot_args_t* pargs, double x, double y) {
100     if (pargs->line_to)
101         pargs->line_to(pargs, x, y, pargs->line_to_baton);
102     else {
103         assert(pargs->cairo);
104         cairo_line_to(pargs->cairo, x, y);
105     }
106 }
107 
plotstuff_rotate_wcs(plot_args_t * pargs,double angle)108 int plotstuff_rotate_wcs(plot_args_t* pargs, double angle) {
109     if (!pargs->wcs) {
110         ERROR("No WCS has been set");
111         return -1;
112     }
113     return anwcs_rotate_wcs(pargs->wcs, angle);
114 }
115 
plotstuff_get_radec_center_and_radius(plot_args_t * pargs,double * p_ra,double * p_dec,double * p_radius)116 int plotstuff_get_radec_center_and_radius(plot_args_t* pargs,
117                                           double* p_ra, double* p_dec, double* p_radius) {
118     int rtn;
119     if (!pargs->wcs)
120         return -1;
121     rtn = anwcs_get_radec_center_and_radius(pargs->wcs, p_ra, p_dec, p_radius);
122     if (rtn)
123         return rtn;
124     if (p_radius && *p_radius == 0.0) {
125         // HACK -- get approximate scale, using plot size.
126         *p_radius = arcsec2deg(anwcs_pixel_scale(pargs->wcs) * hypot(pargs->W, pargs->H)/2.0);
127     }
128     return rtn;
129 }
130 
plotstuff_append_doubles(const char * str,dl * lst)131 int plotstuff_append_doubles(const char* str, dl* lst) {
132     int i;
133     sl* strs = sl_split(NULL, str, " ");
134     for (i=0; i<sl_size(strs); i++)
135         dl_append(lst, atof(sl_get(strs, i)));
136     sl_free2(strs);
137     return 0;
138 }
139 
plotstuff_line_constant_ra(plot_args_t * pargs,double ra,double dec1,double dec2,anbool startwithmove)140 int plotstuff_line_constant_ra(plot_args_t* pargs, double ra, double dec1, double dec2,
141                                anbool startwithmove) {
142     double decstep;
143     double dec;
144     double s;
145     double pixscale;
146     anbool lastok = FALSE;
147     if (!startwithmove)
148         lastok = TRUE;
149     assert(pargs->wcs);
150     pixscale = anwcs_pixel_scale(pargs->wcs);
151     assert(pixscale > 0.0);
152     decstep = arcsec2deg(pixscale * pargs->linestep);
153     logverb("plotstuff_line_constant_ra: RA=%g, Dec=[%g,%g], pixscale %g, decstep %g\n",
154             ra, dec1, dec2, anwcs_pixel_scale(pargs->wcs), decstep);
155     //printf("plotstuff_line_constant_ra: RA=%g, Dec=[%g,%g], pixscale %g, decstep %g\n",
156     //ra, dec1, dec2, anwcs_pixel_scale(pargs->wcs), decstep);
157     s = 1.0;
158     if (dec1 > dec2)
159         s = -1;
160     for (dec=dec1; (s*dec)<=(s*dec2); dec+=(decstep*s)) {
161         double x, y;
162         //logverb("  line_constant_ra: RA,Dec %g,%g\n", ra, dec);
163         //printf("  line_constant_ra: RA,Dec %g,%g\n", ra, dec);
164         if (anwcs_radec2pixelxy(pargs->wcs, ra, dec, &x, &y)) {
165             printf("  bad xy\n");
166             lastok = FALSE;
167             continue;
168         }
169         //printf("  x,y %.1f, %.1f\n", x, y);
170         if (lastok)
171             plotstuff_line_to(pargs, x, y);
172         else
173             plotstuff_move_to(pargs, x, y);
174         lastok = TRUE;
175     }
176     return 0;
177 }
178 
plotstuff_line_constant_dec(plot_args_t * pargs,double dec,double ra1,double ra2)179 int plotstuff_line_constant_dec(plot_args_t* pargs, double dec, double ra1, double ra2) {
180     double rastep;
181     double ra;
182     double f;
183     double s;
184     assert(pargs->wcs);
185     rastep = arcsec2deg(anwcs_pixel_scale(pargs->wcs) * pargs->linestep);
186     f = cos(deg2rad(dec));
187     rastep /= MAX(0.1, f);
188     s = 1.0;
189     if (ra1 > ra2)
190         s = -1.0;
191     for (ra=ra1; (s*ra)<=(s*ra2); ra+=(rastep*s)) {
192         double x, y;
193         if (anwcs_radec2pixelxy(pargs->wcs, ra, dec, &x, &y))
194             continue;
195         if (ra == ra1)
196             plotstuff_move_to(pargs, x, y);
197         else
198             plotstuff_line_to(pargs, x, y);
199     }
200     return 0;
201 }
202 
normra(double ra)203 static double normra(double ra) {
204     while (ra < 0.)
205         ra += 360.;
206     while (ra > 360.)
207         ra -= 360.;
208     return ra;
209 }
210 
plotstuff_line_constant_dec2(plot_args_t * pargs,double dec,double ra1,double ra2,double rastep)211 int plotstuff_line_constant_dec2(plot_args_t* pargs, double dec, double ra1, double ra2, double rastep) {
212     double ra;
213     int n;
214     int done = 0;
215     ra1 = normra(ra1);
216     ra2 = normra(ra2);
217     assert(pargs->wcs);
218     for (ra=ra1, n=0; n<1000000; n++) {
219         double x, y;
220         double ranext;
221         ra = normra(ra);
222         if (anwcs_radec2pixelxy(pargs->wcs, ra, dec, &x, &y))
223             continue;
224         if (n == 0)
225             plotstuff_move_to(pargs, x, y);
226         else
227             plotstuff_line_to(pargs, x, y);
228         if (done)
229             break;
230         ranext = ra + rastep;
231         // will the next step take us past ra2?
232         if (MIN(ra, ranext) < ra2 && ra2 < MAX(ra, ranext)) {
233             ra = ra2;
234             done = 1;
235         } else
236             ra = ranext;
237     }
238     return 0;
239 }
240 
plotstuff_text_radec(plot_args_t * pargs,double ra,double dec,const char * label)241 int plotstuff_text_radec(plot_args_t* pargs, double ra, double dec, const char* label) {
242     double x,y;
243     if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y)) {
244         ERROR("Failed to convert RA,Dec (%g,%g) to pixel position in plot_text_radec\n", ra, dec);
245         return -1;
246     }
247     assert(pargs->cairo);
248     //plotstuff_stack_text(pargs, pargs->cairo, label, x, y);
249     get_text_position(pargs, pargs->cairo, label, &x, &y);
250     plotstuff_move_to(pargs, x, y);
251     cairo_show_text(pargs->cairo, label);
252     return 0;
253 }
254 
plotstuff_text_xy(plot_args_t * pargs,double x,double y,const char * label)255 int plotstuff_text_xy(plot_args_t* pargs, double x, double y, const char* label) {
256     assert(pargs->cairo);
257     get_text_position(pargs, pargs->cairo, label, &x, &y);
258     plotstuff_move_to(pargs, x, y);
259     cairo_show_text(pargs->cairo, label);
260     return 0;
261 }
262 
moveto_lineto_radec(plot_args_t * pargs,double ra,double dec,anbool move)263 static int moveto_lineto_radec(plot_args_t* pargs, double ra, double dec, anbool move) {
264     double x,y;
265     if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y)) {
266         ERROR("Failed to convert RA,Dec (%g,%g) to pixel position in plot_text_radec\n", ra, dec);
267         return -1;
268     }
269     assert(pargs->cairo);
270     (move ? plotstuff_move_to : plotstuff_line_to)(pargs, x, y);
271     return 0;
272 }
273 
plotstuff_move_to_radec(plot_args_t * pargs,double ra,double dec)274 int plotstuff_move_to_radec(plot_args_t* pargs, double ra, double dec) {
275     assert(pargs->cairo);
276     plotstuff_builtin_apply(pargs->cairo, pargs);
277     return moveto_lineto_radec(pargs, ra, dec, TRUE);
278 }
279 
plotstuff_line_to_radec(plot_args_t * pargs,double ra,double dec)280 int plotstuff_line_to_radec(plot_args_t* pargs, double ra, double dec) {
281     return moveto_lineto_radec(pargs, ra, dec, FALSE);
282 }
283 
plotstuff_close_path(plot_args_t * pargs)284 int plotstuff_close_path(plot_args_t* pargs) {
285     assert(pargs->cairo);
286     cairo_close_path(pargs->cairo);
287     return 0;
288 }
289 
plotstuff_fill(plot_args_t * pargs)290 int plotstuff_fill(plot_args_t* pargs) {
291     assert(pargs->cairo);
292     cairo_fill(pargs->cairo);
293     return 0;
294 }
plotstuff_stroke(plot_args_t * pargs)295 int plotstuff_stroke(plot_args_t* pargs) {
296     assert(pargs->cairo);
297     cairo_stroke(pargs->cairo);
298     return 0;
299 }
300 
plotstuff_fill_preserve(plot_args_t * pargs)301 int plotstuff_fill_preserve(plot_args_t* pargs) {
302     assert(pargs->cairo);
303     cairo_fill_preserve(pargs->cairo);
304     return 0;
305 }
plotstuff_stroke_preserve(plot_args_t * pargs)306 int plotstuff_stroke_preserve(plot_args_t* pargs) {
307     assert(pargs->cairo);
308     cairo_stroke_preserve(pargs->cairo);
309     return 0;
310 }
311 
plotstuff_set_dashed(plot_args_t * pargs,double dashlen)312 void plotstuff_set_dashed(plot_args_t* pargs, double dashlen) {
313     assert(pargs->cairo);
314     cairo_set_dash(pargs->cairo, &dashlen, 1, 0);
315 }
316 
plotstuff_set_solid(plot_args_t * pargs)317 void plotstuff_set_solid(plot_args_t* pargs) {
318     assert(pargs->cairo);
319     cairo_set_dash(pargs->cairo, NULL, 0, 0);
320 }
321 
parse_color(const char * color,float * r,float * g,float * b,float * a)322 int parse_color(const char* color, float* r, float* g, float* b, float* a) {
323     if (a) *a = 1.0;
324     return (cairoutils_parse_rgba(color, r, g, b, a) &&
325             cairoutils_parse_color(color, r, g, b));
326 }
327 
parse_color_rgba(const char * color,float * rgba)328 int parse_color_rgba(const char* color, float* rgba) {
329     return parse_color(color, rgba, rgba+1, rgba+2, rgba+3);
330 }
331 
cairo_set_rgba(cairo_t * cairo,const float * rgba)332 void cairo_set_rgba(cairo_t* cairo, const float* rgba) {
333     cairo_set_source_rgba(cairo, rgba[0], rgba[1], rgba[2], rgba[3]);
334 }
335 
cairo_set_color(cairo_t * cairo,const char * color)336 int cairo_set_color(cairo_t* cairo, const char* color) {
337     float rgba[4];
338     int res;
339     res = parse_color_rgba(color, rgba);
340     if (res) {
341         ERROR("Failed to parse color \"%s\"", color);
342         return res;
343     }
344     cairo_set_rgba(cairo, rgba);
345     return res;
346 }
347 
plotstuff_builtin_apply(cairo_t * cairo,plot_args_t * args)348 void plotstuff_builtin_apply(cairo_t* cairo, plot_args_t* args) {
349     //printf("Set rgba %.2f, %.2f, %.2f, %.2f\n", args->rgba[0], args->rgba[1],
350     //args->rgba[2], args->rgba[3]);
351     cairo_set_rgba(cairo, args->rgba);
352     cairo_set_line_width(cairo, args->lw);
353     cairo_set_operator(cairo, args->op);
354     cairo_set_font_size(cairo, args->fontsize);
355 }
356 
plotstuff_set_text_bg_alpha(plot_args_t * pargs,float alpha)357 void plotstuff_set_text_bg_alpha(plot_args_t* pargs, float alpha) {
358     pargs->bg_rgba[3] = alpha;
359 }
360 
plot_builtin_init(plot_args_t * args)361 static void* plot_builtin_init(plot_args_t* args) {
362     parse_color_rgba("gray", args->rgba);
363     parse_color_rgba("black", args->bg_rgba);
364     args->text_bg_layer = 2;
365     args->text_fg_layer = 3;
366     args->marker_fg_layer = 3;
367     args->bg_lw = 3.0;
368     args->lw = 1.0;
369     args->marker = CAIROUTIL_MARKER_CIRCLE;
370     args->markersize = 5.0;
371     args->linestep = 10;
372     args->op = CAIRO_OPERATOR_OVER;
373     args->fontsize = 20;
374     args->halign = 'C';
375     args->valign = 'B';
376     args->cairocmds = bl_new(256, sizeof(cairocmd_t));
377     args->label_offset_x = 10.0;
378     args->label_offset_y =  5.0;
379     return NULL;
380 }
381 
plot_builtin_init2(plot_args_t * pargs,void * baton)382 static int plot_builtin_init2(plot_args_t* pargs, void* baton) {
383     plotstuff_builtin_apply(pargs->cairo, pargs);
384     // Inits that aren't in "plot_builtin"
385     cairo_set_antialias(pargs->cairo, CAIRO_ANTIALIAS_GRAY);
386     return 0;
387 }
388 
plotstuff_set_markersize(plot_args_t * pargs,double ms)389 int plotstuff_set_markersize(plot_args_t* pargs, double ms) {
390     pargs->markersize = ms;
391     return 0;
392 }
393 
plotstuff_set_marker(plot_args_t * pargs,const char * name)394 int plotstuff_set_marker(plot_args_t* pargs, const char* name) {
395     int m = cairoutils_parse_marker(name);
396     if (m == -1) {
397         ERROR("Failed to parse plot_marker \"%s\"", name);
398         return -1;
399     }
400     pargs->marker = m;
401     return 0;
402 }
403 
plotstuff_set_size(plot_args_t * pargs,int W,int H)404 int plotstuff_set_size(plot_args_t* pargs, int W, int H) {
405     pargs->W = W;
406     pargs->H = H;
407     return 0;
408 }
409 
plotstuff_scale_wcs(plot_args_t * pargs,double scale)410 int plotstuff_scale_wcs(plot_args_t* pargs, double scale) {
411     if (!pargs->wcs) {
412         ERROR("No WCS has been set");
413         return -1;
414     }
415     return anwcs_scale_wcs(pargs->wcs, scale);
416 }
417 
plotstuff_set_wcs_file(plot_args_t * pargs,const char * filename,int ext)418 int plotstuff_set_wcs_file(plot_args_t* pargs, const char* filename, int ext) {
419     anwcs_t* wcs = anwcs_open(filename, ext);
420     if (!wcs) {
421         ERROR("Failed to read WCS file \"%s\", extension %i", filename, ext);
422         return -1;
423     }
424     return plotstuff_set_wcs(pargs, wcs);
425 }
426 
plotstuff_set_wcs_sip(plot_args_t * pargs,sip_t * wcs)427 int plotstuff_set_wcs_sip(plot_args_t* pargs, sip_t* wcs) {
428     anwcs_t* anwcs = anwcs_new_sip(wcs);
429     return plotstuff_set_wcs(pargs, anwcs);
430 }
431 
plotstuff_set_wcs_tan(plot_args_t * pargs,tan_t * wcs)432 int plotstuff_set_wcs_tan(plot_args_t* pargs, tan_t* wcs) {
433     anwcs_t* anwcs = anwcs_new_tan(wcs);
434     return plotstuff_set_wcs(pargs, anwcs);
435 }
436 
plotstuff_set_wcs(plot_args_t * pargs,anwcs_t * wcs)437 int plotstuff_set_wcs(plot_args_t* pargs, anwcs_t* wcs) {
438     if (pargs->wcs) {
439         anwcs_free(pargs->wcs);
440     }
441     pargs->wcs = wcs;
442     return 0;
443 }
444 
plotstuff_set_wcs_box(plot_args_t * pargs,float ra,float dec,float width)445 int plotstuff_set_wcs_box(plot_args_t* pargs, float ra, float dec, float width) {
446     logverb("Setting WCS to a box centered at (%g,%g) with width %g deg.\n", ra, dec, width);
447     anwcs_t* wcs = anwcs_create_box_upsidedown(ra, dec, width, pargs->W, pargs->H);
448     return plotstuff_set_wcs(pargs, wcs);
449 }
450 
plot_builtin_command(const char * cmd,const char * cmdargs,plot_args_t * pargs,void * baton)451 static int plot_builtin_command(const char* cmd, const char* cmdargs,
452                                 plot_args_t* pargs, void* baton) {
453     if (streq(cmd, "plot_color")) {
454         if (parse_color_rgba(cmdargs, pargs->rgba)) {
455             ERROR("Failed to parse plot_color: \"%s\"", cmdargs);
456             return -1;
457         }
458     } else if (streq(cmd, "plot_bgcolor")) {
459         if (parse_color_rgba(cmdargs, pargs->bg_rgba)) {
460             ERROR("Failed to parse plot_bgcolor: \"%s\"", cmdargs);
461             return -1;
462         }
463     } else if (streq(cmd, "plot_fontsize")) {
464         pargs->fontsize = atof(cmdargs);
465     } else if (streq(cmd, "plot_alpha")) {
466         if (plotstuff_set_alpha(pargs, atof(cmdargs))) {
467             ERROR("Failed to set alpha");
468             return -1;
469         }
470     } else if (streq(cmd, "plot_op")) {
471         if (streq(cmdargs, "add")) {
472             pargs->op = CAIRO_OPERATOR_ADD;
473         } else if (streq(cmdargs, "reset")) {
474             pargs->op = CAIRO_OPERATOR_OVER;
475         } else {
476             ERROR("Didn't understand op: %s", cmdargs);
477             return -1;
478         }
479     } else if (streq(cmd, "plot_lw")) {
480         pargs->lw = atof(cmdargs);
481     } else if (streq(cmd, "plot_bglw")) {
482         pargs->bg_lw = atof(cmdargs);
483     } else if (streq(cmd, "plot_marker")) {
484         if (plotstuff_set_marker(pargs, cmdargs)) {
485             return -1;
486         }
487     } else if (streq(cmd, "plot_markersize")) {
488         pargs->markersize = atof(cmdargs);
489     } else if (streq(cmd, "plot_size")) {
490         int W, H;
491         if (sscanf(cmdargs, "%i %i", &W, &H) != 2) {
492             ERROR("Failed to parse plot_size args \"%s\"", cmdargs);
493             return -1;
494         }
495         plotstuff_set_size(pargs, W, H);
496     } else if (streq(cmd, "plot_wcs")) {
497         if (plotstuff_set_wcs_file(pargs, cmdargs, 0)) {
498             return -1;
499         }
500     } else if (streq(cmd, "plot_wcs_box")) {
501         float ra, dec, width;
502         if (sscanf(cmdargs, "%f %f %f", &ra, &dec, &width) != 3) {
503             ERROR("Failed to parse plot_wcs_box args \"%s\"", cmdargs);
504             return -1;
505         }
506         if (plotstuff_set_wcs_box(pargs, ra, dec, width)) {
507             return -1;
508         }
509     } else if (streq(cmd, "plot_wcs_setsize")) {
510         assert(pargs->wcs);
511         plotstuff_set_size_wcs(pargs);
512     } else if (streq(cmd, "plot_label_radec")) {
513         assert(pargs->wcs);
514         double ra, dec;
515         int nc;
516         const char* label;
517         if (sscanf(cmdargs, "%lf %lf %n", &ra, &dec, &nc) != 3) {
518             ERROR("Failed to parse plot_label_radec args \"%s\"", cmdargs);
519             return -1;
520         }
521         label = cmdargs + nc;
522         return plotstuff_text_radec(pargs, ra, dec, label);
523     } else {
524         ERROR("Did not understand command: \"%s\"", cmd);
525         return -1;
526     }
527     if (pargs->cairo)
528         plotstuff_builtin_apply(pargs->cairo, pargs);
529     return 0;
530 }
531 
plotstuff_set_size_wcs(plot_args_t * pargs)532 int plotstuff_set_size_wcs(plot_args_t* pargs) {
533     assert(pargs->wcs);
534     return plotstuff_set_size(pargs, (int)ceil(anwcs_imagew(pargs->wcs)), (int)ceil(anwcs_imageh(pargs->wcs)));
535 }
536 
plot_builtin_plot(const char * command,cairo_t * cairo,plot_args_t * pargs,void * baton)537 int plot_builtin_plot(const char* command, cairo_t* cairo, plot_args_t* pargs, void* baton) {
538     //plotstuff_plot_stack(pargs, cairo);
539     return 0;
540 }
541 
cairocmd_init(cairocmd_t * cmd)542 static void cairocmd_init(cairocmd_t* cmd) {
543     if (!cmd)
544         return;
545     memset(cmd, 0, sizeof(cairocmd_t));
546     //cmd->xy = dl_new(32);
547 }
548 
cairocmd_clear(cairocmd_t * cmd)549 static void cairocmd_clear(cairocmd_t* cmd) {
550     if (!cmd)
551         return;
552     free(cmd->text);
553     cmd->text = NULL;
554     if (cmd->xy)
555         dl_free(cmd->xy);
556     cmd->xy = NULL;
557 }
558 
add_cmd(plot_args_t * pargs,cairocmd_t * cmd)559 static void add_cmd(plot_args_t* pargs, cairocmd_t* cmd) {
560     bl_append(pargs->cairocmds, cmd);
561 }
562 
set_cmd_args(plot_args_t * pargs,cairocmd_t * cmd)563 static void set_cmd_args(plot_args_t* pargs, cairocmd_t* cmd) {
564     cmd->marker = pargs->marker;
565     cmd->markersize = pargs->markersize;
566     memcpy(cmd->rgba, pargs->rgba, sizeof(cmd->rgba));
567 }
568 
plotstuff_marker_in_bounds(plot_args_t * pargs,double x,double y)569 anbool plotstuff_marker_in_bounds(plot_args_t* pargs, double x, double y) {
570     double margin = pargs->markersize;
571     return (x >= -margin && x <= (pargs->W + margin) &&
572             y >= -margin && y <= (pargs->H + margin));
573 }
574 
plotstuff_stack_marker(plot_args_t * pargs,double x,double y)575 void plotstuff_stack_marker(plot_args_t* pargs, double x, double y) {
576     cairocmd_t cmd;
577     cairocmd_init(&cmd);
578     set_cmd_args(pargs, &cmd);
579     // BG marker?
580     cmd.layer = pargs->marker_fg_layer;
581     cmd.type = MARKER;
582     // FIXME -- handle cairo half-pixel issues here?
583     cmd.x = x + 0.5;
584     cmd.y = y + 0.5;
585     add_cmd(pargs, &cmd);
586 }
587 
plotstuff_stack_arrow(plot_args_t * pargs,double x,double y,double x2,double y2)588 void plotstuff_stack_arrow(plot_args_t* pargs, double x, double y,
589                            double x2, double y2) {
590     cairocmd_t cmd;
591     cairocmd_init(&cmd);
592     // BG?
593     set_cmd_args(pargs, &cmd);
594     cmd.layer = pargs->marker_fg_layer;
595     cmd.type = ARROW;
596     cmd.x = x;
597     cmd.y = y;
598     cmd.x2 = x2;
599     cmd.y2 = y2;
600     add_cmd(pargs, &cmd);
601 }
602 
get_text_position(plot_args_t * pargs,cairo_t * cairo,const char * txt,double * px,double * py)603 static void get_text_position(plot_args_t* pargs, cairo_t* cairo,
604                               const char* txt, double* px, double* py) {
605     cairo_text_extents_t textents;
606     double l = 0.0,r,t = 0.0,b;
607     double margin = 2.0;
608     double x, y;
609     x = *px;
610     y = *py;
611 
612     x += pargs->label_offset_x;
613     y += pargs->label_offset_y;
614 
615     cairo_text_extents(cairo, txt, &textents);
616 
617     switch (pargs->halign) {
618     case 'L':
619         l = x + textents.x_bearing;
620         break;
621     case 'C':
622         l = x + textents.x_bearing - 0.5*textents.width;
623         break;
624     case 'R':
625         l = x + textents.x_bearing - textents.width;
626         break;
627     }
628     x = l;
629     r = l + textents.width + textents.x_bearing;
630 
631     switch (pargs->valign) {
632     case 'T':
633         t = y + textents.y_bearing + textents.height;
634         //y -= (0.5 * textents.y_bearing);
635         break;
636     case 'C':
637         t = y + textents.y_bearing + 0.5*textents.height;
638         break;
639     case 'B':
640         t = y + textents.y_bearing;
641         break;
642     }
643     b = t + textents.height;
644     y = b;
645 
646     l -= margin;
647     t -= margin;
648     r += margin + 1;
649     b += margin + 1;
650 
651     // move text away from the edges of the image.
652     if (l < 0) {
653         x += -l;
654         l = 0;
655     }
656     if (t < 0) {
657         y += -t;
658         t = 0;
659     }
660     if (r > pargs->W) {
661         x -= (r - pargs->W);
662         r = pargs->W;
663     }
664     if (b > pargs->H) {
665         y -= (b - pargs->H);
666         b = pargs->H;
667     }
668 
669     *px = x;
670     *py = y;
671 }
672 
plotstuff_stack_text(plot_args_t * pargs,cairo_t * cairo,const char * txt,double px,double py)673 void plotstuff_stack_text(plot_args_t* pargs, cairo_t* cairo,
674                           const char* txt, double px, double py) {
675     cairocmd_t cmd;
676     cairocmd_init(&cmd);
677     set_cmd_args(pargs, &cmd);
678     get_text_position(pargs, cairo, txt, &px, &py);
679     cmd.type = TEXT;
680 
681     if (pargs->bg_rgba[3] > 0) {
682         int dx, dy;
683         logverb("Background text RGB [%g, %g, %g] alpha %g\n",
684                 pargs->bg_rgba[0], pargs->bg_rgba[1],
685                 pargs->bg_rgba[2], pargs->bg_rgba[3]);
686         cmd.layer = pargs->text_bg_layer;
687         memcpy(cmd.rgba, pargs->bg_rgba, sizeof(cmd.rgba));
688 
689         if (pargs->bg_box) {
690             // Plot a rectangle behind the text
691             cairo_text_extents_t textents;
692             cairo_text_extents(cairo, txt, &textents);
693             cmd.type = RECTANGLE;
694             cmd.x = px + textents.x_bearing;
695             cmd.y = py + textents.y_bearing;
696             cmd.x2 = cmd.x + textents.width;
697             cmd.y2 = py + textents.y_bearing + textents.height;
698             cmd.fill = TRUE;
699             add_cmd(pargs, &cmd);
700             cmd.type = TEXT;
701         } else {
702             // Plot bg-color text behind
703             for (dy=-1; dy<=1; dy++) {
704                 for (dx=-1; dx<=1; dx++) {
705                     cmd.text = strdup(txt);
706                     cmd.x = px + dx;
707                     cmd.y = py + dy;
708                     add_cmd(pargs, &cmd);
709                 }
710             }
711         }
712     } else
713         logverb("No background behind text\n");
714 
715     cmd.layer = pargs->text_fg_layer;
716     memcpy(cmd.rgba, pargs->rgba, sizeof(cmd.rgba));
717     cmd.text = strdup(txt);
718     cmd.x = px;
719     cmd.y = py;
720     add_cmd(pargs, &cmd);
721 }
722 
plotstuff_marker(plot_args_t * pargs,double x,double y)723 void plotstuff_marker(plot_args_t* pargs, double x, double y) {
724     cairo_t* cairo = pargs->cairo;
725     cairo_move_to(cairo, x, y);
726     cairoutils_draw_marker(cairo, pargs->marker, x, y, pargs->markersize);
727 }
728 
plotstuff_marker_radec(plot_args_t * pargs,double ra,double dec)729 int plotstuff_marker_radec(plot_args_t* pargs, double ra, double dec) {
730     double x,y;
731     //printf("plotstuff_marker_radec(%.3f, %.3f)\n", ra, dec);
732     if (!plotstuff_radec2xy(pargs, ra, dec, &x, &y)) {
733         ERROR("Failed to convert RA,Dec (%g,%g) to pixel position in plot_marker_radec\n", ra, dec);
734         return -1;
735     }
736     assert(pargs->cairo);
737     //logverb("plotstuff_marker_radec (%.3f, %.3f) -> (%.1f, %.1f)\n", ra, dec, x, y);
738     // MAGIC 0.5 -- cairo/FITS coord offset
739     plotstuff_marker(pargs, x-0.5, y-0.5);
740     return 0;
741 }
742 
plotstuff_plot_stack(plot_args_t * pargs,cairo_t * cairo)743 int plotstuff_plot_stack(plot_args_t* pargs, cairo_t* cairo) {
744     int i, j;
745     int layer;
746     anbool morelayers;
747 
748     logverb("Plotting %zu stacked plot commands.\n", bl_size(pargs->cairocmds));
749     morelayers = TRUE;
750     for (layer=0;; layer++) {
751         if (!morelayers)
752             break;
753         morelayers = FALSE;
754         for (i=0; i<bl_size(pargs->cairocmds); i++) {
755             cairocmd_t* cmd = bl_access(pargs->cairocmds, i);
756             if (cmd->layer > layer)
757                 morelayers = TRUE;
758             if (cmd->layer != layer)
759                 continue;
760             cairo_set_rgba(cairo, cmd->rgba);
761             switch (cmd->type) {
762             case CIRCLE:
763                 cairo_move_to(cairo, cmd->x + cmd->radius, cmd->y);
764                 cairo_arc(cairo, cmd->x, cmd->y, cmd->radius, 0, 2*M_PI);
765                 break;
766             case MARKER:
767                 {
768                     double oldmarkersize = pargs->markersize;
769                     int oldmarker = pargs->marker;
770                     pargs->markersize = cmd->markersize;
771                     pargs->marker = cmd->marker;
772                     plotstuff_marker(pargs, cmd->x, cmd->y);
773                     pargs->markersize = oldmarkersize;
774                     pargs->marker = oldmarker;
775                 }
776                 break;
777             case TEXT:
778                 cairo_move_to(cairo, cmd->x, cmd->y);
779                 cairo_show_text(cairo, cmd->text);
780                 break;
781             case LINE:
782             case ARROW:
783                 plotstuff_move_to(pargs, cmd->x, cmd->y);
784                 plotstuff_line_to(pargs, cmd->x2, cmd->y2);
785                 {
786                     double dx = cmd->x - cmd->x2;
787                     double dy = cmd->y - cmd->y2;
788                     double angle = atan2(dy, dx);
789                     double dang = 30. * M_PI/180.0;
790                     double arrowlen = 20;
791                     plotstuff_line_to(pargs,
792                                       cmd->x2 + cos(angle+dang)*arrowlen,
793                                       cmd->y2 + sin(angle+dang)*arrowlen);
794                     plotstuff_move_to(pargs, cmd->x2, cmd->y2);
795                     plotstuff_line_to(pargs,
796                                       cmd->x2 + cos(angle-dang)*arrowlen,
797                                       cmd->y2 + sin(angle-dang)*arrowlen);
798                 }
799                 break;
800             case RECTANGLE:
801                 cairo_move_to(cairo, cmd->x, cmd->y);
802                 cairo_line_to(cairo, cmd->x, cmd->y2);
803                 cairo_line_to(cairo, cmd->x2, cmd->y2);
804                 cairo_line_to(cairo, cmd->x2, cmd->y);
805                 cairo_close_path(cairo);
806                 if (cmd->fill)
807                     cairo_fill(cairo);
808                 break;
809             case POLYGON:
810                 if (!cmd->xy)
811                     break;
812                 for (j=0; j<dl_size(cmd->xy)/2; j++)
813                     (j == 0 ? cairo_move_to : cairo_line_to)(cairo, dl_get(cmd->xy, 2*j+0), dl_get(cmd->xy, 2*j+1));
814                 if (cmd->fill)
815                     cairo_fill(cairo);
816                 break;
817             }
818             cairo_stroke(cairo);
819         }
820     }
821     for (i=0; i<bl_size(pargs->cairocmds); i++) {
822         cairocmd_t* cmd = bl_access(pargs->cairocmds, i);
823         cairocmd_clear(cmd);
824     }
825     bl_remove_all(pargs->cairocmds);
826 
827     return 0;
828 }
829 
plot_builtin_free(plot_args_t * pargs,void * baton)830 static void plot_builtin_free(plot_args_t* pargs, void* baton) {
831     anwcs_free(pargs->wcs);
832     bl_free(pargs->cairocmds);
833 }
834 
835 //static const plotter_t builtin = { "plot", plot_builtin_init, plot_builtin_init2, plot_builtin_command, plot_builtin_plot, plot_builtin_free, NULL };
836 
DECLARE_PLOTTER(builtin)837 DECLARE_PLOTTER(builtin) {
838     DEFINE_PLOTTER_BODY(builtin)
839         p->init2 = plot_builtin_init2;
840     p->name = "plot";
841 }
842 
parse_image_format(const char * fmt)843 int parse_image_format(const char* fmt) {
844     if (strcaseeq(fmt, "png")) {
845         return PLOTSTUFF_FORMAT_PNG;
846     } else if (strcaseeq(fmt, "jpg") || strcaseeq(fmt, "jpeg")) {
847         return PLOTSTUFF_FORMAT_JPG;
848     } else if (strcaseeq(fmt, "ppm")) {
849         return PLOTSTUFF_FORMAT_PPM;
850     } else if (strcaseeq(fmt, "pdf")) {
851         return PLOTSTUFF_FORMAT_PDF;
852     } else if (strcaseeq(fmt, "fits") || strcaseeq(fmt, "fit")) {
853         return PLOTSTUFF_FORMAT_FITS;
854     }
855     ERROR("Unknown image format \"%s\"", fmt);
856     return -1;
857 }
858 
guess_image_format_from_filename(const char * fn)859 int guess_image_format_from_filename(const char* fn) {
860     // look for "."
861     int len = strlen(fn);
862     if (len >= 4 && fn[len-4] == '.') {
863         return parse_image_format(fn + len - 3);
864     }
865     if (len >= 5 && fn[len - 5] == '.') {
866         return parse_image_format(fn + len - 4);
867     }
868     return 0;
869 }
870 
image_format_name_from_code(int code)871 const char* image_format_name_from_code(int code) {
872     if (code == PLOTSTUFF_FORMAT_JPG)
873         return "jpeg";
874     if (code == PLOTSTUFF_FORMAT_PNG)
875         return "png";
876     if (code == PLOTSTUFF_FORMAT_PPM)
877         return "ppm";
878     if (code == PLOTSTUFF_FORMAT_PDF)
879         return "pdf";
880     if (code == PLOTSTUFF_FORMAT_FITS)
881         return "fits";
882     if (code == PLOTSTUFF_FORMAT_MEMIMG)
883         return "memory";
884     return "unknown";
885 }
886 
plotstuff_set_color(plot_args_t * pargs,const char * name)887 int plotstuff_set_color(plot_args_t* pargs, const char* name) {
888     logverb("setting color to \"%s\"\n", name);
889     return parse_color_rgba(name, pargs->rgba);
890 }
891 
plotstuff_get_alpha(const plot_args_t * pargs)892 float plotstuff_get_alpha(const plot_args_t* pargs) {
893     return pargs->rgba[3];
894 }
895 
plotstuff_set_alpha(plot_args_t * pargs,float alpha)896 int plotstuff_set_alpha(plot_args_t* pargs, float alpha) {
897     pargs->rgba[3] = alpha;
898     return 0;
899 }
900 
plotstuff_set_bgcolor(plot_args_t * pargs,const char * name)901 int plotstuff_set_bgcolor(plot_args_t* pargs, const char* name) {
902     return parse_color_rgba(name, pargs->bg_rgba);
903 }
904 
plotstuff_set_bgrgba2(plot_args_t * pargs,float r,float g,float b,float a)905 int plotstuff_set_bgrgba2(plot_args_t* pargs, float r, float g, float b, float a) {
906     pargs->bg_rgba[0] = r;
907     pargs->bg_rgba[1] = g;
908     pargs->bg_rgba[2] = b;
909     pargs->bg_rgba[3] = a;
910     return 0;
911 }
912 
plotstuff_set_rgba(plot_args_t * pargs,const float * rgba)913 int plotstuff_set_rgba(plot_args_t* pargs, const float* rgba) {
914     pargs->rgba[0] = rgba[0];
915     pargs->rgba[1] = rgba[1];
916     pargs->rgba[2] = rgba[2];
917     pargs->rgba[3] = rgba[3];
918     return 0;
919 }
920 
plotstuff_set_rgba2(plot_args_t * pargs,float r,float g,float b,float a)921 int plotstuff_set_rgba2(plot_args_t* pargs, float r, float g, float b, float a) {
922     pargs->rgba[0] = r;
923     pargs->rgba[1] = g;
924     pargs->rgba[2] = b;
925     pargs->rgba[3] = a;
926     return 0;
927 }
928 
plotstuff_init(plot_args_t * pargs)929 int plotstuff_init(plot_args_t* pargs) {
930     int i;
931 
932     memset(pargs, 0, sizeof(plot_args_t));
933 
934     /*
935      plotters[0] = builtin;
936      plotters[1] = plotter_fill;
937      plotters[2] = plotter_xy;
938      plotters[3] = plotter_image;
939      plotters[4] = plotter_annotations;
940      plotters[5] = plotter_grid;
941      plotters[6] = plotter_outline;
942      plotters[7] = plotter_index;
943      plotters[8] = plotter_radec;
944      plotters[9] = plotter_healpix;
945      plotters[10] = plotter_match;
946      */
947 
948     pargs->NP = 11;
949     pargs->plotters = calloc(pargs->NP, sizeof(plotter_t));
950     /*
951      pargs->plotters[0] = builtin_new();
952      pargs->plotters[1] = plot_fill_new();
953      pargs->plotters[2] = plot_xy_new();
954      pargs->plotters[3] = plot_image_new();
955      pargs->plotters[4] = plot_annotations_new();
956      pargs->plotters[5] = plot_grid_new();
957      pargs->plotters[6] = plot_outline_new();
958      pargs->plotters[7] = plot_index_new();
959      pargs->plotters[8] = plot_radec_new();
960      pargs->plotters[9] = plot_healpix_new();
961      pargs->plotters[10] = plot_match_new();
962      */
963     //builtin_describe(pargs->plotters + 0);
964 
965     plot_builtin_describe    (pargs->plotters + 0);
966     plot_fill_describe       (pargs->plotters + 1);
967     plot_xy_describe         (pargs->plotters + 2);
968     plot_image_describe      (pargs->plotters + 3);
969     plot_annotations_describe(pargs->plotters + 4);
970     plot_grid_describe       (pargs->plotters + 5);
971     plot_outline_describe    (pargs->plotters + 6);
972     plot_index_describe      (pargs->plotters + 7);
973     plot_radec_describe      (pargs->plotters + 8);
974     plot_healpix_describe    (pargs->plotters + 9);
975     plot_match_describe      (pargs->plotters + 10);
976 
977     // First init
978     for (i=0; i<pargs->NP; i++)
979         pargs->plotters[i].baton = pargs->plotters[i].init(pargs);
980     return 0;
981 }
982 
plotstuff_init2(plot_args_t * pargs)983 int plotstuff_init2(plot_args_t* pargs) {
984     int i;
985 
986     logverb("Creating drawing surface (%ix%i)\n", pargs->W, pargs->H);
987     // Allocate cairo surface
988     switch (pargs->outformat) {
989     case PLOTSTUFF_FORMAT_PDF:
990         if (pargs->outfn) {
991             pargs->fout = fopen(pargs->outfn, "wb");
992             if (!pargs->fout) {
993                 SYSERROR("Failed to open output file \"%s\"", pargs->outfn);
994                 return -1;
995             }
996         }
997         pargs->target = cairo_pdf_surface_create_for_stream(cairoutils_file_write_func, pargs->fout, pargs->W, pargs->H);
998         break;
999     case PLOTSTUFF_FORMAT_JPG:
1000     case PLOTSTUFF_FORMAT_PPM:
1001     case PLOTSTUFF_FORMAT_PNG:
1002     case PLOTSTUFF_FORMAT_MEMIMG:
1003         pargs->target = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pargs->W, pargs->H);
1004         if (!pargs->target) {
1005             ERROR("Failed to create Cairo image surface of size %i x %i\n", pargs->W, pargs->H);
1006             return -1;
1007         }
1008         if (cairo_surface_status(pargs->target) != CAIRO_STATUS_SUCCESS) {
1009             ERROR("Failed to create Cairo image surface of size %i x %i: %s\n", pargs->W, pargs->H,
1010                   cairo_status_to_string(cairo_surface_status(pargs->target)));
1011             return -1;
1012         }
1013         break;
1014     default:
1015         ERROR("Unknown output format %i", pargs->outformat);
1016         return -1;
1017         break;
1018     }
1019     pargs->cairo = cairo_create(pargs->target);
1020 
1021     /* D'oh, this flips the coord sys, but not text!
1022      // Flip the cairo reference frame (make 0,0 the bottom-left)
1023      cairo_scale(pargs->cairo, 1.0, -1.0);
1024      // FIXME -- could deal with 0.5 issues here!
1025      cairo_translate(pargs->cairo, 0.0, -pargs->H);
1026      */
1027 
1028     for (i=0; i<pargs->NP; i++) {
1029         if (pargs->plotters[i].init2 &&
1030             pargs->plotters[i].init2(pargs, pargs->plotters[i].baton)) {
1031             ERROR("Plot initializer failed");
1032             exit(-1);
1033         }
1034     }
1035 
1036     return 0;
1037 }
1038 
plotstuff_get_config(plot_args_t * pargs,const char * name)1039 void* plotstuff_get_config(plot_args_t* pargs, const char* name) {
1040     int i;
1041     for (i=0; i<pargs->NP; i++) {
1042         if (streq(pargs->plotters[i].name, name))
1043             return pargs->plotters[i].baton;
1044     }
1045     return NULL;
1046 }
1047 
plotstuff_pixel_scale(plot_args_t * pargs)1048 double plotstuff_pixel_scale(plot_args_t* pargs) {
1049     if (!pargs->wcs) {
1050         ERROR("plotstuff_pixel_scale: No WCS defined!");
1051         return 0.0;
1052     }
1053     return anwcs_pixel_scale(pargs->wcs);
1054 }
1055 
plotstuff_radec2xy(plot_args_t * pargs,double ra,double dec,double * x,double * y)1056 anbool plotstuff_radec2xy(plot_args_t* pargs, double ra, double dec,
1057                           double* x, double* y) {
1058     if (!pargs->wcs) {
1059         ERROR("No WCS defined!");
1060         return FALSE;
1061     }
1062     return (anwcs_radec2pixelxy(pargs->wcs, ra, dec, x, y) ? FALSE : TRUE);
1063 }
1064 
plotstuff_xy2radec(plot_args_t * pargs,double x,double y,double * pra,double * pdec)1065 anbool plotstuff_xy2radec(plot_args_t* pargs, double x, double y,
1066                           double* pra, double* pdec) {
1067     if (!pargs->wcs) {
1068         ERROR("No WCS defined!");
1069         return FALSE;
1070     }
1071     return (anwcs_pixelxy2radec(pargs->wcs, x, y, pra, pdec)
1072             ? FALSE : TRUE);
1073 }
1074 
plotstuff_radec_is_inside_image(plot_args_t * pargs,double ra,double dec)1075 anbool plotstuff_radec_is_inside_image(plot_args_t* pargs, double ra, double dec) {
1076     if (!pargs->wcs) {
1077         ERROR("No WCS defined!");
1078         return FALSE;
1079     }
1080     return anwcs_radec_is_inside_image(pargs->wcs, ra, dec);
1081 }
1082 
plotstuff_get_radec_bounds(const plot_args_t * pargs,int stepsize,double * pramin,double * pramax,double * pdecmin,double * pdecmax)1083 void plotstuff_get_radec_bounds(const plot_args_t* pargs, int stepsize,
1084                                 double* pramin, double* pramax,
1085                                 double* pdecmin, double* pdecmax) {
1086     if (!pargs->wcs) {
1087         ERROR("No WCS defined!");
1088         return;
1089     }
1090     return anwcs_get_radec_bounds(pargs->wcs, stepsize, pramin, pramax, pdecmin, pdecmax);
1091 }
1092 
1093 int
1094 ATTRIB_FORMAT(printf,2,3)
plotstuff_run_commandf(plot_args_t * pargs,const char * format,...)1095     plotstuff_run_commandf(plot_args_t* pargs, const char* format, ...) {
1096     char* str;
1097     va_list va;
1098     int rtn;
1099     va_start(va, format);
1100     if (vasprintf(&str, format, va) == -1) {
1101         ERROR("Failed to allocate temporary string to hold command");
1102         return -1;
1103     }
1104     rtn = plotstuff_run_command(pargs, str);
1105     va_end(va);
1106     return rtn;
1107 }
1108 
plotstuff_plot_layer(plot_args_t * pargs,const char * layer)1109 int plotstuff_plot_layer(plot_args_t* pargs, const char* layer) {
1110     int i;
1111     for (i=0; i<pargs->NP; i++) {
1112         if (streq(layer, pargs->plotters[i].name)) {
1113             if (!pargs->cairo) {
1114                 if (plotstuff_init2(pargs)) {
1115                     return -1;
1116                 }
1117             }
1118             if (pargs->plotters[i].doplot) {
1119                 if (pargs->plotters[i].doplot(layer, pargs->cairo, pargs, pargs->plotters[i].baton)) {
1120                     ERROR("Plotter \"%s\" failed on command \"%s\"", pargs->plotters[i].name, layer);
1121                     return -1;
1122                 } else
1123                     return 0;
1124             }
1125         }
1126     }
1127     return -1;
1128 }
1129 
plotstuff_run_command(plot_args_t * pargs,const char * cmd)1130 int plotstuff_run_command(plot_args_t* pargs, const char* cmd) {
1131     int i;
1132     anbool matched = FALSE;
1133     if (!cmd || (strlen(cmd) == 0) || (cmd[0] == '#')) {
1134         return 0;
1135     }
1136     if (!plotstuff_plot_layer(pargs, cmd)) {
1137         return 0;
1138     }
1139     for (i=0; i<pargs->NP; i++) {
1140         if (starts_with(cmd, pargs->plotters[i].name)) {
1141             char* cmdcmd;
1142             char* cmdargs;
1143             if (!split_string_once(cmd, " ", &cmdcmd, &cmdargs)) {
1144                 //ERROR("Failed to split command \"%s\" into words\n", cmd);
1145                 //return -1;
1146                 cmdcmd = strdup(cmd);
1147                 cmdargs = NULL;
1148             }
1149             logmsg("Command \"%s\", args \"%s\"\n", cmdcmd, cmdargs);
1150             if (pargs->plotters[i].command(cmdcmd, cmdargs, pargs, pargs->plotters[i].baton)) {
1151                 ERROR("Plotter \"%s\" failed on command \"%s\"", pargs->plotters[i].name, cmd);
1152                 return -1;
1153             }
1154             free(cmdcmd);
1155             free(cmdargs);
1156         } else
1157             continue;
1158         matched = TRUE;
1159         break;
1160     }
1161     if (!matched) {
1162         ERROR("Did not find a plotter for command \"%s\"", cmd);
1163         return -1;
1164     }
1165     return 0;
1166 }
1167 
plotstuff_read_and_run_command(plot_args_t * pargs,FILE * f)1168 int plotstuff_read_and_run_command(plot_args_t* pargs, FILE* f) {
1169     char* cmd;
1170     int rtn;
1171     cmd = read_string_terminated(stdin, "\n\r\0", 3, FALSE);
1172     logverb("command: \"%s\"\n", cmd);
1173     if (!cmd || feof(f)) {
1174         free(cmd);
1175         return -1;
1176     }
1177     rtn = plotstuff_run_command(pargs, cmd);
1178     free(cmd);
1179     return rtn;
1180 }
1181 
plotstuff_output(plot_args_t * pargs)1182 int plotstuff_output(plot_args_t* pargs) {
1183     switch (pargs->outformat) {
1184     case PLOTSTUFF_FORMAT_PDF:
1185 
1186         if (pargs->outfn && !pargs->fout) {
1187             // open output file if it hasn't already been opened...
1188             pargs->fout = fopen(pargs->outfn, "wb");
1189             if (!pargs->fout) {
1190                 SYSERROR("Failed to open output file \"%s\"", pargs->outfn);
1191                 return -1;
1192             }
1193         }
1194         cairo_surface_flush(pargs->target);
1195         cairo_surface_finish(pargs->target);
1196         cairoutils_surface_status_errors(pargs->target);
1197         cairoutils_cairo_status_errors(pargs->cairo);
1198         if (pargs->outfn) {
1199             if (fclose(pargs->fout)) {
1200                 SYSERROR("Failed to close output file \"%s\"", pargs->outfn);
1201                 return -1;
1202             }
1203             pargs->fout = NULL;
1204         }
1205         break;
1206 
1207     case PLOTSTUFF_FORMAT_JPG:
1208     case PLOTSTUFF_FORMAT_PPM:
1209     case PLOTSTUFF_FORMAT_PNG:
1210     case PLOTSTUFF_FORMAT_MEMIMG:
1211         {
1212             int res;
1213             unsigned char* img = cairo_image_surface_get_data(pargs->target);
1214             // Convert image for output...
1215             cairoutils_argb32_to_rgba(img, pargs->W, pargs->H);
1216             if (pargs->outformat == PLOTSTUFF_FORMAT_MEMIMG) {
1217                 pargs->outimage = img;
1218                 res = 0;
1219                 img = NULL;
1220             } else if (pargs->outformat == PLOTSTUFF_FORMAT_JPG) {
1221                 res = cairoutils_write_jpeg(pargs->outfn, img, pargs->W, pargs->H);
1222             } else if (pargs->outformat == PLOTSTUFF_FORMAT_PPM) {
1223                 res = cairoutils_write_ppm(pargs->outfn, img, pargs->W, pargs->H);
1224             } else if (pargs->outformat == PLOTSTUFF_FORMAT_PNG) {
1225                 res = cairoutils_write_png(pargs->outfn, img, pargs->W, pargs->H);
1226             } else {
1227                 res=-1; // for gcc
1228                 assert(0);
1229             }
1230             if (res)
1231                 ERROR("Failed to write output image");
1232             if (img)
1233                 // Convert image back...
1234                 cairoutils_rgba_to_argb32(img, pargs->W, pargs->H);
1235             return res;
1236         }
1237         break;
1238     default:
1239         ERROR("Unknown output format.");
1240         return -1;
1241     }
1242     return 0;
1243 }
1244 
plotstuff_get_maximum_rgba(plot_args_t * pargs,int * p_r,int * p_g,int * p_b,int * p_a)1245 void plotstuff_get_maximum_rgba(plot_args_t* pargs,
1246                                 int* p_r, int* p_g, int* p_b, int* p_a) {
1247     int i, r, g, b, a;
1248     uint32_t* ipix = (uint32_t*)cairo_image_surface_get_data(pargs->target);
1249     r = g = b = a = 0;
1250     for (i=0; i<(pargs->W * pargs->H); i++) {
1251         a = MAX(a, (ipix[i] >> 24) & 0xff);
1252         r = MAX(r, (ipix[i] >> 16) & 0xff);
1253         g = MAX(g, (ipix[i] >>  8) & 0xff);
1254         b = MAX(b, (ipix[i]      ) & 0xff);
1255     }
1256     if (p_r)
1257         *p_r = r;
1258     if (p_g)
1259         *p_g = g;
1260     if (p_b)
1261         *p_b = b;
1262     if (p_a)
1263         *p_a = a;
1264 }
1265 
1266 
plotstuff_free(plot_args_t * pargs)1267 void plotstuff_free(plot_args_t* pargs) {
1268     int i;
1269     for (i=0; i<pargs->NP; i++) {
1270         pargs->plotters[i].free(pargs, pargs->plotters[i].baton);
1271     }
1272     cairo_destroy(pargs->cairo);
1273     cairo_surface_destroy(pargs->target);
1274 }
1275 
1276