GDS-Render v1.2.1
cairo-renderer.c
Go to the documentation of this file.
1/*
2 * GDSII-Converter
3 * Copyright (C) 2018 Mario Hüttel <mario.huettel@gmx.net>
4 *
5 * This file is part of GDSII-Converter.
6 *
7 * GDSII-Converter is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * GDSII-Converter is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GDSII-Converter. If not, see <http://www.gnu.org/licenses/>.
18 */
29#include <math.h>
30#include <stdlib.h>
31#include <cairo.h>
32#include <cairo-pdf.h>
33#include <cairo-svg.h>
34#include <glib/gi18n.h>
35
37#include <sys/wait.h>
38#include <unistd.h>
39
41 GdsOutputRenderer parent;
42 gboolean svg;
43};
44
45G_DEFINE_TYPE(CairoRenderer, cairo_renderer, GDS_RENDER_TYPE_OUTPUT_RENDERER)
46
47
52 cairo_t *cr;
53 cairo_surface_t *rec;
54 struct layer_info *linfo;
55};
56
61static void revert_inherited_transform(struct cairo_layer *layers)
62{
63 int i;
64
65 for (i = 0; i < MAX_LAYERS; i++) {
66 if (layers[i].cr == NULL)
67 continue;
68 cairo_restore(layers[i].cr);
69 }
70}
71
82 const struct gds_point *origin,
83 double magnification,
84 gboolean flipping,
85 double rotation,
86 double scale)
87{
88 int i;
89 cairo_t *temp_layer_cr;
90
91 for (i = 0; i < MAX_LAYERS; i++) {
92 temp_layer_cr = layers[i].cr;
93 if (temp_layer_cr == NULL)
94 continue;
95
96 /* Save the state and apply transformation */
97 cairo_save(temp_layer_cr);
98 cairo_translate(temp_layer_cr, (double)origin->x/scale, (double)origin->y/scale);
99 cairo_rotate(temp_layer_cr, M_PI*rotation/180.0);
100 cairo_scale(temp_layer_cr, magnification,
101 (flipping == TRUE ? -magnification : magnification));
102 }
103}
104
111static void render_cell(struct gds_cell *cell, struct cairo_layer *layers, double scale)
112{
113 GList *instance_list;
114 struct gds_cell *temp_cell;
115 struct gds_cell_instance *cell_instance;
116 GList *gfx_list;
117 struct gds_graphics *gfx;
118 GList *vertex_list;
119 struct gds_point *vertex;
120 cairo_t *cr;
121
122 /* Render child cells */
123 for (instance_list = cell->child_cells; instance_list != NULL; instance_list = instance_list->next) {
124 cell_instance = (struct gds_cell_instance *)instance_list->data;
125 temp_cell = cell_instance->cell_ref;
126 if (temp_cell != NULL) {
128 &cell_instance->origin,
129 cell_instance->magnification,
130 cell_instance->flipped,
131 cell_instance->angle,
132 scale);
133 render_cell(temp_cell, layers, scale);
135 }
136 }
137
138 /* Render graphics */
139 for (gfx_list = cell->graphic_objs; gfx_list != NULL; gfx_list = gfx_list->next) {
140 gfx = (struct gds_graphics *)gfx_list->data;
141
142 /* Get layer renderer */
143 if (gfx->layer >= MAX_LAYERS)
144 continue;
145
146 cr = layers[gfx->layer].cr;
147 if (cr == NULL)
148 continue;
149
150 /* Apply settings */
151 cairo_set_line_width(cr, (gfx->width_absolute ? gfx->width_absolute/scale : 1));
152
153 switch (gfx->path_render_type) {
154 case PATH_FLUSH:
155 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
156 break;
157 case PATH_ROUNDED:
158 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
159 break;
160 case PATH_SQUARED:
161 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
162 break;
163 }
164
165 /* Add vertices */
166 for (vertex_list = gfx->vertices; vertex_list != NULL; vertex_list = vertex_list->next) {
167 vertex = (struct gds_point *)vertex_list->data;
168
169 /* If first point -> move to, else line to */
170 if (vertex_list->prev == NULL)
171 cairo_move_to(cr, vertex->x/scale, vertex->y/scale);
172 else
173 cairo_line_to(cr, vertex->x/scale, vertex->y/scale);
174 }
175
176 /* Create graphics object */
177 switch (gfx->gfx_type) {
178 case GRAPHIC_PATH:
179 cairo_stroke(cr);
180 break;
181 case GRAPHIC_BOX:
182 /* Expected fallthrough */
183 case GRAPHIC_POLYGON:
184 cairo_set_line_width(cr, 0.1/scale);
185 cairo_close_path(cr);
186 cairo_stroke_preserve(cr); // Prevent graphic glitches
187 cairo_fill(cr);
188 break;
189 }
190 } /* for gfx list */
191}
192
203static int read_line_from_fd(int fd, char *buff, size_t buff_size)
204{
205 ssize_t cnt;
206 char c;
207 unsigned int buff_cnt = 0;
208
209 while ((cnt = read(fd, &c, 1)) == 1) {
210 if (buff_cnt < (buff_size-1)) {
211 buff[buff_cnt++] = c;
212 if (c == '\n')
213 break;
214 } else {
215 break;
216 }
217 }
218
219 buff[buff_cnt] = 0;
220 return (int)buff_cnt;
221}
222
233static int cairo_renderer_render_cell_to_vector_file(GdsOutputRenderer *renderer,
234 struct gds_cell *cell,
235 GList *layer_infos,
236 const char *pdf_file,
237 const char *svg_file,
238 double scale)
239{
240 cairo_surface_t *pdf_surface = NULL, *svg_surface = NULL;
241 cairo_t *pdf_cr = NULL, *svg_cr = NULL;
242 struct layer_info *linfo;
243 struct cairo_layer *layers;
244 struct cairo_layer *lay;
245 GList *info_list;
246 int i;
247 double rec_x0, rec_y0, rec_width, rec_height;
248 double xmin = INT32_MAX, xmax = INT32_MIN, ymin = INT32_MAX, ymax = INT32_MIN;
249 pid_t process_id;
250 int comm_pipe[2];
251 char receive_message[200];
252
253 if (pdf_file == NULL && svg_file == NULL) {
254 /* No output specified */
255 return -1;
256 }
257
258 /* Generate communication pipe for status updates */
259 if (pipe(comm_pipe) == -1)
260 return -2;
261
262 /* Fork to a new child process. This ensures the memory leaks (see issue #16) in Cairo don't
263 * brick everything.
264 *
265 * And by the way: This now bricks all Windows compatibility. Deal with it.
266 */
267 process_id = fork();
268 //process_id = -1;
269 if (process_id < 0) {
270 /* This should not happen */
271 fprintf(stderr, _("Fatal error: Cairo Renderer: Could not spawn child process!"));
272 exit(-2);
273 } else if (process_id > 0) {
274 /* Woohoo... Successfully dumped the shitty code to an unknowing victim */
275 goto ret_parent;
276 }
277
278 /* We are now in a separate process just for rendering the output image.
279 * You may print a log message to the activity bar of the gui by writing a line
280 * teminated with '\n' to comm_pipe[1]. This will be handled by the parent process.
281 * Directly calling the update function
282 * gds_output_renderer_update_async_progress()
283 * does not have any effect because this is a separate process.
284 */
285
286 /*
287 * Close stdin and (stdout and stderr may live on)
288 */
289 close(0);
290 close(comm_pipe[0]);
291
292 layers = (struct cairo_layer *)calloc(MAX_LAYERS, sizeof(struct cairo_layer));
293
294 /* Clear layers */
295 for (i = 0; i < MAX_LAYERS; i++) {
296 layers[i].cr = NULL;
297 layers[i].rec = NULL;
298 }
299
300 /* Create recording surface for each layer */
301 for (info_list = layer_infos; info_list != NULL; info_list = g_list_next(info_list)) {
302 linfo = (struct layer_info *)info_list->data;
303 if (linfo->layer < MAX_LAYERS) {
304 /* Layer shall not be rendered */
305 if (!linfo->render)
306 continue;
307
308 lay = &(layers[(unsigned int)linfo->layer]);
309 lay->linfo = linfo;
310 lay->rec = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA,
311 NULL);
312 lay->cr = cairo_create(layers[(unsigned int)linfo->layer].rec);
313 cairo_scale(lay->cr, 1, -1); // Fix coordinate system
314 cairo_set_source_rgb(lay->cr, linfo->color.red, linfo->color.green, linfo->color.blue);
315 } else {
316 printf("Layer number (%d) too high!\n", linfo->layer);
317 goto ret_clear_layers;
318 }
319 }
320
321 dprintf(comm_pipe[1], "Rendering layers\n");
322 render_cell(cell, layers, scale);
323
324 /* get size of image and top left coordinate */
325 for (info_list = layer_infos; info_list != NULL; info_list = g_list_next(info_list)) {
326 linfo = (struct layer_info *)info_list->data;
327
328 if (linfo->layer >= MAX_LAYERS) {
329 printf(_("Layer number too high / outside of spec.\n"));
330 continue;
331 }
332
333 if (!linfo->render)
334 continue;
335
336 /* Print size */
337 cairo_recording_surface_ink_extents(layers[linfo->layer].rec, &rec_x0, &rec_y0,
338 &rec_width, &rec_height);
339 dprintf(comm_pipe[1], _("Size of layer %d%s%s%s: <%lf x %lf> @ (%lf | %lf)\n"),
340 linfo->layer,
341 (linfo->name && linfo->name[0] ? " (" : ""),
342 (linfo->name && linfo->name[0] ? linfo->name : ""),
343 (linfo->name && linfo->name[0] ? ")" : ""),
344 rec_width, rec_height, rec_x0, rec_y0);
345
346 /* update bounding box */
347 xmin = MIN(xmin, rec_x0);
348 xmax = MAX(xmax, rec_x0);
349 ymin = MIN(ymin, rec_y0);
350 ymax = MAX(ymax, rec_y0);
351 xmin = MIN(xmin, rec_x0+rec_width);
352 xmax = MAX(xmax, rec_x0+rec_width);
353 ymin = MIN(ymin, rec_y0+rec_height);
354 ymax = MAX(ymax, rec_y0+rec_height);
355
356 }
357
358 /* printf("Cell bounding box: (%lf | %lf) -- (%lf | %lf)\n", xmin, ymin, xmax, ymax); */
359
360 if (pdf_file) {
361 pdf_surface = cairo_pdf_surface_create(pdf_file, xmax-xmin, ymax-ymin);
362 pdf_cr = cairo_create(pdf_surface);
363 }
364
365 if (svg_file) {
366 svg_surface = cairo_svg_surface_create(svg_file, xmax-xmin, ymax-ymin);
367 svg_cr = cairo_create(svg_surface);
368 }
369
370 /* Write layers to PDF */
371 for (info_list = layer_infos; info_list != NULL; info_list = g_list_next(info_list)) {
372 linfo = (struct layer_info *)info_list->data;
373
374 if (linfo->layer >= MAX_LAYERS) {
375 printf(_("Layer outside of spec.\n"));
376 continue;
377 }
378
379 if (!linfo->render)
380 continue;
381
382 dprintf(comm_pipe[1], _("Exporting layer %d to file\n"), linfo->layer);
383
384 if (pdf_file && pdf_cr) {
385 cairo_set_source_surface(pdf_cr, layers[linfo->layer].rec, -xmin, -ymin);
386 cairo_paint_with_alpha(pdf_cr, linfo->color.alpha);
387 }
388
389 if (svg_file && svg_cr) {
390 cairo_set_source_surface(svg_cr, layers[linfo->layer].rec, -xmin, -ymin);
391 cairo_paint_with_alpha(svg_cr, linfo->color.alpha);
392 }
393 }
394
395 if (pdf_file) {
396 cairo_show_page(pdf_cr);
397 cairo_destroy(pdf_cr);
398 cairo_surface_destroy(pdf_surface);
399 }
400
401 if (svg_file) {
402 cairo_show_page(svg_cr);
403 cairo_destroy(svg_cr);
404 cairo_surface_destroy(svg_surface);
405 }
406
407ret_clear_layers:
408 for (i = 0; i < MAX_LAYERS; i++) {
409 lay = &layers[i];
410 if (lay->cr) {
411 cairo_destroy(lay->cr);
412 cairo_surface_destroy(lay->rec);
413 }
414 }
415 free(layers);
416
417 printf(_("Cairo export finished. It might still be buggy!\n"));
418
419 /* Suspend child process */
420 exit(0);
421
422ret_parent:
423 close(comm_pipe[1]);
424
425 while (read_line_from_fd(comm_pipe[0], receive_message, sizeof(receive_message)) > 0) {
426 /* Strip \n from string and replace with ' ' */
427 for (i = 0; receive_message[i] != '\0'; i++) {
428 if (receive_message[i] == '\n')
429 receive_message[i] = ' ';
430 }
431
432 /* Update asyc progress*/
433 gds_output_renderer_update_async_progress(renderer, receive_message);
434 }
435
436 waitpid(process_id, NULL, 0);
437
438 close(comm_pipe[0]);
439 return 0;
440}
441
442static void cairo_renderer_init(CairoRenderer *self)
443{
444 /* PDF default */
445 self->svg = FALSE;
446}
447
448static int cairo_renderer_render_output(GdsOutputRenderer *renderer,
449 struct gds_cell *cell,
450 double scale)
451{
452 CairoRenderer *c_renderer = GDS_RENDER_CAIRO_RENDERER(renderer);
453 const char *pdf_file = NULL;
454 const char *svg_file = NULL;
455 LayerSettings *settings;
456 GList *layer_infos = NULL;
457 const char *output_file;
458 int ret;
459
460 if (!c_renderer)
461 return -2000;
462
463 output_file = gds_output_renderer_get_output_file(renderer);
465
466 /* Set layer info list. In case of failure it remains NULL */
467 if (settings)
468 layer_infos = layer_settings_get_layer_info_list(settings);
469
470 if (c_renderer->svg == TRUE)
471 svg_file = output_file;
472 else
473 pdf_file = output_file;
474
475 gds_output_renderer_update_async_progress(renderer, _("Rendering Cairo Output..."));
476 ret = cairo_renderer_render_cell_to_vector_file(renderer, cell, layer_infos, pdf_file, svg_file, scale);
477
478 if (settings)
479 g_object_unref(settings);
480
481 return ret;
482}
483
484static void cairo_renderer_class_init(CairoRendererClass *klass)
485{
486 GdsOutputRendererClass *renderer_class = GDS_RENDER_OUTPUT_RENDERER_CLASS(klass);
487
488 renderer_class->render_output = cairo_renderer_render_output;
489}
490
492{
493 CairoRenderer *renderer;
494
495 renderer = GDS_RENDER_CAIRO_RENDERER(g_object_new(GDS_RENDER_TYPE_CAIRO_RENDERER, NULL));
496 renderer->svg = FALSE;
497
498 return renderer;
499}
500
502{
503 CairoRenderer *renderer;
504
505 renderer = GDS_RENDER_CAIRO_RENDERER(g_object_new(GDS_RENDER_TYPE_CAIRO_RENDERER, NULL));
506 renderer->svg = TRUE;
507
508 return renderer;
509}
510
Header File for Cairo output renderer.
static int cairo_renderer_render_output(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale)
static void render_cell(struct gds_cell *cell, struct cairo_layer *layers, double scale)
render_cell Render a cell with its sub-cells
static void revert_inherited_transform(struct cairo_layer *layers)
Revert the last transformation on all layers.
static int read_line_from_fd(int fd, char *buff, size_t buff_size)
Read a line from a file descriptor.
static void cairo_renderer_class_init(CairoRendererClass *klass)
#define GDS_RENDER_TYPE_CAIRO_RENDERER
CairoRenderer * cairo_renderer_new_pdf()
Create new CairoRenderer for PDF output.
static void apply_inherited_transform_to_all_layers(struct cairo_layer *layers, const struct gds_point *origin, double magnification, gboolean flipping, double rotation, double scale)
Applies transformation to all layers.
static void cairo_renderer_init(CairoRenderer *self)
#define MAX_LAYERS
Maximum layer count the output renderer can process. Typically GDS only specifies up to 255 layers.
static int cairo_renderer_render_cell_to_vector_file(GdsOutputRenderer *renderer, struct gds_cell *cell, GList *layer_infos, const char *pdf_file, const char *svg_file, double scale)
Render cell to a PDF file specified by pdf_file.
CairoRenderer * cairo_renderer_new_svg()
Create new CairoRenderer for SVG output.
@ GRAPHIC_POLYGON
An arbitrary polygon.
Definition: gds-types.h:51
@ GRAPHIC_PATH
Path. Esentially a line.
Definition: gds-types.h:50
@ GRAPHIC_BOX
A rectangle.
Definition: gds-types.h:52
@ PATH_SQUARED
Definition: gds-types.h:58
@ PATH_ROUNDED
Definition: gds-types.h:58
@ PATH_FLUSH
Definition: gds-types.h:58
const char * gds_output_renderer_get_output_file(GdsOutputRenderer *renderer)
Convenience function for getting the "output-file" property.
void gds_output_renderer_update_async_progress(GdsOutputRenderer *renderer, const char *status)
This function emits the 'progress-changed' in the thread/context that triggered an asynchronous rende...
LayerSettings * gds_output_renderer_get_and_ref_layer_settings(GdsOutputRenderer *renderer)
Get layer settings.
#define GDS_RENDER_TYPE_OUTPUT_RENDERER
#define MIN(a, b)
Return smaller number.
Definition: bounding-box.c:36
#define MAX(a, b)
Return bigger number.
Definition: bounding-box.c:37
GList * layer_settings_get_layer_info_list(LayerSettings *settings)
Get a GList with layer_info structs.
GdsOutputRenderer parent
gboolean svg
TRUE: SVG output, FALSE: PDF output.
The cairo_layer struct Each rendered layer is represented by this struct.
cairo_surface_t * rec
Recording surface to hold the layer.
struct layer_info * linfo
Reference to layer information.
cairo_t * cr
cairo context for layer
This represents an instanc of a cell inside another cell.
Definition: gds-types.h:110
int flipped
Mirrored on x-axis before rotation.
Definition: gds-types.h:114
double angle
Angle of rotation (counter clockwise) in degrees.
Definition: gds-types.h:115
double magnification
magnification
Definition: gds-types.h:116
struct gds_cell * cell_ref
Referenced gds_cell structure.
Definition: gds-types.h:112
struct gds_point origin
Origin.
Definition: gds-types.h:113
A Cell inside a gds_library.
Definition: gds-types.h:122
GList * child_cells
List of gds_cell_instance elements.
Definition: gds-types.h:126
GList * graphic_objs
List of gds_graphics.
Definition: gds-types.h:127
A GDS graphics object.
Definition: gds-types.h:98
enum graphics_type gfx_type
Type of graphic.
Definition: gds-types.h:99
enum path_type path_render_type
Line cap.
Definition: gds-types.h:101
int width_absolute
Width. Not used for objects other than paths.
Definition: gds-types.h:102
int16_t layer
Layer the graphic object is on.
Definition: gds-types.h:103
GList * vertices
List of gds_point.
Definition: gds-types.h:100
A point in the 2D plane. Sometimes referred to as vertex.
Definition: gds-types.h:63
Layer information.
int render
true: Render to output
char * name
Layer name.
GdkRGBA color
RGBA color used to render this layer.
int layer
Layer number.