/ampdemo/Common/source/direct2dutility.cpp

http://github.com/Ninputer/AMP-Demo · C++ · 731 lines · 549 code · 99 blank · 83 comment · 124 complexity · f33588722880f02daef1ede070869ce9 MD5 · raw file

  1. //===================================================================================
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //
  4. // THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY
  5. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  6. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  7. // FITNESS FOR A PARTICULAR PURPOSE.
  8. //===================================================================================
  9. #include "StdAfx.h"
  10. #include "Direct2DUtility.h"
  11. using namespace Hilo::Direct2DHelpers;
  12. Direct2DUtility::Direct2DUtility()
  13. {
  14. }
  15. Direct2DUtility::~Direct2DUtility()
  16. {
  17. }
  18. //
  19. // Creates a Direct2D bitmap from the specified
  20. // file name.
  21. //
  22. HRESULT Direct2DUtility::LoadBitmapFromFile(
  23. ID2D1RenderTarget *renderTarget,
  24. const wchar_t *uri,
  25. unsigned int destinationWidth,
  26. unsigned int destinationHeight,
  27. ID2D1Bitmap ** bitmap)
  28. {
  29. HRESULT hr = S_OK;
  30. ComPtr<IWICBitmapDecoder> decoder;
  31. ComPtr<IWICBitmapFrameDecode> bitmapSource;
  32. ComPtr<IWICStream> stream;
  33. ComPtr<IWICFormatConverter> converter;
  34. ComPtr<IWICBitmapScaler> scaler;
  35. ComPtr<IWICImagingFactory> wicFactory;
  36. hr = GetWICFactory(&wicFactory);
  37. if (SUCCEEDED(hr))
  38. {
  39. hr = wicFactory->CreateDecoderFromFilename(
  40. uri,
  41. nullptr,
  42. GENERIC_READ,
  43. WICDecodeMetadataCacheOnLoad,
  44. &decoder);
  45. }
  46. if (SUCCEEDED(hr))
  47. {
  48. // Create the initial frame.
  49. hr = decoder->GetFrame(0, &bitmapSource);
  50. }
  51. if (SUCCEEDED(hr))
  52. {
  53. // Convert the image format to 32bppPBGRA
  54. // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
  55. hr = wicFactory->CreateFormatConverter(&converter);
  56. }
  57. if (SUCCEEDED(hr))
  58. {
  59. // If a new width or height was specified, create an
  60. // IWICBitmapScaler and use it to resize the image.
  61. if (destinationWidth != 0 || destinationHeight != 0)
  62. {
  63. unsigned int originalWidth, originalHeight;
  64. hr = bitmapSource->GetSize(&originalWidth, &originalHeight);
  65. if (SUCCEEDED(hr))
  66. {
  67. if (destinationWidth == 0)
  68. {
  69. float scalar = static_cast<float>(destinationHeight) / static_cast<float>(originalHeight);
  70. destinationWidth = static_cast<unsigned int>(scalar * static_cast<float>(originalWidth));
  71. }
  72. else if (destinationHeight == 0)
  73. {
  74. float scalar = static_cast<float>(destinationWidth) / static_cast<float>(originalWidth);
  75. destinationHeight = static_cast<unsigned int>(scalar * static_cast<float>(originalHeight));
  76. }
  77. hr = wicFactory->CreateBitmapScaler(&scaler);
  78. if (SUCCEEDED(hr))
  79. {
  80. hr = scaler->Initialize(
  81. bitmapSource,
  82. destinationWidth,
  83. destinationHeight,
  84. WICBitmapInterpolationModeCubic);
  85. }
  86. if (SUCCEEDED(hr))
  87. {
  88. hr = converter->Initialize(
  89. scaler,
  90. GUID_WICPixelFormat32bppPBGRA,
  91. WICBitmapDitherTypeNone,
  92. nullptr,
  93. 0.f,
  94. WICBitmapPaletteTypeMedianCut);
  95. }
  96. }
  97. }
  98. else // Don't scale the image.
  99. {
  100. hr = converter->Initialize(
  101. bitmapSource,
  102. GUID_WICPixelFormat32bppPBGRA,
  103. WICBitmapDitherTypeNone,
  104. nullptr,
  105. 0.f,
  106. WICBitmapPaletteTypeMedianCut);
  107. }
  108. }
  109. if (SUCCEEDED(hr))
  110. {
  111. // Create a Direct2D bitmap from the WIC bitmap.
  112. hr = renderTarget->CreateBitmapFromWicBitmap(
  113. converter,
  114. nullptr,
  115. bitmap);
  116. }
  117. return hr;
  118. }
  119. //
  120. // Creates a Direct2D bitmap from the specified
  121. // resource name and type.
  122. //
  123. HRESULT Direct2DUtility::LoadBitmapFromResource(
  124. ID2D1RenderTarget *renderTarget,
  125. const wchar_t *resourceName,
  126. const wchar_t *resourceType,
  127. unsigned int destinationWidth,
  128. unsigned int destinationHeight,
  129. ID2D1Bitmap **bitmap)
  130. {
  131. ComPtr<IWICImagingFactory> wicFactory;
  132. ComPtr<IWICBitmapDecoder> decoder;
  133. ComPtr<IWICBitmapFrameDecode> bitmapSource;
  134. ComPtr<IWICStream> stream;
  135. ComPtr<IWICFormatConverter> formatConverter;
  136. ComPtr<IWICBitmapScaler> scaler;
  137. HRSRC imageResHandle = nullptr;
  138. HGLOBAL imageResDataHandle = nullptr;
  139. void *imageFile = nullptr;
  140. unsigned long imageFileSize = 0;
  141. // Locate the resource.
  142. imageResHandle = ::FindResourceW(HINST_THISCOMPONENT, resourceName, resourceType);
  143. HRESULT hr = imageResHandle ? S_OK : E_FAIL;
  144. if (SUCCEEDED(hr))
  145. {
  146. // Load the resource.
  147. imageResDataHandle = ::LoadResource(HINST_THISCOMPONENT, imageResHandle);
  148. hr = imageResDataHandle ? S_OK : E_FAIL;
  149. }
  150. if (SUCCEEDED(hr))
  151. {
  152. // Lock it to get a system memory pointer.
  153. imageFile = ::LockResource(imageResDataHandle);
  154. hr = imageFile ? S_OK : E_FAIL;
  155. }
  156. if (SUCCEEDED(hr))
  157. {
  158. // Calculate the size.
  159. imageFileSize = SizeofResource(HINST_THISCOMPONENT, imageResHandle);
  160. hr = imageFileSize ? S_OK : E_FAIL;
  161. }
  162. if (SUCCEEDED(hr))
  163. {
  164. hr = GetWICFactory(&wicFactory);
  165. }
  166. if (SUCCEEDED(hr))
  167. {
  168. // Create a WIC stream to map onto the memory.
  169. hr = wicFactory->CreateStream(&stream);
  170. }
  171. if (SUCCEEDED(hr))
  172. {
  173. // Initialize the stream with the memory pointer and size.
  174. hr = stream->InitializeFromMemory(
  175. reinterpret_cast<unsigned char*>(imageFile),
  176. imageFileSize);
  177. }
  178. if (SUCCEEDED(hr))
  179. {
  180. // Create a decoder for the stream.
  181. hr = wicFactory->CreateDecoderFromStream(
  182. stream,
  183. nullptr,
  184. WICDecodeMetadataCacheOnLoad,
  185. &decoder);
  186. }
  187. if (SUCCEEDED(hr))
  188. {
  189. // Create the initial frame.
  190. hr = decoder->GetFrame(0, &bitmapSource);
  191. }
  192. if (SUCCEEDED(hr))
  193. {
  194. // Convert the image format to 32bppPBGRA
  195. // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
  196. hr = wicFactory->CreateFormatConverter(&formatConverter);
  197. }
  198. if (SUCCEEDED(hr))
  199. {
  200. // If a new width or height was specified, create an
  201. // IWICBitmapScaler and use it to resize the image.
  202. if (destinationWidth != 0 || destinationHeight != 0)
  203. {
  204. unsigned int originalWidth, originalHeight;
  205. hr = bitmapSource->GetSize(&originalWidth, &originalHeight);
  206. if (SUCCEEDED(hr))
  207. {
  208. if (destinationWidth == 0)
  209. {
  210. float scalar = static_cast<float>(destinationHeight) / static_cast<float>(originalHeight);
  211. destinationWidth = static_cast<unsigned int>(scalar * static_cast<float>(originalWidth));
  212. }
  213. else if (destinationHeight == 0)
  214. {
  215. float scalar = static_cast<float>(destinationWidth) / static_cast<float>(originalWidth);
  216. destinationHeight = static_cast<unsigned int>(scalar * static_cast<float>(originalHeight));
  217. }
  218. hr = wicFactory->CreateBitmapScaler(&scaler);
  219. if (SUCCEEDED(hr))
  220. {
  221. hr = scaler->Initialize(
  222. bitmapSource,
  223. destinationWidth,
  224. destinationHeight,
  225. WICBitmapInterpolationModeCubic);
  226. if (SUCCEEDED(hr))
  227. {
  228. hr = formatConverter->Initialize(
  229. scaler,
  230. GUID_WICPixelFormat32bppPBGRA,
  231. WICBitmapDitherTypeNone,
  232. nullptr,
  233. 0.f,
  234. WICBitmapPaletteTypeMedianCut);
  235. }
  236. }
  237. }
  238. }
  239. else
  240. {
  241. hr = formatConverter->Initialize(
  242. bitmapSource,
  243. GUID_WICPixelFormat32bppPBGRA,
  244. WICBitmapDitherTypeNone,
  245. nullptr,
  246. 0.f,
  247. WICBitmapPaletteTypeMedianCut);
  248. }
  249. }
  250. if (SUCCEEDED(hr))
  251. {
  252. //create a Direct2D bitmap from the WIC bitmap.
  253. hr = renderTarget->CreateBitmapFromWicBitmap(
  254. formatConverter,
  255. nullptr,
  256. bitmap);
  257. }
  258. return hr;
  259. }
  260. //
  261. // Save a WICBitmap to a file
  262. // The original file name is required to get original metadata and encoding information
  263. //
  264. HRESULT Direct2DUtility::SaveBitmapToFile(IWICBitmap* updatedBitmap,
  265. const wchar_t *uriOriginalFile,
  266. const wchar_t *uriUpdatedFile)
  267. {
  268. unsigned int frameCount = 0;
  269. unsigned int width = 0;
  270. unsigned int height = 0;
  271. bool isUsingTempFile = false;
  272. std::wstring outputFilePath;
  273. GUID containerFormat = GUID_ContainerFormatBmp;
  274. WICPixelFormatGUID pixelFormat = GUID_WICPixelFormatDontCare;
  275. ComPtr<IWICImagingFactory> factory;
  276. ComPtr<IWICStream> stream;
  277. ComPtr<IStream> sharedStream;
  278. ComPtr<IWICBitmapDecoder> decoder;
  279. ComPtr<IWICBitmapEncoder> encoder;
  280. ComPtr<IWICMetadataBlockWriter> blockWriter;
  281. ComPtr<IWICMetadataBlockReader> blockReader;
  282. HRESULT hr = Direct2DUtility::GetWICFactory(&factory);
  283. if (SUCCEEDED(hr))
  284. {
  285. factory->CreateDecoderFromFilename(uriOriginalFile, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder);
  286. }
  287. if (SUCCEEDED(hr))
  288. {
  289. hr = decoder->GetFrameCount(&frameCount);
  290. }
  291. // Calculate temporary file name if an updated file was not specified
  292. if (nullptr == uriUpdatedFile)
  293. {
  294. // Indicate that we're using a tempoary file for saving
  295. isUsingTempFile = true;
  296. // Get location of temporary folder
  297. wchar_t tempFilePathBuffer[MAX_PATH];
  298. unsigned long count = GetTempPath(MAX_PATH, tempFilePathBuffer);
  299. if (count > MAX_PATH || count == 0)
  300. {
  301. // Unable to get temporary path (use current directory)
  302. outputFilePath.append(uriOriginalFile);
  303. }
  304. else
  305. {
  306. // Get file name by itself
  307. outputFilePath.append(uriOriginalFile);
  308. outputFilePath = outputFilePath.substr(outputFilePath.find_last_of('\\') + 1);
  309. // Insert temporary folder before the file name
  310. outputFilePath.insert(0, tempFilePathBuffer);
  311. }
  312. }
  313. else
  314. {
  315. outputFilePath.append(uriUpdatedFile);
  316. }
  317. // File extension to determine which container format to use for the output file
  318. std::wstring fileExtension(outputFilePath.substr(outputFilePath.find_last_of('.')));
  319. // Convert all characters to lower case
  320. std::transform(fileExtension.begin(), fileExtension.end(), fileExtension.begin (), tolower);
  321. if (SUCCEEDED(hr))
  322. {
  323. // Default value is bitmap encoding
  324. if (fileExtension.compare(L".jpg") == 0 ||
  325. fileExtension.compare(L".jpeg") == 0 ||
  326. fileExtension.compare(L".jpe") == 0 ||
  327. fileExtension.compare(L".jfif") == 0)
  328. {
  329. containerFormat = GUID_ContainerFormatJpeg;
  330. }
  331. else if (fileExtension.compare(L".tif") == 0 ||
  332. fileExtension.compare(L".tiff") == 0)
  333. {
  334. containerFormat = GUID_ContainerFormatTiff;
  335. }
  336. else if (fileExtension.compare(L".gif") == 0)
  337. {
  338. containerFormat = GUID_ContainerFormatGif;
  339. }
  340. else if (fileExtension.compare(L".png") == 0)
  341. {
  342. containerFormat = GUID_ContainerFormatPng;
  343. }
  344. else if (fileExtension.compare(L".wmp") == 0)
  345. {
  346. containerFormat = GUID_ContainerFormatWmp;
  347. }
  348. hr = factory->CreateEncoder(containerFormat, nullptr, &encoder);
  349. }
  350. if (SUCCEEDED(hr))
  351. {
  352. // Create a stream for the encoder
  353. hr = factory->CreateStream(&stream);
  354. }
  355. if (SUCCEEDED(hr))
  356. {
  357. // Update temporary file name if needed
  358. if (isUsingTempFile)
  359. {
  360. outputFilePath.append(L".tmp");
  361. }
  362. // Initialize the stream using the output file path
  363. hr = stream->InitializeFromFilename(outputFilePath.c_str(), GENERIC_WRITE);
  364. }
  365. if (SUCCEEDED(hr))
  366. {
  367. // Create encoder to write to image file
  368. hr = encoder->Initialize(stream, WICBitmapEncoderNoCache);
  369. }
  370. // Process each frame
  371. for (unsigned int i = 0; i < frameCount && SUCCEEDED(hr); i++)
  372. {
  373. //Frame variables
  374. ComPtr<IWICBitmapFrameDecode> frameDecode;
  375. ComPtr<IWICBitmapFrameEncode> frameEncode;
  376. ComPtr<IWICMetadataQueryReader> frameQueryReader;
  377. ComPtr<IWICMetadataQueryWriter> frameQueryWriter;
  378. //Get and create image frame
  379. if (SUCCEEDED(hr))
  380. {
  381. hr = decoder->GetFrame(i, &frameDecode);
  382. }
  383. if (SUCCEEDED(hr))
  384. {
  385. hr = encoder->CreateNewFrame(&frameEncode, nullptr);
  386. }
  387. //Initialize the encoder
  388. if (SUCCEEDED(hr))
  389. {
  390. hr = frameEncode->Initialize(nullptr);
  391. }
  392. //Get and set size
  393. if (SUCCEEDED(hr))
  394. {
  395. if (i == 0)
  396. {
  397. hr = updatedBitmap->GetSize(&width, &height);
  398. }
  399. else
  400. {
  401. hr = frameDecode->GetSize(&width, &height);
  402. }
  403. }
  404. if (SUCCEEDED(hr))
  405. {
  406. hr = frameEncode->SetSize(width, height);
  407. }
  408. //Set pixel format
  409. if (SUCCEEDED(hr))
  410. {
  411. frameDecode->GetPixelFormat(&pixelFormat);
  412. }
  413. if (SUCCEEDED(hr))
  414. {
  415. hr = frameEncode->SetPixelFormat(&pixelFormat);
  416. }
  417. //Check that the destination format and source formats are the same
  418. bool formatsEqual = false;
  419. if (SUCCEEDED(hr))
  420. {
  421. GUID srcFormat;
  422. GUID destFormat;
  423. hr = decoder->GetContainerFormat(&srcFormat);
  424. if (SUCCEEDED(hr))
  425. {
  426. hr = encoder->GetContainerFormat(&destFormat);
  427. }
  428. if (SUCCEEDED(hr))
  429. {
  430. formatsEqual = (srcFormat == destFormat) ? true : false;
  431. }
  432. }
  433. if (SUCCEEDED(hr) && formatsEqual)
  434. {
  435. //Copy metadata using metadata block reader/writer
  436. frameDecode->QueryInterface(&blockReader);
  437. frameEncode->QueryInterface(&blockWriter);
  438. if (nullptr != blockReader && nullptr != blockWriter)
  439. {
  440. blockWriter->InitializeFromBlockReader(blockReader);
  441. }
  442. }
  443. if (SUCCEEDED(hr))
  444. {
  445. if (i == 0)
  446. {
  447. // Copy updated bitmap to output
  448. hr = frameEncode->WriteSource(updatedBitmap, nullptr);
  449. }
  450. else
  451. {
  452. // Copy existing image to output
  453. hr = frameEncode->WriteSource(static_cast<IWICBitmapSource*> (frameDecode), nullptr);
  454. }
  455. }
  456. //Commit the frame
  457. if (SUCCEEDED(hr))
  458. {
  459. hr = frameEncode->Commit();
  460. }
  461. }
  462. if (SUCCEEDED(hr))
  463. {
  464. encoder->Commit();
  465. }
  466. // Ensure that the input and output files are not locked by releasing corresponding objects
  467. if (stream)
  468. {
  469. stream = nullptr;
  470. }
  471. if (decoder)
  472. {
  473. decoder = nullptr;
  474. }
  475. if (encoder)
  476. {
  477. encoder = nullptr;
  478. }
  479. if (blockWriter)
  480. {
  481. blockWriter = nullptr;
  482. }
  483. if (blockReader)
  484. {
  485. blockReader = nullptr;
  486. }
  487. if (SUCCEEDED(hr) && isUsingTempFile)
  488. {
  489. // Move temporary file to current file
  490. if (! ::CopyFileW(outputFilePath.c_str(), uriOriginalFile, false))
  491. {
  492. hr = E_FAIL;
  493. }
  494. // Delete the temporary file
  495. ::DeleteFileW(outputFilePath.c_str());
  496. }
  497. return hr;
  498. }
  499. //
  500. // Create a Direct2D Bitmap from a Shell Thumbnail cache
  501. //
  502. HRESULT Direct2DUtility::DecodeImageFromThumbCache(
  503. IShellItem *shellItem,
  504. ID2D1RenderTarget* renderTarget,
  505. unsigned int thumbnailSize,
  506. ID2D1Bitmap **bitmap)
  507. {
  508. ComPtr<IShellItemImageFactory> imageFactory;
  509. ComPtr<IWICFormatConverter> converter;
  510. HRESULT hr = shellItem->QueryInterface(IID_PPV_ARGS(&imageFactory));
  511. HBITMAP hBitmap = nullptr;
  512. if (SUCCEEDED(hr))
  513. {
  514. SIZE size = {thumbnailSize, thumbnailSize};
  515. hr = imageFactory->GetImage(
  516. size,
  517. SIIGBF_BIGGERSIZEOK, // improves performance, since we'll need to resize anyway
  518. &hBitmap);
  519. }
  520. hr = nullptr == hBitmap ? E_FAIL : hr;
  521. ComPtr<IWICImagingFactory> wicFactory;
  522. if (SUCCEEDED(hr))
  523. {
  524. hr = GetWICFactory(&wicFactory);
  525. }
  526. ComPtr<IWICBitmap> wicBitmap;
  527. if (SUCCEEDED(hr))
  528. {
  529. hr = wicFactory->CreateBitmapFromHBITMAP(
  530. hBitmap,
  531. nullptr,
  532. WICBitmapUseAlpha,
  533. &wicBitmap);
  534. }
  535. // Make sure to free the resource as soon as it's not needed.
  536. if (nullptr != hBitmap)
  537. {
  538. ::DeleteObject(hBitmap);
  539. hBitmap = nullptr;
  540. }
  541. if (SUCCEEDED(hr))
  542. {
  543. // Convert the image format to 32bppPBGRA
  544. // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
  545. hr = wicFactory->CreateFormatConverter(&converter);
  546. }
  547. if (SUCCEEDED(hr))
  548. {
  549. hr = converter->Initialize(
  550. wicBitmap,
  551. GUID_WICPixelFormat32bppPBGRA,
  552. WICBitmapDitherTypeNone,
  553. nullptr,
  554. 0.f,
  555. WICBitmapPaletteTypeMedianCut);
  556. }
  557. if (SUCCEEDED(hr))
  558. {
  559. // Create a D2D bitmap from the WIC bitmap
  560. hr = renderTarget->CreateBitmapFromWicBitmap(
  561. converter,
  562. nullptr,
  563. bitmap);
  564. }
  565. return hr;
  566. }
  567. //
  568. // Get a Direct2D factory
  569. //
  570. HRESULT Direct2DUtility::GetD2DFactory(ID2D1Factory** factory)
  571. {
  572. static ComPtr<ID2D1Factory> m_pD2DFactory;
  573. HRESULT hr = S_OK;
  574. if (nullptr == m_pD2DFactory)
  575. {
  576. #if defined(DEBUG) || defined(_DEBUG)
  577. D2D1_FACTORY_OPTIONS options;
  578. options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
  579. hr = D2D1CreateFactory<ID2D1Factory>(
  580. D2D1_FACTORY_TYPE_MULTI_THREADED,
  581. /*options,*/
  582. &m_pD2DFactory);
  583. #else
  584. hr = D2D1CreateFactory(
  585. D2D1_FACTORY_TYPE_MULTI_THREADED,
  586. &m_pD2DFactory);
  587. #endif
  588. }
  589. if (SUCCEEDED(hr))
  590. {
  591. hr = AssignToOutputPointer(factory, m_pD2DFactory);
  592. }
  593. return hr;
  594. }
  595. //
  596. // Get a WIC Imaging factory
  597. //
  598. HRESULT Direct2DUtility::GetWICFactory(IWICImagingFactory** factory)
  599. {
  600. static ComPtr<IWICImagingFactory> m_pWICFactory;
  601. HRESULT hr = S_OK;
  602. if (nullptr == m_pWICFactory)
  603. {
  604. hr = CoCreateInstance(
  605. CLSID_WICImagingFactory,
  606. nullptr,
  607. CLSCTX_INPROC_SERVER,
  608. IID_PPV_ARGS(&m_pWICFactory));
  609. }
  610. if (SUCCEEDED(hr))
  611. {
  612. hr = AssignToOutputPointer(factory, m_pWICFactory);
  613. }
  614. return hr;
  615. }
  616. //
  617. // Get a DirectWrite factory
  618. //
  619. HRESULT Direct2DUtility::GetDWriteFactory(IDWriteFactory** factory )
  620. {
  621. static ComPtr<IDWriteFactory> m_pDWriteFactory;
  622. HRESULT hr = S_OK;
  623. if (nullptr == m_pDWriteFactory)
  624. {
  625. hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_pDWriteFactory), reinterpret_cast<IUnknown**>(&m_pDWriteFactory));
  626. }
  627. if (SUCCEEDED(hr))
  628. {
  629. hr = AssignToOutputPointer(factory, m_pDWriteFactory);
  630. }
  631. return hr;
  632. }