.cur 鼠标光标编辑器

详解透明贴图和三元光栅操作 - CodeBus

鼠标指针文件格式解析------Windows(二) (qq.com)

[C/C++] RGBA数组生成Windows下的ico文件_c ico格式-CSDN博客

色环设计 - CodeBus

左键绘制

右键选颜色

ctrl+右键设置鼠标热点

F1导出.cur文件

cpp 复制代码
//代码来源:色环https://codebus.cn/luoyh/colorcircle
//代码来源:alpha透明贴图: https://codebus.cn/yangw/transparent-putimage

//代码来源:cur格式:https://mp.weixin.qq.com/s?__biz=MzkwNzMzMjIyNg==&mid=2247486257&idx=1&sn=1656090e498c22f391c00d0857f4b856&chksm=c0db94dcf7ac1dcacd4618240e05595b984a05b55d5f9f44d277aae41f4353b06bfa5e4294c4&cur_album_id=2474385342163419137&scene=189#wechat_redirect
//代码来源:icon格式生成文件:https://blog.csdn.net/jinzhuojun/article/details/8007586 
#include <stdio.h>
#include <graphics.h>
#include<math.h>
#pragma warning(disable : 4996)		// VS2022 对某些函数警告,但是为了方便移植,就无视这些警告 这样 Devc++ VC2010 VS2022 都能跑

#define PI acos(-1.0)

// 鼠标文件解析
//https://mp.weixin.qq.com/s?__biz=MzkwNzMzMjIyNg==&mid=2247486257&idx=1&sn=1656090e498c22f391c00d0857f4b856&chksm=c0db94dcf7ac1dcacd4618240e05595b984a05b55d5f9f44d277aae41f4353b06bfa5e4294c4&cur_album_id=2474385342163419137&scene=189#wechat_redirect
// 代码来源
//https://blog.csdn.net/jinzhuojun/article/details/8007586
// 中文注释添加自 @ bilibili 民用级脑的研发记录
// 结构注释来自 https://learn.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)

// 注意这个格式和下文中的 WrietByte 的对应关系

//typedef struct
//{
//    WORD           idReserved;   // Reserved (must be 0)
//    WORD           idType;       // Resource Type (1 for icons)									// 2 指的是 cur 鼠标静态光标文件
//    WORD           idCount;      // How many images?
//    ICONDIRENTRY   idEntries[1]; // An entry for each image (idCount of 'em)
//} ICONDIR, *LPICONDIR;

//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;
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


// 色环
void DrawColorCircle(int x, int y, int r, int h)
{
	int H = h;
	float S = 1.f;
	float V = 1.f;

	for (int i = 0; i < 360; i++)
	{
		H = int(H + 1) % 360;
		setfillcolor(HSVtoRGB((float)H, S, V));
		solidpie(x - r, y - r, x + r, y + r, i * (PI / 180), (i + 1) * (PI / 180));
	}
}

