EtcFilter.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include <stdlib.h>
  2. #include <math.h>
  3. #include "EtcFilter.h"
  4. namespace Etc
  5. {
  6. static const double PiConst = 3.14159265358979323846;
  7. inline double sinc(double x)
  8. {
  9. if ( x == 0.0 )
  10. {
  11. return 1.0;
  12. }
  13. return sin(PiConst * x) / (PiConst * x);
  14. }
  15. //inline float sincf( float x )
  16. //{
  17. // x *= F_PI;
  18. // if (x < 0.01f && x > -0.01f)
  19. // {
  20. // return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
  21. // }
  22. //
  23. // return sinf(x)/x;
  24. //}
  25. //
  26. //double bessel0(double x)
  27. //{
  28. // const double EPSILON_RATIO = 1E-16;
  29. // double xh, sum, pow, ds;
  30. // int k;
  31. //
  32. // xh = 0.5 * x;
  33. // sum = 1.0;
  34. // pow = 1.0;
  35. // k = 0;
  36. // ds = 1.0;
  37. // while (ds > sum * EPSILON_RATIO)
  38. // {
  39. // ++k;
  40. // pow = pow * (xh / k);
  41. // ds = pow * pow;
  42. // sum = sum + ds;
  43. // }
  44. //
  45. // return sum;
  46. //}
  47. //**--------------------------------------------------------------------------
  48. //** Name: kaiser(double alpha, double half_width, double x)
  49. //** Returns:
  50. //** Description: Alpha controls shape of filter. We are using 4.
  51. //**--------------------------------------------------------------------------
  52. //inline double kaiser(double alpha, double half_width, double x)
  53. //{
  54. // double ratio = (x / half_width);
  55. // return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
  56. //}
  57. //
  58. //float Filter_Lanczos4Sinc(float x)
  59. //{
  60. // if (x <= -4.0f || x >= 4.0f) // half-width of 4
  61. // {
  62. // return 0.0;
  63. // }
  64. //
  65. // return sinc(0.875f * x) * sinc(0.25f * x);
  66. //}
  67. //
  68. //double Filter_Kaiser4( double t )
  69. //{
  70. // return kaiser( 4.0, 3.0, t);
  71. //}
  72. //
  73. //double Filter_KaiserOptimal( double t )
  74. //{
  75. // return kaiser( 8.93, 3.0f, t);
  76. //}
  77. double FilterLanczos3( double t )
  78. {
  79. if ( t <= -3.0 || t >= 3.0 )
  80. {
  81. return 0.0;
  82. }
  83. return sinc( t ) * sinc( t / 3.0 );
  84. }
  85. double FilterBox( double t )
  86. {
  87. return ( t > -0.5 && t < 0.5) ? 1.0 : 0.0;
  88. }
  89. double FilterLinear( double t )
  90. {
  91. if (t < 0.0) t = -t;
  92. return (t < 1.0) ? (1.0 - t) : 0.0;
  93. }
  94. //**--------------------------------------------------------------------------
  95. //** Name: CalcContributions( int srcSize,
  96. //** int destSize,
  97. //** double filterSize,
  98. //** bool wrap,
  99. //** double (*FilterProc)(double),
  100. //** FilterWeights contrib[] )
  101. //** Returns: void
  102. //** Description:
  103. //**--------------------------------------------------------------------------
  104. void CalcContributions( int srcSize, int destSize, double filterSize, bool wrap, double (*FilterProc)(double), FilterWeights contrib[] )
  105. {
  106. double scale;
  107. double filterScale;
  108. double center;
  109. double totalWeight;
  110. double weight;
  111. int iRight;
  112. int iLeft;
  113. int iDest;
  114. scale = (double)destSize / srcSize;
  115. if ( scale < 1.0 )
  116. {
  117. filterSize = filterSize / scale;
  118. filterScale = scale;
  119. }
  120. else
  121. {
  122. filterScale = 1.0;
  123. }
  124. if ( filterSize > (double)MaxFilterSize )
  125. {
  126. filterSize = (double)MaxFilterSize;
  127. }
  128. for ( iDest = 0; iDest < destSize; ++iDest )
  129. {
  130. center = (double)iDest / scale;
  131. iLeft = (int)ceil(center - filterSize);
  132. iRight = (int)floor(center + filterSize);
  133. if ( !wrap )
  134. {
  135. if ( iLeft < 0 )
  136. {
  137. iLeft = 0;
  138. }
  139. if ( iRight >= srcSize )
  140. {
  141. iRight = srcSize - 1;
  142. }
  143. }
  144. int numWeights = iRight - iLeft + 1;
  145. contrib[iDest].first = iLeft;
  146. contrib[iDest].numWeights = numWeights;
  147. totalWeight = 0;
  148. double t = ((double)iLeft - center) * filterScale;
  149. for (int i = 0; i < numWeights; i++)
  150. {
  151. weight = (*FilterProc)(t) * filterScale;
  152. totalWeight += weight;
  153. contrib[iDest].weight[i] = weight;
  154. t += filterScale;
  155. }
  156. //**--------------------------------------------------------
  157. //** Normalize weights by dividing by the sum of the weights
  158. //**--------------------------------------------------------
  159. if ( totalWeight > 0.0 )
  160. {
  161. for ( int i = 0; i < numWeights; i++)
  162. {
  163. contrib[iDest].weight[i] /= totalWeight;
  164. }
  165. }
  166. }
  167. }
  168. //**-------------------------------------------------------------------------
  169. //** Name: Filter_TwoPass( RGBCOLOR *pSrcImage,
  170. //** int srcWidth, int srcHeight,
  171. //** RGBCOLOR *pDestImage,
  172. //** int destWidth, int destHeight,
  173. //** double (*FilterProc)(double) )
  174. //** Returns: 0 on failure and 1 on success
  175. //** Description: Filters a 2d image with a two pass filter by averaging the
  176. //** weighted contributions of the pixels within the filter region. The
  177. //** contributions are determined by a weighting function parameter.
  178. //**-------------------------------------------------------------------------
  179. int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
  180. RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) )
  181. {
  182. FilterWeights *contrib;
  183. RGBCOLOR *pPixel;
  184. RGBCOLOR *pSrcPixel;
  185. RGBCOLOR *pTempImage;
  186. int iRow;
  187. int iCol;
  188. int iSrcCol;
  189. int iSrcRow;
  190. int iWeight;
  191. double dRed;
  192. double dGreen;
  193. double dBlue;
  194. double dAlpha;
  195. double filterSize = 3.0;
  196. int maxDim = (srcWidth>srcHeight)?srcWidth:srcHeight;
  197. contrib = (FilterWeights*)malloc(maxDim * sizeof(FilterWeights));
  198. //**------------------------------------------------------------------------
  199. //** Need to create a temporary image to stuff the horizontally scaled image
  200. //**------------------------------------------------------------------------
  201. pTempImage = (RGBCOLOR *)malloc( destWidth * srcHeight * sizeof(RGBCOLOR) );
  202. if ( pTempImage == NULL )
  203. {
  204. // -- GODOT start --
  205. free( contrib );
  206. // -- GODOT end --
  207. return 0;
  208. }
  209. //**-------------------------------------------------------
  210. //** Horizontally filter the image into the temporary image
  211. //**-------------------------------------------------------
  212. bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X);
  213. CalcContributions( srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib );
  214. for ( iRow = 0; iRow < srcHeight; iRow++ )
  215. {
  216. for ( iCol = 0; iCol < destWidth; iCol++ )
  217. {
  218. dRed = 0;
  219. dGreen = 0;
  220. dBlue = 0;
  221. dAlpha = 0;
  222. for ( iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++ )
  223. {
  224. iSrcCol = iWeight + contrib[iCol].first;
  225. if (bWrapHorizontal)
  226. {
  227. iSrcCol = (iSrcCol < 0) ? (srcWidth + iSrcCol) : (iSrcCol >= srcWidth) ? (iSrcCol - srcWidth) : iSrcCol;
  228. }
  229. pSrcPixel = pSrcImage + (iRow * srcWidth) + iSrcCol;
  230. dRed += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[0];
  231. dGreen += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[1];
  232. dBlue += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[2];
  233. dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[3];
  234. }
  235. pPixel = pTempImage + (iRow * destWidth) + iCol;
  236. pPixel->rgba[0] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dRed)));
  237. pPixel->rgba[1] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dGreen)));
  238. pPixel->rgba[2] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dBlue)));
  239. pPixel->rgba[3] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dAlpha)));
  240. }
  241. }
  242. //**-------------------------------------------------------
  243. //** Vertically filter the image into the destination image
  244. //**-------------------------------------------------------
  245. bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y);
  246. CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib);
  247. for ( iCol = 0; iCol < destWidth; iCol++ )
  248. {
  249. for ( iRow = 0; iRow < destHeight; iRow++ )
  250. {
  251. dRed = 0;
  252. dGreen = 0;
  253. dBlue = 0;
  254. dAlpha = 0;
  255. for ( iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++ )
  256. {
  257. iSrcRow = iWeight + contrib[iRow].first;
  258. if (bWrapVertical)
  259. {
  260. iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow;
  261. }
  262. pSrcPixel = pTempImage + (iSrcRow * destWidth) + iCol;
  263. dRed += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[0];
  264. dGreen += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[1];
  265. dBlue += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[2];
  266. dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[3];
  267. }
  268. pPixel = pDestImage + (iRow * destWidth) + iCol;
  269. pPixel->rgba[0] = (unsigned char)(std::max( 0.0, std::min( 255.0, dRed)));
  270. pPixel->rgba[1] = (unsigned char)(std::max( 0.0, std::min( 255.0, dGreen)));
  271. pPixel->rgba[2] = (unsigned char)(std::max( 0.0, std::min( 255.0, dBlue)));
  272. pPixel->rgba[3] = (unsigned char)(std::max( 0.0, std::min( 255.0, dAlpha)));
  273. }
  274. }
  275. free( pTempImage );
  276. free( contrib );
  277. return 1;
  278. }
  279. //**-------------------------------------------------------------------------
  280. //** Name: FilterResample(RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
  281. //** RGBCOLOR *pDstImage, int dstWidth, int dstHeight)
  282. //** Returns: 1
  283. //** Description: This function runs a 2d box filter over the srouce image
  284. //** to produce the destination image.
  285. //**-------------------------------------------------------------------------
  286. void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight,
  287. RGBCOLOR *pDstImage, int dstWidth, int dstHeight )
  288. {
  289. int iRow;
  290. int iCol;
  291. int iSampleRow;
  292. int iSampleCol;
  293. int iFirstSampleRow;
  294. int iFirstSampleCol;
  295. int iLastSampleRow;
  296. int iLastSampleCol;
  297. int red;
  298. int green;
  299. int blue;
  300. int alpha;
  301. int samples;
  302. float xScale;
  303. float yScale;
  304. RGBCOLOR *pSrcPixel;
  305. RGBCOLOR *pDstPixel;
  306. xScale = (float)srcWidth / dstWidth;
  307. yScale = (float)srcHeight / dstHeight;
  308. for ( iRow = 0; iRow < dstHeight; iRow++ )
  309. {
  310. for ( iCol = 0; iCol < dstWidth; iCol++ )
  311. {
  312. iFirstSampleRow = (int)(iRow * yScale);
  313. iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1);
  314. if ( iLastSampleRow >= srcHeight )
  315. {
  316. iLastSampleRow = srcHeight - 1;
  317. }
  318. iFirstSampleCol = (int)(iCol * xScale);
  319. iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1);
  320. if ( iLastSampleCol >= srcWidth )
  321. {
  322. iLastSampleCol = srcWidth - 1;
  323. }
  324. samples = 0;
  325. red = 0;
  326. green = 0;
  327. blue = 0;
  328. alpha = 0;
  329. for ( iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++ )
  330. {
  331. for ( iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++ )
  332. {
  333. pSrcPixel = pSrcImage + iSampleRow * srcWidth + iSampleCol;
  334. red += pSrcPixel->rgba[0];
  335. green += pSrcPixel->rgba[1];
  336. blue += pSrcPixel->rgba[2];
  337. alpha += pSrcPixel->rgba[3];
  338. samples++;
  339. }
  340. }
  341. pDstPixel = pDstImage + iRow * dstWidth + iCol;
  342. if ( samples > 0 )
  343. {
  344. pDstPixel->rgba[0] = static_cast<uint8_t>(red / samples);
  345. pDstPixel->rgba[1] = static_cast<uint8_t>(green / samples);
  346. pDstPixel->rgba[2] = static_cast<uint8_t>(blue / samples);
  347. pDstPixel->rgba[3] = static_cast<uint8_t>(alpha / samples);
  348. }
  349. else
  350. {
  351. pDstPixel->rgba[0] = static_cast<uint8_t>(red);
  352. pDstPixel->rgba[1] = static_cast<uint8_t>(green);
  353. pDstPixel->rgba[2] = static_cast<uint8_t>(blue);
  354. pDstPixel->rgba[3] = static_cast<uint8_t>(alpha);
  355. }
  356. }
  357. }
  358. }
  359. }