Click here to Skip to main content
15,895,142 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.2K   10.4K   54  
How to display a bitmap in your MFC SDI application using FreeImage. Various rescaling algorithms considered.
/*====================================================================*
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
 -  This software is distributed in the hope that it will be
 -  useful, but with NO WARRANTY OF ANY KIND.
 -  No author or distributor accepts responsibility to anyone for the
 -  consequences of using this software, or for whether it serves any
 -  particular purpose or works at all, unless he or she says so in
 -  writing.  Everyone is granted permission to copy, modify and
 -  redistribute this source code, for commercial or non-commercial
 -  purposes, with the following restrictions: (1) the origin of this
 -  source code must not be misrepresented; (2) modified versions must
 -  be plainly marked as such; and (3) this notice may not be removed
 -  or altered from any source or modified source distribution.
 *====================================================================*/

// See http://www.leptonica.com/scaling.html

// (2) This Leptonica code is adopted/modified for the FreeImage Library

//	FreeImage Display Demo v3.1

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

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

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

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

#include "fi_testDoc.h"

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

/*------------------------------------------------------------------*
 *       Binary, grayscale and color scaling by closest pixel sampling *
 *------------------------------------------------------------------*/
/*!
 *  scaleBySamplingLow() and scaleBinaryLow() - united together
 *
 *  Notes:
 *      (1) The dest must be cleared prior to this operation,
 *          and we clear it here in the low-level code.
 *      (2) We reuse dest pixels and dest pixel rows whenever
 *          possible.  This speeds the upscaling; downscaling
 *          is done by strict subsampling and is unaffected.
 *      (3) Because we are sampling and not interpolating, this
 *          routine works directly, without conversion to full
 *          RGB color, for 2, 4 or 8 bpp palette color images.
 */

// This is a "nearest neighbor" sampling algorithm (without filtering)

