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.

440 lines
9.8 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Mario Luzeiro <mrluzeiro@gmail.com>
  5. * Copyright (C) 1992-2015 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 <wx/image.h> // Used for save an image to disk
  30. #include <string.h> // For memcpy
  31. #ifndef CLAMP
  32. #define CLAMP(n, min, max) {if (n < min) n=min; else if (n > max) n = max;}
  33. #endif
  34. CIMAGE::CIMAGE( unsigned int aXsize, unsigned int aYsize )
  35. {
  36. m_wxh = aXsize * aYsize;
  37. m_pixels = (unsigned char*)malloc( m_wxh );
  38. m_width = aXsize;
  39. m_height = aYsize;
  40. m_wraping = (E_WRAP)WRAP_CLAMP;
  41. }
  42. CIMAGE::~CIMAGE()
  43. {
  44. free( m_pixels );
  45. }
  46. unsigned char* CIMAGE::GetBuffer() const
  47. {
  48. return m_pixels;
  49. }
  50. bool CIMAGE::wrapCoords( int *aXo, int *aYo ) const
  51. {
  52. int x = *aXo;
  53. int y = *aYo;
  54. switch(m_wraping)
  55. {
  56. case WRAP_CLAMP:
  57. x = (x < 0 )?0:x;
  58. x = (x >= (int)(m_width - 1))?(m_width - 1):x;
  59. y = (y < 0)?0:y;
  60. y = (y >= (int)(m_height - 1))?(m_height - 1):y;
  61. break;
  62. case WRAP_WRAP:
  63. x = (x < 0)?((m_width - 1)+x):x;
  64. x = (x >= (int)(m_width - 1))?(x - m_width):x;
  65. y = (y < 0)?((m_height - 1)+y):y;
  66. y = (y >= (int)(m_height - 1))?(y - m_height):y;
  67. break;
  68. default:
  69. break;
  70. }
  71. if( (x < 0) || (x >= (int)m_width) ||
  72. (y < 0) || (y >= (int)m_height) )
  73. return false;
  74. *aXo = x;
  75. *aYo = y;
  76. return true;
  77. }
  78. void CIMAGE::Setpixel( int aX, int aY, unsigned char aValue )
  79. {
  80. if( wrapCoords( &aX, &aY ) )
  81. m_pixels[aX + aY * m_width] = aValue;
  82. }
  83. unsigned char CIMAGE::Getpixel( int aX, int aY ) const
  84. {
  85. if( wrapCoords( &aX, &aY ) )
  86. return m_pixels[aX + aY * m_width];
  87. else
  88. return 0;
  89. }
  90. void CIMAGE::Invert()
  91. {
  92. for( unsigned int it = 0; it < m_wxh; it++ )
  93. m_pixels[it] = 255 - m_pixels[it];
  94. }
  95. void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOperation )
  96. {
  97. int aV, bV;
  98. if( aOperation == COPY_RAW )
  99. {
  100. if ( aImgA == NULL )
  101. return;
  102. }
  103. else
  104. {
  105. if ( (aImgA == NULL) || (aImgB == NULL) )
  106. return;
  107. }
  108. switch(aOperation)
  109. {
  110. case COPY_RAW:
  111. memcpy( m_pixels, aImgA->m_pixels, m_wxh );
  112. break;
  113. case COPY_ADD:
  114. for( unsigned int it = 0;it < m_wxh; it++ )
  115. {
  116. aV = aImgA->m_pixels[it];
  117. bV = aImgB->m_pixels[it];
  118. aV = (aV + bV);
  119. aV = (aV > 255)?255:aV;
  120. m_pixels[it] = aV;
  121. }
  122. break;
  123. case COPY_SUB:
  124. for( unsigned int it = 0;it < m_wxh; it++ )
  125. {
  126. aV = aImgA->m_pixels[it];
  127. bV = aImgB->m_pixels[it];
  128. aV = (aV - bV);
  129. aV = (aV < 0)?0:aV;
  130. m_pixels[it] = aV;
  131. }
  132. break;
  133. case COPY_DIF:
  134. for( unsigned int it = 0;it < m_wxh; it++ )
  135. {
  136. aV = aImgA->m_pixels[it];
  137. bV = aImgB->m_pixels[it];
  138. m_pixels[it] = abs(aV - bV);
  139. }
  140. break;
  141. case COPY_MUL:
  142. for( unsigned int it = 0;it < m_wxh; it++ )
  143. {
  144. aV = aImgA->m_pixels[it];
  145. bV = aImgB->m_pixels[it];
  146. m_pixels[it] = (unsigned char)((((float)aV / 255.0f) * ((float)bV / 255.0f)) * 255);
  147. }
  148. break;
  149. case COPY_AND:
  150. for( unsigned int it = 0;it < m_wxh; it++ )
  151. {
  152. m_pixels[it] = aImgA->m_pixels[it] & aImgB->m_pixels[it];
  153. }
  154. break;
  155. case COPY_OR:
  156. for( unsigned int it = 0;it < m_wxh; it++ )
  157. {
  158. m_pixels[it] = aImgA->m_pixels[it] | aImgB->m_pixels[it];
  159. }
  160. break;
  161. case COPY_XOR:
  162. for( unsigned int it = 0;it < m_wxh; it++ )
  163. {
  164. m_pixels[it] = aImgA->m_pixels[it] ^ aImgB->m_pixels[it];
  165. }
  166. break;
  167. case COPY_BLEND50:
  168. for( unsigned int it = 0;it < m_wxh; it++ )
  169. {
  170. aV = aImgA->m_pixels[it];
  171. bV = aImgB->m_pixels[it];
  172. m_pixels[it] = (aV + bV) / 2;
  173. }
  174. break;
  175. case COPY_MIN:
  176. for( unsigned int it = 0;it < m_wxh; it++ )
  177. {
  178. aV = aImgA->m_pixels[it];
  179. bV = aImgB->m_pixels[it];
  180. m_pixels[it] = (aV < bV)?aV:bV;
  181. }
  182. break;
  183. case COPY_MAX:
  184. for( unsigned int it = 0;it < m_wxh; it++ )
  185. {
  186. aV = aImgA->m_pixels[it];
  187. bV = aImgB->m_pixels[it];
  188. m_pixels[it] = (aV > bV)?aV:bV;
  189. }
  190. break;
  191. default:
  192. break;
  193. }
  194. }
  195. // TIP: If you want create or test filters you can use GIMP
  196. // with a generic convolution matrix and get the values from there.
  197. // http://docs.gimp.org/nl/plug-in-convmatrix.html
  198. static const S_FILTER FILTERS[] = {
  199. // Hi Pass
  200. {
  201. { { 0, -1, -1, -1, 0},
  202. {-1, 2, -4, 2, -1},
  203. {-1, -4, 13, -4, -1},
  204. {-1, 2, -4, 2, -1},
  205. { 0, -1, -1, -1, 0}
  206. },
  207. 7,
  208. 255
  209. },
  210. // Blur
  211. {
  212. { { 3, 5, 7, 5, 3},
  213. { 5, 9, 12, 9, 5},
  214. { 7, 12, 20, 12, 7},
  215. { 5, 9, 12, 9, 5},
  216. { 3, 5, 7, 5, 3}
  217. },
  218. 182,
  219. 0
  220. },
  221. // Blur Invert
  222. {
  223. { { 0, 0, 0, 0, 0},
  224. { 0, 0, -1, 0, 0},
  225. { 0, -1, 0, -1, 0},
  226. { 0, 0, -1, 0, 0},
  227. { 0, 0, 0, 0, 0}
  228. },
  229. 4,
  230. 255
  231. },
  232. //
  233. {
  234. { { 0, 2, 4, 2, 0},
  235. { 2, -2, 1, -2, 2},
  236. { 4, 1, -8, 1, 4},
  237. { 2, -2, 1, -2, 2},
  238. { 0, 2, 4, 2, 0}
  239. },
  240. 20,
  241. 0
  242. },
  243. // Cartoon
  244. {
  245. { {-1, -1, -1, -1, 0},
  246. {-1, 0, 0, 0, 0},
  247. {-1, 0, 4, 0, 0},
  248. { 0, 0, 0, 1, 0},
  249. { 0, 0, 0, 0, 4}
  250. },
  251. 3,
  252. 0
  253. },
  254. // Emboss
  255. {
  256. { {-1, -1, -1, -1, 0},
  257. {-1, -1, -1, 0, 1},
  258. {-1, -1, 0, 1, 1},
  259. {-1, 0, 1, 1, 1},
  260. { 0, 1, 1, 1, 1}
  261. },
  262. 1,
  263. 128
  264. },
  265. // Sharpen
  266. {
  267. { {-1, -1, -1, -1, -1},
  268. {-1, 2, 2, 2, -1},
  269. {-1, 2, 8, 2, -1},
  270. {-1, 2, 2, 2, -1},
  271. {-1, -1, -1, -1, -1}
  272. },
  273. 8,
  274. 0
  275. },
  276. // Melt
  277. {
  278. { { 4, 2, 6, 8, 1},
  279. { 1, 2, 5, 4, 2},
  280. { 0, -1, 1, -1, 0},
  281. { 0, 0, -2, 0, 0},
  282. { 0, 0, 0, 0, 0}
  283. },
  284. 32,
  285. 0
  286. },
  287. // Sobel Gx
  288. {
  289. { { 0, 0, 0, 0, 0},
  290. { 0, -1, 0, 1, 0},
  291. { 0, -2, 0, 2, 0},
  292. { 0, -1, 0, 1, 0},
  293. { 0, 0, 0, 0, 0}
  294. },
  295. 1,
  296. 0
  297. },
  298. // Sobel Gy
  299. {
  300. { { 1, 2, 4, 2, 1},
  301. {-1, -1, 0, 1, 1},
  302. {-2, -2, 0, 2, 2},
  303. {-1, -1, 0, 1, 1},
  304. {-1, -2, -4, -2, -1},
  305. },
  306. 1,
  307. 0
  308. }
  309. };// Filters
  310. //!TODO: This functions can be optimized slipting it between the edges and
  311. // do it without use the getpixel function.
  312. // Optimization can be done to m_pixels[ix + iy * m_width]
  313. // but keep in mind the parallel process of the algorithm
  314. void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType )
  315. {
  316. S_FILTER filter = FILTERS[aFilterType];
  317. aInImg->m_wraping = WRAP_CLAMP;
  318. m_wraping = WRAP_CLAMP;
  319. #ifdef USE_OPENMP
  320. #pragma omp parallel for
  321. #endif /* USE_OPENMP */
  322. for( int iy = 0; iy < (int)m_height; iy++)
  323. {
  324. for( int ix = 0; ix < (int)m_width; ix++ )
  325. {
  326. int v = 0;
  327. for( int sy = 0; sy < 5; sy++ )
  328. {
  329. for( int sx = 0; sx < 5; sx++ )
  330. {
  331. int factor = filter.kernel[sx][sy];
  332. unsigned char pixelv = aInImg->Getpixel( ix + sx - 2, iy + sy - 2 );
  333. v += pixelv * factor;
  334. }
  335. }
  336. v /= filter.div;
  337. v += filter.offset;
  338. CLAMP(v, 0, 255);
  339. m_pixels[ix + iy * m_width] = v;
  340. }
  341. }
  342. }
  343. void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray )
  344. {
  345. for( unsigned int i = 0; i < m_wxh; i++ )
  346. {
  347. int v = aNormalizedFloatArray[i] * 255;
  348. CLAMP(v, 0, 255);
  349. m_pixels[i] = v;
  350. }
  351. }
  352. void CIMAGE::SaveAsPNG( wxString aFileName ) const
  353. {
  354. unsigned char* pixelbuffer = (unsigned char*) malloc( m_wxh * 3 );
  355. wxImage image( m_width, m_height );
  356. for( unsigned int i = 0; i < m_wxh; i++)
  357. {
  358. unsigned char v = m_pixels[i];
  359. // Set RGB value with all same values intensities
  360. pixelbuffer[i * 3 + 0] = v;
  361. pixelbuffer[i * 3 + 1] = v;
  362. pixelbuffer[i * 3 + 2] = v;
  363. }
  364. image.SetData( pixelbuffer );
  365. image = image.Mirror( false );
  366. image.SaveFile( aFileName + ".png", wxBITMAP_TYPE_PNG );
  367. image.Destroy();
  368. }