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.

586 lines
18 KiB

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-2021 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 <layer_ids.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 != nullptr )
  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 = nullptr;
  60. m_CmpName = "LOGO";
  61. }
  62. int BITMAPCONV_INFO::ConvertBitmap( potrace_bitmap_t* aPotrace_bitmap, OUTPUT_FMT_ID aFormat,
  63. int aDpi_X, int aDpi_Y, BMP2CMP_MOD_LAYER aModLayer )
  64. {
  65. potrace_param_t* param;
  66. potrace_state_t* st;
  67. // set tracing parameters, starting from defaults
  68. param = potrace_param_default();
  69. if( !param )
  70. {
  71. char msg[256];
  72. sprintf( msg, "Error allocating parameters: %s\n", strerror( errno ) );
  73. m_errors += msg;
  74. return 1;
  75. }
  76. // For parameters: see http://potrace.sourceforge.net/potracelib.pdf
  77. param->turdsize = 0; // area (in pixels) of largest path to be ignored.
  78. // Potrace default is 2
  79. param->opttolerance = 0.2; // curve optimization tolerance. Potrace default is 0.2
  80. /* convert the bitmap to curves */
  81. st = potrace_trace( param, aPotrace_bitmap );
  82. if( !st || st->status != POTRACE_STATUS_OK )
  83. {
  84. if( st )
  85. {
  86. potrace_state_free( st );
  87. }
  88. potrace_param_free( param );
  89. char msg[256];
  90. sprintf( msg, "Error tracing bitmap: %s\n", strerror( errno ) );
  91. m_errors += msg;
  92. return 1;
  93. }
  94. m_PixmapWidth = aPotrace_bitmap->w;
  95. m_PixmapHeight = aPotrace_bitmap->h; // the bitmap size in pixels
  96. m_Paths = st->plist;
  97. switch( aFormat )
  98. {
  99. case KICAD_WKS_LOGO:
  100. m_Format = KICAD_WKS_LOGO;
  101. m_ScaleX = PL_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to micron
  102. m_ScaleY = PL_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom
  103. createOutputData();
  104. break;
  105. case POSTSCRIPT_FMT:
  106. m_Format = POSTSCRIPT_FMT;
  107. m_ScaleX = 1.0; // the conversion scale
  108. m_ScaleY = m_ScaleX;
  109. // output vector data, e.g. as a rudimentary EPS file (mainly for tests)
  110. createOutputData();
  111. break;
  112. case EESCHEMA_FMT:
  113. m_Format = EESCHEMA_FMT;
  114. m_ScaleX = SCH_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to eeschema iu
  115. m_ScaleY = -SCH_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is bottom to Top for components in libs
  116. createOutputData();
  117. break;
  118. case PCBNEW_KICAD_MOD:
  119. m_Format = PCBNEW_KICAD_MOD;
  120. m_ScaleX = PCB_IU_PER_MM * 25.4 / aDpi_X; // the conversion scale from PPI to IU
  121. m_ScaleY = PCB_IU_PER_MM * 25.4 / aDpi_Y; // Y axis is top to bottom in Footprint Editor
  122. createOutputData( aModLayer );
  123. break;
  124. default:
  125. break;
  126. }
  127. bm_free( aPotrace_bitmap );
  128. potrace_state_free( st );
  129. potrace_param_free( param );
  130. return 0;
  131. }
  132. const char* BITMAPCONV_INFO::getBoardLayerName( BMP2CMP_MOD_LAYER aChoice )
  133. {
  134. const char* layerName = "F.SilkS";
  135. switch( aChoice )
  136. {
  137. case MOD_LYR_FSOLDERMASK:
  138. layerName = "F.Mask";
  139. break;
  140. case MOD_LYR_ECO1:
  141. layerName = "Eco1.User";
  142. break;
  143. case MOD_LYR_ECO2:
  144. layerName = "Eco2.User";
  145. break;
  146. case MOD_LYR_FSILKS:
  147. default: // case MOD_LYR_FSILKS only unless there is a bug
  148. break;
  149. }
  150. return layerName;
  151. }
  152. void BITMAPCONV_INFO::outputDataHeader( const char * aBrdLayerName )
  153. {
  154. double Ypos = ( m_PixmapHeight / 2 * m_ScaleY ); // fields Y position in mm
  155. double fieldSize; // fields text size in mm
  156. char strbuf[1024];
  157. switch( m_Format )
  158. {
  159. case POSTSCRIPT_FMT:
  160. /* output vector data, e.g. as a rudimentary EPS file */
  161. m_Data += "%%!PS-Adobe-3.0 EPSF-3.0\n";
  162. sprintf( strbuf, "%%%%BoundingBox: 0 0 %d %d\n", m_PixmapWidth, m_PixmapHeight );
  163. m_Data += strbuf;
  164. m_Data += "gsave\n";
  165. break;
  166. case PCBNEW_KICAD_MOD:
  167. // fields text size = 1.5 mm
  168. // fields text thickness = 1.5 / 5 = 0.3mm
  169. sprintf( strbuf, "(footprint \"%s\" (version 20210606) (generator bitmap2component) "
  170. "(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_WKS_LOGO:
  182. m_Data += "(kicad_wks (version 20210606) (generator bitmap2component)\n";
  183. m_Data += " (polygon (pos 0 0 rbcorner) (rotate 0) (linewidth 0.01)\n";
  184. break;
  185. case EESCHEMA_FMT:
  186. fieldSize = 1.27; // fields text size in mm (= 50 mils)
  187. Ypos /= SCH_IU_PER_MM;
  188. Ypos += fieldSize / 2;
  189. // sprintf( strbuf, "# pixmap size w = %d, h = %d\n#\n", m_PixmapWidth, m_PixmapHeight );
  190. sprintf( strbuf, "(kicad_symbol_lib (version 20210619) (generator bitmap2component)\n"
  191. " (symbol \"%s\" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)\n",
  192. m_CmpName.c_str() );
  193. m_Data += strbuf;
  194. sprintf( strbuf, " (property \"Reference\" \"#G\" (id 0) (at 0 %g 0)\n"
  195. " (effects (font (size %g %g)) hide)\n )\n",
  196. -Ypos, fieldSize, fieldSize );
  197. m_Data += strbuf;
  198. sprintf( strbuf, " (property \"Value\" \"%s\" (id 1) (at 0 %g 0)\n"
  199. " (effects (font (size %g %g)) hide)\n )\n",
  200. m_CmpName.c_str(), Ypos, fieldSize, fieldSize );
  201. m_Data += strbuf;
  202. sprintf( strbuf, " (property \"Footprint\" \"\" (id 2) (at 0 0 0)\n"
  203. " (effects (font (size %g %g)) hide)\n )\n",
  204. fieldSize, fieldSize );
  205. m_Data += strbuf;
  206. sprintf( strbuf, " (property \"Datasheet\" \"\" (id 3) (at 0 0 0)\n"
  207. " (effects (font (size %g %g)) hide)\n )\n",
  208. fieldSize, fieldSize );
  209. m_Data += strbuf;
  210. sprintf( strbuf, " (symbol \"%s_0_0\"\n", m_CmpName.c_str() );
  211. m_Data += strbuf;
  212. break;
  213. }
  214. }
  215. void BITMAPCONV_INFO::outputDataEnd()
  216. {
  217. switch( m_Format )
  218. {
  219. case POSTSCRIPT_FMT:
  220. m_Data += "grestore\n";
  221. m_Data += "%%EOF\n";
  222. break;
  223. case PCBNEW_KICAD_MOD:
  224. m_Data += ")\n";
  225. break;
  226. case KICAD_WKS_LOGO:
  227. m_Data += " )\n)\n";
  228. break;
  229. case EESCHEMA_FMT:
  230. m_Data += " )\n"; // end symbol_0_0
  231. m_Data += " )\n"; // end symbol
  232. m_Data += ")\n"; // end lib
  233. break;
  234. }
  235. }
  236. void BITMAPCONV_INFO::outputOnePolygon( SHAPE_LINE_CHAIN& aPolygon, const char* aBrdLayerName )
  237. {
  238. // write one polygon to output file.
  239. // coordinates are expected in target unit.
  240. int ii, jj;
  241. VECTOR2I currpoint;
  242. char strbuf[1024];
  243. int offsetX = (int)( m_PixmapWidth / 2 * m_ScaleX );
  244. int offsetY = (int)( m_PixmapHeight / 2 * m_ScaleY );
  245. const VECTOR2I startpoint = aPolygon.CPoint( 0 );
  246. switch( m_Format )
  247. {
  248. case POSTSCRIPT_FMT:
  249. offsetY = (int)( m_PixmapHeight * m_ScaleY );
  250. sprintf( strbuf, "newpath\n%d %d moveto\n", startpoint.x, offsetY - startpoint.y );
  251. m_Data += strbuf;
  252. jj = 0;
  253. for( ii = 1; ii < aPolygon.PointCount(); ii++ )
  254. {
  255. currpoint = aPolygon.CPoint( ii );
  256. sprintf( strbuf, " %d %d lineto", currpoint.x, offsetY - currpoint.y );
  257. m_Data += strbuf;
  258. if( jj++ > 6 )
  259. {
  260. jj = 0;
  261. m_Data += "\n";
  262. }
  263. }
  264. m_Data += "\nclosepath fill\n";
  265. break;
  266. case PCBNEW_KICAD_MOD:
  267. {
  268. double width = 0.0; // outline thickness in mm: no thickness
  269. m_Data += " (fp_poly (pts";
  270. jj = 0;
  271. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  272. {
  273. currpoint = aPolygon.CPoint( ii );
  274. sprintf( strbuf, " (xy %f %f)",
  275. ( currpoint.x - offsetX ) / PCB_IU_PER_MM,
  276. ( currpoint.y - offsetY ) / PCB_IU_PER_MM );
  277. m_Data += strbuf;
  278. if( jj++ > 6 )
  279. {
  280. jj = 0;
  281. m_Data += "\n ";
  282. }
  283. }
  284. // No need to close polygon
  285. m_Data += " )";
  286. sprintf( strbuf, "(layer %s) (width %f)\n )\n", aBrdLayerName, width );
  287. m_Data += strbuf;
  288. }
  289. break;
  290. case KICAD_WKS_LOGO:
  291. m_Data += " (pts";
  292. // Internal units = micron, file unit = mm
  293. jj = 0;
  294. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  295. {
  296. currpoint = aPolygon.CPoint( ii );
  297. sprintf( strbuf, " (xy %.3f %.3f)",
  298. ( currpoint.x - offsetX ) / PL_IU_PER_MM,
  299. ( currpoint.y - offsetY ) / PL_IU_PER_MM );
  300. m_Data += strbuf;
  301. if( jj++ > 4 )
  302. {
  303. jj = 0;
  304. m_Data += "\n ";
  305. }
  306. }
  307. // Close polygon
  308. sprintf( strbuf, " (xy %.3f %.3f) )\n",
  309. ( startpoint.x - offsetX ) / PL_IU_PER_MM,
  310. ( startpoint.y - offsetY ) / PL_IU_PER_MM );
  311. m_Data += strbuf;
  312. break;
  313. case EESCHEMA_FMT:
  314. // The polygon outline thickness is fixed here to 0.01 ( 0.0 is the default thickness)
  315. #define SCH_LINE_THICKNESS_MM 0.01
  316. //sprintf( strbuf, "P %d 0 0 %d", (int) aPolygon.PointCount() + 1, EE_LINE_THICKNESS );
  317. m_Data += " (polyline\n (pts\n";
  318. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  319. {
  320. currpoint = aPolygon.CPoint( ii );
  321. sprintf( strbuf, " (xy %f %f)\n",
  322. ( currpoint.x - offsetX ) / SCH_IU_PER_MM,
  323. ( currpoint.y - offsetY ) / SCH_IU_PER_MM );
  324. m_Data += strbuf;
  325. }
  326. // Close polygon
  327. sprintf( strbuf, " (xy %f %f)\n",
  328. ( startpoint.x - offsetX ) / SCH_IU_PER_MM,
  329. ( startpoint.y - offsetY ) / SCH_IU_PER_MM );
  330. m_Data += strbuf;
  331. m_Data += " )\n"; // end pts
  332. sprintf( strbuf, " (stroke (width %g)) (fill (type outline))\n",
  333. SCH_LINE_THICKNESS_MM );
  334. m_Data += strbuf;
  335. m_Data += " )\n"; // end polyline
  336. break;
  337. }
  338. }
  339. void BITMAPCONV_INFO::createOutputData( BMP2CMP_MOD_LAYER aModLayer )
  340. {
  341. std::vector <potrace_dpoint_t> cornersBuffer;
  342. // polyset_areas is a set of polygon to draw
  343. SHAPE_POLY_SET polyset_areas;
  344. // polyset_holes is the set of holes inside polyset_areas outlines
  345. SHAPE_POLY_SET polyset_holes;
  346. potrace_dpoint_t( *c )[3];
  347. LOCALE_IO toggle; // Temporary switch the locale to standard C to r/w floats
  348. // The layer name has meaning only for .kicad_mod files.
  349. // For these files the header creates 2 invisible texts: value and ref
  350. // (needed but not useful) on silk screen layer
  351. outputDataHeader( getBoardLayerName( MOD_LYR_FSILKS ) );
  352. bool main_outline = true;
  353. /* draw each as a polygon with no hole.
  354. * Bezier curves are approximated by a polyline
  355. */
  356. potrace_path_t* paths = m_Paths; // the list of paths
  357. if(!m_Paths)
  358. {
  359. m_errors += "No shape in black and white image to convert: no outline created\n";
  360. }
  361. while( paths != nullptr )
  362. {
  363. int cnt = paths->curve.n;
  364. int* tag = paths->curve.tag;
  365. c = paths->curve.c;
  366. potrace_dpoint_t startpoint = c[cnt - 1][2];
  367. for( int i = 0; i < cnt; i++ )
  368. {
  369. switch( tag[i] )
  370. {
  371. case POTRACE_CORNER:
  372. cornersBuffer.push_back( c[i][1] );
  373. cornersBuffer.push_back( c[i][2] );
  374. startpoint = c[i][2];
  375. break;
  376. case POTRACE_CURVETO:
  377. BezierToPolyline( cornersBuffer, startpoint, c[i][0], c[i][1], c[i][2] );
  378. startpoint = c[i][2];
  379. break;
  380. }
  381. }
  382. // Store current path
  383. if( main_outline )
  384. {
  385. main_outline = false;
  386. // build the current main polygon
  387. polyset_areas.NewOutline();
  388. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  389. {
  390. polyset_areas.Append( int( cornersBuffer[i].x * m_ScaleX ),
  391. int( cornersBuffer[i].y * m_ScaleY ) );
  392. }
  393. }
  394. else
  395. {
  396. // Add current hole in polyset_holes
  397. polyset_holes.NewOutline();
  398. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  399. {
  400. polyset_holes.Append( int( cornersBuffer[i].x * m_ScaleX ),
  401. int( cornersBuffer[i].y * m_ScaleY ) );
  402. }
  403. }
  404. cornersBuffer.clear();
  405. // at the end of a group of a positive path and its negative children, fill.
  406. if( paths->next == nullptr || paths->next->sign == '+' )
  407. {
  408. polyset_areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  409. polyset_holes.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  410. polyset_areas.BooleanSubtract( polyset_holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  411. // Ensure there are no self intersecting polygons
  412. if( polyset_areas.NormalizeAreaOutlines() )
  413. {
  414. // Convert polygon with holes to a unique polygon
  415. polyset_areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  416. // Output current resulting polygon(s)
  417. for( int ii = 0; ii < polyset_areas.OutlineCount(); ii++ )
  418. {
  419. SHAPE_LINE_CHAIN& poly = polyset_areas.Outline( ii );
  420. outputOnePolygon( poly, getBoardLayerName( aModLayer ));
  421. }
  422. polyset_areas.RemoveAllContours();
  423. polyset_holes.RemoveAllContours();
  424. main_outline = true;
  425. }
  426. }
  427. paths = paths->next;
  428. }
  429. outputDataEnd();
  430. }
  431. // a helper function to calculate a square value
  432. inline double square( double x )
  433. {
  434. return x * x;
  435. }
  436. // a helper function to calculate a cube value
  437. inline double cube( double x )
  438. {
  439. return x * x * x;
  440. }
  441. /* render a Bezier curve. */
  442. void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
  443. potrace_dpoint_t p1,
  444. potrace_dpoint_t p2,
  445. potrace_dpoint_t p3,
  446. potrace_dpoint_t p4 )
  447. {
  448. double dd0, dd1, dd, delta, e2, epsilon, t;
  449. // p1 = starting point
  450. /* we approximate the curve by small line segments. The interval
  451. * size, epsilon, is determined on the fly so that the distance
  452. * between the true curve and its approximation does not exceed the
  453. * desired accuracy delta. */
  454. delta = 0.25; /* desired accuracy, in pixels */
  455. /* let dd = maximal value of 2nd derivative over curve - this must
  456. * occur at an endpoint. */
  457. dd0 = square( p1.x - 2 * p2.x + p3.x ) + square( p1.y - 2 * p2.y + p3.y );
  458. dd1 = square( p2.x - 2 * p3.x + p4.x ) + square( p2.y - 2 * p3.y + p4.y );
  459. dd = 6 * sqrt( std::max( dd0, dd1 ) );
  460. e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
  461. epsilon = sqrt( e2 ); /* necessary interval size */
  462. for( t = epsilon; t<1; t += epsilon )
  463. {
  464. potrace_dpoint_t intermediate_point;
  465. intermediate_point.x = p1.x * cube( 1 - t ) +
  466. 3* p2.x* square( 1 - t ) * t +
  467. 3 * p3.x * (1 - t) * square( t ) +
  468. p4.x* cube( t );
  469. intermediate_point.y = p1.y * cube( 1 - t ) +
  470. 3* p2.y* square( 1 - t ) * t +
  471. 3 * p3.y * (1 - t) * square( t ) + p4.y* cube( t );
  472. aCornersBuffer.push_back( intermediate_point );
  473. }
  474. aCornersBuffer.push_back( p4 );
  475. }