|
16 | 16 | # You should have received a copy of the GNU General Public License
|
17 | 17 | # along with this program. If not, see <https://www.gnu.org/licenses/>.
|
18 | 18 |
|
19 |
| -import struct |
20 | 19 | import time
|
21 | 20 |
|
22 | 21 | from serial.tools.list_ports import comports
|
| 22 | +import numpy as np |
23 | 23 |
|
24 | 24 | from library.lcd.lcd_comm import *
|
25 | 25 | from library.log import logger
|
@@ -130,54 +130,78 @@ def SetOrientation(self, orientation: Orientation = Orientation.PORTRAIT):
|
130 | 130 | byteBuffer[10] = (height & 255)
|
131 | 131 | self.lcd_serial.write(bytes(byteBuffer))
|
132 | 132 |
|
| 133 | + @staticmethod |
| 134 | + def imageToRGB565LE(image: Image): |
| 135 | + if image.mode not in ["RGB", "RGBA"]: |
| 136 | + # we need the first 3 channels to be R, G and B |
| 137 | + image = image.convert("RGB") |
| 138 | + |
| 139 | + rgb = np.asarray(image) |
| 140 | + |
| 141 | + # flatten the first 2 dimensions (width and height) into a single stream |
| 142 | + # of RGB pixels |
| 143 | + rgb = rgb.reshape((image.size[1] * image.size[0], -1)) |
| 144 | + |
| 145 | + # extract R, G, B channels and promote them to 16 bits |
| 146 | + r = rgb[:, 0].astype(np.uint16) |
| 147 | + g = rgb[:, 1].astype(np.uint16) |
| 148 | + b = rgb[:, 2].astype(np.uint16) |
| 149 | + |
| 150 | + # construct RGB565 |
| 151 | + r = (r >> 3) |
| 152 | + g = (g >> 2) |
| 153 | + b = (b >> 3) |
| 154 | + rgb565 = (r << 11) | (g << 5) | b |
| 155 | + |
| 156 | + # serialize to little-endian |
| 157 | + return rgb565.newbyteorder('<').tobytes() |
| 158 | + |
133 | 159 | def DisplayPILImage(
|
134 | 160 | self,
|
135 | 161 | image: Image,
|
136 | 162 | x: int = 0, y: int = 0,
|
137 | 163 | image_width: int = 0,
|
138 | 164 | image_height: int = 0
|
139 | 165 | ):
|
| 166 | + width, height = self.get_width(), self.get_height() |
| 167 | + |
140 | 168 | # If the image height/width isn't provided, use the native image size
|
141 | 169 | if not image_height:
|
142 | 170 | image_height = image.size[1]
|
143 | 171 | if not image_width:
|
144 | 172 | image_width = image.size[0]
|
145 | 173 |
|
146 |
| - # If our image is bigger than our display, resize it to fit our screen |
147 |
| - if image.size[1] > self.get_height(): |
148 |
| - image_height = self.get_height() |
149 |
| - if image.size[0] > self.get_width(): |
150 |
| - image_width = self.get_width() |
151 |
| - |
152 |
| - assert x <= self.get_width(), 'Image X coordinate must be <= display width' |
153 |
| - assert y <= self.get_height(), 'Image Y coordinate must be <= display height' |
| 174 | + assert x <= width, 'Image X coordinate must be <= display width' |
| 175 | + assert y <= height, 'Image Y coordinate must be <= display height' |
154 | 176 | assert image_height > 0, 'Image height must be > 0'
|
155 | 177 | assert image_width > 0, 'Image width must be > 0'
|
156 | 178 |
|
| 179 | + # If our image size + the (x, y) position offsets are bigger than |
| 180 | + # our display, reduce the image size to fit our screen |
| 181 | + if x + image_width > width: |
| 182 | + image_width = width - x |
| 183 | + if y + image_height > height: |
| 184 | + image_height = height - y |
| 185 | + |
| 186 | + if image_width != image.size[0] or image_height != image.size[1]: |
| 187 | + image = image.crop((0, 0, image_width, image_height)) |
| 188 | + |
157 | 189 | (x0, y0) = (x, y)
|
158 | 190 | (x1, y1) = (x + image_width - 1, y + image_height - 1)
|
159 | 191 |
|
160 |
| - self.SendCommand(Command.DISPLAY_BITMAP, x0, y0, x1, y1) |
161 |
| - |
162 |
| - pix = image.load() |
163 |
| - line = bytes() |
| 192 | + rgb565le = self.imageToRGB565LE(image) |
164 | 193 |
|
165 | 194 | # Lock queue mutex then queue all the requests for the image data
|
166 | 195 | with self.update_queue_mutex:
|
167 |
| - for h in range(image_height): |
168 |
| - for w in range(image_width): |
169 |
| - R = pix[w, h][0] >> 3 |
170 |
| - G = pix[w, h][1] >> 2 |
171 |
| - B = pix[w, h][2] >> 3 |
172 |
| - |
173 |
| - rgb = (R << 11) | (G << 5) | B |
174 |
| - line += struct.pack('<H', rgb) |
| 196 | + self.SendCommand(Command.DISPLAY_BITMAP, x0, y0, x1, y1) |
175 | 197 |
|
176 |
| - # Send image data by multiple of "display width" bytes |
177 |
| - if len(line) >= self.get_width() * 8: |
178 |
| - self.SendLine(line) |
179 |
| - line = bytes() |
| 198 | + # Send image data by multiple of "display width" bytes |
| 199 | + start = 0 |
| 200 | + end = width * 8 |
| 201 | + while end <= len(rgb565le): |
| 202 | + self.SendLine(rgb565le[start:end]) |
| 203 | + start, end = end, end + width * 8 |
180 | 204 |
|
181 | 205 | # Write last line if needed
|
182 |
| - if len(line) > 0: |
183 |
| - self.SendLine(line) |
| 206 | + if start != len(rgb565le): |
| 207 | + self.SendLine(rgb565le[start:]) |
0 commit comments