Click here to Skip to main content
15,885,278 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

// DisplayThread.cpp : Defines the CDisplayThread class behavior.
//

#include "stdafx.h"
#include "fi_test.h"

#include "MainFrm.h"
#include "fi_testDoc.h"
#include "fi_testView.h"
#include "DisplayThread.h"

#include "Leptonica.h"

#include "Resize2.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

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

IMPLEMENT_DYNCREATE (CDisplayThread, CWinThread)

BEGIN_MESSAGE_MAP(CDisplayThread, CWinThread)

	ON_THREAD_MESSAGE (WM_DRAW_BITMAP, DisplayBitmapThread)
	ON_THREAD_MESSAGE (WM_ZOOM, ZoomBitmapThread)

END_MESSAGE_MAP()

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

BOOL CDisplayThread::InitInstance ()
{
	m_dib2 = NULL;

	m_dcOffscreen.Attach(m_hdcOffscreen);

	m_view_init_height = 0;
	
	m_view_init_width = 0;
	
	m_fit_height = false;

	m_dib_w = 0;

	m_dib_h = 0;

	CRect rcClient;

	m_ResampleMode = 1;
		
	m_pCsView->GetClientRect(&rcClient);

	m_view_init_height = rcClient.Height();
		
	m_view_init_width = rcClient.Width();

	m_dib_w = FreeImage_GetWidth(m_dib);
	
    m_dib_h = FreeImage_GetHeight(m_dib);	
	
	float scale_height = (float)m_dib_h / m_view_init_height; // vert fit value
	
	float scale_width = (float)m_dib_w / m_view_init_width; // horz fit value
	
	// Get the biggest dimension (width or height) which has to be
	// fitted into the client area
	
	m_fit_height = false;
	
	// Seek the tightest fit
	
	if (scale_height > scale_width) m_fit_height = true;
	
	float zoom;
	
	if (m_fit_height) zoom = (float)m_dib_h / m_view_init_height;
	
	else  zoom = (float)m_dib_w / m_view_init_width;

	
	if (zoom < 1) zoom = 1; // if an image is originally smaller than
	// the client area - do not enlarge it (to fit the client area)
	
	m_zoom_fit = zoom; // Remember the "Zoom Fit" zoom value

    return TRUE;
}

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

LRESULT CDisplayThread::DisplayBitmapThread(WPARAM wParam, LPARAM lParam)
{
	float zoom;

	if (wParam) // setting another rescaling algorithm (leaving the same zoom)

	{
		zoom = m_zoom;

		m_ResampleMode = (int)lParam;

		m_dcOffscreen.SelectObject(m_pOldBitmap);

		m_OffscreenBitmap.DeleteObject();

		m_pOldBitmap = NULL;
	}

	else // setting Zoom Fit

	zoom = m_zoom_fit;

	DisplayBitmapZoomedThread(zoom);

	return 0;
}

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

