Add support for filling / thindrawing raw polygons to the HID interface
[geda-pcb/gde.git] / src / hid / png / png.c
blob0aa62acf707bd58e23485c583a7e5285cb56dd8d
1 /* $Id$ */
2 /*Sept 2007: patch to enable slanted squared lines*/
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 2006 Dan McMahill
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Heavily based on the ps HID written by DJ Delorie
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
39 #include "global.h"
40 #include "data.h"
41 #include "misc.h"
43 #include "hid.h"
44 #include "../hidint.h"
45 #include "hid/common/draw_helpers.h"
46 #include "png.h"
48 /* the gd library which makes this all so easy */
49 #include <gd.h>
51 #ifdef HAVE_LIBDMALLOC
52 #include <dmalloc.h>
53 #endif
55 RCSID ("$Id$");
57 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort()
59 static void *color_cache = NULL;
60 static void *brush_cache = NULL;
62 double scale = 1;
63 int x_shift = 0;
64 int y_shift = 0;
65 #define SCALE(x) ((int)((x)/scale + 0.5))
66 #define SCALE_X(x) ((int)(((x) - x_shift)/scale))
67 #define SCALE_Y(x) ((int)(((x) - y_shift)/scale))
69 typedef struct color_struct
71 /* the descriptor used by the gd library */
72 int c;
74 /* so I can figure out what rgb value c refers to */
75 unsigned int r, g, b, a;
77 } color_struct;
79 typedef struct hid_gc_struct
81 HID *me_pointer;
82 EndCapStyle cap;
83 int width;
84 unsigned char r, g, b;
85 int erase;
86 int faded;
87 color_struct *color;
88 gdImagePtr brush;
89 } hid_gc_struct;
91 static color_struct *black = NULL, *white = NULL;
92 static gdImagePtr im = NULL, master_im;
93 static FILE *f = 0;
94 static int linewidth = -1;
95 static int lastgroup = -1;
96 static gdImagePtr lastbrush = (void *) -1;
97 static int lastcap = -1;
98 static int lastcolor = -1;
99 static int print_group[MAX_LAYER];
100 static int print_layer[MAX_LAYER];
102 /* For photo-mode we need the following layers as monochrome masks:
104 top soldermask
105 top silk
106 copper layers
107 drill
110 #define PHOTO_FLIP_X 1
111 #define PHOTO_FLIP_Y 2
113 static int photo_mode, photo_flip;
114 static gdImagePtr photo_copper[MAX_LAYER+2];
115 static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im;
116 static gdImagePtr photo_outline;
117 static int photo_groups[MAX_LAYER+2], photo_ngroups;
119 #define FMT_gif "GIF"
120 #define FMT_jpg "JPEG"
121 #define FMT_png "PNG"
123 static const char *filetypes[] = {
124 #ifdef HAVE_GDIMAGEGIF
125 FMT_gif,
126 #endif
128 #ifdef HAVE_GDIMAGEJPEG
129 FMT_jpg,
130 #endif
132 #ifdef HAVE_GDIMAGEPNG
133 FMT_png,
134 #endif
136 NULL
139 HID_Attribute png_attribute_list[] = {
140 /* other HIDs expect this to be first. */
141 {"outfile", "Graphics output file",
142 HID_String, 0, 0, {0, 0, 0}, 0, 0},
143 #define HA_pngfile 0
145 {"dpi", "Scale factor (pixels/inch). 0 to scale to fix specified size",
146 HID_Integer, 0, 1000, {100, 0, 0}, 0, 0},
147 #define HA_dpi 1
149 {"x-max", "Maximum width (pixels). 0 to not constrain.",
150 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
151 #define HA_xmax 2
153 {"y-max", "Maximum height (pixels). 0 to not constrain.",
154 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
155 #define HA_ymax 3
157 {"xy-max", "Maximum width and height (pixels). 0 to not constrain.",
158 HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
159 #define HA_xymax 4
161 {"as-shown", "Export layers as shown on screen",
162 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
163 #define HA_as_shown 5
165 {"monochrome", "Convert to monochrome",
166 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
167 #define HA_mono 6
169 {"only-visible", "Limit the bounds of the PNG file to the visible items",
170 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
171 #define HA_only_visible 7
173 {"use-alpha", "Make the background and any holes transparent",
174 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
175 #define HA_use_alpha 8
177 {"format", "Graphics file format",
178 HID_Enum, 0, 0, {2, 0, 0}, filetypes, 0},
179 #define HA_filetype 9
181 {"photo-mode", "Photo-realistic mode",
182 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
183 #define HA_photo_mode 10
185 {"photo-flip-x", "Show reverse side of the board, left-right flip",
186 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
187 #define HA_photo_flip_x 11
189 {"photo-flip-y", "Show reverse side of the board, up-down flip",
190 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
191 #define HA_photo_flip_y 12
193 {"ben-mode", ATTR_UNDOCUMENTED,
194 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
195 #define HA_ben_mode 10
197 {"ben-flip-x", ATTR_UNDOCUMENTED,
198 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
199 #define HA_ben_flip_x 11
201 {"ben-flip-y", ATTR_UNDOCUMENTED,
202 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
203 #define HA_ben_flip_y 12
206 #define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0]))
208 REGISTER_ATTRIBUTES (png_attribute_list)
210 static HID_Attr_Val png_values[NUM_OPTIONS];
212 static const char *get_file_suffix(void)
214 const char *fmt;
215 const char *result;
216 fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value];
217 /* or is it filetypes[png_attribute_list[HA_filetype].default_val.int_value]; ? */
218 if (strcmp (fmt, FMT_gif) == 0) result=".gif";
219 else if (strcmp (fmt, FMT_jpg) == 0) result=".jpg";
220 else if (strcmp (fmt, FMT_png) == 0) result=".png";
221 else {
222 fprintf (stderr, "Error: Invalid graphic file format\n");
223 result=".???";
225 return result;
228 static HID_Attribute *
229 png_get_export_options (int *n)
231 static char *last_made_filename = 0;
232 const char *suffix = get_file_suffix();
234 if (PCB) derive_default_filename(PCB->Filename, &png_attribute_list[HA_pngfile], suffix, &last_made_filename);
236 if (n)
237 *n = NUM_OPTIONS;
238 return png_attribute_list;
241 static int comp_layer, solder_layer;
243 static int
244 group_for_layer (int l)
246 if (l < max_layer + 2 && l >= 0)
247 return GetLayerGroupNumberByNumber (l);
248 /* else something unique */
249 return max_layer + 3 + l;
252 static int
253 layer_sort (const void *va, const void *vb)
255 int a = *(int *) va;
256 int b = *(int *) vb;
257 int al = group_for_layer (a);
258 int bl = group_for_layer (b);
259 int d = bl - al;
261 if (a >= 0 && a <= max_layer + 1)
263 int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1);
264 int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1);
265 if (bside != aside)
266 return bside - aside;
268 if (d)
269 return d;
270 return b - a;
273 static char *filename;
274 static BoxType *bounds;
275 static int in_mono, as_shown;
277 void
278 png_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
280 int i;
281 static int saved_layer_stack[MAX_LAYER];
282 BoxType region;
283 FlagType save_flags;
285 f = the_file;
287 region.X1 = 0;
288 region.Y1 = 0;
289 region.X2 = PCB->MaxWidth;
290 region.Y2 = PCB->MaxHeight;
292 if (options[HA_only_visible].int_value)
293 bounds = GetDataBoundingBox (PCB->Data);
294 else
295 bounds = &region;
297 memset (print_group, 0, sizeof (print_group));
298 memset (print_layer, 0, sizeof (print_layer));
300 for (i = 0; i < max_layer; i++)
302 LayerType *layer = PCB->Data->Layer + i;
303 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
304 print_group[GetLayerGroupNumberByNumber (i)] = 1;
306 print_group[GetLayerGroupNumberByNumber (max_layer)] = 1;
307 print_group[GetLayerGroupNumberByNumber (max_layer + 1)] = 1;
308 for (i = 0; i < max_layer; i++)
309 if (print_group[GetLayerGroupNumberByNumber (i)])
310 print_layer[i] = 1;
312 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
313 as_shown = options[HA_as_shown].int_value;
314 if (!options[HA_as_shown].int_value)
316 comp_layer = GetLayerGroupNumberByNumber (max_layer + COMPONENT_LAYER);
317 solder_layer = GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER);
318 qsort (LayerStack, max_layer, sizeof (LayerStack[0]), layer_sort);
320 save_flags = PCB->Flags;
321 CLEAR_FLAG(THINDRAWFLAG, PCB);
322 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
324 if (photo_mode)
326 int i, n=0;
327 if (comp_layer < solder_layer)
328 for (i = comp_layer; i <= solder_layer; i++)
329 photo_groups[n++] = i;
330 else
331 for (i = comp_layer; i >= solder_layer; i--)
332 photo_groups[n++] = i;
333 photo_ngroups = n;
335 if (photo_flip)
337 for (i=0, n=photo_ngroups-1; i<n; i++, n--)
339 int tmp = photo_groups[i];
340 photo_groups[i] = photo_groups[n];
341 photo_groups[n] = tmp;
346 linewidth = -1;
347 lastbrush = (void *) -1;
348 lastcap = -1;
349 lastgroup = -1;
350 lastcolor = -1;
351 lastgroup = -1;
353 in_mono = options[HA_mono].int_value;
355 hid_expose_callback (&png_hid, bounds, 0);
357 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
359 if (!options[HA_as_shown].int_value)
361 PCB->Flags = save_flags;
365 static void
366 blend (color_struct *dest, float a_amount, color_struct *a, color_struct *b)
368 dest->r = a->r * a_amount + b->r * (1 - a_amount);
369 dest->g = a->g * a_amount + b->g * (1 - a_amount);
370 dest->b = a->b * a_amount + b->b * (1 - a_amount);
373 static void
374 rgb (color_struct *dest, int r, int g, int b)
376 dest->r = r;
377 dest->g = g;
378 dest->b = b;
381 static int smshadows[3][3] = {
382 { 1, 20, 1 },
383 { 10, 0, -10 },
384 { -1, -20, -1 },
387 static int shadows[5][5] = {
388 { 1, 1, 1, 1, -1 },
389 { 1, 1, 1, -1, -1 },
390 { 1, 1, 0, -1, -1 },
391 { 1, -1, -1, -1, -1 },
392 { -1, -1, -1, -1, -1 },
395 /* black and white are 0 and 1 */
396 #define TOP_SHADOW 2
397 #define BOTTOM_SHADOW 3
399 static void
400 ts_bs (gdImagePtr im)
402 int x, y, sx, sy, si;
403 for (x=0; x<gdImageSX(im); x++)
404 for (y=0; y<gdImageSY(im); y++)
406 si = 0;
407 for (sx=-2; sx<3; sx++)
408 for (sy=-2; sy<3; sy++)
409 if (!gdImageGetPixel (im, x+sx, y+sy))
410 si += shadows[sx+2][sy+2];
411 if (gdImageGetPixel (im, x, y))
413 if (si > 1)
414 gdImageSetPixel (im, x, y, TOP_SHADOW);
415 else if (si < -1)
416 gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
421 static void
422 ts_bs_sm (gdImagePtr im)
424 int x, y, sx, sy, si;
425 for (x=0; x<gdImageSX(im); x++)
426 for (y=0; y<gdImageSY(im); y++)
428 si = 0;
429 for (sx=-1; sx<2; sx++)
430 for (sy=-1; sy<2; sy++)
431 if (!gdImageGetPixel (im, x+sx, y+sy))
432 si += smshadows[sx+1][sy+1];
433 if (gdImageGetPixel (im, x, y))
435 if (si > 1)
436 gdImageSetPixel (im, x, y, TOP_SHADOW);
437 else if (si < -1)
438 gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
443 static void
444 png_do_export (HID_Attr_Val * options)
446 int save_ons[MAX_LAYER + 2];
447 int i;
448 BoxType *bbox;
449 int w, h;
450 int xmax, ymax, dpi;
451 const char *fmt;
453 if (color_cache)
455 free (color_cache);
456 color_cache = NULL;
459 if (brush_cache)
461 free (brush_cache);
462 brush_cache = NULL;
465 if (!options)
467 png_get_export_options (0);
468 for (i = 0; i < NUM_OPTIONS; i++)
469 png_values[i] = png_attribute_list[i].default_val;
470 options = png_values;
473 if (options[HA_photo_mode].int_value
474 || options[HA_ben_mode].int_value)
476 photo_mode = 1;
477 options[HA_mono].int_value = 1;
478 options[HA_as_shown].int_value = 0;
479 memset (photo_copper, 0, sizeof(photo_copper));
480 photo_silk = photo_mask = photo_drill = 0;
481 photo_outline = 0;
482 if (options[HA_photo_flip_x].int_value
483 || options[HA_ben_flip_x].int_value)
484 photo_flip = PHOTO_FLIP_X;
485 else if (options[HA_photo_flip_y].int_value
486 || options[HA_ben_flip_y].int_value)
487 photo_flip = PHOTO_FLIP_Y;
488 else
489 photo_flip = 0;
491 else
492 photo_mode = 0;
494 filename = options[HA_pngfile].str_value;
495 if (!filename)
496 filename = "pcb-out.png";
498 /* figure out width and height of the board */
499 if (options[HA_only_visible].int_value)
501 bbox = GetDataBoundingBox (PCB->Data);
502 x_shift = bbox->X1;
503 y_shift = bbox->Y1;
504 h = bbox->Y2 - bbox->Y1;
505 w = bbox->X2 - bbox->X1;
507 else
509 x_shift = 0;
510 y_shift = 0;
511 h = PCB->MaxHeight;
512 w = PCB->MaxWidth;
516 * figure out the scale factor we need to make the image
517 * fit in our specified PNG file size
519 xmax = ymax = dpi = 0;
520 if (options[HA_dpi].int_value != 0)
522 dpi = options[HA_dpi].int_value;
523 if (dpi < 0)
525 fprintf (stderr, "ERROR: dpi may not be < 0\n");
526 return;
530 if (options[HA_xmax].int_value > 0)
532 xmax = options[HA_xmax].int_value;
533 dpi = 0;
536 if (options[HA_ymax].int_value > 0)
538 ymax = options[HA_ymax].int_value;
539 dpi = 0;
542 if (options[HA_xymax].int_value > 0)
544 dpi = 0;
545 if (options[HA_xymax].int_value < xmax || xmax == 0)
546 xmax = options[HA_xymax].int_value;
547 if (options[HA_xymax].int_value < ymax || ymax == 0)
548 ymax = options[HA_xymax].int_value;
551 if (xmax < 0 || ymax < 0)
553 fprintf (stderr, "ERROR: xmax and ymax may not be < 0\n");
554 return;
557 if (dpi > 0)
560 * a scale of 1 means 1 pixel is 1/100 mil
561 * a scale of 100,000 means 1 pixel is 1 inch
562 * FIXME -- need to use a macro to go from PCB units
563 * so if we ever change pcb's internal units, this
564 * will get updated.
566 scale = 100000.0 / dpi;
567 w = w / scale;
568 h = h / scale;
570 else if( xmax == 0 && ymax == 0)
572 fprintf(stderr, "ERROR: You may not set both xmax, ymax,"
573 "and xy-max to zero\n");
574 return;
576 else
578 if (ymax == 0
579 || ( (xmax > 0)
580 && ((w / xmax) > (h / ymax)) ) )
582 h = (h * xmax) / w;
583 scale = w / xmax;
584 w = xmax;
586 else
588 w = (w * ymax) / h;
589 scale = h / ymax;
590 h = ymax;
594 im = gdImageCreate (w, h);
595 master_im = im;
598 * Allocate white and black -- the first color allocated
599 * becomes the background color
602 white = (color_struct *) malloc (sizeof (color_struct));
603 white->r = white->g = white->b = 255;
604 if (options[HA_use_alpha].int_value)
605 white->a = 127;
606 else
607 white->a = 0;
608 white->c = gdImageColorAllocateAlpha (im, white->r, white->g, white->b, white->a);
610 black = (color_struct *) malloc (sizeof (color_struct));
611 black->r = black->g = black->b = black->a = 0;
612 black->c = gdImageColorAllocate (im, black->r, black->g, black->b);
614 f = fopen (filename, "wb");
615 if (!f)
617 perror (filename);
618 return;
621 if (!options[HA_as_shown].int_value)
622 hid_save_and_show_layer_ons (save_ons);
624 png_hid_export_to_file (f, options);
626 if (!options[HA_as_shown].int_value)
627 hid_restore_layer_ons (save_ons);
629 if (photo_mode)
631 int x, y;
632 color_struct white, black, fr4;
634 rgb (&white, 255, 255, 255);
635 rgb (&black, 0, 0, 0);
636 rgb (&fr4, 70, 70, 70);
638 im = master_im;
640 ts_bs (photo_copper[photo_groups[0]]);
641 ts_bs (photo_silk);
642 ts_bs_sm (photo_mask);
644 if (photo_outline) {
645 int black=gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00);
647 // go all the way around the image, trying to fill the outline
648 for (x=0; x<gdImageSX(im); x++) {
649 gdImageFillToBorder(photo_outline, x, 0, black, black);
650 gdImageFillToBorder(photo_outline, x, gdImageSY(im)-1, black, black);
652 for (y=1; y<gdImageSY(im)-1; y++) {
653 gdImageFillToBorder(photo_outline, 0, y, black, black);
654 gdImageFillToBorder(photo_outline, gdImageSX(im)-1, y, black, black);
660 for (x=0; x<gdImageSX (im); x++)
662 for (y=0; y<gdImageSY (im); y++)
664 color_struct p, cop;
665 int cc, mask, silk;
666 int transparent;
668 if (photo_outline) {
669 transparent=gdImageGetPixel(photo_outline, x, y);
670 } else {
671 transparent=0;
674 mask = photo_mask ? gdImageGetPixel (photo_mask, x, y) : 0;
675 silk = photo_silk ? gdImageGetPixel (photo_silk, x, y) : 0;
677 if (gdImageGetPixel (photo_copper[photo_groups[1]], x, y))
678 rgb (&cop, 40, 40, 40);
679 else
680 rgb (&cop, 100, 100, 110);
682 if (photo_ngroups == 2)
683 blend (&cop, 0.3, &cop, &fr4);
685 cc = gdImageGetPixel (photo_copper[photo_groups[0]], x, y);
686 if (cc)
688 int r;
690 if (mask)
691 rgb (&cop, 220, 145, 230);
692 else
694 rgb (&cop, 140, 150, 160);
695 #if 1
696 r = (random() % 5 - 2) * 2;
697 cop.r += r;
698 cop.g += r;
699 cop.b += r;
700 #endif
703 if (cc == TOP_SHADOW)
705 cop.r = 255 - (255 - cop.r) * 0.7;
706 cop.g = 255 - (255 - cop.g) * 0.7;
707 cop.b = 255 - (255 - cop.b) * 0.7;
709 if (cc == BOTTOM_SHADOW)
711 cop.r *= 0.7;
712 cop.g *= 0.7;
713 cop.b *= 0.7;
717 if (photo_drill && !gdImageGetPixel (photo_drill, x, y))
719 rgb (&p, 0, 0, 0);
720 transparent=1;
722 else if (silk)
724 if (silk == TOP_SHADOW)
725 rgb (&p, 255, 255, 255);
726 else if (silk == BOTTOM_SHADOW)
727 rgb (&p, 192, 192, 192);
728 else
729 rgb (&p, 224, 224, 224);
731 else if (mask)
733 p = cop;
734 p.r /= 2;
735 p.b /= 2;
736 if (mask == TOP_SHADOW)
737 blend (&p, 0.7, &p, &white);
738 if (mask == BOTTOM_SHADOW)
739 blend (&p, 0.7, &p, &black);
741 else
742 p = cop;
744 if (options[HA_use_alpha].int_value) {
746 cc = (transparent)?\
747 gdImageColorResolveAlpha(im, 0, 0, 0, 127):\
748 gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0);
750 } else {
751 cc = (transparent)?\
752 gdImageColorResolve(im, 0, 0, 0):\
753 gdImageColorResolve(im, p.r, p.g, p.b);
756 if (photo_flip == PHOTO_FLIP_X)
757 gdImageSetPixel (im, gdImageSX (im) - x - 1, y, cc);
758 else if (photo_flip == PHOTO_FLIP_Y)
759 gdImageSetPixel (im, x, gdImageSY (im) - y - 1, cc);
760 else
761 gdImageSetPixel (im, x, y, cc);
766 /* actually write out the image */
767 fmt = filetypes[options[HA_filetype].int_value];
769 if (strcmp (fmt, FMT_gif) == 0)
770 #ifdef HAVE_GDIMAGEGIF
771 gdImageGif (im, f);
772 #else
774 gdImageDestroy (im);
775 return;
777 #endif
778 else if (strcmp (fmt, FMT_jpg) == 0)
779 #ifdef HAVE_GDIMAGEJPEG
780 gdImageJpeg (im, f, -1);
781 #else
783 gdImageDestroy (im);
784 return;
786 #endif
787 else if (strcmp (fmt, FMT_png) == 0)
788 #ifdef HAVE_GDIMAGEPNG
789 gdImagePng (im, f);
790 #else
792 gdImageDestroy (im);
793 return;
795 #endif
796 else
797 fprintf (stderr, "Error: Invalid graphic file format."
798 " This is a bug. Please report it.\n");
800 fclose (f);
802 gdImageDestroy (im);
805 extern void hid_parse_command_line (int *argc, char ***argv);
807 static void
808 png_parse_arguments (int *argc, char ***argv)
810 hid_register_attributes (png_attribute_list,
811 sizeof (png_attribute_list) /
812 sizeof (png_attribute_list[0]));
813 hid_parse_command_line (argc, argv);
817 static int is_mask;
818 static int is_drill;
820 static int
821 png_set_layer (const char *name, int group, int empty)
823 int idx = (group >= 0
824 && group <
825 max_layer) ? PCB->LayerGroups.Entries[group][0] : group;
826 if (name == 0)
827 name = PCB->Data->Layer[idx].Name;
829 if (idx >= 0 && idx < max_layer && !print_layer[idx])
830 return 0;
831 if (SL_TYPE (idx) == SL_ASSY || SL_TYPE (idx) == SL_FAB)
832 return 0;
834 if (strcmp (name, "invisible") == 0)
835 return 0;
837 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
838 is_mask = (SL_TYPE (idx) == SL_MASK);
840 if (SL_TYPE (idx) == SL_PASTE)
841 return 0;
843 if (photo_mode)
845 switch (idx)
847 case SL (SILK, TOP):
848 if (photo_flip)
849 return 0;
850 photo_im = &photo_silk;
851 break;
852 case SL (SILK, BOTTOM):
853 if (!photo_flip)
854 return 0;
855 photo_im = &photo_silk;
856 break;
858 case SL (MASK, TOP):
859 if (photo_flip)
860 return 0;
861 photo_im = &photo_mask;
862 break;
863 case SL (MASK, BOTTOM):
864 if (!photo_flip)
865 return 0;
866 photo_im = &photo_mask;
867 break;
869 case SL (PDRILL, 0):
870 case SL (UDRILL, 0):
871 photo_im = &photo_drill;
872 break;
874 default:
875 if (idx < 0)
876 return 0;
878 if (strcasecmp (name, "outline") == 0)
879 photo_im = &photo_outline;
880 else
881 photo_im = photo_copper + group;
883 break;
886 if (! *photo_im)
888 static color_struct *black = NULL, *white = NULL;
889 *photo_im = gdImageCreate (gdImageSX (im), gdImageSY (im));
891 white = (color_struct *) malloc (sizeof (color_struct));
892 white->r = white->g = white->b = 255;
893 white->a = 0;
894 white->c = gdImageColorAllocate (*photo_im, white->r, white->g, white->b);
896 black = (color_struct *) malloc (sizeof (color_struct));
897 black->r = black->g = black->b = black->a = 0;
898 black->c = gdImageColorAllocate (*photo_im, black->r, black->g, black->b);
900 if (idx == SL (PDRILL, 0)
901 || idx == SL (UDRILL, 0))
902 gdImageFilledRectangle (*photo_im, 0, 0, gdImageSX (im), gdImageSY (im), black->c);
904 im = *photo_im;
905 return 1;
908 if (as_shown)
910 switch (idx)
912 case SL (SILK, TOP):
913 case SL (SILK, BOTTOM):
914 if (SL_MYSIDE (idx))
915 return PCB->ElementOn;
916 return 0;
918 case SL (MASK, TOP):
919 case SL (MASK, BOTTOM):
920 return TEST_FLAG (SHOWMASKFLAG, PCB) && SL_MYSIDE (idx);
923 else
925 if (is_mask)
926 return 0;
928 switch (idx)
930 case SL (SILK, TOP):
931 return 1;
932 case SL (SILK, BOTTOM):
933 return 0;
937 return 1;
941 static hidGC
942 png_make_gc (void)
944 hidGC rv = (hidGC) malloc (sizeof (hid_gc_struct));
945 rv->me_pointer = &png_hid;
946 rv->cap = Trace_Cap;
947 rv->width = 1;
948 rv->color = (color_struct *) malloc (sizeof (color_struct));
949 rv->color->r = rv->color->g = rv->color->b = rv->color->a = 0;
950 rv->color->c = 0;
951 return rv;
954 static void
955 png_destroy_gc (hidGC gc)
957 free (gc);
960 static void
961 png_use_mask (int use_it)
963 /* does nothing */
966 static void
967 png_set_color (hidGC gc, const char *name)
969 hidval cval;
971 if (im == NULL)
972 return;
974 if (name == NULL)
975 name = "#ff0000";
977 if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
979 /* FIXME -- should be background, not white */
980 gc->color = white;
981 gc->erase = 1;
982 return;
984 gc->erase = 0;
986 if (in_mono || (strcmp (name, "#000000") == 0))
988 gc->color = black;
989 return;
992 if (hid_cache_color (0, name, &cval, &color_cache))
994 gc->color = cval.ptr;
996 else if (name[0] == '#')
998 gc->color = (color_struct *) malloc (sizeof (color_struct));
999 sscanf (name + 1, "%2x%2x%2x", &(gc->color->r), &(gc->color->g),
1000 &(gc->color->b));
1001 gc->color->c =
1002 gdImageColorAllocate (im, gc->color->r, gc->color->g, gc->color->b);
1003 cval.ptr = gc->color;
1004 hid_cache_color (1, name, &cval, &color_cache);
1006 else
1008 printf ("WE SHOULD NOT BE HERE!!!\n");
1009 gc->color = black;
1014 static void
1015 png_set_line_cap (hidGC gc, EndCapStyle style)
1017 gc->cap = style;
1020 static void
1021 png_set_line_width (hidGC gc, int width)
1023 gc->width = width;
1026 static void
1027 png_set_draw_xor (hidGC gc, int xor)
1032 static void
1033 png_set_draw_faded (hidGC gc, int faded)
1035 gc->faded = faded;
1038 static void
1039 png_set_line_cap_angle (hidGC gc, int x1, int y1, int x2, int y2)
1041 CRASH;
1044 static void
1045 use_gc (hidGC gc)
1047 int need_brush = 0;
1049 if (gc->me_pointer != &png_hid)
1051 fprintf (stderr, "Fatal: GC from another HID passed to png HID\n");
1052 abort ();
1055 if (linewidth != gc->width)
1057 /* Make sure the scaling doesn't erase lines completely */
1058 if (SCALE (gc->width) == 0 && gc->width > 0)
1059 gdImageSetThickness (im, 1);
1060 else
1061 gdImageSetThickness (im, SCALE (gc->width));
1062 linewidth = gc->width;
1063 need_brush = 1;
1066 if (lastbrush != gc->brush || need_brush)
1068 hidval bval;
1069 char name[256];
1070 char type;
1071 int r;
1073 switch (gc->cap)
1075 case Round_Cap:
1076 case Trace_Cap:
1077 type = 'C';
1078 break;
1079 default:
1080 case Square_Cap:
1081 type = 'S';
1082 break;
1084 r = SCALE (gc->width);
1085 if (r == 0 && gc->width > 0)
1086 r = 1;
1087 sprintf (name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
1088 gc->color->b, type, r);
1090 if (hid_cache_color (0, name, &bval, &brush_cache))
1092 gc->brush = bval.ptr;
1094 else
1096 int bg, fg;
1097 gc->brush = gdImageCreate (r, r);
1098 bg = gdImageColorAllocate (gc->brush, 255, 255, 255);
1099 fg =
1100 gdImageColorAllocateAlpha (gc->brush, gc->color->r, gc->color->g,
1101 gc->color->b, 0);
1102 gdImageColorTransparent (gc->brush, bg);
1105 * if we shrunk to a radius/box width of zero, then just use
1106 * a single pixel to draw with.
1108 if (r <= 1)
1109 gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg);
1110 else
1112 if (type == 'C')
1114 gdImageFilledEllipse (gc->brush, r/2, r/2, r, r, fg);
1115 /* Make sure the ellipse is the right exact size. */
1116 gdImageSetPixel (gc->brush, 0, r/2, fg);
1117 gdImageSetPixel (gc->brush, r-1, r/2, fg);
1118 gdImageSetPixel (gc->brush, r/2, 0, fg);
1119 gdImageSetPixel (gc->brush, r/2, r-1, fg);
1121 else
1122 gdImageFilledRectangle (gc->brush, 0, 0, r-1, r-1, fg);
1124 bval.ptr = gc->brush;
1125 hid_cache_color (1, name, &bval, &brush_cache);
1128 gdImageSetBrush (im, gc->brush);
1129 lastbrush = gc->brush;
1133 #define CBLEND(gc) (((gc->r)<<24)|((gc->g)<<16)|((gc->b)<<8)|(gc->faded))
1134 if (lastcolor != CBLEND (gc))
1136 if (is_drill || is_mask)
1138 #ifdef FIXME
1139 fprintf (f, "%d gray\n", gc->erase ? 0 : 1);
1140 #endif
1141 lastcolor = 0;
1143 else
1145 double r, g, b;
1146 r = gc->r;
1147 g = gc->g;
1148 b = gc->b;
1149 if (gc->faded)
1151 r = 0.8 * 255 + 0.2 * r;
1152 g = 0.8 * 255 + 0.2 * g;
1153 b = 0.8 * 255 + 0.2 * b;
1155 #ifdef FIXME
1156 if (gc->r == gc->g && gc->g == gc->b)
1157 fprintf (f, "%g gray\n", r / 255.0);
1158 else
1159 fprintf (f, "%g %g %g rgb\n", r / 255.0, g / 255.0, b / 255.0);
1160 #endif
1161 lastcolor = CBLEND (gc);
1166 static void
1167 png_draw_rect (hidGC gc, int x1, int y1, int x2, int y2)
1169 use_gc (gc);
1170 gdImageRectangle (im,
1171 SCALE_X (x1), SCALE_Y (y1),
1172 SCALE_X (x2), SCALE_Y (y2), gc->color->c);
1175 static void
1176 png_fill_rect (hidGC gc, int x1, int y1, int x2, int y2)
1178 use_gc (gc);
1179 gdImageSetThickness (im, 0);
1180 linewidth = 0;
1181 gdImageFilledRectangle (im, SCALE_X (x1), SCALE_Y (y1),
1182 SCALE_X (x2)-1, SCALE_Y (y2)-1, gc->color->c);
1185 static void
1186 png_draw_line (hidGC gc, int x1, int y1, int x2, int y2)
1188 if (x1 == x2 && y1 == y2)
1190 int w = gc->width / 2;
1191 png_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
1192 return;
1194 use_gc (gc);
1196 gdImageSetThickness (im, 0);
1197 linewidth = 0;
1198 if(gc->cap != Square_Cap || x1 == x2 || y1 == y2 )
1200 gdImageLine (im, SCALE_X (x1), SCALE_Y (y1),
1201 SCALE_X (x2), SCALE_Y (y2), gdBrushed);
1203 else
1206 * if we are drawing a line with a square end cap and it is
1207 * not purely horizontal or vertical, then we need to draw
1208 * it as a filled polygon.
1210 int fg = gdImageColorResolve (im, gc->color->r, gc->color->g,
1211 gc->color->b),
1212 w = gc->width, dx = x2 - x1, dy = y2 - y1, dwx, dwy;
1213 gdPoint p[4];
1214 double l = sqrt (dx * dx + dy * dy) * 2 * scale;
1215 dwx = -w / l * dy; dwy = w / l * dx;
1216 p[0].x = SCALE_X (x1) + dwx - dwy; p[0].y = SCALE_Y(y1) + dwy + dwx;
1217 p[1].x = SCALE_X (x1) - dwx - dwy; p[1].y = SCALE_Y(y1) - dwy + dwx;
1218 p[2].x = SCALE_X (x2) - dwx + dwy; p[2].y = SCALE_Y(y2) - dwy - dwx;
1219 p[3].x = SCALE_X (x2) + dwx + dwy; p[3].y = SCALE_Y(y2) + dwy - dwx;
1220 gdImageFilledPolygon (im, p, 4, fg);
1224 static void
1225 png_draw_arc (hidGC gc, int cx, int cy, int width, int height,
1226 int start_angle, int delta_angle)
1228 int sa, ea;
1231 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1232 * in pcb, 0 degrees is to the left and +90 degrees is down
1234 start_angle = 180 - start_angle;
1235 delta_angle = -delta_angle;
1236 if (delta_angle > 0)
1238 sa = start_angle;
1239 ea = start_angle + delta_angle;
1241 else
1243 sa = start_angle + delta_angle;
1244 ea = start_angle;
1248 * make sure we start between 0 and 360 otherwise gd does
1249 * strange things
1251 while (sa < 0)
1253 sa += 360;
1254 ea += 360;
1256 while (sa >= 360)
1258 sa -= 360;
1259 ea -= 360;
1262 #if 0
1263 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1264 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1265 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1266 im, SCALE_X (cx), SCALE_Y (cy),
1267 SCALE (width), SCALE (height), sa, ea, gc->color->c);
1268 #endif
1269 use_gc (gc);
1270 gdImageSetThickness (im, 0);
1271 linewidth = 0;
1272 gdImageArc (im, SCALE_X (cx), SCALE_Y (cy),
1273 SCALE (2 * width), SCALE (2 * height), sa, ea, gdBrushed);
1276 static void
1277 png_fill_circle (hidGC gc, int cx, int cy, int radius)
1279 use_gc (gc);
1281 gdImageSetThickness (im, 0);
1282 linewidth = 0;
1283 gdImageFilledEllipse (im, SCALE_X (cx), SCALE_Y (cy),
1284 SCALE (2 * radius), SCALE (2 * radius), gc->color->c);
1288 static void
1289 png_fill_polygon (hidGC gc, int n_coords, int *x, int *y)
1291 int i;
1292 gdPoint *points;
1294 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1295 if (points == NULL)
1297 fprintf (stderr, "ERROR: png_fill_polygon(): malloc failed\n");
1298 exit (1);
1301 use_gc (gc);
1302 for (i = 0; i < n_coords; i++)
1304 points[i].x = SCALE_X (x[i]);
1305 points[i].y = SCALE_Y (y[i]);
1307 gdImageSetThickness (im, 0);
1308 linewidth = 0;
1309 gdImageFilledPolygon (im, points, n_coords, gc->color->c);
1310 free (points);
1313 static void
1314 png_calibrate (double xval, double yval)
1316 CRASH;
1319 static void
1320 png_set_crosshair (int x, int y, int a)
1324 HID png_hid = {
1325 sizeof (HID),
1326 "png",
1327 "GIF/JPEG/PNG export.",
1328 0, /* gui */
1329 0, /* printer */
1330 1, /* exporter */
1331 1, /* poly before */
1332 0, /* poly after */
1333 0, /* poly dicer */
1334 png_get_export_options,
1335 png_do_export,
1336 png_parse_arguments,
1337 0 /* png_invalidate_wh */ ,
1338 0 /* png_invalidate_lr */ ,
1339 0 /* png_invalidate_all */ ,
1340 png_set_layer,
1341 png_make_gc,
1342 png_destroy_gc,
1343 png_use_mask,
1344 png_set_color,
1345 png_set_line_cap,
1346 png_set_line_width,
1347 png_set_draw_xor,
1348 png_set_draw_faded,
1349 png_set_line_cap_angle,
1350 png_draw_line,
1351 png_draw_arc,
1352 png_draw_rect,
1353 png_fill_circle,
1354 png_fill_polygon,
1355 common_fill_pcb_polygon,
1356 0 /* png_thindraw_pcb_polygon */ ,
1357 png_fill_rect,
1358 png_calibrate,
1359 0 /* png_shift_is_pressed */ ,
1360 0 /* png_control_is_pressed */ ,
1361 0 /* png_get_coords */ ,
1362 png_set_crosshair,
1363 0 /* png_add_timer */ ,
1364 0 /* png_stop_timer */ ,
1365 0 /* png_watch_file */ ,
1366 0 /* png_unwatch_file */ ,
1367 0 /* png_add_block_hook */ ,
1368 0 /* png_stop_block_hook */ ,
1369 0 /* png_log */ ,
1370 0 /* png_logv */ ,
1371 0 /* png_confirm_dialog */ ,
1372 0 /* png_close_confirm_dialog */ ,
1373 0 /* png_report_dialog */ ,
1374 0 /* png_prompt_for */ ,
1375 0 /* png_fileselect */ ,
1376 0 /* png_attribute_dialog */ ,
1377 0 /* png_show_item */ ,
1378 0 /* png_beep */ ,
1379 0 /* png_progress */
1382 #include "dolists.h"
1384 void
1385 hid_png_init ()
1387 apply_default_hid (&png_hid, 0);
1388 hid_register_hid (&png_hid);
1390 #include "png_lists.h"