* fix a bug where clicking on the message tab made keyboard accelerators stop working
[geda-gerbv.git] / src / gerbv.c
blobda8f303688454b7e0a3320d97092b53cf17c8dd6
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 "common.h"
65 #include "gerbv.h"
66 #include "gerber.h"
67 #include "drill.h"
69 #ifdef RENDER_USING_GDK
70 #include "draw-gdk.h"
71 #else
72 #include "draw-gdk.h"
73 #include "draw.h"
74 #endif
76 #include "pick-and-place.h"
78 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
79 #define dprintf if(DEBUG) printf
81 #define NUMBER_OF_DEFAULT_COLORS 18
82 #define NUMBER_OF_DEFAULT_TRANSFORMATIONS 20
84 /* ------------------------------------------------------------------ */
85 static gerbv_layer_color defaultColors[NUMBER_OF_DEFAULT_COLORS] = {
86 {115,115,222,177},
87 {255,127,115,177},
88 {193,0,224,177},
89 {117,242,103,177},
90 {0,195,195,177},
91 {213,253,51,177},
92 {209,27,104,177},
93 {255,197,51,177},
94 {186,186,186,177},
95 {211,211,255,177},
96 {253,210,206,177},
97 {236,194,242,177},
98 {208,249,204,177},
99 {183,255,255,177},
100 {241,255,183,177},
101 {255,202,225,177},
102 {253,238,197,177},
103 {226,226,226,177}
106 /* ------------------------------------------------------------------ */
107 static gerbv_user_transformation_t defaultTransformations[NUMBER_OF_DEFAULT_TRANSFORMATIONS] = {
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},
127 {0,0,0,0,FALSE},
130 /* ------------------------------------------------------------------ */
131 gerbv_project_t *
132 gerbv_create_project (void) {
133 gerbv_project_t *returnProject= (gerbv_project_t *) g_new0(gerbv_project_t,1);
135 /* default to using the current directory path for our starting guesses
136 on future file loads */
137 returnProject->path = g_get_current_dir ();
138 /* Will be updated to 0 when first Gerber is loaded */
139 returnProject->last_loaded = -1;
140 returnProject->max_files = 1;
141 returnProject->check_before_delete = TRUE;
142 returnProject->file = g_new0 (gerbv_fileinfo_t *, returnProject->max_files);
144 return returnProject;
147 /* ------------------------------------------------------------------ */
148 void
149 gerbv_destroy_project (gerbv_project_t *gerbvProject){
150 int i;
152 /* destroy all the files attached to the project */
153 for(i = gerbvProject->last_loaded; i >= 0; i--) {
154 if (gerbvProject->file[i])
155 gerbv_destroy_fileinfo (gerbvProject->file[i]);
157 /* destroy strings */
158 g_free (gerbvProject->path);
159 g_free (gerbvProject->execname);
160 g_free (gerbvProject->execpath);
161 g_free (gerbvProject->project);
162 /* destroy the fileinfo array */
163 g_free (gerbvProject->file);
164 g_free (gerbvProject);
167 /* ------------------------------------------------------------------ */
168 void
169 gerbv_destroy_fileinfo (gerbv_fileinfo_t *fileInfo){
170 gerbv_destroy_image (fileInfo->image);
171 g_free (fileInfo->fullPathname);
172 g_free (fileInfo->name);
173 #ifndef RENDER_USING_GDK
174 if (fileInfo->privateRenderData) {
175 cairo_surface_destroy ((cairo_surface_t *)
176 fileInfo->privateRenderData);
178 #endif
181 /* ------------------------------------------------------------------ */
182 void
183 gerbv_open_layer_from_filename(gerbv_project_t *gerbvProject, gchar *filename)
185 gint idx_loaded;
186 dprintf("Opening filename = %s\n", (gchar *) filename);
188 if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
189 GERB_MESSAGE("could not read %s[%d]", (gchar *) filename,
190 gerbvProject->last_loaded);
191 gerbvProject->last_loaded--;
192 } else {
193 idx_loaded = gerbvProject->last_loaded;
194 gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
195 dprintf(" Successfully opened file!\n");
197 } /* gerbv_open_layer_from_filename */
199 /* ------------------------------------------------------------------ */
200 void
201 gerbv_open_layer_from_filename_with_color(gerbv_project_t *gerbvProject, gchar *filename,
202 guint16 red, guint16 green, guint16 blue, guint16 alpha)
204 gint idx_loaded;
205 dprintf("Opening filename = %s\n", (gchar *) filename);
207 if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
208 GERB_MESSAGE("could not read %s[%d]", (gchar *) filename,
209 gerbvProject->last_loaded);
210 gerbvProject->last_loaded--;
211 } else {
212 idx_loaded = gerbvProject->last_loaded;
213 gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
214 GdkColor colorTemplate = {0, red, green, blue};
215 gerbvProject->file[idx_loaded]->color = colorTemplate;
216 gerbvProject->file[idx_loaded]->alpha = alpha;
217 dprintf(" Successfully opened file!\n");
219 } /* gerbv_open_layer_from_filename_with_color */
221 /* ------------------------------------------------------------------ */
222 gboolean
223 gerbv_save_layer_from_index(gerbv_project_t *gerbvProject, gint index, gchar *filename)
225 if (strcmp (gerbvProject->file[index]->image->info->type,"RS274-X (Gerber) File")==0) {
226 gerbv_export_rs274x_file_from_image (filename, gerbvProject->file[index]->image);
228 else if (strcmp (gerbvProject->file[index]->image->info->type,"Excellon Drill File")==0) {
229 gerbv_export_drill_file_from_image (filename, gerbvProject->file[index]->image);
231 else {
232 return FALSE;
234 gerbvProject->file[index]->layer_dirty = FALSE;
235 return TRUE;
236 } /* gerbv_save_project_from_filename */
239 /* ------------------------------------------------------------------ */
241 gerbv_revert_file(gerbv_project_t *gerbvProject, int idx){
242 int rv;
244 rv = gerbv_open_image(gerbvProject, gerbvProject->file[idx]->fullPathname, idx, TRUE, NULL, 0, TRUE);
245 gerbvProject->file[idx]->layer_dirty = FALSE;
246 return rv;
249 /* ------------------------------------------------------------------ */
250 void
251 gerbv_revert_all_files(gerbv_project_t *gerbvProject)
253 int idx;
255 for (idx = 0; idx <= gerbvProject->last_loaded; idx++) {
256 if (gerbvProject->file[idx] && gerbvProject->file[idx]->fullPathname) {
257 (void) gerbv_revert_file (gerbvProject, idx);
258 gerbvProject->file[idx]->layer_dirty = FALSE;
261 } /* gerbv_revert_all_files */
263 /* ------------------------------------------------------------------ */
264 void
265 gerbv_unload_layer(gerbv_project_t *gerbvProject, int index)
267 gint i;
269 gerbv_destroy_fileinfo (gerbvProject->file[index]);
271 /* slide all later layers down to fill the empty slot */
272 for (i=index; i<(gerbvProject->last_loaded); i++) {
273 gerbvProject->file[i]=gerbvProject->file[i+1];
275 /* make sure the final spot is clear */
276 gerbvProject->file[gerbvProject->last_loaded] = NULL;
277 gerbvProject->last_loaded--;
278 } /* gerbv_unload_layer */
280 /* ------------------------------------------------------------------ */
281 void
282 gerbv_unload_all_layers (gerbv_project_t *gerbvProject)
284 int index;
286 /* Must count down since gerbv_unload_layer collapses
287 * layers down. Otherwise, layers slide past the index */
288 for (index = gerbvProject->last_loaded ; index >= 0; index--) {
289 if (gerbvProject->file[index] && gerbvProject->file[index]->name) {
290 gerbv_unload_layer (gerbvProject, index);
293 } /* gerbv_unload_all_layers */
296 /* ------------------------------------------------------------------ */
297 void
298 gerbv_change_layer_order(gerbv_project_t *gerbvProject, gint oldPosition, gint newPosition)
300 gerbv_fileinfo_t *temp_file;
301 int index;
303 temp_file = gerbvProject->file[oldPosition];
305 if (oldPosition < newPosition){
306 for (index = oldPosition; index < newPosition; index++) {
307 gerbvProject->file[index] = gerbvProject->file[index + 1];
309 } else {
310 for (index = oldPosition; index > newPosition; index--) {
311 gerbvProject->file[index] = gerbvProject->file[index - 1];
314 gerbvProject->file[newPosition] = temp_file;
315 } /* gerbv_change_layer_order */
318 /* ------------------------------------------------------------------ */
319 gint
320 gerbv_add_parsed_image_to_project (gerbv_project_t *gerbvProject, gerbv_image_t *parsed_image,
321 gchar *filename, gchar *baseName, int idx, int reload){
322 gerb_verify_error_t error = GERB_IMAGE_OK;
323 int r, g, b;
325 dprintf("In open_image, now error check file....\n");
326 error = gerbv_image_verify(parsed_image);
327 if (error) {
328 GERB_COMPILE_ERROR("%s: Parse error:\n", filename);
329 if (error & GERB_IMAGE_MISSING_NETLIST)
330 GERB_COMPILE_ERROR("* Missing netlist\n");
331 if (error & GERB_IMAGE_MISSING_FORMAT)
332 GERB_COMPILE_ERROR("* Missing format\n");
333 if (error & GERB_IMAGE_MISSING_APERTURES)
334 GERB_COMPILE_ERROR("* Missing apertures/drill sizes\n");
335 if (error & GERB_IMAGE_MISSING_INFO)
336 GERB_COMPILE_ERROR("* Missing info\n");
337 GERB_COMPILE_ERROR("\n");
338 GERB_COMPILE_ERROR("You probably tried to read an RS-274D file, which gerbv doesn't support\n");
339 gerbv_destroy_image(parsed_image);
340 return -1;
343 /* Used to debug parser */
344 //if (gerbvProject->dump_parsed_image)
345 //gerb_image_dump(parsed_image);
348 * If reload, just exchange the image. Else we have to allocate
349 * a new memory before we define anything more.
351 if (reload) {
352 gerbv_destroy_image(gerbvProject->file[idx]->image);
353 gerbvProject->file[idx]->image = parsed_image;
354 return 0;
355 } else {
356 /* Load new file. */
357 gerbvProject->file[idx] = (gerbv_fileinfo_t *) g_new0 (gerbv_fileinfo_t, 1);
358 gerbvProject->file[idx]->image = parsed_image;
362 * Store filename for eventual reload
364 gerbvProject->file[idx]->fullPathname = g_strdup (filename);
365 gerbvProject->file[idx]->name = g_strdup (baseName);
369 r = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].red*257;
370 g = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].green*257;
371 b = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].blue*257;
374 GdkColor colorTemplate = {0, r, g, b};
375 gerbvProject->file[idx]->color = colorTemplate;
376 gerbvProject->file[idx]->alpha = defaultColors[idx % NUMBER_OF_DEFAULT_COLORS].alpha*257;
377 gerbvProject->file[idx]->isVisible = TRUE;
378 gerbvProject->file[idx]->transform = defaultTransformations[idx % NUMBER_OF_DEFAULT_TRANSFORMATIONS];
379 /* update the number of files if we need to */
380 if (gerbvProject->last_loaded <= idx) {
381 gerbvProject->last_loaded = idx;
383 return 1;
386 /* ------------------------------------------------------------------ */
388 gerbv_open_image(gerbv_project_t *gerbvProject, char *filename, int idx, int reload,
389 gerbv_HID_Attribute *fattr, int n_fattr, gboolean forceLoadFile)
391 gerb_file_t *fd;
392 gerbv_image_t *parsed_image = NULL, *parsed_image2 = NULL;
393 gint retv = -1;
394 gboolean isPnpFile = FALSE, foundBinary;
395 gerbv_HID_Attribute *attr_list = NULL;
396 int n_attr = 0;
397 /* If we're reloading, we'll pass in our file format attribute list
398 * since this is our hook for letting the user override the fileformat.
400 if (reload)
402 /* We're reloading so use the attribute list in memory */
403 attr_list = gerbvProject->file[idx]->image->info->attr_list;
404 n_attr = gerbvProject->file[idx]->image->info->n_attr;
406 else
408 /* We're not reloading so use the attribute list read from the
409 * project file if given or NULL otherwise.
411 attr_list = fattr;
412 n_attr = n_fattr;
414 /* if we don't have enough spots, then grow the file list by 2 to account for the possible
415 loading of two images for PNP files */
416 if ((idx+1) >= gerbvProject->max_files) {
417 gerbvProject->file = g_renew (gerbv_fileinfo_t *,
418 gerbvProject->file, gerbvProject->max_files + 2);
420 gerbvProject->file[gerbvProject->max_files] = NULL;
421 gerbvProject->file[gerbvProject->max_files+1] = NULL;
422 gerbvProject->max_files += 2;
425 dprintf("In open_image, about to try opening filename = %s\n", filename);
427 fd = gerb_fopen(filename);
428 if (fd == NULL) {
429 GERB_MESSAGE("Trying to open %s:%s\n", filename, strerror(errno));
430 return -1;
433 /* Store filename info fd for further use */
434 fd->filename = g_strdup(filename);
436 dprintf("In open_image, successfully opened file. Now check its type....\n");
437 /* Here's where we decide what file type we have */
438 /* Note: if the file has some invalid characters in it but still appears to
439 be a valid file, we check with the user if he wants to continue (only
440 if user opens the layer from the menu...if from the command line, we go
441 ahead and try to load it anyways) */
443 if (gerber_is_rs274x_p(fd, &foundBinary)) {
444 dprintf("Found RS-274X file\n");
445 if ((!foundBinary || forceLoadFile)) {
446 /* figure out the directory path in case parse_gerb needs to
447 * load any include files */
448 gchar *currentLoadDirectory = g_path_get_dirname (filename);
449 parsed_image = parse_gerb(fd, currentLoadDirectory);
450 g_free (currentLoadDirectory);
452 } else if(drill_file_p(fd, &foundBinary)) {
453 dprintf("Found drill file\n");
454 if ((!foundBinary || forceLoadFile))
455 parsed_image = parse_drillfile(fd, attr_list, n_attr, reload);
457 } else if (pick_and_place_check_file_type(fd, &foundBinary)) {
458 dprintf("Found pick-n-place file\n");
459 if ((!foundBinary || forceLoadFile)) {
460 pick_and_place_parse_file_to_images(fd, &parsed_image, &parsed_image2);
461 isPnpFile = TRUE;
463 } else if (gerber_is_rs274d_p(fd)) {
464 dprintf("Found RS-274D file");
465 GERB_COMPILE_ERROR("%s: Found RS-274D file -- not supported by gerbv.\n", filename);
466 parsed_image = NULL;
468 } else {
469 /* This is not a known file */
470 dprintf("Unknown filetype");
471 GERB_COMPILE_ERROR("%s: Unknown file type.\n", filename);
472 parsed_image = NULL;
475 gerb_fclose(fd);
476 if (parsed_image == NULL) {
477 return -1;
480 if (parsed_image) {
481 /* strip the filename to the base */
482 gchar *baseName = g_path_get_basename (filename);
483 gchar *displayedName;
484 if (isPnpFile)
485 displayedName = g_strconcat (baseName, " (top)",NULL);
486 else
487 displayedName = g_strdup (baseName);
488 retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image, filename, displayedName, idx, reload);
489 g_free (baseName);
490 g_free (displayedName);
493 /* Set layer_dirty flag to FALSE */
494 gerbvProject->file[idx]->layer_dirty = FALSE;
496 /* for PNP place files, we may need to add a second image for the other
497 board side */
498 if (parsed_image2) {
499 /* strip the filename to the base */
500 gchar *baseName = g_path_get_basename (filename);
501 gchar *displayedName;
502 displayedName = g_strconcat (baseName, " (bottom)",NULL);
503 retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image2, filename, displayedName, idx + 1, reload);
504 g_free (baseName);
505 g_free (displayedName);
508 return retv;
509 } /* open_image */
511 gerbv_image_t *
512 gerbv_create_rs274x_image_from_filename (gchar *filename){
513 gerbv_image_t *returnImage;
514 gerb_file_t *fd;
516 fd = gerb_fopen(filename);
517 if (fd == NULL) {
518 GERB_MESSAGE("Trying to open %s:%s\n", filename, strerror(errno));
519 return NULL;
521 gchar *currentLoadDirectory = g_path_get_dirname (filename);
522 returnImage = parse_gerb(fd, currentLoadDirectory);
523 g_free (currentLoadDirectory);
524 gerb_fclose(fd);
525 return returnImage;
528 /* ------------------------------------------------------------------ */
529 void
530 gerbv_render_get_boundingbox(gerbv_project_t *gerbvProject, gerbv_render_size_t *boundingbox)
532 double x1=HUGE_VAL,y1=HUGE_VAL, x2=-HUGE_VAL,y2=-HUGE_VAL;
533 int i;
534 gerbv_image_info_t *info;
536 for(i = 0; i <= gerbvProject->last_loaded; i++) {
537 if ((gerbvProject->file[i]) && (gerbvProject->file[i]->isVisible)){
538 info = gerbvProject->file[i]->image->info;
540 * Find the biggest image and use as a size reference
542 #ifdef RENDER_USING_GDK
543 x1 = MIN(x1, info->min_x + info->offsetA);
544 y1 = MIN(y1, info->min_y + info->offsetB);
545 x2 = MAX(x2, info->max_x + info->offsetA);
546 y2 = MAX(y2, info->max_y + info->offsetB);
547 #else
548 /* cairo info already has offset calculated into min/max */
549 x1 = MIN(x1, info->min_x);
550 y1 = MIN(y1, info->min_y);
551 x2 = MAX(x2, info->max_x);
552 y2 = MAX(y2, info->max_y);
553 #endif
556 boundingbox->left = x1;
557 boundingbox->right = x2;
558 boundingbox->top = y1;
559 boundingbox->bottom = y2;
562 /* ------------------------------------------------------------------ */
563 void
564 gerbv_render_zoom_to_fit_display (gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo) {
565 gerbv_render_size_t bb;
566 double width, height;
567 double x_scale, y_scale;
569 /* Grab maximal width and height of all layers */
570 gerbv_render_get_boundingbox(gerbvProject, &bb);
571 width = bb.right - bb.left;
572 height = bb.bottom - bb.top;
573 /* add in a 5% buffer around the drawing */
574 width *= 1.05;
575 height *=1.05;
577 /* if the values aren't sane (probably we have no models loaded), then
578 put in some defaults */
579 if ((width < 0.01) && (height < 0.01)) {
580 renderInfo->lowerLeftX = 0.0;
581 renderInfo->lowerLeftY = 0.0;
582 renderInfo->scaleFactorX = 200;
583 renderInfo->scaleFactorY = 200;
584 return;
587 * Calculate scale for both x axis and y axis
589 x_scale = renderInfo->displayWidth / width;
590 y_scale = renderInfo->displayHeight / height;
592 * Take the scale that fits both directions with some extra checks
594 renderInfo->scaleFactorX = MIN(x_scale, y_scale);
595 renderInfo->scaleFactorY = renderInfo->scaleFactorX;
596 if (renderInfo->scaleFactorX < 1){
597 renderInfo->scaleFactorX = 1;
598 renderInfo->scaleFactorY = 1;
600 renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
601 ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
602 renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
603 ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
606 /* ------------------------------------------------------------------ */
607 void
608 gerbv_render_translate_to_fit_display (gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo) {
609 gerbv_render_size_t bb;
611 /* Grab maximal width and height of all layers */
612 gerbv_render_get_boundingbox(gerbvProject, &bb);
613 renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
614 ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
615 renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
616 ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
619 /* ------------------------------------------------------------------ */
620 void
621 gerbv_render_to_pixmap_using_gdk (gerbv_project_t *gerbvProject, GdkPixmap *pixmap,
622 gerbv_render_info_t *renderInfo, gerbv_selection_info_t *selectionInfo,
623 GdkColor *selectionColor){
624 GdkGC *gc = gdk_gc_new(pixmap);
625 GdkPixmap *colorStamp, *clipmask;
626 int i;
629 * Remove old pixmap, allocate a new one, draw the background.
631 if (!gerbvProject->background.pixel)
632 gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->background, FALSE, TRUE);
633 gdk_gc_set_foreground(gc, &gerbvProject->background);
634 gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, -1, -1);
637 * Allocate the pixmap and the clipmask (a one pixel pixmap)
639 colorStamp = gdk_pixmap_new(pixmap, renderInfo->displayWidth,
640 renderInfo->displayHeight, -1);
641 clipmask = gdk_pixmap_new(NULL, renderInfo->displayWidth,
642 renderInfo->displayHeight, 1);
645 * This now allows drawing several layers on top of each other.
646 * Higher layer numbers have higher priority in the Z-order.
648 for(i = gerbvProject->last_loaded; i >= 0; i--) {
649 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
650 gerbv_polarity_t polarity;
652 if (gerbvProject->file[i]->transform.inverted) {
653 if (gerbvProject->file[i]->image->info->polarity == GERBV_POLARITY_POSITIVE)
654 polarity = GERBV_POLARITY_NEGATIVE;
655 else
656 polarity = GERBV_POLARITY_POSITIVE;
657 } else {
658 polarity = gerbvProject->file[i]->image->info->polarity;
662 * Fill up image with all the foreground color. Excess pixels
663 * will be removed by clipmask.
665 if (!gerbvProject->file[i]->color.pixel)
666 gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->file[i]->color, FALSE, TRUE);
667 gdk_gc_set_foreground(gc, &gerbvProject->file[i]->color);
669 /* switch back to regular draw function for the initial
670 bitmap clear */
671 gdk_gc_set_function(gc, GDK_COPY);
672 gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
674 if (renderInfo->renderType == 0) {
675 gdk_gc_set_function(gc, GDK_COPY);
677 else if (renderInfo->renderType == 1) {
678 gdk_gc_set_function(gc, GDK_XOR);
681 * Translation is to get it inside the allocated pixmap,
682 * which is not always centered perfectly for GTK/X.
684 dprintf(" .... calling image2pixmap on image %d...\n", i);
685 // Dirty scaling solution when using GDK; simply use scaling factor for x-axis, ignore y-axis
686 draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[i]->image,
687 renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
688 (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
689 polarity, DRAW_IMAGE, NULL, renderInfo);
692 * Set clipmask and draw the clipped out image onto the
693 * screen pixmap. Afterwards we remove the clipmask, else
694 * it will screw things up when run this loop again.
696 gdk_gc_set_clip_mask(gc, clipmask);
697 gdk_gc_set_clip_origin(gc, 0, 0);
698 gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
699 gdk_gc_set_clip_mask(gc, NULL);
702 /* render the selection group to the top of the output */
703 if ((selectionInfo) && (selectionInfo->type != GERBV_SELECTION_EMPTY)) {
704 if (!selectionColor->pixel)
705 gdk_colormap_alloc_color(gdk_colormap_get_system(), selectionColor, FALSE, TRUE);
707 gdk_gc_set_foreground(gc, selectionColor);
708 gdk_gc_set_function(gc, GDK_COPY);
709 gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
711 /* for now, assume everything in the selection buffer is from one image */
712 gerbv_image_t *matchImage;
713 int j;
714 if (selectionInfo->selectedNodeArray->len > 0) {
715 gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
716 gerbv_selection_item_t, 0);
717 matchImage = (gerbv_image_t *) sItem.image;
719 for(j = gerbvProject->last_loaded; j >= 0; j--) {
720 if ((gerbvProject->file[j]) && (gerbvProject->file[j]->image == matchImage)) {
721 draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[j]->image,
722 renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
723 (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
724 GERBV_POLARITY_POSITIVE, DRAW_SELECTIONS, selectionInfo,
725 renderInfo);
728 gdk_gc_set_clip_mask(gc, clipmask);
729 gdk_gc_set_clip_origin(gc, 0, 0);
730 gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
731 gdk_gc_set_clip_mask(gc, NULL);
735 gdk_pixmap_unref(colorStamp);
736 gdk_pixmap_unref(clipmask);
737 gdk_gc_unref(gc);
740 /* ------------------------------------------------------------------ */
741 #ifndef RENDER_USING_GDK
742 void
743 gerbv_render_all_layers_to_cairo_target_for_vector_output (gerbv_project_t *gerbvProject,
744 cairo_t *cr, gerbv_render_info_t *renderInfo) {
745 int i;
746 gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
747 /* don't paint background for vector output, since it isn't needed */
748 for(i = gerbvProject->last_loaded; i >= 0; i--) {
749 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
751 gerbv_render_layer_to_cairo_target_without_transforming(cr, gerbvProject->file[i], renderInfo);
755 #endif
757 /* ------------------------------------------------------------------ */
758 #ifndef RENDER_USING_GDK
759 void
760 gerbv_render_all_layers_to_cairo_target (gerbv_project_t *gerbvProject, cairo_t *cr,
761 gerbv_render_info_t *renderInfo) {
762 int i;
763 /* fill the background with the appropriate color */
764 cairo_set_source_rgba (cr, (double) gerbvProject->background.red/G_MAXUINT16,
765 (double) gerbvProject->background.green/G_MAXUINT16,
766 (double) gerbvProject->background.blue/G_MAXUINT16, 1);
767 cairo_paint (cr);
768 for(i = gerbvProject->last_loaded; i >= 0; i--) {
769 if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
770 cairo_push_group (cr);
771 gerbv_render_layer_to_cairo_target (cr, gerbvProject->file[i], renderInfo);
772 cairo_pop_group_to_source (cr);
773 cairo_paint_with_alpha (cr, (double) gerbvProject->file[i]->alpha/G_MAXUINT16);
777 #endif
779 /* ------------------------------------------------------------------ */
780 #ifndef RENDER_USING_GDK
781 void
782 gerbv_render_layer_to_cairo_target (cairo_t *cr, gerbv_fileinfo_t *fileInfo,
783 gerbv_render_info_t *renderInfo) {
784 gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
785 gerbv_render_layer_to_cairo_target_without_transforming(cr, fileInfo, renderInfo);
787 #endif
789 /* ------------------------------------------------------------------ */
790 #ifndef RENDER_USING_GDK
791 void
792 gerbv_render_cairo_set_scale_and_translation(cairo_t *cr, gerbv_render_info_t *renderInfo){
793 gdouble translateX, translateY;
795 translateX = (renderInfo->lowerLeftX * renderInfo->scaleFactorX);
796 translateY = (renderInfo->lowerLeftY * renderInfo->scaleFactorY);
798 /* renderTypes 0 and 1 use GDK rendering, so we shouldn't have made it
799 this far */
800 if (renderInfo->renderType == 2) {
801 cairo_set_tolerance (cr, 1.5);
802 cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
804 else if (renderInfo->renderType == 3) {
805 cairo_set_tolerance (cr, 1);
806 /* disable ALL anti-aliasing for now due to the way cairo is rendering
807 ground planes from PCB output */
808 cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
811 /* translate the draw area before drawing. We must translate the whole
812 drawing down an additional displayHeight to account for the negative
813 y flip done later */
814 cairo_translate (cr, -translateX, translateY + renderInfo->displayHeight);
815 /* scale the drawing by the specified scale factor (inverting y since
816 cairo y axis points down) */
817 cairo_scale (cr, renderInfo->scaleFactorX, -renderInfo->scaleFactorY);
819 #endif
821 /* ------------------------------------------------------------------ */
822 #ifndef RENDER_USING_GDK
823 void
824 gerbv_render_layer_to_cairo_target_without_transforming(cairo_t *cr, gerbv_fileinfo_t *fileInfo, gerbv_render_info_t *renderInfo ) {
825 cairo_set_source_rgba (cr, (double) fileInfo->color.red/G_MAXUINT16,
826 (double) fileInfo->color.green/G_MAXUINT16,
827 (double) fileInfo->color.blue/G_MAXUINT16, 1);
829 /* translate the image based on the layer-specific transformation struct */
830 cairo_save (cr);
831 cairo_translate (cr, fileInfo->transform.translateX, fileInfo->transform.translateY);
832 draw_image_to_cairo_target (cr, fileInfo->image, fileInfo->transform.inverted,
833 1.0/MAX(renderInfo->scaleFactorX, renderInfo->scaleFactorY), DRAW_IMAGE, NULL,
834 renderInfo);
835 cairo_restore (cr);
837 #endif
839 void
840 gerbv_attribute_destroy_HID_attribute (gerbv_HID_Attribute *attributeList, int n_attr)
842 int i;
844 /* free the string attributes */
845 for (i = 0 ; i < n_attr ; i++) {
846 if ( (attributeList[i].type == HID_String ||
847 attributeList[i].type == HID_Label) &&
848 attributeList[i].default_val.str_value != NULL) {
849 free (attributeList[i].default_val.str_value);
853 /* and free the attribute list */
854 if (attributeList != NULL) {
855 free (attributeList);
860 /* allocate memory and make a copy of an attribute list */
861 gerbv_HID_Attribute *
862 gerbv_attribute_dup (gerbv_HID_Attribute *attributeList, int n_attr)
864 gerbv_HID_Attribute *nl;
865 int i;
867 nl = (gerbv_HID_Attribute *) malloc (n_attr * sizeof (gerbv_HID_Attribute));
868 if (nl == NULL) {
869 fprintf (stderr, "%s(): malloc failed\n", __FUNCTION__);
870 exit (1);
873 /* copy the attribute list being sure to strdup the strings */
874 for (i = 0 ; i < n_attr ; i++) {
876 if (attributeList[i].type == HID_String ||
877 attributeList[i].type == HID_Label) {
879 if (attributeList[i].default_val.str_value != NULL) {
880 nl[i].default_val.str_value = strdup (attributeList[i].default_val.str_value);
881 } else {
882 nl[i].default_val.str_value = NULL;
885 } else {
886 nl[i] = attributeList[i];
890 return nl;