Skip to content

Commit 41cfd0c

Browse files
authored
Merge pull request #78 from brad-colbert/vbxe_v0
v1.4.0 cont.
2 parents 621e16f + 51c68c2 commit 41cfd0c

File tree

1 file changed

+105
-14
lines changed

1 file changed

+105
-14
lines changed

server/yeet_to_yail.py

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@
2626
SOCKET_WAIT_TIME = 1
2727
GRAPHICS_8 = 2
2828
GRAPHICS_9 = 4
29+
GRAPHICS_VBXE_1 = 0x11
2930
GRAPHICS_RANDOM = 42
3031
YAIL_W = 320
3132
YAIL_H = 220
3233

34+
DL_BLOCK = 0x04
35+
XDL_BLOCK = 0x05
36+
PALETTE_BLOCK = 0x06
37+
IMAGE_BLOCK = 0x07
38+
39+
3340
# The yail_data will contain the image that is to be sent. It
3441
# is protected with a Mutex so that when the image is being sent
3542
# it won't be written by the server.
@@ -41,6 +48,40 @@
4148
filenames = []
4249
camera_name = None
4350

51+
def prep_image_for_vbxe(image: Image.Image, target_width: int=YAIL_W, target_height: int=YAIL_H) -> Image.Image:
52+
logger.info(f'Image size: {image.size}')
53+
54+
# Calculate the new size preserving the aspect ratio
55+
image_ratio = image.width / image.height
56+
target_ratio = target_width / target_height
57+
58+
if image_ratio > target_ratio:
59+
# Image is wider than target, fit to width
60+
new_width = target_width
61+
new_height = int(target_width / image_ratio)
62+
else:
63+
# Image is taller than target, fit to height
64+
new_width = int(target_height * image_ratio)
65+
new_height = target_height
66+
67+
# Resize the image
68+
image = image.resize((new_width, new_height), Image.BILINEAR)
69+
logger.info(f'Image new size: {image.size}')
70+
71+
# Create a new image with the target size and a black background
72+
new_image = Image.new('RGB', (target_width, target_height), (0, 0, 0))
73+
74+
# Calculate the position to paste the resized image onto the black background
75+
paste_x = (target_width - image.width) // 2
76+
paste_y = (target_height - image.height) // 2
77+
78+
# Paste the resized image onto the black background
79+
new_image.paste(image, (paste_x, paste_y))
80+
81+
# Replace the original image with the new image
82+
return new_image
83+
84+
4485
def fix_aspect(image: Image.Image, crop: bool=False) -> Image.Image:
4586
aspect = YAIL_W/YAIL_H # YAIL aspect ratio
4687
aspect_i = 1/aspect
@@ -120,6 +161,28 @@ def convertToYai(image_data: bytearray, gfx_mode: int) -> bytearray:
120161

121162
return image_yai
122163

164+
def convertToYaiVBXE(image_data: bytes, palette_data: bytes, gfx_mode: int) -> bytearray:
165+
import struct
166+
167+
#ttlbytes = YAIL_W * YAIL_H; # image_data.shape[0] * image_data.shape[1]
168+
logger.info('Image data size: %d' % len(image_data))
169+
logger.info('Palette data size: %d' % len(palette_data))
170+
171+
image_yai = bytearray()
172+
image_yai += bytes([1, 4, 0]) # version
173+
image_yai += bytes([gfx_mode]) # Gfx mode (8,9)
174+
image_yai += struct.pack("<B", 2) # number of memory blocks
175+
image_yai += bytes([PALETTE_BLOCK]) # Memory block type
176+
image_yai += struct.pack("<I", len(palette_data)) # palette size
177+
image_yai += bytearray(palette_data) # palette
178+
image_yai += bytes([IMAGE_BLOCK]) # Memory block type
179+
image_yai += struct.pack("<I", len(image_data)) # num bytes height x width
180+
image_yai += bytearray(image_data) # image
181+
182+
logger.info('YAI size: %d' % len(image_yai))
183+
184+
return image_yai
185+
123186
def update_yail_data(data: np.ndarray, gfx_mode: int, thread_safe: bool = True) -> None:
124187
global yail_data
125188
if thread_safe:
@@ -148,6 +211,8 @@ def send_yail_data(client_socket: socket.socket, thread_safe: bool=True) -> None
148211
def stream_YAI(client: str, gfx_mode: int, url: str = None, filepath: str = None) -> bool:
149212
from io import BytesIO
150213