BOOL CDisplayThread::DisplayBitmapZoomedThread(float zoom)
{
	if (m_dib2)
	{
		FreeImage_Unload(m_dib2);
		
		m_dib2 = NULL;	
	}

	// this code chunk is taken out of the CResizeEngine::scale method
	// (called from FreeImage_Rescale)
	unsigned bpp_dst = FreeImage_GetBPP(m_dib);
	if(bpp_dst == 1) {
		// convert output to 8-bit
		bpp_dst = 8;		
	}

	// Calculate the zoomed image width
	m_zdib_width = (int)(FreeImage_GetWidth(m_dib)/zoom);
	
	// Calculate the zoomed image height
    m_zdib_height = (int)(FreeImage_GetHeight(m_dib)/zoom);

	m_OffscreenSize.x = max(m_view_init_width, m_zdib_width);
	
	m_OffscreenSize.y = max(m_view_init_height, m_zdib_height);
	
	// Allocate the "empty" FIBITMAP - just as a BITMAPINFO holder
	m_dib2 = FreeImage_AllocateT(FreeImage_GetImageType(m_dib),
		
		1, 1, // this is a one-by-one pixel sized bitmap ("empty")
		
		bpp_dst,
		
		FreeImage_GetRedMask(m_dib),
		
		FreeImage_GetGreenMask(m_dib),
		
		FreeImage_GetBlueMask(m_dib));

	if (!m_dib2) return false;

	m_pbih = FreeImage_GetInfoHeader(m_dib2);	

	m_pbih->biWidth = m_OffscreenSize.x; // replace 1 by the real backbitmap width

	m_pbih->biHeight = m_OffscreenSize.y; // replace 1 by the real backbitmap height

	m_pbi = FreeImage_GetInfo(m_dib2);	// now we have a full-fledged
	// BITMAPINFO structure for our rescaled image

	// this code chunk is taken out of the CResizeEngine::scale method
	// (called from FreeImage_Rescale)

	RGBQUAD *dst_pal = FreeImage_GetPalette(m_dib2);

	if(bpp_dst == 8)  // build the palette if needed
	{
		if(FreeImage_GetColorType(m_dib2) == FIC_MINISWHITE)
		
			// build an inverted greyscale palette			
			for(int i = 0; i < 256; i++)
			
				dst_pal[i].rgbRed = dst_pal[i].rgbGreen =
					dst_pal[i].rgbBlue = (BYTE)(255 - i);			
		 else 
		
			// build a greyscale palette			
			for(int i = 0; i < 256; i++) 
			
				dst_pal[i].rgbRed = dst_pal[i].rgbGreen =
					dst_pal[i].rgbBlue = (BYTE)i;		
	}	

	// Create the DIB Section
	
	HBITMAP hBitmap = CreateDIBSection (NULL, m_pbi, DIB_RGB_COLORS, (void**)&m_pBits, NULL, 0) ;
	
	if (hBitmap == NULL) return false;

	m_OffscreenBitmap.Attach(hBitmap);

	m_pOldBitmap = m_dcOffscreen.SelectObject(&m_OffscreenBitmap);	

	// Fill the background bitmap with a backcolor
	m_dcOffscreen.FillSolidRect(0, 0, m_OffscreenSize.x,
		m_OffscreenSize.y, RGB(160, 160, 160));		

	// Fill the DIB Section bitmap buffer (e.g. content to be displayed)

	// This function is remade out of pnmscalefixed scaling algorithm
	// from the netpbm project (the sourcecode is taken from WinDjView
	// Scaling.cpp)
	// It rescales a src FIBITMAP and places the result into
	// the provided BYTE* m_pBits buffer

	BOOL res = false;

	switch (m_ResampleMode)
	{

	case 1:

//	res = FilterRcg(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal, 2);

//  break;

	if (zoom > 1.0)
	{

	if(bpp_dst == 8)

	res = LeptonicaScaleGrayAreaMapLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);

	else

	res = LeptonicaScaleColorAreaMapLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);	
	}

	else

	res = LeptonicaScaleLILow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal, true);
	
	break;

	case 2:

	if(bpp_dst == 8)

	res = LeptonicaScaleGrayAreaMapLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);

	else

	res = LeptonicaScaleColorAreaMapLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);	

	break;

	case 3:

	res = LeptonicaScaleBySamplingLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);

	break;

	case 4:	

	res = LeptonicaScaleSmoothLow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, 3, m_pBits, dst_pal);

	break;

	case 5:	

	res = LeptonicaScaleLILow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal, true);

	break;

	case 6:	

	res = LeptonicaScaleLILow(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal, false);	

	break;

	case 7:

	if(bpp_dst == 8)

	res = RescalePnm8(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits, dst_pal);

	else

	res = RescalePnm(m_dib, m_zdib_width, m_zdib_height, m_OffscreenSize, m_pBits);

	case 8:	

	res = FI_Rescale2(m_dib, m_zdib_width, m_zdib_height, FILTER_BILINEAR, bpp_dst, m_OffscreenSize, m_pBits);

	break;

	case 9:	

	res = FI_Rescale2(m_dib, m_zdib_width, m_zdib_height, FILTER_BOX, bpp_dst, m_OffscreenSize, m_pBits);

	break;

	}

	if (!res)
	{
		FreeImage_Unload(m_dib2);
		
		m_dib2 = NULL;		
		
		return false;
	}

	m_pCsView->SetScrollSizes(MM_TEXT, CSize(m_zdib_width-1, m_zdib_height-1)); // equal to the image width and height
	// minus 1 - so that the background 1-pixel-wide line would not be visible 
	// at the bitmap right side by the max scroll-right		
	
	// Set the initial screen position of an image to be displayed
	
	if (m_zdib_width > m_view_init_width)
		// the image width is bigger than the client area width
		
		// Scroll an image half-width so that it would be width-centered	
		m_pCsView->SetScrollPos(SB_HORZ, (m_zdib_width-m_view_init_width)/2);
			
	if (m_zdib_height > m_view_init_height)	
		// the image height is bigger than the client area height
		
		// Scroll an image half-height so that it would be height-centered
		m_pCsView->SetScrollPos(SB_VERT, (m_zdib_height-m_view_init_height)/2);

	
	m_zoom = zoom; // Remember the current zoom value

	DWORD dw_temp_zoom;

	// packing float into DWORD without any fractional part loss
	memcpy(&dw_temp_zoom, &zoom, sizeof(float));

	::SendMessage(m_hFr, WM_SHOW_ZOOM_ON_SCROLLBAR, dw_temp_zoom, 0);

	m_pCsView->Invalidate(false);

	m_pCsView->UpdateWindow();

	return true;
}

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

