From c30285993a316683a42244a942f3477257b1d68b Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 18 Aug 2020 11:25:08 -0500 Subject: [PATCH 01/27] all: Correct right-side of bounding box for slanted fonts; correct y_offset by deleting impact of newlines, bitmap_label: fix anchored_position rounding error, add speedup with load_glyphs --- adafruit_display_text/bitmap_label.py | 42 +++++++++++----------- adafruit_display_text/label.py | 50 ++++++++++----------------- 2 files changed, 38 insertions(+), 54 deletions(-) mode change 100644 => 100755 adafruit_display_text/bitmap_label.py diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py old mode 100644 new mode 100755 index 98f9322..38f6586 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -181,11 +181,7 @@ def __init__( ) label_position_yoffset = int( # To calibrate with label.py positioning - ( - font.get_glyph(ord("M")).height - - text.count("\n") * font.get_bounding_box()[1] * self._line_spacing - ) - / 2 + (font.get_glyph(ord("M")).height) / 2 ) self.tilegrid = displayio.TileGrid( @@ -237,6 +233,9 @@ def _text_bounding_box( # (consistent with label.py) glyphs = "M j'" # choose glyphs with highest ascender and lowest # descender, will depend upon font used + + font.load_glyphs(text + glyphs) + ascender_max = descender_max = 0 for char in glyphs: this_glyph = font.get_glyph(ord(char)) @@ -252,13 +251,7 @@ def _text_bounding_box( left = right = x_start top = bottom = y_start - y_offset_tight = int( - ( - font.get_glyph(ord("M")).height - - text.count("\n") * self._line_spacing_ypixels(font, line_spacing) - ) - / 2 - ) + y_offset_tight = int((font.get_glyph(ord("M")).height) / 2) # this needs to be reviewed (also in label.py), since it doesn't respond # properly to the number of newlines. @@ -283,8 +276,10 @@ def _text_bounding_box( font, line_spacing ) # Add a newline lines += 1 + xright = xposition + my_glyph.width + my_glyph.dx xposition += my_glyph.shift_x - right = max(right, xposition) + + right = max(right, xposition, xright) if yposition == y_start: # first line, find the Ascender height top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight) @@ -297,7 +292,7 @@ def _text_bounding_box( label_calibration_offset = int( ( font.get_glyph(ord("M")).height - - text.count("\n") * self._line_spacing_ypixels(font, line_spacing) + # - text.count("\n") * self._line_spacing_ypixels(font, line_spacing) ) / 2 ) @@ -363,7 +358,11 @@ def _place_text( print("Glyph not found: {}".format(repr(char))) else: - right = max(right, xposition + my_glyph.shift_x) + right = max( + right, + xposition + my_glyph.shift_x, + xposition + my_glyph.width + my_glyph.dx, + ) if yposition == y_start: # first line, find the Ascender height top = min(top, -my_glyph.height - my_glyph.dy) bottom = max(bottom, yposition - my_glyph.dy) @@ -420,7 +419,6 @@ def line_spacing(self): bounding-box height. (E.g. 1.0 is the bounding-box height)""" return self._line_spacing - # pylint: disable=no-self-use @line_spacing.setter def line_spacing(self, new_line_spacing): raise RuntimeError( @@ -462,7 +460,6 @@ def text(self): """Text to displayed.""" return self._text - # pylint: disable=no-self-use @text.setter def text(self, new_text): raise RuntimeError( @@ -474,7 +471,6 @@ def font(self): """Font to use for text display.""" return self.font - # pylint: disable=no-self-use @font.setter def font(self, new_font): raise RuntimeError( @@ -509,12 +505,14 @@ def anchored_position(self, new_position): if (self._anchor_point is not None) and (self._anchored_position is not None): new_x = int( new_position[0] - - self._anchor_point[0] * (self._bounding_box[2] * self._scale) + - round(self._anchor_point[0] * (self._bounding_box[2] * self._scale)) ) new_y = int( - new_position[1] - - (self._anchor_point[1] * self._bounding_box[3] * self.scale) - + round((self._bounding_box[3] * self.scale) / 2.0) + round( + new_position[1] + - (self._anchor_point[1] * self._bounding_box[3] * self.scale) + + ((self._bounding_box[3] * self.scale) / 2.0) + ) ) self.x = new_x self.y = new_y diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 0f1caff..9a11f34 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -22,21 +22,14 @@ """ `adafruit_display_text.label` ==================================================== - Displays text labels using CircuitPython's displayio. - * Author(s): Scott Shawcroft - Implementation Notes -------------------- - **Hardware:** - **Software and Dependencies:** - * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases - """ import displayio @@ -50,7 +43,6 @@ class Label(displayio.Group): properties will be the left edge of the bounding box, and in the center of a M glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, it will try to have it be center-left as close as possible. - :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``. Must include a capital M for measuring character size. :param str text: Text to display @@ -185,13 +177,7 @@ def _update_background_color(self, new_color): self._background_palette[0] = new_color self._background_color = new_color - y_offset = int( - ( - self._font.get_glyph(ord("M")).height - - self.text.count("\n") * self.height * self.line_spacing - ) - / 2 - ) + y_offset = int((self._font.get_glyph(ord("M")).height) / 2) lines = self.text.count("\n") + 1 if not self._added_background_tilegrid: # no bitmap is in the self Group @@ -237,14 +223,7 @@ def _update_text( else: i = 0 tilegrid_count = i - self._font.load_glyphs(new_text + "M") - y_offset = int( - ( - self._font.get_glyph(ord("M")).height - - new_text.count("\n") * self.height * self.line_spacing - ) - / 2 - ) + y_offset = int((self._font.get_glyph(ord("M")).height) / 2) left = right = top = bottom = 0 lines = 1 for character in new_text: @@ -256,7 +235,7 @@ def _update_text( glyph = self._font.get_glyph(ord(character)) if not glyph: continue - right = max(right, x + glyph.shift_x) + right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx) if y == 0: # first line, find the Ascender height top = min(top, -glyph.height - glyph.dy + y_offset) bottom = max(bottom, y - glyph.dy + y_offset) @@ -386,11 +365,16 @@ def anchored_position(self): if self._anchor_point is None: return None return ( - int(self.x + (self._anchor_point[0] * self._boundingbox[2] * self._scale)), int( - self.y - + (self._anchor_point[1] * self._boundingbox[3] * self._scale) - - round((self._boundingbox[3] * self._scale) / 2.0) + self.x + + round(self._anchor_point[0] * self._boundingbox[2] * self._scale) + ), + int( + round( + self.y + + (self._anchor_point[1] * self._boundingbox[3] * self._scale) + - ((self._boundingbox[3] * self._scale) / 2.0) + ) ), ) @@ -400,12 +384,14 @@ def anchored_position(self, new_position): return # Note: anchor_point must be set before setting anchored_position new_x = int( new_position[0] - - self._anchor_point[0] * (self._boundingbox[2] * self._scale) + - round(self._anchor_point[0] * (self._boundingbox[2] * self._scale)) ) new_y = int( - new_position[1] - - (self._anchor_point[1] * self._boundingbox[3] * self._scale) - + round((self._boundingbox[3] * self._scale) / 2.0) + round( + new_position[1] + - (self._anchor_point[1] * self._boundingbox[3] * self._scale) + + ((self._boundingbox[3] * self._scale) / 2.0) + ) ) self.x = new_x self.y = new_y From 6862809305377a80f7615eb4b0ddfae73d452a6e Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 18 Aug 2020 11:59:35 -0500 Subject: [PATCH 02/27] all: Correct left-hand bounding box position, that was problematic for slanted fonts --- adafruit_display_text/bitmap_label.py | 41 ++++++++++++++++++--------- adafruit_display_text/label.py | 14 +++++++-- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 38f6586..6578c46 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -144,7 +144,12 @@ def __init__( # Calculate tight box to provide bounding box dimensions to match label for # anchor_position calculations - (tight_box_x, tight_box_y, x_offset, tight_y_offset) = self._text_bounding_box( + ( + tight_box_x, + tight_box_y, + tight_x_offset, + tight_y_offset, + ) = self._text_bounding_box( text, font, self._line_spacing, background_tight=True, ) @@ -152,6 +157,7 @@ def __init__( box_x = tight_box_x box_y = tight_box_y y_offset = tight_y_offset + x_offset = tight_x_offset else: (box_x, box_y, x_offset, y_offset) = self._text_bounding_box( @@ -176,7 +182,7 @@ def __init__( text, font, self._line_spacing, - padding_left + x_offset, + padding_left - x_offset, padding_top + y_offset, ) @@ -192,7 +198,7 @@ def __init__( tile_width=box_x, tile_height=box_y, default_tile=0, - x=-padding_left, + x=-padding_left + x_offset, y=label_position_yoffset - y_offset - padding_top, ) @@ -248,7 +254,8 @@ def _text_bounding_box( xposition = x_start = 0 # starting x position (left margin) yposition = y_start = 0 - left = right = x_start + left = None + right = x_start top = bottom = y_start y_offset_tight = int((font.get_glyph(ord("M")).height) / 2) @@ -276,6 +283,11 @@ def _text_bounding_box( font, line_spacing ) # Add a newline lines += 1 + if xposition == x_start: + if left is None: + left = my_glyph.dx + else: + left = min(left, my_glyph.dx) xright = xposition + my_glyph.width + my_glyph.dx xposition += my_glyph.shift_x @@ -289,13 +301,10 @@ def _text_bounding_box( ascender_max + descender_max ) - label_calibration_offset = int( - ( - font.get_glyph(ord("M")).height - # - text.count("\n") * self._line_spacing_ypixels(font, line_spacing) - ) - / 2 - ) + label_calibration_offset = int((font.get_glyph(ord("M")).height) / 2) + + if left is None: + left = 0 y_offset_tight = -top + label_calibration_offset @@ -308,7 +317,7 @@ def _text_bounding_box( final_box_height = loose_height final_y_offset = ascender_max - return (final_box_width, final_box_height, 0, final_y_offset) + return (final_box_width, final_box_height, left, final_y_offset) # pylint: disable=too-many-nested-blocks def _place_text( @@ -339,7 +348,8 @@ def _place_text( x_start = xposition # starting x position (left margin) y_start = yposition - left = right = x_start + left = None + right = x_start top = bottom = y_start for char in text: @@ -357,6 +367,11 @@ def _place_text( if my_glyph is None: # Error checking: no glyph found print("Glyph not found: {}".format(repr(char))) else: + if xposition == x_start: + if left is None: + left = my_glyph.dx + else: + left = min(left, my_glyph.dx) right = max( right, diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 9a11f34..30e87e0 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -224,7 +224,9 @@ def _update_text( i = 0 tilegrid_count = i y_offset = int((self._font.get_glyph(ord("M")).height) / 2) - left = right = top = bottom = 0 + right = top = bottom = 0 + left = None + lines = 1 for character in new_text: if character == "\n": @@ -236,6 +238,11 @@ def _update_text( if not glyph: continue right = max(right, x + glyph.shift_x, x + glyph.width + glyph.dx) + if x == 0: + if left is None: + left = glyph.dx + else: + left = min(left, glyph.dx) if y == 0: # first line, find the Ascender height top = min(top, -glyph.height - glyph.dy + y_offset) bottom = max(bottom, y - glyph.dy + y_offset) @@ -271,10 +278,13 @@ def _update_text( i += 1 # Remove the rest + if left is None: + left = 0 + while len(self) > tilegrid_count: # i: self.pop() self._text = new_text - self._boundingbox = (left, top, left + right, bottom - top) + self._boundingbox = (left, top, right - left, bottom - top) if self.background_color is not None: self._update_background_color(self._background_color) From 3b039030916e8a19bc580292f60553c532a2139d Mon Sep 17 00:00:00 2001 From: FoamyGuy Date: Wed, 19 Aug 2020 09:53:28 -0500 Subject: [PATCH 03/27] gracefully recover if load_glpyhs does not exist on font we are using --- adafruit_display_text/label.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index f2ac75a..57832f2 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -237,7 +237,13 @@ def _update_text( else: i = 0 tilegrid_count = i - self._font.load_glyphs(new_text + "M") + + try: + self._font.load_glyphs(new_text + "M") + except AttributeError: + # ignore if font does not have load_glyphs + pass + y_offset = int( ( self._font.get_glyph(ord("M")).height From f37e66c2d338c7b44ecbcd2b4c717eb3bf9709fe Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 20 Aug 2020 11:37:11 -0500 Subject: [PATCH 04/27] Fix bugs with anchored_position and scaling --- adafruit_display_text/bitmap_label.py | 45 ++++++++++----------- adafruit_display_text/label.py | 57 ++++++++++++++++----------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 6578c46..8f57c75 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -88,6 +88,7 @@ class Label(displayio.Group): """ # pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals, too-many-arguments + # pylint: disable=too-many-branches, no-self-use # Note: max_glyphs parameter is unnecessary, this is used for direct # compatibility with label.py @@ -231,16 +232,18 @@ def _line_spacing_ypixels(font, line_spacing): return_value = int(line_spacing * font.get_bounding_box()[1]) return return_value - def _text_bounding_box( - self, text, font, line_spacing, background_tight=False - ): # **** change default background_tight=False + def _text_bounding_box(self, text, font, line_spacing, background_tight=False): # This empirical approach checks several glyphs for maximum ascender and descender height # (consistent with label.py) glyphs = "M j'" # choose glyphs with highest ascender and lowest # descender, will depend upon font used - font.load_glyphs(text + glyphs) + try: + self._font.load_glyphs(text + glyphs) + except AttributeError: + # ignore if font does not have load_glyphs + pass ascender_max = descender_max = 0 for char in glyphs: @@ -251,8 +254,9 @@ def _text_bounding_box( lines = 1 - xposition = x_start = 0 # starting x position (left margin) - yposition = y_start = 0 + xposition = ( + x_start + ) = yposition = y_start = 0 # starting x and y position (left margin) left = None right = x_start @@ -297,24 +301,18 @@ def _text_bounding_box( top = min(top, -my_glyph.height - my_glyph.dy + y_offset_tight) bottom = max(bottom, yposition - my_glyph.dy + y_offset_tight) - loose_height = (lines - 1) * self._line_spacing_ypixels(font, line_spacing) + ( - ascender_max + descender_max - ) - - label_calibration_offset = int((font.get_glyph(ord("M")).height) / 2) - if left is None: left = 0 - y_offset_tight = -top + label_calibration_offset - final_box_width = right - left if background_tight: final_box_height = bottom - top - final_y_offset = y_offset_tight + final_y_offset = -top + y_offset_tight else: - final_box_height = loose_height + final_box_height = (lines - 1) * self._line_spacing_ypixels( + font, line_spacing + ) + (ascender_max + descender_max) final_y_offset = ascender_max return (final_box_width, final_box_height, left, final_y_offset) @@ -518,16 +516,13 @@ def anchored_position(self, new_position): # Set anchored_position if (self._anchor_point is not None) and (self._anchored_position is not None): - new_x = int( + self.x = int( new_position[0] + - (self._bounding_box[0] * self._scale) - round(self._anchor_point[0] * (self._bounding_box[2] * self._scale)) ) - new_y = int( - round( - new_position[1] - - (self._anchor_point[1] * self._bounding_box[3] * self.scale) - + ((self._bounding_box[3] * self.scale) / 2.0) - ) + self.y = int( + new_position[1] + - (self._bounding_box[1] * self._scale) + - round(self._anchor_point[1] * self._bounding_box[3] * self._scale) ) - self.x = new_x - self.y = new_y diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 30e87e0..0eb2cd5 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -91,11 +91,6 @@ def __init__( self.x = x self.y = y - self.palette = displayio.Palette(2) - self.palette[0] = 0 - self.palette.make_transparent(0) - self.palette[1] = color - self.height = self._font.get_bounding_box()[1] self._line_spacing = line_spacing self._boundingbox = None @@ -104,6 +99,12 @@ def __init__( background_tight # sets padding status for text background box ) + # Create the two-color text palette + self.palette = displayio.Palette(2) + self.palette[0] = 0 + self.palette.make_transparent(0) + self.color = color + self._background_color = background_color self._background_palette = displayio.Palette(1) self._added_background_tilegrid = False @@ -177,8 +178,8 @@ def _update_background_color(self, new_color): self._background_palette[0] = new_color self._background_color = new_color + lines = self._text.rstrip("\n").count("\n") + 1 y_offset = int((self._font.get_glyph(ord("M")).height) / 2) - lines = self.text.count("\n") + 1 if not self._added_background_tilegrid: # no bitmap is in the self Group # add bitmap if text is present and bitmap sizes > 0 pixels @@ -223,16 +224,22 @@ def _update_text( else: i = 0 tilegrid_count = i + + try: + self._font.load_glyphs(new_text + "M") + except AttributeError: + # ignore if font does not have load_glyphs + pass + y_offset = int((self._font.get_glyph(ord("M")).height) / 2) + right = top = bottom = 0 left = None - lines = 1 for character in new_text: if character == "\n": y += int(self.height * self._line_spacing) x = 0 - lines += 1 continue glyph = self._font.get_glyph(ord(character)) if not glyph: @@ -312,7 +319,13 @@ def color(self): @color.setter def color(self, new_color): - self.palette[1] = new_color + self._color = new_color + if new_color is not None: + self.palette[1] = new_color + self.palette.make_opaque(1) + else: + self.palette[1] = 0 + self.palette.make_transparent(1) @property def background_color(self): @@ -377,14 +390,13 @@ def anchored_position(self): return ( int( self.x - + round(self._anchor_point[0] * self._boundingbox[2] * self._scale) + + (self._boundingbox[0] * self._scale) + + +round(self._anchor_point[0] * self._boundingbox[2] * self._scale) ), int( - round( - self.y - + (self._anchor_point[1] * self._boundingbox[3] * self._scale) - - ((self._boundingbox[3] * self._scale) / 2.0) - ) + self.y + + (self._boundingbox[1] * self._scale) + + round(self._anchor_point[1] * self._boundingbox[3] * self._scale) ), ) @@ -392,16 +404,13 @@ def anchored_position(self): def anchored_position(self, new_position): if (self._anchor_point is None) or (new_position is None): return # Note: anchor_point must be set before setting anchored_position - new_x = int( + self.x = int( new_position[0] + - (self._boundingbox[0] * self._scale) - round(self._anchor_point[0] * (self._boundingbox[2] * self._scale)) ) - new_y = int( - round( - new_position[1] - - (self._anchor_point[1] * self._boundingbox[3] * self._scale) - + ((self._boundingbox[3] * self._scale) / 2.0) - ) + self.y = int( + new_position[1] + - (self._boundingbox[1] * self._scale) + - round(self._anchor_point[1] * self._boundingbox[3] * self._scale) ) - self.x = new_x - self.y = new_y From 7d70ca7a7f41ad4feff698b9bb36ddcb6f1e78e2 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 20 Aug 2020 12:05:35 -0500 Subject: [PATCH 05/27] Update spacing in doc string --- adafruit_display_text/label.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 0eb2cd5..08905ce 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -44,7 +44,7 @@ class Label(displayio.Group): glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, it will try to have it be center-left as close as possible. :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``. - Must include a capital M for measuring character size. + Must include a capital M for measuring character size. :param str text: Text to display :param int max_glyphs: The largest quantity of glyphs we will display :param int color: Color of all text in RGB hex From 1421c5d57ecc2fed901f065964efbdd92487432d Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 20 Aug 2020 12:10:21 -0500 Subject: [PATCH 06/27] Update docs to try to solve unexpected-indentation --- adafruit_display_text/label.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 08905ce..2af8b9c 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -43,6 +43,7 @@ class Label(displayio.Group): properties will be the left edge of the bounding box, and in the center of a M glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, it will try to have it be center-left as close as possible. + :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``. Must include a capital M for measuring character size. :param str text: Text to display From 704744f4171f324eaf961bd635be1465d3a318b7 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 20 Aug 2020 12:53:29 -0500 Subject: [PATCH 07/27] remove whitespace --- adafruit_display_text/label.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 2af8b9c..e68c47b 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -43,7 +43,7 @@ class Label(displayio.Group): properties will be the left edge of the bounding box, and in the center of a M glyph (if its one line), or the (number of lines * linespacing + M)/2. That is, it will try to have it be center-left as close as possible. - + :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``. Must include a capital M for measuring character size. :param str text: Text to display From ec28d2a8ecaedc5a790eea0ab4aba902cf86d1a1 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 20 Aug 2020 12:59:59 -0500 Subject: [PATCH 08/27] Update header docs to previous release --- adafruit_display_text/label.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index e68c47b..81509d5 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -22,14 +22,21 @@ """ `adafruit_display_text.label` ==================================================== + Displays text labels using CircuitPython's displayio. + * Author(s): Scott Shawcroft + Implementation Notes -------------------- + **Hardware:** + **Software and Dependencies:** + * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases + """ import displayio @@ -45,7 +52,7 @@ class Label(displayio.Group): it will try to have it be center-left as close as possible. :param Font font: A font class that has ``get_bounding_box`` and ``get_glyph``. - Must include a capital M for measuring character size. + Must include a capital M for measuring character size. :param str text: Text to display :param int max_glyphs: The largest quantity of glyphs we will display :param int color: Color of all text in RGB hex From cf6d7130d0def801dd70aaa9755f47eddfe36bc5 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 21 Aug 2020 21:33:47 -0500 Subject: [PATCH 09/27] Delete redundant + sign --- adafruit_display_text/label.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 81509d5..e1e10d2 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -399,7 +399,7 @@ def anchored_position(self): int( self.x + (self._boundingbox[0] * self._scale) - + +round(self._anchor_point[0] * self._boundingbox[2] * self._scale) + + round(self._anchor_point[0] * self._boundingbox[2] * self._scale) ), int( self.y From 3f9ea73480d105ce1db6c769c117febcbcbc5102 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 21 Aug 2020 22:19:32 -0500 Subject: [PATCH 10/27] Add raise from for exception handling --- adafruit_display_text/label.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index e1e10d2..b9f18ea 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -355,8 +355,8 @@ def text(self, new_text): current_anchored_position = self.anchored_position self._update_text(str(new_text)) self.anchored_position = current_anchored_position - except RuntimeError: - raise RuntimeError("Text length exceeds max_glyphs") + except RuntimeError as e: + raise RuntimeError("Text length exceeds max_glyphs") from e @property def font(self): From 61dac4d83dbb3054f35e4ac6a7a8cb65518a18eb Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 21 Aug 2020 22:22:36 -0500 Subject: [PATCH 11/27] rename error variable name for pylint --- adafruit_display_text/label.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index b9f18ea..bc9e987 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -355,8 +355,8 @@ def text(self, new_text): current_anchored_position = self.anchored_position self._update_text(str(new_text)) self.anchored_position = current_anchored_position - except RuntimeError as e: - raise RuntimeError("Text length exceeds max_glyphs") from e + except RuntimeError as run_error: + raise RuntimeError("Text length exceeds max_glyphs") from run_error @property def font(self): From f83687c47a1132c3fcf6475a338760c1058fc077 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 21 Aug 2020 22:26:59 -0500 Subject: [PATCH 12/27] rearrange import statements for pylint --- examples/display_text_background_color_padding.py | 2 +- examples/display_text_textarea_boundingbox.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/display_text_background_color_padding.py b/examples/display_text_background_color_padding.py index 91b694b..476be9a 100755 --- a/examples/display_text_background_color_padding.py +++ b/examples/display_text_background_color_padding.py @@ -8,8 +8,8 @@ # from adafruit_st7789 import ST7789 from adafruit_ili9341 import ILI9341 -from adafruit_display_text import label from adafruit_bitmap_font import bitmap_font +from adafruit_display_text import label # Setup the SPI display diff --git a/examples/display_text_textarea_boundingbox.py b/examples/display_text_textarea_boundingbox.py index 7978abb..fc0cb5e 100644 --- a/examples/display_text_textarea_boundingbox.py +++ b/examples/display_text_textarea_boundingbox.py @@ -1,8 +1,9 @@ import os import board import displayio -from adafruit_display_text.label import Label from adafruit_bitmap_font import bitmap_font +from adafruit_display_text.label import Label + # the current working directory (where this file is) cwd = ("/" + __file__).rsplit("/", 1)[0] From 59c95446054473988d6777709cf098ace2e79182 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 21 Aug 2020 22:30:11 -0500 Subject: [PATCH 13/27] Update font filename --- examples/display_text_background_color_padding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/display_text_background_color_padding.py b/examples/display_text_background_color_padding.py index 476be9a..8e5bda9 100755 --- a/examples/display_text_background_color_padding.py +++ b/examples/display_text_background_color_padding.py @@ -55,7 +55,7 @@ # font=terminalio.FONT # this is the Builtin fixed dimension font -font = bitmap_font.load_font("fonts/BitstreamVeraSans-Roman-24.bdf") +font = bitmap_font.load_font("fonts/Helvetica-Bold-16.bdf") text = [] From a404b09fd79db50ba7be1da877a6f241e9947ec0 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 25 Aug 2020 15:56:46 -0500 Subject: [PATCH 14/27] first commit with mutable text, font and line_spacing --- adafruit_display_text/bitmap_label.py | 288 ++++++++++++++++---------- 1 file changed, 183 insertions(+), 105 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 8f57c75..f24b6a3 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -98,8 +98,8 @@ def __init__( x=0, y=0, text="", - max_glyphs=None, # This input parameter is ignored, only present for compatibility - # with label.py + max_glyphs=None, # This input parameter is ignored, only present for compatibility + # with label.py color=0xFFFFFF, background_color=None, line_spacing=1.25, @@ -114,117 +114,199 @@ def __init__( **kwargs ): - if text == "": - raise RuntimeError( - "Please provide text string, or use label.py for mutable text" - ) + # instance the Group + # this Group will contain just one TileGrid with one contained bitmap + super().__init__( + max_size=1, x=x, y=y, **kwargs + ) # this will include any arguments, including scale self._font = font - # Scale will be passed to Group using kwargs. - if "scale" in kwargs.keys(): - self._scale = kwargs["scale"] - else: - self._scale = 1 + # Create the two-color palette + self.palette = displayio.Palette(2) + self.color = color + self.background_color = background_color + + self._anchor_point = anchor_point + self._anchored_position = anchored_position + + self._scale = 1 # initialize to the default scale of 1 + + # call the text updater with all the arguments. + self._reset_text(font=font, + x=x, + y=y, + text=text, + # color=color, + # background_color=background_color, + line_spacing=line_spacing, + background_tight=background_tight, + padding_top=padding_top, + padding_bottom=padding_bottom, + padding_left=padding_left, + padding_right=padding_right, + anchor_point=anchor_point, + anchored_position=anchored_position, + save_text=save_text, + **kwargs, + ) - self._line_spacing = line_spacing - self._save_text = save_text + + def _reset_text( + self, + font=None, + x=None, + y=None, + text=None, + line_spacing=None, + background_tight=None, + padding_top=None, + padding_bottom=None, + padding_left=None, + padding_right=None, + anchor_point=None, + anchored_position=None, + save_text=None, + **kwargs + ): + + # store the instance variables + + print('_reset_text Text string to print: {}'.format(text)) + + # Store all the instance variables + if font is not None: + self._font = font + if x is not None: + self.x = x + if y is not None: + self.y = y + # if color is not None: + # self.color = color + # if background_color is not None: + # self.background_color = background_color + if line_spacing is not None: + self._line_spacing = line_spacing + if background_tight is not None: + self._background_tight = background_tight + if padding_top is not None: + self._padding_top = max(0, padding_top) + if padding_bottom is not None: + self._padding_bottom = max(0, padding_bottom) + if padding_left is not None: + self._padding_left = max(0, padding_left) + if padding_right is not None: + self._padding_right = max(0, padding_right) + if anchor_point is not None: + self.anchor_point = anchor_point + if anchored_position is not None: + self._anchored_position = anchored_position + if save_text is not None: + self._save_text = save_text + + # if text is not provided as a parameter (text is None), use the previous value. + if (text is None) and self._save_text: + text = self._text if self._save_text: # text string will be saved self._text = text else: self._text = None # save a None value since text string is not saved - # limit padding to >= 0 - padding_top = max(0, padding_top) - padding_bottom = max(0, padding_bottom) - padding_left = max(0, padding_left) - padding_right = max(0, padding_right) - - # Calculate the text bounding box - - # Calculate tight box to provide bounding box dimensions to match label for - # anchor_position calculations - ( - tight_box_x, - tight_box_y, - tight_x_offset, - tight_y_offset, - ) = self._text_bounding_box( - text, font, self._line_spacing, background_tight=True, - ) - - if background_tight: - box_x = tight_box_x - box_y = tight_box_y - y_offset = tight_y_offset - x_offset = tight_x_offset - - else: - (box_x, box_y, x_offset, y_offset) = self._text_bounding_box( - text, font, self._line_spacing, background_tight=background_tight, - ) - # Calculate the background size including padding - box_x = box_x + padding_left + padding_right - box_y = box_y + padding_top + padding_bottom + # Scale will be passed to Group using kwargs. + if "scale" in kwargs.keys(): + self._scale = kwargs["scale"] - # Create the two-color palette - self.palette = displayio.Palette(2) - self.background_color = background_color - self.color = color + # Check for empty string + if (text == "") or (text is None): # If empty string, just create a zero-sized bounding box and that's it. - # Create the bitmap and TileGrid - self.bitmap = displayio.Bitmap(box_x, box_y, len(self.palette)) - - # Place the text into the Bitmap - self._place_text( - self.bitmap, - text, - font, - self._line_spacing, - padding_left - x_offset, - padding_top + y_offset, - ) - - label_position_yoffset = int( # To calibrate with label.py positioning - (font.get_glyph(ord("M")).height) / 2 - ) - - self.tilegrid = displayio.TileGrid( - self.bitmap, - pixel_shader=self.palette, - width=1, - height=1, - tile_width=box_x, - tile_height=box_y, - default_tile=0, - x=-padding_left + x_offset, - y=label_position_yoffset - y_offset - padding_top, - ) + self._bounding_box = ( + 0, + 0, + 0, # zero width with text == "" + 0, # zero height with text == "" + ) + # Clear out any items in the self Group, in case this is an update to the bitmap_label + for item in self: + self.pop(0) + + else: # The text string is not empty, so create the Bitmap and TileGrid and append to the self Group + + + # Calculate the text bounding box + + # Calculate tight box to provide bounding box dimensions to match label for + # anchor_position calculations + ( + tight_box_x, + tight_box_y, + tight_x_offset, + tight_y_offset, + ) = self._text_bounding_box( + text, self._font, self._line_spacing, background_tight=True, + ) # calculate the box size for a tight background + + if self._background_tight: + box_x = tight_box_x + box_y = tight_box_y + y_offset = tight_y_offset + x_offset = tight_x_offset + + else: # calculate the box size for a loose background + (box_x, box_y, x_offset, y_offset) = self._text_bounding_box( + text, self._font, self._line_spacing, background_tight=self._background_tight, + ) + # Calculate the background size including padding + box_x = box_x + self._padding_left + self._padding_right + box_y = box_y + self._padding_top + self._padding_bottom + + # Create the bitmap and TileGrid + self.bitmap = displayio.Bitmap(box_x, box_y, len(self.palette)) + + # Place the text into the Bitmap + self._place_text( + self.bitmap, + text, + self._font, + self._line_spacing, + self._padding_left - x_offset, + self._padding_top + y_offset, + ) - # instance the Group - # this Group will contain just one TileGrid with one contained bitmap - super().__init__( - max_size=1, x=x, y=y, **kwargs - ) # this will include any arguments, including scale - self.append(self.tilegrid) # add the bitmap's tilegrid to the group + label_position_yoffset = int( # To calibrate with label.py positioning + (self._font.get_glyph(ord("M")).height) / 2 + ) - # Update bounding_box values. Note: To be consistent with label.py, - # this is the bounding box for the text only, not including the background. + self.tilegrid = displayio.TileGrid( + self.bitmap, + pixel_shader=self.palette, + width=1, + height=1, + tile_width=box_x, + tile_height=box_y, + default_tile=0, + x=-self._padding_left + x_offset, + y=label_position_yoffset - y_offset - self._padding_top, + ) - self._bounding_box = ( - self.tilegrid.x, - self.tilegrid.y, - tight_box_x, - tight_box_y, - ) + # Clear out any items in the self Group, in case this is an update to the bitmap_label + for item in self: + self.pop(0) + self.append(self.tilegrid) # add the bitmap's tilegrid to the group + + # Update bounding_box values. Note: To be consistent with label.py, + # this is the bounding box for the text only, not including the background. + self._bounding_box = ( + self.tilegrid.x, + self.tilegrid.y, + tight_box_x, + tight_box_y, + ) - self._anchored_position = anchored_position - self.anchor_point = anchor_point self.anchored_position = ( self._anchored_position - ) # sets anchored_position with setter after bitmap is created + ) # set the anchored_position with setter after bitmap is created, sets the x,y positions of the label @staticmethod def _line_spacing_ypixels(font, line_spacing): @@ -240,7 +322,7 @@ def _text_bounding_box(self, text, font, line_spacing, background_tight=False): # descender, will depend upon font used try: - self._font.load_glyphs(text + glyphs) + font.load_glyphs(text + glyphs) except AttributeError: # ignore if font does not have load_glyphs pass @@ -434,9 +516,8 @@ def line_spacing(self): @line_spacing.setter def line_spacing(self, new_line_spacing): - raise RuntimeError( - "line_spacing is immutable for bitmap_label.py; use label.py for mutable line_spacing" - ) + self._reset_text(line_spacing=new_line_spacing) + @property def color(self): @@ -473,11 +554,10 @@ def text(self): """Text to displayed.""" return self._text - @text.setter + @text.setter # Cannot set color or background color with text setter, use separate setter def text(self, new_text): - raise RuntimeError( - "text is immutable for bitmap_label.py; use label.py library for mutable text" - ) + self._reset_text(text=new_text) + print('updated text: {}'.format(new_text)) @property def font(self): @@ -486,9 +566,7 @@ def font(self): @font.setter def font(self, new_font): - raise RuntimeError( - "font is immutable for bitmap_label.py; use label.py library for mutable font" - ) + self._reset_text(font=new_font) @property def anchor_point(self): From 3e8f35f897e05fe3e639ea273a92ae102c12185c Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 25 Aug 2020 22:26:30 -0500 Subject: [PATCH 15/27] Added getter/setters for text, line_spacing and temporary fix for set_scale, some performance speedups by deleting duplication in bounding box calculations --- adafruit_display_text/bitmap_label.py | 126 +++++++++++--------------- 1 file changed, 52 insertions(+), 74 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index f24b6a3..3584e87 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -137,8 +137,6 @@ def __init__( x=x, y=y, text=text, - # color=color, - # background_color=background_color, line_spacing=line_spacing, background_tight=background_tight, padding_top=padding_top, @@ -170,10 +168,6 @@ def _reset_text( **kwargs ): - # store the instance variables - - print('_reset_text Text string to print: {}'.format(text)) - # Store all the instance variables if font is not None: self._font = font @@ -181,10 +175,6 @@ def _reset_text( self.x = x if y is not None: self.y = y - # if color is not None: - # self.color = color - # if background_color is not None: - # self.background_color = background_color if line_spacing is not None: self._line_spacing = line_spacing if background_tight is not None: @@ -236,27 +226,27 @@ def _reset_text( # Calculate the text bounding box - # Calculate tight box to provide bounding box dimensions to match label for + # Calculate both "tight" and "loose" bounding box dimensions to match label for # anchor_position calculations ( - tight_box_x, + box_x, tight_box_y, - tight_x_offset, + x_offset, tight_y_offset, + loose_box_y, + loose_y_offset ) = self._text_bounding_box( - text, self._font, self._line_spacing, background_tight=True, - ) # calculate the box size for a tight background + text, self._font, self._line_spacing, + ) # calculate the box size for a tight and loose backgrounds if self._background_tight: - box_x = tight_box_x box_y = tight_box_y y_offset = tight_y_offset - x_offset = tight_x_offset else: # calculate the box size for a loose background - (box_x, box_y, x_offset, y_offset) = self._text_bounding_box( - text, self._font, self._line_spacing, background_tight=self._background_tight, - ) + box_y = loose_box_y + y_offset = loose_y_offset + # Calculate the background size including padding box_x = box_x + self._padding_left + self._padding_right box_y = box_y + self._padding_top + self._padding_bottom @@ -300,7 +290,7 @@ def _reset_text( self._bounding_box = ( self.tilegrid.x, self.tilegrid.y, - tight_box_x, + box_x, tight_box_y, ) @@ -310,11 +300,11 @@ def _reset_text( @staticmethod def _line_spacing_ypixels(font, line_spacing): - # Note: Scale is not implemented at this time, any scaling is pushed up to the Group level + # Note: Scaling is provided at the Group level return_value = int(line_spacing * font.get_bounding_box()[1]) return return_value - def _text_bounding_box(self, text, font, line_spacing, background_tight=False): + def _text_bounding_box(self, text, font, line_spacing): # This empirical approach checks several glyphs for maximum ascender and descender height # (consistent with label.py) @@ -345,8 +335,6 @@ def _text_bounding_box(self, text, font, line_spacing, background_tight=False): top = bottom = y_start y_offset_tight = int((font.get_glyph(ord("M")).height) / 2) - # this needs to be reviewed (also in label.py), since it doesn't respond - # properly to the number of newlines. newline = False @@ -387,17 +375,19 @@ def _text_bounding_box(self, text, font, line_spacing, background_tight=False): left = 0 final_box_width = right - left - if background_tight: - final_box_height = bottom - top - final_y_offset = -top + y_offset_tight - else: - final_box_height = (lines - 1) * self._line_spacing_ypixels( + + final_box_height_tight = bottom - top + final_y_offset_tight = -top + y_offset_tight + + final_box_height_loose = (lines - 1) * self._line_spacing_ypixels( font, line_spacing - ) + (ascender_max + descender_max) - final_y_offset = ascender_max + ) + (ascender_max + descender_max) + final_y_offset_loose = ascender_max + + # return (final_box_width, final_box_height, left, final_y_offset) - return (final_box_width, final_box_height, left, final_y_offset) + return (final_box_width, final_box_height_tight, left, final_y_offset_tight, final_box_height_loose, final_y_offset_loose) # pylint: disable=too-many-nested-blocks def _place_text( @@ -410,17 +400,10 @@ def _place_text( yposition, text_palette_index=1, background_palette_index=0, - print_only_pixels=True, # print_only_pixels = True: only update the bitmap where the glyph - # pixel color is > 0. This is especially useful for script fonts where glyph - # bounding boxes overlap - # Set `print_only_pixels=False` to write all pixels ): # placeText - Writes text into a bitmap at the specified location. # - # Verify paletteIndex is working properly with * operator, especially - # if accommodating multicolored fonts - # - # Note: Scale is not implemented at this time, is pushed up to Group level + # Note: scale is pushed up to Group level bitmap_width = bitmap.width bitmap_height = bitmap.height @@ -465,38 +448,18 @@ def _place_text( glyph_offset_x = ( my_glyph.tile_index * my_glyph.width ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. - # for BDF loaded fonts, this should equal 0 - - for y in range(my_glyph.height): - for x in range(my_glyph.width): - x_placement = x + xposition + my_glyph.dx - y_placement = y + yposition - my_glyph.height - my_glyph.dy - - if (bitmap_width > x_placement >= 0) and ( - bitmap_height > y_placement >= 0 - ): - - # Allows for remapping the bitmap indexes using paletteIndex - # for background and text. - palette_indexes = ( - background_palette_index, - text_palette_index, - ) - - this_pixel_color = palette_indexes[ - my_glyph.bitmap[ - y * my_glyph.bitmap.width + x + glyph_offset_x - ] - ] - - if not print_only_pixels or this_pixel_color > 0: - # write all characters if printOnlyPixels = False, - # or if thisPixelColor is > 0 - bitmap[ - y_placement * bitmap_width + x_placement - ] = this_pixel_color - elif y_placement > bitmap_height: - break + # for BDF loaded fonts, this should equal 0 + + bitmap.blit( + xposition + my_glyph.dx, + yposition - my_glyph.height - my_glyph.dy, + my_glyph.bitmap, + x1=glyph_offset_x, + y1=0, + x2=glyph_offset_x + my_glyph.width - 1, + y2=0 + my_glyph.height - 1, + skip_index=0, # do not copy over any 0 background pixels + ) xposition = xposition + my_glyph.shift_x @@ -508,6 +471,22 @@ def bounding_box(self): first two numbers are offset from the x, y origin of this group""" return self._bounding_box + # @property + # def scale(self): + # return self._scale + + # @scale.setter + # def scale(self, new_scale): + # self._scale=new_scale + # #super(displayio.Group, self).scale.fset(self, new_scale) + # anchored_position=self._anchored_position # update the anchored_position + + def set_scale(self, new_scale): + """Set the scaling of the label""" + self._scale=int(round(new_scale)) + self.scale=self._scale + self.anchored_position=self._anchored_position + @property def line_spacing(self): """The amount of space between lines of text, in multiples of the font's @@ -557,7 +536,6 @@ def text(self): @text.setter # Cannot set color or background color with text setter, use separate setter def text(self, new_text): self._reset_text(text=new_text) - print('updated text: {}'.format(new_text)) @property def font(self): From a47afc06c2671d86d7022c669328c70ef02e77e5 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 26 Aug 2020 08:16:19 -0500 Subject: [PATCH 16/27] Add try/except backward compatibility for bitmap.blit function in _place_text --- adafruit_display_text/bitmap_label.py | 58 ++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 3584e87..332778d 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -450,21 +450,59 @@ def _place_text( ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. # for BDF loaded fonts, this should equal 0 - bitmap.blit( - xposition + my_glyph.dx, - yposition - my_glyph.height - my_glyph.dy, - my_glyph.bitmap, - x1=glyph_offset_x, - y1=0, - x2=glyph_offset_x + my_glyph.width - 1, - y2=0 + my_glyph.height - 1, - skip_index=0, # do not copy over any 0 background pixels - ) + try: + bitmap.blit( + xposition + my_glyph.dx, + yposition - my_glyph.height - my_glyph.dy, + my_glyph.bitmap, + x1=glyph_offset_x, + y1=0, + x2=glyph_offset_x + my_glyph.width - 1, + y2=0 + my_glyph.height - 1, + skip_index=0, # do not copy over any 0 background pixels + ) + + except: + for y in range(my_glyph.height): + for x in range(my_glyph.width): + x_placement = x + xposition + my_glyph.dx + y_placement = y + yposition - my_glyph.height - my_glyph.dy + + if (bitmap_width > x_placement >= 0) and ( + bitmap_height > y_placement >= 0 + ): + + # Allows for remapping the bitmap indexes using paletteIndex + # for background and text. + palette_indexes = ( + background_palette_index, + text_palette_index, + ) + + this_pixel_color = palette_indexes[ + my_glyph.bitmap[ + y * my_glyph.bitmap.width + x + glyph_offset_x + ] + ] + + if not print_only_pixels or this_pixel_color > 0: + # write all characters if printOnlyPixels = False, + # or if thisPixelColor is > 0 + bitmap[ + y_placement * bitmap_width + x_placement + ] = this_pixel_color + elif y_placement > bitmap_height: + break xposition = xposition + my_glyph.shift_x return (left, top, right - left, bottom - top) # bounding_box + + + + + @property def bounding_box(self): """An (x, y, w, h) tuple that completely covers all glyphs. The From bece1ad28d0eb74da5f148d623b6cfae916c4b60 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Wed, 26 Aug 2020 14:07:49 -0500 Subject: [PATCH 17/27] Bug fix in try/except in _place_text --- adafruit_display_text/bitmap_label.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 332778d..6260087 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -400,6 +400,10 @@ def _place_text( yposition, text_palette_index=1, background_palette_index=0, + print_only_pixels=True, # print_only_pixels = True: only update the bitmap where the glyph + # pixel color is > 0. This is especially useful for script fonts where glyph + # bounding boxes overlap + # Set `print_only_pixels=False` to write all pixels ): # placeText - Writes text into a bitmap at the specified location. # @@ -514,10 +518,16 @@ def bounding_box(self): # return self._scale # @scale.setter + # #@displayio.Group.scale.setter # def scale(self, new_scale): # self._scale=new_scale # #super(displayio.Group, self).scale.fset(self, new_scale) - # anchored_position=self._anchored_position # update the anchored_position + # self.anchored_position=self._anchored_position # update the anchored_position + # #displayio.Group.scale.__set__(self, new_scale) + # #displayio.Group.scale=new_scale + # #setattr(super(), "scale", new_scale) + # #setattr(self, "scale", new_scale) + # super(displayio.Group, self).scale.__set__(self, new_scale) def set_scale(self, new_scale): """Set the scaling of the label""" From 7ca736f7e106b6616e896af20c856700e83f8eed Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 27 Aug 2020 13:58:29 -0500 Subject: [PATCH 18/27] Add scale mutability, bug fix on blit range --- adafruit_display_text/bitmap_label.py | 88 +++++++++++++-------------- 1 file changed, 42 insertions(+), 46 deletions(-) mode change 100755 => 100644 adafruit_display_text/bitmap_label.py diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py old mode 100755 new mode 100644 index 6260087..843bb85 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -114,12 +114,21 @@ def __init__( **kwargs ): + + # Scale will be passed to Group using kwargs. + if "scale" in kwargs.keys(): + scale = kwargs["scale"] + kwargs.pop("scale") # Do not change scale of self Group, use this value to set scale of local_group + # instance the Group - # this Group will contain just one TileGrid with one contained bitmap + # self Group will contain a single local_group which contains one TileGrid which contains the text bitmap super().__init__( max_size=1, x=x, y=y, **kwargs ) # this will include any arguments, including scale + self.local_group=displayio.Group(max_size=1, **kwargs) # local_group holds the tileGrid and sets the scaling + self.append(self.local_group) # the local_group will always stay in the self Group + self._font = font # Create the two-color palette @@ -130,8 +139,6 @@ def __init__( self._anchor_point = anchor_point self._anchored_position = anchored_position - self._scale = 1 # initialize to the default scale of 1 - # call the text updater with all the arguments. self._reset_text(font=font, x=x, @@ -146,6 +153,7 @@ def __init__( anchor_point=anchor_point, anchored_position=anchored_position, save_text=save_text, + scale=scale, **kwargs, ) @@ -164,7 +172,8 @@ def _reset_text( padding_right=None, anchor_point=None, anchored_position=None, - save_text=None, + save_text=None, + scale=None, **kwargs ): @@ -193,6 +202,8 @@ def _reset_text( self._anchored_position = anchored_position if save_text is not None: self._save_text = save_text + if scale is not None: # Scale will be defined in local_group (Note: self should have scale=1) + self.scale=scale # call the setter # if text is not provided as a parameter (text is None), use the previous value. if (text is None) and self._save_text: @@ -203,10 +214,6 @@ def _reset_text( else: self._text = None # save a None value since text string is not saved - # Scale will be passed to Group using kwargs. - if "scale" in kwargs.keys(): - self._scale = kwargs["scale"] - # Check for empty string if (text == "") or (text is None): # If empty string, just create a zero-sized bounding box and that's it. @@ -280,10 +287,10 @@ def _reset_text( y=label_position_yoffset - y_offset - self._padding_top, ) - # Clear out any items in the self Group, in case this is an update to the bitmap_label - for item in self: - self.pop(0) - self.append(self.tilegrid) # add the bitmap's tilegrid to the group + # Clear out any items in the local_group Group, in case this is an update to the bitmap_label + for item in self.local_group: + self.local_group.pop(0) + self.local_group.append(self.tilegrid) # add the bitmap's tilegrid to the group # Update bounding_box values. Note: To be consistent with label.py, # this is the bounding box for the text only, not including the background. @@ -461,8 +468,8 @@ def _place_text( my_glyph.bitmap, x1=glyph_offset_x, y1=0, - x2=glyph_offset_x + my_glyph.width - 1, - y2=0 + my_glyph.height - 1, + x2=glyph_offset_x + my_glyph.width, + y2=0 + my_glyph.height, skip_index=0, # do not copy over any 0 background pixels ) @@ -502,38 +509,22 @@ def _place_text( return (left, top, right - left, bottom - top) # bounding_box - - - - - @property def bounding_box(self): """An (x, y, w, h) tuple that completely covers all glyphs. The first two numbers are offset from the x, y origin of this group""" return self._bounding_box - # @property - # def scale(self): - # return self._scale - - # @scale.setter - # #@displayio.Group.scale.setter - # def scale(self, new_scale): - # self._scale=new_scale - # #super(displayio.Group, self).scale.fset(self, new_scale) - # self.anchored_position=self._anchored_position # update the anchored_position - # #displayio.Group.scale.__set__(self, new_scale) - # #displayio.Group.scale=new_scale - # #setattr(super(), "scale", new_scale) - # #setattr(self, "scale", new_scale) - # super(displayio.Group, self).scale.__set__(self, new_scale) - - def set_scale(self, new_scale): + @property + def scale(self): """Set the scaling of the label""" - self._scale=int(round(new_scale)) - self.scale=self._scale - self.anchored_position=self._anchored_position + return self._scale + + @scale.setter + def scale(self, new_scale): + self.local_group.scale=new_scale + self._scale=new_scale + self.anchored_position=self._anchored_position # update the anchored_position @property def line_spacing(self): @@ -543,7 +534,10 @@ def line_spacing(self): @line_spacing.setter def line_spacing(self, new_line_spacing): - self._reset_text(line_spacing=new_line_spacing) + if self._save_text: + self._reset_text(line_spacing=new_line_spacing) + else: + raise RuntimeError("line_spacing is immutable when save_text is False") @property @@ -592,7 +586,10 @@ def font(self): @font.setter def font(self, new_font): - self._reset_text(font=new_font) + if self._save_text: + self._reset_text(font=new_font) + else: + raise RuntimeError("font is immutable when save_text is False") @property def anchor_point(self): @@ -617,16 +614,15 @@ def anchored_position(self): @anchored_position.setter def anchored_position(self, new_position): self._anchored_position = new_position - # Set anchored_position if (self._anchor_point is not None) and (self._anchored_position is not None): self.x = int( new_position[0] - - (self._bounding_box[0] * self._scale) - - round(self._anchor_point[0] * (self._bounding_box[2] * self._scale)) + - (self._bounding_box[0] * self.scale) + - round(self._anchor_point[0] * (self._bounding_box[2] * self.scale)) ) self.y = int( new_position[1] - - (self._bounding_box[1] * self._scale) - - round(self._anchor_point[1] * self._bounding_box[3] * self._scale) + - (self._bounding_box[1] * self.scale) + - round(self._anchor_point[1] * self._bounding_box[3] * self.scale) ) From 1a4746443085b5f23c7898b9e104cf5528e0377f Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 27 Aug 2020 14:45:35 -0500 Subject: [PATCH 19/27] ran black and pylint updates --- adafruit_display_text/bitmap_label.py | 148 +++++++++++++++----------- 1 file changed, 87 insertions(+), 61 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 843bb85..68351bf 100644 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -98,8 +98,8 @@ def __init__( x=0, y=0, text="", - max_glyphs=None, # This input parameter is ignored, only present for compatibility - # with label.py + max_glyphs=None, # This input parameter is ignored, only present for compatibility + # with label.py color=0xFFFFFF, background_color=None, line_spacing=1.25, @@ -114,22 +114,31 @@ def __init__( **kwargs ): - # Scale will be passed to Group using kwargs. if "scale" in kwargs.keys(): scale = kwargs["scale"] - kwargs.pop("scale") # Do not change scale of self Group, use this value to set scale of local_group + kwargs.pop( + "scale" + ) # Do not change scale of self Group, use this value to set scale of + # local_group # instance the Group - # self Group will contain a single local_group which contains one TileGrid which contains the text bitmap + # self Group will contain a single local_group which contains one TileGrid which contains + # the text bitmap super().__init__( max_size=1, x=x, y=y, **kwargs ) # this will include any arguments, including scale - self.local_group=displayio.Group(max_size=1, **kwargs) # local_group holds the tileGrid and sets the scaling - self.append(self.local_group) # the local_group will always stay in the self Group + self.local_group = displayio.Group( + max_size=1, **kwargs + ) # local_group holds the tileGrid and + # sets the scaling + self.append( + self.local_group + ) # the local_group will always stay in the self Group self._font = font + self._text = text # Create the two-color palette self.palette = displayio.Palette(2) @@ -140,23 +149,23 @@ def __init__( self._anchored_position = anchored_position # call the text updater with all the arguments. - self._reset_text(font=font, - x=x, - y=y, - text=text, - line_spacing=line_spacing, - background_tight=background_tight, - padding_top=padding_top, - padding_bottom=padding_bottom, - padding_left=padding_left, - padding_right=padding_right, - anchor_point=anchor_point, - anchored_position=anchored_position, - save_text=save_text, - scale=scale, - **kwargs, - ) - + self._reset_text( + font=font, + x=x, + y=y, + text=text, + line_spacing=line_spacing, + background_tight=background_tight, + padding_top=padding_top, + padding_bottom=padding_bottom, + padding_left=padding_left, + padding_right=padding_right, + anchor_point=anchor_point, + anchored_position=anchored_position, + save_text=save_text, + scale=scale, + **kwargs, + ) def _reset_text( self, @@ -175,7 +184,7 @@ def _reset_text( save_text=None, scale=None, **kwargs - ): + ): # Store all the instance variables if font is not None: @@ -202,8 +211,10 @@ def _reset_text( self._anchored_position = anchored_position if save_text is not None: self._save_text = save_text - if scale is not None: # Scale will be defined in local_group (Note: self should have scale=1) - self.scale=scale # call the setter + if ( + scale is not None + ): # Scale will be defined in local_group (Note: self should have scale=1) + self.scale = scale # call the setter # if text is not provided as a parameter (text is None), use the previous value. if (text is None) and self._save_text: @@ -214,22 +225,23 @@ def _reset_text( else: self._text = None # save a None value since text string is not saved - # Check for empty string - if (text == "") or (text is None): # If empty string, just create a zero-sized bounding box and that's it. + if (text == "") or ( + text is None + ): # If empty string, just create a zero-sized bounding box and that's it. self._bounding_box = ( - 0, - 0, - 0, # zero width with text == "" - 0, # zero height with text == "" - ) + 0, + 0, + 0, # zero width with text == "" + 0, # zero height with text == "" + ) # Clear out any items in the self Group, in case this is an update to the bitmap_label - for item in self: + for _ in self: self.pop(0) - else: # The text string is not empty, so create the Bitmap and TileGrid and append to the self Group - + else: # The text string is not empty, so create the Bitmap and TileGrid and + # append to the self Group # Calculate the text bounding box @@ -241,19 +253,19 @@ def _reset_text( x_offset, tight_y_offset, loose_box_y, - loose_y_offset + loose_y_offset, ) = self._text_bounding_box( text, self._font, self._line_spacing, - ) # calculate the box size for a tight and loose backgrounds + ) # calculate the box size for a tight and loose backgrounds - if self._background_tight: + if self._background_tight: box_y = tight_box_y y_offset = tight_y_offset - else: # calculate the box size for a loose background + else: # calculate the box size for a loose background box_y = loose_box_y y_offset = loose_y_offset - + # Calculate the background size including padding box_x = box_x + self._padding_left + self._padding_right box_y = box_y + self._padding_top + self._padding_bottom @@ -287,10 +299,13 @@ def _reset_text( y=label_position_yoffset - y_offset - self._padding_top, ) - # Clear out any items in the local_group Group, in case this is an update to the bitmap_label - for item in self.local_group: + # Clear out any items in the local_group Group, in case this is an update to + # the bitmap_label + for _ in self.local_group: self.local_group.pop(0) - self.local_group.append(self.tilegrid) # add the bitmap's tilegrid to the group + self.local_group.append( + self.tilegrid + ) # add the bitmap's tilegrid to the group # Update bounding_box values. Note: To be consistent with label.py, # this is the bounding box for the text only, not including the background. @@ -303,7 +318,8 @@ def _reset_text( self.anchored_position = ( self._anchored_position - ) # set the anchored_position with setter after bitmap is created, sets the x,y positions of the label + ) # set the anchored_position with setter after bitmap is created, sets the + # x,y positions of the label @staticmethod def _line_spacing_ypixels(font, line_spacing): @@ -383,18 +399,24 @@ def _text_bounding_box(self, text, font, line_spacing): final_box_width = right - left - final_box_height_tight = bottom - top final_y_offset_tight = -top + y_offset_tight final_box_height_loose = (lines - 1) * self._line_spacing_ypixels( - font, line_spacing - ) + (ascender_max + descender_max) + font, line_spacing + ) + (ascender_max + descender_max) final_y_offset_loose = ascender_max # return (final_box_width, final_box_height, left, final_y_offset) - return (final_box_width, final_box_height_tight, left, final_y_offset_tight, final_box_height_loose, final_y_offset_loose) + return ( + final_box_width, + final_box_height_tight, + left, + final_y_offset_tight, + final_box_height_loose, + final_y_offset_loose, + ) # pylint: disable=too-many-nested-blocks def _place_text( @@ -459,7 +481,7 @@ def _place_text( glyph_offset_x = ( my_glyph.tile_index * my_glyph.width ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. - # for BDF loaded fonts, this should equal 0 + # for BDF loaded fonts, this should equal 0 try: bitmap.blit( @@ -470,14 +492,16 @@ def _place_text( y1=0, x2=glyph_offset_x + my_glyph.width, y2=0 + my_glyph.height, - skip_index=0, # do not copy over any 0 background pixels + skip_index=0, # do not copy over any 0 background pixels ) except: for y in range(my_glyph.height): for x in range(my_glyph.width): x_placement = x + xposition + my_glyph.dx - y_placement = y + yposition - my_glyph.height - my_glyph.dy + y_placement = ( + y + yposition - my_glyph.height - my_glyph.dy + ) if (bitmap_width > x_placement >= 0) and ( bitmap_height > y_placement >= 0 @@ -492,7 +516,9 @@ def _place_text( this_pixel_color = palette_indexes[ my_glyph.bitmap[ - y * my_glyph.bitmap.width + x + glyph_offset_x + y * my_glyph.bitmap.width + + x + + glyph_offset_x ] ] @@ -519,12 +545,12 @@ def bounding_box(self): def scale(self): """Set the scaling of the label""" return self._scale - + @scale.setter def scale(self, new_scale): - self.local_group.scale=new_scale - self._scale=new_scale - self.anchored_position=self._anchored_position # update the anchored_position + self.local_group.scale = new_scale + self._scale = new_scale + self.anchored_position = self._anchored_position # update the anchored_position @property def line_spacing(self): @@ -539,7 +565,6 @@ def line_spacing(self, new_line_spacing): else: raise RuntimeError("line_spacing is immutable when save_text is False") - @property def color(self): """Color of the text as an RGB hex number.""" @@ -575,17 +600,18 @@ def text(self): """Text to displayed.""" return self._text - @text.setter # Cannot set color or background color with text setter, use separate setter + @text.setter # Cannot set color or background color with text setter, use separate setter def text(self, new_text): self._reset_text(text=new_text) @property def font(self): """Font to use for text display.""" - return self.font + return self._font @font.setter def font(self, new_font): + self._font = new_font if self._save_text: self._reset_text(font=new_font) else: From 98467964bf61e4c021cd9d33cfb15a7ccd564e2a Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 27 Aug 2020 14:55:06 -0500 Subject: [PATCH 20/27] pylint fixes --- adafruit_display_text/bitmap_label.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 68351bf..d7b89a2 100644 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -88,7 +88,7 @@ class Label(displayio.Group): """ # pylint: disable=unused-argument, too-many-instance-attributes, too-many-locals, too-many-arguments - # pylint: disable=too-many-branches, no-self-use + # pylint: disable=too-many-branches, no-self-use, too-many-statements # Note: max_glyphs parameter is unnecessary, this is used for direct # compatibility with label.py @@ -495,7 +495,7 @@ def _place_text( skip_index=0, # do not copy over any 0 background pixels ) - except: + except AttributeError: for y in range(my_glyph.height): for x in range(my_glyph.width): x_placement = x + xposition + my_glyph.dx From df1c8eafd29fed0e6210a53b4e4cde729a926a88 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 27 Aug 2020 16:48:06 -0500 Subject: [PATCH 21/27] fix bug with scale initialization --- adafruit_display_text/bitmap_label.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) mode change 100644 => 100755 adafruit_display_text/bitmap_label.py diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py old mode 100644 new mode 100755 index d7b89a2..1648a34 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -119,8 +119,9 @@ def __init__( scale = kwargs["scale"] kwargs.pop( "scale" - ) # Do not change scale of self Group, use this value to set scale of - # local_group + ) # Do not change scale of self Group, use this value to set scale of local_group + else: + scale = 1 # Set default scale=1 # instance the Group # self Group will contain a single local_group which contains one TileGrid which contains From 9ef676ef6d0fea67dab8ea2dfdbba5b9bda5b159 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 28 Aug 2020 10:12:09 -0500 Subject: [PATCH 22/27] Fix scale bug in label.py, remove kwargs from both and add scale input parameter --- adafruit_display_text/bitmap_label.py | 20 +++-------- adafruit_display_text/label.py | 51 ++++++++++++++++++--------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 1648a34..e05ee12 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -111,29 +111,19 @@ def __init__( anchor_point=None, anchored_position=None, save_text=True, # can reduce memory use if save_text = False - **kwargs + scale=1, ): - # Scale will be passed to Group using kwargs. - if "scale" in kwargs.keys(): - scale = kwargs["scale"] - kwargs.pop( - "scale" - ) # Do not change scale of self Group, use this value to set scale of local_group - else: - scale = 1 # Set default scale=1 - # instance the Group # self Group will contain a single local_group which contains one TileGrid which contains # the text bitmap super().__init__( - max_size=1, x=x, y=y, **kwargs + max_size=1, x=x, y=y, ) # this will include any arguments, including scale self.local_group = displayio.Group( - max_size=1, **kwargs - ) # local_group holds the tileGrid and - # sets the scaling + max_size=1, scale=scale + ) # local_group holds the tileGrid and sets the scaling self.append( self.local_group ) # the local_group will always stay in the self Group @@ -165,7 +155,6 @@ def __init__( anchored_position=anchored_position, save_text=save_text, scale=scale, - **kwargs, ) def _reset_text( @@ -184,7 +173,6 @@ def _reset_text( anchored_position=None, save_text=None, scale=None, - **kwargs ): # Store all the instance variables diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index bc9e987..4ac806a 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -79,18 +79,16 @@ def __init__( padding_right=0, anchor_point=None, anchored_position=None, - **kwargs + scale=1, ): - if "scale" in kwargs.keys(): - self._scale = kwargs["scale"] - else: - self._scale = 1 if not max_glyphs and not text: raise RuntimeError("Please provide a max size, or initial text") if not max_glyphs: max_glyphs = len(text) # add one to max_size for the background bitmap tileGrid - super().__init__(max_size=max_glyphs + 1, **kwargs) + super().__init__(max_size=1) + self.local_group=displayio.Group(max_size=max_glyphs + 1, scale=scale) + self.append(self.local_group) self.width = max_glyphs self._font = font @@ -121,12 +119,15 @@ def __init__( self._padding_bottom = padding_bottom self._padding_left = padding_left self._padding_right = padding_right + + self._scale=scale if text is not None: self._update_text(str(text)) if (anchored_position is not None) and (anchor_point is not None): self.anchored_position = anchored_position + def _create_background_box(self, lines, y_offset): left = self._boundingbox[0] @@ -172,6 +173,8 @@ def _create_background_box(self, lines, y_offset): y=y_box_offset, ) + + return tile_grid def _update_background_color(self, new_color): @@ -179,7 +182,7 @@ def _update_background_color(self, new_color): if new_color is None: self._background_palette.make_transparent(0) if self._added_background_tilegrid: - self.pop(0) + self.local_group.pop(0) self._added_background_tilegrid = False else: self._background_palette.make_opaque(0) @@ -200,10 +203,10 @@ def _update_background_color(self, new_color): self._boundingbox[3] + self._padding_top + self._padding_bottom > 0 ) ): - if len(self) > 0: - self.insert(0, self._create_background_box(lines, y_offset)) + if len(self.local_group) > 0: # This can be simplified in CP v6.0, when group.append(0) bug is corrected + self.local_group.insert(0, self._create_background_box(lines, y_offset)) else: - self.append(self._create_background_box(lines, y_offset)) + self.local_group.append(self._create_background_box(lines, y_offset)) self._added_background_tilegrid = True else: # a bitmap is present in the self Group @@ -217,9 +220,9 @@ def _update_background_color(self, new_color): self._boundingbox[3] + self._padding_top + self._padding_bottom > 0 ) ): - self[0] = self._create_background_box(lines, y_offset) + self.local_group[0] = self._create_background_box(lines, y_offset) else: # delete the existing bitmap - self.pop(0) + self.local_group.pop(0) self._added_background_tilegrid = False def _update_text( @@ -284,10 +287,10 @@ def _update_text( x=position_x, y=position_y, ) - if tilegrid_count < len(self): - self[tilegrid_count] = face + if tilegrid_count < len(self.local_group): + self.local_group[tilegrid_count] = face else: - self.append(face) + self.local_group.append(face) tilegrid_count += 1 x += glyph.shift_x i += 1 @@ -296,8 +299,8 @@ def _update_text( if left is None: left = 0 - while len(self) > tilegrid_count: # i: - self.pop() + while len(self.local_group) > tilegrid_count: # i: + self.local_group.pop() self._text = new_text self._boundingbox = (left, top, right - left, bottom - top) @@ -319,6 +322,7 @@ def line_spacing(self): @line_spacing.setter def line_spacing(self, spacing): self._line_spacing = spacing + self.text=self._text # redraw the box @property def color(self): @@ -358,6 +362,19 @@ def text(self, new_text): except RuntimeError as run_error: raise RuntimeError("Text length exceeds max_glyphs") from run_error + @property + def scale(self): + return self._scale + + @scale.setter + def scale(self, new_scale): + current_anchored_position=self.anchored_position + self._scale=new_scale + self.local_group.scale=new_scale + self.anchored_position=current_anchored_position + + + @property def font(self): """Font to use for text display.""" From 5d299cc0e8a0001b83a946ae5c39b1e3c7c10aaa Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 28 Aug 2020 10:13:52 -0500 Subject: [PATCH 23/27] ran black and pylint --- adafruit_display_text/bitmap_label.py | 2 +- adafruit_display_text/label.py | 34 ++++++++++++++------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index e05ee12..7461c0a 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -118,7 +118,7 @@ def __init__( # self Group will contain a single local_group which contains one TileGrid which contains # the text bitmap super().__init__( - max_size=1, x=x, y=y, + max_size=1, x=x, y=y, ) # this will include any arguments, including scale self.local_group = displayio.Group( diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 4ac806a..0c6b98a 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -87,7 +87,7 @@ def __init__( max_glyphs = len(text) # add one to max_size for the background bitmap tileGrid super().__init__(max_size=1) - self.local_group=displayio.Group(max_size=max_glyphs + 1, scale=scale) + self.local_group = displayio.Group(max_size=max_glyphs + 1, scale=scale) self.append(self.local_group) self.width = max_glyphs @@ -119,15 +119,14 @@ def __init__( self._padding_bottom = padding_bottom self._padding_left = padding_left self._padding_right = padding_right - - self._scale=scale + + self._scale = scale if text is not None: self._update_text(str(text)) if (anchored_position is not None) and (anchor_point is not None): self.anchored_position = anchored_position - def _create_background_box(self, lines, y_offset): left = self._boundingbox[0] @@ -173,8 +172,6 @@ def _create_background_box(self, lines, y_offset): y=y_box_offset, ) - - return tile_grid def _update_background_color(self, new_color): @@ -203,10 +200,16 @@ def _update_background_color(self, new_color): self._boundingbox[3] + self._padding_top + self._padding_bottom > 0 ) ): - if len(self.local_group) > 0: # This can be simplified in CP v6.0, when group.append(0) bug is corrected - self.local_group.insert(0, self._create_background_box(lines, y_offset)) + if ( + len(self.local_group) > 0 + ): # This can be simplified in CP v6.0, when group.append(0) bug is corrected + self.local_group.insert( + 0, self._create_background_box(lines, y_offset) + ) else: - self.local_group.append(self._create_background_box(lines, y_offset)) + self.local_group.append( + self._create_background_box(lines, y_offset) + ) self._added_background_tilegrid = True else: # a bitmap is present in the self Group @@ -322,7 +325,7 @@ def line_spacing(self): @line_spacing.setter def line_spacing(self, spacing): self._line_spacing = spacing - self.text=self._text # redraw the box + self.text = self._text # redraw the box @property def color(self): @@ -364,16 +367,15 @@ def text(self, new_text): @property def scale(self): + """Set the scaling of the label, in integer values""" return self._scale @scale.setter def scale(self, new_scale): - current_anchored_position=self.anchored_position - self._scale=new_scale - self.local_group.scale=new_scale - self.anchored_position=current_anchored_position - - + current_anchored_position = self.anchored_position + self._scale = new_scale + self.local_group.scale = new_scale + self.anchored_position = current_anchored_position @property def font(self): From 4bab6cf4c0a82a23f173309e7008dffc4d904a96 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 28 Aug 2020 10:20:55 -0500 Subject: [PATCH 24/27] Update docs file to include bitmap_label --- docs/api.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 790fedf..920815a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,3 +6,6 @@ .. automodule:: adafruit_display_text.label :members: + +.. automodule:: adafruit_display_text.bitmap_label + :members: From f23910cdce193f7b4c6dc70cbed1ae48fcfdb395 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 28 Aug 2020 15:34:01 -0500 Subject: [PATCH 25/27] Breakout blit function, improve handling of builtin builtin bitmap.blit, add back kwargs passing to self Group instance --- adafruit_display_text/bitmap_label.py | 154 ++++++++++++++++---------- adafruit_display_text/label.py | 13 ++- 2 files changed, 106 insertions(+), 61 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index 7461c0a..bf348fe 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -112,14 +112,18 @@ def __init__( anchored_position=None, save_text=True, # can reduce memory use if save_text = False scale=1, + **kwargs, ): # instance the Group - # self Group will contain a single local_group which contains one TileGrid which contains - # the text bitmap + # self Group will contain a single local_group which contains a Group (self.local_group) + # which contains a TileGrid (self.tilegrid) which contains the text bitmap (self.bitmap) super().__init__( - max_size=1, x=x, y=y, - ) # this will include any arguments, including scale + max_size=1, x=x, y=y, scale=1, **kwargs, + ) + # the self group scale should always remain at 1, the self.local_group will + # be used to set the scale + # **kwargs will pass any additional arguments provided to the Label self.local_group = displayio.Group( max_size=1, scale=scale @@ -418,18 +422,14 @@ def _place_text( yposition, text_palette_index=1, background_palette_index=0, - print_only_pixels=True, # print_only_pixels = True: only update the bitmap where the glyph - # pixel color is > 0. This is especially useful for script fonts where glyph - # bounding boxes overlap - # Set `print_only_pixels=False` to write all pixels + skip_index=0, # set to None to write all pixels, other wise skip this palette index + # when copying glyph bitmaps (this is important for slanted text + # where rectangulary glyph boxes overlap) ): # placeText - Writes text into a bitmap at the specified location. # # Note: scale is pushed up to Group level - bitmap_width = bitmap.width - bitmap_height = bitmap.height - x_start = xposition # starting x position (left margin) y_start = yposition @@ -472,58 +472,94 @@ def _place_text( ) # for type BuiltinFont, this creates the x-offset in the glyph bitmap. # for BDF loaded fonts, this should equal 0 - try: - bitmap.blit( - xposition + my_glyph.dx, - yposition - my_glyph.height - my_glyph.dy, - my_glyph.bitmap, - x1=glyph_offset_x, - y1=0, - x2=glyph_offset_x + my_glyph.width, - y2=0 + my_glyph.height, - skip_index=0, # do not copy over any 0 background pixels - ) - - except AttributeError: - for y in range(my_glyph.height): - for x in range(my_glyph.width): - x_placement = x + xposition + my_glyph.dx - y_placement = ( - y + yposition - my_glyph.height - my_glyph.dy - ) - - if (bitmap_width > x_placement >= 0) and ( - bitmap_height > y_placement >= 0 - ): - - # Allows for remapping the bitmap indexes using paletteIndex - # for background and text. - palette_indexes = ( - background_palette_index, - text_palette_index, - ) - - this_pixel_color = palette_indexes[ - my_glyph.bitmap[ - y * my_glyph.bitmap.width - + x - + glyph_offset_x - ] - ] - - if not print_only_pixels or this_pixel_color > 0: - # write all characters if printOnlyPixels = False, - # or if thisPixelColor is > 0 - bitmap[ - y_placement * bitmap_width + x_placement - ] = this_pixel_color - elif y_placement > bitmap_height: - break + self._blit( + bitmap, + xposition + my_glyph.dx, + yposition - my_glyph.height - my_glyph.dy, + my_glyph.bitmap, + x_1=glyph_offset_x, + y_1=0, + x_2=glyph_offset_x + my_glyph.width, + y_2=0 + my_glyph.height, + skip_index=skip_index, # do not copy over any 0 background pixels + ) xposition = xposition + my_glyph.shift_x return (left, top, right - left, bottom - top) # bounding_box + def _blit( + self, + bitmap, # target bitmap + x, # target x upper left corner + y, # target y upper left corner + source_bitmap, # source bitmap + x_1=0, # source x start + y_1=0, # source y start + x_2=None, # source x end + y_2=None, # source y end + skip_index=None, # palette index that will not be copied + # (for example: the background color of a glyph) + ): + + if hasattr(bitmap, "blit"): # if bitmap has a built-in blit function, call it + # this function should perform its own input checks + bitmap.blit( + x, + y, + source_bitmap, + x1=x_1, + y1=y_1, + x2=x_2, + y2=y_2, + skip_index=skip_index, + ) + + else: # perform pixel by pixel copy of the bitmap + + # Perform input checks + + if x_2 is None: + x_2 = source_bitmap.width + if y_2 is None: + y_2 = source_bitmap.height + + # Rearrange so that x_1 < x_2 and y1 < y2 + if x_1 > x_2: + x_1, x_2 = x_2, x_1 + if y_1 > y_2: + y_1, y_2 = y_2, y_1 + + # Ensure that x2 and y2 are within source bitmap size + x_2 = min(x_2, source_bitmap.width) + y_2 = min(y_2, source_bitmap.height) + + for y_count in range(y_2 - y_1): + for x_count in range(x_2 - x_1): + x_placement = x + x_count + y_placement = y + y_count + + if (bitmap.width > x_placement >= 0) and ( + bitmap.height > y_placement >= 0 + ): # ensure placement is within target bitmap + + # get the palette index from the source bitmap + this_pixel_color = source_bitmap[ + y_1 + + ( + y_count * source_bitmap.width + ) # Direct index into a bitmap array is speedier than [x,y] tuple + + x_1 + + x_count + ] + + if (skip_index is None) or (this_pixel_color != skip_index): + bitmap[ # Direct index into a bitmap array is speedier than [x,y] tuple + y_placement * bitmap.width + x_placement + ] = this_pixel_color + elif y_placement > bitmap.height: + break + @property def bounding_box(self): """An (x, y, w, h) tuple that completely covers all glyphs. The @@ -532,7 +568,7 @@ def bounding_box(self): @property def scale(self): - """Set the scaling of the label""" + """Set the scaling of the label, in integer values""" return self._scale @scale.setter diff --git a/adafruit_display_text/label.py b/adafruit_display_text/label.py index 0c6b98a..8ef4a32 100755 --- a/adafruit_display_text/label.py +++ b/adafruit_display_text/label.py @@ -80,14 +80,23 @@ def __init__( anchor_point=None, anchored_position=None, scale=1, + **kwargs ): if not max_glyphs and not text: raise RuntimeError("Please provide a max size, or initial text") if not max_glyphs: max_glyphs = len(text) # add one to max_size for the background bitmap tileGrid - super().__init__(max_size=1) - self.local_group = displayio.Group(max_size=max_glyphs + 1, scale=scale) + + # instance the Group + # self Group will contain a single local_group which contains a Group (self.local_group) + # which contains a TileGrid + super().__init__( + max_size=1, scale=1, **kwargs + ) # The self scale should always be 1 + self.local_group = displayio.Group( + max_size=max_glyphs + 1, scale=scale + ) # local_group will set the scale self.append(self.local_group) self.width = max_glyphs From 444a8adb73138ddaa222fd7b20880861353a7a50 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Sat, 5 Sep 2020 11:35:28 -0500 Subject: [PATCH 26/27] Correct documentation errors related to mutability --- adafruit_display_text/bitmap_label.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/adafruit_display_text/bitmap_label.py b/adafruit_display_text/bitmap_label.py index bf348fe..26332b7 100755 --- a/adafruit_display_text/bitmap_label.py +++ b/adafruit_display_text/bitmap_label.py @@ -50,13 +50,12 @@ class Label(displayio.Group): """A label displaying a string of text that is stored in a bitmap. Note: This ``bitmap_label.py`` library utilizes a bitmap to display the text. This method is memory-conserving relative to ``label.py``. - For the bitmap_label library, the font, text, and line_spacing must be set at - instancing and are immutable. The ``max_glyphs`` parameter is ignored and is present + The ``max_glyphs`` parameter is ignored and is present only for direct compatability with label.py. - For use cases where text changes are required after the initial instancing, please - use the `label.py` library. - For further reduction in memory usage, set save_text to False (text string will not - be stored). + + For further reduction in memory usage, set ``save_text=False`` (text string will not + be stored and ``line_spacing`` and ``font`` are immutable with ``save_text`` + set to ``False``). The origin point set by ``x`` and ``y`` properties will be the left edge of the bounding box, and in the center of a M From 6513a958673532a0d0414d644f49d5aeb6e96e3f Mon Sep 17 00:00:00 2001 From: Erik Tollerud Date: Sat, 17 Oct 2020 23:59:24 -0400 Subject: [PATCH 27/27] add init --- adafruit_display_text/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 adafruit_display_text/__init__.py diff --git a/adafruit_display_text/__init__.py b/adafruit_display_text/__init__.py new file mode 100644 index 0000000..e69de29