C++原始BMP转换——将BMP转换为MC像素画以及mcfunction文件

转换示例

介绍

编译器:MinGW GCC 9.3.0,编译命令 -Wall -Wl,--stack=998244353 -std=c++14 -O2

由于在 Devcpp 上配置 OpenCV 过于麻烦,且 OpenCV 难以学习,我便通过 了解BMP格式 + fread读取字节直接在无 OpenCV 的 Devcpp 中用数组存储了 BMP 图像(从 title.bmp 输入),并进行了颜色的近似转换,输出到了大小相同的 BMP 中(fout.bmp),同时输出了该 BMP 的 8进制txt(test.txt),以及对应的mcfunction(title.mcfunction)。

注:只支持24位 BMP 或一些32位 BMP 。

Part 1. 输入BMP

在这里插入图片描述
BMP格式详见:BMP图像文件完全解析

按照字节顺序分别存到对应位置即可。

fread() 可以按照字节读入所有,不过返回的值不一定是正确的串长,所以正确的串长应该是上图中第 3~6 个字节表示的 BMP 大小。

编译器所限,目前可以转换的最大图片为 5000 X 5000 (宽和高都不应超过)。

Part 2. 颜色转换

我在 list.txt 中记录了 MC 中 38 种颜色较纯的方块的 I D \tt ID ID 以及 R G B \tt RGB RGB 颜色参数。

list.txt 文件第一行为方块数量,目前为 38 。后面每一行为一个字符串加上三个 0~255 的整数,分别表示:方块ID、R、G、B 。

以下 list.txt 只适用于 MC java edition 1.17 及以上(因为有“铜块”):

38
white_concrete 255 255 255
orange_concrete 226 97 0
magenta_concrete 171 47 161
light_blue_concrete 31 139 201
yellow_concrete 241 176 13
lime_concrete 95 171 19
pink_concrete 215 101 144
gray_concrete 51 55 59
light_gray_concrete 125 125 115
cyan_concrete 13 119 136
purple_concrete 101 26 158
blue_concrete 40 42 144
brown_concrete 97 58 26
green_concrete 71 90 31
red_concrete 144 27 27
black_concrete 2 3 7
gold_block 254 231 77
diamond_block 112 251 240
emerald_block 65 243 132
waxed_copper_block 214 123 91
waxed_oxidized_copper 110 197 159
purpur_block 186 149 186
terracotta 155 96 69
white_terracotta 208 177 160
orange_terracotta 166 86 34
magenta_terracotta 147 82 106
light_blue_terracotta 113 107 136
yellow_terracotta 187 133 27
lime_terracotta 98 114 45
pink_terracotta 165 74 78
gray_terracotta 55 36 27
light_gray_terracotta 131 102 92
cyan_terracotta 84 89 89
purple_terracotta 121 69 86
blue_terracotta 70 54 88
brown_terracotta 73 45 27
green_terracotta 71 78 33
red_terracotta 136 51 37

适合 1.17 版本前的 list.txt (用两种海晶石近似替换了铜块):

38
white_concrete 255 255 255
orange_concrete 226 97 0
magenta_concrete 171 47 161
light_blue_concrete 31 139 201
yellow_concrete 241 176 13
lime_concrete 95 171 19
pink_concrete 215 101 144
gray_concrete 51 55 59
light_gray_concrete 125 125 115
cyan_concrete 13 119 136
purple_concrete 101 26 158
blue_concrete 40 42 144
brown_concrete 97 58 26
green_concrete 71 90 31
red_concrete 144 27 27
black_concrete 2 3 7
gold_block 254 231 77
diamond_block 112 251 240
emerald_block 65 243 132
purpur_block 186 149 186
terracotta 155 96 69
white_terracotta 208 177 160
orange_terracotta 166 86 34
magenta_terracotta 147 82 106
light_blue_terracotta 113 107 136
yellow_terracotta 187 133 27
lime_terracotta 98 114 45
pink_terracotta 165 74 78
gray_terracotta 55 36 27
light_gray_terracotta 131 102 92
cyan_terracotta 84 89 89
purple_terracotta 121 69 86
blue_terracotta 70 54 88
brown_terracotta 73 45 27
green_terracotta 71 78 33
red_terracotta 136 51 37
prismarine_bricks 105 182 171
dark_prismarine 51 86 72

然后对于原图中的一个像素,将其映射到 RGB 颜色立方体中的一个坐标点,找到 38 种方块中与该像素欧几里得距离最短的方块,就可以比较近似地转换颜色。

Part 3. 输出函数

程序一开始需要在 stdin 中输入三个数,分别是图片面向西边时的右下角 X、Y、Z 坐标,

然后,把每个像素对应的方块通过 setblock <x><y><z> [ID] 的指令形式输出到 fout.mcfunction 就好了。

CODE

代码有彩蛋

