Click here to Skip to main content
15,883,730 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.
/*====================================================================*
 -  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 v4.0

//	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"

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

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

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

/*!
 *  scaleGrayAreaMapLow()
 *
 *  This should only be used for downscaling.
 *  We choose to divide each pixel into 16 x 16 sub-pixels.
 *  This is about 2x slower than scaleSmoothLow(), but the results
 *  are significantly better on small text, esp. for downscaling
 *  factors between 1.5 and 5.  All src pixels are subdivided
 *  into 256 sub-pixels, and are weighted by the number of
 *  sub-pixels covered by the dest pixel.
 */
BOOL LeptonicaScaleGrayAreaMapLow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd, CPoint OffscreenSize, 
			   
			   BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned    i, j, k, m, wm2, hm2;
unsigned    xu, yu;  // UL corner in src image, to 1/16 of a pixel
unsigned    xl, yl;  // LR corner in src image, to 1/16 of a pixel
unsigned    xup, yup, xuf, yuf;  // UL src pixel: integer and fraction
unsigned    xlp, ylp, xlf, ylf;  // LR src pixel: integer and fraction
unsigned    delx, dely, area;
unsigned    v00;  // contrib. from UL src pixel
unsigned    v01;  // contrib. from LL src pixel
unsigned    v10;  // contrib from UR src pixel
unsigned    v11;  // contrib from LR src pixel
unsigned    vin;  // contrib from all full interior src pixels
unsigned    vmid;  // contrib from side parts that are full in 1 direction
unsigned    val;
BYTE  *lines, *lined;
double  scx, scy;

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;

// center the bitmap vertically (on the background bitmap)
pBits += (OffscreenSize.y - hd)/2 * wpld_big;

BYTE sval;

	 // (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;

unsigned *srow_u, *scol_u;

    if ((srow_u = (unsigned *)calloc(hd, sizeof(unsigned))) == NULL)	
	return false;

    if ((scol_u = (unsigned *)calloc(wd, sizeof(unsigned))) == NULL)
	return false;
 
    for (i = 0; i < hd; i++)	
		srow_u[i] = MIN<unsigned>((unsigned)(scy * i + 0.5), (unsigned)(16. * (hs - 1)));
	
    for (j = 0; j < wd; j++)	
		scol_u[j] = MIN<unsigned>((unsigned)(scy * j + 0.5), (unsigned)(16. * (ws - 1)));

unsigned xuf16, yuf16;

