This commit is contained in:
JerryXiao 2021-09-30 13:18:38 +08:00
commit 9809bb55e5
Signed by: Jerry
GPG key ID: 22618F758B5BE2E5
6 changed files with 305 additions and 0 deletions

33
mcdraw.py Normal file
View file

@ -0,0 +1,33 @@
from PIL import ImageFont, Image, ImageDraw
import re
try:
font = ImageFont.truetype("wqy-zenhei.ttc", 24, 1)
emfont = ImageFont.truetype("NotoEmoji-Regular.ttf", 24, 0)
except OSError:
print("Please download these fonts to the current dir:\n"
"wqy-zenhei.ttc\n\tfrom https://downloads.sourceforge.net/project/wqy/wqy-zenhei/0.9.45%20%28Fighting-state%20RC1%29/wqy-zenhei-0.9.45.tar.gz"
"\n"
"NotoEmoji-Regular.ttf\n\tfrom https://github.com/googlefonts/noto-emoji/raw/2f1ffdd6fbbd05d6f382138a3d3adcd89c5ce800/fonts/NotoEmoji-Regular.ttf"
"\n\n"
)
raise
def text_to_im(text="紧密团结在鱼塔同志周围"):
emoji = re.compile("^[" u"\U0001F600-\U0001F64F" u"\U0001F300-\U0001F5FF" u"\U0001F680-\U0001F6FF" u"\U0001F1E0-\U0001F1FF" "]$", flags=re.UNICODE)
brush_location = [0]
def draw_single_char(ch, draw=None):
m = emoji.match(ch)
myfont = emfont if m else font
width = myfont.getsize(ch)[0]
if draw:
draw.text((brush_location[0], -2 if m else 0), ch, fill="black", font=myfont, spacing=0)
brush_location[0] += width
return width
def max_height(text):
return max(*[font.getsize(ch)[1] for ch in text], *[emfont.getsize(ch)[1] for ch in text])
width, height = sum(draw_single_char(ch) for ch in text), max_height(text)
im = Image.new('1', (width, height), color="white")
draw = ImageDraw.Draw(im)
for ch in text:
draw_single_char(ch, draw)
return im

20
minecraft-concrete.gpl Normal file
View file

@ -0,0 +1,20 @@
GIMP Palette
Name: minecraft-concrete
Columns: 0
#
208 214 215 white_concrete
226 98 0 orange_concrete
170 45 160 magenta_concrete
30 137 198 light_blue_concrete
242 176 13 yellow_concrete
96 171 19 lime_concrete
214 100 143 pink_concrete
51 55 59 gray_concrete
126 126 116 light_gray_concrete
15 121 138 cyan_concrete
101 26 158 purple_concrete
40 42 144 blue_concrete
97 59 27 brown_concrete
71 90 31 green_concrete
144 29 29 red_concrete
2 3 7 black_concrete

57
minecraft-fullblock.gpl Normal file
View file

@ -0,0 +1,57 @@
GIMP Palette
Name: minecraft-fullblock
Columns: 0
#
247 233 163 smooth_sandstone
199 199 199 mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]
255 0 0 redstone_block
160 160 255 packed_ice
167 167 167 iron_block
0 124 0 oak_leaves[distance=7,persistent=true]
255 255 255 white_concrete
164 168 184 clay
151 109 77 dirt
112 112 112 stone
143 119 72 oak_planks
255 252 245 quartz_block
216 127 51 orange_concrete
178 76 216 magenta_concrete
102 153 216 light_blue_concrete
229 229 51 yellow_concrete
127 204 25 lime_concrete
242 127 165 pink_concrete
76 76 76 gray_concrete
153 153 153 light_gray_concrete
76 127 153 cyan_concrete
127 63 178 purple_concrete
51 76 178 blue_concrete
102 76 51 brown_concrete
102 127 51 green_concrete
153 51 51 red_concrete
25 25 25 black_concrete
250 238 77 gold_block
92 219 213 diamond_block
74 128 255 lapis_block
0 217 58 emerald_block
112 2 0 netherrack
209 177 161 white_terracotta
159 82 36 orange_terracotta
149 87 108 magenta_terracotta
112 108 138 light_blue_terracotta
186 133 36 yellow_terracotta
103 117 53 lime_terracotta
160 77 78 pink_terracotta
57 41 35 gray_terracotta
135 107 98 light_gray_terracotta
87 92 92 cyan_terracotta
122 73 88 purple_terracotta
76 62 92 blue_terracotta
76 50 35 brown_terracotta
76 82 42 green_terracotta
142 60 46 red_terracotta
37 22 16 black_terracotta
189 48 49 crimson_nylium
148 63 97 crimson_planks
22 126 134 warped_nylium
58 142 140 warped_planks
20 180 133 warped_wart_block

