icon文件解析和生成

参考:ICO文件格式的演化
使用GDI+保存带Alpha通道的图像

ICON 格式

文件头 | DIRENTRY | DIRENTRY|DIRENTRY|… Imgdata|ImgdataImgdataImgdata|…

//文件头
//sizeof = 6
typedef struct ICONHEADER
{
	WORD idReserved;//保留用,
	WORD idType;//固定值 1
	WORD idCount;//包含的图片个数
};

//标记每张图片的具体信息
//sizeof = 16
typedef struct ICONDIRENTRY
{
		BYTE bWidth;//图片大小,如果图片宽度大于等于256则为0
		BYTE bHeight;//图片大小,如果图片宽度大于等于256则为0
		BYTE bColorCount;
		BYTE bReserved;
		WORD wPlanes;
		WORD wBitCount;
		//图像大小w*h*4 + 40 header头 + mask
		//mask (h * ((w + 31) / 32 * 32 / 8))  
		DWORD dwBytesInRes;//图片资源大小,需要计算mask
		
		DWORD dwImageOffset;//在文件中的偏移量
}*LPICONDIRENTRY;

如果图片尺寸 <= 128.则保存的BMP数据,否则保存的是png格式的数据。
在这里插入图片描述

从ICON提取PNG文件

static DWORD extractIcoPngs(const std::wstring& iconPath, const std::wstring& outpath)
		{
			//文件不存在
			if (_waccess(iconPath.c_str(), 00) != 00 || _waccess(outpath.c_str(), 00) != 00) return -1;
			//打开图标文件  
			HANDLE hIconFile = CreateFileW(iconPath.c_str(), GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hIconFile == INVALID_HANDLE_VALUE)
			{
				DWORD dwErrCode = GetLastError();
				return dwErrCode;
			}

			//读取文件头
			ICONHEADER header;
			DWORD dwRead = 0;
			memset(&header, 0, sizeof(ICONHEADER));
			if (!ReadFile(hIconFile, &header, sizeof(ICONHEADER), &dwRead, NULL))
			{
				DWORD dwErrCode = GetLastError();
				CloseHandle(hIconFile);
				return dwErrCode;
			}
			
			if (header.idCount <= 0) return -2;

			std::vector<ICONDIRENTRY> vecEntry(header.idCount);
			//读取像信息块
			if (!ReadFile(hIconFile, &vecEntry[0], header.idCount * sizeof(ICONDIRENTRY), &dwRead, NULL))
			{
				DWORD dwErrCode = GetLastError();
				CloseHandle(hIconFile);
				return dwErrCode;
			}
			//读取每一帧的数据
			CLSID pngClsid;
			if (!DCUtil::GetEncoderClsid(L"image/png", &pngClsid)) return -3;

			for (int i = 0; i < vecEntry.size(); i++)
			{
				//根据偏移量读取图像数据
				SetFilePointer(hIconFile, vecEntry[i].dwImageOffset, 0, FILE_BEGIN);

				std::wstring wsave_file = L"";
				//如果图像高度为0,则表示为256尺寸的数据
				if (vecEntry[i].bHeight == 0)
				{
					std::vector<unsigned char> imgdata(vecEntry[i].dwBytesInRes, 0);
					ReadFile(hIconFile, &imgdata[0], vecEntry[i].dwBytesInRes, &dwRead, NULL);
					std::wstring file_name = A2WSTR(fmt::format("{0}_{1}x{2}.png", W2ASTR(jeflib::FileUtil::GetFileBaseNameW(iconPath)), 256, 256));
					wsave_file = jeflib::FileUtil::JoinPathW(outpath, file_name);

					HANDLE hImg = CreateFileW(wsave_file.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
					if (hImg)
					{
						DWORD dwWrite = 0;
						WriteFile(hImg, &imgdata[0], vecEntry[i].dwBytesInRes, &dwWrite, NULL);
						CloseHandle(hImg);
					}
				}
				else
				{
					COLORREF* pBits = nullptr;
					HBITMAP hBitMap = NULL;
					BITMAPINFO bitmapinfo = { 0 };
					if (!ReadFile(hIconFile, &bitmapinfo.bmiHeader, sizeof(BITMAPINFOHEADER), &dwRead, NULL))
					{
						continue;
					}
					bitmapinfo.bmiHeader.biHeight /= 2;
					bitmapinfo.bmiHeader.biSizeImage = bitmapinfo.bmiHeader.biHeight * bitmapinfo.bmiHeader.biWidth * 4;
					hBitMap = CreateDIBSection(nullptr, &bitmapinfo, DIB_RGB_COLORS, (void**)&pBits, NULL, NULL);
					if (hBitMap == nullptr || !ReadFile(hIconFile, pBits, bitmapinfo.bmiHeader.biSizeImage, &dwRead, NULL))
					{
						continue;
					}
					std::wstring file_name = A2WSTR(fmt::format("{0}_{1}x{2}.png", W2ASTR(jeflib::FileUtil::GetFileBaseNameW(iconPath)), bitmapinfo.bmiHeader.biWidth, bitmapinfo.bmiHeader.biHeight));
					wsave_file = jeflib::FileUtil::JoinPathW(outpath, file_name);
					//创建带alpha通道的Bitmap
					Gdiplus::Bitmap* pimg = DCUtil::CreateAlphaBmpFromHBITMAP(hBitMap);
					if (pimg)
					{
						pimg->Save(wsave_file.c_str(), &pngClsid);
						delete pimg;
					}
					::DeleteObject(hBitMap);
				}

				

			}
			CloseHandle(hIconFile);
			return  0;
		}

PNG合成ICO

static DWORD createIcoByPngs(const std::vector<std::wstring>& pngPaths, const std::wstring& iconPath)
		{
			if (pngPaths.empty() || iconPath.empty()) return -1;

			//打开图标文件  
			HANDLE hIconFile = CreateFileW(iconPath.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if (hIconFile == INVALID_HANDLE_VALUE)
			{
				DWORD dwErrcode = GetLastError();
				return dwErrcode;
			}

			
			//检验png有效性,一个尺寸只保存一张图
			std::map<int, ENTRYDATA> mapSizePngImages;
			for (int i = 0; i < pngPaths.size(); i++)
			{
				ENTRYDATA endata;
				endata.nDataSize = FileUtil::GetFileSizeW(pngPaths[i]);
				if(endata.nDataSize <= 0) continue;
				endata.vFileContent.resize(endata.nDataSize);
				{
					HANDLE hImag = CreateFileW(pngPaths[i].c_str(), GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
					if (hImag != INVALID_HANDLE_VALUE)
					{
						DWORD dwRead = 0;
						ReadFile(hImag, &endata.vFileContent[0], endata.vFileContent.size(), &dwRead, NULL);
						CloseHandle(hImag);
					}
				}
				std::shared_ptr<Gdiplus::Bitmap> pImg = std::shared_ptr<Gdiplus::Bitmap>(Gdiplus::Bitmap::FromFile(pngPaths[i].c_str()));
				if (pImg == nullptr || pImg->GetWidth() != pImg->GetHeight() || pImg->GetWidth() <= 0) continue;
				endata.pImg = pImg;
				endata.strIconPath = pngPaths[i];
				//超过128尺寸直接写入png图片数据,否则写入bitmap,并且计算掩码大小
				//图像32位像素 + 40 header头 +AND mask  
				if (pImg->GetWidth() <= 128)
				{
					endata.nDataSize = pImg->GetWidth() * pImg->GetHeight() * 4 + 40 + (pImg->GetHeight() * ((pImg->GetWidth() + 31) / 32 * 32 / 8));
					endata.vFileContent.resize(0);
					mapSizePngImages[pImg->GetWidth()] = endata;
				}
				else
				{
					mapSizePngImages[256] = endata;
				}
				
			}

			if (mapSizePngImages.empty())
			{
				CloseHandle(hIconFile);
				return -2;
			}

			
			ICONHEADER header;
			DWORD dwWrite = 0;
			//写文件头
			memset(&header, 0, sizeof(ICONHEADER));
			header.idReserved = 0;
			header.idCount = mapSizePngImages.size();
			header.idType = 1;
			WriteFile(hIconFile, &header, sizeof(ICONHEADER), &dwWrite, NULL);

			//建立每一个图标的目录信息存放区域 
			LPICONDIRENTRY pIconDirEntry = (LPICONDIRENTRY)new BYTE[header.idCount * sizeof(ICONDIRENTRY)];
			if (pIconDirEntry == NULL)
			{
				CloseHandle(hIconFile);
				return -3;
			}
			memset(pIconDirEntry, 0, header.idCount * sizeof(ICONDIRENTRY));


			//第一张图标的起始位置
			DWORD offset = header.idCount * sizeof(ICONDIRENTRY) + sizeof(ICONHEADER);

			int index = 0;
			for (auto &it:mapSizePngImages)
			{
				pIconDirEntry[index].bWidth = it.first == 256 ? 0: it.first;
				pIconDirEntry[index].bHeight = it.first == 256 ? 0 : it.first;
				pIconDirEntry[index].wBitCount = 32;
				pIconDirEntry[index].bReserved = 0;
				pIconDirEntry[index].wPlanes = 1;
				pIconDirEntry[index].dwBytesInRes = it.second.nDataSize;
				pIconDirEntry[index].dwImageOffset = offset;
				offset += pIconDirEntry[index].dwBytesInRes;
				++index;
			}

			//写图像信息块
			WriteFile(hIconFile, pIconDirEntry, header.idCount * sizeof(ICONDIRENTRY), &dwWrite, NULL);
			delete []((BYTE*)pIconDirEntry);

		
			//写每一帧的数据
			for (auto& it : mapSizePngImages)
			{
				//大于128直接写入png数据
				if (it.first > 128)
				{
					WriteFile(hIconFile, &it.second.vFileContent[0], it.second.vFileContent.size(), &dwWrite, NULL);
				}	
				else
				{
					BITMAPINFOHEADER bitmapHeader;
					memset(&bitmapHeader, 0, sizeof(BITMAPINFOHEADER));
					bitmapHeader.biWidth = it.first;
					//图像高度(XOR图高度+AND图高度)
					bitmapHeader.biHeight = bitmapHeader.biWidth * 2;
					bitmapHeader.biSize = sizeof(BITMAPINFOHEADER);
					bitmapHeader.biPlanes = 1;
					bitmapHeader.biBitCount = 32;
					bitmapHeader.biSizeImage = it.second.nDataSize;
					WriteFile(hIconFile, &bitmapHeader, sizeof(BITMAPINFOHEADER), &dwWrite, NULL);

					//因为icon宽高相等,都等于it.first
					//XOR mask
					int imageDataSize = it.first * it.first;
					int* imageDataBuf = new int[imageDataSize];
					memset(imageDataBuf, 0, imageDataSize);
					Gdiplus::Color bmpColor;
					for (int y = 0; y < it.first; y++)
					{
						for (int x = 0; x < it.first; x++)
						{
							it.second.pImg->GetPixel(x, y, &bmpColor);
							BYTE a = bmpColor.GetAlpha();
							BYTE r = bmpColor.GetRed();
							BYTE b = bmpColor.GetBlue();
							BYTE g = bmpColor.GetGreen();
							int index = y * it.first + x;
							char* p = (char*)(imageDataBuf + index);
							p[0] = b;
							p[1] = g;
							p[2] = r;
							p[3] = a;
						}
					}
					
					char* body = (char*)imageDataBuf;
					for (int y = it.first - 1; y >= 0; --y)
					{
						for (int x = 0; x < it.first; ++x)
						{
							int index = (y * it.first + x) * 4;
							char c = body[index];
							WriteFile(hIconFile, &c, 1, &dwWrite, NULL);  //Blue
							c = body[index + 1];
							WriteFile(hIconFile, &c, 1, &dwWrite, NULL);  //Green
							c = body[index + 2];
							WriteFile(hIconFile, &c, 1, &dwWrite, NULL);  //Red
							c = body[index + 3];
							WriteFile(hIconFile, &c, 1, &dwWrite, NULL);  //Alpha
						}
					}
					
					delete[]imageDataBuf;

					//AND mask	
					for (int y = 0; y < (it.first * ((it.first + 31) / 32 * 32 / 8)); ++y)
					{
						char c = 0;
						WriteFile(hIconFile, &c, 1, &dwWrite, NULL);
					}
				}
			}
			CloseHandle(hIconFile);
			return  true;
		}

GetEncoderClsid

L"image/bmp" L"image/jpeg"  L"image/gif" L"image/tiff" L"image/png"
		static bool GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
		{
			if (format == nullptr || pClsid == nullptr) return false;
			UINT num = 0;                    //number of image encoders
			UINT size = 0;                   //size of the image encoder array in bytes

			int nRet = Gdiplus::GetImageEncodersSize(&num, &size);
			if (nRet != Gdiplus::Status::Ok)
			{
				return false;
			}

			Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
			if (pImageCodecInfo == NULL)
			{
				return false;
			}

			Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);

			for (int i = 0; i < num; i++)
			{
				if (wcscmp(pImageCodecInfo[i].MimeType, format) == 0)
				{
					*pClsid = pImageCodecInfo[i].Clsid;
					free(pImageCodecInfo);
					return true;
				}
			}

			free(pImageCodecInfo);
			return false;
		}
CreateAlphaBmpFromHBITMAP
		static Gdiplus::Bitmap* CreateAlphaBmpFromHBITMAP(HBITMAP hBmp)
		{
			DIBSECTION  dibsection = { 0 };
			int nBytes = ::GetObject(hBmp, sizeof(DIBSECTION), &dibsection);
			if (dibsection.dsBm.bmBitsPixel != 32)
			{
				return  Gdiplus::Bitmap::FromHBITMAP(hBmp, NULL);
			}

			int	width = dibsection.dsBm.bmWidth;
			int height = abs(dibsection.dsBm.bmHeight);
			int pitch = (((width * dibsection.dsBm.bmBitsPixel) + 31) / 32) * 4;        //计算行宽,四字节对齐 ATL::CImage::ComputePitch // 32位位图不存在对齐问题,so其实没必要
			LPVOID bits = dibsection.dsBm.bmBits;

			if (dibsection.dsBmih.biHeight > 0) // 对于DDB,不会取到dsBmih数据,所以biHeight成员始终为0
			{
				bits = LPBYTE(bits) + ((height - 1) * pitch);
				pitch = -pitch;
			}

			return new Gdiplus::Bitmap(width, height, pitch, PixelFormat32bppARGB, static_cast<BYTE*>(bits));
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值