Skip to content

Add bitmap_label.py library for reduced memory usage for displaying text #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Aug 17, 2020

Conversation

kmatch98
Copy link
Contributor

@kmatch98 kmatch98 commented Aug 7, 2020

I created this new bitmap_label.py library as a drop-in replacement for label.py but it uses less memory.

This new library uses a bitmap to store text. It has been calibrated against the position of label.py.

I include two examples that can be used to compare the two versions. The new library consistently uses less memory than the equivalent text in label.py. This can be evaluated directly using the example file display_text_label_vs_bitmap_label_simpletest.py which also displays the amount of memory utilized.

Other impacts:

The speed of using the bitmap_label library is inferior compared to the existing label library, primarily limited by the current of copying the bitmap glyphs. This bitmap copy performance is the subject of a related issue with display.io.

@kmatch98
Copy link
Contributor Author

kmatch98 commented Aug 7, 2020

I commited some updates to resolve some of the pylint errors on bitmap_label but over-rode some of them. Also, the examples retain some pylint errors. Please let me know whether I need to resolve these.

@tannewt tannewt requested a review from FoamyGuy August 10, 2020 21:48
Copy link
Contributor

@FoamyGuy FoamyGuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is close to ready, but I do think there are a few things we can finish up.

There are a few small tidying up things to take care of that are leftover from cookie-cutter.

There are a few tweaks to _memory_saver configuration that I think I'd like to make.

And possibly add a simpletest example, and/or change up the existing ones a bit.

The one other thing that comes to mind now is I think we should provide all of the same properties as the other label.py like color, background_color, text etc... I think most of them can be copied from the existing one Mostly from around here and below in the existing label.

I can help with making these changes tomorrow night.

@kmatch98
Copy link
Contributor Author

kmatch98 commented Aug 12, 2020

I updated the getters and setters to match label.py.

I have a couple of pylint errors that I'm not sure how to resolve. These are three setters (font, text, line_spacing) that only return errors. I suspect it is flagging these functions because they don't use the input variables or any references to self. Any suggestions on how to solve the pylint errors?

iMac-86:adafruit_display_text margaret$ pylint bitmap_label.py
************* Module bitmap_label
bitmap_label.py:424:4: R0201: Method could be a function (no-self-use)
bitmap_label.py:465:4: R0201: Method could be a function (no-self-use)
bitmap_label.py:476:4: R0201: Method could be a function (no-self-use)

Here is the code I used to verify the operation of the getters/setters:

gc.collect()
label_start_memory = gc.mem_free()
start_time = time.monotonic()

bmap_label = label.Label(
    font=terminalio.FONT,
    text=my_string,
    color=0xFFFFFF,
    max_glyphs=len(my_string),
    background_color=0xFF0000,
    padding_bottom=0,
    padding_left=0,
    padding_right=0,
    padding_top=0,
    background_tight=False,
    x=10,
    y=30,
    line_spacing=1.25,
    scale=my_scale,
)
end_time = time.monotonic()

gc.collect()
label_end_memory = gc.mem_free()

bmap_group = displayio.Group(max_size=1)  # Create a group for displaying
bmap_group.append(bmap_label)


print("***")
print("{} memory used: {}".format(version, label_start_memory - label_end_memory))
print("{} time to process: {} seconds".format(version, end_time - start_time))
display.auto_refresh = True

display.show(bmap_group)

print("bounding_box: {}".format(bmap_label.bounding_box))
import time
time.sleep(1)
print('color: {}'.format(bmap_label.color))
bmap_label.color=0xFFFF00
time.sleep(1)
print('background_color: {}'.format(bmap_label.background_color))
bmap_label.background_color=0x00FFFF
time.sleep(1)
print('background_color: {}'.format(bmap_label.background_color))
bmap_label.background_color=0x0000FF
time.sleep(1)

print('text: {}'.format(bmap_label.text))
#bmap_label.text='new text' # should cause an error

print('current linespacing: {}'.format(bmap_label.line_spacing))
#bmap_label.line_spacing=3 # should cause an error
print('font: {}'.format(font))
#bmap_label.font=terminalio.FONT # should cause an error
time.sleep(1)

print('anchored_position: {}'.format(bmap_label.anchored_position))
bmap_label.anchored_position=(320/2, 240/2)
print('anchored_position: {}'.format(bmap_label.anchored_position))
time.sleep(1)