unsigned temp_val;

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

	yu = srow_u[i];
	yl = yu + (unsigned)scy;

	yup = yu >> 4;
	yuf = yu & 0x0f;

	ylp = yl >> 4;
	ylf = yl & 0x0f;

    dely = ylp - yup;

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

	yuf16 = 16 - yuf;

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

	    xu = scol_u[j];
		xl = xu + (unsigned)scx;

	    xup = xu >> 4;
	    xuf = xu & 0x0f;

	    xlp = xl >> 4;
	    xlf = xl & 0x0f;

		delx = xlp - xup;

		xuf16 = 16 - xuf;

		switch (bpp)		
		{

		case 1:		

		// If near the edge, just use a src pixel value
	    if (xlp > wm2 || ylp > hm2)
		{

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

		lined[j] = sval;

		continue;
	    }
	        // Area summed over, in subpixels.  This varies
            // due to the quantization, so we can't simply take
            // the area to be a constant: area = scx * scy.
            area = (xuf16 + ((delx - 1) << 4) + xlf) *
                   (yuf16 + ((dely - 1) << 4) + ylf);

		// Do area map summation
		Get1BitPixel(lines, xup, color_mode, &sval);
		temp_val = xuf16 * yuf16;
		v00 = sval ? (temp_val << 8) - temp_val : 0;
		
		Get1BitPixel(lines, xlp, color_mode, &sval);
		temp_val = xlf * yuf16;
		v10 = sval ? (temp_val << 8) - temp_val : 0;

		Get1BitPixel(lines + dely * wpls, xup, color_mode, &sval);
		temp_val = xuf16 * ylf;
		v01 = sval ? (temp_val << 8) - temp_val : 0;
		
		Get1BitPixel(lines + dely * wpls, xlp, color_mode, &sval);
		temp_val = xlf * ylf;
		v11 = sval ? (temp_val << 8) - temp_val : 0;

            for (vin = 0, k = 1; k < dely; k++)
			{  // for full src pixels
                 for (m = 1; m < delx; m++)
				 {					
					 Get1BitPixel(lines + k * wpls, xup + m, color_mode, &sval);
					 vin += sval ? 65280 : 0; // 255 * 256
                 }
            }

            for (vmid = 0, k = 1; k < dely; k++)  // for left side
				{
				Get1BitPixel(lines + k * wpls, xup, color_mode, &sval);
				vmid += sval ? (xuf16 << 4) * sval : 0;
				}

            for (k = 1; k < dely; k++)  // for right side
				{
				Get1BitPixel(lines + k * wpls, xlp, color_mode, &sval);
				vmid += sval ? (xlf << 4) * sval : 0;
				}

            for (m = 1; m < delx; m++)  // for top side
				{
				Get1BitPixel(lines, xup + m, color_mode, &sval);
				vmid += sval ? (yuf16 << 4) * sval : 0;
				}

            for (m = 1; m < delx; m++)  // for bottom side
				{
				Get1BitPixel(lines + dely * wpls, xup + m, color_mode, &sval);
				vmid += sval ? (ylf << 4) * sval : 0;
				}

	    val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area;

		lined[j] = val;


		break;

//*************************************

		case 8:

		// If near the edge, just use a src pixel value
	    if (xlp > wm2 || ylp > hm2)
		{

		lined[j] = dst_pal[*(lines + xup)].rgbRed;

		continue;
	    }
	        // Area summed over, in subpixels.  This varies
            // due to the quantization, so we can't simply take
            // the area to be a constant: area = scx * scy.
            area = (xuf16 + 16 * (delx - 1) + xlf) *
                   (yuf16 + 16 * (dely - 1) + ylf);

		// Do area map summation
		v00 = xuf16 * yuf16 * dst_pal[*(lines + xup)].rgbRed;
		v10 = xlf * yuf16 * dst_pal[*(lines + xlp)].rgbRed;
		v01 = xuf16 * ylf * dst_pal[*(lines + dely * wpls + xup)].rgbRed;
		v11 = xlf * ylf * dst_pal[*(lines + dely * wpls + xlp)].rgbRed;

            for (vin = 0, k = 1; k < dely; k++)
			{  // for full src pixels
                 for (m = 1; m < delx; m++)
				 {
					 vin += 256 * dst_pal[*(lines + k * wpls + xup + m)].rgbRed;
                 }
            }

            for (vmid = 0, k = 1; k < dely; k++)  // for left side
				vmid += xuf16 * 16 * dst_pal[*(lines + k * wpls + xup)].rgbRed;

            for (k = 1; k < dely; k++)  // for right side
				vmid += xlf * 16 * dst_pal[*(lines + k * wpls + xlp)].rgbRed;

            for (m = 1; m < delx; m++)  // for top side
				vmid += 16 * yuf16 * dst_pal[*(lines + xup + m)].rgbRed;

            for (m = 1; m < delx; m++)  // for bottom side
				vmid += 16 * ylf * dst_pal[*(lines + dely * wpls + xup + m)].rgbRed;

	    val = (v00 + v01 + v10 + v11 + vin + vmid + 128) / area;

		lined[j] = val;

		break;

		} // end switch
	}
    }

	free(srow_u);
	free(scol_u);

    return true;
}

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

/*------------------------------------------------------------------*
 *                  General area mapped gray scaling                *
 *------------------------------------------------------------------*/
/*!
 *  scaleColorAreaMapLow()
 *
 *  This should only be used for downscaling.
 *  We choose to divide each pixel into 16 x 16 sub-pixels.
 *  This is much slower than scaleSmoothLow(), but it gives a
 *  better representation, esp. for downscaling factors between
 *  1.5 and 5.  All src pixels are subdivided into 256 sub-pixels,
 *  and are weighted by the number of sub-pixels covered by
 *  the dest pixel.  This is about 2x slower than scaleSmoothLow(),
 *  but the results are significantly better on small text.
 *
 *  *** Warning: implicit assumption about RGB component ordering ***
 */
