|
20 | 20 | import struct |
21 | 21 |
|
22 | 22 | from serial.tools.list_ports import comports |
| 23 | +import numpy as np |
23 | 24 |
|
24 | 25 | from library.lcd.lcd_comm import * |
25 | 26 | from library.log import logger |
@@ -194,6 +195,39 @@ def SetOrientation(self, orientation: Orientation = Orientation.PORTRAIT): |
194 | 195 | else: |
195 | 196 | self.SendCommand(Command.SET_ORIENTATION, payload=[OrientationValueRevB.ORIENTATION_LANDSCAPE]) |
196 | 197 |
|
| 198 | + @staticmethod |
| 199 | + def imageToRGB565BE(image: Image.Image): |
| 200 | + if image.mode not in ["RGB", "RGBA"]: |
| 201 | + # we need the first 3 channels to be R, G and B |
| 202 | + image = image.convert("RGB") |
| 203 | + |
| 204 | + rgb = np.asarray(image) |
| 205 | + |
| 206 | + # flatten the first 2 dimensions (width and height) into a single stream |
| 207 | + # of RGB pixels |
| 208 | + rgb = rgb.reshape((image.size[1] * image.size[0], -1)) |
| 209 | + |
| 210 | + # extract R, G, B channels and promote them to 16 bits |
| 211 | + r = rgb[:, 0].astype(np.uint16) |
| 212 | + g = rgb[:, 1].astype(np.uint16) |
| 213 | + b = rgb[:, 2].astype(np.uint16) |
| 214 | + |
| 215 | + # construct RGB565 |
| 216 | + r = (r >> 3) |
| 217 | + g = (g >> 2) |
| 218 | + b = (b >> 3) |
| 219 | + rgb565 = (r << 11) | (g << 5) | b |
| 220 | + |
| 221 | + # serialize to big-endian |
| 222 | + return rgb565.astype('>u2').tobytes() |
| 223 | + |
| 224 | + def serialize_image(self, image: Image.Image, height: int, width: int) -> bytes: |
| 225 | + if image.width != width or image.height != height: |
| 226 | + image = image.crop((0, 0, width, height)) |
| 227 | + if self.orientation == Orientation.REVERSE_PORTRAIT or self.orientation == Orientation.REVERSE_LANDSCAPE: |
| 228 | + image = image.rotate(180) |
| 229 | + return self.imageToRGB565BE(image) |
| 230 | + |
197 | 231 | def DisplayPILImage( |
198 | 232 | self, |
199 | 233 | image: Image.Image, |
@@ -231,34 +265,18 @@ def DisplayPILImage( |
231 | 265 | (y0 >> 8) & 255, y0 & 255, |
232 | 266 | (x1 >> 8) & 255, x1 & 255, |
233 | 267 | (y1 >> 8) & 255, y1 & 255]) |
234 | | - pix = image.load() |
235 | | - line = bytes() |
| 268 | + |
| 269 | + rgb565be = self.serialize_image(image, image_height, image_width) |
236 | 270 |
|
237 | 271 | # Lock queue mutex then queue all the requests for the image data |
238 | 272 | with self.update_queue_mutex: |
239 | | - for h in range(image_height): |
240 | | - for w in range(image_width): |
241 | | - if self.orientation == Orientation.PORTRAIT or self.orientation == Orientation.LANDSCAPE: |
242 | | - R = pix[w, h][0] >> 3 |
243 | | - G = pix[w, h][1] >> 2 |
244 | | - B = pix[w, h][2] >> 3 |
245 | | - else: |
246 | | - # Manage reverse orientations from software, because display does not manage it |
247 | | - R = pix[image_width - w - 1, image_height - h - 1][0] >> 3 |
248 | | - G = pix[image_width - w - 1, image_height - h - 1][1] >> 2 |
249 | | - B = pix[image_width - w - 1, image_height - h - 1][2] >> 3 |
250 | | - |
251 | | - # Color information is 0bRRRRRGGGGGGBBBBB |
252 | | - # Revision A: Encode in Little-Endian (native x86/ARM encoding) |
253 | | - # Revition B: Encode in Big-Endian |
254 | | - rgb = (R << 11) | (G << 5) | B |
255 | | - line += struct.pack('>H', rgb) |
256 | | - |
257 | | - # Send image data by multiple of "display width" bytes |
258 | | - if len(line) >= self.get_width() * 8: |
259 | | - self.SendLine(line) |
260 | | - line = bytes() |
| 273 | + # Send image data by multiple of "display width" bytes |
| 274 | + start = 0 |
| 275 | + end = self.get_width() * 8 |
| 276 | + while end <= len(rgb565be): |
| 277 | + self.SendLine(rgb565be[start:end]) |
| 278 | + start, end = end, end + self.get_width() * 8 |
261 | 279 |
|
262 | 280 | # Write last line if needed |
263 | | - if len(line) > 0: |
264 | | - self.SendLine(line) |
| 281 | + if start != len(rgb565be): |
| 282 | + self.SendLine(rgb565be[start:]) |
0 commit comments