Skip to content

Commit e28d23e

Browse files
committed
docs: add template creation guide for Paint.NET users
1 parent 9f7747f commit e28d23e

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

docs/creating-templates.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Creating Templates for Maple (Paint.NET Guide)
2+
3+
This guide explains how to build your own maple templates using [Paint.NET](https://www.getpaint.net/). A template is a `.zip` file containing a set of specially crafted PNG images and a JSON manifest that tells maple how to composite your input images into an animation.
4+
5+
## How Maple Rendering Works
6+
7+
For each frame of an animation, maple uses **five images** to determine how to place your input image onto a scene:
8+
9+
| Image | Purpose |
10+
|---|---|
11+
| **light** | The scene lit with a white surface where the input goes |
12+
| **dark** | The scene lit with a black surface where the input goes |
13+
| **map** | UV coordinate map — tells maple *where* each pixel samples from the input |
14+
| **sel** | Layer selection — tells maple *which* input layer a pixel belongs to |
15+
| **transparent** | The neutral/background frame shown where no input is mapped |
16+
17+
The rendering formula for each pixel is:
18+
19+
```
20+
output = dark + (light - dark) × input_sample
21+
```
22+
23+
This means:
24+
- Where your input image is **white (255)**, the output matches the `light` image.
25+
- Where your input image is **black (0)**, the output matches the `dark` image.
26+
- In-between values are interpolated, producing realistic lighting and shading on the composited image.
27+
28+
## Template Zip Structure
29+
30+
A template zip file contains:
31+
32+
```
33+
template.json
34+
frame0_light.png
35+
frame0_dark.png
36+
frame0_map.png
37+
frame0_sel.png
38+
frame0_transparent.png
39+
frame1_light.png
40+
frame1_dark.png
41+
...
42+
```
43+
44+
For a **static (single-frame) template**, you only need `frame0_*` files.
45+
46+
For an **animated template**, provide `frame0_*` through `frameN_*` (0-indexed), where N = `frames - 1` in your JSON config.
47+
48+
### template.json
49+
50+
```json
51+
{
52+
"width": 400,
53+
"height": 300,
54+
"frames": 1,
55+
"delay": 0.1,
56+
"hold": 1.0,
57+
"palette": [0]
58+
}
59+
```
60+
61+
| Field | Description |
62+
|---|---|
63+
| `width` | Output width in pixels |
64+
| `height` | Output height in pixels |
65+
| `frames` | Total number of frames (1 for static templates) |
66+
| `delay` | Delay between frames in seconds (e.g., `0.05` = 20 FPS, `0.1` = 10 FPS) |
67+
| `hold` | Extra hold time on the last frame in seconds (0 for seamless loops) |
68+
| `palette` | Frame indices used for GIF color palette generation (usually `[0]`) |
69+
70+
## Creating Each Image Layer
71+
72+
All frame images must be the **same dimensions** (`width` × `height` from your JSON).
73+
74+
### Step 1: Create the Scene — `light` and `dark`
75+
76+
These two images define how your scene looks when the input surface is fully white vs fully black. They control the lighting and shading effect.
77+
78+
**In Paint.NET:**
79+
80+
1. Create your scene (e.g., a picture frame, a billboard, a TV screen, a book cover).
81+
2. For the `light` image: render/paint the scene with the target surface as **pure white** (`#FFFFFF`).
82+
3. For the `dark` image: render/paint the scene with the target surface as **pure black** (`#000000`).
83+
4. Everything else in the scene (the frame, the wall, shadows, etc.) should look the same in both images.
84+
85+
The difference between light and dark is what creates the realistic blending effect. Shadows should darken both images equally. Specular highlights should appear in the light image.
86+
87+
> **Tip:** If you're working with a 3D render, render it twice with different surface colors. For hand-drawn scenes, duplicate your artwork and fill the target area with white in one copy and black in the other.
88+
89+
### Step 2: Create the Background — `transparent`
90+
91+
This is what viewers see where no input image is mapped. Typically, this is a full render of your scene without any input content — showing just the background environment.
92+
93+
If your scene has no transparent areas, you can make this identical to the `light` image (maple falls back to `light` if `transparent` is missing).
94+
95+
### Step 3: Create the UV Map — `map`
96+
97+
This is the most technical image. Each pixel's RGBA channels encode where to sample from the input image using a **4096×4096 coordinate system** centered at (2048, 2048).
98+
99+
**Channel encoding:**
100+
101+
| Channel | Encodes |
102+
|---|---|
103+
| **R (Red)** | Low byte of X coordinate (0–255) |
104+
| **G (Green)** | Low byte of Y coordinate (0–255) |
105+
| **B (Blue)** | High bits, packed: `floor(B / 16)` = Y high nibble, `B % 16` = X high nibble |
106+
| **A (Alpha)** | Activation mask — values > 25 mean "active" (map this pixel), ≤ 25 means "skip" |
107+
108+
The full coordinate is computed as:
109+
```
110+
X = R + 256 × (B % 16) - 2048
111+
Y = G + 256 × floor(B / 16) - 2048
112+
```
113+
114+
Coordinates near (0, 0) map to the center of the input image. The range −2048 to +2047 covers the full input.
115+
116+
**Practical approach in Paint.NET for a flat surface:**
117+
118+
For a simple, flat rectangular surface (like a screen or a poster on a wall), you need smooth gradients:
119+
120+
1. Create a new image at your template dimensions.
121+
2. Set the alpha to 255 everywhere the input should appear, 0 everywhere else.
122+
3. For the mapped region:
123+
- **Red channel**: Create a horizontal gradient from 0 on the left to 255 on the right. If your surface spans more than 256 pixels of coordinate range, you'll need the Blue channel's low nibble to carry the overflow.
124+
- **Green channel**: Create a vertical gradient from 0 at the top to 255 at the bottom. Similarly, the Blue channel's high nibble carries overflow.
125+
- **Blue channel**: For small surfaces fitting within a 256-pixel coordinate range, set this to `128` (which centers around coordinate 0: `128 / 16 = 8` for Y high, `128 % 16 = 0` for X high, giving offset 2048 which cancels the -2048). For larger or offset surfaces, compute the appropriate packed value.
126+
127+
> **Simplified approach for beginners:** For a flat, axis-aligned rectangular region, paint the region with R going from 0→255 left to right, G going from 0→255 top to bottom, B = `0x88` (136, which gives high nibble 8 for both X and Y, centering the coordinate), and A = 255. Leave everything else at A = 0. This maps a centered square from the input image onto your surface.
128+
129+
**For curved or perspective-distorted surfaces:**
130+
131+
You will need to compute or paint the UV coordinates accounting for the distortion. Each pixel's RGBA tells maple where in the input image to sample from.
132+
133+
If you have a 3D modeling tool, you can render the UV map directly from your 3D scene. Otherwise, you can approximate perspective distortion by skewing your gradients.
134+
135+
### Step 4: Create the Layer Selection — `sel`
136+
137+
This image determines which input layer each pixel belongs to.
138+
139+
**Channel encoding:**
140+
141+
| Channel | Purpose |
142+
|---|---|
143+
| **R (Red)** | Layer index: `1` = first input, `2` = second input, etc. `0` = no layer |
144+
| **G (Green)** | Unused for layer selection |
145+
| **B (Blue)** | Edge smoothing flag: values > 0 mark pixels for anti-aliased edge blending |
146+
147+
**In Paint.NET:**
148+
149+
1. Create a new image at your template dimensions.
150+
2. Fill the entire image with `R=0, G=0, B=0` (no layer mapped anywhere).
151+
3. Paint the area where input layer 1 should appear with `R=1, G=0, B=0`.
152+
4. If you have a second input layer, paint that area with `R=2, G=0, B=0`.
153+
5. Along the edges of your mapped regions, paint a 1-pixel border with `B > 0` (e.g., `R=0, G=0, B=128`) to enable edge smoothing. This makes the boundary between the composited area and the background look smooth.
154+
155+
> **Tip:** Use the pencil tool (not the brush) to paint exact pixel values. Open the color picker, set the hex values manually, and draw your regions. You can zoom in to verify pixel-level accuracy.
156+
157+
## Single-Layer vs Multi-Layer Templates
158+
159+
**Single-layer** (e.g., flag, toaster): Only uses layer 1. The sel image has `R=1` in the mapped region and `R=0` elsewhere.
160+
161+
**Multi-layer** (e.g., book with front + back cover): Uses layers 1 and 2. Different regions in the sel image have `R=1` or `R=2`. Each layer maps independently via the same UV map — the sel image controls which input goes where.
162+
163+
When a user runs a multi-layer template:
164+
165+
```bash
166+
maple --zip mytemplate.zip --in front.png back.png --gif out.gif
167+
```
168+
169+
`front.png` is layer 1, `back.png` is layer 2.
170+
171+
## Animated Templates
172+
173+
For animations, create the full set of 5 images for every frame. Each frame can have different:
174+
- Camera angles (light/dark/transparent change)
175+
- UV mappings (map changes for moving/rotating surfaces)
176+
- Layer visibility (sel can show/hide layers per frame)
177+
178+
Number your frames starting from 0: `frame0_*.png`, `frame1_*.png`, ..., `frame22_*.png` for a 23-frame animation.
179+
180+
Set `delay` in `template.json` to control playback speed and `hold` for how long the last frame lingers.
181+
182+
## Packaging the Template
183+
184+
1. Select all your frame images and `template.json`.
185+
2. Zip them into a single `.zip` file (files at the root of the archive, no subdirectory).
186+
3. Test it:
187+
188+
```bash
189+
maple --zip mytemplate.zip --in examples/monkey.jpg --gif test.gif
190+
```
191+
192+
Use `--files` to verify maple reads your template correctly:
193+
194+
```bash
195+
maple --zip mytemplate.zip --in examples/monkey.jpg --files
196+
```
197+
198+
## Quick-Start Checklist
199+
200+
- [ ] Decide on output dimensions (`width` × `height`)
201+
- [ ] Decide frame count (1 for static)
202+
- [ ] For each frame, create all 5 PNGs at the same dimensions
203+
- [ ] Write `template.json` with correct `frames` count
204+
- [ ] Ensure the `map` image alpha is 255 in mapped regions, 0 elsewhere
205+
- [ ] Ensure the `sel` image R channel matches your layer count (1 for single-layer)
206+
- [ ] Zip everything (flat, no subdirectories) and test
207+
208+
## Paint.NET-Specific Tips
209+
210+
- **Viewing individual channels:** Use the Channels window (Window → Colors → click channel buttons) to inspect R, G, B, A individually.
211+
- **Setting exact pixel colors:** Use the Color Picker dialog (F8), switch to hex mode, and type exact values like `01000000` (R=1, G=0, B=0, A=0) for sel maps.
212+
- **Gradients:** The built-in gradient tool works for simple flat UV maps. Draw a linear gradient in the R channel for horizontal mapping and G channel for vertical mapping.
213+
- **Working with alpha:** Make sure "Overwrite" blending mode is selected when painting alpha values, so you get exact values rather than blended results.
214+
- **Saving PNGs:** Always save as 32-bit PNG (RGBA) to preserve all four channels. Paint.NET does this by default with the "Flatten" option unchecked.
215+
216+
## Troubleshooting
217+
218+
| Problem | Likely Cause |
219+
|---|---|
220+
| Input image doesn't appear | `sel` R channel isn't set to 1 (or the correct layer number) |
221+
| Black rectangle instead of input | `map` alpha is 0 in the target area — set it to 255 |
222+
| Distorted or garbled output | UV map coordinates are wrong — check R, G, B encoding |
223+
| Seams/harsh edges at boundaries | Missing edge smoothing — paint `B > 0` in the `sel` image along edges |
224+
| "MissingFile" error | Frame file naming is wrong — must be `frame0_light.png` (0-indexed) |
225+
| Wrong number of frames | `frames` in `template.json` doesn't match the number of frame sets in the zip |

0 commit comments

Comments
 (0)