PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/cinder/ip/Blur.cpp

https://github.com/afrancois/Cinder
C++ | 408 lines | 291 code | 80 blank | 37 comment | 52 complexity | e8b40b0d46d447a009696cfae25d0ac8 MD5 | raw file
  1. /*
  2. Copyright (c) 2015, The Cinder Project, All rights reserved.
  3. This code is intended for use with the Cinder C++ library: http://libcinder.org
  4. Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  5. the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice, this list of conditions and
  7. the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
  9. the following disclaimer in the documentation and/or other materials provided with the distribution.
  10. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
  11. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  12. PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  13. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  14. TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  15. HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  16. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  17. POSSIBILITY OF SUCH DAMAGE.
  18. */
  19. #include "cinder/ip/Blur.h"
  20. namespace cinder { namespace ip {
  21. namespace {
  22. template<typename T>
  23. uint8_t getPixelIncrement( const SurfaceT<T> &surface )
  24. {
  25. return surface.getPixelInc();
  26. }
  27. template<typename T>
  28. uint8_t getPixelIncrement( const ChannelT<T> &channel )
  29. {
  30. return channel.getIncrement();
  31. }
  32. template<typename T>
  33. uint8_t getPixelDataOffset( const SurfaceT<T> &surface )
  34. {
  35. // even for XRGB, RGBX, etc, all red/green/blue data is contiguous. We aren't concerned about their order,
  36. // we just want the first color (nonalpha) byte
  37. return std::min( surface.getChannelOrder().getRedOffset(), surface.getChannelOrder().getBlueOffset() );
  38. }
  39. template<typename T>
  40. uint8_t getPixelDataOffset( const ChannelT<T> & /*surface*/ )
  41. {
  42. return 0;
  43. }
  44. // Core implementation of stackBlur algorithm due to Mario Klingemann.
  45. // http://incubator.quasimondo.com/processing/fast_blur_deluxe.php
  46. template<typename T, typename SUMT, typename IMAGET, uint8_t CHANNELS>
  47. void stackBlur_impl( const IMAGET &srcSurface, IMAGET *dstSurface, const Area &area, int radius )
  48. {
  49. const int32_t width = area.getWidth();
  50. const int32_t height = area.getHeight();
  51. const int32_t widthMinusOne = width - 1;
  52. const int32_t heightMinusOne = height - 1;
  53. const int32_t div = radius + radius + 1;
  54. const int32_t radiusPlusOne = radius + 1;
  55. const SUMT divisor = (SUMT)(((div+1)>>1)*((div+1)>>1));
  56. const SUMT invDivisor = 1 / divisor;
  57. const uint8_t srcPixelInc = ( CHANNELS == 4 ) ? 4 : getPixelIncrement( srcSurface );
  58. const uint8_t dstPixelInc = ( CHANNELS == 4 ) ? 4 : getPixelIncrement( *dstSurface );
  59. const ptrdiff_t srcRowInc = srcSurface.getRowBytes() / sizeof(T);
  60. const ptrdiff_t dstRowInc = dstSurface->getRowBytes() / sizeof(T);
  61. const T *srcPixelData = srcSurface.getData( area.getUL() );
  62. T *dstPixelData = dstSurface->getData( area.getUL() );
  63. srcPixelData += getPixelDataOffset( srcSurface );
  64. dstPixelData += getPixelDataOffset( *dstSurface );
  65. std::unique_ptr<SUMT[]> stack( new SUMT[div*CHANNELS] );
  66. SUMT *tempPixelData = (SUMT*)malloc(width * height * sizeof(SUMT) * CHANNELS);
  67. SUMT *channelData = tempPixelData;
  68. SUMT *sir;
  69. SUMT inSum[CHANNELS], outSum[CHANNELS], sum[CHANNELS];
  70. int32_t p, yp;
  71. int stackPointer, rbs;
  72. int yi = 0;
  73. for( int32_t y = 0; y < height; y++ ) {
  74. for( int c = 0; c < CHANNELS; ++c )
  75. inSum[c] = outSum[c] = sum[c] = 0;
  76. for( int32_t i = -radius;i <= radius; i++ ) {
  77. sir = &stack[(i + radius)*CHANNELS];
  78. size_t offset = y * srcRowInc + std::min(widthMinusOne, std::max(i, 0)) * srcPixelInc;
  79. rbs = radiusPlusOne - abs(i);
  80. for( int c = 0; c < CHANNELS; ++c )
  81. sir[c] = srcPixelData[offset + c];
  82. for( int c = 0; c < CHANNELS; ++c )
  83. sum[c] += sir[c] * rbs;
  84. if( i > 0 )
  85. for( int c = 0; c < CHANNELS; ++c )
  86. inSum[c] += sir[c];
  87. else
  88. for( int c = 0; c < CHANNELS; ++c )
  89. outSum[c] += sir[c];
  90. }
  91. stackPointer = radius;
  92. for( int32_t x = 0; x < width; x++ ) {
  93. for( int c = 0; c < CHANNELS; ++c ) {
  94. if( std::is_integral<SUMT>::value )
  95. channelData[c+yi*CHANNELS] = sum[c] / divisor;
  96. else
  97. channelData[c+yi*CHANNELS] = sum[c] * invDivisor;
  98. sum[c] -= outSum[c];
  99. }
  100. int stackStart = stackPointer - radius + div;
  101. sir = &stack[(stackStart % div)*CHANNELS];
  102. for( int c = 0; c < CHANNELS; ++c )
  103. outSum[c] -= sir[c];
  104. size_t offset = y * srcRowInc + std::min(x + radius + 1, widthMinusOne) * srcPixelInc;
  105. for( int c = 0; c < CHANNELS; ++c ) {
  106. sir[c] = srcPixelData[offset+c];
  107. inSum[c] += sir[c];
  108. sum[c] += inSum[c];
  109. }
  110. stackPointer = (stackPointer + 1) % div;
  111. sir = &stack[stackPointer*CHANNELS];
  112. for( int c = 0; c < CHANNELS; ++c ) {
  113. outSum[c] += sir[c];
  114. inSum[c] -= sir[c];
  115. }
  116. yi++;
  117. }
  118. }
  119. for( int32_t x = 0; x < width; x++ ) {
  120. for( int c = 0; c < CHANNELS; ++c )
  121. inSum[c] = outSum[c] = sum[c] = 0;
  122. yp = -radius * width;
  123. for( int i = -radius; i <= radius; i++ ) {
  124. yi = std::max(0, yp) + x;
  125. sir = &stack[(i + radius)*CHANNELS];
  126. for( int c = 0; c < CHANNELS; ++c )
  127. sir[c] = channelData[c+yi*CHANNELS];
  128. rbs = radiusPlusOne - abs(i);
  129. for( int c = 0; c < CHANNELS; ++c )
  130. sum[c] += channelData[c+yi*CHANNELS] * rbs;
  131. if( i > 0 )
  132. for( int c = 0; c < CHANNELS; ++c )
  133. inSum[c] += sir[c];
  134. else
  135. for( int c = 0; c < CHANNELS; ++c )
  136. outSum[c] += sir[c];
  137. if( i < heightMinusOne )
  138. yp += width;
  139. }
  140. size_t offset = x * dstPixelInc;
  141. stackPointer = radius;
  142. for( int32_t y = 0; y < height; y++) {
  143. for( int c = 0; c < CHANNELS; ++c ) {
  144. if( std::is_integral<SUMT>::value )
  145. dstPixelData[offset + c] = (T)(sum[c] / divisor);
  146. else
  147. dstPixelData[offset + c] = (T)(sum[c] * invDivisor);
  148. sum[c] -= outSum[c];
  149. }
  150. int stackStart = stackPointer - radius + div;
  151. sir = &stack[(stackStart % div)*CHANNELS];
  152. for( int c = 0; c < CHANNELS; ++c )
  153. outSum[c] -= sir[c];
  154. p = x + std::min( y + radiusPlusOne, heightMinusOne ) * width;
  155. for( int c = 0; c < CHANNELS; ++c ) {
  156. sir[c] = channelData[c+p*CHANNELS];
  157. inSum[c] += sir[c];
  158. sum[c] += inSum[c];
  159. }
  160. stackPointer = (stackPointer + 1) % div;
  161. sir = &stack[stackPointer*CHANNELS];
  162. for( int c = 0; c < CHANNELS; ++c ) {
  163. outSum[c] += sir[c];
  164. inSum[c] -= sir[c];
  165. }
  166. offset += dstRowInc;
  167. }
  168. }
  169. free( tempPixelData );
  170. }
  171. } // anonymous namespace
  172. ///////////////////////////////////////////////////////////////////////////////////
  173. // Surface8u
  174. void stackBlur( Surface8u *surface, int radius )
  175. {
  176. if( radius < 1 )
  177. return;
  178. if( surface->hasAlpha() )
  179. stackBlur_impl<uint8_t,int32_t,Surface8u,4>( *surface, surface, surface->getBounds(), radius );
  180. else
  181. stackBlur_impl<uint8_t,int32_t,Surface8u,3>( *surface, surface, surface->getBounds(), radius );
  182. }
  183. void stackBlur( Surface8u *surface, const Area &area, int radius )
  184. {
  185. if( radius < 1 )
  186. return;
  187. const Area clippedArea = area.getClipBy( surface->getBounds() );
  188. if( surface->hasAlpha() )
  189. stackBlur_impl<uint8_t,int32_t,Surface8u,4>( *surface, surface, clippedArea, radius );
  190. else
  191. stackBlur_impl<uint8_t,int32_t,Surface8u,3>( *surface, surface, clippedArea, radius );
  192. }
  193. Surface8u stackBlurCopy( const Surface8u &surface, int radius )
  194. {
  195. Surface8u result = surface.clone( false );
  196. if( surface.hasAlpha() )
  197. stackBlur_impl<uint8_t,int32_t,Surface8u,4>( surface, &result, surface.getBounds(), radius );
  198. else
  199. stackBlur_impl<uint8_t,int32_t,Surface8u,3>( surface, &result, surface.getBounds(), radius );
  200. return result;
  201. }
  202. ///////////////////////////////////////////////////////////////////////////////////
  203. // Channel8u
  204. void stackBlur( Channel8u *channel, int radius )
  205. {
  206. if( radius < 1 )
  207. return;
  208. stackBlur_impl<uint8_t,int32_t,Channel8u,1>( *channel, channel, channel->getBounds(), radius );
  209. }
  210. void stackBlur( Channel8u *channel, const Area &area, int radius )
  211. {
  212. if( radius < 1 )
  213. return;
  214. const Area clippedArea = area.getClipBy( channel->getBounds() );
  215. stackBlur_impl<uint8_t,int32_t,Channel8u,1>( *channel, channel, clippedArea, radius );
  216. }
  217. Channel8u stackBlurCopy( const Channel8u &channel, int radius )
  218. {
  219. Channel8u result = channel.clone( false );
  220. stackBlur_impl<uint8_t,int32_t,Channel8u,1>( channel, &result, channel.getBounds(), radius );
  221. return result;
  222. }
  223. ///////////////////////////////////////////////////////////////////////////////////
  224. // Surface16u
  225. void stackBlur( Surface16u *surface, int radius )
  226. {
  227. if( radius < 1 )
  228. return;
  229. if( surface->hasAlpha() )
  230. stackBlur_impl<uint16_t,int64_t,Surface16u,4>( *surface, surface, surface->getBounds(), radius );
  231. else
  232. stackBlur_impl<uint16_t,int64_t,Surface16u,3>( *surface, surface, surface->getBounds(), radius );
  233. }
  234. void stackBlur( Surface16u *surface, const Area &area, int radius )
  235. {
  236. if( radius < 1 )
  237. return;
  238. const Area clippedArea = area.getClipBy( surface->getBounds() );
  239. if( surface->hasAlpha() )
  240. stackBlur_impl<uint16_t,int64_t,Surface16u,4>( *surface, surface, clippedArea, radius );
  241. else
  242. stackBlur_impl<uint16_t,int64_t,Surface16u,3>( *surface, surface, clippedArea, radius );
  243. }
  244. Surface16u stackBlurCopy( const Surface16u &surface, int radius )
  245. {
  246. Surface16u result = surface.clone( false );
  247. if( surface.hasAlpha() )
  248. stackBlur_impl<uint16_t,int64_t,Surface16u,4>( surface, &result, surface.getBounds(), radius );
  249. else
  250. stackBlur_impl<uint16_t,int64_t,Surface16u,3>( surface, &result, surface.getBounds(), radius );
  251. return result;
  252. }
  253. ///////////////////////////////////////////////////////////////////////////////////
  254. // Channel16u
  255. void stackBlur( Channel16u *channel, int radius )
  256. {
  257. if( radius < 1 )
  258. return;
  259. stackBlur_impl<uint16_t,int64_t,Channel16u,1>( *channel, channel, channel->getBounds(), radius );
  260. }
  261. void stackBlur( Channel16u *channel, const Area &area, int radius )
  262. {
  263. if( radius < 1 )
  264. return;
  265. const Area clippedArea = area.getClipBy( channel->getBounds() );
  266. stackBlur_impl<uint16_t,int64_t,Channel16u,1>( *channel, channel, clippedArea, radius );
  267. }
  268. Channel16u stackBlurCopy( const Channel16u &channel, int radius )
  269. {
  270. Channel16u result = channel.clone( false );
  271. stackBlur_impl<uint16_t,int64_t,Channel16u,1>( channel, &result, channel.getBounds(), radius );
  272. return result;
  273. }
  274. ///////////////////////////////////////////////////////////////////////////////////
  275. // Surface32f
  276. void stackBlur( Surface32f *surface, int radius )
  277. {
  278. if( radius < 1 )
  279. return;
  280. if( surface->hasAlpha() )
  281. stackBlur_impl<float,float,Surface32f,4>( *surface, surface, surface->getBounds(), radius );
  282. else
  283. stackBlur_impl<float,float,Surface32f,3>( *surface, surface, surface->getBounds(), radius );
  284. }
  285. void stackBlur( Surface32f *surface, const Area &area, int radius )
  286. {
  287. if( radius < 1 )
  288. return;
  289. const Area clippedArea = area.getClipBy( surface->getBounds() );
  290. if( surface->hasAlpha() )
  291. stackBlur_impl<float,float,Surface32f,4>( *surface, surface, clippedArea, radius );
  292. else
  293. stackBlur_impl<float,float,Surface32f,3>( *surface, surface, clippedArea, radius );
  294. }
  295. Surface32f stackBlurCopy( const Surface32f &surface, int radius )
  296. {
  297. Surface32f result = surface.clone( false );
  298. if( surface.hasAlpha() )
  299. stackBlur_impl<float,float,Surface32f,4>( surface, &result, surface.getBounds(), radius );
  300. else
  301. stackBlur_impl<float,float,Surface32f,3>( surface, &result, surface.getBounds(), radius );
  302. return result;
  303. }
  304. ///////////////////////////////////////////////////////////////////////////////////
  305. // Channel32f
  306. void stackBlur( Channel32f *channel, int radius )
  307. {
  308. if( radius < 1 )
  309. return;
  310. stackBlur_impl<float,float,Channel32f,1>( *channel, channel, channel->getBounds(), radius );
  311. }
  312. void stackBlur( Channel32f *channel, const Area &area, int radius )
  313. {
  314. if( radius < 1 )
  315. return;
  316. const Area clippedArea = area.getClipBy( channel->getBounds() );
  317. stackBlur_impl<float,float,Channel32f,1>( *channel, channel, clippedArea, radius );
  318. }
  319. Channel32f stackBlurCopy( const Channel32f &channel, int radius )
  320. {
  321. Channel32f result = channel.clone( false );
  322. stackBlur_impl<float,float,Channel32f,1>( channel, &result, channel.getBounds(), radius );
  323. return result;
  324. }
  325. } } // namespace cinder::ip