214+
global YAIL_H
215+
151216
# download the body of response by chunk, not immediately
152217
try:
153218
if url is not None:
@@ -184,17 +249,37 @@ def stream_YAI(client: str, gfx_mode: int, url: str = None, filepath: str = None
184249
elif filepath is not None:
185250
image = Image.open(filepath)
186251

187-
gray = image.convert(mode='L')
188-
gray = fix_aspect(gray)
189-
gray = gray.resize((YAIL_W,YAIL_H), Image.LANCZOS)
190-
191-
if gfx_mode == GRAPHICS_8:
192-
gray_dithered = dither_image(gray)
193-
image_data = pack_bits(gray_dithered)
194-
elif gfx_mode == GRAPHICS_9:
195-
image_data = pack_shades(gray)
196-
197-
image_yai = convertToYai(image_data, gfx_mode)
252+
if gfx_mode == GRAPHICS_8 or gfx_mode == GRAPHICS_9:
253+
gray = image.convert(mode='L')
254+
gray = fix_aspect(gray)
255+
gray = gray.resize((YAIL_W,YAIL_H), Image.LANCZOS)
256+
257+
if gfx_mode == GRAPHICS_8:
258+
gray_dithered = dither_image(gray)
259+
image_data = pack_bits(gray_dithered)
260+
elif gfx_mode == GRAPHICS_9:
261+
image_data = pack_shades(gray)
262+
263+
image_yai = convertToYai(image_data, gfx_mode)
264+
265+
else: # VBXE mode
266+
# Make the image fit out screen format but preserve it's aspect ratio
267+
image_resized = prep_image_for_vbxe(image, target_width=320, target_height=240)
268+
# Convert the image to use a palette
269+
image_resized = image_resized.convert('P', palette=Image.ADAPTIVE, colors=256)
270+
logger.info(f'Image size: {image_resized.size}')
271+
#image_resized.show()
272+
# Get the palette
273+
palette = image_resized.getpalette()
274+
# Get the image data
275+
image_resized = image_resized.tobytes()
276+
logger.info(f'Image data size: {len(image_resized)}')
277+
# Offset the palette entries by one
278+
offset_palette = [0] * 3 + palette[:-3]
279+
# Offset the image data by one
280+
offset_image_data = bytes((byte + 1) % 256 for byte in image_resized)
281+
282+
image_yai = convertToYaiVBXE(offset_image_data, offset_palette, gfx_mode)
198283

199284
client.sendall(image_yai)
200285

@@ -288,6 +373,7 @@ def handle_client_connection(client_socket: socket.socket) -> None:
288373
global connections
289374
global camera_thread
290375
global camera_done
376+
global YAIL_H
291377
gfx_mode = GRAPHICS_8
292378
client_mode = None
293379

@@ -377,6 +463,9 @@ def handle_client_connection(client_socket: socket.socket) -> None:
377463
elif tokens[0] == 'gfx':
378464
tokens.pop(0)
379465
gfx_mode = int(tokens[0])
466+
#if gfx_mode > GRAPHICS_9: # VBXE
467+
# global YAIL_H
468+
# YAIL_H = 240
380469
tokens.pop(0)
381470

382471
elif tokens[0] == 'quit':
@@ -476,10 +565,12 @@ def main():
476565
bind_port = int(args.port[0])
477566

478567
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
479-
server.bind((bind_ip, bind_port))
568+
#server.bind((bind_ip, bind_port))
569+
server.bind(('', bind_port))
480570
server.listen(10) # max backlog of connections
481571

482-
logger.info('Listening on {}:{}'.format(bind_ip, bind_port))
572+
#logger.info('Listening on {}:{}'.format(bind_ip, bind_port))
573+
logger.info('Listening on {}:{}'.format('', bind_port))
483574

484575
while True:
485576
client_sock, address = server.accept()
@@ -492,4 +583,4 @@ def main():
492583
client_handler.start()
493584

494585
if __name__ == "__main__":
495-
main()
586+
main()

0 commit comments

Comments
 (0)