file_icon.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. #include <Windows.h>
  2. #include <Commctrl.h>
  3. #include <CommonControls.h>
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <cwchar>
  7. #include <memory>
  8. #include <vector>
  9. #include <system_error>
  10. #include <shlwapi.h>
  11. #include "file_icon.h"
  12. #pragma warning(disable:4458)
  13. #pragma warning(disable:4267)
  14. namespace Gdiplus
  15. {
  16. using std::max;
  17. using std::min;
  18. } // namespace Gdiplus
  19. #include <Gdiplus.h>
  20. class ComInit
  21. {
  22. public:
  23. ComInit() { CoInitializeEx(0, COINIT_MULTITHREADED); }
  24. ~ComInit() { CoUninitialize(); }
  25. private:
  26. ComInit(const ComInit &);
  27. ComInit &operator=(const ComInit &);
  28. };
  29. class GdiPlusInit
  30. {
  31. public:
  32. GdiPlusInit()
  33. {
  34. Gdiplus::GdiplusStartupInput startupInput;
  35. Gdiplus::GdiplusStartup(std::addressof(this->token),
  36. std::addressof(startupInput), nullptr);
  37. }
  38. ~GdiPlusInit() { Gdiplus::GdiplusShutdown(this->token); }
  39. private:
  40. GdiPlusInit(const GdiPlusInit &);
  41. GdiPlusInit &operator=(const GdiPlusInit &);
  42. ULONG_PTR token;
  43. };
  44. struct IStreamDeleter
  45. {
  46. void operator()(IStream *pStream) const { pStream->Release(); }
  47. };
  48. std::unique_ptr<Gdiplus::Bitmap> CreateBitmapFromIcon(
  49. HICON hIcon, std::vector<std::int32_t> &buffer)
  50. {
  51. ICONINFO iconInfo = {0};
  52. GetIconInfo(hIcon, std::addressof(iconInfo));
  53. BITMAP bm = {0};
  54. GetObject(iconInfo.hbmColor, sizeof(bm), std::addressof(bm));
  55. std::unique_ptr<Gdiplus::Bitmap> bitmap;
  56. if (bm.bmBitsPixel == 32) {
  57. auto hDC = GetDC(nullptr);
  58. BITMAPINFO bmi = {0};
  59. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  60. bmi.bmiHeader.biWidth = bm.bmWidth;
  61. bmi.bmiHeader.biHeight = -bm.bmHeight;
  62. bmi.bmiHeader.biPlanes = 1;
  63. bmi.bmiHeader.biBitCount = 32;
  64. bmi.bmiHeader.biCompression = BI_RGB;
  65. auto nBits = bm.bmWidth * bm.bmHeight;
  66. buffer.resize(nBits);
  67. GetDIBits(hDC, iconInfo.hbmColor, 0, bm.bmHeight,
  68. std::addressof(buffer[0]), std::addressof(bmi),
  69. DIB_RGB_COLORS);
  70. auto hasAlpha = false;
  71. for (std::int32_t i = 0; i < nBits; i++) {
  72. if ((buffer[i] & 0xFF000000) != 0) {
  73. hasAlpha = true;
  74. break;
  75. }
  76. }
  77. if (!hasAlpha) {
  78. std::vector<std::int32_t> maskBits(nBits);
  79. GetDIBits(hDC, iconInfo.hbmMask, 0, bm.bmHeight,
  80. std::addressof(maskBits[0]), std::addressof(bmi),
  81. DIB_RGB_COLORS);
  82. for (std::int32_t i = 0; i < nBits; i++) {
  83. if (maskBits[i] == 0) {
  84. buffer[i] |= 0xFF000000;
  85. }
  86. }
  87. }
  88. bitmap.reset(new Gdiplus::Bitmap(
  89. bm.bmWidth, bm.bmHeight, bm.bmWidth * sizeof(std::int32_t),
  90. PixelFormat32bppARGB,
  91. static_cast<BYTE *>(
  92. static_cast<void *>(std::addressof(buffer[0])))));
  93. ReleaseDC(nullptr, hDC);
  94. } else {
  95. bitmap.reset(Gdiplus::Bitmap::FromHICON(hIcon));
  96. }
  97. DeleteObject(iconInfo.hbmColor);
  98. DeleteObject(iconInfo.hbmMask);
  99. return bitmap;
  100. }
  101. int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
  102. {
  103. UINT num = 0u;
  104. UINT size = 0u;
  105. Gdiplus::GetImageEncodersSize(std::addressof(num), std::addressof(size));
  106. if (size == 0u)
  107. {
  108. return -1;
  109. }
  110. std::unique_ptr<Gdiplus::ImageCodecInfo> pImageCodecInfo(
  111. static_cast<Gdiplus::ImageCodecInfo *>(
  112. static_cast<void *>(new BYTE[size])));
  113. if (pImageCodecInfo == nullptr)
  114. {
  115. return -1;
  116. }
  117. GetImageEncoders(num, size, pImageCodecInfo.get());
  118. for (UINT i = 0u; i < num; i++)
  119. {
  120. if (std::wcscmp(pImageCodecInfo.get()[i].MimeType, format) == 0)
  121. {
  122. *pClsid = pImageCodecInfo.get()[i].Clsid;
  123. return i;
  124. }
  125. }
  126. return -1;
  127. }
  128. std::wstring Utf8ToWide(const std::string &src)
  129. {
  130. const auto size =
  131. MultiByteToWideChar(CP_UTF8, 0u, src.data(), -1, nullptr, 0u);
  132. std::vector<wchar_t> dest(size, L'\0');
  133. if (MultiByteToWideChar(CP_UTF8, 0u, src.data(), -1, dest.data(), dest.size()) == 0)
  134. {
  135. throw std::system_error{static_cast<int>(GetLastError()),
  136. std::system_category()};
  137. }
  138. return std::wstring{dest.begin(), dest.end()};
  139. }
  140. std::vector<unsigned char> HIconToPNGBuffer(HICON hIcon)
  141. {
  142. GdiPlusInit init;
  143. std::vector<std::int32_t> buffer;
  144. auto bitmap = CreateBitmapFromIcon(hIcon, buffer);
  145. CLSID encoder;
  146. if (GetEncoderClsid(L"image/png", std::addressof(encoder)) == -1) {
  147. return std::vector<unsigned char>{};
  148. }
  149. IStream *tmp;
  150. if (CreateStreamOnHGlobal(nullptr, TRUE, std::addressof(tmp)) != S_OK) {
  151. return std::vector<unsigned char>{};
  152. }
  153. std::unique_ptr<IStream, IStreamDeleter> pStream{tmp};
  154. if (bitmap->Save(pStream.get(), std::addressof(encoder), nullptr) !=
  155. Gdiplus::Status::Ok) {
  156. return std::vector<unsigned char>{};
  157. }
  158. STATSTG stg = {0};
  159. LARGE_INTEGER offset = {0};
  160. if (pStream->Stat(std::addressof(stg), STATFLAG_NONAME) != S_OK ||
  161. pStream->Seek(offset, STREAM_SEEK_SET, nullptr) != S_OK) {
  162. return std::vector<unsigned char>{};
  163. }
  164. std::vector<unsigned char> result(
  165. static_cast<std::size_t>(stg.cbSize.QuadPart));
  166. ULONG ul;
  167. if (pStream->Read(std::addressof(result[0]),
  168. static_cast<ULONG>(stg.cbSize.QuadPart),
  169. std::addressof(ul)) != S_OK ||
  170. stg.cbSize.QuadPart != ul) {
  171. return std::vector<unsigned char>{};
  172. }
  173. return result;
  174. }
  175. bool HIconToPNGFile(HICON hIcon, const std::string &pngFile)
  176. {
  177. GdiPlusInit init;
  178. std::vector<std::int32_t> buffer;
  179. auto bitmap = CreateBitmapFromIcon(hIcon, buffer);
  180. CLSID encoder;
  181. if (GetEncoderClsid(L"image/png", std::addressof(encoder)) == -1) {
  182. return false;
  183. }
  184. IStream *tmp;
  185. if (SHCreateStreamOnFile(Utf8ToWide(pngFile).c_str(), STGM_WRITE|STGM_CREATE, std::addressof(tmp)) != S_OK) {
  186. return false;
  187. }
  188. std::unique_ptr<IStream, IStreamDeleter> pStream{tmp};
  189. if (bitmap->Save(pStream.get(), std::addressof(encoder), nullptr) !=
  190. Gdiplus::Status::Ok) {
  191. return false;
  192. }
  193. //pStream->Release();
  194. return true;
  195. }
  196. std::vector<unsigned char> GetIcon(const std::string &name, int size,
  197. UINT flag)
  198. {
  199. ComInit init;
  200. flag |= SHGFI_ICON;
  201. switch (size)
  202. {
  203. case 16:
  204. flag |= SHGFI_SMALLICON;
  205. break;
  206. case 32:
  207. flag |= SHGFI_LARGEICON;
  208. break;
  209. case 64:
  210. case 256:
  211. flag |= SHGFI_SYSICONINDEX;
  212. break;
  213. }
  214. SHFILEINFOW sfi = {0};
  215. auto hr = SHGetFileInfoW(Utf8ToWide(name).c_str(), 0, std::addressof(sfi),
  216. sizeof(sfi), flag);
  217. HICON hIcon;
  218. if (FAILED(hr))
  219. {
  220. return std::vector<unsigned char>{};
  221. }
  222. if (size == 16 || size == 32)
  223. {
  224. hIcon = sfi.hIcon;
  225. }
  226. else
  227. {
  228. HIMAGELIST *imageList;
  229. hr = SHGetImageList(
  230. size == 64 ? SHIL_EXTRALARGE : SHIL_JUMBO, IID_IImageList,
  231. static_cast<void **>(
  232. static_cast<void *>(std::addressof(imageList))));
  233. if (FAILED(hr))
  234. {
  235. return std::vector<unsigned char>{};
  236. }
  237. hr = static_cast<IImageList *>(static_cast<void *>(imageList))
  238. ->GetIcon(sfi.iIcon, ILD_TRANSPARENT, std::addressof(hIcon));
  239. if (FAILED(hr))
  240. {
  241. return std::vector<unsigned char>{};
  242. }
  243. }
  244. auto buffer = HIconToPNGBuffer(hIcon);
  245. DestroyIcon(hIcon);
  246. return buffer;
  247. }
  248. bool SaveIcon(const std::string &name, const std::string &pngName, int size, UINT flag)
  249. {
  250. ComInit init;
  251. flag |= SHGFI_ICON;
  252. switch (size)
  253. {
  254. case 16:
  255. flag |= SHGFI_SMALLICON;
  256. break;
  257. case 32:
  258. flag |= SHGFI_LARGEICON;
  259. break;
  260. case 64:
  261. case 256:
  262. flag |= SHGFI_SYSICONINDEX;
  263. break;
  264. }
  265. SHFILEINFOW sfi = {0};
  266. auto hr = SHGetFileInfoW(Utf8ToWide(name).c_str(), 0, std::addressof(sfi),
  267. sizeof(sfi), flag);
  268. HICON hIcon;
  269. if (FAILED(hr)) {
  270. return false;
  271. }
  272. if (size == 16 || size == 32) {
  273. hIcon = sfi.hIcon;
  274. } else {
  275. HIMAGELIST *imageList;
  276. hr = SHGetImageList(
  277. size == 64 ? SHIL_EXTRALARGE : SHIL_JUMBO, IID_IImageList,
  278. static_cast<void **>(
  279. static_cast<void *>(std::addressof(imageList))));
  280. if (FAILED(hr)) {
  281. return false;
  282. }
  283. hr = static_cast<IImageList *>(static_cast<void *>(imageList))
  284. ->GetIcon(sfi.iIcon, ILD_TRANSPARENT, std::addressof(hIcon));
  285. if (FAILED(hr)) {
  286. return false;
  287. }
  288. }
  289. auto ret = HIconToPNGFile(hIcon, pngName);
  290. DestroyIcon(hIcon);
  291. return ret;
  292. }
  293. // Napi::Buffer<char> getIcon(const Napi::CallbackInfo &info)
  294. // {
  295. // Napi::Env env{info.Env()};
  296. // auto path{info[0].As<Napi::String>().Utf8Value()};
  297. // auto data = GetIcon(path, info[1].As<Napi::Number>().Int32Value(), 0);
  298. // uint8_t *arr = &data[0];
  299. // return Napi::Buffer<char>::Copy(
  300. // env, static_cast<char *>(static_cast<void *>(arr)), data.size());
  301. // }