1 /*
2  * MODULE:       g.cairocomp
3  * AUTHOR(S):    Glynn Clements
4  * PURPOSE:      g.cairocomp isn't meant for end users. It's an internal tool for use by
5  *               a wx GUI.
6  *               g.cairocomp composites a series of X pixmaps.
7  * COPYRIGHT:    (C) 2008 by Glynn Clements and the GRASS Development Team
8  *
9  *               This program is free software under the GNU General Public
10  *               License (>=v2). Read the file COPYING that comes with GRASS
11  *               for details.
12  *
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdarg.h>
18 #include <string.h>
19 #include <cairo.h>
20 #include <cairo-xlib.h>
21 #include <cairo-xlib-xrender.h>
22 #include <X11/X.h>
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
25 #include <grass/gis.h>
26 #include <grass/colors.h>
27 #include <grass/glocale.h>
28 
29 static int width, height;
30 static XID output;
31 static Display *dpy;
32 static cairo_surface_t *surface;
33 static cairo_t *cairo;
34 static Screen *screen;
35 static XRenderPictFormat *format;
36 static int depth;
37 
read_xid(const char * filename)38 static XID read_xid(const char *filename)
39 {
40     FILE *fp;
41     char buf[64];
42     long xid;
43 
44     fp = fopen(filename, "r");
45     if (!fp)
46 	G_fatal_error(_("Unable to open input file <%s>"), filename);
47 
48     if (!fgets(buf, sizeof(buf), fp))
49 	G_fatal_error(_("Unable to read input file <%s>"), filename);
50 
51     if (sscanf(buf, "%li", &xid) != 1)
52 	G_fatal_error(_("Unable to parse input file <%s>"), filename);
53 
54     fclose(fp);
55 
56     return (XID) xid;
57 }
58 
write_xid(const char * filename,XID xid)59 static void write_xid(const char *filename, XID xid)
60 {
61     FILE *fp;
62     char buf[64];
63 
64     fp = fopen(filename, "w");
65     if (!fp)
66 	G_fatal_error(_("Unable to open output file <%s>"), filename);
67 
68     sprintf(buf, "0x%08lx\n", (unsigned long) xid);
69 
70     if (fputs(buf, fp) < 0)
71 	G_fatal_error(_("Unable to write output file <%s>"), filename);
72 
73     fclose(fp);
74 }
75 
init_xlib(const char * scr,const char * vis)76 static void init_xlib(const char *scr, const char *vis)
77 {
78     XVisualInfo templ;
79     XVisualInfo *vinfo;
80     int count;
81     Visual *visual;
82     Pixmap pix;
83     int scrn;
84     long visid;
85     cairo_surface_t *s1, *s2;
86 
87     dpy = XOpenDisplay(NULL);
88     if (!dpy)
89 	G_fatal_error(_("Unable to open display"));
90 
91     if (scr)
92 	scrn = atoi(scr);
93     else {
94 	const char *p = getenv("GRASS_RENDER_CAIRO_SCREEN");
95 	if (!p || sscanf(p, "%i", &scrn) != 1)
96 	    scrn = DefaultScreen(dpy);
97     }
98 
99     screen = ScreenOfDisplay(dpy, scrn);
100 
101     if (vis)
102 	visid = strtoul(vis, NULL, 0);
103     else {
104 	const char *p = getenv("GRASS_RENDER_CAIRO_VISUAL");
105 	if (!p || sscanf(p, "%li", &visid) != 1)
106 	    visid = DefaultVisual(dpy, scrn)->visualid;
107     }
108 
109     templ.visualid = visid;
110     templ.screen = scrn;
111     vinfo = XGetVisualInfo(dpy, VisualIDMask|VisualScreenMask, &templ, &count);
112     if (!vinfo || !count)
113 	G_fatal_error(_("Unable to obtain visual"));
114     visual = vinfo[0].visual;
115 
116     pix = XCreatePixmap(dpy, RootWindow(dpy, scrn), 1, 1, vinfo[0].depth);
117     s1 = cairo_xlib_surface_create(dpy, pix, visual, 1, 1);
118     s2 = cairo_surface_create_similar(s1, CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
119     format = cairo_xlib_surface_get_xrender_format(s2);
120     depth = cairo_xlib_surface_get_depth(s2);
121     cairo_surface_destroy(s2);
122     cairo_surface_destroy(s1);
123     XFreePixmap(dpy, pix);
124 
125     output = XCreatePixmap(dpy, RootWindow(dpy, scrn), width, height, depth);
126 
127     surface = cairo_xlib_surface_create_with_xrender_format(dpy, output, screen, format, width, height);
128     if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
129 	G_fatal_error(_("Failed to initialize output surface"));
130 
131     cairo = cairo_create(surface);
132 }
133 
fini_xlib(void)134 static void fini_xlib(void)
135 {
136     cairo_surface_destroy(surface);
137     XFlush(dpy);
138     XSetCloseDownMode(dpy, RetainPermanent);
139     XCloseDisplay(dpy);
140 }
141 
erase(const char * color)142 static void erase(const char *color)
143 {
144     int r, g, b, a;
145     double fr, fg, fb, fa;
146 
147     a = 255;
148     if (sscanf(color, "%d:%d:%d:%d", &r, &g, &b, &a) != 4 ||
149         G_str_to_color(color, &r, &g, &b) != 1)
150 	G_fatal_error(_("Invalid color: %s"), color);
151 
152     fr = r / 255.0;
153     fg = g / 255.0;
154     fb = b / 255.0;
155     fa = a / 255.0;
156 
157     cairo_save(cairo);
158     cairo_set_source_rgba(cairo, fr, fg, fb, fa);
159     cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
160     cairo_paint(cairo);
161     cairo_restore(cairo);
162 }
163 
overlay(XID src,float alpha)164 static void overlay(XID src, float alpha)
165 {
166     cairo_surface_t *src_surf;
167 
168     src_surf = cairo_xlib_surface_create_with_xrender_format(
169 	dpy, src, screen, format, width, height);
170     if (cairo_surface_status(src_surf) != CAIRO_STATUS_SUCCESS)
171 	G_fatal_error(_("Failed to initialize input surface"));
172 
173     cairo_save(cairo);
174     cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
175     cairo_set_source_surface(cairo, src_surf, 0, 0);
176     cairo_pattern_set_filter(cairo_get_source(cairo), CAIRO_FILTER_NEAREST);
177     cairo_paint_with_alpha(cairo, alpha);
178     cairo_restore(cairo);
179 
180     cairo_surface_destroy(src_surf);
181 }
182 
main(int argc,char * argv[])183 int main(int argc, char *argv[])
184 {
185     struct GModule *module;
186     struct
187     {
188 	struct Option *in, *out, *visual, *screen, *alpha, *width, *height, *bg;
189     } opt;
190     struct Flag *flag_d;
191     int i;
192 
193     G_gisinit(argv[0]);
194 
195     module = G_define_module();
196     G_add_keyword(_("general"));
197     G_add_keyword(_("display"));
198     module->description = _("Overlays multiple X Pixmaps.");
199 
200     opt.in = G_define_standard_option(G_OPT_F_INPUT);
201     opt.in->required = YES;
202     opt.in->multiple = YES;
203     opt.in->description = _("Name of input file(s)");
204 
205     opt.out = G_define_standard_option(G_OPT_F_OUTPUT);
206     opt.out->required = YES;
207 
208     opt.visual = G_define_option();
209     opt.visual->key = "visual";
210     opt.visual->type = TYPE_INTEGER;
211     opt.visual->required = NO;
212     opt.visual->description = _("Output Visual XID");
213 
214     opt.screen = G_define_option();
215     opt.screen->key = "screen";
216     opt.screen->type = TYPE_INTEGER;
217     opt.screen->required = NO;
218     opt.screen->description = _("Output screen");
219 
220     opt.alpha = G_define_option();
221     opt.alpha->key = "opacity";
222     opt.alpha->type = TYPE_DOUBLE;
223     opt.alpha->multiple = YES;
224     opt.alpha->description = _("Layer opacities");
225 
226     opt.width = G_define_option();
227     opt.width->key = "width";
228     opt.width->type = TYPE_INTEGER;
229     opt.width->required = YES;
230     opt.width->description = _("Image width");
231 
232     opt.height = G_define_option();
233     opt.height->key = "height";
234     opt.height->type = TYPE_INTEGER;
235     opt.height->required = YES;
236     opt.height->description = _("Image height");
237 
238     opt.bg = G_define_standard_option(G_OPT_C);
239     opt.bg->key = "bgcolor";
240     opt.bg->label = _("Background color (R:G:B:A)");
241     opt.bg->answer = NULL;
242 
243     flag_d = G_define_flag();
244     flag_d->key = 'd';
245     flag_d->description = _("Do not composite; just delete input Pixmaps");
246 
247     if (G_parser(argc, argv))
248 	exit(EXIT_FAILURE);
249 
250     width = atoi(opt.width->answer);
251     height = atoi(opt.height->answer);
252 
253     if (flag_d->answer) {
254 	dpy = XOpenDisplay(NULL);
255 	if (!dpy)
256 	    G_fatal_error(_("Unable to open display"));
257 	for (i = 0; opt.in->answers[i]; i++) {
258 	    unsigned long xid = read_xid(opt.in->answers[i]);
259 	    XKillClient(dpy, xid);
260 	}
261 	XFlush(dpy);
262 	XCloseDisplay(dpy);
263 	return 0;
264     }
265 
266     init_xlib(opt.screen->answer, opt.visual->answer);
267 
268     if (opt.bg->answer)
269 	erase(opt.bg->answer);
270 
271     for (i = 0; opt.in->answers[i]; i++) {
272 	XID input = read_xid(opt.in->answers[i]);
273 	double alpha;
274 
275 	if (opt.alpha->answer && !opt.alpha->answers[i])
276 	    G_fatal_error(
277 		_("input= and opacity= must have the same number of values"));
278 
279 	alpha = opt.alpha->answer ? atof(opt.alpha->answers[i]) : 1.0;
280 
281 	overlay(input, alpha);
282     }
283 
284     write_xid(opt.out->answer, output);
285 
286     fini_xlib();
287 
288     return 0;
289 }
290 
291