← README · Section 4 1 · 2 · 3 · 4 · 한국어
Rough 1 · rough 2 go through the same steps and differ in the values of the table below and, from 0.57, in rough 2 using step 11 (tracing with a G-pen) instead of steps 4 · 6. They use their own random numbers (mulberry32, seed 20261011 for rough 1 and 20261012 for rough 2), so the same picture always gives the same roughs regardless of the other layers.
Below, $s' = \max(1, s)$ is the size factor for stroke width · offsets, $s_g = \max(0.5, s)$ the size factor for blurs · tolerances · length thresholds (small pictures hold the same shape in fewer pixels), 𝑢 is a new random number in $[0, 1)$ each time it is used, and 𝑣 one in $[-1, 1)$.
Note — Why: A rough should wobble like a human stroke, yet the same picture must always give the same rough so results can be compared and reproduced. Separate size ratios keep line widths and shape thresholds from becoming too thin or too large on small pictures. How it works: mulberry32 is a pseudorandom generator that adds a fixed constant to one 32-bit state and mixes it, so the same seed gives the same sequence. A seed reserved for the rough makes it independent of random use in other layers. $s' = \max(1, s)$ keeps line widths from shrinking below 1× on small pictures, and $s_g = \max(0.5, s)$ shrinks length thresholds at most to half. 𝑢 is uniform on $[0, 1)$ and $v = 2u - 1$ on $[-1, 1)$. References: R48 Ettinger 2017 · a rule set by this program
| Value | Rough 1 (concept) | Rough 2 (refined) | Step |
|---|---|---|---|
Part blur $\sigma_p$ · large-part threshold $a_{min}$ |
$6s_g$ · 0.8% of the character area |
$2.5s_g$ · 0.2% |
1 |
| Inner color boundaries · silhouette where line art exists | drawn if line art lies under them · drawn | not drawn · drawn by the line art | 2 |
| Composition offset | rotation $0.05v$ rad, scale $1 + 0.04v$, warp 3.5% of the character size |
none | 3 |
| Inner lines (line alpha threshold · length · density) | 0.3 · at least $40s_g$ (where sparse on a wider view, $12s_g$) · at most 0.11 |
0.25 · at least $12s_g$ (outside the character, $25s_g$) · at most 0.18 (where sparse on a wider view, even if dense) |
4 |
| Inner lines near mass lines | dropped | drawn | 4 |
| Background composition | not drawn | brightness edges of at least $40s_g$, strength 0.45 |
5 |
| Skin color · guessed face areas | a* at least 4 · one mass in pictures with no face found at all | a* at least 2 and a* + b* at least 6 · every mass without a found face | 6 |
| Stroke fitting | break at bends over 0.8 rad, cubic (tolerance $3s_g$, faces $1.2s_g$) |
break at bends over 0.9 rad, fit cubics (tolerance $3.5s_g$, faces $0.7s_g$) between corners (0.65 rad), then join pieces bending 1.2 rad or less into one stroke |
7 |
| Stroke length · overlap | $(110 + 60u)s_g$ · 18% |
$(90 + 50u)s_g$ · 10% |
7 |
| Mass-line radius · offset (first · later passes) · silhouette / inner strength | $1.2s'$ · $0.8s'$ · $2.2s'$ · 0.9 / 0.7 |
$1.7s'$ · $0.3s'$ · $0.5s'$ · 1 / 0.85 |
8 |
| Inner line radius · passes · strength factor | $1.15s'$ · 2 · 0.8 |
$1.6s'$ · 1 (2 with 50%) · 1 |
8 |
| Face stroke radius · darkness · offset | $1.1s'$ · $0.35 + 0.65\bar\ell$ · $0.35s'$, $0.9s'$ |
$1.8s'$ · $0.8 + 0.2\bar\ell$ · $0.12s'$, $0.35s'$ |
8 |
Pressure floor at stroke ends $p_0$ · construction line darkness |
0.18 · ×1.3 | 0.35 · ×0.5 | 8, 9 |
Note — Why: Rough 1 (concept) blocks in large masses and composition with large wobble, while rough 2 (refined) is a committed line close to the real outline, so the two layers use different values. How it works: All lengths and radii are multiples of the size ratios ($s_g$, $s'$), so the same shapes appear at any picture size. Rough 1 has larger blur, tolerance and offset, longer strokes and more overlap; rough 2 the opposite. References: a rule set by this program
Face area 𝐹: for each naming · folder face (step 9 of face finding in 4.5) with eye center $(c_x, c_y)$, eye distance 𝑑, unit eye direction 𝐞 and the downward direction 𝐟 perpendicular to it, the pixels with $\lvert u\rvert \le 1.1$ and $-0.6 \le v \le 1.6$ where $u = (\mathbf p - \mathbf c)\cdot\mathbf e / d$, $v = (\mathbf p - \mathbf c)\cdot\mathbf f / d$ (from eyebrows to chin).
Note — Why: Faces must be drawn differently from elsewhere (no spur pruning, finer), so the face area is fixed first. How it works: It is the rectangle from brows to chin in face-frame coordinates $(u, v)$ (4.5), obtained by dot products with the eye distance 𝑑 and the orthonormal eye basis $(\mathbf e, \mathbf f)$. The same area is found whatever the face size or tilt. References: R35 Strang 2016
a_{min}$ of the character area $N_c$ and at least $150s_g^2$ pixels are the large parts.
The background · character indicator functions are blurred by $\sigma_p$ and the places where the character side is larger form the inside of the silhouette (the silhouette is set by the whole character only); inside it, each pixel takes whichever of the large parts and “the rest” (small pieces · frills) has the largest blurred indicator, giving the map $L_2$.
Painting with the large parts only would leave places full of small pieces empty like background and break the masses apart.
\sigma_p$ gives the share of that part around each pixel. Painting each pixel with the largest value (argmax) is a majority filter that removes small bumps and smooths boundaries. Counting small pieces together as one “rest” group keeps areas full of small pieces from emptying like background. The large-part threshold $150s_g^2$ is an area, so it scales with the square of the size ratio. References: R14 Gonzalez · Woods 2018 · a rule set by this programL_2$ of a pixel and its right · lower neighbor differ, it is a silhouette if one side is outside the character and an inner boundary if the two large parts’ base colors have $\Delta E \ge 15$ (except where they touch the rest, and inside 𝐹).
Lines are traced as in step 7 (pieces of at least $20s_g$ pixels, spurs trimmed by $5s_g$ steps, loops under $30s_g$ pixels dropped), and an inner boundary line is used only if at least 60% of its points lie within $r_k = \max(2, 2.5s_g)$px of line art (line alpha ≥ 0.25).
Color boundaries without a line, such as the same hair split into light and shadow, are not in an artist’s rough. Rough 2 draws no inner boundaries (in complex pictures they become a cell-like polygon mesh).
Rough 2, as if redrawing the real outline over rough 1’s masses, cuts each silhouette line at points with line art within $r_k$ and keeps as mass lines only the stretches without line art (at least $10s_g$). Outlines where line art exists are drawn by the line-art lines of step 4.
\Delta E \ge 15$ between two parts’ base colors is the threshold for clearly different colors, and inner boundaries are used only if 60% of their points lie within $r_k$ of actual line art, keeping only boundaries that have lines. Rough 2 cuts silhouette runs where line art exists and leaves them to the line-art strokes (4), keeping only runs without line art as mass lines. References: R7 CIE 2004 · a rule set by this program\mathbf p' = \mathbf m + z\,R_\theta(\mathbf p - \mathbf m) + 0.035S\,\big(n_x(\mathbf p),\ n_y(\mathbf p)\big)$ before drawing, where $n_x, n_y$ are value noise (4.2) with cell size $\max(8, 0.45S)$.
\mathbf m + zR_\theta(\mathbf p - \mathbf m)$ is a rotation ($R_\theta$, at most about 2.9°) and scaling (±4%) about the center of mass, and the added $0.035S(n_x, n_y)$ is a smooth displacement field from value noise (4.2), bending each place slightly differently. The cell size is 45% of the character diagonal, so the warp acts on large parts. References: R35 Strang 2016 · R47 Ebert et al. 20034s_g$px of a drawn mass line.
Density: the share ρ of center-line · mass-line pixels in a window of radius $r_d = \max(2, \mathrm{round}(10s_g))$, averaged along the line, is compared as $\rho\,(2r_d + 1)/21$, which in terms of the spacing of parallel lines does not depend on size.
Lines in dense places like patterns · hatching · hair grain are not in a rough and are dropped, but lines whose same value in a 4× wider window ($4r_d$) is at most 0.12 (lines gathered in small clusters that are sparse on a wider view: eyes · mouths · fingers) are kept even if short or dense.
Lines shorter than the outside-the-character threshold (table) are used only if at least half their points lie inside the silhouette (dropping signatures · background lettering).
Pattern loops: for each closed line with a perimeter up to $60s_g$ pixels, its center and mean radius 𝑟 are measured; if at least two other closed lines have a radius ratio within 2× and a center distance of at most $6\max(r_i, r_j)$, it is taken as a repeated pattern (sweater pattern · snowflakes · petals · railing cells · window rows) and not drawn.
1/g$ for parallel 1px lines spaced 𝑔 apart. Multiplying by $(2r_d + 1)/21$ normalizes to the reference window (21px across at 𝑠 = 1), so the value stays the same when the picture grows and both spacing and window grow with it. Lines that are sparse in a window 4 times wider form a small cluster in an otherwise empty area and are kept. Pattern rings are detected as three or more nearby closed lines of similar radius. References: a rule set by this program1.5s'$, the magnitude · direction of the Sobel gradient are measured and only maxima along the gradient direction are kept,
and edges are obtained by starting at magnitude 0.045 and continuing into neighbors of at least 0.02 (hysteresis) (excluding within $3s_g$px of drawn mass lines).
These are traced as in step 7, and only lines of at least $40s_g$ (closed lines at least $80s_g$) that pass the density · pattern-loop filters of step 4 are drawn once with strength 0.45 (halftone · grass · cloud texture drops out).
1.5s'$ reduces noise, and length thresholds and the density filter remove short texture. References: R19 Sobel · Feldman 1968 · R18 Canny 19868s_g$ pixels.
r = \max(3, 0.035\sqrt{N_c})$ (widened and narrowed by distance transforms), and among the resulting masses,
those covering at least 0.4% (rough 2: 0.2%) of the character and with dark lines (line alpha ≥ 0.5) in their non-skin holes covering at least 1% of the mass are used, by their inside (more than $2s_g$ from the edge).
In pictures with no face found at all, the one with the most lines in its holes is taken as the face; rough 2 also uses every mass without a found face (unfound faces · hands).
\sqrt{N_c}$ (the square root of the character area, a length), so it matches character size. Requiring dark lines in the holes to be at least 1% of the blob excludes bare arms and legs. References: R15 Soille 2003 · R21 Borgefors 1986\tau = \max(1.5,\ 2.5s_g)$ are grouped into blobs, and only those whose largest radius is $\rho_{max} \ge 2\tau$ and whose long side is at most $4\rho_{max} + 4$px (gathered roundly) are chosen (thick lines · line ends drop out).
The shape formed by the disks of each core pixel’s inner radius (holes filled) is removed from the lines, and its outer contour is traced as a closed line and drawn as face strokes. Thinning a painted iris to a line would leave a single dot. Thin attached eyelash · eyelid lines are still drawn as center lines.
4\rho_{max} + 4$) the shape is compact and round. Collecting a disk of each core pixel’s radius restores the shape, the property of the medial axis transform (Blum) that the union of maximal disks rebuilds a shape. Only that shape’s outer boundary is drawn, as a closed line. References: R37 Blum 1967 · R21 Borgefors 19863s' + 1.5$px whose outward directions have a dot product under $-0.766$ (continuing straight within 40°) are joined into one line.
-0.766 = \cos 140^\circ$, they nearly face each other (within 40° of a straight continuation) and are joined into one line. References: R36 Zhang · Suen 1984 · R14 Gonzalez · Woods 2018\mathrm{clamp}(\mathrm{round}(n/25), 2, 6)$ (1 for rough 2’s mass · inner lines, 2 for face lines) is applied twice and the line is resampled at 1.5px.
\mathrm{clamp}(\mathrm{round}(n/25), 2, 6)$ twice amounts to a triangular weighted average that removes the steps; longer lines get wider windows. Resampling at 1.5px keeps curvature and length calculations independent of point density. References: R14 Gonzalez · Woods 2018i - 4 \to i$ and $i \to i + 4$ exceeds the threshold, is not smaller than at the neighbors, and lies at least 4 points from the previous cut. People lift the pencil and start a new stroke at a bend.
x(t), y(t)$ in arc length $t \in [0, 1]$ are fitted by least squares; if the error at the farthest point exceeds the tolerance, the line is split there and fitted again (under 9 points it is kept as is).
x(t)$ and $y(t)$ are each fitted by a cubic polynomial with least squares (normal equations). If the farthest point’s error exceeds the tolerance, the line is split there and refitted recursively, so it is fitted with the fewest pieces within the tolerance. References: R38 Schneider 1990 · R43 Björck 199610s_g$ are removed, least deviating first, up to $2\times$ the tolerance (removing small teeth).
The line is split at remaining vertices bending more than 0.65 rad and the cubic above is fitted in between. Closed lines with a perimeter up to $60s_g$ (eyes · buttons) are not split at corners but fitted smoothly with the face tolerance.
k = \min(3, \lceil \text{length} / \text{stroke length}\rceil)$ strokes overlapping their neighbors by the overlap share (one curve drawn with 2–3 strokes).
k = \min(3, \lceil \text{length}/\text{stroke length}\rceil)$ sets the number of strokes (at most 3), and neighboring strokes overlap by the overlap ratio so the joins do not break. References: a rule set by this programk = 0, 1, 2$) is:
\ell_e = \min(9s_g, 0.2\,\text{stroke length})$, the first pass has $e_0, e_1 = -0.6u\,\ell_e$ and later passes $e_0, e_1 = (1.6u - 0.6)\,\ell_e$. Positive values start that much later or stop that much earlier (falling short), negative values extend that much along the tangent (overshooting). Face strokes never fall short.
-0.6u\ell_e$, so it always overshoots a little (0–0.6×), and repeat passes use $(1.6u - 0.6)\ell_e$, between −0.6× and 1×, either overshooting or falling short. Capping $\ell_e$ at 20% of the stroke length keeps short strokes from vanishing. References: a rule set by this programt \in [0, 1]$, the stroke is moved along the normal by $o(t) = g_0 + g_l(t - 0.5) + g_1\sin(\pi t + \varphi)$, with $g_0 = 0.9\,\sigma_k v$, $g_l = 1.6\,\sigma_k v$, $g_1 = 1.4\,\sigma_k v$, $\varphi = \pi u$ and $\sigma_k$ the offset of the table (first · later passes).
o(t)$ is a constant (shift) + a linear term (tilt) + a half-period sine (bow). All three amplitudes are proportional to the table offset $\sigma_k$ and the same random 𝑣, and the phase 𝜑 varies the bow per stroke. References: a rule set by this programPressure: with $\tau(t) = \mathrm{smoothstep}(0, t_a, t)\,\big(1 - \mathrm{smoothstep}(1 - t_b, 1, t)\big)$, $t_a = 0.12 + 0.18u$, $t_b = 0.15 + 0.2u$,
$\displaystyle \mathrm{pr}(t) = P_k\,\big(p_0 + (1 - p_0)\,\tau(t)\big)\,\big(0.88 + 0.12\sin(2\pi f t + \psi)\big),\qquad P_0 = 0.75 + 0.3u,\quad P_{k \ge 1} = 0.5 + 0.3u,\quad f = 1 + 2u,\quad \psi = 2\pi u$
$p_0$ is the pressure floor of the table (rough 2 is the settled line, so its ends taper less).
\tau(t)$ multiplies a rising and a falling smoothstep into a trapezoid-like shape, 0 at both ends and 1 in the middle. The pressure floor $p_0$ keeps the ends from going fully to 0, and $0.88 + 0.12\sin(2\pi ft + \psi)$ is a ±12% waver rising and falling 1–3 times per stroke. The first pass is stronger than repeats ($P_0 > P_{k\ge1}$). References: R47 Ebert et al. 2003 · a rule set by this program\lambda = 0.45 + 0.4\,\mathrm{clamp}\big((R/s' - 1.5)/2,\ 0,\ 1\big)$, $r = R\,\big(\lambda + (1.3 - \lambda)\min(1, \mathrm{pr})\big)$.
r = R(\lambda + (1.3 - \lambda)\min(1, \mathrm{pr}))$ interpolates linearly from $\lambda R$ at zero pressure to $1.3R$ at full pressure. The end-width ratio 𝜆 rises from 0.45 to 0.85 as the brush gets thicker (larger 𝑅/𝑠′), so thick brushes taper less. References: a rule set by this programa = o_k\,(0.35 + 0.65\,\mathrm{pr})$, $o_k = (1 \text{ or } 0.75 \text{ for later passes}) \cdot w$. The strength 𝑤 is $0.2 + 0.8\,c^2$ for mass lines (𝑐 the strongest boundary strength within $\mathrm{tol} + 2$px of the stroke: silhouette · inner), $0.2 + 0.8\min(1, \bar\ell)\cdot\text{the table's strength factor}$ for inner lines,
the table’s value for face strokes (ℓ̄ the mean line alpha along the stroke), and the table’s strength for the background composition.
0.35 + 0.65\,\mathrm{pr}$) times the strength 𝑤. For mass lines $0.2 + 0.8c^2$ squares the boundary strength 𝑐 so weak boundaries fade more; inner lines follow the mean line alpha ℓ̄ along them. Repeat passes are 0.75×, so overlaps darken gradually. References: a rule set by this programt^*$),
the coverage is $c = \min\big(1,\ r(t^*) + 0.5 - d\big)$ (not covered if 0 or less) and the value $c \cdot a(t^*)$ (anti-aliasing).
Within one pass each pixel takes its largest value 𝑥, and passes stack by screen: $\mathrm{acc} \gets 1 - (1 - \mathrm{acc})(1 - x)$.
r(t^*)$ there, $c = \min(1, r + 0.5 - d)$ approximates coverage falling from 1 to 0 across a 1px band, giving anti-aliasing (a round-capped segment is a capsule). Within one pass the maximum is used, so overlapping neighboring segments do not darken, and passes accumulate by screen $1 - (1 - \mathrm{acc})(1 - x)$, which never exceeds 1 yet darkens with each overlap. References: R14 Gonzalez · Woods 2018 · R3 ISO 32000-1 2008(\pm0.6, 0.7)$ · under the nose $(0, 0.5)$ · chin $(0, 1.1)$ are skin colored (lightness ≥ 55, a* · b* −2 to 35) or with a score ≥ 12.5 (if none, the highest-scoring such face),
and only if the face center lies on the character, get a skull circle (center $(0, -0.5)$, radius $1.3d$, three arcs of $2\pi/3 + 0.3$ rad), a vertical center line ($v = -1.9$ to $1.9$) and an eye line ($u = -1.5$ to $1.5$), drawn faintly (circle 0.3, lines 0.25, times the table’s factor).
1.3d$ is drawn as three broken arcs ($2\pi/3 + 0.3$ rad each), plus a vertical center line and a horizontal eye line, lightly, in the Loomis manner. It is drawn only for faces with skin at three of four points or a high score, so fake faces get none. References: R39 Loomis 1956 · a rule set by this programa = \min\big(1,\ \mathrm{acc}\,(0.92 + 0.08\,n)\big)$, where 𝑛 is value noise (4.2) with cell size $2.5s'$, a soft pencil grain 2–3px in size. From 0.57 rough 1 (pencil) adds noise $n_f$ with cell $1.1s'$: $a = \min\big(1,\ \mathrm{acc}\cdot\max(0,\ 0.74 + 0.18n + 0.22n_f)\big)$ (paper grain), and rough 2 (G-pen) has no grain: $a = \min(1, \mathrm{acc})$.
Pixels with $\mathrm{acc} < 0.01$ are left empty and the 8-bit alpha is $\mathrm{round}(255a)$. Rough color (default #2F6FE0), opacity $115/255 \approx 45\%$, hidden. The two layers go into the [Rough] folder with rough 1 below and rough 2 above.
2.5s'$. Values below 0.01 are left empty and the rest rounded to 8 bits. The layer opacity $115/255 \approx 45\%$ keeps it faint like an underdrawing. References: R47 Ebert et al. 2003 · a rule set by this program\le 0.25$) with darkness independent of pressure (strength 𝑤 unchanged).
\ell(\mathbf p) - \bar\ell_4(\mathbf p) \ge 0.15$ are collected ($\bar\ell_r$ is the mean of ℓ in a box of radius 𝑟), and $w_p = \sum \ell \,/\, \lvert \mathrm{thin} \rvert$ (ink sum ÷ length of the Zhang–Suen-thinned center line) is the pen width (1.25 if the center line has 50 pixels or fewer). The pen scale is $s_P = \mathrm{clamp}(w_p / 1.25,\ 0.5,\ 3)$.s_g$), the pen width measured from the picture sets the length thresholds · radii. How it works: line area divided by center-line length is the mean width. Only pixels darker than their surroundings are collected so masses of fill · shading do not inflate it. 1.25px is near the middle of pen widths measured on AI-generated pixiv pictures. References: R36 Zhang · Suen 1984 · rule set by this program
- Line selection (local contrast): with $r_C = \max(2,\ \mathrm{round}(3s_P))$, a pixel is a line if $\ell \ge 0.25$ and $\ell - \bar\ell_{r_C} \ge 0.15$ or $\ell - \bar\ell_{2r_C} \ge 0.15$ (two window sizes, 0.58).
Connected components of non-line pixels that do not touch the picture border and have area at most $25s_P^2$ are filled as line (sparkles · dots). In solid cores where the distance to the outside of the line $d_{in}$ exceeds $2.5s_P$, pixels within $2.5s_P - 1$ of the core with $d_{in} > 1.5$ are removed from the line, so filled masses keep only their outline.\max(4, 6s_P)$ pixels, spurs of $\max(2, 2s_P)$ steps pruned); the point list is smoothed twice with a ±2-point moving average and resampled every 1.5px.
It is split only where the angle between the directions $c_w = \max(3,\ \mathrm{round}(3s_P))$ points before and after exceeds 1.05 rad, is a local maximum, and lies at least $2c_w$ points past the previous split (not by length or fit error). An end within 2px of another line is a junction end, otherwise a free end.\sigma = \min(3.5s_P,\ L/6)$px (up to 0.58; from 0.59 see step 12) (beyond the ends the points are mirrored through the end point, so the ends stay in place). Pieces shorter than $4s_P$px are not drawn.\tilde h = \max(0.5,\ \mathrm{med}\,h - 0.5)$ the base radius is $r_0 = \mathrm{clamp}(1.25\tilde h + 0.3,\ 1.15s_P,\ 2.2s_P)$. Emphasis uses the ±20-point mean $\bar h_j$: $e_j = \mathrm{clamp}(0.7 + 0.3(\bar h_j - 0.5)/\tilde h,\ 0.85,\ 1.2)$.
With entry $t_i = \min(0.18L,\ 5s_P)$ and exit $t_o = \min(0.35L,\ 14s_P)$, the pressure at distance 𝑠 from the stroke start is$\displaystyle \mathrm{pr}(s) = \big(0.15 + 0.85\,E(s/t_i)\big)\,\big(0.05 + 0.95\,E((L - s)/t_o)\big),\qquad E(t) = \mathrm{smoothstep}(0, 1, t)$
(junction ends taper only a little: $0.75 + 0.25E$ · $0.7 + 0.3E$), the radius is $r = r_0\,e_j\,\mathrm{pr}$ and the darkness a constant 0.92 (up to 0.58; from 0.59 see step 12). Free ends are extended along their direction by 0.5× the entry · exit length.
o(t) = 0.45s_P\,\big(0.65\sin(2\pi c t + \varphi_1) + 0.35\sin(2\pi \cdot 2.3\,c t + \varphi_2)\big)$, $c = \max(0.3,\ L/(45s_P))$. Strokes run like a right-hander’s, mostly top → bottom and left → right (the tapering end at the bottom · right).45s_P$px, with amplitude about half the pen width. References: rule set by this program\sigma = \min(6s_P,\ L/4)$, the wobble amplitude drops to $0.15s_P$, the base radius is $r_0 = \mathrm{clamp}(1.4\tilde h + 0.3,\ 0.9s_P,\ 3.2s_P)$, and the width emphasis uses the mean $\bar h_j$ over 20 points on each side, $e_j = \mathrm{clamp}(0.7 + 0.3\,\bar h_j/\tilde h,\ 0.75,\ 1.35)$. Pieces split at bends are drawn only if at least $6s_P$ long.
t \in [0, 1]$ along the stroke$\displaystyle q(t) = \mathrm{clamp}\Big(0.5 + 0.5\,e^{-((t - c)/w)^2} + 0.12\sin(2\pi f t + \varphi) + 0.15\min(1,\ \theta/0.8),\ 0.3,\ 1\Big)$
with 𝑐 ∈ [0.3, 0.6] · 𝑤 ∈ [0.3, 0.55] · φ random per stroke, $f = \max(0.5,\ L/(80s_P))$ times a factor in [0.7, 1.3], and θ the bend angle between the directions 3 points before and after. The radius is $r = r_0\,e_j\,\mathrm{pr}\,(0.5 + 0.5q)$ (pr is step 11’s entry · exit taper).
c_N = \mathrm{clamp}\big(\mathrm{med}(\ell - \bar\ell_{r_C})/0.3,\ 0,\ 1\big)$ and the width $w_N = \mathrm{clamp}(2\tilde h/w_p - 0.5,\ 0,\ 1)$, the importance is $m = \min\big(1,\ 0.5c_N + 0.5w_N + 0.25\,[L > 40s_P] + 0.15\,[\tilde\ell > 0.85]\big)$ (𝓁̃: median line alpha on the stroke), and the base darkness $a_0 = 0.95\,(0.7 + 0.3m)$. If more than half of the points are inside a face area, $a_0 \ge 0.95 \times 0.95$. The darkness at a point is $a = a_0\,(0.75 + 0.25q)\,(0.65 + 0.35\,\mathrm{pr})$.L > 20s_P$ get a section of length [0.3, 0.55] starting at [0.12, 0.42] of the stroke drawn once more, offset [0.15, 0.3]$s_P$ along the normal, with radius $0.7\,r_0\,(0.3 + 0.7\sin\pi u)$ and darkness $0.5\,a_0\,(0.3 + 0.7\sin\pi u)$ (𝑢: relative position within the section).8s_P$ (shorter than $12s_P$ if neither end touches another line) are not drawn. From 0.59e they are removed only when more than half of their points have another line within $2.5s_P$ (ladder rungs · spurs running alongside), and lone short strokes (glasses frames · fingers) are kept.c_x \pm 1.0d$ across and $c_y - 0.35d$ to $c_y + 1.25d$ down (for a guessed face, the whole guessed face area) $\ell \ge 0.15$ counts as line regardless of step 11’s local contrast, and solid blobs are not hollowed (one stroke through the middle).
| Part | Content |
|---|---|
| Header | 8BPS, version 1, 6 reserved bytes, 3 channels (4 for transparent pictures), height, width, 8 bits, color mode 3 (RGB), color mode data length 0 |
| Image resources | resolution info (8BIM, 0x03ED, length 16): horizontal · vertical 72 dpi as 16.16 fixed point $72 \cdot 2^{16}$, unit 1 (inch) · 1 |
| Layer info | layer count (negative for transparent pictures: the first alpha of the merged image is the transparency), per layer the bounds (top, left, $\text{bottom} = \text{top} + \text{height}$, $\text{right} = \text{left} + \text{width}$) · 4 channels (ID −1 for alpha, 0 · 1 · 2 for R · G · B, each with its data length) · 8BIM · blend key · opacity (0–255) · clipping (0 / 1) · flags (hidden 2, folder records add 0x18) · 0 |
| Layer extra info | mask length 0, blending ranges (length 40: source · destination ranges 0x0000FFFF for gray + 4 channels, 10 values), Pascal name, luni (Unicode name), lsct for folders |
| Channel data | per layer, alpha, R, G, B in order, PackBits RLE per row (compression 1 + row length table + data) |
| Merged image | the same composite as the preview (on a white backing for opaque pictures), channels R, G, B(, A) all RLE |
Note — Why: The byte layout of the PSD specification is followed exactly so Photoshop and other paint programs can read the file. How it works: Resolution is 16.16 fixed point (the upper 16 bits are the integer part), so 72dpi is $72 \cdot 2^{16}$. Layer bounds are top, left, bottom, right coordinates, so bottom = top + height. For transparent pictures the layer count is written negative to signal that the merged image’s first alpha channel is transparency. References: R4 Adobe PSD
[1-byte length][characters], zero-padded to a multiple of 4 bytes.
luni has length $4 + 2n$ (𝑛 = number of UTF-16 characters) rounded up to a multiple of 4, then [n][UTF-16BE characters][padding]; lsct is length 4 (3) for an end marker, and length 12 (1 open folder / 2 closed folder, 8BIM, blend key) for a folder.
The whole layer info length is also padded to a multiple of 4, and the global mask info length is 0.
luni holds the character count 𝑛 (4 bytes) plus 2 bytes per UTF-16 unit, $4 + 2n$ bytes, rounded up to a multiple of 4 with zeros. Pascal names likewise pad 1 length byte + characters to a multiple of 4. References: R4 Adobe PSD[257 − n][value], otherwise as [n − 1][n values] until the next run of 3 identical values starts (at most 128).
The largest size of one row is reserved as $w + \lceil w/128\rceil + 2$ bytes. Empty layers (folder records) write only the 2-byte compression method per channel.
-(n - 1)$ as a signed byte, i.e. $257 - n$) or “the next 𝑛 bytes as they are” ($n - 1$), with 𝑛 ≤ 128. The worst case is all literals, adding one header per 128 bytes, so $w + \lceil w/128\rceil$ bytes plus 2 spare are reserved. References: R5 Apple 1996</Layer group> end marker (lsct 3) is placed before the folder’s content and the folder record after it.norm, mul , scrn, pass.PSD.rename)When a PSD · PSB comes in as input (extension .psd · .psb, or type image/vnd.adobe.photoshop), this is done instead of splitting a picture.
For each entry of the old name → current name list (색트레스 → 색 트레이스), the file is read byte by byte as below and only the layer records containing the name are rewritten;
if any layer changed, it is saved under the same file name (0.4 s apart for several files). Below, from is the old name and to the current name.
Lr16 · Lr32 · Layr (layer info of 16 · 32-bit pictures) in the global extra info after it, are read the same way.
For each layer, the channel lengths are summed to get the total channel data length, and every from inside the luni names of the extra info is replaced with to.
The Pascal name is also read as EUC-KR, and if it contains from, the changed name is written in EUC-KR (when 255 bytes or less; files re-saved by the Korean edition of Photoshop).
The EUC-KR table is built in reverse by decoding all two-byte codes 0xA1A1–0xFEFE with TextDecoder('euc-kr').8BIM · 8B64 signature is chosen (in PSB, the document’s 8-byte keys and cinf try 8 first, the rest try 4 first).luni is rewritten with length $4 + 2n$ rounded up to a multiple of 4, and only that layer’s extra info length, the layer info length (padded to 4 if it was originally a multiple of 4, otherwise to a multiple of 2; Lr16 etc. to a multiple of 4),
and the layer and mask info length are written anew. Channel pixels · merged image · image resources · global mask are copied byte for byte. If no layer needs changing, nothing is saved and the program says so.
luni length $4 + 2n$ is rounded up to a multiple of 4, and the outer block lengths (layer extra data → layer info → layer and mask info) are corrected by the difference in turn. Pixel data is copied unchanged, so the image does not change. References: R4 Adobe PSDLayers are stacked with the canvas 2D globalCompositeOperation (multiply, screen) and $\texttt{globalAlpha} = \text{opacity}/255$.
Clipping groups work like Photoshop: the clipping layers are blended over the base layer made opaque (all alpha 255), then cut once by the base layer’s alpha (destination-in) and drawn with the base layer’s blend mode · opacity.
If the base layer is hidden, its clipping layers are hidden too. A folder is drawn separately and stacked as one sheet if its opacity is below 255 or its blend mode is not pass-through · normal; otherwise its layers are stacked as they are.
PNG · PSD merged images of opaque pictures are composited on a white backing.
Note — Why: The on-screen preview must match what Photoshop shows so the PSD can be checked before opening it. How it works: The canvas multiply and screen modes use the same blend formulas as PDF/Photoshop, and layer opacity 0–255 is divided into $\texttt{globalAlpha}$ 0–1. Clipping cuts once with the base layer’s alpha (destination-in), so the whole clipping group is confined to the base layer’s shape as in Photoshop. References: R3 ISO 32000-1 2008 · R4 Adobe PSD
\min(255, 8\lvert \text{composite} - \text{original} \rvert)$ (8-bit) per channel.
z = \min((W_{view} - 32)/W, (H_{view} - 32)/H, 4)$, centered $t = (W_{view} - Wz)/2$ (vertically likewise).z' = \mathrm{clamp}(z\,e^{-0.0015\Delta},\ 0.05,\ 32)$ (Δ = wheel amount), $t' = c - (c - t) \cdot z'/z$ to keep the point under cursor 𝑐 fixed. At zoom 2 or more, pixels are enlarged as they are.
z' = z\,e^{-0.0015\Delta}$ is exponential, so equal wheel movement gives equal scale ratios (a log scale). To keep the picture point under the cursor, $(c - t)/z$, fixed after zooming, the offset becomes $t' = c - (c - t)z'/z$. References: a rule set by this program(x_s, y_s)$ is $(\lfloor(x_s - \text{left})/z \rfloor, \lfloor(y_s - \text{top})/z \rfloor)$, and correction strokes are recorded densely at 3px intervals by adding points (rounded) dividing the distance from the previous point into $\lceil \text{distance}/3 \rceil$ parts.44 / \max(W, H)$×), and the opacity slider is $\mathrm{round}(\text{opacity}/2.55)$%.
\lceil \text{distance}/3\rceil$ subdivision points are added so recorded points are at most 3px apart. Thumbnails shrink the long side to 44px, and opacity 0–255 is shown as 0–100% by dividing by 2.55. References: a rule set by this programSend to background
The line alpha is the largest 8-bit alpha $\alpha_8$ (0–255) among all line art layers. “On a line” is $\alpha_8 \ge 110$ (about 0.43), and “surrounding line” is $\alpha_8 \ge 20$ (about 0.08).
The brush width is $R_b = \max(24, \mathrm{round}(0.05 \cdot \max(W, H)))$, and the distance from the stroke is the number of 4-neighbor breadth-first steps starting from the stroke’s points (up to $R_b$).
If all points of the stroke are within $\lvert \Delta x\rvert + \lvert \Delta y\rvert \le 4$ of the first point, it is a single click; otherwise it is rubbing. The length of rubbing is the sum of distances between neighboring points,
and if it is 48px or more, points within 12px of both ends (distance measured along the stroke) are treated as stroke ends.
Note — Why: Even when the user’s brush crosses a line or grazes a boundary, only the intended object must move, so lines, brush width and tap versus drag are settled first. How it works: The largest alpha among line-art layers is the line alpha; 110/255 (about 0.43) or more is an uncrossable line and 20/255 or more a rim line. The brush width is 5% of the long side (at least 24px). If every point is within Manhattan distance 4 of the first, it is one tap with a shaky hand. The 12px at each end of long strokes start and stop imprecisely, so they are excluded as fill seeds. References: a rule set by this program
Sending to background
\Delta E < 12 \land \Delta ab < 9$ from them (generous on brightness difference, narrow on hue difference).
A part’s color is the Lab of the first opaque pixel met in the base color layer, $\Delta ab = \sqrt{\Delta a^{*2} + \Delta b^{*2}}$.
\Delta ab = \sqrt{\Delta a^{*2} + \Delta b^{*2}}$ is the distance in the chromaticity plane, ignoring lightness. $\Delta E < 12$ overall accepts lightness differences generously, and $\Delta ab < 9$ accepts hue and chroma differences narrowly, admitting only parts related by shading. References: R7 CIE 2004\max_c \lvert \Delta O_c\rvert \le 40$ (26 if there is one part, 8-bit).
\max_c \lvert \Delta O_c\rvert < 16$), are kept on the character side.
Return to character: chosen inside the background the same way (tolerance 40, no part distinction), 2 steps toward the background (anti-aliased edge) within the brush width are added, then recorded in the “character” mask and split again.
Loop: with 30 or more points, a stroke length of 120px or more, and the end point within $\max(24, 0.2 \times \text{diagonal of the bounding rectangle})$ of the start point, it is treated as a closed loop.
For each row, the 𝑥 where the loop’s edges cross height $y + 0.5$ ($x = x_a + (y + 0.5 - y_a)(x_b - x_a)/(y_b - y_a)$, edges whose two end points are on different sides of that height) are sorted and
the paired spans $[\lceil x_1 - 0.5\rceil, \lfloor x_2 - 0.5\rfloor]$ are filled (even-odd rule). If the inside is under 400px, it is not treated as a loop.
The inside is matched per piece surrounded by lines (4-connected pieces of the same part for sending to background, 4-connected pieces of the background for returning to character):
if the share 𝑓 of the piece’s pixels inside the loop is $f \ge 0.6$, the whole piece; if $f \le 0.4$, excluded; in between, only the inside of the loop is chosen.
Sending to background uses up to 12 steps from the loop; returning to character widens the chosen place by 2 steps but uses only up to 2 steps from the loop.
Note — Why: When the user draws around an object (lasso), its inside must be selectable at once. How it works: At each row’s pixel-center height $y + 0.5$, the 𝑥 where the loop’s edges cross is found by linear interpolation $x = x_a + (y + 0.5 - y_a)(x_b - x_a)/(y_b - y_a)$, and the sorted crossings are filled in pairs: scanline filling with the even–odd rule. Only edges whose ends lie on different sides of that height are counted, so vertices are not counted twice. Line-enclosed pieces are taken whole at 0.6 or more inside the loop, dropped at 0.4 or less, and only in between follow the loop (the gap between thresholds lets slightly sloppy loops still snap to pieces). References: a rule set by this program
Re-split cache: the automatic background result is reused if $(W, H, \text{picture hash}, \text{line width}, \text{line sensitivity}, \text{line color mode}, \text{background separation})$ are the same.
The picture hash is a two-way FNV-1a (32-bit multiplication) over the RGBA bytes $v_i$, plus the length.
h_1\leftarrow (h_1\oplus v_i)\cdot 16777619,\quad h_1^{(0)}=\mathtt{0x811c9dc5};\qquad
h_2\leftarrow (h_2\oplus v_i)\cdot 2246822519+i,\quad h_2^{(0)}=\mathtt{0x9747b28c}\qquad(\bmod 2^{32})
Note — Why: Background detection is slow, so re-splitting the same picture with the same settings reuses the previous result. A fingerprint is needed to check quickly that the picture is the same. How it works: FNV-1a is a fast hash that XORs each byte and multiplies by the prime 16777619 ($\bmod 2^{32}$). Using a second hash with a different multiplier and seed that also mixes in the position 𝑖, plus the length, makes an accidental match between different pictures roughly $2^{-64}$ likely. References: R40 Fowler et al.