// 数据写入
void Fwrite(FILE* f, char* data, int byte)				// 一次写入 byte 个字节,且只写一次
{
	if(1!=fwrite(data,byte,1,f))
	{
		perror("fwrite error");
		exit(1);
	}
}
// 数据写入
void WriteByte(FILE* f, unsigned int val,int byte)		// 指定写入几个字节
{
	char data[4];
	assert(byte<=4);									// 如果它的条件返回错误,则终止程序执行------菜鸟教程可查
	memcpy((void*)data,(void*)&val,byte);
	Fwrite(f,data,byte);
}
// 生成cur 格式文件 ,改写自 icon格式文件的生成,仅仅是 wPlane wBitCount 分别作为X,Y热点
void generate_ico_file(const char* filename,char * body,
                       int width, int height, int has_alpha,int HotX,int HotY)
{
	int x,y;
	int index=0;
	int Size=0;
	int offset = 6+ 1*16;								// 6 = 2 +2 +2,这6个byte 是ico,cur通用的文件头。 16= 1+1+1+1 +2 +2,是一个图片的索引占 16 个字节, n个图片的索引占6*16个字节,,n个图片的因为在不同设备上使用不同型号分辨率的图片,这一堆图片都在一个 icon 或cur里
	int bpp=32;											// 32位的位图= 8位 alpha + 8 位 R, 8 位 G, 8 位 B

	FILE* outfile=fopen(filename,"wb");
	if(outfile==NULL)
	{
		perror("fopen error");
		exit(1);
	}
	// icon文件头,类型与图片个数
	WriteByte(outfile,0,2);								// idReserved 保留位,一种格式,可用区分其他文件。
	WriteByte(outfile,1,2);								// idType 文件类型,如果是1 ,就是icon文件,如果是2 则是cur鼠标光标文件,这类文件格式一样。
	WriteByte(outfile,1,2);								// idCount 拥有的图片个数,icon为在不同屏幕上正常显示,会存储多个格式的图片以备选择
	// 具体一个图片的索引信息
	WriteByte(outfile,width,1);							// bWidth
	WriteByte(outfile,height,1);						// bHeight
	WriteByte(outfile,0,1);								// bColorCount
	WriteByte(outfile,0,1);								// bReservered
	WriteByte(outfile,HotX,2);								// wPlanes					// 这里是.cur热点位置 X
	WriteByte(outfile,HotY,2);							// wBitCount					// 这里是.cur热点位置 Y

	Size = 40 + height * ((width + 31) / 32 * 32 / 8 + width * 3);	//Note 4 bytes alignment		// 这里计算文件索引头与图像数据的总数 ,40=4+4+4+2+2+4*6 个字节。 (width+31)/32是计算int 型AND 位图个数,width / 32,但是不能确定%32 的部分,所以+31 然后再除以 32 。1个int 有4个字节,共32bit,每个bit表示一个像素是否被光栅操作 AND 覆盖,行数 * 32计算一共需要多少bit ,/8是确定有多少字节。
	if (bpp == 32)
		Size += height * width;
	WriteByte(outfile,Size, 4);			//dwBytesInRes
	WriteByte(outfile,offset, 4);			//dwImageOffset

	WriteByte(outfile,40, 4);				//biSize
	WriteByte(outfile,width, 4);			//biWidth
	WriteByte(outfile,2 * height, 4);		//biHeight
	WriteByte(outfile,1, 2);				//biPlanes
	WriteByte(outfile,bpp, 2);				//biBitCount
	WriteByte(outfile,0, 4);				//biCompression
	WriteByte(outfile,0, 4);				//biSizeImage
	WriteByte(outfile,0, 4);				//biXPelsPerMeter
	WriteByte(outfile,0, 4);				//biYPelsPerMeter
	WriteByte(outfile,0, 4);				//biClrUsed
	WriteByte(outfile,0, 4);				//biClrImportant

	// XOR mask
	for (y=height - 1 ; y >= 0; --y)   					// 调换打印高度就不会读取了 ,确定为倒置打印,windows倒着读取数据.从左往右,从下往上,所以为了图片倒着读取之后是正的,需要把原图第一行像素数据打印到倒数最后一行
	{
		for (x = 0; x < width; ++x)
		{
			index = (y * width + x) * 4;
			WriteByte(outfile, body[index], 1);        //Blue
			WriteByte(outfile, body[index + 1], 1);    //Green
			WriteByte(outfile, body[index + 2], 1);    //Red
			WriteByte(outfile, has_alpha ? body[index + 3] : 255, 1); //Alpha
		}
	}

	// AND mask
	for (y = 0; y < (height * ((width + 31) / 32 * 32 / 8)); ++y)
	{
		WriteByte(outfile, 1, 1);						// 1 在屏幕上显示图片,0则不显示图片,表示为整个图片没有。 三元光栅操作参考
	}

	fclose(outfile);
}


