summaryrefslogtreecommitdiffstats
path: root/Scripts/emotes_v2.py
blob: a9c037f9040b21da08560102901c292f373c6dee (plain)
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)