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