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
40 changes: 37 additions & 3 deletions cdisplayagain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1302,14 +1302,48 @@ def _page_counter_text(self) -> str | None:
return f"{self._current_index + 1}/{total}"

def _page_counter_color(self) -> str:
"""Pick black or white text based on the mean luminance of the current page."""
"""Pick black or white text based on the luminance of the page area where the counter sits.

The counter is drawn at the bottom-right of the canvas. Sample that region
(after accounting for image scaling/positioning) so the text contrasts with
whatever is actually behind it.
"""
if self._current_pil is None:
return "#ffffff"
try:
from PIL import ImageStat

thumb = self._current_pil.convert("L").resize((32, 32), Image.Resampling.BILINEAR)
mean = ImageStat.Stat(thumb).mean[0]
cw = max(1, self.canvas.winfo_width())
ch = max(1, self.canvas.winfo_height())
if self._scaled_size is None:
thumb = self._current_pil.convert("L").resize((32, 32), Image.Resampling.BILINEAR)
mean = ImageStat.Stat(thumb).mean[0]
return "#000000" if mean >= 128 else "#ffffff"

iw, ih = self._scaled_size
margin = 12
cx = cw - margin
cy = ch - margin

image_left = (cw - iw) // 2
image_top = (ch - ih) // 2 if ih <= ch else -self._scroll_offset
img_x = max(0, min(iw - 1, cx - image_left))
img_y = max(0, min(ih - 1, cy - image_top))

sample_size = min(32, iw // 4, ih // 4)
if sample_size < 4:
sample_size = min(4, iw, ih)
left = max(0, img_x - sample_size)
top = max(0, img_y - sample_size)
right = min(iw, img_x + sample_size)
bottom = min(ih, img_y + sample_size)

if left >= right or top >= bottom:
thumb = self._current_pil.convert("L").resize((32, 32), Image.Resampling.BILINEAR)
mean = ImageStat.Stat(thumb).mean[0]
else:
region = self._current_pil.crop((left, top, right, bottom)).convert("L")
mean = ImageStat.Stat(region).mean[0]
except Exception:
return "#ffffff"
return "#000000" if mean >= 128 else "#ffffff"
Expand Down
74 changes: 74 additions & 0 deletions tests/test_page_counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,77 @@ def test_render_current_sync_no_source_clears_counter(tk_root, tmp_path):

viewer._render_current_sync()
assert viewer._page_counter_id is None


def test_counter_color_sampling_logic():
"""Test the coordinate sampling logic directly."""
from PIL import Image, ImageStat

def test_sampling(img, cw, ch, iw, ih, scroll_offset=0):
"""Test sampling logic for given image and dimensions."""
margin = 12
cx = cw - margin
cy = ch - margin

image_left = (cw - iw) // 2
image_top = (ch - ih) // 2 if ih <= ch else -scroll_offset
img_x = max(0, min(iw - 1, cx - image_left))
img_y = max(0, min(ih - 1, cy - image_top))

sample_size = min(32, iw // 4, ih // 4)
if sample_size < 4:
sample_size = min(4, iw, ih)
left = max(0, img_x - sample_size)
top = max(0, img_y - sample_size)
right = min(iw, img_x + sample_size)
bottom = min(ih, img_y + sample_size)

region = img.crop((left, top, right, bottom)).convert("L")
return ImageStat.Stat(region).mean[0]

# Test 1: Normal size image (fits in canvas)
# Create a dark page (100x100) with a white border in the bottom-right
img1 = Image.new("RGB", (100, 100), color=(0, 0, 0))
# Add white border in bottom-right 30x30 area
for x in range(70, 100):
for y in range(70, 100):
img1.putpixel((x, y), (255, 255, 255))

# Test with realistic canvas size
cw, ch = 800, 600
iw, ih = 100, 100
mean1 = test_sampling(img1, cw, ch, iw, ih)

# The counter area should be white, so text should be black
assert mean1 >= 128 # white area

# Test 2: Light page with dark border
img2 = Image.new("RGB", (100, 100), color=(255, 255, 255))
# Add dark border in bottom-right 30x30 area
for x in range(70, 100):
for y in range(70, 100):
img2.putpixel((x, y), (0, 0, 0))

mean2 = test_sampling(img2, cw, ch, iw, ih)
# The counter area should be dark, so text should be white
assert mean2 < 128 # dark area

# Test 3: Tall image (doesn't fit in canvas) - regression test for issue #58
# Create a tall dark page (100x1200) with white border at the bottom
img3 = Image.new("RGB", (100, 1200), color=(0, 0, 0))
# Add white border in bottom 30 rows
for x in range(0, 100):
for y in range(1170, 1200):
img3.putpixel((x, y), (255, 255, 255))

# Test with scroll offset = 0 (showing top of image)
cw, ch = 800, 600
iw, ih = 100, 1200
mean3 = test_sampling(img3, cw, ch, iw, ih, scroll_offset=0)
# Counter is at bottom of canvas, but we're showing top of image, so it should be dark
assert mean3 < 128 # dark area

# Test with scroll offset = 600 (showing bottom of image)
mean4 = test_sampling(img3, cw, ch, iw, ih, scroll_offset=600)
# Counter is at bottom of canvas, showing bottom of image, so it should be white
assert mean4 >= 128 # white area
Loading