init
This commit is contained in:
commit
9809bb55e5
6 changed files with 305 additions and 0 deletions
33
mcdraw.py
Normal file
33
mcdraw.py
Normal 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
20
minecraft-concrete.gpl
Normal 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
57
minecraft-fullblock.gpl
Normal 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
62
minecraft.gpl
Normal 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
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
pillow==8.3.2
|
||||
PyNBT==3.1.0
|
131
schemgen.py
Normal file
131
schemgen.py
Normal 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)
|
Loading…
Reference in a new issue