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.

539 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
  5. * Copyright (C) 1992-2016 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. /**
  25. * @file cimage.cpp
  26. * @brief one 8bit-channel image implementation
  27. */
  28. #include "cimage.h"
  29. #include "buffers_debug.h"
  30. #include <string.h> // For memcpy
  31. #include <atomic>
  32. #include <thread>
  33. #include <chrono>
  34. #ifndef CLAMP
  35. #define CLAMP(n, min, max) {if( n < min ) n=min; else if( n > max ) n = max;}
  36. #endif
  37. CIMAGE::CIMAGE( unsigned int aXsize, unsigned int aYsize )
  38. {
  39. m_wxh = aXsize * aYsize;
  40. m_pixels = (unsigned char*)malloc( m_wxh );
  41. memset( m_pixels, 0, m_wxh );
  42. m_width = aXsize;
  43. m_height = aYsize;
  44. m_wraping = (E_WRAP)WRAP_CLAMP;
  45. }
  46. CIMAGE::CIMAGE( const CIMAGE &aSrcImage )
  47. {
  48. m_wxh = aSrcImage.GetWidth() * aSrcImage.GetHeight();
  49. m_pixels = (unsigned char*)malloc( m_wxh );
  50. memcpy( m_pixels, aSrcImage.GetBuffer(), m_wxh );
  51. m_width = aSrcImage.GetWidth();
  52. m_height = aSrcImage.GetHeight();
  53. m_wraping = (E_WRAP)WRAP_CLAMP;
  54. }
  55. CIMAGE::~CIMAGE()
  56. {
  57. free( m_pixels );
  58. }
  59. unsigned char* CIMAGE::GetBuffer() const
  60. {
  61. return m_pixels;
  62. }
  63. bool CIMAGE::wrapCoords( int *aXo, int *aYo ) const
  64. {
  65. int x = *aXo;
  66. int y = *aYo;
  67. switch(m_wraping)
  68. {
  69. case WRAP_CLAMP:
  70. x = (x < 0 )?0:x;
  71. x = (x >= (int)(m_width - 1))?(m_width - 1):x;
  72. y = (y < 0)?0:y;
  73. y = (y >= (int)(m_height - 1))?(m_height - 1):y;
  74. break;
  75. case WRAP_WRAP:
  76. x = (x < 0)?((m_width - 1)+x):x;
  77. x = (x >= (int)(m_width - 1))?(x - m_width):x;
  78. y = (y < 0)?((m_height - 1)+y):y;
  79. y = (y >= (int)(m_height - 1))?(y - m_height):y;
  80. break;
  81. default:
  82. break;
  83. }
  84. if( (x < 0) || (x >= (int)m_width) ||
  85. (y < 0) || (y >= (int)m_height) )
  86. return false;
  87. *aXo = x;
  88. *aYo = y;
  89. return true;
  90. }
  91. void CIMAGE::plot8CircleLines( int aCx, int aCy, int aX, int aY, unsigned char aValue )
  92. {
  93. Hline( aCx - aX, aCx + aX, aCy + aY, aValue );
  94. Hline( aCx - aX, aCx + aX, aCy - aY, aValue );
  95. Hline( aCx - aY, aCx + aY, aCy + aX, aValue );
  96. Hline( aCx - aY, aCx + aY, aCy - aX, aValue );
  97. }
  98. void CIMAGE::Setpixel( int aX, int aY, unsigned char aValue )
  99. {
  100. if( wrapCoords( &aX, &aY ) )
  101. m_pixels[aX + aY * m_width] = aValue;
  102. }
  103. unsigned char CIMAGE::Getpixel( int aX, int aY ) const
  104. {
  105. if( wrapCoords( &aX, &aY ) )
  106. return m_pixels[aX + aY * m_width];
  107. else
  108. return 0;
  109. }
  110. void CIMAGE::Hline( int aXStart, int aXEnd, int aY, unsigned char aValue )
  111. {
  112. if( ( aY < 0 ) ||
  113. ( aY >= (int)m_height ) ||
  114. ( ( aXStart < 0 ) && ( aXEnd < 0) ) ||
  115. ( ( aXStart >= (int)m_width ) && ( aXEnd >= (int)m_width) ) )
  116. return;
  117. if( aXStart > aXEnd )
  118. {
  119. int swap = aXStart;
  120. aXStart = aXEnd;
  121. aXEnd = swap;
  122. }
  123. // Clamp line
  124. if( aXStart < 0 )
  125. aXStart = 0;
  126. if( aXEnd >= (int)m_width )
  127. aXEnd = m_width - 1;
  128. unsigned char* pixelPtr = &m_pixels[aXStart + aY * m_width];
  129. unsigned char* pixelPtrEnd = pixelPtr + (unsigned int)((aXEnd - aXStart) + 1);
  130. while( pixelPtr < pixelPtrEnd )
  131. {
  132. *pixelPtr = aValue;
  133. pixelPtr++;
  134. }
  135. }
  136. // Based on paper
  137. // http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
  138. void CIMAGE::CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
  139. {
  140. int x = aRadius;
  141. int y = 0;
  142. int xChange = 1 - 2 * aRadius;
  143. int yChange = 0;
  144. int radiusError = 0;
  145. while( x >= y )
  146. {
  147. plot8CircleLines( aCx, aCy, x, y, aValue );
  148. y++;
  149. radiusError += yChange;
  150. yChange += 2;
  151. if( (2 * radiusError + xChange) > 0 )
  152. {
  153. x--;
  154. radiusError += xChange;
  155. xChange += 2;
  156. }
  157. }
  158. }
  159. void CIMAGE::Invert()
  160. {
  161. for( unsigned int it = 0; it < m_wxh; it++ )
  162. m_pixels[it] = 255 - m_pixels[it];
  163. }
  164. void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOperation )
  165. {
  166. int aV, bV;
  167. if( aOperation == COPY_RAW )
  168. {
  169. if( aImgA == NULL )
  170. return;
  171. }
  172. else
  173. {
  174. if( (aImgA == NULL) || (aImgB == NULL) )
  175. return;
  176. }
  177. switch(aOperation)
  178. {
  179. case COPY_RAW:
  180. memcpy( m_pixels, aImgA->m_pixels, m_wxh );
  181. break;
  182. case COPY_ADD:
  183. for( unsigned int it = 0;it < m_wxh; it++ )
  184. {
  185. aV = aImgA->m_pixels[it];
  186. bV = aImgB->m_pixels[it];
  187. aV = (aV + bV);
  188. aV = (aV > 255)?255:aV;
  189. m_pixels[it] = aV;
  190. }
  191. break;
  192. case COPY_SUB:
  193. for( unsigned int it = 0;it < m_wxh; it++ )
  194. {
  195. aV = aImgA->m_pixels[it];
  196. bV = aImgB->m_pixels[it];
  197. aV = (aV - bV);
  198. aV = (aV < 0)?0:aV;
  199. m_pixels[it] = aV;
  200. }
  201. break;
  202. case COPY_DIF:
  203. for( unsigned int it = 0;it < m_wxh; it++ )
  204. {
  205. aV = aImgA->m_pixels[it];
  206. bV = aImgB->m_pixels[it];
  207. m_pixels[it] = abs( aV - bV );
  208. }
  209. break;
  210. case COPY_MUL:
  211. for( unsigned int it = 0;it < m_wxh; it++ )
  212. {
  213. aV = aImgA->m_pixels[it];
  214. bV = aImgB->m_pixels[it];
  215. m_pixels[it] = (unsigned char)((((float)aV / 255.0f) * ((float)bV / 255.0f)) * 255);
  216. }
  217. break;
  218. case COPY_AND:
  219. for( unsigned int it = 0;it < m_wxh; it++ )
  220. {
  221. m_pixels[it] = aImgA->m_pixels[it] & aImgB->m_pixels[it];
  222. }
  223. break;
  224. case COPY_OR:
  225. for( unsigned int it = 0;it < m_wxh; it++ )
  226. {
  227. m_pixels[it] = aImgA->m_pixels[it] | aImgB->m_pixels[it];
  228. }
  229. break;
  230. case COPY_XOR:
  231. for( unsigned int it = 0;it < m_wxh; it++ )
  232. {
  233. m_pixels[it] = aImgA->m_pixels[it] ^ aImgB->m_pixels[it];
  234. }
  235. break;
  236. case COPY_BLEND50:
  237. for( unsigned int it = 0;it < m_wxh; it++ )
  238. {
  239. aV = aImgA->m_pixels[it];
  240. bV = aImgB->m_pixels[it];
  241. m_pixels[it] = (aV + bV) / 2;
  242. }
  243. break;
  244. case COPY_MIN:
  245. for( unsigned int it = 0;it < m_wxh; it++ )
  246. {
  247. aV = aImgA->m_pixels[it];
  248. bV = aImgB->m_pixels[it];
  249. m_pixels[it] = (aV < bV)?aV:bV;
  250. }
  251. break;
  252. case COPY_MAX:
  253. for( unsigned int it = 0;it < m_wxh; it++ )
  254. {
  255. aV = aImgA->m_pixels[it];
  256. bV = aImgB->m_pixels[it];
  257. m_pixels[it] = (aV > bV)?aV:bV;
  258. }
  259. break;
  260. default:
  261. break;
  262. }
  263. }
  264. // TIP: If you want create or test filters you can use GIMP
  265. // with a generic convolution matrix and get the values from there.
  266. // http://docs.gimp.org/nl/plug-in-convmatrix.html
  267. static const S_FILTER FILTERS[] = {
  268. // FILTER_HIPASS
  269. {
  270. { { 0, -1, -1, -1, 0},
  271. {-1, 2, -4, 2, -1},
  272. {-1, -4, 13, -4, -1},
  273. {-1, 2, -4, 2, -1},
  274. { 0, -1, -1, -1, 0}
  275. },
  276. 7,
  277. 255
  278. },
  279. // FILTER_GAUSSIAN_BLUR
  280. {
  281. { { 3, 5, 7, 5, 3},
  282. { 5, 9, 12, 9, 5},
  283. { 7, 12, 20, 12, 7},
  284. { 5, 9, 12, 9, 5},
  285. { 3, 5, 7, 5, 3}
  286. },
  287. 182,
  288. 0
  289. },
  290. // FILTER_GAUSSIAN_BLUR2
  291. {
  292. { { 1, 4, 7, 4, 1},
  293. { 4, 16, 26, 16, 4},
  294. { 7, 26, 41, 26, 7},
  295. { 4, 16, 26, 16, 4},
  296. { 1, 4, 7, 4, 1}
  297. },
  298. 273,
  299. 0
  300. },
  301. // FILTER_INVERT_BLUR
  302. {
  303. { { 0, 0, 0, 0, 0},
  304. { 0, 0, -1, 0, 0},
  305. { 0, -1, 0, -1, 0},
  306. { 0, 0, -1, 0, 0},
  307. { 0, 0, 0, 0, 0}
  308. },
  309. 4,
  310. 255
  311. },
  312. // FILTER_CARTOON
  313. {
  314. { {-1, -1, -1, -1, 0},
  315. {-1, 0, 0, 0, 0},
  316. {-1, 0, 4, 0, 0},
  317. { 0, 0, 0, 1, 0},
  318. { 0, 0, 0, 0, 4}
  319. },
  320. 3,
  321. 0
  322. },
  323. // FILTER_EMBOSS
  324. {
  325. { {-1, -1, -1, -1, 0},
  326. {-1, -1, -1, 0, 1},
  327. {-1, -1, 0, 1, 1},
  328. {-1, 0, 1, 1, 1},
  329. { 0, 1, 1, 1, 1}
  330. },
  331. 1,
  332. 128
  333. },
  334. // FILTER_SHARPEN
  335. {
  336. { {-1, -1, -1, -1, -1},
  337. {-1, 2, 2, 2, -1},
  338. {-1, 2, 8, 2, -1},
  339. {-1, 2, 2, 2, -1},
  340. {-1, -1, -1, -1, -1}
  341. },
  342. 8,
  343. 0
  344. },
  345. // FILTER_MELT
  346. {
  347. { { 4, 2, 6, 8, 1},
  348. { 1, 2, 5, 4, 2},
  349. { 0, -1, 1, -1, 0},
  350. { 0, 0, -2, 0, 0},
  351. { 0, 0, 0, 0, 0}
  352. },
  353. 32,
  354. 0
  355. },
  356. // FILTER_SOBEL_GX
  357. {
  358. { { 0, 0, 0, 0, 0},
  359. { 0, -1, 0, 1, 0},
  360. { 0, -2, 0, 2, 0},
  361. { 0, -1, 0, 1, 0},
  362. { 0, 0, 0, 0, 0}
  363. },
  364. 1,
  365. 0
  366. },
  367. // FILTER_SOBEL_GY
  368. {
  369. { { 1, 2, 4, 2, 1},
  370. {-1, -1, 0, 1, 1},
  371. {-2, -2, 0, 2, 2},
  372. {-1, -1, 0, 1, 1},
  373. {-1, -2, -4, -2, -1},
  374. },
  375. 1,
  376. 0
  377. },
  378. // FILTER_BLUR_3X3
  379. {
  380. { { 0, 0, 0, 0, 0},
  381. { 0, 1, 2, 1, 0},
  382. { 0, 2, 4, 2, 0},
  383. { 0, 1, 2, 1, 0},
  384. { 0, 0, 0, 0, 0},
  385. },
  386. 16,
  387. 0
  388. }
  389. };// Filters
  390. // !TODO: This functions can be optimized slipting it between the edges and
  391. // do it without use the getpixel function.
  392. // Optimization can be done to m_pixels[ix + iy * m_width]
  393. // but keep in mind the parallel process of the algorithm
  394. void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType )
  395. {
  396. S_FILTER filter = FILTERS[aFilterType];
  397. aInImg->m_wraping = WRAP_CLAMP;
  398. m_wraping = WRAP_CLAMP;
  399. std::atomic<size_t> nextRow( 0 );
  400. std::atomic<size_t> threadsFinished( 0 );
  401. size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
  402. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  403. {
  404. std::thread t = std::thread( [&]()
  405. {
  406. for( size_t iy = nextRow.fetch_add( 1 );
  407. iy < m_height;
  408. iy = nextRow.fetch_add( 1 ) )
  409. {
  410. for( size_t ix = 0; ix < m_width; ix++ )
  411. {
  412. int v = 0;
  413. for( size_t sy = 0; sy < 5; sy++ )
  414. {
  415. for( size_t sx = 0; sx < 5; sx++ )
  416. {
  417. int factor = filter.kernel[sx][sy];
  418. unsigned char pixelv = aInImg->Getpixel( ix + sx - 2,
  419. iy + sy - 2 );
  420. v += pixelv * factor;
  421. }
  422. }
  423. v /= filter.div;
  424. v += filter.offset;
  425. CLAMP(v, 0, 255);
  426. //TODO: This needs to write to a separate buffer
  427. m_pixels[ix + iy * m_width] = v;
  428. }
  429. }
  430. threadsFinished++;
  431. } );
  432. t.detach();
  433. }
  434. while( threadsFinished < parallelThreadCount )
  435. std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
  436. }
  437. void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray )
  438. {
  439. for( unsigned int i = 0; i < m_wxh; i++ )
  440. {
  441. int v = aNormalizedFloatArray[i] * 255;
  442. CLAMP( v, 0, 255 );
  443. m_pixels[i] = v;
  444. }
  445. }
  446. void CIMAGE::SaveAsPNG( const wxString& aFileName ) const
  447. {
  448. DBG_SaveBuffer( aFileName, m_pixels, m_width, m_height );
  449. }