Click here to Skip to main content
15,886,788 members
Articles / Desktop Programming / MFC

FreeImage Display Demo

Rate me:
Please Sign up or sign in to vote.
3.98/5 (20 votes)
4 Jun 2008Public Domain17 min read 97.1K   10.4K   54  
How to display a bitmap in your MFC SDI application using FreeImage. Various rescaling algorithms considered.
// ==========================================================
// Upsampling / downsampling classes
//
// Design and implementation by
// - Herv� Drolon (drolon@infonie.fr)
// - Detlev Vendt (detlev.vendt@brillit.de)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================

//	FreeImage Display Demo v3.2

//	Copyright (C) 2008 monday2000@yandex.ru

#include "stdafx.h" // added to the original file to allow the MFC compilation

#include "Resize2.h"

///////////////////////////////////////////////////////////////////
/**
Filter weights table.
This class stores contribution information for an entire line (row or column).
*/


CWeightsTable::CWeightsTable(CGenericFilter *pFilter, DWORD uDstSize, DWORD uSrcSize) {
	DWORD u;
	double dWidth;
	double dFScale = 1.0;
	double dFilterWidth = pFilter->GetWidth();
	
	// scale factor
	double dScale = double(uDstSize) / double(uSrcSize);
	
	if(dScale < 1.0) {
		// minification
		dWidth = dFilterWidth / dScale; 
		dFScale = dScale; 
	} else {
		// magnification
		dWidth= dFilterWidth; 
	}
	
	// allocate a new line contributions structure
	//
	// window size is the number of sampled pixels
	m_WindowSize = 2 * (int)ceil(dWidth) + 1; 
	m_LineLength = uDstSize; 
	// allocate list of contributions 
	m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
	for(u = 0 ; u < m_LineLength ; u++) {
		// allocate contributions for every pixel
		m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
	}
	
	// offset for discrete to continuous coordinate conversion
	double dOffset = (0.5 / dScale) - 0.5;
	
	
	for(u = 0; u < m_LineLength; u++) {
		// scan through line of contributions
		double dCenter = (double)u / dScale + dOffset;   // reverse mapping
		// find the significant edge points that affect the pixel
		int iLeft = MAX (0, (int)floor (dCenter - dWidth)); 
		int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1); 
		
		// cut edge points to fit in filter window in case of spill-off
		if((iRight - iLeft + 1) > int(m_WindowSize)) {
			if(iLeft < (int(uSrcSize) - 1 / 2)) {
				iLeft++; 
			} else {
				iRight--; 
			}
		}
		
		m_WeightTable[u].Left = iLeft; 
		m_WeightTable[u].Right = iRight;
		
		int iSrc = 0;
		double dTotalWeight = 0;  // zero sum of weights
		for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
			// calculate weights
			double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc));
			m_WeightTable[u].Weights[iSrc-iLeft] = weight;
			dTotalWeight += weight;
		}
		if((dTotalWeight > 0) && (dTotalWeight != 1)) {
			// normalize weight of neighbouring points
			for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
				// normalize point
				m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; 
			}
			// simplify the filter, discarding null weights at the right
			iSrc = iRight - iLeft;
			while(m_WeightTable[u].Weights[iSrc] == 0){
				m_WeightTable[u].Right--;
				iSrc--;
				if(m_WeightTable[u].Right == m_WeightTable[u].Left)
					break;
			}
			
		}
	} 
}

CWeightsTable::~CWeightsTable() {
	for(DWORD u = 0; u < m_LineLength; u++) {
		// free contributions for every pixel
		free(m_WeightTable[u].Weights);
	}
	// free list of pixels contributions
	free(m_WeightTable);
}

// ---------------------------------------------

/**
CResizeEngine<br>
This class performs filtered zoom. It scales an image to the desired dimensions with 
any of the CGenericFilter derived filter class.<br>
It works with 8-, 24- and 32-bit buffers.<br><br>

  <b>References</b> : <br>
  [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering. 
  UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
  [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999. 
  [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
  
*/

