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