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