pcb 4.1.1
An interactive printed circuit board layout editor.
|
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 = ®ion; 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 }