/src/FreeImage/Source/FreeImageToolkit/Resize.cpp

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 656 lines · 392 code · 112 blank · 152 comment · 91 complexity · 925ba6e2c2c8f1441e28ee8afe9675e4 MD5 · raw file

  1. // ==========================================================
  2. // Upsampling / downsampling classes
  3. //
  4. // Design and implementation by
  5. // - Hervé Drolon (drolon@infonie.fr)
  6. // - Detlev Vendt (detlev.vendt@brillit.de)
  7. //
  8. // This file is part of FreeImage 3
  9. //
  10. // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
  11. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
  12. // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
  13. // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
  14. // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
  15. // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
  16. // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
  17. // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
  18. // THIS DISCLAIMER.
  19. //
  20. // Use at your own risk!
  21. // ==========================================================
  22. #include "Resize.h"
  23. /**
  24. Filter weights table.
  25. This class stores contribution information for an entire line (row or column).
  26. */
  27. CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
  28. unsigned u;
  29. double dWidth;
  30. double dFScale = 1.0;
  31. const double dFilterWidth = pFilter->GetWidth();
  32. // scale factor
  33. const double dScale = double(uDstSize) / double(uSrcSize);
  34. if(dScale < 1.0) {
  35. // minification
  36. dWidth = dFilterWidth / dScale;
  37. dFScale = dScale;
  38. } else {
  39. // magnification
  40. dWidth= dFilterWidth;
  41. }
  42. // allocate a new line contributions structure
  43. //
  44. // window size is the number of sampled pixels
  45. m_WindowSize = 2 * (int)ceil(dWidth) + 1;
  46. m_LineLength = uDstSize;
  47. // allocate list of contributions
  48. m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
  49. for(u = 0 ; u < m_LineLength ; u++) {
  50. // allocate contributions for every pixel
  51. m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
  52. }
  53. // offset for discrete to continuous coordinate conversion
  54. const double dOffset = (0.5 / dScale) - 0.5;
  55. for(u = 0; u < m_LineLength; u++) {
  56. // scan through line of contributions
  57. const double dCenter = (double)u / dScale + dOffset; // reverse mapping
  58. // find the significant edge points that affect the pixel
  59. int iLeft = MAX (0, (int)floor (dCenter - dWidth));
  60. int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1);
  61. // cut edge points to fit in filter window in case of spill-off
  62. if((iRight - iLeft + 1) > int(m_WindowSize)) {
  63. if(iLeft < (int(uSrcSize) - 1 / 2)) {
  64. iLeft++;
  65. } else {
  66. iRight--;
  67. }
  68. }
  69. m_WeightTable[u].Left = iLeft;
  70. m_WeightTable[u].Right = iRight;
  71. int iSrc = 0;
  72. double dTotalWeight = 0; // zero sum of weights
  73. for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
  74. // calculate weights
  75. const double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc));
  76. m_WeightTable[u].Weights[iSrc-iLeft] = weight;
  77. dTotalWeight += weight;
  78. }
  79. if((dTotalWeight > 0) && (dTotalWeight != 1)) {
  80. // normalize weight of neighbouring points
  81. for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
  82. // normalize point
  83. m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
  84. }
  85. // simplify the filter, discarding null weights at the right
  86. iSrc = iRight - iLeft;
  87. while(m_WeightTable[u].Weights[iSrc] == 0){
  88. m_WeightTable[u].Right--;
  89. iSrc--;
  90. if(m_WeightTable[u].Right == m_WeightTable[u].Left)
  91. break;
  92. }
  93. }
  94. }
  95. }
  96. CWeightsTable::~CWeightsTable() {
  97. for(unsigned u = 0; u < m_LineLength; u++) {
  98. // free contributions for every pixel
  99. free(m_WeightTable[u].Weights);
  100. }
  101. // free list of pixels contributions
  102. free(m_WeightTable);
  103. }
  104. // ---------------------------------------------
  105. /**
  106. CResizeEngine<br>
  107. This class performs filtered zoom. It scales an image to the desired dimensions with
  108. any of the CGenericFilter derived filter class.<br>
  109. It works with 8-, 24- and 32-bit buffers.<br><br>
  110. <b>References</b> : <br>
  111. [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
  112. UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
  113. [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999.
  114. [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
  115. */
  116. FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height) {
  117. unsigned src_width = FreeImage_GetWidth(src);
  118. unsigned src_height = FreeImage_GetHeight(src);
  119. unsigned redMask = FreeImage_GetRedMask(src);
  120. unsigned greenMask = FreeImage_GetGreenMask(src);
  121. unsigned blueMask = FreeImage_GetBlueMask(src);
  122. unsigned bpp = FreeImage_GetBPP(src);
  123. if(bpp == 1) {
  124. // convert output to 8-bit
  125. bpp = 8;
  126. }
  127. FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
  128. // allocate the dst image
  129. FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp, redMask, greenMask, blueMask);
  130. if(!dst) return NULL;
  131. if(bpp == 8) {
  132. if(FreeImage_GetColorType(src) == FIC_MINISWHITE) {
  133. // build an inverted greyscale palette
  134. RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
  135. for(unsigned i = 0; i < 256; i++) {
  136. dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)(255 - i);
  137. }
  138. } else {
  139. // build a greyscale palette
  140. RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
  141. for(unsigned i = 0; i < 256; i++) {
  142. dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)i;
  143. }
  144. }
  145. }
  146. /**
  147. Decide which filtering order (xy or yx) is faster for this mapping.
  148. --- The theory ---
  149. Try to minimize calculations by counting the number of convolution multiplies
  150. if(dst_width*src_height <= src_width*dst_height) {
  151. // xy filtering
  152. } else {
  153. // yx filtering
  154. }
  155. --- The practice ---
  156. Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
  157. if(dst_width*dst_height <= src_width*dst_height) {
  158. // xy filtering
  159. } else {
  160. // yx filtering
  161. }
  162. */
  163. if(dst_width <= src_width) {
  164. // xy filtering
  165. // -------------
  166. // allocate a temporary image
  167. FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask);
  168. if(!tmp) {
  169. FreeImage_Unload(dst);
  170. return NULL;
  171. }
  172. // scale source image horizontally into temporary image
  173. horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height);
  174. // scale temporary image vertically into result image
  175. verticalFilter(tmp, dst_width, src_height, dst, dst_width, dst_height);
  176. // free temporary image
  177. FreeImage_Unload(tmp);
  178. } else {
  179. // yx filtering
  180. // -------------
  181. // allocate a temporary image
  182. FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask);
  183. if(!tmp) {
  184. FreeImage_Unload(dst);
  185. return NULL;
  186. }
  187. // scale source image vertically into temporary image
  188. verticalFilter(src, src_width, src_height, tmp, src_width, dst_height);
  189. // scale temporary image horizontally into result image
  190. horizontalFilter(tmp, src_width, dst_height, dst, dst_width, dst_height);
  191. // free temporary image
  192. FreeImage_Unload(tmp);
  193. }
  194. return dst;
  195. }
  196. /// Performs horizontal image filtering
  197. void CResizeEngine::horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) {
  198. if(dst_width == src_width) {
  199. // no scaling required, just copy
  200. switch(FreeImage_GetBPP(src)) {
  201. case 1:
  202. {
  203. if(FreeImage_GetBPP(dst) != 8) break;
  204. for(unsigned y = 0; y < dst_height; y++) {
  205. // convert each row
  206. BYTE *src_bits = FreeImage_GetScanLine(src, y);
  207. BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
  208. FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
  209. }
  210. }
  211. break;
  212. default:
  213. {
  214. const BYTE *src_bits = FreeImage_GetBits(src);
  215. BYTE *dst_bits = FreeImage_GetBits(dst);
  216. memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
  217. }
  218. break;
  219. }
  220. }
  221. else {
  222. // allocate and calculate the contributions
  223. CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
  224. // step through rows
  225. switch(FreeImage_GetImageType(src)) {
  226. case FIT_BITMAP:
  227. {
  228. switch(FreeImage_GetBPP(src)) {
  229. case 1:
  230. {
  231. // scale and convert to 8-bit
  232. if(FreeImage_GetBPP(dst) != 8) break;
  233. for(unsigned y = 0; y < dst_height; y++) {
  234. // scale each row
  235. const BYTE *src_bits = FreeImage_GetScanLine(src, y);
  236. BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
  237. for(unsigned x = 0; x < dst_width; x++) {
  238. // loop through row
  239. double value = 0;
  240. const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
  241. const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
  242. for(unsigned i = iLeft; i <= iRight; i++) {
  243. // scan between boundaries
  244. // accumulate weighted effect of each neighboring pixel
  245. const double weight = weightsTable.getWeight(x, i-iLeft);
  246. const BYTE pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
  247. value += (weight * (double)pixel);
  248. }
  249. value *= 255;
  250. // clamp and place result in destination pixel
  251. dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 255);
  252. }
  253. }
  254. }
  255. break;
  256. case 8:
  257. case 24:
  258. case 32:
  259. {
  260. // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
  261. const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
  262. for(unsigned y = 0; y < dst_height; y++) {
  263. // scale each row
  264. const BYTE *src_bits = FreeImage_GetScanLine(src, y);
  265. BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
  266. for(unsigned x = 0; x < dst_width; x++) {
  267. // loop through row
  268. double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max
  269. const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
  270. const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
  271. for(unsigned i = iLeft; i <= iRight; i++) {
  272. // scan between boundaries
  273. // accumulate weighted effect of each neighboring pixel
  274. const double weight = weightsTable.getWeight(x, i-iLeft);
  275. unsigned index = i * bytespp; // pixel index
  276. for (unsigned j = 0; j < bytespp; j++) {
  277. value[j] += (weight * (double)src_bits[index++]);
  278. }
  279. }
  280. // clamp and place result in destination pixel
  281. for (unsigned j = 0; j < bytespp; j++) {
  282. dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF);
  283. }
  284. dst_bits += bytespp;
  285. }
  286. }
  287. }
  288. break;
  289. }
  290. }
  291. break;
  292. case FIT_UINT16:
  293. case FIT_RGB16:
  294. case FIT_RGBA16:
  295. {
  296. // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
  297. const unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
  298. for(unsigned y = 0; y < dst_height; y++) {
  299. // scale each row
  300. const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
  301. WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
  302. for(unsigned x = 0; x < dst_width; x++) {
  303. // loop through row
  304. double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max
  305. const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
  306. const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
  307. for(unsigned i = iLeft; i <= iRight; i++) {
  308. // scan between boundaries
  309. // accumulate weighted effect of each neighboring pixel
  310. const double weight = weightsTable.getWeight(x, i-iLeft);
  311. unsigned index = i * wordspp; // pixel index
  312. for (unsigned j = 0; j < wordspp; j++) {
  313. value[j] += (weight * (double)src_bits[index++]);
  314. }
  315. }
  316. // clamp and place result in destination pixel
  317. for (unsigned j = 0; j < wordspp; j++) {
  318. dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFFFF);
  319. }
  320. dst_bits += wordspp;
  321. }
  322. }
  323. }
  324. break;
  325. case FIT_FLOAT:
  326. case FIT_RGBF:
  327. case FIT_RGBAF:
  328. {
  329. // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
  330. const unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
  331. for(unsigned y = 0; y < dst_height; y++) {
  332. // scale each row
  333. const float *src_bits = (float*)FreeImage_GetScanLine(src, y);
  334. float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
  335. for(unsigned x = 0; x < dst_width; x++) {
  336. // loop through row
  337. double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
  338. const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
  339. const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
  340. for(unsigned i = iLeft; i <= iRight; i++) {
  341. // scan between boundaries
  342. // accumulate weighted effect of each neighboring pixel
  343. const double weight = weightsTable.getWeight(x, i-iLeft);
  344. unsigned index = i * floatspp; // pixel index
  345. for (unsigned j = 0; j < floatspp; j++) {
  346. value[j] += (weight * (double)src_bits[index++]);
  347. }
  348. }
  349. // place result in destination pixel
  350. for (unsigned j = 0; j < floatspp; j++) {
  351. dst_bits[j] = (float)value[j];
  352. }
  353. dst_bits += floatspp;
  354. }
  355. }
  356. }
  357. break;
  358. }
  359. }
  360. }
  361. /// Performs vertical image filtering
  362. void CResizeEngine::verticalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) {
  363. if(src_height == dst_height) {
  364. // no scaling required, just copy
  365. switch(FreeImage_GetBPP(src)) {
  366. case 1:
  367. {
  368. if(FreeImage_GetBPP(dst) != 8) break;
  369. for(unsigned y = 0; y < dst_height; y++) {
  370. // convert each row
  371. BYTE *src_bits = FreeImage_GetScanLine(src, y);
  372. BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
  373. FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
  374. }
  375. }
  376. break;
  377. default:
  378. {
  379. const BYTE *src_bits = FreeImage_GetBits(src);
  380. BYTE *dst_bits = FreeImage_GetBits(dst);
  381. memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
  382. }
  383. break;
  384. }
  385. }
  386. else {
  387. // allocate and calculate the contributions
  388. CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
  389. // step through columns
  390. switch(FreeImage_GetImageType(src)) {
  391. case FIT_BITMAP:
  392. {
  393. switch(FreeImage_GetBPP(src)) {
  394. case 1:
  395. {
  396. // scale and convert to 8-bit
  397. if(FreeImage_GetBPP(dst) != 8) break;
  398. const unsigned src_pitch = FreeImage_GetPitch(src);
  399. const unsigned dst_pitch = FreeImage_GetPitch(dst);
  400. for(unsigned x = 0; x < dst_width; x++) {
  401. // work on column x in dst
  402. BYTE *dst_bits = FreeImage_GetBits(dst) + x;
  403. // scale each column
  404. for(unsigned y = 0; y < dst_height; y++) {
  405. // loop through column
  406. double value = 0;
  407. const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
  408. const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
  409. BYTE *src_bits = FreeImage_GetScanLine(src, iLeft);
  410. for(unsigned i = iLeft; i <= iRight; i++) {
  411. // scan between boundaries
  412. // accumulate weighted effect of each neighboring pixel
  413. const double weight = weightsTable.getWeight(y, i-iLeft);
  414. const BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
  415. value += (weight * (double)pixel);
  416. src_bits += src_pitch;
  417. }
  418. value *= 255;
  419. // clamp and place result in destination pixel
  420. *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
  421. dst_bits += dst_pitch;
  422. }
  423. }
  424. }
  425. break;
  426. case 8:
  427. case 24:
  428. case 32:
  429. {
  430. // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
  431. const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
  432. const unsigned src_pitch = FreeImage_GetPitch(src);
  433. const unsigned dst_pitch = FreeImage_GetPitch(dst);
  434. for(unsigned x = 0; x < dst_width; x++) {
  435. const unsigned index = x * bytespp; // pixel index
  436. // work on column x in dst
  437. BYTE *dst_bits = FreeImage_GetBits(dst) + index;
  438. // scale each column
  439. for(unsigned y = 0; y < dst_height; y++) {
  440. // loop through column
  441. double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max
  442. const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
  443. const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
  444. const BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index;
  445. for(unsigned i = iLeft; i <= iRight; i++) {
  446. // scan between boundaries
  447. // accumulate weighted effect of each neighboring pixel
  448. const double weight = weightsTable.getWeight(y, i-iLeft);
  449. for (unsigned j = 0; j < bytespp; j++) {
  450. value[j] += (weight * (double)src_bits[j]);
  451. }
  452. src_bits += src_pitch;
  453. }
  454. // clamp and place result in destination pixel
  455. for (unsigned j = 0; j < bytespp; j++) {
  456. dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF);
  457. }
  458. dst_bits += dst_pitch;
  459. }
  460. }
  461. }
  462. break;
  463. }
  464. }
  465. break;
  466. case FIT_UINT16:
  467. case FIT_RGB16:
  468. case FIT_RGBA16:
  469. {
  470. // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
  471. const unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
  472. const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
  473. const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
  474. for(unsigned x = 0; x < dst_width; x++) {
  475. const unsigned index = x * wordspp; // pixel index
  476. // work on column x in dst
  477. WORD *dst_bits = (WORD*)FreeImage_GetBits(dst) + index;
  478. // scale each column
  479. for(unsigned y = 0; y < dst_height; y++) {
  480. // loop through column
  481. double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max
  482. const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
  483. const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
  484. const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index;
  485. for(unsigned i = iLeft; i <= iRight; i++) {
  486. // scan between boundaries
  487. // accumulate weighted effect of each neighboring pixel
  488. const double weight = weightsTable.getWeight(y, i-iLeft);
  489. for (unsigned j = 0; j < wordspp; j++) {
  490. value[j] += (weight * (double)src_bits[j]);
  491. }
  492. src_bits += src_pitch;
  493. }
  494. // clamp and place result in destination pixel
  495. for (unsigned j = 0; j < wordspp; j++) {
  496. dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFFFF);
  497. }
  498. dst_bits += dst_pitch;
  499. }
  500. }
  501. }
  502. break;
  503. case FIT_FLOAT:
  504. case FIT_RGBF:
  505. case FIT_RGBAF:
  506. {
  507. // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
  508. const unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
  509. const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
  510. const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
  511. for(unsigned x = 0; x < dst_width; x++) {
  512. const unsigned index = x * floatspp; // pixel index
  513. // work on column x in dst
  514. float *dst_bits = (float*)FreeImage_GetBits(dst) + index;
  515. // scale each column
  516. for(unsigned y = 0; y < dst_height; y++) {
  517. // loop through column
  518. double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
  519. const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
  520. const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
  521. const float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index;
  522. for(unsigned i = iLeft; i <= iRight; i++) {
  523. // scan between boundaries
  524. // accumulate weighted effect of each neighboring pixel
  525. const double weight = weightsTable.getWeight(y, i-iLeft);
  526. for (unsigned j = 0; j < floatspp; j++) {
  527. value[j] += (weight * (double)src_bits[j]);
  528. }
  529. src_bits += src_pitch;
  530. }
  531. // clamp and place result in destination pixel
  532. for (unsigned j = 0; j < floatspp; j++) {
  533. dst_bits[j] = (float)value[j];
  534. }
  535. dst_bits += dst_pitch;
  536. }
  537. }
  538. }
  539. break;
  540. }
  541. }
  542. }