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