You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

554 lines
17 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2019 jean-pierre.charras
  5. * Copyright (C) 1992-2019 Kicad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <algorithm> // std::max
  25. #include <cerrno>
  26. #include <cmath>
  27. #include <cstdio>
  28. #include <cstdlib>
  29. #include <cstring>
  30. #include <string>
  31. #include <vector>
  32. #include <convert_to_biu.h>
  33. #include <layers_id_colors_and_visibility.h>
  34. #include <locale_io.h>
  35. #include <potracelib.h>
  36. #include "bitmap2component.h"
  37. /* free a potrace bitmap */
  38. static void bm_free( potrace_bitmap_t* bm )
  39. {
  40. if( bm != NULL )
  41. {
  42. free( bm->map );
  43. }
  44. free( bm );
  45. }
  46. static void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
  47. potrace_dpoint_t p1,
  48. potrace_dpoint_t p2,
  49. potrace_dpoint_t p3,
  50. potrace_dpoint_t p4 );
  51. BITMAPCONV_INFO::BITMAPCONV_INFO( std::string& aData ):
  52. m_Data( aData )
  53. {
  54. m_Format = POSTSCRIPT_FMT;
  55. m_PixmapWidth = 0;
  56. m_PixmapHeight = 0;
  57. m_ScaleX = 1.0;
  58. m_ScaleY = 1.0;
  59. m_Paths = NULL;
  60. m_CmpName = "LOGO";
  61. }
  62. int BITMAPCONV_INFO::ConvertBitmap( potrace_bitmap_t* aPotrace_bitmap,
  63. OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y,
  64. BMP2CMP_MOD_LAYER aModLayer )
  65. {
  66. potrace_param_t* param;
  67. potrace_state_t* st;
  68. // set tracing parameters, starting from defaults
  69. param = potrace_param_default();
  70. if( !param )
  71. {
  72. char msg[256];
  73. sprintf( msg, "Error allocating parameters: %s\n", strerror( errno ) );
  74. m_errors += msg;
  75. return 1;
  76. }
  77. // For parameters: see http://potrace.sourceforge.net/potracelib.pdf
  78. param->turdsize = 0; // area (in pixels) of largest path to be ignored.
  79. // Potrace default is 2
  80. param->opttolerance = 0.2; // curve optimization tolerance. Potrace default is 0.2
  81. /* convert the bitmap to curves */
  82. st = potrace_trace( param, aPotrace_bitmap );
  83. if( !st || st->status != POTRACE_STATUS_OK )
  84. {
  85. if( st )
  86. {
  87. potrace_state_free( st );
  88. }
  89. potrace_param_free( param );
  90. char msg[256];
  91. sprintf( msg, "Error tracing bitmap: %s\n", strerror( errno ) );
  92. m_errors += msg;
  93. return 1;
  94. }
  95. m_PixmapWidth = aPotrace_bitmap->w;
  96. m_PixmapHeight = aPotrace_bitmap->h; // the bitmap size in pixels
  97. m_Paths = st->plist;
  98. switch( aFormat )
  99. {
  100. case KICAD_LOGO:
  101. m_Format = KICAD_LOGO;
  102. m_ScaleX = PL_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to micron
  103. m_ScaleY = PL_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom
  104. createOutputData();
  105. break;
  106. case POSTSCRIPT_FMT:
  107. m_Format = POSTSCRIPT_FMT;
  108. m_ScaleX = 1.0; // the conversion scale
  109. m_ScaleY = m_ScaleX;
  110. // output vector data, e.g. as a rudimentary EPS file (mainly for tests)
  111. createOutputData();
  112. break;
  113. case EESCHEMA_FMT:
  114. m_Format = EESCHEMA_FMT;
  115. m_ScaleX = 1000.0 / aDpi_X; // the conversion scale from PPI to legacy IU (mil)
  116. m_ScaleY = -1000.0 / aDpi_Y; // Y axis is bottom to Top for components in libs
  117. createOutputData();
  118. break;
  119. case PCBNEW_KICAD_MOD:
  120. m_Format = PCBNEW_KICAD_MOD;
  121. m_ScaleX = PCB_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to IU
  122. m_ScaleY = PCB_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom in Footprint Editor
  123. createOutputData( aModLayer );
  124. break;
  125. default:
  126. break;
  127. }
  128. bm_free( aPotrace_bitmap );
  129. potrace_state_free( st );
  130. potrace_param_free( param );
  131. return 0;
  132. }
  133. const char* BITMAPCONV_INFO::getBoardLayerName( BMP2CMP_MOD_LAYER aChoice )
  134. {
  135. const char * layerName = "F.SilkS";
  136. switch( aChoice )
  137. {
  138. case MOD_LYR_FSOLDERMASK:
  139. layerName = "F.Mask";
  140. break;
  141. case MOD_LYR_ECO1:
  142. layerName = "Eco1.User";
  143. break;
  144. case MOD_LYR_ECO2:
  145. layerName = "Eco2.User";
  146. break;
  147. case MOD_LYR_FSILKS:
  148. default: // case MOD_LYR_FSILKS only unless there is a bug
  149. break;
  150. }
  151. return layerName;
  152. }
  153. void BITMAPCONV_INFO::outputDataHeader( const char * aBrdLayerName )
  154. {
  155. int Ypos = (int) ( m_PixmapHeight / 2 * m_ScaleY );
  156. int fieldSize; // fields text size = 60 mils
  157. char strbuf[1024];
  158. switch( m_Format )
  159. {
  160. case POSTSCRIPT_FMT:
  161. /* output vector data, e.g. as a rudimentary EPS file */
  162. m_Data += "%%!PS-Adobe-3.0 EPSF-3.0\n";
  163. sprintf( strbuf, "%%%%BoundingBox: 0 0 %d %d\n", m_PixmapWidth, m_PixmapHeight );
  164. m_Data += strbuf;
  165. m_Data += "gsave\n";
  166. break;
  167. case PCBNEW_KICAD_MOD:
  168. // fields text size = 1.5 mm
  169. // fields text thickness = 1.5 / 5 = 0.3mm
  170. sprintf( strbuf, "(module %s (layer F.Cu)\n (at 0 0)\n", m_CmpName.c_str() );
  171. m_Data += strbuf;
  172. sprintf( strbuf, "(attr board_only exclude_from_pos_files exclude_from_bom)\n");
  173. m_Data += strbuf;
  174. sprintf( strbuf, " (fp_text reference \"G***\" (at 0 0) (layer %s)\n"
  175. " (effects (font (thickness 0.3)))\n )\n", aBrdLayerName );
  176. m_Data += strbuf;
  177. sprintf( strbuf, " (fp_text value \"%s\" (at 0.75 0) (layer %s) hide\n"
  178. " (effects (font (thickness 0.3)))\n )\n", m_CmpName.c_str(), aBrdLayerName );
  179. m_Data += strbuf;
  180. break;
  181. case KICAD_LOGO:
  182. m_Data += "(polygon (pos 0 0 rbcorner) (rotate 0) (linewidth 0.01)\n";
  183. break;
  184. case EESCHEMA_FMT:
  185. sprintf( strbuf, "EESchema-LIBRARY Version 2.3\n" );
  186. m_Data += strbuf;
  187. sprintf( strbuf, "#\n# %s\n", m_CmpName.c_str() );
  188. m_Data += strbuf;
  189. sprintf( strbuf, "# pixmap size w = %d, h = %d\n#\n",
  190. m_PixmapWidth, m_PixmapHeight );
  191. m_Data += strbuf;
  192. // print reference and value
  193. fieldSize = 50; // fields text size = 50 mils
  194. Ypos += fieldSize / 2;
  195. sprintf( strbuf, "DEF %s G 0 40 Y Y 1 F N\n", m_CmpName.c_str() );
  196. m_Data += strbuf;
  197. sprintf( strbuf, "F0 \"#G\" 0 %d %d H I C CNN\n", Ypos, fieldSize );
  198. m_Data += strbuf;
  199. sprintf( strbuf, "F1 \"%s\" 0 %d %d H I C CNN\n", m_CmpName.c_str(), -Ypos, fieldSize );
  200. m_Data += strbuf;
  201. m_Data += "DRAW\n";
  202. break;
  203. }
  204. }
  205. void BITMAPCONV_INFO::outputDataEnd()
  206. {
  207. switch( m_Format )
  208. {
  209. case POSTSCRIPT_FMT:
  210. m_Data += "grestore\n";
  211. m_Data += "%%EOF\n";
  212. break;
  213. case PCBNEW_KICAD_MOD:
  214. m_Data += ")\n";
  215. break;
  216. case KICAD_LOGO:
  217. m_Data += ")\n";
  218. break;
  219. case EESCHEMA_FMT:
  220. m_Data += "ENDDRAW\n";
  221. m_Data += "ENDDEF\n";
  222. break;
  223. }
  224. }
  225. void BITMAPCONV_INFO::outputOnePolygon( SHAPE_LINE_CHAIN & aPolygon, const char* aBrdLayerName )
  226. {
  227. // write one polygon to output file.
  228. // coordinates are expected in target unit.
  229. int ii, jj;
  230. VECTOR2I currpoint;
  231. char strbuf[1024];
  232. int offsetX = (int)( m_PixmapWidth / 2 * m_ScaleX );
  233. int offsetY = (int)( m_PixmapHeight / 2 * m_ScaleY );
  234. const VECTOR2I startpoint = aPolygon.CPoint( 0 );
  235. switch( m_Format )
  236. {
  237. case POSTSCRIPT_FMT:
  238. offsetY = (int)( m_PixmapHeight * m_ScaleY );
  239. sprintf( strbuf, "newpath\n%d %d moveto\n",
  240. startpoint.x, offsetY - startpoint.y );
  241. m_Data += strbuf;
  242. jj = 0;
  243. for( ii = 1; ii < aPolygon.PointCount(); ii++ )
  244. {
  245. currpoint = aPolygon.CPoint( ii );
  246. sprintf( strbuf, " %d %d lineto",
  247. currpoint.x, offsetY - currpoint.y );
  248. m_Data += strbuf;
  249. if( jj++ > 6 )
  250. {
  251. jj = 0;
  252. m_Data += "\n";
  253. }
  254. }
  255. m_Data += "\nclosepath fill\n";
  256. break;
  257. case PCBNEW_KICAD_MOD:
  258. {
  259. double width = 0.0; // outline thickness in mm: no thickness
  260. m_Data += " (fp_poly (pts";
  261. jj = 0;
  262. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  263. {
  264. currpoint = aPolygon.CPoint( ii );
  265. sprintf( strbuf, " (xy %f %f)",
  266. ( currpoint.x - offsetX ) / PCB_IU_PER_MM,
  267. ( currpoint.y - offsetY ) / PCB_IU_PER_MM );
  268. m_Data += strbuf;
  269. if( jj++ > 6 )
  270. {
  271. jj = 0;
  272. m_Data += "\n ";
  273. }
  274. }
  275. // No need to close polygon
  276. m_Data += " )";
  277. sprintf( strbuf, "(layer %s) (width %f)\n )\n", aBrdLayerName, width );
  278. m_Data += strbuf;
  279. }
  280. break;
  281. case KICAD_LOGO:
  282. m_Data += " (pts";
  283. // Internal units = micron, file unit = mm
  284. jj = 0;
  285. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  286. {
  287. currpoint = aPolygon.CPoint( ii );
  288. sprintf( strbuf, " (xy %.3f %.3f)",
  289. ( currpoint.x - offsetX ) / PL_IU_PER_MM,
  290. ( currpoint.y - offsetY ) / PL_IU_PER_MM );
  291. m_Data += strbuf;
  292. if( jj++ > 4 )
  293. {
  294. jj = 0;
  295. m_Data += "\n ";
  296. }
  297. }
  298. // Close polygon
  299. sprintf( strbuf, " (xy %.3f %.3f) )\n",
  300. ( startpoint.x - offsetX ) / PL_IU_PER_MM,
  301. ( startpoint.y - offsetY ) / PL_IU_PER_MM );
  302. m_Data += strbuf;
  303. break;
  304. case EESCHEMA_FMT:
  305. // The polygon outline thickness is fixed here to 1 mil, the minimal
  306. // value in Eeschema (0 means use default thickness for graphics)
  307. #define EE_LINE_THICKNESS 1
  308. sprintf( strbuf, "P %d 0 0 %d",
  309. (int) aPolygon.PointCount() + 1, EE_LINE_THICKNESS );
  310. m_Data += strbuf;
  311. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  312. {
  313. currpoint = aPolygon.CPoint( ii );
  314. sprintf( strbuf, " %d %d",
  315. currpoint.x - offsetX, currpoint.y - offsetY );
  316. m_Data += strbuf;
  317. }
  318. // Close polygon
  319. sprintf( strbuf, " %d %d",
  320. startpoint.x - offsetX, startpoint.y - offsetY );
  321. m_Data += strbuf;
  322. m_Data += " F\n";
  323. break;
  324. }
  325. }
  326. void BITMAPCONV_INFO::createOutputData( BMP2CMP_MOD_LAYER aModLayer )
  327. {
  328. std::vector <potrace_dpoint_t> cornersBuffer;
  329. // polyset_areas is a set of polygon to draw
  330. SHAPE_POLY_SET polyset_areas;
  331. // polyset_holes is the set of holes inside polyset_areas outlines
  332. SHAPE_POLY_SET polyset_holes;
  333. potrace_dpoint_t( *c )[3];
  334. LOCALE_IO toggle; // Temporary switch the locale to standard C to r/w floats
  335. // The layer name has meaning only for .kicad_mod files.
  336. // For these files the header creates 2 invisible texts: value and ref
  337. // (needed but not usefull) on silk screen layer
  338. outputDataHeader( getBoardLayerName( MOD_LYR_FSILKS ) );
  339. bool main_outline = true;
  340. /* draw each as a polygon with no hole.
  341. * Bezier curves are approximated by a polyline
  342. */
  343. potrace_path_t* paths = m_Paths; // the list of paths
  344. if(!m_Paths)
  345. {
  346. m_errors += "No path in black and white image: no outline created\n";
  347. }
  348. while( paths != NULL )
  349. {
  350. int cnt = paths->curve.n;
  351. int* tag = paths->curve.tag;
  352. c = paths->curve.c;
  353. potrace_dpoint_t startpoint = c[cnt - 1][2];
  354. for( int i = 0; i < cnt; i++ )
  355. {
  356. switch( tag[i] )
  357. {
  358. case POTRACE_CORNER:
  359. cornersBuffer.push_back( c[i][1] );
  360. cornersBuffer.push_back( c[i][2] );
  361. startpoint = c[i][2];
  362. break;
  363. case POTRACE_CURVETO:
  364. BezierToPolyline( cornersBuffer, startpoint, c[i][0], c[i][1], c[i][2] );
  365. startpoint = c[i][2];
  366. break;
  367. }
  368. }
  369. // Store current path
  370. if( main_outline )
  371. {
  372. main_outline = false;
  373. // build the current main polygon
  374. polyset_areas.NewOutline();
  375. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  376. {
  377. polyset_areas.Append( int( cornersBuffer[i].x * m_ScaleX ),
  378. int( cornersBuffer[i].y * m_ScaleY ) );
  379. }
  380. }
  381. else
  382. {
  383. // Add current hole in polyset_holes
  384. polyset_holes.NewOutline();
  385. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  386. {
  387. polyset_holes.Append( int( cornersBuffer[i].x * m_ScaleX ),
  388. int( cornersBuffer[i].y * m_ScaleY ) );
  389. }
  390. }
  391. cornersBuffer.clear();
  392. /* at the end of a group of a positive path and its negative children, fill.
  393. */
  394. if( paths->next == NULL || paths->next->sign == '+' )
  395. {
  396. polyset_areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  397. polyset_holes.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  398. polyset_areas.BooleanSubtract( polyset_holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  399. // Ensure there are no self intersecting polygons
  400. polyset_areas.NormalizeAreaOutlines();
  401. // Convert polygon with holes to a unique polygon
  402. polyset_areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  403. // Output current resulting polygon(s)
  404. for( int ii = 0; ii < polyset_areas.OutlineCount(); ii++ )
  405. {
  406. SHAPE_LINE_CHAIN& poly = polyset_areas.Outline( ii );
  407. outputOnePolygon( poly, getBoardLayerName( aModLayer ));
  408. }
  409. polyset_areas.RemoveAllContours();
  410. polyset_holes.RemoveAllContours();
  411. main_outline = true;
  412. }
  413. paths = paths->next;
  414. }
  415. outputDataEnd();
  416. }
  417. // a helper function to calculate a square value
  418. inline double square( double x )
  419. {
  420. return x*x;
  421. }
  422. // a helper function to calculate a cube value
  423. inline double cube( double x )
  424. {
  425. return x*x*x;
  426. }
  427. /* render a Bezier curve. */
  428. void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
  429. potrace_dpoint_t p1,
  430. potrace_dpoint_t p2,
  431. potrace_dpoint_t p3,
  432. potrace_dpoint_t p4 )
  433. {
  434. double dd0, dd1, dd, delta, e2, epsilon, t;
  435. // p1 = starting point
  436. /* we approximate the curve by small line segments. The interval
  437. * size, epsilon, is determined on the fly so that the distance
  438. * between the true curve and its approximation does not exceed the
  439. * desired accuracy delta. */
  440. delta = 0.25; /* desired accuracy, in pixels */
  441. /* let dd = maximal value of 2nd derivative over curve - this must
  442. * occur at an endpoint. */
  443. dd0 = square( p1.x - 2 * p2.x + p3.x ) + square( p1.y - 2 * p2.y + p3.y );
  444. dd1 = square( p2.x - 2 * p3.x + p4.x ) + square( p2.y - 2 * p3.y + p4.y );
  445. dd = 6 * sqrt( std::max( dd0, dd1 ) );
  446. e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
  447. epsilon = sqrt( e2 ); /* necessary interval size */
  448. for( t = epsilon; t<1; t += epsilon )
  449. {
  450. potrace_dpoint_t intermediate_point;
  451. intermediate_point.x = p1.x * cube( 1 - t ) +
  452. 3* p2.x* square( 1 - t ) * t +
  453. 3 * p3.x * (1 - t) * square( t ) +
  454. p4.x* cube( t );
  455. intermediate_point.y = p1.y * cube( 1 - t ) +
  456. 3* p2.y* square( 1 - t ) * t +
  457. 3 * p3.y * (1 - t) * square( t ) + p4.y* cube( t );
  458. aCornersBuffer.push_back( intermediate_point );
  459. }
  460. aCornersBuffer.push_back( p4 );
  461. }