62
minecraft.gpl Normal file
View file

@ -0,0 +1,62 @@
GIMP Palette
Name: minecraft
Columns: 0
#
127 178 56 grass_block[snowy=false]
247 233 163 smooth_sandstone
199 199 199 mushroom_stem[east=true,west=true,north=true,south=true,up=true,down=true]
255 0 0 redstone_block
160 160 255 packed_ice
167 167 167 iron_block
0 124 0 oak_leaves[distance=7,persistent=true]
255 255 255 white_concrete
164 168 184 clay
151 109 77 dirt
112 112 112 stone
64 64 255 water[level=0]
143 119 72 oak_planks
255 252 245 quartz_block
216 127 51 orange_concrete
178 76 216 magenta_concrete
102 153 216 light_blue_concrete
229 229 51 yellow_concrete
127 204 25 lime_concrete
242 127 165 pink_concrete
76 76 76 gray_concrete
153 153 153 light_gray_concrete
76 127 153 cyan_concrete
127 63 178 purple_concrete
51 76 178 blue_concrete
102 76 51 brown_concrete
102 127 51 green_concrete
153 51 51 red_concrete
25 25 25 black_concrete
250 238 77 gold_block
92 219 213 diamond_block
74 128 255 lapis_block
0 217 58 emerald_block
129 86 49 podzol[snowy=false]
112 2 0 netherrack
209 177 161 white_terracotta
159 82 36 orange_terracotta
149 87 108 magenta_terracotta
112 108 138 light_blue_terracotta
186 133 36 yellow_terracotta
103 117 53 lime_terracotta
160 77 78 pink_terracotta
57 41 35 gray_terracotta
135 107 98 light_gray_terracotta
87 92 92 cyan_terracotta
122 73 88 purple_terracotta
76 62 92 blue_terracotta
76 50 35 brown_terracotta
76 82 42 green_terracotta
142 60 46 red_terracotta
37 22 16 black_terracotta
189 48 49 crimson_nylium
148 63 97 crimson_planks
92 25 29 crimson_hyphae[axis=y]
22 126 134 warped_nylium
58 142 140 warped_planks
86 44 62 warped_hyphae[axis=y]
20 180 133 warped_wart_block

2
requirements.txt Normal file
View file

@ -0,0 +1,2 @@
pillow==8.3.2
PyNBT==3.1.0

131
schemgen.py Normal file
View file

