gerbv  2.6A
gerbv.c
Go to the documentation of this file.
1 /*
2  * gEDA - GNU Electronic Design Automation
3  * This file is a part of gerbv.
4  *
5  * Copyright (C) 2000-2003 Stefan Petersen (spe@stacken.kth.se)
6  *
7  * $Id$
8  *
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.
13  *
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.
18  *
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
22  */
23 
24 
30 #include "gerbv.h"
31 
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h>
36 #include <errno.h>
37 
38 #ifdef HAVE_LIBGEN_H
39 # include <libgen.h> /* dirname */
40 #endif
41 
42 #ifdef HAVE_STRING_H
43 # include <string.h>
44 #endif
45 
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 
50 #ifdef HAVE_GETOPT_H
51 # include <getopt.h>
52 #endif
53 
54 #include <pango/pango.h>
55 #include <locale.h>
56 
57 #include "common.h"
58 #include "gerber.h"
59 #include "drill.h"
60 
61 #include "draw-gdk.h"
62 #include "draw.h"
63 
64 #include "pick-and-place.h"
65 
66 /* DEBUG printing. #define DEBUG 1 in config.h to use this fcn. */
67 #define dprintf if(DEBUG) printf
68 
69 #define NUMBER_OF_DEFAULT_COLORS 18
70 #define NUMBER_OF_DEFAULT_TRANSFORMATIONS 20
71 
72 static int defaultColorIndex = 0;
73 
74 /* ------------------------------------------------------------------ */
75 static gerbv_layer_color defaultColors[NUMBER_OF_DEFAULT_COLORS] = {
76  {115,115,222,177},
77  {255,127,115,177},
78  {193,0,224,177},
79  {117,242,103,177},
80  {0,195,195,177},
81  {213,253,51,177},
82  {209,27,104,177},
83  {255,197,51,177},
84  {186,186,186,177},
85  {211,211,255,177},
86  {253,210,206,177},
87  {236,194,242,177},
88  {208,249,204,177},
89  {183,255,255,177},
90  {241,255,183,177},
91  {255,202,225,177},
92  {253,238,197,177},
93  {226,226,226,177}
94 };
95 
96 /* ------------------------------------------------------------------ */
97 static gerbv_user_transformation_t defaultTransformations[NUMBER_OF_DEFAULT_TRANSFORMATIONS] = {
98  {0,0,1,1,0,FALSE,FALSE,FALSE},
99  {0,0,1,1,0,FALSE,FALSE,FALSE},
100  {0,0,1,1,0,FALSE,FALSE,FALSE},
101  {0,0,1,1,0,FALSE,FALSE,FALSE},
102  {0,0,1,1,0,FALSE,FALSE,FALSE},
103  {0,0,1,1,0,FALSE,FALSE,FALSE},
104  {0,0,1,1,0,FALSE,FALSE,FALSE},
105  {0,0,1,1,0,FALSE,FALSE,FALSE},
106  {0,0,1,1,0,FALSE,FALSE,FALSE},
107  {0,0,1,1,0,FALSE,FALSE,FALSE},
108  {0,0,1,1,0,FALSE,FALSE,FALSE},
109  {0,0,1,1,0,FALSE,FALSE,FALSE},
110  {0,0,1,1,0,FALSE,FALSE,FALSE},
111  {0,0,1,1,0,FALSE,FALSE,FALSE},
112  {0,0,1,1,0,FALSE,FALSE,FALSE},
113  {0,0,1,1,0,FALSE,FALSE,FALSE},
114  {0,0,1,1,0,FALSE,FALSE,FALSE},
115  {0,0,1,1,0,FALSE,FALSE,FALSE},
116  {0,0,1,1,0,FALSE,FALSE,FALSE},
117  {0,0,1,1,0,FALSE,FALSE,FALSE},
118 };
119 
120 /* ------------------------------------------------------------------ */
123  gerbv_project_t *returnProject= (gerbv_project_t *) g_new0(gerbv_project_t,1);
124 
125  /* default to using the current directory path for our starting guesses
126  on future file loads */
127  returnProject->path = g_get_current_dir ();
128  /* Will be updated to 0 when first Gerber is loaded */
129  returnProject->last_loaded = -1;
130  returnProject->max_files = 1;
131  returnProject->check_before_delete = TRUE;
132  returnProject->file = g_new0 (gerbv_fileinfo_t *, returnProject->max_files);
133 
134  return returnProject;
135 }
136 
137 /* ------------------------------------------------------------------ */
138 void
140  int i;
141 
142  /* destroy all the files attached to the project */
143  for(i = gerbvProject->last_loaded; i >= 0; i--) {
144  if (gerbvProject->file[i])
145  gerbv_destroy_fileinfo (gerbvProject->file[i]);
146  }
147  /* destroy strings */
148  g_free (gerbvProject->path);
149  g_free (gerbvProject->execname);
150  g_free (gerbvProject->execpath);
151  g_free (gerbvProject->project);
152  /* destroy the fileinfo array */
153  g_free (gerbvProject->file);
154  g_free (gerbvProject);
155 }
156 
157 /* ------------------------------------------------------------------ */
158 void
160  gerbv_destroy_image (fileInfo->image);
161  g_free (fileInfo->fullPathname);
162  g_free (fileInfo->name);
163  if (fileInfo->privateRenderData) {
164  cairo_surface_destroy ((cairo_surface_t *)
165  fileInfo->privateRenderData);
166  }
167 }
168 
169 /* ------------------------------------------------------------------ */
170 void
171 gerbv_open_layer_from_filename(gerbv_project_t *gerbvProject, gchar *filename)
172 {
173  gint idx_loaded;
174  dprintf("Opening filename = %s\n", (gchar *) filename);
175 
176  if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
177  GERB_MESSAGE(_("Could not read %s[%d]"), (gchar *) filename,
178  gerbvProject->last_loaded);
179  gerbvProject->last_loaded--;
180  } else {
181  idx_loaded = gerbvProject->last_loaded;
182  gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
183  dprintf(" Successfully opened file!\n");
184  }
185 } /* gerbv_open_layer_from_filename */
186 
187 /* ------------------------------------------------------------------ */
188 void
190  guint16 red, guint16 green, guint16 blue, guint16 alpha)
191 {
192  gint idx_loaded;
193  dprintf("Opening filename = %s\n", (gchar *) filename);
194 
195  if (gerbv_open_image(gerbvProject, filename, ++gerbvProject->last_loaded, FALSE, NULL, 0, TRUE) == -1) {
196  GERB_MESSAGE(_("Could not read %s[%d]"), (gchar *) filename,
197  gerbvProject->last_loaded);
198  gerbvProject->last_loaded--;
199  } else {
200  idx_loaded = gerbvProject->last_loaded;
201  gerbvProject->file[idx_loaded]->layer_dirty = FALSE;
202  GdkColor colorTemplate = {0, red, green, blue};
203  gerbvProject->file[idx_loaded]->color = colorTemplate;
204  gerbvProject->file[idx_loaded]->alpha = alpha;
205  dprintf(" Successfully opened file!\n");
206  }
207 } /* gerbv_open_layer_from_filename_with_color */
208 
209 /* ------------------------------------------------------------------ */
210 gboolean
211 gerbv_save_layer_from_index(gerbv_project_t *gerbvProject, gint index, gchar *filename)
212 {
213  if (strcmp (gerbvProject->file[index]->image->info->type,"RS274-X (Gerber) File")==0) {
214  gerbv_export_rs274x_file_from_image (filename, gerbvProject->file[index]->image,
215  &gerbvProject->file[index]->transform);
216  }
217  else if (strcmp (gerbvProject->file[index]->image->info->type,"Excellon Drill File")==0) {
218  gerbv_export_drill_file_from_image (filename, gerbvProject->file[index]->image,
219  &gerbvProject->file[index]->transform);
220  }
221  else {
222  return FALSE;
223  }
224  gerbvProject->file[index]->layer_dirty = FALSE;
225  return TRUE;
226 } /* gerbv_save_project_from_filename */
227 
228 
229 /* ------------------------------------------------------------------ */
230 int
231 gerbv_revert_file(gerbv_project_t *gerbvProject, int idx){
232  int rv;
233 
234  rv = gerbv_open_image(gerbvProject, gerbvProject->file[idx]->fullPathname, idx, TRUE, NULL, 0, TRUE);
235  gerbvProject->file[idx]->layer_dirty = FALSE;
236  return rv;
237 }
238 
239 /* ------------------------------------------------------------------ */
240 void
241 gerbv_revert_all_files(gerbv_project_t *gerbvProject)
242 {
243  int idx;
244 
245  for (idx = 0; idx <= gerbvProject->last_loaded; idx++) {
246  if (gerbvProject->file[idx] && gerbvProject->file[idx]->fullPathname) {
247  (void) gerbv_revert_file (gerbvProject, idx);
248  gerbvProject->file[idx]->layer_dirty = FALSE;
249  }
250  }
251 } /* gerbv_revert_all_files */
252 
253 /* ------------------------------------------------------------------ */
254 void
255 gerbv_unload_layer(gerbv_project_t *gerbvProject, int index)
256 {
257  gint i;
258 
259  gerbv_destroy_fileinfo (gerbvProject->file[index]);
260 
261  /* slide all later layers down to fill the empty slot */
262  for (i=index; i<(gerbvProject->last_loaded); i++) {
263  gerbvProject->file[i]=gerbvProject->file[i+1];
264  }
265  /* make sure the final spot is clear */
266  gerbvProject->file[gerbvProject->last_loaded] = NULL;
267  gerbvProject->last_loaded--;
268 } /* gerbv_unload_layer */
269 
270 /* ------------------------------------------------------------------ */
271 void
272 gerbv_unload_all_layers (gerbv_project_t *gerbvProject)
273 {
274  int index;
275 
276  /* Must count down since gerbv_unload_layer collapses
277  * layers down. Otherwise, layers slide past the index */
278  for (index = gerbvProject->last_loaded ; index >= 0; index--) {
279  if (gerbvProject->file[index] && gerbvProject->file[index]->name) {
280  gerbv_unload_layer (gerbvProject, index);
281  }
282  }
283 } /* gerbv_unload_all_layers */
284 
285 
286 /* ------------------------------------------------------------------ */
287 void
288 gerbv_change_layer_order(gerbv_project_t *gerbvProject, gint oldPosition, gint newPosition)
289 {
290  gerbv_fileinfo_t *temp_file;
291  int index;
292 
293  temp_file = gerbvProject->file[oldPosition];
294 
295  if (oldPosition < newPosition){
296  for (index = oldPosition; index < newPosition; index++) {
297  gerbvProject->file[index] = gerbvProject->file[index + 1];
298  }
299  } else {
300  for (index = oldPosition; index > newPosition; index--) {
301  gerbvProject->file[index] = gerbvProject->file[index - 1];
302  }
303  }
304  gerbvProject->file[newPosition] = temp_file;
305 } /* gerbv_change_layer_order */
306 
307 
308 /* ------------------------------------------------------------------ */
309 gint
310 gerbv_add_parsed_image_to_project (gerbv_project_t *gerbvProject, gerbv_image_t *parsed_image,
311  gchar *filename, gchar *baseName, int idx, int reload){
312  gerb_verify_error_t error = GERB_IMAGE_OK;
313  int r, g, b;
314 
315  dprintf("In open_image, now error check file....\n");
316  error = gerbv_image_verify(parsed_image);
317 
318  if (error) {
319  if (error & GERB_IMAGE_MISSING_NETLIST) {
320  GERB_COMPILE_ERROR(_("Missing netlist - aborting file read"));
321  gerbv_destroy_image(parsed_image);
322  return -1;
323  }
324  /* if the error was one of the following, try to open up the file anyways in case
325  the file is a poorly formatted RS-274X file */
326  if (error & GERB_IMAGE_MISSING_FORMAT)
327  g_warning(_("Missing format in file...trying to load anyways\n"));
328  if (error & GERB_IMAGE_MISSING_APERTURES) {
329  g_warning(_("Missing apertures/drill sizes...trying to load anyways\n"));
330  /* step through the file and check for aperture references. For each one found, create
331  a dummy aperture holder to visually draw something on the screen */
332  gerbv_image_create_dummy_apertures (parsed_image);
333  }
334  if (error & GERB_IMAGE_MISSING_INFO)
335  g_warning(_("Missing info...trying to load anyways\n"));
336  }
337 
338  /*
339  * If reload, just exchange the image. Else we have to allocate
340  * a new memory before we define anything more.
341  */
342  if (reload) {
343  gerbv_destroy_image(gerbvProject->file[idx]->image);
344  gerbvProject->file[idx]->image = parsed_image;
345  return 0;
346  } else {
347  /* Load new file. */
348  gerbvProject->file[idx] = (gerbv_fileinfo_t *) g_new0 (gerbv_fileinfo_t, 1);
349  gerbvProject->file[idx]->image = parsed_image;
350  }
351 
352  /*
353  * Store filename for eventual reload
354  */
355  gerbvProject->file[idx]->fullPathname = g_strdup (filename);
356  gerbvProject->file[idx]->name = g_strdup (baseName);
357 
358 
359  r = defaultColors[defaultColorIndex % NUMBER_OF_DEFAULT_COLORS].red*257;
360  g = defaultColors[defaultColorIndex % NUMBER_OF_DEFAULT_COLORS].green*257;
361  b = defaultColors[defaultColorIndex % NUMBER_OF_DEFAULT_COLORS].blue*257;
362 
363  GdkColor colorTemplate = {0, r, g, b};
364  gerbvProject->file[idx]->color = colorTemplate;
365  gerbvProject->file[idx]->alpha = defaultColors[defaultColorIndex % NUMBER_OF_DEFAULT_COLORS].alpha*257;
366  gerbvProject->file[idx]->isVisible = TRUE;
367  gerbvProject->file[idx]->transform = defaultTransformations[defaultColorIndex % NUMBER_OF_DEFAULT_TRANSFORMATIONS];
368  /* update the number of files if we need to */
369  if (gerbvProject->last_loaded <= idx) {
370  gerbvProject->last_loaded = idx;
371  }
372  defaultColorIndex++;
373  return 1;
374 }
375 
376 /* ------------------------------------------------------------------ */
377 int
378 gerbv_open_image(gerbv_project_t *gerbvProject, char *filename, int idx, int reload,
379  gerbv_HID_Attribute *fattr, int n_fattr, gboolean forceLoadFile)
380 {
381  gerb_file_t *fd;
382  gerbv_image_t *parsed_image = NULL, *parsed_image2 = NULL;
383  gint retv = -1;
384  gboolean isPnpFile = FALSE, foundBinary;
385  gerbv_HID_Attribute *attr_list = NULL;
386  int n_attr = 0;
387  /* If we're reloading, we'll pass in our file format attribute list
388  * since this is our hook for letting the user override the fileformat.
389  */
390  if (reload)
391  {
392  /* We're reloading so use the attribute list in memory */
393  attr_list = gerbvProject->file[idx]->image->info->attr_list;
394  n_attr = gerbvProject->file[idx]->image->info->n_attr;
395  }
396  else
397  {
398  /* We're not reloading so use the attribute list read from the
399  * project file if given or NULL otherwise.
400  */
401  attr_list = fattr;
402  n_attr = n_fattr;
403  }
404  /* if we don't have enough spots, then grow the file list by 2 to account for the possible
405  loading of two images for PNP files */
406  if ((idx+1) >= gerbvProject->max_files) {
407  gerbvProject->file = g_renew (gerbv_fileinfo_t *,
408  gerbvProject->file, gerbvProject->max_files + 2);
409 
410  gerbvProject->file[gerbvProject->max_files] = NULL;
411  gerbvProject->file[gerbvProject->max_files+1] = NULL;
412  gerbvProject->max_files += 2;
413  }
414 
415  dprintf("In open_image, about to try opening filename = %s\n", filename);
416 
417  fd = gerb_fopen(filename);
418  if (fd == NULL) {
419  GERB_MESSAGE(_("Trying to open %s: %s"), filename, strerror(errno));
420  return -1;
421  }
422 
423  /* Store filename info fd for further use */
424  fd->filename = g_strdup(filename);
425 
426  dprintf("In open_image, successfully opened file. Now check its type....\n");
427  /* Here's where we decide what file type we have */
428  /* Note: if the file has some invalid characters in it but still appears to
429  be a valid file, we check with the user if he wants to continue (only
430  if user opens the layer from the menu...if from the command line, we go
431  ahead and try to load it anyways) */
432 
433  if (gerber_is_rs274x_p(fd, &foundBinary)) {
434  dprintf("Found RS-274X file\n");
435  if (!foundBinary || forceLoadFile) {
436  /* figure out the directory path in case parse_gerb needs to
437  * load any include files */
438  gchar *currentLoadDirectory = g_path_get_dirname (filename);
439  parsed_image = parse_gerb(fd, currentLoadDirectory);
440  g_free (currentLoadDirectory);
441  }
442  } else if(drill_file_p(fd, &foundBinary)) {
443  dprintf("Found drill file\n");
444  if (!foundBinary || forceLoadFile)
445  parsed_image = parse_drillfile(fd, attr_list, n_attr, reload);
446 
447  } else if (pick_and_place_check_file_type(fd, &foundBinary)) {
448  dprintf("Found pick-n-place file\n");
449  if (!foundBinary || forceLoadFile) {
450  if (!reload) {
451  pick_and_place_parse_file_to_images(fd, &parsed_image, &parsed_image2);
452  } else {
453  switch (gerbvProject->file[idx]->image->layertype) {
455  /* Non NULL pointer is used as "not to reload" mark */
456  parsed_image2 = (void *)!NULL;
457  pick_and_place_parse_file_to_images(fd, &parsed_image, &parsed_image2);
458  parsed_image2 = NULL;
459  break;
461  /* Non NULL pointer is used as "not to reload" mark */
462  parsed_image2 = (void *)!NULL;
463  pick_and_place_parse_file_to_images(fd, &parsed_image2, &parsed_image);
464  parsed_image2 = NULL;
465  break;
466  default:
467  GERB_COMPILE_ERROR(_("%s: unknown pick-and-place board side to reload"), filename);
468  }
469  }
470 
471  isPnpFile = TRUE;
472  }
473  } else if (gerber_is_rs274d_p(fd)) {
474  dprintf("Most likely found a RS-274D file...trying to open anyways\n");
475  g_warning(_("Most likely found a RS-274D file...trying to open anyways\n"));
476  if (!foundBinary || forceLoadFile) {
477  /* figure out the directory path in case parse_gerb needs to
478  * load any include files */
479  gchar *currentLoadDirectory = g_path_get_dirname (filename);
480  parsed_image = parse_gerb(fd, currentLoadDirectory);
481  g_free (currentLoadDirectory);
482  }
483  } else {
484  /* This is not a known file */
485  dprintf("Unknown filetype");
486  GERB_COMPILE_ERROR(_("%s: Unknown file type."), filename);
487  parsed_image = NULL;
488  }
489 
490  gerb_fclose(fd);
491  if (parsed_image == NULL) {
492  return -1;
493  }
494 
495  if (parsed_image) {
496  /* strip the filename to the base */
497  gchar *baseName = g_path_get_basename (filename);
498  gchar *displayedName;
499  if (isPnpFile)
500  displayedName = g_strconcat (baseName, _(" (top)"), NULL);
501  else
502  displayedName = g_strdup (baseName);
503  retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image, filename, displayedName, idx, reload);
504  g_free (baseName);
505  g_free (displayedName);
506  }
507 
508  /* Set layer_dirty flag to FALSE */
509  gerbvProject->file[idx]->layer_dirty = FALSE;
510 
511  /* for PNP place files, we may need to add a second image for the other
512  board side */
513  if (parsed_image2) {
514  /* strip the filename to the base */
515  gchar *baseName = g_path_get_basename (filename);
516  gchar *displayedName;
517  displayedName = g_strconcat (baseName, _(" (bottom)"), NULL);
518  retv = gerbv_add_parsed_image_to_project (gerbvProject, parsed_image2, filename, displayedName, idx + 1, reload);
519  g_free (baseName);
520  g_free (displayedName);
521  }
522 
523  return retv;
524 } /* open_image */
525 
528  gerbv_image_t *returnImage;
529  gerb_file_t *fd;
530 
531  fd = gerb_fopen(filename);
532  if (fd == NULL) {
533  GERB_MESSAGE(_("Trying to open %s: %s"), filename, strerror(errno));
534  return NULL;
535  }
536  gchar *currentLoadDirectory = g_path_get_dirname (filename);
537  returnImage = parse_gerb(fd, currentLoadDirectory);
538  g_free (currentLoadDirectory);
539  gerb_fclose(fd);
540  return returnImage;
541 }
542 
543 /* ------------------------------------------------------------------ */
544 void
545 gerbv_render_get_boundingbox(gerbv_project_t *gerbvProject, gerbv_render_size_t *boundingbox)
546 {
547  double x1=HUGE_VAL,y1=HUGE_VAL, x2=-HUGE_VAL,y2=-HUGE_VAL;
548  int i;
549  gerbv_image_info_t *info;
550  gdouble minX, minY, maxX, maxY;
551 
552  for(i = 0; i <= gerbvProject->last_loaded; i++) {
553  if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible){
554 
555 
556  info = gerbvProject->file[i]->image->info;
557  /*
558  * Find the biggest image and use as a size reference
559  */
560  /* cairo info already has offset calculated into min/max */
561 
562  minX = info->min_x;
563  minY = info->min_y;
564  maxX = info->max_x;
565  maxY = info->max_y;
566 
567  if (!isnormal(minX)||!isnormal(minY)||!isnormal(maxX)||!isnormal(maxY)){
568  continue;
569  }
570  /* transform the bounding box based on the user transform */
571 
572  cairo_matrix_t fullMatrix;
573  cairo_matrix_init (&fullMatrix, 1, 0, 0, 1, 0, 0);
574 
575  cairo_matrix_translate (&fullMatrix, gerbvProject->file[i]->transform.translateX,
576  gerbvProject->file[i]->transform.translateY);
577  // don't use mirroring for the scale matrix
578  gdouble scaleX = gerbvProject->file[i]->transform.scaleX;
579  gdouble scaleY = gerbvProject->file[i]->transform.scaleY;
580  if (gerbvProject->file[i]->transform.mirrorAroundX)
581  scaleY *= -1;
582  if (gerbvProject->file[i]->transform.mirrorAroundY)
583  scaleX *= -1;
584  cairo_matrix_scale (&fullMatrix, scaleX, scaleY);
585  cairo_matrix_rotate (&fullMatrix, gerbvProject->file[i]->transform.rotation);
586 
587  cairo_matrix_transform_point (&fullMatrix, &minX, &minY);
588  cairo_matrix_transform_point (&fullMatrix, &maxX, &maxY);
589  /* compare to both min and max, since a mirror transform may have made the "max"
590  number smaller than the "min" */
591  x1 = MIN(x1, minX);
592  x1 = MIN(x1, maxX);
593  y1 = MIN(y1, minY);
594  y1 = MIN(y1, maxY);
595  x2 = MAX(x2, minX);
596  x2 = MAX(x2, maxX);
597  y2 = MAX(y2, minY);
598  y2 = MAX(y2, maxY);
599  }
600  }
601  boundingbox->left = x1;
602  boundingbox->right = x2;
603  boundingbox->top = y1;
604  boundingbox->bottom = y2;
605 }
606 
607 /* ------------------------------------------------------------------ */
608 void
611  double width, height;
612  double x_scale, y_scale;
613 
614  /* Grab maximal width and height of all layers */
615  gerbv_render_get_boundingbox(gerbvProject, &bb);
616  width = bb.right - bb.left;
617  height = bb.bottom - bb.top;
618  /* add in a 5% buffer around the drawing */
619  width *= 1.05;
620  height *=1.05;
621 
622  /* if the values aren't sane (probably we have no models loaded), then
623  put in some defaults */
624  if (!isnormal(width)||!isnormal(height)||((width < 0.01) && (height < 0.01))) {
625  renderInfo->lowerLeftX = 0.0;
626  renderInfo->lowerLeftY = 0.0;
627  renderInfo->scaleFactorX = 200;
628  renderInfo->scaleFactorY = 200;
629  return;
630  }
631  /*
632  * Calculate scale for both x axis and y axis
633  */
634  x_scale = renderInfo->displayWidth / width;
635  y_scale = renderInfo->displayHeight / height;
636  /*
637  * Take the scale that fits both directions with some extra checks
638  */
639  renderInfo->scaleFactorX = MIN(x_scale, y_scale);
640  renderInfo->scaleFactorY = renderInfo->scaleFactorX;
641  if (renderInfo->scaleFactorX < 1){
642  renderInfo->scaleFactorX = 1;
643  renderInfo->scaleFactorY = 1;
644  }
645  renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
646  ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
647  renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
648  ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
649 }
650 
651 /* ------------------------------------------------------------------ */
652 void
653 gerbv_render_translate_to_fit_display (gerbv_project_t *gerbvProject, gerbv_render_info_t *renderInfo) {
655 
656  /* Grab maximal width and height of all layers */
657  gerbv_render_get_boundingbox(gerbvProject, &bb);
658  renderInfo->lowerLeftX = ((bb.left + bb.right) / 2.0) -
659  ((double) renderInfo->displayWidth / 2.0 / renderInfo->scaleFactorX);
660  renderInfo->lowerLeftY = ((bb.top + bb.bottom) / 2.0) -
661  ((double) renderInfo->displayHeight / 2.0 / renderInfo->scaleFactorY);
662 }
663 
664 /* ------------------------------------------------------------------ */
665 void
666 gerbv_render_to_pixmap_using_gdk (gerbv_project_t *gerbvProject, GdkPixmap *pixmap,
667  gerbv_render_info_t *renderInfo, gerbv_selection_info_t *selectionInfo,
668  GdkColor *selectionColor){
669  GdkGC *gc = gdk_gc_new(pixmap);
670  GdkPixmap *colorStamp, *clipmask;
671  int i;
672 
673  /*
674  * Remove old pixmap, allocate a new one, draw the background.
675  */
676  if (!gerbvProject->background.pixel)
677  gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->background, FALSE, TRUE);
678  gdk_gc_set_foreground(gc, &gerbvProject->background);
679  gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, -1, -1);
680 
681  /*
682  * Allocate the pixmap and the clipmask (a one pixel pixmap)
683  */
684  colorStamp = gdk_pixmap_new(pixmap, renderInfo->displayWidth,
685  renderInfo->displayHeight, -1);
686  clipmask = gdk_pixmap_new(NULL, renderInfo->displayWidth,
687  renderInfo->displayHeight, 1);
688 
689  /*
690  * This now allows drawing several layers on top of each other.
691  * Higher layer numbers have higher priority in the Z-order.
692  */
693  for(i = gerbvProject->last_loaded; i >= 0; i--) {
694  if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
695  /*
696  * Fill up image with all the foreground color. Excess pixels
697  * will be removed by clipmask.
698  */
699  if (!gerbvProject->file[i]->color.pixel)
700  gdk_colormap_alloc_color(gdk_colormap_get_system(), &gerbvProject->file[i]->color, FALSE, TRUE);
701  gdk_gc_set_foreground(gc, &gerbvProject->file[i]->color);
702 
703  /* switch back to regular draw function for the initial
704  bitmap clear */
705  gdk_gc_set_function(gc, GDK_COPY);
706  gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
707 
708  if (renderInfo->renderType == GERBV_RENDER_TYPE_GDK) {
709  gdk_gc_set_function(gc, GDK_COPY);
710  }
711  else if (renderInfo->renderType == GERBV_RENDER_TYPE_GDK_XOR) {
712  gdk_gc_set_function(gc, GDK_XOR);
713  }
714  /*
715  * Translation is to get it inside the allocated pixmap,
716  * which is not always centered perfectly for GTK/X.
717  */
718  dprintf(" .... calling image2pixmap on image %d...\n", i);
719  // Dirty scaling solution when using GDK; simply use scaling factor for x-axis, ignore y-axis
720  draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[i]->image,
721  renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
722  (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
723  DRAW_IMAGE, NULL, renderInfo, gerbvProject->file[i]->transform);
724 
725  /*
726  * Set clipmask and draw the clipped out image onto the
727  * screen pixmap. Afterwards we remove the clipmask, else
728  * it will screw things up when run this loop again.
729  */
730  gdk_gc_set_clip_mask(gc, clipmask);
731  gdk_gc_set_clip_origin(gc, 0, 0);
732  gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
733  gdk_gc_set_clip_mask(gc, NULL);
734  }
735  }
736  /* render the selection group to the top of the output */
737  if (selectionInfo && (selectionInfo->type != GERBV_SELECTION_EMPTY)) {
738  if (!selectionColor->pixel)
739  gdk_colormap_alloc_color(gdk_colormap_get_system(), selectionColor, FALSE, TRUE);
740 
741  gdk_gc_set_foreground(gc, selectionColor);
742  gdk_gc_set_function(gc, GDK_COPY);
743  gdk_draw_rectangle(colorStamp, gc, TRUE, 0, 0, -1, -1);
744 
745  /* for now, assume everything in the selection buffer is from one image */
746  gerbv_image_t *matchImage;
747  int j;
748  if (selectionInfo->selectedNodeArray->len > 0) {
749  gerbv_selection_item_t sItem = g_array_index (selectionInfo->selectedNodeArray,
750  gerbv_selection_item_t, 0);
751  matchImage = (gerbv_image_t *) sItem.image;
752 
753  for(j = gerbvProject->last_loaded; j >= 0; j--) {
754  if (gerbvProject->file[j] && (gerbvProject->file[j]->image == matchImage)) {
755  draw_gdk_image_to_pixmap(&clipmask, gerbvProject->file[j]->image,
756  renderInfo->scaleFactorX, -(renderInfo->lowerLeftX * renderInfo->scaleFactorX),
757  (renderInfo->lowerLeftY * renderInfo->scaleFactorY) + renderInfo->displayHeight,
758  DRAW_SELECTIONS, selectionInfo,
759  renderInfo, gerbvProject->file[j]->transform);
760  }
761  }
762  gdk_gc_set_clip_mask(gc, clipmask);
763  gdk_gc_set_clip_origin(gc, 0, 0);
764  gdk_draw_drawable(pixmap, gc, colorStamp, 0, 0, 0, 0, -1, -1);
765  gdk_gc_set_clip_mask(gc, NULL);
766  }
767  }
768 
769  gdk_pixmap_unref(colorStamp);
770  gdk_pixmap_unref(clipmask);
771  gdk_gc_unref(gc);
772 }
773 
774 /* ------------------------------------------------------------------ */
775 void
776 gerbv_render_all_layers_to_cairo_target_for_vector_output (gerbv_project_t *gerbvProject,
777  cairo_t *cr, gerbv_render_info_t *renderInfo) {
778  int i;
779  gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
780  /* don't paint background for vector output, since it isn't needed */
781  for(i = gerbvProject->last_loaded; i >= 0; i--) {
782  if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
783 
784  gerbv_render_layer_to_cairo_target_without_transforming(cr, gerbvProject->file[i], renderInfo, FALSE);
785  }
786  }
787 }
788 
789 /* ------------------------------------------------------------------ */
790 void
791 gerbv_render_all_layers_to_cairo_target (gerbv_project_t *gerbvProject, cairo_t *cr,
792  gerbv_render_info_t *renderInfo) {
793  int i;
794  /* fill the background with the appropriate color */
795  cairo_set_source_rgba (cr, (double) gerbvProject->background.red/G_MAXUINT16,
796  (double) gerbvProject->background.green/G_MAXUINT16,
797  (double) gerbvProject->background.blue/G_MAXUINT16, 1);
798  cairo_paint (cr);
799  for(i = gerbvProject->last_loaded; i >= 0; i--) {
800  if (gerbvProject->file[i] && gerbvProject->file[i]->isVisible) {
801  cairo_push_group (cr);
802  gerbv_render_layer_to_cairo_target (cr, gerbvProject->file[i], renderInfo);
803  cairo_pop_group_to_source (cr);
804  cairo_paint_with_alpha (cr, (double) gerbvProject->file[i]->alpha/G_MAXUINT16);
805  }
806  }
807 }
808 
809 /* ------------------------------------------------------------------ */
810 void
812  gerbv_render_info_t *renderInfo) {
813  gerbv_render_cairo_set_scale_and_translation(cr, renderInfo);
814  gerbv_render_layer_to_cairo_target_without_transforming(cr, fileInfo, renderInfo, TRUE);
815 }
816 
817 /* ------------------------------------------------------------------ */
818 void
819 gerbv_render_cairo_set_scale_and_translation(cairo_t *cr, gerbv_render_info_t *renderInfo){
820  gdouble translateX, translateY;
821 
822  translateX = (renderInfo->lowerLeftX * renderInfo->scaleFactorX);
823  translateY = (renderInfo->lowerLeftY * renderInfo->scaleFactorY);
824 
825  /* renderTypes 0 and 1 use GDK rendering, so we shouldn't have made it
826  this far */
827  if (renderInfo->renderType == GERBV_RENDER_TYPE_CAIRO_NORMAL) {
828  cairo_set_tolerance (cr, 1.0);
829  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
830  }
831  else if (renderInfo->renderType == GERBV_RENDER_TYPE_CAIRO_HIGH_QUALITY) {
832  cairo_set_tolerance (cr, 0.1);
833  cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
834  }
835 
836  /* translate the draw area before drawing. We must translate the whole
837  drawing down an additional displayHeight to account for the negative
838  y flip done later */
839  cairo_translate (cr, -translateX, translateY + renderInfo->displayHeight);
840  /* scale the drawing by the specified scale factor (inverting y since
841  cairo y axis points down) */
842  cairo_scale (cr, renderInfo->scaleFactorX, -renderInfo->scaleFactorY);
843 }
844 
845 /* ------------------------------------------------------------------ */
846 void
847 gerbv_render_layer_to_cairo_target_without_transforming(cairo_t *cr, gerbv_fileinfo_t *fileInfo, gerbv_render_info_t *renderInfo, gboolean pixelOutput) {
848  cairo_set_source_rgba (cr, (double) fileInfo->color.red/G_MAXUINT16,
849  (double) fileInfo->color.green/G_MAXUINT16,
850  (double) fileInfo->color.blue/G_MAXUINT16, 1);
851 
852  /* translate, rotate, and modify the image based on the layer-specific transformation struct */
853  cairo_save (cr);
854 
855  draw_image_to_cairo_target (cr, fileInfo->image,
856  1.0/MAX(renderInfo->scaleFactorX, renderInfo->scaleFactorY), DRAW_IMAGE, NULL,
857  renderInfo, TRUE, fileInfo->transform, pixelOutput);
858  cairo_restore (cr);
859 }
860 
861 void
862 gerbv_attribute_destroy_HID_attribute (gerbv_HID_Attribute *attributeList, int n_attr)
863 {
864  int i;
865 
866  /* free the string attributes */
867  for (i = 0 ; i < n_attr ; i++) {
868  if ( (attributeList[i].type == HID_String ||
869  attributeList[i].type == HID_Label) &&
870  attributeList[i].default_val.str_value != NULL) {
871  free (attributeList[i].default_val.str_value);
872  }
873  }
874 
875  /* and free the attribute list */
876  if (attributeList != NULL) {
877  free (attributeList);
878  }
879 }
880 
881 
882 /* allocate memory and make a copy of an attribute list */
883 gerbv_HID_Attribute *
884 gerbv_attribute_dup (gerbv_HID_Attribute *attributeList, int n_attr)
885 {
886  gerbv_HID_Attribute *nl;
887  int i;
888 
889  nl = (gerbv_HID_Attribute *) malloc (n_attr * sizeof (gerbv_HID_Attribute));
890  if (nl == NULL) {
891  fprintf (stderr, _("%s(): malloc failed\n"), __FUNCTION__);
892  exit (1);
893  }
894 
895  /* copy the attribute list being sure to strdup the strings */
896  for (i = 0 ; i < n_attr ; i++) {
897 
898  if (attributeList[i].type == HID_String ||
899  attributeList[i].type == HID_Label) {
900 
901  if (attributeList[i].default_val.str_value != NULL) {
902  nl[i].default_val.str_value = strdup (attributeList[i].default_val.str_value);
903  } else {
904  nl[i].default_val.str_value = NULL;
905  }
906 
907  } else {
908  nl[i] = attributeList[i];
909  }
910  }
911 
912  return nl;
913 }
914 
915