Files
crosspoint-reader-mod/scripts/generate_book_icon.py
cottongin dfbc931c14 mod: Phase 1 - bring forward mod-exclusive files with ActivityManager migration
Brings ~55 mod-exclusive files to the upstream-based mod/master-resync branch:

Activities (migrated to new ActivityManager pattern):
- Clock/Time: SetTimeActivity, SetTimezoneOffsetActivity, NtpSyncActivity
- Dictionary: DictionaryDefinitionActivity, DictionarySuggestionsActivity,
  DictionaryWordSelectActivity, LookedUpWordsActivity
- Bookmark: EpubReaderBookmarkSelectionActivity
- Book management: BookManageMenuActivity, EndOfBookMenuActivity
- OPDS: OpdsServerListActivity, OpdsSettingsActivity
- Utility: DirectoryPickerActivity, NumericStepperActivity

Utilities (unchanged):
- BookManager, BookSettings, BookmarkStore, BootNtpSync
- Dictionary, LookupHistory, TimeSync, OpdsServerStore

Libraries: PlaceholderCover, TableData, ChapterXPathIndexer
Scripts: inject_mod_version, generate_book_icon, preview_placeholder_cover
Docs: KOReader sync XPath mapping

Migration changes:
- ActivityWithSubactivity -> Activity base class
- Callback constructors -> finish()/setResult() pattern
- enterNewActivity() -> startActivityForResult()
- Activity::RenderLock&& -> RenderLock&&

These files won't compile yet - they reference mod settings and I18n
strings that will be added in subsequent phases.

Made-with: Cursor
2026-03-07 15:10:00 -05:00

124 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""Generate a 1-bit book icon bitmap as a C header for PlaceholderCoverGenerator.
The icon is a simplified closed book with a spine on the left and 3 text lines.
Output format matches Logo120.h: MSB-first packed 1-bit, 0=black, 1=white.
"""
from PIL import Image, ImageDraw
import sys
def generate_book_icon(size=48):
"""Create a book icon at the given size."""
img = Image.new("1", (size, size), 1) # White background
draw = ImageDraw.Draw(img)
# Scale helper
s = size / 48.0
# Book body (main rectangle, leaving room for spine and pages)
body_left = int(6 * s)
body_top = int(2 * s)
body_right = int(42 * s)
body_bottom = int(40 * s)
# Draw book body outline (2px thick)
for i in range(int(2 * s)):
draw.rectangle(
[body_left + i, body_top + i, body_right - i, body_bottom - i], outline=0
)
# Spine (thicker left edge)
spine_width = int(4 * s)
draw.rectangle([body_left, body_top, body_left + spine_width, body_bottom], fill=0)
# Pages at the bottom (slight offset from body)
pages_top = body_bottom
pages_bottom = int(44 * s)
draw.rectangle(
[body_left + int(2 * s), pages_top, body_right - int(1 * s), pages_bottom],
outline=0,
)
# Page edges (a few lines)
for i in range(3):
y = pages_top + int((i + 1) * 1 * s)
if y < pages_bottom:
draw.line(
[body_left + int(3 * s), y, body_right - int(2 * s), y], fill=0
)
# Text lines on the book cover
text_left = body_left + spine_width + int(4 * s)
text_right = body_right - int(4 * s)
line_thickness = max(1, int(1.5 * s))
text_lines_y = [int(12 * s), int(18 * s), int(24 * s)]
text_widths = [1.0, 0.7, 0.85] # Relative widths for visual interest
for y, w_ratio in zip(text_lines_y, text_widths):
line_right = text_left + int((text_right - text_left) * w_ratio)
for t in range(line_thickness):
draw.line([text_left, y + t, line_right, y + t], fill=0)
return img
def image_to_c_array(img, name="BookIcon"):
"""Convert a 1-bit PIL image to a C header array."""
width, height = img.size
pixels = img.load()
bytes_per_row = width // 8
data = []
for y in range(height):
for bx in range(bytes_per_row):
byte = 0
for bit in range(8):
x = bx * 8 + bit
if x < width:
# 1 = white, 0 = black (matching Logo120.h convention)
if pixels[x, y]:
byte |= 1 << (7 - bit)
data.append(byte)
# Format as C header
lines = []
lines.append("#pragma once")
lines.append("#include <cstdint>")
lines.append("")
lines.append(f"// Book icon: {width}x{height}, 1-bit packed (MSB first)")
lines.append(f"// 0 = black, 1 = white (same format as Logo120.h)")
lines.append(f"static constexpr int BOOK_ICON_WIDTH = {width};")
lines.append(f"static constexpr int BOOK_ICON_HEIGHT = {height};")
lines.append(f"static const uint8_t {name}[] = {{")
# Format data in rows of 16 bytes
for i in range(0, len(data), 16):
chunk = data[i : i + 16]
hex_str = ", ".join(f"0x{b:02x}" for b in chunk)
lines.append(f" {hex_str},")
lines.append("};")
lines.append("")
return "\n".join(lines)
if __name__ == "__main__":
size = int(sys.argv[1]) if len(sys.argv) > 1 else 48
img = generate_book_icon(size)
# Save preview PNG
preview_path = f"mod/book_icon_{size}x{size}.png"
img.resize((size * 4, size * 4), Image.NEAREST).save(preview_path)
print(f"Preview saved to {preview_path}", file=sys.stderr)
# Generate C header
header = image_to_c_array(img, "BookIcon")
output_path = "lib/PlaceholderCover/BookIcon.h"
with open(output_path, "w") as f:
f.write(header)
print(f"C header saved to {output_path}", file=sys.stderr)