PageRenderTime 58ms CodeModel.GetById 8ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/cabalistic/ogredeps/
C++ | 656 lines | 392 code | 112 blank | 152 comment | 84 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
 23#include "Resize.h"
 24
 25/**
 26  Filter weights table.
 27  This class stores contribution information for an entire line (row or column).
 28*/
 29CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
 30	unsigned u;
 31	double dWidth;
 32	double dFScale = 1.0;
 33	const double dFilterWidth = pFilter->GetWidth();
 34
 35	// scale factor
 36	const double dScale = double(uDstSize) / double(uSrcSize);
 37
 38	if(dScale < 1.0) {
 39		// minification
 40		dWidth = dFilterWidth / dScale; 
 41		dFScale = dScale; 
 42	} else {
 43		// magnification
 44		dWidth= dFilterWidth; 
 45	}
 46
 47	// allocate a new line contributions structure
 48	//
 49	// window size is the number of sampled pixels
 50	m_WindowSize = 2 * (int)ceil(dWidth) + 1; 
 51	m_LineLength = uDstSize; 
 52	 // allocate list of contributions 
 53	m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
 54	for(u = 0 ; u < m_LineLength ; u++) {
 55		// allocate contributions for every pixel
 56		m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
 57	}
 58
 59	// offset for discrete to continuous coordinate conversion
 60	const double dOffset = (0.5 / dScale) - 0.5;
 61
 62
 63	for(u = 0; u < m_LineLength; u++) {
 64		// scan through line of contributions
 65		const double dCenter = (double)u / dScale + dOffset;   // reverse mapping
 66		// find the significant edge points that affect the pixel
 67		int iLeft = MAX (0, (int)floor (dCenter - dWidth)); 
 68		int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1); 
 69
 70		// cut edge points to fit in filter window in case of spill-off
 71		if((iRight - iLeft + 1) > int(m_WindowSize)) {
 72			if(iLeft < (int(uSrcSize) - 1 / 2)) {
 73				iLeft++; 
 74			} else {
 75				iRight--; 
 76			}
 77		}
 78
 79		m_WeightTable[u].Left = iLeft; 
 80		m_WeightTable[u].Right = iRight;
 81
 82		int iSrc = 0;
 83		double dTotalWeight = 0;  // zero sum of weights
 84		for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
 85			// calculate weights
 86			const double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc));
 87			m_WeightTable[u].Weights[iSrc-iLeft] = weight;
 88			dTotalWeight += weight;
 89		}
 90		if((dTotalWeight > 0) && (dTotalWeight != 1)) {
 91			// normalize weight of neighbouring points
 92			for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
 93				// normalize point
 94				m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; 
 95			}
 96			// simplify the filter, discarding null weights at the right
 97			iSrc = iRight - iLeft;
 98			while(m_WeightTable[u].Weights[iSrc] == 0){
 99				m_WeightTable[u].Right--;
100				iSrc--;
101				if(m_WeightTable[u].Right == m_WeightTable[u].Left)
102					break;
103			}
104
105		}
106   } 
107}
108
109CWeightsTable::~CWeightsTable() {
110	for(unsigned u = 0; u < m_LineLength; u++) {
111		// free contributions for every pixel
112		free(m_WeightTable[u].Weights);
113	}
114	// free list of pixels contributions
115	free(m_WeightTable);
116}
117
118// ---------------------------------------------
119
120/**
121 CResizeEngine<br>
122 This class performs filtered zoom. It scales an image to the desired dimensions with 
123 any of the CGenericFilter derived filter class.<br>
124 It works with 8-, 24- and 32-bit buffers.<br><br>
125
126 <b>References</b> : <br>
127 [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering. 
128 UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
129 [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999. 
130 [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
131
132*/
133
134FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height) { 
135	unsigned src_width  = FreeImage_GetWidth(src); 
136	unsigned src_height = FreeImage_GetHeight(src);
137
138	unsigned redMask	= FreeImage_GetRedMask(src);
139	unsigned greenMask	= FreeImage_GetGreenMask(src);
140	unsigned blueMask	= FreeImage_GetBlueMask(src);
141
142	unsigned bpp = FreeImage_GetBPP(src);
143	if(bpp == 1) {
144		// convert output to 8-bit
145		bpp = 8;
146	}
147
148	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
149
150	// allocate the dst image
151	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp, redMask, greenMask, blueMask);
152	if(!dst) return NULL;
153	
154	if(bpp == 8) {
155		if(FreeImage_GetColorType(src) == FIC_MINISWHITE) {
156			// build an inverted greyscale palette
157			RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
158			for(unsigned i = 0; i < 256; i++) {
159				dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)(255 - i);
160			}
161		} else {
162			// build a greyscale palette
163			RGBQUAD *dst_pal = FreeImage_GetPalette(dst);
164			for(unsigned i = 0; i < 256; i++) {
165				dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)i;
166			}
167		}
168	}
169
170	/**
171	Decide which filtering order (xy or yx) is faster for this mapping. 
172	--- The theory ---
173	Try to minimize calculations by counting the number of convolution multiplies
174	if(dst_width*src_height <= src_width*dst_height) {
175		// xy filtering
176	} else {
177		// yx filtering
178	}
179	--- The practice ---
180	Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
181	if(dst_width*dst_height <= src_width*dst_height) {
182		// xy filtering
183	} else {
184		// yx filtering
185	}
186	*/
187
188	if(dst_width <= src_width) {
189		// xy filtering
190		// -------------
191
192		// allocate a temporary image
193		FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask);
194		if(!tmp) {
195			FreeImage_Unload(dst);
196			return NULL;
197		}
198
199		// scale source image horizontally into temporary image
200		horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height);
201
202		// scale temporary image vertically into result image    
203		verticalFilter(tmp, dst_width, src_height, dst, dst_width, dst_height);
204
205		// free temporary image
206		FreeImage_Unload(tmp);
207
208	} else {
209		// yx filtering
210		// -------------
211
212		// allocate a temporary image
213		FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask);
214		if(!tmp) {
215			FreeImage_Unload(dst);
216			return NULL;
217		}
218
219		// scale source image vertically into temporary image
220		verticalFilter(src, src_width, src_height, tmp, src_width, dst_height);
221
222		// scale temporary image horizontally into result image    
223		horizontalFilter(tmp, src_width, dst_height, dst, dst_width, dst_height);
224
225		// free temporary image
226		FreeImage_Unload(tmp);
227	}
228
229	return dst;
230} 
231
232
233/// Performs horizontal image filtering
234void CResizeEngine::horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { 
235	if(dst_width == src_width) {
236		// no scaling required, just copy
237		switch(FreeImage_GetBPP(src)) {
238			case 1:
239			{
240				if(FreeImage_GetBPP(dst) != 8) break;
241				for(unsigned y = 0; y < dst_height; y++) {
242					// convert each row 
243					BYTE *src_bits = FreeImage_GetScanLine(src, y);
244					BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
245					FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
246				}
247			}
248			break;
249
250			default:
251			{
252				const BYTE *src_bits = FreeImage_GetBits(src);
253				BYTE *dst_bits = FreeImage_GetBits(dst);
254				memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
255			}
256			break;
257		}
258	}
259	else {
260
261		// allocate and calculate the contributions
262		CWeightsTable weightsTable(m_pFilter, dst_width, src_width); 
263		
264		// step through rows
265		switch(FreeImage_GetImageType(src)) {
266			case FIT_BITMAP:
267			{
268				switch(FreeImage_GetBPP(src)) {
269					case 1:
270					{
271						// scale and convert to 8-bit
272						if(FreeImage_GetBPP(dst) != 8) break;
273
274						for(unsigned y = 0; y < dst_height; y++) {
275							// scale each row 
276							const BYTE *src_bits = FreeImage_GetScanLine(src, y);
277							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
278
279							for(unsigned x = 0; x < dst_width; x++) {
280								// loop through row
281								double value = 0;
282								const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
283								const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
284
285								for(unsigned i = iLeft; i <= iRight; i++) {
286									// scan between boundaries
287									// accumulate weighted effect of each neighboring pixel
288									const double weight = weightsTable.getWeight(x, i-iLeft);
289									
290									const BYTE pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
291									value += (weight * (double)pixel);
292								} 
293								value *= 255;
294
295								// clamp and place result in destination pixel
296								dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 255);
297							} 
298						}
299					}
300					break;
301
302					case 8:
303					case 24:
304					case 32:
305					{
306						// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
307						const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
308
309						for(unsigned y = 0; y < dst_height; y++) {
310							// scale each row 
311							const BYTE *src_bits = FreeImage_GetScanLine(src, y);
312							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
313
314							for(unsigned x = 0; x < dst_width; x++) {
315								// loop through row
316								double value[4] = {0, 0, 0, 0};					// 4 = 32 bpp max
317								const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
318								const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
319
320								for(unsigned i = iLeft; i <= iRight; i++) {
321									// scan between boundaries
322									// accumulate weighted effect of each neighboring pixel
323									const double weight = weightsTable.getWeight(x, i-iLeft);
324									
325									unsigned index = i * bytespp;	// pixel index
326									for (unsigned j = 0; j < bytespp; j++) {
327										value[j] += (weight * (double)src_bits[index++]); 
328									}
329								} 
330
331								// clamp and place result in destination pixel
332								for (unsigned j = 0; j < bytespp; j++) {
333									dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF);
334								}
335
336								dst_bits += bytespp;
337							} 
338						}
339					}
340					break;
341				}
342			}
343			break;
344
345			case FIT_UINT16:
346			case FIT_RGB16:
347			case FIT_RGBA16:
348			{
349				// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
350				const unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
351
352				for(unsigned y = 0; y < dst_height; y++) {
353					// scale each row 
354					const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
355					WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
356
357					for(unsigned x = 0; x < dst_width; x++) {
358						// loop through row
359						double value[4] = {0, 0, 0, 0};					// 4 = 64 bpp max
360						const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
361						const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
362
363						for(unsigned i = iLeft; i <= iRight; i++) {
364							// scan between boundaries
365							// accumulate weighted effect of each neighboring pixel
366							const double weight = weightsTable.getWeight(x, i-iLeft);
367							
368							unsigned index = i * wordspp;	// pixel index
369							for (unsigned j = 0; j < wordspp; j++) {
370								value[j] += (weight * (double)src_bits[index++]); 
371							}
372						} 
373
374						// clamp and place result in destination pixel
375						for (unsigned j = 0; j < wordspp; j++) {
376							dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFFFF);
377						}
378
379						dst_bits += wordspp;
380					} 
381				}
382			}
383			break;
384
385			case FIT_FLOAT:
386			case FIT_RGBF:
387			case FIT_RGBAF:
388			{
389				// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
390				const unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
391
392				for(unsigned y = 0; y < dst_height; y++) {
393					// scale each row 
394					const float *src_bits = (float*)FreeImage_GetScanLine(src, y);
395					float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
396
397					for(unsigned x = 0; x < dst_width; x++) {
398						// loop through row
399						double value[4] = {0, 0, 0, 0};					// 4 = 128 bpp max
400						const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
401						const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
402
403						for(unsigned i = iLeft; i <= iRight; i++) {
404							// scan between boundaries
405							// accumulate weighted effect of each neighboring pixel
406							const double weight = weightsTable.getWeight(x, i-iLeft);
407							
408							unsigned index = i * floatspp;	// pixel index
409							for (unsigned j = 0; j < floatspp; j++) {
410								value[j] += (weight * (double)src_bits[index++]); 
411							}
412						} 
413
414						// place result in destination pixel
415						for (unsigned j = 0; j < floatspp; j++) {
416							dst_bits[j] = (float)value[j];
417						}
418
419						dst_bits += floatspp;
420					} 
421				}
422			}
423			break;
424
425		}
426	}
427} 
428
429/// Performs vertical image filtering
430void CResizeEngine::verticalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { 
431	if(src_height == dst_height) {
432		// no scaling required, just copy
433		switch(FreeImage_GetBPP(src)) {
434			case 1:
435			{
436				if(FreeImage_GetBPP(dst) != 8) break;
437				for(unsigned y = 0; y < dst_height; y++) {
438					// convert each row 
439					BYTE *src_bits = FreeImage_GetScanLine(src, y);
440					BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
441					FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width);
442				}
443			}
444			break;
445
446			default:
447			{
448				const BYTE *src_bits = FreeImage_GetBits(src);
449				BYTE *dst_bits = FreeImage_GetBits(dst);
450				memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst));
451			}
452			break;
453		}
454
455	}
456	else {
457
458		// allocate and calculate the contributions
459		CWeightsTable weightsTable(m_pFilter, dst_height, src_height); 
460
461		// step through columns
462		switch(FreeImage_GetImageType(src)) {
463			case FIT_BITMAP:
464			{
465				switch(FreeImage_GetBPP(src)) {
466					case 1:
467					{
468						// scale and convert to 8-bit
469						if(FreeImage_GetBPP(dst) != 8) break;
470
471						const unsigned src_pitch = FreeImage_GetPitch(src);
472						const unsigned dst_pitch = FreeImage_GetPitch(dst);
473
474						for(unsigned x = 0; x < dst_width; x++) {
475
476							// work on column x in dst
477							BYTE *dst_bits = FreeImage_GetBits(dst) + x;
478
479							// scale each column
480							for(unsigned y = 0; y < dst_height; y++) {
481								// loop through column
482								double value = 0;
483								const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
484								const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
485
486								BYTE *src_bits = FreeImage_GetScanLine(src, iLeft);
487
488								for(unsigned i = iLeft; i <= iRight; i++) {
489									// scan between boundaries
490									// accumulate weighted effect of each neighboring pixel
491									const double weight = weightsTable.getWeight(y, i-iLeft);
492									
493									const BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
494									value += (weight * (double)pixel);
495
496									src_bits += src_pitch;
497								}
498								value *= 255;
499
500								// clamp and place result in destination pixel
501								*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
502
503								dst_bits += dst_pitch;
504							}
505						}
506					}
507					break;
508
509					case 8:
510					case 24:
511					case 32:
512					{
513						// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
514						const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
515
516						const unsigned src_pitch = FreeImage_GetPitch(src);
517						const unsigned dst_pitch = FreeImage_GetPitch(dst);
518
519						for(unsigned x = 0; x < dst_width; x++) {
520							const unsigned index = x * bytespp;	// pixel index
521
522							// work on column x in dst
523							BYTE *dst_bits = FreeImage_GetBits(dst) + index;
524
525							// scale each column
526							for(unsigned y = 0; y < dst_height; y++) {
527								// loop through column
528								double value[4] = {0, 0, 0, 0};					// 4 = 32 bpp max
529								const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
530								const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
531
532								const BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index;
533
534								for(unsigned i = iLeft; i <= iRight; i++) {
535									// scan between boundaries
536									// accumulate weighted effect of each neighboring pixel
537									const double weight = weightsTable.getWeight(y, i-iLeft);
538									for (unsigned j = 0; j < bytespp; j++) {
539										value[j] += (weight * (double)src_bits[j]);
540									}
541
542									src_bits += src_pitch;
543								}
544
545								// clamp and place result in destination pixel
546								for (unsigned j = 0; j < bytespp; j++) {
547									dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF);
548								}
549
550								dst_bits += dst_pitch;
551							}
552						}
553					}
554					break;
555				}
556			}
557			break;
558
559			case FIT_UINT16:
560			case FIT_RGB16:
561			case FIT_RGBA16:
562			{
563				// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
564				const unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
565
566				const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
567				const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
568
569				for(unsigned x = 0; x < dst_width; x++) {
570					const unsigned index = x * wordspp;	// pixel index
571
572					// work on column x in dst
573					WORD *dst_bits = (WORD*)FreeImage_GetBits(dst) + index;
574
575					// scale each column
576					for(unsigned y = 0; y < dst_height; y++) {
577						// loop through column
578						double value[4] = {0, 0, 0, 0};					// 4 = 64 bpp max
579						const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
580						const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
581
582						const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index;
583
584						for(unsigned i = iLeft; i <= iRight; i++) {
585							// scan between boundaries
586							// accumulate weighted effect of each neighboring pixel
587							const double weight = weightsTable.getWeight(y, i-iLeft);
588							for (unsigned j = 0; j < wordspp; j++) {
589								value[j] += (weight * (double)src_bits[j]);
590							}
591
592							src_bits += src_pitch;
593						}
594
595						// clamp and place result in destination pixel
596						for (unsigned j = 0; j < wordspp; j++) {
597							dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFFFF);
598						}
599
600						dst_bits += dst_pitch;
601					}
602				}
603			}
604			break;
605
606			case FIT_FLOAT:
607			case FIT_RGBF:
608			case FIT_RGBAF:
609			{
610				// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
611				const unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
612
613				const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
614				const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
615
616				for(unsigned x = 0; x < dst_width; x++) {
617					const unsigned index = x * floatspp;	// pixel index
618
619					// work on column x in dst
620					float *dst_bits = (float*)FreeImage_GetBits(dst) + index;
621
622					// scale each column
623					for(unsigned y = 0; y < dst_height; y++) {
624						// loop through column
625						double value[4] = {0, 0, 0, 0};					// 4 = 128 bpp max
626						const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
627						const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
628
629						const float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index;
630
631						for(unsigned i = iLeft; i <= iRight; i++) {
632							// scan between boundaries
633							// accumulate weighted effect of each neighboring pixel
634							const double weight = weightsTable.getWeight(y, i-iLeft);
635							for (unsigned j = 0; j < floatspp; j++) {
636								value[j] += (weight * (double)src_bits[j]);
637							}
638
639							src_bits += src_pitch;
640						}
641
642						// clamp and place result in destination pixel
643						for (unsigned j = 0; j < floatspp; j++) {
644							dst_bits[j] = (float)value[j];
645						}
646
647						dst_bits += dst_pitch;
648					}
649				}
650			}
651			break;
652
653		}
654	}
655} 
656