| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- #include <Windows.h>
- #include <Commctrl.h>
- #include <CommonControls.h>
- #include <algorithm>
- #include <cstddef>
- #include <cwchar>
- #include <memory>
- #include <vector>
- #include <system_error>
- #include <shlwapi.h>
- #include "file_icon.h"
- #pragma warning(disable:4458)
- #pragma warning(disable:4267)
- namespace Gdiplus
- {
- using std::max;
- using std::min;
- } // namespace Gdiplus
- #include <Gdiplus.h>
- class ComInit
- {
- public:
- ComInit() { CoInitializeEx(0, COINIT_MULTITHREADED); }
- ~ComInit() { CoUninitialize(); }
- private:
- ComInit(const ComInit &);
- ComInit &operator=(const ComInit &);
- };
- class GdiPlusInit
- {
- public:
- GdiPlusInit()
- {
- Gdiplus::GdiplusStartupInput startupInput;
- Gdiplus::GdiplusStartup(std::addressof(this->token),
- std::addressof(startupInput), nullptr);
- }
- ~GdiPlusInit() { Gdiplus::GdiplusShutdown(this->token); }
- private:
- GdiPlusInit(const GdiPlusInit &);
- GdiPlusInit &operator=(const GdiPlusInit &);
- ULONG_PTR token;
- };
- struct IStreamDeleter
- {
- void operator()(IStream *pStream) const { pStream->Release(); }
- };
- std::unique_ptr<Gdiplus::Bitmap> CreateBitmapFromIcon(
- HICON hIcon, std::vector<std::int32_t> &buffer)
- {
- ICONINFO iconInfo = {0};
- GetIconInfo(hIcon, std::addressof(iconInfo));
- BITMAP bm = {0};
- GetObject(iconInfo.hbmColor, sizeof(bm), std::addressof(bm));
- std::unique_ptr<Gdiplus::Bitmap> bitmap;
- if (bm.bmBitsPixel == 32) {
- auto hDC = GetDC(nullptr);
- BITMAPINFO bmi = {0};
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = bm.bmWidth;
- bmi.bmiHeader.biHeight = -bm.bmHeight;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- auto nBits = bm.bmWidth * bm.bmHeight;
- buffer.resize(nBits);
- GetDIBits(hDC, iconInfo.hbmColor, 0, bm.bmHeight,
- std::addressof(buffer[0]), std::addressof(bmi),
- DIB_RGB_COLORS);
- auto hasAlpha = false;
- for (std::int32_t i = 0; i < nBits; i++) {
- if ((buffer[i] & 0xFF000000) != 0) {
- hasAlpha = true;
- break;
- }
- }
- if (!hasAlpha) {
- std::vector<std::int32_t> maskBits(nBits);
- GetDIBits(hDC, iconInfo.hbmMask, 0, bm.bmHeight,
- std::addressof(maskBits[0]), std::addressof(bmi),
- DIB_RGB_COLORS);
- for (std::int32_t i = 0; i < nBits; i++) {
- if (maskBits[i] == 0) {
- buffer[i] |= 0xFF000000;
- }
- }
- }
- bitmap.reset(new Gdiplus::Bitmap(
- bm.bmWidth, bm.bmHeight, bm.bmWidth * sizeof(std::int32_t),
- PixelFormat32bppARGB,
- static_cast<BYTE *>(
- static_cast<void *>(std::addressof(buffer[0])))));
- ReleaseDC(nullptr, hDC);
- } else {
- bitmap.reset(Gdiplus::Bitmap::FromHICON(hIcon));
- }
- DeleteObject(iconInfo.hbmColor);
- DeleteObject(iconInfo.hbmMask);
- return bitmap;
- }
- int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
- {
- UINT num = 0u;
- UINT size = 0u;
- Gdiplus::GetImageEncodersSize(std::addressof(num), std::addressof(size));
- if (size == 0u)
- {
- return -1;
- }
- std::unique_ptr<Gdiplus::ImageCodecInfo> pImageCodecInfo(
- static_cast<Gdiplus::ImageCodecInfo *>(
- static_cast<void *>(new BYTE[size])));
- if (pImageCodecInfo == nullptr)
- {
- return -1;
- }
- GetImageEncoders(num, size, pImageCodecInfo.get());
- for (UINT i = 0u; i < num; i++)
- {
- if (std::wcscmp(pImageCodecInfo.get()[i].MimeType, format) == 0)
- {
- *pClsid = pImageCodecInfo.get()[i].Clsid;
- return i;
- }
- }
- return -1;
- }
- std::wstring Utf8ToWide(const std::string &src)
- {
- const auto size =
- MultiByteToWideChar(CP_UTF8, 0u, src.data(), -1, nullptr, 0u);
- std::vector<wchar_t> dest(size, L'\0');
- if (MultiByteToWideChar(CP_UTF8, 0u, src.data(), -1, dest.data(), dest.size()) == 0)
- {
- throw std::system_error{static_cast<int>(GetLastError()),
- std::system_category()};
- }
- return std::wstring{dest.begin(), dest.end()};
- }
- std::vector<unsigned char> HIconToPNGBuffer(HICON hIcon)
- {
- GdiPlusInit init;
- std::vector<std::int32_t> buffer;
- auto bitmap = CreateBitmapFromIcon(hIcon, buffer);
- CLSID encoder;
- if (GetEncoderClsid(L"image/png", std::addressof(encoder)) == -1) {
- return std::vector<unsigned char>{};
- }
- IStream *tmp;
- if (CreateStreamOnHGlobal(nullptr, TRUE, std::addressof(tmp)) != S_OK) {
- return std::vector<unsigned char>{};
- }
- std::unique_ptr<IStream, IStreamDeleter> pStream{tmp};
- if (bitmap->Save(pStream.get(), std::addressof(encoder), nullptr) !=
- Gdiplus::Status::Ok) {
- return std::vector<unsigned char>{};
- }
- STATSTG stg = {0};
- LARGE_INTEGER offset = {0};
- if (pStream->Stat(std::addressof(stg), STATFLAG_NONAME) != S_OK ||
- pStream->Seek(offset, STREAM_SEEK_SET, nullptr) != S_OK) {
- return std::vector<unsigned char>{};
- }
- std::vector<unsigned char> result(
- static_cast<std::size_t>(stg.cbSize.QuadPart));
- ULONG ul;
- if (pStream->Read(std::addressof(result[0]),
- static_cast<ULONG>(stg.cbSize.QuadPart),
- std::addressof(ul)) != S_OK ||
- stg.cbSize.QuadPart != ul) {
- return std::vector<unsigned char>{};
- }
- return result;
- }
- bool HIconToPNGFile(HICON hIcon, const std::string &pngFile)
- {
- GdiPlusInit init;
- std::vector<std::int32_t> buffer;
- auto bitmap = CreateBitmapFromIcon(hIcon, buffer);
- CLSID encoder;
- if (GetEncoderClsid(L"image/png", std::addressof(encoder)) == -1) {
- return false;
- }
- IStream *tmp;
- if (SHCreateStreamOnFile(Utf8ToWide(pngFile).c_str(), STGM_WRITE|STGM_CREATE, std::addressof(tmp)) != S_OK) {
- return false;
- }
- std::unique_ptr<IStream, IStreamDeleter> pStream{tmp};
- if (bitmap->Save(pStream.get(), std::addressof(encoder), nullptr) !=
- Gdiplus::Status::Ok) {
- return false;
- }
- //pStream->Release();
- return true;
- }
- std::vector<unsigned char> GetIcon(const std::string &name, int size,
- UINT flag)
- {
- ComInit init;
- flag |= SHGFI_ICON;
- switch (size)
- {
- case 16:
- flag |= SHGFI_SMALLICON;
- break;
- case 32:
- flag |= SHGFI_LARGEICON;
- break;
- case 64:
- case 256:
- flag |= SHGFI_SYSICONINDEX;
- break;
- }
- SHFILEINFOW sfi = {0};
- auto hr = SHGetFileInfoW(Utf8ToWide(name).c_str(), 0, std::addressof(sfi),
- sizeof(sfi), flag);
- HICON hIcon;
- if (FAILED(hr))
- {
- return std::vector<unsigned char>{};
- }
- if (size == 16 || size == 32)
- {
- hIcon = sfi.hIcon;
- }
- else
- {
- HIMAGELIST *imageList;
- hr = SHGetImageList(
- size == 64 ? SHIL_EXTRALARGE : SHIL_JUMBO, IID_IImageList,
- static_cast<void **>(
- static_cast<void *>(std::addressof(imageList))));
- if (FAILED(hr))
- {
- return std::vector<unsigned char>{};
- }
- hr = static_cast<IImageList *>(static_cast<void *>(imageList))
- ->GetIcon(sfi.iIcon, ILD_TRANSPARENT, std::addressof(hIcon));
- if (FAILED(hr))
- {
- return std::vector<unsigned char>{};
- }
- }
- auto buffer = HIconToPNGBuffer(hIcon);
- DestroyIcon(hIcon);
- return buffer;
- }
- bool SaveIcon(const std::string &name, const std::string &pngName, int size, UINT flag)
- {
- ComInit init;
- flag |= SHGFI_ICON;
- switch (size)
- {
- case 16:
- flag |= SHGFI_SMALLICON;
- break;
- case 32:
- flag |= SHGFI_LARGEICON;
- break;
- case 64:
- case 256:
- flag |= SHGFI_SYSICONINDEX;
- break;
- }
- SHFILEINFOW sfi = {0};
- auto hr = SHGetFileInfoW(Utf8ToWide(name).c_str(), 0, std::addressof(sfi),
- sizeof(sfi), flag);
- HICON hIcon;
- if (FAILED(hr)) {
- return false;
- }
- if (size == 16 || size == 32) {
- hIcon = sfi.hIcon;
- } else {
- HIMAGELIST *imageList;
- hr = SHGetImageList(
- size == 64 ? SHIL_EXTRALARGE : SHIL_JUMBO, IID_IImageList,
- static_cast<void **>(
- static_cast<void *>(std::addressof(imageList))));
- if (FAILED(hr)) {
- return false;
- }
- hr = static_cast<IImageList *>(static_cast<void *>(imageList))
- ->GetIcon(sfi.iIcon, ILD_TRANSPARENT, std::addressof(hIcon));
- if (FAILED(hr)) {
- return false;
- }
- }
- auto ret = HIconToPNGFile(hIcon, pngName);
- DestroyIcon(hIcon);
- return ret;
- }
- // Napi::Buffer<char> getIcon(const Napi::CallbackInfo &info)
- // {
- // Napi::Env env{info.Env()};
- // auto path{info[0].As<Napi::String>().Utf8Value()};
- // auto data = GetIcon(path, info[1].As<Napi::Number>().Int32Value(), 0);
- // uint8_t *arr = &data[0];
- // return Napi::Buffer<char>::Copy(
- // env, static_cast<char *>(static_cast<void *>(arr)), data.size());
- // }
|