From 5b1b4f25a6a8da368dc50ae8db1e5f0ba998acd4 Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 25 Nov 2022 17:20:31 +0800 Subject: [PATCH 1/6] Initial working version of rgb565 format extension --- adafruit_framebuf.py | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index e990fb4..7512cba 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -126,6 +126,59 @@ def fill_rect(framebuf, x, y, width, height, color): height -= 1 +class RGB565Format: + """RGB565Format""" + + @staticmethod + def color_to_rgb565(color): + if isinstance(color, tuple): + r = color[0] & 0xf8 + gh = color[1] >> 5 + gl = (color[1] << 5) & 0xe0 + b = color[2] >> 3 + else: + r = (color >> 16) & 0xf8 + gh = (color >> 13) & 0x07 + gl = (color >> 5) & 0xe0 + b = (color >> 3) & 0x1f + hi = r + gh + lo = gl + b + return lo, hi + + @staticmethod + def set_pixel(framebuf, x, y, color): + """Set a given pixel to a color.""" + index = (y * framebuf.stride + x) * 2 + framebuf.buf[index: index+2] = bytes(RGB565Format.color_to_rgb565(color)) + + @staticmethod + def get_pixel(framebuf, x, y): + """Get the color of a given pixel""" + index = (y * framebuf.stride + x) * 2 + cval = framebuf.buf[index] + framebuf.buf[index+1] * 256 + r = cval >> 11 + g = (cval & 0x7e) >> 5 + b = cval & 0x1f + return (r << 19) | (g << 10) | (b << 3) + + @staticmethod + def fill(framebuf, color): + """completely fill/clear the buffer with a color""" + rgb565 = RGB565Format.color_to_rgb565(color) + for i in range(0, len(framebuf.buf), 2): + framebuf.buf[i: i + 2] = bytes(rgb565) + + @staticmethod + def fill_rect(framebuf, x, y, width, height, color): + """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws + both the outline and interior.""" + rgb565 = RGB565Format.color_to_rgb565(color) + for _x in range(x, x + width): + for _y in range(y, y + height): + index = (_y * framebuf.stride + _x) * 2 + framebuf.buf[index: index + 2] = bytes(rgb565) + + class RGB888Format: """RGB888Format""" @@ -203,6 +256,8 @@ def __init__(self, buf, width, height, buf_format=MVLSB, stride=None): self.format = MHMSBFormat() elif buf_format == RGB888: self.format = RGB888Format() + elif buf_format == RGB565: + self.format = RGB565Format() else: raise ValueError("invalid format") self._rotation = 0 @@ -419,7 +474,7 @@ def image(self, img): if self.rotation in (1, 3): width, height = height, width - if isinstance(self.format, RGB888Format) and img.mode != "RGB": + if isinstance(self.format, (RGB565Format, RGB888Format)) and img.mode != "RGB": raise ValueError("Image must be in mode RGB.") if isinstance(self.format, (MHMSBFormat, MVLSBFormat)) and img.mode != "1": raise ValueError("Image must be in mode 1.") From d3a74b3df26f03d9ae70b598100b482b207ea327 Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 25 Nov 2022 18:36:07 +0800 Subject: [PATCH 2/6] WIP checkin - format improvements --- adafruit_framebuf.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index 7512cba..6de0f46 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -127,10 +127,14 @@ def fill_rect(framebuf, x, y, width, height, color): class RGB565Format: - """RGB565Format""" + """ + This class implements the RGB565 format + It assumes a little-endian byte order in the frame buffer + """ @staticmethod def color_to_rgb565(color): + """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes""" if isinstance(color, tuple): r = color[0] & 0xf8 gh = color[1] >> 5 @@ -143,40 +147,37 @@ def color_to_rgb565(color): b = (color >> 3) & 0x1f hi = r + gh lo = gl + b - return lo, hi + return bytes([lo, hi]) - @staticmethod - def set_pixel(framebuf, x, y, color): + def set_pixel(self, framebuf, x, y, color): """Set a given pixel to a color.""" index = (y * framebuf.stride + x) * 2 - framebuf.buf[index: index+2] = bytes(RGB565Format.color_to_rgb565(color)) + framebuf.buf[index: index+2] = self.color_to_rgb565(color) @staticmethod def get_pixel(framebuf, x, y): """Get the color of a given pixel""" index = (y * framebuf.stride + x) * 2 - cval = framebuf.buf[index] + framebuf.buf[index+1] * 256 - r = cval >> 11 - g = (cval & 0x7e) >> 5 - b = cval & 0x1f - return (r << 19) | (g << 10) | (b << 3) + bl, bh = framebuf.buf[index: index+2] + r = bh & 0xf8 + g = ((bh & 0x07) << 5) | ((bl & 0xe0) >> 5) + b = (bl & 0x1f) << 3 + return (r << 16) | (g << 8) | b - @staticmethod - def fill(framebuf, color): + def fill(self, framebuf, color): """completely fill/clear the buffer with a color""" - rgb565 = RGB565Format.color_to_rgb565(color) + rgb565_color = self.color_to_rgb565(color) for i in range(0, len(framebuf.buf), 2): - framebuf.buf[i: i + 2] = bytes(rgb565) + framebuf.buf[i: i + 2] = rgb565_color - @staticmethod - def fill_rect(framebuf, x, y, width, height, color): + def fill_rect(self, framebuf, x, y, width, height, color): """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws both the outline and interior.""" - rgb565 = RGB565Format.color_to_rgb565(color) + rgb565_color = self.color_to_rgb565(color) for _x in range(x, x + width): for _y in range(y, y + height): index = (_y * framebuf.stride + _x) * 2 - framebuf.buf[index: index + 2] = bytes(rgb565) + framebuf.buf[index: index + 2] = rgb565_color class RGB888Format: From ad9c752e7536d0d23a6ef2f09a58ddb6dc0c4792 Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 25 Nov 2022 18:37:19 +0800 Subject: [PATCH 3/6] Code blacked --- adafruit_framebuf.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index 6de0f46..d07eb5e 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -136,15 +136,15 @@ class RGB565Format: def color_to_rgb565(color): """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes""" if isinstance(color, tuple): - r = color[0] & 0xf8 + r = color[0] & 0xF8 gh = color[1] >> 5 - gl = (color[1] << 5) & 0xe0 + gl = (color[1] << 5) & 0xE0 b = color[2] >> 3 else: - r = (color >> 16) & 0xf8 + r = (color >> 16) & 0xF8 gh = (color >> 13) & 0x07 - gl = (color >> 5) & 0xe0 - b = (color >> 3) & 0x1f + gl = (color >> 5) & 0xE0 + b = (color >> 3) & 0x1F hi = r + gh lo = gl + b return bytes([lo, hi]) @@ -152,23 +152,23 @@ def color_to_rgb565(color): def set_pixel(self, framebuf, x, y, color): """Set a given pixel to a color.""" index = (y * framebuf.stride + x) * 2 - framebuf.buf[index: index+2] = self.color_to_rgb565(color) + framebuf.buf[index : index + 2] = self.color_to_rgb565(color) @staticmethod def get_pixel(framebuf, x, y): """Get the color of a given pixel""" index = (y * framebuf.stride + x) * 2 - bl, bh = framebuf.buf[index: index+2] - r = bh & 0xf8 - g = ((bh & 0x07) << 5) | ((bl & 0xe0) >> 5) - b = (bl & 0x1f) << 3 + bl, bh = framebuf.buf[index : index + 2] + r = bh & 0xF8 + g = ((bh & 0x07) << 5) | ((bl & 0xE0) >> 5) + b = (bl & 0x1F) << 3 return (r << 16) | (g << 8) | b def fill(self, framebuf, color): """completely fill/clear the buffer with a color""" rgb565_color = self.color_to_rgb565(color) for i in range(0, len(framebuf.buf), 2): - framebuf.buf[i: i + 2] = rgb565_color + framebuf.buf[i : i + 2] = rgb565_color def fill_rect(self, framebuf, x, y, width, height, color): """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws @@ -177,7 +177,7 @@ def fill_rect(self, framebuf, x, y, width, height, color): for _x in range(x, x + width): for _y in range(y, y + height): index = (_y * framebuf.stride + _x) * 2 - framebuf.buf[index: index + 2] = rgb565_color + framebuf.buf[index : index + 2] = rgb565_color class RGB888Format: From 7090f0dbba57a974afc82455752697e524baa09e Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 2 Dec 2022 17:37:22 +0800 Subject: [PATCH 4/6] Perform some small optimizations --- adafruit_framebuf.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index d07eb5e..dc77c9b 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -136,17 +136,11 @@ class RGB565Format: def color_to_rgb565(color): """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes""" if isinstance(color, tuple): - r = color[0] & 0xF8 - gh = color[1] >> 5 - gl = (color[1] << 5) & 0xE0 - b = color[2] >> 3 + hi = (color[0] & 0xF8) | (color[1] >> 5) + lo = ((color[1] << 5) & 0xE0) | (color[2] >> 3) else: - r = (color >> 16) & 0xF8 - gh = (color >> 13) & 0x07 - gl = (color >> 5) & 0xE0 - b = (color >> 3) & 0x1F - hi = r + gh - lo = gl + b + hi = ((color >> 16) & 0xF8) | ((color >> 13) & 0x07) + lo = ((color >> 5) & 0xE0) | ((color >> 3) & 0x1F) return bytes([lo, hi]) def set_pixel(self, framebuf, x, y, color): @@ -174,9 +168,10 @@ def fill_rect(self, framebuf, x, y, width, height, color): """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws both the outline and interior.""" rgb565_color = self.color_to_rgb565(color) - for _x in range(x, x + width): - for _y in range(y, y + height): - index = (_y * framebuf.stride + _x) * 2 + for _y in range(2 * y, 2 * (y + height), 2): + offset2 = _y * framebuf.stride + for _x in range(2 * x, 2 * (x + width), 2): + index = offset2 + _x framebuf.buf[index : index + 2] = rgb565_color From 3befa993e8db73ae327c92c4675a04803584246d Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 2 Dec 2022 18:19:11 +0800 Subject: [PATCH 5/6] Fix pylint issues --- adafruit_framebuf.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index dc77c9b..1a3fbfc 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -134,14 +134,15 @@ class RGB565Format: @staticmethod def color_to_rgb565(color): - """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes""" + """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes + """ if isinstance(color, tuple): - hi = (color[0] & 0xF8) | (color[1] >> 5) - lo = ((color[1] << 5) & 0xE0) | (color[2] >> 3) + hibyte = (color[0] & 0xF8) | (color[1] >> 5) + lobyte = ((color[1] << 5) & 0xE0) | (color[2] >> 3) else: - hi = ((color >> 16) & 0xF8) | ((color >> 13) & 0x07) - lo = ((color >> 5) & 0xE0) | ((color >> 3) & 0x1F) - return bytes([lo, hi]) + hibyte = ((color >> 16) & 0xF8) | ((color >> 13) & 0x07) + lobyte = ((color >> 5) & 0xE0) | ((color >> 3) & 0x1F) + return bytes([lobyte, hibyte]) def set_pixel(self, framebuf, x, y, color): """Set a given pixel to a color.""" @@ -152,10 +153,10 @@ def set_pixel(self, framebuf, x, y, color): def get_pixel(framebuf, x, y): """Get the color of a given pixel""" index = (y * framebuf.stride + x) * 2 - bl, bh = framebuf.buf[index : index + 2] - r = bh & 0xF8 - g = ((bh & 0x07) << 5) | ((bl & 0xE0) >> 5) - b = (bl & 0x1F) << 3 + lobyte, hibyte = framebuf.buf[index : index + 2] + r = hibyte & 0xF8 + g = ((hibyte & 0x07) << 5) | ((lobyte & 0xE0) >> 5) + b = (lobyte & 0x1F) << 3 return (r << 16) | (g << 8) | b def fill(self, framebuf, color): @@ -167,6 +168,7 @@ def fill(self, framebuf, color): def fill_rect(self, framebuf, x, y, width, height, color): """Draw a rectangle at the given location, size and color. The ``fill_rect`` method draws both the outline and interior.""" + # pylint: disable=too-many-arguments rgb565_color = self.color_to_rgb565(color) for _y in range(2 * y, 2 * (y + height), 2): offset2 = _y * framebuf.stride From 3db43b395ff1bc21792c6c74c722b9653794bd91 Mon Sep 17 00:00:00 2001 From: Phil Sutherland Date: Fri, 2 Dec 2022 18:30:43 +0800 Subject: [PATCH 6/6] Break too-long line --- adafruit_framebuf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_framebuf.py b/adafruit_framebuf.py index 1a3fbfc..7e3ac12 100755 --- a/adafruit_framebuf.py +++ b/adafruit_framebuf.py @@ -134,8 +134,8 @@ class RGB565Format: @staticmethod def color_to_rgb565(color): - """Convert a color in either tuple or 24 bit integer form to RGB565, and return as two bytes - """ + """Convert a color in either tuple or 24 bit integer form to RGB565, + and return as two bytes""" if isinstance(color, tuple): hibyte = (color[0] & 0xF8) | (color[1] >> 5) lobyte = ((color[1] << 5) & 0xE0) | (color[2] >> 3)