Skip to content

adding skip_self_index argument to bitmap.blit() #8136

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 5 commits into from
Jul 18, 2023

Conversation

FoamyGuy
Copy link
Collaborator

@FoamyGuy FoamyGuy commented Jul 1, 2023

This change adds skip_self_index argument to bitmap.blit() this argument functions similarly to the existing skip_index argument, but instead of checking in the source bitmap it checked inside the self (destination) bitmap and if the index at the current location is this value then it will get skipped instead of overwritten from the source.

This allows bitmap.blit() to be used to provide a pretty significant speed up in this example script which is using a process called "morphological dilation" (if I understand it correctly) to add an outlined around some text. Although the underlying code could operate on any shape inside a bitmap, it doesn't have to be text, but text was my primary intended use.

import time
import board
import terminalio
from adafruit_display_text import bitmap_label
import displayio
from adafruit_bitmap_font import bitmap_font

main_group = displayio.Group()
scaled_group = displayio.Group()

background = displayio.OnDiskBitmap("PCB_Background.bmp")
bg_tg = displayio.TileGrid(bitmap=background, pixel_shader=background.pixel_shader)
main_group.append(bg_tg)

display = board.DISPLAY
font_file = "fonts/Arial-Bold-24.bdf"
font = bitmap_font.load_font(font_file)

text = "Outlined Text!"
text_area = bitmap_label.Label(font, text=text, color=0xffffff,
                               padding_left=10, padding_top=10, padding_bottom=10, padding_right=10,
                               scale=1)
text_area.anchor_point = (0, 0)
text_area.anchored_position = (10, 50)
main_group.append(text_area)

source_bmp = text_area.bitmap
other_bmp = displayio.Bitmap(source_bmp.width, source_bmp.height, 5)

other_palette = displayio.Palette(5)
other_palette[0] = text_area._palette[0]
other_palette[1] = text_area._palette[1]
other_palette[2] = 0x000000

other_palette.make_transparent(0)
other_bmp.blit(0, 0, source_bmp)

other_tg = displayio.TileGrid(bitmap=other_bmp, pixel_shader=other_palette)
scaled_group.append(other_tg)

main_group.append(scaled_group)
def add_outline(bitmap, target_color_index, outline_color_index, size=1):
    before = time.monotonic()
    stamp_source = displayio.Bitmap((size*2)+1, (size*2)+1, outline_color_index+1)
    stamp_source.fill(outline_color_index)
    for y in range(bitmap.height):
        for x in range(bitmap.width):
            if bitmap[x,y] == target_color_index:
                # use blit() to go faster
                bitmap.blit(x-size,y-size, stamp_source, skip_self_index=target_color_index)
                # OR 'manually' search and replace pixels:
                # for y_loc in range(-size, size+1):
                #     for x_loc in range(-size, size+1):
                #         if bitmap[x+x_loc, y+y_loc] != target_color_index:
                #             bitmap[x + x_loc, y + y_loc] = outline_color_index

    after = time.monotonic()
    print(f"took: {after - before}")

add_outline(other_bmp, 1, 2, 2)
display.show(main_group)
while True:
    pass

The above script was tested on a PyPortal and produces this output on the display:
image

@FoamyGuy
Copy link
Collaborator Author

FoamyGuy commented Jul 5, 2023

The more recent commits in this branch have refactored bitmap.blit() function out of the Bitmap class in displayio and over into the bitmaptools module. It now accepts two Bitmap arguments instead of using self as destination bitmap.

I made that move because adding this new functionality inside of the Bitmap class in displayio caused a few of the SAMD21 builds in some languages to go over the size limit.

The devices that had problems already seem to have bitmaptools disabled, so adding code into there has no effect on their size, which allows the actions checks to pass.

I'm not 100% certain if that is the best resolution to the issue though. I am open to other ideas / options here are a few that I thought of but didn't elect to do initially:

  • disable displayio entirely on the devices that went over the limit. The new functionality could go back to existing in bitmap.blit() inside of displayio. These devices would lose access to a capability they have today, but some similar SAM21 devices do already have displayio disabled. Many of these devices have limited RAM as well so using displayio can quickly lead to out of memory issues even when it is enabled.
  • disable something else on the devices that went over the limit. The code addition for the new functionality isn't very large, so we could presuamebly get back under the limit by removing something else from the build. I wasn't sure what would be the best candidate to disable, but perhaps there is one that would be okay to cut in order to keep displayio, and have the new functionality remain in displayio.Bitmap.blit() instead of bitmaptools

Also note, as it is now, this is a breaking change, anything using displayio.Bitmap.blit() would need to be changed to bitmaptools.blit() if this were adobted as-is. A quick scan of the bundle and learn guide code reveals these usages that would need updated:

  • examples/ov5640_stopmotion_kaluga1_3.py
  • adafruit_display_text/bitmap_label.py (but it has fallback code)
  • adafruit_displayio_layout/widgets/flip_input.py
  • CircuitPython_ESP32_Camera/esp32-kaluga-onionskin-gif/code.py

@tannewt tannewt added this to the 9.0.0 milestone Jul 5, 2023
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

Moving it makes total sense to me. Good to do the API breakage in 9.0. I think this is ok to do without a backwards compatibility because it is an advanced API.

One suggestion for the new API.

@FoamyGuy
Copy link
Collaborator Author

FoamyGuy commented Jul 5, 2023

Thank you. The latest commit renames skip_index to skip_source_index I re-tested the latest version with a new build on the PyPortal, it works as expected after changing the arg name in the sample code.

I also have an updated version of bitmap_label that checks for and uses this newer API, I'll submit the PR with that over in it's repo.

Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

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

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants