Click here to Skip to main content
15,881,715 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.
//	FreeImage Display Demo v4.0

//	Copyright (C) 2008 monday2000@yandex.ru
//
//	This program is free software; you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation; either version 2 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//	http://www.gnu.org/copyleft/gpl.html
//
//
//	Uses pnmscalefixed scaling algorithm from netpbm project
//	Copyright (C) 1989, 1991 by Jef Poskanzer
//
//
//	This code is taken (and then adopted) from the WinDjView Project
//  "Scaling.cpp" file
//
//	Copyright (C) 2004-2006 Andrew Zhezherun

#include "stdafx.h"

#include "FreeImage.h"
#include "Utilities.h"

#include "Resize2.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#define FULLSCALE  4096
#define SCALESHIFT 12
#define HALFSCALE  2048


void horizontal_scale(const BYTE* inputxelrow, BYTE* newxelrow, unsigned cols, unsigned newcols, unsigned sxscale, int btpp)
{
	// Take the input row inputxelrow[], which is 'cols' columns wide, and
	// scale it by a factor of 'sxcale', which is in SCALEths to create
	// the output row newxelrow[], which is 'newcols' columns wide.
	//
	// 'format' and 'maxval' describe the Netpbm format of the both input and
	// output rows.
	//
	// *stretchP is the number of columns (could be fractional) on the right 
	// that we had to fill by stretching due to rounding problems.
	
	unsigned r = 0, g = 0, b = 0;
	unsigned newcol = 0;
	unsigned fraccoltofill = FULLSCALE;  // Output column is "empty" now
	
	const BYTE* piP = inputxelrow;
	
	BYTE* piN = newxelrow;
	
	for (unsigned col = 0; col < cols; ++col)
	{
		// Process one pixel from input ('inputxelrow')
		unsigned fraccolleft = sxscale;
		
		// Output all columns, if any, that can be filled using information
		// from this input column, in addition what's already in the output column.
		while (fraccolleft >= fraccoltofill)
		{
			// Generate one output pixel in 'newxelrow'.  It will consist
			// of anything accumulated from prior input pixels in 'r','g',
			// and 'b', plus a fraction of the current input pixel.
			
			r += fraccoltofill * piP[FI_RGBA_RED];
			g += fraccoltofill * piP[FI_RGBA_GREEN];
			b += fraccoltofill * piP[FI_RGBA_BLUE];
			piN[FI_RGBA_RED] = (BYTE) (r >> SCALESHIFT);
			piN[FI_RGBA_GREEN] = (BYTE) (g >> SCALESHIFT);
			piN[FI_RGBA_BLUE] = (BYTE) (b >> SCALESHIFT);
			r = g = b = 0;
			
			// Set up to start filling next output column
			piN += btpp;			
			
			++newcol;
			fraccolleft -= fraccoltofill;
			fraccoltofill = FULLSCALE;
		}
		
		// There's not enough left in the current input pixel to fill up
		// a whole output column, so just accumulate the remainder of the
		// pixel into the current output column.
		if (fraccolleft > 0)
		{
			r += fraccolleft * piP[FI_RGBA_RED];
			g += fraccolleft * piP[FI_RGBA_GREEN];
			b += fraccolleft * piP[FI_RGBA_BLUE];
			
			fraccoltofill -= fraccolleft;
		}
		
		piP += btpp;
	}
	
	while (newcol < newcols)
	{
		// We ran out of input columns before we filled up the output
		// columns.  This would be because of rounding down.  For small
		// images, we're probably missing only a tiny fraction of a column,
		// but for large images, it could be multiple columns.
		
		// So we fake the remaining output columns by copying the rightmost
		// legitimate pixel.  We call this stretching.
		
		r += fraccoltofill * (piP - 1)[FI_RGBA_RED] + HALFSCALE; // for rounding
		g += fraccoltofill * (piP - 1)[FI_RGBA_GREEN] + HALFSCALE; // for rounding
		b += fraccoltofill * (piP - 1)[FI_RGBA_BLUE] + HALFSCALE; // for rounding
		
		piN[FI_RGBA_RED] = (BYTE) (r >>= SCALESHIFT);
		piN[FI_RGBA_GREEN] = (BYTE) (g >>= SCALESHIFT);
		piN[FI_RGBA_BLUE] = (BYTE) (b >>= SCALESHIFT);
		
		piN += btpp;
		
		++newcol;
		fraccoltofill = FULLSCALE;
	}
}

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

BOOL RescalePnm(FIBITMAP* src_dib, unsigned nWidth, unsigned nHeight, CPoint OffscreenSize, BYTE* pBits)
{
	int bpp = FreeImage_GetBPP(src_dib);
	
	int btpp = bpp/8; // bytes per pixel
	
	unsigned cols = FreeImage_GetWidth(src_dib);
	
	unsigned rows = FreeImage_GetHeight(src_dib);
	
	unsigned newcols = nWidth;
	
	unsigned newrows = nHeight;
	
	
	// The number of rows we had to fill by stretching because of
	// rounding error, which made us run out of input rows before we
	// had filled up the output rows.
	
	// We round the scale factor down so that we never fill up the
	// output while (a fractional pixel of) input remains unused.
	// Instead, we will run out of input while some of the output is
	// unfilled.  We can address that by stretching, whereas the other
	// case would require throwing away some of the input.
	unsigned sxscale = FULLSCALE * newcols / cols;
	unsigned syscale = FULLSCALE * newrows / rows;
	
	BYTE* tempxelrow = (BYTE*) malloc(CalculateLine(cols, bpp));
	
	unsigned int* rs = (unsigned*) malloc(cols * sizeof(unsigned) * 3);	
	
	unsigned int* gs = rs + cols;
	unsigned int* bs = gs + cols;

	
	unsigned wpld = CalculatePitch(CalculateLine(nWidth, bpp == 1 ? 8 : bpp));
	
	int color_mode = FreeImage_GetColorType(src_dib);
	
	unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
		bpp == 1 ? 8 : bpp));
	
	unsigned dif2_w = (wpld_big - wpld)/2;
	
	// center the bitmap horizontally (on the background bitmap)
	pBits += dif2_w - dif2_w % 3; // why is it needed here to align
	// the image at the 3-byte boundary ???
	
	// center the bitmap vertically (on the background bitmap)
	pBits += (OffscreenSize.y - nHeight)/2 * wpld_big;
	
	if (tempxelrow == NULL || rs == NULL)
	{
		if (tempxelrow != NULL)
			free(tempxelrow);
		if (rs != NULL)
			free(rs);
		
		return false;
	}
	
	unsigned rowsread = 0;
	unsigned fracrowleft = syscale;
	unsigned needtoreadrow = 1;
	unsigned fracrowtofill = FULLSCALE;
	
	for (unsigned col = 0; col < cols; ++col)
		rs[col] = gs[col] = bs[col] = HALFSCALE;
	
	int vertical_stretch = 0;
	
	BYTE* xelrow = 0;
	BYTE* xP;
	BYTE* nxP;
	
	for (unsigned row = 0; row < newrows; ++row)
	{
		// First scale vertically from xelrow into tempxelrow.
		
		while (fracrowleft < fracrowtofill)
		{
			if (needtoreadrow)
			{
				if (rowsread < rows)
				{
					xelrow = FreeImage_GetScanLine(src_dib, rowsread);
					
					++rowsread;
				}
			}
			
			unsigned* prs = rs;
			unsigned* pgs = gs;
			unsigned* pbs = bs;
			unsigned col;
			
			for (col = 0, xP = xelrow; col < cols; ++col)
			{
				*prs++ += fracrowleft * xP[FI_RGBA_RED];
				*pgs++ += fracrowleft * xP[FI_RGBA_GREEN];
				*pbs++ += fracrowleft * xP[FI_RGBA_BLUE];
				
				xP += btpp;
			}
			
			fracrowtofill -= fracrowleft;
			fracrowleft = syscale;
			needtoreadrow = 1;
		}
		
		// Now fracrowleft is >= fracrowtofill, so we can produce a row.
		if (needtoreadrow)
		{
			if (rowsread < rows)
			{
				xelrow = FreeImage_GetScanLine(src_dib, rowsread);
				++rowsread;
				needtoreadrow = 0;
			}
			else
			{
				// We need another input row to fill up this output row,
				// but there aren't any more.  That's because of rounding
				// down on our scaling arithmetic.  So we go ahead with
				// the data from the last row we read, which amounts to
				// stretching out the last output row.
				vertical_stretch += fracrowtofill;
			}
		}
		
		unsigned* prs = rs;
		unsigned* pgs = gs;
		unsigned* pbs = bs;
		unsigned col;
		
		for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col)
		{
			unsigned r = *prs + fracrowtofill * xP[FI_RGBA_RED];
			unsigned g = *pgs + fracrowtofill * xP[FI_RGBA_GREEN];
			unsigned b = *pbs + fracrowtofill * xP[FI_RGBA_BLUE];
			
			nxP[FI_RGBA_RED] = (BYTE) (r >> SCALESHIFT);
			nxP[FI_RGBA_GREEN] = (BYTE) (g >> SCALESHIFT);
			nxP[FI_RGBA_BLUE] = (BYTE) (b >> SCALESHIFT);
			
			*prs++ = HALFSCALE;
			*pgs++ = HALFSCALE;
			*pbs++ = HALFSCALE;
			
			xP += btpp;
			
			nxP += btpp;
		}
		
		fracrowleft -= fracrowtofill;
		if (fracrowleft == 0)
		{
			fracrowleft = syscale;
			needtoreadrow = 1;
		}
		fracrowtofill = FULLSCALE;	
	
	horizontal_scale(tempxelrow, FI_GetScanLine(pBits, wpld_big, row), cols, newcols, sxscale, btpp);

	}
	
	free(tempxelrow);
	free(rs);
	
	return true;
}

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

BOOL RescalePnm8(FIBITMAP* src_dib, unsigned nWidth, unsigned nHeight, 
							  
							  CPoint OffscreenSize, BYTE* pBits, RGBQUAD *dst_pal)
{
	int bpp = FreeImage_GetBPP(src_dib);
	
	int btpp = bpp/8; // bytes per pixel
	
	unsigned cols = FreeImage_GetWidth(src_dib);
	
	unsigned rows = FreeImage_GetHeight(src_dib);
	
	unsigned newcols = nWidth;
	
	unsigned newrows = nHeight;

	// The number of rows we had to fill by stretching because of
	// rounding error, which made us run out of input rows before we
	// had filled up the output rows.
	
	// We round the scale factor down so that we never fill up the
	// output while (a fractional pixel of) input remains unused.
	// Instead, we will run out of input while some of the output is
	// unfilled.  We can address that by stretching, whereas the other
	// case would require throwing away some of the input.
	
	unsigned sxscale = FULLSCALE * newcols / cols;
	
	unsigned syscale = FULLSCALE * newrows / rows;
	
	BYTE* tempxelrow = (BYTE*) malloc(cols * sizeof(BYTE));
	
	unsigned* rs = (unsigned*) malloc(cols * sizeof(unsigned));
	
	
	unsigned wpld = CalculatePitch(CalculateLine(nWidth, bpp == 1 ? 8 : bpp));
	
	int color_mode = FreeImage_GetColorType(src_dib);
	
	unsigned wpld_big = CalculatePitch(CalculateLine(OffscreenSize.x,
		bpp == 1 ? 8 : bpp));
	
	unsigned dif2_w = (wpld_big - wpld)/2;
	
	// center the bitmap horizontally (on the background bitmap)
	pBits += dif2_w - dif2_w % 3; // why is it needed here to align
	// the image at the 3-byte boundary ???
	
	// center the bitmap vertically (on the background bitmap)
	pBits += (OffscreenSize.y - nHeight)/2 * wpld_big;
	
	
	if (tempxelrow == NULL || rs == NULL)
	{
		if (tempxelrow != NULL)
			free(tempxelrow);
		if (rs != NULL)
			free(rs);

		return false;
	}
	
	unsigned rowsread = 0;
	unsigned fracrowleft = syscale;
	unsigned needtoreadrow = 1;
	unsigned fracrowtofill = FULLSCALE;
	
	for (unsigned col = 0; col < cols; ++col)
		rs[col] = HALFSCALE;
	
	int vertical_stretch = 0;
	BYTE* xelrow = 0;
	BYTE* xP;
	BYTE* nxP;
	
	BYTE val;
	
	for (unsigned row = 0; row < newrows; ++row)
	{
		// First scale vertically from xelrow into tempxelrow.

		while (fracrowleft < fracrowtofill) // e.g. if minification
		{
			if (needtoreadrow)
			{
				if (rowsread < rows)
				{
					xelrow = FreeImage_GetScanLine(src_dib, rowsread);						
					
					++rowsread;
				}
			}
			
			unsigned* prs = rs;
			unsigned col;
			for (col = 0, xP = xelrow; col < cols; ++col, ++xP)
			{
				if (bpp == 1)
				{					
					Get1BitPixel(xelrow, col, color_mode, &val);
					
					*prs += fracrowleft * dst_pal[val].rgbRed;					
				}
				else
					
					*prs += fracrowleft * dst_pal[*xP].rgbRed;
				
				++prs;
			}
			
			fracrowtofill -= fracrowleft;
			fracrowleft = syscale;
			needtoreadrow = 1;
		}

		// Now fracrowleft is >= fracrowtofill, so we can produce a row.

		if (needtoreadrow)
		{
			if (rowsread < rows)
			{
				xelrow = FreeImage_GetScanLine(src_dib, rowsread);					
				
				++rowsread;
				needtoreadrow = 0;
			}
			else
			{
				// We need another input row to fill up this output row,
				// but there aren't any more.  That's because of rounding
				// down on our scaling arithmetic.  So we go ahead with
				// the data from the last row we read, which amounts to
				// stretching out the last output row.
				vertical_stretch += fracrowtofill;
			}
		}
		
		unsigned* prs = rs;
		unsigned col;
		for (col = 0, xP = xelrow, nxP = tempxelrow; col < cols; ++col, ++xP, ++nxP, ++prs)
		{
			unsigned r;
			
			if (bpp == 1)
			{
				
				Get1BitPixel(xelrow, col, color_mode, &val);
				
				r = *prs + fracrowtofill * dst_pal[val].rgbRed;
				
			}
			else
				
				r = *prs + fracrowtofill * dst_pal[*xP].rgbRed;
			
			*nxP = (BYTE) (r >> SCALESHIFT);
			*prs = HALFSCALE;
		}
		
		fracrowleft -= fracrowtofill;
		if (fracrowleft == 0)
		{
			fracrowleft = syscale;
			needtoreadrow = 1;
		}
		fracrowtofill = FULLSCALE;
	
	
	horizontal_scale8(tempxelrow, FI_GetScanLine(pBits, wpld_big, row), cols, newcols, sxscale);

	}
	
	free(tempxelrow);
	free(rs);
	
	return true;
}

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

