prole/docs/IMAGE-RESOURCES.md

262 lines
6.6 KiB
Markdown

# Image Resource Management
## Overview
The Knoe Database Installer includes multiple image assets that need to work both when running from source and when packaged as a standalone binary. This document explains how image resources are managed.
## Image Assets
The following images are included in the installer:
### Primary Assets
- **knoeIcon.png** (1,494,782 bytes) - Application icon
- **knoeLogo.png** (2,190,703 bytes) - Main Knoe logo
- **knoeLogoSepia.png** (2,657,956 bytes) - Sepia-toned background logo
- **knoeLogoBlueprint.png** (3,107,147 bytes) - Blueprint style logo
- **knoeIconblueprint.png** (1,570,109 bytes) - Blueprint style icon
### Supplementary Assets
- **knoe-type.gif** (599 bytes) - Small typing animation
## Resource Path Resolution
### The Challenge
When packaging Python applications with PyInstaller, resource files need to be found in two different scenarios:
1. **Development**: Running from source, files are in `PROJECT_ROOT/img/`
2. **Production**: Running from bundle, files are in PyInstaller's temporary extraction directory
### The Solution
The `get_resource_path()` function automatically resolves paths correctly in both scenarios:
```python
def get_resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller."""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = Path(sys._MEIPASS)
except AttributeError:
# Running from source
base_path = PROJECT_ROOT
return base_path / relative_path
```
### Usage
All image loading code uses this function:
```python
# Instead of:
bg_path = Path('img/knoeLogoSepia.png')
# Use:
bg_path = get_resource_path('img/knoeLogoSepia.png')
```
## Implementation Details
### Modified Code Locations
The following locations in `install.py` were updated to use `get_resource_path()`:
1. **Background logo loading** (line ~191)
```python
bg_path = get_resource_path('img/knoeLogoSepia.png')
```
2. **Application icon candidates** (line ~406)
```python
img_candidates = [
get_resource_path('img/knoeIcon.png'),
get_resource_path('img/knoe-type.png'),
get_resource_path('img/knoe-type.gif'),
get_resource_path('img/Knoe.png'),
get_resource_path('img/knoeLogoSepia.png'),
]
```
3. **DMG background image** (line ~3820)
```python
bg_img = get_resource_path('img/knoeLogoSepia.png')
```
### PyInstaller Configuration
The `installer.spec` file includes the entire `img/` directory in the bundle:
```python
datas = [
('installer', 'installer'),
('conf', 'conf'),
('etc', 'etc'),
('img', 'img'), # All images included
('docs', 'docs'),
]
```
This ensures all images are:
1. Copied into the PyInstaller bundle
2. Extracted to the temporary directory at runtime
3. Accessible via `sys._MEIPASS / 'img' / filename`
## Testing
### Verify Resource Paths
Run the test script:
```bash
python3 test_resource_paths.py
```
This tests:
- Path resolution from source
- Path resolution simulating PyInstaller environment
- Existence of all image files
Expected output:
```
✓ App icon
✓ Background logo (sepia)
✓ Main logo
✓ Blueprint logo
✓ Blueprint icon
✓ All tests passed!
Images will be correctly included in PyInstaller build.
```
### Manual Testing
From source:
```bash
python3 install.py --gui
# Check that logo appears in background
```
From built binary:
```bash
make package
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer --gui
# Check that logo appears in background
```
## Icon Formats
### PNG (Source)
- Format: PNG with transparency
- Resolution: 1024x1024 recommended
- Location: `img/knoeIcon.png`
### ICNS (macOS Bundle)
- Generated by build system from PNG
- Contains multiple resolutions (16x16 through 1024x1024)
- Location: `build/knoe.icns` (intermediate), embedded in `.app` bundle
- Generated by: `make build/knoe.icns`
The build system automatically converts PNG to ICNS using macOS tools:
```bash
sips -z 512 512 img/knoeIcon.png --out build/icon.iconset/icon_512x512.png
iconutil -c icns build/icon.iconset -o build/knoe.icns
```
## Background Image Usage
The GUI installer uses `knoeLogoSepia.png` as a subtle background:
1. **Loading**: Image loaded via PIL (Pillow)
2. **Processing**:
- Converted to RGBA
- Blended with white background at 15% opacity
- Creates subtle watermark effect
3. **Rendering**:
- Scaled to fill canvas
- Maintains aspect ratio
- Centered on canvas
Code snippet:
```python
bg_path = get_resource_path('img/knoeLogoSepia.png')
if bg_path.exists():
original = Image.open(str(bg_path)).convert('RGBA')
white_bg = Image.new('RGBA', original.size, (255, 255, 255, 255))
self._bg_pil = Image.blend(white_bg, original, 0.15)
```
## Adding New Images
To add new image resources:
1. **Add image file** to `img/` directory
2. **Update code** to use `get_resource_path()`:
```python
new_img = get_resource_path('img/new_image.png')
```
3. **No spec changes needed** - entire `img/` directory is already included
4. **Test** with `python3 test_resource_paths.py`
## Troubleshooting
### Image Not Found in Built Binary
**Symptom:** Image loads from source but not from built `.app`
**Cause:** Path hardcoded instead of using `get_resource_path()`
**Solution:**
```python
# Wrong:
img_path = PROJECT_ROOT / 'img' / 'logo.png'
# Correct:
img_path = get_resource_path('img/logo.png')
```
### Image Not Showing in GUI
**Cause:** File doesn't exist or wrong path
**Solution:** Check with test script:
```bash
python3 test_resource_paths.py
```
### Build Size Too Large
**Cause:** Large images included in bundle
**Solution:**
- Optimize PNG files: `pngcrush`, `optipng`, etc.
- Consider JPEG for photos (PNG for logos/icons)
- Remove unused images from `img/` directory
## Performance Considerations
### Startup Time
PyInstaller extracts bundled resources to a temporary directory on each launch:
- **Small images** (< 100 KB): Negligible impact
- **Large images** (> 1 MB): 100-200ms extraction time
- **Total time**: ~1-2 seconds for all resources
### Memory Usage
Images are loaded into memory when displayed:
- **knoeLogoSepia.png**: ~2.6 MB on disk, ~10 MB in memory (RGBA)
- **Scaled versions**: Additional memory for display size
### Optimization
For production, consider:
1. Compress images before including
2. Use lazy loading (load only when needed)
3. Cache scaled versions instead of re-scaling
## References
- [PyInstaller Data Files](https://pyinstaller.org/en/stable/spec-files.html#adding-data-files)
- [Pillow (PIL) Documentation](https://pillow.readthedocs.io/)
- [macOS Icon Creation](https://developer.apple.com/design/human-interface-guidelines/app-icons)