int main()
{

	initgraph(1640, 980, 1);
	setbkcolor(GREEN);
	cleardevice();

	int** mesh;
	int drawdesklx;					// 绘图板左上角坐标
	int drawdeskly;
	int pixnum;						// 一个正方形瓦片边长像素
	int mapi;						// 一列有多少个小方格
	int mapj;						// 一行有多少个小方格
	int drawmapi;					// 网格坐标
	int drawmapj;
	int drawoldmapi;				// 同一次绘制的前一个涂色的位置
	int drawoldmapj;
	int isdraw;						// 绘制状态
	int drawdeskrx;					// 绘图板右下角坐标
	int drawdeskry;
	int drawsmallflag;
	drawsmallflag=0;
	drawdesklx=200;
	drawdeskly=200;
	mapi=32;						// 默认 64*64 大小的  .cur文件	// 这里改参数只会改写分辨率,一个像素对应屏幕一个或多个像素,但不会改变图标大小。数值过大会生成失败,原因是int数值上限比较小,不够存储数据了
	mapj=32;														// 这里改参数只会改写分辨率,一个像素对应屏幕一个或多个像素,但不会改变图标大小。 数值过大会生成失败,原因是int数值上限比较小,不够存储数据了
	drawmapi=-1;
	drawmapj=-1;
	pixnum = 16 ;
	drawoldmapi=-1;
	drawoldmapj=-1;
	drawdeskrx=drawdesklx+pixnum*mapj;				// 瓦片个数 * 一个瓦片的像素 = 画板总长度
	drawdeskry=drawdeskly+pixnum*mapi;


	int left=0;								// 刷新绘图板的当前瓦片位置坐标
	int top=0;
	int pentake=0x8FFF0000;					// 选中的 ARGB 透明度与颜色
	int image[mapi * mapj];					// 分配像素内存
	int i, j;

	int background = 0x0000CCFF;			// 背景色:天依蓝,颜色按十六进制数
	int HotX=0;								// 鼠标热点位置
	int HotY=0;

	mesh = new int* [mapi];
	for(int i=0; i<mapi; i++)
		mesh[i]=new int[mapj];

	for(int i=0; i<mapi; i++)
	{
		for(int j=0; j<mapj; j++)
		{
			mesh[i][j]=0;						// 加入默认的背景色

		}
	}

	int h = 0;
	DrawColorCircle(800+640 / 4, 480 / 2, 140, h);		// 绘制色环
	setfillcolor(RGB(128, 126, 129));




	for (int i = 0; i < mapi ; i++)
	{
		left = 0;
		for (int j = 0; j < mapj ; j++)
		{
			int pennumber = mesh[i][j];					// 读取游戏大地图数组序号



//https://codebus.cn/yangw/transparent-putimage
//透明色混合原理与透明色计算代码来源
			int sa = ((pennumber & 0xff000000) >> 24);
			int sr = ((pennumber & 0xff0000) >> 16);	// 源值已经乘过了透明系数
			int sg = ((pennumber & 0xff00) >> 8);		// 源值已经乘过了透明系数
			int sb =   pennumber & 0xff;				// 源值已经乘过了透明系数
			int dr = ((background& 0xff0000) >> 16);
			int dg = ((background & 0xff00) >> 8);
			int db =   background & 0xff;
			int dst = ((sr + dr * (255 - sa) / 255) << 16)		// sr = 0 dr =255, sa=0, dst->red = 255,即sa = 0 时,dr 占优。sr = 255, dr=0, sa=255, dst->red = 255, sa=255时,sr 占优
			          | ((sg + dg * (255 - sa) / 255) << 8)
			          |  (sb + db * (255 - sa) / 255);

			setfillcolor(BGR(dst));								// COLORREF 的颜色格式是 ABGR ,需要把 RGB 转换成 BGR 格式,然后打印出画板的颜色和保存的图片颜色一样,图片按ARGB 保存,但是画板按ABGR打印
			fillrectangle(drawdesklx+left, drawdeskly+top, drawdesklx+left + pixnum, drawdeskly+top + pixnum);

			left += pixnum;										// 往右移动,准备下一次绘制位置,
		}
		top += pixnum;											// 往下移动,准备下一次绘制位置
	}


	char icon[200]="图标预览";
	setbkmode(TRANSPARENT);													// 设置背景为透明
	settextcolor(BLACK);													// 设置字颜色为黑
	outtextxy(40,160,icon);													// 打印提示

	setfillcolor(GREEN);
	fillrectangle(10,100,300,140) ;											// 覆盖之前的文字提示
	char tips[200];
	sprintf(tips,"热点位置 HotX HotY = %d,%d",HotX,HotY);					// 格式化数字
	setbkmode(TRANSPARENT);													// 设置背景为透明
	settextcolor(BLACK);													// 设置字颜色为黑
	outtextxy(10,100,tips);													// 打印提示



	ExMessage m;
	while(1)
	{
		while(peekmessage(&m,EX_KEY|EX_MOUSE,1))
		{
			switch (m.message)
			{
				case WM_LBUTTONDOWN:
					if(m.x>drawdesklx&&m.y>drawdeskly&&m.x<drawdeskrx&&m.y<drawdeskry)										// 绘制
					{
						isdraw=1;
						drawmapi=-1;
						drawmapj=-1;
					}
					break;
				case WM_LBUTTONUP:

					isdraw=0;
					drawoldmapi=-1;
					drawoldmapj=-1;
					drawsmallflag=0;
					break;
				case WM_RBUTTONDOWN:
					if(m.x>drawdesklx&&m.y>drawdeskly&&m.x<drawdeskrx&&m.y<drawdeskry&&m.ctrl)										// 绘制
					{
						HotY=(m.y-drawdeskly)/pixnum;
						HotX=(m.x-drawdesklx)/pixnum;
						setfillcolor(GREEN);
						fillrectangle(10,100,300,140) ;											// 覆盖之前的文字提示
						char tips[200];
						sprintf(tips,"热点位置 HotX HotY = %d,%d",HotX,HotY);					// 格式化数字
						setbkmode(TRANSPARENT);													// 设置背景为透明
						settextcolor(BLACK);													// 设置字颜色为黑
						outtextxy(10,100,tips);													// 打印提示
					}
					else
					{
						pentake = getpixel(m.x,m.y);
						setfillcolor(pentake);
						fillrectangle(800,500,900,600);
						pentake=pentake|0xFF000000;											// 要颜色转换完毕前再补充alpha 通道,否则半透明
						pentake=BGR(pentake);												// 因为 getpixel 已经是 BGR了,但是在后面的上色还个BGR转换,所以还需要再转换一次BGR来抵消后面的BGR转换。
						// BGR转换是因为保存数据时是 RGB 保存,但是当绘制图像时,却是蓝色红色相反,所以绘制的时候要调整蓝色红色,保存的是按RGB来 
						setfillcolor(GREEN);
						fillrectangle(800,400,1140,440);
						char colortips[200];
						sprintf(colortips,"new alpha:%d,red:%d,green:%d,blue:%d",(pentake&0xFF000000)>>24,(pentake&0x00FF0000)>>16,(pentake&0x0000FF00)>>8,(pentake&0x000000FF));
						outtextxy(800,400,colortips);										// 打印颜色提示

					}

					break;
				case WM_KEYDOWN:
					switch (m.vkcode)
					{
						case VK_F1:
							int width=mapj;
							int height=mapi;
							for (i = 0; i < height; ++i)
							{
								for (j = 0; j < width; ++j)
								{
									image[i * width + j]  = mesh[i][j];  										// 透明色 叠加 网格数据
								}																				// Alpha 透明 0x00, red: 00, green: 00, blue: 00  各 bit 位对应 ARGB
							}																					// 0x00000000是完全透明 0x5F000000 可以看出来不完全透明,可知透明度可调节
							generate_ico_file("testv8_透明度测试.cur", (char *)image, width, height, 1,HotX,HotY);
							printf("Save cur,文件名:testv8_透明度测试.cur\n");
							break;
					}
			}
		}
		if(isdraw)
		{
			if(m.x>drawdesklx&&m.y>drawdeskly&&m.x<drawdeskrx&&m.y<drawdeskry)
			{
				drawmapi=(m.y-drawdeskly)/pixnum;
				drawmapj=(m.x-drawdesklx)/pixnum;
				if(drawmapi!=drawoldmapi||drawmapj!=drawoldmapj)		// 检测是否是旧位置
				{
					mesh[drawmapi][drawmapj]=pentake;
					drawoldmapi=drawmapi;
					drawoldmapj=drawmapj;
					drawsmallflag=1;
				}
			}
		}


		if(drawsmallflag==1)
		{
			top=0;
			left=0;
			for (int i = 0; i < mapi ; i++)
			{
				left = 0;
				for (int j = 0; j < mapj ; j++)
				{
					int pennumber = mesh[i][j];							// 读取游戏大地图数组序号
//https://codebus.cn/yangw/transparent-putimage
//透明色混合原理与透明色计算代码来源
					int sa = ((pennumber & 0xff000000) >> 24);
					int sr = ((pennumber & 0xff0000) >> 16);	// 源值已经乘过了透明系数
					int sg = ((pennumber & 0xff00) >> 8);		// 源值已经乘过了透明系数
					int sb =   pennumber & 0xff;				// 源值已经乘过了透明系数
					int dr = ((background& 0xff0000) >> 16);
					int dg = ((background & 0xff00) >> 8);
					int db =   background & 0xff;

					int dst = ((sr + dr * (255 - sa) / 255) << 16)			// sr = 0 dr =255, sa=0, dst->red = 255,即sa = 0 时,dr 占优。sr = 255, dr=0, sa=255, dst->red = 255, sa=255时,sr 占优
					          | ((sg + dg * (255 - sa) / 255) << 8)
					          |  (sb + db * (255 - sa) / 255);
					setfillcolor(BGR(dst));								// COLORREF 的颜色格式是 ABGR ,需要把 RGB 转换成 BGR 格式,然后打印出画板的颜色和保存的图片颜色一样,图片按ARGB 保存,但是画板按ABGR打印
					fillrectangle(drawdesklx+left, drawdeskly+top, drawdesklx+left + pixnum, drawdeskly+top + pixnum);

					left += pixnum;										// 往右移动,准备下一次绘制位置,
				}
				top += pixnum;											// 往下移动,准备下一次绘制位置
			}

			top=0;
			for (int i = 0; i < mapi ; i++)
			{
				left = 0;
				for (int j = 0; j < mapj ; j++)
				{
					int pennumber = mesh[i][j];							// 读取游戏大地图数组序号
//https://codebus.cn/yangw/transparent-putimage
//透明色混合原理与透明色计算代码来源
					int sa = ((pennumber & 0xff000000) >> 24);
					int sr = ((pennumber & 0xff0000) >> 16);	// 源值已经乘过了透明系数
					int sg = ((pennumber & 0xff00) >> 8);		// 源值已经乘过了透明系数
					int sb =   pennumber & 0xff;				// 源值已经乘过了透明系数
					int dr = ((background& 0xff0000) >> 16);
					int dg = ((background & 0xff00) >> 8);
					int db =   background & 0xff;

					int dst = ((sr + dr * (255 - sa) / 255) << 16)			// sr = 0 dr =255, sa=0, dst->red = 255,即sa = 0 时,dr 占优。sr = 255, dr=0, sa=255, dst->red = 255, sa=255时,sr 占优
					          | ((sg + dg * (255 - sa) / 255) << 8)
					          |  (sb + db * (255 - sa) / 255);

					// 绘制预览图
					putpixel(drawdesklx-64+left,drawdeskly-32+top,BGR(dst));						// COLORREF 的颜色格式是 ABGR ,需要把 RGB 转换成 BGR 格式,然后打印出画板的颜色和保存的图片颜色一样,图片按ARGB 保存,但是画板按ABGR打印

					left += 1;										// 往右移动,准备下一次绘制位置,
				}
				top += 1;											// 往下移动,准备下一次绘制位置
			}
		}



		Sleep(2);
	}

	return 0;
}
相关推荐
春日见3 小时前
车辆动力学:前后轮车轴
java·开发语言·驱动开发·docker·计算机外设
PHOSKEY4 小时前
光子精密QM系列闪测仪在鼠标电路板部件质量控制中的核心应用
计算机外设
墩墩冰6 小时前
计算机图形学 分析选择缓冲区中的数字
计算机外设
LYOBOYI1236 小时前
vscode界面美化
ide·vscode·编辑器
浔川python社7 小时前
关于浔川代码编辑器 v5.0 网页版上线时间的通知
编辑器
UI设计兰亭妙微10 小时前
中车株州所显示器界面设计
计算机外设·界面设计
墩墩冰11 小时前
计算机图形学 多视区的显示
计算机外设
浔川python社11 小时前
浔川代码编辑器 v5.0 上线时间公布
编辑器
墩墩冰12 小时前
计算机图形学 GLU库中的二次曲面函数
计算机外设
墩墩冰13 小时前
计算机图形学 利用鼠标实现橡皮筋技术
计算机外设