2626SOCKET_WAIT_TIME = 1
2727GRAPHICS_8 = 2
2828GRAPHICS_9 = 4
29+ GRAPHICS_VBXE_1 = 0x11
2930GRAPHICS_RANDOM = 42
3031YAIL_W = 320
3132YAIL_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.
4148filenames = []
4249camera_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+
4485def 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+
123186def 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
148211def 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
494585if __name__ == "__main__" :
495- main ()
586+ main ()
0 commit comments