@ -0,0 +1,131 @@
from PIL import Image
from pathlib import Path
from itertools import zip_longest
import re
def read_palette(text):
palette = dict()
metadata = dict()
in_body = False
for line in text.split('\n'):
line = line.strip()
if not line or line == "GIMP Palette":
continue
if line == '#':
in_body = True
continue
if in_body:
*rgb, name = line.split()
r, g, b = [int(x) for x in rgb]
index = len(palette)
palette[f"{r:02x}{g:02x}{b:02x}"] = {"rgb": (r, g, b), "name": name if name.startswith("minecraft:") else f"minecraft:{name}", "index": index}
else:
k, *v = line.split(':')
k, v = k.strip(), ':'.join(v).strip()
metadata[k] = v
return {"metadata": metadata, "palette": palette}
def read_imtuple(impath, palette_path):
palette = read_palette(Path(palette_path).read_text())
im = Image.open(impath)
im = im.rotate(180)
im = im.convert('RGB')
used_color_hex = tuple(set(f"{r:02x}{g:02x}{b:02x}" for r, g, b in im.getdata()))
used_mc_palette = tuple(palette["palette"][h]["name"] for h in used_color_hex)
imdata = (used_color_hex.index(f"{r:02x}{g:02x}{b:02x}") for r, g, b in im.getdata())
#imtuple = tuple(zip_longest(*([iter(imdata)] * im.width), fillvalue=0))
#assert len(imtuple) == im.height
imtuple = imdata
return (used_mc_palette, imtuple, im.width, im.height)
def read_text(text):
from mcdraw import text_to_im
im = text_to_im(text)
used_mc_palette = ('minecraft:air', 'minecraft:stone')
imdata = (0 if i else 1 for i in im.getdata())
imtuple = imdata
return (used_mc_palette, imtuple, im.width, im.height)
import gzip
from pynbt import NBTFile, TAG_Int, TAG_Compound, TAG_Short, TAG_Byte_Array, TAG_List, TAG_Int_Array
from io import BytesIO
import struct
class VarInt:
MAX_BYTES = 5
def __init__(self, io, set_value=None):
self._io = io
if set_value:
self.setval(set_value)
def setval(self, value):
ret = bytes()
while True:
byte = value & 0x7F
value >>= 7
ret += struct.pack("B", byte | (0x80 if value > 0 else 0))
if value == 0:
break
if len(ret) > self.MAX_BYTES:
raise ValueError("Tried to write too long of a VarInt")
self._io.write(ret)
@property
def value(self):
number = 0
bytes_encountered = 0
while True:
byte = self._io.read(1)
if len(byte) < 1:
raise EOFError("Unexpected end of message.")
byteord = ord(byte)
number |= (byteord & 0x7F) << 7 * bytes_encountered
if not byteord & 0x80:
break
bytes_encountered += 1
if bytes_encountered > self.MAX_BYTES:
raise ValueError("Tried to read too long of a VarInt")
return number
def assemble_nbtfile(used_mc_palette, imtuple, width, height):
blackdataio = BytesIO()
varint = VarInt(blackdataio)
for i in imtuple:
varint.setval(i)
nbtvalue = {
'Version': TAG_Int(2),
'PaletteMax': TAG_Int(len(used_mc_palette)),
'Palette': TAG_Compound(
{name: TAG_Int(i) for i, name in enumerate(used_mc_palette)}
),
'Metadata': TAG_Compound({
'WEOffsetX': TAG_Int(0),
'WEOffsetY': TAG_Int(0),
'WEOffsetZ': TAG_Int(0)
}),
'Width': TAG_Short(width),
'Height': TAG_Short(1),
'Length': TAG_Short(height),
'DataVersion': TAG_Int(2586),
'BlockData': TAG_Byte_Array(blackdataio.getvalue()),
'BlockEntities': TAG_List(TAG_Compound),
'Offset': TAG_Int_Array([0, 0, 0])
}
nbt = NBTFile(value=nbtvalue, name='Schematic')
return nbt
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='schemgen')
parser.add_argument('-o', '--output', default='output.schem', help='output worldedit schema file')
parser.add_argument('-i', '--input', type=str, default="input.png", help='input image')
parser.add_argument('-t', '--text', type=str, default="", help='input text')
parser.add_argument('-p', '--palette', type=str, default="minecraft.gpl", help='gimp palette file')
args = parser.parse_args()
if args.text:
nbt = assemble_nbtfile(*read_text(args.text))
else:
nbt = assemble_nbtfile(*read_imtuple(args.input, args.palette))
print(nbt.pretty())
with gzip.open(args.output, 'w') as f:
nbt.save(f)