Index: doc-rnd/TODO =================================================================== --- doc-rnd/TODO (revision 1269) +++ doc-rnd/TODO (revision 1270) @@ -8,7 +8,7 @@ - PrintQuotedString(FILE * FP, char *S): remove dynamic string allocation - get rid of gcode/lists.h, ds.[ch] and vector.[ch] - test whether plugin compiled exporters work - - test whether gd based plugins can be compiled to so (png, nelma) + - test whether gd based plugins can be compiled to so (png, nelma) _without_ pushing ldflags or includes onto core Index: scconfig/hooks.c =================================================================== --- scconfig/hooks.c (revision 1269) +++ scconfig/hooks.c (revision 1270) @@ -17,13 +17,6 @@ NULL, NULL }; -const char *exporter_list[] = { - "png", "libs/gui/gd/gdImagePng/presents", - "gif", "libs/gui/gd/gdImageGif/presents", - "jpg", "libs/gui/gd/gdImageJpeg/presents", - NULL, NULL -}; - const arg_auto_set_t disable_libs[] = { /* list of --disable-LIBs and the subtree they affect */ {"disable-gtk", "libs/gui/gtk2", arg_lib_nodes, "$do not compile the gtk HID"}, {"disable-lesstif", "libs/gui/lesstif2", arg_lib_nodes, "$do not compile the lesstif HID"}, @@ -102,6 +95,19 @@ {"buildin-export_nelma", "/local/pcb/export_nelma/buildin", arg_true, "$static link the nelma exporter into the executable"}, {"plugin-export_nelma", "/local/pcb/export_nelma/buildin", arg_false, "$the nelma exporter is a dynamic loadable plugin"}, + {"disable-export_png", "/local/pcb/export_png/enable", arg_false, "$do not compile the png exporter"}, + {"buildin-export_png", "/local/pcb/export_png/buildin", arg_true, "$static link the png exporter into the executable"}, + {"plugin-export_png", "/local/pcb/export_png/buildin", arg_false, "$the png exporter is a dynamic loadable plugin"}, + + {"disable-export_bom", "/local/pcb/export_bom/enable", arg_false, "$do not compile the bom exporter"}, + {"buildin-export_bom", "/local/pcb/export_bom/buildin", arg_true, "$static link the bom exporter into the executable"}, + {"plugin-export_bom", "/local/pcb/export_bom/buildin", arg_false, "$the bom exporter is a dynamic loadable plugin"}, + + {"disable-export_gerber", "/local/pcb/export_gerber/enable", arg_false, "$do not compile the gerber exporter"}, + {"buildin-export_gerber", "/local/pcb/export_gerber/buildin", arg_true, "$static link the gerber exporter into the executable"}, + {"plugin-export_gerber", "/local/pcb/export_gerber/buildin", arg_false, "$the gerber exporter is a dynamic loadable plugin"}, + + {NULL, NULL, NULL, NULL} }; @@ -226,6 +232,18 @@ put("/local/pcb/export_nelma/enable", strue); put("/local/pcb/export_nelma/buildin", strue); + db_mkdir("/local/pcb/export_png"); + put("/local/pcb/export_png/enable", strue); + put("/local/pcb/export_png/buildin", strue); + + db_mkdir("/local/pcb/export_bom"); + put("/local/pcb/export_bom/enable", strue); + put("/local/pcb/export_bom/buildin", strue); + + db_mkdir("/local/pcb/export_gerber"); + put("/local/pcb/export_gerber/enable", strue); + put("/local/pcb/export_gerber/buildin", strue); + return 0; } @@ -375,6 +393,10 @@ report_repeat("WARNING: disabling the nelma exporter because libgd is not found or not configured...\n"); hook_custom_arg("disable-export_nelma", NULL); } + if (istrue(get("/local/pcb/export_png/enable"))) { + report_repeat("WARNING: disabling the png exporter because libgd is not found or not configured...\n"); + hook_custom_arg("disable-export_png", NULL); + } } return 0; @@ -485,7 +507,6 @@ printf("Configuration summary\n"); printf("=====================\n\n"); list_presents("GUI hids: batch", gui_list); - list_presents("Export hids: bom gerber", exporter_list); /* special case because the "presents" node */ printf("%-32s", "Scripting via GPMI: "); @@ -513,10 +534,13 @@ plugin_stat("import_sch:", "/local/pcb/import_sch"); plugin_stat("import_edif: ", "/local/pcb/import_edif"); printf("\n"); + plugin_stat("export_gcode:", "/local/pcb/export_gcode"); + plugin_stat("export_nelma:", "/local/pcb/export_nelma"); + plugin_stat("export_png:", "/local/pcb/export_png"); + plugin_stat("export_bom:", "/local/pcb/export_bom"); + plugin_stat("export_gerber:", "/local/pcb/export_gerber"); plugin_stat("export_ps:", "/local/pcb/export_ps"); plugin_stat("export_lpr:", "/local/pcb/export_lpr"); - plugin_stat("export_gcode:", "/local/pcb/export_gcode"); - plugin_stat("export_nelma:", "/local/pcb/export_nelma"); if (repeat != NULL) printf("\n%s\n", repeat); Index: src/Makefile.in =================================================================== --- src/Makefile.in (revision 1269) +++ src/Makefile.in (revision 1270) @@ -133,9 +133,6 @@ include {Makefile.in.mod/gtk-gdk} include {Makefile.in.mod/lesstif} include {Makefile.in.mod/batch} -include {Makefile.in.mod/bom} -include {Makefile.in.mod/png} -include {Makefile.in.mod/gerber} include {../src_plugins/autoplace/Plug.tmpasm} include {../src_plugins/autoroute/Plug.tmpasm} @@ -154,6 +151,9 @@ include {../src_plugins/export_lpr/Plug.tmpasm} include {../src_plugins/export_gcode/Plug.tmpasm} include {../src_plugins/export_nelma/Plug.tmpasm} +include {../src_plugins/export_png/Plug.tmpasm} +include {../src_plugins/export_gerber/Plug.tmpasm} +include {../src_plugins/export_bom/Plug.tmpasm} append /local/pcb/CFLAGS /target/libs/sul/glib/cflags Index: src/Makefile.in.mod/png =================================================================== --- src/Makefile.in.mod/png (revision 1269) +++ src/Makefile.in.mod/png (nonexistent) @@ -1,8 +0,0 @@ -if /target/libs/gui/gd/presents then -append /local/pcb/HIDS {png} -append /local/pcb/CFLAGS {-I./hid/png} -append /local/pcb/LIBS libs/gui/gd/cflags -append /local/pcb/OBJS {hid/png/png.o} -append /local/pcb/LIBS libs/gui/gd/ldflags -append /local/pcb/ACTION_REG_SRC {hid/png/png.c hid/png/hid.conf} -end Index: src/Makefile.in.mod/gerber =================================================================== --- src/Makefile.in.mod/gerber (revision 1269) +++ src/Makefile.in.mod/gerber (nonexistent) @@ -1,3 +0,0 @@ -append /local/pcb/HIDS {gerber} -append /local/pcb/OBJS {hid/gerber/gerber.o} - Index: src/Makefile.in.mod/bom =================================================================== --- src/Makefile.in.mod/bom (revision 1269) +++ src/Makefile.in.mod/bom (nonexistent) @@ -1,2 +0,0 @@ -append /local/pcb/HIDS {bom} -append /local/pcb/OBJS {hid/bom/bom.o} Index: src/hid/png/png.c =================================================================== --- src/hid/png/png.c (revision 1269) +++ src/hid/png/png.c (nonexistent) @@ -1,1516 +0,0 @@ - /* - * COPYRIGHT - * - * PCB, interactive printed circuit board design - * Copyright (C) 2006 Dan McMahill - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* - * Heavily based on the ps HID written by DJ Delorie - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "global.h" -#include "data.h" -#include "error.h" -#include "misc.h" - -#include "hid.h" -#include "../hidint.h" -#include "hid/common/hidnogui.h" -#include "hid/common/draw_helpers.h" -#include "png.h" - -/* the gd library which makes this all so easy */ -#include - -#include "hid/common/hidinit.h" - - -#define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort() - -static HID png_hid; - -const char *png_cookie = "png HID"; - -static void *color_cache = NULL; -static void *brush_cache = NULL; - -static double bloat = 0; -static double scale = 1; -static Coord x_shift = 0; -static Coord y_shift = 0; -static int show_solder_side; -#define SCALE(w) ((int)((w)/scale + 0.5)) -#define SCALE_X(x) ((int)(((x) - x_shift)/scale)) -#define SCALE_Y(y) ((int)(((show_solder_side ? (PCB->MaxHeight-(y)) : (y)) - y_shift)/scale)) -#define SWAP_IF_SOLDER(a,b) do { int c; if (show_solder_side) { c=a; a=b; b=c; }} while (0) - -/* Used to detect non-trivial outlines */ -#define NOT_EDGE_X(x) ((x) != 0 && (x) != PCB->MaxWidth) -#define NOT_EDGE_Y(y) ((y) != 0 && (y) != PCB->MaxHeight) -#define NOT_EDGE(x,y) (NOT_EDGE_X(x) || NOT_EDGE_Y(y)) - -static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius); - -/* The result of a failed gdImageColorAllocate() call */ -#define BADC -1 - -typedef struct color_struct { - /* the descriptor used by the gd library */ - int c; - - /* so I can figure out what rgb value c refers to */ - unsigned int r, g, b, a; - -} color_struct; - -typedef struct hid_gc_struct { - HID *me_pointer; - EndCapStyle cap; - int width; - unsigned char r, g, b; - color_struct *color; - gdImagePtr brush; - int is_erase; -} hid_gc_struct; - -static color_struct *black = NULL, *white = NULL; -static gdImagePtr im = NULL, master_im, mask_im = NULL; -static FILE *f = 0; -static int linewidth = -1; -static int lastgroup = -1; -static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); -static int lastcap = -1; -static int print_group[MAX_LAYER]; -static int print_layer[MAX_LAYER]; - -/* For photo-mode we need the following layers as monochrome masks: - - top soldermask - top silk - copper layers - drill -*/ - -#define PHOTO_FLIP_X 1 -#define PHOTO_FLIP_Y 2 - -static int photo_mode, photo_flip; -static gdImagePtr photo_copper[MAX_LAYER + 2]; -static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im; -static gdImagePtr photo_outline; -static int photo_groups[MAX_LAYER + 2], photo_ngroups; -static int photo_has_inners; - -static int doing_outline, have_outline; - -#define FMT_gif "GIF" -#define FMT_jpg "JPEG" -#define FMT_png "PNG" - -/* If this table has no elements in it, then we have no reason to - register this HID and will refrain from doing so at the end of this - file. */ - -#undef HAVE_SOME_FORMAT - -static const char *filetypes[] = { -#ifdef HAVE_GDIMAGEPNG - FMT_png, -#define HAVE_SOME_FORMAT 1 -#endif - -#ifdef HAVE_GDIMAGEGIF - FMT_gif, -#define HAVE_SOME_FORMAT 1 -#endif - -#ifdef HAVE_GDIMAGEJPEG - FMT_jpg, -#define HAVE_SOME_FORMAT 1 -#endif - - NULL -}; - -HID_Attribute png_attribute_list[] = { - /* other HIDs expect this to be first. */ - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --outfile -Name of the file to be exported to. Can contain a path. -@end ftable -%end-doc -*/ - {"outfile", "Graphics output file", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_pngfile 0 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --dpi -Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout. -@end ftable -%end-doc -*/ - {"dpi", "Scale factor (pixels/inch). 0 to scale to specified size", - HID_Integer, 0, 1000, {100, 0, 0}, 0, 0}, -#define HA_dpi 1 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --x-max -Width of the png image in pixels. No constraint, when set to 0. -@end ftable -%end-doc -*/ - {"x-max", "Maximum width (pixels). 0 to not constrain", - HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, -#define HA_xmax 2 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --y-max -Height of the png output in pixels. No constraint, when set to 0. -@end ftable -%end-doc -*/ - {"y-max", "Maximum height (pixels). 0 to not constrain", - HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, -#define HA_ymax 3 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --xy-max -Maximum width and height of the PNG output in pixels. No constraint, when set to 0. -@end ftable -%end-doc -*/ - {"xy-max", "Maximum width and height (pixels). 0 to not constrain", - HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, -#define HA_xymax 4 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --as-shown -Export layers as shown on screen. -@end ftable -%end-doc -*/ - {"as-shown", "Export layers as shown on screen", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_as_shown 5 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --monochrome -Convert output to monochrome. -@end ftable -%end-doc -*/ - {"monochrome", "Convert to monochrome", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_mono 6 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --only-vivible -Limit the bounds of the exported PNG image to the visible items. -@end ftable -%end-doc -*/ - {"only-visible", "Limit the bounds of the PNG image to the visible items", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_only_visible 7 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --use-alpha -Make the background and any holes transparent. -@end ftable -%end-doc -*/ - {"use-alpha", "Make the background and any holes transparent", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_use_alpha 8 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --format -File format to be exported. Parameter @code{} can be @samp{PNG}, -@samp{GIF}, or @samp{JPEG}. -@end ftable -%end-doc -*/ - {"format", "Export file format", - HID_Enum, 0, 0, {0, 0, 0}, filetypes, 0}, -#define HA_filetype 9 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --png-bloat -Amount of extra thickness to add to traces, pads, or pin edges. The parameter -@samp{} is a number, appended by a dimension @samp{mm}, @samp{mil}, or -@samp{pix}. If no dimension is given, the default dimension is 1/100 mil. -@end ftable -%end-doc -*/ - {"png-bloat", "Amount (in/mm/mil/pix) to add to trace/pad/pin edges (1 = 1/100 mil)", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_bloat 10 - -/* %start-doc options "93 PNG Options" -@ftable @code -@cindex photo-mode -@item --photo-mode -Export a photo realistic image of the layout. -@end ftable -%end-doc -*/ - {"photo-mode", "Photo-realistic export mode", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_photo_mode 11 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --photo-flip-x -In photo-realistic mode, export the reverse side of the layout. Left-right flip. -@end ftable -%end-doc -*/ - {"photo-flip-x", "Show reverse side of the board, left-right flip", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_photo_flip_x 12 - -/* %start-doc options "93 PNG Options" -@ftable @code -@item --photo-flip-y -In photo-realistic mode, export the reverse side of the layout. Up-down flip. -@end ftable -%end-doc -*/ - {"photo-flip-y", "Show reverse side of the board, up-down flip", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_photo_flip_y 13 - - {"ben-mode", ATTR_UNDOCUMENTED, - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_ben_mode 11 - - {"ben-flip-x", ATTR_UNDOCUMENTED, - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_ben_flip_x 12 - - {"ben-flip-y", ATTR_UNDOCUMENTED, - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_ben_flip_y 13 -}; - -#define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0])) - -REGISTER_ATTRIBUTES(png_attribute_list, png_cookie) - - static HID_Attr_Val png_values[NUM_OPTIONS]; - - static const char *get_file_suffix(void) -{ - const char *result = NULL; - const char *fmt; - - fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value]; - - if (fmt == NULL); /* Do nothing */ - else if (strcmp(fmt, FMT_gif) == 0) - result = ".gif"; - else if (strcmp(fmt, FMT_jpg) == 0) - result = ".jpg"; - else if (strcmp(fmt, FMT_png) == 0) - result = ".png"; - - if (result == NULL) { - fprintf(stderr, "Error: Invalid graphic file format\n"); - result = ".???"; - } - return result; -} - -static HID_Attribute *png_get_export_options(int *n) -{ - static char *last_made_filename = 0; - const char *suffix = get_file_suffix(); - - if (PCB) - derive_default_filename(PCB->Filename, &png_attribute_list[HA_pngfile], suffix, &last_made_filename); - - if (n) - *n = NUM_OPTIONS; - return png_attribute_list; -} - -static int comp_layer, solder_layer; - -static int group_for_layer(int l) -{ - if (l < max_copper_layer + 2 && l >= 0) - return GetLayerGroupNumberByNumber(l); - /* else something unique */ - return max_group + 3 + l; -} - -static int layer_sort(const void *va, const void *vb) -{ - int a = *(int *) va; - int b = *(int *) vb; - int al = group_for_layer(a); - int bl = group_for_layer(b); - int d = bl - al; - - if (a >= 0 && a <= max_copper_layer + 1) { - int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1); - int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1); - if (bside != aside) - return bside - aside; - } - if (d) - return d; - return b - a; -} - -static const char *filename; -static BoxType *bounds; -static int in_mono, as_shown; - -static void parse_bloat(const char *str) -{ - UnitList extra_units = { - {"pix", scale, 0}, - {"px", scale, 0}, - {"", 0, 0} - }; - if (str == NULL) - return; - bloat = GetValueEx(str, NULL, NULL, extra_units, ""); -} - -void png_hid_export_to_file(FILE * the_file, HID_Attr_Val * options) -{ - int i; - static int saved_layer_stack[MAX_LAYER]; - int saved_show_solder_side; - BoxType region; - FlagType save_flags; - - f = the_file; - - region.X1 = 0; - region.Y1 = 0; - region.X2 = PCB->MaxWidth; - region.Y2 = PCB->MaxHeight; - - if (options[HA_only_visible].int_value) - bounds = GetDataBoundingBox(PCB->Data); - else - bounds = ®ion; - - memset(print_group, 0, sizeof(print_group)); - memset(print_layer, 0, sizeof(print_layer)); - - for (i = 0; i < max_copper_layer; i++) { - LayerType *layer = PCB->Data->Layer + i; - if (!LAYER_IS_EMPTY(layer)) - print_group[GetLayerGroupNumberByNumber(i)] = 1; - } - print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1; - print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1; - for (i = 0; i < max_copper_layer; i++) - if (print_group[GetLayerGroupNumberByNumber(i)]) - print_layer[i] = 1; - - memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack)); - save_flags = PCB->Flags; - saved_show_solder_side = Settings.ShowSolderSide; - - as_shown = options[HA_as_shown].int_value; - if (!options[HA_as_shown].int_value) { - CLEAR_FLAG(SHOWMASKFLAG, PCB); - Settings.ShowSolderSide = 0; - - comp_layer = GetLayerGroupNumberByNumber(component_silk_layer); - solder_layer = GetLayerGroupNumberByNumber(solder_silk_layer); - qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort); - - CLEAR_FLAG(THINDRAWFLAG, PCB); - CLEAR_FLAG(THINDRAWPOLYFLAG, PCB); - - if (photo_mode) { - int i, n = 0; - SET_FLAG(SHOWMASKFLAG, PCB); - photo_has_inners = 0; - if (comp_layer < solder_layer) - for (i = comp_layer; i <= solder_layer; i++) { - photo_groups[n++] = i; - if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i)) - photo_has_inners = 1; - } - else - for (i = comp_layer; i >= solder_layer; i--) { - photo_groups[n++] = i; - if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i)) - photo_has_inners = 1; - } - if (!photo_has_inners) { - photo_groups[1] = photo_groups[n - 1]; - n = 2; - } - photo_ngroups = n; - - if (photo_flip) { - for (i = 0, n = photo_ngroups - 1; i < n; i++, n--) { - int tmp = photo_groups[i]; - photo_groups[i] = photo_groups[n]; - photo_groups[n] = tmp; - } - } - } - } - linewidth = -1; - lastbrush = (gdImagePtr) ((void *) -1); - lastcap = -1; - lastgroup = -1; - show_solder_side = Settings.ShowSolderSide; - - in_mono = options[HA_mono].int_value; - - if (!photo_mode && Settings.ShowSolderSide) { - int i, j; - for (i = 0, j = max_copper_layer - 1; i < j; i++, j--) { - int k = LayerStack[i]; - LayerStack[i] = LayerStack[j]; - LayerStack[j] = k; - } - } - - hid_expose_callback(&png_hid, bounds, 0); - - memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack)); - PCB->Flags = save_flags; - Settings.ShowSolderSide = saved_show_solder_side; -} - -static void blend(color_struct * dest, float a_amount, color_struct * a, color_struct * b) -{ - dest->r = a->r * a_amount + b->r * (1 - a_amount); - dest->g = a->g * a_amount + b->g * (1 - a_amount); - dest->b = a->b * a_amount + b->b * (1 - a_amount); -} - -static void rgb(color_struct * dest, int r, int g, int b) -{ - dest->r = r; - dest->g = g; - dest->b = b; -} - -static int smshadows[3][3] = { - {1, 20, 1}, - {10, 0, -10}, - {-1, -20, -1}, -}; - -static int shadows[5][5] = { - {1, 1, 1, 1, -1}, - {1, 1, 1, -1, -1}, - {1, 1, 0, -1, -1}, - {1, -1, -1, -1, -1}, - {-1, -1, -1, -1, -1}, -}; - -/* black and white are 0 and 1 */ -#define TOP_SHADOW 2 -#define BOTTOM_SHADOW 3 - -static void ts_bs(gdImagePtr im) -{ - int x, y, sx, sy, si; - for (x = 0; x < gdImageSX(im); x++) - for (y = 0; y < gdImageSY(im); y++) { - si = 0; - for (sx = -2; sx < 3; sx++) - for (sy = -2; sy < 3; sy++) - if (!gdImageGetPixel(im, x + sx, y + sy)) - si += shadows[sx + 2][sy + 2]; - if (gdImageGetPixel(im, x, y)) { - if (si > 1) - gdImageSetPixel(im, x, y, TOP_SHADOW); - else if (si < -1) - gdImageSetPixel(im, x, y, BOTTOM_SHADOW); - } - } -} - -static void ts_bs_sm(gdImagePtr im) -{ - int x, y, sx, sy, si; - for (x = 0; x < gdImageSX(im); x++) - for (y = 0; y < gdImageSY(im); y++) { - si = 0; - for (sx = -1; sx < 2; sx++) - for (sy = -1; sy < 2; sy++) - if (!gdImageGetPixel(im, x + sx, y + sy)) - si += smshadows[sx + 1][sy + 1]; - if (gdImageGetPixel(im, x, y)) { - if (si > 1) - gdImageSetPixel(im, x, y, TOP_SHADOW); - else if (si < -1) - gdImageSetPixel(im, x, y, BOTTOM_SHADOW); - } - } -} - -static void png_do_export(HID_Attr_Val * options) -{ - int save_ons[MAX_LAYER + 2]; - int i; - BoxType *bbox; - int w, h; - int xmax, ymax, dpi; - const char *fmt; - bool format_error = false; - - if (color_cache) { - free(color_cache); - color_cache = NULL; - } - - if (brush_cache) { - free(brush_cache); - brush_cache = NULL; - } - - if (!options) { - png_get_export_options(0); - for (i = 0; i < NUM_OPTIONS; i++) - png_values[i] = png_attribute_list[i].default_val; - options = png_values; - } - - if (options[HA_photo_mode].int_value || options[HA_ben_mode].int_value) { - photo_mode = 1; - options[HA_mono].int_value = 1; - options[HA_as_shown].int_value = 0; - memset(photo_copper, 0, sizeof(photo_copper)); - photo_silk = photo_mask = photo_drill = 0; - photo_outline = 0; - if (options[HA_photo_flip_x].int_value || options[HA_ben_flip_x].int_value) - photo_flip = PHOTO_FLIP_X; - else if (options[HA_photo_flip_y].int_value || options[HA_ben_flip_y].int_value) - photo_flip = PHOTO_FLIP_Y; - else - photo_flip = 0; - } - else - photo_mode = 0; - - filename = options[HA_pngfile].str_value; - if (!filename) - filename = "pcb-out.png"; - - /* figure out width and height of the board */ - if (options[HA_only_visible].int_value) { - bbox = GetDataBoundingBox(PCB->Data); - x_shift = bbox->X1; - y_shift = bbox->Y1; - h = bbox->Y2 - bbox->Y1; - w = bbox->X2 - bbox->X1; - } - else { - x_shift = 0; - y_shift = 0; - h = PCB->MaxHeight; - w = PCB->MaxWidth; - } - - /* - * figure out the scale factor we need to make the image - * fit in our specified PNG file size - */ - xmax = ymax = dpi = 0; - if (options[HA_dpi].int_value != 0) { - dpi = options[HA_dpi].int_value; - if (dpi < 0) { - fprintf(stderr, "ERROR: dpi may not be < 0\n"); - return; - } - } - - if (options[HA_xmax].int_value > 0) { - xmax = options[HA_xmax].int_value; - dpi = 0; - } - - if (options[HA_ymax].int_value > 0) { - ymax = options[HA_ymax].int_value; - dpi = 0; - } - - if (options[HA_xymax].int_value > 0) { - dpi = 0; - if (options[HA_xymax].int_value < xmax || xmax == 0) - xmax = options[HA_xymax].int_value; - if (options[HA_xymax].int_value < ymax || ymax == 0) - ymax = options[HA_xymax].int_value; - } - - if (xmax < 0 || ymax < 0) { - fprintf(stderr, "ERROR: xmax and ymax may not be < 0\n"); - return; - } - - if (dpi > 0) { - /* - * a scale of 1 means 1 pixel is 1 inch - * a scale of 10 means 1 pixel is 10 inches - */ - scale = INCH_TO_COORD(1) / dpi; - w = w / scale; - h = h / scale; - } - else if (xmax == 0 && ymax == 0) { - fprintf(stderr, "ERROR: You may not set both xmax, ymax," "and xy-max to zero\n"); - return; - } - else { - if (ymax == 0 || ((xmax > 0) - && ((w / xmax) > (h / ymax)))) { - h = (h * xmax) / w; - scale = w / xmax; - w = xmax; - } - else { - w = (w * ymax) / h; - scale = h / ymax; - h = ymax; - } - } - - im = gdImageCreate(w, h); - if (im == NULL) { - Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, w, h); - return; - } - - master_im = im; - - parse_bloat(options[HA_bloat].str_value); - - /* - * Allocate white and black -- the first color allocated - * becomes the background color - */ - - white = (color_struct *) malloc(sizeof(color_struct)); - white->r = white->g = white->b = 255; - if (options[HA_use_alpha].int_value) - white->a = 127; - else - white->a = 0; - white->c = gdImageColorAllocateAlpha(im, white->r, white->g, white->b, white->a); - if (white->c == BADC) { - Message("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__); - return; - } - - gdImageFilledRectangle(im, 0, 0, gdImageSX(im), gdImageSY(im), white->c); - - black = (color_struct *) malloc(sizeof(color_struct)); - black->r = black->g = black->b = black->a = 0; - black->c = gdImageColorAllocate(im, black->r, black->g, black->b); - if (black->c == BADC) { - Message("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__); - return; - } - - f = fopen(filename, "wb"); - if (!f) { - perror(filename); - return; - } - - if (!options[HA_as_shown].int_value) - hid_save_and_show_layer_ons(save_ons); - - png_hid_export_to_file(f, options); - - if (!options[HA_as_shown].int_value) - hid_restore_layer_ons(save_ons); - - if (photo_mode) { - int x, y; - color_struct white, black, fr4; - - rgb(&white, 255, 255, 255); - rgb(&black, 0, 0, 0); - rgb(&fr4, 70, 70, 70); - - im = master_im; - - ts_bs(photo_copper[photo_groups[0]]); - ts_bs(photo_silk); - ts_bs_sm(photo_mask); - - if (photo_outline && have_outline) { - int black = gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00); - - /* go all the way around the image, trying to fill the outline */ - for (x = 0; x < gdImageSX(im); x++) { - gdImageFillToBorder(photo_outline, x, 0, black, black); - gdImageFillToBorder(photo_outline, x, gdImageSY(im) - 1, black, black); - } - for (y = 1; y < gdImageSY(im) - 1; y++) { - gdImageFillToBorder(photo_outline, 0, y, black, black); - gdImageFillToBorder(photo_outline, gdImageSX(im) - 1, y, black, black); - - } - } - - - for (x = 0; x < gdImageSX(im); x++) { - for (y = 0; y < gdImageSY(im); y++) { - color_struct p, cop; - int cc, mask, silk; - int transparent; - - if (photo_outline && have_outline) { - transparent = gdImageGetPixel(photo_outline, x, y); - } - else { - transparent = 0; - } - - mask = photo_mask ? gdImageGetPixel(photo_mask, x, y) : 0; - silk = photo_silk ? gdImageGetPixel(photo_silk, x, y) : 0; - - if (photo_copper[photo_groups[1]] - && gdImageGetPixel(photo_copper[photo_groups[1]], x, y)) - rgb(&cop, 40, 40, 40); - else - rgb(&cop, 100, 100, 110); - - if (photo_ngroups == 2) - blend(&cop, 0.3, &cop, &fr4); - - cc = gdImageGetPixel(photo_copper[photo_groups[0]], x, y); - if (cc) { - int r; - - if (mask) - rgb(&cop, 220, 145, 230); - else { - rgb(&cop, 140, 150, 160); - - r = (rand() % 5 - 2) * 2; - cop.r += r; - cop.g += r; - cop.b += r; - - } - - if (cc == TOP_SHADOW) { - cop.r = 255 - (255 - cop.r) * 0.7; - cop.g = 255 - (255 - cop.g) * 0.7; - cop.b = 255 - (255 - cop.b) * 0.7; - } - if (cc == BOTTOM_SHADOW) { - cop.r *= 0.7; - cop.g *= 0.7; - cop.b *= 0.7; - } - } - - if (photo_drill && !gdImageGetPixel(photo_drill, x, y)) { - rgb(&p, 0, 0, 0); - transparent = 1; - } - else if (silk) { - if (silk == TOP_SHADOW) - rgb(&p, 255, 255, 255); - else if (silk == BOTTOM_SHADOW) - rgb(&p, 192, 192, 192); - else - rgb(&p, 224, 224, 224); - } - else if (mask) { - p = cop; - p.r /= 2; - p.b /= 2; - if (mask == TOP_SHADOW) - blend(&p, 0.7, &p, &white); - if (mask == BOTTOM_SHADOW) - blend(&p, 0.7, &p, &black); - } - else - p = cop; - - if (options[HA_use_alpha].int_value) { - - cc = (transparent) ? gdImageColorResolveAlpha(im, 0, 0, 0, 127) : gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0); - - } - else { - cc = (transparent) ? gdImageColorResolve(im, 0, 0, 0) : gdImageColorResolve(im, p.r, p.g, p.b); - } - - if (photo_flip == PHOTO_FLIP_X) - gdImageSetPixel(im, gdImageSX(im) - x - 1, y, cc); - else if (photo_flip == PHOTO_FLIP_Y) - gdImageSetPixel(im, x, gdImageSY(im) - y - 1, cc); - else - gdImageSetPixel(im, x, y, cc); - } - } - } - - /* actually write out the image */ - fmt = filetypes[options[HA_filetype].int_value]; - - if (fmt == NULL) - format_error = true; - else if (strcmp(fmt, FMT_gif) == 0) -#ifdef HAVE_GDIMAGEGIF - gdImageGif(im, f); -#else - format_error = true; -#endif - else if (strcmp(fmt, FMT_jpg) == 0) -#ifdef HAVE_GDIMAGEJPEG - gdImageJpeg(im, f, -1); -#else - format_error = true; -#endif - else if (strcmp(fmt, FMT_png) == 0) -#ifdef HAVE_GDIMAGEPNG - gdImagePng(im, f); -#else - format_error = true; -#endif - else - format_error = true; - - if (format_error) - fprintf(stderr, "Error: Invalid graphic file format." " This is a bug. Please report it.\n"); - - fclose(f); - - gdImageDestroy(im); -} - -static void png_parse_arguments(int *argc, char ***argv) -{ - hid_register_attributes(png_attribute_list, sizeof(png_attribute_list) / sizeof(png_attribute_list[0]), png_cookie); - hid_parse_command_line(argc, argv); -} - - -static int is_mask; -static int is_drill; - -static int png_set_layer(const char *name, int group, int empty) -{ - int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; - if (name == 0) - name = PCB->Data->Layer[idx].Name; - - doing_outline = 0; - - if (idx >= 0 && idx < max_copper_layer && !print_layer[idx]) - return 0; - if (SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == SL_FAB) - return 0; - - if (strcmp(name, "invisible") == 0) - return 0; - - is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); - is_mask = (SL_TYPE(idx) == SL_MASK); - - if (SL_TYPE(idx) == SL_PASTE) - return 0; - - if (photo_mode) { - switch (idx) { - case SL(SILK, TOP): - if (photo_flip) - return 0; - photo_im = &photo_silk; - break; - case SL(SILK, BOTTOM): - if (!photo_flip) - return 0; - photo_im = &photo_silk; - break; - - case SL(MASK, TOP): - if (photo_flip) - return 0; - photo_im = &photo_mask; - break; - case SL(MASK, BOTTOM): - if (!photo_flip) - return 0; - photo_im = &photo_mask; - break; - - case SL(PDRILL, 0): - case SL(UDRILL, 0): - photo_im = &photo_drill; - break; - - default: - if (idx < 0) - return 0; - - if (strcmp(name, "outline") == 0) { - doing_outline = 1; - have_outline = 0; - photo_im = &photo_outline; - } - else - photo_im = photo_copper + group; - - break; - } - - if (!*photo_im) { - static color_struct *black = NULL, *white = NULL; - *photo_im = gdImageCreate(gdImageSX(im), gdImageSY(im)); - if (photo_im == NULL) { - Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, gdImageSX(im), gdImageSY(im)); - return 0; - } - - - white = (color_struct *) malloc(sizeof(color_struct)); - white->r = white->g = white->b = 255; - white->a = 0; - white->c = gdImageColorAllocate(*photo_im, white->r, white->g, white->b); - if (white->c == BADC) { - Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); - return 0; - } - - black = (color_struct *) malloc(sizeof(color_struct)); - black->r = black->g = black->b = black->a = 0; - black->c = gdImageColorAllocate(*photo_im, black->r, black->g, black->b); - if (black->c == BADC) { - Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); - return 0; - } - - if (idx == SL(PDRILL, 0) - || idx == SL(UDRILL, 0)) - gdImageFilledRectangle(*photo_im, 0, 0, gdImageSX(im), gdImageSY(im), black->c); - } - im = *photo_im; - return 1; - } - - if (as_shown) { - switch (idx) { - case SL(SILK, TOP): - case SL(SILK, BOTTOM): - if (SL_MYSIDE(idx)) - return PCB->ElementOn; - return 0; - - case SL(MASK, TOP): - case SL(MASK, BOTTOM): - return TEST_FLAG(SHOWMASKFLAG, PCB) && SL_MYSIDE(idx); - } - } - else { - if (is_mask) - return 0; - - switch (idx) { - case SL(SILK, TOP): - return 1; - case SL(SILK, BOTTOM): - return 0; - } - } - - return 1; -} - - -static hidGC png_make_gc(void) -{ - hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct)); - rv->me_pointer = &png_hid; - rv->cap = Trace_Cap; - rv->width = 1; - rv->color = (color_struct *) malloc(sizeof(color_struct)); - rv->color->r = rv->color->g = rv->color->b = rv->color->a = 0; - rv->color->c = 0; - rv->is_erase = 0; - return rv; -} - -static void png_destroy_gc(hidGC gc) -{ - free(gc); -} - -static void png_use_mask(int use_it) -{ - if (photo_mode) - return; - - if (use_it == HID_MASK_CLEAR) { - return; - } - if (use_it) { - if (mask_im == NULL) { - mask_im = gdImageCreate(gdImageSX(im), gdImageSY(im)); - if (!mask_im) { - Message("%s(): gdImageCreate(%d, %d) returned NULL. Corrupt export!\n", __FUNCTION__, gdImageSY(im), gdImageSY(im)); - return; - } - gdImagePaletteCopy(mask_im, im); - } - im = mask_im; - gdImageFilledRectangle(mask_im, 0, 0, gdImageSX(mask_im), gdImageSY(mask_im), white->c); - } - else { - int x, y, c; - - im = master_im; - - for (x = 0; x < gdImageSX(im); x++) - for (y = 0; y < gdImageSY(im); y++) { - c = gdImageGetPixel(mask_im, x, y); - if (c) - gdImageSetPixel(im, x, y, c); - } - } -} - -static void png_set_color(hidGC gc, const char *name) -{ - hidval cval; - - if (im == NULL) - return; - - if (name == NULL) - name = "#ff0000"; - - if (strcmp(name, "erase") == 0 || strcmp(name, "drill") == 0) { - gc->color = white; - gc->is_erase = 1; - return; - } - gc->is_erase = 0; - - if (in_mono || (strcmp(name, "#000000") == 0)) { - gc->color = black; - return; - } - - if (hid_cache_color(0, name, &cval, &color_cache)) { - gc->color = (color_struct *) cval.ptr; - } - else if (name[0] == '#') { - gc->color = (color_struct *) malloc(sizeof(color_struct)); - sscanf(name + 1, "%2x%2x%2x", &(gc->color->r), &(gc->color->g), &(gc->color->b)); - gc->color->c = gdImageColorAllocate(master_im, gc->color->r, gc->color->g, gc->color->b); - if (gc->color->c == BADC) { - Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); - return; - } - cval.ptr = gc->color; - hid_cache_color(1, name, &cval, &color_cache); - } - else { - printf("WE SHOULD NOT BE HERE!!!\n"); - gc->color = black; - } - -} - -static void png_set_line_cap(hidGC gc, EndCapStyle style) -{ - gc->cap = style; -} - -static void png_set_line_width(hidGC gc, Coord width) -{ - gc->width = width; -} - -static void png_set_draw_xor(hidGC gc, int xor_) -{ - ; -} - -static void use_gc(hidGC gc) -{ - int need_brush = 0; - - if (gc->me_pointer != &png_hid) { - fprintf(stderr, "Fatal: GC from another HID passed to png HID\n"); - abort(); - } - - if (linewidth != gc->width) { - /* Make sure the scaling doesn't erase lines completely */ - if (SCALE(gc->width) == 0 && gc->width > 0) - gdImageSetThickness(im, 1); - else - gdImageSetThickness(im, SCALE(gc->width + 2 * bloat)); - linewidth = gc->width; - need_brush = 1; - } - - if (lastbrush != gc->brush || need_brush) { - hidval bval; - char name[256]; - char type; - int r; - - switch (gc->cap) { - case Round_Cap: - case Trace_Cap: - type = 'C'; - break; - default: - case Square_Cap: - type = 'S'; - break; - } - if (gc->width) - r = SCALE(gc->width + 2 * bloat); - else - r = 1; - - /* do not allow a brush size that is zero width. In this case limit to a single pixel. */ - if (r == 0) { - r = 1; - } - - sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); - - if (hid_cache_color(0, name, &bval, &brush_cache)) { - gc->brush = (gdImagePtr) bval.ptr; - } - else { - int bg, fg; - gc->brush = gdImageCreate(r, r); - if (gc->brush == NULL) { - Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, r, r); - return; - } - - bg = gdImageColorAllocate(gc->brush, 255, 255, 255); - if (bg == BADC) { - Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); - return; - } - fg = gdImageColorAllocateAlpha(gc->brush, gc->color->r, gc->color->g, gc->color->b, 0); - if (fg == BADC) { - Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); - return; - } - gdImageColorTransparent(gc->brush, bg); - - /* - * if we shrunk to a radius/box width of zero, then just use - * a single pixel to draw with. - */ - if (r <= 1) - gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); - else { - if (type == 'C') { - gdImageFilledEllipse(gc->brush, r / 2, r / 2, r, r, fg); - /* Make sure the ellipse is the right exact size. */ - gdImageSetPixel(gc->brush, 0, r / 2, fg); - gdImageSetPixel(gc->brush, r - 1, r / 2, fg); - gdImageSetPixel(gc->brush, r / 2, 0, fg); - gdImageSetPixel(gc->brush, r / 2, r - 1, fg); - } - else - gdImageFilledRectangle(gc->brush, 0, 0, r - 1, r - 1, fg); - } - bval.ptr = gc->brush; - hid_cache_color(1, name, &bval, &brush_cache); - } - - gdImageSetBrush(im, gc->brush); - lastbrush = gc->brush; - - } -} - -static void png_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageRectangle(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gc->color->c); -} - -static void png_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - use_gc(gc); - gdImageSetThickness(im, 0); - linewidth = 0; - - if (x1 > x2) { - Coord t = x1; - x2 = x2; - x2 = t; - } - if (y1 > y2) { - Coord t = y1; - y2 = y2; - y2 = t; - } - y1 -= bloat; - y2 += bloat; - SWAP_IF_SOLDER(y1, y2); - - gdImageFilledRectangle(im, SCALE_X(x1 - bloat), SCALE_Y(y1), SCALE_X(x2 + bloat) - 1, SCALE_Y(y2) - 1, gc->color->c); - have_outline |= doing_outline; -} - -static void png_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - if (x1 == x2 && y1 == y2) { - Coord w = gc->width / 2; - if (gc->cap != Square_Cap) - png_fill_circle(gc, x1, y1, w); - else - png_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); - return; - } - use_gc(gc); - - if (NOT_EDGE(x1, y1) || NOT_EDGE(x2, y2)) - have_outline |= doing_outline; - if (doing_outline) { - /* Special case - lines drawn along the bottom or right edges - are brought in by a pixel to make sure we have contiguous - outlines. */ - if (x1 == PCB->MaxWidth && x2 == PCB->MaxWidth) { - x1 -= scale / 2; - x2 -= scale / 2; - } - if (y1 == PCB->MaxHeight && y2 == PCB->MaxHeight) { - y1 -= scale / 2; - y2 -= scale / 2; - } - } - - gdImageSetThickness(im, 0); - linewidth = 0; - if (gc->cap != Square_Cap || x1 == x2 || y1 == y2) { - gdImageLine(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gdBrushed); - } - else { - /* - * if we are drawing a line with a square end cap and it is - * not purely horizontal or vertical, then we need to draw - * it as a filled polygon. - */ - int fg = gdImageColorResolve(im, gc->color->r, gc->color->g, - gc->color->b), w = gc->width, dx = x2 - x1, dy = y2 - y1, dwx, dwy; - gdPoint p[4]; - double l = sqrt(dx * dx + dy * dy) * 2; - - w += 2 * bloat; - dwx = -w / l * dy; - dwy = w / l * dx; - p[0].x = SCALE_X(x1 + dwx - dwy); - p[0].y = SCALE_Y(y1 + dwy + dwx); - p[1].x = SCALE_X(x1 - dwx - dwy); - p[1].y = SCALE_Y(y1 - dwy + dwx); - p[2].x = SCALE_X(x2 - dwx + dwy); - p[2].y = SCALE_Y(y2 - dwy - dwx); - p[3].x = SCALE_X(x2 + dwx + dwy); - p[3].y = SCALE_Y(y2 + dwy - dwx); - gdImageFilledPolygon(im, p, 4, fg); - } -} - -static void png_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) -{ - Angle sa, ea; - - /* - * zero angle arcs need special handling as gd will output either - * nothing at all or a full circle when passed delta angle of 0 or 360. - */ - if (delta_angle == 0) { - Coord x = (width * cos(start_angle * M_PI / 180)); - Coord y = (width * sin(start_angle * M_PI / 180)); - x = cx - x; - y = cy + y; - png_fill_circle(gc, x, y, gc->width / 2); - return; - } - - /* - * in gdImageArc, 0 degrees is to the right and +90 degrees is down - * in pcb, 0 degrees is to the left and +90 degrees is down - */ - start_angle = 180 - start_angle; - delta_angle = -delta_angle; - if (show_solder_side) { - start_angle = -start_angle; - delta_angle = -delta_angle; - } - if (delta_angle > 0) { - sa = start_angle; - ea = start_angle + delta_angle; - } - else { - sa = start_angle + delta_angle; - ea = start_angle; - } - - /* - * make sure we start between 0 and 360 otherwise gd does - * strange things - */ - sa = NormalizeAngle(sa); - ea = NormalizeAngle(ea); - - have_outline |= doing_outline; - -#if 0 - printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); - printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", - im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); -#endif - use_gc(gc); - gdImageSetThickness(im, 0); - linewidth = 0; - gdImageArc(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * width), SCALE(2 * height), sa, ea, gdBrushed); -} - -static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) -{ - Coord my_bloat; - - use_gc(gc); - - if (gc->is_erase) - my_bloat = -2 * bloat; - else - my_bloat = 2 * bloat; - - - have_outline |= doing_outline; - - gdImageSetThickness(im, 0); - linewidth = 0; - gdImageFilledEllipse(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * radius + my_bloat), SCALE(2 * radius + my_bloat), gc->color->c); - -} - -static void png_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) -{ - int i; - gdPoint *points; - - points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); - if (points == NULL) { - fprintf(stderr, "ERROR: png_fill_polygon(): malloc failed\n"); - exit(1); - } - - use_gc(gc); - for (i = 0; i < n_coords; i++) { - if (NOT_EDGE(x[i], y[i])) - have_outline |= doing_outline; - points[i].x = SCALE_X(x[i]); - points[i].y = SCALE_Y(y[i]); - } - gdImageSetThickness(im, 0); - linewidth = 0; - gdImageFilledPolygon(im, points, n_coords, gc->color->c); - free(points); -} - -static void png_calibrate(double xval, double yval) -{ - CRASH; -} - -static void png_set_crosshair(int x, int y, int a) -{ -} - -#include "dolists.h" - -void hid_png_init() -{ - memset(&png_hid, 0, sizeof(HID)); - - common_nogui_init(&png_hid); - common_draw_helpers_init(&png_hid); - - png_hid.struct_size = sizeof(HID); - png_hid.name = "png"; - png_hid.description = "GIF/JPEG/PNG export"; - png_hid.exporter = 1; - png_hid.poly_before = 1; - - png_hid.get_export_options = png_get_export_options; - png_hid.do_export = png_do_export; - png_hid.parse_arguments = png_parse_arguments; - png_hid.set_layer = png_set_layer; - png_hid.make_gc = png_make_gc; - png_hid.destroy_gc = png_destroy_gc; - png_hid.use_mask = png_use_mask; - png_hid.set_color = png_set_color; - png_hid.set_line_cap = png_set_line_cap; - png_hid.set_line_width = png_set_line_width; - png_hid.set_draw_xor = png_set_draw_xor; - png_hid.draw_line = png_draw_line; - png_hid.draw_arc = png_draw_arc; - png_hid.draw_rect = png_draw_rect; - png_hid.fill_circle = png_fill_circle; - png_hid.fill_polygon = png_fill_polygon; - png_hid.fill_rect = png_fill_rect; - png_hid.calibrate = png_calibrate; - png_hid.set_crosshair = png_set_crosshair; - -#ifdef HAVE_SOME_FORMAT - hid_register_hid(&png_hid); - -#endif -} Index: src/hid/png/hid.conf =================================================================== --- src/hid/png/hid.conf (revision 1269) +++ src/hid/png/hid.conf (nonexistent) @@ -1 +0,0 @@ -type=export Index: src/hid/png/png.h =================================================================== --- src/hid/png/png.h (revision 1269) +++ src/hid/png/png.h (nonexistent) @@ -1,4 +0,0 @@ -/* $Id$ */ - -extern const char *png_cookie; -extern void png_hid_export_to_file(FILE *, HID_Attr_Val *); Index: src/hid/gerber/gerber.c =================================================================== --- src/hid/gerber/gerber.c (revision 1269) +++ src/hid/gerber/gerber.c (nonexistent) @@ -1,1204 +0,0 @@ -/* $Id$ */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_PWD_H -#include -#endif - -#include - -#include "config.h" -#include "global.h" -#include "data.h" -#include "misc.h" -#include "error.h" -#include "draw.h" -#include "pcb-printf.h" - -#include "hid.h" -#include "../hidint.h" -#include "hid/common/hidnogui.h" -#include "hid/common/draw_helpers.h" -#include "hid/common/hidinit.h" - - -RCSID("$Id$"); - -const char *gerber_cookie = "gerber HID"; - -#define CRASH fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", __FUNCTION__); abort() - -/*----------------------------------------------------------------------------*/ -/* Function prototypes */ -/*----------------------------------------------------------------------------*/ - -static HID_Attribute *gerber_get_export_options(int *n); -static void gerber_do_export(HID_Attr_Val * options); -static void gerber_parse_arguments(int *argc, char ***argv); -static int gerber_set_layer(const char *name, int group, int empty); -static hidGC gerber_make_gc(void); -static void gerber_destroy_gc(hidGC gc); -static void gerber_use_mask(int use_it); -static void gerber_set_color(hidGC gc, const char *name); -static void gerber_set_line_cap(hidGC gc, EndCapStyle style); -static void gerber_set_line_width(hidGC gc, Coord width); -static void gerber_set_draw_xor(hidGC gc, int _xor); -static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); -static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle); -static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); -static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius); -static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); -static void gerber_calibrate(double xval, double yval); -static void gerber_set_crosshair(int x, int y, int action); -static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y); - -/*----------------------------------------------------------------------------*/ -/* Utility routines */ -/*----------------------------------------------------------------------------*/ - -/* These are for films */ -#define gerberX(pcb, x) ((Coord) (x)) -#define gerberY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y))) -#define gerberXOffset(pcb, x) ((Coord) (x)) -#define gerberYOffset(pcb, y) ((Coord) (-(y))) - -/* These are for drills (printed as mils but are really 1/10th mil) */ -#define gerberDrX(pcb, x) ((Coord) (x) * 10) -#define gerberDrY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)) * 10) - -/*----------------------------------------------------------------------------*/ -/* Private data structures */ -/*----------------------------------------------------------------------------*/ - -static int verbose; -static int all_layers; -static int is_mask, was_drill; -static int is_drill; -static int current_mask; -static int flash_drills; -static int copy_outline_mode; -static int name_style; -static LayerTypePtr outline_layer; - -enum ApertureShape { - ROUND, /* Shaped like a circle */ - OCTAGON, /* octagonal shape */ - SQUARE, /* Shaped like a square */ - ROUNDCLEAR, /* clearance in negatives */ - SQUARECLEAR, - THERMAL /* negative thermal relief */ -}; -typedef enum ApertureShape ApertureShape; - -/* This is added to the global aperture array indexes to get gerber - dcode and macro numbers. */ -#define DCODE_BASE 11 - -typedef struct aperture { - int dCode; /* The RS-274X D code */ - Coord width; /* Size in pcb units */ - ApertureShape shape; /* ROUND/SQUARE etc */ - struct aperture *next; -} Aperture; - -typedef struct { - Aperture *data; - int count; -} ApertureList; - -static ApertureList *layer_aptr_list; -static ApertureList *curr_aptr_list; -static int layer_list_max; -static int layer_list_idx; - -typedef struct { - Coord diam; - Coord x; - Coord y; -} PendingDrills; -PendingDrills *pending_drills = NULL; -int n_pending_drills = 0, max_pending_drills = 0; - -/*----------------------------------------------------------------------------*/ -/* Defined Constants */ -/*----------------------------------------------------------------------------*/ -#define AUTO_OUTLINE_WIDTH MIL_TO_COORD(8) /* Auto-geneated outline width of 8 mils */ - -/*----------------------------------------------------------------------------*/ -/* Aperture Routines */ -/*----------------------------------------------------------------------------*/ - -/* Initialize aperture list */ -static void initApertureList(ApertureList * list) -{ - list->data = NULL; - list->count = 0; -} - -static void deinitApertureList(ApertureList * list) -{ - Aperture *search = list->data; - Aperture *next; - while (search) { - next = search->next; - free(search); - search = next; - } - initApertureList(list); -} - -static void resetApertures() -{ - int i; - for (i = 0; i < layer_list_max; ++i) - deinitApertureList(&layer_aptr_list[i]); - free(layer_aptr_list); - layer_aptr_list = NULL; - curr_aptr_list = NULL; - layer_list_max = 0; - layer_list_idx = 0; -} - -/* Create and add a new aperture to the list */ -static Aperture *addAperture(ApertureList * list, Coord width, ApertureShape shape) -{ - static int aperture_count; - - Aperture *app = (Aperture *) malloc(sizeof *app); - if (app == NULL) - return NULL; - - app->width = width; - app->shape = shape; - app->dCode = DCODE_BASE + aperture_count++; - app->next = list->data; - - list->data = app; - ++list->count; - - return app; -} - -/* Fetch an aperture from the list with the specified - * width/shape, creating a new one if none exists */ -static Aperture *findAperture(ApertureList * list, Coord width, ApertureShape shape) -{ - Aperture *search; - - /* we never draw zero-width lines */ - if (width == 0) - return NULL; - - /* Search for an appropriate aperture. */ - for (search = list->data; search; search = search->next) - if (search->width == width && search->shape == shape) - return search; - - /* Failing that, create a new one */ - return addAperture(list, width, shape); -} - -/* Output aperture data to the file */ -static void fprintAperture(FILE * f, Aperture * aptr) -{ - switch (aptr->shape) { - case ROUND: - pcb_fprintf(f, "%%ADD%dC,%.4mi*%%\r\n", aptr->dCode, aptr->width); - break; - case SQUARE: - pcb_fprintf(f, "%%ADD%dR,%.4miX%.4mi*%%\r\n", aptr->dCode, aptr->width, aptr->width); - break; - case OCTAGON: - pcb_fprintf(f, "%%AMOCT%d*5,0,8,0,0,%.4mi,22.5*%%\r\n" - "%%ADD%dOCT%d*%%\r\n", aptr->dCode, (Coord) ((double) aptr->width / COS_22_5_DEGREE), aptr->dCode, aptr->dCode); - break; -#if 0 - case THERMAL: - fprintf(f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\r\n" - "%%ADD%dTHERM%d*%%\r\n", dCode, gap / 100000.0, width / 100000.0, finger / 100000.0, dCode, dCode); - break; - case ROUNDCLEAR: - fprintf(f, "%%ADD%dC,%.4fX%.4f*%%\r\n", dCode, gap / 100000.0, width / 100000.0); - break; - case SQUARECLEAR: - fprintf(f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\r\n", - dCode, gap / 100000.0, gap / 100000.0, width / 100000.0, width / 100000.0); - break; -#else - default: - break; -#endif - } -} - -/* Set the aperture list for the current layer, - * expanding the list buffer if needed */ -static ApertureList *setLayerApertureList(int layer_idx) -{ - if (layer_idx >= layer_list_max) { - int i = layer_list_max; - layer_list_max = 2 * (layer_idx + 1); - layer_aptr_list = (ApertureList *) - realloc(layer_aptr_list, layer_list_max * sizeof(*layer_aptr_list)); - for (; i < layer_list_max; ++i) - initApertureList(&layer_aptr_list[i]); - } - curr_aptr_list = &layer_aptr_list[layer_idx]; - return curr_aptr_list; -} - -/* --------------------------------------------------------------------------- */ - -static HID gerber_hid; - -typedef struct hid_gc_struct { - EndCapStyle cap; - int width; - int color; - int erase; - int drill; -} hid_gc_struct; - -static FILE *f = NULL; -static char *filename = NULL; -static char *filesuff = NULL; -static char *layername = NULL; -static int lncount = 0; - -static int finding_apertures = 0; -static int pagecount = 0; -static int linewidth = -1; -static int lastgroup = -1; -static int lastcap = -1; -static int lastcolor = -1; -static int print_group[MAX_LAYER]; -static int print_layer[MAX_LAYER]; -static int lastX, lastY; /* the last X and Y coordinate */ - -static const char *copy_outline_names[] = { -#define COPY_OUTLINE_NONE 0 - "none", -#define COPY_OUTLINE_MASK 1 - "mask", -#define COPY_OUTLINE_SILK 2 - "silk", -#define COPY_OUTLINE_ALL 3 - "all", - NULL -}; - -static const char *name_style_names[] = { -#define NAME_STYLE_FIXED 0 - "fixed", -#define NAME_STYLE_SINGLE 1 - "single", -#define NAME_STYLE_FIRST 2 - "first", -#define NAME_STYLE_EAGLE 3 - "eagle", - NULL -}; - -static HID_Attribute gerber_options[] = { - -/* %start-doc options "90 Gerber Export" -@ftable @code -@item --gerberfile -Gerber output file prefix. Can include a path. -@end ftable -%end-doc -*/ - {"gerberfile", "Gerber output file base", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_gerberfile 0 - -/* %start-doc options "90 Gerber Export" -@ftable @code -@item --all-layers -Output contains all layers, even empty ones. -@end ftable -%end-doc -*/ - {"all-layers", "Output all layers, even empty ones", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_all_layers 1 - -/* %start-doc options "90 Gerber Export" -@ftable @code -@item --verbose -Print file names and aperture counts on stdout. -@end ftable -%end-doc -*/ - {"verbose", "Print file names and aperture counts on stdout", - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_verbose 2 - {"copy-outline", "Copy outline onto other layers", - HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0}, -#define HA_copy_outline 3 - {"name-style", "Naming style for individual gerber files", - HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0}, -#define HA_name_style 4 -}; - -#define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0])) - -static HID_Attr_Val gerber_values[NUM_OPTIONS]; - -static HID_Attribute *gerber_get_export_options(int *n) -{ - static char *last_made_filename = NULL; - if (PCB) - derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename); - - if (n) - *n = NUM_OPTIONS; - return gerber_options; -} - -static int group_for_layer(int l) -{ - if (l < max_copper_layer + 2 && l >= 0) - return GetLayerGroupNumberByNumber(l); - /* else something unique */ - return max_group + 3 + l; -} - -static int layer_sort(const void *va, const void *vb) -{ - int a = *(int *) va; - int b = *(int *) vb; - int d = group_for_layer(b) - group_for_layer(a); - if (d) - return d; - return b - a; -} - -static void maybe_close_f(FILE * f) -{ - if (f) { - if (was_drill) - fprintf(f, "M30\r\n"); - else - fprintf(f, "M02*\r\n"); - fclose(f); - } -} - -static BoxType region; - -/* Very similar to layer_type_to_file_name() but appends only a - three-character suffix compatible with Eagle's defaults. */ -static void assign_eagle_file_suffix(char *dest, int idx) -{ - int group; - int nlayers; - char *suff = "out"; - - switch (idx) { - case SL(SILK, TOP): - suff = "plc"; - break; - case SL(SILK, BOTTOM): - suff = "pls"; - break; - case SL(MASK, TOP): - suff = "stc"; - break; - case SL(MASK, BOTTOM): - suff = "sts"; - break; - case SL(PDRILL, 0): - suff = "drd"; - break; - case SL(UDRILL, 0): - suff = "dru"; - break; - case SL(PASTE, TOP): - suff = "crc"; - break; - case SL(PASTE, BOTTOM): - suff = "crs"; - break; - case SL(INVISIBLE, 0): - suff = "inv"; - break; - case SL(FAB, 0): - suff = "fab"; - break; - case SL(ASSY, TOP): - suff = "ast"; - break; - case SL(ASSY, BOTTOM): - suff = "asb"; - break; - - default: - group = GetLayerGroupNumberByNumber(idx); - nlayers = PCB->LayerGroups.Number[group]; - if (group == GetLayerGroupNumberByNumber(component_silk_layer)) { - suff = "cmp"; - } - else if (group == GetLayerGroupNumberByNumber(solder_silk_layer)) { - suff = "sol"; - } - else if (nlayers == 1 - && (strcmp(PCB->Data->Layer[idx].Name, "route") == 0 || strcmp(PCB->Data->Layer[idx].Name, "outline") == 0)) { - suff = "oln"; - } - else { - static char buf[20]; - sprintf(buf, "ly%d", group); - suff = buf; - } - break; - } - - strcpy(dest, suff); -} - -static void assign_file_suffix(char *dest, int idx) -{ - int fns_style; - const char *sext = ".gbr"; - - switch (name_style) { - default: - case NAME_STYLE_FIXED: - fns_style = FNS_fixed; - break; - case NAME_STYLE_SINGLE: - fns_style = FNS_single; - break; - case NAME_STYLE_FIRST: - fns_style = FNS_first; - break; - case NAME_STYLE_EAGLE: - assign_eagle_file_suffix(dest, idx); - return; - } - - switch (idx) { - case SL(PDRILL, 0): - sext = ".cnc"; - break; - case SL(UDRILL, 0): - sext = ".cnc"; - break; - } - - strcpy(dest, layer_type_to_file_name(idx, fns_style)); - strcat(dest, sext); -} - -static void gerber_do_export(HID_Attr_Val * options) -{ - const char *fnbase; - int i; - static int saved_layer_stack[MAX_LAYER]; - int save_ons[MAX_LAYER + 2]; - FlagType save_thindraw; - - save_thindraw = PCB->Flags; - CLEAR_FLAG(THINDRAWFLAG, PCB); - CLEAR_FLAG(THINDRAWPOLYFLAG, PCB); - CLEAR_FLAG(CHECKPLANESFLAG, PCB); - - if (!options) { - gerber_get_export_options(NULL); - for (i = 0; i < NUM_OPTIONS; i++) - gerber_values[i] = gerber_options[i].default_val; - options = gerber_values; - } - - fnbase = options[HA_gerberfile].str_value; - if (!fnbase) - fnbase = "pcb-out"; - - verbose = options[HA_verbose].int_value; - all_layers = options[HA_all_layers].int_value; - - copy_outline_mode = options[HA_copy_outline].int_value; - name_style = options[HA_name_style].int_value; - - outline_layer = NULL; - - for (i = 0; i < max_copper_layer; i++) { - LayerType *layer = PCB->Data->Layer + i; - if (strcmp(layer->Name, "outline") == 0 || strcmp(layer->Name, "route") == 0) { - outline_layer = layer; - } - } - - i = strlen(fnbase); - filename = (char *) realloc(filename, i + 40); - strcpy(filename, fnbase); - strcat(filename, "."); - filesuff = filename + strlen(filename); - - if (all_layers) { - memset(print_group, 1, sizeof(print_group)); - memset(print_layer, 1, sizeof(print_layer)); - } - else { - memset(print_group, 0, sizeof(print_group)); - memset(print_layer, 0, sizeof(print_layer)); - } - - hid_save_and_show_layer_ons(save_ons); - for (i = 0; i < max_copper_layer; i++) { - LayerType *layer = PCB->Data->Layer + i; - if (!LAYER_IS_EMPTY(layer)) - print_group[GetLayerGroupNumberByNumber(i)] = 1; - } - print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1; - print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1; - for (i = 0; i < max_copper_layer; i++) - if (print_group[GetLayerGroupNumberByNumber(i)]) - print_layer[i] = 1; - - memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack)); - qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort); - linewidth = -1; - lastcap = -1; - lastgroup = -1; - lastcolor = -1; - - region.X1 = 0; - region.Y1 = 0; - region.X2 = PCB->MaxWidth; - region.Y2 = PCB->MaxHeight; - - pagecount = 1; - resetApertures(); - - lastgroup = -1; - layer_list_idx = 0; - finding_apertures = 1; - hid_expose_callback(&gerber_hid, ®ion, 0); - - layer_list_idx = 0; - finding_apertures = 0; - hid_expose_callback(&gerber_hid, ®ion, 0); - - memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack)); - - maybe_close_f(f); - f = NULL; - hid_restore_layer_ons(save_ons); - PCB->Flags = save_thindraw; -} - -static void gerber_parse_arguments(int *argc, char ***argv) -{ - hid_register_attributes(gerber_options, NUM_OPTIONS, gerber_cookie); - hid_parse_command_line(argc, argv); -} - -static int drill_sort(const void *va, const void *vb) -{ - PendingDrills *a = (PendingDrills *) va; - PendingDrills *b = (PendingDrills *) vb; - if (a->diam != b->diam) - return a->diam - b->diam; - if (a->x != b->x) - return a->x - a->x; - return b->y - b->y; -} - -static int gerber_set_layer(const char *name, int group, int empty) -{ - int want_outline; - char *cp; - int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; - - if (name == NULL) - name = PCB->Data->Layer[idx].Name; - - if (idx >= 0 && idx < max_copper_layer && !print_layer[idx]) - return 0; - - if (strcmp(name, "invisible") == 0) - return 0; - if (SL_TYPE(idx) == SL_ASSY) - return 0; - - flash_drills = 0; - if (strcmp(name, "outline") == 0 || strcmp(name, "route") == 0) - flash_drills = 1; - - if (is_drill && n_pending_drills) { - int i; - /* dump pending drills in sequence */ - qsort(pending_drills, n_pending_drills, sizeof(pending_drills[0]), drill_sort); - for (i = 0; i < n_pending_drills; i++) { - if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam) { - Aperture *ap = findAperture(curr_aptr_list, pending_drills[i].diam, ROUND); - fprintf(f, "T%02d\r\n", ap->dCode); - } - pcb_fprintf(f, "X%06.0mlY%06.0ml\r\n", gerberDrX(PCB, pending_drills[i].x), gerberDrY(PCB, pending_drills[i].y)); - } - free(pending_drills); - n_pending_drills = max_pending_drills = 0; - pending_drills = NULL; - } - - is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); - is_mask = (SL_TYPE(idx) == SL_MASK); - current_mask = 0; -#if 0 - printf("Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask); -#endif - - if (group < 0 || group != lastgroup) { - time_t currenttime; - char utcTime[64]; -#ifdef HAVE_GETPWUID - struct passwd *pwentry; -#endif - ApertureList *aptr_list; - Aperture *search; - - lastgroup = group; - lastX = -1; - lastY = -1; - lastcolor = 0; - linewidth = -1; - lastcap = -1; - - aptr_list = setLayerApertureList(layer_list_idx++); - - if (finding_apertures) - goto emit_outline; - - if (aptr_list->count == 0 && !all_layers) - return 0; - - maybe_close_f(f); - f = NULL; - - pagecount++; - assign_file_suffix(filesuff, idx); - f = fopen(filename, "wb"); /* Binary needed to force CR-LF */ - if (f == NULL) { - Message("Error: Could not open %s for writing.\n", filename); - return 1; - } - - was_drill = is_drill; - - if (verbose) { - int c = aptr_list->count; - printf("Gerber: %d aperture%s in %s\n", c, c == 1 ? "" : "s", filename); - } - - if (is_drill) { - /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is - always six-digit 0.1 mil resolution (i.e. 001100 = 0.11") */ - fprintf(f, "M48\r\n" "INCH\r\n"); - for (search = aptr_list->data; search; search = search->next) - pcb_fprintf(f, "T%02dC%.3mi\r\n", search->dCode, search->width); - fprintf(f, "%%\r\n"); - /* FIXME */ - return 1; - } - - fprintf(f, "G04 start of page %d for group %d idx %d *\r\n", pagecount, group, idx); - - /* Create a portable timestamp. */ - currenttime = time(NULL); - { - /* avoid gcc complaints */ - const char *fmt = "%c UTC"; - strftime(utcTime, sizeof utcTime, fmt, gmtime(¤ttime)); - } - /* Print a cute file header at the beginning of each file. */ - fprintf(f, "G04 Title: %s, %s *\r\n", UNKNOWN(PCB->Name), UNKNOWN(name)); - fprintf(f, "G04 Creator: %s " VERSION " *\r\n", Progname); - fprintf(f, "G04 CreationDate: %s *\r\n", utcTime); - -#ifdef HAVE_GETPWUID - /* ID the user. */ - pwentry = getpwuid(getuid()); - fprintf(f, "G04 For: %s *\r\n", pwentry->pw_name); -#endif - - fprintf(f, "G04 Format: Gerber/RS-274X *\r\n"); - pcb_fprintf(f, "G04 PCB-Dimensions: %.0mc %.0mc *\r\n", PCB->MaxWidth, PCB->MaxHeight); - fprintf(f, "G04 PCB-Coordinate-Origin: lower left *\r\n"); - - /* Signal data in inches. */ - fprintf(f, "%%MOIN*%%\r\n"); - - /* Signal Leading zero suppression, Absolute Data, 2.5 format */ - fprintf(f, "%%FSLAX25Y25*%%\r\n"); - - /* build a legal identifier. */ - if (layername) - free(layername); - layername = strdup(filesuff); - if (strrchr(layername, '.')) - *strrchr(layername, '.') = 0; - - for (cp = layername; *cp; cp++) { - if (isalnum((int) *cp)) - *cp = toupper((int) *cp); - else - *cp = '_'; - } - fprintf(f, "%%LN%s*%%\r\n", layername); - lncount = 1; - - for (search = aptr_list->data; search; search = search->next) - fprintAperture(f, search); - if (aptr_list->count == 0) - /* We need to put *something* in the file to make it be parsed - as RS-274X instead of RS-274D. */ - fprintf(f, "%%ADD11C,0.0100*%%\r\n"); - } - -emit_outline: - /* If we're printing a copper layer other than the outline layer, - and we want to "print outlines", and we have an outline layer, - print the outline layer on this layer also. */ - want_outline = 0; - if (copy_outline_mode == COPY_OUTLINE_MASK && SL_TYPE(idx) == SL_MASK) - want_outline = 1; - if (copy_outline_mode == COPY_OUTLINE_SILK && SL_TYPE(idx) == SL_SILK) - want_outline = 1; - if (copy_outline_mode == COPY_OUTLINE_ALL - && (SL_TYPE(idx) == SL_SILK - || SL_TYPE(idx) == SL_MASK || SL_TYPE(idx) == SL_FAB || SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == 0)) - want_outline = 1; - - if (want_outline && strcmp(name, "outline") - && strcmp(name, "route")) { - if (outline_layer && outline_layer != PCB->Data->Layer + idx) - DrawLayer(outline_layer, ®ion); - else if (!outline_layer) { - hidGC gc = gui->make_gc(); - printf("name %s idx %d\n", name, idx); - if (SL_TYPE(idx) == SL_SILK) - gui->set_line_width(gc, PCB->minSlk); - else if (group >= 0) - gui->set_line_width(gc, PCB->minWid); - else - gui->set_line_width(gc, AUTO_OUTLINE_WIDTH); - gui->draw_line(gc, 0, 0, PCB->MaxWidth, 0); - gui->draw_line(gc, 0, 0, 0, PCB->MaxHeight); - gui->draw_line(gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight); - gui->draw_line(gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight); - gui->destroy_gc(gc); - } - } - - return 1; -} - -static hidGC gerber_make_gc(void) -{ - hidGC rv = (hidGC) calloc(1, sizeof(*rv)); - rv->cap = Trace_Cap; - return rv; -} - -static void gerber_destroy_gc(hidGC gc) -{ - free(gc); -} - -static void gerber_use_mask(int use_it) -{ - current_mask = use_it; -} - -static void gerber_set_color(hidGC gc, const char *name) -{ - if (strcmp(name, "erase") == 0) { - gc->color = 1; - gc->erase = 1; - gc->drill = 0; - } - else if (strcmp(name, "drill") == 0) { - gc->color = 1; - gc->erase = 0; - gc->drill = 1; - } - else { - gc->color = 0; - gc->erase = 0; - gc->drill = 0; - } -} - -static void gerber_set_line_cap(hidGC gc, EndCapStyle style) -{ - gc->cap = style; -} - -static void gerber_set_line_width(hidGC gc, Coord width) -{ - gc->width = width; -} - -static void gerber_set_draw_xor(hidGC gc, int xor_) -{ - ; -} - -static void use_gc(hidGC gc, int radius) -{ - if (radius) { - radius *= 2; - if (radius != linewidth || lastcap != Round_Cap) { - Aperture *aptr = findAperture(curr_aptr_list, radius, ROUND); - if (aptr == NULL) - pcb_fprintf(stderr, "error: aperture for radius %$mS type ROUND is null\n", radius); - else if (f && !is_drill) - fprintf(f, "G54D%d*", aptr->dCode); - linewidth = radius; - lastcap = Round_Cap; - } - } - else if (linewidth != gc->width || lastcap != gc->cap) { - Aperture *aptr; - ApertureShape shape; - - linewidth = gc->width; - lastcap = gc->cap; - switch (gc->cap) { - case Round_Cap: - case Trace_Cap: - shape = ROUND; - break; - default: - case Square_Cap: - shape = SQUARE; - break; - } - aptr = findAperture(curr_aptr_list, linewidth, shape); - if (aptr == NULL) - pcb_fprintf(stderr, "error: aperture for width %$mS type %s is null\n", linewidth, shape == ROUND ? "ROUND" : "SQUARE"); - if (f) - fprintf(f, "G54D%d*", aptr->dCode); - } -#if 0 - if (lastcolor != gc->color) { - c = gc->color; - if (is_drill) - return; - if (is_mask) - c = (gc->erase ? 0 : 1); - lastcolor = gc->color; - if (f) { - if (c) { - fprintf(f, "%%LN%s_C%d*%%\r\n", layername, lncount++); - fprintf(f, "%%LPC*%%\r\n"); - } - else { - fprintf(f, "%%LN%s_D%d*%%\r\n", layername, lncount++); - fprintf(f, "%%LPD*%%\r\n"); - } - } - } -#endif -} - -static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - gerber_draw_line(gc, x1, y1, x1, y2); - gerber_draw_line(gc, x1, y1, x2, y1); - gerber_draw_line(gc, x1, y2, x2, y2); - gerber_draw_line(gc, x2, y1, x2, y2); -} - -static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - bool m = false; - - if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap) { - Coord x[5], y[5]; - double tx, ty, theta; - - theta = atan2(y2 - y1, x2 - x1); - - /* T is a vector half a thickness long, in the direction of - one of the corners. */ - tx = gc->width / 2.0 * cos(theta + M_PI / 4) * sqrt(2.0); - ty = gc->width / 2.0 * sin(theta + M_PI / 4) * sqrt(2.0); - - x[0] = x1 - tx; - y[0] = y1 - ty; - x[1] = x2 + ty; - y[1] = y2 - tx; - x[2] = x2 + tx; - y[2] = y2 + ty; - x[3] = x1 - ty; - y[3] = y1 + tx; - - x[4] = x[0]; - y[4] = y[0]; - gerber_fill_polygon(gc, 5, x, y); - return; - } - - use_gc(gc, 0); - if (!f) - return; - - if (x1 != lastX) { - m = true; - lastX = x1; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); - } - if (y1 != lastY) { - m = true; - lastY = y1; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - } - if ((x1 == x2) && (y1 == y2)) - fprintf(f, "D03*\r\n"); - else { - if (m) - fprintf(f, "D02*"); - if (x2 != lastX) { - lastX = x2; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); - } - if (y2 != lastY) { - lastY = y2; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - - } - fprintf(f, "D01*\r\n"); - } - -} - -static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) -{ - bool m = false; - double arcStartX, arcStopX, arcStartY, arcStopY; - - /* we never draw zero-width lines */ - if (gc->width == 0) - return; - - use_gc(gc, 0); - if (!f) - return; - - arcStartX = cx - width * cos(TO_RADIANS(start_angle)); - arcStartY = cy + height * sin(TO_RADIANS(start_angle)); - - /* I checked three different gerber viewers, and they all disagreed - on how ellipses should be drawn. The spec just calls G74/G75 - "circular interpolation" so there's a chance it just doesn't - support ellipses at all. Thus, we draw them out with line - segments. Note that most arcs in pcb are circles anyway. */ - if (width != height) { - double step, angle; - Coord max = width > height ? width : height; - Coord minr = max - gc->width / 10; - int nsteps; - Coord x0, y0, x1, y1; - - if (minr >= max) - minr = max - 1; - step = acos((double) minr / (double) max) * 180.0 / M_PI; - if (step > 5) - step = 5; - nsteps = abs(delta_angle) / step + 1; - step = (double) delta_angle / nsteps; - - x0 = arcStartX; - y0 = arcStartY; - angle = start_angle; - while (nsteps > 0) { - nsteps--; - x1 = cx - width * cos(TO_RADIANS(angle + step)); - y1 = cy + height * sin(TO_RADIANS(angle + step)); - gerber_draw_line(gc, x0, y0, x1, y1); - x0 = x1; - y0 = y1; - angle += step; - } - return; - } - - arcStopX = cx - width * cos(TO_RADIANS(start_angle + delta_angle)); - arcStopY = cy + height * sin(TO_RADIANS(start_angle + delta_angle)); - if (arcStartX != lastX) { - m = true; - lastX = arcStartX; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); - } - if (arcStartY != lastY) { - m = true; - lastY = arcStartY; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - } - if (m) - fprintf(f, "D02*"); - pcb_fprintf(f, - "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n", - (delta_angle < 0) ? 2 : 3, - gerberX(PCB, arcStopX), gerberY(PCB, arcStopY), - gerberXOffset(PCB, cx - arcStartX), gerberYOffset(PCB, cy - arcStartY)); - lastX = arcStopX; - lastY = arcStopY; -} - -static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) -{ - if (radius <= 0) - return; - if (is_drill) - radius = 50 * round(radius / 50.0); - use_gc(gc, radius); - if (!f) - return; - if (is_drill) { - if (n_pending_drills >= max_pending_drills) { - max_pending_drills += 100; - pending_drills = (PendingDrills *) realloc(pending_drills, max_pending_drills * sizeof(pending_drills[0])); - } - pending_drills[n_pending_drills].x = cx; - pending_drills[n_pending_drills].y = cy; - pending_drills[n_pending_drills].diam = radius * 2; - n_pending_drills++; - return; - } - else if (gc->drill && !flash_drills) - return; - if (cx != lastX) { - lastX = cx; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); - } - if (cy != lastY) { - lastY = cy; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - } - fprintf(f, "D03*\r\n"); -} - -static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) -{ - bool m = false; - int i; - int firstTime = 1; - Coord startX = 0, startY = 0; - - if (is_mask && current_mask == HID_MASK_BEFORE) - return; - - use_gc(gc, 10 * 100); - if (!f) - return; - fprintf(f, "G36*\r\n"); - for (i = 0; i < n_coords; i++) { - if (x[i] != lastX) { - m = true; - lastX = x[i]; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); - } - if (y[i] != lastY) { - m = true; - lastY = y[i]; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - } - if (firstTime) { - firstTime = 0; - startX = x[i]; - startY = y[i]; - if (m) - fprintf(f, "D02*"); - } - else if (m) - fprintf(f, "D01*\r\n"); - m = false; - } - if (startX != lastX) { - m = true; - lastX = startX; - pcb_fprintf(f, "X%.0mc", gerberX(PCB, startX)); - } - if (startY != lastY) { - m = true; - lastY = startY; - pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); - } - if (m) - fprintf(f, "D01*\r\n"); - fprintf(f, "G37*\r\n"); -} - -static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) -{ - Coord x[5]; - Coord y[5]; - x[0] = x[4] = x1; - y[0] = y[4] = y1; - x[1] = x1; - y[1] = y2; - x[2] = x2; - y[2] = y2; - x[3] = x2; - y[3] = y1; - gerber_fill_polygon(gc, 5, x, y); -} - -static void gerber_calibrate(double xval, double yval) -{ - CRASH; -} - -static void gerber_set_crosshair(int x, int y, int action) -{ -} - -void hid_gerber_init() -{ - memset(&gerber_hid, 0, sizeof(gerber_hid)); - - common_nogui_init(&gerber_hid); - common_draw_helpers_init(&gerber_hid); - - gerber_hid.struct_size = sizeof(gerber_hid); - gerber_hid.name = "gerber"; - gerber_hid.description = "RS-274X (Gerber) export"; - gerber_hid.exporter = 1; - - gerber_hid.get_export_options = gerber_get_export_options; - gerber_hid.do_export = gerber_do_export; - gerber_hid.parse_arguments = gerber_parse_arguments; - gerber_hid.set_layer = gerber_set_layer; - gerber_hid.make_gc = gerber_make_gc; - gerber_hid.destroy_gc = gerber_destroy_gc; - gerber_hid.use_mask = gerber_use_mask; - gerber_hid.set_color = gerber_set_color; - gerber_hid.set_line_cap = gerber_set_line_cap; - gerber_hid.set_line_width = gerber_set_line_width; - gerber_hid.set_draw_xor = gerber_set_draw_xor; - gerber_hid.draw_line = gerber_draw_line; - gerber_hid.draw_arc = gerber_draw_arc; - gerber_hid.draw_rect = gerber_draw_rect; - gerber_hid.fill_circle = gerber_fill_circle; - gerber_hid.fill_polygon = gerber_fill_polygon; - gerber_hid.fill_rect = gerber_fill_rect; - gerber_hid.calibrate = gerber_calibrate; - gerber_hid.set_crosshair = gerber_set_crosshair; - - hid_register_hid(&gerber_hid); -} Index: src/hid/gerber/hid.conf =================================================================== --- src/hid/gerber/hid.conf (revision 1269) +++ src/hid/gerber/hid.conf (nonexistent) @@ -1 +0,0 @@ -type=export Index: src/hid/bom/hid.conf =================================================================== --- src/hid/bom/hid.conf (revision 1269) +++ src/hid/bom/hid.conf (nonexistent) @@ -1 +0,0 @@ -type=export Index: src/hid/bom/bom.c =================================================================== --- src/hid/bom/bom.c (revision 1269) +++ src/hid/bom/bom.c (nonexistent) @@ -1,525 +0,0 @@ -/* $Id$ */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "global.h" -#include "data.h" -#include "error.h" -#include "misc.h" -#include "pcb-printf.h" - -#include "hid.h" -#include "hid/common/hidnogui.h" -#include "../hidint.h" - - -RCSID("$Id$"); - -const char *bom_cookie = "bom HID"; - -static HID_Attribute bom_options[] = { -/* %start-doc options "8 BOM Creation" -@ftable @code -@item --bomfile -Name of the BOM output file. -@end ftable -%end-doc -*/ - {"bomfile", "Name of the BOM output file", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_bomfile 0 -/* %start-doc options "8 BOM Creation" -@ftable @code -@item --xyfile -Name of the XY output file. -@end ftable -%end-doc -*/ - {"xyfile", "Name of the XY output file", - HID_String, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_xyfile 1 - -/* %start-doc options "8 BOM Creation" -@ftable @code -@item --xy-unit -Unit of XY dimensions. Defaults to mil. -@end ftable -%end-doc -*/ - {"xy-unit", "XY units", - HID_Unit, 0, 0, {-1, 0, 0}, NULL, 0}, -#define HA_unit 2 - {"xy-in-mm", ATTR_UNDOCUMENTED, - HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, -#define HA_xymm 3 -}; - -#define NUM_OPTIONS (sizeof(bom_options)/sizeof(bom_options[0])) - -static HID_Attr_Val bom_values[NUM_OPTIONS]; - -static const char *bom_filename; -static const char *xy_filename; -static const Unit *xy_unit; - -typedef struct _StringList { - char *str; - struct _StringList *next; -} StringList; - -typedef struct _BomList { - char *descr; - char *value; - int num; - StringList *refdes; - struct _BomList *next; -} BomList; - -static HID_Attribute *bom_get_export_options(int *n) -{ - static char *last_bom_filename = 0; - static char *last_xy_filename = 0; - static int last_unit_value = -1; - - if (bom_options[HA_unit].default_val.int_value == last_unit_value) { - if (Settings.grid_unit) - bom_options[HA_unit].default_val.int_value = Settings.grid_unit->index; - else - bom_options[HA_unit].default_val.int_value = get_unit_struct("mil")->index; - last_unit_value = bom_options[HA_unit].default_val.int_value; - } - if (PCB) { - derive_default_filename(PCB->Filename, &bom_options[HA_bomfile], ".bom", &last_bom_filename); - derive_default_filename(PCB->Filename, &bom_options[HA_xyfile], ".xy", &last_xy_filename); - } - - if (n) - *n = NUM_OPTIONS; - return bom_options; -} - -/* --------------------------------------------------------------------------- - * prints a centroid file in a format which includes data needed by a - * pick and place machine. Further formatting for a particular factory setup - * can easily be generated with awk or perl. In addition, a bill of materials - * file is generated which can be used for checking stock and purchasing needed - * materials. - * returns != zero on error - */ - - -static char *CleanBOMString(char *in) -{ - char *out; - int i; - - if ((out = (char *) malloc((strlen(in) + 1) * sizeof(char))) == NULL) { - fprintf(stderr, "Error: CleanBOMString() malloc() failed\n"); - exit(1); - } - - /* - * copy over in to out with some character conversions. - * Go all the way to then end to get the terminating \0 - */ - for (i = 0; i <= strlen(in); i++) { - switch (in[i]) { - case '"': - out[i] = '\''; - break; - default: - out[i] = in[i]; - } - } - - return out; -} - - -static double xyToAngle(double x, double y) -{ - double theta; - - if ((x > 0.0) && (y >= 0.0)) - theta = 180.0; - else if ((x <= 0.0) && (y > 0.0)) - theta = 90.0; - else if ((x < 0.0) && (y <= 0.0)) - theta = 0.0; - else if ((x >= 0.0) && (y < 0.0)) - theta = 270.0; - else { - theta = 0.0; - Message("xyToAngle(): unable to figure out angle of element\n" - " because the pin is at the centroid of the part.\n" - " This is a BUG!!!\n" " Setting to %g degrees\n", theta); - } - - return (theta); -} - -static StringList *string_insert(char *str, StringList * list) -{ - StringList *newlist, *cur; - - if ((newlist = (StringList *) malloc(sizeof(StringList))) == NULL) { - fprintf(stderr, "malloc() failed in string_insert()\n"); - exit(1); - } - - newlist->next = NULL; - newlist->str = strdup(str); - - if (list == NULL) - return (newlist); - - cur = list; - while (cur->next != NULL) - cur = cur->next; - - cur->next = newlist; - - return (list); -} - -static BomList *bom_insert(char *refdes, char *descr, char *value, BomList * bom) -{ - BomList *newlist, *cur, *prev = NULL; - - if (bom == NULL) { - /* this is the first element so automatically create an entry */ - if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) { - fprintf(stderr, "malloc() failed in bom_insert()\n"); - exit(1); - } - - newlist->next = NULL; - newlist->descr = strdup(descr); - newlist->value = strdup(value); - newlist->num = 1; - newlist->refdes = string_insert(refdes, NULL); - return (newlist); - } - - /* search and see if we already have used one of these - components */ - cur = bom; - while (cur != NULL) { - if ((NSTRCMP(descr, cur->descr) == 0) && (NSTRCMP(value, cur->value) == 0)) { - cur->num++; - cur->refdes = string_insert(refdes, cur->refdes); - break; - } - prev = cur; - cur = cur->next; - } - - if (cur == NULL) { - if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) { - fprintf(stderr, "malloc() failed in bom_insert()\n"); - exit(1); - } - - prev->next = newlist; - - newlist->next = NULL; - newlist->descr = strdup(descr); - newlist->value = strdup(value); - newlist->num = 1; - newlist->refdes = string_insert(refdes, NULL); - } - - return (bom); - -} - -/* - * If fp is not NULL then print out the bill of materials contained in - * bom. Either way, free all memory which has been allocated for bom. - */ -static void print_and_free(FILE * fp, BomList * bom) -{ - BomList *lastb; - StringList *lasts; - char *descr, *value; - - while (bom != NULL) { - if (fp) { - descr = CleanBOMString(bom->descr); - value = CleanBOMString(bom->value); - fprintf(fp, "%d,\"%s\",\"%s\",", bom->num, descr, value); - free(descr); - free(value); - } - - while (bom->refdes != NULL) { - if (fp) { - fprintf(fp, "%s ", bom->refdes->str); - } - free(bom->refdes->str); - lasts = bom->refdes; - bom->refdes = bom->refdes->next; - free(lasts); - } - if (fp) { - fprintf(fp, "\n"); - } - lastb = bom; - bom = bom->next; - free(lastb); - } -} - -static int PrintBOM(void) -{ - char utcTime[64]; - Coord x, y; - double theta = 0.0; - double sumx, sumy; - double pin1x = 0.0, pin1y = 0.0, pin1angle = 0.0; - double pin2x = 0.0, pin2y = 0.0; - int found_pin1; - int found_pin2; - int pin_cnt; - time_t currenttime; - FILE *fp; - BomList *bom = NULL; - char *name, *descr, *value; - - fp = fopen(xy_filename, "w"); - if (!fp) { - gui->log("Cannot open file %s for writing\n", xy_filename); - return 1; - } - - /* Create a portable timestamp. */ - currenttime = time(NULL); - { - /* avoid gcc complaints */ - const char *fmt = "%c UTC"; - strftime(utcTime, sizeof(utcTime), fmt, gmtime(¤ttime)); - } - fprintf(fp, "# $Id"); - fprintf(fp, "$\n"); - fprintf(fp, "# PcbXY Version 1.0\n"); - fprintf(fp, "# Date: %s\n", utcTime); - fprintf(fp, "# Author: %s\n", pcb_author()); - fprintf(fp, "# Title: %s - PCB X-Y\n", UNKNOWN(PCB->Name)); - fprintf(fp, "# RefDes, Description, Value, X, Y, rotation, top/bottom\n"); - fprintf(fp, "# X,Y in %s. rotation in degrees.\n", xy_unit->in_suffix); - fprintf(fp, "# --------------------------------------------\n"); - - /* - * For each element we calculate the centroid of the footprint. - * In addition, we need to extract some notion of rotation. - * While here generate the BOM list - */ - - ELEMENT_LOOP(PCB->Data); - { - - /* initialize our pin count and our totals for finding the - centriod */ - pin_cnt = 0; - sumx = 0.0; - sumy = 0.0; - found_pin1 = 0; - found_pin2 = 0; - - /* insert this component into the bill of materials list */ - bom = bom_insert((char *) UNKNOWN(NAMEONPCB_NAME(element)), - (char *) UNKNOWN(DESCRIPTION_NAME(element)), (char *) UNKNOWN(VALUE_NAME(element)), bom); - - - /* - * iterate over the pins and pads keeping a running count of how - * many pins/pads total and the sum of x and y coordinates - * - * While we're at it, store the location of pin/pad #1 and #2 if - * we can find them - */ - - PIN_LOOP(element); - { - sumx += (double) pin->X; - sumy += (double) pin->Y; - pin_cnt++; - - if (NSTRCMP(pin->Number, "1") == 0) { - pin1x = (double) pin->X; - pin1y = (double) pin->Y; - pin1angle = 0.0; /* pins have no notion of angle */ - found_pin1 = 1; - } - else if (NSTRCMP(pin->Number, "2") == 0) { - pin2x = (double) pin->X; - pin2y = (double) pin->Y; - found_pin2 = 1; - } - } - END_LOOP; - - PAD_LOOP(element); - { - sumx += (pad->Point1.X + pad->Point2.X) / 2.0; - sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0; - pin_cnt++; - - if (NSTRCMP(pad->Number, "1") == 0) { - pin1x = (double) (pad->Point1.X + pad->Point2.X) / 2.0; - pin1y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0; - /* - * NOTE: We swap the Y points because in PCB, the Y-axis - * is inverted. Increasing Y moves down. We want to deal - * in the usual increasing Y moves up coordinates though. - */ - pin1angle = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X); - found_pin1 = 1; - } - else if (NSTRCMP(pad->Number, "2") == 0) { - pin2x = (double) (pad->Point1.X + pad->Point2.X) / 2.0; - pin2y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0; - found_pin2 = 1; - } - - } - END_LOOP; - - if (pin_cnt > 0) { - x = sumx / (double) pin_cnt; - y = sumy / (double) pin_cnt; - - if (found_pin1) { - /* recenter pin #1 onto the axis which cross at the part - centroid */ - pin1x -= x; - pin1y -= y; - pin1y = -1.0 * pin1y; - - /* if only 1 pin, use pin 1's angle */ - if (pin_cnt == 1) - theta = pin1angle; - else { - /* if pin #1 is at (0,0) use pin #2 for rotation */ - if ((pin1x == 0.0) && (pin1y == 0.0)) { - if (found_pin2) - theta = xyToAngle(pin2x, pin2y); - else { - Message - ("PrintBOM(): unable to figure out angle of element\n" - " %s because pin #1 is at the centroid of the part.\n" - " and I could not find pin #2's location\n" - " Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta); - } - } - else - theta = xyToAngle(pin1x, pin1y); - } - } - /* we did not find pin #1 */ - else { - theta = 0.0; - Message - ("PrintBOM(): unable to figure out angle because I could\n" - " not find pin #1 of element %s\n" " Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta); - } - - name = CleanBOMString((char *) UNKNOWN(NAMEONPCB_NAME(element))); - descr = CleanBOMString((char *) UNKNOWN(DESCRIPTION_NAME(element))); - value = CleanBOMString((char *) UNKNOWN(VALUE_NAME(element))); - - y = PCB->MaxHeight - y; - pcb_fprintf(fp, "%m+%s,\"%s\",\"%s\",%mS,%.2mS,%g,%s\n", - xy_unit->allow, name, descr, value, x, y, theta, FRONT(element) == 1 ? "top" : "bottom"); - free(name); - free(descr); - free(value); - } - } - END_LOOP; - - fclose(fp); - - /* Now print out a Bill of Materials file */ - - fp = fopen(bom_filename, "w"); - if (!fp) { - gui->log("Cannot open file %s for writing\n", bom_filename); - print_and_free(NULL, bom); - return 1; - } - - fprintf(fp, "# $Id"); - fprintf(fp, "$\n"); - fprintf(fp, "# PcbBOM Version 1.0\n"); - fprintf(fp, "# Date: %s\n", utcTime); - fprintf(fp, "# Author: %s\n", pcb_author()); - fprintf(fp, "# Title: %s - PCB BOM\n", UNKNOWN(PCB->Name)); - fprintf(fp, "# Quantity, Description, Value, RefDes\n"); - fprintf(fp, "# --------------------------------------------\n"); - - print_and_free(fp, bom); - - fclose(fp); - - return (0); -} - -static void bom_do_export(HID_Attr_Val * options) -{ - int i; - - if (!options) { - bom_get_export_options(0); - for (i = 0; i < NUM_OPTIONS; i++) - bom_values[i] = bom_options[i].default_val; - options = bom_values; - } - - bom_filename = options[HA_bomfile].str_value; - if (!bom_filename) - bom_filename = "pcb-out.bom"; - - xy_filename = options[HA_xyfile].str_value; - if (!xy_filename) - xy_filename = "pcb-out.xy"; - - if (options[HA_unit].int_value == -1) - xy_unit = options[HA_xymm].int_value ? get_unit_struct("mm") - : get_unit_struct("mil"); - else - xy_unit = &get_unit_list()[options[HA_unit].int_value]; - PrintBOM(); -} - -static void bom_parse_arguments(int *argc, char ***argv) -{ - hid_register_attributes(bom_options, sizeof(bom_options) / sizeof(bom_options[0]), bom_cookie); - hid_parse_command_line(argc, argv); -} - -HID bom_hid; - -void hid_bom_init() -{ - memset(&bom_hid, 0, sizeof(HID)); - - common_nogui_init(&bom_hid); - - bom_hid.struct_size = sizeof(HID); - bom_hid.name = "bom"; - bom_hid.description = "Exports a Bill of Materials"; - bom_hid.exporter = 1; - - bom_hid.get_export_options = bom_get_export_options; - bom_hid.do_export = bom_do_export; - bom_hid.parse_arguments = bom_parse_arguments; - - hid_register_hid(&bom_hid); -} Index: src_plugins/export_bom/Makefile =================================================================== --- src_plugins/export_bom/Makefile (nonexistent) +++ src_plugins/export_bom/Makefile (revision 1270) @@ -0,0 +1,5 @@ +all: + cd ../../src && make mod_export_bom + +clean: + rm *.o *.so 2>/dev/null ; true Index: src_plugins/export_bom/Plug.tmpasm =================================================================== --- src_plugins/export_bom/Plug.tmpasm (nonexistent) +++ src_plugins/export_bom/Plug.tmpasm (revision 1270) @@ -0,0 +1,17 @@ +append /local/pcb/export_bom/enable {} +append /local/pcb/export_bom/buildin {} + +put /local/pcb/mod {export_bom} +put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_bom/bom.o @] + +if /local/pcb/export_bom/enable then + append /local/pcb/LIBS libs/gui/gd/ldflags + append /local/pcb/CFLAGS libs/gui/gd/cflags + if /local/pcb/export_bom/buildin then + include {Makefile.in.mod/Buildin} + else + include {Makefile.in.mod/Plugin} + end +else + include {Makefile.in.mod/Disable} +end Index: src_plugins/export_bom/README =================================================================== --- src_plugins/export_bom/README (nonexistent) +++ src_plugins/export_bom/README (revision 1270) @@ -0,0 +1,4 @@ +Export bom (Bill of Materials) + +#state: works +#default: buildin Index: src_plugins/export_bom/bom.c =================================================================== --- src_plugins/export_bom/bom.c (nonexistent) +++ src_plugins/export_bom/bom.c (revision 1270) @@ -0,0 +1,527 @@ +/* $Id$ */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "global.h" +#include "data.h" +#include "error.h" +#include "misc.h" +#include "pcb-printf.h" +#include "plugins.h" + +#include "hid.h" +#include "hid/common/hidnogui.h" +#include "../hidint.h" + + +RCSID("$Id$"); + +const char *bom_cookie = "bom HID"; + +static HID_Attribute bom_options[] = { +/* %start-doc options "8 BOM Creation" +@ftable @code +@item --bomfile +Name of the BOM output file. +@end ftable +%end-doc +*/ + {"bomfile", "Name of the BOM output file", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_bomfile 0 +/* %start-doc options "8 BOM Creation" +@ftable @code +@item --xyfile +Name of the XY output file. +@end ftable +%end-doc +*/ + {"xyfile", "Name of the XY output file", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_xyfile 1 + +/* %start-doc options "8 BOM Creation" +@ftable @code +@item --xy-unit +Unit of XY dimensions. Defaults to mil. +@end ftable +%end-doc +*/ + {"xy-unit", "XY units", + HID_Unit, 0, 0, {-1, 0, 0}, NULL, 0}, +#define HA_unit 2 + {"xy-in-mm", ATTR_UNDOCUMENTED, + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_xymm 3 +}; + +#define NUM_OPTIONS (sizeof(bom_options)/sizeof(bom_options[0])) + +static HID_Attr_Val bom_values[NUM_OPTIONS]; + +static const char *bom_filename; +static const char *xy_filename; +static const Unit *xy_unit; + +typedef struct _StringList { + char *str; + struct _StringList *next; +} StringList; + +typedef struct _BomList { + char *descr; + char *value; + int num; + StringList *refdes; + struct _BomList *next; +} BomList; + +static HID_Attribute *bom_get_export_options(int *n) +{ + static char *last_bom_filename = 0; + static char *last_xy_filename = 0; + static int last_unit_value = -1; + + if (bom_options[HA_unit].default_val.int_value == last_unit_value) { + if (Settings.grid_unit) + bom_options[HA_unit].default_val.int_value = Settings.grid_unit->index; + else + bom_options[HA_unit].default_val.int_value = get_unit_struct("mil")->index; + last_unit_value = bom_options[HA_unit].default_val.int_value; + } + if (PCB) { + derive_default_filename(PCB->Filename, &bom_options[HA_bomfile], ".bom", &last_bom_filename); + derive_default_filename(PCB->Filename, &bom_options[HA_xyfile], ".xy", &last_xy_filename); + } + + if (n) + *n = NUM_OPTIONS; + return bom_options; +} + +/* --------------------------------------------------------------------------- + * prints a centroid file in a format which includes data needed by a + * pick and place machine. Further formatting for a particular factory setup + * can easily be generated with awk or perl. In addition, a bill of materials + * file is generated which can be used for checking stock and purchasing needed + * materials. + * returns != zero on error + */ + + +static char *CleanBOMString(char *in) +{ + char *out; + int i; + + if ((out = (char *) malloc((strlen(in) + 1) * sizeof(char))) == NULL) { + fprintf(stderr, "Error: CleanBOMString() malloc() failed\n"); + exit(1); + } + + /* + * copy over in to out with some character conversions. + * Go all the way to then end to get the terminating \0 + */ + for (i = 0; i <= strlen(in); i++) { + switch (in[i]) { + case '"': + out[i] = '\''; + break; + default: + out[i] = in[i]; + } + } + + return out; +} + + +static double xyToAngle(double x, double y) +{ + double theta; + + if ((x > 0.0) && (y >= 0.0)) + theta = 180.0; + else if ((x <= 0.0) && (y > 0.0)) + theta = 90.0; + else if ((x < 0.0) && (y <= 0.0)) + theta = 0.0; + else if ((x >= 0.0) && (y < 0.0)) + theta = 270.0; + else { + theta = 0.0; + Message("xyToAngle(): unable to figure out angle of element\n" + " because the pin is at the centroid of the part.\n" + " This is a BUG!!!\n" " Setting to %g degrees\n", theta); + } + + return (theta); +} + +static StringList *string_insert(char *str, StringList * list) +{ + StringList *newlist, *cur; + + if ((newlist = (StringList *) malloc(sizeof(StringList))) == NULL) { + fprintf(stderr, "malloc() failed in string_insert()\n"); + exit(1); + } + + newlist->next = NULL; + newlist->str = strdup(str); + + if (list == NULL) + return (newlist); + + cur = list; + while (cur->next != NULL) + cur = cur->next; + + cur->next = newlist; + + return (list); +} + +static BomList *bom_insert(char *refdes, char *descr, char *value, BomList * bom) +{ + BomList *newlist, *cur, *prev = NULL; + + if (bom == NULL) { + /* this is the first element so automatically create an entry */ + if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) { + fprintf(stderr, "malloc() failed in bom_insert()\n"); + exit(1); + } + + newlist->next = NULL; + newlist->descr = strdup(descr); + newlist->value = strdup(value); + newlist->num = 1; + newlist->refdes = string_insert(refdes, NULL); + return (newlist); + } + + /* search and see if we already have used one of these + components */ + cur = bom; + while (cur != NULL) { + if ((NSTRCMP(descr, cur->descr) == 0) && (NSTRCMP(value, cur->value) == 0)) { + cur->num++; + cur->refdes = string_insert(refdes, cur->refdes); + break; + } + prev = cur; + cur = cur->next; + } + + if (cur == NULL) { + if ((newlist = (BomList *) malloc(sizeof(BomList))) == NULL) { + fprintf(stderr, "malloc() failed in bom_insert()\n"); + exit(1); + } + + prev->next = newlist; + + newlist->next = NULL; + newlist->descr = strdup(descr); + newlist->value = strdup(value); + newlist->num = 1; + newlist->refdes = string_insert(refdes, NULL); + } + + return (bom); + +} + +/* + * If fp is not NULL then print out the bill of materials contained in + * bom. Either way, free all memory which has been allocated for bom. + */ +static void print_and_free(FILE * fp, BomList * bom) +{ + BomList *lastb; + StringList *lasts; + char *descr, *value; + + while (bom != NULL) { + if (fp) { + descr = CleanBOMString(bom->descr); + value = CleanBOMString(bom->value); + fprintf(fp, "%d,\"%s\",\"%s\",", bom->num, descr, value); + free(descr); + free(value); + } + + while (bom->refdes != NULL) { + if (fp) { + fprintf(fp, "%s ", bom->refdes->str); + } + free(bom->refdes->str); + lasts = bom->refdes; + bom->refdes = bom->refdes->next; + free(lasts); + } + if (fp) { + fprintf(fp, "\n"); + } + lastb = bom; + bom = bom->next; + free(lastb); + } +} + +static int PrintBOM(void) +{ + char utcTime[64]; + Coord x, y; + double theta = 0.0; + double sumx, sumy; + double pin1x = 0.0, pin1y = 0.0, pin1angle = 0.0; + double pin2x = 0.0, pin2y = 0.0; + int found_pin1; + int found_pin2; + int pin_cnt; + time_t currenttime; + FILE *fp; + BomList *bom = NULL; + char *name, *descr, *value; + + fp = fopen(xy_filename, "w"); + if (!fp) { + gui->log("Cannot open file %s for writing\n", xy_filename); + return 1; + } + + /* Create a portable timestamp. */ + currenttime = time(NULL); + { + /* avoid gcc complaints */ + const char *fmt = "%c UTC"; + strftime(utcTime, sizeof(utcTime), fmt, gmtime(¤ttime)); + } + fprintf(fp, "# $Id"); + fprintf(fp, "$\n"); + fprintf(fp, "# PcbXY Version 1.0\n"); + fprintf(fp, "# Date: %s\n", utcTime); + fprintf(fp, "# Author: %s\n", pcb_author()); + fprintf(fp, "# Title: %s - PCB X-Y\n", UNKNOWN(PCB->Name)); + fprintf(fp, "# RefDes, Description, Value, X, Y, rotation, top/bottom\n"); + fprintf(fp, "# X,Y in %s. rotation in degrees.\n", xy_unit->in_suffix); + fprintf(fp, "# --------------------------------------------\n"); + + /* + * For each element we calculate the centroid of the footprint. + * In addition, we need to extract some notion of rotation. + * While here generate the BOM list + */ + + ELEMENT_LOOP(PCB->Data); + { + + /* initialize our pin count and our totals for finding the + centriod */ + pin_cnt = 0; + sumx = 0.0; + sumy = 0.0; + found_pin1 = 0; + found_pin2 = 0; + + /* insert this component into the bill of materials list */ + bom = bom_insert((char *) UNKNOWN(NAMEONPCB_NAME(element)), + (char *) UNKNOWN(DESCRIPTION_NAME(element)), (char *) UNKNOWN(VALUE_NAME(element)), bom); + + + /* + * iterate over the pins and pads keeping a running count of how + * many pins/pads total and the sum of x and y coordinates + * + * While we're at it, store the location of pin/pad #1 and #2 if + * we can find them + */ + + PIN_LOOP(element); + { + sumx += (double) pin->X; + sumy += (double) pin->Y; + pin_cnt++; + + if (NSTRCMP(pin->Number, "1") == 0) { + pin1x = (double) pin->X; + pin1y = (double) pin->Y; + pin1angle = 0.0; /* pins have no notion of angle */ + found_pin1 = 1; + } + else if (NSTRCMP(pin->Number, "2") == 0) { + pin2x = (double) pin->X; + pin2y = (double) pin->Y; + found_pin2 = 1; + } + } + END_LOOP; + + PAD_LOOP(element); + { + sumx += (pad->Point1.X + pad->Point2.X) / 2.0; + sumy += (pad->Point1.Y + pad->Point2.Y) / 2.0; + pin_cnt++; + + if (NSTRCMP(pad->Number, "1") == 0) { + pin1x = (double) (pad->Point1.X + pad->Point2.X) / 2.0; + pin1y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0; + /* + * NOTE: We swap the Y points because in PCB, the Y-axis + * is inverted. Increasing Y moves down. We want to deal + * in the usual increasing Y moves up coordinates though. + */ + pin1angle = (180.0 / M_PI) * atan2(pad->Point1.Y - pad->Point2.Y, pad->Point2.X - pad->Point1.X); + found_pin1 = 1; + } + else if (NSTRCMP(pad->Number, "2") == 0) { + pin2x = (double) (pad->Point1.X + pad->Point2.X) / 2.0; + pin2y = (double) (pad->Point1.Y + pad->Point2.Y) / 2.0; + found_pin2 = 1; + } + + } + END_LOOP; + + if (pin_cnt > 0) { + x = sumx / (double) pin_cnt; + y = sumy / (double) pin_cnt; + + if (found_pin1) { + /* recenter pin #1 onto the axis which cross at the part + centroid */ + pin1x -= x; + pin1y -= y; + pin1y = -1.0 * pin1y; + + /* if only 1 pin, use pin 1's angle */ + if (pin_cnt == 1) + theta = pin1angle; + else { + /* if pin #1 is at (0,0) use pin #2 for rotation */ + if ((pin1x == 0.0) && (pin1y == 0.0)) { + if (found_pin2) + theta = xyToAngle(pin2x, pin2y); + else { + Message + ("PrintBOM(): unable to figure out angle of element\n" + " %s because pin #1 is at the centroid of the part.\n" + " and I could not find pin #2's location\n" + " Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta); + } + } + else + theta = xyToAngle(pin1x, pin1y); + } + } + /* we did not find pin #1 */ + else { + theta = 0.0; + Message + ("PrintBOM(): unable to figure out angle because I could\n" + " not find pin #1 of element %s\n" " Setting to %g degrees\n", UNKNOWN(NAMEONPCB_NAME(element)), theta); + } + + name = CleanBOMString((char *) UNKNOWN(NAMEONPCB_NAME(element))); + descr = CleanBOMString((char *) UNKNOWN(DESCRIPTION_NAME(element))); + value = CleanBOMString((char *) UNKNOWN(VALUE_NAME(element))); + + y = PCB->MaxHeight - y; + pcb_fprintf(fp, "%m+%s,\"%s\",\"%s\",%mS,%.2mS,%g,%s\n", + xy_unit->allow, name, descr, value, x, y, theta, FRONT(element) == 1 ? "top" : "bottom"); + free(name); + free(descr); + free(value); + } + } + END_LOOP; + + fclose(fp); + + /* Now print out a Bill of Materials file */ + + fp = fopen(bom_filename, "w"); + if (!fp) { + gui->log("Cannot open file %s for writing\n", bom_filename); + print_and_free(NULL, bom); + return 1; + } + + fprintf(fp, "# $Id"); + fprintf(fp, "$\n"); + fprintf(fp, "# PcbBOM Version 1.0\n"); + fprintf(fp, "# Date: %s\n", utcTime); + fprintf(fp, "# Author: %s\n", pcb_author()); + fprintf(fp, "# Title: %s - PCB BOM\n", UNKNOWN(PCB->Name)); + fprintf(fp, "# Quantity, Description, Value, RefDes\n"); + fprintf(fp, "# --------------------------------------------\n"); + + print_and_free(fp, bom); + + fclose(fp); + + return (0); +} + +static void bom_do_export(HID_Attr_Val * options) +{ + int i; + + if (!options) { + bom_get_export_options(0); + for (i = 0; i < NUM_OPTIONS; i++) + bom_values[i] = bom_options[i].default_val; + options = bom_values; + } + + bom_filename = options[HA_bomfile].str_value; + if (!bom_filename) + bom_filename = "pcb-out.bom"; + + xy_filename = options[HA_xyfile].str_value; + if (!xy_filename) + xy_filename = "pcb-out.xy"; + + if (options[HA_unit].int_value == -1) + xy_unit = options[HA_xymm].int_value ? get_unit_struct("mm") + : get_unit_struct("mil"); + else + xy_unit = &get_unit_list()[options[HA_unit].int_value]; + PrintBOM(); +} + +static void bom_parse_arguments(int *argc, char ***argv) +{ + hid_register_attributes(bom_options, sizeof(bom_options) / sizeof(bom_options[0]), bom_cookie); + hid_parse_command_line(argc, argv); +} + +HID bom_hid; + +pcb_uninit_t hid_export_bom_init() +{ + memset(&bom_hid, 0, sizeof(HID)); + + common_nogui_init(&bom_hid); + + bom_hid.struct_size = sizeof(HID); + bom_hid.name = "bom"; + bom_hid.description = "Exports a Bill of Materials"; + bom_hid.exporter = 1; + + bom_hid.get_export_options = bom_get_export_options; + bom_hid.do_export = bom_do_export; + bom_hid.parse_arguments = bom_parse_arguments; + + hid_register_hid(&bom_hid); + return NULL; +} Index: src_plugins/export_gerber/gerber.c =================================================================== --- src_plugins/export_gerber/gerber.c (nonexistent) +++ src_plugins/export_gerber/gerber.c (revision 1270) @@ -0,0 +1,1206 @@ +/* $Id$ */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PWD_H +#include +#endif + +#include + +#include "config.h" +#include "global.h" +#include "data.h" +#include "misc.h" +#include "error.h" +#include "draw.h" +#include "pcb-printf.h" +#include "plugins.h" + +#include "hid.h" +#include "../hidint.h" +#include "hid/common/hidnogui.h" +#include "hid/common/draw_helpers.h" +#include "hid/common/hidinit.h" + + +RCSID("$Id$"); + +const char *gerber_cookie = "gerber HID"; + +#define CRASH fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", __FUNCTION__); abort() + +/*----------------------------------------------------------------------------*/ +/* Function prototypes */ +/*----------------------------------------------------------------------------*/ + +static HID_Attribute *gerber_get_export_options(int *n); +static void gerber_do_export(HID_Attr_Val * options); +static void gerber_parse_arguments(int *argc, char ***argv); +static int gerber_set_layer(const char *name, int group, int empty); +static hidGC gerber_make_gc(void); +static void gerber_destroy_gc(hidGC gc); +static void gerber_use_mask(int use_it); +static void gerber_set_color(hidGC gc, const char *name); +static void gerber_set_line_cap(hidGC gc, EndCapStyle style); +static void gerber_set_line_width(hidGC gc, Coord width); +static void gerber_set_draw_xor(hidGC gc, int _xor); +static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); +static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle); +static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); +static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius); +static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2); +static void gerber_calibrate(double xval, double yval); +static void gerber_set_crosshair(int x, int y, int action); +static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y); + +/*----------------------------------------------------------------------------*/ +/* Utility routines */ +/*----------------------------------------------------------------------------*/ + +/* These are for films */ +#define gerberX(pcb, x) ((Coord) (x)) +#define gerberY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y))) +#define gerberXOffset(pcb, x) ((Coord) (x)) +#define gerberYOffset(pcb, y) ((Coord) (-(y))) + +/* These are for drills (printed as mils but are really 1/10th mil) */ +#define gerberDrX(pcb, x) ((Coord) (x) * 10) +#define gerberDrY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)) * 10) + +/*----------------------------------------------------------------------------*/ +/* Private data structures */ +/*----------------------------------------------------------------------------*/ + +static int verbose; +static int all_layers; +static int is_mask, was_drill; +static int is_drill; +static int current_mask; +static int flash_drills; +static int copy_outline_mode; +static int name_style; +static LayerTypePtr outline_layer; + +enum ApertureShape { + ROUND, /* Shaped like a circle */ + OCTAGON, /* octagonal shape */ + SQUARE, /* Shaped like a square */ + ROUNDCLEAR, /* clearance in negatives */ + SQUARECLEAR, + THERMAL /* negative thermal relief */ +}; +typedef enum ApertureShape ApertureShape; + +/* This is added to the global aperture array indexes to get gerber + dcode and macro numbers. */ +#define DCODE_BASE 11 + +typedef struct aperture { + int dCode; /* The RS-274X D code */ + Coord width; /* Size in pcb units */ + ApertureShape shape; /* ROUND/SQUARE etc */ + struct aperture *next; +} Aperture; + +typedef struct { + Aperture *data; + int count; +} ApertureList; + +static ApertureList *layer_aptr_list; +static ApertureList *curr_aptr_list; +static int layer_list_max; +static int layer_list_idx; + +typedef struct { + Coord diam; + Coord x; + Coord y; +} PendingDrills; +PendingDrills *pending_drills = NULL; +int n_pending_drills = 0, max_pending_drills = 0; + +/*----------------------------------------------------------------------------*/ +/* Defined Constants */ +/*----------------------------------------------------------------------------*/ +#define AUTO_OUTLINE_WIDTH MIL_TO_COORD(8) /* Auto-geneated outline width of 8 mils */ + +/*----------------------------------------------------------------------------*/ +/* Aperture Routines */ +/*----------------------------------------------------------------------------*/ + +/* Initialize aperture list */ +static void initApertureList(ApertureList * list) +{ + list->data = NULL; + list->count = 0; +} + +static void deinitApertureList(ApertureList * list) +{ + Aperture *search = list->data; + Aperture *next; + while (search) { + next = search->next; + free(search); + search = next; + } + initApertureList(list); +} + +static void resetApertures() +{ + int i; + for (i = 0; i < layer_list_max; ++i) + deinitApertureList(&layer_aptr_list[i]); + free(layer_aptr_list); + layer_aptr_list = NULL; + curr_aptr_list = NULL; + layer_list_max = 0; + layer_list_idx = 0; +} + +/* Create and add a new aperture to the list */ +static Aperture *addAperture(ApertureList * list, Coord width, ApertureShape shape) +{ + static int aperture_count; + + Aperture *app = (Aperture *) malloc(sizeof *app); + if (app == NULL) + return NULL; + + app->width = width; + app->shape = shape; + app->dCode = DCODE_BASE + aperture_count++; + app->next = list->data; + + list->data = app; + ++list->count; + + return app; +} + +/* Fetch an aperture from the list with the specified + * width/shape, creating a new one if none exists */ +static Aperture *findAperture(ApertureList * list, Coord width, ApertureShape shape) +{ + Aperture *search; + + /* we never draw zero-width lines */ + if (width == 0) + return NULL; + + /* Search for an appropriate aperture. */ + for (search = list->data; search; search = search->next) + if (search->width == width && search->shape == shape) + return search; + + /* Failing that, create a new one */ + return addAperture(list, width, shape); +} + +/* Output aperture data to the file */ +static void fprintAperture(FILE * f, Aperture * aptr) +{ + switch (aptr->shape) { + case ROUND: + pcb_fprintf(f, "%%ADD%dC,%.4mi*%%\r\n", aptr->dCode, aptr->width); + break; + case SQUARE: + pcb_fprintf(f, "%%ADD%dR,%.4miX%.4mi*%%\r\n", aptr->dCode, aptr->width, aptr->width); + break; + case OCTAGON: + pcb_fprintf(f, "%%AMOCT%d*5,0,8,0,0,%.4mi,22.5*%%\r\n" + "%%ADD%dOCT%d*%%\r\n", aptr->dCode, (Coord) ((double) aptr->width / COS_22_5_DEGREE), aptr->dCode, aptr->dCode); + break; +#if 0 + case THERMAL: + fprintf(f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\r\n" + "%%ADD%dTHERM%d*%%\r\n", dCode, gap / 100000.0, width / 100000.0, finger / 100000.0, dCode, dCode); + break; + case ROUNDCLEAR: + fprintf(f, "%%ADD%dC,%.4fX%.4f*%%\r\n", dCode, gap / 100000.0, width / 100000.0); + break; + case SQUARECLEAR: + fprintf(f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\r\n", + dCode, gap / 100000.0, gap / 100000.0, width / 100000.0, width / 100000.0); + break; +#else + default: + break; +#endif + } +} + +/* Set the aperture list for the current layer, + * expanding the list buffer if needed */ +static ApertureList *setLayerApertureList(int layer_idx) +{ + if (layer_idx >= layer_list_max) { + int i = layer_list_max; + layer_list_max = 2 * (layer_idx + 1); + layer_aptr_list = (ApertureList *) + realloc(layer_aptr_list, layer_list_max * sizeof(*layer_aptr_list)); + for (; i < layer_list_max; ++i) + initApertureList(&layer_aptr_list[i]); + } + curr_aptr_list = &layer_aptr_list[layer_idx]; + return curr_aptr_list; +} + +/* --------------------------------------------------------------------------- */ + +static HID gerber_hid; + +typedef struct hid_gc_struct { + EndCapStyle cap; + int width; + int color; + int erase; + int drill; +} hid_gc_struct; + +static FILE *f = NULL; +static char *filename = NULL; +static char *filesuff = NULL; +static char *layername = NULL; +static int lncount = 0; + +static int finding_apertures = 0; +static int pagecount = 0; +static int linewidth = -1; +static int lastgroup = -1; +static int lastcap = -1; +static int lastcolor = -1; +static int print_group[MAX_LAYER]; +static int print_layer[MAX_LAYER]; +static int lastX, lastY; /* the last X and Y coordinate */ + +static const char *copy_outline_names[] = { +#define COPY_OUTLINE_NONE 0 + "none", +#define COPY_OUTLINE_MASK 1 + "mask", +#define COPY_OUTLINE_SILK 2 + "silk", +#define COPY_OUTLINE_ALL 3 + "all", + NULL +}; + +static const char *name_style_names[] = { +#define NAME_STYLE_FIXED 0 + "fixed", +#define NAME_STYLE_SINGLE 1 + "single", +#define NAME_STYLE_FIRST 2 + "first", +#define NAME_STYLE_EAGLE 3 + "eagle", + NULL +}; + +static HID_Attribute gerber_options[] = { + +/* %start-doc options "90 Gerber Export" +@ftable @code +@item --gerberfile +Gerber output file prefix. Can include a path. +@end ftable +%end-doc +*/ + {"gerberfile", "Gerber output file base", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_gerberfile 0 + +/* %start-doc options "90 Gerber Export" +@ftable @code +@item --all-layers +Output contains all layers, even empty ones. +@end ftable +%end-doc +*/ + {"all-layers", "Output all layers, even empty ones", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_all_layers 1 + +/* %start-doc options "90 Gerber Export" +@ftable @code +@item --verbose +Print file names and aperture counts on stdout. +@end ftable +%end-doc +*/ + {"verbose", "Print file names and aperture counts on stdout", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_verbose 2 + {"copy-outline", "Copy outline onto other layers", + HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0}, +#define HA_copy_outline 3 + {"name-style", "Naming style for individual gerber files", + HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0}, +#define HA_name_style 4 +}; + +#define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0])) + +static HID_Attr_Val gerber_values[NUM_OPTIONS]; + +static HID_Attribute *gerber_get_export_options(int *n) +{ + static char *last_made_filename = NULL; + if (PCB) + derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename); + + if (n) + *n = NUM_OPTIONS; + return gerber_options; +} + +static int group_for_layer(int l) +{ + if (l < max_copper_layer + 2 && l >= 0) + return GetLayerGroupNumberByNumber(l); + /* else something unique */ + return max_group + 3 + l; +} + +static int layer_sort(const void *va, const void *vb) +{ + int a = *(int *) va; + int b = *(int *) vb; + int d = group_for_layer(b) - group_for_layer(a); + if (d) + return d; + return b - a; +} + +static void maybe_close_f(FILE * f) +{ + if (f) { + if (was_drill) + fprintf(f, "M30\r\n"); + else + fprintf(f, "M02*\r\n"); + fclose(f); + } +} + +static BoxType region; + +/* Very similar to layer_type_to_file_name() but appends only a + three-character suffix compatible with Eagle's defaults. */ +static void assign_eagle_file_suffix(char *dest, int idx) +{ + int group; + int nlayers; + char *suff = "out"; + + switch (idx) { + case SL(SILK, TOP): + suff = "plc"; + break; + case SL(SILK, BOTTOM): + suff = "pls"; + break; + case SL(MASK, TOP): + suff = "stc"; + break; + case SL(MASK, BOTTOM): + suff = "sts"; + break; + case SL(PDRILL, 0): + suff = "drd"; + break; + case SL(UDRILL, 0): + suff = "dru"; + break; + case SL(PASTE, TOP): + suff = "crc"; + break; + case SL(PASTE, BOTTOM): + suff = "crs"; + break; + case SL(INVISIBLE, 0): + suff = "inv"; + break; + case SL(FAB, 0): + suff = "fab"; + break; + case SL(ASSY, TOP): + suff = "ast"; + break; + case SL(ASSY, BOTTOM): + suff = "asb"; + break; + + default: + group = GetLayerGroupNumberByNumber(idx); + nlayers = PCB->LayerGroups.Number[group]; + if (group == GetLayerGroupNumberByNumber(component_silk_layer)) { + suff = "cmp"; + } + else if (group == GetLayerGroupNumberByNumber(solder_silk_layer)) { + suff = "sol"; + } + else if (nlayers == 1 + && (strcmp(PCB->Data->Layer[idx].Name, "route") == 0 || strcmp(PCB->Data->Layer[idx].Name, "outline") == 0)) { + suff = "oln"; + } + else { + static char buf[20]; + sprintf(buf, "ly%d", group); + suff = buf; + } + break; + } + + strcpy(dest, suff); +} + +static void assign_file_suffix(char *dest, int idx) +{ + int fns_style; + const char *sext = ".gbr"; + + switch (name_style) { + default: + case NAME_STYLE_FIXED: + fns_style = FNS_fixed; + break; + case NAME_STYLE_SINGLE: + fns_style = FNS_single; + break; + case NAME_STYLE_FIRST: + fns_style = FNS_first; + break; + case NAME_STYLE_EAGLE: + assign_eagle_file_suffix(dest, idx); + return; + } + + switch (idx) { + case SL(PDRILL, 0): + sext = ".cnc"; + break; + case SL(UDRILL, 0): + sext = ".cnc"; + break; + } + + strcpy(dest, layer_type_to_file_name(idx, fns_style)); + strcat(dest, sext); +} + +static void gerber_do_export(HID_Attr_Val * options) +{ + const char *fnbase; + int i; + static int saved_layer_stack[MAX_LAYER]; + int save_ons[MAX_LAYER + 2]; + FlagType save_thindraw; + + save_thindraw = PCB->Flags; + CLEAR_FLAG(THINDRAWFLAG, PCB); + CLEAR_FLAG(THINDRAWPOLYFLAG, PCB); + CLEAR_FLAG(CHECKPLANESFLAG, PCB); + + if (!options) { + gerber_get_export_options(NULL); + for (i = 0; i < NUM_OPTIONS; i++) + gerber_values[i] = gerber_options[i].default_val; + options = gerber_values; + } + + fnbase = options[HA_gerberfile].str_value; + if (!fnbase) + fnbase = "pcb-out"; + + verbose = options[HA_verbose].int_value; + all_layers = options[HA_all_layers].int_value; + + copy_outline_mode = options[HA_copy_outline].int_value; + name_style = options[HA_name_style].int_value; + + outline_layer = NULL; + + for (i = 0; i < max_copper_layer; i++) { + LayerType *layer = PCB->Data->Layer + i; + if (strcmp(layer->Name, "outline") == 0 || strcmp(layer->Name, "route") == 0) { + outline_layer = layer; + } + } + + i = strlen(fnbase); + filename = (char *) realloc(filename, i + 40); + strcpy(filename, fnbase); + strcat(filename, "."); + filesuff = filename + strlen(filename); + + if (all_layers) { + memset(print_group, 1, sizeof(print_group)); + memset(print_layer, 1, sizeof(print_layer)); + } + else { + memset(print_group, 0, sizeof(print_group)); + memset(print_layer, 0, sizeof(print_layer)); + } + + hid_save_and_show_layer_ons(save_ons); + for (i = 0; i < max_copper_layer; i++) { + LayerType *layer = PCB->Data->Layer + i; + if (!LAYER_IS_EMPTY(layer)) + print_group[GetLayerGroupNumberByNumber(i)] = 1; + } + print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1; + print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1; + for (i = 0; i < max_copper_layer; i++) + if (print_group[GetLayerGroupNumberByNumber(i)]) + print_layer[i] = 1; + + memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack)); + qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort); + linewidth = -1; + lastcap = -1; + lastgroup = -1; + lastcolor = -1; + + region.X1 = 0; + region.Y1 = 0; + region.X2 = PCB->MaxWidth; + region.Y2 = PCB->MaxHeight; + + pagecount = 1; + resetApertures(); + + lastgroup = -1; + layer_list_idx = 0; + finding_apertures = 1; + hid_expose_callback(&gerber_hid, ®ion, 0); + + layer_list_idx = 0; + finding_apertures = 0; + hid_expose_callback(&gerber_hid, ®ion, 0); + + memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack)); + + maybe_close_f(f); + f = NULL; + hid_restore_layer_ons(save_ons); + PCB->Flags = save_thindraw; +} + +static void gerber_parse_arguments(int *argc, char ***argv) +{ + hid_register_attributes(gerber_options, NUM_OPTIONS, gerber_cookie); + hid_parse_command_line(argc, argv); +} + +static int drill_sort(const void *va, const void *vb) +{ + PendingDrills *a = (PendingDrills *) va; + PendingDrills *b = (PendingDrills *) vb; + if (a->diam != b->diam) + return a->diam - b->diam; + if (a->x != b->x) + return a->x - a->x; + return b->y - b->y; +} + +static int gerber_set_layer(const char *name, int group, int empty) +{ + int want_outline; + char *cp; + int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; + + if (name == NULL) + name = PCB->Data->Layer[idx].Name; + + if (idx >= 0 && idx < max_copper_layer && !print_layer[idx]) + return 0; + + if (strcmp(name, "invisible") == 0) + return 0; + if (SL_TYPE(idx) == SL_ASSY) + return 0; + + flash_drills = 0; + if (strcmp(name, "outline") == 0 || strcmp(name, "route") == 0) + flash_drills = 1; + + if (is_drill && n_pending_drills) { + int i; + /* dump pending drills in sequence */ + qsort(pending_drills, n_pending_drills, sizeof(pending_drills[0]), drill_sort); + for (i = 0; i < n_pending_drills; i++) { + if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam) { + Aperture *ap = findAperture(curr_aptr_list, pending_drills[i].diam, ROUND); + fprintf(f, "T%02d\r\n", ap->dCode); + } + pcb_fprintf(f, "X%06.0mlY%06.0ml\r\n", gerberDrX(PCB, pending_drills[i].x), gerberDrY(PCB, pending_drills[i].y)); + } + free(pending_drills); + n_pending_drills = max_pending_drills = 0; + pending_drills = NULL; + } + + is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); + is_mask = (SL_TYPE(idx) == SL_MASK); + current_mask = 0; +#if 0 + printf("Layer %s group %d drill %d mask %d\n", name, group, is_drill, is_mask); +#endif + + if (group < 0 || group != lastgroup) { + time_t currenttime; + char utcTime[64]; +#ifdef HAVE_GETPWUID + struct passwd *pwentry; +#endif + ApertureList *aptr_list; + Aperture *search; + + lastgroup = group; + lastX = -1; + lastY = -1; + lastcolor = 0; + linewidth = -1; + lastcap = -1; + + aptr_list = setLayerApertureList(layer_list_idx++); + + if (finding_apertures) + goto emit_outline; + + if (aptr_list->count == 0 && !all_layers) + return 0; + + maybe_close_f(f); + f = NULL; + + pagecount++; + assign_file_suffix(filesuff, idx); + f = fopen(filename, "wb"); /* Binary needed to force CR-LF */ + if (f == NULL) { + Message("Error: Could not open %s for writing.\n", filename); + return 1; + } + + was_drill = is_drill; + + if (verbose) { + int c = aptr_list->count; + printf("Gerber: %d aperture%s in %s\n", c, c == 1 ? "" : "s", filename); + } + + if (is_drill) { + /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is + always six-digit 0.1 mil resolution (i.e. 001100 = 0.11") */ + fprintf(f, "M48\r\n" "INCH\r\n"); + for (search = aptr_list->data; search; search = search->next) + pcb_fprintf(f, "T%02dC%.3mi\r\n", search->dCode, search->width); + fprintf(f, "%%\r\n"); + /* FIXME */ + return 1; + } + + fprintf(f, "G04 start of page %d for group %d idx %d *\r\n", pagecount, group, idx); + + /* Create a portable timestamp. */ + currenttime = time(NULL); + { + /* avoid gcc complaints */ + const char *fmt = "%c UTC"; + strftime(utcTime, sizeof utcTime, fmt, gmtime(¤ttime)); + } + /* Print a cute file header at the beginning of each file. */ + fprintf(f, "G04 Title: %s, %s *\r\n", UNKNOWN(PCB->Name), UNKNOWN(name)); + fprintf(f, "G04 Creator: %s " VERSION " *\r\n", Progname); + fprintf(f, "G04 CreationDate: %s *\r\n", utcTime); + +#ifdef HAVE_GETPWUID + /* ID the user. */ + pwentry = getpwuid(getuid()); + fprintf(f, "G04 For: %s *\r\n", pwentry->pw_name); +#endif + + fprintf(f, "G04 Format: Gerber/RS-274X *\r\n"); + pcb_fprintf(f, "G04 PCB-Dimensions: %.0mc %.0mc *\r\n", PCB->MaxWidth, PCB->MaxHeight); + fprintf(f, "G04 PCB-Coordinate-Origin: lower left *\r\n"); + + /* Signal data in inches. */ + fprintf(f, "%%MOIN*%%\r\n"); + + /* Signal Leading zero suppression, Absolute Data, 2.5 format */ + fprintf(f, "%%FSLAX25Y25*%%\r\n"); + + /* build a legal identifier. */ + if (layername) + free(layername); + layername = strdup(filesuff); + if (strrchr(layername, '.')) + *strrchr(layername, '.') = 0; + + for (cp = layername; *cp; cp++) { + if (isalnum((int) *cp)) + *cp = toupper((int) *cp); + else + *cp = '_'; + } + fprintf(f, "%%LN%s*%%\r\n", layername); + lncount = 1; + + for (search = aptr_list->data; search; search = search->next) + fprintAperture(f, search); + if (aptr_list->count == 0) + /* We need to put *something* in the file to make it be parsed + as RS-274X instead of RS-274D. */ + fprintf(f, "%%ADD11C,0.0100*%%\r\n"); + } + +emit_outline: + /* If we're printing a copper layer other than the outline layer, + and we want to "print outlines", and we have an outline layer, + print the outline layer on this layer also. */ + want_outline = 0; + if (copy_outline_mode == COPY_OUTLINE_MASK && SL_TYPE(idx) == SL_MASK) + want_outline = 1; + if (copy_outline_mode == COPY_OUTLINE_SILK && SL_TYPE(idx) == SL_SILK) + want_outline = 1; + if (copy_outline_mode == COPY_OUTLINE_ALL + && (SL_TYPE(idx) == SL_SILK + || SL_TYPE(idx) == SL_MASK || SL_TYPE(idx) == SL_FAB || SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == 0)) + want_outline = 1; + + if (want_outline && strcmp(name, "outline") + && strcmp(name, "route")) { + if (outline_layer && outline_layer != PCB->Data->Layer + idx) + DrawLayer(outline_layer, ®ion); + else if (!outline_layer) { + hidGC gc = gui->make_gc(); + printf("name %s idx %d\n", name, idx); + if (SL_TYPE(idx) == SL_SILK) + gui->set_line_width(gc, PCB->minSlk); + else if (group >= 0) + gui->set_line_width(gc, PCB->minWid); + else + gui->set_line_width(gc, AUTO_OUTLINE_WIDTH); + gui->draw_line(gc, 0, 0, PCB->MaxWidth, 0); + gui->draw_line(gc, 0, 0, 0, PCB->MaxHeight); + gui->draw_line(gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight); + gui->draw_line(gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight); + gui->destroy_gc(gc); + } + } + + return 1; +} + +static hidGC gerber_make_gc(void) +{ + hidGC rv = (hidGC) calloc(1, sizeof(*rv)); + rv->cap = Trace_Cap; + return rv; +} + +static void gerber_destroy_gc(hidGC gc) +{ + free(gc); +} + +static void gerber_use_mask(int use_it) +{ + current_mask = use_it; +} + +static void gerber_set_color(hidGC gc, const char *name) +{ + if (strcmp(name, "erase") == 0) { + gc->color = 1; + gc->erase = 1; + gc->drill = 0; + } + else if (strcmp(name, "drill") == 0) { + gc->color = 1; + gc->erase = 0; + gc->drill = 1; + } + else { + gc->color = 0; + gc->erase = 0; + gc->drill = 0; + } +} + +static void gerber_set_line_cap(hidGC gc, EndCapStyle style) +{ + gc->cap = style; +} + +static void gerber_set_line_width(hidGC gc, Coord width) +{ + gc->width = width; +} + +static void gerber_set_draw_xor(hidGC gc, int xor_) +{ + ; +} + +static void use_gc(hidGC gc, int radius) +{ + if (radius) { + radius *= 2; + if (radius != linewidth || lastcap != Round_Cap) { + Aperture *aptr = findAperture(curr_aptr_list, radius, ROUND); + if (aptr == NULL) + pcb_fprintf(stderr, "error: aperture for radius %$mS type ROUND is null\n", radius); + else if (f && !is_drill) + fprintf(f, "G54D%d*", aptr->dCode); + linewidth = radius; + lastcap = Round_Cap; + } + } + else if (linewidth != gc->width || lastcap != gc->cap) { + Aperture *aptr; + ApertureShape shape; + + linewidth = gc->width; + lastcap = gc->cap; + switch (gc->cap) { + case Round_Cap: + case Trace_Cap: + shape = ROUND; + break; + default: + case Square_Cap: + shape = SQUARE; + break; + } + aptr = findAperture(curr_aptr_list, linewidth, shape); + if (aptr == NULL) + pcb_fprintf(stderr, "error: aperture for width %$mS type %s is null\n", linewidth, shape == ROUND ? "ROUND" : "SQUARE"); + if (f) + fprintf(f, "G54D%d*", aptr->dCode); + } +#if 0 + if (lastcolor != gc->color) { + c = gc->color; + if (is_drill) + return; + if (is_mask) + c = (gc->erase ? 0 : 1); + lastcolor = gc->color; + if (f) { + if (c) { + fprintf(f, "%%LN%s_C%d*%%\r\n", layername, lncount++); + fprintf(f, "%%LPC*%%\r\n"); + } + else { + fprintf(f, "%%LN%s_D%d*%%\r\n", layername, lncount++); + fprintf(f, "%%LPD*%%\r\n"); + } + } + } +#endif +} + +static void gerber_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + gerber_draw_line(gc, x1, y1, x1, y2); + gerber_draw_line(gc, x1, y1, x2, y1); + gerber_draw_line(gc, x1, y2, x2, y2); + gerber_draw_line(gc, x2, y1, x2, y2); +} + +static void gerber_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + bool m = false; + + if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap) { + Coord x[5], y[5]; + double tx, ty, theta; + + theta = atan2(y2 - y1, x2 - x1); + + /* T is a vector half a thickness long, in the direction of + one of the corners. */ + tx = gc->width / 2.0 * cos(theta + M_PI / 4) * sqrt(2.0); + ty = gc->width / 2.0 * sin(theta + M_PI / 4) * sqrt(2.0); + + x[0] = x1 - tx; + y[0] = y1 - ty; + x[1] = x2 + ty; + y[1] = y2 - tx; + x[2] = x2 + tx; + y[2] = y2 + ty; + x[3] = x1 - ty; + y[3] = y1 + tx; + + x[4] = x[0]; + y[4] = y[0]; + gerber_fill_polygon(gc, 5, x, y); + return; + } + + use_gc(gc, 0); + if (!f) + return; + + if (x1 != lastX) { + m = true; + lastX = x1; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); + } + if (y1 != lastY) { + m = true; + lastY = y1; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + } + if ((x1 == x2) && (y1 == y2)) + fprintf(f, "D03*\r\n"); + else { + if (m) + fprintf(f, "D02*"); + if (x2 != lastX) { + lastX = x2; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); + } + if (y2 != lastY) { + lastY = y2; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + + } + fprintf(f, "D01*\r\n"); + } + +} + +static void gerber_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) +{ + bool m = false; + double arcStartX, arcStopX, arcStartY, arcStopY; + + /* we never draw zero-width lines */ + if (gc->width == 0) + return; + + use_gc(gc, 0); + if (!f) + return; + + arcStartX = cx - width * cos(TO_RADIANS(start_angle)); + arcStartY = cy + height * sin(TO_RADIANS(start_angle)); + + /* I checked three different gerber viewers, and they all disagreed + on how ellipses should be drawn. The spec just calls G74/G75 + "circular interpolation" so there's a chance it just doesn't + support ellipses at all. Thus, we draw them out with line + segments. Note that most arcs in pcb are circles anyway. */ + if (width != height) { + double step, angle; + Coord max = width > height ? width : height; + Coord minr = max - gc->width / 10; + int nsteps; + Coord x0, y0, x1, y1; + + if (minr >= max) + minr = max - 1; + step = acos((double) minr / (double) max) * 180.0 / M_PI; + if (step > 5) + step = 5; + nsteps = abs(delta_angle) / step + 1; + step = (double) delta_angle / nsteps; + + x0 = arcStartX; + y0 = arcStartY; + angle = start_angle; + while (nsteps > 0) { + nsteps--; + x1 = cx - width * cos(TO_RADIANS(angle + step)); + y1 = cy + height * sin(TO_RADIANS(angle + step)); + gerber_draw_line(gc, x0, y0, x1, y1); + x0 = x1; + y0 = y1; + angle += step; + } + return; + } + + arcStopX = cx - width * cos(TO_RADIANS(start_angle + delta_angle)); + arcStopY = cy + height * sin(TO_RADIANS(start_angle + delta_angle)); + if (arcStartX != lastX) { + m = true; + lastX = arcStartX; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); + } + if (arcStartY != lastY) { + m = true; + lastY = arcStartY; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + } + if (m) + fprintf(f, "D02*"); + pcb_fprintf(f, + "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n", + (delta_angle < 0) ? 2 : 3, + gerberX(PCB, arcStopX), gerberY(PCB, arcStopY), + gerberXOffset(PCB, cx - arcStartX), gerberYOffset(PCB, cy - arcStartY)); + lastX = arcStopX; + lastY = arcStopY; +} + +static void gerber_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) +{ + if (radius <= 0) + return; + if (is_drill) + radius = 50 * round(radius / 50.0); + use_gc(gc, radius); + if (!f) + return; + if (is_drill) { + if (n_pending_drills >= max_pending_drills) { + max_pending_drills += 100; + pending_drills = (PendingDrills *) realloc(pending_drills, max_pending_drills * sizeof(pending_drills[0])); + } + pending_drills[n_pending_drills].x = cx; + pending_drills[n_pending_drills].y = cy; + pending_drills[n_pending_drills].diam = radius * 2; + n_pending_drills++; + return; + } + else if (gc->drill && !flash_drills) + return; + if (cx != lastX) { + lastX = cx; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); + } + if (cy != lastY) { + lastY = cy; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + } + fprintf(f, "D03*\r\n"); +} + +static void gerber_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) +{ + bool m = false; + int i; + int firstTime = 1; + Coord startX = 0, startY = 0; + + if (is_mask && current_mask == HID_MASK_BEFORE) + return; + + use_gc(gc, 10 * 100); + if (!f) + return; + fprintf(f, "G36*\r\n"); + for (i = 0; i < n_coords; i++) { + if (x[i] != lastX) { + m = true; + lastX = x[i]; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, lastX)); + } + if (y[i] != lastY) { + m = true; + lastY = y[i]; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + } + if (firstTime) { + firstTime = 0; + startX = x[i]; + startY = y[i]; + if (m) + fprintf(f, "D02*"); + } + else if (m) + fprintf(f, "D01*\r\n"); + m = false; + } + if (startX != lastX) { + m = true; + lastX = startX; + pcb_fprintf(f, "X%.0mc", gerberX(PCB, startX)); + } + if (startY != lastY) { + m = true; + lastY = startY; + pcb_fprintf(f, "Y%.0mc", gerberY(PCB, lastY)); + } + if (m) + fprintf(f, "D01*\r\n"); + fprintf(f, "G37*\r\n"); +} + +static void gerber_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + Coord x[5]; + Coord y[5]; + x[0] = x[4] = x1; + y[0] = y[4] = y1; + x[1] = x1; + y[1] = y2; + x[2] = x2; + y[2] = y2; + x[3] = x2; + y[3] = y1; + gerber_fill_polygon(gc, 5, x, y); +} + +static void gerber_calibrate(double xval, double yval) +{ + CRASH; +} + +static void gerber_set_crosshair(int x, int y, int action) +{ +} + +pcb_uninit_t hid_export_gerber_init() +{ + memset(&gerber_hid, 0, sizeof(gerber_hid)); + + common_nogui_init(&gerber_hid); + common_draw_helpers_init(&gerber_hid); + + gerber_hid.struct_size = sizeof(gerber_hid); + gerber_hid.name = "gerber"; + gerber_hid.description = "RS-274X (Gerber) export"; + gerber_hid.exporter = 1; + + gerber_hid.get_export_options = gerber_get_export_options; + gerber_hid.do_export = gerber_do_export; + gerber_hid.parse_arguments = gerber_parse_arguments; + gerber_hid.set_layer = gerber_set_layer; + gerber_hid.make_gc = gerber_make_gc; + gerber_hid.destroy_gc = gerber_destroy_gc; + gerber_hid.use_mask = gerber_use_mask; + gerber_hid.set_color = gerber_set_color; + gerber_hid.set_line_cap = gerber_set_line_cap; + gerber_hid.set_line_width = gerber_set_line_width; + gerber_hid.set_draw_xor = gerber_set_draw_xor; + gerber_hid.draw_line = gerber_draw_line; + gerber_hid.draw_arc = gerber_draw_arc; + gerber_hid.draw_rect = gerber_draw_rect; + gerber_hid.fill_circle = gerber_fill_circle; + gerber_hid.fill_polygon = gerber_fill_polygon; + gerber_hid.fill_rect = gerber_fill_rect; + gerber_hid.calibrate = gerber_calibrate; + gerber_hid.set_crosshair = gerber_set_crosshair; + + hid_register_hid(&gerber_hid); + return NULL; +} Index: src_plugins/export_nelma/Plug.tmpasm =================================================================== --- src_plugins/export_nelma/Plug.tmpasm (revision 1269) +++ src_plugins/export_nelma/Plug.tmpasm (revision 1270) @@ -5,6 +5,8 @@ put /local/pcb/mod/OBJS [@ $(PLUGDIR)/export_nelma/nelma.o @] if /local/pcb/export_nelma/enable then + append /local/pcb/LIBS libs/gui/gd/ldflags + append /local/pcb/CFLAGS libs/gui/gd/cflags if /local/pcb/export_nelma/buildin then include {Makefile.in.mod/Buildin} else Index: src_plugins/export_png/png.c =================================================================== --- src_plugins/export_png/png.c (nonexistent) +++ src_plugins/export_png/png.c (revision 1270) @@ -0,0 +1,1518 @@ + /* + * COPYRIGHT + * + * PCB, interactive printed circuit board design + * Copyright (C) 2006 Dan McMahill + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Heavily based on the ps HID written by DJ Delorie + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include "data.h" +#include "error.h" +#include "misc.h" +#include "plugins.h" + +#include "hid.h" +#include "../hidint.h" +#include "hid/common/hidnogui.h" +#include "hid/common/draw_helpers.h" +#include "png.h" + +/* the gd library which makes this all so easy */ +#include + +#include "hid/common/hidinit.h" + + +#define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort() + +static HID png_hid; + +const char *png_cookie = "png HID"; + +static void *color_cache = NULL; +static void *brush_cache = NULL; + +static double bloat = 0; +static double scale = 1; +static Coord x_shift = 0; +static Coord y_shift = 0; +static int show_solder_side; +#define SCALE(w) ((int)((w)/scale + 0.5)) +#define SCALE_X(x) ((int)(((x) - x_shift)/scale)) +#define SCALE_Y(y) ((int)(((show_solder_side ? (PCB->MaxHeight-(y)) : (y)) - y_shift)/scale)) +#define SWAP_IF_SOLDER(a,b) do { int c; if (show_solder_side) { c=a; a=b; b=c; }} while (0) + +/* Used to detect non-trivial outlines */ +#define NOT_EDGE_X(x) ((x) != 0 && (x) != PCB->MaxWidth) +#define NOT_EDGE_Y(y) ((y) != 0 && (y) != PCB->MaxHeight) +#define NOT_EDGE(x,y) (NOT_EDGE_X(x) || NOT_EDGE_Y(y)) + +static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius); + +/* The result of a failed gdImageColorAllocate() call */ +#define BADC -1 + +typedef struct color_struct { + /* the descriptor used by the gd library */ + int c; + + /* so I can figure out what rgb value c refers to */ + unsigned int r, g, b, a; + +} color_struct; + +typedef struct hid_gc_struct { + HID *me_pointer; + EndCapStyle cap; + int width; + unsigned char r, g, b; + color_struct *color; + gdImagePtr brush; + int is_erase; +} hid_gc_struct; + +static color_struct *black = NULL, *white = NULL; +static gdImagePtr im = NULL, master_im, mask_im = NULL; +static FILE *f = 0; +static int linewidth = -1; +static int lastgroup = -1; +static gdImagePtr lastbrush = (gdImagePtr) ((void *) -1); +static int lastcap = -1; +static int print_group[MAX_LAYER]; +static int print_layer[MAX_LAYER]; + +/* For photo-mode we need the following layers as monochrome masks: + + top soldermask + top silk + copper layers + drill +*/ + +#define PHOTO_FLIP_X 1 +#define PHOTO_FLIP_Y 2 + +static int photo_mode, photo_flip; +static gdImagePtr photo_copper[MAX_LAYER + 2]; +static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im; +static gdImagePtr photo_outline; +static int photo_groups[MAX_LAYER + 2], photo_ngroups; +static int photo_has_inners; + +static int doing_outline, have_outline; + +#define FMT_gif "GIF" +#define FMT_jpg "JPEG" +#define FMT_png "PNG" + +/* If this table has no elements in it, then we have no reason to + register this HID and will refrain from doing so at the end of this + file. */ + +#undef HAVE_SOME_FORMAT + +static const char *filetypes[] = { +#ifdef HAVE_GDIMAGEPNG + FMT_png, +#define HAVE_SOME_FORMAT 1 +#endif + +#ifdef HAVE_GDIMAGEGIF + FMT_gif, +#define HAVE_SOME_FORMAT 1 +#endif + +#ifdef HAVE_GDIMAGEJPEG + FMT_jpg, +#define HAVE_SOME_FORMAT 1 +#endif + + NULL +}; + +HID_Attribute png_attribute_list[] = { + /* other HIDs expect this to be first. */ + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --outfile +Name of the file to be exported to. Can contain a path. +@end ftable +%end-doc +*/ + {"outfile", "Graphics output file", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_pngfile 0 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --dpi +Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout. +@end ftable +%end-doc +*/ + {"dpi", "Scale factor (pixels/inch). 0 to scale to specified size", + HID_Integer, 0, 1000, {100, 0, 0}, 0, 0}, +#define HA_dpi 1 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --x-max +Width of the png image in pixels. No constraint, when set to 0. +@end ftable +%end-doc +*/ + {"x-max", "Maximum width (pixels). 0 to not constrain", + HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, +#define HA_xmax 2 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --y-max +Height of the png output in pixels. No constraint, when set to 0. +@end ftable +%end-doc +*/ + {"y-max", "Maximum height (pixels). 0 to not constrain", + HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, +#define HA_ymax 3 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --xy-max +Maximum width and height of the PNG output in pixels. No constraint, when set to 0. +@end ftable +%end-doc +*/ + {"xy-max", "Maximum width and height (pixels). 0 to not constrain", + HID_Integer, 0, 10000, {0, 0, 0}, 0, 0}, +#define HA_xymax 4 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --as-shown +Export layers as shown on screen. +@end ftable +%end-doc +*/ + {"as-shown", "Export layers as shown on screen", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_as_shown 5 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --monochrome +Convert output to monochrome. +@end ftable +%end-doc +*/ + {"monochrome", "Convert to monochrome", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_mono 6 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --only-vivible +Limit the bounds of the exported PNG image to the visible items. +@end ftable +%end-doc +*/ + {"only-visible", "Limit the bounds of the PNG image to the visible items", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_only_visible 7 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --use-alpha +Make the background and any holes transparent. +@end ftable +%end-doc +*/ + {"use-alpha", "Make the background and any holes transparent", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_use_alpha 8 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --format +File format to be exported. Parameter @code{} can be @samp{PNG}, +@samp{GIF}, or @samp{JPEG}. +@end ftable +%end-doc +*/ + {"format", "Export file format", + HID_Enum, 0, 0, {0, 0, 0}, filetypes, 0}, +#define HA_filetype 9 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --png-bloat +Amount of extra thickness to add to traces, pads, or pin edges. The parameter +@samp{} is a number, appended by a dimension @samp{mm}, @samp{mil}, or +@samp{pix}. If no dimension is given, the default dimension is 1/100 mil. +@end ftable +%end-doc +*/ + {"png-bloat", "Amount (in/mm/mil/pix) to add to trace/pad/pin edges (1 = 1/100 mil)", + HID_String, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_bloat 10 + +/* %start-doc options "93 PNG Options" +@ftable @code +@cindex photo-mode +@item --photo-mode +Export a photo realistic image of the layout. +@end ftable +%end-doc +*/ + {"photo-mode", "Photo-realistic export mode", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_photo_mode 11 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --photo-flip-x +In photo-realistic mode, export the reverse side of the layout. Left-right flip. +@end ftable +%end-doc +*/ + {"photo-flip-x", "Show reverse side of the board, left-right flip", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_photo_flip_x 12 + +/* %start-doc options "93 PNG Options" +@ftable @code +@item --photo-flip-y +In photo-realistic mode, export the reverse side of the layout. Up-down flip. +@end ftable +%end-doc +*/ + {"photo-flip-y", "Show reverse side of the board, up-down flip", + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_photo_flip_y 13 + + {"ben-mode", ATTR_UNDOCUMENTED, + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_ben_mode 11 + + {"ben-flip-x", ATTR_UNDOCUMENTED, + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_ben_flip_x 12 + + {"ben-flip-y", ATTR_UNDOCUMENTED, + HID_Boolean, 0, 0, {0, 0, 0}, 0, 0}, +#define HA_ben_flip_y 13 +}; + +#define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0])) + +REGISTER_ATTRIBUTES(png_attribute_list, png_cookie) + + static HID_Attr_Val png_values[NUM_OPTIONS]; + + static const char *get_file_suffix(void) +{ + const char *result = NULL; + const char *fmt; + + fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value]; + + if (fmt == NULL); /* Do nothing */ + else if (strcmp(fmt, FMT_gif) == 0) + result = ".gif"; + else if (strcmp(fmt, FMT_jpg) == 0) + result = ".jpg"; + else if (strcmp(fmt, FMT_png) == 0) + result = ".png"; + + if (result == NULL) { + fprintf(stderr, "Error: Invalid graphic file format\n"); + result = ".???"; + } + return result; +} + +static HID_Attribute *png_get_export_options(int *n) +{ + static char *last_made_filename = 0; + const char *suffix = get_file_suffix(); + + if (PCB) + derive_default_filename(PCB->Filename, &png_attribute_list[HA_pngfile], suffix, &last_made_filename); + + if (n) + *n = NUM_OPTIONS; + return png_attribute_list; +} + +static int comp_layer, solder_layer; + +static int group_for_layer(int l) +{ + if (l < max_copper_layer + 2 && l >= 0) + return GetLayerGroupNumberByNumber(l); + /* else something unique */ + return max_group + 3 + l; +} + +static int layer_sort(const void *va, const void *vb) +{ + int a = *(int *) va; + int b = *(int *) vb; + int al = group_for_layer(a); + int bl = group_for_layer(b); + int d = bl - al; + + if (a >= 0 && a <= max_copper_layer + 1) { + int aside = (al == solder_layer ? 0 : al == comp_layer ? 2 : 1); + int bside = (bl == solder_layer ? 0 : bl == comp_layer ? 2 : 1); + if (bside != aside) + return bside - aside; + } + if (d) + return d; + return b - a; +} + +static const char *filename; +static BoxType *bounds; +static int in_mono, as_shown; + +static void parse_bloat(const char *str) +{ + UnitList extra_units = { + {"pix", scale, 0}, + {"px", scale, 0}, + {"", 0, 0} + }; + if (str == NULL) + return; + bloat = GetValueEx(str, NULL, NULL, extra_units, ""); +} + +void png_hid_export_to_file(FILE * the_file, HID_Attr_Val * options) +{ + int i; + static int saved_layer_stack[MAX_LAYER]; + int saved_show_solder_side; + BoxType region; + FlagType save_flags; + + f = the_file; + + region.X1 = 0; + region.Y1 = 0; + region.X2 = PCB->MaxWidth; + region.Y2 = PCB->MaxHeight; + + if (options[HA_only_visible].int_value) + bounds = GetDataBoundingBox(PCB->Data); + else + bounds = ®ion; + + memset(print_group, 0, sizeof(print_group)); + memset(print_layer, 0, sizeof(print_layer)); + + for (i = 0; i < max_copper_layer; i++) { + LayerType *layer = PCB->Data->Layer + i; + if (!LAYER_IS_EMPTY(layer)) + print_group[GetLayerGroupNumberByNumber(i)] = 1; + } + print_group[GetLayerGroupNumberByNumber(solder_silk_layer)] = 1; + print_group[GetLayerGroupNumberByNumber(component_silk_layer)] = 1; + for (i = 0; i < max_copper_layer; i++) + if (print_group[GetLayerGroupNumberByNumber(i)]) + print_layer[i] = 1; + + memcpy(saved_layer_stack, LayerStack, sizeof(LayerStack)); + save_flags = PCB->Flags; + saved_show_solder_side = Settings.ShowSolderSide; + + as_shown = options[HA_as_shown].int_value; + if (!options[HA_as_shown].int_value) { + CLEAR_FLAG(SHOWMASKFLAG, PCB); + Settings.ShowSolderSide = 0; + + comp_layer = GetLayerGroupNumberByNumber(component_silk_layer); + solder_layer = GetLayerGroupNumberByNumber(solder_silk_layer); + qsort(LayerStack, max_copper_layer, sizeof(LayerStack[0]), layer_sort); + + CLEAR_FLAG(THINDRAWFLAG, PCB); + CLEAR_FLAG(THINDRAWPOLYFLAG, PCB); + + if (photo_mode) { + int i, n = 0; + SET_FLAG(SHOWMASKFLAG, PCB); + photo_has_inners = 0; + if (comp_layer < solder_layer) + for (i = comp_layer; i <= solder_layer; i++) { + photo_groups[n++] = i; + if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i)) + photo_has_inners = 1; + } + else + for (i = comp_layer; i >= solder_layer; i--) { + photo_groups[n++] = i; + if (i != comp_layer && i != solder_layer && !IsLayerGroupEmpty(i)) + photo_has_inners = 1; + } + if (!photo_has_inners) { + photo_groups[1] = photo_groups[n - 1]; + n = 2; + } + photo_ngroups = n; + + if (photo_flip) { + for (i = 0, n = photo_ngroups - 1; i < n; i++, n--) { + int tmp = photo_groups[i]; + photo_groups[i] = photo_groups[n]; + photo_groups[n] = tmp; + } + } + } + } + linewidth = -1; + lastbrush = (gdImagePtr) ((void *) -1); + lastcap = -1; + lastgroup = -1; + show_solder_side = Settings.ShowSolderSide; + + in_mono = options[HA_mono].int_value; + + if (!photo_mode && Settings.ShowSolderSide) { + int i, j; + for (i = 0, j = max_copper_layer - 1; i < j; i++, j--) { + int k = LayerStack[i]; + LayerStack[i] = LayerStack[j]; + LayerStack[j] = k; + } + } + + hid_expose_callback(&png_hid, bounds, 0); + + memcpy(LayerStack, saved_layer_stack, sizeof(LayerStack)); + PCB->Flags = save_flags; + Settings.ShowSolderSide = saved_show_solder_side; +} + +static void blend(color_struct * dest, float a_amount, color_struct * a, color_struct * b) +{ + dest->r = a->r * a_amount + b->r * (1 - a_amount); + dest->g = a->g * a_amount + b->g * (1 - a_amount); + dest->b = a->b * a_amount + b->b * (1 - a_amount); +} + +static void rgb(color_struct * dest, int r, int g, int b) +{ + dest->r = r; + dest->g = g; + dest->b = b; +} + +static int smshadows[3][3] = { + {1, 20, 1}, + {10, 0, -10}, + {-1, -20, -1}, +}; + +static int shadows[5][5] = { + {1, 1, 1, 1, -1}, + {1, 1, 1, -1, -1}, + {1, 1, 0, -1, -1}, + {1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1}, +}; + +/* black and white are 0 and 1 */ +#define TOP_SHADOW 2 +#define BOTTOM_SHADOW 3 + +static void ts_bs(gdImagePtr im) +{ + int x, y, sx, sy, si; + for (x = 0; x < gdImageSX(im); x++) + for (y = 0; y < gdImageSY(im); y++) { + si = 0; + for (sx = -2; sx < 3; sx++) + for (sy = -2; sy < 3; sy++) + if (!gdImageGetPixel(im, x + sx, y + sy)) + si += shadows[sx + 2][sy + 2]; + if (gdImageGetPixel(im, x, y)) { + if (si > 1) + gdImageSetPixel(im, x, y, TOP_SHADOW); + else if (si < -1) + gdImageSetPixel(im, x, y, BOTTOM_SHADOW); + } + } +} + +static void ts_bs_sm(gdImagePtr im) +{ + int x, y, sx, sy, si; + for (x = 0; x < gdImageSX(im); x++) + for (y = 0; y < gdImageSY(im); y++) { + si = 0; + for (sx = -1; sx < 2; sx++) + for (sy = -1; sy < 2; sy++) + if (!gdImageGetPixel(im, x + sx, y + sy)) + si += smshadows[sx + 1][sy + 1]; + if (gdImageGetPixel(im, x, y)) { + if (si > 1) + gdImageSetPixel(im, x, y, TOP_SHADOW); + else if (si < -1) + gdImageSetPixel(im, x, y, BOTTOM_SHADOW); + } + } +} + +static void png_do_export(HID_Attr_Val * options) +{ + int save_ons[MAX_LAYER + 2]; + int i; + BoxType *bbox; + int w, h; + int xmax, ymax, dpi; + const char *fmt; + bool format_error = false; + + if (color_cache) { + free(color_cache); + color_cache = NULL; + } + + if (brush_cache) { + free(brush_cache); + brush_cache = NULL; + } + + if (!options) { + png_get_export_options(0); + for (i = 0; i < NUM_OPTIONS; i++) + png_values[i] = png_attribute_list[i].default_val; + options = png_values; + } + + if (options[HA_photo_mode].int_value || options[HA_ben_mode].int_value) { + photo_mode = 1; + options[HA_mono].int_value = 1; + options[HA_as_shown].int_value = 0; + memset(photo_copper, 0, sizeof(photo_copper)); + photo_silk = photo_mask = photo_drill = 0; + photo_outline = 0; + if (options[HA_photo_flip_x].int_value || options[HA_ben_flip_x].int_value) + photo_flip = PHOTO_FLIP_X; + else if (options[HA_photo_flip_y].int_value || options[HA_ben_flip_y].int_value) + photo_flip = PHOTO_FLIP_Y; + else + photo_flip = 0; + } + else + photo_mode = 0; + + filename = options[HA_pngfile].str_value; + if (!filename) + filename = "pcb-out.png"; + + /* figure out width and height of the board */ + if (options[HA_only_visible].int_value) { + bbox = GetDataBoundingBox(PCB->Data); + x_shift = bbox->X1; + y_shift = bbox->Y1; + h = bbox->Y2 - bbox->Y1; + w = bbox->X2 - bbox->X1; + } + else { + x_shift = 0; + y_shift = 0; + h = PCB->MaxHeight; + w = PCB->MaxWidth; + } + + /* + * figure out the scale factor we need to make the image + * fit in our specified PNG file size + */ + xmax = ymax = dpi = 0; + if (options[HA_dpi].int_value != 0) { + dpi = options[HA_dpi].int_value; + if (dpi < 0) { + fprintf(stderr, "ERROR: dpi may not be < 0\n"); + return; + } + } + + if (options[HA_xmax].int_value > 0) { + xmax = options[HA_xmax].int_value; + dpi = 0; + } + + if (options[HA_ymax].int_value > 0) { + ymax = options[HA_ymax].int_value; + dpi = 0; + } + + if (options[HA_xymax].int_value > 0) { + dpi = 0; + if (options[HA_xymax].int_value < xmax || xmax == 0) + xmax = options[HA_xymax].int_value; + if (options[HA_xymax].int_value < ymax || ymax == 0) + ymax = options[HA_xymax].int_value; + } + + if (xmax < 0 || ymax < 0) { + fprintf(stderr, "ERROR: xmax and ymax may not be < 0\n"); + return; + } + + if (dpi > 0) { + /* + * a scale of 1 means 1 pixel is 1 inch + * a scale of 10 means 1 pixel is 10 inches + */ + scale = INCH_TO_COORD(1) / dpi; + w = w / scale; + h = h / scale; + } + else if (xmax == 0 && ymax == 0) { + fprintf(stderr, "ERROR: You may not set both xmax, ymax," "and xy-max to zero\n"); + return; + } + else { + if (ymax == 0 || ((xmax > 0) + && ((w / xmax) > (h / ymax)))) { + h = (h * xmax) / w; + scale = w / xmax; + w = xmax; + } + else { + w = (w * ymax) / h; + scale = h / ymax; + h = ymax; + } + } + + im = gdImageCreate(w, h); + if (im == NULL) { + Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, w, h); + return; + } + + master_im = im; + + parse_bloat(options[HA_bloat].str_value); + + /* + * Allocate white and black -- the first color allocated + * becomes the background color + */ + + white = (color_struct *) malloc(sizeof(color_struct)); + white->r = white->g = white->b = 255; + if (options[HA_use_alpha].int_value) + white->a = 127; + else + white->a = 0; + white->c = gdImageColorAllocateAlpha(im, white->r, white->g, white->b, white->a); + if (white->c == BADC) { + Message("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__); + return; + } + + gdImageFilledRectangle(im, 0, 0, gdImageSX(im), gdImageSY(im), white->c); + + black = (color_struct *) malloc(sizeof(color_struct)); + black->r = black->g = black->b = black->a = 0; + black->c = gdImageColorAllocate(im, black->r, black->g, black->b); + if (black->c == BADC) { + Message("%s(): gdImageColorAllocateAlpha() returned NULL. Aborting export.\n", __FUNCTION__); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + perror(filename); + return; + } + + if (!options[HA_as_shown].int_value) + hid_save_and_show_layer_ons(save_ons); + + png_hid_export_to_file(f, options); + + if (!options[HA_as_shown].int_value) + hid_restore_layer_ons(save_ons); + + if (photo_mode) { + int x, y; + color_struct white, black, fr4; + + rgb(&white, 255, 255, 255); + rgb(&black, 0, 0, 0); + rgb(&fr4, 70, 70, 70); + + im = master_im; + + ts_bs(photo_copper[photo_groups[0]]); + ts_bs(photo_silk); + ts_bs_sm(photo_mask); + + if (photo_outline && have_outline) { + int black = gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00); + + /* go all the way around the image, trying to fill the outline */ + for (x = 0; x < gdImageSX(im); x++) { + gdImageFillToBorder(photo_outline, x, 0, black, black); + gdImageFillToBorder(photo_outline, x, gdImageSY(im) - 1, black, black); + } + for (y = 1; y < gdImageSY(im) - 1; y++) { + gdImageFillToBorder(photo_outline, 0, y, black, black); + gdImageFillToBorder(photo_outline, gdImageSX(im) - 1, y, black, black); + + } + } + + + for (x = 0; x < gdImageSX(im); x++) { + for (y = 0; y < gdImageSY(im); y++) { + color_struct p, cop; + int cc, mask, silk; + int transparent; + + if (photo_outline && have_outline) { + transparent = gdImageGetPixel(photo_outline, x, y); + } + else { + transparent = 0; + } + + mask = photo_mask ? gdImageGetPixel(photo_mask, x, y) : 0; + silk = photo_silk ? gdImageGetPixel(photo_silk, x, y) : 0; + + if (photo_copper[photo_groups[1]] + && gdImageGetPixel(photo_copper[photo_groups[1]], x, y)) + rgb(&cop, 40, 40, 40); + else + rgb(&cop, 100, 100, 110); + + if (photo_ngroups == 2) + blend(&cop, 0.3, &cop, &fr4); + + cc = gdImageGetPixel(photo_copper[photo_groups[0]], x, y); + if (cc) { + int r; + + if (mask) + rgb(&cop, 220, 145, 230); + else { + rgb(&cop, 140, 150, 160); + + r = (rand() % 5 - 2) * 2; + cop.r += r; + cop.g += r; + cop.b += r; + + } + + if (cc == TOP_SHADOW) { + cop.r = 255 - (255 - cop.r) * 0.7; + cop.g = 255 - (255 - cop.g) * 0.7; + cop.b = 255 - (255 - cop.b) * 0.7; + } + if (cc == BOTTOM_SHADOW) { + cop.r *= 0.7; + cop.g *= 0.7; + cop.b *= 0.7; + } + } + + if (photo_drill && !gdImageGetPixel(photo_drill, x, y)) { + rgb(&p, 0, 0, 0); + transparent = 1; + } + else if (silk) { + if (silk == TOP_SHADOW) + rgb(&p, 255, 255, 255); + else if (silk == BOTTOM_SHADOW) + rgb(&p, 192, 192, 192); + else + rgb(&p, 224, 224, 224); + } + else if (mask) { + p = cop; + p.r /= 2; + p.b /= 2; + if (mask == TOP_SHADOW) + blend(&p, 0.7, &p, &white); + if (mask == BOTTOM_SHADOW) + blend(&p, 0.7, &p, &black); + } + else + p = cop; + + if (options[HA_use_alpha].int_value) { + + cc = (transparent) ? gdImageColorResolveAlpha(im, 0, 0, 0, 127) : gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0); + + } + else { + cc = (transparent) ? gdImageColorResolve(im, 0, 0, 0) : gdImageColorResolve(im, p.r, p.g, p.b); + } + + if (photo_flip == PHOTO_FLIP_X) + gdImageSetPixel(im, gdImageSX(im) - x - 1, y, cc); + else if (photo_flip == PHOTO_FLIP_Y) + gdImageSetPixel(im, x, gdImageSY(im) - y - 1, cc); + else + gdImageSetPixel(im, x, y, cc); + } + } + } + + /* actually write out the image */ + fmt = filetypes[options[HA_filetype].int_value]; + + if (fmt == NULL) + format_error = true; + else if (strcmp(fmt, FMT_gif) == 0) +#ifdef HAVE_GDIMAGEGIF + gdImageGif(im, f); +#else + format_error = true; +#endif + else if (strcmp(fmt, FMT_jpg) == 0) +#ifdef HAVE_GDIMAGEJPEG + gdImageJpeg(im, f, -1); +#else + format_error = true; +#endif + else if (strcmp(fmt, FMT_png) == 0) +#ifdef HAVE_GDIMAGEPNG + gdImagePng(im, f); +#else + format_error = true; +#endif + else + format_error = true; + + if (format_error) + fprintf(stderr, "Error: Invalid graphic file format." " This is a bug. Please report it.\n"); + + fclose(f); + + gdImageDestroy(im); +} + +static void png_parse_arguments(int *argc, char ***argv) +{ + hid_register_attributes(png_attribute_list, sizeof(png_attribute_list) / sizeof(png_attribute_list[0]), png_cookie); + hid_parse_command_line(argc, argv); +} + + +static int is_mask; +static int is_drill; + +static int png_set_layer(const char *name, int group, int empty) +{ + int idx = (group >= 0 && group < max_group) ? PCB->LayerGroups.Entries[group][0] : group; + if (name == 0) + name = PCB->Data->Layer[idx].Name; + + doing_outline = 0; + + if (idx >= 0 && idx < max_copper_layer && !print_layer[idx]) + return 0; + if (SL_TYPE(idx) == SL_ASSY || SL_TYPE(idx) == SL_FAB) + return 0; + + if (strcmp(name, "invisible") == 0) + return 0; + + is_drill = (SL_TYPE(idx) == SL_PDRILL || SL_TYPE(idx) == SL_UDRILL); + is_mask = (SL_TYPE(idx) == SL_MASK); + + if (SL_TYPE(idx) == SL_PASTE) + return 0; + + if (photo_mode) { + switch (idx) { + case SL(SILK, TOP): + if (photo_flip) + return 0; + photo_im = &photo_silk; + break; + case SL(SILK, BOTTOM): + if (!photo_flip) + return 0; + photo_im = &photo_silk; + break; + + case SL(MASK, TOP): + if (photo_flip) + return 0; + photo_im = &photo_mask; + break; + case SL(MASK, BOTTOM): + if (!photo_flip) + return 0; + photo_im = &photo_mask; + break; + + case SL(PDRILL, 0): + case SL(UDRILL, 0): + photo_im = &photo_drill; + break; + + default: + if (idx < 0) + return 0; + + if (strcmp(name, "outline") == 0) { + doing_outline = 1; + have_outline = 0; + photo_im = &photo_outline; + } + else + photo_im = photo_copper + group; + + break; + } + + if (!*photo_im) { + static color_struct *black = NULL, *white = NULL; + *photo_im = gdImageCreate(gdImageSX(im), gdImageSY(im)); + if (photo_im == NULL) { + Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, gdImageSX(im), gdImageSY(im)); + return 0; + } + + + white = (color_struct *) malloc(sizeof(color_struct)); + white->r = white->g = white->b = 255; + white->a = 0; + white->c = gdImageColorAllocate(*photo_im, white->r, white->g, white->b); + if (white->c == BADC) { + Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); + return 0; + } + + black = (color_struct *) malloc(sizeof(color_struct)); + black->r = black->g = black->b = black->a = 0; + black->c = gdImageColorAllocate(*photo_im, black->r, black->g, black->b); + if (black->c == BADC) { + Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); + return 0; + } + + if (idx == SL(PDRILL, 0) + || idx == SL(UDRILL, 0)) + gdImageFilledRectangle(*photo_im, 0, 0, gdImageSX(im), gdImageSY(im), black->c); + } + im = *photo_im; + return 1; + } + + if (as_shown) { + switch (idx) { + case SL(SILK, TOP): + case SL(SILK, BOTTOM): + if (SL_MYSIDE(idx)) + return PCB->ElementOn; + return 0; + + case SL(MASK, TOP): + case SL(MASK, BOTTOM): + return TEST_FLAG(SHOWMASKFLAG, PCB) && SL_MYSIDE(idx); + } + } + else { + if (is_mask) + return 0; + + switch (idx) { + case SL(SILK, TOP): + return 1; + case SL(SILK, BOTTOM): + return 0; + } + } + + return 1; +} + + +static hidGC png_make_gc(void) +{ + hidGC rv = (hidGC) malloc(sizeof(hid_gc_struct)); + rv->me_pointer = &png_hid; + rv->cap = Trace_Cap; + rv->width = 1; + rv->color = (color_struct *) malloc(sizeof(color_struct)); + rv->color->r = rv->color->g = rv->color->b = rv->color->a = 0; + rv->color->c = 0; + rv->is_erase = 0; + return rv; +} + +static void png_destroy_gc(hidGC gc) +{ + free(gc); +} + +static void png_use_mask(int use_it) +{ + if (photo_mode) + return; + + if (use_it == HID_MASK_CLEAR) { + return; + } + if (use_it) { + if (mask_im == NULL) { + mask_im = gdImageCreate(gdImageSX(im), gdImageSY(im)); + if (!mask_im) { + Message("%s(): gdImageCreate(%d, %d) returned NULL. Corrupt export!\n", __FUNCTION__, gdImageSY(im), gdImageSY(im)); + return; + } + gdImagePaletteCopy(mask_im, im); + } + im = mask_im; + gdImageFilledRectangle(mask_im, 0, 0, gdImageSX(mask_im), gdImageSY(mask_im), white->c); + } + else { + int x, y, c; + + im = master_im; + + for (x = 0; x < gdImageSX(im); x++) + for (y = 0; y < gdImageSY(im); y++) { + c = gdImageGetPixel(mask_im, x, y); + if (c) + gdImageSetPixel(im, x, y, c); + } + } +} + +static void png_set_color(hidGC gc, const char *name) +{ + hidval cval; + + if (im == NULL) + return; + + if (name == NULL) + name = "#ff0000"; + + if (strcmp(name, "erase") == 0 || strcmp(name, "drill") == 0) { + gc->color = white; + gc->is_erase = 1; + return; + } + gc->is_erase = 0; + + if (in_mono || (strcmp(name, "#000000") == 0)) { + gc->color = black; + return; + } + + if (hid_cache_color(0, name, &cval, &color_cache)) { + gc->color = (color_struct *) cval.ptr; + } + else if (name[0] == '#') { + gc->color = (color_struct *) malloc(sizeof(color_struct)); + sscanf(name + 1, "%2x%2x%2x", &(gc->color->r), &(gc->color->g), &(gc->color->b)); + gc->color->c = gdImageColorAllocate(master_im, gc->color->r, gc->color->g, gc->color->b); + if (gc->color->c == BADC) { + Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); + return; + } + cval.ptr = gc->color; + hid_cache_color(1, name, &cval, &color_cache); + } + else { + printf("WE SHOULD NOT BE HERE!!!\n"); + gc->color = black; + } + +} + +static void png_set_line_cap(hidGC gc, EndCapStyle style) +{ + gc->cap = style; +} + +static void png_set_line_width(hidGC gc, Coord width) +{ + gc->width = width; +} + +static void png_set_draw_xor(hidGC gc, int xor_) +{ + ; +} + +static void use_gc(hidGC gc) +{ + int need_brush = 0; + + if (gc->me_pointer != &png_hid) { + fprintf(stderr, "Fatal: GC from another HID passed to png HID\n"); + abort(); + } + + if (linewidth != gc->width) { + /* Make sure the scaling doesn't erase lines completely */ + if (SCALE(gc->width) == 0 && gc->width > 0) + gdImageSetThickness(im, 1); + else + gdImageSetThickness(im, SCALE(gc->width + 2 * bloat)); + linewidth = gc->width; + need_brush = 1; + } + + if (lastbrush != gc->brush || need_brush) { + hidval bval; + char name[256]; + char type; + int r; + + switch (gc->cap) { + case Round_Cap: + case Trace_Cap: + type = 'C'; + break; + default: + case Square_Cap: + type = 'S'; + break; + } + if (gc->width) + r = SCALE(gc->width + 2 * bloat); + else + r = 1; + + /* do not allow a brush size that is zero width. In this case limit to a single pixel. */ + if (r == 0) { + r = 1; + } + + sprintf(name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g, gc->color->b, type, r); + + if (hid_cache_color(0, name, &bval, &brush_cache)) { + gc->brush = (gdImagePtr) bval.ptr; + } + else { + int bg, fg; + gc->brush = gdImageCreate(r, r); + if (gc->brush == NULL) { + Message("%s(): gdImageCreate(%d, %d) returned NULL. Aborting export.\n", __FUNCTION__, r, r); + return; + } + + bg = gdImageColorAllocate(gc->brush, 255, 255, 255); + if (bg == BADC) { + Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); + return; + } + fg = gdImageColorAllocateAlpha(gc->brush, gc->color->r, gc->color->g, gc->color->b, 0); + if (fg == BADC) { + Message("%s(): gdImageColorAllocate() returned NULL. Aborting export.\n", __FUNCTION__); + return; + } + gdImageColorTransparent(gc->brush, bg); + + /* + * if we shrunk to a radius/box width of zero, then just use + * a single pixel to draw with. + */ + if (r <= 1) + gdImageFilledRectangle(gc->brush, 0, 0, 0, 0, fg); + else { + if (type == 'C') { + gdImageFilledEllipse(gc->brush, r / 2, r / 2, r, r, fg); + /* Make sure the ellipse is the right exact size. */ + gdImageSetPixel(gc->brush, 0, r / 2, fg); + gdImageSetPixel(gc->brush, r - 1, r / 2, fg); + gdImageSetPixel(gc->brush, r / 2, 0, fg); + gdImageSetPixel(gc->brush, r / 2, r - 1, fg); + } + else + gdImageFilledRectangle(gc->brush, 0, 0, r - 1, r - 1, fg); + } + bval.ptr = gc->brush; + hid_cache_color(1, name, &bval, &brush_cache); + } + + gdImageSetBrush(im, gc->brush); + lastbrush = gc->brush; + + } +} + +static void png_draw_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageRectangle(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gc->color->c); +} + +static void png_fill_rect(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + use_gc(gc); + gdImageSetThickness(im, 0); + linewidth = 0; + + if (x1 > x2) { + Coord t = x1; + x2 = x2; + x2 = t; + } + if (y1 > y2) { + Coord t = y1; + y2 = y2; + y2 = t; + } + y1 -= bloat; + y2 += bloat; + SWAP_IF_SOLDER(y1, y2); + + gdImageFilledRectangle(im, SCALE_X(x1 - bloat), SCALE_Y(y1), SCALE_X(x2 + bloat) - 1, SCALE_Y(y2) - 1, gc->color->c); + have_outline |= doing_outline; +} + +static void png_draw_line(hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) +{ + if (x1 == x2 && y1 == y2) { + Coord w = gc->width / 2; + if (gc->cap != Square_Cap) + png_fill_circle(gc, x1, y1, w); + else + png_fill_rect(gc, x1 - w, y1 - w, x1 + w, y1 + w); + return; + } + use_gc(gc); + + if (NOT_EDGE(x1, y1) || NOT_EDGE(x2, y2)) + have_outline |= doing_outline; + if (doing_outline) { + /* Special case - lines drawn along the bottom or right edges + are brought in by a pixel to make sure we have contiguous + outlines. */ + if (x1 == PCB->MaxWidth && x2 == PCB->MaxWidth) { + x1 -= scale / 2; + x2 -= scale / 2; + } + if (y1 == PCB->MaxHeight && y2 == PCB->MaxHeight) { + y1 -= scale / 2; + y2 -= scale / 2; + } + } + + gdImageSetThickness(im, 0); + linewidth = 0; + if (gc->cap != Square_Cap || x1 == x2 || y1 == y2) { + gdImageLine(im, SCALE_X(x1), SCALE_Y(y1), SCALE_X(x2), SCALE_Y(y2), gdBrushed); + } + else { + /* + * if we are drawing a line with a square end cap and it is + * not purely horizontal or vertical, then we need to draw + * it as a filled polygon. + */ + int fg = gdImageColorResolve(im, gc->color->r, gc->color->g, + gc->color->b), w = gc->width, dx = x2 - x1, dy = y2 - y1, dwx, dwy; + gdPoint p[4]; + double l = sqrt(dx * dx + dy * dy) * 2; + + w += 2 * bloat; + dwx = -w / l * dy; + dwy = w / l * dx; + p[0].x = SCALE_X(x1 + dwx - dwy); + p[0].y = SCALE_Y(y1 + dwy + dwx); + p[1].x = SCALE_X(x1 - dwx - dwy); + p[1].y = SCALE_Y(y1 - dwy + dwx); + p[2].x = SCALE_X(x2 - dwx + dwy); + p[2].y = SCALE_Y(y2 - dwy - dwx); + p[3].x = SCALE_X(x2 + dwx + dwy); + p[3].y = SCALE_Y(y2 + dwy - dwx); + gdImageFilledPolygon(im, p, 4, fg); + } +} + +static void png_draw_arc(hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle) +{ + Angle sa, ea; + + /* + * zero angle arcs need special handling as gd will output either + * nothing at all or a full circle when passed delta angle of 0 or 360. + */ + if (delta_angle == 0) { + Coord x = (width * cos(start_angle * M_PI / 180)); + Coord y = (width * sin(start_angle * M_PI / 180)); + x = cx - x; + y = cy + y; + png_fill_circle(gc, x, y, gc->width / 2); + return; + } + + /* + * in gdImageArc, 0 degrees is to the right and +90 degrees is down + * in pcb, 0 degrees is to the left and +90 degrees is down + */ + start_angle = 180 - start_angle; + delta_angle = -delta_angle; + if (show_solder_side) { + start_angle = -start_angle; + delta_angle = -delta_angle; + } + if (delta_angle > 0) { + sa = start_angle; + ea = start_angle + delta_angle; + } + else { + sa = start_angle + delta_angle; + ea = start_angle; + } + + /* + * make sure we start between 0 and 360 otherwise gd does + * strange things + */ + sa = NormalizeAngle(sa); + ea = NormalizeAngle(ea); + + have_outline |= doing_outline; + +#if 0 + printf("draw_arc %d,%d %dx%d %d..%d %d..%d\n", cx, cy, width, height, start_angle, delta_angle, sa, ea); + printf("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", + im, SCALE_X(cx), SCALE_Y(cy), SCALE(width), SCALE(height), sa, ea, gc->color->c); +#endif + use_gc(gc); + gdImageSetThickness(im, 0); + linewidth = 0; + gdImageArc(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * width), SCALE(2 * height), sa, ea, gdBrushed); +} + +static void png_fill_circle(hidGC gc, Coord cx, Coord cy, Coord radius) +{ + Coord my_bloat; + + use_gc(gc); + + if (gc->is_erase) + my_bloat = -2 * bloat; + else + my_bloat = 2 * bloat; + + + have_outline |= doing_outline; + + gdImageSetThickness(im, 0); + linewidth = 0; + gdImageFilledEllipse(im, SCALE_X(cx), SCALE_Y(cy), SCALE(2 * radius + my_bloat), SCALE(2 * radius + my_bloat), gc->color->c); + +} + +static void png_fill_polygon(hidGC gc, int n_coords, Coord * x, Coord * y) +{ + int i; + gdPoint *points; + + points = (gdPoint *) malloc(n_coords * sizeof(gdPoint)); + if (points == NULL) { + fprintf(stderr, "ERROR: png_fill_polygon(): malloc failed\n"); + exit(1); + } + + use_gc(gc); + for (i = 0; i < n_coords; i++) { + if (NOT_EDGE(x[i], y[i])) + have_outline |= doing_outline; + points[i].x = SCALE_X(x[i]); + points[i].y = SCALE_Y(y[i]); + } + gdImageSetThickness(im, 0); + linewidth = 0; + gdImageFilledPolygon(im, points, n_coords, gc->color->c); + free(points); +} + +static void png_calibrate(double xval, double yval) +{ + CRASH; +} + +static void png_set_crosshair(int x, int y, int a) +{ +} + +#include "dolists.h" + +pcb_uninit_t hid_export_png_init() +{ + memset(&png_hid, 0, sizeof(HID)); + + common_nogui_init(&png_hid); + common_draw_helpers_init(&png_hid); + + png_hid.struct_size = sizeof(HID); + png_hid.name = "png"; + png_hid.description = "GIF/JPEG/PNG export"; + png_hid.exporter = 1; + png_hid.poly_before = 1; + + png_hid.get_export_options = png_get_export_options; + png_hid.do_export = png_do_export; + png_hid.parse_arguments = png_parse_arguments; + png_hid.set_layer = png_set_layer; + png_hid.make_gc = png_make_gc; + png_hid.destroy_gc = png_destroy_gc; + png_hid.use_mask = png_use_mask; + png_hid.set_color = png_set_color; + png_hid.set_line_cap = png_set_line_cap; + png_hid.set_line_width = png_set_line_width; + png_hid.set_draw_xor = png_set_draw_xor; + png_hid.draw_line = png_draw_line; + png_hid.draw_arc = png_draw_arc; + png_hid.draw_rect = png_draw_rect; + png_hid.fill_circle = png_fill_circle; + png_hid.fill_polygon = png_fill_polygon; + png_hid.fill_rect = png_fill_rect; + png_hid.calibrate = png_calibrate; + png_hid.set_crosshair = png_set_crosshair; + +#ifdef HAVE_SOME_FORMAT + hid_register_hid(&png_hid); + +#endif + return NULL; +} Index: src_plugins/export_png/png.h =================================================================== --- src_plugins/export_png/png.h (nonexistent) +++ src_plugins/export_png/png.h (revision 1270) @@ -0,0 +1,4 @@ +/* $Id$ */ + +extern const char *png_cookie; +extern void png_hid_export_to_file(FILE *, HID_Attr_Val *);