KiCad PCB EDA Suite
cimage.cpp
Go to the documentation of this file.
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 
30 #include "cimage.h"
31 #include "buffers_debug.h"
32 #include <string.h> // For memcpy
33 
34 #include <atomic>
35 #include <thread>
36 #include <chrono>
37 
38 #ifndef CLAMP
39 #define CLAMP(n, min, max) {if( n < min ) n=min; else if( n > max ) n = max;}
40 #endif
41 
42 
43 CIMAGE::CIMAGE( unsigned int aXsize, unsigned int aYsize )
44 {
45  m_wxh = aXsize * aYsize;
46  m_pixels = (unsigned char*)malloc( m_wxh );
47  memset( m_pixels, 0, m_wxh );
48  m_width = aXsize;
49  m_height = aYsize;
51 }
52 
53 
54 CIMAGE::CIMAGE( const CIMAGE &aSrcImage )
55 {
56  m_wxh = aSrcImage.GetWidth() * aSrcImage.GetHeight();
57  m_pixels = (unsigned char*)malloc( m_wxh );
58  memcpy( m_pixels, aSrcImage.GetBuffer(), m_wxh );
59  m_width = aSrcImage.GetWidth();
60  m_height = aSrcImage.GetHeight();
62 }
63 
64 
66 {
67  free( m_pixels );
68 }
69 
70 
71 unsigned char* CIMAGE::GetBuffer() const
72 {
73  return m_pixels;
74 }
75 
76 
77 bool CIMAGE::wrapCoords( int *aXo, int *aYo ) const
78 {
79  int x = *aXo;
80  int y = *aYo;
81 
82  switch(m_wraping)
83  {
84  case WRAP_CLAMP:
85  x = (x < 0 )?0:x;
86  x = (x >= (int)(m_width - 1))?(m_width - 1):x;
87  y = (y < 0)?0:y;
88  y = (y >= (int)(m_height - 1))?(m_height - 1):y;
89  break;
90 
91  case WRAP_WRAP:
92  x = (x < 0)?((m_width - 1)+x):x;
93  x = (x >= (int)(m_width - 1))?(x - m_width):x;
94  y = (y < 0)?((m_height - 1)+y):y;
95  y = (y >= (int)(m_height - 1))?(y - m_height):y;
96  break;
97 
98  default:
99  break;
100  }
101 
102  if( (x < 0) || (x >= (int)m_width) ||
103  (y < 0) || (y >= (int)m_height) )
104  return false;
105 
106  *aXo = x;
107  *aYo = y;
108 
109  return true;
110 }
111 
112 void CIMAGE::plot8CircleLines( int aCx, int aCy, int aX, int aY, unsigned char aValue )
113 {
114  Hline( aCx - aX, aCx + aX, aCy + aY, aValue );
115  Hline( aCx - aX, aCx + aX, aCy - aY, aValue );
116  Hline( aCx - aY, aCx + aY, aCy + aX, aValue );
117  Hline( aCx - aY, aCx + aY, aCy - aX, aValue );
118 }
119 
120 
121 void CIMAGE::Setpixel( int aX, int aY, unsigned char aValue )
122 {
123  if( wrapCoords( &aX, &aY ) )
124  m_pixels[aX + aY * m_width] = aValue;
125 }
126 
127 
128 unsigned char CIMAGE::Getpixel( int aX, int aY ) const
129 {
130  if( wrapCoords( &aX, &aY ) )
131  return m_pixels[aX + aY * m_width];
132  else
133  return 0;
134 }
135 
136 
137 void CIMAGE::Hline( int aXStart, int aXEnd, int aY, unsigned char aValue )
138 {
139  if( ( aY < 0 ) ||
140  ( aY >= (int)m_height ) ||
141  ( ( aXStart < 0 ) && ( aXEnd < 0) ) ||
142  ( ( aXStart >= (int)m_width ) && ( aXEnd >= (int)m_width) ) )
143  return;
144 
145  if( aXStart > aXEnd )
146  {
147  int swap = aXStart;
148 
149  aXStart = aXEnd;
150  aXEnd = swap;
151  }
152 
153  // Clamp line
154  if( aXStart < 0 )
155  aXStart = 0;
156 
157  if( aXEnd >= (int)m_width )
158  aXEnd = m_width - 1;
159 
160  unsigned char* pixelPtr = &m_pixels[aXStart + aY * m_width];
161  unsigned char* pixelPtrEnd = pixelPtr + (unsigned int)((aXEnd - aXStart) + 1);
162 
163  while( pixelPtr < pixelPtrEnd )
164  {
165  *pixelPtr = aValue;
166  pixelPtr++;
167  }
168 }
169 
170 // Based on paper
171 // http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
172 void CIMAGE::CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
173 {
174  int x = aRadius;
175  int y = 0;
176  int xChange = 1 - 2 * aRadius;
177  int yChange = 0;
178  int radiusError = 0;
179 
180  while( x >= y )
181  {
182  plot8CircleLines( aCx, aCy, x, y, aValue );
183  y++;
184  radiusError += yChange;
185  yChange += 2;
186 
187  if( (2 * radiusError + xChange) > 0 )
188  {
189  x--;
190  radiusError += xChange;
191  xChange += 2;
192  }
193  }
194 }
195 
196 
198 {
199  for( unsigned int it = 0; it < m_wxh; it++ )
200  m_pixels[it] = 255 - m_pixels[it];
201 }
202 
203 
204 void CIMAGE::CopyFull( const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOperation )
205 {
206  int aV, bV;
207 
208  if( aOperation == COPY_RAW )
209  {
210  if( aImgA == NULL )
211  return;
212  }
213  else
214  {
215  if( (aImgA == NULL) || (aImgB == NULL) )
216  return;
217  }
218 
219  switch(aOperation)
220  {
221  case COPY_RAW:
222  memcpy( m_pixels, aImgA->m_pixels, m_wxh );
223  break;
224 
225  case COPY_ADD:
226  for( unsigned int it = 0;it < m_wxh; it++ )
227  {
228  aV = aImgA->m_pixels[it];
229  bV = aImgB->m_pixels[it];
230 
231  aV = (aV + bV);
232  aV = (aV > 255)?255:aV;
233 
234  m_pixels[it] = aV;
235  }
236  break;
237 
238  case COPY_SUB:
239  for( unsigned int it = 0;it < m_wxh; it++ )
240  {
241  aV = aImgA->m_pixels[it];
242  bV = aImgB->m_pixels[it];
243 
244  aV = (aV - bV);
245  aV = (aV < 0)?0:aV;
246 
247  m_pixels[it] = aV;
248  }
249  break;
250 
251  case COPY_DIF:
252  for( unsigned int it = 0;it < m_wxh; it++ )
253  {
254  aV = aImgA->m_pixels[it];
255  bV = aImgB->m_pixels[it];
256 
257  m_pixels[it] = abs( aV - bV );
258  }
259  break;
260 
261  case COPY_MUL:
262  for( unsigned int it = 0;it < m_wxh; it++ )
263  {
264  aV = aImgA->m_pixels[it];
265  bV = aImgB->m_pixels[it];
266 
267  m_pixels[it] = (unsigned char)((((float)aV / 255.0f) * ((float)bV / 255.0f)) * 255);
268  }
269  break;
270 
271  case COPY_AND:
272  for( unsigned int it = 0;it < m_wxh; it++ )
273  {
274  m_pixels[it] = aImgA->m_pixels[it] & aImgB->m_pixels[it];
275  }
276  break;
277 
278  case COPY_OR:
279  for( unsigned int it = 0;it < m_wxh; it++ )
280  {
281  m_pixels[it] = aImgA->m_pixels[it] | aImgB->m_pixels[it];
282  }
283  break;
284 
285  case COPY_XOR:
286  for( unsigned int it = 0;it < m_wxh; it++ )
287  {
288  m_pixels[it] = aImgA->m_pixels[it] ^ aImgB->m_pixels[it];
289  }
290  break;
291 
292  case COPY_BLEND50:
293  for( unsigned int it = 0;it < m_wxh; it++ )
294  {
295  aV = aImgA->m_pixels[it];
296  bV = aImgB->m_pixels[it];
297 
298  m_pixels[it] = (aV + bV) / 2;
299  }
300  break;
301 
302  case COPY_MIN:
303  for( unsigned int it = 0;it < m_wxh; it++ )
304  {
305  aV = aImgA->m_pixels[it];
306  bV = aImgB->m_pixels[it];
307 
308  m_pixels[it] = (aV < bV)?aV:bV;
309  }
310  break;
311 
312  case COPY_MAX:
313  for( unsigned int it = 0;it < m_wxh; it++ )
314  {
315  aV = aImgA->m_pixels[it];
316  bV = aImgB->m_pixels[it];
317 
318  m_pixels[it] = (aV > bV)?aV:bV;
319  }
320  break;
321 
322  default:
323  break;
324  }
325 }
326 
327 // TIP: If you want create or test filters you can use GIMP
328 // with a generic convolution matrix and get the values from there.
329 // http://docs.gimp.org/nl/plug-in-convmatrix.html
330 static const S_FILTER FILTERS[] = {
331  // FILTER_HIPASS
332  {
333  { { 0, -1, -1, -1, 0},
334  {-1, 2, -4, 2, -1},
335  {-1, -4, 13, -4, -1},
336  {-1, 2, -4, 2, -1},
337  { 0, -1, -1, -1, 0}
338  },
339  7,
340  255
341  },
342 
343  // FILTER_GAUSSIAN_BLUR
344  {
345  { { 3, 5, 7, 5, 3},
346  { 5, 9, 12, 9, 5},
347  { 7, 12, 20, 12, 7},
348  { 5, 9, 12, 9, 5},
349  { 3, 5, 7, 5, 3}
350  },
351  182,
352  0
353  },
354 
355  // FILTER_GAUSSIAN_BLUR2
356  {
357  { { 1, 4, 7, 4, 1},
358  { 4, 16, 26, 16, 4},
359  { 7, 26, 41, 26, 7},
360  { 4, 16, 26, 16, 4},
361  { 1, 4, 7, 4, 1}
362  },
363  273,
364  0
365  },
366 
367  // FILTER_INVERT_BLUR
368  {
369  { { 0, 0, 0, 0, 0},
370  { 0, 0, -1, 0, 0},
371  { 0, -1, 0, -1, 0},
372  { 0, 0, -1, 0, 0},
373  { 0, 0, 0, 0, 0}
374  },
375  4,
376  255
377  },
378 
379  // FILTER_CARTOON
380  {
381  { {-1, -1, -1, -1, 0},
382  {-1, 0, 0, 0, 0},
383  {-1, 0, 4, 0, 0},
384  { 0, 0, 0, 1, 0},
385  { 0, 0, 0, 0, 4}
386  },
387  3,
388  0
389  },
390 
391  // FILTER_EMBOSS
392  {
393  { {-1, -1, -1, -1, 0},
394  {-1, -1, -1, 0, 1},
395  {-1, -1, 0, 1, 1},
396  {-1, 0, 1, 1, 1},
397  { 0, 1, 1, 1, 1}
398  },
399  1,
400  128
401  },
402 
403  // FILTER_SHARPEN
404  {
405  { {-1, -1, -1, -1, -1},
406  {-1, 2, 2, 2, -1},
407  {-1, 2, 8, 2, -1},
408  {-1, 2, 2, 2, -1},
409  {-1, -1, -1, -1, -1}
410  },
411  8,
412  0
413  },
414 
415  // FILTER_MELT
416  {
417  { { 4, 2, 6, 8, 1},
418  { 1, 2, 5, 4, 2},
419  { 0, -1, 1, -1, 0},
420  { 0, 0, -2, 0, 0},
421  { 0, 0, 0, 0, 0}
422  },
423  32,
424  0
425  },
426 
427  // FILTER_SOBEL_GX
428  {
429  { { 0, 0, 0, 0, 0},
430  { 0, -1, 0, 1, 0},
431  { 0, -2, 0, 2, 0},
432  { 0, -1, 0, 1, 0},
433  { 0, 0, 0, 0, 0}
434  },
435  1,
436  0
437  },
438 
439  // FILTER_SOBEL_GY
440  {
441  { { 1, 2, 4, 2, 1},
442  {-1, -1, 0, 1, 1},
443  {-2, -2, 0, 2, 2},
444  {-1, -1, 0, 1, 1},
445  {-1, -2, -4, -2, -1},
446  },
447  1,
448  0
449  },
450 
451  // FILTER_BLUR_3X3
452  {
453  { { 0, 0, 0, 0, 0},
454  { 0, 1, 2, 1, 0},
455  { 0, 2, 4, 2, 0},
456  { 0, 1, 2, 1, 0},
457  { 0, 0, 0, 0, 0},
458  },
459  16,
460  0
461  }
462 };// Filters
463 
464 
465 // !TODO: This functions can be optimized slipting it between the edges and
466 // do it without use the getpixel function.
467 // Optimization can be done to m_pixels[ix + iy * m_width]
468 // but keep in mind the parallel process of the algorithm
469 void CIMAGE::EfxFilter( CIMAGE *aInImg, E_FILTER aFilterType )
470 {
471  S_FILTER filter = FILTERS[aFilterType];
472 
473  aInImg->m_wraping = WRAP_CLAMP;
475 
476  std::atomic<size_t> nextRow( 0 );
477  std::atomic<size_t> threadsFinished( 0 );
478 
479  size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
480 
481  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
482  {
483  std::thread t = std::thread( [&]()
484  {
485  for( size_t iy = nextRow.fetch_add( 1 );
486  iy < m_height;
487  iy = nextRow.fetch_add( 1 ) )
488  {
489  for( size_t ix = 0; ix < m_width; ix++ )
490  {
491  int v = 0;
492 
493  for( size_t sy = 0; sy < 5; sy++ )
494  {
495  for( size_t sx = 0; sx < 5; sx++ )
496  {
497  int factor = filter.kernel[sx][sy];
498  unsigned char pixelv = aInImg->Getpixel( ix + sx - 2,
499  iy + sy - 2 );
500 
501  v += pixelv * factor;
502  }
503  }
504 
505  v /= filter.div;
506  v += filter.offset;
507  CLAMP(v, 0, 255);
508  //TODO: This needs to write to a separate buffer
509  m_pixels[ix + iy * m_width] = v;
510  }
511  }
512 
513  threadsFinished++;
514  } );
515 
516  t.detach();
517  }
518 
519  while( threadsFinished < parallelThreadCount )
520  std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
521 }
522 
523 
524 void CIMAGE::SetPixelsFromNormalizedFloat( const float * aNormalizedFloatArray )
525 {
526  for( unsigned int i = 0; i < m_wxh; i++ )
527  {
528  int v = aNormalizedFloatArray[i] * 255;
529 
530  CLAMP( v, 0, 255 );
531  m_pixels[i] = v;
532  }
533 }
534 
535 
536 void CIMAGE::SaveAsPNG( const wxString& aFileName ) const
537 {
538  DBG_SaveBuffer( aFileName, m_pixels, m_width, m_height );
539 }
Coords are wrapped arround.
Definition: cimage.h:55
bool wrapCoords(int *aXo, int *aYo) const
Function wrapCoords calculate the coordinates points in accord with the current clamping settings...
Definition: cimage.cpp:77
~CIMAGE()
Definition: cimage.cpp:65
unsigned int m_height
height of the image
Definition: cimage.h:224
#define CLAMP(n, min, max)
Definition: cimage.cpp:39
unsigned char * GetBuffer() const
Function GetBuffer get the image buffer pointer.
Definition: cimage.cpp:71
unsigned char Getpixel(int aX, int aY) const
Function Getpixel get the pixel value from pixel position, position is clamped in accord with the cur...
Definition: cimage.cpp:128
void SaveAsPNG(const wxString &aFileName) const
Function SaveAsPNG save image buffer to a PNG file into the working folder.
Definition: cimage.cpp:536
unsigned char * m_pixels
buffer to store the image 8bit-channel
Definition: cimage.h:222
void Hline(int aXStart, int aXEnd, int aY, unsigned char aValue)
hline - Draws an horizontal line
Definition: cimage.cpp:137
void CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
CircleFilled.
Definition: cimage.cpp:172
#define abs(a)
Definition: auxiliary.h:84
void Setpixel(int aX, int aY, unsigned char aValue)
Function Setpixel set a value in a pixel position, position is clamped in accord with the current cla...
Definition: cimage.cpp:121
unsigned int GetHeight() const
Definition: cimage.h:207
void DBG_SaveBuffer(const wxString &aFileName, const unsigned char *aInBuffer, unsigned int aXSize, unsigned int aYSize)
CIMAGE(unsigned int aXsize, unsigned int aYsize)
Constructor CIMAGE constructs a CIMAGE based on image size.
Definition: cimage.cpp:43
E_IMAGE_OP
Image operation type.
Definition: cimage.h:36
unsigned int GetWidth() const
Definition: cimage.h:206
void CopyFull(const CIMAGE *aImgA, const CIMAGE *aImgB, E_IMAGE_OP aOperation)
Function CopyFull perform a copy operation, based on operation type.
Definition: cimage.cpp:204
void Invert()
Function Invert invert the values of image this <- (255 - this)
Definition: cimage.cpp:197
unsigned char offset
Definition: cimage.h:78
unsigned int m_width
width of the image
Definition: cimage.h:223
unsigned int m_wxh
width * height precalc value
Definition: cimage.h:225
void plot8CircleLines(int aCx, int aCy, int aX, int aY, unsigned char aValue)
Definition: cimage.cpp:112
one 8bit-channel image definition
E_WRAP m_wraping
current wrapping type
Definition: cimage.h:226
signed char kernel[5][5]
Definition: cimage.h:76
unsigned int div
Definition: cimage.h:77
Definition: cimage.h:43
5x5 Filter struct parameters
Definition: cimage.h:75
static const S_FILTER FILTERS[]
Definition: cimage.cpp:330
size_t i
Definition: json11.cpp:597
E_FILTER
Filter type enumeration.
Definition: cimage.h:60
void SetPixelsFromNormalizedFloat(const float *aNormalizedFloatArray)
Function SetPixelsFromNormalizedFloat set the current channel from a float normalized (0...
Definition: cimage.cpp:524
Class CIMAGE manages a 8-bit channel image.
Definition: cimage.h:86
void EfxFilter(CIMAGE *aInImg, E_FILTER aFilterType)
Function EfxFilter apply a filter to the input image and stores it in the image class this <- FilterT...
Definition: cimage.cpp:469
E_WRAP
Image wrap type enumeration.
Definition: cimage.h:52
Coords are clamped to image size.
Definition: cimage.h:54