pcb 4.1.1
An interactive printed circuit board layout editor.

png.c

Go to the documentation of this file.
00001  /*
00002  *                            COPYRIGHT
00003  *
00004  *  PCB, interactive printed circuit board design
00005  *  Copyright (C) 2006 Dan McMahill
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License along
00018  *  with this program; if not, write to the Free Software Foundation, Inc.,
00019  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00020  *
00021  */
00022 
00023 /*
00024  *  Heavily based on the ps HID written by DJ Delorie
00025  */
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #include "config.h"
00029 #endif
00030 
00031 #include <stdio.h>
00032 #include <stdarg.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <assert.h>
00036 #include <math.h>
00037 
00038 #include "global.h"
00039 #include "data.h"
00040 #include "error.h"
00041 #include "misc.h"
00042 
00043 #include "hid.h"
00044 #include "hid_draw.h"
00045 #include "../hidint.h"
00046 #include "hid/common/hidnogui.h"
00047 #include "hid/common/draw_helpers.h"
00048 #include "png.h"
00049 
00050 /* the gd library which makes this all so easy */
00051 #include <gd.h>
00052 
00053 #include "hid/common/hidinit.h"
00054 
00055 #ifdef HAVE_LIBDMALLOC
00056 #include <dmalloc.h>
00057 #endif
00058 
00059 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented PNG function %s.\n", __FUNCTION__); abort()
00060 
00061 static HID png_hid;
00062 static HID_DRAW png_graphics;
00063 
00064 static void *color_cache = NULL;
00065 static void *brush_cache = NULL;
00066 
00067 static double bloat = 0;
00068 static double scale = 1;
00069 static Coord x_shift = 0;
00070 static Coord y_shift = 0;
00071 static int show_bottom_side;
00072 #define SCALE(w)   ((int)round((w)/scale))
00073 #define SCALE_X(x) ((int)round(((x) - x_shift)/scale))
00074 #define SCALE_Y(y) ((int)round(((show_bottom_side ? (PCB->MaxHeight-(y)) : (y)) - y_shift)/scale))
00075 #define SWAP_IF_SOLDER(a,b) do { Coord c; if (show_bottom_side) { c=a; a=b; b=c; }} while (0)
00076 
00077 /* Used to detect non-trivial outlines */
00078 #define NOT_EDGE_X(x) ((x) != 0 && (x) != PCB->MaxWidth)
00079 #define NOT_EDGE_Y(y) ((y) != 0 && (y) != PCB->MaxHeight)
00080 #define NOT_EDGE(x,y) (NOT_EDGE_X(x) || NOT_EDGE_Y(y))
00081 
00082 static void png_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
00083 
00084 /* The result of a failed gdImageColorAllocate() call */
00085 #define BADC -1
00086 
00087 typedef struct color_struct
00088 {
00089   /* the descriptor used by the gd library */
00090   int c;
00091 
00092   /* so I can figure out what rgb value c refers to */
00093   unsigned int r, g, b, a;
00094 
00095 } color_struct;
00096 
00097 typedef struct hid_gc_struct
00098 {
00099   HID *me_pointer;
00100   EndCapStyle cap;
00101   int width;
00102   unsigned char r, g, b;
00103   color_struct *color;
00104   gdImagePtr brush;
00105   int is_erase;
00106 } hid_gc_struct;
00107 
00108 static color_struct *black = NULL, *white = NULL;
00109 static gdImagePtr im = NULL, master_im, mask_im = NULL;
00110 static FILE *f = 0;
00111 static int linewidth = -1;
00112 static int lastgroup = -1;
00113 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
00114 static int lastcap = -1;
00115 static int print_group[MAX_GROUP];
00116 static int print_layer[MAX_ALL_LAYER];
00117 
00118 /* For photo-mode we need the following layers as monochrome masks:
00119 
00120    top soldermask
00121    top silk
00122    copper layers
00123    drill
00124 */
00125 
00126 #define PHOTO_FLIP_X    1
00127 #define PHOTO_FLIP_Y    2
00128 
00129 static int photo_mode, photo_flip;
00130 static gdImagePtr photo_copper[MAX_ALL_LAYER];
00131 static gdImagePtr photo_silk, photo_mask, photo_drill, *photo_im;
00132 static gdImagePtr photo_outline;
00133 static int photo_groups[MAX_ALL_LAYER], photo_ngroups;
00134 static int photo_has_inners;
00135 
00136 static int doing_outline, have_outline;
00137 
00138 #define FMT_gif "GIF"
00139 #define FMT_jpg "JPEG"
00140 #define FMT_png "PNG"
00141 
00142 /* If this table has no elements in it, then we have no reason to
00143    register this HID and will refrain from doing so at the end of this
00144    file.  */
00145 
00146 #undef HAVE_SOME_FORMAT
00147 
00148 static const char *filetypes[] = {
00149 #ifdef HAVE_GDIMAGEPNG
00150   FMT_png,
00151 #define HAVE_SOME_FORMAT 1
00152 #endif
00153 
00154 #ifdef HAVE_GDIMAGEGIF
00155   FMT_gif,
00156 #define HAVE_SOME_FORMAT 1
00157 #endif
00158 
00159 #ifdef HAVE_GDIMAGEJPEG
00160   FMT_jpg,
00161 #define HAVE_SOME_FORMAT 1
00162 #endif
00163 
00164   NULL
00165 };
00166 
00167 
00168 static const char *mask_colour_names[] = {
00169   "green",
00170   "red",
00171   "blue",
00172   "purple",
00173   "black",
00174   "white",
00175   NULL
00176 };
00177 
00178 // These values were arrived at through trial and error.
00179 // One potential improvement (especially for white) is
00180 // to use separate color_structs for the multiplication
00181 // and addition parts of the mask math.
00182 static const color_struct mask_colours[] = {
00183 #define MASK_COLOUR_GREEN 0
00184   {.r = 60, .g = 160, .b = 60},
00185 #define MASK_COLOUR_RED 1
00186   {.r = 140, .g = 25, .b = 25},
00187 #define MASK_COLOUR_BLUE 2
00188   {.r = 50, .g = 50, .b = 160},
00189 #define MASK_COLOUR_PURPLE 3
00190   {.r = 60, .g = 20, .b = 70},
00191 #define MASK_COLOUR_BLACK 4
00192   {.r = 20, .g = 20, .b = 20},
00193 #define MASK_COLOUR_WHITE 5
00194   {.r = 167, .g = 230, .b = 162}, // <-- needs improvement over FR4
00195   {}
00196 };
00197 
00198 
00199 static const char *plating_type_names[] = {
00200 #define PLATING_TIN 0
00201   "tinned",
00202 #define PLATING_GOLD 1
00203   "gold",
00204 #define PLATING_SILVER 2
00205   "silver",
00206 #define PLATING_COPPER 3
00207   "copper",
00208   NULL
00209 };
00210 
00211 
00212 
00213 static const char *silk_colour_names[] = {
00214   "white",
00215   "black",
00216   "yellow",
00217   NULL
00218 };
00219 
00220 static const color_struct silk_colours[] = {
00221 #define SILK_COLOUR_WHITE 0
00222   {.r = 224, .g = 224, .b = 224},
00223 #define SILK_COLOUR_BLACK 1
00224   {.r = 14, .g = 14, .b = 14},
00225 #define SILK_COLOUR_YELLOW 2
00226   {.r = 185, .g = 185, .b = 10},
00227   {}
00228 };
00229 
00230 static const color_struct silk_top_shadow = {.r = 21, .g = 21, .b = 21};
00231 static const color_struct silk_bottom_shadow = {.r = 14, .g = 14, .b = 14};
00232 
00233 HID_Attribute png_attribute_list[] = {
00234   /* other HIDs expect this to be first.  */
00235 
00236 /* %start-doc options "93 PNG Options"
00237 @ftable @code
00238 @item --outfile <string>
00239 Name of the file to be exported to.
00240 Parameter @code{<string>} can include a path.
00241 @end ftable
00242 %end-doc
00243 */
00244   {"outfile", "Graphics output file",
00245    HID_String, 0, 0, {0, 0, 0}, 0, 0},
00246 #define HA_pngfile 0
00247 
00248 /* %start-doc options "93 PNG Options"
00249 @ftable @code
00250 @item --dpi
00251 Scale factor in pixels/inch. Set to 0 to scale to size specified in the layout.
00252 @end ftable
00253 %end-doc
00254 */
00255   {"dpi", "Scale factor (pixels/inch). 0 to scale to specified size",
00256    HID_Integer, 0, 10000, {100, 0, 0}, 0, 0},
00257 #define HA_dpi 1
00258 
00259 /* %start-doc options "93 PNG Options"
00260 @ftable @code
00261 @item --x-max
00262 Width of the png image in pixels. No constraint, when set to 0.
00263 @end ftable
00264 %end-doc
00265 */
00266   {"x-max", "Maximum width (pixels).  0 to not constrain",
00267    HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
00268 #define HA_xmax 2
00269 
00270 /* %start-doc options "93 PNG Options"
00271 @ftable @code
00272 @item --y-max
00273 Height of the png output in pixels. No constraint, when set to 0.
00274 @end ftable
00275 %end-doc
00276 */
00277   {"y-max", "Maximum height (pixels).  0 to not constrain",
00278    HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
00279 #define HA_ymax 3
00280 
00281 /* %start-doc options "93 PNG Options"
00282 @ftable @code
00283 @item --xy-max
00284 Maximum width and height of the PNG output in pixels. No constraint, when set to 0.
00285 @end ftable
00286 %end-doc
00287 */
00288   {"xy-max", "Maximum width and height (pixels).  0 to not constrain",
00289    HID_Integer, 0, 10000, {0, 0, 0}, 0, 0},
00290 #define HA_xymax 4
00291 
00292 /* %start-doc options "93 PNG Options"
00293 @ftable @code
00294 @item --screen-layer-order
00295 Export layers in the order shown on screen.
00296 @end ftable
00297 %end-doc
00298 */
00299   {"screen-layer-order", "Export layers in the order shown on screen",
00300    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00301 #define HA_as_shown 5
00302 
00303 /* %start-doc options "93 PNG Options"
00304 @ftable @code
00305 @item --monochrome
00306 Convert output to monochrome.
00307 @end ftable
00308 %end-doc
00309 */
00310   {"monochrome", "Convert to monochrome",
00311    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00312 #define HA_mono 6
00313 
00314 /* %start-doc options "93 PNG Options"
00315 @ftable @code
00316 @item --only-visible
00317 Limit the bounds of the exported PNG image to the visible items.
00318 @end ftable
00319 %end-doc
00320 */
00321   {"only-visible", "Limit the bounds of the PNG image to the visible items",
00322    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00323 #define HA_only_visible 7
00324 
00325 /* %start-doc options "93 PNG Options"
00326 @ftable @code
00327 @item --use-alpha
00328 Make the background and any holes transparent.
00329 @end ftable
00330 %end-doc
00331 */
00332   {"use-alpha", "Make the background and any holes transparent",
00333    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00334 #define HA_use_alpha 8
00335 
00336 /* %start-doc options "93 PNG Options"
00337 @ftable @code
00338 @item --fill-holes
00339 Drill holes in pins/pads are filled, not hollow.
00340 @end ftable
00341 %end-doc
00342 */
00343   {"fill-holes", "Drill holes in pins/pads are filled, not hollow",
00344    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00345 #define HA_fill_holes 9
00346 
00347 /* %start-doc options "93 PNG Options"
00348 @ftable @code
00349 @item --format <string>
00350 File format to be exported. Parameter @code{<string>} can be @samp{PNG},
00351 @samp{GIF}, or @samp{JPEG}.
00352 @end ftable
00353 %end-doc
00354 */
00355   {"format", "Export file format",
00356    HID_Enum, 0, 0, {0, 0, 0}, filetypes, 0},
00357 #define HA_filetype 10
00358 
00359 /* %start-doc options "93 PNG Options"
00360 @ftable @code
00361 @item --png-bloat <num><dim>
00362 Amount of extra thickness to add to traces, pads, or pin edges. The parameter
00363 @samp{<num><dim>} is a number, appended by a dimension @samp{mm}, @samp{mil}, or
00364 @samp{pix}. If no dimension is given, the default dimension is 1/100 mil.
00365 @end ftable
00366 %end-doc
00367 */
00368   {"png-bloat", "Amount (in/mm/mil/pix) to add to trace/pad/pin edges (1 = 1/100 mil)",
00369    HID_String, 0, 0, {0, 0, 0}, 0, 0},
00370 #define HA_bloat 11
00371 
00372 /* %start-doc options "93 PNG Options"
00373 @ftable @code
00374 @cindex photo-mode
00375 @item --photo-mode
00376 Export a photo realistic image of the layout.
00377 @end ftable
00378 %end-doc
00379 */
00380   {"photo-mode", "Photo-realistic export mode",
00381    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00382 #define HA_photo_mode 12
00383 
00384 /* %start-doc options "93 PNG Options"
00385 @ftable @code
00386 @item --photo-flip-x
00387 In photo-realistic mode, export the reverse side of the layout. Left-right flip.
00388 @end ftable
00389 %end-doc
00390 */
00391   {"photo-flip-x", "Show reverse side of the board, left-right flip",
00392    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00393 #define HA_photo_flip_x 13
00394 
00395 /* %start-doc options "93 PNG Options"
00396 @ftable @code
00397 @item --photo-flip-y
00398 In photo-realistic mode, export the reverse side of the layout. Up-down flip.
00399 @end ftable
00400 %end-doc
00401 */
00402   {"photo-flip-y", "Show reverse side of the board, up-down flip",
00403    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00404 #define HA_photo_flip_y 14
00405 
00406 /* %start-doc options "93 PNG Options"
00407 @ftable @code
00408 @cindex photo-mask-colour
00409 @item --photo-mask-colour <colour>
00410 In photo-realistic mode, export the solder mask as this colour.
00411 Parameter @code{<colour>} can be @samp{green}, @samp{red}, @samp{blue},
00412 @samp{purple}, @samp{black}, or @samp{white}.
00413 @end ftable
00414 %end-doc
00415 */
00416   {"photo-mask-colour", "Colour for the exported colour mask",
00417    HID_Enum, 0, 0, {0, 0, 0}, mask_colour_names, 0},
00418 #define HA_photo_mask_colour 15
00419 
00420 /* %start-doc options "93 PNG Options"
00421 @ftable @code
00422 @cindex photo-plating
00423 @item --photo-plating <colour>
00424 In photo-realistic mode, export the exposed copper as though it has this type
00425 of plating. Parameter @code{<colour>} can be @samp{tinned}, @samp{gold},
00426 @samp{silver}, or @samp{copper}.
00427 @end ftable
00428 %end-doc
00429 */
00430   {"photo-plating", "Type of plating applied to exposed copper in photo-mode",
00431    HID_Enum, 0, 0, {0, 0, 0}, plating_type_names, 0},
00432 #define HA_photo_plating 16
00433 
00434 /* %start-doc options "93 PNG Options"
00435 @ftable @code
00436 @cindex photo-silk-colour
00437 @item --photo-silk-colour <colour>
00438 In photo-realistic mode, export the silk screen as this colour. Parameter
00439 @code{<colour>} can be @samp{white}, @samp{black}, or @samp{yellow}.
00440 @end ftable
00441 %end-doc
00442 */
00443   {"photo-silk-colour", "Colour for the exported colour mask",
00444    HID_Enum, 0, 0, {0, 0, 0}, silk_colour_names, 0},
00445 #define HA_photo_silk_colour 17
00446 
00447   {"ben-mode", ATTR_UNDOCUMENTED,
00448    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00449 #define HA_ben_mode 12
00450 
00451   {"ben-flip-x", ATTR_UNDOCUMENTED,
00452    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00453 #define HA_ben_flip_x 13
00454 
00455   {"ben-flip-y", ATTR_UNDOCUMENTED,
00456    HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
00457 #define HA_ben_flip_y 14
00458 };
00459 
00460 #define NUM_OPTIONS (sizeof(png_attribute_list)/sizeof(png_attribute_list[0]))
00461 
00462 REGISTER_ATTRIBUTES (png_attribute_list)
00463 
00464 static HID_Attr_Val png_values[NUM_OPTIONS];
00465 
00466 static const char *get_file_suffix(void)
00467 {
00468   const char *result = NULL;
00469   const char *fmt;
00470 
00471   fmt = filetypes[png_attribute_list[HA_filetype].default_val.int_value];
00472 
00473   if (fmt == NULL)
00474     ; /* Do nothing */
00475   else if (strcmp (fmt, FMT_gif) == 0)
00476     result=".gif";
00477   else if (strcmp (fmt, FMT_jpg) == 0)
00478     result=".jpg";
00479   else if (strcmp (fmt, FMT_png) == 0)
00480     result=".png";
00481 
00482   if (result == NULL)
00483     {
00484       fprintf (stderr, "Error:  Invalid graphic file format\n");
00485       result=".???";
00486     }
00487   return result;
00488 }
00489 
00490 static HID_Attribute *
00491 png_get_export_options (int *n)
00492 {
00493   static char *last_made_filename = 0;
00494   const char *suffix = get_file_suffix();
00495 
00496   if (PCB)
00497     derive_default_filename (PCB->Filename,
00498                              &png_attribute_list[HA_pngfile],
00499                              suffix,
00500                              &last_made_filename);
00501 
00502   if (n)
00503     *n = NUM_OPTIONS;
00504   return png_attribute_list;
00505 }
00506 
00507 static int top_group, bottom_group;
00508 
00509 static int
00510 layer_stack_sort (const void *va, const void *vb)
00511 {
00512   int a_layer = *(int *) va;
00513   int b_layer = *(int *) vb;
00514   int a_group = GetLayerGroupNumberByNumber (a_layer);
00515   int b_group = GetLayerGroupNumberByNumber (b_layer);
00516   int aside = (a_group == bottom_group ? 0 : a_group == top_group ? 2 : 1);
00517   int bside = (b_group == bottom_group ? 0 : b_group == top_group ? 2 : 1);
00518 
00519   if (bside != aside)
00520     return bside - aside;
00521 
00522   if (b_group != a_group)
00523     return b_group - a_group;
00524 
00525   return b_layer - a_layer;
00526 }
00527 
00528 static const char *filename;
00529 static BoxType *bounds;
00530 static int in_mono, as_shown, fill_holes;
00531 
00532 static void
00533 parse_bloat (const char *str)
00534 {
00535   UnitList extra_units = {
00536     { "pix", scale, 0 },
00537     { "px", scale, 0 },
00538     { "", 0, 0 }
00539   };
00540   if (str == NULL)
00541     return;
00542   bloat = GetValueEx (str, NULL, NULL, extra_units, "");
00543 }
00544 
00545 void
00546 png_hid_export_to_file (FILE * the_file, HID_Attr_Val * options)
00547 {
00548   int i;
00549   static int saved_layer_stack[MAX_LAYER];
00550   int saved_show_bottom_side;
00551   BoxType region;
00552   FlagType save_flags;
00553 
00554   f = the_file;
00555 
00556   region.X1 = 0;
00557   region.Y1 = 0;
00558   region.X2 = PCB->MaxWidth;
00559   region.Y2 = PCB->MaxHeight;
00560 
00561   if (options[HA_only_visible].int_value)
00562     bounds = GetDataBoundingBox (PCB->Data);    
00563   else
00564     bounds = &region;
00565 
00566   memset (print_group, 0, sizeof (print_group));
00567   memset (print_layer, 0, sizeof (print_layer));
00568 
00569   for (i = 0; i < max_copper_layer; i++)
00570     {
00571       LayerType *layer = PCB->Data->Layer + i;
00572       if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
00573         print_group[GetLayerGroupNumberByNumber (i)] = 1;
00574     }
00575   print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
00576   print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
00577   for (i = 0; i < max_copper_layer; i++)
00578     if (print_group[GetLayerGroupNumberByNumber (i)])
00579       print_layer[i] = 1;
00580 
00581   memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
00582   save_flags = PCB->Flags;
00583   saved_show_bottom_side = Settings.ShowBottomSide;
00584 
00585   as_shown = options[HA_as_shown].int_value;
00586   fill_holes = options[HA_fill_holes].int_value;
00587 
00588   if (!options[HA_as_shown].int_value)
00589     {
00590       CLEAR_FLAG (SHOWMASKFLAG, PCB);
00591       Settings.ShowBottomSide = 0;
00592 
00593       top_group = GetLayerGroupNumberBySide (TOP_SIDE);
00594       bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
00595       qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
00596 
00597       CLEAR_FLAG(THINDRAWFLAG, PCB);
00598       CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
00599 
00600       if (photo_mode)
00601         {
00602           int i, n=0;
00603           SET_FLAG (SHOWMASKFLAG, PCB);
00604           photo_has_inners = 0;
00605           if (top_group < bottom_group)
00606             for (i = top_group; i <= bottom_group; i++)
00607               {
00608                 photo_groups[n++] = i;
00609                 if (i != top_group && i != bottom_group
00610                     && ! IsLayerGroupEmpty (i))
00611                   photo_has_inners = 1;
00612               }
00613           else
00614             for (i = top_group; i >= bottom_group; i--)
00615               {
00616                 photo_groups[n++] = i;
00617                 if (i != top_group && i != bottom_group
00618                     && ! IsLayerGroupEmpty (i))
00619                   photo_has_inners = 1;
00620               }
00621           if (!photo_has_inners)
00622             {
00623               photo_groups[1] = photo_groups[n - 1];
00624               n = 2;
00625             }
00626           photo_ngroups = n;
00627 
00628           if (photo_flip)
00629             {
00630               for (i=0, n=photo_ngroups-1; i<n; i++, n--)
00631                 {
00632                   int tmp = photo_groups[i];
00633                   photo_groups[i] = photo_groups[n];
00634                   photo_groups[n] = tmp;
00635                 }
00636             }
00637         }
00638     }
00639   linewidth = -1;
00640   lastbrush = (gdImagePtr)((void *) -1);
00641   lastcap = -1;
00642   lastgroup = -1;
00643   show_bottom_side = Settings.ShowBottomSide;
00644 
00645   in_mono = options[HA_mono].int_value;
00646 
00647   if (!photo_mode && Settings.ShowBottomSide)
00648     {
00649       int i, j;
00650       for (i=0, j=max_copper_layer-1; i<j; i++, j--)
00651         {
00652           int k = LayerStack[i];
00653           LayerStack[i] = LayerStack[j];
00654           LayerStack[j] = k;
00655         }
00656     }
00657 
00658   hid_expose_callback (&png_hid, bounds, 0);
00659 
00660   memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
00661   PCB->Flags = save_flags;
00662   Settings.ShowBottomSide = saved_show_bottom_side;
00663 }
00664 
00665 static void
00666 clip (color_struct *dest, color_struct *source)
00667 {
00668 #define CLIP(var) \
00669   dest->var = source->var;      \
00670   if (dest->var > 255) dest->var = 255; \
00671   if (dest->var < 0)   dest->var = 0;
00672 
00673   CLIP (r);
00674   CLIP (g);
00675   CLIP (b);
00676 #undef CLIP
00677 }
00678 
00679 static void
00680 blend (color_struct *dest, double a_amount, color_struct *a, color_struct *b)
00681 {
00682   dest->r = a->r * a_amount + b->r * (1 - a_amount);
00683   dest->g = a->g * a_amount + b->g * (1 - a_amount);
00684   dest->b = a->b * a_amount + b->b * (1 - a_amount);
00685 }
00686 
00687 static void
00688 multiply (color_struct *dest, color_struct *a, color_struct *b)
00689 {
00690   dest->r = (a->r * b->r) / 255;
00691   dest->g = (a->g * b->g) / 255;
00692   dest->b = (a->b * b->b) / 255;
00693 }
00694 
00695 static void
00696 add (color_struct *dest, double a_amount, const color_struct *a, double b_amount, const color_struct *b)
00697 {
00698   dest->r = a->r * a_amount + b->r * b_amount;
00699   dest->g = a->g * a_amount + b->g * b_amount;
00700   dest->b = a->b * a_amount + b->b * b_amount;
00701 
00702   clip (dest, dest);
00703 }
00704 
00705 static void
00706 subtract (color_struct *dest, double a_amount, const color_struct *a, double b_amount, const color_struct *b)
00707 {
00708   dest->r = a->r * a_amount - b->r * b_amount;
00709   dest->g = a->g * a_amount - b->g * b_amount;
00710   dest->b = a->b * a_amount - b->b * b_amount;
00711 
00712   clip (dest, dest);
00713 }
00714 
00715 static void
00716 rgb (color_struct *dest, int r, int g, int b)
00717 {
00718   dest->r = r;
00719   dest->g = g;
00720   dest->b = b;
00721 }
00722 
00723 static int smshadows[3][3] = {
00724   {  1,  20,   1 },
00725   { 10,   0, -10 },
00726   { -1, -20,  -1 },
00727 };
00728 
00729 static int shadows[5][5] = {
00730   {  1,  1,   1,   1, -1 },
00731   {  1,  1,   1,  -1, -1 },
00732   {  1,  1,   0,  -1, -1 },
00733   {  1, -1,  -1,  -1, -1 },
00734   { -1, -1,  -1,  -1, -1 },
00735 };
00736 
00737 /* black and white are 0 and 1 */
00738 #define TOP_SHADOW 2
00739 #define BOTTOM_SHADOW 3
00740 
00741 static void
00742 ts_bs (gdImagePtr im)
00743 {
00744   int x, y, sx, sy, si;
00745   for (x=0; x<gdImageSX(im); x++)
00746     for (y=0; y<gdImageSY(im); y++)
00747       {
00748         si = 0;
00749         for (sx=-2; sx<3; sx++)
00750           for (sy=-2; sy<3; sy++)
00751             if (!gdImageGetPixel (im, x+sx, y+sy))
00752               si += shadows[sx+2][sy+2];
00753         if (gdImageGetPixel (im, x, y))
00754           {
00755             if (si > 1)
00756               gdImageSetPixel (im, x, y, TOP_SHADOW);
00757             else if (si < -1)
00758               gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
00759           }
00760       }
00761 }
00762 
00763 static void
00764 ts_bs_sm (gdImagePtr im)
00765 {
00766   int x, y, sx, sy, si;
00767   for (x=0; x<gdImageSX(im); x++)
00768     for (y=0; y<gdImageSY(im); y++)
00769       {
00770         si = 0;
00771         for (sx=-1; sx<2; sx++)
00772           for (sy=-1; sy<2; sy++)
00773             if (!gdImageGetPixel (im, x+sx, y+sy))
00774               si += smshadows[sx+1][sy+1];
00775         if (gdImageGetPixel (im, x, y))
00776           {
00777             if (si > 1)
00778               gdImageSetPixel (im, x, y, TOP_SHADOW);
00779             else if (si < -1)
00780               gdImageSetPixel (im, x, y, BOTTOM_SHADOW);
00781           }
00782       }
00783 }
00784 
00785 static void
00786 png_do_export (HID_Attr_Val * options)
00787 {
00788   int save_ons[MAX_ALL_LAYER];
00789   int i;
00790   BoxType *bbox;
00791   Coord w, h;
00792   Coord xmax, ymax;
00793   int dpi;
00794   const char *fmt;
00795   bool format_error = false;
00796 
00797   if (color_cache)
00798     {
00799       free (color_cache);
00800       color_cache = NULL;
00801     }
00802 
00803   if (brush_cache)
00804     {
00805       free (brush_cache);
00806       brush_cache = NULL;
00807     }
00808 
00809   if (!options)
00810     {
00811       png_get_export_options (0);
00812       for (i = 0; i < NUM_OPTIONS; i++)
00813         png_values[i] = png_attribute_list[i].default_val;
00814       options = png_values;
00815     }
00816 
00817   if (options[HA_photo_mode].int_value
00818       || options[HA_ben_mode].int_value)
00819     {
00820       photo_mode = 1;
00821       options[HA_mono].int_value = 1;
00822       options[HA_as_shown].int_value = 0;
00823       memset (photo_copper, 0, sizeof(photo_copper));
00824       photo_silk = photo_mask = photo_drill = 0;
00825       photo_outline = 0;
00826       if (options[HA_photo_flip_x].int_value
00827           || options[HA_ben_flip_x].int_value)
00828         photo_flip = PHOTO_FLIP_X;
00829       else if (options[HA_photo_flip_y].int_value
00830                || options[HA_ben_flip_y].int_value)
00831         photo_flip = PHOTO_FLIP_Y;
00832       else
00833         photo_flip = 0;
00834     }
00835   else
00836     photo_mode = 0;
00837 
00838   filename = options[HA_pngfile].str_value;
00839   if (!filename)
00840     filename = "pcb-out.png";
00841 
00842   /* figure out width and height of the board */
00843   if (options[HA_only_visible].int_value)
00844     {
00845       bbox = GetDataBoundingBox (PCB->Data);
00846       if (bbox == NULL)
00847         {
00848           fprintf (stderr, _("ERROR:  Unable to determine bounding box limits\n"));
00849           fprintf (stderr, _("ERROR:  Does the file contain any data?\n"));
00850           return;
00851         }
00852       x_shift = bbox->X1;
00853       y_shift = bbox->Y1;
00854       h = bbox->Y2 - bbox->Y1;
00855       w = bbox->X2 - bbox->X1;
00856     }
00857   else
00858     {
00859       x_shift = 0;
00860       y_shift = 0;
00861       h = PCB->MaxHeight;
00862       w = PCB->MaxWidth;
00863     }
00864 
00865   /*
00866    * figure out the scale factor we need to make the image
00867    * fit in our specified PNG file size
00868    */
00869   xmax = ymax = dpi = 0;
00870   if (options[HA_dpi].int_value != 0)
00871     {
00872       dpi = options[HA_dpi].int_value;
00873       if (dpi < 0)
00874         {
00875           fprintf (stderr, "ERROR:  dpi may not be < 0\n");
00876           return;
00877         }
00878     }
00879 
00880   if (options[HA_xmax].int_value > 0)
00881     {
00882       xmax = options[HA_xmax].int_value;
00883       dpi = 0;
00884     }
00885 
00886   if (options[HA_ymax].int_value > 0)
00887     {
00888       ymax = options[HA_ymax].int_value;
00889       dpi = 0;
00890     }
00891 
00892   if (options[HA_xymax].int_value > 0)
00893     {
00894       dpi = 0;
00895       if (options[HA_xymax].int_value < xmax || xmax == 0)
00896         xmax = options[HA_xymax].int_value;
00897       if (options[HA_xymax].int_value < ymax || ymax == 0)
00898         ymax = options[HA_xymax].int_value;
00899     }
00900 
00901   if (xmax < 0 || ymax < 0)
00902     {
00903       fprintf (stderr, "ERROR:  xmax and ymax may not be < 0\n");
00904       return;
00905     }
00906     
00907   if (dpi > 0)
00908     {
00909       /*
00910        * a scale of 1  means 1 pixel is 1 inch
00911        * a scale of 10 means 1 pixel is 10 inches
00912        */
00913       scale = round(INCH_TO_COORD(1) / (double) dpi);
00914       w = w / scale;
00915       h = h / scale;
00916     }
00917   else if( xmax == 0 && ymax == 0)
00918     {
00919       fprintf(stderr, "ERROR:  You may not set both xmax, ymax,"
00920               "and xy-max to zero\n");
00921       return;
00922     }
00923   else
00924     {
00925       if (ymax == 0 
00926           || ( (xmax > 0) 
00927                && ((w / xmax) > (h / ymax)) ) )
00928         {
00929           scale = w / xmax;
00930           h = h / scale;
00931           w = xmax;
00932         }
00933       else
00934         {
00935           scale = h / ymax;
00936           w = w / scale;
00937           h = ymax;
00938         }
00939     }
00940 
00941   im = gdImageCreate (w, h);
00942   if (im == NULL) 
00943     {
00944       Message ("%s():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", __FUNCTION__, w, h);
00945       return;
00946     }
00947   
00948   master_im = im;
00949 
00950   parse_bloat (options[HA_bloat].str_value);
00951 
00952   /* 
00953    * Allocate white and black -- the first color allocated
00954    * becomes the background color
00955    */
00956 
00957   white = (color_struct *) malloc (sizeof (color_struct));
00958   white->r = white->g = white->b = 255;
00959   if (options[HA_use_alpha].int_value)
00960     white->a = 127;
00961   else
00962     white->a = 0;
00963   white->c = gdImageColorAllocateAlpha (im, white->r, white->g, white->b, white->a);
00964   if (white->c == BADC) 
00965     {
00966       Message ("%s():  gdImageColorAllocateAlpha() returned NULL.  Aborting export.\n", __FUNCTION__);
00967       return;
00968     }
00969 
00970   gdImageFilledRectangle (im, 0, 0, gdImageSX (im), gdImageSY (im), white->c);
00971 
00972   black = (color_struct *) malloc (sizeof (color_struct));
00973   black->r = black->g = black->b = black->a = 0;
00974   black->c = gdImageColorAllocate (im, black->r, black->g, black->b);
00975   if (black->c == BADC) 
00976     {
00977       Message ("%s():  gdImageColorAllocateAlpha() returned NULL.  Aborting export.\n", __FUNCTION__);
00978       return;
00979     }
00980 
00981   f = fopen (filename, "wb");
00982   if (!f)
00983     {
00984       perror (filename);
00985       return;
00986     }
00987 
00988   if (!options[HA_as_shown].int_value)
00989     hid_save_and_show_layer_ons (save_ons);
00990 
00991   png_hid_export_to_file (f, options);
00992 
00993   if (!options[HA_as_shown].int_value)
00994     hid_restore_layer_ons (save_ons);
00995 
00996   if (photo_mode)
00997     {
00998       int x, y;
00999       color_struct white, black, fr4;
01000 
01001       rgb (&white, 255, 255, 255);
01002       rgb (&black, 0, 0, 0);
01003       rgb (&fr4, 70, 70, 70);
01004 
01005       im = master_im;
01006 
01007       if (photo_copper[photo_groups[0]])
01008         ts_bs (photo_copper[photo_groups[0]]);
01009       if (photo_silk)
01010         ts_bs (photo_silk);
01011       if (photo_mask)
01012         ts_bs_sm (photo_mask);
01013 
01014       if (photo_outline && have_outline) {
01015         int black=gdImageColorResolve(photo_outline, 0x00, 0x00, 0x00);
01016 
01017         // go all the way around the image, trying to fill the outline
01018         for (x=0; x<gdImageSX(im); x++) {
01019           gdImageFillToBorder(photo_outline, x, 0, black, black);
01020           gdImageFillToBorder(photo_outline, x, gdImageSY(im)-1, black, black);
01021         }
01022         for (y=1; y<gdImageSY(im)-1; y++) {
01023           gdImageFillToBorder(photo_outline, 0, y, black, black);
01024           gdImageFillToBorder(photo_outline, gdImageSX(im)-1, y, black, black);
01025 
01026         }
01027       }
01028 
01029 
01030       for (x=0; x<gdImageSX (im); x++) 
01031         {
01032           for (y=0; y<gdImageSY (im); y++)
01033             {
01034               color_struct p, cop;
01035               color_struct mask_colour, silk_colour;
01036               int cc, mask, silk;
01037               int transparent;
01038              
01039               if (photo_outline && have_outline) {
01040                 transparent=gdImageGetPixel(photo_outline, x, y);             
01041               } else {
01042                 transparent=0;
01043               }
01044 
01045               mask = photo_mask ? gdImageGetPixel (photo_mask, x, y) : 0;
01046               silk = photo_silk ? gdImageGetPixel (photo_silk, x, y) : 0;
01047 
01048               if (photo_copper[photo_groups[1]]
01049                   && gdImageGetPixel (photo_copper[photo_groups[1]], x, y))
01050                 rgb (&cop, 40, 40, 40);
01051               else
01052                 rgb (&cop, 100, 100, 110);
01053 
01054               if (photo_ngroups == 2)
01055                 blend (&cop, 0.3, &cop, &fr4);
01056               
01057               if (photo_copper[photo_groups[0]])
01058                 cc = gdImageGetPixel (photo_copper[photo_groups[0]], x, y);
01059               else
01060                 cc = 0;
01061 
01062               if (cc)
01063                 {
01064                   int r;
01065                   
01066                   if (mask)
01067                     rgb (&cop, 220, 145, 230);
01068                   else
01069                     {
01070                       if (options[HA_photo_plating].int_value == PLATING_GOLD)
01071                         {
01072                           // ENIG
01073                           rgb (&cop, 185, 146, 52);
01074 
01075                           // increase top shadow to increase shininess
01076                           if (cc == TOP_SHADOW)
01077                             blend (&cop, 0.7, &cop, &white);
01078                         }
01079                       else if (options[HA_photo_plating].int_value == PLATING_TIN)
01080                         {
01081                           // tinned
01082                           rgb (&cop, 140, 150, 160);
01083 
01084                           // add some variation to make it look more matte
01085                           r = (rand() % 5 - 2) * 2;
01086                           cop.r += r;
01087                           cop.g += r;
01088                           cop.b += r;
01089                         }
01090                       else if (options[HA_photo_plating].int_value == PLATING_SILVER)
01091                         {
01092                           // silver
01093                           rgb (&cop, 192, 192, 185);
01094 
01095                           // increase top shadow to increase shininess
01096                           if (cc == TOP_SHADOW)
01097                             blend (&cop, 0.7, &cop, &white);
01098                         }
01099                       else if (options[HA_photo_plating].int_value == PLATING_COPPER)
01100                         {
01101                           // copper
01102                           rgb (&cop, 184, 115, 51);
01103 
01104                           // increase top shadow to increase shininess
01105                           if (cc == TOP_SHADOW)
01106                             blend (&cop, 0.7, &cop, &white);
01107                         }
01108                     }
01109                   
01110                   if (cc == TOP_SHADOW)
01111                     blend (&cop, 0.7, &cop, &white);
01112                   if (cc == BOTTOM_SHADOW)
01113                     blend (&cop, 0.7, &cop, &black);
01114                 }
01115 
01116               if (photo_drill && !gdImageGetPixel (photo_drill, x, y)) 
01117                 {               
01118                   rgb (&p, 0, 0, 0);
01119                   transparent=1;
01120                 }
01121               else if (silk)
01122                 {
01123                   silk_colour = silk_colours[options[HA_photo_silk_colour].int_value];
01124                   blend (&p, 1.0, &silk_colour, &silk_colour);
01125                   if (silk == TOP_SHADOW)
01126                     add (&p, 1.0, &p, 1.0, &silk_top_shadow);
01127                   else if (silk == BOTTOM_SHADOW)
01128                     subtract (&p, 1.0, &p, 1.0, &silk_bottom_shadow);
01129                 }
01130               else if (mask)
01131                 {
01132                   p = cop;
01133                   mask_colour = mask_colours[options[HA_photo_mask_colour].int_value];
01134                   multiply (&p, &p, &mask_colour);
01135                   add (&p, 1, &p, 0.2, &mask_colour);
01136                   if (mask == TOP_SHADOW)
01137                     blend (&p, 0.7, &p, &white);
01138                   if (mask == BOTTOM_SHADOW)
01139                     blend (&p, 0.7, &p, &black);
01140                 }
01141               else
01142                 p = cop;
01143               
01144               if (options[HA_use_alpha].int_value) {
01145 
01146                 cc = (transparent)?\
01147                   gdImageColorResolveAlpha(im, 0, 0, 0, 127):\
01148                   gdImageColorResolveAlpha(im, p.r, p.g, p.b, 0);
01149 
01150               } else {
01151                 cc = (transparent)?\
01152                   gdImageColorResolve(im, 0, 0, 0):\
01153                   gdImageColorResolve(im, p.r, p.g, p.b);
01154               }           
01155 
01156               if (photo_flip == PHOTO_FLIP_X)
01157                 gdImageSetPixel (im, gdImageSX (im) - x - 1, y, cc);
01158               else if (photo_flip == PHOTO_FLIP_Y)
01159                 gdImageSetPixel (im, x, gdImageSY (im) - y - 1, cc);
01160               else
01161                 gdImageSetPixel (im, x, y, cc);
01162             }
01163         }
01164     }
01165 
01166   /* actually write out the image */
01167   fmt = filetypes[options[HA_filetype].int_value];
01168 
01169   if (fmt == NULL)
01170     format_error = true;
01171   else if (strcmp (fmt, FMT_gif) == 0)
01172 #ifdef HAVE_GDIMAGEGIF
01173     gdImageGif (im, f);
01174 #else
01175     format_error = true;
01176 #endif
01177   else if (strcmp (fmt, FMT_jpg) == 0)
01178 #ifdef HAVE_GDIMAGEJPEG
01179     gdImageJpeg (im, f, -1);
01180 #else
01181     format_error = true;
01182 #endif
01183   else if (strcmp (fmt, FMT_png) == 0)
01184 #ifdef HAVE_GDIMAGEPNG
01185     gdImagePng (im, f);
01186 #else
01187     format_error = true;
01188 #endif
01189   else
01190     format_error = true;
01191 
01192   if (format_error)
01193     fprintf (stderr, "Error:  Invalid graphic file format."
01194                      "  This is a bug.  Please report it.\n");
01195 
01196   fclose (f);
01197 
01198   gdImageDestroy (im);
01199 }
01200 
01201 static void
01202 png_parse_arguments (int *argc, char ***argv)
01203 {
01204   hid_register_attributes (png_attribute_list,
01205                            sizeof (png_attribute_list) /
01206                            sizeof (png_attribute_list[0]));
01207   hid_parse_command_line (argc, argv);
01208 }
01209 
01210 
01211 static int is_mask;
01212 static int is_drill;
01213 static int is_copper;
01214 
01215 static int
01216 png_set_layer (const char *name, int group, int empty)
01217 {
01218   int idx = (group >= 0
01219              && group <
01220              max_group) ? PCB->LayerGroups.Entries[group][0] : group;
01221   if (name == 0)
01222     name = PCB->Data->Layer[idx].Name;
01223 
01224   doing_outline = 0;
01225 
01226   if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
01227     return 0;
01228   if (SL_TYPE (idx) == SL_ASSY || SL_TYPE (idx) == SL_FAB)
01229     return 0;
01230 
01231   if (strcmp (name, "invisible") == 0)
01232     return 0;
01233 
01234   is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
01235   is_mask = (SL_TYPE (idx) == SL_MASK);
01236   is_copper = (SL_TYPE (idx) == 0);
01237 
01238   if (is_drill && fill_holes)
01239     return 0;
01240 
01241   if (SL_TYPE (idx) == SL_PASTE)
01242     return 0;
01243 
01244   if (photo_mode)
01245     {
01246       switch (idx)
01247         {
01248         case SL (SILK, TOP):
01249           if (photo_flip)
01250             return 0;
01251           photo_im = &photo_silk;
01252           break;
01253         case SL (SILK, BOTTOM):
01254           if (!photo_flip)
01255             return 0;
01256           photo_im = &photo_silk;
01257           break;
01258 
01259         case SL (MASK, TOP):
01260           if (photo_flip)
01261             return 0;
01262           photo_im = &photo_mask;
01263           break;
01264         case SL (MASK, BOTTOM):
01265           if (!photo_flip)
01266             return 0;
01267           photo_im = &photo_mask;
01268           break;
01269 
01270         case SL (PDRILL, 0):
01271         case SL (UDRILL, 0):
01272           photo_im = &photo_drill;
01273           break;
01274 
01275         default:
01276           if (idx < 0)
01277             return 0;
01278 
01279           if (strcmp (name, "outline") == 0)
01280             {
01281               doing_outline = 1;
01282               have_outline = 0;
01283               photo_im = &photo_outline;
01284             }
01285           else
01286             photo_im = photo_copper + group;
01287 
01288           break;
01289         }
01290 
01291       if (! *photo_im)
01292         {
01293           static color_struct *black = NULL, *white = NULL;
01294           *photo_im = gdImageCreate (gdImageSX (im), gdImageSY (im));
01295           if (photo_im == NULL) 
01296             {
01297               Message ("%s():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", __FUNCTION__, 
01298                        gdImageSX (im), gdImageSY (im));
01299               return 0;
01300             }
01301 
01302 
01303           white = (color_struct *) malloc (sizeof (color_struct));
01304           white->r = white->g = white->b = 255;
01305           white->a = 0;
01306           white->c = gdImageColorAllocate (*photo_im, white->r, white->g, white->b);
01307           if (white->c == BADC) 
01308             {
01309               Message ("%s():  gdImageColorAllocate() returned NULL.  Aborting export.\n", __FUNCTION__);
01310               return 0;
01311             }
01312 
01313           black = (color_struct *) malloc (sizeof (color_struct));
01314           black->r = black->g = black->b = black->a = 0;
01315           black->c = gdImageColorAllocate (*photo_im, black->r, black->g, black->b);
01316           if (black->c == BADC) 
01317             {
01318               Message ("%s(): gdImageColorAllocate() returned NULL.  Aborting export.\n", __FUNCTION__);
01319               return 0;
01320             }
01321 
01322           if (idx == SL (PDRILL, 0)
01323               || idx == SL (UDRILL, 0))
01324             gdImageFilledRectangle (*photo_im, 0, 0, gdImageSX (im), gdImageSY (im), black->c);
01325         }
01326       im = *photo_im;
01327       return 1;
01328     }
01329 
01330   if (as_shown)
01331     {
01332       switch (idx)
01333         {
01334         case SL (SILK, TOP):
01335         case SL (SILK, BOTTOM):
01336           if (SL_MYSIDE (idx))
01337             return PCB->ElementOn;
01338           return 0;
01339 
01340         case SL (MASK, TOP):
01341         case SL (MASK, BOTTOM):
01342           return TEST_FLAG (SHOWMASKFLAG, PCB) && SL_MYSIDE (idx);
01343         }
01344     }
01345   else
01346     {
01347       if (is_mask)
01348         return 0;
01349 
01350       switch (idx)
01351         {
01352         case SL (SILK, TOP):
01353           return 1;
01354         case SL (SILK, BOTTOM):
01355           return 0;
01356         }
01357     }
01358 
01359   return 1;
01360 }
01361 
01362 
01363 static hidGC
01364 png_make_gc (void)
01365 {
01366   hidGC rv = (hidGC) malloc (sizeof (hid_gc_struct));
01367   rv->me_pointer = &png_hid;
01368   rv->cap = Trace_Cap;
01369   rv->width = 1;
01370   rv->color = (color_struct *) malloc (sizeof (color_struct));
01371   rv->color->r = rv->color->g = rv->color->b = rv->color->a = 0;
01372   rv->color->c = 0;
01373   rv->is_erase = 0;
01374   return rv;
01375 }
01376 
01377 static void
01378 png_destroy_gc (hidGC gc)
01379 {
01380   free (gc);
01381 }
01382 
01383 static void
01384 png_use_mask (enum mask_mode mode)
01385 {
01386   if (photo_mode)
01387     return;
01388 
01389   if (mode == HID_MASK_CLEAR)
01390     {
01391       return;
01392     }
01393   if (mode != HID_MASK_OFF)
01394     {
01395       if (mask_im == NULL)
01396         {
01397           mask_im = gdImageCreate (gdImageSX (im), gdImageSY (im));
01398           if (!mask_im)
01399             {
01400               Message ("%s():  gdImageCreate(%d, %d) returned NULL.  Corrupt export!\n",
01401                        __FUNCTION__, gdImageSY (im), gdImageSY (im));
01402               return;
01403             }
01404           gdImagePaletteCopy (mask_im, im);
01405         }
01406       im = mask_im;
01407       gdImageFilledRectangle (mask_im, 0, 0, gdImageSX (mask_im), gdImageSY (mask_im), white->c);
01408     }
01409   else
01410     {
01411       int x, y, c;
01412 
01413       im = master_im;
01414 
01415       for (x=0; x<gdImageSX (im); x++)
01416         for (y=0; y<gdImageSY (im); y++)
01417           {
01418             c = gdImageGetPixel (mask_im, x, y);
01419             if (c)
01420               gdImageSetPixel (im, x, y, c);
01421           }
01422     }
01423 }
01424 
01425 static void
01426 png_set_color (hidGC gc, const char *name)
01427 {
01428   hidval cval;
01429 
01430   if (im == NULL)
01431     return;
01432 
01433   if (name == NULL)
01434     name = "#ff0000";
01435 
01436   if (strcmp (name, "erase") == 0 || strcmp (name, "drill") == 0)
01437     {
01438       gc->color = white;
01439       gc->is_erase = 1;
01440       return;
01441     }
01442   gc->is_erase = 0;
01443 
01444   if (in_mono || (strcmp (name, "#000000") == 0))
01445     {
01446       gc->color = black;
01447       return;
01448     }
01449 
01450   if (hid_cache_color (0, name, &cval, &color_cache))
01451     {
01452       gc->color = (color_struct *)cval.ptr;
01453     }
01454   else if (name[0] == '#')
01455     {
01456       gc->color = (color_struct *) malloc (sizeof (color_struct));
01457       sscanf (name + 1, "%2x%2x%2x", &(gc->color->r), &(gc->color->g),
01458               &(gc->color->b));
01459       gc->color->c =
01460         gdImageColorAllocate (master_im, gc->color->r, gc->color->g, gc->color->b);
01461       if (gc->color->c == BADC) 
01462         {
01463           Message ("%s():  gdImageColorAllocate() returned NULL.  Aborting export.\n", __FUNCTION__);
01464           return;
01465         }
01466       cval.ptr = gc->color;
01467       hid_cache_color (1, name, &cval, &color_cache);
01468     }
01469   else
01470     {
01471       printf ("WE SHOULD NOT BE HERE!!!\n");
01472       gc->color = black;
01473     }
01474 
01475 }
01476 
01477 static void
01478 png_set_line_cap (hidGC gc, EndCapStyle style)
01479 {
01480   gc->cap = style;
01481 }
01482 
01483 static void
01484 png_set_line_width (hidGC gc, Coord width)
01485 {
01486   gc->width = width;
01487 }
01488 
01489 static void
01490 png_set_draw_xor (hidGC gc, int xor_)
01491 {
01492   ;
01493 }
01494 
01495 static void
01496 use_gc (hidGC gc)
01497 {
01498   int need_brush = 0;
01499 
01500   if (gc->me_pointer != &png_hid)
01501     {
01502       fprintf (stderr, "Fatal: GC from another HID passed to png HID\n");
01503       abort ();
01504     }
01505 
01506   if (linewidth != gc->width)
01507     {
01508       /* Make sure the scaling doesn't erase lines completely */
01509       if (SCALE (gc->width) == 0 && gc->width > 0)
01510         gdImageSetThickness (im, 1);
01511       else
01512         gdImageSetThickness (im, SCALE (gc->width + 2*bloat));
01513       linewidth = gc->width;
01514       need_brush = 1;
01515     }
01516 
01517   if (lastbrush != gc->brush || need_brush)
01518     {
01519       hidval bval;
01520       char name[256];
01521       char type;
01522       int r;
01523 
01524       switch (gc->cap)
01525         {
01526         case Round_Cap:
01527         case Trace_Cap:
01528           type = 'C';
01529           break;
01530         default:
01531         case Square_Cap:
01532           type = 'S';
01533           break;
01534         }
01535       if (gc->width)
01536         r = SCALE (gc->width + 2*bloat);
01537       else
01538         r = 1;
01539 
01540       /* do not allow a brush size that is zero width.  In this case limit to a single pixel. */
01541       if (r == 0)
01542         {
01543           r = 1;
01544         }
01545 
01546       sprintf (name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
01547                gc->color->b, type, r);
01548 
01549       if (hid_cache_color (0, name, &bval, &brush_cache))
01550         {
01551           gc->brush = (gdImagePtr)bval.ptr;
01552         }
01553       else
01554         {
01555           int bg, fg;
01556           gc->brush = gdImageCreate (r, r);
01557           if (gc->brush == NULL) 
01558             {
01559               Message ("%s():  gdImageCreate(%d, %d) returned NULL.  Aborting export.\n", __FUNCTION__, r, r);
01560               return;
01561             }
01562 
01563           bg = gdImageColorAllocate (gc->brush, 255, 255, 255);
01564           if (bg == BADC) 
01565             {
01566               Message ("%s():  gdImageColorAllocate() returned NULL.  Aborting export.\n", __FUNCTION__);
01567               return;
01568             }
01569           fg =
01570             gdImageColorAllocateAlpha (gc->brush, gc->color->r, gc->color->g,
01571                                        gc->color->b, 0); 
01572           if (fg == BADC) 
01573             {
01574               Message ("%s():  gdImageColorAllocate() returned NULL.  Aborting export.\n", __FUNCTION__);
01575               return;
01576             }
01577           gdImageColorTransparent (gc->brush, bg);
01578 
01579           /*
01580            * if we shrunk to a radius/box width of zero, then just use
01581            * a single pixel to draw with.
01582            */
01583           if (r <= 1)
01584             gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg);
01585           else
01586             {
01587               if (type == 'C')
01588                 {
01589                   gdImageFilledEllipse (gc->brush, r/2, r/2, r, r, fg);
01590                   /* Make sure the ellipse is the right exact size.  */
01591                   gdImageSetPixel (gc->brush, 0, r/2, fg);
01592                   gdImageSetPixel (gc->brush, r-1, r/2, fg);
01593                   gdImageSetPixel (gc->brush, r/2, 0, fg);
01594                   gdImageSetPixel (gc->brush, r/2, r-1, fg);
01595                 }
01596               else
01597                 gdImageFilledRectangle (gc->brush, 0, 0, r-1, r-1, fg);
01598             }
01599           bval.ptr = gc->brush;
01600           hid_cache_color (1, name, &bval, &brush_cache);
01601         }
01602 
01603       gdImageSetBrush (im, gc->brush);
01604       lastbrush = gc->brush;
01605 
01606     }
01607 }
01608 
01609 static void
01610 png_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01611 {
01612   use_gc (gc);
01613   gdImageRectangle (im,
01614                     SCALE_X (x1), SCALE_Y (y1),
01615                     SCALE_X (x2), SCALE_Y (y2), gc->color->c);
01616 }
01617 
01618 static void
01619 png_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01620 {
01621   use_gc (gc);
01622   gdImageSetThickness (im, 0);
01623   linewidth = 0;
01624 
01625   y1 -= bloat;
01626   y2 += bloat;
01627   SWAP_IF_SOLDER (y1, y2);
01628 
01629   gdImageFilledRectangle (im, SCALE_X (x1-bloat), SCALE_Y (y1),
01630                           SCALE_X (x2+bloat)-1, SCALE_Y (y2)-1, gc->color->c);
01631   have_outline |= doing_outline;
01632 }
01633 
01634 static void
01635 png_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
01636 {
01637   if (x1 == x2 && y1 == y2)
01638     {
01639       Coord w = gc->width / 2;
01640       if (gc->cap != Square_Cap)
01641         png_fill_circle (gc, x1, y1, w);
01642       else
01643         png_fill_rect (gc, x1 - w, y1 - w, x1 + w, y1 + w);
01644       return;
01645     }
01646   use_gc (gc);
01647 
01648   if (NOT_EDGE (x1, y1) || NOT_EDGE (x2, y2))
01649     have_outline |= doing_outline;
01650   if (doing_outline)
01651     {
01652       /* Special case - lines drawn along the bottom or right edges
01653          are brought in by a pixel to make sure we have contiguous
01654          outlines.  */
01655       if (x1 == PCB->MaxWidth && x2 == PCB->MaxWidth)
01656         {
01657           x1 -= scale/2;
01658           x2 -= scale/2;
01659         }
01660       if (y1 == PCB->MaxHeight && y2 == PCB->MaxHeight)
01661         {
01662           y1 -= scale/2;
01663           y2 -= scale/2;
01664         }
01665     }
01666 
01667   gdImageSetThickness (im, 0);
01668   linewidth = 0;
01669   if(gc->cap != Square_Cap || x1 == x2 || y1 == y2 )
01670     {
01671       gdImageLine (im, SCALE_X (x1), SCALE_Y (y1),
01672                    SCALE_X (x2), SCALE_Y (y2), gdBrushed);
01673     }
01674   else
01675     {
01676       /*
01677        * if we are drawing a line with a square end cap and it is
01678        * not purely horizontal or vertical, then we need to draw
01679        * it as a filled polygon.
01680        */
01681       int fg = gdImageColorResolve (im, gc->color->r, gc->color->g,
01682                                     gc->color->b);
01683       Coord w = gc->width;
01684       Coord dwx, dwy;
01685 
01686       gdPoint p[4];
01687       double l = Distance(x1, y1, x2, y2) * 2;
01688 
01689       w += 2 * bloat;
01690       dwx = -w / l * (y2 - y1); dwy =  w / l * (x2 - x1);
01691       p[0].x = SCALE_X (x1 + dwx - dwy); p[0].y = SCALE_Y(y1 + dwy + dwx);
01692       p[1].x = SCALE_X (x1 - dwx - dwy); p[1].y = SCALE_Y(y1 - dwy + dwx);
01693       p[2].x = SCALE_X (x2 - dwx + dwy); p[2].y = SCALE_Y(y2 - dwy - dwx);
01694       p[3].x = SCALE_X (x2 + dwx + dwy); p[3].y = SCALE_Y(y2 + dwy - dwx);
01695       gdImageFilledPolygon (im, p, 4, fg);
01696     }
01697 }
01698 
01699 static void
01700 png_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
01701               Angle start_angle, Angle delta_angle)
01702 {
01703   Angle sa, ea;
01704 
01705   /*
01706    * zero angle arcs need special handling as gd will output either
01707    * nothing at all or a full circle when passed delta angle of 0 or 360.
01708    */
01709   if (delta_angle == 0) {
01710     Coord x = (width * cos (start_angle * M_PI / 180));
01711     Coord y = (width * sin (start_angle * M_PI / 180));
01712     x = cx - x;
01713     y = cy + y;
01714     png_fill_circle (gc, x, y, gc->width / 2);
01715     return;
01716   }
01717 
01718   /* 
01719    * in gdImageArc, 0 degrees is to the right and +90 degrees is down
01720    * in pcb, 0 degrees is to the left and +90 degrees is down
01721    */
01722   start_angle = 180 - start_angle;
01723   delta_angle = -delta_angle;
01724   if (show_bottom_side)
01725     {
01726       start_angle = - start_angle;
01727       delta_angle = -delta_angle;
01728     }
01729   if (delta_angle > 0)
01730     {
01731       sa = start_angle;
01732       ea = start_angle + delta_angle;
01733     }
01734   else
01735     {
01736       sa = start_angle + delta_angle;
01737       ea = start_angle;
01738     }
01739 
01740   /* 
01741    * make sure we start between 0 and 360 otherwise gd does
01742    * strange things
01743    */
01744   sa = NormalizeAngle (sa);
01745   ea = NormalizeAngle (ea);
01746 
01747   have_outline |= doing_outline;
01748 
01749 #if 0
01750   printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
01751           cx, cy, width, height, start_angle, delta_angle, sa, ea);
01752   printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
01753           im, SCALE_X (cx), SCALE_Y (cy),
01754           SCALE (width), SCALE (height), sa, ea, gc->color->c);
01755 #endif
01756   use_gc (gc);
01757   gdImageSetThickness (im, 0);
01758   linewidth = 0;
01759   gdImageArc (im, SCALE_X (cx), SCALE_Y (cy),
01760               SCALE (2 * width), SCALE (2 * height), sa, ea, gdBrushed);
01761 }
01762 
01763 static void
01764 png_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
01765 {
01766   Coord my_bloat;
01767 
01768   use_gc (gc);
01769 
01770   if (fill_holes && gc->is_erase && is_copper)
01771     return;
01772 
01773   if (gc->is_erase)
01774     my_bloat = -2 * bloat;
01775   else
01776     my_bloat = 2 * bloat;
01777 
01778 
01779   have_outline |= doing_outline;
01780 
01781   gdImageSetThickness (im, 0);
01782   linewidth = 0;
01783   gdImageFilledEllipse (im, SCALE_X (cx), SCALE_Y (cy),
01784                         SCALE (2 * radius + my_bloat), SCALE (2 * radius + my_bloat), gc->color->c);
01785 
01786 }
01787 
01788 static void
01789 png_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
01790 {
01791   int i;
01792   gdPoint *points;
01793 
01794   points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
01795   if (points == NULL)
01796     {
01797       fprintf (stderr, "ERROR:  png_fill_polygon():  malloc failed\n");
01798       exit (1);
01799     }
01800 
01801   use_gc (gc);
01802   for (i = 0; i < n_coords; i++)
01803     {
01804       if (NOT_EDGE (x[i], y[i]))
01805         have_outline |= doing_outline;
01806       points[i].x = SCALE_X (x[i]);
01807       points[i].y = SCALE_Y (y[i]);
01808     }
01809   gdImageSetThickness (im, 0);
01810   linewidth = 0;
01811   gdImageFilledPolygon (im, points, n_coords, gc->color->c);
01812   free (points);
01813 }
01814 
01815 static void
01816 png_calibrate (double xval, double yval)
01817 {
01818   CRASH;
01819 }
01820 
01821 static void
01822 png_set_crosshair (int x, int y, int a)
01823 {
01824 }
01825 
01826 #include "dolists.h"
01827 
01828 void
01829 hid_png_init ()
01830 {
01831   memset (&png_hid, 0, sizeof (HID));
01832   memset (&png_graphics, 0, sizeof (HID_DRAW));
01833 
01834   common_nogui_init (&png_hid);
01835   common_draw_helpers_init (&png_graphics);
01836 
01837   png_hid.struct_size = sizeof (HID);
01838   png_hid.name        = "png";
01839   png_hid.description = "GIF/JPEG/PNG export";
01840   png_hid.exporter    = 1;
01841   png_hid.poly_before = 1;
01842 
01843   png_hid.get_export_options  = png_get_export_options;
01844   png_hid.do_export           = png_do_export;
01845   png_hid.parse_arguments     = png_parse_arguments;
01846   png_hid.set_layer           = png_set_layer;
01847   png_hid.calibrate           = png_calibrate;
01848   png_hid.set_crosshair       = png_set_crosshair;
01849 
01850   png_hid.graphics            = &png_graphics;
01851 
01852   png_graphics.make_gc        = png_make_gc;
01853   png_graphics.destroy_gc     = png_destroy_gc;
01854   png_graphics.use_mask       = png_use_mask;
01855   png_graphics.set_color      = png_set_color;
01856   png_graphics.set_line_cap   = png_set_line_cap;
01857   png_graphics.set_line_width = png_set_line_width;
01858   png_graphics.set_draw_xor   = png_set_draw_xor;
01859   png_graphics.draw_line      = png_draw_line;
01860   png_graphics.draw_arc       = png_draw_arc;
01861   png_graphics.draw_rect      = png_draw_rect;
01862   png_graphics.fill_circle    = png_fill_circle;
01863   png_graphics.fill_polygon   = png_fill_polygon;
01864   png_graphics.fill_rect      = png_fill_rect;
01865 
01866 #ifdef HAVE_SOME_FORMAT
01867   hid_register_hid (&png_hid);
01868 
01869 #include "png_lists.h"
01870 #endif
01871 }