gerbv
draw.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 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <math.h> /* ceil(), atan2() */
32 
33 #ifdef HAVE_STRING_H
34 # include <string.h>
35 #endif
36 
37 #include "gerbv.h"
38 #include "draw.h"
39 #include "common.h"
40 #include "selection.h"
41 
42 #undef DPRINTF
43 #define DPRINTF(...) do { if (DEBUG) printf(__VA_ARGS__); } while (0)
44 
45 static gboolean draw_do_vector_export_fix(cairo_t *cairoTarget,
46  double *bg_red, double *bg_green, double *bg_blue);
47 
54 void
55 draw_cairo_line_to (cairo_t *cairoTarget, gdouble x, gdouble y,
56  gboolean adjustByHalf, gboolean pixelOutput)
57 {
58  if (pixelOutput) {
59  cairo_user_to_device (cairoTarget, &x, &y);
60  x = round(x);
61  y = round(y);
62  if (adjustByHalf) {
63  x += 0.5;
64  y += 0.5;
65  }
66  cairo_device_to_user (cairoTarget, &x, &y);
67  }
68  cairo_line_to (cairoTarget, x, y);
69 }
70 
77 void
78 draw_cairo_move_to (cairo_t *cairoTarget, gdouble x, gdouble y,
79  gboolean oddWidth, gboolean pixelOutput)
80 {
81  if (pixelOutput) {
82  cairo_user_to_device (cairoTarget, &x, &y);
83  x = round(x);
84  y = round(y);
85  if (oddWidth) {
86  x += 0.5;
87  y += 0.5;
88  }
89  cairo_device_to_user (cairoTarget, &x, &y);
90  }
91  cairo_move_to (cairoTarget, x, y);
92 }
93 
99 void
100 draw_cairo_translate_adjust (cairo_t *cairoTarget, gdouble x, gdouble y,
101  gboolean pixelOutput)
102 {
103  if (pixelOutput) {
104  cairo_user_to_device (cairoTarget, &x, &y);
105  x = round(x);
106  y = round(y);
107  cairo_device_to_user (cairoTarget, &x, &y);
108  }
109 
110  cairo_translate (cairoTarget, x, y);
111 }
112 
119 static gboolean
121  gerbv_selection_info_t *selectionInfo, gboolean remove)
122 {
123  gerbv_selection_item_t sItem;
124 
125  for (guint i = 0; i < selection_length (selectionInfo); i++) {
126  sItem = selection_get_item_by_index (selectionInfo, i);
127  if (sItem.net == net) {
128  if (remove)
129  selection_clear_item_by_index (selectionInfo, i);
130 
131  return TRUE;
132  }
133  }
134 
135  return FALSE;
136 }
137 
138 static void
139 draw_check_if_object_is_in_selected_area (cairo_t *cairoTarget,
140  gboolean isStroke, gerbv_selection_info_t *selectionInfo,
141  gerbv_image_t *image, struct gerbv_net *net,
142  enum draw_mode drawMode)
143 {
144  gerbv_selection_item_t sItem = {image, net};
145  gdouble corner1X, corner1Y, corner2X, corner2Y;
146  gdouble x1, x2, y1, y2;
147  gdouble minX, minY, maxX, maxY;
148 
149  corner1X = selectionInfo->lowerLeftX;
150  corner1Y = selectionInfo->lowerLeftY;
151  corner2X = selectionInfo->upperRightX;
152  corner2Y = selectionInfo->upperRightY;
153 
154  /* calculate the coordinate of the user's click in the current
155  transformation matrix */
156  cairo_device_to_user (cairoTarget, &corner1X, &corner1Y);
157  cairo_device_to_user (cairoTarget, &corner2X, &corner2Y);
158 
159  switch (selectionInfo->type) {
161  /* use the cairo in_fill routine to see if the point is within the
162  drawn area */
163  if ((isStroke && cairo_in_stroke (cairoTarget, corner1X, corner1Y)) ||
164  (!isStroke && cairo_in_fill (cairoTarget, corner1X, corner1Y))) {
165 
167  selectionInfo,
168  (drawMode == FIND_SELECTIONS_TOGGLE))) {
169  selection_add_item (selectionInfo, &sItem);
170  }
171  }
172  break;
173 
175  /* we can't assume the "lowerleft" corner is actually in the lower left,
176  since the cairo transformation matrix may be mirrored,etc */
177  minX = MIN(corner1X,corner2X);
178  maxX = MAX(corner1X,corner2X);
179  minY = MIN(corner1Y,corner2Y);
180  maxY = MAX(corner1Y,corner2Y);
181 
182  if (isStroke)
183  cairo_stroke_extents (cairoTarget, &x1, &y1, &x2, &y2);
184  else
185  cairo_fill_extents (cairoTarget, &x1, &y1, &x2, &y2);
186 
187  if ((minX < x1) && (minY < y1) && (maxX > x2) && (maxY > y2)) {
189  selectionInfo,
190  (drawMode == FIND_SELECTIONS_TOGGLE))) {
191  selection_add_item (selectionInfo, &sItem);
192  }
193  }
194  break;
195  default:
196  break;
197  }
198  /* clear the path, since we didn't actually draw it and cairo
199  doesn't reset it after the previous calls */
200  cairo_new_path (cairoTarget);
201 }
202 
203 static void
204 draw_fill (cairo_t *cairoTarget, enum draw_mode drawMode,
205  gerbv_selection_info_t *selectionInfo,
206  gerbv_image_t *image, struct gerbv_net *net)
207 {
208  if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
209  cairo_fill (cairoTarget);
210  else
211  draw_check_if_object_is_in_selected_area (cairoTarget, FALSE,
212  selectionInfo, image, net, drawMode);
213 }
214 
215 static void
216 draw_stroke (cairo_t *cairoTarget, enum draw_mode drawMode,
217  gerbv_selection_info_t *selectionInfo,
218  gerbv_image_t *image, struct gerbv_net *net)
219 {
220  if ((drawMode == DRAW_IMAGE) || (drawMode == DRAW_SELECTIONS))
221  cairo_stroke (cairoTarget);
222  else
223  draw_check_if_object_is_in_selected_area (cairoTarget, TRUE,
224  selectionInfo, image, net, drawMode);
225 }
226 
230 static void
231 gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
232 {
233  cairo_arc (cairoTarget, 0.0, 0.0, diameter/2.0, 0, 2.0*M_PI);
234 
235  return;
236 }
237 
243 static void
244 gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width, gdouble height,
245  gboolean pixelOutput)
246 {
247  if (pixelOutput) {
248  cairo_user_to_device_distance (cairoTarget, &width, &height);
249  width -= (int)round(width) % 2;
250  height -= (int)round(height) % 2;
251  cairo_device_to_user_distance (cairoTarget, &width, &height);
252  }
253 
254  cairo_rectangle (cairoTarget, -width/2.0, -height/2.0, width, height);
255 
256  return;
257 }
258 
263 static void
264 gerbv_draw_oblong(cairo_t *cairoTarget, gdouble width, gdouble height)
265 {
266  /* --- This stuff produces a line + rounded ends --- */
267  gdouble circleDiameter, strokeDistance;
268 
269  cairo_new_path (cairoTarget);
270  if (width < height) {
271  circleDiameter = width;
272  strokeDistance = (height - width)/2.0;
273  cairo_arc (cairoTarget, 0.0, strokeDistance, circleDiameter/2.0, 0, -M_PI);
274  cairo_line_to (cairoTarget, -circleDiameter/2.0, -strokeDistance);
275  cairo_arc (cairoTarget, 0.0, -strokeDistance, circleDiameter/2.0, -M_PI, 0);
276  cairo_line_to (cairoTarget, circleDiameter/2.0, strokeDistance);
277  } else {
278  circleDiameter = height;
279  strokeDistance = (width - height)/2.0;
280  cairo_arc (cairoTarget, -strokeDistance, 0.0,
281  circleDiameter/2.0, M_PI_2, -M_PI_2);
282  cairo_line_to (cairoTarget, strokeDistance,
283  -circleDiameter/2.0);
284  cairo_arc (cairoTarget, strokeDistance, 0.0,
285  circleDiameter/2.0, -M_PI_2, M_PI_2);
286  cairo_line_to (cairoTarget, -strokeDistance,
287  circleDiameter/2.0);
288  }
289 
290 #if 0
291  /* --- This stuff produces an oval pad --- */
292  /* cairo doesn't have a function to draw ovals, so we must
293  * draw an arc and stretch it by scaling different x and y values */
294  cairo_save (cairoTarget);
295  cairo_scale (cairoTarget, width, height);
296  gerbv_draw_circle (cairoTarget, 1);
297  cairo_restore (cairoTarget);
298 #endif
299  return;
300 }
301 
302 static void
303 gerbv_draw_polygon(cairo_t *cairoTarget, gdouble outsideDiameter,
304  gdouble numberOfSides, gdouble degreesOfRotation)
305 {
306  int i, numberOfSidesInteger = (int) numberOfSides;
307 
308  cairo_rotate(cairoTarget, DEG2RAD(degreesOfRotation));
309  cairo_move_to(cairoTarget, outsideDiameter / 2.0, 0);
310 
311  /* skip first point, since we've moved there already */
312  for (i = 1; i < numberOfSidesInteger; i++){
313  gdouble angle = ((double)i)*M_PI*2.0 / numberOfSidesInteger;
314  cairo_line_to (cairoTarget, cos(angle) * outsideDiameter / 2.0,
315  sin(angle) * outsideDiameter / 2.0);
316  }
317 
318  return;
319 }
320 
321 
322 static void
323 gerbv_draw_aperture_hole(cairo_t *cairoTarget,
324  gdouble dimensionX, gdouble dimensionY, gboolean pixelOutput)
325 {
326  if (dimensionX) {
327  cairo_new_sub_path (cairoTarget);
328  if (dimensionY)
329  gerbv_draw_rectangle (cairoTarget,
330  dimensionX, dimensionY, pixelOutput);
331  else
332  gerbv_draw_circle (cairoTarget, dimensionX);
333  }
334 
335  return;
336 }
337 
338 static void
339 draw_update_macro_exposure (cairo_t *cairoTarget, cairo_operator_t clearOperator,
340  cairo_operator_t darkOperator, gdouble exposureSetting){
341 
342  if (exposureSetting == 0.0) {
343  cairo_set_operator (cairoTarget, clearOperator);
344  } else if (exposureSetting == 1.0) {
345  cairo_set_operator (cairoTarget, darkOperator);
346  } else if (exposureSetting == 2.0) {
347  /* reverse current exposure setting */
348  cairo_operator_t currentOperator = cairo_get_operator (cairoTarget);
349  if (currentOperator == clearOperator) {
350  cairo_set_operator (cairoTarget, darkOperator);
351  } else {
352  cairo_set_operator (cairoTarget, clearOperator);
353  }
354  }
355 }
356 
357 
358 static int
359 gerbv_draw_amacro(cairo_t *cairoTarget, cairo_operator_t clearOperator,
360  cairo_operator_t darkOperator, gerbv_simplified_amacro_t *s,
361  gint usesClearPrimitive, gdouble pixelWidth, enum draw_mode drawMode,
362  gerbv_selection_info_t *selectionInfo,
363  gerbv_image_t *image, struct gerbv_net *net)
364 {
365  gerbv_simplified_amacro_t *ls = s;
366  gboolean doVectorExportFix;
367  double bg_r, bg_g, bg_b; /* Background color */
368  int ret = 1;
369 
370  DPRINTF("Drawing simplified aperture macros:\n");
371 
372  doVectorExportFix =
373  draw_do_vector_export_fix (cairoTarget, &bg_r, &bg_g, &bg_b);
374 
375  switch (cairo_surface_get_type (cairo_get_target (cairoTarget))) {
376 
377  case CAIRO_SURFACE_TYPE_PDF:
378  case CAIRO_SURFACE_TYPE_PS:
379  case CAIRO_SURFACE_TYPE_SVG:
380 
381  /* Don't limit "pixel width" in vector export */
382  pixelWidth = DBL_MIN;
383 
384  break;
385 
386  default:
387  break;
388  }
389 
390  if (usesClearPrimitive)
391  cairo_push_group (cairoTarget);
392 
393  while (ls != NULL) {
394  /*
395  * This handles the exposure thing in the aperture macro
396  * The exposure is always the first element on stack independent
397  * of aperture macro.
398  */
399  cairo_save (cairoTarget);
400  cairo_new_path(cairoTarget);
401 
402  DPRINTF("\t%s(): drawing %s\n", __FUNCTION__,
403  gerbv_aperture_type_name(ls->type));
404 
405  switch (ls->type) {
406 
408  draw_update_macro_exposure (cairoTarget,
409  clearOperator, darkOperator,
410  ls->parameter[CIRCLE_EXPOSURE]);
411  cairo_rotate (cairoTarget, DEG2RAD(
412  ls->parameter[CIRCLE_ROTATION]));
413  cairo_translate (cairoTarget,
414  ls->parameter[CIRCLE_CENTER_X],
415  ls->parameter[CIRCLE_CENTER_Y]);
416  gerbv_draw_circle (cairoTarget,
417  ls->parameter[CIRCLE_DIAMETER]);
418 
419  if (doVectorExportFix
420  && CAIRO_OPERATOR_CLEAR ==
421  cairo_get_operator (cairoTarget)) {
422  cairo_save (cairoTarget);
423  cairo_set_source_rgba (cairoTarget,
424  bg_r, bg_g, bg_b, 1.0);
425  cairo_set_operator (cairoTarget,
426  CAIRO_OPERATOR_OVER);
427 
428  draw_fill (cairoTarget, drawMode,
429  selectionInfo, image, net);
430 
431  cairo_restore (cairoTarget);
432 
433  break;
434  }
435 
436  draw_fill (cairoTarget, drawMode,
437  selectionInfo, image, net);
438  break;
439 
441  draw_update_macro_exposure (cairoTarget,
442  clearOperator, darkOperator,
443  ls->parameter[OUTLINE_EXPOSURE]);
444  cairo_rotate (cairoTarget, DEG2RAD(ls->parameter[
445  OUTLINE_ROTATION_IDX(ls->parameter)]));
446  cairo_move_to (cairoTarget,
447  ls->parameter[OUTLINE_FIRST_X],
448  ls->parameter[OUTLINE_FIRST_Y]);
449 
450  for (int point = 1; point <
451  1 + (int)ls->parameter[
452  OUTLINE_NUMBER_OF_POINTS];
453  point++) {
454  cairo_line_to (cairoTarget,
455  ls->parameter[OUTLINE_X_IDX_OF_POINT(
456  point)],
457  ls->parameter[OUTLINE_Y_IDX_OF_POINT(
458  point)]);
459  }
460 
461  if (doVectorExportFix
462  && CAIRO_OPERATOR_CLEAR ==
463  cairo_get_operator (cairoTarget)) {
464  cairo_save (cairoTarget);
465  cairo_set_source_rgba (cairoTarget,
466  bg_r, bg_g, bg_b, 1.0);
467  cairo_set_operator (cairoTarget,
468  CAIRO_OPERATOR_OVER);
469 
470  draw_fill (cairoTarget, drawMode,
471  selectionInfo, image, net);
472 
473  cairo_restore (cairoTarget);
474 
475  break;
476  }
477 
478  /* Although the gerber specs allow for an open outline,
479  * I interpret it to mean the outline should be closed
480  * by the rendering softare automatically, since there
481  * is no dimension for line thickness. */
482  draw_fill (cairoTarget, drawMode,
483  selectionInfo, image, net);
484  break;
485 
487  draw_update_macro_exposure (cairoTarget,
488  clearOperator, darkOperator,
489  ls->parameter[POLYGON_EXPOSURE]);
490  cairo_translate (cairoTarget,
491  ls->parameter[POLYGON_CENTER_X],
492  ls->parameter[POLYGON_CENTER_Y]);
493  gerbv_draw_polygon(cairoTarget,
494  ls->parameter[POLYGON_DIAMETER],
495  ls->parameter[POLYGON_NUMBER_OF_POINTS],
496  ls->parameter[POLYGON_ROTATION]);
497 
498  if (doVectorExportFix
499  && CAIRO_OPERATOR_CLEAR ==
500  cairo_get_operator (cairoTarget)) {
501  cairo_save (cairoTarget);
502  cairo_set_source_rgba (cairoTarget,
503  bg_r, bg_g, bg_b, 1.0);
504  cairo_set_operator (cairoTarget,
505  CAIRO_OPERATOR_OVER);
506 
507  draw_fill (cairoTarget, drawMode,
508  selectionInfo, image, net);
509 
510  cairo_restore (cairoTarget);
511 
512  break;
513  }
514 
515  draw_fill (cairoTarget, drawMode,
516  selectionInfo, image, net);
517  break;
518 
520  gdouble diameter, diameterDifference, crosshairRadius;
521 
522  cairo_translate (cairoTarget,
523  ls->parameter[MOIRE_CENTER_X],
524  ls->parameter[MOIRE_CENTER_Y]);
525  cairo_rotate (cairoTarget,
526  DEG2RAD(ls->parameter[MOIRE_ROTATION]));
527  diameter = ls->parameter[MOIRE_OUTSIDE_DIAMETER]
528  - ls->parameter[MOIRE_CIRCLE_THICKNESS];
529  diameterDifference = 2*(ls->parameter[MOIRE_GAP_WIDTH]
530  + ls->parameter[MOIRE_CIRCLE_THICKNESS]);
531  cairo_set_line_width (cairoTarget,
532  ls->parameter[MOIRE_CIRCLE_THICKNESS]);
533 
534  if (doVectorExportFix
535  && CAIRO_OPERATOR_CLEAR ==
536  cairo_get_operator (cairoTarget)) {
537  cairo_save (cairoTarget);
538  cairo_set_source_rgba (cairoTarget,
539  bg_r, bg_g, bg_b, 1.0);
540  cairo_set_operator (cairoTarget,
541  CAIRO_OPERATOR_OVER);
542  }
543 
544  for (int circle = 0; circle < (int)ls->parameter[
545  MOIRE_NUMBER_OF_CIRCLES]; circle++) {
546  gdouble dia =
547  diameter - diameterDifference * circle;
548 
549  if (dia <= 0) {
550  GERB_COMPILE_WARNING (_("Ignoring %s "
551  "with non positive diameter"),
553  ls->type));
554  continue;
555  }
556 
557  gerbv_draw_circle (cairoTarget, dia);
558  draw_stroke (cairoTarget, drawMode,
559  selectionInfo, image, net);
560  }
561 
562 
563  cairo_set_line_width (cairoTarget,
564  ls->parameter[MOIRE_CROSSHAIR_THICKNESS]);
565  crosshairRadius =
566  ls->parameter[MOIRE_CROSSHAIR_LENGTH] / 2.0;
567  cairo_move_to (cairoTarget, -crosshairRadius, 0);
568  cairo_line_to (cairoTarget, crosshairRadius, 0);
569  cairo_move_to (cairoTarget, 0, -crosshairRadius);
570  cairo_line_to (cairoTarget, 0, crosshairRadius);
571 
572  draw_stroke (cairoTarget, drawMode,
573  selectionInfo, image, net);
574 
575  if (doVectorExportFix
576  && CAIRO_OPERATOR_CLEAR ==
577  cairo_get_operator (cairoTarget)) {
578  cairo_restore (cairoTarget);
579  }
580 
581  break;
582  }
584  gdouble startAngle1, startAngle2, endAngle1, endAngle2;
585 
586  cairo_translate (cairoTarget,
587  ls->parameter[THERMAL_CENTER_X],
588  ls->parameter[THERMAL_CENTER_Y]);
589  cairo_rotate (cairoTarget,
590  DEG2RAD(ls->parameter[THERMAL_ROTATION]));
591  startAngle1 = asin (
592  ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/
593  ls->parameter[THERMAL_INSIDE_DIAMETER]);
594  endAngle1 = M_PI_2 - startAngle1;
595  endAngle2 = asin (
596  ls->parameter[THERMAL_CROSSHAIR_THICKNESS]/
597  ls->parameter[THERMAL_OUTSIDE_DIAMETER]);
598  startAngle2 = M_PI_2 - endAngle2;
599 
600  if (doVectorExportFix
601  && CAIRO_OPERATOR_CLEAR ==
602  cairo_get_operator (cairoTarget)) {
603  cairo_save (cairoTarget);
604  cairo_set_source_rgba (cairoTarget,
605  bg_r, bg_g, bg_b, 1.0);
606  cairo_set_operator (cairoTarget,
607  CAIRO_OPERATOR_OVER);
608 
609  /* */
610 
611  cairo_restore (cairoTarget);
612 
613  break;
614  }
615 
616  for (gint i = 0; i < 4; i++) {
617  cairo_arc (cairoTarget, 0, 0,
618  ls->parameter[
619  THERMAL_INSIDE_DIAMETER]/2.0,
620  startAngle1, endAngle1);
621  cairo_arc_negative (cairoTarget, 0, 0,
622  ls->parameter[
623  THERMAL_OUTSIDE_DIAMETER]/2.0,
624  startAngle2, endAngle2);
625  draw_fill (cairoTarget,
626  drawMode, selectionInfo,
627  image, net);
628  cairo_rotate (cairoTarget, M_PI_2);
629  }
630 
631  break;
632  }
634  draw_update_macro_exposure (cairoTarget,
635  clearOperator, darkOperator,
636  ls->parameter[LINE20_EXPOSURE]);
637  cairo_set_line_width (cairoTarget,
638  MAX(ls->parameter[LINE20_LINE_WIDTH],
639  pixelWidth));
640  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_BUTT);
641  cairo_rotate (cairoTarget, DEG2RAD(
642  ls->parameter[LINE20_ROTATION]));
643  cairo_move_to (cairoTarget,
644  ls->parameter[LINE20_START_X],
645  ls->parameter[LINE20_START_Y]);
646  cairo_line_to (cairoTarget,
647  ls->parameter[LINE20_END_X],
648  ls->parameter[LINE20_END_Y]);
649 
650  if (doVectorExportFix
651  && CAIRO_OPERATOR_CLEAR ==
652  cairo_get_operator (cairoTarget)) {
653  cairo_save (cairoTarget);
654  cairo_set_source_rgba (cairoTarget,
655  bg_r, bg_g, bg_b, 1.0);
656  cairo_set_operator (cairoTarget,
657  CAIRO_OPERATOR_OVER);
658 
659  draw_stroke (cairoTarget, drawMode,
660  selectionInfo, image, net);
661 
662  cairo_restore (cairoTarget);
663 
664  break;
665  }
666 
667  draw_stroke (cairoTarget, drawMode,
668  selectionInfo, image, net);
669  break;
670 
672  draw_update_macro_exposure (cairoTarget,
673  clearOperator, darkOperator,
674  ls->parameter[LINE21_EXPOSURE]);
675  cairo_rotate (cairoTarget, DEG2RAD(
676  ls->parameter[LINE21_ROTATION]));
677  cairo_translate (cairoTarget,
678  ls->parameter[LINE21_CENTER_X],
679  ls->parameter[LINE21_CENTER_Y]);
680  cairo_rectangle (cairoTarget,
681  -MAX(ls->parameter[LINE21_WIDTH]/2.0,
682  pixelWidth),
683  -MAX(ls->parameter[LINE21_HEIGHT]/2.0,
684  pixelWidth),
685  MAX(ls->parameter[LINE21_WIDTH],
686  pixelWidth),
687  MAX(ls->parameter[LINE21_HEIGHT],
688  pixelWidth));
689  if (doVectorExportFix
690  && CAIRO_OPERATOR_CLEAR ==
691  cairo_get_operator (cairoTarget)) {
692  cairo_save (cairoTarget);
693  cairo_set_source_rgba (cairoTarget,
694  bg_r, bg_g, bg_b, 1.0);
695  cairo_set_operator (cairoTarget,
696  CAIRO_OPERATOR_OVER);
697 
698  draw_fill (cairoTarget, drawMode,
699  selectionInfo, image, net);
700 
701  cairo_restore (cairoTarget);
702 
703  break;
704  }
705 
706  draw_fill (cairoTarget, drawMode,
707  selectionInfo, image, net);
708  break;
709 
711  draw_update_macro_exposure (cairoTarget,
712  clearOperator, darkOperator,
713  ls->parameter[LINE22_EXPOSURE]);
714  cairo_rotate (cairoTarget, DEG2RAD(
715  ls->parameter[LINE22_ROTATION]));
716  cairo_translate (cairoTarget,
717  ls->parameter[LINE22_LOWER_LEFT_X],
718  ls->parameter[LINE22_LOWER_LEFT_Y]);
719  cairo_rectangle (cairoTarget, 0, 0,
720  MAX(ls->parameter[LINE22_WIDTH],
721  pixelWidth),
722  MAX(ls->parameter[LINE22_HEIGHT],
723  pixelWidth));
724 
725  if (doVectorExportFix
726  && CAIRO_OPERATOR_CLEAR ==
727  cairo_get_operator (cairoTarget)) {
728  cairo_save (cairoTarget);
729  cairo_set_source_rgba (cairoTarget,
730  bg_r, bg_g, bg_b, 1.0);
731  cairo_set_operator (cairoTarget,
732  CAIRO_OPERATOR_OVER);
733 
734  draw_fill (cairoTarget, drawMode,
735  selectionInfo, image, net);
736 
737  cairo_restore (cairoTarget);
738 
739  break;
740  }
741 
742  draw_fill (cairoTarget, drawMode,
743  selectionInfo, image, net);
744  break;
745 
746  default:
747  GERB_COMPILE_WARNING(_("Unknown macro type: %s"),
748  gerbv_aperture_type_name(ls->type));
749  ret = 0;
750  }
751 
752  cairo_restore (cairoTarget);
753  ls = ls->next;
754  }
755 
756  if (usesClearPrimitive) {
757  cairo_pop_group_to_source (cairoTarget);
758  cairo_paint (cairoTarget);
759  }
760 
761  return ret;
762 }
763 
764 void
765 draw_apply_netstate_transformation (cairo_t *cairoTarget, gerbv_netstate_t *state)
766 {
767  /* apply scale factor */
768  cairo_scale (cairoTarget, state->scaleA, state->scaleB);
769  /* apply offset */
770  cairo_translate (cairoTarget, state->offsetA, state->offsetB);
771  /* apply mirror */
772  switch (state->mirrorState) {
773  case GERBV_MIRROR_STATE_FLIPA:
774  cairo_scale (cairoTarget, -1, 1);
775  break;
776  case GERBV_MIRROR_STATE_FLIPB:
777  cairo_scale (cairoTarget, 1, -1);
778  break;
779  case GERBV_MIRROR_STATE_FLIPAB:
780  cairo_scale (cairoTarget, -1, -1);
781  break;
782  default:
783  break;
784  }
785  /* finally, apply axis select */
786  if (state->axisSelect == GERBV_AXIS_SELECT_SWAPAB) {
787  /* we do this by rotating 270 (counterclockwise, then mirroring
788  the Y axis */
789  cairo_rotate (cairoTarget, M_PI + M_PI_2);
790  cairo_scale (cairoTarget, 1, -1);
791  }
792 }
793 
794 void
795 draw_render_polygon_object (gerbv_net_t *oldNet, cairo_t *cairoTarget,
796  gdouble sr_x, gdouble sr_y, gerbv_image_t *image,
797  enum draw_mode drawMode, gerbv_selection_info_t *selectionInfo,
798  gboolean pixelOutput)
799 {
800  gerbv_net_t *currentNet, *polygonStartNet;
801  int haveDrawnFirstFillPoint = 0;
802  gdouble x2,y2,cp_x=0,cp_y=0;
803 
804  haveDrawnFirstFillPoint = FALSE;
805  /* save the first net in the polygon as the "ID" net pointer
806  in case we are saving this net to the selection array */
807  polygonStartNet = oldNet;
808  cairo_new_path(cairoTarget);
809 
810  for (currentNet = oldNet->next; currentNet!=NULL;
811  currentNet = currentNet->next) {
812  x2 = currentNet->stop_x + sr_x;
813  y2 = currentNet->stop_y + sr_y;
814 
815  /* translate circular x,y data as well */
816  if (currentNet->cirseg) {
817  cp_x = currentNet->cirseg->cp_x + sr_x;
818  cp_y = currentNet->cirseg->cp_y + sr_y;
819  }
820  if (!haveDrawnFirstFillPoint) {
821  draw_cairo_move_to (cairoTarget, x2, y2, FALSE, pixelOutput);
822  haveDrawnFirstFillPoint=TRUE;
823  continue;
824  }
825 
826  switch (currentNet->interpolation) {
831  draw_cairo_line_to (cairoTarget, x2, y2, FALSE, pixelOutput);
832  break;
835  if (currentNet->cirseg->angle2 > currentNet->cirseg->angle1) {
836  cairo_arc (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
837  DEG2RAD(currentNet->cirseg->angle1),
838  DEG2RAD(currentNet->cirseg->angle2));
839  } else {
840  cairo_arc_negative (cairoTarget, cp_x, cp_y, currentNet->cirseg->width/2.0,
841  DEG2RAD(currentNet->cirseg->angle1),
842  DEG2RAD(currentNet->cirseg->angle2));
843  }
844  break;
846  cairo_close_path(cairoTarget);
847  /* turn off anti-aliasing for polygons, since it shows seams
848  with adjacent polygons (usually on PCB ground planes) */
849  cairo_antialias_t oldAlias = cairo_get_antialias (cairoTarget);
850  cairo_set_antialias (cairoTarget, CAIRO_ANTIALIAS_NONE);
851  draw_fill (cairoTarget, drawMode, selectionInfo, image, polygonStartNet);
852  cairo_set_antialias (cairoTarget, oldAlias);
853  return;
854  default :
855  break;
856  }
857  }
858 }
859 
865 static void
866 draw_cairo_cross (cairo_t *cairoTarget, gdouble xc, gdouble yc, gdouble r)
867 {
868  cairo_move_to (cairoTarget, xc, yc - r);
869  cairo_rel_line_to (cairoTarget, 0, 2*r);
870  cairo_move_to (cairoTarget, xc - r, yc);
871  cairo_rel_line_to (cairoTarget, 2*r, 0);
872  cairo_stroke (cairoTarget);
873 }
874 
875 static int
876 draw_calc_pnp_mark_coords(struct gerbv_net *start_net,
877  double *label_x, double *label_y)
878 {
879  double x, y;
880  struct gerbv_net *net = start_net;
881  const char *label = NULL;
882 
883  if (net && net->label)
884  label = net->label->str;
885 
886  if (label == NULL)
887  return 0;
888 
889  x = HUGE_VAL; y = -HUGE_VAL;
890  do {
891  if (!net->label
892  || 0 != g_strcmp0 (net->label->str, label))
893  break;
894 
895  /* Search top left corner */
896  if (net->boundingBox.top != HUGE_VAL) {
897  /* Bounding box not calculated */
898  x = MIN(x, net->boundingBox.left);
899  y = MAX(y, net->boundingBox.top);
900  } else {
901  x = MIN(x, net->stop_x);
902  y = MAX(y, net->stop_y + 0.01/2);
903  /* 0.01 default line width */
904  }
905  } while (NULL !=
907 
908  *label_x = x;
909  *label_y = y;
910 
911  return 1;
912 }
913 
914 int
915 draw_image_to_cairo_target (cairo_t *cairoTarget, gerbv_image_t *image,
916  gdouble pixelWidth, enum draw_mode drawMode,
917  gerbv_selection_info_t *selectionInfo,
918  gerbv_render_info_t *renderInfo, gboolean allowOptimization,
919  gerbv_user_transformation_t transform, gboolean pixelOutput)
920 {
921  const int hole_cross_inc_px = 8;
922  struct gerbv_net *net, *polygonStartNet=NULL;
923  double x1, y1, x2, y2, cp_x=0, cp_y=0;
924  gdouble *p, p0, p1, dx, dy, lineWidth, r;
925  gerbv_netstate_t *oldState;
926  gerbv_layer_t *oldLayer;
927  cairo_operator_t drawOperatorClear, drawOperatorDark;
928  gboolean invertPolarity = FALSE, oddWidth = FALSE;
929  gdouble minX=0, minY=0, maxX=0, maxY=0;
930  gdouble criticalRadius;
931  gdouble scaleX = transform.scaleX;
932  gdouble scaleY = transform.scaleY;
933  /* Keep PNP label not mirrored */
934  gdouble pnp_label_scale_x = 1, pnp_label_scale_y= -1;
935  gboolean limitLineWidth = TRUE;
936  gboolean displayPixel = TRUE;
937  gboolean doVectorExportFix;
938  double bg_r, bg_g, bg_b; /* Background color */
939 
940 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 16, 0)
941  // Fix for cairo 1.17.6 and above which sets to surface unit back to PT (default: UNIT_USER)
942 
943  cairo_surface_t* cSurface = cairo_get_target(cairoTarget);
944  if (cairo_surface_get_type(cSurface) == CAIRO_SURFACE_TYPE_SVG) {
945  cairo_svg_surface_set_document_unit(cSurface, CAIRO_SVG_UNIT_PT);
946  }
947 #endif
948 
949  doVectorExportFix =
950  draw_do_vector_export_fix (cairoTarget, &bg_r, &bg_g, &bg_b);
951 
952  /* If we are scaling the image at all, ignore the line width checks
953  * since scaled up lines can still be visible */
954  if ((scaleX != 1)||(scaleY != 1)){
955  limitLineWidth = FALSE;
956  }
957 
958  if (transform.mirrorAroundX) {
959  scaleY *= -1;
960  pnp_label_scale_y = 1;
961  }
962 
963  if (transform.mirrorAroundY) {
964  scaleX *= -1;
965  pnp_label_scale_x= -1;
966  }
967 
968  cairo_translate (cairoTarget, transform.translateX, transform.translateY);
969  cairo_scale (cairoTarget, scaleX, scaleY);
970  cairo_rotate (cairoTarget, transform.rotation);
971 
972  gboolean useOptimizations = allowOptimization;
973 
974  /* If the user is using any transformations for this layer, then don't
975  * bother using rendering optimizations */
976  if (fabs(transform.translateX) > GERBV_PRECISION_LINEAR_INCH
977  || fabs(transform.translateY) > GERBV_PRECISION_LINEAR_INCH
978  || fabs(transform.scaleX - 1) > GERBV_PRECISION_LINEAR_INCH
979  || fabs(transform.scaleY - 1) > GERBV_PRECISION_LINEAR_INCH
980  || fabs(transform.rotation) > GERBV_PRECISION_ANGLE_RAD
981  || transform.mirrorAroundX || transform.mirrorAroundY)
982  useOptimizations = FALSE;
983 
984  if (useOptimizations && pixelOutput) {
985  minX = renderInfo->lowerLeftX;
986  minY = renderInfo->lowerLeftY;
987  maxX = renderInfo->lowerLeftX + (renderInfo->displayWidth /
988  renderInfo->scaleFactorX);
989  maxY = renderInfo->lowerLeftY + (renderInfo->displayHeight /
990  renderInfo->scaleFactorY);
991  }
992 
993  /* do initial justify */
994  cairo_translate (cairoTarget, image->info->imageJustifyOffsetActualA,
995  image->info->imageJustifyOffsetActualB);
996 
997  /* set the fill rule so aperture holes are cleared correctly */
998  cairo_set_fill_rule (cairoTarget, CAIRO_FILL_RULE_EVEN_ODD);
999  /* offset image */
1000  cairo_translate (cairoTarget, image->info->offsetA, image->info->offsetB);
1001  /* do image rotation */
1002  cairo_rotate (cairoTarget, image->info->imageRotation);
1003 
1004  /* load in polarity operators depending on the image polarity */
1005  invertPolarity = transform.inverted;
1006  if (image->info->polarity == GERBV_POLARITY_NEGATIVE)
1007  invertPolarity = !invertPolarity;
1008  if (drawMode == DRAW_SELECTIONS)
1009  invertPolarity = FALSE;
1010 
1011  if (invertPolarity) {
1012  drawOperatorClear = CAIRO_OPERATOR_OVER;
1013  drawOperatorDark = CAIRO_OPERATOR_CLEAR;
1014  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
1015  cairo_paint (cairoTarget);
1016  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
1017  } else {
1018  drawOperatorClear = CAIRO_OPERATOR_CLEAR;
1019  drawOperatorDark = CAIRO_OPERATOR_OVER;
1020  }
1021 
1022  /* next, push two cairo states to simulate the first layer and netstate
1023  translations (these will be popped when another layer or netstate is
1024  started */
1025  cairo_save (cairoTarget);
1026  cairo_save (cairoTarget);
1027 
1028  /* store the current layer and netstate so we know when they change */
1029  oldLayer = image->layers;
1030  oldState = image->states;
1031 
1032  const char *pnp_net_label_str_prev = NULL;
1033 
1034  for (net = image->netlist->next; net != NULL;
1036 
1037  /* check if this is a new layer */
1038  if (net->layer != oldLayer){
1039  /* it's a new layer, so recalculate the new transformation matrix
1040  for it */
1041  cairo_restore (cairoTarget);
1042  cairo_restore (cairoTarget);
1043  cairo_save (cairoTarget);
1044  /* do any rotations */
1045  cairo_rotate (cairoTarget, net->layer->rotation);
1046  /* handle the layer polarity */
1047  if ((net->layer->polarity == GERBV_POLARITY_CLEAR)^invertPolarity) {
1048  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_CLEAR);
1049  drawOperatorClear = CAIRO_OPERATOR_OVER;
1050  drawOperatorDark = CAIRO_OPERATOR_CLEAR;
1051  }
1052  else {
1053  cairo_set_operator (cairoTarget, CAIRO_OPERATOR_OVER);
1054  drawOperatorClear = CAIRO_OPERATOR_CLEAR;
1055  drawOperatorDark = CAIRO_OPERATOR_OVER;
1056  }
1057 
1058  /* Draw any knockout areas */
1059  gerbv_knockout_t *ko = &net->layer->knockout;
1060  if (ko->firstInstance == TRUE) {
1061  cairo_save (cairoTarget);
1062 
1063  if (ko->polarity == GERBV_POLARITY_CLEAR) {
1064  cairo_set_operator (cairoTarget, drawOperatorClear);
1065  } else {
1066  cairo_set_operator (cairoTarget, drawOperatorDark);
1067  }
1068 
1069  if (doVectorExportFix
1070  && CAIRO_OPERATOR_CLEAR ==
1071  cairo_get_operator (cairoTarget)) {
1072 
1073  cairo_set_operator (cairoTarget,
1074  CAIRO_OPERATOR_OVER);
1075  cairo_set_source_rgba (
1076  cairoTarget, bg_r,
1077  bg_g, bg_b, 1.0);
1078  }
1079 
1080  cairo_new_path (cairoTarget);
1081  cairo_rectangle (cairoTarget,
1082  ko->lowerLeftX - ko->border,
1083  ko->lowerLeftY - ko->border,
1084  ko->width + 2*ko->border,
1085  ko->height + 2*ko->border);
1086  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
1087 
1088  cairo_restore (cairoTarget);
1089  }
1090 
1091  /* Finally, reapply old netstate transformation */
1092  cairo_save (cairoTarget);
1093  draw_apply_netstate_transformation (cairoTarget, net->state);
1094  oldLayer = net->layer;
1095  }
1096 
1097  /* check if this is a new netstate */
1098  if (net->state != oldState){
1099  /* pop the transformation matrix back to the "pre-state" state and
1100  resave it */
1101  cairo_restore (cairoTarget);
1102  cairo_save (cairoTarget);
1103  /* it's a new state, so recalculate the new transformation matrix
1104  for it */
1105  draw_apply_netstate_transformation (cairoTarget, net->state);
1106  oldState = net->state;
1107  }
1108 
1109  /* if we are only drawing from the selection buffer, search if this net is
1110  in the buffer */
1111  if (drawMode == DRAW_SELECTIONS) {
1112  /* this flag makes sure we don't draw any unintentional polygons...
1113  if we've successfully entered a polygon (the first net matches, and
1114  we don't want to check the nets inside the polygon) then
1115  polygonStartNet will be set */
1116  if (!polygonStartNet) {
1118  selectionInfo, FALSE))
1119  continue;
1120  }
1121  }
1122 
1123  /* Render any labels attached to this net */
1124  /* NOTE: this is currently only used on PNP files, so we may
1125  make some assumptions here... */
1126  if (drawMode != DRAW_SELECTIONS && net->label
1129  && g_strcmp0 (net->label->str, pnp_net_label_str_prev)) {
1130 
1131  double mark_x, mark_y;
1132 
1133  /* Add PNP text label only one time per
1134  * net and if it is not selected. */
1135  pnp_net_label_str_prev =
1136  net->label->str;
1137 
1138  if (draw_calc_pnp_mark_coords(net, &mark_x, &mark_y)) {
1139  cairo_save (cairoTarget);
1140 
1141  cairo_set_font_size (cairoTarget, 0.05);
1142  cairo_move_to (cairoTarget, mark_x, mark_y);
1143  cairo_scale (cairoTarget, pnp_label_scale_x,
1144  pnp_label_scale_y);
1145  cairo_show_text (cairoTarget, net->label->str);
1146 
1147  cairo_restore (cairoTarget);
1148  }
1149  }
1150 
1151  /* step and repeat */
1152  gerbv_step_and_repeat_t *sr = &net->layer->stepAndRepeat;
1153  int ix, iy;
1154  for (ix = 0; ix < sr->X; ix++) {
1155  for (iy = 0; iy < sr->Y; iy++) {
1156  double sr_x = ix * sr->dist_X;
1157  double sr_y = iy * sr->dist_Y;
1158 
1159  if (useOptimizations && pixelOutput
1160  && ((net->boundingBox.right+sr_x < minX)
1161  || (net->boundingBox.left+sr_x > maxX)
1162  || (net->boundingBox.top+sr_y < minY)
1163  || (net->boundingBox.bottom+sr_y > maxY))) {
1164  continue;
1165  }
1166 
1167  x1 = net->start_x + sr_x;
1168  y1 = net->start_y + sr_y;
1169  x2 = net->stop_x + sr_x;
1170  y2 = net->stop_y + sr_y;
1171 
1172  /* translate circular x,y data as well */
1173  if (net->cirseg) {
1174  cp_x = net->cirseg->cp_x + sr_x;
1175  cp_y = net->cirseg->cp_y + sr_y;
1176  }
1177 
1178  /* Polygon area fill routines */
1179  switch (net->interpolation) {
1181 
1182  if (doVectorExportFix
1183  && CAIRO_OPERATOR_CLEAR ==
1184  cairo_get_operator (cairoTarget)) {
1185 
1186  cairo_save (cairoTarget);
1187 
1188  cairo_set_operator (cairoTarget,
1189  CAIRO_OPERATOR_OVER);
1190  cairo_set_source_rgba (
1191  cairoTarget, bg_r,
1192  bg_g, bg_b, 1.0);
1193 
1194  draw_render_polygon_object (net,
1195  cairoTarget,
1196  sr_x, sr_y, image,
1197  drawMode, selectionInfo,
1198  pixelOutput);
1199 
1200  cairo_restore (cairoTarget);
1201  } else {
1202  draw_render_polygon_object (net,
1203  cairoTarget,
1204  sr_x, sr_y, image,
1205  drawMode, selectionInfo,
1206  pixelOutput);
1207  }
1208 
1209  continue;
1211  continue;
1212  default :
1213  break;
1214  }
1215 
1216  /*
1217  * If aperture state is off we allow use of undefined apertures.
1218  * This happens when gerber files starts, but hasn't decided on
1219  * which aperture to use.
1220  */
1221  if (image->aperture[net->aperture] == NULL)
1222  continue;
1223 
1224  switch (net->aperture_state) {
1226  /* if the aperture width is truly 0, then render as a 1 pixel width
1227  line. 0 diameter apertures are used by some programs to draw labels,
1228  etc, and they are rendered by other programs as 1 pixel wide */
1229  /* NOTE: also, make sure all lines are at least 1 pixel wide, so they
1230  always show up at low zoom levels */
1231 
1232  if (limitLineWidth&&((image->aperture[net->aperture]->parameter[0] < pixelWidth)&&
1233  (pixelOutput)))
1234  criticalRadius = pixelWidth/2.0;
1235  else
1236  criticalRadius = image->aperture[net->aperture]->parameter[0]/2.0;
1237  lineWidth = criticalRadius*2.0;
1238  // convert to a pixel integer
1239  cairo_user_to_device_distance (cairoTarget, &lineWidth, &x1);
1240  if (pixelOutput) {
1241  lineWidth = round(lineWidth);
1242  if ((int)lineWidth % 2) {
1243  oddWidth = TRUE;
1244  }
1245  else {
1246  oddWidth = FALSE;
1247  }
1248  }
1249  cairo_device_to_user_distance (cairoTarget, &lineWidth, &x1);
1250  cairo_set_line_width (cairoTarget, lineWidth);
1251 
1252  switch (net->interpolation) {
1257  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
1258 
1259  /* weed out any lines that are
1260  * obviously not going to
1261  * render on the visible screen */
1262  switch (image->aperture[net->aperture]->type) {
1263  case GERBV_APTYPE_CIRCLE :
1264  if (renderInfo->show_cross_on_drill_holes
1265  && image->layertype == GERBV_LAYERTYPE_DRILL) {
1266  /* Draw center crosses on slot hole */
1267  cairo_set_line_width (cairoTarget, pixelWidth);
1268  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
1269  r = image->aperture[net->aperture]->parameter[0]/2.0 +
1270  hole_cross_inc_px*pixelWidth;
1271  draw_cairo_cross (cairoTarget, x1, y1, r);
1272  draw_cairo_cross (cairoTarget, x2, y2, r);
1273  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
1274  cairo_set_line_width (cairoTarget, lineWidth);
1275  }
1276 
1277  draw_cairo_move_to (cairoTarget, x1, y1, oddWidth, pixelOutput);
1278  draw_cairo_line_to (cairoTarget, x2, y2, oddWidth, pixelOutput);
1279 
1280  if (doVectorExportFix
1281  && CAIRO_OPERATOR_CLEAR ==
1282  cairo_get_operator (cairoTarget)) {
1283  cairo_save (cairoTarget);
1284  cairo_set_source_rgba (
1285  cairoTarget,
1286  bg_r,
1287  bg_g,
1288  bg_b,
1289  1.0);
1290  cairo_set_operator (
1291  cairoTarget,
1292  CAIRO_OPERATOR_OVER);
1293 
1294  draw_stroke (
1295  cairoTarget,
1296  drawMode,
1297  selectionInfo,
1298  image, net);
1299 
1300  cairo_restore (
1301  cairoTarget);
1302  } else {
1303  draw_stroke (
1304  cairoTarget,
1305  drawMode,
1306  selectionInfo,
1307  image, net);
1308  }
1309 
1310  break;
1311  case GERBV_APTYPE_RECTANGLE :
1312  dx = image->aperture[net->aperture]->parameter[0]/2;
1313  dy = image->aperture[net->aperture]->parameter[1]/2;
1314  if(x1 > x2)
1315  dx = -dx;
1316  if(y1 > y2)
1317  dy = -dy;
1318  cairo_new_path(cairoTarget);
1319  draw_cairo_move_to (cairoTarget, x1 - dx, y1 - dy, FALSE, pixelOutput);
1320  draw_cairo_line_to (cairoTarget, x1 - dx, y1 + dy, FALSE, pixelOutput);
1321  draw_cairo_line_to (cairoTarget, x2 - dx, y2 + dy, FALSE, pixelOutput);
1322  draw_cairo_line_to (cairoTarget, x2 + dx, y2 + dy, FALSE, pixelOutput);
1323  draw_cairo_line_to (cairoTarget, x2 + dx, y2 - dy, FALSE, pixelOutput);
1324  draw_cairo_line_to (cairoTarget, x1 + dx, y1 - dy, FALSE, pixelOutput);
1325  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
1326  break;
1327  /* TODO: for now, just render ovals or polygons like a circle */
1328  case GERBV_APTYPE_OVAL :
1329  case GERBV_APTYPE_POLYGON :
1330  draw_cairo_move_to (cairoTarget, x1,y1, oddWidth, pixelOutput);
1331  draw_cairo_line_to (cairoTarget, x2,y2, oddWidth, pixelOutput);
1332  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
1333  break;
1334  /* macros can only be flashed, so ignore any that might be here */
1335  default:
1336  GERB_COMPILE_WARNING(
1337  _("Unknown aperture type: %s"),
1339  image->aperture[net->aperture]->type)));
1340  break;
1341  }
1342  break;
1345  /* cairo doesn't have a function to draw oval arcs, so we must
1346  * draw an arc and stretch it by scaling different x and y values
1347  */
1348  cairo_new_path(cairoTarget);
1349  if (image->aperture[net->aperture]->type == GERBV_APTYPE_RECTANGLE) {
1350  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
1351  }
1352  else {
1353  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
1354  }
1355  cairo_save (cairoTarget);
1356  cairo_translate(cairoTarget, cp_x, cp_y);
1357  cairo_scale (cairoTarget, net->cirseg->width, net->cirseg->height);
1358  if (net->cirseg->angle2 > net->cirseg->angle1) {
1359  cairo_arc (cairoTarget, 0.0, 0.0, 0.5,
1360  DEG2RAD(net->cirseg->angle1),
1361  DEG2RAD(net->cirseg->angle2));
1362  }
1363  else {
1364  cairo_arc_negative (cairoTarget, 0.0, 0.0, 0.5,
1365  DEG2RAD(net->cirseg->angle1),
1366  DEG2RAD(net->cirseg->angle2));
1367  }
1368  cairo_restore (cairoTarget);
1369  draw_stroke (cairoTarget, drawMode, selectionInfo, image, net);
1370  break;
1371  default :
1372  GERB_COMPILE_WARNING(
1373  _("Unknown interpolation type: %s"),
1375  break;
1376  }
1377  break;
1379  break;
1381  p = image->aperture[net->aperture]->parameter;
1382 
1383  cairo_save (cairoTarget);
1384  draw_cairo_translate_adjust(cairoTarget, x2, y2, pixelOutput);
1385 
1386  switch (image->aperture[net->aperture]->type) {
1387  case GERBV_APTYPE_CIRCLE :
1388  if (renderInfo->show_cross_on_drill_holes
1389  && image->layertype == GERBV_LAYERTYPE_DRILL) {
1390  /* Draw center cross on drill hole */
1391  cairo_set_line_width (cairoTarget, pixelWidth);
1392  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_SQUARE);
1393  r = p[0]/2.0 + hole_cross_inc_px*pixelWidth;
1394  draw_cairo_cross (cairoTarget, 0, 0, r);
1395  cairo_set_line_width (cairoTarget, lineWidth);
1396  cairo_set_line_cap (cairoTarget, CAIRO_LINE_CAP_ROUND);
1397  }
1398 
1399  gerbv_draw_circle(cairoTarget, p[0]);
1400  gerbv_draw_aperture_hole (cairoTarget, p[1], p[2], pixelOutput);
1401  break;
1402  case GERBV_APTYPE_RECTANGLE :
1403  // some CAD programs use very thin flashed rectangles to compose
1404  // logos/images, so we must make sure those display here
1405  displayPixel = pixelOutput;
1406  p0 = p[0];
1407  p1 = p[1];
1408  if (limitLineWidth && (p[0] < pixelWidth) && pixelOutput) {
1409  p0 = pixelWidth;
1410  displayPixel = FALSE;
1411  }
1412  if (limitLineWidth && (p[1] < pixelWidth) && pixelOutput) {
1413  p1 = pixelWidth;
1414  displayPixel = FALSE;
1415  }
1416  gerbv_draw_rectangle(cairoTarget, p0, p1, displayPixel);
1417  gerbv_draw_aperture_hole (cairoTarget, p[2], p[3], displayPixel);
1418  break;
1419  case GERBV_APTYPE_OVAL :
1420  gerbv_draw_oblong(cairoTarget, p[0], p[1]);
1421  gerbv_draw_aperture_hole (cairoTarget, p[2], p[3], pixelOutput);
1422  break;
1423  case GERBV_APTYPE_POLYGON :
1424  gerbv_draw_polygon(cairoTarget, p[0], p[1], p[2]);
1425  gerbv_draw_aperture_hole (cairoTarget, p[3], p[4], pixelOutput);
1426  break;
1427  case GERBV_APTYPE_MACRO :
1428 /* TODO: to do it properly for vector export (doVectorExportFix) draw all
1429  * macros with some vector library with logical operators */
1430  gerbv_draw_amacro(cairoTarget, drawOperatorClear, drawOperatorDark,
1431  image->aperture[net->aperture]->simplified,
1432  (gint)p[0], pixelWidth,
1433  drawMode, selectionInfo, image, net);
1434  break;
1435  default :
1436  GERB_COMPILE_WARNING(
1437  _("Unknown aperture type: %s"),
1439  image->aperture[net->aperture]->type)));
1440  return 0;
1441  }
1442 
1443  /* And finally fill the path */
1444  if (doVectorExportFix
1445  && CAIRO_OPERATOR_CLEAR ==
1446  cairo_get_operator (cairoTarget)) {
1447  cairo_set_source_rgba (
1448  cairoTarget,
1449  bg_r, bg_g,
1450  bg_b, 1.0);
1451  cairo_set_operator (cairoTarget,
1452  CAIRO_OPERATOR_OVER);
1453  }
1454 
1455  draw_fill (cairoTarget, drawMode, selectionInfo, image, net);
1456  cairo_restore (cairoTarget);
1457  break;
1458  default:
1459  GERB_COMPILE_WARNING(
1460  _("Unknown aperture state: %s"),
1462  net->aperture_state)));
1463 
1464  return 0;
1465  }
1466  }
1467  }
1468  }
1469 
1470  /* restore the initial two state saves (one for layer, one for netstate)*/
1471  cairo_restore (cairoTarget);
1472  cairo_restore (cairoTarget);
1473 
1474  return 1;
1475 }
1476 
1477 /* Check if Cairo target require the vector export fix to be done and if so try
1478  * to retrieve background color. */
1479 static gboolean
1480 draw_do_vector_export_fix(cairo_t *cairoTarget,
1481  double *bg_red, double *bg_green, double *bg_blue)
1482 {
1483  /* Cairo library produce _raster_ output if polygon is cleared by
1484  * negative aperture or other polygon. A workaround is to draw over
1485  * with background color instead of clearing. Drawback is: there won't
1486  * be any see thru negative opening if two layers printed one on the
1487  * another. */
1488 
1489  switch (cairo_surface_get_type (cairo_get_target (cairoTarget))) {
1490 
1491  case CAIRO_SURFACE_TYPE_PDF:
1492  case CAIRO_SURFACE_TYPE_PS:
1493  case CAIRO_SURFACE_TYPE_SVG: {
1494  double *p0, *p1, *p2;
1495 
1496  /* Get background color from cairo user data to emulate clear
1497  * operator */
1498  p0 = cairo_get_user_data (cairoTarget,
1499  (cairo_user_data_key_t *)0);
1500  p1 = cairo_get_user_data (cairoTarget,
1501  (cairo_user_data_key_t *)1);
1502  p2 = cairo_get_user_data (cairoTarget,
1503  (cairo_user_data_key_t *)2);
1504 
1505  if (p0 != NULL && p1 != NULL && p2 != NULL) {
1506  *bg_red = *p0;
1507  *bg_green = *p1;
1508  *bg_blue = *p2;
1509  } else {
1510  *bg_red = *bg_green = *bg_blue = 1.0;
1511  }
1512 
1513  break;
1514  }
1515 
1516  default:
1517  return FALSE;
1518  }
1519 
1520  return TRUE;
1521 }
void draw_cairo_translate_adjust(cairo_t *cairoTarget, gdouble x, gdouble y, gboolean pixelOutput)
Cairo translate user-space origin.
Definition: draw.c:100
static gboolean draw_net_is_in_selection_buffer_remove(gerbv_net_t *net, gerbv_selection_info_t *selectionInfo, gboolean remove)
Check if net is in selection buffer and possibly deselect it.
Definition: draw.c:120
static void draw_cairo_cross(cairo_t *cairoTarget, gdouble xc, gdouble yc, gdouble r)
Draw Cairo cross.
Definition: draw.c:866
static void gerbv_draw_rectangle(cairo_t *cairoTarget, gdouble width, gdouble height, gboolean pixelOutput)
Draw the rectangle centered at current Cairo coordinates.
Definition: draw.c:244
void draw_cairo_line_to(cairo_t *cairoTarget, gdouble x, gdouble y, gboolean adjustByHalf, gboolean pixelOutput)
Draw Cairo line from current coordinates.
Definition: draw.c:55
void draw_cairo_move_to(cairo_t *cairoTarget, gdouble x, gdouble y, gboolean oddWidth, gboolean pixelOutput)
Move Cairo coordinates.
Definition: draw.c:78
static void gerbv_draw_oblong(cairo_t *cairoTarget, gdouble width, gdouble height)
Draw the oblong centered at current Cairo coordinates.
Definition: draw.c:264
static void gerbv_draw_circle(cairo_t *cairoTarget, gdouble diameter)
Draw the circle centered at current Cairo coordinates.
Definition: draw.c:231
Header info for the cairo rendering functions and the related selection calculating functions.
gerbv_net_t * gerbv_image_return_next_renderable_object(gerbv_net_t *oldNet)
Return the next net entry which corresponds to a unique visible object.
Definition: gerb_image.c:1356
const char * gerbv_interpolation_name(gerbv_interpolation_t interp)
Return string name of gerbv_interpolation_t interpolation.
Definition: gerbv.c:115
const char * gerbv_aperture_state_name(gerbv_aperture_state_t state)
Definition: gerbv.c:99
const char * gerbv_aperture_type_name(gerbv_aperture_type_t type)
Return string name of gerbv_aperture_type_t aperture type.
Definition: gerbv.c:72
The main header file for the libgerbv library.
@ GERBV_APERTURE_STATE_OFF
Definition: gerbv.h:178
@ GERBV_APERTURE_STATE_ON
Definition: gerbv.h:179
@ GERBV_APERTURE_STATE_FLASH
Definition: gerbv.h:180
@ GERBV_POLARITY_CLEAR
Definition: gerbv.h:284
@ GERBV_POLARITY_NEGATIVE
Definition: gerbv.h:282
@ GERBV_SELECTION_POINT_CLICK
Definition: gerbv.h:355
@ GERBV_SELECTION_DRAG_BOX
Definition: gerbv.h:356
@ GERBV_APTYPE_MACRO_LINE20
Definition: gerbv.h:170
@ GERBV_APTYPE_MACRO_LINE21
Definition: gerbv.h:171
@ GERBV_APTYPE_OVAL
Definition: gerbv.h:162
@ GERBV_APTYPE_MACRO_OUTLINE
Definition: gerbv.h:166
@ GERBV_APTYPE_MACRO_CIRCLE
Definition: gerbv.h:165
@ GERBV_APTYPE_MACRO
Definition: gerbv.h:164
@ GERBV_APTYPE_CIRCLE
Definition: gerbv.h:160
@ GERBV_APTYPE_POLYGON
Definition: gerbv.h:163
@ GERBV_APTYPE_MACRO_POLYGON
Definition: gerbv.h:167
@ GERBV_APTYPE_RECTANGLE
Definition: gerbv.h:161
@ GERBV_APTYPE_MACRO_THERMAL
Definition: gerbv.h:169
@ GERBV_APTYPE_MACRO_LINE22
Definition: gerbv.h:172
@ GERBV_APTYPE_MACRO_MOIRE
Definition: gerbv.h:168
@ GERBV_INTERPOLATION_LINEARx01
Definition: gerbv.h:304
@ GERBV_INTERPOLATION_PAREA_START
Definition: gerbv.h:308
@ GERBV_INTERPOLATION_LINEARx001
Definition: gerbv.h:305
@ GERBV_INTERPOLATION_DELETED
Definition: gerbv.h:310
@ GERBV_INTERPOLATION_CW_CIRCULAR
Definition: gerbv.h:306
@ GERBV_INTERPOLATION_PAREA_END
Definition: gerbv.h:309
@ GERBV_INTERPOLATION_LINEARx10
Definition: gerbv.h:303
@ GERBV_INTERPOLATION_CCW_CIRCULAR
Definition: gerbv.h:307
@ GERBV_INTERPOLATION_LINEARx1
Definition: gerbv.h:302
@ GERBV_LAYERTYPE_PICKANDPLACE_BOT
Definition: gerbv.h:331
@ GERBV_LAYERTYPE_PICKANDPLACE_TOP
Definition: gerbv.h:330
@ GERBV_LAYERTYPE_DRILL
Definition: gerbv.h:329
Header info for the selection support functions for libgerbv.
gerbv_layer_t * layers
Definition: gerbv.h:724
gerbv_layertype_t layertype
Definition: gerbv.h:722
gerbv_net_t * netlist
Definition: gerbv.h:729
gerbv_aperture_t * aperture[APERTURE_MAX]
Definition: gerbv.h:723
gerbv_netstate_t * states
Definition: gerbv.h:725
gerbv_image_info_t * info
Definition: gerbv.h:728
gerbv_step_and_repeat_t stepAndRepeat
Definition: gerbv.h:635
gerbv_polarity_t polarity
Definition: gerbv.h:638
gerbv_knockout_t knockout
Definition: gerbv.h:636
gdouble rotation
Definition: gerbv.h:637
gerbv_render_size_t boundingBox
Definition: gerbv.h:661
gerbv_layer_t * layer
Definition: gerbv.h:668
double stop_y
Definition: gerbv.h:660
GString * label
Definition: gerbv.h:667
gerbv_aperture_state_t aperture_state
Definition: gerbv.h:663
double stop_x
Definition: gerbv.h:659
double start_x
Definition: gerbv.h:657
gerbv_netstate_t * state
Definition: gerbv.h:669
struct gerbv_net * next
Definition: gerbv.h:666
double start_y
Definition: gerbv.h:658
gerbv_interpolation_t interpolation
Definition: gerbv.h:664
gerbv_cirseg_t * cirseg
Definition: gerbv.h:665
int aperture
Definition: gerbv.h:662
gdouble offsetB
Definition: gerbv.h:649
gdouble scaleA
Definition: gerbv.h:650
gdouble scaleB
Definition: gerbv.h:651
gerbv_axis_select_t axisSelect
Definition: gerbv.h:645
gdouble offsetA
Definition: gerbv.h:648
gerbv_mirror_state_t mirrorState
Definition: gerbv.h:646
gdouble lowerLeftY
Definition: gerbv.h:778
gboolean show_cross_on_drill_holes
Definition: gerbv.h:782
gdouble scaleFactorX
Definition: gerbv.h:775
gint displayHeight
Definition: gerbv.h:781
gdouble lowerLeftX
Definition: gerbv.h:777
gdouble scaleFactorY
Definition: gerbv.h:776