You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
978 lines
26 KiB
978 lines
26 KiB
/*------------------------------------ |
|
* VisualPng.C -- Shows a PNG image |
|
*------------------------------------ |
|
* |
|
* Copyright 2000,2017 Willem van Schaik. |
|
* |
|
* This code is released under the libpng license. |
|
* For conditions of distribution and use, see the disclaimer |
|
* and license in png.h |
|
*/ |
|
|
|
/* switches */ |
|
|
|
/* defines */ |
|
|
|
#define PROGNAME "VisualPng" |
|
#define LONGNAME "Win32 Viewer for PNG-files" |
|
#define VERSION "1.0 of 2000 June 07" |
|
|
|
/* constants */ |
|
|
|
#define MARGIN 8 |
|
|
|
/* standard includes */ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <windows.h> |
|
#include <zlib.h> |
|
|
|
/* application includes */ |
|
|
|
#include "png.h" |
|
#include "pngfile.h" |
|
#include "resource.h" |
|
|
|
/* macros */ |
|
|
|
/* function prototypes */ |
|
|
|
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); |
|
BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; |
|
|
|
BOOL CenterAbout (HWND hwndChild, HWND hwndParent); |
|
|
|
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, |
|
int *pFileIndex); |
|
|
|
BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex, |
|
PTSTR pstrPrevName, PTSTR pstrNextName); |
|
|
|
BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName, |
|
png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, |
|
png_color *pBkgColor); |
|
|
|
BOOL DisplayImage (HWND hwnd, BYTE **ppDib, |
|
BYTE **ppDiData, int cxWinSize, int cyWinSize, |
|
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, |
|
BOOL bStretched); |
|
|
|
BOOL InitBitmap ( |
|
BYTE *pDiData, int cxWinSize, int cyWinSize); |
|
|
|
BOOL FillBitmap ( |
|
BYTE *pDiData, int cxWinSize, int cyWinSize, |
|
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, |
|
BOOL bStretched); |
|
|
|
/* a few global variables */ |
|
|
|
static char *szProgName = PROGNAME; |
|
static char *szAppName = LONGNAME; |
|
static char *szIconName = PROGNAME; |
|
static char szCmdFileName [MAX_PATH]; |
|
|
|
/* MAIN routine */ |
|
|
|
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, |
|
PSTR szCmdLine, int iCmdShow) |
|
{ |
|
HACCEL hAccel; |
|
HWND hwnd; |
|
MSG msg; |
|
WNDCLASS wndclass; |
|
int ixBorders, iyBorders; |
|
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW; |
|
wndclass.lpfnWndProc = WndProc; |
|
wndclass.cbClsExtra = 0; |
|
wndclass.cbWndExtra = 0; |
|
wndclass.hInstance = hInstance; |
|
wndclass.hIcon = LoadIcon (hInstance, szIconName) ; |
|
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); |
|
wndclass.hbrBackground = NULL; /* (HBRUSH) GetStockObject (GRAY_BRUSH); */ |
|
wndclass.lpszMenuName = szProgName; |
|
wndclass.lpszClassName = szProgName; |
|
|
|
if (!RegisterClass (&wndclass)) |
|
{ |
|
MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"), |
|
szProgName, MB_ICONERROR); |
|
return 0; |
|
} |
|
|
|
/* if filename given on commandline, store it */ |
|
if ((szCmdLine != NULL) && (*szCmdLine != '\0')) |
|
if (szCmdLine[0] == '"') |
|
strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2); |
|
else |
|
strcpy (szCmdFileName, szCmdLine); |
|
else |
|
strcpy (szCmdFileName, ""); |
|
|
|
/* calculate size of window-borders */ |
|
ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) + |
|
GetSystemMetrics (SM_CXDLGFRAME)); |
|
iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) + |
|
GetSystemMetrics (SM_CYDLGFRAME)) + |
|
GetSystemMetrics (SM_CYCAPTION) + |
|
GetSystemMetrics (SM_CYMENUSIZE) + |
|
1; /* WvS: don't ask me why? */ |
|
|
|
hwnd = CreateWindow (szProgName, szAppName, |
|
WS_OVERLAPPEDWINDOW, |
|
CW_USEDEFAULT, CW_USEDEFAULT, |
|
512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders, |
|
/* CW_USEDEFAULT, CW_USEDEFAULT, */ |
|
NULL, NULL, hInstance, NULL); |
|
|
|
ShowWindow (hwnd, iCmdShow); |
|
UpdateWindow (hwnd); |
|
|
|
hAccel = LoadAccelerators (hInstance, szProgName); |
|
|
|
while (GetMessage (&msg, NULL, 0, 0)) |
|
{ |
|
if (!TranslateAccelerator (hwnd, hAccel, &msg)) |
|
{ |
|
TranslateMessage (&msg); |
|
DispatchMessage (&msg); |
|
} |
|
} |
|
return msg.wParam; |
|
} |
|
|
|
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, |
|
LPARAM lParam) |
|
{ |
|
static HINSTANCE hInstance ; |
|
static HDC hdc; |
|
static PAINTSTRUCT ps; |
|
static HMENU hMenu; |
|
|
|
static BITMAPFILEHEADER *pbmfh; |
|
static BITMAPINFOHEADER *pbmih; |
|
static BYTE *pbImage; |
|
static int cxWinSize, cyWinSize; |
|
static int cxImgSize, cyImgSize; |
|
static int cImgChannels; |
|
static png_color bkgColor = {127, 127, 127}; |
|
|
|
static BOOL bStretched = TRUE; |
|
|
|
static BYTE *pDib = NULL; |
|
static BYTE *pDiData = NULL; |
|
|
|
static TCHAR szImgPathName [MAX_PATH]; |
|
static TCHAR szTitleName [MAX_PATH]; |
|
|
|
static TCHAR *pPngFileList = NULL; |
|
static int iPngFileCount; |
|
static int iPngFileIndex; |
|
|
|
BOOL bOk; |
|
|
|
switch (message) |
|
{ |
|
case WM_CREATE: |
|
hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; |
|
PngFileInitialize (hwnd); |
|
|
|
strcpy (szImgPathName, ""); |
|
|
|
/* in case we process file given on command-line */ |
|
|
|
if (szCmdFileName[0] != '\0') |
|
{ |
|
strcpy (szImgPathName, szCmdFileName); |
|
|
|
/* read the other png-files in the directory for later */ |
|
/* next/previous commands */ |
|
|
|
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, |
|
&iPngFileIndex); |
|
|
|
/* load the image from file */ |
|
|
|
if (!LoadImageFile (hwnd, szImgPathName, |
|
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) |
|
return 0; |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
} |
|
|
|
return 0; |
|
|
|
case WM_SIZE: |
|
cxWinSize = LOWORD (lParam); |
|
cyWinSize = HIWORD (lParam); |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
|
|
return 0; |
|
|
|
case WM_INITMENUPOPUP: |
|
hMenu = GetMenu (hwnd); |
|
|
|
if (pbImage) |
|
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED); |
|
else |
|
EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED); |
|
|
|
return 0; |
|
|
|
case WM_COMMAND: |
|
hMenu = GetMenu (hwnd); |
|
|
|
switch (LOWORD (wParam)) |
|
{ |
|
case IDM_FILE_OPEN: |
|
|
|
/* show the File Open dialog box */ |
|
|
|
if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName)) |
|
return 0; |
|
|
|
/* read the other png-files in the directory for later */ |
|
/* next/previous commands */ |
|
|
|
BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, |
|
&iPngFileIndex); |
|
|
|
/* load the image from file */ |
|
|
|
if (!LoadImageFile (hwnd, szImgPathName, |
|
&pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) |
|
return 0; |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
|
|
return 0; |
|
|
|
case IDM_FILE_SAVE: |
|
|
|
/* show the File Save dialog box */ |
|
|
|
if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName)) |
|
return 0; |
|
|
|
/* save the PNG to a disk file */ |
|
|
|
SetCursor (LoadCursor (NULL, IDC_WAIT)); |
|
ShowCursor (TRUE); |
|
|
|
bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize, |
|
bkgColor); |
|
|
|
ShowCursor (FALSE); |
|
SetCursor (LoadCursor (NULL, IDC_ARROW)); |
|
|
|
if (!bOk) |
|
MessageBox (hwnd, TEXT ("Error in saving the PNG image"), |
|
szProgName, MB_ICONEXCLAMATION | MB_OK); |
|
return 0; |
|
|
|
case IDM_FILE_NEXT: |
|
|
|
/* read next entry in the directory */ |
|
|
|
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, |
|
NULL, szImgPathName)) |
|
{ |
|
if (strcmp (szImgPathName, "") == 0) |
|
return 0; |
|
|
|
/* load the image from file */ |
|
|
|
if (!LoadImageFile (hwnd, szImgPathName, &pbImage, |
|
&cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) |
|
return 0; |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
} |
|
|
|
return 0; |
|
|
|
case IDM_FILE_PREVIOUS: |
|
|
|
/* read previous entry in the directory */ |
|
|
|
if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, |
|
szImgPathName, NULL)) |
|
{ |
|
|
|
if (strcmp (szImgPathName, "") == 0) |
|
return 0; |
|
|
|
/* load the image from file */ |
|
|
|
if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, |
|
&cyImgSize, &cImgChannels, &bkgColor)) |
|
return 0; |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
} |
|
|
|
return 0; |
|
|
|
case IDM_FILE_EXIT: |
|
|
|
/* more cleanup needed... */ |
|
|
|
/* free image buffer */ |
|
|
|
if (pDib != NULL) |
|
{ |
|
free (pDib); |
|
pDib = NULL; |
|
} |
|
|
|
/* free file-list */ |
|
|
|
if (pPngFileList != NULL) |
|
{ |
|
free (pPngFileList); |
|
pPngFileList = NULL; |
|
} |
|
|
|
/* let's go ... */ |
|
|
|
exit (0); |
|
|
|
return 0; |
|
|
|
case IDM_OPTIONS_STRETCH: |
|
bStretched = !bStretched; |
|
if (bStretched) |
|
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED); |
|
else |
|
CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED); |
|
|
|
/* invalidate the client area for later update */ |
|
|
|
InvalidateRect (hwnd, NULL, TRUE); |
|
|
|
/* display the PNG into the DIBitmap */ |
|
|
|
DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); |
|
|
|
return 0; |
|
|
|
case IDM_HELP_ABOUT: |
|
DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; |
|
return 0; |
|
|
|
} /* end switch */ |
|
|
|
break; |
|
|
|
case WM_PAINT: |
|
hdc = BeginPaint (hwnd, &ps); |
|
|
|
if (pDib) |
|
SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0, |
|
0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS); |
|
|
|
EndPaint (hwnd, &ps); |
|
return 0; |
|
|
|
case WM_DESTROY: |
|
if (pbmfh) |
|
{ |
|
free (pbmfh); |
|
pbmfh = NULL; |
|
} |
|
|
|
PostQuitMessage (0); |
|
return 0; |
|
} |
|
|
|
return DefWindowProc (hwnd, message, wParam, lParam); |
|
} |
|
|
|
BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, |
|
WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (message) |
|
{ |
|
case WM_INITDIALOG : |
|
ShowWindow (hDlg, SW_HIDE); |
|
CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER)); |
|
ShowWindow (hDlg, SW_SHOW); |
|
return TRUE ; |
|
|
|
case WM_COMMAND : |
|
switch (LOWORD (wParam)) |
|
{ |
|
case IDOK : |
|
case IDCANCEL : |
|
EndDialog (hDlg, 0) ; |
|
return TRUE ; |
|
} |
|
break ; |
|
} |
|
return FALSE ; |
|
} |
|
|
|
/*--------------- |
|
* CenterAbout |
|
*--------------- |
|
*/ |
|
BOOL CenterAbout (HWND hwndChild, HWND hwndParent) |
|
{ |
|
RECT rChild, rParent, rWorkArea; |
|
int wChild, hChild, wParent, hParent; |
|
int xNew, yNew; |
|
BOOL bResult; |
|
|
|
/* Get the Height and Width of the child window */ |
|
GetWindowRect (hwndChild, &rChild); |
|
wChild = rChild.right - rChild.left; |
|
hChild = rChild.bottom - rChild.top; |
|
|
|
/* Get the Height and Width of the parent window */ |
|
GetWindowRect (hwndParent, &rParent); |
|
wParent = rParent.right - rParent.left; |
|
hParent = rParent.bottom - rParent.top; |
|
|
|
/* Get the limits of the 'workarea' */ |
|
bResult = SystemParametersInfo( |
|
SPI_GETWORKAREA, /* system parameter to query or set */ |
|
sizeof(RECT), |
|
&rWorkArea, |
|
0); |
|
if (!bResult) { |
|
rWorkArea.left = rWorkArea.top = 0; |
|
rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); |
|
rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); |
|
} |
|
|
|
/* Calculate new X position, then adjust for workarea */ |
|
xNew = rParent.left + ((wParent - wChild) /2); |
|
if (xNew < rWorkArea.left) { |
|
xNew = rWorkArea.left; |
|
} else if ((xNew+wChild) > rWorkArea.right) { |
|
xNew = rWorkArea.right - wChild; |
|
} |
|
|
|
/* Calculate new Y position, then adjust for workarea */ |
|
yNew = rParent.top + ((hParent - hChild) /2); |
|
if (yNew < rWorkArea.top) { |
|
yNew = rWorkArea.top; |
|
} else if ((yNew+hChild) > rWorkArea.bottom) { |
|
yNew = rWorkArea.bottom - hChild; |
|
} |
|
|
|
/* Set it, and return */ |
|
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | |
|
SWP_NOZORDER); |
|
} |
|
|
|
/*---------------- |
|
* BuildPngList |
|
*---------------- |
|
*/ |
|
BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, |
|
int *pFileIndex) |
|
{ |
|
static TCHAR szImgPathName [MAX_PATH]; |
|
static TCHAR szImgFileName [MAX_PATH]; |
|
static TCHAR szImgFindName [MAX_PATH]; |
|
|
|
WIN32_FIND_DATA finddata; |
|
HANDLE hFind; |
|
|
|
static TCHAR szTmp [MAX_PATH]; |
|
BOOL bOk; |
|
int i, ii; |
|
int j, jj; |
|
|
|
/* free previous file-list */ |
|
|
|
if (*ppFileList != NULL) |
|
{ |
|
free (*ppFileList); |
|
*ppFileList = NULL; |
|
} |
|
|
|
/* extract foldername, filename and search-name */ |
|
|
|
strcpy (szImgPathName, pstrPathName); |
|
strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1); |
|
|
|
strcpy (szImgFindName, szImgPathName); |
|
*(strrchr (szImgFindName, '\\') + 1) = '\0'; |
|
strcat (szImgFindName, "*.png"); |
|
|
|
/* first cycle: count number of files in directory for memory allocation */ |
|
|
|
*pFileCount = 0; |
|
|
|
hFind = FindFirstFile(szImgFindName, &finddata); |
|
bOk = (hFind != (HANDLE) -1); |
|
|
|
while (bOk) |
|
{ |
|
*pFileCount += 1; |
|
bOk = FindNextFile(hFind, &finddata); |
|
} |
|
FindClose(hFind); |
|
|
|
/* allocation memory for file-list */ |
|
|
|
*ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH); |
|
|
|
/* second cycle: read directory and store filenames in file-list */ |
|
|
|
hFind = FindFirstFile(szImgFindName, &finddata); |
|
bOk = (hFind != (HANDLE) -1); |
|
|
|
i = 0; |
|
ii = 0; |
|
while (bOk) |
|
{ |
|
strcpy (*ppFileList + ii, szImgPathName); |
|
strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName); |
|
|
|
if (strcmp(pstrPathName, *ppFileList + ii) == 0) |
|
*pFileIndex = i; |
|
|
|
ii += MAX_PATH; |
|
i++; |
|
|
|
bOk = FindNextFile(hFind, &finddata); |
|
} |
|
FindClose(hFind); |
|
|
|
/* finally we must sort the file-list */ |
|
|
|
for (i = 0; i < *pFileCount - 1; i++) |
|
{ |
|
ii = i * MAX_PATH; |
|
for (j = i+1; j < *pFileCount; j++) |
|
{ |
|
jj = j * MAX_PATH; |
|
if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0) |
|
{ |
|
strcpy (szTmp, *ppFileList + jj); |
|
strcpy (*ppFileList + jj, *ppFileList + ii); |
|
strcpy (*ppFileList + ii, szTmp); |
|
|
|
/* check if this was the current image that we moved */ |
|
|
|
if (*pFileIndex == i) |
|
*pFileIndex = j; |
|
else |
|
if (*pFileIndex == j) |
|
*pFileIndex = i; |
|
} |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/*---------------- |
|
* SearchPngList |
|
*---------------- |
|
*/ |
|
|
|
BOOL SearchPngList ( |
|
TCHAR *pFileList, int FileCount, int *pFileIndex, |
|
PTSTR pstrPrevName, PTSTR pstrNextName) |
|
{ |
|
if (FileCount > 0) |
|
{ |
|
/* get previous entry */ |
|
|
|
if (pstrPrevName != NULL) |
|
{ |
|
if (*pFileIndex > 0) |
|
*pFileIndex -= 1; |
|
else |
|
*pFileIndex = FileCount - 1; |
|
|
|
strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH)); |
|
} |
|
|
|
/* get next entry */ |
|
|
|
if (pstrNextName != NULL) |
|
{ |
|
if (*pFileIndex < FileCount - 1) |
|
*pFileIndex += 1; |
|
else |
|
*pFileIndex = 0; |
|
|
|
strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH)); |
|
} |
|
|
|
return TRUE; |
|
} |
|
else |
|
{ |
|
return FALSE; |
|
} |
|
} |
|
|
|
/*----------------- |
|
* LoadImageFile |
|
*----------------- |
|
*/ |
|
|
|
BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName, |
|
png_byte **ppbImage, int *pxImgSize, int *pyImgSize, |
|
int *piChannels, png_color *pBkgColor) |
|
{ |
|
static TCHAR szTmp [MAX_PATH]; |
|
|
|
/* if there's an existing PNG, free the memory */ |
|
|
|
if (*ppbImage) |
|
{ |
|
free (*ppbImage); |
|
*ppbImage = NULL; |
|
} |
|
|
|
/* Load the entire PNG into memory */ |
|
|
|
SetCursor (LoadCursor (NULL, IDC_WAIT)); |
|
ShowCursor (TRUE); |
|
|
|
PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, |
|
pBkgColor); |
|
|
|
ShowCursor (FALSE); |
|
SetCursor (LoadCursor (NULL, IDC_ARROW)); |
|
|
|
if (*ppbImage != NULL) |
|
{ |
|
sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); |
|
SetWindowText (hwnd, szTmp); |
|
} |
|
else |
|
{ |
|
MessageBox (hwnd, TEXT ("Error in loading the PNG image"), |
|
szProgName, MB_ICONEXCLAMATION | MB_OK); |
|
return FALSE; |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/*---------------- |
|
* DisplayImage |
|
*---------------- |
|
*/ |
|
BOOL DisplayImage (HWND hwnd, BYTE **ppDib, |
|
BYTE **ppDiData, int cxWinSize, int cyWinSize, |
|
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, |
|
BOOL bStretched) |
|
{ |
|
BYTE *pDib = *ppDib; |
|
BYTE *pDiData = *ppDiData; |
|
/* BITMAPFILEHEADER *pbmfh; */ |
|
BITMAPINFOHEADER *pbmih; |
|
WORD wDIRowBytes; |
|
png_color bkgBlack = {0, 0, 0}; |
|
png_color bkgGray = {127, 127, 127}; |
|
png_color bkgWhite = {255, 255, 255}; |
|
|
|
/* allocate memory for the Device Independent bitmap */ |
|
|
|
wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2; |
|
|
|
if (pDib) |
|
{ |
|
free (pDib); |
|
pDib = NULL; |
|
} |
|
|
|
if (cyWinSize > ((size_t)(-1))/wDIRowBytes) { |
|
{ |
|
MessageBox (hwnd, TEXT ("Visual PNG: image is too big"); |
|
} |
|
if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) + |
|
wDIRowBytes * cyWinSize))) |
|
{ |
|
MessageBox (hwnd, TEXT ("Error in displaying the PNG image"), |
|
szProgName, MB_ICONEXCLAMATION | MB_OK); |
|
*ppDib = pDib = NULL; |
|
return FALSE; |
|
} |
|
*ppDib = pDib; |
|
memset (pDib, 0, sizeof(BITMAPINFOHEADER)); |
|
|
|
/* initialize the dib-structure */ |
|
|
|
pbmih = (BITMAPINFOHEADER *) pDib; |
|
pbmih->biSize = sizeof(BITMAPINFOHEADER); |
|
pbmih->biWidth = cxWinSize; |
|
pbmih->biHeight = -((long) cyWinSize); |
|
pbmih->biPlanes = 1; |
|
pbmih->biBitCount = 24; |
|
pbmih->biCompression = 0; |
|
pDiData = pDib + sizeof(BITMAPINFOHEADER); |
|
*ppDiData = pDiData; |
|
|
|
/* first fill bitmap with gray and image border */ |
|
|
|
InitBitmap (pDiData, cxWinSize, cyWinSize); |
|
|
|
/* then fill bitmap with image */ |
|
|
|
if (pbImage) |
|
{ |
|
FillBitmap ( |
|
pDiData, cxWinSize, cyWinSize, |
|
pbImage, cxImgSize, cyImgSize, cImgChannels, |
|
bStretched); |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/*-------------- |
|
* InitBitmap |
|
*-------------- |
|
*/ |
|
BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize) |
|
{ |
|
BYTE *dst; |
|
int x, y, col; |
|
|
|
/* initialize the background with gray */ |
|
|
|
dst = pDiData; |
|
for (y = 0; y < cyWinSize; y++) |
|
{ |
|
col = 0; |
|
for (x = 0; x < cxWinSize; x++) |
|
{ |
|
/* fill with GRAY */ |
|
*dst++ = 127; |
|
*dst++ = 127; |
|
*dst++ = 127; |
|
col += 3; |
|
} |
|
/* rows start on 4 byte boundaries */ |
|
while ((col % 4) != 0) |
|
{ |
|
dst++; |
|
col++; |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/*-------------- |
|
* FillBitmap |
|
*-------------- |
|
*/ |
|
BOOL FillBitmap ( |
|
BYTE *pDiData, int cxWinSize, int cyWinSize, |
|
BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, |
|
BOOL bStretched) |
|
{ |
|
BYTE *pStretchedImage; |
|
BYTE *pImg; |
|
BYTE *src, *dst; |
|
BYTE r, g, b, a; |
|
const int cDIChannels = 3; |
|
WORD wImgRowBytes; |
|
WORD wDIRowBytes; |
|
int cxNewSize, cyNewSize; |
|
int cxImgPos, cyImgPos; |
|
int xImg, yImg; |
|
int xWin, yWin; |
|
int xOld, yOld; |
|
int xNew, yNew; |
|
|
|
if (bStretched) |
|
{ |
|
cxNewSize = cxWinSize - 2 * MARGIN; |
|
cyNewSize = cyWinSize - 2 * MARGIN; |
|
|
|
/* stretch the image to it's window determined size */ |
|
|
|
/* the following two are mathematically the same, but the first |
|
* has side-effects because of rounding |
|
*/ |
|
/* if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) */ |
|
if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize)) |
|
{ |
|
cyNewSize = cxNewSize * cyImgSize / cxImgSize; |
|
cxImgPos = MARGIN; |
|
cyImgPos = (cyWinSize - cyNewSize) / 2; |
|
} |
|
else |
|
{ |
|
cxNewSize = cyNewSize * cxImgSize / cyImgSize; |
|
cyImgPos = MARGIN; |
|
cxImgPos = (cxWinSize - cxNewSize) / 2; |
|
} |
|
|
|
if (cyNewSize > ((size_t)(-1))/(cImgChannels * cxNewSize)) { |
|
{ |
|
MessageBox (hwnd, TEXT ("Visual PNG: stretched image is too big"); |
|
} |
|
pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize); |
|
pImg = pStretchedImage; |
|
|
|
for (yNew = 0; yNew < cyNewSize; yNew++) |
|
{ |
|
yOld = yNew * cyImgSize / cyNewSize; |
|
for (xNew = 0; xNew < cxNewSize; xNew++) |
|
{ |
|
xOld = xNew * cxImgSize / cxNewSize; |
|
|
|
r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0); |
|
g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1); |
|
b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2); |
|
*pImg++ = r; |
|
*pImg++ = g; |
|
*pImg++ = b; |
|
if (cImgChannels == 4) |
|
{ |
|
a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) |
|
+ 3); |
|
*pImg++ = a; |
|
} |
|
} |
|
} |
|
|
|
/* calculate row-bytes */ |
|
|
|
wImgRowBytes = cImgChannels * cxNewSize; |
|
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; |
|
|
|
/* copy image to screen */ |
|
|
|
for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++) |
|
{ |
|
if (yWin >= cyWinSize - cyImgPos) |
|
break; |
|
src = pStretchedImage + yImg * wImgRowBytes; |
|
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; |
|
|
|
for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++) |
|
{ |
|
if (xWin >= cxWinSize - cxImgPos) |
|
break; |
|
r = *src++; |
|
g = *src++; |
|
b = *src++; |
|
*dst++ = b; /* note the reverse order */ |
|
*dst++ = g; |
|
*dst++ = r; |
|
if (cImgChannels == 4) |
|
{ |
|
a = *src++; |
|
} |
|
} |
|
} |
|
|
|
/* free memory */ |
|
|
|
if (pStretchedImage != NULL) |
|
{ |
|
free (pStretchedImage); |
|
pStretchedImage = NULL; |
|
} |
|
|
|
} |
|
|
|
/* process the image not-stretched */ |
|
|
|
else |
|
{ |
|
/* calculate the central position */ |
|
|
|
cxImgPos = (cxWinSize - cxImgSize) / 2; |
|
cyImgPos = (cyWinSize - cyImgSize) / 2; |
|
|
|
/* check for image larger than window */ |
|
|
|
if (cxImgPos < MARGIN) |
|
cxImgPos = MARGIN; |
|
if (cyImgPos < MARGIN) |
|
cyImgPos = MARGIN; |
|
|
|
/* calculate both row-bytes */ |
|
|
|
wImgRowBytes = cImgChannels * cxImgSize; |
|
wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; |
|
|
|
/* copy image to screen */ |
|
|
|
for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++) |
|
{ |
|
if (yWin >= cyWinSize - MARGIN) |
|
break; |
|
src = pbImage + yImg * wImgRowBytes; |
|
dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; |
|
|
|
for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++) |
|
{ |
|
if (xWin >= cxWinSize - MARGIN) |
|
break; |
|
r = *src++; |
|
g = *src++; |
|
b = *src++; |
|
*dst++ = b; /* note the reverse order */ |
|
*dst++ = g; |
|
*dst++ = r; |
|
if (cImgChannels == 4) |
|
{ |
|
a = *src++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/*----------------- |
|
* end of source |
|
*----------------- |
|
*/
|
|
|