void horizontal_scale8(const BYTE* inputxelrow, BYTE* newxelrow, unsigned cols, unsigned newcols, unsigned sxscale)
{
	// Take the input row inputxelrow[], which is 'cols' columns wide, and
	// scale it by a factor of 'sxcale', which is in SCALEths to create
	// the output row newxelrow[], which is 'newcols' columns wide.
	//
	// 'format' and 'maxval' describe the Netpbm format of the both input and
	// output rows.
	//
	// *stretchP is the number of columns (could be fractional) on the right 
	// that we had to fill by stretching due to rounding problems.

	unsigned newcol = 0;
	unsigned fraccoltofill = FULLSCALE;
	unsigned r = 0;
	const BYTE* piP = inputxelrow;
	BYTE* piN = newxelrow;
	
	for (unsigned col = 0; col < cols; ++col, ++piP)
	{
		// Process one pixel from input ('inputxelrow')
		unsigned fraccolleft = sxscale;

		// Output all columns, if any, that can be filled using information
		// from this input column, in addition what's already in the output column.
		while (fraccolleft >= fraccoltofill)
		{
			// Generate one output pixel in 'newxelrow'.  It will consist
			// of anything accumulated from prior input pixels
			// plus a fraction of the current input pixel.
			r += fraccoltofill * (*piP);
			*piN = (BYTE) (r >> SCALESHIFT);
			
			r = 0;
			
			// Set up to start filling next output column
			++piN;
			++newcol;
			fraccolleft -= fraccoltofill;
			fraccoltofill = FULLSCALE;
		}

		// There's not enough left in the current input pixel to fill up
		// a whole output column, so just accumulate the remainder of the
		// pixel into the current output column.
		if (fraccolleft > 0)
		{
			r += fraccolleft * (*piP);
			fraccoltofill -= fraccolleft;
		}
	}
	
	while (newcol < newcols)
	{
		// We ran out of input columns before we filled up the output
		// columns.  This would be because of rounding down.  For small
		// images, we're probably missing only a tiny fraction of a column,
		// but for large images, it could be multiple columns.
		
		// So we fake the remaining output columns by copying the rightmost
		// legitimate pixel.  We call this stretching.
		r += fraccoltofill * (*(piP - 1)) + HALFSCALE;
		*piN = (BYTE) (r >> SCALESHIFT);
		
		++piN;
		++newcol;
		fraccoltofill = FULLSCALE;
	}
}

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

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

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

inline void 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;
}

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