Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"last_updated": "2026-09-02",
"verified": true,
"screenshot": "",
"latest_version": "1.0.4"
"latest_version": "1.0.5"
},
{
"id": "baseball-scoreboard",
Expand Down
31 changes: 19 additions & 12 deletions plugins/7-segment-clock/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,26 @@ def _colorize(
black pixel is unlit. Unlit pixels come back fully transparent so the
caller can paste the result straight over whatever is already on the
panel.

Brightness decides which pixels are lit, and alpha is deliberately
ignored. Every asset on disk is mode "1" carrying a tRNS chunk, and both
loaders open them with .convert("RGBA"), which turns the WHITE (lit)
pixels into alpha 0 and the black ones into alpha 255 -- exactly
inverted. The previous rule, `a > 0 and (r or g or b)`, was therefore
false for every pixel: lit ones failed the alpha half, unlit ones failed
the colour half. The result was a fully transparent tile, so the clock
drew nothing at all.

Do not "restore" an alpha check here. By the time this runs the alpha is
already inverted, so honouring it blanks the display again.
"""
rgba = base_image.convert("RGBA")
colored_image = Image.new("RGBA", rgba.size, (0, 0, 0, 0))
lit = (*color, 255)
clear = (0, 0, 0, 0)

source = rgba.load()
target = colored_image.load()
for x in range(rgba.width):
for y in range(rgba.height):
r, g, b, a = source[x, y]
# Visible and not black => a lit segment.
target[x, y] = lit if (a > 0 and (r or g or b)) else clear
# Lit == bright. `point` flattens to a hard 0/255 mask; the sources are
# 1-bit, so there are no midtones to lose.
mask = base_image.convert("L").point(lambda v: 255 if v > 0 else 0)

colored_image = Image.new("RGBA", base_image.size, (0, 0, 0, 0))
colored_image.paste(Image.new("RGBA", base_image.size, (*color, 255)),
(0, 0), mask)

if scale != 1.0:
new_width = max(1, int(colored_image.width * scale))
Expand Down
8 changes: 7 additions & 1 deletion plugins/7-segment-clock/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "7-segment-clock",
"name": "7-Segment Clock",
"version": "1.0.4",
"version": "1.0.5",
"description": "Display a retro-style 7-segment clock with customizable colors",
"author": "LEDMatrix",
"entry_point": "manager.py",
Expand All @@ -24,6 +24,12 @@
"default_duration": 15,
"config_schema": "config_schema.json",
"versions": [
{
"released": "2026-09-02",
"version": "1.0.5",
"notes": "Fix the clock rendering nothing at all. The digit and separator assets are 1-bit bitmaps with a transparency chunk, so loading them with .convert(\"RGBA\") hands back alpha 0 for the lit (white) pixels and alpha 255 for the unlit black ones -- exactly inverted. The recolour step asked for \"visible AND not black\", which was false for every pixel: lit ones failed the visibility half and unlit ones failed the colour half, so each frame came out fully transparent and the panel stayed dark. Lit-ness is now read from brightness, which the inverted alpha cannot affect. All eight safety-harness renders match the goldens captured before the regression, so the display returns to exactly what it drew before rather than merely drawing something.",
"ledmatrix_min_version": "2.0.0"
},
{
"released": "2026-09-02",
"version": "1.0.4",
Expand Down
Loading