[windows] cur 파일

cur 파일은 Windows 운영체제에서 마우스 커서로 사용되는 리소스파일이다.
인터넷 변환기가 있으나, 여러개를 동시에 만드는건 돈을 내야 해서 직접 포맷을 보고 구현하였다.
아래는 cur 파일의 포맷이다.

아래는 RGBA의 값을 가지는 cv::Mat을 cur 파일 버퍼로 만드는 소스이다.


#include<opencv2/opencv.hpp>
#pragma pack(push,1)
struct CUR_Entires {
	unsigned char width;
	unsigned char height;
	unsigned char color_count;
	unsigned char reserved;
	unsigned short x_hot_spot;
	unsigned short y_hot_spot;
	unsigned int size_in_bytes;
	unsigned int file_offset;
};
struct CUR_InfoHeader {
	unsigned int size;
	unsigned int width;
	unsigned int height;
	unsigned short planes;
	unsigned short bit_count;
	unsigned int compression;
	unsigned int image_size;
	unsigned int x_pixels_per_M;
	unsigned int y_pixels_per_M;
	unsigned int colors_used;
	unsigned int colors_important;
};
struct CUR_Header {
	short reserved = 0;
	short type = 2;
	short count = 1;
	CUR_Entires entires;
	CUR_InfoHeader info_header;
};
#pragma pack(pop)

uchar* ToCur(cv::Mat img) {
	//https://www.daubnet.com/en/file-format-cur
	if (img.channels() != 4) {
		return nullptr;
	}
	if (img.cols != 32 && img.rows != 32) {
		return nullptr;
	}
	int W = 32;
	int H = 32;
	CUR_Header header;
	header.reserved = 0;
	header.type = 2;
	header.count = 1;
	header.entires.width = 32;
	header.entires.height = 32;
	header.entires.color_count = 0;
	header.entires.reserved = 0;
	header.entires.x_hot_spot = 15;
	header.entires.y_hot_spot = 15;
	header.entires.size_in_bytes = 40 + 4 * (W*H) + (W*H) / 8;
	header.entires.file_offset = 22;
	header.info_header.size = 40;
	header.info_header.width = 32;
	header.info_header.height = 64;
	header.info_header.planes = 1;
	header.info_header.bit_count = 32;
	header.info_header.compression = 0;
	header.info_header.image_size = (W*H) * 4;
	header.info_header.x_pixels_per_M = 0;
	header.info_header.y_pixels_per_M = 0;
	header.info_header.colors_used = 0;
	header.info_header.colors_important = 0;
	int size = sizeof(header) + (W*H) * 4 + (W*H) / 8;
	uchar* cur = (uchar*)calloc(size, sizeof(uchar));
	uchar* p_header = cur;
	uchar* p_colors = cur + sizeof(header);
	unsigned int* p_monochrome = (unsigned int*)(cur + (sizeof(header) + (W*H) * 4));

	memcpy(p_header, &header, sizeof(header));

	for (int y = 31; y >= 0; y--) {
		for (int x = 31; x >= 0; x--) {
			cv::Vec4b& c = img.at<cv::Vec4b>(y, x);
			*p_colors++ = c[0];
			*p_colors++ = c[1];
			*p_colors++ = c[2];
			*p_colors++ = 0;
			if (c[3] == 255) {
				*p_monochrome |= 1 << x;
			}
		}
		char* cm = (char*)p_monochrome;
		cm[0] ^= cm[3];
		cm[3] ^= cm[0];
		cm[0] ^= cm[3];
		cm[1] ^= cm[2];
		cm[2] ^= cm[1];
		cm[1] ^= cm[2];
		p_monochrome++;
	}
	return cur;
}
int main(){
    cv::Mat img = cv::imread("arrow.png", cv::IMREAD_UNCHANGED);
    FILE* fp = fopen(path.c_str(), "wb");
	fwrite(cur, 4294, 1, fp);
	fclose(fp);
	free(cur);
    return 0;
}

References