CDisplayThread::CDisplayThread()
{

}

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

CDisplayThread::~CDisplayThread()
{
	m_dcOffscreen.SelectObject(m_pOldBitmap);
	
	m_OffscreenBitmap.DeleteObject();
	
	m_pOldBitmap = NULL;

	m_dcOffscreen.Detach();

	if (m_dib2)
	{
		FreeImage_Unload(m_dib2);
		
		m_dib2 = NULL;	
	}
	// erase zoom val on status bar
	::SendMessage(m_hFr, WM_SHOW_ZOOM_ON_SCROLLBAR, 0, (LPARAM)1);
}

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

CDisplayThread::CDisplayThread(FIBITMAP* dib, HDC hdcOffscreen, HWND hFr, CScrollView* pCsView)
{

m_dib = dib;

m_hdcOffscreen = hdcOffscreen;

m_hFr = hFr;

m_pCsView = pCsView;

}

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

LRESULT CDisplayThread::ZoomBitmapThread(WPARAM wParam, LPARAM lParam)
{
	m_dcOffscreen.SelectObject(m_pOldBitmap);

	m_OffscreenBitmap.DeleteObject();

	m_pOldBitmap = NULL;

	float zoom = m_zoom;

	if (lParam) // Zoom Fit

		zoom = m_zoom_fit;

	else
	{

	if (wParam) // MouseWheel Up

		zoom *= 1.1f;  // Zoom Out (Smaller)
	
	else // MouseWheel Down

		zoom /= 1.1f;  // Zoom In (Bigger)
	}

	if (zoom < 0.05 * m_zoom_fit || 
		zoom > 50 * m_zoom_fit)

		return 0;

	float temp = zoom - m_zoom_fit;

	if (temp < 0) temp = -temp; // not using fabs() - for speed
	
	// it the current zoom value is "zoom-fit" - assign the stored
    // zoom-fit value to it - to correct the accumulated rounding error
	if (temp < 0.05) zoom = m_zoom_fit;

	DisplayBitmapZoomedThread(zoom);

	return 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