BOOL CResizeEngine::scale2(FIBITMAP *src, unsigned dst_width, unsigned dst_height, int bpp_dst, CPoint OffscreenSize, BYTE* pBits)
{ 
	DWORD src_width  = FreeImage_GetWidth(src); 
	DWORD src_height = FreeImage_GetHeight(src);
	
	unsigned redMask	= FreeImage_GetRedMask(src);
	unsigned greenMask	= FreeImage_GetGreenMask(src);
	unsigned blueMask	= FreeImage_GetBlueMask(src);
	
	unsigned bpp = FreeImage_GetBPP(src);
	if(bpp == 1) {
		// convert output to 8-bit
		bpp = 8;
	}
	
	FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
	
	// decide which filtering order (xy or yx) is faster for this mapping by
	// counting convolution multiplies
	
	if(dst_width*src_height <= dst_height*src_width) {
		// xy filtering
		// -------------
		
		// allocate a temporary image
		FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask);
		if(!tmp) return false;
		
		// scale source image horizontally into temporary image
		horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height);
		
		// scale temporary image vertically into result image    
		verticalFilter2(tmp, dst_width, src_height, dst_width, dst_height, bpp_dst, OffscreenSize, pBits);
		
		// free temporary image
		FreeImage_Unload(tmp);
		
	} else {
		// yx filtering
		// -------------
		
		// allocate a temporary image
		FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask);
		if(!tmp) return false;
		
		// scale source image vertically into temporary image
		verticalFilter(src, src_width, src_height, tmp, src_width, dst_height);
		
		// scale temporary image horizontally into result image    
		horizontalFilter2(tmp, src_width, dst_height, dst_width, dst_height, bpp_dst, OffscreenSize, pBits);
		
		// free temporary image
		FreeImage_Unload(tmp);
	}
	
	return true;
} 

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

/// Performs horizontal image filtering
void CResizeEngine::horizontalFilter2(FIBITMAP *src, unsigned src_width, unsigned src_height, unsigned dst_width, unsigned dst_height, int bpp_dst, CPoint OffscreenSize, BYTE* pBits)
{ 
	unsigned wpld = CalculatePitch(CalculateLine(dst_width, bpp_dst == 1 ? 8 : bpp_dst));
	
	int color_mode = FreeImage_GetColorType(src);
	
	unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
		bpp_dst == 1 ? 8 : bpp_dst));
	
	unsigned dif2_w = (wpld_big - wpld)/2;
	
	// center the bitmap horizontally (on the background bitmap)
	pBits += dif2_w + 6 - dif2_w % 6; // why is it needed here to align
	// the image at the 6-byte boundary ???
	
	// center the bitmap vertically (on the background bitmap)
	pBits += (OffscreenSize.y - dst_height)/2 * wpld_big;


	unsigned index;	// pixel index
	
	// allocate and calculate the contributions
	CWeightsTable weightsTable(m_pFilter, dst_width, src_width); 
	
	// step through rows
	switch(FreeImage_GetImageType(src)) {
	case FIT_BITMAP:
		{
			switch(FreeImage_GetBPP(src)) {
			case 1:
				{
					// scale and convert to 8-bit
					
					if(bpp_dst != 8) break;
					
					for(unsigned y = 0; y < dst_height; y++) {
						// scale each row 
						BYTE *src_bits = FreeImage_GetScanLine(src, y);
						
						BYTE *dst_bits = FI_GetScanLine(pBits, wpld_big, y);
						
						for(unsigned x = 0; x < dst_width; x++) {
							// loop through row
							double value = 0;
							int iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(x, i-iLeft);
								
								BYTE pixel;

								Get1BitPixel(src_bits, i, color_mode, &pixel);								
								
								value += (weight * (double)pixel);
							} 
							value *= 255;
							
							// clamp and place result in destination pixel
							dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(value + 0.5)), (int)255);
						} 
					}
				}
				break;
				
			case 8:
			case 24:
			case 32:
				{
					// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
					unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
					
					for(unsigned y = 0; y < dst_height; y++) {
						// scale each row 
						BYTE *src_bits = FreeImage_GetScanLine(src, y);
						
						BYTE *dst_bits = FI_GetScanLine(pBits, wpld_big, y);
						
						for(unsigned x = 0; x < dst_width; x++) {
							// loop through row
							double value[4] = {0, 0, 0, 0};					// 4 = 32bpp max
							int iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(x, i-iLeft);
								
								index = i * bytespp;
								for (unsigned j = 0; j < bytespp; j++) {
									value[j] += (weight * (double)src_bits[index++]); 
								}
							} 
							
							// clamp and place result in destination pixel
							for (unsigned j = 0; j < bytespp; j++) {
								dst_bits[j] = (BYTE)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)255);
							}
							
							dst_bits += bytespp;
						} 
					}
				}
				break;
			}
		}
		break;
		
	case FIT_UINT16:
	case FIT_RGB16:
	case FIT_RGBA16:
		{
			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
			unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
			
			for(unsigned y = 0; y < dst_height; y++) {
				// scale each row 
				WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
				
				WORD *dst_bits = (WORD*)FI_GetScanLine(pBits, wpld_big, y);
				
				for(unsigned x = 0; x < dst_width; x++) {
					// loop through row
					double value[4] = {0, 0, 0, 0};					// 4 = 64bpp max
					int iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
					int iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
					
					for(int i = iLeft; i <= iRight; i++) {
						// scan between boundaries
						// accumulate weighted effect of each neighboring pixel
						double weight = weightsTable.getWeight(x, i-iLeft);
						
						index = i * wordspp;
						for (unsigned j = 0; j < wordspp; j++) {
							value[j] += (weight * (double)src_bits[index++]); 
						}
					} 
					
					// clamp and place result in destination pixel
					for (unsigned j = 0; j < wordspp; j++) {
						dst_bits[j] = (WORD)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)0xFFFF);
					}
					
					dst_bits += wordspp;
				} 
			}
		}
		break;
		
	case FIT_FLOAT:
	case FIT_RGBF:
	case FIT_RGBAF:
		{
			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
			unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
			
			for(unsigned y = 0; y < dst_height; y++) {
				// scale each row 
				float *src_bits = (float*)FreeImage_GetScanLine(src, y);
				
				float *dst_bits = (float*)FI_GetScanLine(pBits, wpld_big, y);
				
				for(unsigned x = 0; x < dst_width; x++) {
					// loop through row
					double value[4] = {0, 0, 0, 0};					// 4 = 64bpp max
					int iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
					int iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
					
					for(int i = iLeft; i <= iRight; i++) {
						// scan between boundaries
						// accumulate weighted effect of each neighboring pixel
						double weight = weightsTable.getWeight(x, i-iLeft);
						
						index = i * floatspp;
						for (unsigned j = 0; j < floatspp; j++) {
							value[j] += (weight * (double)src_bits[index++]); 
						}
					} 
					
					// place result in destination pixel
					for (unsigned j = 0; j < floatspp; j++) {
						dst_bits[j] = (float)value[j];
					}
					
					dst_bits += floatspp;
				} 
			}
		}
		break;
		
		}
} 

//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

/// Performs vertical image filtering
void CResizeEngine::verticalFilter2(FIBITMAP *src, unsigned src_width, unsigned src_height, unsigned dst_width, unsigned dst_height, int bpp_dst, CPoint OffscreenSize, BYTE* pBits)
{
	unsigned wpld = CalculatePitch(CalculateLine(dst_width, bpp_dst == 1 ? 8 : bpp_dst));
	
	int color_mode = FreeImage_GetColorType(src);
	
	unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
		bpp_dst == 1 ? 8 : bpp_dst));
	
	unsigned dif2_w = (wpld_big - wpld)/2;
	
	// center the bitmap horizontally (on the background bitmap)
	pBits += dif2_w + 6 - dif2_w % 6; // why is it needed here to align
	// the image at the 6-byte boundary ???
	
	// center the bitmap vertically (on the background bitmap)
	pBits += (OffscreenSize.y - dst_height)/2 * wpld_big;
	

	unsigned index;	// pixel index
	
	// allocate and calculate the contributions
	CWeightsTable weightsTable(m_pFilter, dst_height, src_height); 
	
	
	// step through columns
	switch(FreeImage_GetImageType(src)) {
	case FIT_BITMAP:
		{
			switch(FreeImage_GetBPP(src)) {
			case 1:
				{
					// scale and convert to 8-bit
					if(bpp_dst != 8) break;
					
					unsigned src_pitch = FreeImage_GetPitch(src);					

					unsigned dst_pitch = wpld_big;
					
					for(unsigned x = 0; x < dst_width; x++) {
						
						// work on column x in dst
						
						BYTE *dst_bits = pBits + x;
						
						// scale each column
						for(unsigned y = 0; y < dst_height; y++) {
							// loop through column
							double value = 0;
							int iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
							
							BYTE *src_bits = FreeImage_GetScanLine(src, iLeft);
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(y, i-iLeft);	
								
								BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
								value += (weight * (double)pixel);
								
								src_bits += src_pitch;
							}
							value *= 255;
							
							// clamp and place result in destination pixel
							*dst_bits = (BYTE)MIN(MAX((int)0, (int)(value + 0.5)), (int)255);
							
							dst_bits += dst_pitch;
						}
					}
				}
				break;
				
			case 8:
			case 24:
			case 32:
				{
					// Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
					unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
					
					unsigned src_pitch = FreeImage_GetPitch(src);
					
					unsigned dst_pitch = wpld_big;
					
					for(unsigned x = 0; x < dst_width; x++) {
						index = x * bytespp;
						
						// work on column x in dst
						
						BYTE *dst_bits = pBits + index;
						
						// scale each column
						for(unsigned y = 0; y < dst_height; y++) {
							// loop through column
							double value[4] = {0, 0, 0, 0};					// 4 = 32bpp max
							int iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
							
							BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index;
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(y, i-iLeft);							
								for (unsigned j = 0; j < bytespp; j++) {
									value[j] += (weight * (double)src_bits[j]);
								}
								
								src_bits += src_pitch;
							}
							
							// clamp and place result in destination pixel
							for (unsigned j = 0; j < bytespp; j++) {
								dst_bits[j] = (BYTE)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)255);
							}
							
							dst_bits += dst_pitch;
						}
					}
				}
				break;
			}
		}
		break;
		
			case FIT_UINT16:
			case FIT_RGB16:
			case FIT_RGBA16:
				{
					// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
					unsigned wordspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(WORD);
					
					unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);					

					unsigned dst_pitch = wpld_big / sizeof(WORD);
					
					for(unsigned x = 0; x < dst_width; x++) {
						index = x * wordspp;
						
						// work on column x in dst
						
						WORD *dst_bits = (WORD*)pBits + index;
						
						// scale each column
						for(unsigned y = 0; y < dst_height; y++) {
							// loop through column
							double value[4] = {0, 0, 0, 0};					// 4 = 64bpp max
							int iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
							
							WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index;
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(y, i-iLeft);							
								for (unsigned j = 0; j < wordspp; j++) {
									value[j] += (weight * (double)src_bits[j]);
								}
								
								src_bits += src_pitch;
							}
							
							// clamp and place result in destination pixel
							for (unsigned j = 0; j < wordspp; j++) {
								dst_bits[j] = (WORD)MIN(MAX((int)0, (int)(value[j] + 0.5)), (int)0xFFFF);
							}
							
							dst_bits += dst_pitch;
						}
					}
				}
				break;
				
			case FIT_FLOAT:
			case FIT_RGBF:
			case FIT_RGBAF:
				{
					// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
					unsigned floatspp = (FreeImage_GetLine(src) / FreeImage_GetWidth(src)) / sizeof(float);
					
					unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);					

					unsigned dst_pitch = wpld_big / sizeof(float);
					
					for(unsigned x = 0; x < dst_width; x++) {
						index = x * floatspp;
						
						// work on column x in dst
						float *dst_bits = (float*)pBits + index;
						
						// scale each column
						for(unsigned y = 0; y < dst_height; y++) {
							// loop through column
							double value[4] = {0, 0, 0, 0};					// 4 = 64bpp max
							int iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
							int iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
							
							float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index;
							
							for(int i = iLeft; i <= iRight; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								double weight = weightsTable.getWeight(y, i-iLeft);							
								for (unsigned j = 0; j < floatspp; j++) {
									value[j] += (weight * (double)src_bits[j]);
								}
								
								src_bits += src_pitch;
							}
							
							// clamp and place result in destination pixel
							for (unsigned j = 0; j < floatspp; j++) {
								dst_bits[j] = (float)value[j];
							}
							
							dst_bits += dst_pitch;
						}
					}
				}
				break;
		}
} 


/////////////////////////////////////////////////////////

void CResizeEngine::Get1BitPixel(BYTE* line, int x, int color_mode, BYTE* value)
{
if (color_mode== FIC_MINISWHITE)

*value = (line[x >> 3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 255;

else

*value = (line[x >> 3] & (0x80 >> (x & 0x07))) != 0 ? 255 : 0;
}

//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////

BYTE* CResizeEngine::FI_GetScanLine(BYTE* pBits, unsigned pitch, int scanline)
{
	return CalculateScanLine(pBits, pitch, scanline);
}

//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
/*
BYTE* CResizeEngine::FI_GetScanLine(BYTE* pBits, unsigned width, int bpp, int scanline)
{
	return CalculateScanLine(pBits, CalculatePitch(CalculateLine(width, bpp)), scanline);
}
*/
/////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions