GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-112dd97adf
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 contributor)
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 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
53 static void init_xlib(void)
54 {
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, "%lu", &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 =
84  XGetVisualInfo(ca.dpy, VisualIDMask | VisualScreenMask, &templ, &count);
85  if (!vinfo || !count)
86  G_fatal_error(_("Unable to obtain visual"));
87  visual = vinfo[0].visual;
88 
89  ca.screen = ScreenOfDisplay(ca.dpy, scrn);
90  pix = XCreatePixmap(ca.dpy, RootWindow(ca.dpy, scrn), 1, 1, vinfo[0].depth);
91  s1 = cairo_xlib_surface_create(ca.dpy, pix, visual, 1, 1);
92  s2 = cairo_surface_create_similar(s1, CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
93  ca.format = cairo_xlib_surface_get_xrender_format(s2);
94  ca.depth = cairo_xlib_surface_get_depth(s2);
95  cairo_surface_destroy(s2);
96  cairo_surface_destroy(s1);
97  XFreePixmap(ca.dpy, pix);
98 
99  if (!ca.win)
100  ca.win = XCreatePixmap(ca.dpy, RootWindow(ca.dpy, scrn), ca.width,
101  ca.height, ca.depth);
102 }
103 
104 static void fini_xlib(void)
105 {
106  XSetCloseDownMode(ca.dpy, RetainTemporary);
107  XCloseDisplay(ca.dpy);
108 }
109 #endif
110 
111 static void init_file(void)
112 {
113  int is_vector = 0;
114 
115 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
116  int is_xlib = 0;
117 #endif
118  int do_read = 0;
119  int do_map = 0;
120  char *p;
121 
122  /* set image properties */
125  ca.stride = ca.width * 4;
126 
127  /* get file name */
128  p = getenv("GRASS_RENDER_FILE");
129  if (!p || strlen(p) == 0)
130  p = DEFAULT_FILE_NAME;
131  G_debug(1, "cairo: GRASS_RENDER_FILE=%s", p);
132 
133  ca.file_name = p;
134 
135  /* get file type (from extension) */
136  if (ends_with(ca.file_name, ".ppm"))
138  else if (ends_with(ca.file_name, ".bmp"))
140 #if CAIRO_HAS_PNG_FUNCTIONS
141  else if (ends_with(ca.file_name, ".png"))
143 #endif
144 #if CAIRO_HAS_PDF_SURFACE
145  else if (ends_with(ca.file_name, ".pdf"))
147 #endif
148 #if CAIRO_HAS_PS_SURFACE
149  else if (ends_with(ca.file_name, ".ps"))
151 #endif
152 #if CAIRO_HAS_SVG_SURFACE
153  else if (ends_with(ca.file_name, ".svg"))
155 #endif
156 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
157  else if (ends_with(ca.file_name, ".xid"))
159 #endif
160  else
161  G_fatal_error(_("Unknown file extension: %s"), p);
162  G_debug(1, "cairo: file type=%d", ca.file_type);
163 
164  switch (ca.file_type) {
165  case FTYPE_PDF:
166  case FTYPE_PS:
167  case FTYPE_SVG:
168  is_vector = 1;
169  break;
170 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
171  case FTYPE_X11:
172  is_xlib = 1;
173  break;
174 #endif
175  }
176 
177  p = getenv("GRASS_RENDER_FILE_MAPPED");
178  do_map = p && strcmp(p, "TRUE") == 0 && ends_with(ca.file_name, ".bmp");
179  G_debug(1, "cairo: GRASS_RENDER_FILE_MAPPED=%d", do_map);
180 
181  p = getenv("GRASS_RENDER_FILE_READ");
182  do_read = p && strcmp(p, "TRUE") == 0;
183  G_debug(1, "cairo: GRASS_RENDER_FILE_READ=%d", do_read);
184 
185  if (is_vector) {
186  do_read = do_map = 0;
187  ca.bgcolor_a = 1.0;
188  }
189 
190  if (do_read && access(ca.file_name, 0) != 0)
191  do_read = 0;
192 
193  G_verbose_message(_("cairo: collecting to file '%s'"), ca.file_name);
194  G_verbose_message(_("cairo: image size %dx%d"), ca.width, ca.height);
195 
196  if (do_read && do_map)
197  map_file();
198 
199 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
200  if (is_xlib) {
201  if (do_read)
202  cairo_read_xid();
203  else
204  ca.win = 0;
205  init_xlib();
206  ca.mapped = 1;
207  }
208 #endif
209 
210  if (!ca.mapped && !is_vector)
212 
213  init_cairo();
214 
215  if (!do_read && !is_vector) {
216  Cairo_Erase();
217  ca.modified = 1;
218  }
219 
220  if (do_read && !ca.mapped)
222 
223  if (do_map && !ca.mapped) {
225  map_file();
226  init_cairo();
227  }
228 }
229 
230 /*!
231  \brief Initialize driver
232 
233  Set background color, transparency, drawable, antialias mode, etc.
234 
235  \return 0
236  */
238 {
239  cairo_antialias_t antialias;
240  char *p;
241 
242  G_gisinit("Cairo driver");
243 
244  /* get background color */
245  p = getenv("GRASS_RENDER_BACKGROUNDCOLOR");
246  if (p && *p) {
247  unsigned int red, green, blue;
248 
249  if (sscanf(p, "%02x%02x%02x", &red, &green, &blue) == 3 ||
250  G_str_to_color(p, (int *)&red, (int *)&green, (int *)&blue) == 1) {
251  ca.bgcolor_r = CAIROCOLOR(red);
252  ca.bgcolor_g = CAIROCOLOR(green);
253  ca.bgcolor_b = CAIROCOLOR(blue);
254  }
255  else
256  G_fatal_error("Unknown background color: %s", p);
257  G_debug(1, "cairo: GRASS_RENDER_BACKGROUNDCOLOR=%s", p);
258  }
259  else
260  ca.bgcolor_r = ca.bgcolor_g = ca.bgcolor_b = 1.0;
261 
262  /* get background transparency setting */
263  p = getenv("GRASS_RENDER_TRANSPARENT");
264  if (p && strcmp(p, "TRUE") == 0)
265  ca.bgcolor_a = 0.0;
266  else
267  ca.bgcolor_a = 1.0;
268  G_debug(1, "cairo: GRASS_RENDER_TRANSPARENT=%s", p ? p : "FALSE");
269 
270  antialias = CAIRO_ANTIALIAS_DEFAULT;
271  p = getenv("GRASS_RENDER_ANTIALIAS");
272  if (p && G_strcasecmp(p, "default") == 0)
273  antialias = CAIRO_ANTIALIAS_DEFAULT;
274  if (p && G_strcasecmp(p, "none") == 0)
275  antialias = CAIRO_ANTIALIAS_NONE;
276  if (p && G_strcasecmp(p, "gray") == 0)
277  antialias = CAIRO_ANTIALIAS_GRAY;
278  if (p && G_strcasecmp(p, "subpixel") == 0)
279  antialias = CAIRO_ANTIALIAS_SUBPIXEL;
280  G_debug(1, "cairo: GRASS_RENDER_ANTIALIAS=%s", p ? p : "FALSE");
281 
282  init_file();
283 
284  cairo_set_antialias(cairo, antialias);
285 
286  return 0;
287 }
288 
289 /*!
290  \brief Get render file
291 
292  \return file name
293  */
294 const char *Cairo_Graph_get_file(void)
295 {
296  return ca.file_name;
297 }
298 
299 /*!
300  \brief Close driver
301  */
303 {
304  G_debug(1, "Cairo_Graph_close");
305 
306 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
307  if (ca.file_type == FTYPE_X11) {
308  XFlush(cairo_xlib_surface_get_display(surface));
309  ca.mapped = 0;
310  }
311 #endif
312 
314 
315  if (cairo) {
316  cairo_destroy(cairo);
317  cairo = NULL;
318  }
319  if (surface) {
320  cairo_surface_destroy(surface);
321  surface = NULL;
322  }
323 
324 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
325  if (ca.file_type == FTYPE_X11)
326  fini_xlib();
327 #endif
328 }
329 
330 static void init_cairo(void)
331 {
332  G_debug(1, "init_cairo");
333 
334  /* create cairo surface */
335  switch (ca.file_type) {
336  case FTYPE_PPM:
337  case FTYPE_BMP:
338  case FTYPE_PNG:
339  surface = (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 = (cairo_surface_t *)cairo_pdf_surface_create(
345  ca.file_name, (double)ca.width, (double)ca.height);
346  break;
347 #endif
348 #if CAIRO_HAS_PS_SURFACE
349  case FTYPE_PS:
350  surface = (cairo_surface_t *)cairo_ps_surface_create(
351  ca.file_name, (double)ca.width, (double)ca.height);
352  break;
353 #endif
354 #if CAIRO_HAS_SVG_SURFACE
355  case FTYPE_SVG:
356  surface = (cairo_surface_t *)cairo_svg_surface_create(
357  ca.file_name, (double)ca.width, (double)ca.height);
358  break;
359 #endif
360 #if CAIRO_HAS_XLIB_XRENDER_SURFACE
361  case FTYPE_X11:
362  surface =
363  (cairo_surface_t *)cairo_xlib_surface_create_with_xrender_format(
364  ca.dpy, ca.win, ca.screen, ca.format, ca.width, ca.height);
365  break;
366 #endif
367  default:
368  G_fatal_error(_("Unknown Cairo surface type"));
369  break;
370  }
371 
372  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
373  G_fatal_error(_("Failed to initialize Cairo surface"
374  " (width: %d, height: %d): %s"),
375  ca.width, ca.height,
376  cairo_status_to_string(cairo_surface_status(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, string + strlen(string) - strlen(suffix)) == 0;
388 }
389 
390 static void map_file(void)
391 {
392 #ifndef __MINGW32__
393  size_t size = HEADER_SIZE + ca.width * ca.height * sizeof(unsigned int);
394  void *ptr;
395  int fd;
396 
397  fd = open(ca.file_name, O_RDWR);
398  if (fd < 0)
399  return;
400 
401  ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)0);
402  if (ptr == MAP_FAILED)
403  return;
404 
405  if (ca.grid) {
406  cairo_destroy(cairo);
407  cairo_surface_destroy(surface);
408  G_free(ca.grid);
409  }
410  ca.grid = (unsigned char *)ptr + HEADER_SIZE;
411 
412  close(fd);
413 
414  ca.mapped = 1;
415 #endif
416 }
cairo_surface_t * surface
struct cairo_state ca
const char * Cairo_Graph_get_file(void)
Get render file.
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:58
void Cairo_Erase(void)
Erase screen.
#define FTYPE_PPM
Definition: cairodriver.h:54
#define FTYPE_PDF
Definition: cairodriver.h:57
#define HEADER_SIZE
Definition: cairodriver.h:46
#define FTYPE_X11
Definition: cairodriver.h:60
void cairo_read_xid(void)
Definition: read_xid.c:5
void cairo_read_image(void)
#define CAIROCOLOR(a)
Definition: cairodriver.h:50
void cairo_write_image(void)
#define FTYPE_PNG
Definition: cairodriver.h:56
#define FTYPE_SVG
Definition: cairodriver.h:59
#define FTYPE_BMP
Definition: cairodriver.h:55
#define DEFAULT_FILE_NAME
Definition: cairodriver.h:44
#define NULL
Definition: ccmath.h:32
int G_str_to_color(const char *, int *, int *, int *)
Parse color string and set red,green,blue.
Definition: color_str.c:101
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition: defs/gis.h:94
void void G_verbose_message(const char *,...) __attribute__((format(printf
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
int G_debug(int, const char *,...) __attribute__((format(printf
int screen_height
Definition: driver/init.c:30
int screen_width
Definition: driver/init.c:29
#define G_gisinit(pgm)
Definition: gis.h:72
#define FALSE
Definition: gis.h:83
#define _(str)
Definition: glocale.h:10
int count
double bgcolor_r
Definition: cairodriver.h:70
unsigned char * grid
Definition: cairodriver.h:69
double bgcolor_a
Definition: cairodriver.h:70
double bgcolor_g
Definition: cairodriver.h:70
char * file_name
Definition: cairodriver.h:66
double bgcolor_b
Definition: cairodriver.h:70