Move some fields from the HID* structure to HID_DRAW* and HID_DRAW_CLASS*
[geda-pcb/pcjc2.git] / src / hid / png / png.c
blob6ae0811d8e9b96a80f0b0387199c45f8fd8d5690
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 2006 Dan McMahill
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 * Heavily based on the ps HID written by DJ Delorie
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #include <stdio.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <math.h>
38 #include "global.h"
39 #include "data.h"
40 #include "error.h"
41 #include "misc.h"
43 #include "hid.h"
44 #include "hid_draw.h"
45 #include "../hidint.h"
46 #include "hid/common/hidnogui.h"
47 #include "hid/common/draw_helpers.h"
48 #include "png.h"
50 /* the gd library which makes this all so easy */
51 #include <gd.h>
53 #include "hid/common/hidinit.h"
55 #ifdef HAVE_LIBDMALLOC
56 #include <dmalloc.h>
57 #endif
59 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort()
61 static HID png_hid;
62 static HID_DRAW png_graphics;
63 static HID_DRAW_CLASS png_graphics_class;
65 static void *color_cache = NULL;
66 static void *brush_cache = NULL;
68 static double bloat = 0;
69 static double scale = 1;
70 static Coord x_shift = 0;
71 static Coord y_shift = 0;
72 static int show_bottom_side;
73 #define SCALE(w) ((int)round((w)/scale))
74 #define SCALE_X(x) ((int)round(((x) - x_shift)/scale))
75 #define SCALE_Y(y) ((int)round(((show_bottom_side ? (PCB->MaxHeight-(y)) : (y)) - y_shift)/scale))
76 #define SWAP_IF_SOLDER(a,b) do { Coord c; if (show_bottom_side) { c=a; a=b; b=c; }} while (0)
78 /* Used to detect non-trivial outlines */
79 #define NOT_EDGE_X(x) ((x) != 0 && (x) != PCB->MaxWidth)
80 #define NOT_EDGE_Y(y) ((y) != 0 && (y) != PCB->MaxHeight)
81 #define NOT_EDGE(x,y) (NOT_EDGE_X(x) || NOT_EDGE_Y(y))
83 static void png_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
85 /* The result of a failed gdImageColorAllocate() call */
86 #define BADC -1
88 typedef struct color_struct
90 /* the descriptor used by the gd library */
91 int c;
93 /* so I can figure out what rgb value c refers to */
94 unsigned int r, g, b, a;
96 } color_struct;
98 typedef struct png_gc_struct
100 struct hid_gc_struct hid_gc; /* Parent */
102 EndCapStyle cap;
103 int width;
104 unsigned char r, g, b;
105 color_struct *color;
106 gdImagePtr brush;
107 int is_erase;
108 } *pngGC;
110 static color_struct *black = NULL, *white = NULL;
111 static gdImagePtr im = NULL, master_im, mask_im = NULL;
112 static FILE *f = 0;
113 static int linewidth = -1;
114 static int lastgroup = -1;
115 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
116 static int lastcap = -1;
117 static int print_group[MAX_GROUP];
118 static int print_layer[MAX_LAYER];
120 /* For photo-mode we need the following layers as monochrome masks:
122 top soldermask
123 top silk
124 copper layers
125 drill
128 #define PHOTO_FLIP_X 1
129 #define PHOTO_FLIP_Y 2
131 static int photo_mode, photo_flip;
132 static gdImagePtr photo_copper[MAX_LAYER+2];
133 static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im;
134 static gdImagePtr photo_outline;
135 static int photo_groups[MAX_LAYER+2], photo_ngroups;
136 static int photo_has_inners;
138 static int doing_outline, have_outline;
140 #define FMT_gif "GIF"
141 #define FMT_jpg "JPEG"
142 #define FMT_png "PNG"
144 /* If this table has no elements in it, then we have no reason to
145 register this HID and will refrain from doing so at the end of this
146 file. */
148 #undef HAVE_SOME_FORMAT
150 static const char *filetypes[] = {
151 #ifdef HAVE_GDIMAGEPNG
152 FMT_png,
153 #define HAVE_SOME_FORMAT 1
154 #endif
156 #ifdef HAVE_GDIMAGEGIF
157 FMT_gif,
158 #define HAVE_SOME_FORMAT 1
159 #endif
161 #ifdef HAVE_GDIMAGEJPEG
162 FMT_jpg,
163 #define HAVE_SOME_FORMAT 1
164 #endif
166 NULL
170 static const char *mask_colour_names[] = {
171 "green",
172 "red",
173 "blue",
174 "purple",
175 "black",
176 "white",
177 NULL
180 // These values were arrived at through trial and error.
181 // One potential improvement (especially for white) is
182 // to use separate color_structs for the multiplication
183 // and addition parts of the mask math.
184 static const color_struct mask_colours[] = {
185 #define MASK_COLOUR_GREEN 0
186 {.r = 60, .g = 160, .b = 60},
187 #define MASK_COLOUR_RED 1
188 {.r = 140, .g = 25, .b = 25},
189 #define MASK_COLOUR_BLUE 2
190 {.r = 50, .g = 50, .b = 160},
191 #define MASK_COLOUR_PURPLE 3
192 {.r = 60, .g = 20, .b = 70},
193 #define MASK_COLOUR_BLACK 4
194 {.r = 20, .g = 20, .b = 20},
195 #define MASK_COLOUR_WHITE 5
196 {.r = 167, .g = 230, .b = 162}, // <-- needs improvement over FR4
201 static const char *plating_type_names[] = {
202 #define PLATING_TIN 0
203 "tinned",
204 #define PLATING_GOLD 1
205 "gold",
206 #define PLATING_SILVER 2
207 "silver",
208 #define PLATING_COPPER 3
209 "copper",
210 NULL
215 static const char *silk_colour_names[] = {
216 "white",
217 "black",
218 "yellow",
219 NULL
222 static const color_struct silk_colours[] = {
223 #define SILK_COLOUR_WHITE 0
224 {.r = 224, .g = 224, .b = 224},
225 #define SILK_COLOUR_BLACK 1
226 {.r = 14, .g = 14, .b = 14},
227 #define SILK_COLOUR_YELLOW 2
228 {.r = 185, .g = 185, .b = 10},
232 static const color_struct silk_top_shadow = {.r = 21, .g = 21, .b = 21};
233 static const color_struct silk_bottom_shadow = {.r = 14, .g = 14, .b = 14};
235 HID_Attribute png_attribute_list[] = {
236 /* other HIDs expect this to be first. */
238 /* %start-doc options "93 PNG Options"
239 @ftable @code
240 @item --outfile <string>
241 Name of the file to be exported to. Can contain a path.
242 @end ftable
243 %end-doc
245 {"outfile", "Graphics output file",
246 HID_String, 0, 0, {0, 0, 0}, 0, 0},
247 #define HA_pngfile 0
249 /* %start-doc options "93 PNG Options"
250 @ftable @code
251 @item --dpi
252 Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout.
253 @end ftable
254 %end-doc
256 {"dpi", "Scale factor (pixels/inch). 0 to scale to specified size",
257 HID_Integer, 0, 1000, {100, 0, 0}, 0, 0},
258 #define HA_dpi 1
260 /* %start-doc options "93 PNG Options"
261 @ftable @code
262 @item --x-max
263 Width of the png image in pixels. No constraint, when set to 0.
264 @end ftable
265 %end-doc
267 {"x-max", "Maximum width (pixels). 0 to not constrain",
268 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
269 #define HA_xmax 2
271 /* %start-doc options "93 PNG Options"
272 @ftable @code
273 @item --y-max
274 Height of the png output in pixels. No constraint, when set to 0.
275 @end ftable
276 %end-doc
278 {"y-max", "Maximum height (pixels). 0 to not constrain",
279 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
280 #define HA_ymax 3
282 /* %start-doc options "93 PNG Options"
283 @ftable @code
284 @item --xy-max
285 Maximum width and height of the PNG output in pixels. No constraint, when set to 0.
286 @end ftable
287 %end-doc
289 {"xy-max", "Maximum width and height (pixels). 0 to not constrain",
290 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
291 #define HA_xymax 4
293 /* %start-doc options "93 PNG Options"
294 @ftable @code
295 @item --as-shown
296 Export layers as shown on screen.
297 @end ftable
298 %end-doc
300 {"as-shown", "Export layers as shown on screen",
301 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
302 #define HA_as_shown 5
304 /* %start-doc options "93 PNG Options"
305 @ftable @code
306 @item --monochrome
307 Convert output to monochrome.
308 @end ftable
309 %end-doc
311 {"monochrome", "Convert to monochrome",
312 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
313 #define HA_mono 6
315 /* %start-doc options "93 PNG Options"
316 @ftable @code
317 @item --only-visible
318 Limit the bounds of the exported PNG image to the visible items.
319 @end ftable
320 %end-doc
322 {"only-visible", "Limit the bounds of the PNG image to the visible items",
323 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
324 #define HA_only_visible 7
326 /* %start-doc options "93 PNG Options"
327 @ftable @code
328 @item --use-alpha
329 Make the background and any holes transparent.
330 @end ftable
331 %end-doc
333 {"use-alpha", "Make the background and any holes transparent",
334 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
335 #define HA_use_alpha 8
337 /* %start-doc options "93 PNG Options"
338 @ftable @code
339 @item --fill-holes
340 Drill holes in pins/pads are filled, not hollow.
341 @end ftable
342 %end-doc
344 {"fill-holes", "Drill holes in pins/pads are filled, not hollow",
345 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
346 #define HA_fill_holes 9
348 /* %start-doc options "93 PNG Options"
349 @ftable @code
350 @item --format <string>
351 File format to be exported. Parameter @code{<string>} can be @samp{PNG},
352 @samp{GIF}, or @samp{JPEG}.
353 @end ftable
354 %end-doc
356 {"format", "Export file format",
357 HID_Enum, 0, 0, {0, 0, 0}, filetypes, 0},
358 #define HA_filetype 10
360 /* %start-doc options "93 PNG Options"
361 @ftable @code
362 @item --png-bloat <num><dim>
363 Amount of extra thickness to add to traces, pads, or pin edges. The parameter
364 @samp{<num><dim>} is a number, appended by a dimension @samp{mm}, @samp{mil}, or
365 @samp{pix}. If no dimension is given, the default dimension is 1/100 mil.
366 @end ftable
367 %end-doc
369 {"png-bloat", "Amount (in/mm/mil/pix) to add to trace/pad/pin edges (1 = 1/100 mil)",
370 HID_String, 0, 0, {0, 0, 0}, 0, 0},
371 #define HA_bloat 11
373 /* %start-doc options "93 PNG Options"
374 @ftable @code
375 @cindex photo-mode
376 @item --photo-mode
377 Export a photo realistic image of the layout.
378 @end ftable
379 %end-doc
381 {"photo-mode", "Photo-realistic export mode",
382 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
383 #define HA_photo_mode 12
385 /* %start-doc options "93 PNG Options"
386 @ftable @code
387 @item --photo-flip-x
388 In photo-realistic mode, export the reverse side of the layout. Left-right flip.
389 @end ftable
390 %end-doc
392 {"photo-flip-x", "Show reverse side of the board, left-right flip",
393 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
394 #define HA_photo_flip_x 13
396 /* %start-doc options "93 PNG Options"
397 @ftable @code
398 @item --photo-flip-y
399 In photo-realistic mode, export the reverse side of the layout. Up-down flip.
400 @end ftable
401 %end-doc
403 {"photo-flip-y", "Show reverse side of the board, up-down flip",
404 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
405 #define HA_photo_flip_y 14
407 /* %start-doc options "93 PNG Options"
408 @ftable @code
409 @cindex photo-mask-colour
410 @item --photo-mask-colour <colour>
411 In photo-realistic mode, export the solder mask as this colour. Parameter
412 @code{<colour>} can be @samp{green}, @samp{red}, @samp{blue}, or @samp{purple}.
413 @end ftable
414 %end-doc
416 {"photo-mask-colour", "Colour for the exported colour mask",
417 HID_Enum, 0, 0, {0, 0, 0}, mask_colour_names, 0},
418 #define HA_photo_mask_colour 15
420 /* %start-doc options "93 PNG Options"
421 @ftable @code
422 @cindex photo-plating
423 @item --photo-plating
424 In photo-realistic mode, export the exposed copper as though it has this type
425 of plating. Parameter @code{<colour>} can be @samp{tinned}, @samp{gold},
426 @samp{silver}, or @samp{copper}.
427 @end ftable
428 %end-doc
430 {"photo-plating", "Type of plating applied to exposed copper in photo-mode",
431 HID_Enum, 0, 0, {0, 0, 0}, plating_type_names, 0},
432 #define HA_photo_plating 16
434 /* %start-doc options "93 PNG Options"
435 @ftable @code
436 @cindex photo-silk-colour
437 @item --photo-silk-colour
438 In photo-realistic mode, export the silk screen as this colour. Parameter
439 @code{<colour>} can be @samp{white}, @samp{black}, or @samp{yellow}.
440 @end ftable
441 %end-doc
443 {"photo-silk-colour", "Colour for the exported colour mask",
444 HID_Enum, 0, 0, {0, 0, 0}, silk_colour_names, 0},
445 #define HA_photo_silk_colour 17
447 {"ben-mode", ATTR_UNDOCUMENTED,
448 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
449 #define HA_ben_mode 12
451 {"ben-flip-x", ATTR_UNDOCUMENTED,
452 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
453 #define HA_ben_flip_x 13
455 {"ben-flip-y", ATTR_UNDOCUMENTED,
456 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
457 #define HA_ben_flip_y 14
460 #define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0]))
462 REGISTER_ATTRIBUTES (png_attribute_list)
464 static HID_Attr_Val png_values[NUM_OPTIONS];
466 static const char *get_file_suffix(void)
468 const char *result = NULL;
469 const char *fmt;
471 fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value];
473 if (fmt == NULL)
474 ; /* Do nothing */
475 else if (strcmp (fmt, FMT_gif) == 0)
476 result=".gif";
477 else if (strcmp (fmt, FMT_jpg) == 0)
478 result=".jpg";
479 else if (strcmp (fmt, FMT_png) == 0)
480 result=".png";
482 if (result == NULL)
484 fprintf (stderr, "Error: Invalid graphic file format\n");
485 result=".???";
487 return result;
490 static HID_Attribute *
491 png_get_export_options (int *n)
493 static char *last_made_filename = 0;
494 const char *suffix = get_file_suffix();
496 if (PCB)
497 derive_default_filename (PCB->Filename,
498 &png_attribute_list[HA_pngfile],
499 suffix,
500 &last_made_filename);
502 if (n)
503 *n = NUM_OPTIONS;
504 return png_attribute_list;
507 static int top_group, bottom_group;
509 static int
510 layer_stack_sort (const void *va, const void *vb)
512 int a_layer = *(int *) va;
513 int b_layer = *(int *) vb;
514 int a_group = GetLayerGroupNumberByNumber (a_layer);
515 int b_group = GetLayerGroupNumberByNumber (b_layer);
516 int aside = (a_group == bottom_group ? 0 : a_group == top_group ? 2 : 1);
517 int bside = (b_group == bottom_group ? 0 : b_group == top_group ? 2 : 1);
519 if (bside != aside)
520 return bside - aside;
522 if (b_group != a_group)
523 return b_group - a_group;
525 return b_layer - a_layer;
528 static const char *filename;
529 static BoxType *bounds;
530 static int in_mono, as_shown, fill_holes;
532 static void
533 parse_bloat (const char *str)
535 UnitList extra_units = {
536 { "pix", scale, 0 },
537 { "px", scale, 0 },
538 { "", 0, 0 }
540 if (str == NULL)
541 return;
542 bloat = GetValueEx (str, NULL, NULL, extra_units, "");
545 void
546 png_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
548 int i;
549 static int saved_layer_stack[MAX_LAYER];
550 int saved_show_bottom_side;
551 BoxType region;
552 FlagType save_flags;
554 f = the_file;
556 region.X1 = 0;
557 region.Y1 = 0;
558 region.X2 = PCB->MaxWidth;
559 region.Y2 = PCB->MaxHeight;
561 if (options[HA_only_visible].int_value)
562 bounds = GetDataBoundingBox (PCB->Data);
563 else
564 bounds = &region;
566 memset (print_group, 0, sizeof (print_group));
567 memset (print_layer, 0, sizeof (print_layer));
569 for (i = 0; i < max_copper_layer; i++)
571 LayerType *layer = PCB->Data->Layer + i;
572 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
573 print_group[GetLayerGroupNumberByNumber (i)] = 1;
575 print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
576 print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
577 for (i = 0; i < max_copper_layer; i++)
578 if (print_group[GetLayerGroupNumberByNumber (i)])
579 print_layer[i] = 1;
581 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
582 save_flags = PCB->Flags;
583 saved_show_bottom_side = Settings.ShowBottomSide;
585 as_shown = options[HA_as_shown].int_value;
586 fill_holes = options[HA_fill_holes].int_value;
588 if (!options[HA_as_shown].int_value)
590 CLEAR_FLAG (SHOWMASKFLAG, PCB);
591 Settings.ShowBottomSide = 0;
593 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
594 bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
595 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
597 CLEAR_FLAG(THINDRAWFLAG, PCB);
598 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
600 if (photo_mode)
602 int i, n=0;
603 SET_FLAG (SHOWMASKFLAG, PCB);
604 photo_has_inners = 0;
605 if (top_group < bottom_group)
606 for (i = top_group; i <= bottom_group; i++)
608 photo_groups[n++] = i;
609 if (i != top_group && i != bottom_group
610 && ! IsLayerGroupEmpty (i))
611 photo_has_inners = 1;
613 else
614 for (i = top_group; i >= bottom_group; i--)
616 photo_groups[n++] = i;
617 if (i != top_group && i != bottom_group
618 && ! IsLayerGroupEmpty (i))
619 photo_has_inners = 1;
621 if (!photo_has_inners)
623 photo_groups[1] = photo_groups[n - 1];
624 n = 2;
626 photo_ngroups = n;
628 if (photo_flip)
630 for (i=0, n=photo_ngroups-1; i<n; i++, n--)
632 int tmp = photo_groups[i];
633 photo_groups[i] = photo_groups[n];
634 photo_groups[n] = tmp;
639 linewidth = -1;
640 lastbrush = (gdImagePtr)((void *) -1);
641 lastcap = -1;
642 lastgroup = -1;
643 show_bottom_side = Settings.ShowBottomSide;
645 in_mono = options[HA_mono].int_value;
647 if (!photo_mode && Settings.ShowBottomSide)
649 int i, j;
650 for (i=0, j=max_copper_layer-1; i<j; i++, j--)
652 int k = LayerStack[i];
653 LayerStack[i] = LayerStack[j];
654 LayerStack[j] = k;
658 hid_expose_callback (&png_hid, bounds, 0);
660 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
661 PCB->Flags = save_flags;
662 Settings.ShowBottomSide = saved_show_bottom_side;
665 static void
666 clip (color_struct *dest, color_struct *source)
668 #define CLIP(var) \
669 dest->var = source->var; \
670 if (dest->var > 255) dest->var = 255; \
671 if (dest->var < 0) dest->var = 0;
673 CLIP (r);
674 CLIP (g);
675 CLIP (b);
676 #undef CLIP
679 static void
680 blend (color_struct *dest, double a_amount, color_struct *a, color_struct *b)
682 dest->r = a->r * a_amount + b->r * (1 - a_amount);
683 dest->g = a->g * a_amount + b->g * (1 - a_amount);
684 dest->b = a->b * a_amount + b->b * (1 - a_amount);
687 static void
688 multiply (color_struct *dest, color_struct *a, color_struct *b)
690 dest->r = (a->r * b->r) / 255;
691 dest->g = (a->g * b->g) / 255;
692 dest->b = (a->b * b->b) / 255;
695 static void
696 add (color_struct *dest, double a_amount, const color_struct *a, double b_amount, const color_struct *b)
698 dest->r = a->r * a_amount + b->r * b_amount;
699 dest->g = a->g * a_amount + b->g * b_amount;
700 dest->b = a->b * a_amount + b->b * b_amount;
702 clip (dest, dest);
705 static void
706 subtract (color_struct *dest, double a_amount, const color_struct *a, double b_amount, const color_struct *b)
708 dest->r = a->r * a_amount - b->r * b_amount;
709 dest->g = a->g * a_amount - b->g * b_amount;
710 dest->b = a->b * a_amount - b->b * b_amount;
712 clip (dest, dest);
715 static void
716 rgb (color_struct *dest, int r, int g, int b)
718 dest->r = r;
719 dest->g = g;
720 dest->b = b;
723 static int smshadows[3][3] = {
724 { 1, 20, 1 },
725 { 10, 0, -10 },
726 { -1, -20, -1 },
729 static int shadows[5][5] = {
730 { 1, 1, 1, 1, -1 },
731 { 1, 1, 1, -1, -1 },
732 { 1, 1, 0, -1, -1 },
733 { 1, -1, -1, -1, -1 },
734 { -1, -1, -1, -1, -1 },
737 /* black and white are 0 and 1 */
738 #define TOP_SHADOW 2
739 #define BOTTOM_SHADOW 3
741 static void
742 ts_bs (gdImagePtr im)
744 int x, y, sx, sy, si;
745 for (x=0; x<gdImageSX(im); x++)
746 for (y=0; y<gdImageSY(im); y++)
748 si = 0;
749 for (sx=-2; sx<3; sx++)
750 for (sy=-2; sy<3; sy++)
751 if (!gdImageGetPixel (im, x+sx, y+sy))
752 si += shadows[sx+2][sy+2];
753 if (gdImageGetPixel (im, x, y))
755 if (si > 1)
756 gdImageSetPixel (im, x, y, TOP_SHADOW);
757 else if (si < -1)
758 gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
763 static void
764 ts_bs_sm (gdImagePtr im)
766 int x, y, sx, sy, si;
767 for (x=0; x<gdImageSX(im); x++)
768 for (y=0; y<gdImageSY(im); y++)
770 si = 0;
771 for (sx=-1; sx<2; sx++)
772 for (sy=-1; sy<2; sy++)
773 if (!gdImageGetPixel (im, x+sx, y+sy))
774 si += smshadows[sx+1][sy+1];
775 if (gdImageGetPixel (im, x, y))
777 if (si > 1)
778 gdImageSetPixel (im, x, y, TOP_SHADOW);
779 else if (si < -1)
780 gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
785 static void
786 png_do_export (HID_Attr_Val * options)
788 int save_ons[MAX_LAYER + 2];
789 int i;
790 BoxType *bbox;
791 Coord w, h;
792 Coord xmax, ymax;
793 int dpi;
794 const char *fmt;
795 bool format_error = false;
797 if (color_cache)
799 free (color_cache);
800 color_cache = NULL;
803 if (brush_cache)
805 free (brush_cache);
806 brush_cache = NULL;
809 if (!options)
811 png_get_export_options (0);
812 for (i = 0; i < NUM_OPTIONS; i++)
813 png_values[i] = png_attribute_list[i].default_val;
814 options = png_values;
817 if (options[HA_photo_mode].int_value
818 || options[HA_ben_mode].int_value)
820 photo_mode = 1;
821 options[HA_mono].int_value = 1;
822 options[HA_as_shown].int_value = 0;
823 memset (photo_copper, 0, sizeof(photo_copper));
824 photo_silk = photo_mask = photo_drill = 0;
825 photo_outline = 0;
826 if (options[HA_photo_flip_x].int_value
827 || options[HA_ben_flip_x].int_value)
828 photo_flip = PHOTO_FLIP_X;
829 else if (options[HA_photo_flip_y].int_value
830 || options[HA_ben_flip_y].int_value)
831 photo_flip = PHOTO_FLIP_Y;
832 else
833 photo_flip = 0;
835 else
836 photo_mode = 0;
838 filename = options[HA_pngfile].str_value;
839 if (!filename)
840 filename = "pcb-out.png";
842 /* figure out width and height of the board */
843 if (options[HA_only_visible].int_value)
845 bbox = GetDataBoundingBox (PCB->Data);
846 x_shift = bbox->X1;
847 y_shift = bbox->Y1;
848 h = bbox->Y2 - bbox->Y1;
849 w = bbox->X2 - bbox->X1;
851 else
853 x_shift = 0;
854 y_shift = 0;
855 h = PCB->MaxHeight;
856 w = PCB->MaxWidth;
860 * figure out the scale factor we need to make the image
861 * fit in our specified PNG file size
863 xmax = ymax = dpi = 0;
864 if (options[HA_dpi].int_value != 0)
866 dpi = options[HA_dpi].int_value;
867 if (dpi < 0)
869 fprintf (stderr, "ERROR: dpi may not be < 0\n");
870 return;
874 if (options[HA_xmax].int_value > 0)
876 xmax = options[HA_xmax].int_value;
877 dpi = 0;
880 if (options[HA_ymax].int_value > 0)
882 ymax = options[HA_ymax].int_value;
883 dpi = 0;
886 if (options[HA_xymax].int_value > 0)
888 dpi = 0;
889 if (options[HA_xymax].int_value < xmax || xmax == 0)
890 xmax = options[HA_xymax].int_value;
891 if (options[HA_xymax].int_value < ymax || ymax == 0)
892 ymax = options[HA_xymax].int_value;
895 if (xmax < 0 || ymax < 0)
897 fprintf (stderr, "ERROR: xmax and ymax may not be < 0\n");
898 return;
901 if (dpi > 0)
904 * a scale of 1 means 1 pixel is 1 inch
905 * a scale of 10 means 1 pixel is 10 inches
907 scale = round(INCH_TO_COORD(1) / (double) dpi);
908 w = w / scale;
909 h = h / scale;
911 else if( xmax == 0 && ymax == 0)
913 fprintf(stderr, "ERROR: You may not set both xmax, ymax,"
914 "and xy-max to zero\n");
915 return;
917 else
919 if (ymax == 0
920 || ( (xmax > 0)
921 && ((w / xmax) > (h / ymax)) ) )
923 scale = w / xmax;
924 h = h / scale;
925 w = xmax;
927 else
929 scale = h / ymax;
930 w = w / scale;
931 h = ymax;
935 im = gdImageCreate (w, h);
936 if (im == NULL)
938 Message ("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, w, h);
939 return;
942 master_im = im;
944 parse_bloat (options[HA_bloat].str_value);
947 * Allocate white and black -- the first color allocated
948 * becomes the background color
951 white = (color_struct *) malloc (sizeof (color_struct));
952 white->r = white->g = white->b = 255;
953 if (options[HA_use_alpha].int_value)
954 white->a = 127;
955 else
956 white->a = 0;
957 white->c = gdImageColorAllocateAlpha (im, white->r, white->g, white->b, white->a);
958 if (white->c == BADC)
960 Message ("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__);
961 return;
964 gdImageFilledRectangle (im, 0, 0, gdImageSX (im), gdImageSY (im), white->c);
966 black = (color_struct *) malloc (sizeof (color_struct));
967 black->r = black->g = black->b = black->a = 0;
968 black->c = gdImageColorAllocate (im, black->r, black->g, black->b);
969 if (black->c == BADC)
971 Message ("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__);
972 return;
975 f = fopen (filename, "wb");
976 if (!f)
978 perror (filename);
979 return;
982 if (!options[HA_as_shown].int_value)
983 hid_save_and_show_layer_ons (save_ons);
985 png_hid_export_to_file (f, options);
987 if (!options[HA_as_shown].int_value)
988 hid_restore_layer_ons (save_ons);
990 if (photo_mode)
992 int x, y;
993 color_struct white, black, fr4;
995 rgb (&white, 255, 255, 255);
996 rgb (&black, 0, 0, 0);
997 rgb (&fr4, 70, 70, 70);
999 im = master_im;
1001 ts_bs (photo_copper[photo_groups[0]]);
1002 ts_bs (photo_silk);
1003 ts_bs_sm (photo_mask);
1005 if (photo_outline && have_outline) {
1006 int black=gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00);
1008 // go all the way around the image, trying to fill the outline
1009 for (x=0; x<gdImageSX(im); x++) {
1010 gdImageFillToBorder(photo_outline, x, 0, black, black);
1011 gdImageFillToBorder(photo_outline, x, gdImageSY(im)-1, black, black);
1013 for (y=1; y<gdImageSY(im)-1; y++) {
1014 gdImageFillToBorder(photo_outline, 0, y, black, black);
1015 gdImageFillToBorder(photo_outline, gdImageSX(im)-1, y, black, black);
1021 for (x=0; x<gdImageSX (im); x++)
1023 for (y=0; y<gdImageSY (im); y++)
1025 color_struct p, cop;
1026 color_struct mask_colour, silk_colour;
1027 int cc, mask, silk;
1028 int transparent;
1030 if (photo_outline && have_outline) {
1031 transparent=gdImageGetPixel(photo_outline, x, y);
1032 } else {
1033 transparent=0;
1036 mask = photo_mask ? gdImageGetPixel (photo_mask, x, y) : 0;
1037 silk = photo_silk ? gdImageGetPixel (photo_silk, x, y) : 0;
1039 if (photo_copper[photo_groups[1]]
1040 && gdImageGetPixel (photo_copper[photo_groups[1]], x, y))
1041 rgb (&cop, 40, 40, 40);
1042 else
1043 rgb (&cop, 100, 100, 110);
1045 if (photo_ngroups == 2)
1046 blend (&cop, 0.3, &cop, &fr4);
1048 cc = gdImageGetPixel (photo_copper[photo_groups[0]], x, y);
1049 if (cc)
1051 int r;
1053 if (mask)
1054 rgb (&cop, 220, 145, 230);
1055 else
1057 if (options[HA_photo_plating].int_value == PLATING_GOLD)
1059 // ENIG
1060 rgb (&cop, 185, 146, 52);
1062 // increase top shadow to increase shininess
1063 if (cc == TOP_SHADOW)
1064 blend (&cop, 0.7, &cop, &white);
1066 else if (options[HA_photo_plating].int_value == PLATING_TIN)
1068 // tinned
1069 rgb (&cop, 140, 150, 160);
1071 // add some variation to make it look more matte
1072 r = (rand() % 5 - 2) * 2;
1073 cop.r += r;
1074 cop.g += r;
1075 cop.b += r;
1077 else if (options[HA_photo_plating].int_value == PLATING_SILVER)
1079 // silver
1080 rgb (&cop, 192, 192, 185);
1082 // increase top shadow to increase shininess
1083 if (cc == TOP_SHADOW)
1084 blend (&cop, 0.7, &cop, &white);
1086 else if (options[HA_photo_plating].int_value == PLATING_COPPER)
1088 // copper
1089 rgb (&cop, 184, 115, 51);
1091 // increase top shadow to increase shininess
1092 if (cc == TOP_SHADOW)
1093 blend (&cop, 0.7, &cop, &white);
1097 if (cc == TOP_SHADOW)
1098 blend (&cop, 0.7, &cop, &white);
1099 if (cc == BOTTOM_SHADOW)
1100 blend (&cop, 0.7, &cop, &black);
1103 if (photo_drill && !gdImageGetPixel (photo_drill, x, y))
1105 rgb (&p, 0, 0, 0);
1106 transparent=1;
1108 else if (silk)
1110 silk_colour = silk_colours[options[HA_photo_silk_colour].int_value];
1111 blend (&p, 1.0, &silk_colour, &silk_colour);
1112 if (silk == TOP_SHADOW)
1113 add (&p, 1.0, &p, 1.0, &silk_top_shadow);
1114 else if (silk == BOTTOM_SHADOW)
1115 subtract (&p, 1.0, &p, 1.0, &silk_bottom_shadow);
1117 else if (mask)
1119 p = cop;
1120 mask_colour = mask_colours[options[HA_photo_mask_colour].int_value];
1121 multiply (&p, &p, &mask_colour);
1122 add (&p, 1, &p, 0.2, &mask_colour);
1123 if (mask == TOP_SHADOW)
1124 blend (&p, 0.7, &p, &white);
1125 if (mask == BOTTOM_SHADOW)
1126 blend (&p, 0.7, &p, &black);
1128 else
1129 p = cop;
1131 if (options[HA_use_alpha].int_value) {
1133 cc = (transparent)?\
1134 gdImageColorResolveAlpha(im, 0, 0, 0, 127):\
1135 gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0);
1137 } else {
1138 cc = (transparent)?\
1139 gdImageColorResolve(im, 0, 0, 0):\
1140 gdImageColorResolve(im, p.r, p.g, p.b);
1143 if (photo_flip == PHOTO_FLIP_X)
1144 gdImageSetPixel (im, gdImageSX (im) - x - 1, y, cc);
1145 else if (photo_flip == PHOTO_FLIP_Y)
1146 gdImageSetPixel (im, x, gdImageSY (im) - y - 1, cc);
1147 else
1148 gdImageSetPixel (im, x, y, cc);
1153 /* actually write out the image */
1154 fmt = filetypes[options[HA_filetype].int_value];
1156 if (fmt == NULL)
1157 format_error = true;
1158 else if (strcmp (fmt, FMT_gif) == 0)
1159 #ifdef HAVE_GDIMAGEGIF
1160 gdImageGif (im, f);
1161 #else
1162 format_error = true;
1163 #endif
1164 else if (strcmp (fmt, FMT_jpg) == 0)
1165 #ifdef HAVE_GDIMAGEJPEG
1166 gdImageJpeg (im, f, -1);
1167 #else
1168 format_error = true;
1169 #endif
1170 else if (strcmp (fmt, FMT_png) == 0)
1171 #ifdef HAVE_GDIMAGEPNG
1172 gdImagePng (im, f);
1173 #else
1174 format_error = true;
1175 #endif
1176 else
1177 format_error = true;
1179 if (format_error)
1180 fprintf (stderr, "Error: Invalid graphic file format."
1181 " This is a bug. Please report it.\n");
1183 fclose (f);
1185 gdImageDestroy (im);
1188 static void
1189 png_parse_arguments (int *argc, char ***argv)
1191 hid_register_attributes (png_attribute_list,
1192 sizeof (png_attribute_list) /
1193 sizeof (png_attribute_list[0]));
1194 hid_parse_command_line (argc, argv);
1198 static int is_mask;
1199 static int is_drill;
1200 static int is_copper;
1202 static int
1203 png_set_layer (const char *name, int group, int empty)
1205 int idx = (group >= 0
1206 && group <
1207 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
1208 if (name == 0)
1209 name = PCB->Data->Layer[idx].Name;
1211 doing_outline = 0;
1213 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
1214 return 0;
1215 if (SL_TYPE (idx) == SL_ASSY || SL_TYPE (idx) == SL_FAB)
1216 return 0;
1218 if (strcmp (name, "invisible") == 0)
1219 return 0;
1221 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
1222 is_mask = (SL_TYPE (idx) == SL_MASK);
1223 is_copper = (SL_TYPE (idx) == 0);
1225 if (is_drill && fill_holes)
1226 return 0;
1228 if (SL_TYPE (idx) == SL_PASTE)
1229 return 0;
1231 if (photo_mode)
1233 switch (idx)
1235 case SL (SILK, TOP):
1236 if (photo_flip)
1237 return 0;
1238 photo_im = &photo_silk;
1239 break;
1240 case SL (SILK, BOTTOM):
1241 if (!photo_flip)
1242 return 0;
1243 photo_im = &photo_silk;
1244 break;
1246 case SL (MASK, TOP):
1247 if (photo_flip)
1248 return 0;
1249 photo_im = &photo_mask;
1250 break;
1251 case SL (MASK, BOTTOM):
1252 if (!photo_flip)
1253 return 0;
1254 photo_im = &photo_mask;
1255 break;
1257 case SL (PDRILL, 0):
1258 case SL (UDRILL, 0):
1259 photo_im = &photo_drill;
1260 break;
1262 default:
1263 if (idx < 0)
1264 return 0;
1266 if (strcmp (name, "outline") == 0)
1268 doing_outline = 1;
1269 have_outline = 0;
1270 photo_im = &photo_outline;
1272 else
1273 photo_im = photo_copper + group;
1275 break;
1278 if (! *photo_im)
1280 static color_struct *black = NULL, *white = NULL;
1281 *photo_im = gdImageCreate (gdImageSX (im), gdImageSY (im));
1282 if (photo_im == NULL)
1284 Message ("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__,
1285 gdImageSX (im), gdImageSY (im));
1286 return 0;
1290 white = (color_struct *) malloc (sizeof (color_struct));
1291 white->r = white->g = white->b = 255;
1292 white->a = 0;
1293 white->c = gdImageColorAllocate (*photo_im, white->r, white->g, white->b);
1294 if (white->c == BADC)
1296 Message ("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__);
1297 return 0;
1300 black = (color_struct *) malloc (sizeof (color_struct));
1301 black->r = black->g = black->b = black->a = 0;
1302 black->c = gdImageColorAllocate (*photo_im, black->r, black->g, black->b);
1303 if (black->c == BADC)
1305 Message ("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__);
1306 return 0;
1309 if (idx == SL (PDRILL, 0)
1310 || idx == SL (UDRILL, 0))
1311 gdImageFilledRectangle (*photo_im, 0, 0, gdImageSX (im), gdImageSY (im), black->c);
1313 im = *photo_im;
1314 return 1;
1317 if (as_shown)
1319 switch (idx)
1321 case SL (SILK, TOP):
1322 case SL (SILK, BOTTOM):
1323 if (SL_MYSIDE (idx))
1324 return PCB->ElementOn;
1325 return 0;
1327 case SL (MASK, TOP):
1328 case SL (MASK, BOTTOM):
1329 return TEST_FLAG (SHOWMASKFLAG, PCB) && SL_MYSIDE (idx);
1332 else
1334 if (is_mask)
1335 return 0;
1337 switch (idx)
1339 case SL (SILK, TOP):
1340 return 1;
1341 case SL (SILK, BOTTOM):
1342 return 0;
1346 return 1;
1350 static hidGC
1351 png_make_gc (void)
1353 hidGC gc = (hidGC) calloc (1, sizeof (struct png_gc_struct));
1354 pngGC png_gc = (pngGC)gc;
1356 gc->hid = &png_hid;
1357 gc->hid_draw = &png_graphics;
1359 png_gc->cap = Trace_Cap;
1360 png_gc->width = 1;
1361 png_gc->color = (color_struct *) malloc (sizeof (color_struct));
1362 png_gc->color->r = png_gc->color->g = png_gc->color->b = png_gc->color->a = 0;
1363 png_gc->color->c = 0;
1364 png_gc->is_erase = 0;
1366 return gc;
1369 static void
1370 png_destroy_gc (hidGC gc)
1372 free (gc);
1375 static void
1376 png_use_mask (enum mask_mode mode)
1378 if (photo_mode)
1379 return;
1381 if (mode == HID_MASK_CLEAR)
1383 return;
1385 if (mode != HID_MASK_OFF)
1387 if (mask_im == NULL)
1389 mask_im = gdImageCreate (gdImageSX (im), gdImageSY (im));
1390 if (!mask_im)
1392 Message ("%s(): gdImageCreate(%d, %d) returned NULL. Corrupt export!\n",
1393 __FUNCTION__, gdImageSY (im), gdImageSY (im));
1394 return;
1396 gdImagePaletteCopy (mask_im, im);
1398 im = mask_im;
1399 gdImageFilledRectangle (mask_im, 0, 0, gdImageSX (mask_im), gdImageSY (mask_im), white->c);
1401 else
1403 int x, y, c;
1405 im = master_im;
1407 for (x=0; x<gdImageSX (im); x++)
1408 for (y=0; y<gdImageSY (im); y++)
1410 c = gdImageGetPixel (mask_im, x, y);
1411 if (c)
1412 gdImageSetPixel (im, x, y, c);
1417 static void
1418 png_set_color (hidGC gc, const char *name)
1420 pngGC png_gc = (pngGC)gc;
1421 hidval cval;
1423 if (im == NULL)
1424 return;
1426 if (name == NULL)
1427 name = "#ff0000";
1429 if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
1431 png_gc->color = white;
1432 png_gc->is_erase = 1;
1433 return;
1435 png_gc->is_erase = 0;
1437 if (in_mono || (strcmp (name, "#000000") == 0))
1439 png_gc->color = black;
1440 return;
1443 if (hid_cache_color (0, name, &cval, &color_cache))
1445 png_gc->color = (color_struct *)cval.ptr;
1447 else if (name[0] == '#')
1449 png_gc->color = (color_struct *) malloc (sizeof (color_struct));
1450 sscanf (name + 1, "%2x%2x%2x", &(png_gc->color->r), &(png_gc->color->g),
1451 &(png_gc->color->b));
1452 png_gc->color->c =
1453 gdImageColorAllocate (master_im, png_gc->color->r, png_gc->color->g, png_gc->color->b);
1454 if (png_gc->color->c == BADC)
1456 Message ("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__);
1457 return;
1459 cval.ptr = png_gc->color;
1460 hid_cache_color (1, name, &cval, &color_cache);
1462 else
1464 printf ("WE SHOULD NOT BE HERE!!!\n");
1465 png_gc->color = black;
1470 static void
1471 png_set_line_cap (hidGC gc, EndCapStyle style)
1473 pngGC png_gc = (pngGC)gc;
1475 png_gc->cap = style;
1478 static void
1479 png_set_line_width (hidGC gc, Coord width)
1481 pngGC png_gc = (pngGC)gc;
1483 png_gc->width = width;
1486 static void
1487 png_set_draw_xor (hidGC gc, int xor_)
1492 static void
1493 use_gc (hidGC gc)
1495 pngGC png_gc = (pngGC)gc;
1497 int need_brush = 0;
1499 if (gc->hid != &png_hid)
1501 fprintf (stderr, "Fatal: GC from another HID passed to png HID\n");
1502 abort ();
1505 if (linewidth != png_gc->width)
1507 /* Make sure the scaling doesn't erase lines completely */
1508 if (SCALE (png_gc->width) == 0 && png_gc->width > 0)
1509 gdImageSetThickness (im, 1);
1510 else
1511 gdImageSetThickness (im, SCALE (png_gc->width + 2*bloat));
1512 linewidth = png_gc->width;
1513 need_brush = 1;
1516 if (lastbrush != png_gc->brush || need_brush)
1518 hidval bval;
1519 char name[256];
1520 char type;
1521 int r;
1523 switch (png_gc->cap)
1525 case Round_Cap:
1526 case Trace_Cap:
1527 type = 'C';
1528 break;
1529 default:
1530 case Square_Cap:
1531 type = 'S';
1532 break;
1534 if (png_gc->width)
1535 r = SCALE (png_gc->width + 2*bloat);
1536 else
1537 r = 1;
1539 /* do not allow a brush size that is zero width. In this case limit to a single pixel. */
1540 if (r == 0)
1542 r = 1;
1545 sprintf (name, "#%.2x%.2x%.2x_%c_%d", png_gc->color->r, png_gc->color->g,
1546 png_gc->color->b, type, r);
1548 if (hid_cache_color (0, name, &bval, &brush_cache))
1550 png_gc->brush = (gdImagePtr)bval.ptr;
1552 else
1554 int bg, fg;
1555 png_gc->brush = gdImageCreate (r, r);
1556 if (png_gc->brush == NULL)
1558 Message ("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, r, r);
1559 return;
1562 bg = gdImageColorAllocate (png_gc->brush, 255, 255, 255);
1563 if (bg == BADC)
1565 Message ("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__);
1566 return;
1568 fg =
1569 gdImageColorAllocateAlpha (png_gc->brush, png_gc->color->r, png_gc->color->g, png_gc->color->b, 0);
1570 if (fg == BADC)
1572 Message ("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__);
1573 return;
1575 gdImageColorTransparent (png_gc->brush, bg);
1578 * if we shrunk to a radius/box width of zero, then just use
1579 * a single pixel to draw with.
1581 if (r <= 1)
1582 gdImageFilledRectangle (png_gc->brush, 0, 0, 0, 0, fg);
1583 else
1585 if (type == 'C')
1587 gdImageFilledEllipse (png_gc->brush, r/2, r/2, r, r, fg);
1588 /* Make sure the ellipse is the right exact size. */
1589 gdImageSetPixel (png_gc->brush, 0, r/2, fg);
1590 gdImageSetPixel (png_gc->brush, r-1, r/2, fg);
1591 gdImageSetPixel (png_gc->brush, r/2, 0, fg);
1592 gdImageSetPixel (png_gc->brush, r/2, r-1, fg);
1594 else
1595 gdImageFilledRectangle (png_gc->brush, 0, 0, r-1, r-1, fg);
1597 bval.ptr = png_gc->brush;
1598 hid_cache_color (1, name, &bval, &brush_cache);
1601 gdImageSetBrush (im, png_gc->brush);
1602 lastbrush = png_gc->brush;
1607 static void
1608 png_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1610 pngGC png_gc = (pngGC)gc;
1612 use_gc (gc);
1613 gdImageRectangle (im,
1614 SCALE_X (x1), SCALE_Y (y1),
1615 SCALE_X (x2), SCALE_Y (y2), png_gc->color->c);
1618 static void
1619 png_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1621 pngGC png_gc = (pngGC)gc;
1623 use_gc (gc);
1624 gdImageSetThickness (im, 0);
1625 linewidth = 0;
1627 y1 -= bloat;
1628 y2 += bloat;
1629 SWAP_IF_SOLDER (y1, y2);
1631 gdImageFilledRectangle (im, SCALE_X (x1-bloat), SCALE_Y (y1),
1632 SCALE_X (x2+bloat)-1, SCALE_Y (y2)-1, png_gc->color->c);
1633 have_outline |= doing_outline;
1636 static void
1637 png_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1639 pngGC png_gc = (pngGC)gc;
1641 if (x1 == x2 && y1 == y2)
1643 Coord w = png_gc->width / 2;
1644 if (png_gc->cap != Square_Cap)
1645 png_fill_circle (gc, x1, y1, w);
1646 else
1647 png_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
1648 return;
1650 use_gc (gc);
1652 if (NOT_EDGE (x1, y1) || NOT_EDGE (x2, y2))
1653 have_outline |= doing_outline;
1654 if (doing_outline)
1656 /* Special case - lines drawn along the bottom or right edges
1657 are brought in by a pixel to make sure we have contiguous
1658 outlines. */
1659 if (x1 == PCB->MaxWidth && x2 == PCB->MaxWidth)
1661 x1 -= scale/2;
1662 x2 -= scale/2;
1664 if (y1 == PCB->MaxHeight && y2 == PCB->MaxHeight)
1666 y1 -= scale/2;
1667 y2 -= scale/2;
1671 gdImageSetThickness (im, 0);
1672 linewidth = 0;
1673 if(png_gc->cap != Square_Cap || x1 == x2 || y1 == y2 )
1675 gdImageLine (im, SCALE_X (x1), SCALE_Y (y1),
1676 SCALE_X (x2), SCALE_Y (y2), gdBrushed);
1678 else
1681 * if we are drawing a line with a square end cap and it is
1682 * not purely horizontal or vertical, then we need to draw
1683 * it as a filled polygon.
1685 int fg = gdImageColorResolve (im, png_gc->color->r, png_gc->color->g,
1686 png_gc->color->b);
1687 Coord w = png_gc->width;
1688 Coord dwx, dwy;
1690 gdPoint p[4];
1691 double l = Distance(x1, y1, x2, y2) * 2;
1693 w += 2 * bloat;
1694 dwx = -w / l * (y2 - y1); dwy = w / l * (x2 - x1);
1695 p[0].x = SCALE_X (x1 + dwx - dwy); p[0].y = SCALE_Y(y1 + dwy + dwx);
1696 p[1].x = SCALE_X (x1 - dwx - dwy); p[1].y = SCALE_Y(y1 - dwy + dwx);
1697 p[2].x = SCALE_X (x2 - dwx + dwy); p[2].y = SCALE_Y(y2 - dwy - dwx);
1698 p[3].x = SCALE_X (x2 + dwx + dwy); p[3].y = SCALE_Y(y2 + dwy - dwx);
1699 gdImageFilledPolygon (im, p, 4, fg);
1703 static void
1704 png_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1705 Angle start_angle, Angle delta_angle)
1707 pngGC png_gc = (pngGC)gc;
1708 Angle sa, ea;
1711 * zero angle arcs need special handling as gd will output either
1712 * nothing at all or a full circle when passed delta angle of 0 or 360.
1714 if (delta_angle == 0) {
1715 Coord x = (width * cos (start_angle * M_PI / 180));
1716 Coord y = (width * sin (start_angle * M_PI / 180));
1717 x = cx - x;
1718 y = cy + y;
1719 png_fill_circle (gc, x, y, png_gc->width / 2);
1720 return;
1724 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1725 * in pcb, 0 degrees is to the left and +90 degrees is down
1727 start_angle = 180 - start_angle;
1728 delta_angle = -delta_angle;
1729 if (show_bottom_side)
1731 start_angle = - start_angle;
1732 delta_angle = -delta_angle;
1734 if (delta_angle > 0)
1736 sa = start_angle;
1737 ea = start_angle + delta_angle;
1739 else
1741 sa = start_angle + delta_angle;
1742 ea = start_angle;
1746 * make sure we start between 0 and 360 otherwise gd does
1747 * strange things
1749 sa = NormalizeAngle (sa);
1750 ea = NormalizeAngle (ea);
1752 have_outline |= doing_outline;
1754 #if 0
1755 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1756 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1757 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1758 im, SCALE_X (cx), SCALE_Y (cy),
1759 SCALE (width), SCALE (height), sa, ea, png_gc->color->c);
1760 #endif
1761 use_gc (gc);
1762 gdImageSetThickness (im, 0);
1763 linewidth = 0;
1764 gdImageArc (im, SCALE_X (cx), SCALE_Y (cy),
1765 SCALE (2 * width), SCALE (2 * height), sa, ea, gdBrushed);
1768 static void
1769 png_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1771 pngGC png_gc = (pngGC)gc;
1772 Coord my_bloat;
1774 use_gc (gc);
1776 if (fill_holes && png_gc->is_erase && is_copper)
1777 return;
1779 if (png_gc->is_erase)
1780 my_bloat = -2 * bloat;
1781 else
1782 my_bloat = 2 * bloat;
1785 have_outline |= doing_outline;
1787 gdImageSetThickness (im, 0);
1788 linewidth = 0;
1789 gdImageFilledEllipse (im, SCALE_X (cx), SCALE_Y (cy),
1790 SCALE (2 * radius + my_bloat), SCALE (2 * radius + my_bloat), png_gc->color->c);
1794 static void
1795 png_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1797 pngGC png_gc = (pngGC)gc;
1798 int i;
1799 gdPoint *points;
1801 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1802 if (points == NULL)
1804 fprintf (stderr, "ERROR: png_fill_polygon(): malloc failed\n");
1805 exit (1);
1808 use_gc (gc);
1809 for (i = 0; i < n_coords; i++)
1811 if (NOT_EDGE (x[i], y[i]))
1812 have_outline |= doing_outline;
1813 points[i].x = SCALE_X (x[i]);
1814 points[i].y = SCALE_Y (y[i]);
1816 gdImageSetThickness (im, 0);
1817 linewidth = 0;
1818 gdImageFilledPolygon (im, points, n_coords, png_gc->color->c);
1819 free (points);
1822 static void
1823 png_calibrate (double xval, double yval)
1825 CRASH;
1828 static void
1829 png_set_crosshair (int x, int y, int a)
1833 #include "dolists.h"
1835 void
1836 hid_png_init ()
1838 memset (&png_hid, 0, sizeof (HID));
1839 memset (&png_graphics, 0, sizeof (HID_DRAW));
1840 memset (&png_graphics_class, 0, sizeof (HID_DRAW_CLASS));
1842 common_nogui_init (&png_hid);
1844 png_hid.struct_size = sizeof (HID);
1845 png_hid.name = "png";
1846 png_hid.description = "GIF/JPEG/PNG export";
1847 png_hid.exporter = 1;
1849 png_hid.get_export_options = png_get_export_options;
1850 png_hid.do_export = png_do_export;
1851 png_hid.parse_arguments = png_parse_arguments;
1852 png_hid.calibrate = png_calibrate;
1853 png_hid.set_crosshair = png_set_crosshair;
1855 png_hid.graphics = &png_graphics;
1857 common_draw_helpers_class_init (&png_graphics_class);
1859 png_graphics_class.set_layer = png_set_layer;
1860 png_graphics_class.make_gc = png_make_gc;
1861 png_graphics_class.destroy_gc = png_destroy_gc;
1862 png_graphics_class.use_mask = png_use_mask;
1863 png_graphics_class.set_color = png_set_color;
1864 png_graphics_class.set_line_cap = png_set_line_cap;
1865 png_graphics_class.set_line_width = png_set_line_width;
1866 png_graphics_class.set_draw_xor = png_set_draw_xor;
1867 png_graphics_class.draw_line = png_draw_line;
1868 png_graphics_class.draw_arc = png_draw_arc;
1869 png_graphics_class.draw_rect = png_draw_rect;
1870 png_graphics_class.fill_circle = png_fill_circle;
1871 png_graphics_class.fill_polygon = png_fill_polygon;
1872 png_graphics_class.fill_rect = png_fill_rect;
1874 png_graphics.klass = &png_graphics_class;
1875 png_graphics.poly_before = true;
1876 common_draw_helpers_init (&png_graphics);
1878 #ifdef HAVE_SOME_FORMAT
1879 hid_register_hid (&png_hid);
1881 #include "png_lists.h"
1882 #endif