代码来源
读取格式来自:Icons | Microsoft Learn
rgb打印来自:位图(BMP)文件结构分析以及使用C++实现位图的读写与显示 - 简书 (jianshu.com)
位图转 icon 文件网站:
w在线生成透明ICO图标 - 在线工具 (toollist.net)
Dev-C++ undefined reference to `_imp_SetPixel` 解决方法_为什么找不到标识符setpixel怎么解决-CSDN博客
#include <stdio.h>
#include <windows.h>
#pragma warning(disable : 4996) // visual studio 宏定义,避免移植到Visual studio编译报错
// 核心代码自
//https://learn.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)
//掩码图,其中有 XOR AND 俩图片,都是用一个bit 0表示黑,1表示白,即一个字节对应八个像素
typedef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
// 一个ICON 文件有多个图片,是因为不同机器上识别不同格式图片,这里是紧随ICON文件头之后的图片信息结构体
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
// ICON 文件头
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
// 用于显示 Icon 的全部结构体数据 ,如果正常显示,说明微软的代码靠谱
void showIcon(LPICONDIR pIconDir,LPICONDIRENTRY pIconDirEntry,LPICONIMAGE pIconImage)
{
printf("pIconDir\n");
printf("%0x\n",pIconDir->idReserved);
printf("%0x\n",pIconDir->idType);
printf("%0x\n",pIconDir->idCount);
printf("pIconDirEntry\n");
printf("bHeight = %d\n",pIconDirEntry->bHeight);
printf("bWidth = %d\n",pIconDirEntry->bWidth);
printf("%0x\n",pIconDirEntry->bColorCount);
printf("%0x\n",pIconDirEntry->bReserved);
printf("%0x\n",pIconDirEntry->wPlanes);
printf("%0x\n",pIconDirEntry->wBitCount);
printf("%ld\n",pIconDirEntry->dwBytesInRes);
printf("%ld\n",pIconDirEntry->dwImageOffset);
printf("pIconImage->icHeader\n");
// https://zhuanlan.zhihu.com/p/339055549
// https://blog.csdn.net/wangwenjing90/article/details/8537355
// https://blog.csdn.net/weixin_44350503/article/details/90082061
printf("%ld\n",pIconImage->icHeader.biSize); // biSize = BMP头的info头部分的文件大小(以字节为单位)
// icHeader 结构的 biHeight 成员表示 XOR 和 AND 掩码的组合高度。
printf("icHeader.biHeight = %ld\n",pIconImage->icHeader.biHeight);
printf("icHeader.biWidth = %ld\n",pIconImage->icHeader.biWidth);
printf("%d\n",pIconImage->icHeader.biPlanes); // biPlanes 颜色平面数 https://blog.csdn.net/mmxida/article/details/122362449#:~:text=%E5%9C%A8%E4%B8%80%E4%B8%AA%E9%BB%98%E8%AE%A4%E7%9A%8416%E8%89%B2%E7%9A%84%E8%B0%83%E8%89%B2%E6%9D%BF%E4%B8%AD%EF%BC%8C%E9%A2%9C%E8%89%B2%E8%A2%AB%E7%B3%BB%E7%BB%9F%E6%8C%89%E7%85%A7%E4%B8%80%E5%AE%9A%E7%9A%84%E8%A7%84%E5%88%99%E8%BF%9B%E8%A1%8C%E5%88%86%E9%85%8D%EF%BC%8C%E4%BE%8B%E5%A6%82%E4%BD%8D0%E8%A1%A8%E7%A4%BA%E8%93%9D%E8%89%B2%E9%80%9A%E9%81%93%EF%BC%8C%E4%BD%8D1%E8%A1%A8%E7%A4%BA%E7%BB%BF%E8%89%B2%E9%80%9A%E9%81%93%EF%BC%8C%E4%BD%8D2%E8%A1%A8%E7%A4%BA%E7%BA%A2%E8%89%B2%E9%80%9A%E9%81%93%EF%BC%8C%E4%BD%8D4%E8%A1%A8%E7%A4%BA%E5%BC%BA%E5%BA%A6%E9%80%9A%E9%81%93%E3%80%82%20%E6%A0%B9%E6%8D%AE%E8%BF%99%E4%B8%AA%E8%A7%84%E5%88%99%EF%BC%8C%E8%BF%994%E4%B8%AA%E4%BD%8D%E5%8F%AF%E4%BB%A5%E8%A2%AB%E8%A7%A3%E9%87%8A%E4%B8%BA%EF%BC%9A%E5%BC%BA%E5%BA%A6%E5%B9%B3%E9%9D%A2%EF%BC%8C%E7%BA%A2%E8%89%B2%E5%B9%B3%E9%9D%A2%EF%BC%8C%E7%BB%BF%E8%89%B2%E5%B9%B3%E9%9D%A2%E5%92%8C%E8%93%9D%E8%89%B2%E5%B9%B3%E9%9D%A2%E3%80%82%20%E5%AF%B9%E4%BA%8E%E6%9C%80%E5%90%8E%E4%B8%89%E4%B8%AA%E5%B9%B3%E9%9D%A2%EF%BC%8C%E6%82%A8%E5%8F%AF%E4%BB%A5%E6%83%B3%E8%B1%A1%E6%AF%8F%E4%B8%AA%E5%B9%B3%E9%9D%A2%E9%83%BD%E4%BB%A3%E8%A1%A8%E4%BA%86%E5%A6%82%E6%9E%9C%E5%8F%AA%E6%9C%89%E7%9B%B8%E5%BA%94%E7%9A%84%E7%94%B5%E5%AD%90%E6%9E%AA%E5%9C%A8%E5%8F%91%E5%B0%84%E6%97%B6%E4%BD%A0%E7%9C%8B%E5%88%B0%E7%9A%84%E6%83%85%E5%86%B5%E3%80%82%20%E7%94%B1%E4%BA%8E%E8%BF%99%E6%98%AFEGA%E7%9A%84%E6%9C%AC%E6%9C%BA%E9%A2%9C%E8%89%B2%E6%A0%BC%E5%BC%8F%EF%BC%8C%E5%9B%A0%E6%AD%A4%E9%9C%80%E8%A6%81%E6%9C%89%E4%B8%80%E7%A7%8D%E6%96%B9%E6%B3%95%E5%9C%A8BITMAP%E7%BB%93%E6%9E%84%E4%B8%AD%E8%A1%A8%E8%BE%BE%E8%BF%99%E7%A7%8D%E9%A2%9C%E8%89%B2%E6%A0%BC%E5%BC%8F%EF%BC%8C%E4%BB%A5%E4%BE%BF,Windows%E5%8F%AF%E4%BB%A5%E8%A1%A8%E7%A4%BA%E4%B8%8E%E8%AE%BE%E5%A4%87%E7%9B%B8%E5%85%B3%E7%9A%84%E4%BD%8D%E5%9B%BE%E3%80%82%20%E5%9B%A0%E6%AD%A4%E8%AF%9E%E7%94%9F%E4%BA%86%E5%B9%B3%E9%9D%A2%E9%A2%9C%E8%89%B2%E6%A0%BC%E5%BC%8F%E3%80%82%20%E5%AF%B9%E4%BA%8E16%E8%89%B2%E5%B9%B3%E9%9D%A2%E7%9A%84%E4%BD%8D%E5%9B%BE%EF%BC%8C%E5%B9%B3%E9%9D%A2%E6%95%B0%E4%B8%BA%204%EF%BC%8C%E6%AF%8F%E4%B8%AA%E5%83%8F%E7%B4%A0%E7%9A%84%E4%BD%8D%E6%95%B0%E4%B8%BA%201%E3%80%82
printf("%d\n",pIconImage->icHeader.biBitCount); // biBitCount 每个像素的位数 图像位深度:1,4,8,16,24,32
printf("%ld\n",pIconImage->icHeader.biCompression); // biCompression 图像数据压缩类型:0(BI_RGB,不压缩),1(BI_RLE8,8比特游程编码),2(BI_RLE4,4比特游程编码),3(BI_BITFIELDS,比特域),4(BI_JPEG),5(BI_PNG)
printf("%ld\n",pIconImage->icHeader.biSizeImage); // biSizeImage 图像大小,用BI_RGB格式时,可设置为0.
// https://blog.csdn.net/wangwenjing90/article/details/8537355
printf("%ld\n",pIconImage->icHeader.biXPelsPerMeter); // biXPelsPerMeter 水平分辨率,用于多图片存进 ICON时,在不同设备上像素密集程度不同,用于选取密集程度较接近当前设备的图片 ,但是一般不用管。
printf("%ld\n",pIconImage->icHeader.biYPelsPerMeter); // biYpelxPerMeter 垂直分辨率
printf("%ld\n",pIconImage->icHeader.biClrUsed); // biCLrUsed 颜色索引数,查颜色表 https://blog.csdn.net/OrdinaryMatthew/article/details/114644916
printf("%ld\n",pIconImage->icHeader.biClrImportant); // biClrImortant 对图像显示有重要影响的颜色索引数,0表示都重要
// 文件最顶上的微软教程链接中解释 is Colors 对应颜色表,因为 icHeader的 颜色索引不用了,isColors 成为了索引
printf("pIconImage->icColors[0]\n"); // icColors 成员是 RGBQUAD 的数组。此数组中的元素数是通过检查 icHeader 成员来确定的。
printf("%d\n",pIconImage->icColors[0].rgbRed);
printf("%d\n",pIconImage->icColors[0].rgbGreen);
printf("%d\n",pIconImage->icColors[0].rgbBlue);
printf("%d\n",pIconImage->icColors[0].rgbReserved);
printf("\n");
}
// 按名称复刻,发现isColor就是当前的像素数据
//代码来源 https://www.jianshu.com/p/1e4f302f43d0
void showBitmap(LPICONIMAGE pIconImage) {
HWND hWindow; //窗口句柄
HDC hDc; //绘图设备环境句柄
int yOffset = 150;
hWindow = GetForegroundWindow();
hDc = GetDC(hWindow);
int posX, posY;
for (int i = 0; i < pIconImage->icHeader.biWidth*pIconImage->icHeader.biHeight/2; i++){ // 除以二是因为 biHeight 计算的是掩码图 和色彩图的总数
char blue = pIconImage->icColors[i].rgbBlue; // 读取一个字节的数据 刚好 char 就是一个字节,里面的 0 1 变成 0-255
char green = pIconImage->icColors[i].rgbGreen;
char red = pIconImage->icColors[i].rgbRed;
posX = i % pIconImage->icHeader.biWidth;
posY = pIconImage->icHeader.biHeight - i / pIconImage->icHeader.biWidth + yOffset;
SetPixel(hDc, posX, posY, RGB(red, green, blue));
}
}
int main()
{
LPICONDIR pIconDir; // 用于记录一个完整的ICON 数据头
// https://blog.csdn.net/jeanphorn/article/details/44982273
HANDLE hFile= CreateFile("22.ico",GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING, //打开已存在的文件
FILE_ATTRIBUTE_NORMAL,
NULL);
DWORD dwBytesRead; // 要读取的字节数,DROWD 就是 unsigned _long32 ctrl+鼠标左键可查
// We need an ICONDIR to hold the data
pIconDir = (LPICONDIR)malloc( sizeof( ICONDIR ) );
// Read the Reserved word
dwBytesRead=1;
ReadFile( hFile, &(pIconDir->idReserved), sizeof( WORD ), &dwBytesRead, NULL );
// Read the Type word - make sure it is 1 for icons
dwBytesRead=1;
ReadFile( hFile, &(pIconDir->idType), sizeof( WORD ), &dwBytesRead, NULL );
// Read the count - how many images in this file?
dwBytesRead=1; // deByteRead=1 是指读的 1个 WORD ,就是读取一个字,这里对应读取两个字节因为unsigned short是两个字节
ReadFile( hFile, &(pIconDir->idCount), sizeof( WORD ), &dwBytesRead, NULL );
// Reallocate IconDir so that idEntries has enough room for idCount elements
pIconDir = (LPICONDIR)realloc( pIconDir, ( sizeof( WORD ) * 3 ) +
( sizeof( ICONDIRENTRY ) * pIconDir->idCount ) );
// Read the ICONDIRENTRY elements
ReadFile( hFile, pIconDir->idEntries, pIconDir->idCount * sizeof(ICONDIRENTRY),
&dwBytesRead, NULL );
// Loop through and read in each image
int i;
LPICONIMAGE pIconImage;
for(i=0; i<pIconDir->idCount; i++)
{
// Allocate memory to hold the image
pIconImage =(LPICONIMAGE) malloc( pIconDir->idEntries[i].dwBytesInRes );
// Seek to the location in the file that has the image
SetFilePointer( hFile, pIconDir->idEntries[i].dwImageOffset,
NULL, FILE_BEGIN );
// Read the image data
dwBytesRead=1; // 字节数已经在 dwBytesInRes 中给出了,所以 dwBytesRead 成为 1就是 只读取一次
ReadFile( hFile, pIconImage, pIconDir->idEntries[i].dwBytesInRes,
&dwBytesRead, NULL );
// Here, pIconImage is an ICONIMAGE structure. Party on it :)
//显示读取的数据信息
showIcon(pIconDir,&pIconDir->idEntries[i],pIconImage);
// Then, free the associated memory
showBitmap(pIconImage);
Sleep(3000);
free( pIconImage );
}
// Clean up the ICONDIR memory
free( pIconDir );
return 0;
}