print('anchor_point: {}'.format(bmap_label.anchor_point))
bmap_label.anchor_point=(0.5,0.5)
time.sleep(1)

print('anchored_position: {}'.format(bmap_label.anchored_position))
bmap_label.anchored_position=(320/2, 240/2)
print('anchored_position: {}'.format(bmap_label.anchored_position))
time.sleep(1)

print('anchor_point: {}'.format(bmap_label.anchor_point))
bmap_label.anchor_point=(0,0)
print('anchor_point: {}'.format(bmap_label.anchor_point))
time.sleep(1)


print('anchor_point: {}'.format(bmap_label.anchor_point))
bmap_label.anchor_point=(0.5,0.5)
time.sleep(1)




while True:
    pass

@FoamyGuy
Copy link
Contributor

I think it's okay to add the pylint ignore inside of those three functions to disable the no-self-use check.

Although I'm also curious how hard it would be (or if there is any downside) to allow those properties to be mutable by having them call _place_text() again. I'm going to try it out and see if I can get it working.

@kmatch98
Copy link
Contributor Author

Good point maybe if the bitmap is recreated then it will should work fine. Just have to be sure it releases the previous bitmap so it can get garbage collected. Keep me posted on what you find.

@FoamyGuy
Copy link
Contributor

I got dug into it some and I see it's going to be a bit more difficult that I was thinking to make them mutable at least for text.

I think we will need to recreate the tilegrid too possibly, and do some calculations to figure out the correct size again. Which will mean that we need a few of the parameters to be stored as class variables so that they can be accessed outside of __init__()

I think it's a good goal to make them mutable at some point if we can, but I'm not sure it's worth holding up this PR for it.

For now I do think it's okay to add the pylint ignores since those methods currently are only provided for API matching and to throw the error letting the user know that they aren't mutable. They don't need to use self for anything in order to do that.

@kmatch98
Copy link
Contributor Author

Ok, I just pushed with the pylint: disable=no-self-use to clear the pylint error.
I think I covered all your previous corrections, please let me know what else you find.

Future: Maybe this should be an issue

I agree that making it mutable will take some time. I've never done anything with actively freeing up instances, so that will be new ground. But I'll spend my time working on getting the bitmap.blit function cleaned up before returning to this.

@kmatch98
Copy link
Contributor Author

I merged your last changes and all passed the checks. I like the quote on the comparison example.

I think I resolved all of your Review changes that you requested, but double check. Otherwise, I think this is good to go.

@kmatch98
Copy link
Contributor Author

I hunted for the Strange glyph loading memory usage and remembered why.

For determining the ascender/descender height, It checks the max dimensions of several glyphs: M, j, space, and ‘ apostrophe. So it’s best to load these four glyphs are loaded in the load_glyphs statement. I will update the examples tomorrow to add the apostrophe.

@FoamyGuy
Copy link
Contributor

I looked over the current version tonight and it looks good to me. I'll mark it approve after you get that last change in and we can see if we get any other feedback prior to merging

@kmatch98
Copy link
Contributor Author

Ok, I just updated the examples and passed the checks. Let me know what else you find that needs updates.

Future enhancements:

  • Make bitmap_label type more mutable
  • If bitmap.blit function is merged in the CircuitPython core, then update bitmap_label.py to utilize this faster copy function

Copy link
Contributor

@FoamyGuy FoamyGuy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me at this point. Thanks for working on this Kmatch98!

@kmatch98
Copy link
Contributor Author

Awesome. Thanks for your help working on this!

@kattni kattni merged commit a99517f into adafruit:master Aug 17, 2020
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Aug 19, 2020
Updating https://github.com/adafruit/Adafruit_CircuitPython_EPD to 2.7.0 from 2.6.2:
  > Merge pull request adafruit/Adafruit_CircuitPython_EPD#41 from makermelissa/master
  > Merge pull request adafruit/Adafruit_CircuitPython_EPD#40 from makermelissa/master

Updating https://github.com/adafruit/Adafruit_CircuitPython_Display_Text to 2.8.1 from 2.8.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_Display_Text#83 from FoamyGuy/fix_anchored_position_rounding
  > Merge pull request adafruit/Adafruit_CircuitPython_Display_Text#85 from kmatch98/glyph_speedup
  > Merge pull request adafruit/Adafruit_CircuitPython_Display_Text#79 from kmatch98/bitmap_label
  > Merge pull request adafruit/Adafruit_CircuitPython_Display_Text#81 from kmatch98/anchor_fix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants