* pre-calculate a bounding box for every object during file parsing, and use the...
[geda-gerbv/spe.git] / src / gerbv.c
blobc0106e25b171e5735dbc8139fa81568f739601bf
1 /*
2 * gEDA - GNU Electronic Design Automation
3 * This file is a part of gerbv.
5 * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
7 * $Id$
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
25 /** \file gerbv.c
26 \brief This file contains high-level functions for the libgerbv library
27 \ingroup libgerbv
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
34 #include <assert.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <math.h>
38 #ifdef HAVE_LIBGEN_H
39 #include <libgen.h> /* dirname */
40 #endif
41 #include <errno.h>
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
51 #include <glib.h>
52 #include <gtk/gtk.h>
53 #include <gdk/gdk.h>
54 #include <gdk/gdkkeysyms.h>
56 #ifdef HAVE_GETOPT_H
57 #include <getopt.h>
58 #endif
60 #include <pango/pango.h>
62 #include <locale.h>
64 #include "gerbv.h"
65 #include "gerber.h"
66 #include "drill.h"
68 #ifdef RENDER_USING_GDK
69 #include "draw-gdk.h"
70 #else
71 #include "draw-gdk.h"
72 #include "draw.h"
73 #endif
75 #include "pick-and-place.h"
77 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
78 #define dprintf if(DEBUG) printf
80 #define NUMBER_OF_DEFAULT_COLORS 18
81 #define NUMBER_OF_DEFAULT_TRANSFORMATIONS 20
83 /* ------------------------------------------------------------------ */
84 static gerbv_layer_color defaultColors[NUMBER_OF_DEFAULT_COLORS] = {
85 {115,115,222,177},
86 {255,127,115,177},
87 {193,0,224,177},
88 {117,242,103,177},
89 {0,195,195,177},
90 {213,253,51,177},
91 {209,27,104,177},
92 {255,197,51,177},
93 {186,186,186,177},
94 {211,211,255,177},
95 {253,210,206,177},
96 {236,194,242,177},
97 {208,249,204,177},
98 {183,255,255,177},
99 {241,255,183,177},
100 {255,202,225,177},
101 {253,238,197,177},
102 {226,226,226,177}
105 /* ------------------------------------------------------------------ */
106 static gerbv_user_transformation_t defaultTransformations[NUMBER_OF_DEFAULT_TRANSFORMATIONS] = {
107 {0,0,0,0,FALSE},
108 {0,0,0,0,FALSE},
109 {0,0,0,0,FALSE},
110 {0,0,0,0,FALSE},
111 {0,0,0,0,FALSE},
112 {0,0,0,0,FALSE},
113 {0,0,0,0,FALSE},
114 {0,0,0,0,FALSE},
115 {0,0,0,0,FALSE},
116 {0,0,0,0,FALSE},
117 {0,0,0,0,FALSE},
118 {0,0,0,0,FALSE},
119 {0,0,0,0,FALSE},
120 {0,0,0,0,FALSE},
121 {0,0,0,0,FALSE},
122 {0,0,0,0,FALSE},
123 {0,0,0,0,FALSE},
124 {0,0,0,0,FALSE},
125 {0,0,0,0,FALSE},
126 {0,0,0,0,FALSE},
129 /* ------------------------------------------------------------------ */
130 gerbv_project_t *
131 gerbv_create_project (void) {
132 gerbv_project_t *returnProject= (gerbv_project_t *) g_new0(gerbv_project_t,1);
134 /* default to using the current directory path for our starting guesses
135 on future file loads */
136 returnProject->path = g_get_current_dir ();
137 /* Will be updated to 0 when first Gerber is loaded */
138 returnProject->last_loaded = -1;
139 returnProject->max_files = 1;
140 returnProject->check_before_delete = TRUE;
141 returnProject->file = g_new0 (gerbv_fileinfo_t *, returnProject->max_files);
143 return returnProject;
146 /* ------------------------------------------------------------------ */
147 void
148 gerbv_destroy_project (gerbv_project_t *gerbvProject){
149 int i;
151 /* destroy all the files attached to the project */
152 for(i = gerbvProject->last_loaded; i >= 0; i--) {
153 if (gerbvProject->file[i])
154 gerbv_destroy_fileinfo (gerbvProject->file[i]);
156 /* destroy strings */
157 g_free (gerbvProject->path);
158 g_free (gerbvProject->execname);
159 g_free (gerbvProject->execpath);
160 g_free (gerbvProject->project);
161 /* destroy the fileinfo array */
162 g_free (gerbvProject->file);
163 g_free (gerbvProject);
166 /* ------------------------------------------------------------------ */
167 void
168 gerbv_destroy_fileinfo (gerbv_fileinfo_t *fileInfo){
169 gerbv_destroy_image (fileInfo->image);
170 g_free (fileInfo->fullPathname);
171 g_free (fileInfo->name);
172 #ifndef RENDER_USING_GDK
173 if (fileInfo->privateRenderData) {
174 cairo_surface_destroy ((cairo_surface_t *)
175 fileInfo->privateRenderData);
177 #endif
180 /* ------------------------------------------------------------------ */
181 void
182 gerbv_open_layer_from_filename(gerbv_project_t *gerbvProject, gchar *filename)
184 gint idx_loaded;
185 dprintf("Opening filename = %s\n", (gchar *) filename);
187 if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
188 GERB_MESSAGE("could not read %s[%d]", (gchar *) filename,
189 gerbvProject->last_loaded);
190 gerbvProject->last_loaded--;
191 } else {
192 idx_loaded = gerbvProject->last_loaded;
193 gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
194 dprintf(" Successfully opened file!\n");
196 } /* gerbv_open_layer_from_filename */
198 /* ------------------------------------------------------------------ */
199 void
200 gerbv_open_layer_from_filename_with_color(gerbv_project_t *gerbvProject, gchar *filename,
201 guint16 red, guint16 green, guint16 blue, guint16 alpha)
203 gint idx_loaded;
204 dprintf("Opening filename = %s\n", (gchar *) filename);
206 if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
207 GERB_MESSAGE("could not read %s[%d]", (gchar *) filename,
208 gerbvProject->last_loaded);
209 gerbvProject->last_loaded--;
210 } else {
211 idx_loaded = gerbvProject->last_loaded;
212 gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
213 GdkColor colorTemplate = {0, red, green, blue};
214 gerbvProject->file[idx_loaded]->color = colorTemplate;
215 gerbvProject->file[idx_loaded]->alpha = alpha;
216 dprintf(" Successfully opened file!\n");
218 } /* gerbv_open_layer_from_filename_with_color */
220 /* ------------------------------------------------------------------ */
221 gboolean
222 gerbv_save_layer_from_index(gerbv_project_t *gerbvProject, gint index, gchar *filename)
224 if (strcmp (gerbvProject->file[index]->image->info->type,"RS274-X (Gerber) File")==0) {
225 gerbv_export_rs274x_file_from_image (filename, gerbvProject->file[index]->image);
227 else if (strcmp (gerbvProject->file[index]->image->info->type,"Excellon Drill File")==0) {
228 gerbv_export_drill_file_from_image (filename, gerbvProject->file[index]->image);
230 else {
231 return FALSE;
233 gerbvProject->file[index]->layer_dirty = FALSE;
234 return TRUE;
235 } /* gerbv_save_project_from_filename */
238 /* ------------------------------------------------------------------ */
240 gerbv_revert_file(gerbv_project_t *gerbvProject, int idx){
241 int rv;
243 rv = gerbv_open_image(gerbvProject, gerbvProject->file[idx]->fullPathname, idx, TRUE, NULL, 0, TRUE);
244 gerbvProject->file[idx]->layer_dirty = FALSE;
245 return rv;
248 /* ------------------------------------------------------------------ */
249 void
250 gerbv_revert_all_files(gerbv_project_t *gerbvProject)
252 int idx;
254 for (idx = 0; idx <= gerbvProject->last_loaded; idx++) {
255 if (gerbvProject->file[idx] && gerbvProject->file[idx]->fullPathname) {
256 (void) gerbv_revert_file (gerbvProject, idx);
257 gerbvProject->file[idx]->layer_dirty = FALSE;
260 } /* gerbv_revert_all_files */
262 /* ------------------------------------------------------------------ */
263 void
264 gerbv_unload_layer(gerbv_project_t *gerbvProject, int index)
266 gint i;
268 gerbv_destroy_fileinfo (gerbvProject->file[index]);
270 /* slide all later layers down to fill the empty slot */
271 for (i=index; i<(gerbvProject->last_loaded); i++) {
272 gerbvProject->file[i]=gerbvProject->file[i+1];
274 /* make sure the final spot is clear */
275 gerbvProject->file[gerbvProject->last_loaded] = NULL;
276 gerbvProject->last_loaded--;
277 } /* gerbv_unload_layer */
279 /* ------------------------------------------------------------------ */
280 void
281 gerbv_unload_all_layers (gerbv_project_t *gerbvProject)
283 int index;
285 /* Must count down since gerbv_unload_layer collapses
286 * layers down. Otherwise, layers slide past the index */
287 for (index = gerbvProject->last_loaded ; index >= 0; index--) {
288 if (gerbvProject->file[index] && gerbvProject->file[index]->name) {
289 gerbv_unload_layer (gerbvProject, index);
292 } /* gerbv_unload_all_layers */
295 /* ------------------------------------------------------------------ */
296 void
297 gerbv_change_layer_order(gerbv_project_t *gerbvProject, gint oldPosition, gint newPosition)
299 gerbv_fileinfo_t *temp_file;
300 int index;
302 temp_file = gerbvProject->file[oldPosition];
304 if (oldPosition < newPosition){
305 for (index = oldPosition; index < newPosition; index++) {
306 gerbvProject->file[index] = gerbvProject->file[index + 1];
308 } else {
309 for (index = oldPosition; index > newPosition; index--) {
310 gerbvProject->file[index] = gerbvProject->file[index - 1];
313 gerbvProject->file[newPosition] = temp_file;
314 } /* gerbv_change_layer_order */
317 /* ------------------------------------------------------------------ */
318 gint
319 gerbv_add_parsed_image_to_project (gerbv_project_t *gerbvProject, gerbv_image_t *parsed_image,
320 gchar *filename, gchar *baseName, int idx, int reload){
321 gerb_verify_error_t error = GERB_IMAGE_OK;
322 int r, g, b;
324 dprintf("In open_image, now error check file....\n");
325 error = gerbv_image_verify(parsed_image);
326 if (error) {
327 GERB_COMPILE_ERROR("%s: Parse error:\n", filename);
328 if (error & GERB_IMAGE_MISSING_NETLIST)
329 GERB_COMPILE_ERROR("* Missing netlist\n");
330 if (error & GERB_IMAGE_MISSING_FORMAT)
331 GERB_COMPILE_ERROR("* Missing format\n");
332 if (error & GERB_IMAGE_MISSING_APERTURES)
333 GERB_COMPILE_ERROR("* Missing apertures/drill sizes\n");
334 if (error & GERB_IMAGE_MISSING_INFO)
335 GERB_COMPILE_ERROR("* Missing info\n");
336 GERB_COMPILE_ERROR("\n");
337 GERB_COMPILE_ERROR("You probably tried to read an RS-274D file, which gerbv doesn't support\n");
338 gerbv_destroy_image(parsed_image);
339 return -1;
342 /* Used to debug parser */
343 //if (gerbvProject->dump_parsed_image)
344 //gerb_image_dump(parsed_image);
347 * If reload, just exchange the image. Else we have to allocate
348 * a new memory before we define anything more.
350 if (reload) {
351 gerbv_destroy_image(gerbvProject->file[idx]->image);
352 gerbvProject->file[idx]->image = parsed_image;
353 return 0;
354 } else {
355 /* Load new file. */
356 gerbvProject->file[idx] = (gerbv_fileinfo_t *) g_new0 (gerbv_fileinfo_t, 1);
357 gerbvProject->file[idx]->image = parsed_image;
361 * Store filename for eventual reload
363 gerbvProject->file[idx]->fullPathname = g_strdup (filename);
364 gerbvProject->file[idx]->name = g_strdup (baseName);
368 r = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].red*257;
369 g = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].green*257;
370 b = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].blue*257;
373 GdkColor colorTemplate = {0, r, g, b};
374 gerbvProject->file[idx]->color = colorTemplate;
375 gerbvProject->file[idx]->alpha = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].alpha*257;
376 gerbvProject->file[idx]->isVisible = TRUE;
377 gerbvProject->file[idx]->transform = defaultTransformations[idx % NUMBER_OF_DEFAULT_TRANSFORMATIONS];
378 /* update the number of files if we need to */
379 if (gerbvProject->last_loaded <= idx) {
380 gerbvProject->last_loaded = idx;
382 return 1;
385 /* ------------------------------------------------------------------ */
387 gerbv_open_image(gerbv_project_t *gerbvProject, char *filename, int idx, int reload,
388 gerbv_HID_Attribute *fattr, int n_fattr, gboolean forceLoadFile)
390 gerb_file_t *fd;
391 gerbv_image_t *parsed_image = NULL, *parsed_image2 = NULL;
392 gint retv = -1;
393 gboolean isPnpFile = FALSE, foundBinary;
394 gerbv_HID_Attribute *attr_list = NULL;
395 int n_attr = 0;
396 /* If we're reloading, we'll pass in our file format attribute list
397 * since this is our hook for letting the user override the fileformat.
399 if (reload)
401 /* We're reloading so use the attribute list in memory */
402 attr_list = gerbvProject->file[idx]->image->info->attr_list;
403 n_attr = gerbvProject->file[idx]->image->info->n_attr;
405 else
407 /* We're not reloading so use the attribute list read from the
408 * project file if given or NULL otherwise.
410 attr_list = fattr;
411 n_attr = n_fattr;
413 /* if we don't have enough spots, then grow the file list by 2 to account for the possible
414 loading of two images for PNP files */
415 if ((idx+1) >= gerbvProject->max_files) {
416 gerbvProject->file = g_renew (gerbv_fileinfo_t *,
417 gerbvProject->file, gerbvProject->max_files + 2);
419 gerbvProject->file[gerbvProject->max_files] = NULL;
420 gerbvProject->file[gerbvProject->max_files+1] = NULL;
421 gerbvProject->max_files += 2;
424 dprintf("In open_image, about to try opening filename = %s\n", filename);
426 fd = gerb_fopen(filename);
427 if (fd == NULL) {
428 GERB_MESSAGE("Trying to open %s:%s\n", filename, strerror(errno));
429 return -1;
432 /* Store filename info fd for further use */
433 fd->filename = g_strdup(filename);
435 dprintf("In open_image, successfully opened file. Now check its type....\n");
436 /* Here's where we decide what file type we have */
437 /* Note: if the file has some invalid characters in it but still appears to
438 be a valid file, we check with the user if he wants to continue (only
439 if user opens the layer from the menu...if from the command line, we go
440 ahead and try to load it anyways) */
442 if (gerber_is_rs274x_p(fd, &foundBinary)) {
443 dprintf("Found RS-274X file\n");
444 if ((!foundBinary || forceLoadFile)) {
445 /* figure out the directory path in case parse_gerb needs to
446 * load any include files */
447 gchar *currentLoadDirectory = g_path_get_dirname (filename);
448 parsed_image = parse_gerb(fd, currentLoadDirectory);
449 g_free (currentLoadDirectory);
451 } else if(drill_file_p(fd, &foundBinary)) {
452 dprintf("Found drill file\n");
453 if ((!foundBinary || forceLoadFile))
454 parsed_image = parse_drillfile(fd, attr_list, n_attr, reload);
456 } else if (pick_and_place_check_file_type(fd, &foundBinary)) {
457 dprintf("Found pick-n-place file\n");
458 if ((!foundBinary || forceLoadFile)) {
459 pick_and_place_parse_file_to_images(fd, &parsed_image, &parsed_image2);
460 isPnpFile = TRUE;
462 } else if (gerber_is_rs274d_p(fd)) {
463 dprintf("Found RS-274D file");
464 GERB_COMPILE_ERROR("%s: Found RS-274D file -- not supported by gerbv.\n", filename);
465 parsed_image = NULL;
467 } else {
468 /* This is not a known file */
469 dprintf("Unknown filetype");
470 GERB_COMPILE_ERROR("%s: Unknown file type.\n", filename);
471 parsed_image = NULL;
474 gerb_fclose(fd);
475 if (parsed_image == NULL) {
476 return -1;
479 if (parsed_image) {
480 /* strip the filename to the base */
481 gchar *baseName = g_path_get_basename (filename);
482 gchar *displayedName;
483 if (isPnpFile)
484 displayedName = g_strconcat (baseName, " (top)",NULL);
485 else
486 displayedName = g_strdup (baseName);
487 retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image, filename, displayedName, idx, reload);
488 g_free (baseName);
489 g_free (displayedName);
492 /* Set layer_dirty flag to FALSE */
493 gerbvProject->file[idx]->layer_dirty = FALSE;
495 /* for PNP place files, we may need to add a second image for the other
496 board side */
497 if (parsed_image2) {
498 /* strip the filename to the base */
499 gchar *baseName = g_path_get_basename (filename);
500 gchar *displayedName;
501 displayedName = g_strconcat (baseName, " (bottom)",NULL);
502 retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image2, filename, displayedName, idx + 1, reload);
503 g_free (baseName);
504 g_free (displayedName);
507 return retv;
508 } /* open_image */
510 gerbv_image_t *
511 gerbv_create_rs274x_image_from_filename (gchar *filename){
512 gerbv_image_t *returnImage;
513 gerb_file_t *fd;
515 fd = gerb_fopen(filename);
516 if (fd == NULL) {
517 GERB_MESSAGE("Trying to open %s:%s\n", filename, strerror(errno));
518 return NULL;
520 gchar *currentLoadDirectory = g_path_get_dirname (filename);
521 returnImage = parse_gerb(fd, currentLoadDirectory);
522 g_free (currentLoadDirectory);
523 gerb_fclose(fd);
524 return returnImage;
527 /* ------------------------------------------------------------------ */
528 void
529 gerbv_render_get_boundingbox(gerbv_project_t *gerbvProject, gerbv_render_size_t *boundingbox)
531 double x1=HUGE_VAL,y1=HUGE_VAL, x2=-HUGE_VAL,y2=-HUGE_VAL;
532 int i;
533 gerbv_image_info_t *info;
535 for(i = 0; i <= gerbvProject->last_loaded; i++) {
536 if ((gerbvProject->file[i]) && (gerbvProject->file[i]->isVisible)){
537 info = gerbvProject->file[i]->image->info;
539 * Find the biggest image and use as a size reference
541 #ifdef RENDER_USING_GDK
542 x1 = MIN(x1, info->min_x + info->offsetA);
543 y1 = MIN(y1, info->min_y + info->offsetB);
544 x2 = MAX(x2, info->max_x + info->offsetA);
545 y2 = MAX(y2, info->max_y + info->offsetB);
546 #else
547 /* cairo info already has offset calculated into min/max */
548 x1 = MIN(x1, info->min_x);
549 y1 = MIN(y1, info->min_y);
550 x2 = MAX(x2, info->max_x);
551 y2 = MAX(y2, info->max_y);
552 #endif
555 boundingbox->left = x1;
556 boundingbox->right = x2;
557 boundingbox->top = y1;
558 boundingbox->bottom = y2;
561 /* ------------------------------------------------------------------ */
562 void
563 gerbv_render_zoom_to_fit_display (gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo) {
564 gerbv_render_size_t bb;
565 double width, height;
566 double x_scale, y_scale;
568 /* Grab maximal width and height of all layers */
569 gerbv_render_get_boundingbox(gerbvProject, &bb);
570 width = bb.right - bb.left;
571 height = bb.bottom - bb.top;
572 /* add in a 5% buffer around the drawing */
573 width *= 1.05;
574 height *=1.05;
576 /* if the values aren't sane (probably we have no models loaded), then
577 put in some defaults */
578 if ((width < 0.01) && (height < 0.01)) {
579 renderInfo->lowerLeftX = 0.0;
580 renderInfo->lowerLeftY = 0.0;
581 renderInfo->scaleFactorX = 200;
582 renderInfo->scaleFactorY = 200;
583 return;
586 * Calculate scale for both x axis and y axis
588 x_scale = renderInfo->displayWidth / width;
589 y_scale = renderInfo->displayHeight / height;
591 * Take the scale that fits both directions with some extra checks
593 renderInfo->scaleFactorX = MIN(x_scale, y_scale);
594 renderInfo->scaleFactorY = renderInfo->scaleFactorX;
595 if (renderInfo->scaleFactorX < 1){
596 renderInfo->scaleFactorX = 1;
597 renderInfo->scaleFactorY = 1;
599 renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
600 ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
601 renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
602 ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
605 /* ------------------------------------------------------------------ */
606 void
607 gerbv_render_translate_to_fit_display (gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo) {
608 gerbv_render_size_t bb;
610 /* Grab maximal width and height of all layers */
611 gerbv_render_get_boundingbox(gerbvProject, &bb);
612 renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
613 ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
614 renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
615 ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
618 /* ------------------------------------------------------------------ */
619 void
620 gerbv_render_to_pixmap_using_gdk (gerbv_project_t *gerbvProject, GdkPixmap *pixmap,
621 gerbv_render_info_t *renderInfo, gerbv_selection_info_t *selectionInfo,
622 GdkColor *selectionColor){
623 GdkGC *gc = gdk_gc_new(pixmap);
624 GdkPixmap *colorStamp, *clipmask;
625 int i;
628 * Remove old pixmap, allocate a new one, draw the background.
630 if (!gerbvProject->background.pixel)
631 gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->background, FALSE, TRUE);
632 gdk_gc_set_foreground(gc, &gerbvProject->background);
633 gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, -1, -1);
636 * Allocate the pixmap and the clipmask (a one pixel pixmap)
638 colorStamp = gdk_pixmap_new(pixmap, renderInfo->displayWidth,
639 renderInfo->displayHeight, -1);
640 clipmask = gdk_pixmap_new(NULL, renderInfo->displayWidth,
641 renderInfo->displayHeight, 1);
644 * This now allows drawing several layers on top of each other.
645 * Higher layer numbers have higher priority in the Z-order.
647 for(i = gerbvProject->last_loaded; i >= 0; i--) {
648 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
649 gerbv_polarity_t polarity;
651 if (gerbvProject->file[i]->transform.inverted) {
652 if (gerbvProject->file[i]->image->info->polarity == GERBV_POLARITY_POSITIVE)
653 polarity = GERBV_POLARITY_NEGATIVE;
654 else
655 polarity = GERBV_POLARITY_POSITIVE;
656 } else {
657 polarity = gerbvProject->file[i]->image->info->polarity;
661 * Fill up image with all the foreground color. Excess pixels
662 * will be removed by clipmask.
664 if (!gerbvProject->file[i]->color.pixel)
665 gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->file[i]->color, FALSE, TRUE);
666 gdk_gc_set_foreground(gc, &gerbvProject->file[i]->color);
668 /* switch back to regular draw function for the initial
669 bitmap clear */
670 gdk_gc_set_function(gc, GDK_COPY);
671 gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
673 if (renderInfo->renderType == 0) {
674 gdk_gc_set_function(gc, GDK_COPY);
676 else if (renderInfo->renderType == 1) {
677 gdk_gc_set_function(gc, GDK_XOR);
680 * Translation is to get it inside the allocated pixmap,
681 * which is not always centered perfectly for GTK/X.
683 dprintf(" .... calling image2pixmap on image %d...\n", i);
684 // Dirty scaling solution when using GDK; simply use scaling factor for x-axis, ignore y-axis
685 draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[i]->image,
686 renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
687 (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
688 polarity, DRAW_IMAGE, NULL, renderInfo);
691 * Set clipmask and draw the clipped out image onto the
692 * screen pixmap. Afterwards we remove the clipmask, else
693 * it will screw things up when run this loop again.
695 gdk_gc_set_clip_mask(gc, clipmask);
696 gdk_gc_set_clip_origin(gc, 0, 0);
697 gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
698 gdk_gc_set_clip_mask(gc, NULL);
701 /* render the selection group to the top of the output */
702 if ((selectionInfo) && (selectionInfo->type != GERBV_SELECTION_EMPTY)) {
703 if (!selectionColor->pixel)
704 gdk_colormap_alloc_color(gdk_colormap_get_system(), selectionColor, FALSE, TRUE);
706 gdk_gc_set_foreground(gc, selectionColor);
707 gdk_gc_set_function(gc, GDK_COPY);
708 gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
710 /* for now, assume everything in the selection buffer is from one image */
711 gerbv_image_t *matchImage;
712 int j;
713 if (selectionInfo->selectedNodeArray->len > 0) {
714 gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
715 gerbv_selection_item_t, 0);
716 matchImage = (gerbv_image_t *) sItem.image;
718 for(j = gerbvProject->last_loaded; j >= 0; j--) {
719 if ((gerbvProject->file[j]) && (gerbvProject->file[j]->image == matchImage)) {
720 draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[j]->image,
721 renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
722 (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
723 GERBV_POLARITY_POSITIVE, DRAW_SELECTIONS, selectionInfo,
724 renderInfo);
727 gdk_gc_set_clip_mask(gc, clipmask);
728 gdk_gc_set_clip_origin(gc, 0, 0);
729 gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
730 gdk_gc_set_clip_mask(gc, NULL);
734 gdk_pixmap_unref(colorStamp);
735 gdk_pixmap_unref(clipmask);
736 gdk_gc_unref(gc);
739 /* ------------------------------------------------------------------ */
740 #ifndef RENDER_USING_GDK
741 void
742 gerbv_render_all_layers_to_cairo_target_for_vector_output (gerbv_project_t *gerbvProject,
743 cairo_t *cr, gerbv_render_info_t *renderInfo) {
744 int i;
745 gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
746 /* don't paint background for vector output, since it isn't needed */
747 for(i = gerbvProject->last_loaded; i >= 0; i--) {
748 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
750 gerbv_render_layer_to_cairo_target_without_transforming(cr, gerbvProject->file[i], renderInfo);
754 #endif
756 /* ------------------------------------------------------------------ */
757 #ifndef RENDER_USING_GDK
758 void
759 gerbv_render_all_layers_to_cairo_target (gerbv_project_t *gerbvProject, cairo_t *cr,
760 gerbv_render_info_t *renderInfo) {
761 int i;
762 /* fill the background with the appropriate color */
763 cairo_set_source_rgba (cr, (double) gerbvProject->background.red/G_MAXUINT16,
764 (double) gerbvProject->background.green/G_MAXUINT16,
765 (double) gerbvProject->background.blue/G_MAXUINT16, 1);
766 cairo_paint (cr);
767 for(i = gerbvProject->last_loaded; i >= 0; i--) {
768 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
769 cairo_push_group (cr);
770 gerbv_render_layer_to_cairo_target (cr, gerbvProject->file[i], renderInfo);
771 cairo_pop_group_to_source (cr);
772 cairo_paint_with_alpha (cr, (double) gerbvProject->file[i]->alpha/G_MAXUINT16);
776 #endif
778 /* ------------------------------------------------------------------ */
779 #ifndef RENDER_USING_GDK
780 void
781 gerbv_render_layer_to_cairo_target (cairo_t *cr, gerbv_fileinfo_t *fileInfo,
782 gerbv_render_info_t *renderInfo) {
783 gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
784 gerbv_render_layer_to_cairo_target_without_transforming(cr, fileInfo, renderInfo);
786 #endif
788 /* ------------------------------------------------------------------ */
789 #ifndef RENDER_USING_GDK
790 void
791 gerbv_render_cairo_set_scale_and_translation(cairo_t *cr, gerbv_render_info_t *renderInfo){
792 gdouble translateX, translateY;
794 translateX = (renderInfo->lowerLeftX * renderInfo->scaleFactorX);
795 translateY = (renderInfo->lowerLeftY * renderInfo->scaleFactorY);
797 /* renderTypes 0 and 1 use GDK rendering, so we shouldn't have made it
798 this far */
799 if (renderInfo->renderType == 2) {
800 cairo_set_tolerance (cr, 1.5);
801 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
803 else if (renderInfo->renderType == 3) {
804 cairo_set_tolerance (cr, 1);
805 /* disable ALL anti-aliasing for now due to the way cairo is rendering
806 ground planes from PCB output */
807 cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
810 /* translate the draw area before drawing. We must translate the whole
811 drawing down an additional displayHeight to account for the negative
812 y flip done later */
813 cairo_translate (cr, -translateX, translateY + renderInfo->displayHeight);
814 /* scale the drawing by the specified scale factor (inverting y since
815 cairo y axis points down) */
816 cairo_scale (cr, renderInfo->scaleFactorX, -renderInfo->scaleFactorY);
818 #endif
820 /* ------------------------------------------------------------------ */
821 #ifndef RENDER_USING_GDK
822 void
823 gerbv_render_layer_to_cairo_target_without_transforming(cairo_t *cr, gerbv_fileinfo_t *fileInfo, gerbv_render_info_t *renderInfo ) {
824 cairo_set_source_rgba (cr, (double) fileInfo->color.red/G_MAXUINT16,
825 (double) fileInfo->color.green/G_MAXUINT16,
826 (double) fileInfo->color.blue/G_MAXUINT16, 1);
828 /* translate the image based on the layer-specific transformation struct */
829 cairo_save (cr);
830 cairo_translate (cr, fileInfo->transform.translateX, fileInfo->transform.translateY);
831 draw_image_to_cairo_target (cr, fileInfo->image, fileInfo->transform.inverted,
832 1.0/MAX(renderInfo->scaleFactorX, renderInfo->scaleFactorY), DRAW_IMAGE, NULL,
833 renderInfo);
834 cairo_restore (cr);
836 #endif
838 void
839 gerbv_attribute_destroy_HID_attribute (gerbv_HID_Attribute *attributeList, int n_attr)
841 int i;
843 /* free the string attributes */
844 for (i = 0 ; i < n_attr ; i++) {
845 if ( (attributeList[i].type == HID_String ||
846 attributeList[i].type == HID_Label) &&
847 attributeList[i].default_val.str_value != NULL) {
848 free (attributeList[i].default_val.str_value);
852 /* and free the attribute list */
853 if (attributeList != NULL) {
854 free (attributeList);
859 /* allocate memory and make a copy of an attribute list */
860 gerbv_HID_Attribute *
861 gerbv_attribute_dup (gerbv_HID_Attribute *attributeList, int n_attr)
863 gerbv_HID_Attribute *nl;
864 int i;
866 nl = (gerbv_HID_Attribute *) malloc (n_attr * sizeof (gerbv_HID_Attribute));
867 if (nl == NULL) {
868 fprintf (stderr, "%s(): malloc failed\n", __FUNCTION__);
869 exit (1);
872 /* copy the attribute list being sure to strdup the strings */
873 for (i = 0 ; i < n_attr ; i++) {
875 if (attributeList[i].type == HID_String ||
876 attributeList[i].type == HID_Label) {
878 if (attributeList[i].default_val.str_value != NULL) {
879 nl[i].default_val.str_value = strdup (attributeList[i].default_val.str_value);
880 } else {
881 nl[i].default_val.str_value = NULL;
884 } else {
885 nl[i] = attributeList[i];
889 return nl;