1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
#!/usr/bin/env python3
import argparse
import os
import pickle
import sys
from math import floor
from PIL import Image
from typing import Any, Dict, List, Tuple
# The character range [0x0000, 0xDFFF] is reserved for text.
# The range [0xE000, infinity) is left over for emotes.
EMOTES_LETTER_OFFSET = 0xE000
EMOTES_HEIGHT = 512
EMOTES_TEX_SZ = 4096
def superimpose_image(base_img: Image, overlay_img: Image, position: Tuple[int, int]) -> Image:
base_img.paste(overlay_img, position, overlay_img)
return base_img
def i_to_pos(i, sm_wd, sm_ht, big_wd, big_ht) -> Tuple[int, int]:
x = i * sm_wd % big_wd
row = floor((i * sm_wd) / big_wd)
y = row * sm_ht
return int(x), int(y)
def get_images_from_directory(directory_path: str) -> List[Tuple[Any, str]]:
images = []
for filename in os.listdir(directory_path):
file_path = os.path.join(directory_path, filename)
if os.path.isfile(file_path) and file_path.endswith(".png"):
image = Image.open(file_path).convert("RGBA")
name = os.path.basename(filename).split('.')[0]
images.append((image, name))
return images
def split_resized_image(img, wd: int, ht: int) -> List[Any]:
aspect_ratio = img.width / img.height
width = int(ht * aspect_ratio)
img = img.resize((width, ht))
split_images = []
for i in range(0, img.width, wd):
split_image = img.crop((i, 0, i + wd, ht))
split_images.append(split_image)
return split_images
def resize_image_with_aspect_ratio(img: Image, aspect_ratio: float) -> Image:
original_width, original_height = img.size
new_width = int(original_height * aspect_ratio)
new_height = original_height
return img.resize((new_width, new_height))
def resize_image_to_height(img: Image, height: int) -> Image:
aspect_ratio = img.width / img.height
new_width = int(height * aspect_ratio)
return img.resize((new_width, height))
class EmotesState:
def __init__(self):
self.bits = {}
def load(self, pickle_path):
try:
with open(pickle_path, 'rb') as f:
self.bits = pickle.load(f)
except FileNotFoundError:
print(f"Emotes map does not exist at {pickle_path}",
file=sys.stderr)
# This is quite slow since we do a search and replace (O(n))
# for each keyword O(m) times each variant of said keyword (O(k)).
# Thus total complexity is O(m*n*k). All three of these numbers are
# typically small: m and k typically < 10, n typically < 200.
#
# Naively one might split the input into words, but this only works for
# English-like languages. Eastern Asian languages like Japanese don't
# really divide into words AFAIK so this wouldn't work for them.
#
# Unless the performance becomes a user-reported problem, stick with this
# inefficient but reliable method.
def encode_emotes(self, msg: str):
for keyword, bits in self.bits.items():
bits_str = ""
for bit in bits:
bits_str += chr(bit)
# ALL CAPS
tmp = keyword.upper()
msg = msg.replace(tmp, bits_str)
# lowercase
tmp = keyword.lower()
msg = msg.replace(tmp, bits_str)
# Capitalized
tmp = keyword.lower().capitalize()
msg = msg.replace(tmp, bits_str)
# dashes inserted
tmp = '-'.join(keyword.upper())
msg = msg.replace(tmp, bits_str)
# uppercase, spaces inserted
tmp = ' '.join(keyword.upper())
msg = msg.replace(tmp, bits_str)
# lowercase, spaces inserted
tmp = ' '.join(keyword.lower())
msg = msg.replace(tmp, bits_str)
# uppercase, commas and spaces inserted
tmp = ', '.join(keyword.upper())
msg = msg.replace(tmp, bits_str)
return msg
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("dir", type=str, help="directory to get images from")
parser.add_argument("board_aspect_ratio", help="aspect ratio of a cell in the board")
parser.add_argument("texture_aspect_ratio", help="aspect ratio of a cell in the texture")
parser.add_argument("tex_path", type=str, help="path to save the texture to")
parser.add_argument("pickle_path", type=str, help="path to save the texture index to")
args = parser.parse_args()
directory_path = args.dir
board_aspect_ratio = int(args.board_aspect_ratio)
texture_aspect_ratio = int(args.texture_aspect_ratio)
base_img = Image.new("RGBA", (EMOTES_TEX_SZ, EMOTES_TEX_SZ), (0, 0, 0, 0))
images_and_filenames = get_images_from_directory(directory_path)
i = 0
bits = {} # Dict[str, List[int]]
for img, filename in images_and_filenames:
print(f"Adding {filename}")
img = resize_image_with_aspect_ratio(img, board_aspect_ratio)
img = resize_image_to_height(img, EMOTES_HEIGHT)
img_fragments = split_resized_image(img, int(EMOTES_HEIGHT / texture_aspect_ratio), EMOTES_HEIGHT)
img_bits = [] # List[int]
for img_fragment in img_fragments:
i = i + 1
img_pos = i_to_pos(i,
EMOTES_HEIGHT / texture_aspect_ratio, EMOTES_HEIGHT,
EMOTES_TEX_SZ, EMOTES_TEX_SZ)
print(f"{img_pos}")
superimpose_image(base_img, img_fragment, img_pos)
img_bits.append(EMOTES_LETTER_OFFSET + i)
emote_name = os.path.basename(filename).split('.')[0]
print(f"{emote_name} -> {img_bits}")
bits[emote_name] = img_bits
base_img.save(args.tex_path)
with open(args.pickle_path, 'wb') as f:
pickle.dump(bits, f)
|