Windows2003-3790/windows/advcore/gdiplus/test/thumbtst/thumbtst.cpp
2020-09-30 16:53:55 +02:00

811 lines
20 KiB
C++

//
// Simple test program for imaging library
//
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>
#include <objbase.h>
#include <urlmon.h>
#include <commdlg.h>
#include <math.h>
#include <gdiplus.h>
#include <gdiplusflat.h>
using namespace Gdiplus;
#include "../gpinit.inc"
#define k_DefaultWidth 720
#define k_DefaultHeight 480
#define THUMBSIZE 120
#define MAX_FILENAME 1000
CHAR* g_pcProgramName = NULL; // program name
HINSTANCE g_hAppInstance; // handle to the application instance
HWND g_hwndMain; // handle to application's main window
int g_iTotalNumOfImages;
int lastS;
int numX;
int g_iNumFileNames;
Image** g_ppThumbImages;
CHAR** g_ppcInputFilenames;
RECT g_ThumbRect;
BOOL g_fVerbose = FALSE;
BOOL g_fHighQualityThumb = FALSE;
#define ERREXIT(args) { printf args; exit(-1); }
#define VERBOSE(args) printf args
//
// Helper class to convert ANSI strings to Unicode strings
//
inline BOOL
UnicodeToAnsiStr(
const WCHAR* unicodeStr,
CHAR* ansiStr,
INT ansiSize
)
{
return WideCharToMultiByte(CP_ACP, 0, unicodeStr, -1, ansiStr, ansiSize,
NULL, NULL) > 0;
}
inline BOOL
AnsiToUnicodeStr(
const CHAR* ansiStr,
WCHAR* unicodeStr,
INT unicodeSize
)
{
return MultiByteToWideChar(CP_ACP, 0, ansiStr, -1, unicodeStr,
unicodeSize) > 0;
}
class UnicodeStrFromAnsi
{
public:
UnicodeStrFromAnsi(const CHAR* ansiStr)
{
if (ansiStr == NULL)
{
valid = TRUE;
unicodeStr = NULL;
}
else
{
// NOTE: we only handle strings with length < MAX_PATH.
valid = AnsiToUnicodeStr(ansiStr, buf, MAX_PATH);
unicodeStr = valid ? buf : NULL;
}
}
BOOL IsValid() const
{
return valid;
}
operator WCHAR*()
{
return unicodeStr;
}
private:
BOOL valid;
WCHAR* unicodeStr;
WCHAR buf[MAX_PATH];
};
//
// Create thumbnails for the specified list of files
//
VOID
DrawThumbnails(
const RECT rect,
HDC hdc
)
{
if ( (rect.bottom - rect.top <= 0) || (rect.right - rect.left <= 0) )
{
return;
}
// Figure out how many rows and columns we need to divide in order to put
// "g_iTotalNumOfImages" images within the fixed size "rect"
// Basically "iNumColumns" * "iNumRows" should >= "g_iTotalNumOfImages"
int iWindowWidth = rect.right - rect.left;
int iWindowHeight = rect.bottom - rect.top;
int iSum = 1;
while ( ((iWindowWidth / iSum) * (iWindowHeight / iSum))
>= g_iTotalNumOfImages )
{
iSum++;
}
iSum--;
int iNumColumns = iWindowWidth / iSum;
int iNumRows = iWindowHeight / iSum;
lastS = iSum; // Reset the global
numX = iNumColumns; // Reset the global
Graphics* pGraphics = new Graphics(g_hwndMain);
int x = 0;
int y = 0;
for ( int i = 0; i < g_iTotalNumOfImages; i++ )
{
if ( NULL == g_ppThumbImages[i] )
{
// Bad image. But we still leave the position for it so that it can
// be easily noticed
x++;
if (x >= iNumColumns)
{
x = 0;
y++;
}
continue;
}
// Copy thumbnail bitmap image data to offscreen memory DC
int tx;
int ty;
SizeF size;
g_ppThumbImages[i]->GetPhysicalDimension(&size);
float dAspect = size.Width / size.Height;
RECT r;
if ( dAspect > 1 )
{
tx = iSum;
ty = (int)(iSum / dAspect);
int d = (iSum - ty) / 2;
r.left = x * iSum;
r.top = y * iSum + d;
r.right = x * iSum + tx;
r.bottom = y * iSum + ty + d;
}
else
{
ty = iSum;
tx = (int)(iSum * dAspect);
int d = (iSum - tx) / 2;
r.left = x * iSum + d;
r.top = y * iSum;
r.right = x * iSum + tx + d;
r.bottom = y * iSum + ty;
}
if ( g_fHighQualityThumb == FALSE )
{
Rect dstRect(r.left, r.top, tx, ty);
pGraphics->DrawImage(g_ppThumbImages[i],
dstRect,
0,
0,
(UINT)g_ppThumbImages[i]->GetWidth(),
(UINT)g_ppThumbImages[i]->GetHeight(),
UnitPixel,
NULL,
NULL,
NULL);
}
else
{
// Generate high quality thumbnail based on required size
Bitmap* dstBmp = new Bitmap(tx, ty, PixelFormat32bppPARGB);
Graphics* gdst = new Graphics(dstBmp);
// Ask the source image for it's size.
int width = g_ppThumbImages[i]->GetWidth();
int height = g_ppThumbImages[i]->GetHeight();
// Compute the optimal scale factor without changing the aspect ratio
float scalex = (float)width / tx;
float scaley = (float)height / ty;
float scale = min(scalex, scaley);
Rect dstRect(0, 0, tx, ty);
// Set the resampling quality to the bicubic filter
gdst->SetInterpolationMode(InterpolationModeHighQualityBicubic);
// Set the compositing quality to copy source pixels rather than
// alpha blending. This will preserve any alpha in the source image.
gdst->SetCompositingMode(CompositingModeSourceCopy);
ImageAttributes imgAttr;
imgAttr.SetWrapMode(WrapModeTileFlipXY);
// Draw the source image onto the destination with the correct scale
// and quality settings.
GpStatus status = gdst->DrawImage(g_ppThumbImages[i],
dstRect,
0,
0,
INT((tx * scale) + 0.5),
INT((ty * scale) + 0.5),
UnitPixel,
&imgAttr);
if( status != Ok )
{
printf("Error drawing the image\n");
continue;
}
// Draw the result onto the screen
Rect drawDstRect(r.left, r.top, tx, ty);
pGraphics->DrawImage(dstBmp,
drawDstRect,
0,
0,
(UINT)dstBmp->GetWidth(),
(UINT)dstBmp->GetHeight(),
UnitPixel,
NULL,
NULL,
NULL);
delete dstBmp;
delete gdst;
}
x++;
if (x >= iNumColumns)
{
x = 0;
y++;
}
}// Loop through all the thumbnail images to draw
delete pGraphics;
}
//
// Handle window repaint event
//
VOID
DoPaint(
HWND hwnd
)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
const RECT r = {rect.left, rect.top, rect.right, rect.bottom};
DrawThumbnails(r, hdc);
EndPaint(hwnd, &ps);
}// DoPaint()
//
// Window callback procedure
//
LRESULT CALLBACK
MyWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
int index=0;
switch (uMsg)
{
case WM_PAINT:
DoPaint(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_LBUTTONDBLCLK:
if (lastS == 0)
{
lastS = 1;
}
index = (HIWORD(lParam) / lastS) * numX + (LOWORD(lParam) / lastS);
if (index < g_iNumFileNames)
{
char cmdLine[MAX_FILENAME];
strcpy(cmdLine, "frametest ");
strcat(cmdLine, g_ppcInputFilenames[index]);
WinExec(cmdLine, SW_SHOWNORMAL);
}
break;
case WM_SIZE:
SendMessage(g_hwndMain, WM_ERASEBKGND, WPARAM(GetDC(g_hwndMain)), NULL);
InvalidateRect(g_hwndMain, NULL, FALSE);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//
// Create main application window
//
#define MYWNDCLASSNAME "ThumbTst"
VOID
CreateMainWindow(
VOID
)
{
// Use a hatch brush as background so that we can get transparent info
// from the source image
HBRUSH hBrush = CreateHatchBrush(HS_HORIZONTAL,
RGB(0, 200, 0));
// Register window class
WNDCLASS wndClass =
{
CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
MyWindowProc,
0,
0,
g_hAppInstance,
LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW),
hBrush,
NULL,
MYWNDCLASSNAME
};
RegisterClass(&wndClass);
// Calculate default window size
INT iWidth = g_ThumbRect.right + 2 * GetSystemMetrics(SM_CXFRAME);
INT iHeight = g_ThumbRect.bottom
+ 2 * GetSystemMetrics(SM_CYFRAME)
+ GetSystemMetrics(SM_CYCAPTION);
// Create application window
g_hwndMain = CreateWindow(MYWNDCLASSNAME,
MYWNDCLASSNAME,
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
iWidth,
iHeight,
NULL,
NULL,
g_hAppInstance,
NULL);
}// CreateMainWindow()
void
DisplayImageInfo(
Image* pImage
)
{
UINT uiImagePixelFormat = pImage->GetPixelFormat();
UINT uiImageFlags = pImage->GetFlags();
VERBOSE(("Width = %d\n", pImage->GetWidth()));
VERBOSE(("Width = %d\n", pImage->GetHeight()));
// Pixel format
switch ( uiImagePixelFormat )
{
case PixelFormat1bppIndexed:
VERBOSE(("Color depth: 1 BPP INDEXED\n"));
break;
case PixelFormat4bppIndexed:
VERBOSE(("Color depth: 4 BPP INDEXED\n"));
break;
case PixelFormat8bppIndexed:
VERBOSE(("Color depth: 8 BPP INDEXED\n"));
break;
case PixelFormat16bppGrayScale:
VERBOSE(("Color depth: 16 BPP GRAY SCALE\n"));
break;
case PixelFormat16bppRGB555:
VERBOSE(("Color depth: 16 BPP RGB 555\n"));
break;
case PixelFormat16bppRGB565:
VERBOSE(("Color depth: 16 BPP RGB 565\n"));
break;
case PixelFormat16bppARGB1555:
VERBOSE(("Color depth: 16 BPP ARGB 1555\n"));
break;
case PixelFormat24bppRGB:
VERBOSE(("Color depth: 24 BPP RGB\n"));
break;
case PixelFormat32bppARGB:
VERBOSE(("Color depth: 32 BPP ARGB\n"));
break;
case PixelFormat32bppPARGB:
VERBOSE(("Color depth: 32 BPP PARGB\n"));
break;
case PixelFormat48bppRGB:
VERBOSE(("Color depth: 48 BPP PARGB\n"));
case PixelFormat64bppARGB:
VERBOSE(("Color depth: 64 BPP ARGB\n"));
break;
case PixelFormat64bppPARGB:
VERBOSE(("Color depth: 64 BPP PARGB\n"));
break;
default:
VERBOSE(("Unknown color depth\n"));
break;
}// Color format
// Physical dimension info
VERBOSE(("X DPI (dots per inch) = %f\n",pImage->GetHorizontalResolution()));
VERBOSE(("Y DPI (dots per inch) = %f\n",pImage->GetVerticalResolution()));
// Pixel size
if ( uiImageFlags & ImageFlagsHasRealPixelSize )
{
VERBOSE(("---The pixel size info is from the original image\n"));
}
else
{
VERBOSE(("---The pixel size info is NOT from the original image\n"));
}
// DPI info
if ( uiImageFlags & ImageFlagsHasRealPixelSize )
{
VERBOSE(("---The pixel size info is from the original image\n"));
}
else
{
VERBOSE(("---The pixel size info is NOT from the original image\n"));
}
// Transparency info
if ( uiImageFlags & ImageFlagsHasAlpha )
{
VERBOSE(("This image contains alpha pixels\n"));
if ( uiImageFlags & ImageFlagsHasTranslucent )
{
VERBOSE(("---It has non-0 and 1 alpha pixels (TRANSLUCENT)\n"));
}
}
else
{
VERBOSE(("This image does not contain alpha pixels\n"));
}
// Display color space
if ( uiImageFlags & ImageFlagsColorSpaceRGB )
{
VERBOSE(("This image is in RGB color space\n"));
}
else if ( uiImageFlags & ImageFlagsColorSpaceCMYK )
{
VERBOSE(("This image is in CMYK color space\n"));
}
else if ( uiImageFlags & ImageFlagsColorSpaceGRAY )
{
VERBOSE(("This image is a gray scale image\n"));
}
else if ( uiImageFlags & ImageFlagsColorSpaceYCCK )
{
VERBOSE(("This image is in YCCK color space\n"));
}
else if ( uiImageFlags & ImageFlagsColorSpaceYCBCR )
{
VERBOSE(("This image is in YCBCR color space\n"));
}
}// DisplayImageInfo()
//
// Create thumbnails for the specified list of files
//
VOID
CreateThumbnails(
CHAR** ppcFilenames
)
{
// Generate thumbnails
UINT uiTimer;
if ( g_fVerbose == TRUE )
{
uiTimer = GetTickCount();
}
g_ppThumbImages = new Image*[g_iTotalNumOfImages];
Image* pSrcImage = NULL;
// Loop through all the images and generate thumbnail
for ( int i = 0; i < g_iTotalNumOfImages; i++ )
{
g_ppThumbImages[i] = NULL;
// Get a Unicode file name
UnicodeStrFromAnsi namestr(ppcFilenames[i]);
VERBOSE(("Loading - %s\n", ppcFilenames[i]));
if ( namestr.IsValid() == FALSE )
{
VERBOSE(("Couldn't open image file: %s\n", ppcFilenames[i]));
continue;
}
else if ( g_fHighQualityThumb == FALSE )
{
pSrcImage = new Image(namestr, TRUE);
if ( (pSrcImage == NULL) || (pSrcImage->GetLastStatus() != Ok) )
{
VERBOSE(("Couldn't open image file: %s\n", ppcFilenames[i]));
if ( pSrcImage != NULL )
{
delete pSrcImage;
pSrcImage = NULL;
}
continue;
}
if ( g_fVerbose == TRUE )
{
DisplayImageInfo(pSrcImage);
}
// Get build in thumbnail image if there is one. Otherwise, GDI+
// will generate one for us
g_ppThumbImages[i] = pSrcImage->GetThumbnailImage(0, 0, NULL, NULL);
delete pSrcImage;
pSrcImage = NULL;
}
else
{
// High quality thumbnail images. We don't generate it here
g_ppThumbImages[i] = new Image(namestr, TRUE);
if ( g_ppThumbImages[i] == NULL )
{
VERBOSE(("Couldn't open image file: %s\n", ppcFilenames[i]));
continue;
}
if ( g_fVerbose == TRUE )
{
DisplayImageInfo(g_ppThumbImages[i]);
}
}
}// Loop through all the images
if ( g_fVerbose == TRUE )
{
uiTimer = GetTickCount() - uiTimer;
VERBOSE(("Generate %d thumbnails in %dmsec\n", g_iTotalNumOfImages,
uiTimer));
}
}// CreateThumbnails
void
USAGE()
{
printf("******************************************************\n");
printf("Usage: thumbtst [-?] [-v] ImageFileNames\n");
printf("-v Verbose image information output\n");
printf("-h Generate higher quality thumbnail\n");
printf("-? Print this usage message\n");
printf("ImageFileNames Files to be opened\n\n");
printf("Sample usage:\n");
char myChar = '\\';
printf(" thumbtst.exe c:%cpublic%c*.jpg\n", myChar, myChar);
}// USAGE()
char html_header[1024] = "<html>\n<head>\n <title>My Fun Photo Album</title>\n</head>\n</html>\0";
void
OutputHTML()
{
FILE* hFile = fopen("mytest.html", "w");
if ( hFile == NULL )
{
return;
}
fprintf(hFile, "%s", html_header);
fclose(hFile);
}// OutputHTML()
void
ValidateArguments(
int argc,
char* argv[]
)
{
g_pcProgramName = *argv++;
argc--;
g_hAppInstance = GetModuleHandle(NULL);
while ( argc > 0 )
{
if ( strcmp(*argv, "-v") == 0 )
{
argc--;
argv++;
g_fVerbose = TRUE;
}
else if ( strcmp(*argv, "-h") == 0 )
{
argc--;
argv++;
g_fHighQualityThumb = TRUE;
}
else if ( strcmp(*argv, "-?") == 0 )
{
USAGE();
exit(1);
}
else
{
// Get the pointer to image file list
g_ppcInputFilenames = argv;
g_iNumFileNames = argc;
// Total number of images
// Note: if you do "thumbtst.exe c:\temp\*.jpg", this argc is
// actually the total number of images in that dir. While in argv,
// it points to each image under that dir
g_iTotalNumOfImages = argc;
return;
}
}// while (argc > 0 )
if ( argc == 0 )
{
USAGE();
exit(1);
}
return;
}// ValidateArguments()
//
// Main program entrypoint
//
INT _cdecl
main(
INT argc,
CHAR** argv
)
{
ValidateArguments(argc, argv);
g_ThumbRect.left = 0;
g_ThumbRect.top = 0;
g_ThumbRect.right = k_DefaultWidth;
g_ThumbRect.bottom = k_DefaultHeight;
// Generate thumbnail images and store it in g_ppThumbImages
CreateThumbnails(g_ppcInputFilenames);
// Create the main application window
CreateMainWindow();
// OutputHTML();
// Main message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Clean up
// Note: above while loop won't finish until we don't have any messages
for ( int i = 0; i < g_iTotalNumOfImages; i++ )
{
if ( NULL != g_ppThumbImages[i] )
{
delete g_ppThumbImages[i];
}
}
delete [] g_ppThumbImages;
return (INT)(msg.wParam);
}// main()