// // Simple test program for imaging library // #include #include #include #include #include #include #include #include #include #include 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] = "\n\n My Fun Photo Album\n\n\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()