GRASS GIS 8 Programmer's Manual 8.2.1RC1(2022)-exported
cairodriver/graph.c
Go to the documentation of this file.
1/*!
2 \file lib/cairodriver/graph.c
3
4 \brief GRASS cairo display driver - driver settings
5
6 (C) 2007-2008, 2011 by Lars Ahlzen and the GRASS Development Team
7
8 This program is free software under the GNU General Public License
9 (>=v2). Read the file COPYING that comes with GRASS for details.
10
11 \author Lars Ahlzen <lars ahlzen.com> (original contibutor)
12 \author Glynn Clements
13*/
14
15#include "cairodriver.h"
16
17#if CAIRO_HAS_PS_SURFACE
18#include <cairo-ps.h>
19#endif
20#if CAIRO_HAS_PDF_SURFACE
21#include <cairo-pdf.h>
22#endif
23#if CAIRO_HAS_SVG_SURFACE
24#include <cairo-svg.h>
25#endif
26#if CAIRO_HAS_XLIB_XRENDER_SURFACE
27#include <cairo-xlib.h>
28#include <cairo-xlib-xrender.h>
29#endif
30
31#include <unistd.h>
32#ifndef __MINGW32__
33#include <fcntl.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <sys/mman.h>
37#endif
38
39#include <grass/colors.h>
40#include <grass/glocale.h>
41
43
44/* cairo objects */
45cairo_surface_t *surface;
46cairo_t *cairo;
47
48static void init_cairo(void);
49static int ends_with(const char *string, const char *suffix);
50static void map_file(void);
51
52static void init_xlib(void)
53{
54#if CAIRO_HAS_XLIB_XRENDER_SURFACE
55 char *p;
56 unsigned long xid;
57 XVisualInfo templ;
58 XVisualInfo *vinfo;
59 int count;
60 Visual *visual;
61 int scrn;
62 Pixmap pix;
63 cairo_surface_t *s1, *s2;
64
65 ca.dpy = XOpenDisplay(NULL);
66 if (!ca.dpy)
67 G_fatal_error(_("Unable to open display"));
68
69 p = getenv("GRASS_RENDER_CAIRO_SCREEN");
70 if (!p || sscanf(p, "%i", &scrn) != 1) {
71 G_debug(1, "cairo: GRASS_RENDER_CAIRO_SCREEN=%s", p);
72 scrn = DefaultScreen(ca.dpy);
73 }
74
75 p = getenv("GRASS_RENDER_CAIRO_VISUAL");
76 if (!p || sscanf(p, "%li", &xid) != 1) {
77 G_debug(1, "cairo: GRASS_RENDER_CAIRO_VISUAL=%s", p);
78 xid = DefaultVisual(ca.dpy, scrn)->visualid;
79 }
80 templ.visualid = xid;
81 templ.screen = scrn;
82
83 vinfo = XGetVisualInfo(ca.dpy, VisualIDMask|VisualScreenMask, &templ, &count);
84 if (!vinfo || !count)
85 G_fatal_error(_("Unable to obtain visual"));
86 visual = vinfo[0].visual;
87
88 ca.screen = ScreenOfDisplay(ca.dpy, scrn);
89 pix = XCreatePixmap(ca.dpy, RootWindow(ca.dpy, scrn), 1, 1, vinfo[0].depth);
90 s1 = cairo_xlib_surface_create(ca.dpy, pix, visual, 1, 1);
91 s2 = cairo_surface_create_similar(s1, CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
92 ca.format = cairo_xlib_surface_get_xrender_format(s2);
93 ca.depth = cairo_xlib_surface_get_depth(s2);
94 cairo_surface_destroy(s2);
95 cairo_surface_destroy(s1);
96 XFreePixmap(ca.dpy, pix);
97
98 if (!ca.win)
99 ca.win = XCreatePixmap(
100 ca.dpy, RootWindow(ca.dpy, scrn),
101 ca.width, ca.height, ca.depth);
102#endif
103}
104
105static void fini_xlib(void)
106{
107#if CAIRO_HAS_XLIB_XRENDER_SURFACE
108 XSetCloseDownMode(ca.dpy, RetainTemporary);
109 XCloseDisplay(ca.dpy);
110#endif
111}
112
113static void init_file(void)
114{
115 int is_vector = 0;
116 int is_xlib = 0;
117 int do_read = 0;
118 int do_map = 0;
119 char *p;
120
121 /* set image properties */
124 ca.stride = ca.width * 4;
125
126 /* get file name */
127 p = getenv("GRASS_RENDER_FILE");
128 if (!p || strlen(p) == 0)
130 G_debug(1, "cairo: GRASS_RENDER_FILE=%s", p);
131
132 ca.file_name = p;
133
134 /* get file type (from extension) */
135 if (ends_with(ca.file_name, ".ppm"))
137 else if (ends_with(ca.file_name, ".bmp"))
139#if CAIRO_HAS_PNG_FUNCTIONS
140 else if (ends_with(ca.file_name, ".png"))
142#endif
143#if CAIRO_HAS_PDF_SURFACE
144 else if (ends_with(ca.file_name, ".pdf"))
146#endif
147#if CAIRO_HAS_PS_SURFACE
148 else if (ends_with(ca.file_name, ".ps"))
150#endif
151#if CAIRO_HAS_SVG_SURFACE
152 else if (ends_with(ca.file_name, ".svg"))
154#endif
155#if CAIRO_HAS_XLIB_XRENDER_SURFACE
156 else if (ends_with(ca.file_name, ".xid"))
158#endif
159 else
160 G_fatal_error(_("Unknown file extension: %s"), p);
161 G_debug(1, "cairo: file type=%d", ca.file_type);
162
163 switch (ca.file_type) {
164 case FTYPE_PDF:
165 case FTYPE_PS:
166 case FTYPE_SVG:
167 is_vector = 1;
168 break;
169 case FTYPE_X11:
170 is_xlib = 1;
171 break;
172 }
173
174 p = getenv("GRASS_RENDER_FILE_MAPPED");
175 do_map = p && strcmp(p, "TRUE") == 0 && ends_with(ca.file_name, ".bmp");
176 G_debug(1, "cairo: GRASS_RENDER_FILE_MAPPED=%d", do_map);
177
178 p = getenv("GRASS_RENDER_FILE_READ");
179 do_read = p && strcmp(p, "TRUE") == 0;
180 G_debug(1, "cairo: GRASS_RENDER_FILE_READ=%d", do_read);
181
182 if (is_vector) {
183 do_read = do_map = 0;
184 ca.bgcolor_a = 1.0;
185 }
186
187 if (do_read && access(ca.file_name, 0) != 0)
188 do_read = 0;
189
190 G_verbose_message(_("cairo: collecting to file '%s'"),
191 ca.file_name);
192 G_verbose_message(_("cairo: image size %dx%d"),
193 ca.width, ca.height);
194
195 if (do_read && do_map)
196 map_file();
197
198#if CAIRO_HAS_XLIB_XRENDER_SURFACE
199 if (is_xlib) {
200 if (do_read)
202 else
203 ca.win = 0;
204 init_xlib();
205 ca.mapped = 1;
206 }
207#endif
208
209 if (!ca.mapped && !is_vector)
210 ca.grid = G_malloc(ca.height * ca.stride);
211
212 init_cairo();
213
214 if (!do_read && !is_vector) {
215 Cairo_Erase();
216 ca.modified = 1;
217 }
218
219 if (do_read && !ca.mapped)
221
222 if (do_map && !ca.mapped) {
224 map_file();
225 init_cairo();
226 }
227}
228
229/*!
230 \brief Initialize driver
231
232 Set background color, transparency, drawable, antialias mode, etc.
233
234 \return 0
235*/
237{
238 cairo_antialias_t antialias;
239 char *p;
240
241 G_gisinit("Cairo driver");
242
243 /* get background color */
244 p = getenv("GRASS_RENDER_BACKGROUNDCOLOR");
245 if (p && *p) {
246 unsigned int red, green, blue;
247
248 if (sscanf(p, "%02x%02x%02x", &red, &green, &blue) == 3 ||
249 G_str_to_color(p, (int *)&red, (int *)&green, (int *)&blue) == 1) {
250 ca.bgcolor_r = CAIROCOLOR(red);
251 ca.bgcolor_g = CAIROCOLOR(green);
252 ca.bgcolor_b = CAIROCOLOR(blue);
253 }
254 else
255 G_fatal_error("Unknown background color: %s", p);
256 G_debug(1, "cairo: GRASS_RENDER_BACKGROUNDCOLOR=%s", p);
257 }
258 else
260
261 /* get background transparency setting */
262 p = getenv("GRASS_RENDER_TRANSPARENT");
263 if (p && strcmp(p, "TRUE") == 0)
264 ca.bgcolor_a = 0.0;
265 else
266 ca.bgcolor_a = 1.0;
267 G_debug(1, "cairo: GRASS_RENDER_TRANSPARENT=%s", p ? p : "FALSE");
268
269 antialias = CAIRO_ANTIALIAS_DEFAULT;
270 p = getenv("GRASS_RENDER_ANTIALIAS");
271 if (p && G_strcasecmp(p, "default") == 0)
272 antialias = CAIRO_ANTIALIAS_DEFAULT;
273 if (p && G_strcasecmp(p, "none") == 0)
274 antialias = CAIRO_ANTIALIAS_NONE;
275 if (p && G_strcasecmp(p, "gray") == 0)
276 antialias = CAIRO_ANTIALIAS_GRAY;
277 if (p && G_strcasecmp(p, "subpixel") == 0)
278 antialias = CAIRO_ANTIALIAS_SUBPIXEL;
279 G_debug(1, "cairo: GRASS_RENDER_ANTIALIAS=%s", p ? p : "FALSE");
280
281 init_file();
282
283 cairo_set_antialias(cairo, antialias);
284
285 return 0;
286}
287
288/*!
289 \brief Get render file
290
291 \return file name
292*/
293const char *Cairo_Graph_get_file(void)
294{
295 return ca.file_name;
296}
297
298/*!
299 \brief Close driver
300*/
302{
303 G_debug(1, "Cairo_Graph_close");
304
305#if CAIRO_HAS_XLIB_XRENDER_SURFACE
306 if (ca.file_type == FTYPE_X11) {
307 XFlush(cairo_xlib_surface_get_display(surface));
308 ca.mapped = 0;
309 }
310#endif
311
313
314 if (cairo) {
315 cairo_destroy(cairo);
316 cairo = NULL;
317 }
318 if (surface) {
319 cairo_surface_destroy(surface);
320 surface = NULL;
321 }
322
323#if CAIRO_HAS_XLIB_XRENDER_SURFACE
324 if (ca.file_type == FTYPE_X11)
325 fini_xlib();
326#endif
327}
328
329static void init_cairo(void)
330{
331 G_debug(1, "init_cairo");
332
333 /* create cairo surface */
334 switch (ca.file_type) {
335 case FTYPE_PPM:
336 case FTYPE_BMP:
337 case FTYPE_PNG:
338 surface =
339 (cairo_surface_t *) cairo_image_surface_create_for_data(
340 ca.grid, CAIRO_FORMAT_ARGB32, ca.width, ca.height, ca.stride);
341 break;
342#if CAIRO_HAS_PDF_SURFACE
343 case FTYPE_PDF:
344 surface =
345 (cairo_surface_t *) cairo_pdf_surface_create(
346 ca.file_name, (double) ca.width, (double) ca.height);
347 break;
348#endif
349#if CAIRO_HAS_PS_SURFACE
350 case FTYPE_PS:
351 surface =
352 (cairo_surface_t *) cairo_ps_surface_create(
353 ca.file_name, (double) ca.width, (double) ca.height);
354 break;
355#endif
356#if CAIRO_HAS_SVG_SURFACE
357 case FTYPE_SVG:
358 surface =
359 (cairo_surface_t *) cairo_svg_surface_create(
360 ca.file_name, (double) ca.width, (double) ca.height);
361 break;
362#endif
363#if CAIRO_HAS_XLIB_XRENDER_SURFACE
364 case FTYPE_X11:
365 surface =
366 (cairo_surface_t *) cairo_xlib_surface_create_with_xrender_format(
367 ca.dpy, ca.win, ca.screen, ca.format, ca.width, ca.height);
368 break;
369#endif
370 default:
371 G_fatal_error(_("Unknown Cairo surface type"));
372 break;
373 }
374
375 if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
376 G_fatal_error(_("Failed to initialize Cairo surface"
377 " (width: %d, height: %d): %s"),
378 ca.width, ca.height,
379 cairo_status_to_string(cairo_surface_status(surface)));
380
381 cairo = cairo_create(surface);
382}
383
384/* Returns TRUE if string ends with suffix (case insensitive) */
385static int ends_with(const char *string, const char *suffix)
386{
387 if (strlen(string) < strlen(suffix))
388 return FALSE;
389
390 return G_strcasecmp(suffix,
391 string + strlen(string) - strlen(suffix)) == 0;
392}
393
394static void map_file(void)
395{
396#ifndef __MINGW32__
397 size_t size = HEADER_SIZE + ca.width * ca.height * sizeof(unsigned int);
398 void *ptr;
399 int fd;
400
401 fd = open(ca.file_name, O_RDWR);
402 if (fd < 0)
403 return;
404
405 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) 0);
406 if (ptr == MAP_FAILED)
407 return;
408
409 if (ca.grid) {
410 cairo_destroy(cairo);
411 cairo_surface_destroy(surface);
412 G_free(ca.grid);
413 }
414 ca.grid = (unsigned char *) ptr + HEADER_SIZE;
415
416 close(fd);
417
418 ca.mapped = 1;
419#endif
420}
421
void G_free(void *buf)
Free allocated memory.
Definition: alloc.c:149
const char * Cairo_Graph_get_file(void)
Get render file.
cairo_surface_t * surface
struct cairo_state ca
int Cairo_Graph_set(void)
Initialize driver.
void Cairo_Graph_close(void)
Close driver.
cairo_t * cairo
GRASS cairo display driver - header file.
#define FTYPE_PS
Definition: cairodriver.h:57
void Cairo_Erase(void)
Erase screen.
#define FTYPE_PPM
Definition: cairodriver.h:53
#define FTYPE_PDF
Definition: cairodriver.h:56
#define HEADER_SIZE
Definition: cairodriver.h:45
#define FTYPE_X11
Definition: cairodriver.h:59
void cairo_read_xid(void)
Definition: read_xid.c:5
void cairo_read_image(void)
#define CAIROCOLOR(a)
Definition: cairodriver.h:49
void cairo_write_image(void)
#define FTYPE_PNG
Definition: cairodriver.h:55
#define FTYPE_SVG
Definition: cairodriver.h:58
#define FTYPE_BMP
Definition: cairodriver.h:54
#define DEFAULT_FILE_NAME
Definition: cairodriver.h:43
#define NULL
Definition: ccmath.h:32
int G_str_to_color(const char *str, int *red, int *grn, int *blu)
Parse color string and set red,green,blue.
Definition: color_str.c:112
#define FALSE
Definition: dbfopen.c:182
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition: debug.c:65
int screen_height
Definition: driver/init.c:30
int screen_width
Definition: driver/init.c:29
void G_verbose_message(const char *msg,...)
Print a message to stderr but only if module is in verbose mode.
Definition: gis/error.c:109
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160
int count
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:47
double bgcolor_r
Definition: cairodriver.h:69
unsigned char * grid
Definition: cairodriver.h:68
double bgcolor_a
Definition: cairodriver.h:69
double bgcolor_g
Definition: cairodriver.h:69
char * file_name
Definition: cairodriver.h:65
double bgcolor_b
Definition: cairodriver.h:69