Leaked source code of windows server 2003
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.

1467 lines
30 KiB

  1. //
  2. // Simple test program for imaging library
  3. //
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <windows.h>
  8. #include <objbase.h>
  9. #include <urlmon.h>
  10. #include <commdlg.h>
  11. #include <imaging.h>
  12. #include <initguid.h>
  13. #include <imgguids.h>
  14. #include "rsrc.h"
  15. CHAR* programName; // program name
  16. HINSTANCE appInstance; // handle to the application instance
  17. HWND hwndMain; // handle to application's main window
  18. IImagingFactory* imgfact; // pointer to IImageingFactory object
  19. IImage* curImage; // pointer to IImage object
  20. CHAR curFilename[MAX_PATH]; // current image filename
  21. INT scaleMethod = IDM_SCALE_NEIGHBOR;
  22. //
  23. // Display an error message dialog
  24. //
  25. BOOL
  26. CheckHRESULT(
  27. HRESULT hr,
  28. INT line
  29. )
  30. {
  31. if (SUCCEEDED(hr))
  32. return TRUE;
  33. CHAR buf[1024];
  34. sprintf(buf, "Error on line %d: 0x%x\n", line, hr);
  35. MessageBoxA(hwndMain, buf, programName, MB_OK);
  36. return FALSE;
  37. }
  38. #define CHECKHR(hr) CheckHRESULT(hr, __LINE__)
  39. #define LASTWIN32HRESULT HRESULT_FROM_WIN32(GetLastError())
  40. #if DBG
  41. #define VERBOSE(args) printf args
  42. #else
  43. #define VERBOSE(args)
  44. #endif
  45. //
  46. // Helper class to convert ANSI strings to Unicode strings
  47. //
  48. inline BOOL
  49. UnicodeToAnsiStr(
  50. const WCHAR* unicodeStr,
  51. CHAR* ansiStr,
  52. INT ansiSize
  53. )
  54. {
  55. return WideCharToMultiByte(
  56. CP_ACP,
  57. 0,
  58. unicodeStr,
  59. -1,
  60. ansiStr,
  61. ansiSize,
  62. NULL,
  63. NULL) > 0;
  64. }
  65. inline BOOL
  66. AnsiToUnicodeStr(
  67. const CHAR* ansiStr,
  68. WCHAR* unicodeStr,
  69. INT unicodeSize
  70. )
  71. {
  72. return MultiByteToWideChar(
  73. CP_ACP,
  74. 0,
  75. ansiStr,
  76. -1,
  77. unicodeStr,
  78. unicodeSize) > 0;
  79. }
  80. class UnicodeStrFromAnsi
  81. {
  82. public:
  83. UnicodeStrFromAnsi(const CHAR* ansiStr)
  84. {
  85. if (ansiStr == NULL)
  86. {
  87. valid = TRUE;
  88. unicodeStr = NULL;
  89. }
  90. else
  91. {
  92. // NOTE: we only handle strings with length < MAX_PATH.
  93. valid = AnsiToUnicodeStr(ansiStr, buf, MAX_PATH);
  94. unicodeStr = valid ? buf : NULL;
  95. }
  96. }
  97. BOOL IsValid() const
  98. {
  99. return valid;
  100. }
  101. operator WCHAR*()
  102. {
  103. return unicodeStr;
  104. }
  105. private:
  106. BOOL valid;
  107. WCHAR* unicodeStr;
  108. WCHAR buf[MAX_PATH];
  109. };
  110. //
  111. // Get scale method strings and interpolation hints
  112. //
  113. const CHAR*
  114. GetScaleMethodStr()
  115. {
  116. switch (scaleMethod)
  117. {
  118. case IDM_SCALE_GDI: return "GDI";
  119. case IDM_SCALE_GDIHT: return "GDI + Halftone";
  120. case IDM_SCALE_NEIGHBOR: return "Nearest Neighbor";
  121. case IDM_SCALE_BILINEAR: return "Bilinear";
  122. case IDM_SCALE_AVERAGING: return "Averaging";
  123. case IDM_SCALE_BICUBIC: return "Bicubic";
  124. default: return "Unknown";
  125. }
  126. }
  127. InterpolationHint
  128. GetScaleMethodInterp()
  129. {
  130. switch (scaleMethod)
  131. {
  132. case IDM_SCALE_BILINEAR: return INTERP_BILINEAR;
  133. case IDM_SCALE_AVERAGING: return INTERP_AVERAGING;
  134. case IDM_SCALE_BICUBIC: return INTERP_BICUBIC;
  135. case IDM_SCALE_NEIGHBOR: return INTERP_NEAREST_NEIGHBOR;
  136. case IDM_SCALE_GDI:
  137. case IDM_SCALE_GDIHT:
  138. default: return INTERP_DEFAULT;
  139. }
  140. }
  141. //
  142. // Get pixel format strings
  143. //
  144. const CHAR*
  145. GetPixelFormatStr(
  146. PixelFormatID pixfmt
  147. )
  148. {
  149. switch (pixfmt)
  150. {
  151. case PIXFMT_1BPP_INDEXED: return "1bpp indexed";
  152. case PIXFMT_4BPP_INDEXED: return "4bpp indexed";
  153. case PIXFMT_8BPP_INDEXED: return "8bpp indexed";
  154. case PIXFMT_16BPP_GRAYSCALE: return "16bpp grayscale";
  155. case PIXFMT_16BPP_RGB555: return "16bpp RGB 5-5-5";
  156. case PIXFMT_16BPP_RGB565: return "16bpp RGB 5-6-5";
  157. case PIXFMT_16BPP_ARGB1555: return "16bpp ARGB 1-5-5-5";
  158. case PIXFMT_24BPP_RGB: return "24bpp RGB";
  159. case PIXFMT_32BPP_RGB: return "32bpp RGB";
  160. case PIXFMT_32BPP_ARGB: return "32bpp ARGB";
  161. case PIXFMT_32BPP_PARGB: return "32bpp premultiplied ARGB";
  162. case PIXFMT_48BPP_RGB: return "48bpp RGB";
  163. case PIXFMT_64BPP_ARGB: return "64bpp ARGB";
  164. case PIXFMT_64BPP_PARGB: return "64bpp premultiplied ARGB";
  165. case PIXFMT_UNDEFINED:
  166. default: return "Unknown";
  167. }
  168. }
  169. //
  170. // Force a refresh of the image window
  171. //
  172. VOID RefreshImageDisplay()
  173. {
  174. InvalidateRect(hwndMain, NULL, FALSE);
  175. // Update window title
  176. CHAR title[2*MAX_PATH];
  177. CHAR* p = title;
  178. strcpy(p, curFilename);
  179. p += strlen(p);
  180. HRESULT hr;
  181. SIZE size;
  182. IBitmapImage* bmp;
  183. hr = curImage->QueryInterface(IID_IBitmapImage, (VOID**) &bmp);
  184. if (FAILED(hr))
  185. {
  186. // Decoded image
  187. hr = curImage->GetPhysicalDimension(&size);
  188. if (SUCCEEDED(hr))
  189. {
  190. sprintf(p, ", Dimension: %0.2fx%0.2fmm", size.cx / 100.0, size.cy / 100.0);
  191. p += strlen(p);
  192. }
  193. }
  194. else
  195. {
  196. // In-memory bitmap image
  197. hr = bmp->GetSize(&size);
  198. if (CHECKHR(hr))
  199. {
  200. sprintf(p, ", Size: %dx%d", size.cx, size.cy);
  201. p += strlen(p);
  202. }
  203. PixelFormatID pixfmt;
  204. hr = bmp->GetPixelFormatID(&pixfmt);
  205. if (CHECKHR(hr))
  206. {
  207. sprintf(p, ", Pixel Format: %s", GetPixelFormatStr(pixfmt));
  208. p += strlen(p);
  209. }
  210. bmp->Release();
  211. }
  212. sprintf(p, ", Scale Method: %s", GetScaleMethodStr());
  213. p += strlen(p);
  214. SetWindowText(hwndMain, title);
  215. }
  216. //
  217. // Set the current image
  218. //
  219. VOID
  220. SetCurrentImage(
  221. IUnknown* unk,
  222. const CHAR* filename = NULL
  223. )
  224. {
  225. IImage* image;
  226. if (filename != NULL)
  227. {
  228. // Decoded image
  229. image = (IImage*) unk;
  230. strcpy(curFilename, filename);
  231. }
  232. else
  233. {
  234. // In-memory bitmap image
  235. HRESULT hr;
  236. hr = unk->QueryInterface(IID_IImage, (VOID**) &image);
  237. unk->Release();
  238. if (!CHECKHR(hr))
  239. return;
  240. strcpy(curFilename, "In-memory Bitmap");
  241. }
  242. if (curImage)
  243. curImage->Release();
  244. curImage = image;
  245. RefreshImageDisplay();
  246. }
  247. //
  248. // Resize the window so it fits the image
  249. //
  250. #define MINWINWIDTH 200
  251. #define MINWINHEIGHT 100
  252. #define MAXWINWIDTH 1024
  253. #define MAXWINHEIGHT 768
  254. VOID
  255. DoSizeWindowToFit(
  256. HWND hwnd,
  257. BOOL strict = FALSE
  258. )
  259. {
  260. HRESULT hr;
  261. IBitmapImage* bmp;
  262. SIZE size;
  263. // Check if the current image is a bitmap image
  264. // in that case, we'll get the pixel dimension
  265. hr = curImage->QueryInterface(IID_IBitmapImage, (VOID**) &bmp);
  266. if (SUCCEEDED(hr))
  267. {
  268. hr = bmp->GetSize(&size);
  269. bmp->Release();
  270. }
  271. // Otherwise, try to get device-independent image dimension
  272. if (FAILED(hr))
  273. {
  274. hr = curImage->GetPhysicalDimension(&size);
  275. if (FAILED(hr))
  276. return;
  277. size.cx = (INT) (size.cx * 96.0 / 2540.0 + 0.5);
  278. size.cy = (INT) (size.cy * 96.0 / 2540.0 + 0.5);
  279. }
  280. if (SUCCEEDED(hr))
  281. {
  282. // Figure out window border dimensions
  283. RECT r1, r2;
  284. INT w, h;
  285. w = size.cx;
  286. h = size.cy;
  287. if (!strict)
  288. {
  289. if (w < MINWINWIDTH)
  290. w = MINWINWIDTH;
  291. else if (w > MAXWINWIDTH)
  292. w = MAXWINWIDTH;
  293. if (h < MINWINHEIGHT)
  294. h = MINWINHEIGHT;
  295. else if (h > MAXWINHEIGHT)
  296. h = MAXWINHEIGHT;
  297. }
  298. GetWindowRect(hwnd, &r1);
  299. GetClientRect(hwnd, &r2);
  300. w += (r1.right - r1.left) - (r2.right - r2.left);
  301. h += (r1.bottom - r1.top) - (r2.bottom - r2.top);
  302. // Resize the window
  303. do
  304. {
  305. SetWindowPos(
  306. hwnd,
  307. NULL,
  308. 0, 0,
  309. w, h,
  310. SWP_NOMOVE | SWP_NOZORDER);
  311. GetClientRect(hwnd, &r2);
  312. h += GetSystemMetrics(SM_CYMENU);
  313. }
  314. while (r2.bottom == 0);
  315. }
  316. }
  317. //
  318. // Convert current image to a bitmap image
  319. //
  320. IBitmapImage*
  321. ConvertImageToBitmap(
  322. IImage* image,
  323. INT width = 0,
  324. INT height = 0,
  325. PixelFormatID pixfmt = PIXFMT_DONTCARE,
  326. InterpolationHint hint = INTERP_DEFAULT
  327. )
  328. {
  329. if (!image)
  330. return NULL;
  331. HRESULT hr;
  332. IBitmapImage* bmp;
  333. hr = image->QueryInterface(IID_IBitmapImage, (VOID**) &bmp);
  334. if (SUCCEEDED(hr))
  335. {
  336. SIZE size;
  337. PixelFormatID fmt;
  338. // Current image is already a bitmap image and
  339. // its dimension and pixel format are already as expected
  340. hr = bmp->GetSize(&size);
  341. if (!CHECKHR(hr))
  342. return NULL;
  343. hr = bmp->GetPixelFormatID(&fmt);
  344. if (!CHECKHR(hr))
  345. return NULL;
  346. if ((width == 0 || size.cx == width) &&
  347. (height == 0 || size.cy == height) &&
  348. (pixfmt == PIXFMT_DONTCARE || pixfmt == fmt))
  349. {
  350. return bmp;
  351. }
  352. bmp->Release();
  353. }
  354. // Convert the current image to a bitmap image
  355. if (width == 0 && height == 0)
  356. {
  357. ImageInfo imageInfo;
  358. hr = image->GetImageInfo(&imageInfo);
  359. // If the source image is scalable, then compute
  360. // the appropriate pixel dimension for the bitmap
  361. if (SUCCEEDED(hr) && (imageInfo.Flags & IMGFLAG_SCALABLE))
  362. {
  363. width = (INT) (96.0 * imageInfo.Width / imageInfo.Xdpi + 0.5);
  364. height = (INT) (96.0 * imageInfo.Height / imageInfo.Ydpi + 0.5);
  365. }
  366. }
  367. hr = imgfact->CreateBitmapFromImage(
  368. image,
  369. width,
  370. height,
  371. pixfmt,
  372. hint,
  373. &bmp);
  374. return SUCCEEDED(hr) ? bmp : NULL;
  375. }
  376. //
  377. // Create an image object from a file
  378. //
  379. VOID
  380. OpenImageFile(
  381. const CHAR* filename
  382. )
  383. {
  384. HRESULT hr;
  385. IImage* image;
  386. IStream* stream;
  387. static BOOL useUrlMon = FALSE;
  388. if (useUrlMon)
  389. {
  390. // Use URLMON.DLL to turn file into stream
  391. CHAR fullpath[MAX_PATH];
  392. CHAR* p;
  393. if (!GetFullPathName(filename, MAX_PATH, fullpath, &p))
  394. return;
  395. hr = URLOpenBlockingStreamA(NULL, fullpath, &stream, 0, NULL);
  396. if (!CHECKHR(hr))
  397. return;
  398. hr = imgfact->CreateImageFromStream(stream, &image);
  399. stream->Release();
  400. }
  401. else
  402. {
  403. // Use filename directly
  404. UnicodeStrFromAnsi namestr(filename);
  405. if (namestr.IsValid())
  406. hr = imgfact->CreateImageFromFile(namestr, &image);
  407. else
  408. hr = E_FAIL;
  409. }
  410. // Set the new image as the current image
  411. if (CHECKHR(hr))
  412. {
  413. SetCurrentImage(image, filename);
  414. DoSizeWindowToFit(hwndMain);
  415. }
  416. }
  417. //
  418. // Save the current image to a file
  419. //
  420. VOID
  421. SaveImageFile(
  422. const CHAR* filename,
  423. const CLSID* clsid
  424. )
  425. {
  426. if (!curImage)
  427. return;
  428. // Create an encoder object
  429. HRESULT hr;
  430. IImageEncoder* encoder;
  431. UnicodeStrFromAnsi namestr(filename);
  432. if (namestr.IsValid())
  433. hr = imgfact->CreateImageEncoderToFile(clsid, namestr, &encoder);
  434. else
  435. hr = E_FAIL;
  436. if (!CHECKHR(hr))
  437. return;
  438. // Get an IImageSink interface to the encoder
  439. IImageSink* sink;
  440. hr = encoder->GetEncodeSink(&sink);
  441. #if defined(ROTATION_TEST)
  442. // Rotation test
  443. EncoderParams* pMyEncoderParams;
  444. pMyEncoderParams = (EncoderParams*)malloc
  445. ( sizeof(EncoderParams)
  446. + sizeof(EncoderParam));
  447. pMyEncoderParams->Params[0].paramGuid = ENCODER_ROTATION;
  448. pMyEncoderParams->Params[0].Value = "90";
  449. pMyEncoderParams->Count = 1;
  450. hr = encoder->SetEncoderParam(pMyEncoderParams);
  451. free(pMyEncoderParams);
  452. #endif
  453. if (CHECKHR(hr))
  454. {
  455. hr = curImage->PushIntoSink(sink);
  456. CHECKHR(hr);
  457. sink->Release();
  458. }
  459. encoder->TerminateEncoder();
  460. encoder->Release();
  461. }
  462. //
  463. // Handle window repaint event
  464. //
  465. VOID
  466. DoPaint(
  467. HWND hwnd
  468. )
  469. {
  470. HDC hdc;
  471. PAINTSTRUCT ps;
  472. RECT rect;
  473. DWORD timer;
  474. HRESULT hr = E_FAIL;
  475. hdc = BeginPaint(hwnd, &ps);
  476. GetClientRect(hwnd, &rect);
  477. if (scaleMethod == IDM_SCALE_GDIHT)
  478. SetStretchBltMode(hdc, HALFTONE);
  479. else
  480. SetStretchBltMode(hdc, COLORONCOLOR);
  481. VERBOSE(("Scale method: %d, ", scaleMethod));
  482. timer = GetTickCount();
  483. if (scaleMethod == IDM_SCALE_GDI ||
  484. scaleMethod == IDM_SCALE_GDIHT)
  485. {
  486. hr = curImage->Draw(hdc, &rect, NULL);
  487. VERBOSE(("GDI time: %dms\n", GetTickCount() - timer));
  488. }
  489. else
  490. {
  491. IBitmapImage* bmp;
  492. bmp = ConvertImageToBitmap(
  493. curImage,
  494. rect.right,
  495. rect.bottom,
  496. PIXFMT_DONTCARE,
  497. GetScaleMethodInterp());
  498. if (!bmp)
  499. goto endPaint;
  500. VERBOSE(("Stretch time: %dms, ", GetTickCount() - timer));
  501. IImage* image;
  502. hr = bmp->QueryInterface(IID_IImage, (VOID**) &image);
  503. bmp->Release();
  504. if (FAILED(hr))
  505. goto endPaint;
  506. timer = GetTickCount();
  507. hr = image->Draw(hdc, &rect, NULL);
  508. VERBOSE(("GDI time: %dms\n", GetTickCount() - timer));
  509. image->Release();
  510. }
  511. endPaint:
  512. if (FAILED(hr))
  513. FillRect(hdc, &rect, (HBRUSH) GetStockObject(BLACK_BRUSH));
  514. EndPaint(hwnd, &ps);
  515. }
  516. //
  517. // Convert the current image to a bitmap
  518. //
  519. VOID
  520. DoConvertToBitmap(
  521. HWND hwnd,
  522. INT menuCmd
  523. )
  524. {
  525. // Map menu selection to its corresponding pixel format
  526. PixelFormatID pixfmt;
  527. switch (menuCmd)
  528. {
  529. case IDM_CONVERT_RGB555:
  530. pixfmt = PIXFMT_16BPP_RGB555;
  531. break;
  532. case IDM_CONVERT_RGB565:
  533. pixfmt = PIXFMT_16BPP_RGB565;
  534. break;
  535. case IDM_CONVERT_RGB24:
  536. pixfmt = PIXFMT_24BPP_RGB;
  537. break;
  538. case IDM_CONVERT_RGB32:
  539. pixfmt = PIXFMT_32BPP_RGB;
  540. break;
  541. case IDM_CONVERT_ARGB:
  542. default:
  543. pixfmt = PIXFMT_32BPP_ARGB;
  544. break;
  545. }
  546. // Convert the current image to a bitmap image
  547. IBitmapImage* bmp = ConvertImageToBitmap(curImage, 0, 0, pixfmt);
  548. // Set the bitmap image as the current image
  549. if (bmp)
  550. SetCurrentImage(bmp);
  551. }
  552. //
  553. // Compose a file type filter string given an array of
  554. // ImageCodecInfo structures
  555. //
  556. #define SizeofWSTR(s) (sizeof(WCHAR) * (wcslen(s) + 1))
  557. #define SizeofSTR(s) (strlen(s) + 1)
  558. CHAR*
  559. MakeFilterFromCodecs(
  560. UINT count,
  561. const ImageCodecInfo* codecs,
  562. BOOL open
  563. )
  564. {
  565. static const CHAR allFiles[] = "All Files\0*.*\0";
  566. // Figure out the total size of the filter string
  567. UINT index, size;
  568. for (index=size=0; index < count; index++)
  569. {
  570. size += SizeofWSTR(codecs[index].FormatDescription) +
  571. SizeofWSTR(codecs[index].FilenameExtension);
  572. }
  573. if (open)
  574. size += sizeof(allFiles);
  575. size += sizeof(CHAR);
  576. // Allocate memory
  577. CHAR *filter = (CHAR*) malloc(size);
  578. CHAR* p = filter;
  579. const WCHAR* ws;
  580. if (!filter)
  581. return NULL;
  582. for (index=0; index < count; index++)
  583. {
  584. ws = codecs[index].FormatDescription;
  585. size = SizeofWSTR(ws);
  586. if (UnicodeToAnsiStr(ws, p, size))
  587. p += SizeofSTR(p);
  588. else
  589. break;
  590. ws = codecs[index].FilenameExtension;
  591. size = SizeofWSTR(ws);
  592. if (UnicodeToAnsiStr(ws, p, size))
  593. p += SizeofSTR(p);
  594. else
  595. break;
  596. }
  597. if (index < count)
  598. {
  599. free(filter);
  600. return NULL;
  601. }
  602. if (open)
  603. {
  604. size = sizeof(allFiles);
  605. memcpy(p, allFiles, size);
  606. p += size;
  607. }
  608. *((CHAR*) p) = '\0';
  609. return filter;
  610. }
  611. //
  612. // Open image file
  613. //
  614. VOID
  615. DoOpen(
  616. HWND hwnd
  617. )
  618. {
  619. OPENFILENAME ofn;
  620. CHAR filename[MAX_PATH];
  621. ZeroMemory(&ofn, sizeof(ofn));
  622. ofn.lStructSize = sizeof(ofn);
  623. ofn.hwndOwner = hwnd;
  624. ofn.hInstance = appInstance;
  625. ofn.lpstrFile = filename;
  626. ofn.nMaxFile = MAX_PATH;
  627. ofn.lpstrTitle = "Open Image File";
  628. ofn.lpstrInitialDir = ".";
  629. ofn.Flags = OFN_FILEMUSTEXIST;
  630. filename[0] = '\0';
  631. // Make up the file type filter string
  632. HRESULT hr;
  633. ImageCodecInfo* codecs;
  634. UINT count;
  635. hr = imgfact->GetInstalledDecoders(&count, &codecs);
  636. if (!CHECKHR(hr))
  637. return;
  638. CHAR* filter = MakeFilterFromCodecs(count, codecs, TRUE);
  639. if (codecs)
  640. CoTaskMemFree(codecs);
  641. if (!filter)
  642. {
  643. CHECKHR(LASTWIN32HRESULT);
  644. return;
  645. }
  646. ofn.lpstrFilter = filter;
  647. // Present the file/open dialog
  648. if (GetOpenFileName(&ofn))
  649. OpenImageFile(filename);
  650. free(filter);
  651. }
  652. //
  653. // Save image file
  654. //
  655. VOID
  656. DoSave(
  657. HWND hwnd
  658. )
  659. {
  660. OPENFILENAME ofn;
  661. CHAR filename[MAX_PATH];
  662. ZeroMemory(&ofn, sizeof(ofn));
  663. ofn.lStructSize = sizeof(ofn);
  664. ofn.hwndOwner = hwnd;
  665. ofn.hInstance = appInstance;
  666. ofn.lpstrFile = filename;
  667. ofn.nMaxFile = MAX_PATH;
  668. ofn.lpstrTitle = "Save Image File";
  669. ofn.lpstrInitialDir = ".";
  670. ofn.Flags = OFN_CREATEPROMPT | OFN_OVERWRITEPROMPT;
  671. filename[0] = '\0';
  672. // Make up the file type filter string
  673. HRESULT hr;
  674. ImageCodecInfo* codecs;
  675. UINT count;
  676. hr = imgfact->GetInstalledEncoders(&count, &codecs);
  677. if (!CHECKHR(hr))
  678. return;
  679. CHAR* filter = MakeFilterFromCodecs(count, codecs, FALSE);
  680. if (!filter)
  681. {
  682. CHECKHR(LASTWIN32HRESULT);
  683. }
  684. else
  685. {
  686. ofn.lpstrFilter = filter;
  687. // Present the file/save dialog
  688. if (GetSaveFileName(&ofn))
  689. {
  690. UINT index = ofn.nFilterIndex;
  691. if (index == 0 || index > count)
  692. index = 0;
  693. else
  694. index--;
  695. SaveImageFile(filename, &codecs[index].Clsid);
  696. }
  697. free(filter);
  698. }
  699. CoTaskMemFree(codecs);
  700. }
  701. //
  702. // Crop the image
  703. //
  704. // NOTE: We're not spending time here to do a fancy UI.
  705. // So we'll just inset the image by 5 pixels each time.
  706. //
  707. VOID
  708. DoCrop(
  709. HWND hwnd
  710. )
  711. {
  712. IBitmapImage* bmp;
  713. if (bmp = ConvertImageToBitmap(curImage))
  714. {
  715. HRESULT hr;
  716. IBasicBitmapOps* bmpops = NULL;
  717. SIZE size;
  718. hr = bmp->QueryInterface(IID_IBasicBitmapOps, (VOID**) &bmpops);
  719. if (CHECKHR(hr))
  720. hr = bmp->GetSize(&size);
  721. if (CHECKHR(hr))
  722. {
  723. RECT r = { 5, 5, size.cx - 5, size.cy - 5 };
  724. IBitmapImage* newbmp;
  725. hr = bmpops->Clone(&r, &newbmp, TRUE);
  726. if (CHECKHR(hr))
  727. SetCurrentImage(newbmp);
  728. }
  729. if (bmp) bmp->Release();
  730. if (bmpops) bmpops->Release();
  731. }
  732. }
  733. //
  734. // Resize the image to the current window size, using bilinear scaling
  735. //
  736. VOID
  737. DoResize(
  738. HWND hwnd
  739. )
  740. {
  741. RECT rect;
  742. HRESULT hr;
  743. IBitmapImage* bmp;
  744. GetClientRect(hwnd, &rect);
  745. bmp = ConvertImageToBitmap(
  746. curImage,
  747. rect.right,
  748. rect.bottom,
  749. PIXFMT_DONTCARE,
  750. INTERP_BILINEAR);
  751. if (bmp)
  752. SetCurrentImage(bmp);
  753. }
  754. //
  755. // Flip or rotate the image
  756. //
  757. VOID
  758. DoFlipRotate(
  759. HWND hwnd,
  760. INT menuCmd
  761. )
  762. {
  763. IBitmapImage* bmp;
  764. IBitmapImage* newbmp;
  765. IBasicBitmapOps* bmpops;
  766. HRESULT hr;
  767. bmp = ConvertImageToBitmap(curImage);
  768. if (!bmp)
  769. return;
  770. hr = bmp->QueryInterface(IID_IBasicBitmapOps, (VOID**) &bmpops);
  771. if (CHECKHR(hr))
  772. {
  773. switch (menuCmd)
  774. {
  775. case IDM_BMP_FLIPX:
  776. hr = bmpops->Flip(TRUE, FALSE, &newbmp);
  777. break;
  778. case IDM_BMP_FLIPY:
  779. hr = bmpops->Flip(FALSE, TRUE, &newbmp);
  780. break;
  781. case IDM_BMP_ROTATE90:
  782. hr = bmpops->Rotate(90, INTERP_DEFAULT, &newbmp);
  783. break;
  784. case IDM_BMP_ROTATE270:
  785. hr = bmpops->Rotate(270, INTERP_DEFAULT, &newbmp);
  786. break;
  787. }
  788. bmpops->Release();
  789. if (CHECKHR(hr))
  790. {
  791. SetCurrentImage(newbmp);
  792. if (menuCmd == IDM_BMP_ROTATE90 ||
  793. menuCmd == IDM_BMP_ROTATE270)
  794. {
  795. DoSizeWindowToFit(hwnd);
  796. }
  797. }
  798. }
  799. bmp->Release();
  800. }
  801. //
  802. // Perform point operation on the image
  803. //
  804. VOID
  805. DoPointOps(
  806. HWND hwnd,
  807. INT menuCmd
  808. )
  809. {
  810. IBitmapImage* bmp;
  811. IBitmapImage* newbmp;
  812. IBasicBitmapOps* bmpops;
  813. HRESULT hr;
  814. bmp = ConvertImageToBitmap(curImage);
  815. if (!bmp)
  816. return;
  817. hr = bmp->QueryInterface(IID_IBasicBitmapOps, (VOID**) &bmpops);
  818. if (CHECKHR(hr))
  819. {
  820. switch (menuCmd)
  821. {
  822. case IDM_BRIGHTEN:
  823. hr = bmpops->AdjustBrightness(0.1f);
  824. break;
  825. case IDM_DARKEN:
  826. hr = bmpops->AdjustBrightness(-0.1f);
  827. break;
  828. case IDM_INCCONTRAST:
  829. hr = bmpops->AdjustContrast(-0.1f, 1.1f);
  830. break;
  831. case IDM_DECCONTRAST:
  832. hr = bmpops->AdjustContrast(0.1f, 0.9f);
  833. break;
  834. case IDM_INCGAMMA:
  835. hr = bmpops->AdjustGamma(1.1f);
  836. break;
  837. case IDM_DECGAMMA:
  838. hr = bmpops->AdjustGamma(0.9f);
  839. break;
  840. }
  841. bmpops->Release();
  842. if (CHECKHR(hr))
  843. SetCurrentImage(bmp);
  844. }
  845. if (FAILED(hr))
  846. bmp->Release();
  847. }
  848. VOID
  849. DisplayProperties(
  850. IPropertySetStorage *propSetStg
  851. )
  852. {
  853. HRESULT hresult;
  854. IPropertyStorage *propStg;
  855. IEnumSTATPROPSTG *enumPS;
  856. hresult = propSetStg->Open(FMTID_ImageInformation, STGM_READ | STGM_SHARE_EXCLUSIVE, &propStg);
  857. if (FAILED(hresult))
  858. {
  859. //printf("DisplayProperties: failed to open propSetStg\n");
  860. return;
  861. }
  862. hresult = propStg->Enum(&enumPS);
  863. if (FAILED(hresult))
  864. {
  865. printf("DisplayProperties: failed to create enumerator\n");
  866. return;
  867. }
  868. hresult = enumPS->Reset();
  869. if (FAILED(hresult))
  870. {
  871. printf("DisplayProperties: failed to reset enumerator\n");
  872. return;
  873. }
  874. STATPROPSTG sps;
  875. while ((enumPS->Next(1, &sps, NULL)) == S_OK)
  876. {
  877. if (sps.lpwstrName)
  878. {
  879. wprintf(sps.lpwstrName);
  880. CoTaskMemFree(sps.lpwstrName);
  881. PROPSPEC propSpec[1];
  882. PROPVARIANT propVariant[1];
  883. propSpec[0].ulKind = PRSPEC_PROPID;
  884. propSpec[0].propid = sps.propid;
  885. hresult = propStg->ReadMultiple(1, propSpec, propVariant);
  886. if (FAILED(hresult))
  887. {
  888. printf("DisplayProperties: failed in ReadMultiple\n");
  889. }
  890. switch(propVariant[0].vt)
  891. {
  892. case VT_BSTR:
  893. wprintf(L" : %s\n", propVariant[0].bstrVal);
  894. break;
  895. case VT_I4:
  896. wprintf(L" : %d\n", propVariant[0].lVal);
  897. break;
  898. case VT_R8:
  899. wprintf(L" : %f\n", (FLOAT) propVariant[0].dblVal);
  900. break;
  901. default:
  902. wprintf(L"Unknown VT type\n");
  903. break;
  904. }
  905. PropVariantClear(&propVariant[0]);
  906. }
  907. }
  908. enumPS->Release();
  909. propStg->Release();
  910. }
  911. //
  912. // Handle menu commands
  913. //
  914. VOID
  915. DoMenuCommand(
  916. HWND hwnd,
  917. INT menuCmd
  918. )
  919. {
  920. switch (menuCmd)
  921. {
  922. case IDM_OPEN:
  923. DoOpen(hwnd);
  924. break;
  925. case IDM_SAVE:
  926. DoSave(hwnd);
  927. break;
  928. case IDM_QUIT:
  929. PostQuitMessage(0);
  930. break;
  931. case IDM_FIT_WINDOW:
  932. DoSizeWindowToFit(hwnd, TRUE);
  933. break;
  934. case IDM_CONVERT_RGB555:
  935. case IDM_CONVERT_RGB565:
  936. case IDM_CONVERT_RGB24:
  937. case IDM_CONVERT_RGB32:
  938. case IDM_CONVERT_ARGB:
  939. DoConvertToBitmap(hwnd, menuCmd);
  940. break;
  941. case IDM_SCALE_GDI:
  942. case IDM_SCALE_GDIHT:
  943. case IDM_SCALE_NEIGHBOR:
  944. case IDM_SCALE_BILINEAR:
  945. case IDM_SCALE_AVERAGING:
  946. case IDM_SCALE_BICUBIC:
  947. scaleMethod = menuCmd;
  948. RefreshImageDisplay();
  949. break;
  950. case IDM_BMP_CROP:
  951. DoCrop(hwnd);
  952. break;
  953. case IDM_BMP_RESIZE:
  954. DoResize(hwnd);
  955. break;
  956. case IDM_BMP_FLIPX:
  957. case IDM_BMP_FLIPY:
  958. case IDM_BMP_ROTATE90:
  959. case IDM_BMP_ROTATE270:
  960. DoFlipRotate(hwnd, menuCmd);
  961. break;
  962. case IDM_BRIGHTEN:
  963. case IDM_DARKEN:
  964. case IDM_INCCONTRAST:
  965. case IDM_DECCONTRAST:
  966. case IDM_INCGAMMA:
  967. case IDM_DECGAMMA:
  968. DoPointOps(hwnd, menuCmd);
  969. break;
  970. }
  971. }
  972. //
  973. // Window callback procedure
  974. //
  975. LRESULT CALLBACK
  976. MyWindowProc(
  977. HWND hwnd,
  978. UINT uMsg,
  979. WPARAM wParam,
  980. LPARAM lParam
  981. )
  982. {
  983. switch (uMsg)
  984. {
  985. case WM_COMMAND:
  986. DoMenuCommand(hwnd, LOWORD(wParam));
  987. break;
  988. case WM_PAINT:
  989. DoPaint(hwnd);
  990. break;
  991. case WM_DESTROY:
  992. PostQuitMessage(0);
  993. break;
  994. default:
  995. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  996. }
  997. return 0;
  998. }
  999. //
  1000. // Create main application window
  1001. //
  1002. #define MYWNDCLASSNAME "ImgTest"
  1003. VOID
  1004. CreateMainWindow(
  1005. VOID
  1006. )
  1007. {
  1008. HBRUSH hBrush = CreateSolidBrush(RGB(255, 250, 250));
  1009. //
  1010. // Register window class
  1011. //
  1012. WNDCLASS wndClass =
  1013. {
  1014. CS_HREDRAW|CS_VREDRAW,
  1015. MyWindowProc,
  1016. 0,
  1017. 0,
  1018. appInstance,
  1019. LoadIcon(NULL, IDI_APPLICATION),
  1020. LoadCursor(NULL, IDC_ARROW),
  1021. hBrush,
  1022. MAKEINTRESOURCE(IDR_MAINMENU),
  1023. MYWNDCLASSNAME
  1024. };
  1025. RegisterClass(&wndClass);
  1026. hwndMain = CreateWindow(
  1027. MYWNDCLASSNAME,
  1028. MYWNDCLASSNAME,
  1029. WS_OVERLAPPEDWINDOW,
  1030. CW_USEDEFAULT,
  1031. CW_USEDEFAULT,
  1032. CW_USEDEFAULT,
  1033. CW_USEDEFAULT,
  1034. NULL,
  1035. NULL,
  1036. appInstance,
  1037. NULL);
  1038. if (!hwndMain)
  1039. {
  1040. CHECKHR(HRESULT_FROM_WIN32(GetLastError()));
  1041. exit(-1);
  1042. }
  1043. }
  1044. //
  1045. // Create a new test bitmap object from scratch
  1046. //
  1047. #define STEPS 16
  1048. VOID
  1049. CreateNewTestBitmap()
  1050. {
  1051. IBitmapImage* bmp;
  1052. BitmapData bmpdata;
  1053. HRESULT hr;
  1054. hr = imgfact->CreateNewBitmap(
  1055. STEPS,
  1056. STEPS,
  1057. PIXFMT_32BPP_ARGB,
  1058. &bmp);
  1059. if (!CHECKHR(hr))
  1060. return;
  1061. hr = bmp->LockBits(
  1062. NULL,
  1063. IMGLOCK_WRITE,
  1064. PIXFMT_DONTCARE,
  1065. &bmpdata);
  1066. if (!CHECKHR(hr))
  1067. {
  1068. bmp->Release();
  1069. return;
  1070. }
  1071. // Make a horizontal blue gradient
  1072. UINT x, y;
  1073. ARGB colors[STEPS];
  1074. for (x=0; x < STEPS; x++)
  1075. colors[x] = MAKEARGB(255, 0, 0, x * 255 / (STEPS-1));
  1076. for (y=0; y < STEPS; y++)
  1077. {
  1078. ARGB* p = (ARGB*) ((BYTE*) bmpdata.Scan0 + y*bmpdata.Stride);
  1079. for (x=0; x < STEPS; x++)
  1080. *p++ = colors[(x+y) % STEPS];
  1081. }
  1082. bmp->UnlockBits(&bmpdata);
  1083. SetCurrentImage(bmp);
  1084. }
  1085. //
  1086. // Main program entrypoint
  1087. //
  1088. INT _cdecl
  1089. main(
  1090. INT argc,
  1091. CHAR **argv
  1092. )
  1093. {
  1094. programName = *argv++;
  1095. argc--;
  1096. appInstance = GetModuleHandle(NULL);
  1097. CoInitialize(NULL);
  1098. //
  1099. // Create an IImagingFactory object
  1100. //
  1101. HRESULT hr;
  1102. hr = CoCreateInstance(
  1103. CLSID_ImagingFactory,
  1104. NULL,
  1105. CLSCTX_INPROC_SERVER,
  1106. IID_IImagingFactory,
  1107. (VOID**) &imgfact);
  1108. if (!CHECKHR(hr))
  1109. exit(-1);
  1110. //
  1111. // Create the main application window
  1112. //
  1113. CreateMainWindow();
  1114. //
  1115. // Create a test image
  1116. //
  1117. if (argc != 0)
  1118. OpenImageFile(*argv);
  1119. if (!curImage)
  1120. CreateNewTestBitmap();
  1121. if (!curImage)
  1122. exit(-1);
  1123. DoSizeWindowToFit(hwndMain);
  1124. ShowWindow(hwndMain, SW_SHOW);
  1125. //
  1126. // Main message loop
  1127. //
  1128. MSG msg;
  1129. HACCEL accel;
  1130. accel = LoadAccelerators(appInstance, MAKEINTRESOURCE(IDR_ACCELTABLE));
  1131. while (GetMessage(&msg, NULL, 0, 0))
  1132. {
  1133. if (!TranslateAccelerator(msg.hwnd, accel, &msg))
  1134. {
  1135. TranslateMessage(&msg);
  1136. DispatchMessage(&msg);
  1137. }
  1138. }
  1139. curImage->Release();
  1140. imgfact->Release();
  1141. CoUninitialize();
  1142. return (INT)(msg.wParam);
  1143. }