GRASS GIS 7 Programmer's Manual  7.9.dev(2020)-547ded6c0
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 
42 struct cairo_state ca;
43 
44 /* cairo objects */
45 cairo_surface_t *surface;
46 cairo_t *cairo;
47 
48 static void init_cairo(void);
49 static int ends_with(const char *string, const char *suffix);
50 static void map_file(void);
51 
52 static 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 
105 static 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 
113 static 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)
129  p = DEFAULT_FILE_NAME;
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)
201  cairo_read_xid();
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
259  ca.bgcolor_r = ca.bgcolor_g = ca.bgcolor_b = 1.0;
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 */
293 const 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 
329 static 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 
378  cairo = cairo_create(surface);
379 }
380 
381 /* Returns TRUE if string ends with suffix (case insensitive) */
382 static int ends_with(const char *string, const char *suffix)
383 {
384  if (strlen(string) < strlen(suffix))
385  return FALSE;
386 
387  return G_strcasecmp(suffix,
388  string + strlen(string) - strlen(suffix)) == 0;
389 }
390 
391 static void map_file(void)
392 {
393 #ifndef __MINGW32__
394  size_t size = HEADER_SIZE + ca.width * ca.height * sizeof(unsigned int);
395  void *ptr;
396  int fd;
397 
398  fd = open(ca.file_name, O_RDWR);
399  if (fd < 0)
400  return;
401 
402  ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) 0);
403  if (ptr == MAP_FAILED)
404  return;
405 
406  if (ca.grid) {
407  cairo_destroy(cairo);
408  cairo_surface_destroy(surface);
409  G_free(ca.grid);
410  }
411  ca.grid = (unsigned char *) ptr + HEADER_SIZE;
412 
413  close(fd);
414 
415  ca.mapped = 1;
416 #endif
417 }
418 
void cairo_read_xid(void)
Definition: read_xid.c:5
double bgcolor_a
Definition: cairodriver.h:69
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:46
#define FTYPE_BMP
Definition: cairodriver.h:54
void G_free(void *buf)
Free allocated memory.
Definition: gis/alloc.c:149
#define HEADER_SIZE
Definition: cairodriver.h:45
void Cairo_Graph_close(void)
Close driver.
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
#define CAIROCOLOR(a)
Definition: cairodriver.h:49
int screen_width
Definition: driver/init.c:29
int count
int screen_height
Definition: driver/init.c:30
#define FTYPE_PS
Definition: cairodriver.h:57
#define FTYPE_PPM
Definition: cairodriver.h:53
#define FTYPE_X11
Definition: cairodriver.h:59
#define NULL
Definition: ccmath.h:32
char * file_name
Definition: cairodriver.h:65
const char * Cairo_Graph_get_file(void)
Get render file.
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:160
cairo_surface_t * surface
cairo_t * cairo
double bgcolor_r
Definition: cairodriver.h:69
struct cairo_state ca
#define FALSE
Definition: gis.h:60
double bgcolor_b
Definition: cairodriver.h:69
int Cairo_Graph_set(void)
Initialize driver.
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition: debug.c:65
#define FTYPE_PNG
Definition: cairodriver.h:55
#define G_gisinit(pgm)
Definition: gis.h:51
GRASS cairo display driver - header file.
unsigned char * grid
Definition: cairodriver.h:68
void cairo_write_image(void)
void cairo_read_image(void)
#define FTYPE_SVG
Definition: cairodriver.h:58
#define _(str)
Definition: glocale.h:13
#define DEFAULT_FILE_NAME
Definition: cairodriver.h:43
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
void Cairo_Erase(void)
Erase screen.
#define FTYPE_PDF
Definition: cairodriver.h:56
double bgcolor_g
Definition: cairodriver.h:69
char * getenv()