GRASS GIS 8 Programmer's Manual 8.3.2(2024)-exported
Loading...
Searching...
No Matches
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
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
52#if CAIRO_HAS_XLIB_XRENDER_SURFACE
53static 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
104static void fini_xlib(void)
105{
106 XSetCloseDownMode(ca.dpy, RetainTemporary);
107 XCloseDisplay(ca.dpy);
108}
109#endif
110
111static 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)
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)
203 else
204 ca.win = 0;
205 init_xlib();
206 ca.mapped = 1;
207 }
208#endif
209
210 if (!ca.mapped && !is_vector)
211 ca.grid = G_malloc(ca.height * ca.stride);
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
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 */
294const 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
330static 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) */
382static 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
390static 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}
void G_free(void *buf)
Free allocated memory.
Definition alloc.c:150
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: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 *str, int *red, int *grn, int *blu)
Parse color string and set red,green,blue.
Definition color_str.c:101
#define FALSE
Definition dbfopen.c:74
int G_debug(int level, const char *msg,...)
Print debugging message.
Definition debug.c:66
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:108
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition gis/error.c:159
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: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