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
124 lines
3.9 KiB
Python
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)
|