BOOL CFi_testDoc::LeptonicaScaleBySamplingLow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd, CPoint OffscreenSize, 
			   
			   BYTE* pBits, RGBQUAD *dst_pal)
{			   
unsigned    i, j;
unsigned    xs, prevxs;
BYTE sval;
unsigned   *srow, *scol;
BYTE   csval;
BYTE sval4[4] = {0};
BYTE  *lines, *prevlines, *lined, *prevlined;
double  wratio, hratio;

BYTE *datas = FreeImage_GetBits(src_dib);

unsigned ws = FreeImage_GetWidth(src_dib);

unsigned hs = FreeImage_GetHeight(src_dib);

int bpp = FreeImage_GetBPP(src_dib);

int btpp = bpp/8;

unsigned wpls = FreeImage_GetPitch(src_dib);

unsigned wpld = CalculatePitch(CalculateLine(wd, 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 + 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 - hd)/2 * wpld_big;

    
	// the source row corresponding to dest row i ==> srow[i]
	// the source col corresponding to dest col j ==> scol[j]
    if ((srow = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)
	
	return false;

    if ((scol = (unsigned *)calloc(wd, sizeof(unsigned))) == NULL)

	return false;

    wratio = (double)ws / (double)wd;
    hratio = (double)hs / (double)hd;

    for (i = 0; i < hd; i++)
	srow[i] = MIN<unsigned>((unsigned)(hratio * i + 0.5), hs - 1);

    for (j = 0; j < wd; j++)
	scol[j] = MIN<unsigned>((unsigned)(wratio * j + 0.5), ws - 1);

    prevlines = NULL;

    for (i = 0; i < hd; i++)
	{
	lines = datas + srow[i] * wpls;	
	lined = pBits + i * wpld_big;

	if (lines != prevlines)
	{  // make dest from new source row
	    prevxs = -1;
	    sval = 0;
        csval = 0;

	    switch (bpp)
	    {

		case 1:

		for (j = 0; j < wd; j++)
		{
		    xs = scol[j];

		    if (xs != prevxs)
			{  // get dest pix from source col

			Get1BitPixel(lines, xs, color_mode, &sval);

			sval = dst_pal[sval].rgbRed;

			lined[j] = sval;

			prevxs = xs;
		    }
		    else   // copy prev dest pix

			lined[j] = sval;
		}
		break;

	    case 8:

		for (j = 0; j < wd; j++)
		{
		    xs = scol[j];

		    if (xs != prevxs)
			{  // get dest pix from source col

			sval = dst_pal[*(lines + xs)].rgbRed;

			lined[j] = sval;

			prevxs = xs;
		    }
		    else   // copy prev dest pix

			lined[j] = sval;
		}
		break;

	    case 24:

		for (j = 0; j < wd; j++)
		{
		    xs = scol[j];

		    if (xs != prevxs)
			{  // get dest pix from source col

				memcpy((char *)sval4, (char *)(lines + xs*btpp), 3);

				memcpy((char *)(lined + j*btpp), (char *)sval4, 3);

				prevxs = xs;
		    }
		    else   // copy prev dest pix

				memcpy((char *)(lined + j*btpp), (char *)sval4, 3);			
		}
		break;

	    default:

		return false;

		break;

	    }
	}
	else
	{  // lines == prevlines; copy prev dest row	    
		prevlined = lined - wpld_big;

		memcpy((char *)lined, (char *)prevlined, wpld);
	}
	prevlines = lines;
    }

    free(srow);
    free(scol);

    return true;
}

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

/*------------------------------------------------------------------*
 * Color, grayscale and binary downsampling with (antialias) smoothing *
 *------------------------------------------------------------------*/
/*!
 *  scaleSmoothLow()
 *
 *  Notes:
 *  (1) This function is called on 1-, 8- or 24 bpp src and dest images.
 *  (2) size is the full width of the lowpass smoothing filter.
 *      It is correlated with the reduction ratio, being the
 *      nearest integer such that size is approximately equal to hs / hd.
 *
 */

BOOL CFi_testDoc::LeptonicaScaleSmoothLow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd,  CPoint OffscreenSize,
			   
			   unsigned size, BYTE* pBits,
			   
			   RGBQUAD *dst_pal)
{
unsigned    i, j, m, n, xstart;
unsigned    val, rval, gval, bval;
unsigned   *srow, *scol;
BYTE  *lines, *lined, *line, *mpixels, sval;
BYTE*   pixel;
double  wratio, hratio, norm;

BYTE *datas = FreeImage_GetBits(src_dib);

unsigned ws = FreeImage_GetWidth(src_dib);

unsigned hs = FreeImage_GetHeight(src_dib);

int bpp = FreeImage_GetBPP(src_dib);

int btpp = bpp/8;

unsigned wpls = FreeImage_GetPitch(src_dib);

unsigned wpld = CalculatePitch(CalculateLine(wd, 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 + 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 - hd)/2 * wpld_big;


	// Each dest pixel at (j,i) is computed as the average
	// of size^2 corresponding src pixels.
	// We store the UL corner location of the square of
	// src pixels that correspond to dest pixel (j,i).
	// The are labelled by the arrays srow[i] and scol[j].
    if ((srow = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)
	return false;
    if ((scol = (unsigned *)calloc(wd, sizeof(unsigned))) == NULL)
	return false;

    norm = 1. / (double)(size * size);
    wratio = (double)ws / (double)wd;
    hratio = (double)hs / (double)hd;
    for (i = 0; i < hd; i++)
	srow[i] = MIN<unsigned>((unsigned)(hratio * i + 0.5), hs - size);
    for (j = 0; j < wd; j++)
	scol[j] = MIN<unsigned>((unsigned)(wratio * j + 0.5), ws - size);

    // For each dest pixel, compute average
	switch (bpp)
	{

	case 1:
	
	for (i = 0; i < hd; i++)
	{
	    lines = datas + srow[i] * wpls;
		lined = pBits + i * wpld_big;

	    for (j = 0; j < wd; j++)
		{
		xstart = scol[j];
		val = 0;

		for (m = 0; m < size; m++)
		{
		    line = lines + m * wpls;

		    for (n = 0; n < size; n++)
			{			

			Get1BitPixel(line, n + xstart, color_mode, &sval);

			val += dst_pal[sval].rgbRed;
			
		    }
		}
		val = (unsigned)((double)val * norm);		

		lined[j] = val;
	    }
	}

	break;
    
	case 8:
	
	for (i = 0; i < hd; i++)
	{
	    lines = datas + srow[i] * wpls;	    
		lined = pBits + i * wpld_big;

	    for (j = 0; j < wd; j++)
		{
		xstart = scol[j];
		val = 0;

		for (m = 0; m < size; m++)
		{
		    line = lines + m * wpls;

		    for (n = 0; n < size; n++)
			{

			val += dst_pal[*(line + xstart + n)].rgbRed;
		    }
		}
		val = (unsigned)((double)val * norm);		

		lined[j] = val;
	    }
	}
    break;

	case 24:
	  
	for (i = 0; i < hd; i++)
	{
	    lines = datas + srow[i] * wpls;
		lined = pBits + i * wpld_big;		

	    for (j = 0; j < wd; j++)
		{
		xstart = scol[j]*btpp;

		rval = gval = bval = 0;

		for (m = 0; m < size; m++)
		{
		    mpixels = lines + m * wpls + xstart;

		    for (n = 0; n < size; n++)
			{
			pixel = (mpixels + n*btpp);

			rval += pixel[FI_RGBA_RED];
			gval += pixel[FI_RGBA_GREEN];
			bval += pixel[FI_RGBA_BLUE];
		    }
		}
		rval = (unsigned)((double)rval * norm);
		gval = (unsigned)((double)gval * norm);
		bval = (unsigned)((double)bval * norm);

		(lined + j*btpp)[FI_RGBA_RED] = rval;
		(lined + j*btpp)[FI_RGBA_GREEN] = gval;
		(lined + j*btpp)[FI_RGBA_BLUE] = bval;	
	    }
	}
    }

    free(srow);
    free(scol);

    return true;
}

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

/*------------------------------------------------------------------*
 *     General linear interpolated color, binary and gray scaling   *
 *------------------------------------------------------------------*/
/*!
 *  scaleGrayLILow() and scaleColorLILow() - united together
 *
 *  We choose to divide each pixel into 16 x 16 sub-pixels.
 *  Linear interpolation is equivalent to finding the 
 *  fractional area (i.e., number of sub-pixels divided
 *  by 256) associated with each of the four nearest src pixels,
 *  and weighting each pixel value by this fractional area.
 */

BOOL CFi_testDoc::LeptonicaScaleLILow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd, CPoint OffscreenSize,
			   
			   BYTE  *pBits, RGBQUAD *dst_pal, BOOL bInterpolate)
{
long    i, j, wm2, hm2;
long    xpm, ypm;  // location in src image, to 1/16 of a pixel
long    xp, yp, xf, yf;  // src pixel and pixel fraction coordinates
long    v00, v01, v10, v11;
BYTE    val;
BYTE  *lines, *lined;
double  scx, scy;

BYTE *datas = FreeImage_GetBits(src_dib);

long ws = FreeImage_GetWidth(src_dib);

long hs = FreeImage_GetHeight(src_dib);

long bpp = FreeImage_GetBPP(src_dib);

int btpp = bpp/8;

long wpls = FreeImage_GetPitch(src_dib);

long wpld = CalculatePitch(CalculateLine(wd, 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 + 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 - hd)/2 * wpld_big;


	// (scx, scy) are scaling factors that are applied to the
	// dest coords to get the corresponding src coords.
	// We need them because we iterate over dest pixels
	// and must find the corresponding set of src pixels.
    scx = 16. * (double)ws / (double)wd;
    scy = 16. * (double)hs / (double)hd;

    wm2 = ws - 2;
    hm2 = hs - 2;

	switch (bpp)
	{

	case 1:

	// iterate over the destination pixels
    for (i = 0; i < hd; i++)	
	{

	ypm = (long)(scy * (double)i + 0.5);
	yp = ypm >> 4;
	yf = ypm & 0x0f;
	
	lined = pBits + i * wpld_big;
	lines = datas + yp * wpls;

		for (j = 0; j < wd; j++)
		{

	    xpm = (long)(scx * (double)j + 0.5);
	    xp = xpm >> 4;
	    xf = xpm & 0x0f;

		// if near the edge, just use the src pixel value
	    if (xp > wm2 || yp > hm2)
		{
			
		Get1BitPixel(lines, xp, color_mode, &val);

		lined[j] = dst_pal[val].rgbRed;

		continue;
	    }
		 
		if (!bInterpolate)
		{// simply subsample (which is faster but gives lousy results!)				
		
		Get1BitPixel(lines, xp, color_mode, &val);

		lined[j] = dst_pal[val].rgbRed;	
		}

		else		
		{ //do bilinear interpolation

		Get1BitPixel(lines, xp, color_mode, &val);

		v00 = (16 - xf) * (16 - yf) * dst_pal[val].rgbRed;

		Get1BitPixel(lines, xp + 1, color_mode, &val);

		v10 = xf * (16 - yf) * dst_pal[val].rgbRed;

		Get1BitPixel(lines + wpls, + xp, color_mode, &val);

		v01 = (16 - xf) * yf * dst_pal[val].rgbRed;

		Get1BitPixel(lines + wpls, xp + 1, color_mode, &val);

		v11 = xf * yf * dst_pal[val].rgbRed;

	    val = (BYTE)((v00 + v01 + v10 + v11 + 128) / 256);

		lined[j] = val;

		}

		}
    }
	
	break;	

	case 8:

	// iterate over the destination pixels
    for (i = 0; i < hd; i++)	
	{

	ypm = (long)(scy * (double)i + 0.5);
	yp = ypm >> 4;
	yf = ypm & 0x0f;

	lined = pBits + i * wpld_big;
	lines = datas + yp * wpls;

		for (j = 0; j < wd; j++)
		{

	    xpm = (long)(scx * (double)j + 0.5);
	    xp = xpm >> 4;
	    xf = xpm & 0x0f;

		// if near the edge, just use the src pixel value
	    if (xp > wm2 || yp > hm2)
		{
			
		lined[j] = dst_pal[*(lines + xp)].rgbRed;

		continue;
	    }
		 
		if (!bInterpolate)
		{// simply subsample (which is faster but gives lousy results!)				
			lined[j] = dst_pal[*(lines + xp)].rgbRed;		
		}

		else		
		{ //do bilinear interpolation
		
		v00 = (16 - xf) * (16 - yf) * dst_pal[*(lines + xp)].rgbRed;

		v10 = xf * (16 - yf) * dst_pal[*(lines + xp + 1)].rgbRed;

		v01 = (16 - xf) * yf * dst_pal[*(lines + wpls + xp)].rgbRed;

		v11 = xf * yf * dst_pal[*(lines + wpls + xp + 1)].rgbRed;		

	    val = (BYTE)((v00 + v01 + v10 + v11 + 128) / 256);

		lined[j] = val;

		}

		}
    }

	break;

	case 24:

	long    v00r, v01r, v10r, v11r, v00g, v01g, v10g, v11g;
	long    v00b, v01b, v10b, v11b, area00, area01, area10, area11;
	BYTE    *pixels1, *pixels2, *pixels3, *pixels4;

	// iterate over the destination pixels
    for (i = 0; i < hd; i++)
	{
	ypm = (long)(scy * (double)i + 0.5);
	yp = ypm >> 4;
	yf = ypm & 0x0f;

	lined = pBits + i * wpld_big;
	lines = datas + yp * wpls;

	for (j = 0; j < wd; j++)
	{
	    xpm = (long)(scx * (double)j + 0.5);
	    xp = xpm >> 4;
	    xf = xpm & 0x0f;

		// if near the edge, just use the src pixel value
	    if (xp > wm2 || yp > hm2)
		{
			(lined + j*btpp)[FI_RGBA_RED] = (lines + xp*btpp)[FI_RGBA_RED];
			(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xp*btpp)[FI_RGBA_GREEN];
			(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xp*btpp)[FI_RGBA_BLUE];

		continue;
	    }

		if (!bInterpolate) // simply subsample (which is faster but gives lousy results!)
		{
			(lined + j*btpp)[FI_RGBA_RED] = (lines + xp*btpp)[FI_RGBA_RED];
			(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xp*btpp)[FI_RGBA_GREEN];
			(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xp*btpp)[FI_RGBA_BLUE];
		}
		else
		{

		// do bilinear interpolation.  This is a simple
		// generalization of the calculation in scaleGrayLILow()
            pixels1 = lines + xp*btpp;
            pixels2 = lines + xp*btpp + 1;
            pixels3 = lines + wpls + xp*btpp;
            pixels4 = lines + wpls + xp*btpp + 1;

	    area00 = (16 - xf) * (16 - yf);
	    area10 = xf * (16 - yf);
	    area01 = (16 - xf) * yf;
	    area11 = xf * yf;

	    v00r = area00 * pixels1[FI_RGBA_RED];
	    v00g = area00 * pixels1[FI_RGBA_GREEN];
	    v00b = area00 * pixels1[FI_RGBA_BLUE];

	    v10r = area10 * pixels1[FI_RGBA_RED];
	    v10g = area10 * pixels1[FI_RGBA_GREEN];
	    v10b = area10 * pixels1[FI_RGBA_BLUE];

	    v01r = area01 * pixels1[FI_RGBA_RED];
	    v01g = area01 * pixels1[FI_RGBA_GREEN];
	    v01b = area01 * pixels1[FI_RGBA_BLUE];

	    v11r = area11 * pixels1[FI_RGBA_RED];
	    v11g = area11 * pixels1[FI_RGBA_GREEN];
	    v11b = area11 * pixels1[FI_RGBA_BLUE];

		(lined + j*btpp)[FI_RGBA_RED] = (BYTE)((v00r + v10r + v01r + v11r + 128) / 256);
		(lined + j*btpp)[FI_RGBA_GREEN] = (BYTE)((v00g + v10g + v01g + v11g + 128) / 256);
		(lined + j*btpp)[FI_RGBA_BLUE] = (BYTE)((v00b + v10b + v01b + v11b + 128) / 256);
		}
	}
    }

	}

    return true;
}

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

void CFi_testDoc::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