BOOL LeptonicaScaleColorAreaMapLow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd, CPoint OffscreenSize, 
			   
			   BYTE* pBits, RGBQUAD *dst_pal)
{
unsigned    i, j, k, m, wm2, hm2;
unsigned    area00, area10, area01, area11, areal, arear, areat, areab;
unsigned    xu, yu;  // UL corner in src image, to 1/16 of a pixel
unsigned    xl, yl;  // LR corner in src image, to 1/16 of a pixel
unsigned    xup, yup, xuf, yuf;  // UL src pixel: integer and fraction
unsigned    xlp, ylp, xlf, ylf;  // LR src pixel: integer and fraction
unsigned    delx, dely, area;
unsigned    v00r, v00g, v00b;  // contrib. from UL src pixel
unsigned    v01r, v01g, v01b;  // contrib. from LL src pixel
unsigned    v10r, v10g, v10b;  // contrib from UR src pixel
unsigned    v11r, v11g, v11b;  // contrib from LR src pixel
unsigned    vinr, ving, vinb;  // contrib from all full interior src pixels
unsigned    vmidr, vmidg, vmidb;  // contrib from side parts
unsigned    valr, valg, valb;
BYTE   pixel00[4], pixel10[4], pixel01[4], pixel11[4], pixel[4];
BYTE  *lines, *lined;
double  scx, scy;

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 - 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 - 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;

	/* Iterate over the destination pixels */
    for (i = 0; i < hd; i++)
	{
	yu = (unsigned)(scy * i + 0.5);
	yl = (unsigned)(scy * (i + 1.0) + 0.5);

	yup = yu >> 4;
	yuf = yu & 0x0f;

	ylp = yl >> 4;
	ylf = yl & 0x0f;

	dely = ylp - yup;

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

	for (j = 0; j < wd; j++)
	{
	    xu = (unsigned)(scx * j + 0.5);
	    xl = (unsigned)(scx * (j + 1.0) + 0.5);

	    xup = xu >> 4;
	    xuf = xu & 0x0f;

	    xlp = xl >> 4;
	    xlf = xl & 0x0f;

        delx = xlp - xup;

		// If near the edge, just use a src pixel value
	    if (xlp > wm2 || ylp > hm2)
		{
		//*(lined + j*btpp) = *(lines + xup*btpp);
		(lined + j*btpp)[FI_RGBA_RED] = (lines + xup*btpp)[FI_RGBA_RED];
		(lined + j*btpp)[FI_RGBA_GREEN] = (lines + xup*btpp)[FI_RGBA_GREEN];
		(lined + j*btpp)[FI_RGBA_BLUE] = (lines + xup*btpp)[FI_RGBA_BLUE];
		continue;
	    }
	        // Area summed over, in subpixels.  This varies
            // due to the quantization, so we can't simply take
            // the area to be a constant: area = scx * scy.
            area = ((16 - xuf) + 16 * (delx - 1) + xlf) *
                   ((16 - yuf) + 16 * (dely - 1) + ylf);

		// Do area map summation
	    //pixel00 = *(lines + xup*btpp);
		pixel00[FI_RGBA_RED] = (lines + xup*btpp)[FI_RGBA_RED];
		pixel00[FI_RGBA_GREEN] = (lines + xup*btpp)[FI_RGBA_GREEN];
		pixel00[FI_RGBA_BLUE] = (lines + xup*btpp)[FI_RGBA_BLUE];

	    //pixel10 = *(lines + xlp*btpp);
		pixel10[FI_RGBA_RED] = (lines + xlp*btpp)[FI_RGBA_RED];
		pixel10[FI_RGBA_GREEN] = (lines + xlp*btpp)[FI_RGBA_GREEN];
		pixel10[FI_RGBA_BLUE] = (lines + xlp*btpp)[FI_RGBA_BLUE];

	    //pixel01 = *(lines + dely * wpls +  xup*btpp);
		pixel01[FI_RGBA_RED] = (lines + dely * wpls +  xup*btpp)[FI_RGBA_RED];
		pixel01[FI_RGBA_GREEN] = (lines + dely * wpls +  xup*btpp)[FI_RGBA_GREEN];
		pixel01[FI_RGBA_BLUE] = (lines + dely * wpls +  xup*btpp)[FI_RGBA_BLUE];

	    //pixel11 = *(lines + dely * wpls +  xlp*btpp);
		pixel11[FI_RGBA_RED] = (lines + dely * wpls +  xlp*btpp)[FI_RGBA_RED];
		pixel11[FI_RGBA_GREEN] = (lines + dely * wpls +  xlp*btpp)[FI_RGBA_GREEN];
		pixel11[FI_RGBA_BLUE] = (lines + dely * wpls +  xlp*btpp)[FI_RGBA_BLUE];

	    area00 = (16 - xuf) * (16 - yuf);
	    area10 = xlf * (16 - yuf);
	    area01 = (16 - xuf) * ylf;
	    area11 = xlf * ylf;

	    //v00r = area00 * (pixel00 >> 24);
		v00r = area00 * pixel00[FI_RGBA_RED];
	    //v00g = area00 * ((pixel00 >> 16) & 0xff);
		v00g = area00 * pixel00[FI_RGBA_GREEN];
	    //v00b = area00 * ((pixel00 >> 8) & 0xff);
		v00b = area00 * pixel00[FI_RGBA_BLUE];

	    //v10r = area10 * (pixel10 >> 24);
		v10r = area10 * pixel10[FI_RGBA_RED];
	    //v10g = area10 * ((pixel10 >> 16) & 0xff);
		v10g = area10 * pixel10[FI_RGBA_GREEN];
	    //v10b = area10 * ((pixel10 >> 8) & 0xff);
		v10b = area10 * pixel10[FI_RGBA_BLUE];

	    //v01r = area01 * (pixel01 >> 24);
		v01r = area01 * pixel01[FI_RGBA_RED];
	    //v01g = area01 * ((pixel01 >> 16) & 0xff);
		v01g = area01 * pixel01[FI_RGBA_GREEN];
	    //v01b = area01 * ((pixel01 >> 8) & 0xff);
		v01b = area01 * pixel01[FI_RGBA_BLUE];

	    //v11r = area11 * (pixel11 >> 24);
		v11r = area11 * pixel11[FI_RGBA_RED];
	    //v11g = area11 * ((pixel11 >> 16) & 0xff);
		v11g = area11 * pixel11[FI_RGBA_GREEN];
	    //v11b = area11 * ((pixel11 >> 8) & 0xff);
		v11b = area11 * pixel11[FI_RGBA_BLUE];

	    vinr = ving = vinb = 0;

            for (k = 1; k < dely; k++)
			{  // for full src pixels
                for (m = 1; m < delx; m++)
				{

		    //pixel = *(lines + k * wpls + (xup + m)*btpp);
			pixel[FI_RGBA_RED] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_RED];
			pixel[FI_RGBA_GREEN] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_GREEN];
			pixel[FI_RGBA_BLUE] = (lines + k * wpls + (xup + m)*btpp)[FI_RGBA_BLUE];


		    //vinr += 256 * (pixel >> 24);
			vinr += 256 * pixel[FI_RGBA_RED];
		    //ving += 256 * ((pixel >> 16) & 0xff);
			ving += 256 * pixel[FI_RGBA_GREEN];
		    //vinb += 256 * ((pixel >> 8) & 0xff);
			vinb += 256 * pixel[FI_RGBA_BLUE];
                }
            }

	    vmidr = vmidg = vmidb = 0;

	    areal = (16 - xuf) * 16;
	    arear = xlf * 16;
	    areat = 16 * (16 - yuf);
	    areab = 16 * ylf;

        for (k = 1; k < dely; k++)
		{  // for left side
		//pixel = *(lines + k * wpls + xup*btpp);
		pixel[FI_RGBA_RED] = (lines + k * wpls + xup*btpp)[FI_RGBA_RED];
		pixel[FI_RGBA_GREEN] = (lines + k * wpls + xup*btpp)[FI_RGBA_GREEN];
		pixel[FI_RGBA_BLUE] = (lines + k * wpls + xup*btpp)[FI_RGBA_BLUE];

		//vmidr += areal * (pixel >> 24);
		vmidr += areal * pixel[FI_RGBA_RED];
		//vmidg += areal * ((pixel >> 16) & 0xff);
		vmidg += areal * pixel[FI_RGBA_GREEN];
		//vmidb += areal * ((pixel >> 8) & 0xff);
		vmidb += areal * pixel[FI_RGBA_BLUE];
	    }

        for (k = 1; k < dely; k++)
		{  // for right side
		//pixel = *(lines + k * wpls + xlp*btpp);
		pixel[FI_RGBA_RED] = (lines + k * wpls + xlp*btpp)[FI_RGBA_RED];
		pixel[FI_RGBA_GREEN] = (lines + k * wpls + xlp*btpp)[FI_RGBA_GREEN];
		pixel[FI_RGBA_BLUE] = (lines + k * wpls + xlp*btpp)[FI_RGBA_BLUE];

		//vmidr += arear * (pixel >> 24);
		vmidr += arear * pixel[FI_RGBA_RED];
		//vmidg += arear * ((pixel >> 16) & 0xff);
		vmidg += arear * pixel[FI_RGBA_GREEN];
		//vmidb += arear * ((pixel >> 8) & 0xff);
		vmidb += arear * pixel[FI_RGBA_BLUE];
	    }

        for (m = 1; m < delx; m++)
		{  // for top side
		//pixel = *(lines + (xup + m)*btpp);
		pixel[FI_RGBA_RED] = (lines + (xup + m)*btpp)[FI_RGBA_RED];
		pixel[FI_RGBA_GREEN] = (lines + (xup + m)*btpp)[FI_RGBA_GREEN];
		pixel[FI_RGBA_BLUE] = (lines + (xup + m)*btpp)[FI_RGBA_BLUE];

		//vmidr += areat * (pixel >> 24);
		vmidr += areat * pixel[FI_RGBA_RED];
		//vmidg += areat * ((pixel >> 16) & 0xff);
		vmidg += areat * pixel[FI_RGBA_GREEN];
		//vmidb += areat * ((pixel >> 8) & 0xff);
		vmidb += areat * pixel[FI_RGBA_BLUE];
	    }
        
		for (m = 1; m < delx; m++)
		{  // for bottom side
		//pixel = *(lines + dely * wpls + (xup + m)*btpp);
		pixel[FI_RGBA_RED] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_RED];
		pixel[FI_RGBA_GREEN] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_GREEN];
		pixel[FI_RGBA_BLUE] = (lines + dely * wpls + (xup + m)*btpp)[FI_RGBA_BLUE];

		vmidr += areab * pixel[FI_RGBA_RED];
		vmidg += areab * pixel[FI_RGBA_GREEN];
		vmidb += areab * pixel[FI_RGBA_BLUE];
	    }

	    // Sum all the contributions
	    valr = (v00r + v01r + v10r + v11r + vinr + vmidr + 128) / area;
	    valg = (v00g + v01g + v10g + v11g + ving + vmidg + 128) / area;
	    valb = (v00b + v01b + v10b + v11b + vinb + vmidb + 128) / area;

	    //*(lined + j*btpp) = (valr << 24 ) | (valg << 16) | (valb << 8);

		(lined + j*btpp)[FI_RGBA_RED] = valr;
		(lined + j*btpp)[FI_RGBA_GREEN] = valg;
		(lined + j*btpp)[FI_RGBA_BLUE] = valb;
	}
    }

    return true;
}

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

/*------------------------------------------------------------------*
 *       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 LeptonicaScaleBySamplingLow(FIBITMAP* src_dib,
			   
               unsigned wd, unsigned hd, CPoint OffscreenSize, 
			   
			   BYTE* pBits, RGBQUAD *dst_pal)
{			   
unsigned    i, j;
unsigned    xs, prevxs;
BYTE sval;
long   *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 - 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 - 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 = (long *)calloc(hd, sizeof(long))) == NULL)
	
	return false;

    if ((scol = (long *)calloc(wd, sizeof(long))) == 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 * (double)i + 0.5), hs - 1);

    for (j = 0; j < wd; j++)
	scol[j] = MIN<unsigned>((unsigned)(wratio * (double)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 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 - 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 - 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 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 - 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 - 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;
}

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