#include <iostream>
#include <windows.h>
#define MAXN 5005 // max Hight - 1
#define MAXM 5005 // max Width - 1
#define UINT unsigned int
#define UCHA unsigned char
//---------  OI part  ---------
	#include<map>
	#include<set>
	#include<cmath>
	#include<stack>
	#include<vector>
	#include<cstdio>
	#include<cstring>
	#include<algorithm>
	#define LL long long
	#define ENDL putchar('\n')
	#define DB double
	#define lowbit(x) (-(x) & (x))
	#define FI first
	#define SE second
	LL read() {
		LL f = 1,x = 0;int s = getchar();
		while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
		while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
		return f*x;
	}
	void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
	void putnum(LL x) {
		if(!x) {putchar('0');return ;}
		if(x<0) putchar('-'),x = -x;
		return putpos(x);
	}
	void AIput(LL x,int c) {putnum(x);putchar(c);}
//-----------------------------
using namespace std;
void SetFont(int size = 30) { // Useless function
	CONSOLE_FONT_INFOEX cfi;
	cfi.cbSize = sizeof cfi;
	cfi.dwFontSize.X = 4;
	cfi.dwFontSize.Y = 8;
	SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
}
void putbit(char c) {
	for(int i = (1<<7);i > 0;i >>= 1) {
		putchar((c&i) ? '1':'0');
	}
	putchar(' ');
	return ;
}
UINT n,m,s,o,k;
void Putuint(UCHA *s,UINT x,bool big) {
	if(big) {
		s[3] = x & 255; x >>= 8;
		s[2] = x & 255; x >>= 8;
		s[1] = x & 255; x >>= 8;
		s[0] = x & 255; x >>= 8;
	}
	else {
		s[0] = x & 255; x >>= 8;
		s[1] = x & 255; x >>= 8;
		s[2] = x & 255; x >>= 8;
		s[3] = x & 255; x >>= 8;
	}
	return ;
}
const int CMAXN = MAXN*MAXM+100;
struct PT{
	UCHA B,G,R,A;
	PT(){B=G=R=A=0;}
	PT(int r,int g,int b,int a){B=b;G=g;R=r;A=a;}
	void setcl(int r,int g,int b,int a){B=b;G=g;R=r;A=a;}
	bool isblack() {return !B && !G && !R;}
	int Y() {return (0.299*R)+(0.587*G)+(0.114*B);}
	int S() {
		int mx = max(B,max(G,R));
		if(mx == 0) return 0;
		return (mx-min(B,min(G,R)))*255/mx;
	}
};
PT blc[200];
int rsb[MAXN][MAXM],cntb;
LL dis(PT a,PT b) {
	LL yd = 0;//a.S()-b.S(); //                                                                         模式开关!!!把 "0;//" 去掉可添加"颜色饱和度"作为参数
	return yd*yd+(a.R-b.R)*1ll*(a.R-b.R) + (a.G-b.G)*1ll*(a.G-b.G) + (a.B-b.B)*1ll*(a.B-b.B);
}
char nameid[200][100];
template<const int CMAXN>struct BMP{
	UCHA a[CMAXN];
	int n,m;
	UINT len,pos,header,tp,picsize;
	vector<vector<PT> > mt;
	void Read(const char *s) {
		freopen(s,"r",stdin);
		fread(a,1,CMAXN,stdin);
		// a[0],a[1]: BM
		UINT SIZE = ((UINT)a[5]<<24) | ((UINT)a[4]<<16) | ((UINT)a[3]<<8) | ((UINT)a[2]);
		// a[6~9] = 0
		len = SIZE;
		pos = ((UINT)a[13]<<24) | ((UINT)a[12]<<16) | ((UINT)a[11]<<8) | ((UINT)a[10]); // 54
		header = ((UINT)a[17]<<24) | ((UINT)a[16]<<16) | ((UINT)a[15]<<8) | ((UINT)a[14]); // 40
		m = ((UINT)a[21]<<24) | ((UINT)a[20]<<16) | ((UINT)a[19]<<8) | ((UINT)a[18]); // Width
		n = ((UINT)a[25]<<24) | ((UINT)a[24]<<16) | ((UINT)a[23]<<8) | ((UINT)a[22]); // Hight
		// a[26] = 1 , a[27] = 0
		tp = ((UINT)a[29]<<8) | ((UINT)a[28]); // Color type , a[29] = 0
		// a[30~33] = 0, meaning no compress
		picsize = ((UINT)a[37]<<24) | ((UINT)a[36]<<16) | ((UINT)a[35]<<8) | ((UINT)a[34]);
		// a[38~53] = 0
		UINT ad = pos,el = (4 - (tp>>3)*m%4)%4;
		cerr<<"color_type:"<<tp<<" tail_size:"<<el<<endl;
		mt.resize(n+1);
		for(int i = 1;i <= n;i ++) {
			mt[i].resize(m+1);
			for(int j = 1;j <= m;j ++) {
				mt[i][j].B = a[ad ++];
				mt[i][j].G = a[ad ++];
				mt[i][j].R = a[ad ++];
				if(tp == 32) mt[i][j].A = a[ad ++];
			}
			ad += el;
		}return ;
	}
	void Writ() {
		a[0] = 'B'; a[1] = 'M';
		tp = 24; pos = 54; header = 40; 
		UINT ad = 54,el = (4-3*m%4)%4;
		for(int i = 1;i <= n;i ++,ad += el) {
			for(int j = 1;j <= m;j ++,ad += 3) {
				a[ad] = mt[i][j].B;
				a[ad+1] = mt[i][j].G;
				a[ad+2] = mt[i][j].R;
			}
			for(UINT j = 0;j < el;j ++) a[ad+j] = 0;
		}
		picsize = ad-54; len = ad;
		Putuint(a+2,len,0);
		Putuint(a+6,0u,0);
		Putuint(a+10,pos,0);
		Putuint(a+14,header,0);
		Putuint(a+18,m,0);
		Putuint(a+22,n,0);
		a[26] = 1u; a[27] = 0u;
		a[28] = 24u;a[29] = 0u;
		Putuint(a+30,0u,0);
		Putuint(a+34,picsize,0);
		Putuint(a+38,0u,0);Putuint(a+42,0u,0);Putuint(a+46,0u,0);Putuint(a+50,0u,0);
		return ;
	}
	void WriteBMP(const char *s) {
		freopen(s,"wb",stdout);
		fwrite(a,1,len,stdout);
		return ;
	}
	void WriteMCF(const char *s,bool fill_mt) {
		int xx0,yy0,zz0;
		freopen("CON","r",stdin);
		cerr<<"<x><y><z>:"<<endl;
		xx0 = read(); yy0 = read(); zz0 = read(); // Input initial 3-dimensional coordinate
		freopen(s,"w",stdout);
		printf("fill %d %d %d %d %d %d air\n",xx0,yy0+1,zz0,xx0,yy0+n,zz0-m+1); // Y and Z
		for(int i = 1;i <= n;i ++) {
			for(int j = 1;j <= m;j ++) {
				if(mt[i][j].isblack()) continue;
				LL ds = dis(mt[i][j],blc[0]);
				UINT bli = 0;
				for(int k = 1;k < cntb;k ++) {
					LL d2 = dis(mt[i][j],blc[k]);
					if(d2 < ds) {
						ds = d2; bli = k;
					}
				}
				if(fill_mt) mt[i][j] = blc[bli];
//				printf("setblock %d %d %d %s\n",xx0+j-1,yy0+i-1,zz0,nameid[bli]); // X and Y
				printf("setblock %d %d %d %s\n",xx0,yy0+i,zz0-m+j,nameid[bli]); // Y and Z
			}
		}
		return ;
	}
	void Decrease_size(int dx,int dy) {
		vector<vector<PT> > mt2;
		int n2 = (n+dx-1)/dx,m2 = (m+dy-1)/dy;
		mt2.resize(n2+1);
		for(int i = 1,i2 = 1;i <= n;i += dx,i2 ++) {
			mt2[i2].resize(m2+1);
			for(int j = 1,j2 = 1;j <= m;j += dy,j2 ++) {
				int ct = 0;
				int r = 0,g = 0,b = 0,a = 0;
				for(int s = i;s < i+dx && s <= n;s ++) {
					for(int o = j;o < j+dy && o <= m;o ++) {
						if(mt[s][o].isblack()) continue;
						r += mt[s][o].R;
						g += mt[s][o].G;
						b += mt[s][o].B;
						a += mt[s][o].A;
						ct ++;
					}
				}
				ct = max(ct,1);
				r /= ct; g /= ct;
				b /= ct; a /= ct;
				mt2[i2][j2].setcl(r,g,b,a);
			}
		}
		n = n2;m = m2;
		swap(mt,mt2);
		return ;
	}
};
BMP<CMAXN> A;
char SM(char c) {return c>='A'&&c<='Z' ? (c+'a'-'A'):c;}
int main() {
	freopen("list.txt","r",stdin);
	cntb = read();
	for(int i = 0;i < cntb;i ++) {
		scanf("%s",nameid[i]);
		blc[i].R = read();
		blc[i].G = read();
		blc[i].B = read();
	}
	blc[cntb].R = blc[cntb].G = blc[cntb].B = 0;
	
	freopen("CON","r",stdin);
	cerr<<"Open file: ";
	char ss[105],c = getchar();
	int cnt = 0;
	while(c != '\n') ss[cnt ++] = c,c = getchar();
	ss[cnt] = '\0';
	if(cnt < 4 || ss[cnt-4] != '.' || SM(ss[cnt-3]) != 'b' || SM(ss[cnt-2]) != 'm' || SM(ss[cnt-1]) != 'p') {
		ss[cnt ++] = '.';
		ss[cnt ++] = 'b';
		ss[cnt ++] = 'm';
		ss[cnt ++] = 'p';
		ss[cnt] = '\0';
	}
	A.Read(ss);
	
//	A.Decrease_size(4,4);
	
	freopen("CON","r",stdin);
	cerr<<"Function file: ";
	c = getchar(); cnt = 0;
	while(c != '\n') ss[cnt ++] = c,c = getchar();
	ss[cnt] = '\0';
	for(int i = 0;i < cnt;i ++) {
		if(ss[i] >= 'A' && ss[i] <= 'Z') ss[i] += 'a'-'A';
	}
	A.WriteMCF(ss,1);
	A.Writ();
	A.WriteBMP("fout.bmp");
	return 0;
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值